Skip to content

Commit

Permalink
Move DispatchMap pointer to MethodTable (#85698)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
MichalStrehovsky authored May 3, 2023
1 parent edd6d63 commit b412b6b
Show file tree
Hide file tree
Showing 13 changed files with 60 additions and 91 deletions.
58 changes: 26 additions & 32 deletions src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -856,48 +856,31 @@ 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;
}
}

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<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.
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -1450,6 +1442,7 @@ public ref T GetField<T>(uint offset)
internal static uint GetSizeofEEType(
ushort cVirtuals,
ushort cInterfaces,
bool fHasDispatchMap,
bool fHasFinalizer,
bool fRequiresOptionalFields,
bool fHasSealedVirtuals,
Expand All @@ -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) +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ private struct TypeManager
{
public IntPtr OsHandle;
public IntPtr ReadyToRunHeader;
public IntPtr DispatchMap;
}

public TypeManagerHandle(IntPtr handleValue)
Expand Down Expand Up @@ -48,13 +47,5 @@ public unsafe IntPtr OsModuleBase
return _handleValue->OsHandle;
}
}

public unsafe IntPtr DispatchMap
{
get
{
return _handleValue->DispatchMap;
}
}
}
}
1 change: 0 additions & 1 deletion src/coreclr/nativeaot/Runtime/TypeManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 0 additions & 3 deletions src/coreclr/nativeaot/Runtime/TypeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ enum class ReadyToRunSectionType
StringTable = 200,
GCStaticRegion = 201,
ThreadStaticRegion = 202,
InterfaceDispatchTable = 203,
// unused = 203,
TypeManagerIndirection = 204,
EagerCctor = 205,
FrozenObjectRegion = 206,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();

Expand All @@ -251,6 +250,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
int cbEEType = (int)MethodTable.GetSizeofEEType(
numVtableSlots,
runtimeInterfacesLength,
hasDispatchMap,
hasFinalizer,
cbOptionalFieldsSize > 0,
hasSealedVTable,
Expand Down Expand Up @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ internal enum EETypeFlags : uint
/// </summary>
EETypeKindMask = 0x00030000,

// Unused = 0x00040000,
/// <summary>
/// Type has an associated dispatch map.
/// </summary>
HasDispatchMap = 0x00040000,

/// <summary>
/// This type was dynamically allocated at runtime.
Expand Down Expand Up @@ -177,6 +180,7 @@ internal enum EETypeField
ETF_InterfaceMap,
ETF_TypeManagerIndirection,
ETF_WritableData,
ETF_DispatchMap,
ETF_Finalizer,
ETF_OptionalFieldsPtr,
ETF_SealedVirtualSlots,
Expand Down Expand Up @@ -236,11 +240,6 @@ internal enum EETypeOptionalFieldTag : byte
/// </summary>
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>
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public enum ReadyToRunSectionType
StringTable = 200, // Unused
GCStaticRegion = 201,
ThreadStaticRegion = 202,
InterfaceDispatchTable = 203,
// Unused = 203,
TypeManagerIndirection = 204,
EagerCctor = 205,
FrozenObjectRegion = 206,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

/// <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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,11 +391,6 @@ private void CreateNodeCaches()
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);
Expand Down Expand Up @@ -759,13 +754,6 @@ internal InterfaceDispatchMapNode InterfaceDispatchMap(TypeDesc type)
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)
Expand Down Expand Up @@ -1248,11 +1236,6 @@ public string GetSymbolAlternateName(ISymbolNode node)
"__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();
Expand All @@ -1276,7 +1259,6 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory>
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");
Expand All @@ -1285,7 +1267,6 @@ public virtual void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory>
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);

Expand Down

0 comments on commit b412b6b

Please sign in to comment.