Skip to content

Commit

Permalink
Use static sealed vtable for dynamically created types (#79332)
Browse files Browse the repository at this point in the history
The sealed vtable is identical for all canonically equivalent types. We had a to make a copy because in the presence of universal shared generics, new sealed vtables could be created for dynamic types and those need to use pointer-sized slots (the slots are relative pointers in the static case and have been since Redhawk times).
  • Loading branch information
MichalStrehovsky authored Dec 7, 2022
1 parent 69805c3 commit 9b29f96
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 113 deletions.
46 changes: 22 additions & 24 deletions src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,41 +1091,39 @@ private static IntPtr FollowRelativePointer(int* pDist)
return result;
}

internal IntPtr GetSealedVirtualSlot(ushort slotNumber)
#if TYPE_LOADER_IMPLEMENTATION
internal
#else
private
#endif
void* GetSealedVirtualTable()
{
Debug.Assert((RareFlags & EETypeRareFlags.HasSealedVTableEntriesFlag) != 0);

fixed (MethodTable* pThis = &this)
uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots);
byte* pThis = (byte*)Unsafe.AsPointer(ref this);
if (IsDynamicType || !SupportsRelativePointers)
{
if (IsDynamicType || !SupportsRelativePointers)
{
uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots);
IntPtr* pSealedVirtualsSlotTable = *(IntPtr**)((byte*)pThis + cbSealedVirtualSlotsTypeOffset);
return pSealedVirtualsSlotTable[slotNumber];
}
else
{
uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots);
int* pSealedVirtualsSlotTable = (int*)FollowRelativePointer((int*)((byte*)pThis + cbSealedVirtualSlotsTypeOffset));
IntPtr result = FollowRelativePointer(&pSealedVirtualsSlotTable[slotNumber]);
return result;
}
return *(void**)(pThis + cbSealedVirtualSlotsTypeOffset);
}
else
{
return (void*)FollowRelativePointer((int*)(pThis + cbSealedVirtualSlotsTypeOffset));
}
}

#if TYPE_LOADER_IMPLEMENTATION
internal void SetSealedVirtualSlot(IntPtr value, ushort slotNumber)
internal IntPtr GetSealedVirtualSlot(ushort slotNumber)
{
Debug.Assert(IsDynamicType);

fixed (MethodTable* pThis = &this)
void* pSealedVtable = GetSealedVirtualTable();
if (!SupportsRelativePointers)
{
uint cbSealedVirtualSlotsTypeOffset = GetFieldOffset(EETypeField.ETF_SealedVirtualSlots);
IntPtr* pSealedVirtualsSlotTable = *(IntPtr**)((byte*)pThis + cbSealedVirtualSlotsTypeOffset);
pSealedVirtualsSlotTable[slotNumber] = value;
return ((IntPtr*)pSealedVtable)[slotNumber];
}
else
{
return FollowRelativePointer(&((int*)pSealedVtable)[slotNumber]);
}
}
#endif

internal byte* OptionalFieldsPtr
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,82 +237,76 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo

