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.
{
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;
}
}
{
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.
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)
{
internal static uint GetSizeofEEType(
ushort cVirtuals,
ushort cInterfaces,
+ bool fHasDispatchMap,
bool fHasFinalizer,
bool fRequiresOptionalFields,
bool fHasSealedVirtuals,
(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) +
{
public IntPtr OsHandle;
public IntPtr ReadyToRunHeader;
- public IntPtr DispatchMap;
}
public TypeManagerHandle(IntPtr handleValue)
return _handleValue->OsHandle;
}
}
-
- public unsafe IntPtr DispatchMap
- {
- get
- {
- return _handleValue->DispatchMap;
- }
- }
}
}
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)
#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;
StringTable = 200,
GCStaticRegion = 201,
ThreadStaticRegion = 202,
- InterfaceDispatchTable = 203,
+ // unused = 203,
TypeManagerIndirection = 204,
EagerCctor = 205,
FrozenObjectRegion = 206,
int baseSize = 0;
bool isValueType;
+ bool hasDispatchMap;
bool hasFinalizer;
bool isNullable;
bool isArray;
baseSize = (int)pTemplateEEType->RawBaseSize;
isValueType = pTemplateEEType->IsValueType;
hasFinalizer = pTemplateEEType->IsFinalizable;
+ hasDispatchMap = pTemplateEEType->HasDispatchMap;
isNullable = pTemplateEEType->IsNullable;
flags = pTemplateEEType->Flags;
isArray = pTemplateEEType->IsArray;
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();
int cbEEType = (int)MethodTable.GetSizeofEEType(
numVtableSlots,
runtimeInterfacesLength,
+ hasDispatchMap,
hasFinalizer,
cbOptionalFieldsSize > 0,
hasSealedVTable,
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)
{
/// </summary>
EETypeKindMask = 0x00030000,
- // Unused = 0x00040000,
+ /// <summary>
+ /// Type has an associated dispatch map.
+ /// </summary>
+ HasDispatchMap = 0x00040000,
/// <summary>
/// This type was dynamically allocated at runtime.
ETF_InterfaceMap,
ETF_TypeManagerIndirection,
ETF_WritableData,
+ ETF_DispatchMap,
ETF_Finalizer,
ETF_OptionalFieldsPtr,
ETF_SealedVirtualSlots,
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,
StringTable = 200, // Unused
GCStaticRegion = 201,
ThreadStaticRegion = 202,
- InterfaceDispatchTable = 203,
+ // Unused = 203,
TypeManagerIndirection = 204,
EagerCctor = 205,
FrozenObjectRegion = 206,
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))
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
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);
flags |= (uint)EETypeFlags.HasSealedVTableEntriesFlag;
}
+ if (MightHaveInterfaceDispatchMap(factory))
+ {
+ flags |= (uint)EETypeFlags.HasDispatchMap;
+ }
+
if (HasOptionalFields)
{
flags |= (uint)EETypeFlags.OptionalFieldsFlag;
}
}
+ 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();
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)
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);
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)
"__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();
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");
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);