Skip to content

Commit

Permalink
Shuffle MethodTable flags (#106182)
Browse files Browse the repository at this point in the history
* Move IsByRefLikeFlag from RareFlags to ExtendedFlags

* Generic param count to BaseSize

* Replace RareFlags with ETF_DynamicTypeFlags

* Move ValueTypeFieldPadding

* Move NullableValueOffset

* GC.Collect

---------

Co-authored-by: Filip Navara <[email protected]>
  • Loading branch information
MichalStrehovsky and filipnavara authored Aug 11, 2024
1 parent fb8ae3e commit 7cb32e1
Show file tree
Hide file tree
Showing 16 changed files with 219 additions and 840 deletions.
186 changes: 40 additions & 146 deletions src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,31 +146,6 @@ private unsafe struct RelatedTypeUnion
public MethodTable* _pRelatedParameterType;
}

private static unsafe class OptionalFieldsReader
{
internal static uint GetInlineField(byte* pFields, EETypeOptionalFieldTag eTag, uint uiDefaultValue)
{
if (pFields == null)
return uiDefaultValue;

bool isLastField = false;
while (!isLastField)
{
byte fieldHeader = NativePrimitiveDecoder.ReadUInt8(ref pFields);
isLastField = (fieldHeader & 0x80) != 0;
EETypeOptionalFieldTag eCurrentTag = (EETypeOptionalFieldTag)(fieldHeader & 0x7f);
uint uiCurrentValue = NativePrimitiveDecoder.DecodeUnsigned(ref pFields);

// If we found a tag match return the current value.
if (eCurrentTag == eTag)
return uiCurrentValue;
}

// Reached end of stream without getting a match. Field is not present so return default value.
return uiDefaultValue;
}
}

/// <summary>
/// Gets a value indicating whether the statically generated data structures use relative pointers.
/// </summary>
Expand Down Expand Up @@ -200,17 +175,6 @@ internal static bool SupportsRelativePointers

// vtable follows

// These masks and paddings have been chosen so that the ValueTypePadding field can always fit in a byte of data.
// if the alignment is 8 bytes or less. If the alignment is higher then there may be a need for more bits to hold
// the rest of the padding data.
// If paddings of greater than 7 bytes are necessary, then the high bits of the field represent that padding
private const uint ValueTypePaddingLowMask = 0x7;
private const uint ValueTypePaddingHighMask = 0xFFFFFF00;
private const uint ValueTypePaddingMax = 0x07FFFFFF;
private const int ValueTypePaddingHighShift = 8;
private const uint ValueTypePaddingAlignmentMask = 0xF8;
private const int ValueTypePaddingAlignmentShift = 3;

internal bool HasComponentSize
{
get
Expand Down Expand Up @@ -255,13 +219,13 @@ internal ushort GenericParameterCount
get
{
Debug.Assert(IsGenericTypeDefinition);
return ComponentSize;
return (ushort)_uBaseSize;
}
#if TYPE_LOADER_IMPLEMENTATION
set
{
Debug.Assert(IsGenericTypeDefinition);
ComponentSize = value;
_uBaseSize = value;
}
#endif
}
Expand Down Expand Up @@ -376,14 +340,6 @@ private EETypeKind Kind
}
}

internal bool HasOptionalFields
{
get
{
return (_uFlags & (uint)EETypeFlags.OptionalFieldsFlag) != 0;
}
}

// Mark or determine that a type is generic and one or more of it's type parameters is co- or
// contra-variant. This only applies to interface and delegate types.
internal bool HasGenericVariance
Expand Down Expand Up @@ -618,7 +574,7 @@ internal bool IsByRefLike
{
get
{
return (RareFlags & EETypeRareFlags.IsByRefLikeFlag) != 0;
return IsValueType && (_uFlags & (uint)EETypeFlagsEx.IsByRefLikeFlag) != 0;
}
}

