Move DispatchMap pointer to MethodTable (#85698)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Wed, 3 May 2023 13:49:18 +0000 (22:49 +0900)
committerGitHub <noreply@github.com>
Wed, 3 May 2023 13:49:18 +0000 (06:49 -0700)
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.

13 files changed:
src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
src/coreclr/nativeaot/Common/src/Internal/Runtime/TypeManagerHandle.cs
src/coreclr/nativeaot/Runtime/TypeManager.cpp
src/coreclr/nativeaot/Runtime/TypeManager.h
src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h
src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs
src/coreclr/tools/Common/Internal/Runtime/MethodTable.Constants.cs
src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CanonicalEETypeNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs

index a6897c8..4debbad 100644 (file)
@@ -856,22 +856,7 @@ namespace Internal.Runtime
         {
             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 @@ namespace Internal.Runtime
         {
             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<Pointer<DispatchMap>>(EETypeField.ETF_DispatchMap).Value;
+
+                return GetField<RelativePointer<DispatchMap>>(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 @@ namespace Internal.Runtime
                 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 @@ namespace Internal.Runtime
         internal static uint GetSizeofEEType(
             ushort cVirtuals,
             ushort cInterfaces,
+            bool fHasDispatchMap,
             bool fHasFinalizer,
             bool fRequiresOptionalFields,
             bool fHasSealedVirtuals,
@@ -1464,6 +1457,7 @@ namespace Internal.Runtime
                 (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) +
index 9780acd..d1559fc 100644 (file)
@@ -20,7 +20,6 @@ namespace Internal.Runtime
         {
             public IntPtr OsHandle;
             public IntPtr ReadyToRunHeader;
-            public IntPtr DispatchMap;
         }
 
         public TypeManagerHandle(IntPtr handleValue)
@@ -48,13 +47,5 @@ namespace Internal.Runtime
                 return _handleValue->OsHandle;
             }
         }
-
-        public unsafe IntPtr DispatchMap
-        {
-            get
-            {
-                return _handleValue->DispatchMap;
-            }
-        }
     }
 }
index a8e3729..8458f88 100644 (file)
@@ -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)
index 84d1ead..91a38ff 100644 (file)
@@ -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;
index e343de0..750facc 100644 (file)
@@ -43,7 +43,7 @@ enum class ReadyToRunSectionType
     StringTable                 = 200,
     GCStaticRegion              = 201,
     ThreadStaticRegion          = 202,
-    InterfaceDispatchTable      = 203,
+    // unused                   = 203,
     TypeManagerIndirection      = 204,
     EagerCctor                  = 205,
     FrozenObjectRegion          = 206,
index 0509ea7..fe136fa 100644 (file)
@@ -155,6 +155,7 @@ namespace Internal.Runtime.TypeLoader
                 int baseSize = 0;
 
                 bool isValueType;
+                bool hasDispatchMap;
                 bool hasFinalizer;
                 bool isNullable;
                 bool isArray;
@@ -172,6 +173,7 @@ namespace Internal.Runtime.TypeLoader
                 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 @@ namespace Internal.Runtime.TypeLoader
                 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 @@ namespace Internal.Runtime.TypeLoader
                     int cbEEType = (int)MethodTable.GetSizeofEEType(
                         numVtableSlots,
                         runtimeInterfacesLength,
+                        hasDispatchMap,
                         hasFinalizer,
                         cbOptionalFieldsSize > 0,
                         hasSealedVTable,
@@ -300,6 +300,12 @@ namespace Internal.Runtime.TypeLoader
                     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)
                     {
index 29a1044..98b77cc 100644 (file)
@@ -16,7 +16,10 @@ namespace Internal.Runtime
         /// </summary>
         EETypeKindMask = 0x00030000,
 
-        // Unused = 0x00040000,
+        /// <summary>
+        /// Type has an associated dispatch map.
+        /// </summary>
+        HasDispatchMap = 0x00040000,
 
         /// <summary>
         /// This type was dynamically allocated at runtime.
@@ -177,6 +180,7 @@ namespace Internal.Runtime
         ETF_InterfaceMap,
         ETF_TypeManagerIndirection,
         ETF_WritableData,
+        ETF_DispatchMap,
         ETF_Finalizer,
         ETF_OptionalFieldsPtr,
         ETF_SealedVirtualSlots,
@@ -237,11 +241,6 @@ namespace Internal.Runtime
         RareFlags,
 
         /// <summary>
-        /// Index of the dispatch map pointer in the DispatchMap table
-        /// </summary>
-        DispatchMap,
-
-        /// <summary>
         /// Padding added to a value type when allocated on the GC heap
         /// </summary>
         ValueTypeFieldPadding,
index 52d098a..54f9001 100644 (file)
@@ -78,7 +78,7 @@ namespace Internal.Runtime
         StringTable = 200, // Unused
         GCStaticRegion = 201,
         ThreadStaticRegion = 202,
-        InterfaceDispatchTable = 203,
+        // Unused = 203,
         TypeManagerIndirection = 204,
         EagerCctor = 205,
         FrozenObjectRegion = 206,
index 5001927..7a6467a 100644 (file)
@@ -42,9 +42,6 @@ namespace ILCompiler.DependencyAnalysis
 
             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))
