| File: Compiler\DependencyAnalysis\InheritedVirtualMethodsNode.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun) |
// 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 System.Diagnostics; using ILCompiler.DependencyAnalysisFramework; using Internal.TypeSystem; namespace ILCompiler.DependencyAnalysis.ReadyToRun { /// <summary> /// Type discovery node for virtual dispatch dependency analysis. /// /// For GVMs: Marked InterestingForDynamicDependencyAnalysis when the type /// has generic virtual method slots in its hierarchy so that GVMDependenciesNode /// can iterate on these type nodes to discover new generic virtual method targets. /// /// For non-GVMs: Has conditional static dependencies that compile virtual method /// implementations when their corresponding virtual slot is marked as used /// (via VirtualMethodUseNode). /// </summary> public class InheritedVirtualMethodsNode : DependencyNodeCore<NodeFactory> { private readonly TypeDesc _type; private readonly bool _interestingForDynamicDependencyAnalysis; public InheritedVirtualMethodsNode(TypeDesc type) { Debug.Assert(type.IsDefType && !type.IsInterface); Debug.Assert(!type.IsGenericDefinition); Debug.Assert(type == type.ConvertToCanonForm(CanonicalFormKind.Specific)); _type = type; _interestingForDynamicDependencyAnalysis = TypeHasGVMSlots(type); } public TypeDesc Type => _type; public override bool InterestingForDynamicDependencyAnalysis => _interestingForDynamicDependencyAnalysis; public override bool HasDynamicDependencies => false; public override bool HasConditionalStaticDependencies => true; public override bool StaticDependenciesAreComputed => true; public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context) => null; public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null; public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory) { DefType defType = (DefType)_type; List<CombinedDependencyListEntry> result = new List<CombinedDependencyListEntry>(); // Class virtual method path: for each virtual slot, compile the implementation // on this type if the slot-defining method is used. foreach (MethodDesc decl in defType.EnumAllVirtualSlots()) { // GVMs are tracked by the orthogonal GVMDependenciesNode mechanism. if (decl.HasInstantiation) continue; MethodDesc impl = defType.FindVirtualFunctionTargetMethodOnObjectType(decl); if (impl.IsAbstract) continue; // Given we are scanning for non-GVMs here, if the type has no generic type arguments // or they are Canon, they should already be included in the compilation if (!impl.OwningType.HasInstantiation || !HasNonCanonicalInstantiationArguments(impl.OwningType)) continue; MethodDesc canonImpl = impl.GetCanonMethodTarget(CanonicalFormKind.Specific); Debug.Assert(!canonImpl.OwningType.IsGenericDefinition); DependencyNodeCore<NodeFactory> implNode = GetVirtualMethodImplNode(factory, canonImpl); if (implNode is null) continue; result.Add(new CombinedDependencyListEntry(implNode, factory.VirtualMethodUse(decl), "Virtual method")); } // Interface method path: for each interface implemented by this type, // compile the implementation if the interface method slot is used. DefType[] runtimeInterfaces = defType.RuntimeInterfaces; DefType defTypeDefinition = (DefType)defType.GetTypeDefinition(); DefType[] definitionRuntimeInterfaces = defTypeDefinition.RuntimeInterfaces; for (int interfaceIndex = 0; interfaceIndex < runtimeInterfaces.Length; interfaceIndex++) { DefType interfaceType = runtimeInterfaces[interfaceIndex]; DefType definitionInterfaceType = definitionRuntimeInterfaces[interfaceIndex]; foreach (MethodDesc interfaceMethod in interfaceType.EnumAllVirtualSlots()) { // GVMs handled in GVMDependenciesNode. SVMs handled at call site. if (interfaceMethod.HasInstantiation || interfaceMethod.Signature.IsStatic) continue; MethodDesc interfaceMethodDefinition = interfaceMethod; if (interfaceType != definitionInterfaceType) interfaceMethodDefinition = factory.TypeSystemContext.GetMethodForInstantiatedType( interfaceMethodDefinition.GetTypicalMethodDefinition(), (InstantiatedType)definitionInterfaceType); MethodDesc implMethod = defTypeDefinition.InstantiateAsOpen().ResolveInterfaceMethodTarget(interfaceMethodDefinition); if (implMethod is null) { // The method might be implemented through a default interface method var resolution = defTypeDefinition.InstantiateAsOpen().ResolveInterfaceMethodToDefaultImplementationOnType(interfaceMethodDefinition, out implMethod); if (resolution != DefaultInterfaceMethodResolution.DefaultImplementation) { implMethod = null; } } if (implMethod is not null) { implMethod = implMethod.InstantiateSignature(defType.Instantiation, Instantiation.Empty); if (implMethod.IsVirtual && !implMethod.IsFinal && !implMethod.OwningType.IsInterface) { // The interface resolves to a virtual method that can be overridden. // Mark the class virtual slot as used so the class path compiles the // actual final target (which may be an override further down the hierarchy). result.Add(new CombinedDependencyListEntry(factory.VirtualMethodUse(implMethod), factory.VirtualMethodUse(interfaceMethod), "Interface method")); } else { MethodDesc canonImpl = implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); DependencyNodeCore<NodeFactory> implNode = GetVirtualMethodImplNode(factory, canonImpl); if (implNode is not null) { result.Add(new CombinedDependencyListEntry(implNode, factory.VirtualMethodUse(interfaceMethod), "Interface method")); } } } } } return result; } private static bool TypeHasGVMSlots(TypeDesc type) { TypeDesc currentType = type; while (currentType != null) { foreach (MethodDesc method in currentType.GetVirtualMethods()) { if (method.HasInstantiation) return true; } currentType = currentType.BaseType; } // Look for generic DIMs from implemented interfaces foreach (DefType interfaceType in type.RuntimeInterfaces) { foreach (MethodDesc interfaceMethod in interfaceType.GetVirtualMethods()) { if (interfaceMethod.HasInstantiation && !interfaceMethod.IsAbstract) return true; } } return false; } private static bool HasNonCanonicalInstantiationArguments(TypeDesc type) { TypeDesc canonType = type.Context.CanonType; foreach (TypeDesc arg in type.Instantiation) { if (arg != canonType) return true; } return false; } private static DependencyNodeCore<NodeFactory> GetVirtualMethodImplNode(NodeFactory factory, MethodDesc method) { if (!factory.CompilationModuleGroup.ContainsMethodBody(method, false)) return null; try { factory.DetectGenericCycles(method, method); return factory.CompiledMethodNode(method); } catch (TypeSystemException) { return null; } } protected override string GetName(NodeFactory factory) => $"Inherited virtual methods on {_type}"; } }