GenericMethodHashtable is a hashtable of all method generic dictionaries statically present in the image. In practice, we don't need a database of all of them - only those that are reflection-visible, or have a type loader template. This PR implements that.
Saves 0.2% on BasicMinimalApi. A bit less than I hoped for but since I already have this...
protected override TypeSystemContext Context => _owningMethod.Context;
public override TypeSystemEntity OwningEntity => _owningMethod;
public MethodDesc OwningMethod => _owningMethod;
- public override bool HasConditionalStaticDependencies => false;
+ public override bool HasConditionalStaticDependencies => true;
+
+ public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
+ {
+ return factory.MetadataManager.GetConditionalDependenciesDueToGenericDictionary(factory, _owningMethod);
+ }
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
{
if (factory.CompilationModuleGroup.ContainsMethodBody(canonicalTarget, false))
dependencies.Add(GetDictionaryLayout(factory), "Layout");
- // TODO-SIZE: We probably don't need to add these for all dictionaries
- GenericMethodsHashtableNode.GetGenericMethodsHashtableDependenciesForMethod(ref dependencies, factory, _owningMethod);
+ factory.MetadataManager.GetDependenciesDueToGenericDictionary(ref dependencies, factory, _owningMethod);
factory.InteropStubManager.AddMarshalAPIsGenericDependencies(ref dependencies, factory, _owningMethod);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+
+using ILCompiler.DependencyAnalysisFramework;
+
+using Internal.TypeSystem;
+
+using Debug = System.Diagnostics.Debug;
+
+namespace ILCompiler.DependencyAnalysis
+{
+ public class GenericMethodsHashtableEntryNode : DependencyNodeCore<NodeFactory>
+ {
+ private readonly MethodDesc _method;
+
+ public GenericMethodsHashtableEntryNode(MethodDesc method)
+ {
+ _method = method;
+ }
+
+ public MethodDesc Method => _method;
+
+ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
+ {
+ DependencyList dependencies = null;
+ GenericMethodsHashtableNode.GetGenericMethodsHashtableDependenciesForMethod(ref dependencies, factory, _method);
+ Debug.Assert(dependencies != null);
+ return dependencies;
+ }
+ protected override string GetName(NodeFactory factory)
+ {
+ return "Generic methods hashtable entry: " + _method.ToString();
+ }
+
+ public override bool InterestingForDynamicDependencyAnalysis => false;
+ public override bool HasDynamicDependencies => false;
+ public override bool HasConditionalStaticDependencies => false;
+ public override bool StaticDependenciesAreComputed => true;
+ public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) => null;
+ public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;
+ }
+}
Section nativeSection = nativeWriter.NewSection();
nativeSection.Place(hashtable);
- foreach (var dictionaryNode in factory.MetadataManager.GetCompiledGenericDictionaries())
+ foreach (MethodDesc method in factory.MetadataManager.GetGenericMethodHashtableEntries())
{
- MethodGenericDictionaryNode methodDictionary = dictionaryNode as MethodGenericDictionaryNode;
- if (methodDictionary == null)
- continue;
-
- MethodDesc method = methodDictionary.OwningMethod;
-
Debug.Assert(method.HasInstantiation && !method.IsCanonicalMethod(CanonicalFormKind.Any));
Vertex fullMethodSignature;
}
// Method's dictionary pointer
+ var dictionaryNode = factory.MethodGenericDictionary(method);
Vertex dictionaryVertex = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(dictionaryNode));
Vertex entry = nativeWriter.GetTuple(dictionaryVertex, fullMethodSignature);
public static void GetGenericMethodsHashtableDependenciesForMethod(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
{
+ dependencies ??= new DependencyList();
+
Debug.Assert(method.HasInstantiation && !method.IsCanonicalMethod(CanonicalFormKind.Any));
// Method's containing type
NativeLayoutVertexNode nameAndSig = factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition());
NativeLayoutSavedVertexNode placedNameAndSig = factory.NativeLayout.PlacedSignatureVertex(nameAndSig);
dependencies.Add(new DependencyListEntry(placedNameAndSig, "GenericMethodsHashtable entry signature"));
-
- ISymbolNode dictionaryNode = factory.MethodGenericDictionary(method);
- dependencies.Add(new DependencyListEntry(dictionaryNode, "GenericMethodsHashtable entry dictionary"));
}
protected internal override int Phase => (int)ObjectNodePhase.Ordered;
return new GenericVirtualMethodImplNode(method);
});
+ _genericMethodEntries = new NodeCache<MethodDesc, GenericMethodsHashtableEntryNode>(method =>
+ {
+ return new GenericMethodsHashtableEntryNode(method);
+ });
+
_gvmTableEntries = new NodeCache<TypeDesc, TypeGVMEntriesNode>(type =>
{
return new TypeGVMEntriesNode(type);
return _gvmImpls.GetOrAdd(method);
}
+ private NodeCache<MethodDesc, GenericMethodsHashtableEntryNode> _genericMethodEntries;
+ public GenericMethodsHashtableEntryNode GenericMethodsHashtableEntry(MethodDesc method)
+ {
+ return _genericMethodEntries.GetOrAdd(method);
+ }
+
private NodeCache<TypeDesc, TypeGVMEntriesNode> _gvmTableEntries;
internal TypeGVMEntriesNode TypeGVMEntries(TypeDesc type)
{
using ReflectionMapBlob = Internal.Runtime.ReflectionMapBlob;
using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
using CombinedDependencyList = System.Collections.Generic.List<ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.CombinedDependencyListEntry>;
+using CombinedDependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.CombinedDependencyListEntry;
using MethodIL = Internal.IL.MethodIL;
using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue<Internal.TypeSystem.TypeDesc>;
private HashSet<NativeLayoutTemplateMethodSignatureVertexNode> _templateMethodEntries = new HashSet<NativeLayoutTemplateMethodSignatureVertexNode>();
private readonly SortedSet<TypeDesc> _typeTemplates = new SortedSet<TypeDesc>(TypeSystemComparer.Instance);
private readonly SortedSet<MetadataType> _typesWithGenericStaticBaseInfo = new SortedSet<MetadataType>(TypeSystemComparer.Instance);
+ private readonly SortedSet<MethodDesc> _genericMethodHashtableEntries = new SortedSet<MethodDesc>(TypeSystemComparer.Instance);
private List<(DehydratableObjectNode Node, ObjectNode.ObjectData Data)> _dehydratableData = new List<(DehydratableObjectNode Node, ObjectNode.ObjectData data)>();
{
_typesWithGenericStaticBaseInfo.Add(genericStaticBaseInfo.Type);
}
+
+ if (obj is GenericMethodsHashtableEntryNode genericMethodsHashtableEntryNode)
+ {
+ _genericMethodHashtableEntries.Add(genericMethodsHashtableEntryNode.Method);
+ }
}
protected virtual bool AllMethodsCanBeReflectable => false;
return true;
}
+ public void GetDependenciesDueToGenericDictionary(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
+ {
+ MetadataCategory category = GetMetadataCategory(method.GetCanonMethodTarget(CanonicalFormKind.Specific));
+
+ if ((category & MetadataCategory.RuntimeMapping) != 0)
+ {
+ // If the method is visible from reflection, we need to keep track of this statically generated
+ // dictionary to make sure MakeGenericMethod works even without a type loader template
+ dependencies ??= new DependencyList();
+ dependencies.Add(factory.GenericMethodsHashtableEntry(method), "Reflection visible dictionary");
+ }
+ }
+
+ public IEnumerable<CombinedDependencyListEntry> GetConditionalDependenciesDueToGenericDictionary(NodeFactory factory, MethodDesc method)
+ {
+ // If there's a template for this method, we need to keep track of the dictionary so that we
+ // don't accidentally create a new dictionary for the same method at runtime.
+ yield return new CombinedDependencyListEntry(
+ factory.GenericMethodsHashtableEntry(method),
+ factory.NativeLayout.TemplateMethodEntry(method.GetCanonMethodTarget(CanonicalFormKind.Specific)),
+ "Runtime-constructable dictionary");
+ }
+
/// <summary>
/// This method is an extension point that can provide additional metadata-based dependencies to compiled method bodies.
/// </summary>
return _typesWithGenericStaticBaseInfo;
}
+ public IEnumerable<MethodDesc> GetGenericMethodHashtableEntries()
+ {
+ return _genericMethodHashtableEntries;
+ }
+
internal IEnumerable<IMethodBodyNode> GetCompiledMethodBodies()
{
return _methodBodiesGenerated;
<Compile Include="Compiler\DependencyAnalysis\ExternSymbolsImportedNodeProvider.cs" />
<Compile Include="Compiler\DependencyAnalysis\FieldRvaDataNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\FunctionPointerMapNode.cs" />
+ <Compile Include="Compiler\DependencyAnalysis\GenericMethodsHashtableEntryNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\GenericStaticBaseInfoNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\GenericVarianceNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\GenericVirtualMethodImplNode.cs" />