Skip to content

Commit 6bf6bab

Browse files
authored
[Clang][ARM][Sema] Check validity of ldrexd/strexd builtin calls (#164919)
This change enables validation checks against the following two ARM atomic builtins: ``` __builtin_arm_ldrexd __builtin_arm_strexd ``` Previously, no checks existed for these builtins, so under a release compiler, it would be possible to emit `ldrexd`/`strexd` under ARM targets which set the LDREX mask (returned via `getARMLDREXMask`) to signify these as unsupported instructions. For example, the following would compile with errors: ```c > type atomics.c long long func(void) { long long num = 0; __builtin_arm_strex(42, &num); return __builtin_arm_ldrex(&num); } ``` ``` > clang --target=armv7m-linux-gnueabi -S atomics.c -o - atomics.c:3:5: error: address argument to load or store exclusive builtin must be a pointer to 1,2 or 4 byte type ('volatile long long *' invalid) 3 | __builtin_arm_strex(42, &num); | ^ atomics.c:4:12: error: address argument to load or store exclusive builtin must be a pointer to 1,2 or 4 byte type ('const volatile long long *' invalid) 4 | return __builtin_arm_ldrex(&num); | ^ 2 errors generated. ``` However, a similar program would compile without errors: ```c > type atomics.c long long func(void) { long long num = 0; __builtin_arm_strexd(42, &num); return __builtin_arm_ldrexd(&num); } ``` ``` > clang --target=armv7m-linux-gnueabi -S atomics.c -o - ... strexd r1, r2, r3, [r0] ldrexd r0, r1, [r0] ... ``` With this change, we now have appropriate compile-time errors: ``` > clang --target=armv7m-linux-gnueabi -S atomics.c -o - atomics.c:3:5: error: load and store exclusive builtins are not available on this architecture 3 | __builtin_arm_strexd(42, &num); | ^ ~~~~ atomics.c:4:12: error: load and store exclusive builtins are not available on this architecture 4 | return __builtin_arm_ldrexd(&num); | ^ ~~~~ 2 errors generated. ```
1 parent dbd9818 commit 6bf6bab

File tree

9 files changed

+209
-5
lines changed

9 files changed

+209
-5
lines changed

clang/include/clang/Basic/BuiltinsARM.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ BUILTIN(__builtin_arm_cls, "UiZUi", "nc")
125125
BUILTIN(__builtin_arm_cls64, "UiWUi", "nc")
126126

127127
// Store and load exclusive
128-
BUILTIN(__builtin_arm_ldrexd, "LLUiv*", "")
129-
BUILTIN(__builtin_arm_strexd, "iLLUiv*", "")
128+
BUILTIN(__builtin_arm_ldrexd, "v.", "t")
129+
BUILTIN(__builtin_arm_strexd, "i.", "t")
130130

131131
BUILTIN(__builtin_arm_ldrex, "v.", "t")
132132
BUILTIN(__builtin_arm_ldaex, "v.", "t")

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9405,7 +9405,7 @@ def err_atomic_exclusive_builtin_pointer_size : Error<
94059405
"%select{|,| or }7%select{|8}8"
94069406
" byte type (%0 invalid)">;
94079407
def err_atomic_exclusive_builtin_pointer_size_none : Error<
9408-
"load and store exclusive builtins are not available on this architecture">;
9408+
"%select{|eight-byte }0load and store exclusive builtins are not available on this architecture">;
94099409
def err_atomic_builtin_ext_int_size : Error<
94109410
"atomic memory operand must have a power-of-two size">;
94119411
def err_atomic_builtin_bit_int_prohibit : Error<

clang/lib/Sema/SemaARM.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,18 +850,23 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(const TargetInfo &TI,
850850
unsigned BuiltinID,
851851
CallExpr *TheCall) {
852852
assert((BuiltinID == ARM::BI__builtin_arm_ldrex ||
853+
BuiltinID == ARM::BI__builtin_arm_ldrexd ||
853854
BuiltinID == ARM::BI__builtin_arm_ldaex ||
854855
BuiltinID == ARM::BI__builtin_arm_strex ||
856+
BuiltinID == ARM::BI__builtin_arm_strexd ||
855857
BuiltinID == ARM::BI__builtin_arm_stlex ||
856858
BuiltinID == AArch64::BI__builtin_arm_ldrex ||
857859
BuiltinID == AArch64::BI__builtin_arm_ldaex ||
858860
BuiltinID == AArch64::BI__builtin_arm_strex ||
859861
BuiltinID == AArch64::BI__builtin_arm_stlex) &&
860862
"unexpected ARM builtin");
861863
bool IsLdrex = BuiltinID == ARM::BI__builtin_arm_ldrex ||
864+
BuiltinID == ARM::BI__builtin_arm_ldrexd ||
862865
BuiltinID == ARM::BI__builtin_arm_ldaex ||
863866
BuiltinID == AArch64::BI__builtin_arm_ldrex ||
864867
BuiltinID == AArch64::BI__builtin_arm_ldaex;
868+
bool IsDoubleWord = BuiltinID == ARM::BI__builtin_arm_ldrexd ||
869+
BuiltinID == ARM::BI__builtin_arm_strexd;
865870

866871
ASTContext &Context = getASTContext();
867872
DeclRefExpr *DRE =
@@ -928,6 +933,11 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(const TargetInfo &TI,
928933
if (!TI.getTriple().isAArch64()) {
929934
unsigned Mask = TI.getARMLDREXMask();
930935
unsigned Bits = Context.getTypeSize(ValType);
936+
if (IsDoubleWord) {
937+
// Explicit request for ldrexd/strexd means only double word sizes
938+
// supported if the target supports them.
939+
Mask &= TargetInfo::ARM_LDREX_D;
940+
}
931941
bool Supported =
932942
(llvm::isPowerOf2_64(Bits)) && Bits >= 8 && (Mask & (Bits / 8));
933943

@@ -968,8 +978,11 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(const TargetInfo &TI,
968978
}
969979
}
970980
} else {
981+
bool EmitDoubleWordDiagnostic =
982+
IsDoubleWord && !Mask && TI.getARMLDREXMask();
971983
Diag(DRE->getBeginLoc(),
972984
diag::err_atomic_exclusive_builtin_pointer_size_none)
985+
<< (EmitDoubleWordDiagnostic ? 1 : 0)
973986
<< PointerArg->getSourceRange();
974987
}
975988
}
@@ -1013,8 +1026,10 @@ bool SemaARM::CheckARMBuiltinFunctionCall(const TargetInfo &TI,
10131026
unsigned BuiltinID,
10141027
CallExpr *TheCall) {
10151028
if (BuiltinID == ARM::BI__builtin_arm_ldrex ||
1029+
BuiltinID == ARM::BI__builtin_arm_ldrexd ||
10161030
BuiltinID == ARM::BI__builtin_arm_ldaex ||
10171031
BuiltinID == ARM::BI__builtin_arm_strex ||
1032+
BuiltinID == ARM::BI__builtin_arm_strexd ||
10181033
BuiltinID == ARM::BI__builtin_arm_stlex) {
10191034
return CheckARMBuiltinExclusiveCall(TI, BuiltinID, TheCall);
10201035
}

clang/test/CodeGen/builtins-arm-exclusive.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,49 @@ int test_stlex_128(__int128 *addr, __int128 val) {
312312
}
313313

314314
#endif
315+
316+
#ifdef __arm__
317+
// ARM exclusive atomic builtins
318+
319+
int test_ldrexd(char *addr, long long *addr64, float *addrfloat) {
320+
// CHECK-LABEL: @test_ldrexd
321+
int sum = 0;
322+
sum += __builtin_arm_ldrexd((long long *)addr);
323+
// CHECK: call { i32, i32 } @llvm.arm.ldrexd(ptr %addr)
324+
325+
sum += __builtin_arm_ldrexd(addr64);
326+
// CHECK: call { i32, i32 } @llvm.arm.ldrexd(ptr %addr64)
327+
328+
sum += __builtin_arm_ldrexd((double *)addr);
329+
// CHECK: [[STRUCTRES:%.*]] = call { i32, i32 } @llvm.arm.ldrexd(ptr %addr)
330+
// CHECK: [[RESHI:%.*]] = extractvalue { i32, i32 } [[STRUCTRES]], 1
331+
// CHECK: [[RESLO:%.*]] = extractvalue { i32, i32 } [[STRUCTRES]], 0
332+
// CHECK: [[RESHI64:%.*]] = zext i32 [[RESHI]] to i64
333+
// CHECK: [[RESLO64:%.*]] = zext i32 [[RESLO]] to i64
334+
// CHECK: [[RESHIHI:%.*]] = shl nuw i64 [[RESHI64]], 32
335+
// CHECK: [[INTRES:%.*]] = or i64 [[RESHIHI]], [[RESLO64]]
336+
337+
return sum;
338+
}
339+
340+
int test_strexd(char *addr) {
341+
// CHECK-LABEL: @test_strexd
342+
int res = 0;
343+
res |= __builtin_arm_strexd(42, (long long *)addr);
344+
// CHECK: store i64 42, ptr [[TMP:%.*]], align 8
345+
// CHECK: [[LOHI:%.*]] = load { i32, i32 }, ptr [[TMP]]
346+
// CHECK: [[LO:%.*]] = extractvalue { i32, i32 } [[LOHI]], 0
347+
// CHECK: [[HI:%.*]] = extractvalue { i32, i32 } [[LOHI]], 1
348+
// CHECK: call i32 @llvm.arm.strexd(i32 [[LO]], i32 [[HI]], ptr %addr)
349+
350+
res |= __builtin_arm_strexd(3.14159, (double *)addr);
351+
// CHECK: store double 3.141590e+00, ptr [[TMP:%.*]], align 8
352+
// CHECK: [[LOHI:%.*]] = load { i32, i32 }, ptr [[TMP]]
353+
// CHECK: [[LO:%.*]] = extractvalue { i32, i32 } [[LOHI]], 0
354+
// CHECK: [[HI:%.*]] = extractvalue { i32, i32 } [[LOHI]], 1
355+
// CHECK: call i32 @llvm.arm.strexd(i32 [[LO]], i32 [[HI]], ptr %addr)
356+
357+
return res;
358+
}
359+
360+
#endif

clang/test/CodeGenCXX/builtins-arm-exclusive.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,35 @@ void test_ldrex() {
2222
void tset_strex() {
2323
__builtin_arm_strex(true, &b);
2424
}
25+
26+
#ifdef __arm__
27+
// ARM exclusive atomic builtins
28+
29+
long long c;
30+
31+
// CHECK-LABEL: @_Z11test_ldrexdv()
32+
// CHECK: [[STRUCTRES:%.*]] = call { i32, i32 } @llvm.arm.ldrexd(ptr @c)
33+
// CHECK: [[RESHI:%.*]] = extractvalue { i32, i32 } [[STRUCTRES]], 1
34+
// CHECK: [[RESLO:%.*]] = extractvalue { i32, i32 } [[STRUCTRES]], 0
35+
// CHECK: [[RESHI64:%.*]] = zext i32 [[RESHI]] to i64
36+
// CHECK: [[RESLO64:%.*]] = zext i32 [[RESLO]] to i64
37+
// CHECK: [[RESHIHI:%.*]] = shl nuw i64 [[RESHI64]], 32
38+
// CHECK: [[INTRES:%.*]] = or i64 [[RESHIHI]], [[RESLO64]]
39+
// CHECK: store i64 [[INTRES]], ptr @c, align 8
40+
41+
void test_ldrexd() {
42+
c = __builtin_arm_ldrexd(&c);
43+
}
44+
45+
// CHECK-LABEL: @_Z11tset_strexdv()
46+
// CHECK: store i64 42, ptr [[TMP:%.*]], align 8
47+
// CHECK: [[LOHI:%.*]] = load { i32, i32 }, ptr [[TMP]]
48+
// CHECK: [[LO:%.*]] = extractvalue { i32, i32 } [[LOHI]], 0
49+
// CHECK: [[HI:%.*]] = extractvalue { i32, i32 } [[LOHI]], 1
50+
// CHECK: %{{.*}} = call i32 @llvm.arm.strexd(i32 [[LO]], i32 [[HI]], ptr @c)
51+
52+
void tset_strexd() {
53+
__builtin_arm_strexd(42, &c);
54+
}
55+
56+
#endif

clang/test/Sema/builtins-arm-exclusive-124.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,27 @@ int test_strex(char *addr) {
2424
res |= __builtin_arm_strex(42, (long long *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 1,2 or 4 byte type}}
2525
return res;
2626
}
27+
28+
int test_ldrexd(char *addr) {
29+
int sum = 0;
30+
sum += __builtin_arm_ldrexd(addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
31+
sum += __builtin_arm_ldrexd((short *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
32+
sum += __builtin_arm_ldrexd((int *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
33+
sum += __builtin_arm_ldrexd((long long *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
34+
sum += __builtin_arm_ldrexd((unsigned long long *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
35+
sum += __builtin_arm_ldrexd((float *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
36+
sum += __builtin_arm_ldrexd((double *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
37+
return sum;
38+
}
39+
40+
int test_strexd(char *addr) {
41+
int res = 0;
42+
res |= __builtin_arm_strexd(4, addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
43+
res |= __builtin_arm_strexd(42, (short *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
44+
res |= __builtin_arm_strexd(42, (int *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
45+
res |= __builtin_arm_strexd(42, (long long *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
46+
res |= __builtin_arm_strexd(42, (unsigned long long *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
47+
res |= __builtin_arm_strexd(2.71828f, (float *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
48+
res |= __builtin_arm_strexd(3.14159, (double *)addr); // expected-error {{eight-byte load and store exclusive builtins are not available on this architecture}}
49+
return res;
50+
}

clang/test/Sema/builtins-arm-exclusive-4.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,21 @@ int test_strex(char *addr) {
2020
res |= __builtin_arm_strex(42, (long long *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 4 byte type}}
2121
return res;
2222
}
23+
24+
int test_ldrexd(char *addr) {
25+
int sum = 0;
26+
sum += __builtin_arm_ldrexd(addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
27+
sum += __builtin_arm_ldrexd((short *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
28+
sum += __builtin_arm_ldrexd((int *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
29+
sum += __builtin_arm_ldrexd((long long *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
30+
return sum;
31+
}
32+
33+
int test_strexd(char *addr) {
34+
int res = 0;
35+
res |= __builtin_arm_strexd(4, addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
36+
res |= __builtin_arm_strexd(42, (short *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
37+
res |= __builtin_arm_strexd(42, (int *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
38+
res |= __builtin_arm_strexd(42, (long long *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
39+
return res;
40+
}

clang/test/Sema/builtins-arm-exclusive-none.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %clang_cc1 -triple armv6m -fsyntax-only -verify %s
22

33
// Armv6-M does not support exclusive loads/stores at all, so all uses of
4-
// __builtin_arm_ldrex and __builtin_arm_strex is forbidden.
4+
// __builtin_arm_ldrex[d] and __builtin_arm_strex[d] is forbidden.
55

66
int test_ldrex(char *addr) {
77
int sum = 0;
@@ -20,3 +20,21 @@ int test_strex(char *addr) {
2020
res |= __builtin_arm_strex(42, (long long *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
2121
return res;
2222
}
23+
24+
int test_ldrexd(char *addr) {
25+
int sum = 0;
26+
sum += __builtin_arm_ldrexd(addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
27+
sum += __builtin_arm_ldrexd((short *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
28+
sum += __builtin_arm_ldrexd((int *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
29+
sum += __builtin_arm_ldrexd((long long *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
30+
return sum;
31+
}
32+
33+
int test_strexd(char *addr) {
34+
int res = 0;
35+
res |= __builtin_arm_strexd(4, addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
36+
res |= __builtin_arm_strexd(42, (short *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
37+
res |= __builtin_arm_strexd(42, (int *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
38+
res |= __builtin_arm_strexd(42, (long long *)addr); // expected-error {{load and store exclusive builtins are not available on this architecture}}
39+
return res;
40+
}

clang/test/Sema/builtins-arm-exclusive.c

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %clang_cc1 -triple armv7 -fsyntax-only -verify %s
22

3-
// General tests of __builtin_arm_ldrex and __builtin_arm_strex error checking.
3+
// General tests of __builtin_arm_ldrex[d] and __builtin_arm_strex[d] error checking.
44
//
55
// This test is compiled for Armv7-A, which provides exclusive load/store
66
// instructions for 1-, 2-, 4- and 8-byte quantities. Other Arm architecture
@@ -63,6 +63,57 @@ int test_strex(char *addr) {
6363
return res;
6464
}
6565

66+
int test_ldrexd(char *addr) {
67+
int sum = 0;
68+
sum += __builtin_arm_ldrexd(addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
69+
sum += __builtin_arm_ldrexd((short *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
70+
sum += __builtin_arm_ldrexd((int *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
71+
sum += __builtin_arm_ldrexd((long long *)addr);
72+
sum += __builtin_arm_ldrexd((float *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
73+
sum += __builtin_arm_ldrexd((double *)addr);
74+
sum += *__builtin_arm_ldrexd((int **)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
75+
sum += __builtin_arm_ldrexd((struct Simple **)addr)->a; // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
76+
sum += __builtin_arm_ldrexd((volatile char *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
77+
sum += __builtin_arm_ldrexd((const volatile char *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
78+
79+
// In principle this might be valid, but stick to ints and floats for scalar
80+
// types at the moment.
81+
sum += __builtin_arm_ldrexd((struct Simple *)addr).a; // expected-error {{address argument to atomic builtin must be a pointer to}}
82+
83+
sum += __builtin_arm_ldrexd((__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 8 byte type}}
84+
85+
__builtin_arm_ldrexd(); // expected-error {{too few arguments to function call}}
86+
__builtin_arm_ldrexd(1, 2); // expected-error {{too many arguments to function call}}
87+
return sum;
88+
}
89+
90+
int test_strexd(char *addr) {
91+
int res = 0;
92+
struct Simple var = {0};
93+
res |= __builtin_arm_strexd(4, addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
94+
res |= __builtin_arm_strexd(42, (short *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
95+
res |= __builtin_arm_strexd(42, (int *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
96+
res |= __builtin_arm_strexd(42, (long long *)addr);
97+
res |= __builtin_arm_strexd(2.71828f, (float *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
98+
res |= __builtin_arm_strexd(3.14159, (double *)addr);
99+
res |= __builtin_arm_strexd(&var, (struct Simple **)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
100+
101+
res |= __builtin_arm_strexd(42, (volatile char *)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
102+
res |= __builtin_arm_strexd(42, (char *const)addr); // expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
103+
res |= __builtin_arm_strexd(42, (const char *)addr); // expected-warning {{passing 'const char *' to parameter of type 'volatile char *' discards qualifiers}} expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
104+
105+
106+
res |= __builtin_arm_strexd(var, (struct Simple *)addr); // expected-error {{address argument to atomic builtin must be a pointer to}}
107+
res |= __builtin_arm_strexd(var, (struct Simple **)addr); // expected-error {{passing 'struct Simple' to parameter of incompatible type 'struct Simple *'}} expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
108+
res |= __builtin_arm_strexd(&var, (struct Simple **)addr).a; // expected-error {{is not a structure or union}} expected-error {{address argument to load or store exclusive builtin must be a pointer to 8 byte type}}
109+
110+
res |= __builtin_arm_strexd(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 8 byte type}}
111+
112+
__builtin_arm_strexd(1); // expected-error {{too few arguments to function call}}
113+
__builtin_arm_strexd(1, 2, 3); // expected-error {{too many arguments to function call}}
114+
return res;
115+
}
116+
66117
int test_ldaex(char *addr) {
67118
int sum = 0;
68119
sum += __builtin_arm_ldaex(addr);

0 commit comments

Comments
 (0)