diff options
author | David Wrighton <davidwr@microsoft.com> | 2017-10-25 00:31:50 +0300 |
---|---|---|
committer | David Wrighton <davidwr@microsoft.com> | 2017-10-25 00:31:50 +0300 |
commit | 789e00c53bf1d0786150ce5d8b6cb3cb0301a9ec (patch) | |
tree | 69e873e7b17007e81b77c462ead177a7850fb82e /src/ILCompiler.Compiler | |
parent | dce2dbd1b167199c80fd5d871e0ef8c30972b3ae (diff) |
Fixes for dependency tracking around EETypes and templates
- The existence of a static field block for a type which has canonically equivalent reflectable implementation requires the type that contains the static field to be necessary. This allows FieldInfo.GetValue/SetValue to work correctly
- If a constructed type has a generic virtual method defined upon it, and universal generics is enabled, force the virtual method to have a universal shared generic implementation
- Add a helper routine for getting the maximally constructed type symbol.
[tfs-changeset: 1679063]
Diffstat (limited to 'src/ILCompiler.Compiler')
12 files changed, 133 insertions, 34 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs index 19fde9b0b..443716a15 100644 --- a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs +++ b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs @@ -334,14 +334,7 @@ namespace ILCompiler public void AddCompilationRoot(TypeDesc type, string reason) { - if (!ConstructedEETypeNode.CreationAllowed(type)) - { - _graph.AddRoot(_factory.NecessaryTypeSymbol(type), reason); - } - else - { - _graph.AddRoot(_factory.ConstructedTypeSymbol(type), reason); - } + _graph.AddRoot(_factory.MaximallyConstructableType(type), reason); } public void RootThreadStaticBaseForType(TypeDesc type, string reason) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs index cd5b438ad..afca8fa75 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs @@ -56,8 +56,12 @@ namespace ILCompiler.DependencyAnalysis // Track generic virtual methods that will get added to the GVM tables if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type)) + { dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type), "Type with generic virtual methods")); + AddDependenciesForUniversalGVMSupport(factory, _type, ref dependencyList); + } + return dependencyList; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index 350214ec6..0237f9b8b 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -119,6 +119,8 @@ namespace ILCompiler.DependencyAnalysis if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type)) { dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type), "Type with generic virtual methods")); + + AddDependenciesForUniversalGVMSupport(factory, _type, ref dependencyList); } if (factory.TypeSystemContext.HasLazyStaticConstructor(_type)) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs index 2d22f146e..2fba4a059 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/EETypeNode.cs @@ -146,10 +146,41 @@ namespace ILCompiler.DependencyAnalysis public override bool IsShareable => IsTypeNodeShareable(_type); + private bool CanonFormTypeMayExist + { + get + { + if (!_type.HasInstantiation) + return false; + + if (!_type.Context.SupportsCanon) + return false; + + // If type is already in canon form, a canonically equivalent type cannot exist + if (_type.IsCanonicalSubtype(CanonicalFormKind.Any)) + return false; + + // If we reach here, a universal canon variant can exist (if universal canon is supported) + if (_type.Context.SupportsUniversalCanon) + return true; + + // Attempt to convert to canon. If the type changes, then the CanonForm exists + return (_type.ConvertToCanonForm(CanonicalFormKind.Specific) != _type); + } + } + public sealed override bool HasConditionalStaticDependencies { get { + // If the type is can be converted to some interesting canon type, and this is the non-constructed variant of an EEType + // we may need to trigger the fully constructed type to exist to make the behavior of the type consistent + // in reflection and generic template expansion scenarios + if (CanonFormTypeMayExist && ProjectNDependencyBehavior.EnableFullAnalysis) + { + return true; + } + if (!EmitVirtualSlotsAndInterfaces) return false; @@ -170,9 +201,22 @@ namespace ILCompiler.DependencyAnalysis } } - public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) + public sealed override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) { - Debug.Assert(EmitVirtualSlotsAndInterfaces); + IEETypeNode maximallyConstructableType = factory.MaximallyConstructableType(_type); + + if (maximallyConstructableType != this) + { + // EEType upgrading from necessary to constructed if some template instantation exists that matches up + if (CanonFormTypeMayExist) + { + yield return new CombinedDependencyListEntry(maximallyConstructableType, factory.MaximallyConstructableType(_type.ConvertToCanonForm(CanonicalFormKind.Specific)), "Trigger full type generation if canonical form exists"); + + if (_type.Context.SupportsUniversalCanon) + yield return new CombinedDependencyListEntry(maximallyConstructableType, factory.MaximallyConstructableType(_type.ConvertToCanonForm(CanonicalFormKind.Universal)), "Trigger full type generation if universal canonical form exists"); + } + yield break; + } DefType defType = _type.GetClosestDefType(); @@ -1028,6 +1072,62 @@ namespace ILCompiler.DependencyAnalysis } } + public static void AddDependenciesForStaticsNode(NodeFactory factory, TypeDesc type, ref DependencyList dependencies) + { + if ((factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) + return; + + // To ensure that the behvior of FieldInfo.GetValue/SetValue remains correct, + // if a type may be reflectable, and it is generic, if a canonical instantiation of reflection + // can exist which can refer to the associated type of this static base, ensure that type + // has an EEType. (Which will allow the static field lookup logic to find the right type) + if (type.HasInstantiation && factory.MetadataManager.SupportsReflection && !factory.MetadataManager.IsReflectionBlocked(type)) + { + // This current implementation is slightly generous, as it does not attempt to restrict + // the created types to the maximum extent by investigating reflection data and such. Here we just + // check if we support use of a canonically equivalent type to perform reflection. + // We don't check to see if reflection is enabled on the type. + if (factory.TypeSystemContext.SupportsUniversalCanon + || (factory.TypeSystemContext.SupportsCanon && (type != type.ConvertToCanonForm(CanonicalFormKind.Specific)))) + { + if (dependencies == null) + dependencies = new DependencyList(); + + dependencies.Add(factory.NecessaryTypeSymbol(type), "Static block owning type is necessary for canonically equivalent reflection"); + } + } + } + + protected static void AddDependenciesForUniversalGVMSupport(NodeFactory factory, TypeDesc type, ref DependencyList dependencies) + { + if (factory.TypeSystemContext.SupportsUniversalCanon) + { + if ((factory.Target.Abi == TargetAbi.ProjectN) && !ProjectNDependencyBehavior.EnableFullAnalysis) + return; + + foreach (MethodDesc method in type.GetMethods()) + { + if (!method.IsVirtual || !method.HasInstantiation) + continue; + + if (method.IsAbstract) + continue; + + TypeDesc[] universalCanonArray = new TypeDesc[method.Instantiation.Length]; + for (int i = 0; i < universalCanonArray.Length; i++) + universalCanonArray[i] = factory.TypeSystemContext.UniversalCanonType; + + MethodDesc universalCanonMethodNonCanonicalized = method.MakeInstantiatedMethod(new Instantiation(universalCanonArray)); + MethodDesc universalCanonGVMMethod = universalCanonMethodNonCanonicalized.GetCanonMethodTarget(CanonicalFormKind.Universal); + + if (dependencies == null) + dependencies = new DependencyList(); + + dependencies.Add(new DependencyListEntry(factory.MethodEntrypoint(universalCanonGVMMethod), "USG GVM Method")); + } + } + } + private struct SlotCounter { private int _startBytes; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs index e80d799de..64c99b3b1 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs @@ -75,6 +75,8 @@ namespace ILCompiler.DependencyAnalysis } dependencyList.Add(factory.GCStaticIndirection(_type), "GC statics indirection"); + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList); + return dependencyList; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs index 8ccbd0c08..d6a4b295c 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs @@ -62,11 +62,7 @@ namespace ILCompiler.DependencyAnalysis { // If the type does not have fully constructed type, don't track its dependencies. // TODO: Remove the workaround once we stop using the STS dependency analysis. - IDependencyNode node; - if (ConstructedEETypeNode.CreationAllowed(type)) - node = factory.ConstructedTypeSymbol(type); - else - node = factory.NecessaryTypeSymbol(type); + IDependencyNode node = factory.MaximallyConstructableType(type); if (!node.Marked) continue; @@ -111,11 +107,7 @@ namespace ILCompiler.DependencyAnalysis { // If the type does not have fully constructed type, don't track its dependencies. // TODO: Remove the workaround once we stop using the STS dependency analysis. - IDependencyNode node; - if (ConstructedEETypeNode.CreationAllowed(templateType)) - node = factory.ConstructedTypeSymbol(templateType); - else - node = factory.NecessaryTypeSymbol(templateType); + IDependencyNode node = factory.MaximallyConstructableType(templateType); if (!node.Marked) return; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index 2b181fa90..1ade913fe 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -920,11 +920,7 @@ namespace ILCompiler.DependencyAnalysis public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context) { - ISymbolNode typeNode; - if (!ConstructedEETypeNode.CreationAllowed(_type)) - typeNode = context.NecessaryTypeSymbol(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); - else - typeNode = context.ConstructedTypeSymbol(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); + ISymbolNode typeNode = context.MaximallyConstructableType(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); yield return new DependencyListEntry(typeNode, "Template EEType"); diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 91bcfdb34..6d267094e 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -533,6 +533,14 @@ namespace ILCompiler.DependencyAnalysis private NodeCache<TypeDesc, IEETypeNode> _clonedTypeSymbols; + public IEETypeNode MaximallyConstructableType(TypeDesc type) + { + if (ConstructedEETypeNode.CreationAllowed(type)) + return ConstructedTypeSymbol(type); + else + return NecessaryTypeSymbol(type); + } + public IEETypeNode ConstructedClonedTypeSymbol(TypeDesc type) { Debug.Assert(!TypeCannotHaveEEType(type)); diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs index 359930727..7feef9bba 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs @@ -102,14 +102,17 @@ namespace ILCompiler.DependencyAnalysis protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { + DependencyList dependencyList = null; + if (factory.TypeSystemContext.HasEagerStaticConstructor(_type)) { - var result = new DependencyList(); - result.Add(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor"); - return result; + dependencyList = new DependencyList(); + dependencyList.Add(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor"); } - return null; + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList); + + return dependencyList; } public override ObjectData GetData(NodeFactory factory, bool relocsOnly) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsNode.cs index 9a66b0e43..eed926b2c 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ThreadStaticsNode.cs @@ -53,7 +53,7 @@ namespace ILCompiler.DependencyAnalysis public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) { - List<DependencyListEntry> result = new List<DependencyListEntry>(); + DependencyList result = new DependencyList(); result.Add(new DependencyListEntry(GetGCStaticEETypeNode(factory), "ThreadStatic EEType")); @@ -62,6 +62,7 @@ namespace ILCompiler.DependencyAnalysis result.Add(new DependencyListEntry(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor")); } + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref result); return result; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcThreadStaticsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcThreadStaticsNode.cs index c998cb55f..ccf216622 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcThreadStaticsNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/UtcThreadStaticsNode.cs @@ -43,6 +43,7 @@ namespace ILCompiler.DependencyAnalysis } dependencyList.Add(((UtcNodeFactory)factory).TypeThreadStaticGCDescNode(_type), "GC Desc"); + EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList); return dependencyList; } diff --git a/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs b/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs index b30e9e7b4..cf4df3cd0 100644 --- a/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs +++ b/src/ILCompiler.Compiler/src/IL/ILImporter.Scanner.cs @@ -753,10 +753,7 @@ namespace Internal.IL } else { - if (ConstructedEETypeNode.CreationAllowed(type)) - _dependencies.Add(_factory.ConstructedTypeSymbol(type), "ldtoken"); - else - _dependencies.Add(_factory.NecessaryTypeSymbol(type), "ldtoken"); + _dependencies.Add(_factory.MaximallyConstructableType(type), "ldtoken"); } // If this is a ldtoken Type / GetValueInternal sequence, we're done. |