[Mono] Add intrinsics for several operators (#66317)
authorMeri Khamoyan <96171496+mkhamoyan@users.noreply.github.com>
Thu, 10 Mar 2022 21:56:07 +0000 (01:56 +0400)
committerGitHub <noreply@github.com>
Thu, 10 Mar 2022 21:56:07 +0000 (22:56 +0100)
This PR contributes to  #64072

Added intrinsics for following operators
- Operator *
- Operator /
- Operator |
- Operator &
- Operator ^
- Unary operator -
- Unary operator ~

Co-authored-by: Fan Yang <yangfan@microsoft.com>
src/mono/mono/mini/simd-intrinsics.c
src/mono/mono/mini/simd-methods.h

index 2ddb075..b4e4d42 100644 (file)
@@ -260,6 +260,115 @@ emit_simd_ins_for_sig (MonoCompile *cfg, MonoClass *klass, int opcode, int instc
        return ins;
 }
 
+static gboolean type_enum_is_unsigned (MonoTypeEnum type);
+static gboolean type_enum_is_float (MonoTypeEnum type);
+
+static MonoInst*
+emit_simd_ins_for_binary_op (MonoCompile *cfg, MonoClass *klass, MonoMethodSignature *fsig, MonoInst **args, MonoTypeEnum arg_type, int id)
+{
+       int instc0 = -1;
+       int op = OP_XBINOP;
+
+       if (id == SN_BitwiseAnd || id == SN_BitwiseOr || id == SN_Xor ||
+               id == SN_op_BitwiseAnd || id == SN_op_BitwiseOr || id == SN_op_ExclusiveOr) {
+               op = OP_XBINOP_FORCEINT;
+       
+               switch (id) {
+               case SN_BitwiseAnd:
+               case SN_op_BitwiseAnd:
+                       instc0 = XBINOP_FORCEINT_AND;
+                       break;
+               case SN_BitwiseOr:
+               case SN_op_BitwiseOr:
+                       instc0 = XBINOP_FORCEINT_OR;
+                       break;
+               case SN_op_ExclusiveOr:
+               case SN_Xor:
+                       instc0 = XBINOP_FORCEINT_XOR;
+                       break;
+               }
+       } else {
+               if (type_enum_is_float (arg_type)) {
+                       switch (id) {
+                       case SN_Add:
+                       case SN_op_Addition:
+                               instc0 = OP_FADD;
+                               break;
+                       case SN_Divide:
+                       case SN_op_Division:
+                               instc0 = OP_FDIV;
+                               break;
+                       case SN_Max:
+                               instc0 = OP_FMAX;
+                               break;
+                       case SN_Min:
+                               instc0 = OP_FMIN;
+                               break;
+                       case SN_Multiply:
+                       case SN_op_Multiply:
+                               instc0 = OP_FMUL;
+                               break;
+                       case SN_Subtract:
+                       case SN_op_Subtraction:
+                               instc0 = OP_FSUB;
+                               break;
+                       default:
+                               g_assert_not_reached ();
+                       }
+               } else {
+                       switch (id) {
+                       case SN_Add:
+                       case SN_op_Addition:
+                               instc0 = OP_IADD;
+                               break;
+                       case SN_Divide:
+                       case SN_op_Division:
+                               return NULL;
+                       case SN_Max:
+                               instc0 = OP_IMAX;
+                               break;
+                       case SN_Min:
+                               instc0 = OP_IMIN;
+                               break;
+                       case SN_Multiply:
+                       case SN_op_Multiply:
+                               instc0 = OP_IMUL;
+                               break;
+                       case SN_Subtract:
+                       case SN_op_Subtraction:
+                               instc0 = OP_ISUB;
+                               break;
+                       default:
+                               g_assert_not_reached ();
+                       }
+               }
+       }
+       return emit_simd_ins_for_sig (cfg, klass, op, instc0, arg_type, fsig, args);
+}
+
+static MonoInst*
+emit_simd_ins_for_unary_op (MonoCompile *cfg, MonoClass *klass, MonoMethodSignature *fsig, MonoInst **args, MonoTypeEnum arg_type, int id)
+{
+#ifdef TARGET_ARM64
+       int op = -1;
+       switch (id){
+       case SN_Negate:
+       case SN_op_UnaryNegation:
+               op = OP_ARM64_XNEG;
+               break;
+       case SN_OnesComplement:
+       case SN_op_OnesComplement:
+               op = OP_ARM64_MVN;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+       return emit_simd_ins_for_sig (cfg, klass, op, -1, arg_type, fsig, args);
+#else
+       return NULL;
+#endif
+}
+
 static gboolean
 is_hw_intrinsics_class (MonoClass *klass, const char *name, gboolean *is_64bit)
 {
@@ -285,9 +394,6 @@ get_underlying_type (MonoType* type)
                return type->type;
 }
 
-static gboolean type_enum_is_unsigned (MonoTypeEnum type);
-static gboolean type_enum_is_float (MonoTypeEnum type);
-
 static MonoInst*
 emit_xcompare (MonoCompile *cfg, MonoClass *klass, MonoTypeEnum etype, MonoInst *arg1, MonoInst *arg2)
 {
@@ -804,91 +910,25 @@ emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsi
 #endif
 }
        case SN_Add:
+       case SN_BitwiseAnd:
+       case SN_BitwiseOr:
        case SN_Divide:
        case SN_Max:
        case SN_Min:
        case SN_Multiply:
-       case SN_Subtract: {
+       case SN_Subtract:
+       case SN_Xor:
                if (!is_element_type_primitive (fsig->params [0]))
                        return NULL;
-               int instc0 = -1;
-               if (type_enum_is_float (arg0_type)) {
-                       switch (id) {
-                       case SN_Add:
-                               instc0 = OP_FADD;
-                               break;
-                       case SN_Divide:
-                               instc0 = OP_FDIV;
-                               break;
-                       case SN_Max:
-                               instc0 = OP_FMAX;
-                               break;
-                       case SN_Min:
-                               instc0 = OP_FMIN;
-                               break;
-                       case SN_Multiply:
-                               instc0 = OP_FMUL;
-                               break;
-                       case SN_Subtract:
-                               instc0 = OP_FSUB;
-                               break;
-                       default:
-                               g_assert_not_reached ();
-                       }
-               } else {
-                       switch (id) {
-                       case SN_Add:
-                               instc0 = OP_IADD;
-                               break;
-                       case SN_Divide:
-                               return NULL;
-                       case SN_Max:
-                               instc0 = OP_IMAX;
-                               break;
-                       case SN_Min:
-                               instc0 = OP_IMIN;
-                               break;
-                       case SN_Multiply:
-                               instc0 = OP_IMUL;
-                               break;
-                       case SN_Subtract:
-                               instc0 = OP_ISUB;
-                               break;
-                       default:
-                               g_assert_not_reached ();
-                       }
-               }
-               return emit_simd_ins_for_sig (cfg, klass, OP_XBINOP, instc0, arg0_type, fsig, args);
-       }
+               return emit_simd_ins_for_binary_op (cfg, klass, fsig, args, arg0_type, id);
        case SN_AndNot:
+#ifdef TARGET_ARM64
                if (!is_element_type_primitive (fsig->params [0]))
                        return NULL;
-#ifdef TARGET_ARM64
                return emit_simd_ins_for_sig (cfg, klass, OP_ARM64_BIC, -1, arg0_type, fsig, args);
 #else
                return NULL;
 #endif
-       case SN_BitwiseAnd:
-       case SN_BitwiseOr:
-       case SN_Xor: {
-               if (!is_element_type_primitive (fsig->params [0]))
-                       return NULL;
-               int instc0 = -1;
-               switch (id) {
-               case SN_BitwiseAnd:
-                       instc0 = XBINOP_FORCEINT_AND;
-                       break;
-               case SN_BitwiseOr:
-                       instc0 = XBINOP_FORCEINT_OR;
-                       break;
-               case SN_Xor:
-                       instc0 = XBINOP_FORCEINT_XOR;
-                       break;
-               default:
-                       g_assert_not_reached ();
-               }
-               return emit_simd_ins_for_sig (cfg, klass, OP_XBINOP_FORCEINT, instc0, arg0_type, fsig, args);
-       }
        case SN_As:
        case SN_AsByte:
        case SN_AsDouble:
@@ -916,9 +956,9 @@ emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsi
 #endif
        }
        case SN_ConditionalSelect: {
+#ifdef TARGET_ARM64
                if (!is_element_type_primitive (fsig->params [0]))
                        return NULL;
-#ifdef TARGET_ARM64
                return emit_simd_ins_for_sig (cfg, klass, OP_ARM64_BSL, -1, arg0_type, fsig, args);
 #else
                return NULL;
@@ -1062,17 +1102,12 @@ emit_sri_vector (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsi
        case SN_OnesComplement: {
                if (!is_element_type_primitive (fsig->params [0]))
                        return NULL;
-#ifdef TARGET_ARM64
-               int op = id == SN_Negate ? OP_ARM64_XNEG : OP_ARM64_MVN;
-               return emit_simd_ins_for_sig (cfg, klass, op, -1, arg0_type, fsig, args);
-#else
-               return NULL;
-#endif
+               return emit_simd_ins_for_unary_op (cfg, klass, fsig, args, arg0_type, id);
        }
        case SN_Sqrt: {
+#ifdef TARGET_ARM64
                if (!is_element_type_primitive (fsig->params [0]))
                        return NULL;
-#ifdef TARGET_ARM64
                if (!type_enum_is_float (arg0_type))
                        return NULL;
                return emit_simd_ins_for_sig (cfg, klass, OP_XOP_OVR_X_X, INTRINS_AARCH64_ADV_SIMD_FSQRT, arg0_type, fsig, args);
@@ -1130,9 +1165,16 @@ static guint16 vector64_vector128_t_methods [] = {
        SN_get_IsSupported,
        SN_get_Zero,
        SN_op_Addition,
+       SN_op_BitwiseAnd,
+       SN_op_BitwiseOr,
+       SN_op_Division,
        SN_op_Equality,
+       SN_op_ExclusiveOr,
        SN_op_Inequality,
+       SN_op_Multiply,
+       SN_op_OnesComplement,
        SN_op_Subtraction,
+       SN_op_UnaryNegation,
 };
 
 static MonoInst*
@@ -1152,6 +1194,7 @@ emit_vector64_vector128_t (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
        g_assert (size > 0);
        g_assert (esize > 0);
        int len = size / esize;
+       MonoTypeEnum arg0_type = fsig->param_count > 0 ? get_underlying_type (fsig->params [0]) : MONO_TYPE_VOID;
 
        if (!MONO_TYPE_IS_INTRINSICS_VECTOR_PRIMITIVE (etype))
                return NULL;
@@ -1197,27 +1240,32 @@ emit_vector64_vector128_t (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
                break;
        }
        case SN_op_Addition:
-       case SN_op_Subtraction: {       
-               if (!(fsig->param_count == 2 && mono_metadata_type_equal (fsig->ret, type) && mono_metadata_type_equal (fsig->params [0], type) && mono_metadata_type_equal (fsig->params [1], type)))
+       case SN_op_BitwiseAnd:
+       case SN_op_BitwiseOr:
+       case SN_op_Division:
+       case SN_op_ExclusiveOr:
+       case SN_op_Multiply:
+       case SN_op_Subtraction: {
+               if (fsig->param_count != 2 )
                        return NULL;
-               MonoInst *ins = emit_simd_ins (cfg, klass, OP_XBINOP, args [0]->dreg, args [1]->dreg);
-               ins->inst_c1 = etype->type;
-               if (etype->type == MONO_TYPE_R4 || etype->type == MONO_TYPE_R8)
-                       ins->inst_c0 = id == SN_op_Addition ? OP_FADD : OP_FSUB;
-               else
-                       ins->inst_c0 = id == SN_op_Addition ? OP_IADD : OP_ISUB;
-               return ins;
+               MonoTypeEnum arg0_type = fsig->param_count > 0 ? get_underlying_type (fsig->params [0]) : MONO_TYPE_VOID;
+               return emit_simd_ins_for_binary_op (cfg, klass, fsig, args, arg0_type, id);
+               
        }
        case SN_op_Equality:
        case SN_op_Inequality:
-               g_assert (fsig->param_count == 2 && fsig->ret->type == MONO_TYPE_BOOLEAN &&
-                                 mono_metadata_type_equal (fsig->params [0], type) &&
-                                 mono_metadata_type_equal (fsig->params [1], type));
+               if (fsig->param_count != 2 )
+                       return NULL;
                switch (id) {
                        case SN_op_Equality: return emit_xequal (cfg, klass, args [0], args [1]);
                        case SN_op_Inequality: return emit_not_xequal (cfg, klass, args [0], args [1]);
                        default: g_assert_not_reached ();
                }
+       case SN_op_OnesComplement:
+       case SN_op_UnaryNegation:
+               if (fsig->param_count != 1 )
+                       return NULL;
+               return emit_simd_ins_for_unary_op (cfg, klass, fsig, args, arg0_type, id);
        default:
                break;
        }
index 7a5a84b..a66dd7b 100644 (file)
@@ -35,7 +35,9 @@ METHOD(op_ExclusiveOr)
 METHOD(op_Explicit)
 METHOD(op_Inequality)
 METHOD(op_Multiply)
+METHOD(op_OnesComplement)
 METHOD(op_Subtraction)
+METHOD(op_UnaryNegation)
 // Vector
 METHOD(ConvertToInt32)
 METHOD(ConvertToInt32WithTruncation)