https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/183843
>From 2923f457626a2300b18cf7a034fe98c90ea2bb55 Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Fri, 27 Feb 2026 15:40:22 -0600 Subject: [PATCH 1/2] [Clang] Permit floating point and pointer values in most atomic ops Summary: We already support floating point arguments for the standard atomic functions. I do not know if this was an oversight or a conscious choice, but this was not applied to the `_n` extensions. This PR permits it for non-x87 floating point types, as these are all power of two primitive types smaller than 16 bytes. --- clang/lib/Sema/SemaChecking.cpp | 27 ++++++++++++++++----------- clang/test/Sema/atomic-ops.c | 19 +++++++++++++------ clang/test/Sema/scoped-atomic-ops.c | 26 ++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 5ee2c4274b089..23d4317c78dba 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4664,11 +4664,13 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, case AtomicExpr::AO__hip_atomic_load: case AtomicExpr::AO__atomic_load_n: case AtomicExpr::AO__scoped_atomic_load_n: + ArithAllows = AOEVT_Pointer | AOEVT_FP; Form = Load; break; case AtomicExpr::AO__atomic_load: case AtomicExpr::AO__scoped_atomic_load: + ArithAllows = AOEVT_Pointer | AOEVT_FP; Form = LoadCopy; break; @@ -4679,6 +4681,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, case AtomicExpr::AO__atomic_store_n: case AtomicExpr::AO__scoped_atomic_store: case AtomicExpr::AO__scoped_atomic_store_n: + ArithAllows = AOEVT_Pointer | AOEVT_FP; Form = Copy; break; case AtomicExpr::AO__atomic_fetch_add: @@ -4753,11 +4756,13 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, case AtomicExpr::AO__opencl_atomic_exchange: case AtomicExpr::AO__atomic_exchange_n: case AtomicExpr::AO__scoped_atomic_exchange_n: + ArithAllows = AOEVT_Pointer | AOEVT_FP; Form = Xchg; break; case AtomicExpr::AO__atomic_exchange: case AtomicExpr::AO__scoped_atomic_exchange: + ArithAllows = AOEVT_Pointer | AOEVT_FP; Form = GNUXchg; break; @@ -4774,6 +4779,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, case AtomicExpr::AO__atomic_compare_exchange_n: case AtomicExpr::AO__scoped_atomic_compare_exchange: case AtomicExpr::AO__scoped_atomic_compare_exchange_n: + ArithAllows = AOEVT_Pointer; Form = GNUCmpXchg; break; @@ -4876,11 +4882,16 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, } // For an arithmetic operation, the implied arithmetic must be well-formed. - if (Form == Arithmetic) { + // For _n operations, the value type must also be a valid atomic type. + if (Form == Arithmetic || IsN) { // GCC does not enforce these rules for GNU atomics, but we do to help catch // trivial type errors. auto IsAllowedValueType = [&](QualType ValType, unsigned AllowedType) -> bool { + bool IsX87LongDouble = + ValType->isSpecificBuiltinType(BuiltinType::LongDouble) && + &Context.getTargetInfo().getLongDoubleFormat() == + &llvm::APFloat::x87DoubleExtended(); if (ValType->isIntegerType()) return true; if (ValType->isPointerType()) @@ -4888,9 +4899,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, if (!(ValType->isFloatingType() && (AllowedType & AOEVT_FP))) return false; // LLVM Parser does not allow atomicrmw with x86_fp80 type. - if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) && - &Context.getTargetInfo().getLongDoubleFormat() == - &llvm::APFloat::x87DoubleExtended()) + if (IsX87LongDouble) return false; return true; }; @@ -4899,7 +4908,9 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, ? (ArithAllows & AOEVT_Pointer ? diag::err_atomic_op_needs_atomic_int_ptr_or_fp : diag::err_atomic_op_needs_atomic_int_or_fp) - : diag::err_atomic_op_needs_atomic_int; + : (ArithAllows & AOEVT_Pointer + ? diag::err_atomic_op_needs_atomic_int_or_ptr + : diag::err_atomic_op_needs_atomic_int); Diag(ExprRange.getBegin(), DID) << IsC11 << Ptr->getType() << Ptr->getSourceRange(); return ExprError(); @@ -4909,12 +4920,6 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, diag::err_incomplete_type)) { return ExprError(); } - } else if (IsN && !ValType->isIntegerType() && !ValType->isPointerType()) { - // For __atomic_*_n operations, the value type must be a scalar integral or - // pointer type which is 1, 2, 4, 8 or 16 bytes in length. - Diag(ExprRange.getBegin(), diag::err_atomic_op_needs_atomic_int_or_ptr) - << IsC11 << Ptr->getType() << Ptr->getSourceRange(); - return ExprError(); } if (!IsC11 && !AtomTy.isTriviallyCopyableType(Context) && diff --git a/clang/test/Sema/atomic-ops.c b/clang/test/Sema/atomic-ops.c index 0e39777a0172c..3318e369f3e0d 100644 --- a/clang/test/Sema/atomic-ops.c +++ b/clang/test/Sema/atomic-ops.c @@ -156,7 +156,8 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci, _Atomic(int*) *p, _Atomic(float) *f, _Atomic(double) *d, _Atomic(long double) *ld, int *I, const int *CI, - int **P, float *F, double *D, struct S *s1, struct S *s2) { + int **P, float *F, double *D, long double *LD, + struct S *s1, struct S *s2) { __c11_atomic_init(I, 5); // expected-error {{pointer to _Atomic}} __c11_atomic_init(ci, 5); // expected-error {{address argument to atomic operation must be a pointer to non-const _Atomic type ('const _Atomic(int) *' invalid)}} @@ -174,8 +175,10 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci, int load_n_1 = __atomic_load_n(I, memory_order_relaxed); int *load_n_2 = __atomic_load_n(P, memory_order_relaxed); - float load_n_3 = __atomic_load_n(D, memory_order_relaxed); // expected-error {{must be a pointer to integer or pointer}} - __atomic_load_n(s1, memory_order_relaxed); // expected-error {{must be a pointer to integer or pointer}} + double load_n_3 = __atomic_load_n(D, memory_order_relaxed); + float load_n_4 = __atomic_load_n(F, memory_order_relaxed); + __atomic_load_n(LD, memory_order_relaxed); // fp80-error {{must be a pointer to integer, pointer or supported floating point type}} + __atomic_load_n(s1, memory_order_relaxed); // expected-error {{must be a pointer to integer, pointer or supported floating point type}} load_n_1 = __atomic_load_n(CI, memory_order_relaxed); __atomic_load(i, I, memory_order_relaxed); // expected-error {{must be a pointer to a trivially-copyable type}} @@ -198,8 +201,10 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci, __atomic_store_n(I, 4.0, memory_order_release); __atomic_store_n(CI, 4, memory_order_release); // expected-error {{address argument to atomic operation must be a pointer to non-const type ('const int *' invalid)}} __atomic_store_n(I, P, memory_order_release); // expected-error {{parameter of type 'int'}} - __atomic_store_n(i, 1, memory_order_release); // expected-error {{must be a pointer to integer or pointer}} - __atomic_store_n(s1, *s2, memory_order_release); // expected-error {{must be a pointer to integer or pointer}} + __atomic_store_n(i, 1, memory_order_release); // expected-error {{must be a pointer to integer, pointer or supported floating point type}} + __atomic_store_n(s1, *s2, memory_order_release); // expected-error {{must be a pointer to integer, pointer or supported floating point type}} + __atomic_store_n(D, 1.0, memory_order_release); + __atomic_store_n(F, 1.0f, memory_order_release); __atomic_store_n(I, I, memory_order_release); // expected-error {{incompatible pointer to integer conversion passing 'int *' to parameter of type 'int'; dereference with *}} __atomic_store(I, *P, memory_order_release); @@ -209,7 +214,8 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci, int exchange_1 = __c11_atomic_exchange(i, 1, memory_order_seq_cst); int exchange_2 = __c11_atomic_exchange(I, 1, memory_order_seq_cst); // expected-error {{must be a pointer to _Atomic}} - int exchange_3 = __atomic_exchange_n(i, 1, memory_order_seq_cst); // expected-error {{must be a pointer to integer or pointer}} + int exchange_3 = __atomic_exchange_n(i, 1, memory_order_seq_cst); // expected-error {{must be a pointer to integer, pointer or supported floating point type}} + double exchange_5 = __atomic_exchange_n(D, 2.0, memory_order_seq_cst); int exchange_4 = __atomic_exchange_n(I, 1, memory_order_seq_cst); __atomic_exchange(s1, s2, s2, memory_order_seq_cst); @@ -275,6 +281,7 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci, _Bool cmpexch_6 = __atomic_compare_exchange_n(I, I, P, 0, memory_order_seq_cst, memory_order_seq_cst); // expected-error {{passing 'int **' to parameter of type 'int'}} (void)__atomic_compare_exchange_n(CI, I, 5, 1, memory_order_seq_cst, memory_order_seq_cst); // expected-error {{address argument to atomic operation must be a pointer to non-const type ('const int *' invalid)}} (void)__atomic_compare_exchange_n(I, CI, 5, 1, memory_order_seq_cst, memory_order_seq_cst); // expected-warning {{passing 'const int *' to parameter of type 'int *' discards qualifiers}} + (void)__atomic_compare_exchange_n(D, D, 5, 1, memory_order_seq_cst, memory_order_seq_cst); // expected-error {{must be a pointer to integer or pointer}} _Bool cmpexch_7 = __atomic_compare_exchange(I, I, 5, 1, memory_order_seq_cst, memory_order_seq_cst); // expected-error {{passing 'int' to parameter of type 'int *'}} _Bool cmpexch_8 = __atomic_compare_exchange(I, P, I, 0, memory_order_seq_cst, memory_order_seq_cst); // expected-error {{; dereference with *}} diff --git a/clang/test/Sema/scoped-atomic-ops.c b/clang/test/Sema/scoped-atomic-ops.c index 267c913dc9f9f..ab89f0738d0b5 100644 --- a/clang/test/Sema/scoped-atomic-ops.c +++ b/clang/test/Sema/scoped-atomic-ops.c @@ -117,3 +117,29 @@ int fi7a(_Bool *c) { return __scoped_atomic_exchange_n(c, 1, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); } + +float ff1a(float *i) { + float cmp = 0; + float desired = 1; + return __scoped_atomic_compare_exchange(i, &cmp, &desired, 0, + __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE, + __MEMORY_SCOPE_SYSTEM); +} + +float ff2a(float *i) { + float cmp = 0; + return __scoped_atomic_compare_exchange_n(i, &cmp, 1, 1, __ATOMIC_ACQUIRE, // expected-error {{must be a pointer to integer or pointer}} + __ATOMIC_ACQUIRE, + __MEMORY_SCOPE_SYSTEM); +} + +float ff3a(float *c, float *d) { + float ret; + __scoped_atomic_exchange(c, d, &ret, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); + return ret; +} + +float ff4a(_Bool *c) { + return __scoped_atomic_exchange_n(c, 1, __ATOMIC_RELAXED, + __MEMORY_SCOPE_SYSTEM); +} >From e9398e19d1cb61dd030036669f6500d80250682d Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Tue, 3 Mar 2026 16:07:13 -0600 Subject: [PATCH 2/2] Release notes --- clang/docs/ReleaseNotes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3c3e5f5a77a40..f2ea45789fd27 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -145,6 +145,9 @@ C23 Feature Support Non-comprehensive list of changes in this release ------------------------------------------------- +- Added support for floating point and pointer values in most ``__atomic_`` + builtins. + - Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right`` for bit rotation of unsigned integers including ``_BitInt`` types. Rotation counts are normalized modulo the bit-width and support negative values. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
