diff options
5 files changed, 117 insertions, 5 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs index cff7fe56f..31a0f6821 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GVMDependenciesNode.cs @@ -20,6 +20,8 @@ namespace ILCompiler.DependencyAnalysis /// </summary> public class GVMDependenciesNode : DependencyNodeCore<NodeFactory> { + private const int UniversalCanonGVMDepthHeuristic_NonCanonDepth = 2; + private const int UniversalCanonGVMDepthHeuristic_CanonDepth = 2; private readonly MethodDesc _method; public MethodDesc Method => _method; @@ -61,6 +63,12 @@ namespace ILCompiler.DependencyAnalysis { MethodDesc canonMethodTarget = instantiatedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (context.TypeSystemContext.SupportsUniversalCanon && canonMethodTarget.IsGenericDepthGreaterThan(UniversalCanonGVMDepthHeuristic_CanonDepth)) + { + // fall back to using the universal generic variant of the generic method + yield break; + } + bool getUnboxingStub = instantiatedMethod.OwningType.IsValueType; yield return new DependencyListEntry(context.MethodEntrypoint(canonMethodTarget, getUnboxingStub), "GVM Dependency - Canon method"); @@ -68,15 +76,17 @@ namespace ILCompiler.DependencyAnalysis { // Dependency includes the generic method dictionary of the instantiation, and all its dependencies. This is done by adding the // ShadowConcreteMethod to the list of dynamic dependencies. The generic dictionary will be reported as a dependency of the ShadowConcreteMethod - // TODO: detect large recursive generics and fallback to USG templates Debug.Assert(!instantiatedMethod.IsCanonicalMethod(CanonicalFormKind.Any)); + + if (context.TypeSystemContext.SupportsUniversalCanon && instantiatedMethod.IsGenericDepthGreaterThan(UniversalCanonGVMDepthHeuristic_NonCanonDepth)) + { + // fall back to using the universal generic variant of the generic method + yield break; + } + yield return new DependencyListEntry(context.ShadowConcreteMethod(instantiatedMethod), "GVM Dependency - Dictionary"); } } - else - { - // TODO: universal generics - } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index 915ceb88d..6c1be4884 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -992,6 +992,16 @@ namespace ILCompiler.DependencyAnalysis } } + if (_type.BaseType != null && !_type.BaseType.IsRuntimeDeterminedSubtype) + { + TypeDesc baseType = _type.BaseType; + do + { + yield return new DependencyListEntry(context.MaximallyConstructableType(baseType), "base types of canonical types must have their full vtables"); + baseType = baseType.BaseType; + } while (baseType != null); + } + if (_type.BaseType != null && _type.BaseType.IsRuntimeDeterminedSubtype) { yield return new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(_type.BaseType)), "template base type"); @@ -1034,6 +1044,12 @@ namespace ILCompiler.DependencyAnalysis else { typeForFieldLayout = new DependencyListEntry(context.NativeLayout.PlacedSignatureVertex(context.NativeLayout.TypeSignatureVertex(field.FieldType)), "universal field layout type"); + + // And ensure the type can be properly laid out + foreach (var dependency in context.NativeLayout.TemplateConstructableTypes(field.FieldType)) + { + yield return new DependencyListEntry(dependency, "template construction dependency"); + } } yield return typeForFieldLayout; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs index 196e7836d..5392ad1cd 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.NativeLayout.cs @@ -229,6 +229,13 @@ namespace ILCompiler.DependencyAnalysis TypeDesc canonicalType = type.ConvertToCanonForm(CanonicalFormKind.Specific); yield return _factory.MaximallyConstructableType(canonicalType); + // Add a dependency on the template for this type, if the canonical type should be generated into this binary. + if (canonicalType.IsCanonicalSubtype(CanonicalFormKind.Any) && !_factory.NecessaryTypeSymbol(canonicalType).RepresentsIndirectionCell) + { + if (!_factory.TypeSystemContext.IsCanonicalDefinitionType(canonicalType, CanonicalFormKind.Any)) + yield return _factory.NativeLayout.TemplateTypeLayout(canonicalType); + } + foreach (TypeDesc instantiationType in type.Instantiation) { foreach (var dependency in TemplateConstructableTypes(instantiationType)) diff --git a/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs b/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs index 57cfa1786..15070a4b4 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -76,5 +77,41 @@ namespace ILCompiler { return method.IsPInvoke && ((method is Internal.IL.Stubs.PInvokeTargetNativeMethod) || Internal.IL.McgInteropSupport.IsPregeneratedInterop(method)); } + + /// <summary> + /// What is the maximum number of steps that need to be taken from this type to its most contained generic type. + /// i.e. + /// SomeGenericType<System.Int32>.Method<System.Int32> => 1 + /// SomeType.Method<System.Int32> => 0 + /// SomeType.Method<List<System.Int32>> => 1 + /// </summary> + public static int GetGenericDepth(this MethodDesc method) + { + int genericDepth = method.OwningType.GetGenericDepth(); + foreach (TypeDesc type in method.Instantiation) + { + genericDepth = Math.Max(genericDepth, type.GetGenericDepth()); + } + return genericDepth; + } + + /// <summary> + /// Determine if a type has a generic depth greater than a given value + /// </summary> + /// <param name="depth"></param> + /// <returns></returns> + public static bool IsGenericDepthGreaterThan(this MethodDesc method, int depth) + { + if (method.OwningType.IsGenericDepthGreaterThan(depth)) + return true; + + foreach (TypeDesc type in method.Instantiation) + { + if (type.IsGenericDepthGreaterThan(depth)) + return true; + } + + return false; + } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs index 49719733f..5cf56d172 100644 --- a/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs +++ b/src/ILCompiler.Compiler/src/Compiler/TypeExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using Internal.IL; using Internal.TypeSystem; @@ -141,5 +142,46 @@ namespace ILCompiler Debug.Assert(false); return -1; } + + /// <summary> + /// What is the maximum number of steps that need to be taken from this type to its most contained generic type. + /// i.e. + /// System.Int32 => 0 + /// List<System.Int32> => 1 + /// Dictionary<System.Int32,System.Int32> => 1 + /// Dictionary<List<System.Int32>,<System.Int32> => 2 + /// </summary> + public static int GetGenericDepth(this TypeDesc type) + { + if (type.HasInstantiation) + { + int maxGenericDepthInInstantiation = 0; + foreach (TypeDesc instantiationType in type.Instantiation) + { + maxGenericDepthInInstantiation = Math.Max(instantiationType.GetGenericDepth(), maxGenericDepthInInstantiation); + } + + return maxGenericDepthInInstantiation + 1; + } + + return 0; + } + + /// <summary> + /// Determine if a type has a generic depth greater than a given value + /// </summary> + public static bool IsGenericDepthGreaterThan(this TypeDesc type, int depth) + { + if (depth < 0) + return true; + + foreach (TypeDesc instantiationType in type.Instantiation) + { + if (instantiationType.IsGenericDepthGreaterThan(depth - 1)) + return true; + } + + return false; + } } } |