Skip to content
Merged
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
46 changes: 45 additions & 1 deletion src/mono/mono/mini/intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,18 @@ llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
if (in_corlib && !strcmp (m_class_get_name (cmethod->klass), "MathF") && cfg->r4fp) {
// (float)
if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R4) {
if (!strcmp (cmethod->name, "Ceiling")) {
if (!strcmp (cmethod->name, "Abs")) {
Comment thread
tannergooding marked this conversation as resolved.
opcode = OP_ABSF;
} else if (!strcmp (cmethod->name, "Ceiling")) {
opcode = OP_CEILF;
} else if (!strcmp (cmethod->name, "Cos")) {
opcode = OP_COSF;
} else if (!strcmp (cmethod->name, "Exp")) {
opcode = OP_EXPF;
} else if (!strcmp (cmethod->name, "Floor")) {
opcode = OP_FLOORF;
} else if (!strcmp (cmethod->name, "Log")) {
opcode = OP_LOGF;
} else if (!strcmp (cmethod->name, "Log2")) {
opcode = OP_LOG2F;
} else if (!strcmp (cmethod->name, "Log10")) {
Expand Down Expand Up @@ -285,6 +289,46 @@ llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
}
}

if (in_corlib && (!strcmp (m_class_get_name (cmethod->klass), "Single") || !strcmp (m_class_get_name (cmethod->klass), "Double"))) {
// Recognize a handful of helpers on the primitive `Single` / `Double` types
// (forwarded from INumberBase<TSelf> / INumber<TSelf>). These don't have
// corresponding APIs on Math/MathF -- e.g. there is no `Math.MinNumber` --
// so the existing Math/MathF block above doesn't catch them.
//
// * MinNumber / MaxNumber: IEEE 754-2008 minNum / maxNum (NaN-suppressing).
// Lower to llvm.minnum / llvm.maxnum, which on AArch64 maps to a single
// fminnm / fmaxnm instruction; matches the BCL spec "if either is NaN
// return the non-NaN; if both are NaN return NaN".
// * 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.
opcode = 0;
if (fsig->param_count == 1 &&
(fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8)) {
gboolean is_r4 = fsig->params [0]->type == MONO_TYPE_R4;
if (!strcmp (cmethod->name, "Abs"))
opcode = is_r4 ? OP_ABSF : OP_ABS;
}
if (fsig->param_count == 2 &&
fsig->params [0]->type == fsig->params [1]->type &&
(fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8)) {
gboolean is_r4 = fsig->params [0]->type == MONO_TYPE_R4;
if (!strcmp (cmethod->name, "MaxNumber"))
opcode = is_r4 ? OP_RMAXNUM : OP_FMAXNUM;
else if (!strcmp (cmethod->name, "MinNumber"))
opcode = is_r4 ? OP_RMINNUM : OP_FMINNUM;
}
if (opcode) {
MONO_INST_NEW (cfg, ins, opcode);
ins->type = STACK_R8;
ins->dreg = mono_alloc_dreg (cfg, (MonoStackType)ins->type);
ins->sreg1 = args [0]->dreg;
if (fsig->param_count > 1)
ins->sreg2 = args [1]->dreg;
MONO_ADD_INS (cfg->cbb, ins);
}
}

if (in_corlib && !strcmp (m_class_get_name (cmethod->klass), "SpanHelpers")) {
if (!strcmp (cmethod->name, "Memmove") && fsig->param_count == 3 && m_type_is_byref (fsig->params [0]) && m_type_is_byref (fsig->params [1]) && !cmethod->is_inflated) {
MonoBasicBlock *end_bb;
Expand Down
14 changes: 14 additions & 0 deletions src/mono/mono/mini/llvm-intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ INTRINS_OVR(POW, pow, Generic, LLVMDoubleType ())
INTRINS_OVR(EXP, exp, Generic, LLVMDoubleType ())
INTRINS_OVR(EXPF, exp, Generic, LLVMFloatType ())
INTRINS_OVR(LOG, log, Generic, LLVMDoubleType ())
INTRINS_OVR(LOGF, log, Generic, LLVMFloatType ())
INTRINS_OVR(LOG2, log2, Generic, LLVMDoubleType ())
INTRINS_OVR(LOG2F, log2, Generic, LLVMFloatType ())
INTRINS_OVR(LOG10, log10, Generic, LLVMDoubleType ())
Expand All @@ -96,6 +97,19 @@ INTRINS_OVR(TRUNC, trunc, Generic, LLVMDoubleType ())
INTRINS_OVR(TRUNCF, trunc, Generic, LLVMFloatType ())
INTRINS_OVR(COPYSIGN, copysign, Generic, LLVMDoubleType ())
INTRINS_OVR(COPYSIGNF, copysign, Generic, LLVMFloatType ())
/*
* IEEE 754-2008 minNum/maxNum (NaN-suppressing). When exactly one operand
* is NaN they return the other; when both are NaN they return NaN. This is
* what `float.MinNumber` / `double.MinNumber` (and the Max variants,
* surfaced via INumber<TSelf> on the primitive Single/Double/Half types)
* are documented to do, and on AArch64 these lower to single fminnm/fmaxnm
* instructions. Use llvm.minimum/maximum (see above) for the NaN-propagating
* Math.Min/Math.Max instead.
*/
INTRINS_OVR(MINNUM, minnum, Generic, LLVMDoubleType ())
INTRINS_OVR(MINNUMF, minnum, Generic, LLVMFloatType ())
INTRINS_OVR(MAXNUM, maxnum, Generic, LLVMDoubleType ())
INTRINS_OVR(MAXNUMF, maxnum, 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
33 changes: 33 additions & 0 deletions src/mono/mono/mini/mini-llvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -7494,6 +7494,13 @@ MONO_RESTORE_WARNING
values [ins->dreg] = call_intrins (ctx, INTRINS_LOG, args, dname);
break;
}
case OP_LOGF: {
LLVMValueRef args [1];

args [0] = convert (ctx, lhs, LLVMFloatType ());
values [ins->dreg] = call_intrins (ctx, INTRINS_LOGF, args, dname);
break;
}
case OP_TRUNC: {
LLVMValueRef args [1];

Expand Down Expand Up @@ -7703,6 +7710,32 @@ MONO_RESTORE_WARNING
break;
}

case OP_FMINNUM:
case OP_FMAXNUM:
case OP_RMINNUM:
case OP_RMAXNUM: {
/*
* IEEE 754-2008 minNum/maxNum (NaN-suppressing). Maps directly to
* llvm.minnum/maxnum, which is what `float.MinNumber` /
* `double.MinNumber` (and the Max variants, surfaced via
* INumber<TSelf> on the primitive Single/Double/Half types) specify.
* On AArch64 this lowers to a single fminnm/fmaxnm instruction.
*/
gboolean is_r4 = ins->opcode == OP_RMINNUM || ins->opcode == OP_RMAXNUM;
LLVMTypeRef t = is_r4 ? LLVMFloatType () : LLVMDoubleType ();
LLVMValueRef args [2] = { convert (ctx, lhs, t), convert (ctx, rhs, t) };
IntrinsicId iid;
switch (ins->opcode) {
case OP_FMAXNUM: iid = INTRINS_MAXNUM; break;
case OP_FMINNUM: iid = INTRINS_MINNUM; break;
case OP_RMAXNUM: iid = INTRINS_MAXNUMF; break;
case OP_RMINNUM: iid = INTRINS_MINNUMF; break;
default: g_assert_not_reached (); break;
}
values [ins->dreg] = call_intrins (ctx, iid, args, dname);
break;
}

/*
* See the ARM64 comment in mono/utils/atomic.h for an explanation of why this
* hack is necessary (for now).
Expand Down
5 changes: 5 additions & 0 deletions src/mono/mono/mini/mini-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -684,9 +684,13 @@ MINI_OP(OP_LMIN, "long_min", LREG, LREG, LREG)
MINI_OP(OP_LMAX, "long_max", LREG, LREG, LREG)
MINI_OP(OP_RMAX, "rmax", FREG, FREG, FREG)
MINI_OP(OP_RMIN, "rmin", FREG, FREG, FREG)
MINI_OP(OP_RMAXNUM, "rmaxnum", FREG, FREG, FREG)
MINI_OP(OP_RMINNUM, "rminnum", FREG, FREG, FREG)
MINI_OP(OP_RPOW, "rpow", FREG, FREG, FREG)
MINI_OP(OP_FMAX, "fmax", FREG, FREG, FREG)
MINI_OP(OP_FMIN, "fmin", FREG, FREG, FREG)
MINI_OP(OP_FMAXNUM, "fmaxnum", FREG, FREG, FREG)
MINI_OP(OP_FMINNUM, "fminnum", FREG, FREG, FREG)
MINI_OP(OP_FPOW, "fpow", FREG, FREG, FREG)
MINI_OP(OP_RCOPYSIGN,"rcopysign", FREG, FREG, FREG)
MINI_OP(OP_FCOPYSIGN,"fcopysign", FREG, FREG, FREG)
Expand Down Expand Up @@ -740,6 +744,7 @@ MINI_OP(OP_LOG2, "log2", FREG, FREG, NONE)
MINI_OP(OP_LOG2F, "log2f", FREG, FREG, NONE)
MINI_OP(OP_LOG10, "log10", FREG, FREG, NONE)
MINI_OP(OP_LOG10F, "log10f", FREG, FREG, NONE)
MINI_OP(OP_LOGF, "logf", FREG, FREG, NONE)
MINI_OP(OP_TRUNC, "trunc", FREG, FREG, NONE)
MINI_OP(OP_TRUNCF, "truncf", FREG, FREG, NONE)
MINI_OP(OP_ABSF, "absf", FREG, FREG, NONE)
Expand Down
Loading