Expand Down Expand Up @@ -809,19 +765,8 @@ internal uint ValueTypeFieldPadding
{
get
{
byte* optionalFields = OptionalFieldsPtr;

// If there are no optional fields then the padding must have been the default, 0.
if (optionalFields == null)
return 0;

// Get the value from the optional fields. The default is zero if that particular field was not included.
// The low bits of this field is the ValueType field padding, the rest of the byte is the alignment if present
uint ValueTypeFieldPaddingData = OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ValueTypeFieldPadding, 0);
uint padding = ValueTypeFieldPaddingData & ValueTypePaddingLowMask;
// If there is additional padding, the other bits have that data
padding |= (ValueTypeFieldPaddingData & ValueTypePaddingHighMask) >> (ValueTypePaddingHighShift - ValueTypePaddingAlignmentShift);
return padding;
Debug.Assert(IsValueType);
return (_uFlags & (uint)EETypeFlagsEx.ValueTypeFieldPaddingMask) >> ValueTypeFieldPaddingConsts.Shift;
}
}

Expand Down Expand Up @@ -956,17 +901,8 @@ internal byte NullableValueOffset
get
{
Debug.Assert(IsNullable);

// Grab optional fields. If there aren't any then the offset was the default of 1 (immediately after the
// Nullable's boolean flag).
byte* optionalFields = OptionalFieldsPtr;
if (optionalFields == null)
return 1;

// The offset is never zero (Nullable has a boolean there indicating whether the value is valid). So the
// offset is encoded - 1 to save space. The zero below is the default value if the field wasn't encoded at
// all.
return (byte)(OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.NullableValueOffset, 0) + 1);
int log2valueoffset = (int)(_uFlags & (ushort)EETypeFlagsEx.NullableValueOffsetMask) >> NullableValueOffsetConsts.Shift;
return (byte)(1 << log2valueoffset);
}
}

Expand Down Expand Up @@ -1033,32 +969,6 @@ internal IntPtr GetSealedVirtualSlot(ushort slotNumber)
}
}

internal byte* OptionalFieldsPtr
{
get
{
if (!HasOptionalFields)
return null;

uint offset = GetFieldOffset(EETypeField.ETF_OptionalFieldsPtr);

if (IsDynamicType || !SupportsRelativePointers)
return GetField<Pointer<byte>>(offset).Value;

return GetField<RelativePointer<byte>>(offset).Value;
}
#if TYPE_LOADER_IMPLEMENTATION
set
{
Debug.Assert(IsDynamicType);

_uFlags |= (uint)EETypeFlags.OptionalFieldsFlag;

GetField<IntPtr>(EETypeField.ETF_OptionalFieldsPtr) = (IntPtr)value;
}
#endif
}

internal MethodTable* DynamicTemplateType
{
get
Expand All @@ -1079,21 +989,21 @@ internal bool IsDynamicTypeWithCctor
{
get
{
return (RareFlags & EETypeRareFlags.IsDynamicTypeWithLazyCctor) != 0;
return (DynamicTypeFlags & DynamicTypeFlags.HasLazyCctor) != 0;
}
}

internal IntPtr DynamicGcStaticsData
{
get
{
Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0);
Debug.Assert((DynamicTypeFlags & DynamicTypeFlags.HasGCStatics) != 0);
return GetField<IntPtr>(EETypeField.ETF_DynamicGcStatics);
}
#if TYPE_LOADER_IMPLEMENTATION
set
{
Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0);
Debug.Assert((DynamicTypeFlags & DynamicTypeFlags.HasGCStatics) != 0);
GetField<IntPtr>(EETypeField.ETF_DynamicGcStatics) = value;
}
#endif
Expand All @@ -1103,13 +1013,13 @@ internal IntPtr DynamicNonGcStaticsData
{
get
{
Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0);
Debug.Assert((DynamicTypeFlags & DynamicTypeFlags.HasNonGCStatics) != 0);
return GetField<IntPtr>(EETypeField.ETF_DynamicNonGcStatics);
}
#if TYPE_LOADER_IMPLEMENTATION
set
{
Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0);
Debug.Assert((DynamicTypeFlags & DynamicTypeFlags.HasNonGCStatics) != 0);
GetField<IntPtr>(EETypeField.ETF_DynamicNonGcStatics) = value;
}
#endif
Expand All @@ -1119,13 +1029,13 @@ internal IntPtr DynamicThreadStaticsIndex
{
get
{
Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0);
Debug.Assert((DynamicTypeFlags & DynamicTypeFlags.HasThreadStatics) != 0);
return GetField<IntPtr>(EETypeField.ETF_DynamicThreadStaticOffset);
}
#if TYPE_LOADER_IMPLEMENTATION
set
{
Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0);
Debug.Assert((DynamicTypeFlags & DynamicTypeFlags.HasThreadStatics) != 0);
GetField<IntPtr>(EETypeField.ETF_DynamicThreadStaticOffset) = value;
}
#endif
Expand Down Expand Up @@ -1191,37 +1101,20 @@ internal void* WritableData
#endif
}

