Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/mono/mono/mini/intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
11 changes: 7 additions & 4 deletions src/mono/mono/mini/llvm-intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 ())
Expand Down
34 changes: 11 additions & 23 deletions src/mono/mono/mini/mini-llvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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<TSelf> 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;
}

Expand Down
Loading