[clang] [Clang] Add support for fp when using min_fetch/max_fetch atomics (PR #160330)

Amina Chabane via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 29 08:22:14 PDT 2025


https://github.com/Amichaxx updated https://github.com/llvm/llvm-project/pull/160330

>From a8043a7fd01823f05246b857f29b0957484b5222 Mon Sep 17 00:00:00 2001
From: Amichaxx <amina.chabane at arm.com>
Date: Mon, 22 Sep 2025 15:55:57 +0000
Subject: [PATCH 1/3] [Clang] Add support for fp when using min_fetch/max_fetch
 atomics

Previously when using min_fetch/max_fetch atomics with floating point types, LLVM would emit a crash. This patch updates the EmitPostAtomicMinMax function in CGAtomic.cpp to take floating point types. Included is a clang CodeGen test atomic-ops-float-check-minmax.c
---
 clang/lib/CodeGen/CGAtomic.cpp                |  18 ++-
 .../AArch64/atomic-ops-float-check-minmax.c   | 125 ++++++++++++++++++
 2 files changed, 140 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c

diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 9106c4cd8e139..99a06e527033b 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -507,20 +507,32 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
                                          bool IsSigned,
                                          llvm::Value *OldVal,
                                          llvm::Value *RHS) {
+  llvm::Type *ValTy = OldVal->getType();
+
   llvm::CmpInst::Predicate Pred;
+  bool IsFP = ValTy->isFloatingPointTy();
+
   switch (Op) {
   default:
     llvm_unreachable("Unexpected min/max operation");
+
   case AtomicExpr::AO__atomic_max_fetch:
   case AtomicExpr::AO__scoped_atomic_max_fetch:
-    Pred = IsSigned ? llvm::CmpInst::ICMP_SGT : llvm::CmpInst::ICMP_UGT;
+    Pred = IsFP ? llvm::CmpInst::FCMP_OGT
+                : (IsSigned ? llvm::CmpInst::ICMP_SGT : llvm::CmpInst::ICMP_UGT);
     break;
+
   case AtomicExpr::AO__atomic_min_fetch:
   case AtomicExpr::AO__scoped_atomic_min_fetch:
-    Pred = IsSigned ? llvm::CmpInst::ICMP_SLT : llvm::CmpInst::ICMP_ULT;
+    Pred = IsFP ? llvm::CmpInst::FCMP_OLT
+                : (IsSigned ? llvm::CmpInst::ICMP_SLT : llvm::CmpInst::ICMP_ULT);
     break;
   }
-  llvm::Value *Cmp = Builder.CreateICmp(Pred, OldVal, RHS, "tst");
+
+  llvm::Value *Cmp = IsFP
+      ? static_cast<llvm::Value*>(Builder.CreateFCmp(Pred, OldVal, RHS, "tst"))
+      : static_cast<llvm::Value*>(Builder.CreateICmp(Pred, OldVal, RHS, "tst"));
+
   return Builder.CreateSelect(Cmp, OldVal, RHS, "newval");
 }
 
