From: Tim Northover Date: Wed, 2 Jul 2014 12:56:02 +0000 (+0000) Subject: ARM: add support for v8 ldaex/stlex builtins. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ce437d1ab312eeb3a267b5038a5ddba04f2f9e7b;p=clang ARM: add support for v8 ldaex/stlex builtins. ARMv8 adds (to both AArch32 and AArch64) acquiring and releasing variants of the exclusive operations, in line with the C++11 memory model. This adds support for two new intrinsics to expose them to C & C++ developers directly: __builtin_arm_ldaex and __builtin_arm_stlex, in direct analogy with the versions with no implicit barrier. rdar://problem/15885451 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@212175 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/docs/LanguageExtensions.rst b/docs/LanguageExtensions.rst index de9d513d5b..ad7821af70 100644 --- a/docs/LanguageExtensions.rst +++ b/docs/LanguageExtensions.rst @@ -1580,7 +1580,9 @@ instructions for implementing atomic operations. .. code-block:: c T __builtin_arm_ldrex(const volatile T *addr); + T __builtin_arm_ldaex(const volatile T *addr); int __builtin_arm_strex(T val, volatile T *addr); + int __builtin_arm_stlex(T val, volatile T *addr); void __builtin_arm_clrex(void); The types ``T`` currently supported are: @@ -1589,11 +1591,11 @@ The types ``T`` currently supported are: * Pointer types. Note that the compiler does not guarantee it will not insert stores which clear -the exclusive monitor in between an ``ldrex`` and its paired ``strex``. In -practice this is only usually a risk when the extra store is on the same cache -line as the variable being modified and Clang will only insert stack stores on -its own, so it is best not to use these operations on variables with automatic -storage duration. +the exclusive monitor in between an ``ldrex`` type operation and its paired +``strex``. In practice this is only usually a risk when the extra store is on +the same cache line as the variable being modified and Clang will only insert +stack stores on its own, so it is best not to use these operations on variables +with automatic storage duration. Also, loads and stores may be implicit in code written between the ``ldrex`` and ``strex``. Clang will not necessarily mitigate the effects of these either, so diff --git a/include/clang/Basic/BuiltinsAArch64.def b/include/clang/Basic/BuiltinsAArch64.def index c2161785b3..c3852fbb42 100644 --- a/include/clang/Basic/BuiltinsAArch64.def +++ b/include/clang/Basic/BuiltinsAArch64.def @@ -18,7 +18,9 @@ BUILTIN(__clear_cache, "vv*v*", "i") BUILTIN(__builtin_arm_ldrex, "v.", "t") +BUILTIN(__builtin_arm_ldaex, "v.", "t") BUILTIN(__builtin_arm_strex, "i.", "t") +BUILTIN(__builtin_arm_stlex, "i.", "t") BUILTIN(__builtin_arm_clrex, "v", "") // Bit manipulation diff --git a/include/clang/Basic/BuiltinsARM.def b/include/clang/Basic/BuiltinsARM.def index 00cd2c1de4..bc8186f96a 100644 --- a/include/clang/Basic/BuiltinsARM.def +++ b/include/clang/Basic/BuiltinsARM.def @@ -32,7 +32,9 @@ BUILTIN(__builtin_arm_ldrexd, "LLUiv*", "") BUILTIN(__builtin_arm_strexd, "iLLUiv*", "") BUILTIN(__builtin_arm_ldrex, "v.", "t") +BUILTIN(__builtin_arm_ldaex, "v.", "t") BUILTIN(__builtin_arm_strex, "i.", "t") +BUILTIN(__builtin_arm_stlex, "i.", "t") BUILTIN(__builtin_arm_clrex, "v", "") // VFP diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index d90fa69ad1..fb71d1d830 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -3074,9 +3074,12 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, } if (BuiltinID == ARM::BI__builtin_arm_ldrexd || - (BuiltinID == ARM::BI__builtin_arm_ldrex && + ((BuiltinID == ARM::BI__builtin_arm_ldrex || + BuiltinID == ARM::BI__builtin_arm_ldaex) && getContext().getTypeSize(E->getType()) == 64)) { - Function *F = CGM.getIntrinsic(Intrinsic::arm_ldrexd); + Function *F = CGM.getIntrinsic(BuiltinID == ARM::BI__builtin_arm_ldaex + ? Intrinsic::arm_ldaexd + : Intrinsic::arm_ldrexd); Value *LdPtr = EmitScalarExpr(E->getArg(0)); Value *Val = Builder.CreateCall(F, Builder.CreateBitCast(LdPtr, Int8PtrTy), @@ -3093,7 +3096,8 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, return Builder.CreateBitCast(Val, ConvertType(E->getType())); } - if (BuiltinID == ARM::BI__builtin_arm_ldrex) { + if (BuiltinID == ARM::BI__builtin_arm_ldrex || + BuiltinID == ARM::BI__builtin_arm_ldaex) { Value *LoadAddr = EmitScalarExpr(E->getArg(0)); QualType Ty = E->getType(); @@ -3102,7 +3106,10 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, getContext().getTypeSize(Ty)); LoadAddr = Builder.CreateBitCast(LoadAddr, IntResTy->getPointerTo()); - Function *F = CGM.getIntrinsic(Intrinsic::arm_ldrex, LoadAddr->getType()); + Function *F = CGM.getIntrinsic(BuiltinID == ARM::BI__builtin_arm_ldaex + ? Intrinsic::arm_ldaex + : Intrinsic::arm_ldrex, + LoadAddr->getType()); Value *Val = Builder.CreateCall(F, LoadAddr, "ldrex"); if (RealResTy->isPointerTy()) @@ -3114,9 +3121,12 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, } if (BuiltinID == ARM::BI__builtin_arm_strexd || - (BuiltinID == ARM::BI__builtin_arm_strex && + ((BuiltinID == ARM::BI__builtin_arm_stlex || + BuiltinID == ARM::BI__builtin_arm_strex) && getContext().getTypeSize(E->getArg(0)->getType()) == 64)) { - Function *F = CGM.getIntrinsic(Intrinsic::arm_strexd); + Function *F = CGM.getIntrinsic(BuiltinID == ARM::BI__builtin_arm_stlex + ? Intrinsic::arm_stlexd + : Intrinsic::arm_strexd); llvm::Type *STy = llvm::StructType::get(Int32Ty, Int32Ty, NULL); Value *Tmp = CreateMemTemp(E->getArg(0)->getType()); @@ -3132,7 +3142,8 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, return Builder.CreateCall3(F, Arg0, Arg1, StPtr, "strexd"); } - if (BuiltinID == ARM::BI__builtin_arm_strex) { + if (BuiltinID == ARM::BI__builtin_arm_strex || + BuiltinID == ARM::BI__builtin_arm_stlex) { Value *StoreVal = EmitScalarExpr(E->getArg(0)); Value *StoreAddr = EmitScalarExpr(E->getArg(1)); @@ -3148,7 +3159,10 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, StoreVal = Builder.CreateZExtOrBitCast(StoreVal, Int32Ty); } - Function *F = CGM.getIntrinsic(Intrinsic::arm_strex, StoreAddr->getType()); + Function *F = CGM.getIntrinsic(BuiltinID == ARM::BI__builtin_arm_stlex + ? Intrinsic::arm_stlex + : Intrinsic::arm_strex, + StoreAddr->getType()); return Builder.CreateCall2(F, StoreVal, StoreAddr, "strex"); } @@ -3791,9 +3805,12 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, return EmitNounwindRuntimeCall(CGM.CreateRuntimeFunction(FTy, Name), Ops); } - if (BuiltinID == AArch64::BI__builtin_arm_ldrex && + if ((BuiltinID == AArch64::BI__builtin_arm_ldrex || + BuiltinID == AArch64::BI__builtin_arm_ldaex) && getContext().getTypeSize(E->getType()) == 128) { - Function *F = CGM.getIntrinsic(Intrinsic::aarch64_ldxp); + Function *F = CGM.getIntrinsic(BuiltinID == AArch64::BI__builtin_arm_ldaex + ? Intrinsic::aarch64_ldaxp + : Intrinsic::aarch64_ldxp); Value *LdPtr = EmitScalarExpr(E->getArg(0)); Value *Val = Builder.CreateCall(F, Builder.CreateBitCast(LdPtr, Int8PtrTy), @@ -3809,7 +3826,8 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, Val = Builder.CreateShl(Val0, ShiftCst, "shl", true /* nuw */); Val = Builder.CreateOr(Val, Val1); return Builder.CreateBitCast(Val, ConvertType(E->getType())); - } else if (BuiltinID == AArch64::BI__builtin_arm_ldrex) { + } else if (BuiltinID == AArch64::BI__builtin_arm_ldrex || + BuiltinID == AArch64::BI__builtin_arm_ldaex) { Value *LoadAddr = EmitScalarExpr(E->getArg(0)); QualType Ty = E->getType(); @@ -3818,7 +3836,10 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, getContext().getTypeSize(Ty)); LoadAddr = Builder.CreateBitCast(LoadAddr, IntResTy->getPointerTo()); - Function *F = CGM.getIntrinsic(Intrinsic::aarch64_ldxr, LoadAddr->getType()); + Function *F = CGM.getIntrinsic(BuiltinID == AArch64::BI__builtin_arm_ldaex + ? Intrinsic::aarch64_ldaxr + : Intrinsic::aarch64_ldxr, + LoadAddr->getType()); Value *Val = Builder.CreateCall(F, LoadAddr, "ldxr"); if (RealResTy->isPointerTy()) @@ -3828,9 +3849,12 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, return Builder.CreateBitCast(Val, RealResTy); } - if (BuiltinID == AArch64::BI__builtin_arm_strex && + if ((BuiltinID == AArch64::BI__builtin_arm_strex || + BuiltinID == AArch64::BI__builtin_arm_stlex) && getContext().getTypeSize(E->getArg(0)->getType()) == 128) { - Function *F = CGM.getIntrinsic(Intrinsic::aarch64_stxp); + Function *F = CGM.getIntrinsic(BuiltinID == AArch64::BI__builtin_arm_stlex + ? Intrinsic::aarch64_stlxp + : Intrinsic::aarch64_stxp); llvm::Type *STy = llvm::StructType::get(Int64Ty, Int64Ty, NULL); Value *One = llvm::ConstantInt::get(Int32Ty, 1); @@ -3847,7 +3871,8 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, Value *StPtr = Builder.CreateBitCast(EmitScalarExpr(E->getArg(1)), Int8PtrTy); return Builder.CreateCall3(F, Arg0, Arg1, StPtr, "stxp"); - } else if (BuiltinID == AArch64::BI__builtin_arm_strex) { + } else if (BuiltinID == AArch64::BI__builtin_arm_strex || + BuiltinID == AArch64::BI__builtin_arm_stlex) { Value *StoreVal = EmitScalarExpr(E->getArg(0)); Value *StoreAddr = EmitScalarExpr(E->getArg(1)); @@ -3863,7 +3888,10 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, StoreVal = Builder.CreateZExtOrBitCast(StoreVal, Int64Ty); } - Function *F = CGM.getIntrinsic(Intrinsic::aarch64_stxr, StoreAddr->getType()); + Function *F = CGM.getIntrinsic(BuiltinID == AArch64::BI__builtin_arm_stlex + ? Intrinsic::aarch64_stlxr + : Intrinsic::aarch64_stxr, + StoreAddr->getType()); return Builder.CreateCall2(F, StoreVal, StoreAddr, "stxr"); } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index cd31f72c65..9fa8fd1e41 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -486,12 +486,18 @@ bool Sema::CheckNeonBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { bool Sema::CheckARMBuiltinExclusiveCall(unsigned BuiltinID, CallExpr *TheCall, unsigned MaxWidth) { assert((BuiltinID == ARM::BI__builtin_arm_ldrex || + BuiltinID == ARM::BI__builtin_arm_ldaex || BuiltinID == ARM::BI__builtin_arm_strex || + BuiltinID == ARM::BI__builtin_arm_stlex || BuiltinID == AArch64::BI__builtin_arm_ldrex || - BuiltinID == AArch64::BI__builtin_arm_strex) && + BuiltinID == AArch64::BI__builtin_arm_ldaex || + BuiltinID == AArch64::BI__builtin_arm_strex || + BuiltinID == AArch64::BI__builtin_arm_stlex) && "unexpected ARM builtin"); bool IsLdrex = BuiltinID == ARM::BI__builtin_arm_ldrex || - BuiltinID == AArch64::BI__builtin_arm_ldrex; + BuiltinID == ARM::BI__builtin_arm_ldaex || + BuiltinID == AArch64::BI__builtin_arm_ldrex || + BuiltinID == AArch64::BI__builtin_arm_ldaex; DeclRefExpr *DRE =cast(TheCall->getCallee()->IgnoreParenCasts()); @@ -598,7 +604,9 @@ bool Sema::CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { llvm::APSInt Result; if (BuiltinID == ARM::BI__builtin_arm_ldrex || - BuiltinID == ARM::BI__builtin_arm_strex) { + BuiltinID == ARM::BI__builtin_arm_ldaex || + BuiltinID == ARM::BI__builtin_arm_strex || + BuiltinID == ARM::BI__builtin_arm_stlex) { return CheckARMBuiltinExclusiveCall(BuiltinID, TheCall, 64); } @@ -627,7 +635,9 @@ bool Sema::CheckAArch64BuiltinFunctionCall(unsigned BuiltinID, llvm::APSInt Result; if (BuiltinID == AArch64::BI__builtin_arm_ldrex || - BuiltinID == AArch64::BI__builtin_arm_strex) { + BuiltinID == AArch64::BI__builtin_arm_ldaex || + BuiltinID == AArch64::BI__builtin_arm_strex || + BuiltinID == AArch64::BI__builtin_arm_stlex) { return CheckARMBuiltinExclusiveCall(BuiltinID, TheCall, 128); } diff --git a/test/CodeGen/builtins-arm-exclusive.c b/test/CodeGen/builtins-arm-exclusive.c index ea1cfb87cb..2b10238c0f 100644 --- a/test/CodeGen/builtins-arm-exclusive.c +++ b/test/CodeGen/builtins-arm-exclusive.c @@ -1,5 +1,5 @@ // REQUIRES: arm-registered-target -// RUN: %clang_cc1 -Wall -Werror -triple thumbv7-linux-gnueabi -fno-signed-char -O3 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -Wall -Werror -triple thumbv8-linux-gnueabi -fno-signed-char -O3 -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -Wall -Werror -triple arm64-apple-ios7.0 -O3 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-ARM64 // Make sure the canonical use works before going into smaller details: @@ -116,6 +116,90 @@ int test_ldrex(char *addr, long long *addr64, float *addrfloat) { return sum; } +int test_ldaex(char *addr, long long *addr64, float *addrfloat) { +// CHECK-LABEL: @test_ldaex +// CHECK-ARM64-LABEL: @test_ldaex + int sum = 0; + sum += __builtin_arm_ldaex(addr); +// CHECK: [[INTRES:%.*]] = tail call i32 @llvm.arm.ldaex.p0i8(i8* %addr) +// CHECK: and i32 [[INTRES]], 255 + +// CHECK-ARM64: [[INTRES:%.*]] = tail call i64 @llvm.aarch64.ldaxr.p0i8(i8* %addr) +// CHECK-ARM64: [[TRUNCRES:%.*]] = trunc i64 [[INTRES]] to i32 +// CHECK-ARM64: [[SEXTTMP:%.*]] = shl i32 [[TRUNCRES]], 24 +// CHECK-ARM64: ashr exact i32 [[SEXTTMP]], 24 + + sum += __builtin_arm_ldaex((short *)addr); +// CHECK: [[ADDR16:%.*]] = bitcast i8* %addr to i16* +// CHECK: [[INTRES:%.*]] = tail call i32 @llvm.arm.ldaex.p0i16(i16* [[ADDR16]]) +// CHECK: [[TMPSEXT:%.*]] = shl i32 [[INTRES]], 16 +// CHECK: ashr exact i32 [[TMPSEXT]], 16 + +// CHECK-ARM64: [[ADDR16:%.*]] = bitcast i8* %addr to i16* +// CHECK-ARM64: [[INTRES:%.*]] = tail call i64 @llvm.aarch64.ldaxr.p0i16(i16* [[ADDR16]]) +// CHECK-ARM64: [[TRUNCRES:%.*]] = trunc i64 [[INTRES]] to i32 +// CHECK-ARM64: [[TMPSEXT:%.*]] = shl i32 [[TRUNCRES]], 16 +// CHECK-ARM64: ashr exact i32 [[TMPSEXT]], 16 + + sum += __builtin_arm_ldaex((int *)addr); +// CHECK: [[ADDR32:%.*]] = bitcast i8* %addr to i32* +// CHECK: call i32 @llvm.arm.ldaex.p0i32(i32* [[ADDR32]]) + +// CHECK-ARM64: [[ADDR32:%.*]] = bitcast i8* %addr to i32* +// CHECK-ARM64: [[INTRES:%.*]] = tail call i64 @llvm.aarch64.ldaxr.p0i32(i32* [[ADDR32]]) +// CHECK-ARM64: trunc i64 [[INTRES]] to i32 + + sum += __builtin_arm_ldaex((long long *)addr); +// CHECK: call { i32, i32 } @llvm.arm.ldaexd(i8* %addr) + +// CHECK-ARM64: [[ADDR64:%.*]] = bitcast i8* %addr to i64* +// CHECK-ARM64: call i64 @llvm.aarch64.ldaxr.p0i64(i64* [[ADDR64]]) + + sum += __builtin_arm_ldaex(addr64); +// CHECK: [[ADDR64_AS8:%.*]] = bitcast i64* %addr64 to i8* +// CHECK: call { i32, i32 } @llvm.arm.ldaexd(i8* [[ADDR64_AS8]]) + +// CHECK-ARM64: call i64 @llvm.aarch64.ldaxr.p0i64(i64* %addr64) + + sum += __builtin_arm_ldaex(addrfloat); +// CHECK: [[INTADDR:%.*]] = bitcast float* %addrfloat to i32* +// CHECK: [[INTRES:%.*]] = tail call i32 @llvm.arm.ldaex.p0i32(i32* [[INTADDR]]) +// CHECK: bitcast i32 [[INTRES]] to float + +// CHECK-ARM64: [[INTADDR:%.*]] = bitcast float* %addrfloat to i32* +// CHECK-ARM64: [[INTRES:%.*]] = tail call i64 @llvm.aarch64.ldaxr.p0i32(i32* [[INTADDR]]) +// CHECK-ARM64: [[TRUNCRES:%.*]] = trunc i64 [[INTRES]] to i32 +// CHECK-ARM64: bitcast i32 [[TRUNCRES]] to float + + sum += __builtin_arm_ldaex((double *)addr); +// CHECK: [[STRUCTRES:%.*]] = tail call { i32, i32 } @llvm.arm.ldaexd(i8* %addr) +// CHECK: [[RESHI:%.*]] = extractvalue { i32, i32 } [[STRUCTRES]], 1 +// CHECK: [[RESLO:%.*]] = extractvalue { i32, i32 } [[STRUCTRES]], 0 +// CHECK: [[RESHI64:%.*]] = zext i32 [[RESHI]] to i64 +// CHECK: [[RESLO64:%.*]] = zext i32 [[RESLO]] to i64 +// CHECK: [[RESHIHI:%.*]] = shl nuw i64 [[RESHI64]], 32 +// CHECK: [[INTRES:%.*]] = or i64 [[RESHIHI]], [[RESLO64]] +// CHECK: bitcast i64 [[INTRES]] to double + +// CHECK-ARM64: [[INTRES:%.*]] = tail call i64 @llvm.aarch64.ldaxr.p0i64(i64* [[ADDR64]]) +// CHECK-ARM64: bitcast i64 [[INTRES]] to double + + sum += *__builtin_arm_ldaex((int **)addr); +// CHECK: [[INTRES:%.*]] = tail call i32 @llvm.arm.ldaex.p0i32(i32* [[ADDR32]]) +// CHECK: inttoptr i32 [[INTRES]] to i32* + +// CHECK-ARM64: [[INTRES:%.*]] = tail call i64 @llvm.aarch64.ldaxr.p0i64(i64* [[ADDR64]]) +// CHECK-ARM64: inttoptr i64 [[INTRES]] to i32* + + sum += __builtin_arm_ldaex((struct Simple **)addr)->a; +// CHECK: [[INTRES:%.*]] = tail call i32 @llvm.arm.ldaex.p0i32(i32* [[ADDR32]]) +// CHECK: inttoptr i32 [[INTRES]] to %struct.Simple* + +// CHECK-ARM64: [[INTRES:%.*]] = tail call i64 @llvm.aarch64.ldaxr.p0i64(i64* [[ADDR64]]) +// CHECK-ARM64: inttoptr i64 [[INTRES]] to %struct.Simple* + return sum; +} + int test_strex(char *addr) { // CHECK-LABEL: @test_strex // CHECK-ARM64-LABEL: @test_strex @@ -166,6 +250,56 @@ int test_strex(char *addr) { return res; } +int test_stlex(char *addr) { +// CHECK-LABEL: @test_stlex +// CHECK-ARM64-LABEL: @test_stlex + int res = 0; + struct Simple var = {0}; + res |= __builtin_arm_stlex(4, addr); +// CHECK: call i32 @llvm.arm.stlex.p0i8(i32 4, i8* %addr) + +// CHECK-ARM64: call i32 @llvm.aarch64.stlxr.p0i8(i64 4, i8* %addr) + + res |= __builtin_arm_stlex(42, (short *)addr); +// CHECK: [[ADDR16:%.*]] = bitcast i8* %addr to i16* +// CHECK: call i32 @llvm.arm.stlex.p0i16(i32 42, i16* [[ADDR16]]) + +// CHECK-ARM64: [[ADDR16:%.*]] = bitcast i8* %addr to i16* +// CHECK-ARM64: call i32 @llvm.aarch64.stlxr.p0i16(i64 42, i16* [[ADDR16]]) + + res |= __builtin_arm_stlex(42, (int *)addr); +// CHECK: [[ADDR32:%.*]] = bitcast i8* %addr to i32* +// CHECK: call i32 @llvm.arm.stlex.p0i32(i32 42, i32* [[ADDR32]]) + +// CHECK-ARM64: [[ADDR32:%.*]] = bitcast i8* %addr to i32* +// CHECK-ARM64: call i32 @llvm.aarch64.stlxr.p0i32(i64 42, i32* [[ADDR32]]) + + res |= __builtin_arm_stlex(42, (long long *)addr); +// CHECK: call i32 @llvm.arm.stlexd(i32 42, i32 0, i8* %addr) + +// CHECK-ARM64: [[ADDR64:%.*]] = bitcast i8* %addr to i64* +// CHECK-ARM64: call i32 @llvm.aarch64.stlxr.p0i64(i64 42, i64* [[ADDR64]]) + + res |= __builtin_arm_stlex(2.71828f, (float *)addr); +// CHECK: call i32 @llvm.arm.stlex.p0i32(i32 1076754509, i32* [[ADDR32]]) + +// CHECK-ARM64: call i32 @llvm.aarch64.stlxr.p0i32(i64 1076754509, i32* [[ADDR32]]) + + res |= __builtin_arm_stlex(3.14159, (double *)addr); +// CHECK: call i32 @llvm.arm.stlexd(i32 -266631570, i32 1074340345, i8* %addr) + +// CHECK-ARM64: call i32 @llvm.aarch64.stlxr.p0i64(i64 4614256650576692846, i64* [[ADDR64]]) + + res |= __builtin_arm_stlex(&var, (struct Simple **)addr); +// CHECK: [[INTVAL:%.*]] = ptrtoint i16* %var to i32 +// CHECK: call i32 @llvm.arm.stlex.p0i32(i32 [[INTVAL]], i32* [[ADDR32]]) + +// CHECK-ARM64: [[INTVAL:%.*]] = ptrtoint i16* %var to i64 +// CHECK-ARM64: call i32 @llvm.aarch64.stlxr.p0i64(i64 [[INTVAL]], i64* [[ADDR64]]) + + return res; +} + void test_clrex() { // CHECK-LABEL: @test_clrex // CHECK-ARM64-LABEL: @test_clrex @@ -203,4 +337,31 @@ int test_strex_128(__int128 *addr, __int128 val) { // CHECK-ARM64: [[ADDR8:%.*]] = bitcast i128* %addr to i8* // CHECK-ARM64: [[RES:%.*]] = tail call i32 @llvm.aarch64.stxp(i64 [[VALLO]], i64 [[VALHI]], i8* [[ADDR8]]) } + +__int128 test_ldaex_128(__int128 *addr) { +// CHECK-ARM64-LABEL: @test_ldaex_128 + + return __builtin_arm_ldaex(addr); +// CHECK-ARM64: [[ADDR8:%.*]] = bitcast i128* %addr to i8* +// CHECK-ARM64: [[STRUCTRES:%.*]] = tail call { i64, i64 } @llvm.aarch64.ldaxp(i8* [[ADDR8]]) +// CHECK-ARM64: [[RESHI:%.*]] = extractvalue { i64, i64 } [[STRUCTRES]], 1 +// CHECK-ARM64: [[RESLO:%.*]] = extractvalue { i64, i64 } [[STRUCTRES]], 0 +// CHECK-ARM64: [[RESHI64:%.*]] = zext i64 [[RESHI]] to i128 +// CHECK-ARM64: [[RESLO64:%.*]] = zext i64 [[RESLO]] to i128 +// CHECK-ARM64: [[RESHIHI:%.*]] = shl nuw i128 [[RESHI64]], 64 +// CHECK-ARM64: [[INTRES:%.*]] = or i128 [[RESHIHI]], [[RESLO64]] +// CHECK-ARM64: ret i128 [[INTRES]] +} + +int test_stlex_128(__int128 *addr, __int128 val) { +// CHECK-ARM64-LABEL: @test_stlex_128 + + return __builtin_arm_stlex(val, addr); +// CHECK-ARM64: [[VALLO:%.*]] = trunc i128 %val to i64 +// CHECK-ARM64: [[VALHI128:%.*]] = lshr i128 %val, 64 +// CHECK-ARM64: [[VALHI:%.*]] = trunc i128 [[VALHI128]] to i64 +// CHECK-ARM64: [[ADDR8:%.*]] = bitcast i128* %addr to i8* +// CHECK-ARM64: [[RES:%.*]] = tail call i32 @llvm.aarch64.stlxp(i64 [[VALLO]], i64 [[VALHI]], i8* [[ADDR8]]) +} + #endif diff --git a/test/Sema/builtins-arm-exclusive.c b/test/Sema/builtins-arm-exclusive.c index 8c78403114..4e6a96bc32 100644 --- a/test/Sema/builtins-arm-exclusive.c +++ b/test/Sema/builtins-arm-exclusive.c @@ -55,6 +55,57 @@ int test_strex(char *addr) { return res; } +int test_ldaex(char *addr) { + int sum = 0; + sum += __builtin_arm_ldaex(addr); + sum += __builtin_arm_ldaex((short *)addr); + sum += __builtin_arm_ldaex((int *)addr); + sum += __builtin_arm_ldaex((long long *)addr); + sum += __builtin_arm_ldaex((float *)addr); + sum += __builtin_arm_ldaex((double *)addr); + sum += *__builtin_arm_ldaex((int **)addr); + sum += __builtin_arm_ldaex((struct Simple **)addr)->a; + sum += __builtin_arm_ldaex((volatile char *)addr); + sum += __builtin_arm_ldaex((const volatile char *)addr); + + // In principle this might be valid, but stick to ints and floats for scalar + // types at the moment. + sum += __builtin_arm_ldaex((struct Simple *)addr).a; // expected-error {{address argument to atomic builtin must be a pointer to}} + + sum += __builtin_arm_ldaex((__int128 *)addr); // expected-error {{__int128 is not supported on this target}} expected-error {{address argument to load or store exclusive builtin must be a pointer to 1,2,4 or 8 byte type}} + + __builtin_arm_ldaex(); // expected-error {{too few arguments to function call}} + __builtin_arm_ldaex(1, 2); // expected-error {{too many arguments to function call}} + return sum; +} + +int test_stlex(char *addr) { + int res = 0; + struct Simple var = {0}; + res |= __builtin_arm_stlex(4, addr); + res |= __builtin_arm_stlex(42, (short *)addr); + res |= __builtin_arm_stlex(42, (int *)addr); + res |= __builtin_arm_stlex(42, (long long *)addr); + res |= __builtin_arm_stlex(2.71828f, (float *)addr); + res |= __builtin_arm_stlex(3.14159, (double *)addr); + res |= __builtin_arm_stlex(&var, (struct Simple **)addr); + + res |= __builtin_arm_stlex(42, (volatile char *)addr); + res |= __builtin_arm_stlex(42, (char *const)addr); + res |= __builtin_arm_stlex(42, (const char *)addr); // expected-warning {{passing 'const char *' to parameter of type 'volatile char *' discards qualifiers}} + + + res |= __builtin_arm_stlex(var, (struct Simple *)addr); // expected-error {{address argument to atomic builtin must be a pointer to}} + res |= __builtin_arm_stlex(var, (struct Simple **)addr); // expected-error {{passing 'struct Simple' to parameter of incompatible type 'struct Simple *'}} + res |= __builtin_arm_stlex(&var, (struct Simple **)addr).a; // expected-error {{is not a structure or union}} + + res |= __builtin_arm_stlex(1, (__int128 *)addr); // expected-error {{__int128 is not supported on this target}} expected-error {{address argument to load or store exclusive builtin must be a pointer to 1,2,4 or 8 byte type}} + + __builtin_arm_stlex(1); // expected-error {{too few arguments to function call}} + __builtin_arm_stlex(1, 2, 3); // expected-error {{too many arguments to function call}} + return res; +} + void test_clrex() { __builtin_arm_clrex(); __builtin_arm_clrex(1); // expected-error {{too many arguments to function call}} diff --git a/test/Sema/builtins-arm64-exclusive.c b/test/Sema/builtins-arm64-exclusive.c index 0be9c1753f..4d678cc980 100644 --- a/test/Sema/builtins-arm64-exclusive.c +++ b/test/Sema/builtins-arm64-exclusive.c @@ -53,6 +53,55 @@ int test_strex(char *addr) { return res; } +int test_ldaex(char *addr) { + int sum = 0; + sum += __builtin_arm_ldaex(addr); + sum += __builtin_arm_ldaex((short *)addr); + sum += __builtin_arm_ldaex((int *)addr); + sum += __builtin_arm_ldaex((long long *)addr); + sum += __builtin_arm_ldaex((__int128 *)addr); + sum += __builtin_arm_ldaex((float *)addr); + sum += __builtin_arm_ldaex((double *)addr); + sum += *__builtin_arm_ldaex((int **)addr); + sum += __builtin_arm_ldaex((struct Simple **)addr)->a; + sum += __builtin_arm_ldaex((volatile char *)addr); + sum += __builtin_arm_ldaex((const volatile char *)addr); + + // In principle this might be valid, but stick to ints and floats for scalar + // types at the moment. + sum += __builtin_arm_ldaex((struct Simple *)addr).a; // expected-error {{address argument to atomic builtin must be a pointer to}} + + __builtin_arm_ldaex(); // expected-error {{too few arguments to function call}} + __builtin_arm_ldaex(1, 2); // expected-error {{too many arguments to function call}} + return sum; +} + +int test_stlex(char *addr) { + int res = 0; + struct Simple var = {0}; + res |= __builtin_arm_stlex(4, addr); + res |= __builtin_arm_stlex(42, (short *)addr); + res |= __builtin_arm_stlex(42, (int *)addr); + res |= __builtin_arm_stlex(42, (long long *)addr); + res |= __builtin_arm_stlex(42, (__int128 *)addr); + res |= __builtin_arm_stlex(2.71828f, (float *)addr); + res |= __builtin_arm_stlex(3.14159, (double *)addr); + res |= __builtin_arm_stlex(&var, (struct Simple **)addr); + + res |= __builtin_arm_stlex(42, (volatile char *)addr); + res |= __builtin_arm_stlex(42, (char *const)addr); + res |= __builtin_arm_stlex(42, (const char *)addr); // expected-warning {{passing 'const char *' to parameter of type 'volatile char *' discards qualifiers}} + + + res |= __builtin_arm_stlex(var, (struct Simple *)addr); // expected-error {{address argument to atomic builtin must be a pointer to}} + res |= __builtin_arm_stlex(var, (struct Simple **)addr); // expected-error {{passing 'struct Simple' to parameter of incompatible type 'struct Simple *'}} + res |= __builtin_arm_stlex(&var, (struct Simple **)addr).a; // expected-error {{is not a structure or union}} + + __builtin_arm_stlex(1); // expected-error {{too few arguments to function call}} + __builtin_arm_stlex(1, 2, 3); // expected-error {{too many arguments to function call}} + return res; +} + void test_clrex() { __builtin_arm_clrex(); __builtin_arm_clrex(1); // expected-error {{too many arguments to function call}}