Skip to content

Commit 060240c

Browse files
committed
[CHERI-RISC-V] Report true for __atomic_always_lock_free(sizeof(__intcap))
Now that we can expand all 2*XLen atomics inline (at least for purecap), we can report true for this builtin. This fixes problems such as std::atomic<void*>::is_lock_free reporting false in C++14 mode as well as a compilation error in compiler-rt atomic.c.
1 parent 3f32365 commit 060240c

File tree

4 files changed

+48
-39
lines changed

4 files changed

+48
-39
lines changed

clang/lib/Basic/Targets/RISCV.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,15 @@ class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
190190
void setMaxAtomicWidth() override {
191191
MaxAtomicPromoteWidth = 128;
192192

193-
if (ISAInfo->hasExtension("a"))
193+
if (ISAInfo->hasExtension("a")) {
194194
MaxAtomicInlineWidth = 32;
195+
// With CHERI we support capability-size integer atomic operations without
196+
// a libcall. Currently this is limited to purecap since in hybrid mode
197+
// RMW/CMPXCHG with a capability pointer does not work yet.
198+
// See https://github.com/CTSRD-CHERI/llvm-project/pull/490
199+
if (CapabilityABI)
200+
MaxAtomicInlineWidth = 64;
201+
}
195202
}
196203

