diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs index 0f5a9d3c15293..31c868a77f00b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs @@ -210,9 +210,9 @@ public MethodDesc ExpandIntrinsicForCallsite(MethodDesc intrinsicMethod, MethodD return intrinsicMethod; } - public bool HasFixedSlotVTable(TypeDesc type) + public bool NeedsSlotUseTracking(TypeDesc type) { - return NodeFactory.VTable(type).HasFixedSlots; + return !NodeFactory.VTable(type).HasKnownVirtualMethodUse; } public bool IsEffectivelySealed(TypeDesc type) @@ -388,43 +388,37 @@ public GenericDictionaryLookup ComputeGenericLookup(MethodDesc contextMethod, Re lookupKind = ReadyToRunHelperId.TypeHandle; } - // Can we do a fixed lookup? Start by checking if we can get to the dictionary. - // Context source having a vtable with fixed slots is a prerequisite. - if (contextSource == GenericContextSource.MethodParameter - || HasFixedSlotVTable(contextMethod.OwningType)) + DictionaryLayoutNode dictionaryLayout; + if (contextSource == GenericContextSource.MethodParameter) + dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod); + else + dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod.OwningType); + + // If the dictionary layout has fixed slots, we can compute the lookup now. Otherwise defer to helper. + if (dictionaryLayout.HasFixedSlots) { - DictionaryLayoutNode dictionaryLayout; - if (contextSource == GenericContextSource.MethodParameter) - dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod); - else - dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod.OwningType); + int pointerSize = _nodeFactory.Target.PointerSize; - // If the dictionary layout has fixed slots, we can compute the lookup now. Otherwise defer to helper. - if (dictionaryLayout.HasFixedSlots) + GenericLookupResult lookup = ReadyToRunGenericHelperNode.GetLookupSignature(_nodeFactory, lookupKind, targetOfLookup); + if (dictionaryLayout.TryGetSlotForEntry(lookup, out int dictionarySlot)) { - int pointerSize = _nodeFactory.Target.PointerSize; + int dictionaryOffset = dictionarySlot * pointerSize; - GenericLookupResult lookup = ReadyToRunGenericHelperNode.GetLookupSignature(_nodeFactory, lookupKind, targetOfLookup); - if (dictionaryLayout.TryGetSlotForEntry(lookup, out int dictionarySlot)) + if (contextSource == GenericContextSource.MethodParameter) { - int dictionaryOffset = dictionarySlot * pointerSize; - - if (contextSource == GenericContextSource.MethodParameter) - { - return GenericDictionaryLookup.CreateFixedLookup(contextSource, dictionaryOffset); - } - else - { - int vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(_nodeFactory, contextMethod.OwningType); - int vtableOffset = EETypeNode.GetVTableOffset(pointerSize) + vtableSlot * pointerSize; - return GenericDictionaryLookup.CreateFixedLookup(contextSource, vtableOffset, dictionaryOffset); - } + return GenericDictionaryLookup.CreateFixedLookup(contextSource, dictionaryOffset); } else { - return GenericDictionaryLookup.CreateNullLookup(contextSource); + int vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(_nodeFactory, contextMethod.OwningType); + int vtableOffset = EETypeNode.GetVTableOffset(pointerSize) + vtableSlot * pointerSize; + return GenericDictionaryLookup.CreateFixedLookup(contextSource, vtableOffset, dictionaryOffset); } } + else + { + return GenericDictionaryLookup.CreateNullLookup(contextSource); + } } // Fixed lookup not possible - use helper. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index a6e213d34120b..f0a837fdfc21b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -386,7 +386,7 @@ public sealed override IEnumerable GetConditionalSt DefType defType = _type.GetClosestDefType(); // If we're producing a full vtable, none of the dependencies are conditional. - if (!factory.VTable(defType).HasFixedSlots) + if (!factory.VTable(defType).HasKnownVirtualMethodUse) { bool isNonInterfaceAbstractType = !defType.IsInterface && ((MetadataType)defType).IsAbstract; @@ -1035,7 +1035,7 @@ private void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objDa // It's only okay to touch the actual list of slots if we're in the final emission phase // or the vtable is not built lazily. - if (relocsOnly && !declVTable.HasFixedSlots) + if (relocsOnly && !declVTable.HasKnownVirtualMethodUse) return; // Interface types don't place anything else in their physical vtable. @@ -1063,13 +1063,19 @@ private void OutputVirtualSlots(NodeFactory factory, ref ObjectDataBuilder objDa // No generic virtual methods can appear in the vtable! Debug.Assert(!declMethod.HasInstantiation); - MethodDesc implMethod = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(declMethod); - // Final NewSlot methods cannot be overridden, and therefore can be placed in the sealed-vtable to reduce the size of the vtable // of this type and any type that inherits from it. if (declMethod.CanMethodBeInSealedVTable(factory) && !declType.IsArrayTypeWithoutGenericInterfaces()) continue; + if (!declVTable.IsSlotUsed(declMethod)) + { + objData.EmitZeroPointer(); + continue; + } + + MethodDesc implMethod = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(declMethod); + bool shouldEmitImpl = !implMethod.IsAbstract; // We do a size optimization that removes support for built-in ValueType Equals/GetHashCode diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs index efbc64f88799d..17f717d49bb5a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs @@ -837,7 +837,7 @@ public override IEnumerable> NonRelocDependencie // If we're producing a full vtable for the type, we don't need to report virtual method use. // We also don't report virtual method use for generic virtual methods - tracking those is orthogonal. - if (!factory.VTable(canonMethod.OwningType).HasFixedSlots && !canonMethod.HasInstantiation) + if (!factory.VTable(canonMethod.OwningType).HasKnownVirtualMethodUse && !canonMethod.HasInstantiation) { // Report the method as virtually used so that types that could be used here at runtime // have the appropriate implementations generated. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs index aa3883a2db509..5d22535ce5e7a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchCellNode.cs @@ -54,7 +54,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto { DependencyList result = new DependencyList(); - if (!factory.VTable(_targetMethod.OwningType).HasFixedSlots) + if (!factory.VTable(_targetMethod.OwningType).HasKnownVirtualMethodUse) { result.Add(factory.VirtualMethodUse(_targetMethod), "Interface method use"); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs index c902c896cebae..45cc7ca69680a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs @@ -96,18 +96,8 @@ public static bool MightHaveInterfaceDispatchMap(TypeDesc type, NodeFactory fact null : (InstantiatedType)declType.GetTypeDefinition().RuntimeInterfaces[interfaceIndex]; - IEnumerable slots; - - // If the vtable has fixed slots, we can query it directly. - // If it's a lazily built vtable, we might not be able to query slots - // just yet, so approximate by looking at all methods. VTableSliceNode vtableSlice = factory.VTable(interfaceType); - if (vtableSlice.HasFixedSlots) - slots = vtableSlice.Slots; - else - slots = interfaceType.GetAllVirtualMethods(); - - foreach (MethodDesc slotMethod in slots) + foreach (MethodDesc slotMethod in vtableSlice.Slots) { MethodDesc declMethod = slotMethod; @@ -176,12 +166,16 @@ private void EmitDispatchMap(ref ObjectDataBuilder builder, NodeFactory factory) if (!factory.InterfaceUse(interfaceType.GetTypeDefinition()).Marked) continue; - IReadOnlyList virtualSlots = factory.VTable(interfaceType).Slots; + VTableSliceNode interfaceVTable = factory.VTable(interfaceType); + IReadOnlyList virtualSlots = interfaceVTable.Slots; for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++) { MethodDesc declMethod = virtualSlots[interfaceMethodSlot]; + if (!interfaceVTable.IsSlotUsed(declMethod)) + continue; + if (!declMethod.Signature.IsStatic && !needsEntriesForInstanceInterfaceMethodImpls) continue; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index 037a1aff34162..60c6158de41a2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -1405,7 +1405,7 @@ private static void ProcessVTableEntriesForCallingConventionSignatureGeneration( break; case VTableEntriesToProcess.AllOnTypesThatShouldProduceFullVTables: - if (factory.VTable(declType).HasFixedSlots) + if (factory.VTable(declType).HasKnownVirtualMethodUse) { vtableEntriesToProcess = factory.VTable(declType).Slots; } @@ -1416,7 +1416,7 @@ private static void ProcessVTableEntriesForCallingConventionSignatureGeneration( break; case VTableEntriesToProcess.AllOnTypesThatProducePartialVTables: - if (factory.VTable(declType).HasFixedSlots) + if (factory.VTable(declType).HasKnownVirtualMethodUse) { vtableEntriesToProcess = Array.Empty(); } @@ -1639,7 +1639,7 @@ public sealed override IEnumerable GetStaticDependencies(No if (method.IsRuntimeDeterminedExactMethod) method = method.GetCanonMethodTarget(CanonicalFormKind.Specific); - if (!factory.VTable(method.OwningType).HasFixedSlots) + if (!factory.VTable(method.OwningType).HasKnownVirtualMethodUse) { yield return new DependencyListEntry(factory.VirtualMethodUse(method), "Slot number"); } 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 3d213666a3d87..4f7b48f33030e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -361,7 +361,7 @@ private void CreateNodeCaches() { // We don't need to track virtual method uses for types that have a vtable with a known layout. // It's a waste of CPU time and memory. - Debug.Assert(!VTable(method.OwningType).HasFixedSlots); + Debug.Assert(method.OwningType.IsGenericDefinition || !VTable(method.OwningType).HasKnownVirtualMethodUse); return new VariantInterfaceMethodUseNode(method); }); @@ -1159,7 +1159,7 @@ protected override VirtualMethodUseNode CreateValueFromKey(MethodDesc key) { // We don't need to track virtual method uses for types that have a vtable with a known layout. // It's a waste of CPU time and memory. - Debug.Assert(!_factory.VTable(key.OwningType).HasFixedSlots); + Debug.Assert(!_factory.VTable(key.OwningType).HasKnownVirtualMethodUse); return new VirtualMethodUseNode(key); } protected override int GetKeyHashCode(MethodDesc key) => key.GetHashCode(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs index cb54964bcb05b..284adf484ca6f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs @@ -154,7 +154,7 @@ public IEnumerable InstantiateDependencies(NodeFactory fact if (createInfo.NeedsVirtualMethodUseTracking) { MethodDesc instantiatedTargetMethod = createInfo.TargetMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation); - if (!factory.VTable(instantiatedTargetMethod.OwningType).HasFixedSlots) + if (!factory.VTable(instantiatedTargetMethod.OwningType).HasKnownVirtualMethodUse) { result.Add( new DependencyListEntry( diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs index f3a4410dcbab6..ab9ccafa2db95 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs @@ -43,7 +43,7 @@ public enum ReadyToRunHelperId ConstrainedDirectCall, } - public partial class ReadyToRunHelperNode : AssemblyStubNode, INodeWithDebugInfo + public partial class ReadyToRunHelperNode : AssemblyStubNode { private readonly ReadyToRunHelperId _id; private readonly object _target; @@ -64,7 +64,6 @@ public ReadyToRunHelperNode(ReadyToRunHelperId id, object target) defType.ComputeStaticFieldLayout(StaticLayoutKind.StaticRegionSizesAndFields); } break; - case ReadyToRunHelperId.VirtualCall: case ReadyToRunHelperId.ResolveVirtualFunction: { // Make sure we aren't trying to callvirt Object.Finalize @@ -92,9 +91,6 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde { switch (_id) { - case ReadyToRunHelperId.VirtualCall: - sb.Append("__VirtualCall_").Append(nameMangler.GetMangledMethodName((MethodDesc)_target)); - break; case ReadyToRunHelperId.GetNonGCStaticBase: sb.Append("__GetNonGCStaticBase_").Append(nameMangler.GetMangledTypeName((TypeDesc)_target)); break; @@ -122,7 +118,7 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { - if (_id == ReadyToRunHelperId.VirtualCall || _id == ReadyToRunHelperId.ResolveVirtualFunction) + if (_id == ReadyToRunHelperId.ResolveVirtualFunction) { var targetMethod = (MethodDesc)_target; @@ -131,7 +127,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact #if !SUPPORT_JIT factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencyList, factory, targetMethod); - if (!factory.VTable(targetMethod.OwningType).HasFixedSlots) + if (!factory.VTable(targetMethod.OwningType).HasKnownVirtualMethodUse) { dependencyList.Add(factory.VirtualMethodUse((MethodDesc)_target), "ReadyToRun Virtual Method Call"); @@ -152,7 +148,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact #if !SUPPORT_JIT factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref dependencyList, factory, targetMethod); - if (!factory.VTable(info.TargetMethod.OwningType).HasFixedSlots) + if (!factory.VTable(info.TargetMethod.OwningType).HasKnownVirtualMethodUse) { dependencyList ??= new DependencyList(); dependencyList.Add(factory.VirtualMethodUse(info.TargetMethod), "ReadyToRun Delegate to virtual method"); @@ -176,39 +172,6 @@ public override IEnumerable GetConditionalStaticDep return dependencyList; } - IEnumerable INodeWithDebugInfo.GetNativeSequencePoints() - { - if (_id == ReadyToRunHelperId.VirtualCall) - { - // Generate debug information that lets debuggers step into the virtual calls. - // We generate a step into sequence point at the point where the helper jumps to - // the target of the virtual call. - TargetDetails target = ((MethodDesc)_target).Context.Target; - int debuggerStepInOffset = -1; - switch (target.Architecture) - { - case TargetArchitecture.X64: - debuggerStepInOffset = 3; - break; - } - if (debuggerStepInOffset != -1) - { - return new NativeSequencePoint[] - { - new NativeSequencePoint(0, string.Empty, WellKnownLineNumber.DebuggerStepThrough), - new NativeSequencePoint(debuggerStepInOffset, string.Empty, WellKnownLineNumber.DebuggerStepIn) - }; - } - } - - return Array.Empty(); - } - - IEnumerable INodeWithDebugInfo.GetDebugVars() - { - return Array.Empty(); - } - #if !SUPPORT_JIT public override int ClassCode => -911637948; @@ -224,7 +187,6 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer case ReadyToRunHelperId.GetGCStaticBase: case ReadyToRunHelperId.GetThreadStaticBase: return comparer.Compare((TypeDesc)_target, (TypeDesc)((ReadyToRunHelperNode)other)._target); - case ReadyToRunHelperId.VirtualCall: case ReadyToRunHelperId.ResolveVirtualFunction: return comparer.Compare((MethodDesc)_target, (MethodDesc)((ReadyToRunHelperNode)other)._target); case ReadyToRunHelperId.DelegateCtor: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedMethodNode.cs index 79c291cdcf332..6fae955a5e5c9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedMethodNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectedMethodNode.cs @@ -68,7 +68,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto } else { - if (ReflectionVirtualInvokeMapNode.NeedsVirtualInvokeInfo(factory, slotDefiningMethod) && !factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) + if (ReflectionVirtualInvokeMapNode.NeedsVirtualInvokeInfo(factory, slotDefiningMethod) && !factory.VTable(slotDefiningMethod.OwningType).HasKnownVirtualMethodUse) dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Virtually callable reflectable method"); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs index b2f8e973c1672..9c49be270bdd9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionVirtualInvokeMapNode.cs @@ -95,7 +95,7 @@ public static void GetVirtualInvokeMapDependencies(ref DependencyList dependenci if (!method.HasInstantiation) { MethodDesc slotDefiningMethod = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(method); - if (!factory.VTable(slotDefiningMethod.OwningType).HasFixedSlots) + if (!factory.VTable(slotDefiningMethod.OwningType).HasKnownVirtualMethodUse) { dependencies.Add(factory.VirtualMethodUse(slotDefiningMethod), "Reflection virtual invoke method"); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs index ff7745b59f329..69f02c2406cfa 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs @@ -101,10 +101,11 @@ public bool BuildSealedVTableSlots(NodeFactory factory, bool relocsOnly) return true; DefType declType = _type.GetClosestDefType(); + VTableSliceNode declTypeVTable = factory.VTable(declType); // It's only okay to touch the actual list of slots if we're in the final emission phase // or the vtable is not built lazily. - if (relocsOnly && !factory.VTable(declType).HasFixedSlots) + if (relocsOnly && !declTypeVTable.HasKnownVirtualMethodUse) return false; _sealedVTableEntries = new List(); @@ -117,10 +118,13 @@ public bool BuildSealedVTableSlots(NodeFactory factory, bool relocsOnly) bool needsEntriesForInstanceInterfaceMethodImpls = !isInterface || ((MetadataType)declType).IsDynamicInterfaceCastableImplementation(); - IReadOnlyList virtualSlots = factory.VTable(declType).Slots; + IReadOnlyList virtualSlots = declTypeVTable.Slots; for (int i = 0; i < virtualSlots.Count; i++) { + if (!declTypeVTable.IsSlotUsed(virtualSlots[i])) + continue; + if (!virtualSlots[i].Signature.IsStatic && !needsEntriesForInstanceInterfaceMethodImpls) continue; @@ -146,12 +150,16 @@ public bool BuildSealedVTableSlots(NodeFactory factory, bool relocsOnly) var interfaceType = declTypeRuntimeInterfaces[interfaceIndex]; var definitionInterfaceType = declTypeDefinitionRuntimeInterfaces[interfaceIndex]; - virtualSlots = factory.VTable(interfaceType).Slots; + VTableSliceNode interfaceVTable = factory.VTable(interfaceType); + virtualSlots = interfaceVTable.Slots; for (int interfaceMethodSlot = 0; interfaceMethodSlot < virtualSlots.Count; interfaceMethodSlot++) { MethodDesc declMethod = virtualSlots[interfaceMethodSlot]; + if (!interfaceVTable.IsSlotUsed(declMethod)) + continue; + if (!declMethod.Signature.IsStatic && !needsEntriesForInstanceInterfaceMethodImpls) continue; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs index d6ecb47a38d61..b88fbd54e5b52 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunHelperNode.cs @@ -17,29 +17,6 @@ protected override void EmitCode(NodeFactory factory, ref ARMEmitter encoder, bo { switch (Id) { - case ReadyToRunHelperId.VirtualCall: - { - MethodDesc targetMethod = (MethodDesc)Target; - - Debug.Assert(!targetMethod.OwningType.IsInterface); - Debug.Assert(!targetMethod.CanMethodBeInSealedVTable(factory)); - - int pointerSize = factory.Target.PointerSize; - - int slot = 0; - if (!relocsOnly) - { - slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); - Debug.Assert(slot != -1); - } - - encoder.EmitLDR(encoder.TargetRegister.InterproceduralScratch, encoder.TargetRegister.Arg0, 0); - encoder.EmitLDR(encoder.TargetRegister.InterproceduralScratch, encoder.TargetRegister.InterproceduralScratch, - EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize)); - encoder.EmitJMP(encoder.TargetRegister.InterproceduralScratch); - } - break; - case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs index 7f672fca6fa15..5ab16c1814cee 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs @@ -18,29 +18,6 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, { switch (Id) { - case ReadyToRunHelperId.VirtualCall: - { - MethodDesc targetMethod = (MethodDesc)Target; - - Debug.Assert(!targetMethod.OwningType.IsInterface); - Debug.Assert(!targetMethod.CanMethodBeInSealedVTable(factory)); - - int pointerSize = factory.Target.PointerSize; - - int slot = 0; - if (!relocsOnly) - { - slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); - Debug.Assert(slot != -1); - } - - encoder.EmitLDR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg0, 0); - encoder.EmitLDR(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.IntraProcedureCallScratch1, - EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize)); - encoder.EmitJMP(encoder.TargetRegister.IntraProcedureCallScratch1); - } - break; - case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs index 2b54f9ccdcc63..db4d1855e20fb 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs @@ -18,29 +18,6 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter enc { switch (Id) { - case ReadyToRunHelperId.VirtualCall: - { - MethodDesc targetMethod = (MethodDesc)Target; - - Debug.Assert(!targetMethod.OwningType.IsInterface); - Debug.Assert(!targetMethod.CanMethodBeInSealedVTable(factory)); - - int pointerSize = factory.Target.PointerSize; - - int slot = 0; - if (!relocsOnly) - { - slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); - Debug.Assert(slot != -1); - } - - encoder.EmitLD(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg0, 0); - encoder.EmitLD(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.IntraProcedureCallScratch1, - EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize)); - encoder.EmitJMP(encoder.TargetRegister.IntraProcedureCallScratch1); - } - break; - case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs index 1d430fb1f96fa..620878463e3e3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunHelperNode.cs @@ -18,29 +18,6 @@ protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter encoder { switch (Id) { - case ReadyToRunHelperId.VirtualCall: - { - MethodDesc targetMethod = (MethodDesc)Target; - - Debug.Assert(!targetMethod.OwningType.IsInterface); - Debug.Assert(!targetMethod.CanMethodBeInSealedVTable(factory)); - - int pointerSize = factory.Target.PointerSize; - - int slot = 0; - if (!relocsOnly) - { - slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); - Debug.Assert(slot != -1); - } - - encoder.EmitLD(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.Arg0, 0); - encoder.EmitLD(encoder.TargetRegister.IntraProcedureCallScratch1, encoder.TargetRegister.IntraProcedureCallScratch1, - EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize)); - encoder.EmitJMP(encoder.TargetRegister.IntraProcedureCallScratch1); - } - break; - case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index ab20cf1c892fa..4b1b2d247f44d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -18,31 +18,6 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo { switch (Id) { - case ReadyToRunHelperId.VirtualCall: - { - MethodDesc targetMethod = (MethodDesc)Target; - - Debug.Assert(!targetMethod.OwningType.IsInterface); - Debug.Assert(!targetMethod.CanMethodBeInSealedVTable(factory)); - - AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); - encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); - - int pointerSize = factory.Target.PointerSize; - - int slot = 0; - if (!relocsOnly) - { - slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); - Debug.Assert(slot != -1); - } - Debug.Assert(((NativeSequencePoint[])((INodeWithDebugInfo)this).GetNativeSequencePoints())[1].NativeOffset == encoder.Builder.CountBytes); - - AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); - encoder.EmitJmpToAddrMode(ref jmpAddrMode); - } - break; - case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs index 186ea99cca979..239bfd338d9c3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X86/X86ReadyToRunHelperNode.cs @@ -19,30 +19,6 @@ protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bo { switch (Id) { - case ReadyToRunHelperId.VirtualCall: - { - MethodDesc targetMethod = (MethodDesc)Target; - - Debug.Assert(!targetMethod.OwningType.IsInterface); - Debug.Assert(!targetMethod.CanMethodBeInSealedVTable(factory)); - - AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int32); - encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); - - int pointerSize = factory.Target.PointerSize; - - int slot = 0; - if (!relocsOnly) - { - slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); - Debug.Assert(slot != -1); - } - - AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int32); - encoder.EmitJmpToAddrMode(ref jmpAddrMode); - } - break; - case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VTableSliceNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VTableSliceNode.cs index 8a398b7809e26..9050cf8c097fe 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VTableSliceNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VTableSliceNode.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using Internal.TypeSystem; @@ -21,20 +22,54 @@ public abstract class VTableSliceNode : DependencyNodeCore public VTableSliceNode(TypeDesc type) { Debug.Assert(!type.IsArray, "Wanted to call GetClosestDefType?"); + Debug.Assert(!type.IsGenericDefinition); _type = type; } + protected static MethodDesc[] ComputeSlots(TypeDesc type) + { + var slots = default(ArrayBuilder); + + bool isObjectType = type.IsObject; + DefType defType = type.GetClosestDefType(); + + IEnumerable allSlots = type.IsInterface ? + type.GetAllVirtualMethods() : defType.EnumAllVirtualSlots(); + + foreach (var method in allSlots) + { + // GVMs are not emitted in the type's vtable. + if (method.HasInstantiation) + continue; + + // Finalizers are called via a field on the MethodTable, not through the VTable + if (isObjectType && method.Name == "Finalize") + continue; + + // Current type doesn't define this slot. + if (method.OwningType != defType) + continue; + + slots.Add(method); + } + + return slots.ToArray(); + } + public abstract IReadOnlyList Slots { get; } + public abstract bool IsSlotUsed(MethodDesc slot); + public TypeDesc Type => _type; /// - /// Gets a value indicating whether the slots are assigned at the beginning of the compilation. + /// Gets a value indicating whether is needed to track virtual method uses + /// in this vtable slice. /// - public abstract bool HasFixedSlots + public abstract bool HasKnownVirtualMethodUse { get; } @@ -47,10 +82,14 @@ public override IEnumerable GetStaticDependencies(NodeFacto { if (_type.HasBaseType) { - return new[] { new DependencyListEntry(factory.VTable(_type.BaseType), "Base type VTable") }; + yield return new DependencyListEntry(factory.VTable(_type.BaseType), "Base type VTable"); } - return null; + TypeDesc canonType = _type.ConvertToCanonForm(CanonicalFormKind.Specific); + if (_type != canonType) + { + yield return new DependencyListEntry(factory.VTable(canonType), "Canonical type VTable"); + } } public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; @@ -66,9 +105,9 @@ public override IEnumerable GetStaticDependencies(NodeFacto /// internal class PrecomputedVTableSliceNode : VTableSliceNode { - private readonly IReadOnlyList _slots; + private readonly MethodDesc[] _slots; - public PrecomputedVTableSliceNode(TypeDesc type, IReadOnlyList slots) + public PrecomputedVTableSliceNode(TypeDesc type, MethodDesc[] slots) : base(type) { _slots = slots; @@ -82,7 +121,13 @@ public override IReadOnlyList Slots } } - public override bool HasFixedSlots + public override bool IsSlotUsed(MethodDesc slot) + { + Debug.Assert(Array.IndexOf(_slots, slot) != -1); + return true; + } + + public override bool HasKnownVirtualMethodUse { get { @@ -101,36 +146,6 @@ public EagerlyBuiltVTableSliceNode(TypeDesc type) : base(type, ComputeSlots(type)) { } - - private static MethodDesc[] ComputeSlots(TypeDesc type) - { - var slots = default(ArrayBuilder); - - bool isObjectType = type.IsObject; - DefType defType = type.GetClosestDefType(); - - IEnumerable allSlots = type.IsInterface ? - type.GetAllVirtualMethods() : defType.EnumAllVirtualSlots(); - - foreach (var method in allSlots) - { - // GVMs are not emitted in the type's vtable. - if (method.HasInstantiation) - continue; - - // Finalizers are called via a field on the MethodTable, not through the VTable - if (isObjectType && method.Name == "Finalize") - continue; - - // Current type doesn't define this slot. - if (method.OwningType != defType) - continue; - - slots.Add(method); - } - - return slots.ToArray(); - } } /// @@ -139,43 +154,37 @@ private static MethodDesc[] ComputeSlots(TypeDesc type) /// internal sealed class LazilyBuiltVTableSliceNode : VTableSliceNode { - private HashSet _usedMethods = new HashSet(); - private MethodDesc[] _slots; + private readonly HashSet _usedMethods = new HashSet(); + private readonly MethodDesc[] _slots; +#if DEBUG + private bool _isLocked; +#endif - public LazilyBuiltVTableSliceNode(TypeDesc type) + public LazilyBuiltVTableSliceNode(TypeDesc type, MethodDesc[] slots = null) : base(type) { + _slots = slots ?? ComputeSlots(type); } public override IReadOnlyList Slots { get { - if (_slots == null) - { - // Sort the lazily populated slots in metadata order (the order in which they show up - // in GetAllMethods()). - // This ensures that Foo and Foo will end up with the same vtable - // no matter the order in which VirtualMethodUse nodes populated it. - ArrayBuilder slotsBuilder = default(ArrayBuilder); - DefType defType = _type.GetClosestDefType(); - foreach (var method in defType.GetAllMethods()) - { - if (_usedMethods.Contains(method)) - slotsBuilder.Add(method); - } - Debug.Assert(_usedMethods.Count == slotsBuilder.Count); - _slots = slotsBuilder.ToArray(); - - // Null out used methods so that we AV if someone tries to add now. - _usedMethods = null; - } - return _slots; } } - public override bool HasFixedSlots + public override bool IsSlotUsed(MethodDesc slot) + { + Debug.Assert(Array.IndexOf(_slots, slot) != -1); +#if DEBUG + _isLocked = true; +#endif + + return _usedMethods.Contains(slot); + } + + public override bool HasKnownVirtualMethodUse { get { @@ -188,8 +197,11 @@ public void AddEntry(MethodDesc virtualMethod) // GVMs are not emitted in the type's vtable. Debug.Assert(!virtualMethod.HasInstantiation); Debug.Assert(virtualMethod.IsVirtual); - Debug.Assert(_slots == null && _usedMethods != null); Debug.Assert(virtualMethod.OwningType == _type); + Debug.Assert(Array.IndexOf(_slots, virtualMethod) != -1); +#if DEBUG + Debug.Assert(!_isLocked); +#endif // Finalizers are called via a field on the MethodTable, not through the VTable if (_type.IsObject && virtualMethod.Name == "Finalize") diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs index 2a637c46a77d6..ca716e795ccb1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/VirtualMethodUseNode.cs @@ -85,7 +85,7 @@ public override IEnumerable GetConditionalStaticDep DefType universalCanonicalOwningType = (DefType)_decl.OwningType.ConvertToCanonForm(CanonicalFormKind.Universal); Debug.Assert(universalCanonicalOwningType.IsCanonicalSubtype(CanonicalFormKind.Universal)); - if (!factory.VTable(universalCanonicalOwningType).HasFixedSlots) + if (!factory.VTable(universalCanonicalOwningType).HasKnownVirtualMethodUse) { // This code ensures that in cases where we don't structurally force all universal canonical instantiations // to have full vtables, that we ensure that all vtables are equivalently shaped between universal and non-universal types diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index 46e3df57efd1f..97820c425b33a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -264,7 +264,7 @@ public ReadOnlyFieldPolicy GetReadOnlyFieldPolicy() private sealed class ScannedVTableProvider : VTableSliceProvider { - private Dictionary> _vtableSlices = new Dictionary>(); + private readonly Dictionary _vtableSlices = new Dictionary(); public ScannedVTableProvider(ImmutableArray> markedNodes) { @@ -273,7 +273,16 @@ public ScannedVTableProvider(ImmutableArray> mar var vtableSliceNode = node as VTableSliceNode; if (vtableSliceNode != null) { - _vtableSlices.Add(vtableSliceNode.Type, vtableSliceNode.Slots); + ArrayBuilder usedSlots = default; + + for (int i = 0; i < vtableSliceNode.Slots.Count; i++) + { + MethodDesc slot = vtableSliceNode.Slots[i]; + if (vtableSliceNode.IsSlotUsed(slot)) + usedSlots.Add(slot); + } + + _vtableSlices.Add(vtableSliceNode.Type, usedSlots.ToArray()); } } } @@ -284,7 +293,7 @@ internal override VTableSliceNode GetSlice(TypeDesc type) // https://github.com/dotnet/corert/issues/3873 if (type.GetTypeDefinition() is Internal.TypeSystem.Ecma.EcmaType) { - if (!_vtableSlices.TryGetValue(type, out IReadOnlyList slots)) + if (!_vtableSlices.TryGetValue(type, out MethodDesc[] slots)) { // If we couldn't find the vtable slice information for this type, it's because the scanner // didn't correctly predict what will be needed. @@ -297,7 +306,7 @@ internal override VTableSliceNode GetSlice(TypeDesc type) string typeName = ExceptionTypeNameFormatter.Instance.FormatName(type); throw new ScannerFailedException($"VTable of type '{typeName}' not computed by the IL scanner."); } - return new PrecomputedVTableSliceNode(type, slots); + return new LazilyBuiltVTableSliceNode(type, slots); } else return new LazilyBuiltVTableSliceNode(type); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index e048adcf9f4bd..34f2c243d8d75 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -707,11 +707,7 @@ private void ImportCall(ILOpcode opcode, int token) _dependencies.Add(_factory.InterfaceDispatchCell(method), reason); } } - else if (_compilation.HasFixedSlotVTable(method.OwningType)) - { - // No dependencies: virtual call through the vtable - } - else + else if (_compilation.NeedsSlotUseTracking(method.OwningType)) { MethodDesc slotDefiningMethod = targetMethod.IsNewSlot ? targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod); diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 0c283d9f53a67..1eafeaf01db7a 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -1615,31 +1615,14 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO pResult->nullInstanceCheck = false; } - else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) == 0 - // Canonically-equivalent types have the same vtable layout. Check the canonical form. - // We don't want to accidentally ask about Foo that may or may not - // be available to ask vtable questions about. - // This can happen in inlining that the scanner didn't expect. - && _compilation.HasFixedSlotVTable(targetMethod.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific))) + else if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) == 0) { pResult->kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_VTABLE; pResult->nullInstanceCheck = true; } else { - ReadyToRunHelperId helperId; - if ((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0) - { - pResult->kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_LDVIRTFTN; - helperId = ReadyToRunHelperId.ResolveVirtualFunction; - } - else - { - // CORINFO_CALL_CODE_POINTER tells the JIT that this is indirect - // call that should not be inlined. - pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER; - helperId = ReadyToRunHelperId.VirtualCall; - } + pResult->kind = CORINFO_CALL_KIND.CORINFO_VIRTUALCALL_LDVIRTFTN; // If this is a non-interface call, we actually don't need a runtime lookup to find the target. // We don't even need to keep track of the runtime-determined method being called because the system ensures @@ -1650,7 +1633,6 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO // We need JitInterface changes to fully support this. // If this is LDVIRTFTN of an interface method that is part of a verifiable delegate creation sequence, // RyuJIT is not going to use this value. - Debug.Assert(helperId == ReadyToRunHelperId.ResolveVirtualFunction); pResult->exactContextNeedsRuntimeLookup = false; pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ExternSymbol("NYI_LDVIRTFTN")); } @@ -1666,7 +1648,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( - _compilation.NodeFactory.ReadyToRunHelper(helperId, slotDefiningMethod)); + _compilation.NodeFactory.ReadyToRunHelper(ReadyToRunHelperId.ResolveVirtualFunction, slotDefiningMethod)); } // The current NativeAOT ReadyToRun helpers do not handle null thisptr - ask the JIT to emit explicit null checks @@ -1840,12 +1822,16 @@ private void getMethodVTableOffset(CORINFO_METHOD_STRUCT_* method, ref uint offs // Canonically-equivalent types have the same slots, so ask for Foo<__Canon, __Canon>. methodDesc = methodDesc.GetCanonMethodTarget(CanonicalFormKind.Specific); - int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(_compilation.NodeFactory, methodDesc, methodDesc.OwningType); + TypeDesc owningType = methodDesc.OwningType; + int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(_compilation.NodeFactory, methodDesc, owningType); if (slot == -1) { throw new InvalidOperationException(methodDesc.ToString()); } + if (_compilation.NeedsSlotUseTracking(owningType)) + (_additionalDependencies ??= new ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList()).Add(_compilation.NodeFactory.VirtualMethodUse(methodDesc), "Virtual method call"); + offsetAfterIndirection = (uint)(EETypeNode.GetVTableOffset(pointerSize) + slot * pointerSize); }