[clang] [compiler-rt] [llvm] [clang] builtins for atomicrmw fminmax/_num (PR #187139)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 1 11:43:49 PDT 2026
https://github.com/gonzalobg updated https://github.com/llvm/llvm-project/pull/187139
>From e18ac24f3d3750ba4f62ac5f2f74e2ba36b5b207 Mon Sep 17 00:00:00 2001
From: Gonzalo Brito Gadeschi <gonzalob at nvidia.com>
Date: Tue, 17 Mar 2026 15:24:47 -0700
Subject: [PATCH 1/6] [clang] builtins for atomicrmw fminmax/_num
---
clang/include/clang/Basic/Builtins.td | 96 +++++
.../clang/Basic/DiagnosticSemaKinds.td | 3 +
clang/lib/AST/Expr.cpp | 16 +
clang/lib/CodeGen/CGAtomic.cpp | 109 ++++-
clang/lib/Sema/SemaChecking.cpp | 32 +-
.../AArch64/atomic-ops-float-check-minmax.c | 59 +++
clang/test/Sema/atomic-ops.c | 88 +++-
clang/test/Sema/scoped-atomic-ops.c | 32 ++
.../builtins/Unit/atomic_fp_minmax_test.c | 397 ++++++++++++++++++
9 files changed, 818 insertions(+), 14 deletions(-)
create mode 100644 compiler-rt/test/builtins/Unit/atomic_fp_minmax_test.c
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index a1c99ccba7676..ea4b0cee32213 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -2139,6 +2139,30 @@ def AtomicMinFetch : AtomicBuiltin {
let Prototype = "void(...)";
}
+def AtomicFMinimumFetch : AtomicBuiltin {
+ let Spellings = ["__atomic_fminimum_fetch"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def AtomicFMaximumFetch : AtomicBuiltin {
+ let Spellings = ["__atomic_fmaximum_fetch"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def AtomicFMinimumNumFetch : AtomicBuiltin {
+ let Spellings = ["__atomic_fminimum_num_fetch"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def AtomicFMaximumNumFetch : AtomicBuiltin {
+ let Spellings = ["__atomic_fmaximum_num_fetch"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
def AtomicNandFetch : AtomicBuiltin {
let Spellings = ["__atomic_nand_fetch"];
let Attributes = [CustomTypeChecking];
@@ -2284,6 +2308,30 @@ def ScopedAtomicFetchMax : AtomicBuiltin {
let Prototype = "void(...)";
}
+def ScopedAtomicFetchFMinimum : AtomicBuiltin {
+ let Spellings = ["__scoped_atomic_fetch_fminimum"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def ScopedAtomicFetchFMaximum : AtomicBuiltin {
+ let Spellings = ["__scoped_atomic_fetch_fmaximum"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def ScopedAtomicFetchFMinimumNum : AtomicBuiltin {
+ let Spellings = ["__scoped_atomic_fetch_fminimum_num"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def ScopedAtomicFetchFMaximumNum : AtomicBuiltin {
+ let Spellings = ["__scoped_atomic_fetch_fmaximum_num"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
def ScopedAtomicAddFetch : AtomicBuiltin {
let Spellings = ["__scoped_atomic_add_fetch"];
let Attributes = [CustomTypeChecking];
@@ -2332,6 +2380,30 @@ def ScopedAtomicMaxFetch : AtomicBuiltin {
let Prototype = "void(...)";
}
+def ScopedAtomicFMinimumFetch : AtomicBuiltin {
+ let Spellings = ["__scoped_atomic_fminimum_fetch"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def ScopedAtomicFMaximumFetch : AtomicBuiltin {
+ let Spellings = ["__scoped_atomic_fmaximum_fetch"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def ScopedAtomicFMinimumNumFetch : AtomicBuiltin {
+ let Spellings = ["__scoped_atomic_fminimum_num_fetch"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def ScopedAtomicFMaximumNumFetch : AtomicBuiltin {
+ let Spellings = ["__scoped_atomic_fmaximum_num_fetch"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
def ScopedAtomicUInc : AtomicBuiltin {
let Spellings = ["__scoped_atomic_fetch_uinc"];
let Attributes = [CustomTypeChecking];
@@ -2436,6 +2508,30 @@ def AtomicFetchMin : AtomicBuiltin {
let Prototype = "void(...)";
}
+def AtomicFetchFMinimum : AtomicBuiltin {
+ let Spellings = ["__atomic_fetch_fminimum"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def AtomicFetchFMaximum : AtomicBuiltin {
+ let Spellings = ["__atomic_fetch_fmaximum"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def AtomicFetchFMinimumNum : AtomicBuiltin {
+ let Spellings = ["__atomic_fetch_fminimum_num"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
+def AtomicFetchFMaximumNum : AtomicBuiltin {
+ let Spellings = ["__atomic_fetch_fmaximum_num"];
+ let Attributes = [CustomTypeChecking];
+ let Prototype = "void(...)";
+}
+
def AtomicFetchUInc : AtomicBuiltin {
let Spellings = ["__atomic_fetch_uinc"];
let Attributes = [CustomTypeChecking];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d4d09a8ecef36..30508e063ac39 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9630,6 +9630,9 @@ def err_atomic_op_needs_atomic_int_or_fp : Error<
def err_atomic_op_needs_atomic_int : Error<
"address argument to atomic operation must be a pointer to "
"%select{|atomic }0integer (%1 invalid)">;
+def err_atomic_op_needs_atomic_fp
+ : Error<"address argument to atomic operation must be a pointer to "
+ "%select{|atomic }0floating point type (%1 invalid)">;
def warn_atomic_op_has_invalid_memory_order : Warning<
"%select{|success |failure }0memory order argument to atomic operation is invalid">,
InGroup<DiagGroup<"atomic-memory-ordering">>;
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 185e887fb05c3..f05d4c6a495ac 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -5288,8 +5288,16 @@ unsigned AtomicExpr::getNumSubExprs(AtomicOp Op) {
case AO__atomic_nand_fetch:
case AO__atomic_min_fetch:
case AO__atomic_max_fetch:
+ case AO__atomic_fminimum_fetch:
+ case AO__atomic_fmaximum_fetch:
+ case AO__atomic_fminimum_num_fetch:
+ case AO__atomic_fmaximum_num_fetch:
case AO__atomic_fetch_min:
case AO__atomic_fetch_max:
+ case AO__atomic_fetch_fminimum:
+ case AO__atomic_fetch_fmaximum:
+ case AO__atomic_fetch_fminimum_num:
+ case AO__atomic_fetch_fmaximum_num:
case AO__atomic_fetch_uinc:
case AO__atomic_fetch_udec:
return 3;
@@ -5311,8 +5319,16 @@ unsigned AtomicExpr::getNumSubExprs(AtomicOp Op) {
case AO__scoped_atomic_nand_fetch:
case AO__scoped_atomic_min_fetch:
case AO__scoped_atomic_max_fetch:
+ case AO__scoped_atomic_fminimum_fetch:
+ case AO__scoped_atomic_fmaximum_fetch:
+ case AO__scoped_atomic_fminimum_num_fetch:
+ case AO__scoped_atomic_fmaximum_num_fetch:
case AO__scoped_atomic_fetch_min:
case AO__scoped_atomic_fetch_max:
+ case AO__scoped_atomic_fetch_fminimum:
+ case AO__scoped_atomic_fetch_fmaximum:
+ case AO__scoped_atomic_fetch_fminimum_num:
+ case AO__scoped_atomic_fetch_fmaximum_num:
case AO__scoped_atomic_exchange_n:
case AO__scoped_atomic_fetch_uinc:
case AO__scoped_atomic_fetch_udec:
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 859ab20bb6740..4f02b27eb4f31 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -530,10 +530,27 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
const bool IsFP = OldVal->getType()->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;
+ llvm::Intrinsic::ID IID;
+ if (Op == AtomicExpr::AO__atomic_max_fetch ||
+ Op == AtomicExpr::AO__scoped_atomic_max_fetch)
+ IID = llvm::Intrinsic::maxnum;
+ else if (Op == AtomicExpr::AO__atomic_min_fetch ||
+ Op == AtomicExpr::AO__scoped_atomic_min_fetch)
+ IID = llvm::Intrinsic::minnum;
+ else if (Op == AtomicExpr::AO__atomic_fmaximum_fetch ||
+ Op == AtomicExpr::AO__scoped_atomic_fmaximum_fetch)
+ IID = llvm::Intrinsic::maximum;
+ else if (Op == AtomicExpr::AO__atomic_fminimum_fetch ||
+ Op == AtomicExpr::AO__scoped_atomic_fminimum_fetch)
+ IID = llvm::Intrinsic::minimum;
+ else if (Op == AtomicExpr::AO__atomic_fmaximum_num_fetch ||
+ Op == AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch)
+ IID = llvm::Intrinsic::maximumnum;
+ else if (Op == AtomicExpr::AO__atomic_fminimum_num_fetch ||
+ Op == AtomicExpr::AO__scoped_atomic_fminimum_num_fetch)
+ IID = llvm::Intrinsic::minimumnum;
+ else
+ llvm_unreachable("Unexpected atomic FP min/max operation");
return Builder.CreateBinaryIntrinsic(IID, OldVal, RHS, llvm::FMFSource(),
"newval");
@@ -545,10 +562,18 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
llvm_unreachable("Unexpected min/max operation");
case AtomicExpr::AO__atomic_max_fetch:
case AtomicExpr::AO__scoped_atomic_max_fetch:
+ case AtomicExpr::AO__atomic_fmaximum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
+ case AtomicExpr::AO__atomic_fmaximum_num_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
Pred = IsSigned ? llvm::CmpInst::ICMP_SGT : llvm::CmpInst::ICMP_UGT;
break;
case AtomicExpr::AO__atomic_min_fetch:
case AtomicExpr::AO__scoped_atomic_min_fetch:
+ case AtomicExpr::AO__atomic_fminimum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
+ case AtomicExpr::AO__atomic_fminimum_num_fetch:
+ case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
Pred = IsSigned ? llvm::CmpInst::ICMP_SLT : llvm::CmpInst::ICMP_ULT;
break;
}
@@ -705,6 +730,28 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
: llvm::AtomicRMWInst::UMin);
break;
+ case AtomicExpr::AO__atomic_fminimum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
+ PostOpMinMax = true;
+ [[fallthrough]];
+ case AtomicExpr::AO__atomic_fetch_fminimum:
+ case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
+ assert(E->getValueType()->isFloatingType() &&
+ "fminimum operations only support floating-point types");
+ Op = llvm::AtomicRMWInst::FMinimum;
+ break;
+
+ case AtomicExpr::AO__atomic_fminimum_num_fetch:
+ case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
+ PostOpMinMax = true;
+ [[fallthrough]];
+ case AtomicExpr::AO__atomic_fetch_fminimum_num:
+ case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
+ assert(E->getValueType()->isFloatingType() &&
+ "fminimum_num operations only support floating-point types");
+ Op = llvm::AtomicRMWInst::FMinimumNum;
+ break;
+
case AtomicExpr::AO__atomic_max_fetch:
case AtomicExpr::AO__scoped_atomic_max_fetch:
PostOpMinMax = true;
@@ -721,6 +768,28 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
: llvm::AtomicRMWInst::UMax);
break;
+ case AtomicExpr::AO__atomic_fmaximum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
+ PostOpMinMax = true;
+ [[fallthrough]];
+ case AtomicExpr::AO__atomic_fetch_fmaximum:
+ case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
+ assert(E->getValueType()->isFloatingType() &&
+ "fmaximum operations only support floating-point types");
+ Op = llvm::AtomicRMWInst::FMaximum;
+ break;
+
+ case AtomicExpr::AO__atomic_fmaximum_num_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
+ PostOpMinMax = true;
+ [[fallthrough]];
+ case AtomicExpr::AO__atomic_fetch_fmaximum_num:
+ case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
+ assert(E->getValueType()->isFloatingType() &&
+ "fmaximum_num operations only support floating-point types");
+ Op = llvm::AtomicRMWInst::FMaximumNum;
+ break;
+
case AtomicExpr::AO__atomic_and_fetch:
case AtomicExpr::AO__scoped_atomic_and_fetch:
PostOp = llvm::Instruction::And;
@@ -1047,10 +1116,18 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
case AtomicExpr::AO__scoped_atomic_fetch_max:
case AtomicExpr::AO__scoped_atomic_fetch_min:
case AtomicExpr::AO__scoped_atomic_fetch_sub:
+ case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
+ case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
+ case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
+ case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
case AtomicExpr::AO__scoped_atomic_add_fetch:
case AtomicExpr::AO__scoped_atomic_max_fetch:
case AtomicExpr::AO__scoped_atomic_min_fetch:
case AtomicExpr::AO__scoped_atomic_sub_fetch:
+ case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
[[fallthrough]];
case AtomicExpr::AO__atomic_fetch_and:
@@ -1093,6 +1170,14 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
case AtomicExpr::AO__scoped_atomic_exchange_n:
case AtomicExpr::AO__scoped_atomic_fetch_uinc:
case AtomicExpr::AO__scoped_atomic_fetch_udec:
+ case AtomicExpr::AO__atomic_fetch_fminimum:
+ case AtomicExpr::AO__atomic_fetch_fmaximum:
+ case AtomicExpr::AO__atomic_fetch_fminimum_num:
+ case AtomicExpr::AO__atomic_fetch_fmaximum_num:
+ case AtomicExpr::AO__atomic_fminimum_fetch:
+ case AtomicExpr::AO__atomic_fmaximum_fetch:
+ case AtomicExpr::AO__atomic_fminimum_num_fetch:
+ case AtomicExpr::AO__atomic_fmaximum_num_fetch:
Val1 = EmitValToTemp(*this, E->getVal1());
break;
}
@@ -1292,6 +1377,22 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
case AtomicExpr::AO__opencl_atomic_fetch_max:
case AtomicExpr::AO__scoped_atomic_fetch_max:
case AtomicExpr::AO__scoped_atomic_max_fetch:
+ case AtomicExpr::AO__atomic_fetch_fminimum:
+ case AtomicExpr::AO__atomic_fetch_fmaximum:
+ case AtomicExpr::AO__atomic_fetch_fminimum_num:
+ case AtomicExpr::AO__atomic_fetch_fmaximum_num:
+ case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
+ case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
+ case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
+ case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
+ case AtomicExpr::AO__atomic_fminimum_fetch:
+ case AtomicExpr::AO__atomic_fmaximum_fetch:
+ case AtomicExpr::AO__atomic_fminimum_num_fetch:
+ case AtomicExpr::AO__atomic_fmaximum_num_fetch:
+ case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
case AtomicExpr::AO__scoped_atomic_fetch_uinc:
case AtomicExpr::AO__scoped_atomic_fetch_udec:
case AtomicExpr::AO__atomic_test_and_set:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 29add9d092e6b..e71ad1f21bcbb 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -4701,6 +4701,25 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
ArithAllows = AOEVT_Pointer | AOEVT_FP;
Form = Arithmetic;
break;
+ case AtomicExpr::AO__atomic_fetch_fminimum:
+ case AtomicExpr::AO__atomic_fetch_fmaximum:
+ case AtomicExpr::AO__atomic_fminimum_fetch:
+ case AtomicExpr::AO__atomic_fmaximum_fetch:
+ case AtomicExpr::AO__atomic_fetch_fminimum_num:
+ case AtomicExpr::AO__atomic_fetch_fmaximum_num:
+ case AtomicExpr::AO__atomic_fminimum_num_fetch:
+ case AtomicExpr::AO__atomic_fmaximum_num_fetch:
+ case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
+ case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
+ case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
+ case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
+ case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
+ case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
+ case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
+ ArithAllows = AOEVT_FP;
+ Form = Arithmetic;
+ break;
case AtomicExpr::AO__atomic_fetch_max:
case AtomicExpr::AO__atomic_fetch_min:
case AtomicExpr::AO__atomic_max_fetch:
@@ -4715,7 +4734,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
case AtomicExpr::AO__opencl_atomic_fetch_min:
case AtomicExpr::AO__hip_atomic_fetch_max:
case AtomicExpr::AO__hip_atomic_fetch_min:
- ArithAllows = AOEVT_FP;
+ ArithAllows = AOEVT_Pointer | AOEVT_FP;
Form = Arithmetic;
break;
case AtomicExpr::AO__c11_atomic_fetch_and:
@@ -4893,7 +4912,9 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
&Context.getTargetInfo().getLongDoubleFormat() ==
&llvm::APFloat::x87DoubleExtended();
if (ValType->isIntegerType())
- return true;
+ // Special case: f-prefixed operations (AOEVT_FP exactly) reject
+ // integers
+ return AllowedType != AOEVT_FP;
if (ValType->isPointerType())
return AllowedType & AOEVT_Pointer;
if (!(ValType->isFloatingType() && (AllowedType & AOEVT_FP)))
@@ -4904,13 +4925,16 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
return true;
};
if (!IsAllowedValueType(ValType, ArithAllows)) {
- auto DID = ArithAllows & AOEVT_FP
+ auto DID =
+ ArithAllows == AOEVT_FP
+ ? diag::err_atomic_op_needs_atomic_fp
+ : (ArithAllows & AOEVT_FP
? (ArithAllows & AOEVT_Pointer
? diag::err_atomic_op_needs_atomic_int_ptr_or_fp
: diag::err_atomic_op_needs_atomic_int_or_fp)
: (ArithAllows & AOEVT_Pointer
? diag::err_atomic_op_needs_atomic_int_or_ptr
- : diag::err_atomic_op_needs_atomic_int);
+ : diag::err_atomic_op_needs_atomic_int));
Diag(ExprRange.getBegin(), DID)
<< IsC11 << Ptr->getType() << Ptr->getSourceRange();
return ExprError();
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 4d9b29b789507..d525a6d86f1f1 100644
--- a/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
+++ b/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
@@ -115,3 +115,62 @@ void test_minmax_postop(float *f32, _Float16 *f16, __bf16 *bf16, double *f64) {
*f16 = __atomic_min_fetch(f16, 42.1, memory_order_release);
*bf16 = __atomic_min_fetch(bf16, 42.1, memory_order_release);
}
+
+// CHECK-LABEL: define dso_local void @test_fminimum_fmaximum_postop(
+// CHECK-SAME: ptr noundef [[F32:%.*]], ptr noundef [[F16:%.*]], ptr noundef [[BF16:%.*]], ptr noundef [[F64:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK: [[TMP0:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
+// CHECK: store double 4.210000e+01, ptr [[DOTATOMICTMP]], align 8
+// CHECK: [[TMP1:%.*]] = load double, ptr [[DOTATOMICTMP]], align 8
+// CHECK: [[TMP2:%.*]] = atomicrmw fmaximum ptr [[TMP0]], double [[TMP1]] release, align 8
+// CHECK: [[NEWVAL:%.*]] = call double @llvm.maximum.f64(double [[TMP2]], double [[TMP1]])
+// CHECK: store double [[NEWVAL]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK: [[TMP3:%.*]] = load double, ptr [[ATOMIC_TEMP]], align 8
+// CHECK: [[TMP4:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
+// CHECK: store double [[TMP3]], ptr [[TMP4]], align 8
+void test_fminimum_fmaximum_postop(float *f32, _Float16 *f16, __bf16 *bf16, double *f64) {
+ *f64 = __atomic_fmaximum_fetch(f64, 42.1, memory_order_release);
+ *f32 = __atomic_fmaximum_fetch(f32, 42.1, memory_order_release);
+ *f16 = __atomic_fmaximum_fetch(f16, 42.1, memory_order_release);
+ *bf16 = __atomic_fmaximum_fetch(bf16, 42.1, memory_order_release);
+ *f64 = __atomic_fminimum_fetch(f64, 42.1, memory_order_release);
+ *f32 = __atomic_fminimum_fetch(f32, 42.1, memory_order_release);
+ *f16 = __atomic_fminimum_fetch(f16, 42.1, memory_order_release);
+ *bf16 = __atomic_fminimum_fetch(bf16, 42.1, memory_order_release);
+}
+
+// CHECK-LABEL: define dso_local void @test_fminimumnum_fmaximumnum_postop(
+// CHECK-SAME: ptr noundef [[F32:%.*]], ptr noundef [[F16:%.*]], ptr noundef [[BF16:%.*]], ptr noundef [[F64:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK: [[TMP0:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
+// CHECK: store double 4.210000e+01, ptr [[DOTATOMICTMP]], align 8
+// CHECK: [[TMP1:%.*]] = load double, ptr [[DOTATOMICTMP]], align 8
+// CHECK: [[TMP2:%.*]] = atomicrmw fmaximumnum ptr [[TMP0]], double [[TMP1]] release, align 8
+// CHECK: [[NEWVAL:%.*]] = call double @llvm.maximumnum.f64(double [[TMP2]], double [[TMP1]])
+// CHECK: store double [[NEWVAL]], ptr [[ATOMIC_TEMP]], align 8
+// CHECK: [[TMP3:%.*]] = load double, ptr [[ATOMIC_TEMP]], align 8
+// CHECK: [[TMP4:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
+// CHECK: store double [[TMP3]], ptr [[TMP4]], align 8
+void test_fminimumnum_fmaximumnum_postop(float *f32, _Float16 *f16, __bf16 *bf16, double *f64) {
+ *f64 = __atomic_fmaximum_num_fetch(f64, 42.1, memory_order_release);
+ *f32 = __atomic_fmaximum_num_fetch(f32, 42.1, memory_order_release);
+ *f16 = __atomic_fmaximum_num_fetch(f16, 42.1, memory_order_release);
+ *bf16 = __atomic_fmaximum_num_fetch(bf16, 42.1, memory_order_release);
+ *f64 = __atomic_fminimum_num_fetch(f64, 42.1, memory_order_release);
+ *f32 = __atomic_fminimum_num_fetch(f32, 42.1, memory_order_release);
+ *f16 = __atomic_fminimum_num_fetch(f16, 42.1, memory_order_release);
+ *bf16 = __atomic_fminimum_num_fetch(bf16, 42.1, memory_order_release);
+}
+
+// CHECK-LABEL: define dso_local void @test_fetch_variants(
+// CHECK-SAME: ptr noundef [[F64:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK: [[TMP0:%.*]] = atomicrmw fminimum ptr {{%.*}}, double {{%.*}} release, align 8
+// CHECK: [[TMP1:%.*]] = atomicrmw fmaximum ptr {{%.*}}, double {{%.*}} release, align 8
+// CHECK: [[TMP2:%.*]] = atomicrmw fminimumnum ptr {{%.*}}, double {{%.*}} release, align 8
+// CHECK: [[TMP3:%.*]] = atomicrmw fmaximumnum ptr {{%.*}}, double {{%.*}} release, align 8
+void test_fetch_variants(double *f64) {
+ double old1 = __atomic_fetch_fminimum(f64, 42.1, memory_order_release);
+ double old2 = __atomic_fetch_fmaximum(f64, 42.1, memory_order_release);
+ double old3 = __atomic_fetch_fminimum_num(f64, 42.1, memory_order_release);
+ double old4 = __atomic_fetch_fmaximum_num(f64, 42.1, memory_order_release);
+}
diff --git a/clang/test/Sema/atomic-ops.c b/clang/test/Sema/atomic-ops.c
index 3318e369f3e0d..d930af0d0e139 100644
--- a/clang/test/Sema/atomic-ops.c
+++ b/clang/test/Sema/atomic-ops.c
@@ -231,15 +231,15 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci,
__c11_atomic_fetch_add(d, 1.0, memory_order_seq_cst);
__c11_atomic_fetch_add(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer, pointer or supported floating point type}}
__c11_atomic_fetch_min(i, 1, memory_order_seq_cst);
- __c11_atomic_fetch_min(p, 1, memory_order_seq_cst); // expected-error {{must be a pointer to atomic integer or supported floating point type}}
+ __c11_atomic_fetch_min(p, 1, memory_order_seq_cst);
__c11_atomic_fetch_min(f, 1.0f, memory_order_seq_cst);
__c11_atomic_fetch_min(d, 1.0, memory_order_seq_cst);
- __c11_atomic_fetch_min(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer or supported floating point type}}
+ __c11_atomic_fetch_min(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer, pointer or supported floating point type}}
__c11_atomic_fetch_max(i, 1, memory_order_seq_cst);
- __c11_atomic_fetch_max(p, 1, memory_order_seq_cst); // expected-error {{must be a pointer to atomic integer or supported floating point type}}
+ __c11_atomic_fetch_max(p, 1, memory_order_seq_cst);
__c11_atomic_fetch_max(f, 1.0f, memory_order_seq_cst);
__c11_atomic_fetch_max(d, 1.0, memory_order_seq_cst);
- __c11_atomic_fetch_max(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer or supported floating point type}}
+ __c11_atomic_fetch_max(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer, pointer or supported floating point type}}
__atomic_fetch_add(i, 3, memory_order_seq_cst); // expected-error {{pointer to integer, pointer or supported floating point type}}
__atomic_fetch_sub(I, 3, memory_order_seq_cst);
@@ -250,8 +250,20 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci,
__atomic_fetch_min(D, 3, memory_order_seq_cst);
__atomic_fetch_max(F, 3, memory_order_seq_cst);
__atomic_fetch_max(D, 3, memory_order_seq_cst);
- __atomic_fetch_max(P, 3, memory_order_seq_cst); // expected-error {{must be a pointer to integer or supported floating point type}}
+ __atomic_fetch_max(P, 3, memory_order_seq_cst);
__atomic_fetch_max(p, 3); // expected-error {{too few arguments to function call, expected 3, have 2}}
+ __atomic_fetch_fminimum(F, 3, memory_order_seq_cst);
+ __atomic_fetch_fminimum(D, 3, memory_order_seq_cst);
+ __atomic_fetch_fmaximum(F, 3, memory_order_seq_cst);
+ __atomic_fetch_fmaximum(D, 3, memory_order_seq_cst);
+ __atomic_fetch_fmaximum(P, 3, memory_order_seq_cst); // expected-error {{must be a pointer to floating point type}}
+ __atomic_fetch_fmaximum(p, 3); // expected-error {{too few arguments to function call, expected 3, have 2}}
+ __atomic_fetch_fminimum_num(F, 3, memory_order_seq_cst);
+ __atomic_fetch_fminimum_num(D, 3, memory_order_seq_cst);
+ __atomic_fetch_fmaximum_num(F, 3, memory_order_seq_cst);
+ __atomic_fetch_fmaximum_num(D, 3, memory_order_seq_cst);
+ __atomic_fetch_fmaximum_num(P, 3, memory_order_seq_cst); // expected-error {{must be a pointer to floating point type}}
+ __atomic_fetch_fmaximum_num(p, 3); // expected-error {{too few arguments to function call, expected 3, have 2}}
__atomic_fetch_uinc(F, 1, memory_order_seq_cst); // expected-error {{address argument to atomic operation must be a pointer to integer}}
__atomic_fetch_udec(F, 1, memory_order_seq_cst); // expected-error {{address argument to atomic operation must be a pointer to integer}}
@@ -388,7 +400,7 @@ void PR16931(int* x) { // expected-note {{passing argument to parameter 'x' here
PR16931(&flagvar); // expected-error {{incompatible pointer types}}
}
-void memory_checks(_Atomic(int) *Ap, int *p, int val) {
+void memory_checks(_Atomic(int) *Ap, int *p, int val, float *fp, float fval) {
(void)__c11_atomic_load(Ap, memory_order_relaxed);
(void)__c11_atomic_load(Ap, memory_order_acquire);
(void)__c11_atomic_load(Ap, memory_order_consume);
@@ -601,6 +613,20 @@ void memory_checks(_Atomic(int) *Ap, int *p, int val) {
(void)__atomic_fetch_min(p, val, memory_order_acq_rel);
(void)__atomic_fetch_min(p, val, memory_order_seq_cst);
+ (void)__atomic_fetch_fminimum(fp, fval, memory_order_relaxed);
+ (void)__atomic_fetch_fminimum(fp, fval, memory_order_acquire);
+ (void)__atomic_fetch_fminimum(fp, fval, memory_order_consume);
+ (void)__atomic_fetch_fminimum(fp, fval, memory_order_release);
+ (void)__atomic_fetch_fminimum(fp, fval, memory_order_acq_rel);
+ (void)__atomic_fetch_fminimum(fp, fval, memory_order_seq_cst);
+
+ (void)__atomic_fetch_fminimum_num(fp, fval, memory_order_relaxed);
+ (void)__atomic_fetch_fminimum_num(fp, fval, memory_order_acquire);
+ (void)__atomic_fetch_fminimum_num(fp, fval, memory_order_consume);
+ (void)__atomic_fetch_fminimum_num(fp, fval, memory_order_release);
+ (void)__atomic_fetch_fminimum_num(fp, fval, memory_order_acq_rel);
+ (void)__atomic_fetch_fminimum_num(fp, fval, memory_order_seq_cst);
+
(void)__atomic_fetch_uinc(p, val, memory_order_relaxed);
(void)__atomic_fetch_uinc(p, val, memory_order_acquire);
(void)__atomic_fetch_uinc(p, val, memory_order_consume);
@@ -622,6 +648,20 @@ void memory_checks(_Atomic(int) *Ap, int *p, int val) {
(void)__atomic_fetch_max(p, val, memory_order_acq_rel);
(void)__atomic_fetch_max(p, val, memory_order_seq_cst);
+ (void)__atomic_fetch_fmaximum(fp, fval, memory_order_relaxed);
+ (void)__atomic_fetch_fmaximum(fp, fval, memory_order_acquire);
+ (void)__atomic_fetch_fmaximum(fp, fval, memory_order_consume);
+ (void)__atomic_fetch_fmaximum(fp, fval, memory_order_release);
+ (void)__atomic_fetch_fmaximum(fp, fval, memory_order_acq_rel);
+ (void)__atomic_fetch_fmaximum(fp, fval, memory_order_seq_cst);
+
+ (void)__atomic_fetch_fmaximum_num(fp, fval, memory_order_relaxed);
+ (void)__atomic_fetch_fmaximum_num(fp, fval, memory_order_acquire);
+ (void)__atomic_fetch_fmaximum_num(fp, fval, memory_order_consume);
+ (void)__atomic_fetch_fmaximum_num(fp, fval, memory_order_release);
+ (void)__atomic_fetch_fmaximum_num(fp, fval, memory_order_acq_rel);
+ (void)__atomic_fetch_fmaximum_num(fp, fval, memory_order_seq_cst);
+
(void)__atomic_and_fetch(p, val, memory_order_relaxed);
(void)__atomic_and_fetch(p, val, memory_order_acquire);
(void)__atomic_and_fetch(p, val, memory_order_consume);
@@ -664,6 +704,34 @@ void memory_checks(_Atomic(int) *Ap, int *p, int val) {
(void)__atomic_min_fetch(p, val, memory_order_acq_rel);
(void)__atomic_min_fetch(p, val, memory_order_seq_cst);
+ (void)__atomic_fmaximum_fetch(fp, fval, memory_order_relaxed);
+ (void)__atomic_fmaximum_fetch(fp, fval, memory_order_acquire);
+ (void)__atomic_fmaximum_fetch(fp, fval, memory_order_consume);
+ (void)__atomic_fmaximum_fetch(fp, fval, memory_order_release);
+ (void)__atomic_fmaximum_fetch(fp, fval, memory_order_acq_rel);
+ (void)__atomic_fmaximum_fetch(fp, fval, memory_order_seq_cst);
+
+ (void)__atomic_fminimum_fetch(fp, fval, memory_order_relaxed);
+ (void)__atomic_fminimum_fetch(fp, fval, memory_order_acquire);
+ (void)__atomic_fminimum_fetch(fp, fval, memory_order_consume);
+ (void)__atomic_fminimum_fetch(fp, fval, memory_order_release);
+ (void)__atomic_fminimum_fetch(fp, fval, memory_order_acq_rel);
+ (void)__atomic_fminimum_fetch(fp, fval, memory_order_seq_cst);
+
+ (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_relaxed);
+ (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_acquire);
+ (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_consume);
+ (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_release);
+ (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_acq_rel);
+ (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_seq_cst);
+
+ (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_relaxed);
+ (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_acquire);
+ (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_consume);
+ (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_release);
+ (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_acq_rel);
+ (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_seq_cst);
+
(void)__atomic_exchange_n(p, val, memory_order_relaxed);
(void)__atomic_exchange_n(p, val, memory_order_acquire);
(void)__atomic_exchange_n(p, val, memory_order_consume);
@@ -851,6 +919,14 @@ 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}}
+ (void)__atomic_fetch_fminimum((volatile float*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ (void)__atomic_fetch_fminimum((float*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ (void)__atomic_fetch_fmaximum((volatile float*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ (void)__atomic_fetch_fmaximum((float*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ (void)__atomic_fetch_fminimum_num((volatile float*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ (void)__atomic_fetch_fminimum_num((float*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ (void)__atomic_fetch_fmaximum_num((volatile float*)0, 42, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
+ (void)__atomic_fetch_fmaximum_num((float*)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.
diff --git a/clang/test/Sema/scoped-atomic-ops.c b/clang/test/Sema/scoped-atomic-ops.c
index 49ddc64ce23eb..716cf14d0b4a3 100644
--- a/clang/test/Sema/scoped-atomic-ops.c
+++ b/clang/test/Sema/scoped-atomic-ops.c
@@ -93,6 +93,38 @@ void fi3e(float *a, float *b, float *c, float *d, float *e, float *f) {
*e = __scoped_atomic_fetch_udec(e, 1u, __ATOMIC_RELAXED, 42); // expected-error {{address argument to atomic operation must be a pointer to integer ('float *' invalid)}}
}
+void fi3f(float *a, float *b, float *c, float *d, double *e, double *f, double *g, double *h) {
+ *a = __scoped_atomic_fetch_fminimum(a, 1.0f, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+ *b = __scoped_atomic_fetch_fmaximum(b, 1.0f, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+ *c = __scoped_atomic_fetch_fminimum_num(c, 1.0f, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+ *d = __scoped_atomic_fetch_fmaximum_num(d, 1.0f, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+ *e = __scoped_atomic_fetch_fminimum(e, 1.0, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+ *f = __scoped_atomic_fetch_fmaximum(f, 1.0, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+ *g = __scoped_atomic_fetch_fminimum_num(g, 1.0, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+ *h = __scoped_atomic_fetch_fmaximum_num(h, 1.0, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM);
+}
+
+void fi3g(float *a, float *b, float *c, float *d) {
+ *a = __scoped_atomic_fetch_fminimum(a, 1.0f, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}}
+ *b = __scoped_atomic_fetch_fmaximum(b, 1.0f, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}}
+ *c = __scoped_atomic_fetch_fminimum_num(c, 1.0f, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}}
+ *d = __scoped_atomic_fetch_fmaximum_num(d, 1.0f, __ATOMIC_RELAXED); // expected-error {{too few arguments to function call, expected 4, have 3}}
+}
+
+void fi3h(float *a, float *b, float *c, float *d) {
+ *a = __scoped_atomic_fetch_fminimum(a, 1.0f, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}}
+ *b = __scoped_atomic_fetch_fmaximum(b, 1.0f, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}}
+ *c = __scoped_atomic_fetch_fminimum_num(c, 1.0f, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}}
+ *d = __scoped_atomic_fetch_fmaximum_num(d, 1.0f, __ATOMIC_RELAXED, 42); // expected-error {{synchronization scope argument to atomic operation is invalid}}
+}
+
+void fi3i(int *a, int *b, int *c, int *d) {
+ *a = __scoped_atomic_fetch_fminimum(a, 1.0f, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic operation must be a pointer to floating point type}}
+ *b = __scoped_atomic_fetch_fmaximum(b, 1.0f, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic operation must be a pointer to floating point type}}
+ *c = __scoped_atomic_fetch_fminimum_num(c, 1.0f, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic operation must be a pointer to floating point type}}
+ *d = __scoped_atomic_fetch_fmaximum_num(d, 1.0f, __ATOMIC_RELAXED, __MEMORY_SCOPE_SYSTEM); // expected-error {{address argument to atomic operation must be a pointer to floating point type}}
+}
+
int fi4a(int *i) {
int cmp = 0;
int desired = 1;
diff --git a/compiler-rt/test/builtins/Unit/atomic_fp_minmax_test.c b/compiler-rt/test/builtins/Unit/atomic_fp_minmax_test.c
new file mode 100644
index 0000000000000..b526e216be614
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/atomic_fp_minmax_test.c
@@ -0,0 +1,397 @@
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: native-run
+//===-- atomic_fp_minmax_test.c - Test FP atomic min/max operations -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file tests the floating-point atomic min/max builtins, focusing on
+// IEEE 754 corner cases: NaN, +/-infinity, +/-zero.
+//
+// There are three families of operations with different semantics:
+// 1. fminimum/fmaximum: IEEE 754-2019 minimum/maximum
+// - Propagates NaN (any NaN input produces NaN output)
+// - Distinguishes -0 and +0 (minimum(-0, +0) = -0, maximum(-0, +0) = +0)
+//
+// 2. fminimumnum/fmaximumnum: IEEE 754-2019 minimumNumber/maximumNumber
+// - Propagates numbers over NaN (minimumNumber(2.0, NaN) = 2.0)
+// - Treats -0 and +0 as equivalent
+//
+// 3. minnum/maxnum (existing __atomic_min_fetch for floats): IEEE 754-2008
+// - Propagates numbers over NaN (minnum(2.0, NaN) = 2.0)
+// - Treats -0 and +0 as equivalent
+//
+//===----------------------------------------------------------------------===//
+
+#include <float.h>
+#include <math.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#undef NDEBUG
+#include <assert.h>
+
+// Memory order for all tests
+#define MO memory_order_seq_cst
+
+// Helper to check if a float is NaN
+static inline bool is_nan_f(float x) { return x != x; }
+static inline bool is_nan_d(double x) { return x != x; }
+
+// Helper to check if two floats have the same bit pattern (for +0/-0 distinction)
+static inline bool same_bits_f(float a, float b) {
+ uint32_t a_bits, b_bits;
+ memcpy(&a_bits, &a, sizeof(float));
+ memcpy(&b_bits, &b, sizeof(float));
+ return a_bits == b_bits;
+}
+
+static inline bool same_bits_d(double a, double b) {
+ uint64_t a_bits, b_bits;
+ memcpy(&a_bits, &a, sizeof(double));
+ memcpy(&b_bits, &b, sizeof(double));
+ return a_bits == b_bits;
+}
+
+// Helper to create negative zero
+static inline float neg_zero_f(void) { return -0.0f; }
+static inline double neg_zero_d(void) { return -0.0; }
+
+//===----------------------------------------------------------------------===//
+// Test fminimum_fetch and fetch_fminimum (propagates NaN, distinguishes zeros)
+//===----------------------------------------------------------------------===//
+
+void test_fminimum_float(void) {
+ printf("Testing __atomic_fminimum_fetch (float)...\n");
+
+ // Test 1: Normal values
+ {
+ float x = 5.0f;
+ float result = __atomic_fminimum_fetch(&x, 3.0f, MO);
+ assert(result == 3.0f && "fminimum(5.0, 3.0) should be 3.0");
+ assert(x == 3.0f && "stored value should be 3.0");
+ }
+
+ {
+ float x = 2.0f;
+ float result = __atomic_fminimum_fetch(&x, 7.0f, MO);
+ assert(result == 2.0f && "fminimum(2.0, 7.0) should be 2.0");
+ assert(x == 2.0f && "stored value should be 2.0");
+ }
+
+ // Test 2: NaN propagation - CRITICAL: fminimum propagates NaN
+ {
+ float x = 1.0f;
+ float result = __atomic_fminimum_fetch(&x, NAN, MO);
+ assert(is_nan_f(result) && "fminimum(1.0, NaN) should be NaN");
+ assert(is_nan_f(x) && "stored value should be NaN");
+ }
+
+ {
+ float x = NAN;
+ float result = __atomic_fminimum_fetch(&x, 1.0f, MO);
+ assert(is_nan_f(result) && "fminimum(NaN, 1.0) should be NaN");
+ assert(is_nan_f(x) && "stored value should be NaN");
+ }
+
+ // Test 3: Zero handling - CRITICAL: fminimum distinguishes -0 and +0
+ {
+ float x = 0.0f;
+ float result = __atomic_fminimum_fetch(&x, neg_zero_f(), MO);
+ assert(same_bits_f(result, neg_zero_f()) &&
+ "fminimum(+0, -0) should be -0");
+ assert(same_bits_f(x, neg_zero_f()) && "stored value should be -0");
+ }
+
+ {
+ float x = neg_zero_f();
+ float result = __atomic_fminimum_fetch(&x, 0.0f, MO);
+ assert(same_bits_f(result, neg_zero_f()) &&
+ "fminimum(-0, +0) should be -0");
+ assert(same_bits_f(x, neg_zero_f()) && "stored value should be -0");
+ }
+
+ // Test 4: Infinity
+ {
+ float x = INFINITY;
+ float result = __atomic_fminimum_fetch(&x, 1.0f, MO);
+ assert(result == 1.0f && "fminimum(+inf, 1.0) should be 1.0");
+ }
+
+ {
+ float x = -INFINITY;
+ float result = __atomic_fminimum_fetch(&x, 1.0f, MO);
+ assert(result == -INFINITY && "fminimum(-inf, 1.0) should be -inf");
+ }
+
+ // Test 5: fetch variant (returns old value)
+ {
+ float x = 5.0f;
+ float old = __atomic_fetch_fminimum(&x, 3.0f, MO);
+ assert(old == 5.0f && "fetch_fminimum should return old value");
+ assert(x == 3.0f && "stored value should be 3.0");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fmaximum_float(void) {
+ printf("Testing __atomic_fmaximum_fetch (float)...\n");
+
+ // Test 1: Normal values
+ {
+ float x = 5.0f;
+ float result = __atomic_fmaximum_fetch(&x, 3.0f, MO);
+ assert(result == 5.0f && "fmaximum(5.0, 3.0) should be 5.0");
+ }
+
+ // Test 2: NaN propagation
+ {
+ float x = 1.0f;
+ float result = __atomic_fmaximum_fetch(&x, NAN, MO);
+ assert(is_nan_f(result) && "fmaximum(1.0, NaN) should be NaN");
+ }
+
+ // Test 3: Zero handling - fmaximum(+0, -0) should be +0
+ {
+ float x = 0.0f;
+ float result = __atomic_fmaximum_fetch(&x, neg_zero_f(), MO);
+ assert(same_bits_f(result, 0.0f) && "fmaximum(+0, -0) should be +0");
+ }
+
+ {
+ float x = neg_zero_f();
+ float result = __atomic_fmaximum_fetch(&x, 0.0f, MO);
+ assert(same_bits_f(result, 0.0f) && "fmaximum(-0, +0) should be +0");
+ }
+
+ // Test 4: Infinity
+ {
+ float x = INFINITY;
+ float result = __atomic_fmaximum_fetch(&x, 1.0f, MO);
+ assert(result == INFINITY && "fmaximum(+inf, 1.0) should be +inf");
+ }
+
+ printf(" PASSED\n");
+}
+
+//===----------------------------------------------------------------------===//
+// Test fminimumnum_fetch (propagates numbers, treats zeros as equivalent)
+//===----------------------------------------------------------------------===//
+
+void test_fminimum_num_float(void) {
+ printf("Testing __atomic_fminimum_num_fetch (float)...\n");
+
+ // Test 1: Normal values
+ {
+ float x = 5.0f;
+ float result = __atomic_fminimum_num_fetch(&x, 3.0f, MO);
+ assert(result == 3.0f && "fminimumnum(5.0, 3.0) should be 3.0");
+ }
+
+ // Test 2: NaN handling - CRITICAL: fminimumnum propagates NUMBER over NaN
+ {
+ float x = 1.0f;
+ float result = __atomic_fminimum_num_fetch(&x, NAN, MO);
+ assert(result == 1.0f &&
+ "fminimumnum(1.0, NaN) should be 1.0 (number over NaN)");
+ assert(x == 1.0f && "stored value should be 1.0");
+ }
+
+ {
+ float x = NAN;
+ float result = __atomic_fminimum_num_fetch(&x, 2.0f, MO);
+ assert(result == 2.0f &&
+ "fminimumnum(NaN, 2.0) should be 2.0 (number over NaN)");
+ assert(x == 2.0f && "stored value should be 2.0");
+ }
+
+ {
+ float x = NAN;
+ float result = __atomic_fminimum_num_fetch(&x, NAN, MO);
+ assert(is_nan_f(result) && "fminimumnum(NaN, NaN) should be NaN");
+ }
+
+ // Test 3: Zero handling - fminimumnum treats +0 and -0 as equivalent
+ // The result can be either, but should pick the minimum value
+ {
+ float x = 0.0f;
+ float result = __atomic_fminimum_num_fetch(&x, neg_zero_f(), MO);
+ // Result should be a zero (either +0 or -0 is acceptable per IEEE 754)
+ assert(result == 0.0f && "fminimumnum(+0, -0) should be zero");
+ }
+
+ // Test 4: Infinity
+ {
+ float x = INFINITY;
+ float result = __atomic_fminimum_num_fetch(&x, 1.0f, MO);
+ assert(result == 1.0f && "fminimumnum(+inf, 1.0) should be 1.0");
+ }
+
+ // Test 5: fetch variant
+ {
+ float x = NAN;
+ float old = __atomic_fetch_fminimum_num(&x, 3.0f, MO);
+ assert(is_nan_f(old) && "fetch_fminimum_num should return old value (NaN)");
+ assert(x == 3.0f && "stored value should be 3.0");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fmaximum_num_float(void) {
+ printf("Testing __atomic_fmaximum_num_fetch (float)...\n");
+
+ // Test 1: Normal values
+ {
+ float x = 5.0f;
+ float result = __atomic_fmaximum_num_fetch(&x, 3.0f, MO);
+ assert(result == 5.0f && "fmaximumnum(5.0, 3.0) should be 5.0");
+ }
+
+ // Test 2: NaN handling - propagates number over NaN
+ {
+ float x = 1.0f;
+ float result = __atomic_fmaximum_num_fetch(&x, NAN, MO);
+ assert(result == 1.0f && "fmaximumnum(1.0, NaN) should be 1.0");
+ }
+
+ {
+ float x = NAN;
+ float result = __atomic_fmaximum_num_fetch(&x, 2.0f, MO);
+ assert(result == 2.0f && "fmaximumnum(NaN, 2.0) should be 2.0");
+ }
+
+ // Test 3: Zero handling - treats +0 and -0 as equivalent
+ {
+ float x = 0.0f;
+ float result = __atomic_fmaximum_num_fetch(&x, neg_zero_f(), MO);
+ assert(result == 0.0f && "fmaximumnum(+0, -0) should be zero");
+ }
+
+ printf(" PASSED\n");
+}
+
+//===----------------------------------------------------------------------===//
+// Double precision tests
+//===----------------------------------------------------------------------===//
+
+void test_fminimum_double(void) {
+ printf("Testing __atomic_fminimum_fetch (double)...\n");
+
+ // Test NaN propagation
+ {
+ double x = 1.0;
+ double result = __atomic_fminimum_fetch(&x, NAN, MO);
+ assert(is_nan_d(result) && "fminimum(1.0, NaN) should be NaN (double)");
+ }
+
+ // Test zero distinction
+ {
+ double x = 0.0;
+ double result = __atomic_fminimum_fetch(&x, neg_zero_d(), MO);
+ assert(same_bits_d(result, neg_zero_d()) &&
+ "fminimum(+0, -0) should be -0 (double)");
+ }
+
+ // Test normal values
+ {
+ double x = 3.14;
+ double result = __atomic_fminimum_fetch(&x, 2.71, MO);
+ assert(result == 2.71 && "fminimum(3.14, 2.71) should be 2.71");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fmaximum_double(void) {
+ printf("Testing __atomic_fmaximum_fetch (double)...\n");
+
+ // Test NaN propagation
+ {
+ double x = 1.0;
+ double result = __atomic_fmaximum_fetch(&x, NAN, MO);
+ assert(is_nan_d(result) && "fmaximum(1.0, NaN) should be NaN (double)");
+ }
+
+ // Test zero distinction - fmaximum(+0, -0) = +0
+ {
+ double x = 0.0;
+ double result = __atomic_fmaximum_fetch(&x, neg_zero_d(), MO);
+ assert(same_bits_d(result, 0.0) &&
+ "fmaximum(+0, -0) should be +0 (double)");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fminimum_num_double(void) {
+ printf("Testing __atomic_fminimum_num_fetch (double)...\n");
+
+ // Test number over NaN
+ {
+ double x = NAN;
+ double result = __atomic_fminimum_num_fetch(&x, 2.5, MO);
+ assert(result == 2.5 && "fminimumnum(NaN, 2.5) should be 2.5 (double)");
+ }
+
+ // Test normal values
+ {
+ double x = 10.5;
+ double result = __atomic_fminimum_num_fetch(&x, 8.3, MO);
+ assert(result == 8.3 && "fminimumnum(10.5, 8.3) should be 8.3");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fmaximum_num_double(void) {
+ printf("Testing __atomic_fmaximum_num_fetch (double)...\n");
+
+ // Test number over NaN
+ {
+ double x = NAN;
+ double result = __atomic_fmaximum_num_fetch(&x, 2.5, MO);
+ assert(result == 2.5 && "fmaximumnum(NaN, 2.5) should be 2.5 (double)");
+ }
+
+ printf(" PASSED\n");
+}
+
+//===----------------------------------------------------------------------===//
+// Main test runner
+//===----------------------------------------------------------------------===//
+
+int main(void) {
+ printf("\n");
+ printf("=============================================================\n");
+ printf("Atomic Floating-Point Min/Max Tests\n");
+ printf("=============================================================\n");
+ printf("\n");
+
+ printf("--- fminimum/fmaximum (propagate NaN, distinguish zeros) ---\n");
+ test_fminimum_float();
+ test_fmaximum_float();
+ test_fminimum_double();
+ test_fmaximum_double();
+
+ printf("\n--- fminimumnum/fmaximumnum (prefer numbers, treat zeros equal) "
+ "---\n");
+ test_fminimum_num_float();
+ test_fmaximum_num_float();
+ test_fminimum_num_double();
+ test_fmaximum_num_double();
+
+ printf("\n");
+ printf("=============================================================\n");
+ printf("All tests PASSED!\n");
+ printf("=============================================================\n");
+ printf("\n");
+
+ return 0;
+}
>From ec49bd7f6c0c48085372a2a455c6149287b365e5 Mon Sep 17 00:00:00 2001
From: gonzalobg <65027571+gonzalobg at users.noreply.github.com>
Date: Wed, 25 Mar 2026 15:18:54 +0000
Subject: [PATCH 2/6] revert min/max pointer change
---
clang/lib/Sema/SemaChecking.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index e71ad1f21bcbb..0ab6ddfd9e072 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -4734,7 +4734,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
case AtomicExpr::AO__opencl_atomic_fetch_min:
case AtomicExpr::AO__hip_atomic_fetch_max:
case AtomicExpr::AO__hip_atomic_fetch_min:
- ArithAllows = AOEVT_Pointer | AOEVT_FP;
+ ArithAllows = AOEVT_FP;
Form = Arithmetic;
break;
case AtomicExpr::AO__c11_atomic_fetch_and:
>From 7ca3cec5f38b5a574d4d8313b0f953744b87519b Mon Sep 17 00:00:00 2001
From: Gonzalo Brito Gadeschi <gonzalob at nvidia.com>
Date: Fri, 1 May 2026 11:00:48 -0700
Subject: [PATCH 3/6] remove the _fetch variants
---
clang/include/clang/Basic/Builtins.td | 48 -----------------
clang/lib/AST/Expr.cpp | 8 ---
clang/lib/CodeGen/CGAtomic.cpp | 52 -------------------
clang/lib/Sema/SemaChecking.cpp | 15 ++----
.../AArch64/atomic-ops-float-check-minmax.c | 46 ----------------
clang/test/Sema/atomic-ops.c | 37 ++-----------
6 files changed, 9 insertions(+), 197 deletions(-)
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index ea4b0cee32213..e37c586cc19a5 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -2139,30 +2139,6 @@ def AtomicMinFetch : AtomicBuiltin {
let Prototype = "void(...)";
}
-def AtomicFMinimumFetch : AtomicBuiltin {
- let Spellings = ["__atomic_fminimum_fetch"];
- let Attributes = [CustomTypeChecking];
- let Prototype = "void(...)";
-}
-
-def AtomicFMaximumFetch : AtomicBuiltin {
- let Spellings = ["__atomic_fmaximum_fetch"];
- let Attributes = [CustomTypeChecking];
- let Prototype = "void(...)";
-}
-
-def AtomicFMinimumNumFetch : AtomicBuiltin {
- let Spellings = ["__atomic_fminimum_num_fetch"];
- let Attributes = [CustomTypeChecking];
- let Prototype = "void(...)";
-}
-
-def AtomicFMaximumNumFetch : AtomicBuiltin {
- let Spellings = ["__atomic_fmaximum_num_fetch"];
- let Attributes = [CustomTypeChecking];
- let Prototype = "void(...)";
-}
-
def AtomicNandFetch : AtomicBuiltin {
let Spellings = ["__atomic_nand_fetch"];
let Attributes = [CustomTypeChecking];
@@ -2380,30 +2356,6 @@ def ScopedAtomicMaxFetch : AtomicBuiltin {
let Prototype = "void(...)";
}
-def ScopedAtomicFMinimumFetch : AtomicBuiltin {
- let Spellings = ["__scoped_atomic_fminimum_fetch"];
- let Attributes = [CustomTypeChecking];
- let Prototype = "void(...)";
-}
-
-def ScopedAtomicFMaximumFetch : AtomicBuiltin {
- let Spellings = ["__scoped_atomic_fmaximum_fetch"];
- let Attributes = [CustomTypeChecking];
- let Prototype = "void(...)";
-}
-
-def ScopedAtomicFMinimumNumFetch : AtomicBuiltin {
- let Spellings = ["__scoped_atomic_fminimum_num_fetch"];
- let Attributes = [CustomTypeChecking];
- let Prototype = "void(...)";
-}
-
-def ScopedAtomicFMaximumNumFetch : AtomicBuiltin {
- let Spellings = ["__scoped_atomic_fmaximum_num_fetch"];
- let Attributes = [CustomTypeChecking];
- let Prototype = "void(...)";
-}
-
def ScopedAtomicUInc : AtomicBuiltin {
let Spellings = ["__scoped_atomic_fetch_uinc"];
let Attributes = [CustomTypeChecking];
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index f05d4c6a495ac..46574e24d2e12 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -5288,10 +5288,6 @@ unsigned AtomicExpr::getNumSubExprs(AtomicOp Op) {
case AO__atomic_nand_fetch:
case AO__atomic_min_fetch:
case AO__atomic_max_fetch:
- case AO__atomic_fminimum_fetch:
- case AO__atomic_fmaximum_fetch:
- case AO__atomic_fminimum_num_fetch:
- case AO__atomic_fmaximum_num_fetch:
case AO__atomic_fetch_min:
case AO__atomic_fetch_max:
case AO__atomic_fetch_fminimum:
@@ -5319,10 +5315,6 @@ unsigned AtomicExpr::getNumSubExprs(AtomicOp Op) {
case AO__scoped_atomic_nand_fetch:
case AO__scoped_atomic_min_fetch:
case AO__scoped_atomic_max_fetch:
- case AO__scoped_atomic_fminimum_fetch:
- case AO__scoped_atomic_fmaximum_fetch:
- case AO__scoped_atomic_fminimum_num_fetch:
- case AO__scoped_atomic_fmaximum_num_fetch:
case AO__scoped_atomic_fetch_min:
case AO__scoped_atomic_fetch_max:
case AO__scoped_atomic_fetch_fminimum:
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 4f02b27eb4f31..3e521ef8d7643 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -537,18 +537,6 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
else if (Op == AtomicExpr::AO__atomic_min_fetch ||
Op == AtomicExpr::AO__scoped_atomic_min_fetch)
IID = llvm::Intrinsic::minnum;
- else if (Op == AtomicExpr::AO__atomic_fmaximum_fetch ||
- Op == AtomicExpr::AO__scoped_atomic_fmaximum_fetch)
- IID = llvm::Intrinsic::maximum;
- else if (Op == AtomicExpr::AO__atomic_fminimum_fetch ||
- Op == AtomicExpr::AO__scoped_atomic_fminimum_fetch)
- IID = llvm::Intrinsic::minimum;
- else if (Op == AtomicExpr::AO__atomic_fmaximum_num_fetch ||
- Op == AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch)
- IID = llvm::Intrinsic::maximumnum;
- else if (Op == AtomicExpr::AO__atomic_fminimum_num_fetch ||
- Op == AtomicExpr::AO__scoped_atomic_fminimum_num_fetch)
- IID = llvm::Intrinsic::minimumnum;
else
llvm_unreachable("Unexpected atomic FP min/max operation");
@@ -562,18 +550,10 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
llvm_unreachable("Unexpected min/max operation");
case AtomicExpr::AO__atomic_max_fetch:
case AtomicExpr::AO__scoped_atomic_max_fetch:
- case AtomicExpr::AO__atomic_fmaximum_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
- case AtomicExpr::AO__atomic_fmaximum_num_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
Pred = IsSigned ? llvm::CmpInst::ICMP_SGT : llvm::CmpInst::ICMP_UGT;
break;
case AtomicExpr::AO__atomic_min_fetch:
case AtomicExpr::AO__scoped_atomic_min_fetch:
- case AtomicExpr::AO__atomic_fminimum_fetch:
- case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
- case AtomicExpr::AO__atomic_fminimum_num_fetch:
- case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
Pred = IsSigned ? llvm::CmpInst::ICMP_SLT : llvm::CmpInst::ICMP_ULT;
break;
}
@@ -730,10 +710,6 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
: llvm::AtomicRMWInst::UMin);
break;
- case AtomicExpr::AO__atomic_fminimum_fetch:
- case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
- PostOpMinMax = true;
- [[fallthrough]];
case AtomicExpr::AO__atomic_fetch_fminimum:
case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
assert(E->getValueType()->isFloatingType() &&
@@ -741,10 +717,6 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
Op = llvm::AtomicRMWInst::FMinimum;
break;
- case AtomicExpr::AO__atomic_fminimum_num_fetch:
- case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
- PostOpMinMax = true;
- [[fallthrough]];
case AtomicExpr::AO__atomic_fetch_fminimum_num:
case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
assert(E->getValueType()->isFloatingType() &&
@@ -768,10 +740,6 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
: llvm::AtomicRMWInst::UMax);
break;
- case AtomicExpr::AO__atomic_fmaximum_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
- PostOpMinMax = true;
- [[fallthrough]];
case AtomicExpr::AO__atomic_fetch_fmaximum:
case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
assert(E->getValueType()->isFloatingType() &&
@@ -779,10 +747,6 @@ static void EmitAtomicOp(CodeGenFunction &CGF, AtomicExpr *E, Address Dest,
Op = llvm::AtomicRMWInst::FMaximum;
break;
- case AtomicExpr::AO__atomic_fmaximum_num_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
- PostOpMinMax = true;
- [[fallthrough]];
case AtomicExpr::AO__atomic_fetch_fmaximum_num:
case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
assert(E->getValueType()->isFloatingType() &&
@@ -1124,10 +1088,6 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
case AtomicExpr::AO__scoped_atomic_max_fetch:
case AtomicExpr::AO__scoped_atomic_min_fetch:
case AtomicExpr::AO__scoped_atomic_sub_fetch:
- case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
- case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
[[fallthrough]];
case AtomicExpr::AO__atomic_fetch_and:
@@ -1174,10 +1134,6 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
case AtomicExpr::AO__atomic_fetch_fmaximum:
case AtomicExpr::AO__atomic_fetch_fminimum_num:
case AtomicExpr::AO__atomic_fetch_fmaximum_num:
- case AtomicExpr::AO__atomic_fminimum_fetch:
- case AtomicExpr::AO__atomic_fmaximum_fetch:
- case AtomicExpr::AO__atomic_fminimum_num_fetch:
- case AtomicExpr::AO__atomic_fmaximum_num_fetch:
Val1 = EmitValToTemp(*this, E->getVal1());
break;
}
@@ -1385,14 +1341,6 @@ RValue CodeGenFunction::EmitAtomicExpr(AtomicExpr *E) {
case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
- case AtomicExpr::AO__atomic_fminimum_fetch:
- case AtomicExpr::AO__atomic_fmaximum_fetch:
- case AtomicExpr::AO__atomic_fminimum_num_fetch:
- case AtomicExpr::AO__atomic_fmaximum_num_fetch:
- case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
- case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
case AtomicExpr::AO__scoped_atomic_fetch_uinc:
case AtomicExpr::AO__scoped_atomic_fetch_udec:
case AtomicExpr::AO__atomic_test_and_set:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 0ab6ddfd9e072..cf5c02ad7d861 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -4650,6 +4650,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
AOEVT_None = 0,
AOEVT_Pointer = 1,
AOEVT_FP = 2,
+ AOEVT_Int = 4,
};
unsigned ArithAllows = AOEVT_None;
@@ -4703,20 +4704,12 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
break;
case AtomicExpr::AO__atomic_fetch_fminimum:
case AtomicExpr::AO__atomic_fetch_fmaximum:
- case AtomicExpr::AO__atomic_fminimum_fetch:
- case AtomicExpr::AO__atomic_fmaximum_fetch:
case AtomicExpr::AO__atomic_fetch_fminimum_num:
case AtomicExpr::AO__atomic_fetch_fmaximum_num:
- case AtomicExpr::AO__atomic_fminimum_num_fetch:
- case AtomicExpr::AO__atomic_fmaximum_num_fetch:
case AtomicExpr::AO__scoped_atomic_fetch_fminimum:
case AtomicExpr::AO__scoped_atomic_fetch_fmaximum:
- case AtomicExpr::AO__scoped_atomic_fminimum_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_fetch:
case AtomicExpr::AO__scoped_atomic_fetch_fminimum_num:
case AtomicExpr::AO__scoped_atomic_fetch_fmaximum_num:
- case AtomicExpr::AO__scoped_atomic_fminimum_num_fetch:
- case AtomicExpr::AO__scoped_atomic_fmaximum_num_fetch:
ArithAllows = AOEVT_FP;
Form = Arithmetic;
break;
@@ -4734,7 +4727,7 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
case AtomicExpr::AO__opencl_atomic_fetch_min:
case AtomicExpr::AO__hip_atomic_fetch_max:
case AtomicExpr::AO__hip_atomic_fetch_min:
- ArithAllows = AOEVT_FP;
+ ArithAllows = AOEVT_Int | AOEVT_FP;
Form = Arithmetic;
break;
case AtomicExpr::AO__c11_atomic_fetch_and:
@@ -4913,8 +4906,8 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
&llvm::APFloat::x87DoubleExtended();
if (ValType->isIntegerType())
// Special case: f-prefixed operations (AOEVT_FP exactly) reject
- // integers
- return AllowedType != AOEVT_FP;
+ // integers. Explicit AOEVT_Int or other combinations allow integers.
+ return (AllowedType & AOEVT_Int) || AllowedType != AOEVT_FP;
if (ValType->isPointerType())
return AllowedType & AOEVT_Pointer;
if (!(ValType->isFloatingType() && (AllowedType & AOEVT_FP)))
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 d525a6d86f1f1..46d9e387c0326 100644
--- a/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
+++ b/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
@@ -116,52 +116,6 @@ void test_minmax_postop(float *f32, _Float16 *f16, __bf16 *bf16, double *f64) {
*bf16 = __atomic_min_fetch(bf16, 42.1, memory_order_release);
}
-// CHECK-LABEL: define dso_local void @test_fminimum_fmaximum_postop(
-// CHECK-SAME: ptr noundef [[F32:%.*]], ptr noundef [[F16:%.*]], ptr noundef [[BF16:%.*]], ptr noundef [[F64:%.*]]) #[[ATTR0:[0-9]+]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK: [[TMP0:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
-// CHECK: store double 4.210000e+01, ptr [[DOTATOMICTMP]], align 8
-// CHECK: [[TMP1:%.*]] = load double, ptr [[DOTATOMICTMP]], align 8
-// CHECK: [[TMP2:%.*]] = atomicrmw fmaximum ptr [[TMP0]], double [[TMP1]] release, align 8
-// CHECK: [[NEWVAL:%.*]] = call double @llvm.maximum.f64(double [[TMP2]], double [[TMP1]])
-// CHECK: store double [[NEWVAL]], ptr [[ATOMIC_TEMP]], align 8
-// CHECK: [[TMP3:%.*]] = load double, ptr [[ATOMIC_TEMP]], align 8
-// CHECK: [[TMP4:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
-// CHECK: store double [[TMP3]], ptr [[TMP4]], align 8
-void test_fminimum_fmaximum_postop(float *f32, _Float16 *f16, __bf16 *bf16, double *f64) {
- *f64 = __atomic_fmaximum_fetch(f64, 42.1, memory_order_release);
- *f32 = __atomic_fmaximum_fetch(f32, 42.1, memory_order_release);
- *f16 = __atomic_fmaximum_fetch(f16, 42.1, memory_order_release);
- *bf16 = __atomic_fmaximum_fetch(bf16, 42.1, memory_order_release);
- *f64 = __atomic_fminimum_fetch(f64, 42.1, memory_order_release);
- *f32 = __atomic_fminimum_fetch(f32, 42.1, memory_order_release);
- *f16 = __atomic_fminimum_fetch(f16, 42.1, memory_order_release);
- *bf16 = __atomic_fminimum_fetch(bf16, 42.1, memory_order_release);
-}
-
-// CHECK-LABEL: define dso_local void @test_fminimumnum_fmaximumnum_postop(
-// CHECK-SAME: ptr noundef [[F32:%.*]], ptr noundef [[F16:%.*]], ptr noundef [[BF16:%.*]], ptr noundef [[F64:%.*]]) #[[ATTR0:[0-9]+]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK: [[TMP0:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
-// CHECK: store double 4.210000e+01, ptr [[DOTATOMICTMP]], align 8
-// CHECK: [[TMP1:%.*]] = load double, ptr [[DOTATOMICTMP]], align 8
-// CHECK: [[TMP2:%.*]] = atomicrmw fmaximumnum ptr [[TMP0]], double [[TMP1]] release, align 8
-// CHECK: [[NEWVAL:%.*]] = call double @llvm.maximumnum.f64(double [[TMP2]], double [[TMP1]])
-// CHECK: store double [[NEWVAL]], ptr [[ATOMIC_TEMP]], align 8
-// CHECK: [[TMP3:%.*]] = load double, ptr [[ATOMIC_TEMP]], align 8
-// CHECK: [[TMP4:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
-// CHECK: store double [[TMP3]], ptr [[TMP4]], align 8
-void test_fminimumnum_fmaximumnum_postop(float *f32, _Float16 *f16, __bf16 *bf16, double *f64) {
- *f64 = __atomic_fmaximum_num_fetch(f64, 42.1, memory_order_release);
- *f32 = __atomic_fmaximum_num_fetch(f32, 42.1, memory_order_release);
- *f16 = __atomic_fmaximum_num_fetch(f16, 42.1, memory_order_release);
- *bf16 = __atomic_fmaximum_num_fetch(bf16, 42.1, memory_order_release);
- *f64 = __atomic_fminimum_num_fetch(f64, 42.1, memory_order_release);
- *f32 = __atomic_fminimum_num_fetch(f32, 42.1, memory_order_release);
- *f16 = __atomic_fminimum_num_fetch(f16, 42.1, memory_order_release);
- *bf16 = __atomic_fminimum_num_fetch(bf16, 42.1, memory_order_release);
-}
-
// CHECK-LABEL: define dso_local void @test_fetch_variants(
// CHECK-SAME: ptr noundef [[F64:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK: [[TMP0:%.*]] = atomicrmw fminimum ptr {{%.*}}, double {{%.*}} release, align 8
diff --git a/clang/test/Sema/atomic-ops.c b/clang/test/Sema/atomic-ops.c
index d930af0d0e139..a3bc8d2ec75e7 100644
--- a/clang/test/Sema/atomic-ops.c
+++ b/clang/test/Sema/atomic-ops.c
@@ -231,15 +231,15 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci,
__c11_atomic_fetch_add(d, 1.0, memory_order_seq_cst);
__c11_atomic_fetch_add(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer, pointer or supported floating point type}}
__c11_atomic_fetch_min(i, 1, memory_order_seq_cst);
- __c11_atomic_fetch_min(p, 1, memory_order_seq_cst);
+ __c11_atomic_fetch_min(p, 1, memory_order_seq_cst); // expected-error {{must be a pointer to atomic integer or supported floating point type}}
__c11_atomic_fetch_min(f, 1.0f, memory_order_seq_cst);
__c11_atomic_fetch_min(d, 1.0, memory_order_seq_cst);
- __c11_atomic_fetch_min(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer, pointer or supported floating point type}}
+ __c11_atomic_fetch_min(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer or supported floating point type}}
__c11_atomic_fetch_max(i, 1, memory_order_seq_cst);
- __c11_atomic_fetch_max(p, 1, memory_order_seq_cst);
+ __c11_atomic_fetch_max(p, 1, memory_order_seq_cst); // expected-error {{must be a pointer to atomic integer or supported floating point type}}
__c11_atomic_fetch_max(f, 1.0f, memory_order_seq_cst);
__c11_atomic_fetch_max(d, 1.0, memory_order_seq_cst);
- __c11_atomic_fetch_max(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer, pointer or supported floating point type}}
+ __c11_atomic_fetch_max(ld, 1.0, memory_order_seq_cst); // fp80-error {{must be a pointer to atomic integer or supported floating point type}}
__atomic_fetch_add(i, 3, memory_order_seq_cst); // expected-error {{pointer to integer, pointer or supported floating point type}}
__atomic_fetch_sub(I, 3, memory_order_seq_cst);
@@ -250,7 +250,7 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci,
__atomic_fetch_min(D, 3, memory_order_seq_cst);
__atomic_fetch_max(F, 3, memory_order_seq_cst);
__atomic_fetch_max(D, 3, memory_order_seq_cst);
- __atomic_fetch_max(P, 3, memory_order_seq_cst);
+ __atomic_fetch_max(P, 3, memory_order_seq_cst); // expected-error {{must be a pointer to integer or supported floating point type}}
__atomic_fetch_max(p, 3); // expected-error {{too few arguments to function call, expected 3, have 2}}
__atomic_fetch_fminimum(F, 3, memory_order_seq_cst);
__atomic_fetch_fminimum(D, 3, memory_order_seq_cst);
@@ -704,33 +704,6 @@ void memory_checks(_Atomic(int) *Ap, int *p, int val, float *fp, float fval) {
(void)__atomic_min_fetch(p, val, memory_order_acq_rel);
(void)__atomic_min_fetch(p, val, memory_order_seq_cst);
- (void)__atomic_fmaximum_fetch(fp, fval, memory_order_relaxed);
- (void)__atomic_fmaximum_fetch(fp, fval, memory_order_acquire);
- (void)__atomic_fmaximum_fetch(fp, fval, memory_order_consume);
- (void)__atomic_fmaximum_fetch(fp, fval, memory_order_release);
- (void)__atomic_fmaximum_fetch(fp, fval, memory_order_acq_rel);
- (void)__atomic_fmaximum_fetch(fp, fval, memory_order_seq_cst);
-
- (void)__atomic_fminimum_fetch(fp, fval, memory_order_relaxed);
- (void)__atomic_fminimum_fetch(fp, fval, memory_order_acquire);
- (void)__atomic_fminimum_fetch(fp, fval, memory_order_consume);
- (void)__atomic_fminimum_fetch(fp, fval, memory_order_release);
- (void)__atomic_fminimum_fetch(fp, fval, memory_order_acq_rel);
- (void)__atomic_fminimum_fetch(fp, fval, memory_order_seq_cst);
-
- (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_relaxed);
- (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_acquire);
- (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_consume);
- (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_release);
- (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_acq_rel);
- (void)__atomic_fmaximum_num_fetch(fp, fval, memory_order_seq_cst);
-
- (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_relaxed);
- (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_acquire);
- (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_consume);
- (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_release);
- (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_acq_rel);
- (void)__atomic_fminimum_num_fetch(fp, fval, memory_order_seq_cst);
(void)__atomic_exchange_n(p, val, memory_order_relaxed);
(void)__atomic_exchange_n(p, val, memory_order_acquire);
>From 13a2cc68809320c819010001e77b9302e4e77021 Mon Sep 17 00:00:00 2001
From: Gonzalo Brito Gadeschi <gonzalob at nvidia.com>
Date: Fri, 1 May 2026 11:14:08 -0700
Subject: [PATCH 4/6] move test to cross-project-tests
---
.../builtins/Unit/atomic_fp_minmax_test.c | 397 -----------------
.../atomics/atomic_fp_minmax_test.c | 398 ++++++++++++++++++
cross-project-tests/atomics/lit.local.cfg | 2 +
3 files changed, 400 insertions(+), 397 deletions(-)
delete mode 100644 compiler-rt/test/builtins/Unit/atomic_fp_minmax_test.c
create mode 100644 cross-project-tests/atomics/atomic_fp_minmax_test.c
create mode 100644 cross-project-tests/atomics/lit.local.cfg
diff --git a/compiler-rt/test/builtins/Unit/atomic_fp_minmax_test.c b/compiler-rt/test/builtins/Unit/atomic_fp_minmax_test.c
deleted file mode 100644
index b526e216be614..0000000000000
--- a/compiler-rt/test/builtins/Unit/atomic_fp_minmax_test.c
+++ /dev/null
@@ -1,397 +0,0 @@
-// RUN: %clang_builtins %s %librt -o %t && %run %t
-// REQUIRES: native-run
-//===-- atomic_fp_minmax_test.c - Test FP atomic min/max operations -------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file tests the floating-point atomic min/max builtins, focusing on
-// IEEE 754 corner cases: NaN, +/-infinity, +/-zero.
-//
-// There are three families of operations with different semantics:
-// 1. fminimum/fmaximum: IEEE 754-2019 minimum/maximum
-// - Propagates NaN (any NaN input produces NaN output)
-// - Distinguishes -0 and +0 (minimum(-0, +0) = -0, maximum(-0, +0) = +0)
-//
-// 2. fminimumnum/fmaximumnum: IEEE 754-2019 minimumNumber/maximumNumber
-// - Propagates numbers over NaN (minimumNumber(2.0, NaN) = 2.0)
-// - Treats -0 and +0 as equivalent
-//
-// 3. minnum/maxnum (existing __atomic_min_fetch for floats): IEEE 754-2008
-// - Propagates numbers over NaN (minnum(2.0, NaN) = 2.0)
-// - Treats -0 and +0 as equivalent
-//
-//===----------------------------------------------------------------------===//
-
-#include <float.h>
-#include <math.h>
-#include <stdatomic.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#undef NDEBUG
-#include <assert.h>
-
-// Memory order for all tests
-#define MO memory_order_seq_cst
-
-// Helper to check if a float is NaN
-static inline bool is_nan_f(float x) { return x != x; }
-static inline bool is_nan_d(double x) { return x != x; }
-
-// Helper to check if two floats have the same bit pattern (for +0/-0 distinction)
-static inline bool same_bits_f(float a, float b) {
- uint32_t a_bits, b_bits;
- memcpy(&a_bits, &a, sizeof(float));
- memcpy(&b_bits, &b, sizeof(float));
- return a_bits == b_bits;
-}
-
-static inline bool same_bits_d(double a, double b) {
- uint64_t a_bits, b_bits;
- memcpy(&a_bits, &a, sizeof(double));
- memcpy(&b_bits, &b, sizeof(double));
- return a_bits == b_bits;
-}
-
-// Helper to create negative zero
-static inline float neg_zero_f(void) { return -0.0f; }
-static inline double neg_zero_d(void) { return -0.0; }
-
-//===----------------------------------------------------------------------===//
-// Test fminimum_fetch and fetch_fminimum (propagates NaN, distinguishes zeros)
-//===----------------------------------------------------------------------===//
-
-void test_fminimum_float(void) {
- printf("Testing __atomic_fminimum_fetch (float)...\n");
-
- // Test 1: Normal values
- {
- float x = 5.0f;
- float result = __atomic_fminimum_fetch(&x, 3.0f, MO);
- assert(result == 3.0f && "fminimum(5.0, 3.0) should be 3.0");
- assert(x == 3.0f && "stored value should be 3.0");
- }
-
- {
- float x = 2.0f;
- float result = __atomic_fminimum_fetch(&x, 7.0f, MO);
- assert(result == 2.0f && "fminimum(2.0, 7.0) should be 2.0");
- assert(x == 2.0f && "stored value should be 2.0");
- }
-
- // Test 2: NaN propagation - CRITICAL: fminimum propagates NaN
- {
- float x = 1.0f;
- float result = __atomic_fminimum_fetch(&x, NAN, MO);
- assert(is_nan_f(result) && "fminimum(1.0, NaN) should be NaN");
- assert(is_nan_f(x) && "stored value should be NaN");
- }
-
- {
- float x = NAN;
- float result = __atomic_fminimum_fetch(&x, 1.0f, MO);
- assert(is_nan_f(result) && "fminimum(NaN, 1.0) should be NaN");
- assert(is_nan_f(x) && "stored value should be NaN");
- }
-
- // Test 3: Zero handling - CRITICAL: fminimum distinguishes -0 and +0
- {
- float x = 0.0f;
- float result = __atomic_fminimum_fetch(&x, neg_zero_f(), MO);
- assert(same_bits_f(result, neg_zero_f()) &&
- "fminimum(+0, -0) should be -0");
- assert(same_bits_f(x, neg_zero_f()) && "stored value should be -0");
- }
-
- {
- float x = neg_zero_f();
- float result = __atomic_fminimum_fetch(&x, 0.0f, MO);
- assert(same_bits_f(result, neg_zero_f()) &&
- "fminimum(-0, +0) should be -0");
- assert(same_bits_f(x, neg_zero_f()) && "stored value should be -0");
- }
-
- // Test 4: Infinity
- {
- float x = INFINITY;
- float result = __atomic_fminimum_fetch(&x, 1.0f, MO);
- assert(result == 1.0f && "fminimum(+inf, 1.0) should be 1.0");
- }
-
- {
- float x = -INFINITY;
- float result = __atomic_fminimum_fetch(&x, 1.0f, MO);
- assert(result == -INFINITY && "fminimum(-inf, 1.0) should be -inf");
- }
-
- // Test 5: fetch variant (returns old value)
- {
- float x = 5.0f;
- float old = __atomic_fetch_fminimum(&x, 3.0f, MO);
- assert(old == 5.0f && "fetch_fminimum should return old value");
- assert(x == 3.0f && "stored value should be 3.0");
- }
-
- printf(" PASSED\n");
-}
-
-void test_fmaximum_float(void) {
- printf("Testing __atomic_fmaximum_fetch (float)...\n");
-
- // Test 1: Normal values
- {
- float x = 5.0f;
- float result = __atomic_fmaximum_fetch(&x, 3.0f, MO);
- assert(result == 5.0f && "fmaximum(5.0, 3.0) should be 5.0");
- }
-
- // Test 2: NaN propagation
- {
- float x = 1.0f;
- float result = __atomic_fmaximum_fetch(&x, NAN, MO);
- assert(is_nan_f(result) && "fmaximum(1.0, NaN) should be NaN");
- }
-
- // Test 3: Zero handling - fmaximum(+0, -0) should be +0
- {
- float x = 0.0f;
- float result = __atomic_fmaximum_fetch(&x, neg_zero_f(), MO);
- assert(same_bits_f(result, 0.0f) && "fmaximum(+0, -0) should be +0");
- }
-
- {
- float x = neg_zero_f();
- float result = __atomic_fmaximum_fetch(&x, 0.0f, MO);
- assert(same_bits_f(result, 0.0f) && "fmaximum(-0, +0) should be +0");
- }
-
- // Test 4: Infinity
- {
- float x = INFINITY;
- float result = __atomic_fmaximum_fetch(&x, 1.0f, MO);
- assert(result == INFINITY && "fmaximum(+inf, 1.0) should be +inf");
- }
-
- printf(" PASSED\n");
-}
-
-//===----------------------------------------------------------------------===//
-// Test fminimumnum_fetch (propagates numbers, treats zeros as equivalent)
-//===----------------------------------------------------------------------===//
-
-void test_fminimum_num_float(void) {
- printf("Testing __atomic_fminimum_num_fetch (float)...\n");
-
- // Test 1: Normal values
- {
- float x = 5.0f;
- float result = __atomic_fminimum_num_fetch(&x, 3.0f, MO);
- assert(result == 3.0f && "fminimumnum(5.0, 3.0) should be 3.0");
- }
-
- // Test 2: NaN handling - CRITICAL: fminimumnum propagates NUMBER over NaN
- {
- float x = 1.0f;
- float result = __atomic_fminimum_num_fetch(&x, NAN, MO);
- assert(result == 1.0f &&
- "fminimumnum(1.0, NaN) should be 1.0 (number over NaN)");
- assert(x == 1.0f && "stored value should be 1.0");
- }
-
- {
- float x = NAN;
- float result = __atomic_fminimum_num_fetch(&x, 2.0f, MO);
- assert(result == 2.0f &&
- "fminimumnum(NaN, 2.0) should be 2.0 (number over NaN)");
- assert(x == 2.0f && "stored value should be 2.0");
- }
-
- {
- float x = NAN;
- float result = __atomic_fminimum_num_fetch(&x, NAN, MO);
- assert(is_nan_f(result) && "fminimumnum(NaN, NaN) should be NaN");
- }
-
- // Test 3: Zero handling - fminimumnum treats +0 and -0 as equivalent
- // The result can be either, but should pick the minimum value
- {
- float x = 0.0f;
- float result = __atomic_fminimum_num_fetch(&x, neg_zero_f(), MO);
- // Result should be a zero (either +0 or -0 is acceptable per IEEE 754)
- assert(result == 0.0f && "fminimumnum(+0, -0) should be zero");
- }
-
- // Test 4: Infinity
- {
- float x = INFINITY;
- float result = __atomic_fminimum_num_fetch(&x, 1.0f, MO);
- assert(result == 1.0f && "fminimumnum(+inf, 1.0) should be 1.0");
- }
-
- // Test 5: fetch variant
- {
- float x = NAN;
- float old = __atomic_fetch_fminimum_num(&x, 3.0f, MO);
- assert(is_nan_f(old) && "fetch_fminimum_num should return old value (NaN)");
- assert(x == 3.0f && "stored value should be 3.0");
- }
-
- printf(" PASSED\n");
-}
-
-void test_fmaximum_num_float(void) {
- printf("Testing __atomic_fmaximum_num_fetch (float)...\n");
-
- // Test 1: Normal values
- {
- float x = 5.0f;
- float result = __atomic_fmaximum_num_fetch(&x, 3.0f, MO);
- assert(result == 5.0f && "fmaximumnum(5.0, 3.0) should be 5.0");
- }
-
- // Test 2: NaN handling - propagates number over NaN
- {
- float x = 1.0f;
- float result = __atomic_fmaximum_num_fetch(&x, NAN, MO);
- assert(result == 1.0f && "fmaximumnum(1.0, NaN) should be 1.0");
- }
-
- {
- float x = NAN;
- float result = __atomic_fmaximum_num_fetch(&x, 2.0f, MO);
- assert(result == 2.0f && "fmaximumnum(NaN, 2.0) should be 2.0");
- }
-
- // Test 3: Zero handling - treats +0 and -0 as equivalent
- {
- float x = 0.0f;
- float result = __atomic_fmaximum_num_fetch(&x, neg_zero_f(), MO);
- assert(result == 0.0f && "fmaximumnum(+0, -0) should be zero");
- }
-
- printf(" PASSED\n");
-}
-
-//===----------------------------------------------------------------------===//
-// Double precision tests
-//===----------------------------------------------------------------------===//
-
-void test_fminimum_double(void) {
- printf("Testing __atomic_fminimum_fetch (double)...\n");
-
- // Test NaN propagation
- {
- double x = 1.0;
- double result = __atomic_fminimum_fetch(&x, NAN, MO);
- assert(is_nan_d(result) && "fminimum(1.0, NaN) should be NaN (double)");
- }
-
- // Test zero distinction
- {
- double x = 0.0;
- double result = __atomic_fminimum_fetch(&x, neg_zero_d(), MO);
- assert(same_bits_d(result, neg_zero_d()) &&
- "fminimum(+0, -0) should be -0 (double)");
- }
-
- // Test normal values
- {
- double x = 3.14;
- double result = __atomic_fminimum_fetch(&x, 2.71, MO);
- assert(result == 2.71 && "fminimum(3.14, 2.71) should be 2.71");
- }
-
- printf(" PASSED\n");
-}
-
-void test_fmaximum_double(void) {
- printf("Testing __atomic_fmaximum_fetch (double)...\n");
-
- // Test NaN propagation
- {
- double x = 1.0;
- double result = __atomic_fmaximum_fetch(&x, NAN, MO);
- assert(is_nan_d(result) && "fmaximum(1.0, NaN) should be NaN (double)");
- }
-
- // Test zero distinction - fmaximum(+0, -0) = +0
- {
- double x = 0.0;
- double result = __atomic_fmaximum_fetch(&x, neg_zero_d(), MO);
- assert(same_bits_d(result, 0.0) &&
- "fmaximum(+0, -0) should be +0 (double)");
- }
-
- printf(" PASSED\n");
-}
-
-void test_fminimum_num_double(void) {
- printf("Testing __atomic_fminimum_num_fetch (double)...\n");
-
- // Test number over NaN
- {
- double x = NAN;
- double result = __atomic_fminimum_num_fetch(&x, 2.5, MO);
- assert(result == 2.5 && "fminimumnum(NaN, 2.5) should be 2.5 (double)");
- }
-
- // Test normal values
- {
- double x = 10.5;
- double result = __atomic_fminimum_num_fetch(&x, 8.3, MO);
- assert(result == 8.3 && "fminimumnum(10.5, 8.3) should be 8.3");
- }
-
- printf(" PASSED\n");
-}
-
-void test_fmaximum_num_double(void) {
- printf("Testing __atomic_fmaximum_num_fetch (double)...\n");
-
- // Test number over NaN
- {
- double x = NAN;
- double result = __atomic_fmaximum_num_fetch(&x, 2.5, MO);
- assert(result == 2.5 && "fmaximumnum(NaN, 2.5) should be 2.5 (double)");
- }
-
- printf(" PASSED\n");
-}
-
-//===----------------------------------------------------------------------===//
-// Main test runner
-//===----------------------------------------------------------------------===//
-
-int main(void) {
- printf("\n");
- printf("=============================================================\n");
- printf("Atomic Floating-Point Min/Max Tests\n");
- printf("=============================================================\n");
- printf("\n");
-
- printf("--- fminimum/fmaximum (propagate NaN, distinguish zeros) ---\n");
- test_fminimum_float();
- test_fmaximum_float();
- test_fminimum_double();
- test_fmaximum_double();
-
- printf("\n--- fminimumnum/fmaximumnum (prefer numbers, treat zeros equal) "
- "---\n");
- test_fminimum_num_float();
- test_fmaximum_num_float();
- test_fminimum_num_double();
- test_fmaximum_num_double();
-
- printf("\n");
- printf("=============================================================\n");
- printf("All tests PASSED!\n");
- printf("=============================================================\n");
- printf("\n");
-
- return 0;
-}
diff --git a/cross-project-tests/atomics/atomic_fp_minmax_test.c b/cross-project-tests/atomics/atomic_fp_minmax_test.c
new file mode 100644
index 0000000000000..0efee31de0f13
--- /dev/null
+++ b/cross-project-tests/atomics/atomic_fp_minmax_test.c
@@ -0,0 +1,398 @@
+// RUN: %clang %s -O2 -o %t && %run %t
+//===-- atomic_fp_minmax_test.c - Test FP atomic min/max operations -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file tests the floating-point atomic fetch-before min/max builtins,
+// focusing on IEEE 754 corner cases: NaN, +/-infinity, +/-zero.
+//
+// Two families of operations:
+// 1. fetch_fminimum/fetch_fmaximum: IEEE 754-2019 minimum/maximum
+// - Propagates NaN (any NaN input produces NaN output)
+// - Distinguishes -0 and +0 (minimum(-0, +0) = -0, maximum(-0, +0) = +0)
+//
+// 2. fetch_fminimum_num/fetch_fmaximum_num: IEEE 754-2019 minimumNumber/maximumNumber
+// - Propagates numbers over NaN (minimumNumber(2.0, NaN) = 2.0)
+// - Treats -0 and +0 as equivalent
+//
+// All builtins return the old (pre-operation) value.
+//
+//===----------------------------------------------------------------------===//
+
+#include <float.h>
+#include <math.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#undef NDEBUG
+#include <assert.h>
+
+// Memory order for all tests
+#define MO memory_order_seq_cst
+
+// Helper to check if a float is NaN
+static inline bool is_nan_f(float x) { return x != x; }
+static inline bool is_nan_d(double x) { return x != x; }
+
+// Helper to check if two floats have the same bit pattern (for +0/-0
+// distinction)
+static inline bool same_bits_f(float a, float b) {
+ uint32_t a_bits, b_bits;
+ memcpy(&a_bits, &a, sizeof(float));
+ memcpy(&b_bits, &b, sizeof(float));
+ return a_bits == b_bits;
+}
+
+static inline bool same_bits_d(double a, double b) {
+ uint64_t a_bits, b_bits;
+ memcpy(&a_bits, &a, sizeof(double));
+ memcpy(&b_bits, &b, sizeof(double));
+ return a_bits == b_bits;
+}
+
+// Helper to create negative zero
+static inline float neg_zero_f(void) { return -0.0f; }
+static inline double neg_zero_d(void) { return -0.0; }
+
+//===----------------------------------------------------------------------===//
+// fetch_fminimum / fetch_fmaximum (propagates NaN, distinguishes zeros)
+//===----------------------------------------------------------------------===//
+
+void test_fetch_fminimum_float(void) {
+ printf("Testing __atomic_fetch_fminimum (float)...\n");
+
+ // Returns old value, stores minimum
+ {
+ float x = 5.0f;
+ float old = __atomic_fetch_fminimum(&x, 3.0f, MO);
+ assert(old == 5.0f && "fetch_fminimum should return old value");
+ assert(x == 3.0f && "stored value should be 3.0");
+ }
+
+ {
+ float x = 2.0f;
+ float old = __atomic_fetch_fminimum(&x, 7.0f, MO);
+ assert(old == 2.0f && "fetch_fminimum should return old value");
+ assert(x == 2.0f && "stored value should be 2.0 (unchanged)");
+ }
+
+ // NaN propagation: fminimum(1.0, NaN) = NaN
+ {
+ float x = 1.0f;
+ float old = __atomic_fetch_fminimum(&x, NAN, MO);
+ assert(old == 1.0f && "fetch_fminimum should return old value");
+ assert(is_nan_f(x) && "stored value should be NaN");
+ }
+
+ // NaN propagation: fminimum(NaN, 1.0) = NaN
+ {
+ float x = NAN;
+ float old = __atomic_fetch_fminimum(&x, 1.0f, MO);
+ assert(is_nan_f(old) && "fetch_fminimum should return old NaN");
+ assert(is_nan_f(x) && "stored value should be NaN");
+ }
+
+ // Zero distinction: fminimum(+0, -0) = -0
+ {
+ float x = 0.0f;
+ float old = __atomic_fetch_fminimum(&x, neg_zero_f(), MO);
+ assert(same_bits_f(old, 0.0f) && "fetch_fminimum should return old +0");
+ assert(same_bits_f(x, neg_zero_f()) && "stored value should be -0");
+ }
+
+ // Zero distinction: fminimum(-0, +0) = -0
+ {
+ float x = neg_zero_f();
+ float old = __atomic_fetch_fminimum(&x, 0.0f, MO);
+ assert(same_bits_f(old, neg_zero_f()) && "fetch_fminimum should return old -0");
+ assert(same_bits_f(x, neg_zero_f()) && "stored value should be -0");
+ }
+
+ // Infinity
+ {
+ float x = INFINITY;
+ float old = __atomic_fetch_fminimum(&x, 1.0f, MO);
+ assert(old == INFINITY && "fetch_fminimum should return old +inf");
+ assert(x == 1.0f && "stored value should be 1.0");
+ }
+
+ {
+ float x = -INFINITY;
+ float old = __atomic_fetch_fminimum(&x, 1.0f, MO);
+ assert(old == -INFINITY && "fetch_fminimum should return old -inf");
+ assert(x == -INFINITY && "stored value should be -inf (unchanged)");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fetch_fmaximum_float(void) {
+ printf("Testing __atomic_fetch_fmaximum (float)...\n");
+
+ // Returns old value, stores maximum
+ {
+ float x = 5.0f;
+ float old = __atomic_fetch_fmaximum(&x, 3.0f, MO);
+ assert(old == 5.0f && "fetch_fmaximum should return old value");
+ assert(x == 5.0f && "stored value should be 5.0 (unchanged)");
+ }
+
+ {
+ float x = 2.0f;
+ float old = __atomic_fetch_fmaximum(&x, 7.0f, MO);
+ assert(old == 2.0f && "fetch_fmaximum should return old value");
+ assert(x == 7.0f && "stored value should be 7.0");
+ }
+
+ // NaN propagation: fmaximum(1.0, NaN) = NaN
+ {
+ float x = 1.0f;
+ float old = __atomic_fetch_fmaximum(&x, NAN, MO);
+ assert(old == 1.0f && "fetch_fmaximum should return old value");
+ assert(is_nan_f(x) && "stored value should be NaN");
+ }
+
+ // Zero distinction: fmaximum(+0, -0) = +0
+ {
+ float x = 0.0f;
+ float old = __atomic_fetch_fmaximum(&x, neg_zero_f(), MO);
+ assert(same_bits_f(old, 0.0f) && "fetch_fmaximum should return old +0");
+ assert(same_bits_f(x, 0.0f) && "stored value should be +0");
+ }
+
+ // Zero distinction: fmaximum(-0, +0) = +0
+ {
+ float x = neg_zero_f();
+ float old = __atomic_fetch_fmaximum(&x, 0.0f, MO);
+ assert(same_bits_f(old, neg_zero_f()) && "fetch_fmaximum should return old -0");
+ assert(same_bits_f(x, 0.0f) && "stored value should be +0");
+ }
+
+ // Infinity
+ {
+ float x = INFINITY;
+ float old = __atomic_fetch_fmaximum(&x, 1.0f, MO);
+ assert(old == INFINITY && "fetch_fmaximum should return old +inf");
+ assert(x == INFINITY && "stored value should be +inf (unchanged)");
+ }
+
+ printf(" PASSED\n");
+}
+
+//===----------------------------------------------------------------------===//
+// fetch_fminimum_num / fetch_fmaximum_num (propagates numbers, zeros equal)
+//===----------------------------------------------------------------------===//
+
+void test_fetch_fminimum_num_float(void) {
+ printf("Testing __atomic_fetch_fminimum_num (float)...\n");
+
+ // Returns old value, stores minimumnum
+ {
+ float x = 5.0f;
+ float old = __atomic_fetch_fminimum_num(&x, 3.0f, MO);
+ assert(old == 5.0f && "fetch_fminimum_num should return old value");
+ assert(x == 3.0f && "stored value should be 3.0");
+ }
+
+ // Number over NaN: fminimumnum(1.0, NaN) = 1.0
+ {
+ float x = 1.0f;
+ float old = __atomic_fetch_fminimum_num(&x, NAN, MO);
+ assert(old == 1.0f && "fetch_fminimum_num should return old value");
+ assert(x == 1.0f && "stored value should be 1.0 (number over NaN)");
+ }
+
+ // Number over NaN: fminimumnum(NaN, 2.0) = 2.0
+ {
+ float x = NAN;
+ float old = __atomic_fetch_fminimum_num(&x, 2.0f, MO);
+ assert(is_nan_f(old) && "fetch_fminimum_num should return old NaN");
+ assert(x == 2.0f && "stored value should be 2.0 (number over NaN)");
+ }
+
+ // NaN + NaN = NaN
+ {
+ float x = NAN;
+ float old = __atomic_fetch_fminimum_num(&x, NAN, MO);
+ assert(is_nan_f(old) && "fetch_fminimum_num should return old NaN");
+ assert(is_nan_f(x) && "stored value should be NaN");
+ }
+
+ // Infinity
+ {
+ float x = INFINITY;
+ float old = __atomic_fetch_fminimum_num(&x, 1.0f, MO);
+ assert(old == INFINITY && "fetch_fminimum_num should return old +inf");
+ assert(x == 1.0f && "stored value should be 1.0");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fetch_fmaximum_num_float(void) {
+ printf("Testing __atomic_fetch_fmaximum_num (float)...\n");
+
+ // Returns old value, stores maximumnum
+ {
+ float x = 5.0f;
+ float old = __atomic_fetch_fmaximum_num(&x, 3.0f, MO);
+ assert(old == 5.0f && "fetch_fmaximum_num should return old value");
+ assert(x == 5.0f && "stored value should be 5.0 (unchanged)");
+ }
+
+ // Number over NaN: fmaximumnum(1.0, NaN) = 1.0
+ {
+ float x = 1.0f;
+ float old = __atomic_fetch_fmaximum_num(&x, NAN, MO);
+ assert(old == 1.0f && "fetch_fmaximum_num should return old value");
+ assert(x == 1.0f && "stored value should be 1.0 (number over NaN)");
+ }
+
+ // Number over NaN: fmaximumnum(NaN, 2.0) = 2.0
+ {
+ float x = NAN;
+ float old = __atomic_fetch_fmaximum_num(&x, 2.0f, MO);
+ assert(is_nan_f(old) && "fetch_fmaximum_num should return old NaN");
+ assert(x == 2.0f && "stored value should be 2.0 (number over NaN)");
+ }
+
+ printf(" PASSED\n");
+}
+
+//===----------------------------------------------------------------------===//
+// Double precision tests
+//===----------------------------------------------------------------------===//
+
+void test_fetch_fminimum_double(void) {
+ printf("Testing __atomic_fetch_fminimum (double)...\n");
+
+ // NaN propagation
+ {
+ double x = 1.0;
+ double old = __atomic_fetch_fminimum(&x, NAN, MO);
+ assert(old == 1.0 && "fetch_fminimum should return old value");
+ assert(is_nan_d(x) && "stored value should be NaN");
+ }
+
+ // Zero distinction: fminimum(+0, -0) = -0
+ {
+ double x = 0.0;
+ double old = __atomic_fetch_fminimum(&x, neg_zero_d(), MO);
+ assert(same_bits_d(old, 0.0) && "fetch_fminimum should return old +0");
+ assert(same_bits_d(x, neg_zero_d()) && "stored value should be -0");
+ }
+
+ // Normal values
+ {
+ double x = 3.14;
+ double old = __atomic_fetch_fminimum(&x, 2.71, MO);
+ assert(old == 3.14 && "fetch_fminimum should return old value");
+ assert(x == 2.71 && "stored value should be 2.71");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fetch_fmaximum_double(void) {
+ printf("Testing __atomic_fetch_fmaximum (double)...\n");
+
+ // NaN propagation
+ {
+ double x = 1.0;
+ double old = __atomic_fetch_fmaximum(&x, NAN, MO);
+ assert(old == 1.0 && "fetch_fmaximum should return old value");
+ assert(is_nan_d(x) && "stored value should be NaN");
+ }
+
+ // Zero distinction: fmaximum(-0, +0) = +0
+ {
+ double x = neg_zero_d();
+ double old = __atomic_fetch_fmaximum(&x, 0.0, MO);
+ assert(same_bits_d(old, neg_zero_d()) && "fetch_fmaximum should return old -0");
+ assert(same_bits_d(x, 0.0) && "stored value should be +0");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fetch_fminimum_num_double(void) {
+ printf("Testing __atomic_fetch_fminimum_num (double)...\n");
+
+ // Number over NaN
+ {
+ double x = NAN;
+ double old = __atomic_fetch_fminimum_num(&x, 2.5, MO);
+ assert(is_nan_d(old) && "fetch_fminimum_num should return old NaN");
+ assert(x == 2.5 && "stored value should be 2.5");
+ }
+
+ // Normal values
+ {
+ double x = 10.5;
+ double old = __atomic_fetch_fminimum_num(&x, 8.3, MO);
+ assert(old == 10.5 && "fetch_fminimum_num should return old value");
+ assert(x == 8.3 && "stored value should be 8.3");
+ }
+
+ printf(" PASSED\n");
+}
+
+void test_fetch_fmaximum_num_double(void) {
+ printf("Testing __atomic_fetch_fmaximum_num (double)...\n");
+
+ // Number over NaN
+ {
+ double x = NAN;
+ double old = __atomic_fetch_fmaximum_num(&x, 2.5, MO);
+ assert(is_nan_d(old) && "fetch_fmaximum_num should return old NaN");
+ assert(x == 2.5 && "stored value should be 2.5");
+ }
+
+ // Normal values
+ {
+ double x = 1.0;
+ double old = __atomic_fetch_fmaximum_num(&x, 5.5, MO);
+ assert(old == 1.0 && "fetch_fmaximum_num should return old value");
+ assert(x == 5.5 && "stored value should be 5.5");
+ }
+
+ printf(" PASSED\n");
+}
+
+//===----------------------------------------------------------------------===//
+// Main test runner
+//===----------------------------------------------------------------------===//
+
+int main(void) {
+ printf("\n");
+ printf("=============================================================\n");
+ printf("Atomic Floating-Point Min/Max Tests\n");
+ printf("=============================================================\n");
+ printf("\n");
+
+ printf("--- fetch_fminimum/fetch_fmaximum (propagate NaN, distinguish zeros) ---\n");
+ test_fetch_fminimum_float();
+ test_fetch_fmaximum_float();
+ test_fetch_fminimum_double();
+ test_fetch_fmaximum_double();
+
+ printf("\n--- fetch_fminimum_num/fetch_fmaximum_num (prefer numbers, treat zeros equal) ---\n");
+ test_fetch_fminimum_num_float();
+ test_fetch_fmaximum_num_float();
+ test_fetch_fminimum_num_double();
+ test_fetch_fmaximum_num_double();
+
+ printf("\n");
+ printf("=============================================================\n");
+ printf("All tests PASSED!\n");
+ printf("=============================================================\n");
+ printf("\n");
+
+ return 0;
+}
diff --git a/cross-project-tests/atomics/lit.local.cfg b/cross-project-tests/atomics/lit.local.cfg
new file mode 100644
index 0000000000000..530f4c01646ff
--- /dev/null
+++ b/cross-project-tests/atomics/lit.local.cfg
@@ -0,0 +1,2 @@
+if "clang" not in config.available_features:
+ config.unsupported = True
>From 2abd3c1beef3bfed5565e3e42004e924f6fa1dcd Mon Sep 17 00:00:00 2001
From: Gonzalo Brito Gadeschi <gonzalob at nvidia.com>
Date: Fri, 1 May 2026 11:31:18 -0700
Subject: [PATCH 5/6] more clean up
---
clang/lib/CodeGen/CGAtomic.cpp | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 3e521ef8d7643..6f883212b2cd3 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -530,16 +530,11 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
const bool IsFP = OldVal->getType()->isFloatingPointTy();
if (IsFP) {
- llvm::Intrinsic::ID IID;
- if (Op == AtomicExpr::AO__atomic_max_fetch ||
- Op == AtomicExpr::AO__scoped_atomic_max_fetch)
- IID = llvm::Intrinsic::maxnum;
- else if (Op == AtomicExpr::AO__atomic_min_fetch ||
- Op == AtomicExpr::AO__scoped_atomic_min_fetch)
- IID = llvm::Intrinsic::minnum;
- else
- llvm_unreachable("Unexpected atomic FP min/max operation");
-
+ 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, llvm::FMFSource(),
"newval");
}
>From d84c48d53f2d6b1af772ed8bafbab4150da736a8 Mon Sep 17 00:00:00 2001
From: Gonzalo Brito Gadeschi <gonzalob at nvidia.com>
Date: Fri, 1 May 2026 11:43:19 -0700
Subject: [PATCH 6/6] formatting
---
clang/lib/CodeGen/CGAtomic.cpp | 9 ++++-----
.../AArch64/atomic-ops-float-check-minmax.c | 4 ++++
.../atomics/atomic_fp_minmax_test.c | 18 ++++++++++++------
3 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/clang/lib/CodeGen/CGAtomic.cpp b/clang/lib/CodeGen/CGAtomic.cpp
index 6f883212b2cd3..25fc7c1d32800 100644
--- a/clang/lib/CodeGen/CGAtomic.cpp
+++ b/clang/lib/CodeGen/CGAtomic.cpp
@@ -530,11 +530,10 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
const bool IsFP = OldVal->getType()->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;
+ 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, llvm::FMFSource(),
"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 46d9e387c0326..7d70577eb6a25 100644
--- a/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
+++ b/clang/test/CodeGen/AArch64/atomic-ops-float-check-minmax.c
@@ -122,6 +122,10 @@ void test_minmax_postop(float *f32, _Float16 *f16, __bf16 *bf16, double *f64) {
// CHECK: [[TMP1:%.*]] = atomicrmw fmaximum ptr {{%.*}}, double {{%.*}} release, align 8
// CHECK: [[TMP2:%.*]] = atomicrmw fminimumnum ptr {{%.*}}, double {{%.*}} release, align 8
// CHECK: [[TMP3:%.*]] = atomicrmw fmaximumnum ptr {{%.*}}, double {{%.*}} release, align 8
+// CHECK-NOT: call {{.*}} @llvm.minimum
+// CHECK-NOT: call {{.*}} @llvm.maximum
+// CHECK-NOT: call {{.*}} @llvm.minimumnum
+// CHECK-NOT: call {{.*}} @llvm.maximumnum
void test_fetch_variants(double *f64) {
double old1 = __atomic_fetch_fminimum(f64, 42.1, memory_order_release);
double old2 = __atomic_fetch_fmaximum(f64, 42.1, memory_order_release);
diff --git a/cross-project-tests/atomics/atomic_fp_minmax_test.c b/cross-project-tests/atomics/atomic_fp_minmax_test.c
index 0efee31de0f13..7eb24dc493e43 100644
--- a/cross-project-tests/atomics/atomic_fp_minmax_test.c
+++ b/cross-project-tests/atomics/atomic_fp_minmax_test.c
@@ -15,7 +15,8 @@
// - Propagates NaN (any NaN input produces NaN output)
// - Distinguishes -0 and +0 (minimum(-0, +0) = -0, maximum(-0, +0) = +0)
//
-// 2. fetch_fminimum_num/fetch_fmaximum_num: IEEE 754-2019 minimumNumber/maximumNumber
+// 2. fetch_fminimum_num/fetch_fmaximum_num: IEEE 754-2019
+// minimumNumber/maximumNumber
// - Propagates numbers over NaN (minimumNumber(2.0, NaN) = 2.0)
// - Treats -0 and +0 as equivalent
//
@@ -110,7 +111,8 @@ void test_fetch_fminimum_float(void) {
{
float x = neg_zero_f();
float old = __atomic_fetch_fminimum(&x, 0.0f, MO);
- assert(same_bits_f(old, neg_zero_f()) && "fetch_fminimum should return old -0");
+ assert(same_bits_f(old, neg_zero_f()) &&
+ "fetch_fminimum should return old -0");
assert(same_bits_f(x, neg_zero_f()) && "stored value should be -0");
}
@@ -170,7 +172,8 @@ void test_fetch_fmaximum_float(void) {
{
float x = neg_zero_f();
float old = __atomic_fetch_fmaximum(&x, 0.0f, MO);
- assert(same_bits_f(old, neg_zero_f()) && "fetch_fmaximum should return old -0");
+ assert(same_bits_f(old, neg_zero_f()) &&
+ "fetch_fmaximum should return old -0");
assert(same_bits_f(x, 0.0f) && "stored value should be +0");
}
@@ -314,7 +317,8 @@ void test_fetch_fmaximum_double(void) {
{
double x = neg_zero_d();
double old = __atomic_fetch_fmaximum(&x, 0.0, MO);
- assert(same_bits_d(old, neg_zero_d()) && "fetch_fmaximum should return old -0");
+ assert(same_bits_d(old, neg_zero_d()) &&
+ "fetch_fmaximum should return old -0");
assert(same_bits_d(x, 0.0) && "stored value should be +0");
}
@@ -376,13 +380,15 @@ int main(void) {
printf("=============================================================\n");
printf("\n");
- printf("--- fetch_fminimum/fetch_fmaximum (propagate NaN, distinguish zeros) ---\n");
+ printf("--- fetch_fminimum/fetch_fmaximum (propagate NaN, distinguish zeros) "
+ "---\n");
test_fetch_fminimum_float();
test_fetch_fmaximum_float();
test_fetch_fminimum_double();
test_fetch_fmaximum_double();
- printf("\n--- fetch_fminimum_num/fetch_fmaximum_num (prefer numbers, treat zeros equal) ---\n");
+ printf("\n--- fetch_fminimum_num/fetch_fmaximum_num (prefer numbers, treat "
+ "zeros equal) ---\n");
test_fetch_fminimum_num_float();
test_fetch_fmaximum_num_float();
test_fetch_fminimum_num_double();
More information about the cfe-commits
mailing list