diff --git a/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c b/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
new file mode 100644
index 0000000000000..e871f6a00e145
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
@@ -0,0 +1,125 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 %s -emit-llvm -o - -ffreestanding -triple=aarch64-linux-gnu -pthread -target-feature +bf16 | FileCheck %s
+
+#include <stdint.h>
+#include <stdatomic.h>
+
+// CHECK-LABEL: define dso_local void @test_minmax_postop(
+// CHECK-SAME: ptr noundef [[F32:%.*]], ptr noundef [[F16:%.*]], ptr noundef [[BF16:%.*]], ptr noundef [[F64:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[F32_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[F16_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[BF16_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[F64_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP1:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[ATOMIC_TEMP2:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[DOTATOMICTMP5:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[ATOMIC_TEMP6:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[DOTATOMICTMP9:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[ATOMIC_TEMP10:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[DOTATOMICTMP13:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP14:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP17:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[ATOMIC_TEMP18:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[DOTATOMICTMP21:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[ATOMIC_TEMP22:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[DOTATOMICTMP25:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[ATOMIC_TEMP26:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    store ptr [[F32]], ptr [[F32_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[F16]], ptr [[F16_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[BF16]], ptr [[BF16_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[F64]], ptr [[F64_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
+// CHECK-NEXT:    store double 4.210000e+01, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load double, ptr [[DOTATOMICTMP]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = atomicrmw fmax ptr [[TMP0]], double [[TMP1]] release, align 8
+// CHECK-NEXT:    [[TST:%.*]] = fcmp ogt double [[TMP2]], [[TMP1]]
+// CHECK-NEXT:    [[NEWVAL:%.*]] = select i1 [[TST]], double [[TMP2]], double [[TMP1]]
+// CHECK-NEXT:    store double [[NEWVAL]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = load double, ptr [[ATOMIC_TEMP]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
+// CHECK-NEXT:    store double [[TMP3]], ptr [[TMP4]], align 8
+// CHECK-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
+// CHECK-NEXT:    store float 0x40450CCCC0000000, ptr [[DOTATOMICTMP1]], align 4
+// CHECK-NEXT:    [[TMP6:%.*]] = load float, ptr [[DOTATOMICTMP1]], align 4
+// CHECK-NEXT:    [[TMP7:%.*]] = atomicrmw fmax ptr [[TMP5]], float [[TMP6]] release, align 4
+// CHECK-NEXT:    [[TST3:%.*]] = fcmp ogt float [[TMP7]], [[TMP6]]
+// CHECK-NEXT:    [[NEWVAL4:%.*]] = select i1 [[TST3]], float [[TMP7]], float [[TMP6]]
+// CHECK-NEXT:    store float [[NEWVAL4]], ptr [[ATOMIC_TEMP2]], align 4
+// CHECK-NEXT:    [[TMP8:%.*]] = load float, ptr [[ATOMIC_TEMP2]], align 4
+// CHECK-NEXT:    [[TMP9:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
+// CHECK-NEXT:    store float [[TMP8]], ptr [[TMP9]], align 4
+// CHECK-NEXT:    [[TMP10:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
+// CHECK-NEXT:    store half 0xH5143, ptr [[DOTATOMICTMP5]], align 2
+// CHECK-NEXT:    [[TMP11:%.*]] = load half, ptr [[DOTATOMICTMP5]], align 2
+// CHECK-NEXT:    [[TMP12:%.*]] = atomicrmw fmax ptr [[TMP10]], half [[TMP11]] release, align 2
+// CHECK-NEXT:    [[TST7:%.*]] = fcmp ogt half [[TMP12]], [[TMP11]]
+// CHECK-NEXT:    [[NEWVAL8:%.*]] = select i1 [[TST7]], half [[TMP12]], half [[TMP11]]
+// CHECK-NEXT:    store half [[NEWVAL8]], ptr [[ATOMIC_TEMP6]], align 2
+// CHECK-NEXT:    [[TMP13:%.*]] = load half, ptr [[ATOMIC_TEMP6]], align 2
+// CHECK-NEXT:    [[TMP14:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
+// CHECK-NEXT:    store half [[TMP13]], ptr [[TMP14]], align 2
+// CHECK-NEXT:    [[TMP15:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
+// CHECK-NEXT:    store bfloat 0xR4228, ptr [[DOTATOMICTMP9]], align 2
+// CHECK-NEXT:    [[TMP16:%.*]] = load bfloat, ptr [[DOTATOMICTMP9]], align 2
+// CHECK-NEXT:    [[TMP17:%.*]] = atomicrmw fmax ptr [[TMP15]], bfloat [[TMP16]] release, align 2
+// CHECK-NEXT:    [[TST11:%.*]] = fcmp ogt bfloat [[TMP17]], [[TMP16]]
+// CHECK-NEXT:    [[NEWVAL12:%.*]] = select i1 [[TST11]], bfloat [[TMP17]], bfloat [[TMP16]]
+// CHECK-NEXT:    store bfloat [[NEWVAL12]], ptr [[ATOMIC_TEMP10]], align 2
+// CHECK-NEXT:    [[TMP18:%.*]] = load bfloat, ptr [[ATOMIC_TEMP10]], align 2
+// CHECK-NEXT:    [[TMP19:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
+// CHECK-NEXT:    store bfloat [[TMP18]], ptr [[TMP19]], align 2
+// CHECK-NEXT:    [[TMP20:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
+// CHECK-NEXT:    store double 4.210000e+01, ptr [[DOTATOMICTMP13]], align 8
+// CHECK-NEXT:    [[TMP21:%.*]] = load double, ptr [[DOTATOMICTMP13]], align 8
+// CHECK-NEXT:    [[TMP22:%.*]] = atomicrmw fmin ptr [[TMP20]], double [[TMP21]] release, align 8
+// CHECK-NEXT:    [[TST15:%.*]] = fcmp olt double [[TMP22]], [[TMP21]]
+// CHECK-NEXT:    [[NEWVAL16:%.*]] = select i1 [[TST15]], double [[TMP22]], double [[TMP21]]
+// CHECK-NEXT:    store double [[NEWVAL16]], ptr [[ATOMIC_TEMP14]], align 8
+// CHECK-NEXT:    [[TMP23:%.*]] = load double, ptr [[ATOMIC_TEMP14]], align 8
+// CHECK-NEXT:    [[TMP24:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
+// CHECK-NEXT:    store double [[TMP23]], ptr [[TMP24]], align 8
+// CHECK-NEXT:    [[TMP25:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
+// CHECK-NEXT:    store float 0x40450CCCC0000000, ptr [[DOTATOMICTMP17]], align 4
+// CHECK-NEXT:    [[TMP26:%.*]] = load float, ptr [[DOTATOMICTMP17]], align 4
+// CHECK-NEXT:    [[TMP27:%.*]] = atomicrmw fmin ptr [[TMP25]], float [[TMP26]] release, align 4
+// CHECK-NEXT:    [[TST19:%.*]] = fcmp olt float [[TMP27]], [[TMP26]]
+// CHECK-NEXT:    [[NEWVAL20:%.*]] = select i1 [[TST19]], float [[TMP27]], float [[TMP26]]
+// CHECK-NEXT:    store float [[NEWVAL20]], ptr [[ATOMIC_TEMP18]], align 4
+// CHECK-NEXT:    [[TMP28:%.*]] = load float, ptr [[ATOMIC_TEMP18]], align 4
+// CHECK-NEXT:    [[TMP29:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
+// CHECK-NEXT:    store float [[TMP28]], ptr [[TMP29]], align 4
+// CHECK-NEXT:    [[TMP30:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
+// CHECK-NEXT:    store half 0xH5143, ptr [[DOTATOMICTMP21]], align 2
+// CHECK-NEXT:    [[TMP31:%.*]] = load half, ptr [[DOTATOMICTMP21]], align 2
+// CHECK-NEXT:    [[TMP32:%.*]] = atomicrmw fmin ptr [[TMP30]], half [[TMP31]] release, align 2
+// CHECK-NEXT:    [[TST23:%.*]] = fcmp olt half [[TMP32]], [[TMP31]]
+// CHECK-NEXT:    [[NEWVAL24:%.*]] = select i1 [[TST23]], half [[TMP32]], half [[TMP31]]
+// CHECK-NEXT:    store half [[NEWVAL24]], ptr [[ATOMIC_TEMP22]], align 2
+// CHECK-NEXT:    [[TMP33:%.*]] = load half, ptr [[ATOMIC_TEMP22]], align 2
+// CHECK-NEXT:    [[TMP34:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
+// CHECK-NEXT:    store half [[TMP33]], ptr [[TMP34]], align 2
+// CHECK-NEXT:    [[TMP35:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
+// CHECK-NEXT:    store bfloat 0xR4228, ptr [[DOTATOMICTMP25]], align 2
+// CHECK-NEXT:    [[TMP36:%.*]] = load bfloat, ptr [[DOTATOMICTMP25]], align 2
+// CHECK-NEXT:    [[TMP37:%.*]] = atomicrmw fmin ptr [[TMP35]], bfloat [[TMP36]] release, align 2
+// CHECK-NEXT:    [[TST27:%.*]] = fcmp olt bfloat [[TMP37]], [[TMP36]]
+// CHECK-NEXT:    [[NEWVAL28:%.*]] = select i1 [[TST27]], bfloat [[TMP37]], bfloat [[TMP36]]
+// CHECK-NEXT:    store bfloat [[NEWVAL28]], ptr [[ATOMIC_TEMP26]], align 2
+// CHECK-NEXT:    [[TMP38:%.*]] = load bfloat, ptr [[ATOMIC_TEMP26]], align 2
+// CHECK-NEXT:    [[TMP39:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
+// CHECK-NEXT:    store bfloat [[TMP38]], ptr [[TMP39]], align 2
+// CHECK-NEXT:    ret void
+//
+void test_minmax_postop(float *f32, _Float16 *f16, __bf16 *bf16, double *f64) {
+  *f64  = __atomic_max_fetch(f64,  42.1, memory_order_release);
+  *f32  = __atomic_max_fetch(f32,  42.1, memory_order_release);
+  *f16  = __atomic_max_fetch(f16,  42.1, memory_order_release);
+  *bf16 = __atomic_max_fetch(bf16, 42.1, memory_order_release);
+  *f64  = __atomic_min_fetch(f64,  42.1, memory_order_release);
+  *f32  = __atomic_min_fetch(f32,  42.1, memory_order_release);
+  *f16  = __atomic_min_fetch(f16,  42.1, memory_order_release);
+  *bf16 = __atomic_min_fetch(bf16, 42.1, memory_order_release);
+}

