Limit crossgen2 instantiating depth (#791)
authorJan Vorlicek <janvorli@microsoft.com>
Thu, 12 Dec 2019 17:24:12 +0000 (18:24 +0100)
committerGitHub <noreply@github.com>
Thu, 12 Dec 2019 17:24:12 +0000 (18:24 +0100)
Two coreclr pri1 tests were causing the crossgen2 to run until all
memory was depleeted due to the fact that we were not limiting depth of
instantiations of methods and those tests had recursive instantiation.
An example taken from one of the tests is this:
```
private static T meth<T>(int v, T x)
{
   //Recursive generic
   return ((v >= 0) ? meth<Tuple<T, T>>(v - 1, new Tuple<T, T>(x, x)).Field0 : x);
}
```

This change fixes it by adding the instantiation depth check for
function calls. If the level of instantiation for any of the generic
parameters exceeds a preset value (currently 10, taken from old
crossgen), the caller is not compiled by crossgen2. Such a method will
be compiled at runtime by JIT instead.

src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs

index e662812..1fafa05 100644 (file)
@@ -889,6 +889,31 @@ namespace Internal.JitInterface
             return false;
         }
 
+        private bool IsGenericTooDeeplyNested(Instantiation instantiation, int nestingLevel)
+        {
+            const int MaxInstatiationNesting = 10;
+
+            if (nestingLevel == MaxInstatiationNesting)
+            {
+                return true;
+            }
+
+            foreach (TypeDesc instantiationType in instantiation)
+            {
+                if (instantiationType.HasInstantiation && IsGenericTooDeeplyNested(instantiationType.Instantiation, nestingLevel + 1))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private bool IsGenericTooDeeplyNested(Instantiation instantiation)
+        {
+            return IsGenericTooDeeplyNested(instantiation, 0);
+        }
+
         private void ceeInfoGetCallInfo(
             ref CORINFO_RESOLVED_TOKEN pResolvedToken,
             CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken,
@@ -928,6 +953,17 @@ namespace Internal.JitInterface
             useInstantiatingStub = originalMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstMethodDescArg();
 
             callerMethod = HandleToObject(callerHandle);
+
+            if (originalMethod.HasInstantiation && IsGenericTooDeeplyNested(originalMethod.Instantiation))
+            {
+                throw new RequiresRuntimeJitException(callerMethod.ToString() + " -> " + originalMethod.ToString());
+            }
+
+            if (originalMethod.OwningType.HasInstantiation && IsGenericTooDeeplyNested(originalMethod.OwningType.Instantiation))
+            {
+                throw new RequiresRuntimeJitException(callerMethod.ToString() + " -> " + originalMethod.ToString());
+            }
+
             if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(callerMethod))
             {
                 // We must abort inline attempts calling from outside of the version bubble being compiled