197204
uint64_t getPointerRangeForCHERICapability() const override { return 32; }
@@ -226,8 +233,15 @@ class LLVM_LIBRARY_VISIBILITY RISCV64TargetInfo : public RISCVTargetInfo {
226233
void setMaxAtomicWidth() override {
227234
MaxAtomicPromoteWidth = 128;
228235

229-
if (ISAInfo->hasExtension("a"))
236+
if (ISAInfo->hasExtension("a")) {
230237
MaxAtomicInlineWidth = 64;
238+
// With CHERI we support capability-size integer atomic operations without
239+
// a libcall. Currently this is limited to purecap since in hybrid mode
240+
// RMW/CMPXCHG with a capability pointer does not work yet.
241+
// See https://github.com/CTSRD-CHERI/llvm-project/pull/490
242+
if (CapabilityABI)
243+
MaxAtomicInlineWidth = 128;
244+
}
231245
}
232246

233247
uint64_t getPointerRangeForCHERICapability() const override { return 64; }

clang/test/CodeGen/cheri/atomic-lock-free.c

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature
22
/// Check that we emit inline atomics rather than library calls for capability-size atomics
3-
// RUN: %riscv64_cheri_purecap_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify | opt -S -mem2reg | FileCheck %s --check-prefixes=PURECAP64
4-
// RUN: %riscv64_cheri_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify | opt -S -mem2reg | FileCheck %s --check-prefixes=HYBRID64
5-
// RUN: %riscv32_cheri_purecap_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify | opt -S -mem2reg | FileCheck %s --check-prefixes=PURECAP32
6-
// RUN: %riscv32_cheri_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify | opt -S -mem2reg | FileCheck %s --check-prefixes=HYBRID32
3+
// RUN: %riscv64_cheri_purecap_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify=purecap | opt -S -mem2reg | FileCheck %s --check-prefixes=PURECAP64
4+
// RUN: %riscv64_cheri_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify=hybrid | opt -S -mem2reg | FileCheck %s --check-prefixes=HYBRID64
5+
// RUN: %riscv32_cheri_purecap_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify=purecap | opt -S -mem2reg | FileCheck %s --check-prefixes=PURECAP32
6+
// RUN: %riscv32_cheri_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify=hybrid | opt -S -mem2reg | FileCheck %s --check-prefixes=HYBRID32
7+
// purecap-no-diagnostics
78

89
#if __CHERI_CAPABILITY_WIDTH__ == 64
910
typedef __INT64_TYPE__ cap_size_int;
@@ -70,9 +71,7 @@ __intcap load_cap(__intcap* i) {
7071
// PURECAP64-LABEL: define {{[^@]+}}@loadi128
7172
// PURECAP64-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
7273
// PURECAP64-NEXT: entry:
73-
// PURECAP64-NEXT: [[ATOMIC_TEMP:%.*]] = alloca i128, align 16, addrspace(200)
74-
// PURECAP64-NEXT: call void @__atomic_load(i64 noundef 16, ptr addrspace(200) noundef [[I]], ptr addrspace(200) noundef [[ATOMIC_TEMP]], i32 noundef signext 5)
75-
// PURECAP64-NEXT: [[TMP0:%.*]] = load i128, ptr addrspace(200) [[ATOMIC_TEMP]], align 16
74+
// PURECAP64-NEXT: [[TMP0:%.*]] = load atomic i128, ptr addrspace(200) [[I]] seq_cst, align 16
7675
// PURECAP64-NEXT: ret i128 [[TMP0]]
7776
//
7877
// HYBRID64-LABEL: define {{[^@]+}}@loadi128
@@ -86,8 +85,8 @@ __intcap load_cap(__intcap* i) {
8685
// PURECAP32-LABEL: define {{[^@]+}}@loadi128
8786
// PURECAP32-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
8887
// PURECAP32-NEXT: entry:
89-
// PURECAP32-NEXT: [[CALL:%.*]] = call i64 @__atomic_load_8(ptr addrspace(200) noundef [[I]], i32 noundef 5)
90-
// PURECAP32-NEXT: ret i64 [[CALL]]
88+
// PURECAP32-NEXT: [[TMP0:%.*]] = load atomic i64, ptr addrspace(200) [[I]] seq_cst, align 8
89+
// PURECAP32-NEXT: ret i64 [[TMP0]]
9190
//
9291
// HYBRID32-LABEL: define {{[^@]+}}@loadi128
9392
// HYBRID32-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
@@ -97,7 +96,7 @@ __intcap load_cap(__intcap* i) {
9796
//
9897
cap_size_int loadi128(cap_size_int* i) {
9998
return __atomic_load_n(i, __ATOMIC_SEQ_CST);
100-
// expected-warning@-1{{large atomic operation may incur significant performance penalty}}
99+
// hybrid-warning@-1{{large atomic operation may incur significant performance penalty}}
101100
}
102101

103102
// PURECAP64-LABEL: define {{[^@]+}}@xchg_long
@@ -159,11 +158,7 @@ __intcap xchg_cap(__intcap* i, __intcap val) {
159158
// PURECAP64-LABEL: define {{[^@]+}}@xchg_i128
160159
// PURECAP64-SAME: (ptr addrspace(200) noundef [[I:%.*]], i128 noundef [[VAL:%.*]]) addrspace(200) #[[ATTR0]] {
161160
// PURECAP64-NEXT: entry:
162-
// PURECAP64-NEXT: [[DOTATOMICTMP:%.*]] = alloca i128, align 16, addrspace(200)
163-
// PURECAP64-NEXT: [[ATOMIC_TEMP:%.*]] = alloca i128, align 16, addrspace(200)
164-
// PURECAP64-NEXT: store i128 [[VAL]], ptr addrspace(200) [[DOTATOMICTMP]], align 16
165-
// PURECAP64-NEXT: call void @__atomic_exchange(i64 noundef 16, ptr addrspace(200) noundef [[I]], ptr addrspace(200) noundef [[DOTATOMICTMP]], ptr addrspace(200) noundef [[ATOMIC_TEMP]], i32 noundef signext 5)
166-
// PURECAP64-NEXT: [[TMP0:%.*]] = load i128, ptr addrspace(200) [[ATOMIC_TEMP]], align 16
161+
// PURECAP64-NEXT: [[TMP0:%.*]] = atomicrmw xchg ptr addrspace(200) [[I]], i128 [[VAL]] seq_cst, align 16
167162
// PURECAP64-NEXT: ret i128 [[TMP0]]
168163
//
169164
// HYBRID64-LABEL: define {{[^@]+}}@xchg_i128
@@ -179,8 +174,8 @@ __intcap xchg_cap(__intcap* i, __intcap val) {
179174
// PURECAP32-LABEL: define {{[^@]+}}@xchg_i128
180175
// PURECAP32-SAME: (ptr addrspace(200) noundef [[I:%.*]], i64 noundef [[VAL:%.*]]) addrspace(200) #[[ATTR0]] {
181176
// PURECAP32-NEXT: entry:
182-
// PURECAP32-NEXT: [[CALL:%.*]] = call i64 @__atomic_exchange_8(ptr addrspace(200) noundef [[I]], i64 noundef [[VAL]], i32 noundef 5)
183-
// PURECAP32-NEXT: ret i64 [[CALL]]
177+
// PURECAP32-NEXT: [[TMP0:%.*]] = atomicrmw xchg ptr addrspace(200) [[I]], i64 [[VAL]] seq_cst, align 8
178+
// PURECAP32-NEXT: ret i64 [[TMP0]]
184179
//
185180
// HYBRID32-LABEL: define {{[^@]+}}@xchg_i128
186181
// HYBRID32-SAME: (ptr noundef [[I:%.*]], i64 noundef [[VAL:%.*]]) #[[ATTR0]] {
@@ -190,7 +185,7 @@ __intcap xchg_cap(__intcap* i, __intcap val) {
190185
//
191186
cap_size_int xchg_i128(cap_size_int* i, cap_size_int val) {
192187
return __atomic_exchange_n(i, val, __ATOMIC_SEQ_CST);
193-
// expected-warning@-1{{large atomic operation may incur significant performance penalty}}
188+
// hybrid-warning@-1{{large atomic operation may incur significant performance penalty}}
194189
}
195190

196191
// PURECAP64-LABEL: define {{[^@]+}}@lock_free_long
@@ -223,8 +218,7 @@ _Bool lock_free_long(long* l) {
223218
// PURECAP64-LABEL: define {{[^@]+}}@lock_free_cap
224219
// PURECAP64-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
225220
// PURECAP64-NEXT: entry:
226-
// PURECAP64-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_is_lock_free(i64 noundef 16, ptr addrspace(200) noundef [[I]])
227-
// PURECAP64-NEXT: ret i1 [[CALL]]
221+
// PURECAP64-NEXT: ret i1 true
228222
//
229223
// HYBRID64-LABEL: define {{[^@]+}}@lock_free_cap
230224
// HYBRID64-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
@@ -235,8 +229,7 @@ _Bool lock_free_long(long* l) {
235229
// PURECAP32-LABEL: define {{[^@]+}}@lock_free_cap
236230
// PURECAP32-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
237231
// PURECAP32-NEXT: entry:
238-
// PURECAP32-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_is_lock_free(i32 noundef 8, ptr addrspace(200) noundef [[I]])
239-
// PURECAP32-NEXT: ret i1 [[CALL]]
232+
// PURECAP32-NEXT: ret i1 true
240233
//
241234
// HYBRID32-LABEL: define {{[^@]+}}@lock_free_cap
242235
// HYBRID32-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
@@ -245,17 +238,17 @@ _Bool lock_free_long(long* l) {
245238
// HYBRID32-NEXT: ret i1 [[CALL]]
246239
//
247240
_Bool lock_free_cap(__intcap* i) {
248-
// TODO: _Static_assert(__atomic_always_lock_free(sizeof(*i), 0), "");
241+
#ifdef __CHERI_PURE_CAPABILITY__
242+
_Static_assert(__atomic_always_lock_free(sizeof(*i), 0), "");
243+
#endif
249244
return __atomic_is_lock_free(sizeof(*i), i);
250245
}
251246

252247
//
253-
// FIXME: should return true here
254248
// PURECAP64-LABEL: define {{[^@]+}}@lock_free_i128
255249
// PURECAP64-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
256250
// PURECAP64-NEXT: entry:
257-
// PURECAP64-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_is_lock_free(i64 noundef 16, ptr addrspace(200) noundef [[I]])
258-
// PURECAP64-NEXT: ret i1 [[CALL]]
251+
// PURECAP64-NEXT: ret i1 true
259252
//
260253
// HYBRID64-LABEL: define {{[^@]+}}@lock_free_i128
261254
// HYBRID64-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
@@ -266,8 +259,7 @@ _Bool lock_free_cap(__intcap* i) {
266259
// PURECAP32-LABEL: define {{[^@]+}}@lock_free_i128
267260
// PURECAP32-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
268261
// PURECAP32-NEXT: entry:
269-
// PURECAP32-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_is_lock_free(i32 noundef 8, ptr addrspace(200) noundef [[I]])
270-
// PURECAP32-NEXT: ret i1 [[CALL]]
262+
// PURECAP32-NEXT: ret i1 true
271263
//
272264
// HYBRID32-LABEL: define {{[^@]+}}@lock_free_i128
273265
// HYBRID32-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
@@ -276,6 +268,8 @@ _Bool lock_free_cap(__intcap* i) {
276268
// HYBRID32-NEXT: ret i1 [[CALL]]
277269
//
278270
_Bool lock_free_i128(cap_size_int* i) {
279-
// TODO: _Static_assert(__atomic_always_lock_free(sizeof(*i), 0), "");
271+
#ifdef __CHERI_PURE_CAPABILITY__
272+
_Static_assert(__atomic_always_lock_free(sizeof(*i), 0), "");
273+
#endif
280274
return __atomic_is_lock_free(sizeof(*i), i);
281275
}

clang/test/Preprocessor/cheri-lock-free.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/// Check that we report pointers as being always lock-free, otherwise <atomic>
22
/// ends up using locks with -ffreestanding.
33
// RUN: %riscv32_cheri_cc1 -fgnuc-version=4.2.1 -target-feature +a -E -dM %s \
4-
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32 --implicit-check-not=_LOCK_FREE
4+
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32-HYBRID --implicit-check-not=_LOCK_FREE
55
// RUN: %riscv32_cheri_purecap_cc1 -fgnuc-version=4.2.1 -target-feature +a -E -dM %s \
6-
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32 --implicit-check-not=_LOCK_FREE
6+
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32-PURECAP --implicit-check-not=_LOCK_FREE
77
// RUN: %riscv64_cheri_cc1 -fgnuc-version=4.2.1 -target-feature +a -E -dM %s \
88
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-64 --implicit-check-not=_LOCK_FREE
99
// RUN: %riscv64_cheri_purecap_cc1 -fgnuc-version=4.2.1 -target-feature +a -E -dM %s \
@@ -15,7 +15,9 @@
1515
// CHECK: #define __CLANG_ATOMIC_CHAR_LOCK_FREE 2
1616
// CHECK: #define __CLANG_ATOMIC_INT_LOCK_FREE 2
1717
// CHECK-64: #define __CLANG_ATOMIC_LLONG_LOCK_FREE 2
18-
// CHECK-32: #define __CLANG_ATOMIC_LLONG_LOCK_FREE 1
18+
// NB: LLONG is always lockfree for RV32 purecap since we use capability atomics.
19+
// CHECK-32-HYBRID: #define __CLANG_ATOMIC_LLONG_LOCK_FREE 1
20+
// CHECK-32-PURECAP: #define __CLANG_ATOMIC_LLONG_LOCK_FREE 2
1921
// CHECK: #define __CLANG_ATOMIC_LONG_LOCK_FREE 2
2022
// CHECK: #define __CLANG_ATOMIC_POINTER_LOCK_FREE 2
2123
// CHECK: #define __CLANG_ATOMIC_SHORT_LOCK_FREE 2
@@ -26,7 +28,9 @@
2628
// CHECK: #define __GCC_ATOMIC_CHAR_LOCK_FREE 2
2729
// CHECK: #define __GCC_ATOMIC_INT_LOCK_FREE 2
2830
// CHECK-64: #define __GCC_ATOMIC_LLONG_LOCK_FREE 2
29-
// CHECK-32: #define __GCC_ATOMIC_LLONG_LOCK_FREE 1
31+
// NB: LLONG is always lockfree for RV32 purecap since we use capability atomics.
32+
// CHECK-32-HYBRID: #define __GCC_ATOMIC_LLONG_LOCK_FREE 1
33+
// CHECK-32-PURECAP: #define __GCC_ATOMIC_LLONG_LOCK_FREE 2
3034
// CHECK: #define __GCC_ATOMIC_LONG_LOCK_FREE 2
3135
// CHECK: #define __GCC_ATOMIC_POINTER_LOCK_FREE 2
3236
// CHECK: #define __GCC_ATOMIC_SHORT_LOCK_FREE 2

clang/test/Sema/cheri/atomic-lock-free.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,15 @@
88
// RUN: %riscv64_cheri_cc1 -target-feature +a %s -fsyntax-only -verify=hybrid
99
// RUN: %riscv32_cheri_purecap_cc1 -target-feature +a %s -fsyntax-only -verify=purecap
1010
// RUN: %riscv32_cheri_cc1 -target-feature +a %s -fsyntax-only -verify=hybrid
11+
// purecap-no-diagnostics
1112

1213
_Static_assert(__atomic_always_lock_free(sizeof(char), 0), "");
1314
_Static_assert(__atomic_always_lock_free(sizeof(short), 0), "");
1415
_Static_assert(__atomic_always_lock_free(sizeof(int), 0), "");
1516
_Static_assert(__atomic_always_lock_free(sizeof(__INTPTR_TYPE__), 0), "");
16-
// FIXME: purecap-error@-1{{static assertion failed due to requirement '__atomic_always_lock_free(sizeof(__intcap), 0)'}}
1717
_Static_assert(__atomic_always_lock_free(sizeof(__UINTPTR_TYPE__), 0), "");
18-
// FIXME: purecap-error@-1{{static assertion failed due to requirement '__atomic_always_lock_free(sizeof(unsigned __intcap), 0)'}}
1918
_Static_assert(__atomic_always_lock_free(sizeof(void *), 0), "");
20-
// FIXME: purecap-error@-1{{static assertion failed due to requirement '__atomic_always_lock_free(sizeof(void *), 0)'}}
2119
/// TODO: it would be nice if hybrid mode also allowed lock-free sizeof(void * __capability)
2220
/// but this is not currently true since atomic RMW/CMPXCHG with capability
2321
/// pointers are not supported.
2422
_Static_assert(__atomic_always_lock_free(sizeof(void * __capability), 0), ""); // hybrid-error{{static assertion failed due to requirement '__atomic_always_lock_free(sizeof(void * __capability), 0)'}}
25-
// FIXME: purecap-error@-1{{static assertion failed due to requirement '__atomic_always_lock_free(sizeof(void *), 0)'}}

0 commit comments

Comments
 (0)