Fix mdarrays (#39984)
authorDavid Wrighton <davidwr@microsoft.com>
Tue, 28 Jul 2020 17:07:39 +0000 (10:07 -0700)
committerGitHub <noreply@github.com>
Tue, 28 Jul 2020 17:07:39 +0000 (10:07 -0700)
- MDArrays are special and method references to the methods needs to be encoded with their type in more cases than normal methods
- Only generate the owner type if the memberref does not encode the same owner type
- Add tests for various multidimensional array scenarios to crossgen2smoke

Fix issue #38260

src/coreclr/src/tools/Common/Compiler/TypeExtensions.cs
src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs
src/coreclr/tests/issues.targets
src/tests/readytorun/crossgen2/Program.cs
src/tests/readytorun/crossgen2/helperildll.il

index 89fd08f..dd54a13 100644 (file)
@@ -97,6 +97,19 @@ namespace ILCompiler
             return arrayMethod != null && arrayMethod.Kind == ArrayMethodKind.Address;
         }
 
+
+        /// <summary>
+        /// Returns true if '<paramref name="method"/>' is one of the special methods on multidimensional array types (set, get, address).
+        /// </summary>
+        public static bool IsArrayMethod(this MethodDesc method)
+        {
+            var arrayMethod = method as ArrayMethod;
+            return arrayMethod != null && (arrayMethod.Kind == ArrayMethodKind.Address || 
+                                           arrayMethod.Kind == ArrayMethodKind.Get || 
+                                           arrayMethod.Kind == ArrayMethodKind.Set || 
+                                           arrayMethod.Kind == ArrayMethodKind.Ctor);
+        }
+
         /// <summary>
         /// Gets a value indicating whether this type has any generic virtual methods.
         /// </summary>
index 54b9849..fd80e7e 100644 (file)
@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
 
 using Internal.TypeSystem;
 using Internal.TypeSystem.Ecma;
