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

Reply via email to