Skip to content

Commit

Permalink
[CHERI-RISC-V] Report true for __atomic_always_lock_free(sizeof(__int…
Browse files Browse the repository at this point in the history
…cap))

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.
  • Loading branch information
arichardson committed Sep 22, 2023
1 parent 143dfde commit d488825
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 39 deletions.
18 changes: 16 additions & 2 deletions clang/lib/Basic/Targets/RISCV.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,15 @@ class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
void setMaxAtomicWidth() override {
MaxAtomicPromoteWidth = 128;

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

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

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

uint64_t getPointerRangeForCHERICapability() const override { return 64; }
Expand Down
52 changes: 23 additions & 29 deletions clang/test/CodeGen/cheri/atomic-lock-free.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature
/// Check that we emit inline atomics rather than library calls for capability-size atomics
// RUN: %riscv64_cheri_purecap_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify | opt -S -mem2reg | FileCheck %s --check-prefixes=PURECAP64
// RUN: %riscv64_cheri_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify | opt -S -mem2reg | FileCheck %s --check-prefixes=HYBRID64
// RUN: %riscv32_cheri_purecap_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify | opt -S -mem2reg | FileCheck %s --check-prefixes=PURECAP32
// RUN: %riscv32_cheri_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify | opt -S -mem2reg | FileCheck %s --check-prefixes=HYBRID32
// 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
// RUN: %riscv64_cheri_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify=hybrid | opt -S -mem2reg | FileCheck %s --check-prefixes=HYBRID64
// 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
// RUN: %riscv32_cheri_cc1 -target-feature +a %s -emit-llvm -o - -disable-O0-optnone -verify=hybrid | opt -S -mem2reg | FileCheck %s --check-prefixes=HYBRID32
// purecap-no-diagnostics

#if __CHERI_CAPABILITY_WIDTH__ == 64
typedef __INT64_TYPE__ cap_size_int;
Expand Down Expand Up @@ -70,9 +71,7 @@ __intcap load_cap(__intcap* i) {
// PURECAP64-LABEL: define {{[^@]+}}@loadi128
// PURECAP64-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
// PURECAP64-NEXT: entry:
// PURECAP64-NEXT: [[ATOMIC_TEMP:%.*]] = alloca i128, align 16, addrspace(200)
// PURECAP64-NEXT: call void @__atomic_load(i64 noundef 16, ptr addrspace(200) noundef [[I]], ptr addrspace(200) noundef [[ATOMIC_TEMP]], i32 noundef signext 5)
// PURECAP64-NEXT: [[TMP0:%.*]] = load i128, ptr addrspace(200) [[ATOMIC_TEMP]], align 16
// PURECAP64-NEXT: [[TMP0:%.*]] = load atomic i128, ptr addrspace(200) [[I]] seq_cst, align 16
// PURECAP64-NEXT: ret i128 [[TMP0]]
//
// HYBRID64-LABEL: define {{[^@]+}}@loadi128
Expand All @@ -86,8 +85,8 @@ __intcap load_cap(__intcap* i) {
// PURECAP32-LABEL: define {{[^@]+}}@loadi128
// PURECAP32-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
// PURECAP32-NEXT: entry:
// PURECAP32-NEXT: [[CALL:%.*]] = call i64 @__atomic_load_8(ptr addrspace(200) noundef [[I]], i32 noundef 5)
// PURECAP32-NEXT: ret i64 [[CALL]]
// PURECAP32-NEXT: [[TMP0:%.*]] = load atomic i64, ptr addrspace(200) [[I]] seq_cst, align 8
// PURECAP32-NEXT: ret i64 [[TMP0]]
//
// HYBRID32-LABEL: define {{[^@]+}}@loadi128
// HYBRID32-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
Expand All @@ -97,7 +96,7 @@ __intcap load_cap(__intcap* i) {
//
cap_size_int loadi128(cap_size_int* i) {
return __atomic_load_n(i, __ATOMIC_SEQ_CST);
// expected-warning@-1{{large atomic operation may incur significant performance penalty}}
// hybrid-warning@-1{{large atomic operation may incur significant performance penalty}}
}

// PURECAP64-LABEL: define {{[^@]+}}@xchg_long
Expand Down Expand Up @@ -159,11 +158,7 @@ __intcap xchg_cap(__intcap* i, __intcap val) {
// PURECAP64-LABEL: define {{[^@]+}}@xchg_i128
// PURECAP64-SAME: (ptr addrspace(200) noundef [[I:%.*]], i128 noundef [[VAL:%.*]]) addrspace(200) #[[ATTR0]] {
// PURECAP64-NEXT: entry:
// PURECAP64-NEXT: [[DOTATOMICTMP:%.*]] = alloca i128, align 16, addrspace(200)
// PURECAP64-NEXT: [[ATOMIC_TEMP:%.*]] = alloca i128, align 16, addrspace(200)
// PURECAP64-NEXT: store i128 [[VAL]], ptr addrspace(200) [[DOTATOMICTMP]], align 16
// 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)
// PURECAP64-NEXT: [[TMP0:%.*]] = load i128, ptr addrspace(200) [[ATOMIC_TEMP]], align 16
// PURECAP64-NEXT: [[TMP0:%.*]] = atomicrmw xchg ptr addrspace(200) [[I]], i128 [[VAL]] seq_cst, align 16
// PURECAP64-NEXT: ret i128 [[TMP0]]
//
// HYBRID64-LABEL: define {{[^@]+}}@xchg_i128
Expand All @@ -179,8 +174,8 @@ __intcap xchg_cap(__intcap* i, __intcap val) {
// PURECAP32-LABEL: define {{[^@]+}}@xchg_i128
// PURECAP32-SAME: (ptr addrspace(200) noundef [[I:%.*]], i64 noundef [[VAL:%.*]]) addrspace(200) #[[ATTR0]] {
// PURECAP32-NEXT: entry:
// PURECAP32-NEXT: [[CALL:%.*]] = call i64 @__atomic_exchange_8(ptr addrspace(200) noundef [[I]], i64 noundef [[VAL]], i32 noundef 5)
// PURECAP32-NEXT: ret i64 [[CALL]]
// PURECAP32-NEXT: [[TMP0:%.*]] = atomicrmw xchg ptr addrspace(200) [[I]], i64 [[VAL]] seq_cst, align 8
// PURECAP32-NEXT: ret i64 [[TMP0]]
//
// HYBRID32-LABEL: define {{[^@]+}}@xchg_i128
// HYBRID32-SAME: (ptr noundef [[I:%.*]], i64 noundef [[VAL:%.*]]) #[[ATTR0]] {
Expand All @@ -190,7 +185,7 @@ __intcap xchg_cap(__intcap* i, __intcap val) {
//
cap_size_int xchg_i128(cap_size_int* i, cap_size_int val) {
return __atomic_exchange_n(i, val, __ATOMIC_SEQ_CST);
// expected-warning@-1{{large atomic operation may incur significant performance penalty}}
// hybrid-warning@-1{{large atomic operation may incur significant performance penalty}}
}

// PURECAP64-LABEL: define {{[^@]+}}@lock_free_long
Expand Down Expand Up @@ -223,8 +218,7 @@ _Bool lock_free_long(long* l) {
// PURECAP64-LABEL: define {{[^@]+}}@lock_free_cap
// PURECAP64-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
// PURECAP64-NEXT: entry:
// PURECAP64-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_is_lock_free(i64 noundef 16, ptr addrspace(200) noundef [[I]])
// PURECAP64-NEXT: ret i1 [[CALL]]
// PURECAP64-NEXT: ret i1 true
//
// HYBRID64-LABEL: define {{[^@]+}}@lock_free_cap
// HYBRID64-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
Expand All @@ -235,8 +229,7 @@ _Bool lock_free_long(long* l) {
// PURECAP32-LABEL: define {{[^@]+}}@lock_free_cap
// PURECAP32-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
// PURECAP32-NEXT: entry:
// PURECAP32-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_is_lock_free(i32 noundef 8, ptr addrspace(200) noundef [[I]])
// PURECAP32-NEXT: ret i1 [[CALL]]
// PURECAP32-NEXT: ret i1 true
//
// HYBRID32-LABEL: define {{[^@]+}}@lock_free_cap
// HYBRID32-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
Expand All @@ -245,17 +238,17 @@ _Bool lock_free_long(long* l) {
// HYBRID32-NEXT: ret i1 [[CALL]]
//
_Bool lock_free_cap(__intcap* i) {
// TODO: _Static_assert(__atomic_always_lock_free(sizeof(*i), 0), "");
#ifdef __CHERI_PURE_CAPABILITY__
_Static_assert(__atomic_always_lock_free(sizeof(*i), 0), "");
#endif
return __atomic_is_lock_free(sizeof(*i), i);
}

//
// FIXME: should return true here
// PURECAP64-LABEL: define {{[^@]+}}@lock_free_i128
// PURECAP64-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
// PURECAP64-NEXT: entry:
// PURECAP64-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_is_lock_free(i64 noundef 16, ptr addrspace(200) noundef [[I]])
// PURECAP64-NEXT: ret i1 [[CALL]]
// PURECAP64-NEXT: ret i1 true
//
// HYBRID64-LABEL: define {{[^@]+}}@lock_free_i128
// HYBRID64-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
Expand All @@ -266,8 +259,7 @@ _Bool lock_free_cap(__intcap* i) {
// PURECAP32-LABEL: define {{[^@]+}}@lock_free_i128
// PURECAP32-SAME: (ptr addrspace(200) noundef [[I:%.*]]) addrspace(200) #[[ATTR0]] {
// PURECAP32-NEXT: entry:
// PURECAP32-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_is_lock_free(i32 noundef 8, ptr addrspace(200) noundef [[I]])
// PURECAP32-NEXT: ret i1 [[CALL]]
// PURECAP32-NEXT: ret i1 true
//
// HYBRID32-LABEL: define {{[^@]+}}@lock_free_i128
// HYBRID32-SAME: (ptr noundef [[I:%.*]]) #[[ATTR0]] {
Expand All @@ -276,6 +268,8 @@ _Bool lock_free_cap(__intcap* i) {
// HYBRID32-NEXT: ret i1 [[CALL]]
//
_Bool lock_free_i128(cap_size_int* i) {
// TODO: _Static_assert(__atomic_always_lock_free(sizeof(*i), 0), "");
#ifdef __CHERI_PURE_CAPABILITY__
_Static_assert(__atomic_always_lock_free(sizeof(*i), 0), "");
#endif
return __atomic_is_lock_free(sizeof(*i), i);
}
12 changes: 8 additions & 4 deletions clang/test/Preprocessor/cheri-lock-free.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/// Check that we report pointers as being always lock-free, otherwise <atomic>
/// ends up using locks with -ffreestanding.
// RUN: %riscv32_cheri_cc1 -fgnuc-version=4.2.1 -target-feature +a -E -dM %s \
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32 --implicit-check-not=_LOCK_FREE
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32-HYBRID --implicit-check-not=_LOCK_FREE
// RUN: %riscv32_cheri_purecap_cc1 -fgnuc-version=4.2.1 -target-feature +a -E -dM %s \
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32 --implicit-check-not=_LOCK_FREE
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32-PURECAP --implicit-check-not=_LOCK_FREE
// RUN: %riscv64_cheri_cc1 -fgnuc-version=4.2.1 -target-feature +a -E -dM %s \
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-64 --implicit-check-not=_LOCK_FREE
// RUN: %riscv64_cheri_purecap_cc1 -fgnuc-version=4.2.1 -target-feature +a -E -dM %s \
Expand All @@ -15,7 +15,9 @@
// CHECK: #define __CLANG_ATOMIC_CHAR_LOCK_FREE 2
// CHECK: #define __CLANG_ATOMIC_INT_LOCK_FREE 2
// CHECK-64: #define __CLANG_ATOMIC_LLONG_LOCK_FREE 2
// CHECK-32: #define __CLANG_ATOMIC_LLONG_LOCK_FREE 1
// NB: LLONG is always lockfree for RV32 purecap since we use capability atomics.
// CHECK-32-HYBRID: #define __CLANG_ATOMIC_LLONG_LOCK_FREE 1
// CHECK-32-PURECAP: #define __CLANG_ATOMIC_LLONG_LOCK_FREE 2
// CHECK: #define __CLANG_ATOMIC_LONG_LOCK_FREE 2
// CHECK: #define __CLANG_ATOMIC_POINTER_LOCK_FREE 2
// CHECK: #define __CLANG_ATOMIC_SHORT_LOCK_FREE 2
Expand All @@ -26,7 +28,9 @@
// CHECK: #define __GCC_ATOMIC_CHAR_LOCK_FREE 2
// CHECK: #define __GCC_ATOMIC_INT_LOCK_FREE 2
// CHECK-64: #define __GCC_ATOMIC_LLONG_LOCK_FREE 2
// CHECK-32: #define __GCC_ATOMIC_LLONG_LOCK_FREE 1
// NB: LLONG is always lockfree for RV32 purecap since we use capability atomics.
// CHECK-32-HYBRID: #define __GCC_ATOMIC_LLONG_LOCK_FREE 1
// CHECK-32-PURECAP: #define __GCC_ATOMIC_LLONG_LOCK_FREE 2
// CHECK: #define __GCC_ATOMIC_LONG_LOCK_FREE 2
// CHECK: #define __GCC_ATOMIC_POINTER_LOCK_FREE 2
// CHECK: #define __GCC_ATOMIC_SHORT_LOCK_FREE 2
Expand Down
5 changes: 1 addition & 4 deletions clang/test/Sema/cheri/atomic-lock-free.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,15 @@
// RUN: %riscv64_cheri_cc1 -target-feature +a %s -fsyntax-only -verify=hybrid
// RUN: %riscv32_cheri_purecap_cc1 -target-feature +a %s -fsyntax-only -verify=purecap
// RUN: %riscv32_cheri_cc1 -target-feature +a %s -fsyntax-only -verify=hybrid
// purecap-no-diagnostics

_Static_assert(__atomic_always_lock_free(sizeof(char), 0), "");
_Static_assert(__atomic_always_lock_free(sizeof(short), 0), "");
_Static_assert(__atomic_always_lock_free(sizeof(int), 0), "");
_Static_assert(__atomic_always_lock_free(sizeof(__INTPTR_TYPE__), 0), "");
// FIXME: purecap-error@-1{{static assertion failed due to requirement '__atomic_always_lock_free(sizeof(__intcap), 0)'}}
_Static_assert(__atomic_always_lock_free(sizeof(__UINTPTR_TYPE__), 0), "");
// FIXME: purecap-error@-1{{static assertion failed due to requirement '__atomic_always_lock_free(sizeof(unsigned __intcap), 0)'}}
_Static_assert(__atomic_always_lock_free(sizeof(void *), 0), "");
// FIXME: purecap-error@-1{{static assertion failed due to requirement '__atomic_always_lock_free(sizeof(void *), 0)'}}
/// TODO: it would be nice if hybrid mode also allowed lock-free sizeof(void * __capability)
/// but this is not currently true since atomic RMW/CMPXCHG with capability
/// pointers are not supported.
_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)'}}
// FIXME: purecap-error@-1{{static assertion failed due to requirement '__atomic_always_lock_free(sizeof(void *), 0)'}}

0 comments on commit d488825

Please sign in to comment.