Fix failure to generate ngen pdb in crossgen (#89415)
authorDavid Wrighton <davidwr@microsoft.com>
Tue, 25 Jul 2023 21:30:50 +0000 (14:30 -0700)
committerGitHub <noreply@github.com>
Tue, 25 Jul 2023 21:30:50 +0000 (14:30 -0700)
* Fix issue in R2R compilation where we would compile a method we could not generate a name for
- This is caused by the the type of a parameter having a generic parameter which derived from a type not found in the compilation information
- There are 2 fixes here
  1. Change the logic for computing the set of compilable methods to require that all parameters are loadable
  2. Change the implementation of loadable types to check generic instantiations for loadability
    - This required implementing a stack system to check types for loadability, as loading can require circular references to be resolved.
    - Add a check that the instantiation types of an instantiated generic are loadable.
    - Generic interfaces on arrays work now, so enable the the constraint checking logic.

src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Validation.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs
src/tests/readytorun/regressions/GitHub_89337/GitHub_89337.cs [new file with mode: 0644]
src/tests/readytorun/regressions/GitHub_89337/GitHub_89337.il [new file with mode: 0644]
src/tests/readytorun/regressions/GitHub_89337/GitHub_89337.ilproj [new file with mode: 0644]

index 018f576..9a9bc97 100644 (file)
@@ -1,6 +1,8 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System;
+using System.Collections.Generic;
 using Internal.TypeSystem;
 
 using Debug = System.Diagnostics.Debug;
@@ -11,13 +13,180 @@ namespace ILCompiler
     // in unpredictable spots.
     public partial class CompilerTypeSystemContext
     {
+        [ThreadStatic]
+        private static List<TypeLoadabilityCheckInProgress> t_typeLoadCheckInProgressStack;
+        private static List<TypeDesc> EmptyList = new List<TypeDesc>();
+        private readonly ValidTypeHashTable _validTypes = new ValidTypeHashTable();
+
         /// <summary>
         /// Ensures that the type can be fully loaded. The method will throw one of the type system
         /// exceptions if the type is not loadable.
         /// </summary>
         public void EnsureLoadableType(TypeDesc type)
         {
-            _validTypes.GetOrCreateValue(type);
+            if (type == null)
+                return;
+
+            if (_validTypes.Contains(type))
+                return;
+
+            // Use a scheme where we push a stack of types in the process of loading
+            // When the stack pops, without throwing an exception, the type will be marked as being detected as successfully loadable.
+            // We need this complex scheme, as types can have circular dependencies. In addition, due to circular references, we can
+            // be forced to move when a type is successfully marked as loaded up the stack to an earlier call to EnsureLoadableType
+            //
+            // For example, consider the following case:
+            // interface IInterface<T> {}
+            // interface IPassThruInterface<T> : IInterface<T> {}
+            // interface ISimpleInterface {}
+            // class A<T> : IInterface<A<T>>, IPassThruInterface<A<T>>, ISimpleInterface {}
+            // class B : A<B> {}
+            //
+            // We call EnsureLoadableType on B
+            //
+            // This will generate the following interesting stacks of calls to EnsureLoadableType
+            //
+            // B -> A<B> -> B
+            //  This stack indicates that A<B> can only be considered loadable if B is considered loadable, so we must defer marking
+            //  A<B> as loadable until we finish processing B.
+            //
+            // B -> A<B> -> ISimpleInterface
+            //  Since examining ISimpleInterface does not have any dependency on B or A<B>, it can be marked as loadable as soon
+            //  as we finish processing it.
+            //
+            // B -> A<B> -> IInterface<A<B>> -> A<B>
+            //  This stack indicates that IInterface<A<B>> can be considered loadable if A<B> is considered loadable. We must defer
+            //  marking IInterface<A<B>> as loadable until we are able to mark A<B> as loadable. Based on the stack above, that can
+            //  only happen once B is considered loadable.
+            //
+            // B -> A<B> -> IPassthruInterface<A<B>> -> IInterface<A<B>>
+            //  This stack indicates that IPassthruInterface<A<B>> can be considered loadable if IInterface<A<B>> is considered
+            //  loadable. If this happens after the IInterface<A<B>> is marked as being loadable once B is considered loadable
+            //  then we will push the loadibility marking to the B level at this point. OR we will continue to recurse and the logic
+            //  will note that IInterface<A<B>> needs A<B> needs B which will move the marking up at that point.
+
+            if (PushTypeLoadInProgress(type))
+                return;
+
+            bool threwException = true;
+            try
+            {
+                EnsureLoadableTypeUncached(type);
+                threwException = false;
+            }
+            finally
+            {
+                PopTypeLoadabilityCheckInProgress(threwException);
+            }
+        }
+
+        private sealed class TypeLoadabilityCheckInProgress
+        {
+            public TypeDesc TypeInLoadabilityCheck;
+            public bool MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown;
+            public List<TypeDesc> OtherTypesToMarkAsSuccessfullyLoaded;
+
+            public void AddToOtherTypesToMarkAsSuccessfullyLoaded(TypeDesc type)
+            {
+                if (OtherTypesToMarkAsSuccessfullyLoaded == EmptyList)
+                {
+                    OtherTypesToMarkAsSuccessfullyLoaded = new List<TypeDesc>();
+                }
+
+                Debug.Assert(!OtherTypesToMarkAsSuccessfullyLoaded.Contains(type));
+                OtherTypesToMarkAsSuccessfullyLoaded.Add(type);
+            }
+        }
+
+        // Returns true to indicate the type should be considered to be loadable (although it might not be, actually safety may require more code to execute)
+        private static bool PushTypeLoadInProgress(TypeDesc type)
+        {
+            t_typeLoadCheckInProgressStack ??= new List<TypeLoadabilityCheckInProgress>();
+
+            // Walk stack to see if the specified type is already in the process of being type checked.
+            int typeLoadCheckInProgressStackOffset = -1;
+            bool checkingMode = false; // Checking for match on TypeLoadabilityCheck field or in OtherTypesToMarkAsSuccessfullyLoaded. (true for OtherTypesToMarkAsSuccessfullyLoaded)
+            for (int typeCheckDepth = t_typeLoadCheckInProgressStack.Count - 1; typeCheckDepth >= 0; typeCheckDepth--)
+            {
+                if (t_typeLoadCheckInProgressStack[typeCheckDepth].TypeInLoadabilityCheck == type)
+                {
+                    // The stack contains the interesting type.
+                    if (t_typeLoadCheckInProgressStack[typeCheckDepth].MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown)
+                    {
+                        // And this is the level where the type is known to be successfully loaded.
+                        typeLoadCheckInProgressStackOffset = typeCheckDepth;
+                        break;
+                    }
+                }
+                else if (t_typeLoadCheckInProgressStack[typeCheckDepth].OtherTypesToMarkAsSuccessfullyLoaded.Contains(type))
+                {
+                    // We've found where the type will be marked as successfully loaded.
+                    typeLoadCheckInProgressStackOffset = typeCheckDepth;
+                    break;
+                }
+            }
+
+            if (checkingMode)
+            {
+                // If we enabled checkingMode we should always have found the type
+                Debug.Assert(typeLoadCheckInProgressStackOffset != -1);
+            }
+
+            if (typeLoadCheckInProgressStackOffset == -1)
+            {
+                // The type is not already in the process of being checked for loadability, so return false to indicate that normal load checking should begin
+                TypeLoadabilityCheckInProgress typeCheckInProgress = new TypeLoadabilityCheckInProgress();
+                typeCheckInProgress.TypeInLoadabilityCheck = type;
+                typeCheckInProgress.OtherTypesToMarkAsSuccessfullyLoaded = EmptyList;
+                typeCheckInProgress.MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown = true;
+                t_typeLoadCheckInProgressStack.Add(typeCheckInProgress);
+                return false;
+            }
+
+            // Move timing of when types are considered loaded back to the point at which we mark this type as loaded
+            var typeLoadCheckToAddTo = t_typeLoadCheckInProgressStack[typeLoadCheckInProgressStackOffset];
+            for (int typeCheckDepth = t_typeLoadCheckInProgressStack.Count - 1; typeCheckDepth > typeLoadCheckInProgressStackOffset; typeCheckDepth--)
+            {
+                if (t_typeLoadCheckInProgressStack[typeCheckDepth].MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown)
+                {
+                    typeLoadCheckToAddTo.AddToOtherTypesToMarkAsSuccessfullyLoaded(t_typeLoadCheckInProgressStack[typeCheckDepth].TypeInLoadabilityCheck);
+                    t_typeLoadCheckInProgressStack[typeCheckDepth].MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown = false;
+                }
+
+                foreach (var typeToMove in t_typeLoadCheckInProgressStack[typeCheckDepth].OtherTypesToMarkAsSuccessfullyLoaded)
+                {
+                    typeLoadCheckToAddTo.AddToOtherTypesToMarkAsSuccessfullyLoaded(typeToMove);
+                }
+
+                t_typeLoadCheckInProgressStack[typeCheckDepth].OtherTypesToMarkAsSuccessfullyLoaded = EmptyList;
+            }
+
+            // We are going to report that the type should be considered to be loadable at this stage
+            return true;
+        }
+
+        private void PopTypeLoadabilityCheckInProgress(bool exceptionThrown)
+        {
+            Debug.Assert(EmptyList.Count == 0);
+            var typeLoadabilityCheck = t_typeLoadCheckInProgressStack[t_typeLoadCheckInProgressStack.Count - 1];
+            t_typeLoadCheckInProgressStack.RemoveAt(t_typeLoadCheckInProgressStack.Count - 1);
+
+            if (!exceptionThrown)
+            {
+                if (!typeLoadabilityCheck.MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown)
+                {
+                    Debug.Assert(typeLoadabilityCheck.OtherTypesToMarkAsSuccessfullyLoaded.Count == 0);
+                }
+
+                if (typeLoadabilityCheck.MarkTypeAsSuccessfullyLoadedIfNoExceptionThrown)
+                {
+                    _validTypes.GetOrCreateValue(typeLoadabilityCheck.TypeInLoadabilityCheck);
+                    foreach (var type in typeLoadabilityCheck.OtherTypesToMarkAsSuccessfullyLoaded)
+                    {
+                        _validTypes.GetOrCreateValue(type);
+                    }
+                }
+            }
         }
 
         public void EnsureLoadableMethod(MethodDesc method)
@@ -37,11 +206,10 @@ namespace ILCompiler
         {
             protected override bool CompareKeyToValue(TypeDesc key, TypeDesc value) => key == value;
             protected override bool CompareValueToValue(TypeDesc value1, TypeDesc value2) => value1 == value2;
-            protected override TypeDesc CreateValueFromKey(TypeDesc key) => EnsureLoadableTypeUncached(key);
+            protected override TypeDesc CreateValueFromKey(TypeDesc key) => key;
             protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode();
             protected override int GetValueHashCode(TypeDesc value) => value.GetHashCode();
         }
-        private readonly ValidTypeHashTable _validTypes = new ValidTypeHashTable();
 
         private static TypeDesc EnsureLoadableTypeUncached(TypeDesc type)
         {
@@ -53,7 +221,11 @@ namespace ILCompiler
                 }
             }
 
-            if (type.IsParameterizedType)
+            if (type.IsGenericParameter)
+            {
+                // Generic parameters don't need validation
+            }
+            else if (type.IsParameterizedType)
             {
                 // Validate parameterized types
                 var parameterizedType = (ParameterizedType)type;
@@ -161,16 +333,14 @@ namespace ILCompiler
                     {
                         ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
                     }
+
+                    ((CompilerTypeSystemContext)type.Context).EnsureLoadableType(typeArg);
                 }
 
-                // Don't validate constraints with crossgen2 - the type system is not set up correctly
-                // and doesn't see generic interfaces on arrays.
-#if !READYTORUN
                 if (!defType.IsCanonicalSubtype(CanonicalFormKind.Any) && !defType.CheckConstraints())
                 {
                     ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
                 }
-#endif
 
                 // Check the type doesn't have bogus MethodImpls or overrides and we can get the finalizer.
                 defType.GetFinalizer();
index dd977c2..ac79e7e 100644 (file)
@@ -119,6 +119,8 @@ namespace ILCompiler
                     ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
                 }
             }
