Merge pull request #2213 from kumpera/widen-unsafe-mov
authorRodrigo Kumpera <kumpera@gmail.com>
Thu, 12 Nov 2015 00:15:39 +0000 (19:15 -0500)
committerRodrigo Kumpera <kumpera@gmail.com>
Thu, 12 Nov 2015 00:15:39 +0000 (19:15 -0500)
[jit] Add support for widening of Array.UnsafeMov intrisic. Fixes #35310

mcs/class/corlib/System/Array.cs
mono/mini/method-to-ir.c
mono/tests/enum.cs

index 32e873227de30ee98a4cafc752a52642ff3bdf31..8f6e0aa098e40b238d2738503705207086c3eb80 100644 (file)
@@ -3189,6 +3189,19 @@ namespace System
                //
                // Moved value from instance into target of different type with no checks (JIT intristics)
                //
+               // Restrictions:
+               //
+               // S and R must either:
+               //       both be blitable valuetypes
+               //       both be reference types (IOW, an unsafe cast)
+               // S and R cannot be float or double
+               // S and R must either:
+               //       both be a struct
+               //       both be a scalar
+               // S and R must either:
+               //       be of same size
+               //       both be a scalar of size <= 4
+               //
                internal static R UnsafeMov<S,R> (S instance) {
                        return (R)(object) instance;
                }
index c54c08f6a9ed22c6bffc3b8ba44f6ba2c3bf7df7..10655663a538d7af0ffb32a1b29c2efedfa25691 100644 (file)
@@ -5686,12 +5686,26 @@ static gboolean
 is_unsafe_mov_compatible (MonoCompile *cfg, MonoClass *param_klass, MonoClass *return_klass)
 {
        uint32_t align;
+       int param_size, return_size;
 
        param_klass = mono_class_from_mono_type (mini_get_underlying_type (&param_klass->byval_arg));
+       return_klass = mono_class_from_mono_type (mini_get_underlying_type (&return_klass->byval_arg));
 
-       //Only allow for valuetypes
-       if (!param_klass->valuetype || !return_klass->valuetype)
+       if (cfg->verbose_level > 3)
+               printf ("[UNSAFE-MOV-INTRISIC] %s <- %s\n", return_klass->name, param_klass->name);
+
+       //Don't allow mixing reference types with value types
+       if (param_klass->valuetype != return_klass->valuetype) {
+               if (cfg->verbose_level > 3)
+                       printf ("[UNSAFE-MOV-INTRISIC]\tone of the args is a valuetype and the other is not\n");
                return FALSE;
+       }
+
+       if (!param_klass->valuetype) {
+               if (cfg->verbose_level > 3)
+                       printf ("[UNSAFE-MOV-INTRISIC]\targs are reference types\n");
+               return TRUE;
+       }
 
        //That are blitable
        if (param_klass->has_references || return_klass->has_references)
@@ -5699,17 +5713,51 @@ is_unsafe_mov_compatible (MonoCompile *cfg, MonoClass *param_klass, MonoClass *r
 
        /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
        if ((MONO_TYPE_ISSTRUCT (&param_klass->byval_arg) && !MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)) ||
-               (!MONO_TYPE_ISSTRUCT (&param_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)))
+               (!MONO_TYPE_ISSTRUCT (&param_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg))) {
+                       if (cfg->verbose_level > 3)
+                               printf ("[UNSAFE-MOV-INTRISIC]\tmixing structs and scalars\n");
                return FALSE;
+       }
 
        if (param_klass->byval_arg.type == MONO_TYPE_R4 || param_klass->byval_arg.type == MONO_TYPE_R8 ||
-               return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8)
+               return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8) {
+               if (cfg->verbose_level > 3)
+                       printf ("[UNSAFE-MOV-INTRISIC]\tfloat or double are not supported\n");
                return FALSE;
+       }
 
-       //And have the same size
-       if (mono_class_value_size (param_klass, &align) != mono_class_value_size (return_klass, &align))
+       param_size = mono_class_value_size (param_klass, &align);
+       return_size = mono_class_value_size (return_klass, &align);
+
+       //We can do it if sizes match
+       if (param_size == return_size) {
+               if (cfg->verbose_level > 3)
+                       printf ("[UNSAFE-MOV-INTRISIC]\tsame size\n");
+               return TRUE;
+       }
+
+       //No simple way to handle struct if sizes don't match
+       if (MONO_TYPE_ISSTRUCT (&param_klass->byval_arg)) {
+               if (cfg->verbose_level > 3)
+                       printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch and type is a struct\n");
                return FALSE;
-       return TRUE;
+       }
+
+       /*
+        * Same reg size category.
+        * A quick note on why we don't require widening here.
+        * The intrinsic is "R Array.UnsafeMov<S,R> (S s)".
+        *
+        * Since the source value comes from a function argument, the JIT will already have
+        * the value in a VREG and performed any widening needed before (say, when loading from a field).
+        */
+       if (param_size <= 4 && return_size <= 4) {
+               if (cfg->verbose_level > 3)
+                       printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch but both are of the same reg class\n");
+               return TRUE;
+       }
+
+       return FALSE;
 }
 
 static MonoInst*