>From 38f95fa6050e1ea3466c644e1795e86d38550c77 Mon Sep 17 00:00:00 2001
From: Amichaxx <amina.chabane at arm.com>
Date: Fri, 26 Sep 2025 14:34:00 +0000
Subject: [PATCH 2/3] - Formatting - Amended logic to allow Clang to lower to
 expected intrinsics - Added Sema tests to atomic-ops-fp-minmax.c

---
 clang/lib/CodeGen/CGAtomic.cpp                |  27 +++--
 .../AArch64/atomic-ops-float-check-minmax.c   | 100 ++++++++----------
 clang/test/Sema/atomic-ops-fp-minmax.c        |  16 +++
 clang/test/Sema/atomic-ops.c                  |   2 +-
 4 files changed, 76 insertions(+), 69 deletions(-)
 create mode 100644 clang/test/Sema/atomic-ops-fp-minmax.c

diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 99a06e527033b..d0283dfd5a59d 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -507,32 +507,31 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
                                          bool IsSigned,
                                          llvm::Value *OldVal,
                                          llvm::Value *RHS) {
-  llvm::Type *ValTy = OldVal->getType();
+  const bool IsFP = OldVal->getType()->isFloatingPointTy();
 
-  llvm::CmpInst::Predicate Pred;
-  bool IsFP = ValTy->isFloatingPointTy();
+  if (IsFP) {
+    llvm::Intrinsic::ID IID = (Op == AtomicExpr::AO__atomic_max_fetch ||
+                               Op == AtomicExpr::AO__scoped_atomic_max_fetch)
+                                  ? llvm::Intrinsic::maxnum
+                                  : llvm::Intrinsic::minnum;
+
+    return Builder.CreateBinaryIntrinsic(IID, OldVal, RHS, nullptr, "newval");
+  }
 
+  llvm::CmpInst::Predicate Pred;
   switch (Op) {
   default:
     llvm_unreachable("Unexpected min/max operation");
-
   case AtomicExpr::AO__atomic_max_fetch:
   case AtomicExpr::AO__scoped_atomic_max_fetch:
-    Pred = IsFP ? llvm::CmpInst::FCMP_OGT
-                : (IsSigned ? llvm::CmpInst::ICMP_SGT : llvm::CmpInst::ICMP_UGT);
+    Pred = IsSigned ? llvm::CmpInst::ICMP_SGT : llvm::CmpInst::ICMP_UGT;
     break;
-
   case AtomicExpr::AO__atomic_min_fetch:
   case AtomicExpr::AO__scoped_atomic_min_fetch:
-    Pred = IsFP ? llvm::CmpInst::FCMP_OLT
-                : (IsSigned ? llvm::CmpInst::ICMP_SLT : llvm::CmpInst::ICMP_ULT);
+    Pred = IsSigned ? llvm::CmpInst::ICMP_SLT : llvm::CmpInst::ICMP_ULT;
     break;
   }
-
-  llvm::Value *Cmp = IsFP
-      ? static_cast<llvm::Value*>(Builder.CreateFCmp(Pred, OldVal, RHS, "tst"))
-      : static_cast<llvm::Value*>(Builder.CreateICmp(Pred, OldVal, RHS, "tst"));
-
+  llvm::Value *Cmp = Builder.CreateICmp(Pred, OldVal, RHS, "tst");
   return Builder.CreateSelect(Cmp, OldVal, RHS, "newval");
 }
 
