From 060240c81ab6ed459d4b257ca200089199a1dc89 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Fri, 22 Sep 2023 09:47:42 -0700 Subject: [PATCH] [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::is_lock_free reporting false in C++14 mode as well as a compilation error in compiler-rt atomic.c. --- clang/lib/Basic/Targets/RISCV.h | 18 ++++++- clang/test/CodeGen/cheri/atomic-lock-free.c | 52 +++++++++------------ clang/test/Preprocessor/cheri-lock-free.c | 12 +++-- clang/test/Sema/cheri/atomic-lock-free.c | 5 +- 4 files changed, 48 insertions(+), 39 deletions(-) diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h index a7c2b456b104..d1e333120e16 100644 --- a/clang/lib/Basic/Targets/RISCV.h +++ b/clang/lib/Basic/Targets/RISCV.h @@ -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; } @@ -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; } diff --git a/clang/test/CodeGen/cheri/atomic-lock-free.c b/clang/test/CodeGen/cheri/atomic-lock-free.c index c7dac7b32066..8d53b89c5ead 100644 --- a/clang/test/CodeGen/cheri/atomic-lock-free.c +++ b/clang/test/CodeGen/cheri/atomic-lock-free.c @@ -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; @@ -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 @@ -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]] { @@ -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 @@ -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 @@ -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]] { @@ -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 @@ -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]] { @@ -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]] { @@ -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]] { @@ -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]] { @@ -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); } diff --git a/clang/test/Preprocessor/cheri-lock-free.c b/clang/test/Preprocessor/cheri-lock-free.c index 17bb21e3c359..350025a6eec5 100644 --- a/clang/test/Preprocessor/cheri-lock-free.c +++ b/clang/test/Preprocessor/cheri-lock-free.c @@ -1,9 +1,9 @@ /// Check that we report pointers as being always lock-free, otherwise /// 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 \ @@ -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 @@ -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 diff --git a/clang/test/Sema/cheri/atomic-lock-free.c b/clang/test/Sema/cheri/atomic-lock-free.c index e68d322411e3..746e5676f56b 100644 --- a/clang/test/Sema/cheri/atomic-lock-free.c +++ b/clang/test/Sema/cheri/atomic-lock-free.c @@ -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)'}}