From 64c515addd264bb00efb305278067a1c5bde4992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 3 May 2023 16:13:58 +0900 Subject: [PATCH] Move DispatchMap pointer to MethodTable Sending this for consideration. The old approach also had an advantage. Wouldn't be the end of the world if we keep that. Before this PR, accessing dispatch map involved: * Reading optional fields to find the field with the right tag * The optional field contained an integer index into a table * The index was used to index into a dispatch map table to find a pointer to the actual dispatch map * We then followed the pointer to get to the dispatch map. The advantage of this scheme is smaller working set (MethodTable is smaller), but this assumes the MethodTable has other optional fields (because we still need a pointer to the optional fields). Turns out most MethodTables only need optional fields pointer because of the dispatch map and if we move them to MethodTable, we no longer need an optional field pointer. This PR simply moves the dispatch map pointer to MethodTable. I'm seeing another 15 kB saving on BasicMinimalApi. Plus the code looks simpler. --- .../src/Internal/Runtime/MethodTable.cs | 58 +++++++++---------- .../src/Internal/Runtime/TypeManagerHandle.cs | 9 --- src/coreclr/nativeaot/Runtime/TypeManager.cpp | 1 - src/coreclr/nativeaot/Runtime/TypeManager.h | 3 - .../nativeaot/Runtime/inc/ModuleHeaders.h | 2 +- .../Runtime/TypeLoader/EETypeCreator.cs | 12 +++- .../Internal/Runtime/MethodTable.Constants.cs | 11 ++-- .../Common/Internal/Runtime/ModuleHeaders.cs | 2 +- .../DependencyAnalysis/CanonicalEETypeNode.cs | 3 - .../ConstructedEETypeNode.cs | 6 -- .../Compiler/DependencyAnalysis/EETypeNode.cs | 24 ++++++-- .../InterfaceDispatchMapNode.cs | 1 - .../DependencyAnalysis/NodeFactory.cs | 19 ------ 13 files changed, 60 insertions(+), 91 deletions(-) diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs index a6897c8b11ba25..4debbad687ed12 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs @@ -856,22 +856,7 @@ internal bool HasDispatchMap { get { - if (NumInterfaces == 0) - return false; - byte* optionalFields = OptionalFieldsPtr; - - const uint NoDispatchMap = 0xffffffff; - uint idxDispatchMap = NoDispatchMap; - if (optionalFields != null) - idxDispatchMap = OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.DispatchMap, NoDispatchMap); - - if (idxDispatchMap == NoDispatchMap) - { - if (IsDynamicType) - return DynamicTemplateType->HasDispatchMap; - return false; - } - return true; + return (_uFlags & (uint)EETypeFlags.HasDispatchMap) != 0; } } @@ -879,25 +864,23 @@ internal DispatchMap* DispatchMap { get { - if (NumInterfaces == 0) - return null; - byte* optionalFields = OptionalFieldsPtr; - const uint NoDispatchMap = 0xffffffff; - uint idxDispatchMap = NoDispatchMap; - if (optionalFields != null) - idxDispatchMap = OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.DispatchMap, NoDispatchMap); - if (idxDispatchMap == NoDispatchMap) - { - if (IsDynamicType) - return DynamicTemplateType->DispatchMap; + if (!HasDispatchMap) return null; - } - if (SupportsRelativePointers) - return (DispatchMap*)FollowRelativePointer((int*)TypeManager.DispatchMap + idxDispatchMap); - else - return ((DispatchMap**)TypeManager.DispatchMap)[idxDispatchMap]; + if (IsDynamicType || !SupportsRelativePointers) + return GetField>(EETypeField.ETF_DispatchMap).Value; + + return GetField>(EETypeField.ETF_DispatchMap).Value; } +#if TYPE_LOADER_IMPLEMENTATION + set + { + Debug.Assert(IsDynamicType && HasDispatchMap); + + fixed (MethodTable* pThis = &this) + *(DispatchMap**)((byte*)pThis + GetFieldOffset(EETypeField.ETF_DispatchMap)) = value; + } +#endif } // Get the address of the finalizer method for finalizable types. @@ -1345,6 +1328,15 @@ public uint GetFieldOffset(EETypeField eField) cbOffset += relativeOrFullPointerOffset; } + // Followed by pointer to the dispatch map + if (eField == EETypeField.ETF_DispatchMap) + { + Debug.Assert(HasDispatchMap); + return cbOffset; + } + if (HasDispatchMap) + cbOffset += relativeOrFullPointerOffset; + // Followed by the pointer to the finalizer method. if (eField == EETypeField.ETF_Finalizer) { @@ -1450,6 +1442,7 @@ public ref T GetField(uint offset) internal static uint GetSizeofEEType( ushort cVirtuals, ushort cInterfaces, + bool fHasDispatchMap, bool fHasFinalizer, bool fRequiresOptionalFields, bool fHasSealedVirtuals, @@ -1464,6 +1457,7 @@ internal static uint GetSizeofEEType( (sizeof(MethodTable*) * cInterfaces) + sizeof(IntPtr) + // TypeManager (SupportsWritableData ? sizeof(IntPtr) : 0) + // WritableData + (fHasDispatchMap ? sizeof(UIntPtr) : 0) + (fHasFinalizer ? sizeof(UIntPtr) : 0) + (fRequiresOptionalFields ? sizeof(IntPtr) : 0) + (fHasSealedVirtuals ? sizeof(IntPtr) : 0) + diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.cs index 9780acd39f72a9..d1559fce550ea5 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.cs @@ -20,7 +20,6 @@ private struct TypeManager { public IntPtr OsHandle; public IntPtr ReadyToRunHeader; - public IntPtr DispatchMap; } public TypeManagerHandle(IntPtr handleValue) @@ -48,13 +47,5 @@ public unsafe IntPtr OsModuleBase return _handleValue->OsHandle; } } - - public unsafe IntPtr DispatchMap - { - get - { - return _handleValue->DispatchMap; - } - } } } diff --git a/src/coreclr/nativeaot/Runtime/TypeManager.cpp b/src/coreclr/nativeaot/Runtime/TypeManager.cpp index a8e372956aedd9..8458f883be2b59 100644 --- a/src/coreclr/nativeaot/Runtime/TypeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/TypeManager.cpp @@ -45,7 +45,6 @@ TypeManager::TypeManager(HANDLE osModule, ReadyToRunHeader * pHeader, void** pCl int length; m_pStaticsGCDataSection = (uint8_t*)GetModuleSection(ReadyToRunSectionType::GCStaticRegion, &length); m_pThreadStaticsDataSection = (uint8_t*)GetModuleSection(ReadyToRunSectionType::ThreadStaticRegion, &length); - m_pDispatchMapTable = (DispatchMap **)GetModuleSection(ReadyToRunSectionType::InterfaceDispatchTable, &length); } void * TypeManager::GetModuleSection(ReadyToRunSectionType sectionId, int * length) diff --git a/src/coreclr/nativeaot/Runtime/TypeManager.h b/src/coreclr/nativeaot/Runtime/TypeManager.h index 84d1ead3de344f..91a38ffbbacf98 100644 --- a/src/coreclr/nativeaot/Runtime/TypeManager.h +++ b/src/coreclr/nativeaot/Runtime/TypeManager.h @@ -4,14 +4,11 @@ #include "ModuleHeaders.h" #include "ICodeManager.h" -class DispatchMap; - class TypeManager { // NOTE: Part of this layout is a contract with the managed side in TypeManagerHandle.cs HANDLE m_osModule; ReadyToRunHeader * m_pHeader; - DispatchMap** m_pDispatchMapTable; uint8_t* m_pStaticsGCDataSection; uint8_t* m_pThreadStaticsDataSection; void** m_pClasslibFunctions; diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index 7d0c985486c9d1..86281a25627ca8 100644 --- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -43,7 +43,7 @@ enum class ReadyToRunSectionType StringTable = 200, GCStaticRegion = 201, ThreadStaticRegion = 202, - InterfaceDispatchTable = 203, + // unused = 203, TypeManagerIndirection = 204, EagerCctor = 205, FrozenObjectRegion = 206, diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs index 0509ea7217f960..fe136fabc5427d 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs @@ -155,6 +155,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo int baseSize = 0; bool isValueType; + bool hasDispatchMap; bool hasFinalizer; bool isNullable; bool isArray; @@ -172,6 +173,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo baseSize = (int)pTemplateEEType->RawBaseSize; isValueType = pTemplateEEType->IsValueType; hasFinalizer = pTemplateEEType->IsFinalizable; + hasDispatchMap = pTemplateEEType->HasDispatchMap; isNullable = pTemplateEEType->IsNullable; flags = pTemplateEEType->Flags; isArray = pTemplateEEType->IsArray; @@ -225,9 +227,6 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo if (rareFlags != 0) optionalFields.SetFieldValue(EETypeOptionalFieldTag.RareFlags, rareFlags); - // Dispatch map is fetched from template type - optionalFields.ClearField(EETypeOptionalFieldTag.DispatchMap); - // Compute size of optional fields encoding cbOptionalFieldsSize = optionalFields.Encode(); @@ -251,6 +250,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo int cbEEType = (int)MethodTable.GetSizeofEEType( numVtableSlots, runtimeInterfacesLength, + hasDispatchMap, hasFinalizer, cbOptionalFieldsSize > 0, hasSealedVTable, @@ -300,6 +300,12 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo for (int i = 0; i < numVtableSlots; i++) pVtable[i] = pTemplateVtable[i]; + // Copy dispatch map from the template type + if (hasDispatchMap) + { + pEEType->DispatchMap = pTemplateEEType->DispatchMap; + } + // Copy Pointer to finalizer method from the template type if (hasFinalizer) { diff --git a/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs b/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs index 29a1044f2c8068..98b77ccfd9bb10 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs @@ -16,7 +16,10 @@ internal enum EETypeFlags : uint /// EETypeKindMask = 0x00030000, - // Unused = 0x00040000, + /// + /// Type has an associated dispatch map. + /// + HasDispatchMap = 0x00040000, /// /// This type was dynamically allocated at runtime. @@ -177,6 +180,7 @@ internal enum EETypeField ETF_InterfaceMap, ETF_TypeManagerIndirection, ETF_WritableData, + ETF_DispatchMap, ETF_Finalizer, ETF_OptionalFieldsPtr, ETF_SealedVirtualSlots, @@ -236,11 +240,6 @@ internal enum EETypeOptionalFieldTag : byte /// RareFlags, - /// - /// Index of the dispatch map pointer in the DispatchMap table - /// - DispatchMap, - /// /// Padding added to a value type when allocated on the GC heap /// diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index f1985389496664..a6369493929245 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -78,7 +78,7 @@ public enum ReadyToRunSectionType StringTable = 200, // Unused GCStaticRegion = 201, ThreadStaticRegion = 202, - InterfaceDispatchTable = 203, + // Unused = 203, TypeManagerIndirection = 204, EagerCctor = 205, FrozenObjectRegion = 206, diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs index 500192714f719e..7a6467a96e9e85 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs @@ -42,9 +42,6 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact DefType closestDefType = _type.GetClosestDefType(); - if (MightHaveInterfaceDispatchMap(factory)) - dependencyList.Add(factory.InterfaceDispatchMap(_type), "Canonical interface dispatch map"); - dependencyList.Add(factory.VTable(closestDefType), "VTable"); if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal)) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index 1e9e3144d2be0d..716c2047b237f9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -36,12 +36,6 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact DefType closestDefType = _type.GetClosestDefType(); - if (MightHaveInterfaceDispatchMap(factory)) - { - TypeDesc canonType = _type.ConvertToCanonForm(CanonicalFormKind.Specific); - dependencyList.Add(factory.InterfaceDispatchMap(canonType), "Interface dispatch map"); - } - if (_type.IsArray) { // Array MethodTable depends on System.Array's virtuals. Array EETypes don't point to 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 01cde356ab263e..6498c340e5cc53 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -689,6 +689,7 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo OutputTypeManagerIndirection(factory, ref objData); OutputWritableData(factory, ref objData); + OutputDispatchMap(factory, ref objData); OutputFinalizerMethod(factory, ref objData); OutputOptionalFields(factory, ref objData); OutputSealedVTable(factory, relocsOnly, ref objData); @@ -740,6 +741,11 @@ private void OutputFlags(NodeFactory factory, ref ObjectDataBuilder objData, boo flags |= (uint)EETypeFlags.HasSealedVTableEntriesFlag; } + if (MightHaveInterfaceDispatchMap(factory)) + { + flags |= (uint)EETypeFlags.HasDispatchMap; + } + if (HasOptionalFields) { flags |= (uint)EETypeFlags.OptionalFieldsFlag; @@ -1200,17 +1206,23 @@ private void OutputFunctionPointerParameters(NodeFactory factory, ref ObjectData } } + private void OutputDispatchMap(NodeFactory factory, ref ObjectDataBuilder objData) + { + if (MightHaveInterfaceDispatchMap(factory)) + { + ISymbolNode dispatchMap = factory.InterfaceDispatchMap(_type.ConvertToCanonForm(CanonicalFormKind.Specific)); + if (factory.Target.SupportsRelativePointers) + objData.EmitReloc(dispatchMap, RelocType.IMAGE_REL_BASED_RELPTR32); + else + objData.EmitPointerReloc(dispatchMap); + } + } + /// /// Populate the OptionalFieldsRuntimeBuilder if any optional fields are required. /// protected internal virtual void ComputeOptionalEETypeFields(NodeFactory factory, bool relocsOnly) { - if (!relocsOnly && MightHaveInterfaceDispatchMap(factory)) - { - TypeDesc canonType = _type.ConvertToCanonForm(CanonicalFormKind.Specific); - _optionalFieldsBuilder.SetFieldValue(EETypeOptionalFieldTag.DispatchMap, checked((uint)factory.InterfaceDispatchMapIndirection(canonType).IndexFromBeginningOfArray)); - } - ComputeRareFlags(factory); ComputeNullableValueOffset(); ComputeValueTypeFieldPadding(); 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 14011621e4cf5f..38104d7ab015c9 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs @@ -50,7 +50,6 @@ public override ObjectNodeSection GetSection(NodeFactory factory) protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { var result = new DependencyList(); - result.Add(factory.InterfaceDispatchMapIndirection(_type), "Interface dispatch map indirection node"); // VTable slots of implemented interfaces are consulted during emission foreach (TypeDesc runtimeInterface in _type.RuntimeInterfaces) 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 043f2aed75eb54..c726a9dc0a66d5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -391,11 +391,6 @@ private void CreateNodeCaches() return new EmbeddedTrimmingDescriptorNode(module); }); - _interfaceDispatchMapIndirectionNodes = new NodeCache((TypeDesc type) => - { - return DispatchMapTable.NewNodeWithSymbol(InterfaceDispatchMap(type)); - }); - _genericCompositions = new NodeCache((Instantiation details) => { return new GenericCompositionNode(details); @@ -759,13 +754,6 @@ internal InterfaceDispatchMapNode InterfaceDispatchMap(TypeDesc type) return _interfaceDispatchMaps.GetOrAdd(type); } - private NodeCache _interfaceDispatchMapIndirectionNodes; - - public EmbeddedObjectNode InterfaceDispatchMapIndirection(TypeDesc type) - { - return _interfaceDispatchMapIndirectionNodes.GetOrAdd(type); - } - private NodeCache _genericCompositions; internal ISymbolNode GenericComposition(Instantiation details) @@ -1248,11 +1236,6 @@ public string GetSymbolAlternateName(ISymbolNode node) "__EagerCctorEnd", null); - public ArrayOfEmbeddedPointersNode DispatchMapTable = new ArrayOfEmbeddedPointersNode( - "__DispatchMapTableStart", - "__DispatchMapTableEnd", - new SortableDependencyNode.ObjectNodeComparer(CompilerComparer.Instance)); - public ArrayOfFrozenObjectsNode FrozenSegmentRegion = new ArrayOfFrozenObjectsNode(); internal ModuleInitializerListNode ModuleInitializerList = new ModuleInitializerListNode(); @@ -1276,7 +1259,6 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase graph.AddRoot(ThreadStaticsRegion, "ThreadStaticsRegion is always generated"); graph.AddRoot(EagerCctorTable, "EagerCctorTable is always generated"); graph.AddRoot(TypeManagerIndirection, "TypeManagerIndirection is always generated"); - graph.AddRoot(DispatchMapTable, "DispatchMapTable is always generated"); graph.AddRoot(FrozenSegmentRegion, "FrozenSegmentRegion is always generated"); graph.AddRoot(InterfaceDispatchCellSection, "Interface dispatch cell section is always generated"); graph.AddRoot(ModuleInitializerList, "Module initializer list is always generated"); @@ -1285,7 +1267,6 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase ReadyToRunHeader.Add(ReadyToRunSectionType.ThreadStaticRegion, ThreadStaticsRegion, ThreadStaticsRegion.StartSymbol, ThreadStaticsRegion.EndSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.EagerCctor, EagerCctorTable, EagerCctorTable.StartSymbol, EagerCctorTable.EndSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.TypeManagerIndirection, TypeManagerIndirection, TypeManagerIndirection); - ReadyToRunHeader.Add(ReadyToRunSectionType.InterfaceDispatchTable, DispatchMapTable, DispatchMapTable.StartSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.FrozenObjectRegion, FrozenSegmentRegion, FrozenSegmentRegion, FrozenSegmentRegion.EndSymbol); ReadyToRunHeader.Add(ReadyToRunSectionType.ModuleInitializerList, ModuleInitializerList, ModuleInitializerList, ModuleInitializerList.EndSymbol);