diff --git a/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c b/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
index e871f6a00e145..4d9b29b789507 100644
--- a/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
+++ b/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
@@ -1,5 +1,5 @@
 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
-// RUN: %clang_cc1 %s -emit-llvm -o - -ffreestanding -triple=aarch64-linux-gnu -pthread -target-feature +bf16 | FileCheck %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -ffreestanding -triple=aarch64-linux-gnu -pthread | FileCheck %s
 
 #include <stdint.h>
 #include <stdatomic.h>
@@ -15,18 +15,18 @@
 // CHECK-NEXT:    [[ATOMIC_TEMP:%.*]] = alloca double, align 8
 // CHECK-NEXT:    [[DOTATOMICTMP1:%.*]] = alloca float, align 4
 // CHECK-NEXT:    [[ATOMIC_TEMP2:%.*]] = alloca float, align 4
-// CHECK-NEXT:    [[DOTATOMICTMP5:%.*]] = alloca half, align 2
-// CHECK-NEXT:    [[ATOMIC_TEMP6:%.*]] = alloca half, align 2
-// CHECK-NEXT:    [[DOTATOMICTMP9:%.*]] = alloca bfloat, align 2
-// CHECK-NEXT:    [[ATOMIC_TEMP10:%.*]] = alloca bfloat, align 2
-// CHECK-NEXT:    [[DOTATOMICTMP13:%.*]] = alloca double, align 8
-// CHECK-NEXT:    [[ATOMIC_TEMP14:%.*]] = alloca double, align 8
-// CHECK-NEXT:    [[DOTATOMICTMP17:%.*]] = alloca float, align 4
-// CHECK-NEXT:    [[ATOMIC_TEMP18:%.*]] = alloca float, align 4
-// CHECK-NEXT:    [[DOTATOMICTMP21:%.*]] = alloca half, align 2
-// CHECK-NEXT:    [[ATOMIC_TEMP22:%.*]] = alloca half, align 2
-// CHECK-NEXT:    [[DOTATOMICTMP25:%.*]] = alloca bfloat, align 2
-// CHECK-NEXT:    [[ATOMIC_TEMP26:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[DOTATOMICTMP4:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[ATOMIC_TEMP5:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[DOTATOMICTMP7:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[ATOMIC_TEMP8:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[DOTATOMICTMP10:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[ATOMIC_TEMP11:%.*]] = alloca double, align 8
+// CHECK-NEXT:    [[DOTATOMICTMP13:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[ATOMIC_TEMP14:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[DOTATOMICTMP16:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[ATOMIC_TEMP17:%.*]] = alloca half, align 2
+// CHECK-NEXT:    [[DOTATOMICTMP19:%.*]] = alloca bfloat, align 2
+// CHECK-NEXT:    [[ATOMIC_TEMP20:%.*]] = alloca bfloat, align 2
 // CHECK-NEXT:    store ptr [[F32]], ptr [[F32_ADDR]], align 8
 // CHECK-NEXT:    store ptr [[F16]], ptr [[F16_ADDR]], align 8
 // CHECK-NEXT:    store ptr [[BF16]], ptr [[BF16_ADDR]], align 8
