diff options
author | Fadi Hanna <fadim@microsoft.com> | 2017-10-25 00:39:09 +0300 |
---|---|---|
committer | Fadi Hanna <fadim@microsoft.com> | 2017-10-25 00:39:09 +0300 |
commit | 27a9c6cf779e7a0424d45e05f8eb589068b67c82 (patch) | |
tree | 0d52e3481b177d0451208843ecb91f16805f207a /src/ILCompiler.Compiler | |
parent | 789e00c53bf1d0786150ce5d8b6cb3cb0301a9ec (diff) |
Adding support for the DefaultConstructorMap hashtable, to enable proper construction of types accessible through lazy generics code.
[tfs-changeset: 1679067]
Diffstat (limited to 'src/ILCompiler.Compiler')
5 files changed, 182 insertions, 3 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DefaultConstructorMapNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DefaultConstructorMapNode.cs new file mode 100644 index 000000000..81e8eb38d --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/DefaultConstructorMapNode.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// 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 System.Diagnostics; +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.NativeFormat; + +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// <summary> + /// Dependency analysis node used to keep track of types needing an entry in the default + /// constructor map hashtable + /// </summary> + internal sealed class DefaultConstructorFromLazyNode : DependencyNodeCore<NodeFactory> + { + public TypeDesc TypeNeedingDefaultCtor { get; } + + public DefaultConstructorFromLazyNode(TypeDesc type) + { + Debug.Assert(!type.IsRuntimeDeterminedSubtype); + Debug.Assert(type == type.ConvertToCanonForm(CanonicalFormKind.Specific)); + Debug.Assert(type.GetDefaultConstructor() != null && !type.IsValueType); + + TypeNeedingDefaultCtor = type; + } + + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => "__DefaultConstructorFromLazyNode_" + factory.NameMangler.GetMangledTypeName(TypeNeedingDefaultCtor); + public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory context) => null; + public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null; + + public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context) + { + yield return new DependencyListEntry( + context.MethodEntrypoint(TypeNeedingDefaultCtor.GetDefaultConstructor(), TypeNeedingDefaultCtor.IsValueType), + "DefaultConstructorNode"); + } + } + + /// <summary> + /// DefaultConstructorMap blob, containing information on default constructor entrypoints of all types used + /// by lazy generic instantiations. + /// </summary> + internal class DefaultConstructorMapNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + private ExternalReferencesTableNode _externalReferences; + + public DefaultConstructorMapNode(ExternalReferencesTableNode externalReferences) + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__DefaultConstructor_Map_End", true); + _externalReferences = externalReferences; + } + + public ISymbolNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__DefaultConstructor_Map"); + } + + public int Offset => 0; + public override bool IsShareable => false; + public override ObjectNodeSection Section => _externalReferences.Section; + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false; + public override bool StaticDependenciesAreComputed => true; + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This node does not trigger generation of other nodes. + if (relocsOnly) + return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this }); + + var writer = new NativeWriter(); + var defaultConstructorHashtable = new VertexHashtable(); + + Section defaultConstructorHashtableSection = writer.NewSection(); + defaultConstructorHashtableSection.Place(defaultConstructorHashtable); + + foreach (var ctorNeeded in factory.MetadataManager.GetDefaultConstructorsNeeded()) + { + MethodDesc defaultCtor = ctorNeeded.TypeNeedingDefaultCtor.GetDefaultConstructor(); + Debug.Assert(defaultCtor != null); + + ISymbolNode typeNode = factory.NecessaryTypeSymbol(ctorNeeded.TypeNeedingDefaultCtor); + ISymbolNode defaultCtorNode = factory.MethodEntrypoint(defaultCtor, false); + + Vertex vertex = writer.GetTuple( + writer.GetUnsignedConstant(_externalReferences.GetIndex(typeNode)), + writer.GetUnsignedConstant(_externalReferences.GetIndex(defaultCtorNode))); + + int hashCode = ctorNeeded.TypeNeedingDefaultCtor.GetHashCode(); + defaultConstructorHashtable.Append((uint)hashCode, defaultConstructorHashtableSection.Place(vertex)); + } + + byte[] hashTableBytes = writer.Save(); + + _endSymbol.SetSymbolOffset(hashTableBytes.Length); + + return new ObjectData(hashTableBytes, Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs index 6c1f68d4a..8e300741d 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GenericDictionaryNode.cs @@ -131,7 +131,7 @@ namespace ILCompiler.DependencyAnalysis protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { - DependencyList result = result = new DependencyList(); + DependencyList result = new DependencyList(); result.Add(GetDictionaryLayout(factory), "Layout"); @@ -151,6 +151,21 @@ namespace ILCompiler.DependencyAnalysis } } + // Lazy generic use of the Activator.CreateInstance<T> heuristic requires tracking type parameters that are used in lazy generics. + if (factory.LazyGenericsPolicy.UsesLazyGenerics(_owningType)) + { + foreach (var arg in _owningType.Instantiation) + { + // Skip types that do not have a default constructor (not interesting). + if (arg.IsValueType || arg.GetDefaultConstructor() == null) + continue; + + result.Add(new DependencyListEntry( + factory.DefaultConstructorFromLazy(arg.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Default constructor for lazy generics")); + } + } + return result; } @@ -211,6 +226,31 @@ namespace ILCompiler.DependencyAnalysis factory.InteropStubManager.AddMarshalAPIsGenericDependencies(ref dependencies, factory, _owningMethod); + // Lazy generic use of the Activator.CreateInstance<T> heuristic requires tracking type parameters that are used in lazy generics. + if (factory.LazyGenericsPolicy.UsesLazyGenerics(_owningMethod)) + { + foreach (var arg in _owningMethod.OwningType.Instantiation) + { + // Skip types that do not have a default constructor (not interesting). + if (arg.IsValueType || arg.GetDefaultConstructor() == null) + continue; + + dependencies.Add(new DependencyListEntry( + factory.DefaultConstructorFromLazy(arg.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Default constructor for lazy generics")); + } + foreach (var arg in _owningMethod.Instantiation) + { + // Skip types that do not have a default constructor (not interesting). + if (arg.IsValueType || arg.GetDefaultConstructor() == null) + continue; + + dependencies.Add(new DependencyListEntry( + factory.DefaultConstructorFromLazy(arg.ConvertToCanonForm(CanonicalFormKind.Specific)), + "Default constructor for lazy generics")); + } + } + return dependencies; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 6d267094e..978d5a5db 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -483,6 +483,11 @@ namespace ILCompiler.DependencyAnalysis return new StringAllocatorMethodNode(constructor); }); + _defaultConstructorFromLazyNodes = new NodeCache<TypeDesc, DefaultConstructorFromLazyNode>(type => + { + return new DefaultConstructorFromLazyNode(type); + }); + NativeLayout = new NativeLayoutHelper(this); WindowsDebugData = new WindowsDebugDataHelper(this); } @@ -781,19 +786,23 @@ namespace ILCompiler.DependencyAnalysis } private NodeCache<MethodKey, IMethodNode> _shadowConcreteMethods; - public IMethodNode ShadowConcreteMethod(MethodDesc method, bool isUnboxingStub = false) { return _shadowConcreteMethods.GetOrAdd(new MethodKey(method, isUnboxingStub)); } private NodeCache<MethodDesc, IMethodNode> _runtimeDeterminedMethods; - public IMethodNode RuntimeDeterminedMethod(MethodDesc method) { return _runtimeDeterminedMethods.GetOrAdd(method); } + private NodeCache<TypeDesc, DefaultConstructorFromLazyNode> _defaultConstructorFromLazyNodes; + internal DefaultConstructorFromLazyNode DefaultConstructorFromLazy(TypeDesc type) + { + return _defaultConstructorFromLazyNodes.GetOrAdd(type); + } + private static readonly string[][] s_helperEntrypointNames = new string[][] { new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnGCStaticBase" }, new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnNonGCStaticBase" }, diff --git a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs index 23eaac139..818c040f4 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MetadataManager.cs @@ -45,6 +45,7 @@ namespace ILCompiler private HashSet<IMethodBodyNode> _methodBodiesGenerated = new HashSet<IMethodBodyNode>(); private List<ModuleDesc> _modulesWithMetadata = new List<ModuleDesc>(); private List<TypeGVMEntriesNode> _typeGVMEntries = new List<TypeGVMEntriesNode>(); + private HashSet<DefaultConstructorFromLazyNode> _defaultConstructorsNeeded = new HashSet<DefaultConstructorFromLazyNode>(); internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } internal DynamicInvokeTemplateDataNode DynamicInvokeTemplateData { get; private set; } @@ -135,6 +136,9 @@ namespace ILCompiler var virtualInvokeMapNode = new ReflectionVirtualInvokeMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.VirtualInvokeMap), virtualInvokeMapNode, virtualInvokeMapNode, virtualInvokeMapNode.EndSymbol); + var defaultConstructorMapNode = new DefaultConstructorMapNode(commonFixupsTableNode); + header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.DefaultConstructorMap), defaultConstructorMapNode, defaultConstructorMapNode, defaultConstructorMapNode.EndSymbol); + // The external references tables should go last header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CommonFixupsTable), commonFixupsTableNode, commonFixupsTableNode, commonFixupsTableNode.EndSymbol); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode, nativeReferencesTableNode, nativeReferencesTableNode.EndSymbol); @@ -199,6 +203,12 @@ namespace ILCompiler _genericDictionariesGenerated.Add(dictionaryNode); } + var ctorFromLazyGenericsNode = obj as DefaultConstructorFromLazyNode; + if (ctorFromLazyGenericsNode != null) + { + _defaultConstructorsNeeded.Add(ctorFromLazyGenericsNode); + } + // TODO: temporary until we have an IL scanning Metadata Manager. We shouldn't have to keep track of these. var moduleMetadataNode = obj as ModuleMetadataNode; if (moduleMetadataNode != null) @@ -570,6 +580,11 @@ namespace ILCompiler return _methodBodiesGenerated; } + internal IEnumerable<DefaultConstructorFromLazyNode> GetDefaultConstructorsNeeded() + { + return _defaultConstructorsNeeded; + } + internal bool TypeGeneratesEEType(TypeDesc type) { return _typesWithEETypesGenerated.Contains(type); diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 1f95fd8d7..86c3983c2 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -103,6 +103,7 @@ <Compile Include="Compiler\CompilerTypeSystemContext.Sorting.cs" /> <Compile Include="Compiler\CompilerGeneratedInteropStubManager.cs" /> <Compile Include="Compiler\DebugInformationProvider.cs" /> + <Compile Include="Compiler\DependencyAnalysis\DefaultConstructorMapNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\WindowsDebugILImagesSection.cs" /> <Compile Include="Compiler\DependencyAnalysis\WindowsDebugManagedNativeDictionaryInfoSection.cs" /> <Compile Include="Compiler\DependencyAnalysis\WindowsDebugMergedAssemblyRecordsSection.cs" /> |