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 <[email protected]> Date: Tue, 17 Mar 2026 15:24:47 -0700 Subject: [PATCH 1/2] [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 <[email protected]> Date: Wed, 25 Mar 2026 15:18:54 +0000 Subject: [PATCH 2/2] 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: _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