internal unsafe EETypeRareFlags RareFlags
internal DynamicTypeFlags DynamicTypeFlags
{
get
{
// If there are no optional fields then none of the rare flags have been set.
// Get the flags from the optional fields. The default is zero if that particular field was not included.
return HasOptionalFields ? (EETypeRareFlags)OptionalFieldsReader.GetInlineField(OptionalFieldsPtr, EETypeOptionalFieldTag.RareFlags, 0) : 0;
Debug.Assert(IsDynamicType);
return (DynamicTypeFlags)GetField<nint>(EETypeField.ETF_DynamicTypeFlags);
}
}

internal int FieldAlignmentRequirement
{
get
#if TYPE_LOADER_IMPLEMENTATION
set
{
byte* optionalFields = OptionalFieldsPtr;

// If there are no optional fields then the alignment must have been the default, IntPtr.Size.
// (This happens for all reference types, and for valuetypes with default alignment and no padding)
if (optionalFields == null)
return IntPtr.Size;

// Get the value from the optional fields. The default is zero if that particular field was not included.
// The low bits of this field is the ValueType field padding, the rest of the value is the alignment if present
uint alignmentValue = (OptionalFieldsReader.GetInlineField(optionalFields, EETypeOptionalFieldTag.ValueTypeFieldPadding, 0) & ValueTypePaddingAlignmentMask) >> ValueTypePaddingAlignmentShift;

// Alignment is stored as 1 + the log base 2 of the alignment, except a 0 indicates standard pointer alignment.
if (alignmentValue == 0)
return IntPtr.Size;
else
return 1 << ((int)alignmentValue - 1);
Debug.Assert(IsDynamicType);
GetField<nint>(EETypeField.ETF_DynamicTypeFlags) = (nint)value;
}
#endif
}

internal EETypeElementType ElementType
Expand Down Expand Up @@ -1283,15 +1176,6 @@ public uint GetFieldOffset(EETypeField eField)
if (IsFinalizable)
cbOffset += relativeOrFullPointerOffset;

// Followed by the pointer to the optional fields.
if (eField == EETypeField.ETF_OptionalFieldsPtr)
{
Debug.Assert(HasOptionalFields);
return cbOffset;
}
if (HasOptionalFields)
cbOffset += relativeOrFullPointerOffset;

// Followed by the pointer to the sealed virtual slots
if (eField == EETypeField.ETF_SealedVirtualSlots)
return cbOffset;
Expand Down Expand Up @@ -1338,26 +1222,37 @@ public uint GetFieldOffset(EETypeField eField)
if (IsDynamicType)
cbOffset += (uint)IntPtr.Size;

EETypeRareFlags rareFlags = RareFlags;
DynamicTypeFlags dynamicTypeFlags = 0;
if (eField == EETypeField.ETF_DynamicTypeFlags)
{
Debug.Assert(IsDynamicType);
return cbOffset;
}
if (IsDynamicType)
{
dynamicTypeFlags = (DynamicTypeFlags)GetField<nint>(cbOffset);
cbOffset += (uint)IntPtr.Size;
}

