From 978161364f623817409cbfca8d13d2767efb3f3f Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Wed, 4 Sep 2019 16:54:52 +0300 Subject: [PATCH] Implement more Math LLVM intrinsics (mono/mono#16578) * Implement more Math intrinsics * Test on CI * Update pipeline-netcore-runtime.yml * undo "test on CI" Commit migrated from https://github.com/mono/mono/commit/30625b25df4e3d83f6abb30cea2bd57ff0c49539 --- src/mono/mono/mini/intrinsics.c | 153 +++++++++++++++++++++++++++------------- src/mono/mono/mini/mini-llvm.c | 120 +++++++++++++++++++++++++++++-- src/mono/mono/mini/mini-ops.h | 11 +++ 3 files changed, 229 insertions(+), 55 deletions(-) diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 962de10..57d621f 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -109,69 +109,124 @@ llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign { MonoInst *ins = NULL; int opcode = 0; + // Convert Math and MathF methods into LLVM intrinsics, e.g. MathF.Sin -> @llvm.sin.f32 + 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")) { + 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, "Log2")) { + opcode = OP_LOG2F; + } else if (!strcmp (cmethod->name, "Log10")) { + opcode = OP_LOG10F; + } else if (!strcmp (cmethod->name, "Sin")) { + opcode = OP_SINF; + } else if (!strcmp (cmethod->name, "Sqrt")) { + opcode = OP_SQRTF; + } else if (!strcmp (cmethod->name, "Truncate")) { + opcode = OP_TRUNCF; + } + } + // (float, float) + if (fsig->param_count == 2 && fsig->params [0]->type == MONO_TYPE_R4 && fsig->params [1]->type == MONO_TYPE_R4) { + if (!strcmp (cmethod->name, "Pow")) { + opcode = OP_RPOW; + } else if (!strcmp (cmethod->name, "CopySign")) { + opcode = OP_RCOPYSIGN; + } + } + // (float, float, float) + if (fsig->param_count == 3 && fsig->params [0]->type == MONO_TYPE_R4 && fsig->params [1]->type == MONO_TYPE_R4 && fsig->params [2]->type == MONO_TYPE_R4) { + if (!strcmp (cmethod->name, "FusedMultiplyAdd")) { + opcode = OP_FMAF; + } + } - if (in_corlib && !strcmp (m_class_get_name (cmethod->klass), "MathF") && fsig->param_count && fsig->params [0]->type == MONO_TYPE_R4 && cfg->r4fp) { - if (!strcmp (cmethod->name, "Sin")) - opcode = OP_SINF; - else if (!strcmp (cmethod->name, "Cos")) - opcode = OP_COSF; - else if (!strcmp (cmethod->name, "Abs")) - opcode = OP_ABSF; - else if (!strcmp (cmethod->name, "Sqrt")) - opcode = OP_SQRTF; - else if (!strcmp (cmethod->name, "Floor")) - opcode = OP_FLOORF; - else if (!strcmp (cmethod->name, "Ceiling")) - opcode = OP_CEILF; - else if (!strcmp (cmethod->name, "FusedMultiplyAdd")) - opcode = OP_FMAF; - else if (!strcmp (cmethod->name, "Pow")) - opcode = OP_RPOW; - if (opcode && fsig->param_count > 0) { + 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) { // POW + if (fsig->param_count > 1) { ins->sreg2 = args [1]->dreg; } - if (fsig->param_count > 2) { // FMA + if (fsig->param_count > 2) { ins->sreg3 = args [2]->dreg; } g_assert (fsig->param_count <= 3); MONO_ADD_INS (cfg->cbb, ins); } } - /* The LLVM backend supports these intrinsics */ + if (cmethod->klass == mono_class_try_get_math_class ()) { - if (strcmp (cmethod->name, "Sin") == 0) { - opcode = OP_SIN; - } else if (strcmp (cmethod->name, "Cos") == 0) { - opcode = OP_COS; - } else if (strcmp (cmethod->name, "Sqrt") == 0) { - opcode = OP_SQRT; - } else if (strcmp (cmethod->name, "Floor") == 0) { - opcode = OP_FLOOR; - } else if (strcmp (cmethod->name, "Ceiling") == 0) { - opcode = OP_CEIL; - } else if (strcmp (cmethod->name, "FusedMultiplyAdd") == 0) { - opcode = OP_FMA; - } else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) { - opcode = OP_ABS; + // (double) + if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R8) { + if (!strcmp (cmethod->name, "Abs")) { + opcode = OP_ABS; + } else if (!strcmp (cmethod->name, "Ceiling")) { + opcode = OP_CEIL; + } else if (!strcmp (cmethod->name, "Cos")) { + opcode = OP_COS; + } else if (!strcmp (cmethod->name, "Exp")) { + opcode = OP_EXP; + } else if (!strcmp (cmethod->name, "Floor")) { + opcode = OP_FLOOR; + } else if (!strcmp (cmethod->name, "Log")) { + opcode = OP_LOG; + } else if (!strcmp (cmethod->name, "Log2")) { + opcode = OP_LOG2; + } else if (!strcmp (cmethod->name, "Log10")) { + opcode = OP_LOG10; + } else if (!strcmp (cmethod->name, "Sin")) { + opcode = OP_SIN; + } else if (!strcmp (cmethod->name, "Sqrt")) { + opcode = OP_SQRT; + } else if (!strcmp (cmethod->name, "Truncate")) { + opcode = OP_TRUNC; + } + } + // (double, double) + if (fsig->param_count == 2 && fsig->params [0]->type == MONO_TYPE_R8 && fsig->params [1]->type == MONO_TYPE_R8) { + // Max and Min can only be optimized in fast math mode + if (!strcmp (cmethod->name, "Max") && mono_use_fast_math) { + opcode = OP_FMAX; + } else if (!strcmp (cmethod->name, "Min") && mono_use_fast_math) { + opcode = OP_FMIN; + } else if (!strcmp (cmethod->name, "Pow")) { + opcode = OP_FPOW; + } else if (!strcmp (cmethod->name, "CopySign")) { + opcode = OP_FCOPYSIGN; + } + } + // (double, double, double) + if (fsig->param_count == 3 && fsig->params [0]->type == MONO_TYPE_R8 && fsig->params [1]->type == MONO_TYPE_R8 && fsig->params [2]->type == MONO_TYPE_R8) { + if (!strcmp (cmethod->name, "FusedMultiplyAdd")) { + opcode = OP_FMA; + } } - // Max and Min can only be optimized in fast math mode - else if (strcmp (cmethod->name, "Max") == 0 && mono_use_fast_math && fsig->params [0]->type == MONO_TYPE_R8) { - opcode = OP_FMAX; - } else if (strcmp (cmethod->name, "Min") == 0 && mono_use_fast_math && fsig->params [0]->type == MONO_TYPE_R8) { - opcode = OP_FMIN; + + // Math also contains overloads for floats (MathF inlines them) + // (float) + if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R4) { + if (!strcmp (cmethod->name, "Abs")) { + opcode = OP_ABSF; + } } - // Math.Max/Min also have float overloads (MathF.Max/Min just redirect to them) - else if (strcmp (cmethod->name, "Max") == 0 && mono_use_fast_math && fsig->params [0]->type == MONO_TYPE_R4) { - opcode = OP_RMAX; - } else if (strcmp (cmethod->name, "Min") == 0 && mono_use_fast_math && fsig->params [0]->type == MONO_TYPE_R4) { - opcode = OP_RMIN; - } else if (strcmp (cmethod->name, "Pow") == 0) { - opcode = OP_FPOW; + // (float, float) + if (fsig->param_count == 2 && fsig->params [0]->type == MONO_TYPE_R4 && fsig->params [1]->type == MONO_TYPE_R4) { + if (!strcmp (cmethod->name, "Max") && mono_use_fast_math) { + opcode = OP_RMAX; + } else if (!strcmp (cmethod->name, "Min") && mono_use_fast_math) { + opcode = OP_RMIN; + } else if (!strcmp (cmethod->name, "Pow")) { + opcode = OP_RPOW; + } } if (opcode && fsig->param_count > 0) { @@ -179,10 +234,10 @@ llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign ins->type = STACK_R8; ins->dreg = mono_alloc_dreg (cfg, (MonoStackType)ins->type); ins->sreg1 = args [0]->dreg; - if (fsig->param_count > 1) { // POW, MIN, MAX + if (fsig->param_count > 1) { ins->sreg2 = args [1]->dreg; } - if (fsig->param_count > 2) { // FMA + if (fsig->param_count > 2) { ins->sreg3 = args [2]->dreg; } g_assert (fsig->param_count <= 3); diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 61eeb11..255c70d 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -307,6 +307,17 @@ typedef enum { INTRINS_FMAF, INTRINS_POW, INTRINS_POWF, + INTRINS_EXP, + INTRINS_EXPF, + INTRINS_LOG, + INTRINS_LOG2, + INTRINS_LOG2F, + INTRINS_LOG10, + INTRINS_LOG10F, + INTRINS_TRUNC, + INTRINS_TRUNCF, + INTRINS_COPYSIGN, + INTRINS_COPYSIGNF, INTRINS_EXPECT_I8, INTRINS_EXPECT_I1, INTRINS_CTPOP_I32, @@ -5963,6 +5974,69 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_SINF), args, 1, dname); break; } + case OP_EXP: { + LLVMValueRef args [1]; + + args [0] = convert (ctx, lhs, LLVMDoubleType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_EXP), args, 1, dname); + break; + } + case OP_EXPF: { + LLVMValueRef args [1]; + + args [0] = convert (ctx, lhs, LLVMFloatType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_EXPF), args, 1, dname); + break; + } + case OP_LOG2: { + LLVMValueRef args [1]; + + args [0] = convert (ctx, lhs, LLVMDoubleType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_LOG2), args, 1, dname); + break; + } + case OP_LOG2F: { + LLVMValueRef args [1]; + + args [0] = convert (ctx, lhs, LLVMFloatType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_LOG2F), args, 1, dname); + break; + } + case OP_LOG10: { + LLVMValueRef args [1]; + + args [0] = convert (ctx, lhs, LLVMDoubleType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_LOG10), args, 1, dname); + break; + } + case OP_LOG10F: { + LLVMValueRef args [1]; + + args [0] = convert (ctx, lhs, LLVMFloatType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_LOG10F), args, 1, dname); + break; + } + case OP_LOG: { + LLVMValueRef args [1]; + + args [0] = convert (ctx, lhs, LLVMDoubleType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_LOG), args, 1, dname); + break; + } + case OP_TRUNC: { + LLVMValueRef args [1]; + + args [0] = convert (ctx, lhs, LLVMDoubleType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_TRUNC), args, 1, dname); + break; + } + case OP_TRUNCF: { + LLVMValueRef args [1]; + + args [0] = convert (ctx, lhs, LLVMFloatType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_TRUNCF), args, 1, dname); + break; + } case OP_COS: { LLVMValueRef args [1]; @@ -6076,6 +6150,22 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_POW), args, 2, dname); break; } + case OP_FCOPYSIGN: { + LLVMValueRef args [2]; + + args [0] = convert (ctx, lhs, LLVMDoubleType ()); + args [1] = convert (ctx, rhs, LLVMDoubleType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_COPYSIGN), args, 2, dname); + break; + } + case OP_RCOPYSIGN: { + LLVMValueRef args [2]; + + args [0] = convert (ctx, lhs, LLVMFloatType ()); + args [1] = convert (ctx, rhs, LLVMFloatType ()); + values [ins->dreg] = LLVMBuildCall (builder, get_intrins (ctx, INTRINS_COPYSIGNF), args, 2, dname); + break; + } case OP_IMIN: case OP_LMIN: @@ -8599,6 +8689,17 @@ static IntrinsicDesc intrinsics[] = { {INTRINS_SQRTF, "llvm.sqrt.f32"}, {INTRINS_POWF, "llvm.pow.f32"}, {INTRINS_POW, "llvm.pow.f64"}, + {INTRINS_EXP, "llvm.exp.f64"}, + {INTRINS_EXPF, "llvm.exp.f32"}, + {INTRINS_LOG, "llvm.log.f64"}, + {INTRINS_LOG2, "llvm.log2.f64"}, + {INTRINS_LOG2F, "llvm.log2.f32"}, + {INTRINS_LOG10, "llvm.log10.f64"}, + {INTRINS_LOG10F, "llvm.log10.f32"}, + {INTRINS_TRUNC, "llvm.trunc.f64"}, + {INTRINS_TRUNCF, "llvm.trunc.f32"}, + {INTRINS_COPYSIGN, "llvm.copysign.f64"}, + {INTRINS_COPYSIGNF, "llvm.copysign.f32"}, {INTRINS_EXPECT_I8, "llvm.expect.i8"}, {INTRINS_EXPECT_I1, "llvm.expect.i1"}, {INTRINS_CTPOP_I32, "llvm.ctpop.i32"}, @@ -8761,31 +8862,38 @@ add_intrinsic (LLVMModuleRef module, int id) AddFunc (module, name, LLVMFloatType (), params, 3); break; } + case INTRINS_EXP: + case INTRINS_LOG: + case INTRINS_LOG2: + case INTRINS_LOG10: + case INTRINS_TRUNC: case INTRINS_SIN: case INTRINS_COS: case INTRINS_SQRT: case INTRINS_FLOOR: case INTRINS_CEIL: case INTRINS_FABS: { - LLVMTypeRef params [] = { LLVMDoubleType () }; - - AddFunc (module, name, LLVMDoubleType (), params, 1); + AddFunc1 (module, name, LLVMDoubleType (), LLVMDoubleType ()); break; } + case INTRINS_EXPF: + case INTRINS_LOG2F: + case INTRINS_LOG10F: + case INTRINS_TRUNCF: case INTRINS_SINF: case INTRINS_COSF: case INTRINS_SQRTF: case INTRINS_FLOORF: case INTRINS_CEILF: case INTRINS_ABSF: { - LLVMTypeRef params [] = { LLVMFloatType () }; - - AddFunc (module, name, LLVMFloatType (), params, 1); + AddFunc1 (module, name, LLVMFloatType (), LLVMFloatType ()); break; } + case INTRINS_COPYSIGNF: case INTRINS_POWF: AddFunc2 (module, name, LLVMFloatType (), LLVMFloatType (), LLVMFloatType ()); break; + case INTRINS_COPYSIGN: case INTRINS_POW: AddFunc2 (module, name, LLVMDoubleType (), LLVMDoubleType (), LLVMDoubleType ()); break; diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index e4494a6..29643f1 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -677,6 +677,8 @@ 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_FPOW, "fpow", FREG, FREG, FREG) +MINI_OP(OP_RCOPYSIGN,"rcopysign", FREG, FREG, FREG) +MINI_OP(OP_FCOPYSIGN,"fcopysign", FREG, FREG, FREG) /* opcodes most architecture have */ MINI_OP(OP_ADC, "adc", IREG, IREG, IREG) @@ -720,6 +722,15 @@ MINI_OP(OP_FLOOR, "floor", FREG, FREG, NONE) MINI_OP3(OP_FMA, "fma", FREG, FREG, FREG, FREG) MINI_OP(OP_SINF, "sinf", FREG, FREG, NONE) MINI_OP(OP_COSF, "cosf", FREG, FREG, NONE) +MINI_OP(OP_EXPF, "expf", FREG, FREG, NONE) +MINI_OP(OP_EXP, "exp", FREG, FREG, NONE) +MINI_OP(OP_LOG, "log", FREG, FREG, NONE) +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_TRUNC, "trunc", FREG, FREG, NONE) +MINI_OP(OP_TRUNCF, "truncf", FREG, FREG, NONE) MINI_OP(OP_ABSF, "absf", FREG, FREG, NONE) MINI_OP(OP_SQRTF, "sqrtf", FREG, FREG, NONE) MINI_OP(OP_CEILF, "ceilf", FREG, FREG, NONE) -- 2.7.4