// Optional fields encoding
int cbOptionalFieldsSize;
OptionalFieldsRuntimeBuilder optionalFields;
{
optionalFields = new OptionalFieldsRuntimeBuilder(pTemplateEEType != null ? pTemplateEEType->OptionalFieldsPtr : null);

uint rareFlags = optionalFields.GetFieldValue(EETypeOptionalFieldTag.RareFlags, 0);
OptionalFieldsRuntimeBuilder optionalFields = new OptionalFieldsRuntimeBuilder(pTemplateEEType->OptionalFieldsPtr);

if (state.NumSealedVTableEntries > 0)
rareFlags |= (uint)EETypeRareFlags.HasSealedVTableEntriesFlag;
uint rareFlags = optionalFields.GetFieldValue(EETypeOptionalFieldTag.RareFlags, 0);

if (state.NonGcDataSize != 0)
rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithNonGcStatics;
if (state.NonGcDataSize != 0)
rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithNonGcStatics;

if (state.GcDataSize != 0)
rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithGcStatics;
if (state.GcDataSize != 0)
rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithGcStatics;

if (state.ThreadDataSize != 0)
rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithThreadStatics;
if (state.ThreadDataSize != 0)
rareFlags |= (uint)EETypeRareFlags.IsDynamicTypeWithThreadStatics;

#if TARGET_ARM
if (state.FieldAlignment == 8)
rareFlags |= (uint)EETypeRareFlags.RequiresAlign8Flag;
else
rareFlags &= ~(uint)EETypeRareFlags.RequiresAlign8Flag;
if (state.FieldAlignment == 8)
rareFlags |= (uint)EETypeRareFlags.RequiresAlign8Flag;
else
rareFlags &= ~(uint)EETypeRareFlags.RequiresAlign8Flag;
#endif

#if TARGET_ARM || TARGET_ARM64
if (state.IsHFA)
rareFlags |= (uint)EETypeRareFlags.IsHFAFlag;
else
rareFlags &= ~(uint)EETypeRareFlags.IsHFAFlag;
if (state.IsHFA)
rareFlags |= (uint)EETypeRareFlags.IsHFAFlag;
else
rareFlags &= ~(uint)EETypeRareFlags.IsHFAFlag;
#endif
if (state.HasStaticConstructor)
rareFlags |= (uint)EETypeRareFlags.HasCctorFlag;
else
rareFlags &= ~(uint)EETypeRareFlags.HasCctorFlag;
if (state.HasStaticConstructor)
rareFlags |= (uint)EETypeRareFlags.HasCctorFlag;
else
rareFlags &= ~(uint)EETypeRareFlags.HasCctorFlag;

if (isAbstractClass)
rareFlags |= (uint)EETypeRareFlags.IsAbstractClassFlag;
else
rareFlags &= ~(uint)EETypeRareFlags.IsAbstractClassFlag;
if (isAbstractClass)
rareFlags |= (uint)EETypeRareFlags.IsAbstractClassFlag;
else
rareFlags &= ~(uint)EETypeRareFlags.IsAbstractClassFlag;

if (isByRefLike)
rareFlags |= (uint)EETypeRareFlags.IsByRefLikeFlag;
else
rareFlags &= ~(uint)EETypeRareFlags.IsByRefLikeFlag;
if (isByRefLike)
rareFlags |= (uint)EETypeRareFlags.IsByRefLikeFlag;
else
rareFlags &= ~(uint)EETypeRareFlags.IsByRefLikeFlag;

if (isNullable)
{
uint nullableValueOffset = state.NullableValueOffset;
if (isNullable)
{
uint nullableValueOffset = state.NullableValueOffset;

// The stored offset is never zero (Nullable has a boolean there indicating whether the value is valid).
// If the real offset is one, then the field isn't set. Otherwise the offset is encoded - 1 to save space.
if (nullableValueOffset == 1)
optionalFields.ClearField(EETypeOptionalFieldTag.NullableValueOffset);
else
optionalFields.SetFieldValue(EETypeOptionalFieldTag.NullableValueOffset, checked(nullableValueOffset - 1));
}
else
{
// The stored offset is never zero (Nullable has a boolean there indicating whether the value is valid).
// If the real offset is one, then the field isn't set. Otherwise the offset is encoded - 1 to save space.
if (nullableValueOffset == 1)
optionalFields.ClearField(EETypeOptionalFieldTag.NullableValueOffset);
}
else
optionalFields.SetFieldValue(EETypeOptionalFieldTag.NullableValueOffset, checked(nullableValueOffset - 1));
}
else
{
optionalFields.ClearField(EETypeOptionalFieldTag.NullableValueOffset);
}

optionalFields.SetFieldValue(EETypeOptionalFieldTag.RareFlags, rareFlags);
optionalFields.SetFieldValue(EETypeOptionalFieldTag.RareFlags, rareFlags);

// Dispatch map is fetched from template type
optionalFields.ClearField(EETypeOptionalFieldTag.DispatchMap);
// Dispatch map is fetched from template type
optionalFields.ClearField(EETypeOptionalFieldTag.DispatchMap);

optionalFields.ClearField(EETypeOptionalFieldTag.ValueTypeFieldPadding);
optionalFields.ClearField(EETypeOptionalFieldTag.ValueTypeFieldPadding);

if (valueTypeFieldPaddingEncoded != 0)
optionalFields.SetFieldValue(EETypeOptionalFieldTag.ValueTypeFieldPadding, valueTypeFieldPaddingEncoded);
if (valueTypeFieldPaddingEncoded != 0)
optionalFields.SetFieldValue(EETypeOptionalFieldTag.ValueTypeFieldPadding, valueTypeFieldPaddingEncoded);

// Compute size of optional fields encoding
cbOptionalFieldsSize = optionalFields.Encode();
Debug.Assert(cbOptionalFieldsSize > 0);
}
// Compute size of optional fields encoding
cbOptionalFieldsSize = optionalFields.Encode();
Debug.Assert(cbOptionalFieldsSize > 0);

// Note: The number of vtable slots on the MethodTable to create is not necessary equal to the number of
// vtable slots on the template type for universal generics (see ComputeVTableLayout)
Expand All @@ -332,7 +326,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
runtimeInterfacesLength,
hasFinalizer,
true,
state.NumSealedVTableEntries > 0,
(rareFlags & (int)EETypeRareFlags.HasSealedVTableEntriesFlag) != 0,
isGeneric,
state.NonGcDataSize != 0,
state.GcDataSize != 0,
Expand Down Expand Up @@ -385,18 +379,10 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
}

// Copy the sealed vtable entries if they exist on the template type
if (state.NumSealedVTableEntries > 0)
if ((rareFlags & (int)EETypeRareFlags.HasSealedVTableEntriesFlag) != 0)
{
state.HalfBakedSealedVTable = MemoryHelpers.AllocateMemory((int)state.NumSealedVTableEntries * IntPtr.Size);

uint cbSealedVirtualSlotsTypeOffset = pEEType->GetFieldOffset(EETypeField.ETF_SealedVirtualSlots);
*((IntPtr*)((byte*)pEEType + cbSealedVirtualSlotsTypeOffset)) = state.HalfBakedSealedVTable;

for (ushort i = 0; i < state.NumSealedVTableEntries; i++)
{
IntPtr value = pTemplateEEType->GetSealedVirtualSlot(i);
pEEType->SetSealedVirtualSlot(value, i);
}
*((void**)((byte*)pEEType + cbSealedVirtualSlotsTypeOffset)) = pTemplateEEType->GetSealedVirtualTable();
}

