Implement more Math LLVM intrinsics (mono/mono#16578)
authorEgor Bogatov <egorbo@gmail.com>
Wed, 4 Sep 2019 13:54:52 +0000 (16:54 +0300)
committerZoltan Varga <vargaz@gmail.com>
Wed, 4 Sep 2019 13:54:52 +0000 (09:54 -0400)
* 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
src/mono/mono/mini/mini-llvm.c
src/mono/mono/mini/mini-ops.h

index 962de10..57d621f 100644 (file)
@@ -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);
index 61eeb11..255c70d 100644 (file)
@@ -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;
index e4494a6..29643f1 100644 (file)
@@ -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)