.. 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:
* 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
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
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
}
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),
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();
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())
}
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());
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));
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");
}
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),
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();
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())
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);
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));
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");
}
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<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
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);
}
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);
}
// 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:
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
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
// 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
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}}
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}}