diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 7e026e0e68bd98..17a9c14f81b464 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -297,8 +297,8 @@ llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign // // * MinNumber / MaxNumber: IEEE 754-2019 minimumNumber / maximumNumber // (NaN-suppressing and sign-of-zero aware, treating -0 as less than +0). - // Lowered in mini-llvm.c by composing llvm.minimum / llvm.maximum with an - // explicit NaN fixup; see the OP_FMINNUM case there and llvm-intrinsics.h. + // Lower to llvm.minimumnum / llvm.maximumnum, which on AArch64 maps to a + // single fminnm / fmaxnm instruction; see mini-llvm.c and llvm-intrinsics.h. // * Abs: BCL forwarder to MathF.Abs / Math.Abs. Today this usually inlines // into the Math/MathF recognition above, but adding direct recognition // keeps the lowering working even if the JIT inliner declines. diff --git a/src/mono/mono/mini/llvm-intrinsics.h b/src/mono/mono/mini/llvm-intrinsics.h index 848ef32a64bf82..ce09bacbd80b4a 100644 --- a/src/mono/mono/mini/llvm-intrinsics.h +++ b/src/mono/mono/mini/llvm-intrinsics.h @@ -98,11 +98,14 @@ INTRINS_OVR(TRUNCF, trunc, Generic, LLVMFloatType ()) INTRINS_OVR(COPYSIGN, copysign, Generic, LLVMDoubleType ()) INTRINS_OVR(COPYSIGNF, copysign, Generic, LLVMFloatType ()) /* - * `float.MinNumber` / `double.MinNumber` (and the Max variants) are lowered in - * mini-llvm.c by composing llvm.minimum/maximum (above) with an explicit NaN - * fixup; see the OP_FMINNUM case there for why we don't use llvm.minnum/maxnum - * or llvm.minimumnum/maximumnum directly. + * IEEE 754-2019 minimumNumber/maximumNumber (NaN-suppressing and sign-of-zero + * aware). Use llvm.minimum/maximum (above) for the NaN-propagating + * Math.Min/Math.Max instead. */ +INTRINS_OVR(MINIMUMNUM, minimumnum, Generic, LLVMDoubleType ()) +INTRINS_OVR(MINIMUMNUMF, minimumnum, Generic, LLVMFloatType ()) +INTRINS_OVR(MAXIMUMNUM, maximumnum, Generic, LLVMDoubleType ()) +INTRINS_OVR(MAXIMUMNUMF, maximumnum, Generic, LLVMFloatType ()) INTRINS_OVR(EXPECT_I8, expect, Generic, LLVMInt8Type ()) INTRINS_OVR(EXPECT_I1, expect, Generic, LLVMInt1Type ()) INTRINS_OVR(CTPOP_I32, ctpop, Generic, LLVMInt32Type ()) diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index f3166c3d2881f1..7f97befdc31241 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -7628,32 +7628,20 @@ MONO_RESTORE_WARNING case OP_RMAXNUM: { /* * IEEE 754-2019 minimumNumber/maximumNumber (NaN-suppressing and - * sign-of-zero aware), as specified by `float.MinNumber` / - * `double.MinNumber` (and the Max variants, surfaced via - * INumber on the primitive Single/Double/Half types). - * - * We compose this from llvm.minimum/maximum (sign-of-zero aware but - * NaN-propagating) plus an explicit NaN fixup, rather than lowering - * directly to an intrinsic: llvm.minnum/maxnum leave the sign of zero - * unspecified (minnum(+0, -0) may return +0 on x86, see dotnet/runtime - * #131130) and llvm.minimumnum/maximumnum are miscompiled by the x86 - * backend in the LLVM version we build against. When exactly one operand - * is NaN we return the other; when both are NaN the NaN flows through. + * sign-of-zero aware). Maps directly to llvm.minimumnum/maximumnum. */ gboolean is_r4 = ins->opcode == OP_RMINNUM || ins->opcode == OP_RMAXNUM; - gboolean is_max = ins->opcode == OP_FMAXNUM || ins->opcode == OP_RMAXNUM; LLVMTypeRef t = is_r4 ? LLVMFloatType () : LLVMDoubleType (); - LLVMValueRef l = convert (ctx, lhs, t); - LLVMValueRef r = convert (ctx, rhs, t); - LLVMValueRef args [2] = { l, r }; - IntrinsicId iid = is_max ? (is_r4 ? INTRINS_MAXIMUMF : INTRINS_MAXIMUM) - : (is_r4 ? INTRINS_MINIMUMF : INTRINS_MINIMUM); - LLVMValueRef result = call_intrins (ctx, iid, args, ""); - LLVMValueRef l_nan = LLVMBuildFCmp (builder, LLVMRealUNO, l, l, ""); - LLVMValueRef r_nan = LLVMBuildFCmp (builder, LLVMRealUNO, r, r, ""); - result = LLVMBuildSelect (builder, r_nan, l, result, ""); - result = LLVMBuildSelect (builder, l_nan, r, result, dname); - values [ins->dreg] = result; + LLVMValueRef args [2] = { convert (ctx, lhs, t), convert (ctx, rhs, t) }; + IntrinsicId iid; + switch (ins->opcode) { + case OP_FMAXNUM: iid = INTRINS_MAXIMUMNUM; break; + case OP_FMINNUM: iid = INTRINS_MINIMUMNUM; break; + case OP_RMAXNUM: iid = INTRINS_MAXIMUMNUMF; break; + case OP_RMINNUM: iid = INTRINS_MINIMUMNUMF; break; + default: g_assert_not_reached (); break; + } + values [ins->dreg] = call_intrins (ctx, iid, args, dname); break; }