diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs index 7883e22a5e233..215a0f5132521 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs @@ -152,13 +152,24 @@ private class GenericStaticConstrainedMethodCell : GenericDictionaryCell internal override void Prepare(TypeBuilder builder) { _resolvedMethod = TypeLoaderEnvironment.GVMLookupForSlotWorker(ConstraintType, ConstrainedMethod); - builder.PrepareMethod(_resolvedMethod); + + if (_resolvedMethod.CanShareNormalGenericCode()) + builder.PrepareMethod(_resolvedMethod); } internal override IntPtr Create(TypeBuilder builder) { - IntPtr methodDictionary = _resolvedMethod.RuntimeMethodDictionary; - return FunctionPointerOps.GetGenericMethodFunctionPointer(_resolvedMethod.FunctionPointer, methodDictionary); + if (_resolvedMethod.CanShareNormalGenericCode()) + { + IntPtr methodDictionary = _resolvedMethod.RuntimeMethodDictionary; + return FunctionPointerOps.GetGenericMethodFunctionPointer(_resolvedMethod.FunctionPointer, methodDictionary); + } + else + { + if (!TypeLoaderEnvironment.Instance.TryLookupExactMethodPointer(_resolvedMethod, out nint result)) + Environment.FailFast("Unable to find exact method pointer for a resolved GVM."); + return result; + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsEntryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsEntryNode.cs new file mode 100644 index 0000000000000..121e6209f9c17 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsEntryNode.cs @@ -0,0 +1,47 @@ +// 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 ExactMethodInstantiationsEntryNode : DependencyNodeCore + { + private readonly MethodDesc _method; + + public ExactMethodInstantiationsEntryNode(MethodDesc method) + { + Debug.Assert(!method.IsAbstract); + Debug.Assert(method.HasInstantiation); + Debug.Assert(!method.GetCanonMethodTarget(CanonicalFormKind.Specific).IsCanonicalMethod(CanonicalFormKind.Any)); + _method = method; + } + + public MethodDesc Method => _method; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList dependencies = null; + ExactMethodInstantiationsNode.GetExactMethodInstantiationDependenciesForMethod(ref dependencies, factory, _method); + Debug.Assert(dependencies != null); + return dependencies; + } + protected override string GetName(NodeFactory factory) + { + return "Exact 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 GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs index a67a38f22d07d..9e1a274e30bc4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExactMethodInstantiationsNode.cs @@ -50,11 +50,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) nativeSection.Place(hashtable); - foreach (MethodDesc method in factory.MetadataManager.GetCompiledMethods()) + foreach (MethodDesc method in factory.MetadataManager.GetExactMethodHashtableEntries()) { - if (!IsMethodEligibleForTracking(factory, method)) - continue; - // Get the method pointer vertex bool getUnboxingStub = method.OwningType.IsValueType && !method.Signature.IsStatic; @@ -106,9 +103,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) public static void GetExactMethodInstantiationDependenciesForMethod(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { - if (!IsMethodEligibleForTracking(factory, method)) - return; - dependencies ??= new DependencyList(); // Method entry point dependency @@ -129,31 +123,6 @@ public static void GetExactMethodInstantiationDependenciesForMethod(ref Dependen dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(nameAndSig), "Exact method instantiation entry")); } - private static bool IsMethodEligibleForTracking(NodeFactory factory, MethodDesc method) - { - // Runtime determined methods should never show up here. - Debug.Assert(!method.IsRuntimeDeterminedExactMethod); - - if (method.IsAbstract) - return false; - - if (!method.HasInstantiation) - return false; - - // This hashtable is only for method instantiations that don't use generic dictionaries, - // so check if the given method is shared before proceeding - if (method.IsSharedByGenericInstantiations || method.GetCanonMethodTarget(CanonicalFormKind.Specific) != method) - return false; - - // The hashtable is used to find implementations of generic virtual methods at runtime - if (method.IsVirtual) - return true; - - // The rest of the entries are potentially only useful for the universal - // canonical type loader. - return factory.TypeSystemContext.SupportsUniversalCanon; - } - protected internal override int Phase => (int)ObjectNodePhase.Ordered; public override int ClassCode => (int)ObjectNodeOrder.ExactMethodInstantiationsNode; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVirtualMethodImplNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVirtualMethodImplNode.cs index b7be9dd525f5c..1fb8ab0a4cc20 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVirtualMethodImplNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericVirtualMethodImplNode.cs @@ -64,6 +64,10 @@ public override IEnumerable GetStaticDependencies(NodeFacto dependencies.Add(factory.NativeLayout.TemplateMethodEntry(_method), "GVM Dependency - Template entry"); dependencies.Add(factory.NativeLayout.TemplateMethodLayout(_method), "GVM Dependency - Template"); } + else + { + dependencies.Add(factory.ExactMethodInstantiationsHashtableEntry(_method), "GVM Dependency - runtime lookups"); + } } return dependencies; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index 39304ac7c97da..e8704b461c787 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -321,6 +321,11 @@ private void CreateNodeCaches() return new GenericMethodsHashtableEntryNode(method); }); + _exactMethodEntries = new NodeCache(method => + { + return new ExactMethodInstantiationsEntryNode(method); + }); + _gvmTableEntries = new NodeCache(type => { return new TypeGVMEntriesNode(type); @@ -1056,6 +1061,12 @@ public GenericMethodsHashtableEntryNode GenericMethodsHashtableEntry(MethodDesc return _genericMethodEntries.GetOrAdd(method); } + private NodeCache _exactMethodEntries; + public ExactMethodInstantiationsEntryNode ExactMethodInstantiationsHashtableEntry(MethodDesc method) + { + return _exactMethodEntries.GetOrAdd(method); + } + private NodeCache _gvmTableEntries; internal TypeGVMEntriesNode TypeGVMEntries(TypeDesc type) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 835b601cf6496..09b5bf1b1a866 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -77,6 +77,7 @@ private readonly SortedSet _typeGVMEntries private readonly SortedSet _typeTemplates = new SortedSet(TypeSystemComparer.Instance); private readonly SortedSet _typesWithGenericStaticBaseInfo = new SortedSet(TypeSystemComparer.Instance); private readonly SortedSet _genericMethodHashtableEntries = new SortedSet(TypeSystemComparer.Instance); + private readonly SortedSet _exactMethodHashtableEntries = new SortedSet(TypeSystemComparer.Instance); private readonly HashSet _usedInterfaces = new HashSet(); private List<(DehydratableObjectNode Node, ObjectNode.ObjectData Data)> _dehydratableData = new List<(DehydratableObjectNode Node, ObjectNode.ObjectData data)>(); @@ -330,6 +331,11 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) _genericMethodHashtableEntries.Add(genericMethodsHashtableEntryNode.Method); } + if (obj is ExactMethodInstantiationsEntryNode exactMethodsHashtableEntryNode) + { + _exactMethodHashtableEntries.Add(exactMethodsHashtableEntryNode.Method); + } + if (obj is InterfaceUseNode interfaceUse) { _usedInterfaces.Add(interfaceUse.Type); @@ -605,11 +611,6 @@ public virtual void GetDependenciesForOverridingMethod(ref CombinedDependencyLis /// public void GetDependenciesDueToMethodCodePresence(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, MethodIL methodIL) { - if (method.HasInstantiation) - { - ExactMethodInstantiationsNode.GetExactMethodInstantiationDependenciesForMethod(ref dependencies, factory, method); - } - InlineableStringsResourceNode.AddDependenciesDueToResourceStringUse(ref dependencies, factory, method); GetDependenciesDueToMethodCodePresenceInternal(ref dependencies, factory, method, methodIL); @@ -1012,6 +1013,11 @@ public IEnumerable GetGenericMethodHashtableEntries() return _genericMethodHashtableEntries; } + public IEnumerable GetExactMethodHashtableEntries() + { + return _exactMethodHashtableEntries; + } + public bool IsInterfaceUsed(TypeDesc type) { Debug.Assert(type.IsTypeDefinition); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index c884192f582d4..a3ae45253866a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -411,6 +411,7 @@ + diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs index 488171e0f27ec..e39a42c9ba29d 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs @@ -57,6 +57,7 @@ internal static int Run() TestGvmLookupDependency.Run(); Test99198Regression.Run(); Test102259Regression.Run(); + Test104913Regression.Run(); TestInvokeMemberCornerCaseInGenerics.Run(); TestRefAny.Run(); TestNullableCasting.Run(); @@ -3596,6 +3597,38 @@ public static void Run() } } + class Test104913Regression + { + interface IFoo + { + (Type, Type) InvokeInstance() where T : IBar; + } + + class Foo : IFoo + { + public (Type, Type) InvokeInstance() where T : IBar + => (typeof(T), T.InvokeStatic()); + } + + interface IBar + { + static abstract Type InvokeStatic(); + } + + class Bar : IBar + { + public static Type InvokeStatic() + => typeof(T); + } + + public static void Run() + { + (Type t1, Type t2) = ((IFoo)new Foo()).InvokeInstance(); + if (t1 != typeof(Bar) || t2 != typeof(int)) + throw new Exception(); + } + } + class TestInvokeMemberCornerCaseInGenerics { class Generic