Skip to content

Cleanup LowLevelList usage in GCLayout #116316

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 8, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -399,27 +399,20 @@ private static void CreateInstanceGCDesc(TypeBuilderState state, MethodTable* pT
pEEType->ContainsGCPointers = false;
}
}
else if (gcBitfield != null)
else
{
if (cbGCDesc != 0)
Debug.Assert(gcBitfield == null);

if (pTemplateEEType != null)
{
pEEType->ContainsGCPointers = true;
CreateGCDesc(gcBitfield, baseSize, isValueType, false, ((void**)pEEType) - 1);
Buffer.MemoryCopy((byte*)pTemplateEEType - cbGCDesc, (byte*)pEEType - cbGCDesc, cbGCDesc, cbGCDesc);
pEEType->ContainsGCPointers = pTemplateEEType->ContainsGCPointers;
}
else
{
pEEType->ContainsGCPointers = false;
}
}
else if (pTemplateEEType != null)
{
Buffer.MemoryCopy((byte*)pTemplateEEType - cbGCDesc, (byte*)pEEType - cbGCDesc, cbGCDesc, cbGCDesc);
pEEType->ContainsGCPointers = pTemplateEEType->ContainsGCPointers;
}
else
{
pEEType->ContainsGCPointers = false;
}
}

private static unsafe int GetInstanceGCDescSize(TypeBuilderState state, MethodTable* pTemplateEEType, bool isValueType, bool isArray)
@@ -442,24 +435,24 @@ private static unsafe int GetInstanceGCDescSize(TypeBuilderState state, MethodTa
return series > 0 ? (series + 2) * IntPtr.Size : 0;
}
}
else if (gcBitfield != null)
{
int series = CreateGCDesc(gcBitfield, 0, isValueType, false, null);
return series > 0 ? (series * 2 + 1) * IntPtr.Size : 0;
}
else if (pTemplateEEType != null)
{
return RuntimeAugments.GetGCDescSize(pTemplateEEType->ToRuntimeTypeHandle());
}
else
{
return 0;
Debug.Assert(gcBitfield == null);

if (pTemplateEEType != null)
{
return RuntimeAugments.GetGCDescSize(pTemplateEEType->ToRuntimeTypeHandle());
}
else
{
return 0;
}
}
}

private static bool IsAllGCPointers(LowLevelList<bool> bitfield)
private static bool IsAllGCPointers(bool[] bitfield)
{
int count = bitfield.Count;
int count = bitfield.Length;
Debug.Assert(count > 0);

for (int i = 0; i < count; i++)
@@ -471,7 +464,7 @@ private static bool IsAllGCPointers(LowLevelList<bool> bitfield)
return true;
}

