Skip to content

Commit

Permalink
Limit crossgen2 instantiating depth (dotnet#791)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
janvorli authored Dec 12, 2019
1 parent 3951076 commit e8a90ce
Showing 1 changed file with 36 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,31 @@ private static bool IsDynamicStatics(TypeDesc type)
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,
Expand Down Expand Up @@ -928,6 +953,17 @@ private void ceeInfoGetCallInfo(
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
Expand Down

0 comments on commit e8a90ce

Please sign in to comment.