Skip to content
Open
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/BuiltinsPPC.def
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,10 @@ TARGET_BUILTIN(__builtin_darn_32, "i", "", "isa-v30-instructions")
TARGET_BUILTIN(__builtin_unpack_vector_int128, "ULLiV1LLLii", "", "vsx")
TARGET_BUILTIN(__builtin_pack_vector_int128, "V1LLLiULLiULLi", "", "vsx")

// AMO builtins
TARGET_BUILTIN(__builtin_amo_lwat, "UiUi*UiIi", "", "isa-v30-instructions")
TARGET_BUILTIN(__builtin_amo_ldat, "ULiULi*ULiIi", "", "isa-v30-instructions")

// Set the floating point rounding mode
BUILTIN(__builtin_setrnd, "di", "")

Expand Down
1 change: 1 addition & 0 deletions clang/lib/Headers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ set(opencl_files

set(ppc_files
altivec.h
amo.h
)

set(ppc_htm_files
Expand Down
97 changes: 97 additions & 0 deletions clang/lib/Headers/amo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*===---- amo.h - PowerPC Atomic Memory Operations ------------------------===*\
*
* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
* See https://llvm.org/LICENSE.txt for license information.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*
\*===----------------------------------------------------------------------===*/

/* This header provides compatibility wrappers for GCC's AMO functions.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can just say provides compatibility with GCC AMO functions.

* The functions here call Clang's underlying AMO builtins.
*/

#ifndef _AMO_H
#define _AMO_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* AMO Load Operation Codes (FC values) */
enum {
_AMO_LD_ADD = 0x00, /* Fetch and Add */
_AMO_LD_XOR = 0x01, /* Fetch and XOR */
_AMO_LD_IOR = 0x02, /* Fetch and OR */
_AMO_LD_AND = 0x03, /* Fetch and AND */
_AMO_LD_UMAX = 0x04, /* Fetch and Maximum Unsigned */
_AMO_LD_SMAX = 0x05, /* Fetch and Maximum Signed */
_AMO_LD_UMIN = 0x06, /* Fetch and Minimum Unsigned */
_AMO_LD_SMIN = 0x07, /* Fetch and Minimum Signed */
_AMO_LD_SWAP = 0x08 /* Swap */
};

/* 32-bit unsigned AMO load operations */
static inline uint32_t amo_lwat_add(uint32_t *ptr, uint32_t val) {
return __builtin_amo_lwat(ptr, val, _AMO_LD_ADD);
}

static inline uint32_t amo_lwat_xor(uint32_t *ptr, uint32_t val) {
return __builtin_amo_lwat(ptr, val, _AMO_LD_XOR);
}

static inline uint32_t amo_lwat_ior(uint32_t *ptr, uint32_t val) {
return __builtin_amo_lwat(ptr, val, _AMO_LD_IOR);
}

static inline uint32_t amo_lwat_and(uint32_t *ptr, uint32_t val) {
return __builtin_amo_lwat(ptr, val, _AMO_LD_AND);
}

static inline uint32_t amo_lwat_umax(uint32_t *ptr, uint32_t val) {
return __builtin_amo_lwat(ptr, val, _AMO_LD_UMAX);
}

static inline uint32_t amo_lwat_umin(uint32_t *ptr, uint32_t val) {
return __builtin_amo_lwat(ptr, val, _AMO_LD_UMIN);
}

static inline uint32_t amo_lwat_swap(uint32_t *ptr, uint32_t val) {
return __builtin_amo_lwat(ptr, val, _AMO_LD_SWAP);
}

/* 64-bit unsigned AMO load operations */
static inline uint64_t amo_ldat_add(uint64_t *ptr, uint64_t val) {
return __builtin_amo_ldat(ptr, val, _AMO_LD_ADD);
}

static inline uint64_t amo_ldat_xor(uint64_t *ptr, uint64_t val) {
return __builtin_amo_ldat(ptr, val, _AMO_LD_XOR);
}

static inline uint64_t amo_ldat_ior(uint64_t *ptr, uint64_t val) {
return __builtin_amo_ldat(ptr, val, _AMO_LD_IOR);
}

static inline uint64_t amo_ldat_and(uint64_t *ptr, uint64_t val) {
return __builtin_amo_ldat(ptr, val, _AMO_LD_AND);
}

static inline uint64_t amo_ldat_umax(uint64_t *ptr, uint64_t val) {
return __builtin_amo_ldat(ptr, val, _AMO_LD_UMAX);
}

static inline uint64_t amo_ldat_umin(uint64_t *ptr, uint64_t val) {
return __builtin_amo_ldat(ptr, val, _AMO_LD_UMIN);
}

static inline uint64_t amo_ldat_swap(uint64_t *ptr, uint64_t val) {
return __builtin_amo_ldat(ptr, val, _AMO_LD_SWAP);
}

#ifdef __cplusplus
}
#endif

#endif /* _AMO_H */
15 changes: 15 additions & 0 deletions clang/lib/Sema/SemaPPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ static bool isPPC_64Builtin(unsigned BuiltinID) {
case PPC::BI__builtin_ppc_fetch_and_andlp:
case PPC::BI__builtin_ppc_fetch_and_orlp:
case PPC::BI__builtin_ppc_fetch_and_swaplp:
case PPC::BI__builtin_amo_lwat:
case PPC::BI__builtin_amo_ldat:
return true;
}
return false;
Expand Down Expand Up @@ -253,6 +255,19 @@ bool SemaPPC::CheckPPCBuiltinFunctionCall(const TargetInfo &TI,
case PPC::BI__builtin_##Name: \
return BuiltinPPCMMACall(TheCall, BuiltinID, Types);
#include "clang/Basic/BuiltinsPPC.def"
case PPC::BI__builtin_amo_lwat:
case PPC::BI__builtin_amo_ldat: {
llvm::APSInt Result;
if (SemaRef.BuiltinConstantArg(TheCall, 2, Result))
return true;
unsigned Val = Result.getZExtValue();
static constexpr unsigned ValidFC[] = {0, 1, 2, 3, 4, 6, 8};
if (llvm::is_contained(ValidFC, Val))
return false;
Expr *Arg = TheCall->getArg(2);
return SemaRef.Diag(Arg->getBeginLoc(), diag::err_argument_invalid_range)
<< toString(Result, 10) << "0-4, 6" << "8" << Arg->getSourceRange();
}
}
llvm_unreachable("must return from switch");
}
Expand Down
18 changes: 18 additions & 0 deletions clang/test/CodeGen/PowerPC/builtins-amo-err.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// RUN: not %clang_cc1 -triple powerpc-ibm-aix -target-cpu pwr9 \
// RUN: -emit-llvm %s -o - 2>&1 | FileCheck %s --check-prefix=AIX32-ERROR
// RUN: not %clang_cc1 -triple powerpc64-ibm-aix -target-cpu pwr9 \
// RUN: -emit-llvm %s -o - 2>&1 | FileCheck %s --check-prefix=FC-ERROR