private static unsafe int CreateArrayGCDesc(LowLevelList<bool> bitfield, int rank, bool isSzArray, void* gcdesc)
private static unsafe int CreateArrayGCDesc(bool[] bitfield, int rank, bool isSzArray, void* gcdesc)
{
if (bitfield == null)
return 0;
@@ -495,7 +488,7 @@ private static unsafe int CreateArrayGCDesc(LowLevelList<bool> bitfield, int ran
int first = -1;
int last = 0;
short numPtrs = 0;
while (i < bitfield.Count)
while (i < bitfield.Length)
{
if (bitfield[i])
{
@@ -513,7 +506,7 @@ private static unsafe int CreateArrayGCDesc(LowLevelList<bool> bitfield, int ran
numSeries++;
numPtrs = 0;

while ((i < bitfield.Count) && (bitfield[i]))
while ((i < bitfield.Length) && (bitfield[i]))
{
numPtrs++;
i++;
@@ -531,7 +524,7 @@ private static unsafe int CreateArrayGCDesc(LowLevelList<bool> bitfield, int ran
{
if (numSeries > 0)
{
*ptr-- = (short)((first + bitfield.Count - last) * IntPtr.Size);
*ptr-- = (short)((first + bitfield.Length - last) * IntPtr.Size);
*ptr-- = numPtrs;

*(void**)gcdesc = (void*)-numSeries;
@@ -542,69 +535,6 @@ private static unsafe int CreateArrayGCDesc(LowLevelList<bool> bitfield, int ran
return numSeries;
}

private static unsafe int CreateGCDesc(LowLevelList<bool> bitfield, int size, bool isValueType, bool isStatic, void* gcdesc)
{
int offs = 0;
// if this type is a class we have to account for the gcdesc.
if (isValueType)
offs = IntPtr.Size;

if (bitfield == null)
return 0;

void** ptr = (void**)gcdesc - 1;

int* staticPtr = isStatic ? ((int*)gcdesc + 1) : null;

int numSeries = 0;
int i = 0;
while (i < bitfield.Count)
{
if (bitfield[i])
{
numSeries++;
int seriesOffset = i * IntPtr.Size + offs;
int seriesSize = 0;

while ((i < bitfield.Count) && (bitfield[i]))
{
seriesSize += IntPtr.Size;
i++;
}


if (gcdesc != null)
{
if (staticPtr != null)
{
*staticPtr++ = seriesSize;
*staticPtr++ = seriesOffset;
}
else
{
seriesSize -= size;
*ptr-- = (void*)seriesOffset;
*ptr-- = (void*)seriesSize;
}
}
}
else
{
i++;
}
}

if (gcdesc != null)
{
if (staticPtr != null)
*(int*)gcdesc = numSeries;
else
*(void**)gcdesc = (void*)numSeries;
}

return numSeries;
}

public static RuntimeTypeHandle CreateFunctionPointerEEType(uint hashCodeOfNewType, RuntimeTypeHandle returnTypeHandle, RuntimeTypeHandle[] parameterHandles, FunctionPointerType functionPointerType)
{
TypeBuilderState state = new TypeBuilderState(functionPointerType);
Original file line number Diff line number Diff line change
@@ -13,18 +13,6 @@

namespace Internal.Runtime.TypeLoader
{
internal static class LowLevelListExtensions
{
public static void Expand<T>(this LowLevelList<T> list, int count)
{
if (list.Capacity < count)
list.Capacity = count;

while (list.Count < count)
list.Add(default(T));
}
}

internal class TypeBuilder
{
public TypeBuilder()
@@ -447,24 +435,22 @@ internal void ParseNativeLayoutInfo(TypeBuilderState state, TypeDesc type)
/// </summary>
internal unsafe struct GCLayout
{
private LowLevelList<bool> _bitfield;
private bool[] _bitfield;
private unsafe void* _gcdesc;
private int _size;
private bool _isReferenceTypeGCLayout;

public static GCLayout None { get { return default(GCLayout); } }
public static GCLayout SingleReference { get; } = new GCLayout(new LowLevelList<bool>(new bool[1] { true }), false);
public static GCLayout SingleReference { get; } = new GCLayout([true]);

public bool IsNone { get { return _bitfield == null && _gcdesc == null; } }

public GCLayout(LowLevelList<bool> bitfield, bool isReferenceTypeGCLayout)
public GCLayout(bool[] bitfield)
{
Debug.Assert(bitfield != null);

_bitfield = bitfield;
_gcdesc = null;
_size = 0;
_isReferenceTypeGCLayout = isReferenceTypeGCLayout;
}

public GCLayout(RuntimeTypeHandle rtth)
@@ -473,37 +459,27 @@ public GCLayout(RuntimeTypeHandle rtth)
Debug.Assert(MethodTable != null);

_bitfield = null;
_isReferenceTypeGCLayout = false; // This field is only used for the LowLevelList<bool> path
_gcdesc = MethodTable->ContainsGCPointers ? (void**)MethodTable - 1 : null;
_size = (int)MethodTable->BaseSize;
}

/// <summary>
/// Writes this layout to the given bitfield.
/// Gets this layout in bitfield array.
/// </summary>
/// <param name="bitfield">The bitfield to write a layout to (may be null, at which
/// point it will be created and assigned).</param>
/// <param name="offset">The offset at which we need to write the bitfield.</param>
public void WriteToBitfield(LowLevelList<bool> bitfield, int offset)
/// <returns>The layout in bitfield.</returns>
public bool[] AsBitfield()
{
ArgumentNullException.ThrowIfNull(bitfield);

if (IsNone)
return;
// This method should only be called when not none.
Debug.Assert(!IsNone);

// Ensure exactly one of these two are set.
Debug.Assert(_gcdesc != null ^ _bitfield != null);

if (_bitfield != null)
MergeBitfields(bitfield, offset);
else
WriteGCDescToBitfield(bitfield, offset);
return _bitfield ?? WriteGCDescToBitfield();
}

private unsafe void WriteGCDescToBitfield(LowLevelList<bool> bitfield, int offset)
private unsafe bool[] WriteGCDescToBitfield()
{
int startIndex = offset / IntPtr.Size;

void** ptr = (void**)_gcdesc;
Debug.Assert(_gcdesc != null);

@@ -512,8 +488,8 @@ private unsafe void WriteGCDescToBitfield(LowLevelList<bool> bitfield, int offse
Debug.Assert(count >= 0);

// Ensure capacity for the values we are about to write
int capacity = startIndex + _size / IntPtr.Size - 2;
bitfield.Expand(capacity);
int capacity = _size / IntPtr.Size - 2;
bool[] bitfield = new bool[capacity];

while (count-- >= 0)
{
@@ -524,35 +500,10 @@ private unsafe void WriteGCDescToBitfield(LowLevelList<bool> bitfield, int offse
Debug.Assert(offs >= 0);

for (int i = 0; i < len; i++)
bitfield[startIndex + offs + i] = true;
bitfield[offs + i] = true;
}
}

private void MergeBitfields(LowLevelList<bool> outputBitfield, int offset)
{
int startIndex = offset / IntPtr.Size;

// These routines represent the GC layout after the MethodTable pointer
// in an object, but the LowLevelList<bool> bitfield logically contains
// the EETypepointer if it is describing a reference type. So, skip the
// first value.
int itemsToSkip = _isReferenceTypeGCLayout ? 1 : 0;

// Assert that we only skip a non-reported pointer.
Debug.Assert(itemsToSkip == 0 || _bitfield[0] == false);

// Ensure capacity for the values we are about to write
int capacity = startIndex + _bitfield.Count - itemsToSkip;
outputBitfield.Expand(capacity);


for (int i = itemsToSkip; i < _bitfield.Count; i++)
{
// We should never overwrite a TRUE value in the table.
Debug.Assert(!outputBitfield[startIndex + i - itemsToSkip] || _bitfield[i]);

outputBitfield[startIndex + i - itemsToSkip] = _bitfield[i];
}
return bitfield;
}
}

Original file line number Diff line number Diff line change
@@ -306,9 +306,9 @@ public ushort NumVTableSlots

// Sentinel static to allow us to initialize _instanceLayout to something
// and then detect that InstanceGCLayout should return null
private static LowLevelList<bool> s_emptyLayout = new LowLevelList<bool>();
private static readonly bool[] s_emptyLayout = [];

private LowLevelList<bool> _instanceGCLayout;
private bool[] _instanceGCLayout;

/// <summary>
/// The instance gc layout of a dynamically laid out type.
@@ -324,14 +324,12 @@ public ushort NumVTableSlots
/// If the type is a valuetype array, this is the layout of the valuetype held in the array if the type has GC reference fields
/// Otherwise, it is the layout of the fields in the type.
/// </summary>
public LowLevelList<bool> InstanceGCLayout
public bool[] InstanceGCLayout
{
get
{
if (_instanceGCLayout == null)
{
LowLevelList<bool> instanceGCLayout;

if (TypeBeingBuilt is ArrayType)
{
if (!IsArrayOfReferenceTypes)
@@ -340,9 +338,7 @@ public LowLevelList<bool> InstanceGCLayout
TypeBuilder.GCLayout elementGcLayout = GetFieldGCLayout(arrayType.ElementType);
if (!elementGcLayout.IsNone)
{
instanceGCLayout = new LowLevelList<bool>();
elementGcLayout.WriteToBitfield(instanceGCLayout, 0);
_instanceGCLayout = instanceGCLayout;
_instanceGCLayout = elementGcLayout.AsBitfield();
}
}
else
Loading
Oops, something went wrong.