@@ -35,8 +35,7 @@
 // CHECK-NEXT:    store double 4.210000e+01, ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP1:%.*]] = load double, ptr [[DOTATOMICTMP]], align 8
 // CHECK-NEXT:    [[TMP2:%.*]] = atomicrmw fmax ptr [[TMP0]], double [[TMP1]] release, align 8
-// CHECK-NEXT:    [[TST:%.*]] = fcmp ogt double [[TMP2]], [[TMP1]]
-// CHECK-NEXT:    [[NEWVAL:%.*]] = select i1 [[TST]], double [[TMP2]], double [[TMP1]]
+// CHECK-NEXT:    [[NEWVAL:%.*]] = call double @llvm.maxnum.f64(double [[TMP2]], double [[TMP1]])
 // CHECK-NEXT:    store double [[NEWVAL]], ptr [[ATOMIC_TEMP]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = load double, ptr [[ATOMIC_TEMP]], align 8
 // CHECK-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
@@ -45,70 +44,63 @@
 // CHECK-NEXT:    store float 0x40450CCCC0000000, ptr [[DOTATOMICTMP1]], align 4
 // CHECK-NEXT:    [[TMP6:%.*]] = load float, ptr [[DOTATOMICTMP1]], align 4
 // CHECK-NEXT:    [[TMP7:%.*]] = atomicrmw fmax ptr [[TMP5]], float [[TMP6]] release, align 4
-// CHECK-NEXT:    [[TST3:%.*]] = fcmp ogt float [[TMP7]], [[TMP6]]
-// CHECK-NEXT:    [[NEWVAL4:%.*]] = select i1 [[TST3]], float [[TMP7]], float [[TMP6]]
-// CHECK-NEXT:    store float [[NEWVAL4]], ptr [[ATOMIC_TEMP2]], align 4
+// CHECK-NEXT:    [[NEWVAL3:%.*]] = call float @llvm.maxnum.f32(float [[TMP7]], float [[TMP6]])
+// CHECK-NEXT:    store float [[NEWVAL3]], ptr [[ATOMIC_TEMP2]], align 4
 // CHECK-NEXT:    [[TMP8:%.*]] = load float, ptr [[ATOMIC_TEMP2]], align 4
 // CHECK-NEXT:    [[TMP9:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
 // CHECK-NEXT:    store float [[TMP8]], ptr [[TMP9]], align 4
 // CHECK-NEXT:    [[TMP10:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