void test_amo() {
unsigned int *ptr1, value1;
// AIX32-ERROR: error: this builtin is only available on 64-bit targets
__builtin_amo_lwat(ptr1, value1, 0);
// FC-ERROR: argument value 9 is outside the valid range [0-4, 6, 8]
__builtin_amo_lwat(ptr1, value1, 9);

unsigned long int *ptr2, value2;
// AIX32-ERROR: error: this builtin is only available on 64-bit targets
__builtin_amo_ldat(ptr2, value2, 3);
// FC-ERROR: error: argument value 26 is outside the valid range [0-4, 6, 8]
__builtin_amo_ldat(ptr2, value2, 26);
}
58 changes: 58 additions & 0 deletions clang/test/CodeGen/PowerPC/builtins-ppc-amo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
// RUN: %clang_cc1 -O3 -triple powerpc64le-unknown-unknown -target-cpu pwr9 \
// RUN: -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -O3 -triple powerpc64-ibm-aix -target-cpu pwr9 \
// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=AIX

// CHECK-LABEL: define dso_local void @test_unsigned_lwat(
// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef zeroext [[VALUE:%.*]], ptr noundef writeonly captures(none) initializes((0, 4)) [[RESP:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[TMP0:%.*]] = tail call i32 @llvm.ppc.amo.lwat(ptr [[PTR]], i32 [[VALUE]], i32 0)
// CHECK-NEXT: store i32 [[TMP0]], ptr [[RESP]], align 4, !tbaa [[INT_TBAA2:![0-9]+]]
// CHECK-NEXT: ret void
//
// AIX-LABEL: define void @test_unsigned_lwat(
// AIX-SAME: ptr noundef [[PTR:%.*]], i32 noundef zeroext [[VALUE:%.*]], ptr noundef writeonly captures(none) initializes((0, 4)) [[RESP:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
// AIX-NEXT: [[ENTRY:.*:]]
// AIX-NEXT: [[TMP0:%.*]] = tail call i32 @llvm.ppc.amo.lwat(ptr [[PTR]], i32 [[VALUE]], i32 0)
// AIX-NEXT: store i32 [[TMP0]], ptr [[RESP]], align 4, !tbaa [[INT_TBAA2:![0-9]+]]
// AIX-NEXT: ret void
//
void test_unsigned_lwat(unsigned int *ptr, unsigned int value, unsigned int * resp) {
unsigned int res = __builtin_amo_lwat(ptr, value, 0);
*resp = res;
}