@@ -5718,7 +5766,7 @@ emit_array_unsafe_mov (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **a
        MonoClass *param_klass = mono_class_from_mono_type (fsig->params [0]);
        MonoClass *return_klass = mono_class_from_mono_type (fsig->ret);
 
-       //Valuetypes that are semantically equivalent
+       //Valuetypes that are semantically equivalent or numbers than can be widened to
        if (is_unsafe_mov_compatible (cfg, param_klass, return_klass))
                return args [0];
 
index 2f8f55cd53abcc3b48cbbdeee2f1fdfaf677f757..fa65a2c6f74a99808858fae0cd6918c90141726f 100644 (file)
+using System;
+using System.Collections.Generic;
+
 namespace Test {
 
-public enum YaddaYadda {
-       buba,
-       birba,
-       dadoom,
-};
-
-public enum byteenum : byte {
-       zero,
-       one,
-       two,
-       three
-}
+       enum ByteEnum : byte {
+               A = 10
+       }
 
-public enum longenum: long {
-       s0 = 0,
-       s1 = 1
-}
+       enum SByteEnum : sbyte {
+               A = -11
+       }
 
-public enum sbyteenum : sbyte {
-       d0,
-       d1
-}
+       enum ShortEnum : short {
+               A = -12
+       }
+
+       enum UShortEnum : ushort {
+               A = 13
+       }
+
+       enum IntEnum : int {
+               A = -15
+       }
+
+       enum UIntEnum : uint {
+               A = 16
+       }
+
+       enum LongEnum : long {
+               A = -153453525432334L
+       }
+
+       enum ULongEnum : ulong {
+               A = 164923797563459L
+       }
+
+       public enum YaddaYadda {
+               buba,
+               birba,
+               dadoom,
+       };
+
+       public enum byteenum : byte {
+               zero,
+               one,
+               two,
+               three
+       }
+
+       public enum longenum: long {
+               s0 = 0,
+               s1 = 1
+       }
+
+       public enum sbyteenum : sbyte {
+               d0,
+               d1
+       }
+
+       public class Tests {
+               public static int test_0_basic_enum_vals ()
+               {
+                       YaddaYadda val = YaddaYadda.dadoom;
+                       byteenum be = byteenum.one;
+                       if (val != YaddaYadda.dadoom)
+                               return 1;
+                       if (be != (byteenum)1)
+                               return 2;
+                       return 0;
+               }
+
+               public static int test_0_byte_enum_hashcode ()
+               {
+                       if (ByteEnum.A.GetHashCode () != EqualityComparer<ByteEnum>.Default.GetHashCode (ByteEnum.A))
+                               return 1;
+                       if (ByteEnum.A.GetHashCode () != ((byte)ByteEnum.A).GetHashCode () )
+                               return 2;
+                       return 0;
+               }
+
+               public static int test_0_sbyte_enum_hashcode ()
+               {
+                       if (SByteEnum.A.GetHashCode () != EqualityComparer<SByteEnum>.Default.GetHashCode (SByteEnum.A))
+                               return 1;
+                       if (SByteEnum.A.GetHashCode () != ((sbyte)SByteEnum.A).GetHashCode () )
+                               return 2;
+                       return 0;
+               }
+
+               public static int test_0_short_enum_hashcode ()
+               {
+                       if (ShortEnum.A.GetHashCode () != EqualityComparer<ShortEnum>.Default.GetHashCode (ShortEnum.A))
+                               return 1;
+                       if (ShortEnum.A.GetHashCode () != ((short)ShortEnum.A).GetHashCode () )
+                               return 2;
+                       return 0;
+               }
+
+               public static int test_0_ushort_enum_hashcode ()
+               {
+                       if (UShortEnum.A.GetHashCode () != EqualityComparer<UShortEnum>.Default.GetHashCode (UShortEnum.A))
+                               return 1;
+                       if (UShortEnum.A.GetHashCode () != ((ushort)UShortEnum.A).GetHashCode () )
+                               return 2;
+                       return 0;
+               }
+
+               public static int test_0_int_enum_hashcode ()
+               {
+                       if (IntEnum.A.GetHashCode () != EqualityComparer<IntEnum>.Default.GetHashCode (IntEnum.A))
+                               return 1;
+                       if (IntEnum.A.GetHashCode () != ((int)IntEnum.A).GetHashCode () )
+                               return 2;
+                       return 0;
+               }
+
+               public static int test_0_uint_enum_hashcode ()
+               {
+                       if (UIntEnum.A.GetHashCode () != EqualityComparer<UIntEnum>.Default.GetHashCode (UIntEnum.A))
+                               return 1;
+                       if (UIntEnum.A.GetHashCode () != ((uint)UIntEnum.A).GetHashCode () )
+                               return 2;
+                       return 0;
+               }
+
+               public static int test_0_long_enum_hashcode ()
+               {
+                       if (LongEnum.A.GetHashCode () != EqualityComparer<LongEnum>.Default.GetHashCode (LongEnum.A))
+                               return 1;
+                       if (LongEnum.A.GetHashCode () != ((long)LongEnum.A).GetHashCode () )
+                               return 2;
+                       return 0;
+               }
+
+               public static int test_0_ulong_enum_hashcode ()
+               {
+                       if (ULongEnum.A.GetHashCode () != EqualityComparer<ULongEnum>.Default.GetHashCode (ULongEnum.A))
+                               return 1;
+                       if (ULongEnum.A.GetHashCode () != ((ulong)ULongEnum.A).GetHashCode () )
+                               return 2;
+                       return 0;
+               }
+
+               public static int Main (String[] args) {
+                       return TestDriver.RunTests (typeof (Tests), args);
+               }
 
-public class test {
-       public static int Main () {
-               YaddaYadda val = YaddaYadda.dadoom;
-               byteenum be = byteenum.one;
-               if (val != YaddaYadda.dadoom)
-                       return 1;
-               if (be != (byteenum)1)
-                       return 2;
-               return 0;
        }
-}
 
 }