+
+            ((CompilerTypeSystemContext)type.Context).EnsureLoadableType(type);
         }
 
         private static Instantiation GetInstantiationThatMeetsConstraints(Instantiation definition)
diff --git a/src/tests/readytorun/regressions/GitHub_89337/GitHub_89337.cs b/src/tests/readytorun/regressions/GitHub_89337/GitHub_89337.cs
new file mode 100644 (file)
index 0000000..73cf62d
--- /dev/null
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// This is the C# file used to generate GitHub_89337.il
+
+class GitHub_89337
+{
+    class Generic<T>
+    {
+    }
+
+    class Derived : System.Xml.NameTable { }
+    static Generic<Derived>? Passthru(Generic<Derived>? param)
+    {
+        return param;
+    }
+
+    static int Main()
+    {
+        return 100;
+    }
+}
diff --git a/src/tests/readytorun/regressions/GitHub_89337/GitHub_89337.il b/src/tests/readytorun/regressions/GitHub_89337/GitHub_89337.il
new file mode 100644 (file)
index 0000000..9b67161
--- /dev/null
@@ -0,0 +1,206 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// This test checks that R2R binaries with non-findable dependencies can still build correctly.
+
+// The only difference between this file and GitHub_89337.cs, is that references to the System.Xml.ReaderWriter
+// assembly have been renamed to references to NonexistentAssembly
+
+//  .NET IL Disassembler.  Version 8.0.0-dev
+
+
+
+// Metadata version: v4.0.30319
+.assembly extern System.Runtime
+{
+  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
+  .ver 6:0:0:0
+}
+.assembly extern NonexistentAssembly
+{
+  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
+  .ver 6:0:0:0
+}
+.assembly GitHub_89337
+{
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
+                                                                                                                   63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
+
+  // --- The following custom attribute is added automatically, do not uncomment -------
+  //  .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
+
+  .custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 18 2E 4E 45 54 43 6F 72 65 41 70 70 2C 56   // ....NETCoreApp,V
+                                                                                                              65 72 73 69 6F 6E 3D 76 36 2E 30 01 00 54 0E 14   // ersion=v6.0..T..
+                                                                                                              46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61 79   // FrameworkDisplay
+                                                                                                              4E 61 6D 65 08 2E 4E 45 54 20 36 2E 30 )          // Name..NET 6.0
+  .custom instance void [System.Runtime]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 0C 47 69 74 48 75 62 5F 38 39 33 33 37 00   // ...GitHub_89337.
+                                                                                                      00 )
+  .custom instance void [System.Runtime]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 05 44 65 62 75 67 00 00 )                   // ...Debug..
+  .custom instance void [System.Runtime]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..
+  .custom instance void [System.Runtime]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 05 31 2E 30 2E 30 00 00 )                   // ...1.0.0..
+  .custom instance void [System.Runtime]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0C 47 69 74 48 75 62 5F 38 39 33 33 37 00   // ...GitHub_89337.
+                                                                                                      00 )
+  .custom instance void [System.Runtime]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0C 47 69 74 48 75 62 5F 38 39 33 33 37 00   // ...GitHub_89337.
+                                                                                                    00 )
+  .hash algorithm 0x00008004
+  .ver 1:0:0:0
+}
+.module GitHub_89337.dll
+// MVID: {9cd3bf31-7da9-45e9-a188-4b1492ff96cd}
+.imagebase 0x00400000
+.file alignment 0x00000200
+.stackreserve 0x00100000
+.subsystem 0x0003       // WINDOWS_CUI
+.corflags 0x00000001    //  ILONLY
+// Image base: 0x00000271F1C10000
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+
+.class private auto ansi sealed beforefieldinit Microsoft.CodeAnalysis.EmbeddedAttribute
+       extends [System.Runtime]System.Attribute
+{
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
+  .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 )
+  .method public hidebysig specialname rtspecialname
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [System.Runtime]System.Attribute::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method EmbeddedAttribute::.ctor
+
+} // end of class Microsoft.CodeAnalysis.EmbeddedAttribute
+
+.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.NullableAttribute
+       extends [System.Runtime]System.Attribute
+{
+  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
+  .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 )
+  .custom instance void [System.Runtime]System.AttributeUsageAttribute::.ctor(valuetype [System.Runtime]System.AttributeTargets) = ( 01 00 84 6B 00 00 02 00 54 02 0D 41 6C 6C 6F 77   // ...k....T..Allow
+                                                                                                                                     4D 75 6C 74 69 70 6C 65 00 54 02 09 49 6E 68 65   // Multiple.T..Inhe
+                                                                                                                                     72 69 74 65 64 00 )                               // rited.
+  .field public initonly uint8[] NullableFlags
+  .method public hidebysig specialname rtspecialname
+          instance void  .ctor(uint8 A_1) cil managed
+  {
+    // Code size       24 (0x18)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [System.Runtime]System.Attribute::.ctor()
+    IL_0006:  nop
+    IL_0007:  ldarg.0
+    IL_0008:  ldc.i4.1
+    IL_0009:  newarr     [System.Runtime]System.Byte
+    IL_000e:  dup
+    IL_000f:  ldc.i4.0
+    IL_0010:  ldarg.1
+    IL_0011:  stelem.i1
+    IL_0012:  stfld      uint8[] System.Runtime.CompilerServices.NullableAttribute::NullableFlags
+    IL_0017:  ret
+  } // end of method NullableAttribute::.ctor
+
+  .method public hidebysig specialname rtspecialname
+          instance void  .ctor(uint8[] A_1) cil managed
+  {
+    // Code size       15 (0xf)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [System.Runtime]System.Attribute::.ctor()
+    IL_0006:  nop
+    IL_0007:  ldarg.0
+    IL_0008:  ldarg.1
+    IL_0009:  stfld      uint8[] System.Runtime.CompilerServices.NullableAttribute::NullableFlags
+    IL_000e:  ret
+  } // end of method NullableAttribute::.ctor
+
+} // end of class System.Runtime.CompilerServices.NullableAttribute
+
+.class private auto ansi beforefieldinit GitHub_89337
+       extends [System.Runtime]System.Object
+{
+  .class auto ansi nested private beforefieldinit Generic`1<T>
+         extends [System.Runtime]System.Object
+  {
+    .param type T
+      .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 )
+    .method public hidebysig specialname rtspecialname
+            instance void  .ctor() cil managed
+    {
+      // Code size       8 (0x8)
+      .maxstack  8
+      IL_0000:  ldarg.0
+      IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
+      IL_0006:  nop
+      IL_0007:  ret
+    } // end of method Generic`1::.ctor
+
+  } // end of class Generic`1
+
+  .class auto ansi nested private beforefieldinit Derived
+         extends [NonexistentAssembly]System.Xml.NameTable
+  {
+    .method public hidebysig specialname rtspecialname
+            instance void  .ctor() cil managed
+    {
+      // Code size       8 (0x8)
+      .maxstack  8
+      IL_0000:  ldarg.0
+      IL_0001:  call       instance void [NonexistentAssembly]System.Xml.NameTable::.ctor()
+      IL_0006:  nop
+      IL_0007:  ret
+    } // end of method Derived::.ctor
+
+  } // end of class Derived
+
+  .method private hidebysig static class GitHub_89337/Generic`1<class GitHub_89337/Derived>
+          Passthru(class GitHub_89337/Generic`1<class GitHub_89337/Derived> param) cil managed
+  {
+    .param [0]
+    .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 02 01 00 00 )
+    .param [1]
+    .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 02 01 00 00 )
+    // Code size       7 (0x7)
+    .maxstack  1
+    .locals init (class GitHub_89337/Generic`1<class GitHub_89337/Derived> V_0)
+    IL_0000:  nop
+    IL_0001:  ldarg.0
+    IL_0002:  stloc.0
+    IL_0003:  br.s       IL_0005
+
+    IL_0005:  ldloc.0
+    IL_0006:  ret
+  } // end of method GitHub_89337::Passthru
+
+  .method private hidebysig static int32
+          Main() cil managed
+  {
+    .entrypoint
+    // Code size       8 (0x8)
+    .maxstack  1
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldc.i4.s   100
+    IL_0003:  stloc.0
+    IL_0004:  br.s       IL_0006
+
+    IL_0006:  ldloc.0
+    IL_0007:  ret
+  } // end of method GitHub_89337::Main
+
+  .method public hidebysig specialname rtspecialname
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method GitHub_89337::.ctor
+
+} // end of class GitHub_89337
\ No newline at end of file
diff --git a/src/tests/readytorun/regressions/GitHub_89337/GitHub_89337.ilproj b/src/tests/readytorun/regressions/GitHub_89337/GitHub_89337.ilproj
new file mode 100644 (file)
index 0000000..f51eb88
--- /dev/null
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="GitHub_89337.il" />
+  </ItemGroup>
+</Project>