-// CHECK-NEXT:    store half 0xH5143, ptr [[DOTATOMICTMP5]], align 2
-// CHECK-NEXT:    [[TMP11:%.*]] = load half, ptr [[DOTATOMICTMP5]], align 2
+// CHECK-NEXT:    store half 0xH5143, ptr [[DOTATOMICTMP4]], align 2
+// CHECK-NEXT:    [[TMP11:%.*]] = load half, ptr [[DOTATOMICTMP4]], align 2
 // CHECK-NEXT:    [[TMP12:%.*]] = atomicrmw fmax ptr [[TMP10]], half [[TMP11]] release, align 2
-// CHECK-NEXT:    [[TST7:%.*]] = fcmp ogt half [[TMP12]], [[TMP11]]
-// CHECK-NEXT:    [[NEWVAL8:%.*]] = select i1 [[TST7]], half [[TMP12]], half [[TMP11]]
-// CHECK-NEXT:    store half [[NEWVAL8]], ptr [[ATOMIC_TEMP6]], align 2
-// CHECK-NEXT:    [[TMP13:%.*]] = load half, ptr [[ATOMIC_TEMP6]], align 2
+// CHECK-NEXT:    [[NEWVAL6:%.*]] = call half @llvm.maxnum.f16(half [[TMP12]], half [[TMP11]])
+// CHECK-NEXT:    store half [[NEWVAL6]], ptr [[ATOMIC_TEMP5]], align 2
+// CHECK-NEXT:    [[TMP13:%.*]] = load half, ptr [[ATOMIC_TEMP5]], align 2
 // CHECK-NEXT:    [[TMP14:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
 // CHECK-NEXT:    store half [[TMP13]], ptr [[TMP14]], align 2
 // CHECK-NEXT:    [[TMP15:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
-// CHECK-NEXT:    store bfloat 0xR4228, ptr [[DOTATOMICTMP9]], align 2
-// CHECK-NEXT:    [[TMP16:%.*]] = load bfloat, ptr [[DOTATOMICTMP9]], align 2
+// CHECK-NEXT:    store bfloat 0xR4228, ptr [[DOTATOMICTMP7]], align 2
+// CHECK-NEXT:    [[TMP16:%.*]] = load bfloat, ptr [[DOTATOMICTMP7]], align 2
 // CHECK-NEXT:    [[TMP17:%.*]] = atomicrmw fmax ptr [[TMP15]], bfloat [[TMP16]] release, align 2
-// CHECK-NEXT:    [[TST11:%.*]] = fcmp ogt bfloat [[TMP17]], [[TMP16]]
-// CHECK-NEXT:    [[NEWVAL12:%.*]] = select i1 [[TST11]], bfloat [[TMP17]], bfloat [[TMP16]]
-// CHECK-NEXT:    store bfloat [[NEWVAL12]], ptr [[ATOMIC_TEMP10]], align 2
-// CHECK-NEXT:    [[TMP18:%.*]] = load bfloat, ptr [[ATOMIC_TEMP10]], align 2
+// CHECK-NEXT:    [[NEWVAL9:%.*]] = call bfloat @llvm.maxnum.bf16(bfloat [[TMP17]], bfloat [[TMP16]])
+// CHECK-NEXT:    store bfloat [[NEWVAL9]], ptr [[ATOMIC_TEMP8]], align 2
+// CHECK-NEXT:    [[TMP18:%.*]] = load bfloat, ptr [[ATOMIC_TEMP8]], align 2
 // CHECK-NEXT:    [[TMP19:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
 // CHECK-NEXT:    store bfloat [[TMP18]], ptr [[TMP19]], align 2
 // CHECK-NEXT:    [[TMP20:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
-// CHECK-NEXT:    store double 4.210000e+01, ptr [[DOTATOMICTMP13]], align 8
-// CHECK-NEXT:    [[TMP21:%.*]] = load double, ptr [[DOTATOMICTMP13]], align 8
+// CHECK-NEXT:    store double 4.210000e+01, ptr [[DOTATOMICTMP10]], align 8
+// CHECK-NEXT:    [[TMP21:%.*]] = load double, ptr [[DOTATOMICTMP10]], align 8
 // CHECK-NEXT:    [[TMP22:%.*]] = atomicrmw fmin ptr [[TMP20]], double [[TMP21]] release, align 8