@@ -441,11 +442,18 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
 
                             // Owner type is needed for type specs to instantiating stubs or generics with signature variables still present
                             if (!method.Method.OwningType.IsDefType &&
-                                ((flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0 || method.Method.OwningType.ContainsSignatureVariables())
-                                || method.Method.IsArrayAddressMethod())
+                                ((flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0 || method.Method.OwningType.ContainsSignatureVariables()))
                             {
                                 flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType;
                             }
+                            else if (method.Method.IsArrayMethod())
+                            {
+                                var memberRefMethod = method.Token.Module.GetMethod(MetadataTokens.EntityHandle((int)method.Token.Token));
+                                if (memberRefMethod.OwningType != method.Method.OwningType)
+                                {
+                                    flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType;
+                                }
+                            }
 
                             EmitUInt(flags);
                             if ((flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0)
index e33de4d..ac5adde 100644 (file)
         <ExcludeList Include="$(XunitTestBinBase)/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded/*">
             <Issue>https://github.com/dotnet/runtime/issues/38096</Issue>
         </ExcludeList>
-        <ExcludeList Include="$(XunitTestBinBase)/JIT/Generics/Arrays/TypeParameters/MultiDim/**/*">
-            <Issue>https://github.com/dotnet/runtime/issues/38260</Issue>
-        </ExcludeList>
         <ExcludeList Include="$(XunitTestBinBase)/JIT/Methodical/tailcall_v4/hijacking/*">
             <Issue>https://github.com/dotnet/runtime/issues/7597</Issue>
         </ExcludeList>
         <ExcludeList Include="$(XunitTestBinBase)/Loader/binding/tracing/BinderTracingTest.Basic/*">
             <Issue>https://github.com/dotnet/runtime/issues/38290</Issue>
         </ExcludeList>
-        <ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/regressions/Dev12_518401/dev12_518401/dev12_518401/*">
-            <Issue>https://github.com/dotnet/runtime/issues/38260</Issue>
-        </ExcludeList>
         <ExcludeList Include="$(XunitTestBinBase)/tracing/tracevalidation/tracelogging/tracelogging/*">
             <Issue>https://github.com/dotnet/runtime/issues/32728</Issue>
         </ExcludeList>
index bd00957..e2da743 100644 (file)
@@ -1626,6 +1626,145 @@ internal class Program
         return true;
     }
 
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static bool CheckArrayVal<T>(ref T refVal, T testValue) where T:IEquatable<T>
+    {
+        return refVal.Equals(testValue);
+    }
+
+    struct SomeLargeStruct : IEquatable<SomeLargeStruct>
+    {
+        public SomeLargeStruct(int _xVal)
+        {
+            x = _xVal;
+            y = 0;
+            z = 0;
+            w = 0;
+        }
+        public int x;
+        public int y;
+        public int z;
+        public int w;
+
+        public bool Equals(SomeLargeStruct other)
+        {
+            return (x == other.x) && (y == other.y) && (z == other.z) && (w == other.w);
+        }
+        public override bool Equals(object other)
+        {
+            return Equals((SomeLargeStruct)other);
+        }
+
+        public override int GetHashCode() { return x; }
+    }
+
+    class SomeClass : IEquatable<SomeClass>
+    {
+        public SomeClass(int _xVal)
+        {
+            x = _xVal;
+            y = 0;
+            z = 0;
+            w = 0;
+        }
+        public int x;
+        public int y;
+        public int z;
+        public int w;
+
+        public bool Equals(SomeClass other)
+        {
+            return (x == other.x) && (y == other.y) && (z == other.z) && (w == other.w);
+        }
+        public override bool Equals(object other)
+        {
+            return Equals((SomeClass)other);
+        }
+
+        public override int GetHashCode() { return x; }
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static bool DoLargeStructMDArrayTest(SomeLargeStruct testValue)
+    {
+        SomeLargeStruct[,] array = new SomeLargeStruct[2,2];
+        array[0,0] = testValue;
+        if (!CheckArrayVal(ref array[0,0], testValue))
+        {
+            return false;
+        }
+        if (!testValue.Equals(array[0,0]))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static bool DoGenericArrayTest<T> (T testValue) where T:IEquatable<T>
+    {
+        T[,] array = new T[2,2];
+        array[0,0] = testValue;
+        if (!CheckArrayVal(ref array[0,0], testValue))
+        {
+            return false;
+        }
+        if (!testValue.Equals(array[0,0]))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static bool TestGenericMDArrayBehavior()
+    {
+        if (!DoGenericArrayTest<int>(42))
+        {
+            return false;
+        }
+
+        if (!DoGenericArrayTest<SomeClass>(new SomeClass(42)))
+        {
+            return false;
+        }
+
+        SomeLargeStruct testStruct = new SomeLargeStruct(42);
+        if (!DoGenericArrayTest<SomeLargeStruct>(testStruct))
+        {
+            return false;
+        }
+
+        if (!DoLargeStructMDArrayTest(testStruct))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    private static bool ArrayLdtokenTests()
+    {
+        // We're testing that mapping from ldtoken to RuntimeMethodHandle works for various ways that 
+        // ldtokens can be referenced (either via a generic token, or not.
+        // (there are slightly different codepaths in crossgen for this)
+        // Incorrect encoding will trigger a BadImageFormatException
+        RuntimeMethodHandle rmhCtor1 = default(RuntimeMethodHandle);
+        RuntimeMethodHandle rmhCtor2 = default(RuntimeMethodHandle);
+        RuntimeMethodHandle rmhSet = default(RuntimeMethodHandle);
+        RuntimeMethodHandle rmhGet = default(RuntimeMethodHandle);
+        RuntimeMethodHandle rmhAddress = default(RuntimeMethodHandle);
+        HelperGenericILCode<string>.LdTokenArrayMethods(ref rmhCtor1, ref rmhCtor2, ref rmhSet, ref rmhGet, ref rmhAddress);
+        HelperGenericILCode<object>.LdTokenArrayMethods(ref rmhCtor1, ref rmhCtor2, ref rmhSet, ref rmhGet, ref rmhAddress);
+        HelperILCode.LdTokenArrayMethodsInt(ref rmhCtor1, ref rmhCtor2, ref rmhSet, ref rmhGet, ref rmhAddress);
+        HelperILCode.LdTokenArrayMethodsString(ref rmhCtor1, ref rmhCtor2, ref rmhSet, ref rmhGet, ref rmhAddress);
+
+        return true;
+    }
+
     public static int Main(string[] args)
     {
         _passedTests = new List<string>();
@@ -1691,6 +1830,8 @@ internal class Program
         RunTest("ExplicitlySizedStructTest", ExplicitlySizedStructTest());
         RunTest("ExplicitlySizedClassTest", ExplicitlySizedClassTest());
         RunTest("GenericLdtokenTest", GenericLdtokenTest());
+        RunTest("ArrayLdtokenTests", ArrayLdtokenTests());
+        RunTest("TestGenericMDArrayBehavior", TestGenericMDArrayBehavior());
 
         File.Delete(TextFileName);
 
index 4a41944..752d16c 100644 (file)
      ret
   }
 
+  .method public hidebysig static  void LdTokenArrayMethodsInt(valuetype[mscorlib]System.RuntimeMethodHandle& ctor, valuetype[mscorlib]System.RuntimeMethodHandle& ctor2, valuetype[mscorlib]System.RuntimeMethodHandle& set, valuetype[mscorlib]System.RuntimeMethodHandle& get, valuetype[mscorlib]System.RuntimeMethodHandle& address)
+  {
+     ldarg 0
+     ldtoken method instance void int32[,]::.ctor(int32, int32, int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 1
+     ldtoken method instance void int32[,]::.ctor(int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 2
+     ldtoken method instance void int32[,]::Set(int32, int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 3
+     ldtoken method instance int32 int32[,]::Get(int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 4
+     ldtoken method instance int32& int32[,]::Address(int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+     ret
+  }
+
+  .method public hidebysig static  void LdTokenArrayMethodsString(valuetype[mscorlib]System.RuntimeMethodHandle& ctor, valuetype[mscorlib]System.RuntimeMethodHandle& ctor2, valuetype[mscorlib]System.RuntimeMethodHandle& set, valuetype[mscorlib]System.RuntimeMethodHandle& get, valuetype[mscorlib]System.RuntimeMethodHandle& address)
+  {
+     ldarg 0
+     ldtoken method instance void string[,]::.ctor(int32, int32, int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 1
+     ldtoken method instance void string[,]::.ctor(int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 2
+     ldtoken method instance void string[,]::Set(int32, int32, string)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 3
+     ldtoken method instance string string[,]::Get(int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 4
+     ldtoken method instance string& string[,]::Address(int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+     ret
+  }
+
+
   .method public hidebysig static valuetype[mscorlib]System.RuntimeMethodHandle ForceStuffToBeCompiled() cil managed noinlining
   {
      call valuetype[mscorlib]System.RuntimeMethodHandle class HelperGenericILCode`1<object>::GetGenericFunctionMethodHandle<valuetype [helperdll]GenericStructForLdtoken`1<string>>()
      call valuetype[mscorlib]System.RuntimeMethodHandle HelperILCode::GetGenericFunctionMethodHandle<string>()
      ret
   }
+  .method public hidebysig static void ForceStuffToBeCompiled3(valuetype[mscorlib]System.RuntimeMethodHandle& ctor, valuetype[mscorlib]System.RuntimeMethodHandle& ctor2, valuetype[mscorlib]System.RuntimeMethodHandle& set, valuetype[mscorlib]System.RuntimeMethodHandle& get, valuetype[mscorlib]System.RuntimeMethodHandle& address) cil managed noinlining
+  {
+     ldarg 0
+     ldarg 1
+     ldarg 2
+     ldarg 3
+     ldarg 4
+     call void class HelperGenericILCode`1<object>::LdTokenArrayMethods(valuetype[mscorlib]System.RuntimeMethodHandle&, valuetype[mscorlib]System.RuntimeMethodHandle&, valuetype[mscorlib]System.RuntimeMethodHandle&, valuetype[mscorlib]System.RuntimeMethodHandle&, valuetype[mscorlib]System.RuntimeMethodHandle&)
+     ldarg 0
+     ldarg 1
+     ldarg 2
+     ldarg 3
+     ldarg 4
+     call void class HelperGenericILCode`1<int32>::LdTokenArrayMethods(valuetype[mscorlib]System.RuntimeMethodHandle& ctor, valuetype[mscorlib]System.RuntimeMethodHandle& ctor2, valuetype[mscorlib]System.RuntimeMethodHandle& set, valuetype[mscorlib]System.RuntimeMethodHandle& get, valuetype[mscorlib]System.RuntimeMethodHandle& address)
+     ret
+  }
 }
 
 .class auto ansi public beforefieldinit HelperGenericILCode`1<T>
      ldtoken method instance int32 valuetype [helperdll]GenericStructForLdtoken`1<!0>::GenericFunction<!!0>(!0, !!0, string, int32)
      ret
   }
+  .method public hidebysig static  void LdTokenArrayMethods(valuetype[mscorlib]System.RuntimeMethodHandle& ctor, valuetype[mscorlib]System.RuntimeMethodHandle& ctor2, valuetype[mscorlib]System.RuntimeMethodHandle& set, valuetype[mscorlib]System.RuntimeMethodHandle& get, valuetype[mscorlib]System.RuntimeMethodHandle& address)
+  {
+     ldarg 0
+     ldtoken method instance void !0[,]::.ctor(int32, int32, int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 1
+     ldtoken method instance void !0[,]::.ctor(int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 2
+     ldtoken method instance void !0[,]::Set(int32, int32, !0)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 3
+     ldtoken method instance !0 !0[,]::Get(int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+
+     ldarg 4
+     ldtoken method instance !0& !0[,]::Address(int32, int32)
+     stobj valuetype[mscorlib]System.RuntimeMethodHandle
+     ret
+  }
 }