if (eField == EETypeField.ETF_DynamicGcStatics)
{
Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0);
Debug.Assert((dynamicTypeFlags & DynamicTypeFlags.HasGCStatics) != 0);
return cbOffset;
}
if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithGcStatics) != 0)
if ((dynamicTypeFlags & DynamicTypeFlags.HasGCStatics) != 0)
cbOffset += (uint)IntPtr.Size;

if (eField == EETypeField.ETF_DynamicNonGcStatics)
{
Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0);
Debug.Assert((dynamicTypeFlags & DynamicTypeFlags.HasNonGCStatics) != 0);
return cbOffset;
}
if ((rareFlags & EETypeRareFlags.IsDynamicTypeWithNonGcStatics) != 0)
if ((dynamicTypeFlags & DynamicTypeFlags.HasNonGCStatics) != 0)
cbOffset += (uint)IntPtr.Size;

if (eField == EETypeField.ETF_DynamicThreadStaticOffset)
{
Debug.Assert((rareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0);
Debug.Assert((dynamicTypeFlags & DynamicTypeFlags.HasThreadStatics) != 0);
return cbOffset;
}

Expand All @@ -1383,7 +1278,6 @@ internal static uint GetSizeofEEType(
ushort cInterfaces,
bool fHasDispatchMap,
bool fHasFinalizer,
bool fRequiresOptionalFields,
bool fHasSealedVirtuals,
bool fHasGenericInfo,
int cFunctionPointerTypeParameters,
Expand All @@ -1398,10 +1292,10 @@ internal static uint GetSizeofEEType(
sizeof(IntPtr) + // WritableData
(fHasDispatchMap ? sizeof(UIntPtr) : 0) +
(fHasFinalizer ? sizeof(UIntPtr) : 0) +
(fRequiresOptionalFields ? sizeof(IntPtr) : 0) +
(fHasSealedVirtuals ? sizeof(IntPtr) : 0) +
cFunctionPointerTypeParameters * sizeof(IntPtr) +
(fHasGenericInfo ? sizeof(IntPtr) * 2 : 0) + // pointers to GenericDefinition and GenericComposition
sizeof(IntPtr) + // dynamic type flags
(fHasNonGcStatics ? sizeof(IntPtr) : 0) + // pointer to data
(fHasGcStatics ? sizeof(IntPtr) : 0) + // pointer to data
(fHasThreadStatics ? sizeof(IntPtr) : 0)); // threadstatic index cell
Expand Down
7 changes: 3 additions & 4 deletions src/coreclr/nativeaot/Runtime/inc/MethodTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ enum EETypeField
ETF_TypeManagerIndirection,
ETF_WritableData,
ETF_Finalizer,
ETF_OptionalFieldsPtr,
ETF_SealedVirtualSlots,
ETF_DynamicTemplateType,
ETF_GenericDefinition,
Expand Down Expand Up @@ -126,9 +125,6 @@ class MethodTable
// simplified version of MethodTable. See LimitedEEType definition below.
EETypeKindMask = 0x00030000,

// This type has optional fields present.
OptionalFieldsFlag = 0x00040000,

// GC depends on this bit, this bit must be zero
CollectibleFlag = 0x00200000,

Expand Down Expand Up @@ -162,6 +158,9 @@ class MethodTable
HasCriticalFinalizerFlag = 0x0002,
IsTrackedReferenceWithFinalizerFlag = 0x0004,

// This MethodTable is for a Byref-like class (TypedReference, Span<T>, ...)
IsByRefLikeFlag = 0x0010,

// This type requires 8-byte alignment for its fields on certain platforms (ARM32, WASM)
RequiresAlign8Flag = 0x1000
};
Expand Down
Loading

0 comments on commit 7cb32e1

Please sign in to comment.