-// CHECK-NEXT:    [[TST15:%.*]] = fcmp olt double [[TMP22]], [[TMP21]]
-// CHECK-NEXT:    [[NEWVAL16:%.*]] = select i1 [[TST15]], double [[TMP22]], double [[TMP21]]
-// CHECK-NEXT:    store double [[NEWVAL16]], ptr [[ATOMIC_TEMP14]], align 8
-// CHECK-NEXT:    [[TMP23:%.*]] = load double, ptr [[ATOMIC_TEMP14]], align 8
+// CHECK-NEXT:    [[NEWVAL12:%.*]] = call double @llvm.minnum.f64(double [[TMP22]], double [[TMP21]])
+// CHECK-NEXT:    store double [[NEWVAL12]], ptr [[ATOMIC_TEMP11]], align 8
+// CHECK-NEXT:    [[TMP23:%.*]] = load double, ptr [[ATOMIC_TEMP11]], align 8
 // CHECK-NEXT:    [[TMP24:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
 // CHECK-NEXT:    store double [[TMP23]], ptr [[TMP24]], align 8
 // CHECK-NEXT:    [[TMP25:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
-// CHECK-NEXT:    store float 0x40450CCCC0000000, ptr [[DOTATOMICTMP17]], align 4
-// CHECK-NEXT:    [[TMP26:%.*]] = load float, ptr [[DOTATOMICTMP17]], align 4
+// CHECK-NEXT:    store float 0x40450CCCC0000000, ptr [[DOTATOMICTMP13]], align 4
+// CHECK-NEXT:    [[TMP26:%.*]] = load float, ptr [[DOTATOMICTMP13]], align 4
 // CHECK-NEXT:    [[TMP27:%.*]] = atomicrmw fmin ptr [[TMP25]], float [[TMP26]] release, align 4
-// CHECK-NEXT:    [[TST19:%.*]] = fcmp olt float [[TMP27]], [[TMP26]]
-// CHECK-NEXT:    [[NEWVAL20:%.*]] = select i1 [[TST19]], float [[TMP27]], float [[TMP26]]
-// CHECK-NEXT:    store float [[NEWVAL20]], ptr [[ATOMIC_TEMP18]], align 4
-// CHECK-NEXT:    [[TMP28:%.*]] = load float, ptr [[ATOMIC_TEMP18]], align 4
+// CHECK-NEXT:    [[NEWVAL15:%.*]] = call float @llvm.minnum.f32(float [[TMP27]], float [[TMP26]])
+// CHECK-NEXT:    store float [[NEWVAL15]], ptr [[ATOMIC_TEMP14]], align 4
+// CHECK-NEXT:    [[TMP28:%.*]] = load float, ptr [[ATOMIC_TEMP14]], align 4
 // CHECK-NEXT:    [[TMP29:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
 // CHECK-NEXT:    store float [[TMP28]], ptr [[TMP29]], align 4
 // CHECK-NEXT:    [[TMP30:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
-// CHECK-NEXT:    store half 0xH5143, ptr [[DOTATOMICTMP21]], align 2
-// CHECK-NEXT:    [[TMP31:%.*]] = load half, ptr [[DOTATOMICTMP21]], align 2
+// CHECK-NEXT:    store half 0xH5143, ptr [[DOTATOMICTMP16]], align 2
+// CHECK-NEXT:    [[TMP31:%.*]] = load half, ptr [[DOTATOMICTMP16]], align 2
 // CHECK-NEXT:    [[TMP32:%.*]] = atomicrmw fmin ptr [[TMP30]], half [[TMP31]] release, align 2
-// CHECK-NEXT:    [[TST23:%.*]] = fcmp olt half [[TMP32]], [[TMP31]]
-// CHECK-NEXT:    [[NEWVAL24:%.*]] = select i1 [[TST23]], half [[TMP32]], half [[TMP31]]
-// CHECK-NEXT:    store half [[NEWVAL24]], ptr [[ATOMIC_TEMP22]], align 2
-// CHECK-NEXT:    [[TMP33:%.*]] = load half, ptr [[ATOMIC_TEMP22]], align 2
+// CHECK-NEXT:    [[NEWVAL18:%.*]] = call half @llvm.minnum.f16(half [[TMP32]], half [[TMP31]])
+// CHECK-NEXT:    store half [[NEWVAL18]], ptr [[ATOMIC_TEMP17]], align 2
+// CHECK-NEXT:    [[TMP33:%.*]] = load half, ptr [[ATOMIC_TEMP17]], align 2
 // CHECK-NEXT:    [[TMP34:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
 // CHECK-NEXT:    store half [[TMP33]], ptr [[TMP34]], align 2
 // CHECK-NEXT:    [[TMP35:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