if (MethodTable.SupportsWritableData)
Expand Down Expand Up @@ -455,7 +441,6 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
state.HalfBakedDictionary = state.Dictionary.Allocate();

Debug.Assert(!state.HalfBakedRuntimeTypeHandle.IsNull());
Debug.Assert((state.NumSealedVTableEntries == 0 && state.HalfBakedSealedVTable == IntPtr.Zero) || (state.NumSealedVTableEntries > 0 && state.HalfBakedSealedVTable != IntPtr.Zero));
Debug.Assert((state.Dictionary == null && state.HalfBakedDictionary == IntPtr.Zero) || (state.Dictionary != null && state.HalfBakedDictionary != IntPtr.Zero));

successful = true;
Expand All @@ -466,8 +451,6 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
{
if (eeTypePtrPlusGCDesc != IntPtr.Zero)
MemoryHelpers.FreeMemory(eeTypePtrPlusGCDesc);
if (state.HalfBakedSealedVTable != IntPtr.Zero)
MemoryHelpers.FreeMemory(state.HalfBakedSealedVTable);
if (state.HalfBakedDictionary != IntPtr.Zero)
MemoryHelpers.FreeMemory(state.HalfBakedDictionary);
if (gcStaticData != IntPtr.Zero)
Expand Down Expand Up @@ -749,7 +732,6 @@ public static RuntimeTypeHandle CreateEEType(TypeDesc type, TypeBuilderState sta
Debug.Assert(false == state.HasStaticConstructor);
Debug.Assert(0 == state.GcDataSize);
Debug.Assert(0 == state.ThreadStaticOffset);
Debug.Assert(0 == state.NumSealedVTableEntries);
Debug.Assert(IntPtr.Zero == state.GcStaticDesc);
Debug.Assert(IntPtr.Zero == state.ThreadStaticDesc);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,11 +473,6 @@ internal void ParseNativeLayoutInfo(TypeBuilderState state, TypeDesc type)
typeInfoParser.SkipInteger(); // Handled in type layout algorithm
break;

case BagElementKind.SealedVTableEntries:
TypeLoaderLogger.WriteLine("Found BagElementKind.SealedVTableEntries");
state.NumSealedVTableEntries = typeInfoParser.GetUnsigned();
break;

case BagElementKind.DictionaryLayout:
TypeLoaderLogger.WriteLine("Found BagElementKind.DictionaryLayout");
Debug.Assert(!isTemplateUniversalCanon, "Universal template nativelayout do not have DictionaryLayout");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public TypeBuilderState(TypeDesc typeBeingBuilt)

public RuntimeTypeHandle HalfBakedRuntimeTypeHandle;
public IntPtr HalfBakedDictionary;
public IntPtr HalfBakedSealedVTable;

private bool _templateComputed;
private bool _nativeLayoutTokenComputed;
Expand Down Expand Up @@ -344,7 +343,6 @@ public bool HasStaticConstructor
public IntPtr GcStaticDesc;
public IntPtr ThreadStaticDesc;
public uint ThreadStaticOffset;
public uint NumSealedVTableEntries;
public GenericVariance[] GenericVarianceFlags;

// Sentinel static to allow us to initialize _instanceLayout to something
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ enum BagElementKind : uint
ThreadStaticOffset = 0x4a,
FieldLayout = 0x4b,
// unused = 0x4c,
SealedVTableEntries = 0x4d,
// unused = 0x4d,
ClassConstructorPointer = 0x4e,
// unused = 0x4f,
GenericVarianceInfo = 0x50,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1229,16 +1229,6 @@ public override Vertex WriteVertex(NodeFactory factory)
layoutInfo.Append(BagElementKind.BaseType, factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.TypeSignatureVertex(_type.BaseType)).WriteVertex(factory));
}

if (!_type.IsArrayTypeWithoutGenericInterfaces() && ConstructedEETypeNode.CreationAllowed(_type))
{
SealedVTableNode sealedVTable = factory.SealedVTable(_type.ConvertToCanonForm(CanonicalFormKind.Specific));

sealedVTable.BuildSealedVTableSlots(factory, relocsOnly: false /* This is the final emission phase */);

if (sealedVTable.NumSealedVTableEntries > 0)
layoutInfo.AppendUnsigned(BagElementKind.SealedVTableEntries, (uint)sealedVTable.NumSealedVTableEntries);
}

if (_type.GetTypeDefinition().HasVariance || factory.TypeSystemContext.IsGenericArrayInterfaceType(_type))
{
// Runtime casting logic relies on all interface types implemented on arrays
Expand Down

0 comments on commit 9b29f96

Please sign in to comment.