index 1e9e314..716c204 100644 (file)
@@ -36,12 +36,6 @@ namespace ILCompiler.DependencyAnalysis
 
             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
index 01cde35..6498c34 100644 (file)
@@ -689,6 +689,7 @@ namespace ILCompiler.DependencyAnalysis
 
             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 @@ namespace ILCompiler.DependencyAnalysis
                     flags |= (uint)EETypeFlags.HasSealedVTableEntriesFlag;
             }
 
+            if (MightHaveInterfaceDispatchMap(factory))
+            {
+                flags |= (uint)EETypeFlags.HasDispatchMap;
+            }
+
             if (HasOptionalFields)
             {
                 flags |= (uint)EETypeFlags.OptionalFieldsFlag;
@@ -1200,17 +1206,23 @@ namespace ILCompiler.DependencyAnalysis
             }
         }
 
+        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);
+            }
+        }
+
         /// <summary>
         /// Populate the OptionalFieldsRuntimeBuilder if any optional fields are required.
         /// </summary>
         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();
index 1401162..38104d7 100644 (file)
@@ -50,7 +50,6 @@ namespace ILCompiler.DependencyAnalysis
         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)
index 043f2ae..c726a9d 100644 (file)
@@ -391,11 +391,6 @@ namespace ILCompiler.DependencyAnalysis
                 return new EmbeddedTrimmingDescriptorNode(module);
             });
 
-            _interfaceDispatchMapIndirectionNodes = new NodeCache<TypeDesc, EmbeddedObjectNode>((TypeDesc type) =>
-            {
-                return DispatchMapTable.NewNodeWithSymbol(InterfaceDispatchMap(type));
-            });
-
             _genericCompositions = new NodeCache<Instantiation, GenericCompositionNode>((Instantiation details) =>
             {
                 return new GenericCompositionNode(details);
@@ -759,13 +754,6 @@ namespace ILCompiler.DependencyAnalysis
             return _interfaceDispatchMaps.GetOrAdd(type);
         }
 
-        private NodeCache<TypeDesc, EmbeddedObjectNode> _interfaceDispatchMapIndirectionNodes;
-
-        public EmbeddedObjectNode InterfaceDispatchMapIndirection(TypeDesc type)
-        {
-            return _interfaceDispatchMapIndirectionNodes.GetOrAdd(type);
-        }
-
         private NodeCache<Instantiation, GenericCompositionNode> _genericCompositions;
 
         internal ISymbolNode GenericComposition(Instantiation details)
@@ -1248,11 +1236,6 @@ namespace ILCompiler.DependencyAnalysis
             "__EagerCctorEnd",
             null);
 
-        public ArrayOfEmbeddedPointersNode<InterfaceDispatchMapNode> DispatchMapTable = new ArrayOfEmbeddedPointersNode<InterfaceDispatchMapNode>(
-            "__DispatchMapTableStart",
-            "__DispatchMapTableEnd",
-            new SortableDependencyNode.ObjectNodeComparer(CompilerComparer.Instance));
-
         public ArrayOfFrozenObjectsNode FrozenSegmentRegion = new ArrayOfFrozenObjectsNode();
 
         internal ModuleInitializerListNode ModuleInitializerList = new ModuleInitializerListNode();
@@ -1276,7 +1259,6 @@ namespace ILCompiler.DependencyAnalysis
             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 @@ namespace ILCompiler.DependencyAnalysis
             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);