-// CHECK-NEXT:    store bfloat 0xR4228, ptr [[DOTATOMICTMP25]], align 2
-// CHECK-NEXT:    [[TMP36:%.*]] = load bfloat, ptr [[DOTATOMICTMP25]], align 2
+// CHECK-NEXT:    store bfloat 0xR4228, ptr [[DOTATOMICTMP19]], align 2
+// CHECK-NEXT:    [[TMP36:%.*]] = load bfloat, ptr [[DOTATOMICTMP19]], align 2
 // CHECK-NEXT:    [[TMP37:%.*]] = atomicrmw fmin ptr [[TMP35]], bfloat [[TMP36]] release, align 2
-// CHECK-NEXT:    [[TST27:%.*]] = fcmp olt bfloat [[TMP37]], [[TMP36]]
-// CHECK-NEXT:    [[NEWVAL28:%.*]] = select i1 [[TST27]], bfloat [[TMP37]], bfloat [[TMP36]]
-// CHECK-NEXT:    store bfloat [[NEWVAL28]], ptr [[ATOMIC_TEMP26]], align 2
-// CHECK-NEXT:    [[TMP38:%.*]] = load bfloat, ptr [[ATOMIC_TEMP26]], align 2
+// CHECK-NEXT:    [[NEWVAL21:%.*]] = call bfloat @llvm.minnum.bf16(bfloat [[TMP37]], bfloat [[TMP36]])
+// CHECK-NEXT:    store bfloat [[NEWVAL21]], ptr [[ATOMIC_TEMP20]], align 2
+// CHECK-NEXT:    [[TMP38:%.*]] = load bfloat, ptr [[ATOMIC_TEMP20]], align 2
 // CHECK-NEXT:    [[TMP39:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
 // CHECK-NEXT:    store bfloat [[TMP38]], ptr [[TMP39]], align 2
 // CHECK-NEXT:    ret void
diff --git a/clang/test/Sema/atomic-ops-fp-minmax.c b/clang/test/Sema/atomic-ops-fp-minmax.c
new file mode 100644
index 0000000000000..970af9f769bd8
--- /dev/null
+++ b/clang/test/Sema/atomic-ops-fp-minmax.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -verify -ffreestanding -triple=aarch64-linux-gnu %s
+// REQUIRES: aarch64-registered-target
+
+#include <stdatomic.h>
+
+void memory_checks(_Float16 *p16, __bf16 *pbf, float *pf, double *pd) {
+  (void)__atomic_fetch_min(p16, (_Float16)1.0f, memory_order_relaxed);
+  (void)__atomic_fetch_max(pbf, (__bf16)2.0f, memory_order_acquire);
+  (void)__atomic_fetch_min(pf, 3.0f, memory_order_release);
+  (void)__atomic_fetch_max(pd, 4.0, memory_order_seq_cst);
+}
+
+void nullPointerWarning(void) {
+  (void)__atomic_fetch_min((volatile float*)0, 42.0, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
+  (void)__atomic_fetch_max((float*)0, 42.0, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
+}
diff --git a/clang/test/Sema/atomic-ops.c b/clang/test/Sema/atomic-ops.c
index ddeb29e19f760..47195b9dd4086 100644
--- a/clang/test/Sema/atomic-ops.c
+++ b/clang/test/Sema/atomic-ops.c
@@ -827,7 +827,7 @@ void nullPointerWarning(void) {
   (void)__atomic_fetch_min((int*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
   (void)__atomic_fetch_max((volatile int*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
   (void)__atomic_fetch_max((int*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
-
+  
   // These don't warn: the "desired" parameter is passed by value. Even for
   // atomic pointers the "desired" result can be NULL.
   __c11_atomic_init(&vai, 0);

>From d34a251bc2000c54f7264a3decd9893530d5fbf3 Mon Sep 17 00:00:00 2001
From: Amichaxx <amina.chabane at arm.com>
Date: Mon, 29 Sep 2025 15:21:58 +0000
Subject: [PATCH 3/3] Accidental newline

---
 clang/test/Sema/atomic-ops.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Sema/atomic-ops.c b/clang/test/Sema/atomic-ops.c
index 47195b9dd4086..ddeb29e19f760 100644
--- a/clang/test/Sema/atomic-ops.c
+++ b/clang/test/Sema/atomic-ops.c
@@ -827,7 +827,7 @@ void nullPointerWarning(void) {
   (void)__atomic_fetch_min((int*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
   (void)__atomic_fetch_max((volatile int*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
   (void)__atomic_fetch_max((int*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
-  
+
   // These don't warn: the "desired" parameter is passed by value. Even for
   // atomic pointers the "desired" result can be NULL.
   __c11_atomic_init(&vai, 0);



More information about the cfe-commits mailing list