// CHECK-LABEL: define dso_local void @test_unsigned_ldat(
// CHECK-SAME: ptr noundef [[PTR:%.*]], i64 noundef [[VALUE:%.*]], ptr noundef writeonly captures(none) initializes((0, 8)) [[RESP:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.ppc.amo.ldat(ptr [[PTR]], i64 [[VALUE]], i32 3)
// CHECK-NEXT: store i64 [[TMP0]], ptr [[RESP]], align 8, !tbaa [[LONG_TBAA6:![0-9]+]]
// CHECK-NEXT: ret void
//
// AIX-LABEL: define void @test_unsigned_ldat(
// AIX-SAME: ptr noundef [[PTR:%.*]], i64 noundef [[VALUE:%.*]], ptr noundef writeonly captures(none) initializes((0, 8)) [[RESP:%.*]]) local_unnamed_addr #[[ATTR0]] {
// AIX-NEXT: [[ENTRY:.*:]]
// AIX-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.ppc.amo.ldat(ptr [[PTR]], i64 [[VALUE]], i32 3)
// AIX-NEXT: store i64 [[TMP0]], ptr [[RESP]], align 8, !tbaa [[LONG_TBAA6:![0-9]+]]
// AIX-NEXT: ret void
//
void test_unsigned_ldat(unsigned long int *ptr, unsigned long int value, unsigned long int * resp) {
unsigned long int res = __builtin_amo_ldat(ptr, value, 3);
*resp = res;
}
//.
// CHECK: [[INT_TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0}
// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0}
// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0}
// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"}
// CHECK: [[LONG_TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0}
// CHECK: [[META7]] = !{!"long", [[META4]], i64 0}
//.
// AIX: [[INT_TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0}
// AIX: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0}
// AIX: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0}
// AIX: [[META5]] = !{!"Simple C/C++ TBAA"}
// AIX: [[LONG_TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0}
// AIX: [[META7]] = !{!"long", [[META4]], i64 0}
//.
91 changes: 91 additions & 0 deletions clang/test/CodeGen/PowerPC/ppc-amo-header.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// REQUIRES: powerpc-registered-target
// RUN: %clang_cc1 -triple powerpc64le-unknown-linux-gnu -target-cpu pwr9 \
// RUN: -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -triple powerpc64-ibm-aix -target-cpu pwr9 \
// RUN: -emit-llvm %s -o - | FileCheck %s

#include <amo.h>

uint32_t test_lwat_add(uint32_t *ptr, uint32_t val) {
// CHECK-LABEL: @test_lwat_add
// CHECK: call i32 @llvm.ppc.amo.lwat(ptr %{{.*}}, i32 %{{.*}}, i32 0)
return amo_lwat_add(ptr, val);
}

uint32_t test_lwat_xor(uint32_t *ptr, uint32_t val) {
// CHECK-LABEL: @test_lwat_xor
// CHECK: call i32 @llvm.ppc.amo.lwat(ptr %{{.*}}, i32 %{{.*}}, i32 1)
return amo_lwat_xor(ptr, val);
}

uint32_t test_lwat_ior(uint32_t *ptr, uint32_t val) {
// CHECK-LABEL: @test_lwat_ior
// CHECK: call i32 @llvm.ppc.amo.lwat(ptr %{{.*}}, i32 %{{.*}}, i32 2)
return amo_lwat_ior(ptr, val);
}

uint32_t test_lwat_and(uint32_t *ptr, uint32_t val) {
// CHECK-LABEL: @test_lwat_and
// CHECK: call i32 @llvm.ppc.amo.lwat(ptr %{{.*}}, i32 %{{.*}}, i32 3)
return amo_lwat_and(ptr, val);
}

uint32_t test_lwat_umax(uint32_t *ptr, uint32_t val) {
// CHECK-LABEL: @test_lwat_umax
// CHECK: call i32 @llvm.ppc.amo.lwat(ptr %{{.*}}, i32 %{{.*}}, i32 4)
return amo_lwat_umax(ptr, val);
}

uint32_t test_lwat_umin(uint32_t *ptr, uint32_t val) {
// CHECK-LABEL: @test_lwat_umin
// CHECK: call i32 @llvm.ppc.amo.lwat(ptr %{{.*}}, i32 %{{.*}}, i32 6)
return amo_lwat_umin(ptr, val);
}

uint32_t test_lwat_swap(uint32_t *ptr, uint32_t val) {
// CHECK-LABEL: @test_lwat_swap
// CHECK: call i32 @llvm.ppc.amo.lwat(ptr %{{.*}}, i32 %{{.*}}, i32 8)
return amo_lwat_swap(ptr, val);
}

uint64_t test_ldat_add(uint64_t *ptr, uint64_t val) {
// CHECK-LABEL: @test_ldat_add
// CHECK: call i64 @llvm.ppc.amo.ldat(ptr %{{.*}}, i64 %{{.*}}, i32 0)
return amo_ldat_add(ptr, val);
}

uint64_t test_ldat_xor(uint64_t *ptr, uint64_t val) {
// CHECK-LABEL: @test_ldat_xor
// CHECK: call i64 @llvm.ppc.amo.ldat(ptr %{{.*}}, i64 %{{.*}}, i32 1)
return amo_ldat_xor(ptr, val);
}

uint64_t test_ldat_ior(uint64_t *ptr, uint64_t val) {
// CHECK-LABEL: @test_ldat_ior
// CHECK: call i64 @llvm.ppc.amo.ldat(ptr %{{.*}}, i64 %{{.*}}, i32 2)
return amo_ldat_ior(ptr, val);
}

uint64_t test_ldat_and(uint64_t *ptr, uint64_t val) {
// CHECK-LABEL: @test_ldat_and
// CHECK: call i64 @llvm.ppc.amo.ldat(ptr %{{.*}}, i64 %{{.*}}, i32 3)
return amo_ldat_and(ptr, val);
}

uint64_t test_ldat_umax(uint64_t *ptr, uint64_t val) {
// CHECK-LABEL: @test_ldat_umax
// CHECK: call i64 @llvm.ppc.amo.ldat(ptr %{{.*}}, i64 %{{.*}}, i32 4)
return amo_ldat_umax(ptr, val);
}

uint64_t test_ldat_umin(uint64_t *ptr, uint64_t val) {
// CHECK-LABEL: @test_ldat_umin
// CHECK: call i64 @llvm.ppc.amo.ldat(ptr %{{.*}}, i64 %{{.*}}, i32 6)
return amo_ldat_umin(ptr, val);
}

uint64_t test_ldat_swap(uint64_t *ptr, uint64_t val) {
// CHECK-LABEL: @test_ldat_swap
// CHECK: call i64 @llvm.ppc.amo.ldat(ptr %{{.*}}, i64 %{{.*}}, i32 8)
return amo_ldat_swap(ptr, val);
}
12 changes: 12 additions & 0 deletions llvm/include/llvm/IR/IntrinsicsPowerPC.td
Original file line number Diff line number Diff line change
Expand Up @@ -2139,3 +2139,15 @@ let TargetPrefix = "ppc" in {
Intrinsic<[], [llvm_i64_ty, llvm_i64_ty, llvm_ptr_ty],
[IntrArgMemOnly, IntrWriteMem, NoCapture<ArgIndex<2>>]>;
}

// AMO intrisics
let TargetPrefix = "ppc" in {
def int_ppc_amo_lwat : ClangBuiltin<"__builtin_amo_lwat">,
DefaultAttrsIntrinsic<[llvm_i32_ty],[llvm_ptr_ty,
llvm_i32_ty, llvm_i32_ty],
[IntrArgMemOnly, ImmArg<ArgIndex<2>>]>;
def int_ppc_amo_ldat : ClangBuiltin<"__builtin_amo_ldat">,
DefaultAttrsIntrinsic<[llvm_i64_ty],[llvm_ptr_ty,
llvm_i64_ty, llvm_i32_ty],
[IntrArgMemOnly, ImmArg<ArgIndex<2>>]>;
}
Loading
Loading