Skip to content
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

Remove RawArrayData #110902

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
Expand Up @@ -378,7 +378,7 @@ public static unsafe void Clear(Array array, int index, int length)
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);

ref byte p = ref Unsafe.As<RawArrayData>(array).Data;
ref byte p = ref array.RawData;
int lowerBound = 0;

MethodTable* pMT = RuntimeHelpers.GetMethodTable(array);
Expand Down Expand Up @@ -598,10 +598,10 @@ private unsafe void InternalSetValue(object? value, nint flattenedIndex)
GC.KeepAlive(this); // Keep the method table alive
}

public int Length => checked((int)Unsafe.As<RawArrayData>(this).Length);
public int Length => checked((int)RawLength);

// This could return a length greater than int.MaxValue
internal nuint NativeLength => Unsafe.As<RawArrayData>(this).Length;
internal nuint NativeLength => RawLength;

public long LongLength => (long)NativeLength;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,10 +394,10 @@ internal static unsafe nuint GetRawObjectDataSize(object obj)
{
MethodTable* pMT = GetMethodTable(obj);

// See comment on RawArrayData for details
// See comment on Array for details
nuint rawSize = pMT->BaseSize - (nuint)(2 * sizeof(IntPtr));
if (pMT->HasComponentSize)
rawSize += (uint)Unsafe.As<RawArrayData>(obj).Length * (nuint)pMT->ComponentSize;
rawSize += Unsafe.As<Array>(obj).RawLength * (nuint)pMT->ComponentSize;

GC.KeepAlive(obj); // Keep MethodTable alive

Expand All @@ -417,8 +417,8 @@ internal static unsafe ushort GetElementSize(this Array array)
internal static unsafe ref int GetMultiDimensionalArrayBounds(Array array)
{
Debug.Assert(GetMultiDimensionalArrayRank(array) > 0);
// See comment on RawArrayData for details
return ref Unsafe.As<byte, int>(ref Unsafe.As<RawArrayData>(array).Data);
// See comment on Array for details
return ref Unsafe.As<byte, int>(ref array.RawData);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -595,24 +595,6 @@ internal sealed class RawData
public byte Data;
}

// CLR arrays are laid out in memory as follows (multidimensional array bounds are optional):
// [ sync block || pMethodTable || num components || MD array bounds || array data .. ]
// ^ ^ ^ ^ returned reference
// | | \-- ref Unsafe.As<RawArrayData>(array).Data
// \-- array \-- ref Unsafe.As<RawData>(array).Data
// The BaseSize of an array includes all the fields before the array data,
// including the sync block and method table. The reference to RawData.Data
// points at the number of components, skipping over these two pointer-sized fields.
[NonVersionable] // This only applies to field layout
internal sealed class RawArrayData
{
public uint Length; // Array._numComponents padded to IntPtr
#if TARGET_64BIT
public uint Padding;
#endif
public byte Data;
}

// Subset of src\vm\methoddesc.hpp
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct MethodDesc
Expand Down Expand Up @@ -825,7 +807,7 @@ public bool IsMultiDimensionalArray
get
{
Debug.Assert(HasComponentSize);
// See comment on RawArrayData for details
// See comment on Array for details
return BaseSize > (uint)(3 * sizeof(IntPtr));
}
}
Expand All @@ -837,7 +819,7 @@ public int MultiDimensionalArrayRank
get
{
Debug.Assert(HasComponentSize);
// See comment on RawArrayData for details
// See comment on Array for details
return (int)((BaseSize - (uint)(3 * sizeof(IntPtr))) / (uint)(2 * sizeof(int)));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public static ref byte GetArrayDataReference(Array array)
// If needed, we can save one or two instructions per call by marking this method as intrinsic and asking the JIT
// to special-case arrays of known type and dimension.

// See comment on RawArrayData (in RuntimeHelpers.CoreCLR.cs) for details
return ref Unsafe.AddByteOffset(ref Unsafe.As<RawData>(array).Data, (nuint)RuntimeHelpers.GetMethodTable(array)->BaseSize - (nuint)(2 * sizeof(IntPtr)));
// See comment on Array for details
return ref Unsafe.AddByteOffset(ref array.RawData, RuntimeHelpers.GetMethodTable(array)->BaseSize - (3 * (nuint)sizeof(IntPtr)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ private static unsafe void InitializeGlobalTablesForModule(TypeManagerHandle typ

// Call write barrier directly. Assigning object reference does a type check.
Debug.Assert((uint)moduleIndex < (uint)gcStaticBaseSpines.Length);
ref object rawSpineIndexData = ref Unsafe.As<byte, object>(ref Unsafe.As<RawArrayData>(gcStaticBaseSpines).Data);
ref object rawSpineIndexData = ref MemoryMarshal.GetArrayDataReference(gcStaticBaseSpines);
Unsafe.Add(ref rawSpineIndexData, moduleIndex) = spine;
}

Expand Down Expand Up @@ -191,7 +191,7 @@ private static unsafe object[] InitializeStatics(IntPtr gcStaticRegionStart, int

object[] spine = new object[length / (MethodTable.SupportsRelativePointers ? sizeof(int) : sizeof(nint))];

ref object rawSpineData = ref Unsafe.As<byte, object>(ref Unsafe.As<RawArrayData>(spine).Data);
ref object rawSpineData = ref MemoryMarshal.GetArrayDataReference(spine);

int currentBase = 0;
for (byte* block = (byte*)gcStaticRegionStart;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ internal bool IsMultiDimensionalArray
get
{
Debug.Assert(HasComponentSize);
// See comment on RawArrayData for details
// See comment on Array for details
return BaseSize > (uint)(3 * sizeof(IntPtr));
}
}
Expand Down
22 changes: 6 additions & 16 deletions src/coreclr/nativeaot/Runtime.Base/src/System/Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,18 @@ namespace System

public partial class Array
{
// CS0169: The field 'Array._numComponents' is never used
#pragma warning disable 0169
// This field should be the first field in Array as the runtime/compilers depend on it
private int _numComponents;
#pragma warning restore
internal uint RawLength; // Array._numComponents padded to IntPtr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should follow dotnet/runtime naming conventions for fields.

#if TARGET_64BIT
internal uint RawPadding;
#endif
internal byte RawData;

public int Length => (int)Unsafe.As<RawArrayData>(this).Length;
public int Length => (int)RawLength;
}

// To accommodate class libraries that wish to implement generic interfaces on arrays, all class libraries
// are now required to provide an Array<T> class that derives from Array.
internal class Array<T> : Array
{
}

[StructLayout(LayoutKind.Sequential)]
internal class RawArrayData
{
public uint Length; // Array._numComponents padded to IntPtr
#if TARGET_64BIT
public uint Padding;
#endif
public byte Data;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -761,9 +761,6 @@ private static unsafe void ThrowArrayMismatchException(object?[] array)
// This will throw NullReferenceException if obj is null.
if ((nuint)index >= (uint)array.Length)
ThrowIndexOutOfRangeException(array);

Debug.Assert(index >= 0);
ref object? element = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index);
#else
if (array is null)
{
Expand All @@ -773,9 +770,11 @@ private static unsafe void ThrowArrayMismatchException(object?[] array)
{
throw elementType->GetClasslibException(ExceptionIDs.IndexOutOfRange);
}
ref object rawData = ref Unsafe.As<byte, object>(ref Unsafe.As<RawArrayData>(array).Data);
ref object element = ref Unsafe.Add(ref rawData, index);
#endif

Debug.Assert(index >= 0);
ref object? element = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index);

MethodTable* arrayElemType = array.GetMethodTable()->RelatedParameterType;

if (elementType != arrayElemType)
Expand All @@ -793,9 +792,6 @@ public static unsafe void StelemRef(object?[] array, nint index, object? obj)
// This will throw NullReferenceException if obj is null.
if ((nuint)index >= (uint)array.Length)
ThrowIndexOutOfRangeException(array);

Debug.Assert(index >= 0);
ref object? element = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index);
#else
if (array is null)
{
Expand All @@ -807,10 +803,11 @@ public static unsafe void StelemRef(object?[] array, nint index, object? obj)
{
throw array.GetMethodTable()->GetClasslibException(ExceptionIDs.IndexOutOfRange);
}
ref object rawData = ref Unsafe.As<byte, object>(ref Unsafe.As<RawArrayData>(array).Data);
ref object element = ref Unsafe.Add(ref rawData, index);
#endif

Debug.Assert(index >= 0);
ref object? element = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index);

MethodTable* elementType = array.GetMethodTable()->RelatedParameterType;

if (obj == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,10 @@ namespace System
// IList<U> and IReadOnlyList<U>, where T : U dynamically. See the SZArrayHelper class for details.
public abstract partial class Array : ICollection, IEnumerable, IList, IStructuralComparable, IStructuralEquatable, ICloneable
{
// CS0169: The field 'Array._numComponents' is never used
// CA1823: Unused field '_numComponents'
#pragma warning disable 0169
#pragma warning disable CA1823
// This field should be the first field in Array as the runtime/compilers depend on it
[NonSerialized]
private int _numComponents;
#pragma warning restore

public int Length => checked((int)Unsafe.As<RawArrayData>(this).Length);
public int Length => checked((int)RawLength);

// This could return a length greater than int.MaxValue
internal nuint NativeLength => Unsafe.As<RawArrayData>(this).Length;
internal nuint NativeLength => RawLength;

public long LongLength => (long)NativeLength;

Expand Down Expand Up @@ -159,7 +150,7 @@ public unsafe void Initialize()
private ref int GetRawMultiDimArrayBounds()
{
Debug.Assert(!IsSzArray);
return ref Unsafe.As<byte, int>(ref Unsafe.As<RawArrayData>(this).Data);
return ref Unsafe.As<byte, int>(ref RawData);
}

// Provides a strong exception guarantee - either it succeeds, or
Expand Down Expand Up @@ -579,7 +570,7 @@ public static unsafe void Clear(Array array, int index, int length)
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);

ref byte p = ref Unsafe.As<RawArrayData>(array).Data;
ref byte p = ref array.RawData;
int lowerBound = 0;

MethodTable* mt = array.GetMethodTable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,10 @@ internal static unsafe nuint GetRawObjectDataSize(this object obj)
{
MethodTable* pMT = GetMethodTable(obj);

// See comment on RawArrayData for details
// See comment on Array for details
nuint rawSize = pMT->BaseSize - (nuint)(2 * sizeof(IntPtr));
if (pMT->HasComponentSize)
rawSize += (uint)Unsafe.As<RawArrayData>(obj).Length * (nuint)pMT->ComponentSize;
rawSize += Unsafe.As<Array>(obj).RawLength * (nuint)pMT->ComponentSize;

GC.KeepAlive(obj); // Keep MethodTable alive

Expand Down Expand Up @@ -414,24 +414,6 @@ public static unsafe int SizeOf(RuntimeTypeHandle type)
}
}

// CLR arrays are laid out in memory as follows (multidimensional array bounds are optional):
// [ sync block || pMethodTable || num components || MD array bounds || array data .. ]
// ^ ^ ^ ^ returned reference
// | | \-- ref Unsafe.As<RawArrayData>(array).Data
// \-- array \-- ref Unsafe.As<RawData>(array).Data
// The BaseSize of an array includes all the fields before the array data,
// including the sync block and method table. The reference to RawData.Data
// points at the number of components, skipping over these two pointer-sized fields.
[StructLayout(LayoutKind.Sequential)]
internal class RawArrayData
{
public uint Length; // Array._numComponents padded to IntPtr
#if TARGET_64BIT
public uint Padding;
#endif
public byte Data;
}

[StructLayout(LayoutKind.Sequential)]
internal class RawData
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public static ref byte GetArrayDataReference(Array array)
// If needed, we can save one or two instructions per call by marking this method as intrinsic and asking the JIT
// to special-case arrays of known type and dimension.

// See comment on RawArrayData (in RuntimeHelpers.CoreCLR.cs) for details
return ref Unsafe.AddByteOffset(ref Unsafe.As<RawData>(array).Data, (nuint)array.GetMethodTable()->BaseSize - (nuint)(2 * sizeof(IntPtr)));
// See comment on Array for details
return ref Unsafe.AddByteOffset(ref array.RawData, array.GetMethodTable()->BaseSize - (3 * (nuint)sizeof(IntPtr)));
}
}
}
4 changes: 4 additions & 0 deletions src/coreclr/vm/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy
pClass->SetRank (Rank);
pClass->SetMethodTable (pMT);

// this only reflects the fields declared in IL, not actual layout
// only used for debug asserts
INDEBUG(pClass->SetNumInstanceFields(ARRAY_ILFIELDS));

// Fill In the method table
pClass->SetNumMethods(static_cast<WORD>(numVirtuals + numNonVirtualSlots));

Expand Down
12 changes: 5 additions & 7 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ DEFINE_METHOD(ARG_ITERATOR, CTOR2, .ctor,
DEFINE_CLASS(ARGUMENT_HANDLE, System, RuntimeArgumentHandle)

DEFINE_CLASS(ARRAY, System, Array)
DEFINE_FIELD(ARRAY, LENGTH, RawLength)
#ifdef TARGET_64BIT
DEFINE_FIELD(ARRAY, PADDING, RawPadding)
#endif
DEFINE_FIELD(ARRAY, DATA, RawData)

DEFINE_CLASS(ARRAY_WITH_OFFSET, Interop, ArrayWithOffset)
DEFINE_FIELD(ARRAY_WITH_OFFSET, M_ARRAY, m_array)
Expand Down Expand Up @@ -722,13 +727,6 @@ DEFINE_METHOD(INTERLOCKED, COMPARE_EXCHANGE_LONG, CompareExchange, SM_
DEFINE_CLASS(RAW_DATA, CompilerServices, RawData)
DEFINE_FIELD(RAW_DATA, DATA, Data)

DEFINE_CLASS(RAW_ARRAY_DATA, CompilerServices, RawArrayData)
DEFINE_FIELD(RAW_ARRAY_DATA, LENGTH, Length)
#ifdef TARGET_64BIT
DEFINE_FIELD(RAW_ARRAY_DATA, PADDING, Padding)
#endif
DEFINE_FIELD(RAW_ARRAY_DATA, DATA, Data)

DEFINE_CLASS(PORTABLE_TAIL_CALL_FRAME, CompilerServices, PortableTailCallFrame)
DEFINE_FIELD(PORTABLE_TAIL_CALL_FRAME, TAILCALL_AWARE_RETURN_ADDRESS, TailCallAwareReturnAddress)
DEFINE_FIELD(PORTABLE_TAIL_CALL_FRAME, NEXT_CALL, NextCall)
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/vm/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ struct RCW;
#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */)
#endif

#ifdef TARGET_64BIT
#define ARRAY_ILFIELDS 3
#else
#define ARRAY_ILFIELDS 2
#endif

#define ARRAYBASE_BASESIZE (OBJHEADER_SIZE + ARRAYBASE_SIZE)

//
Expand Down
27 changes: 23 additions & 4 deletions src/libraries/System.Private.CoreLib/src/System/Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@ public abstract partial class Array : ICloneable, IList, IStructuralComparable,
// Large value types may benefit from a smaller number.
internal const int IntrosortSizeThreshold = 16;

// CLR arrays are laid out in memory as follows (multidimensional array bounds are optional):
// [ sync block || pMethodTable || num components || MD array bounds || array data .. ]
// ^ ^ ^ ^ returned reference
// | | \-- ref array.RawData
// \-- array \-- ref Unsafe.As<RawData>(array).Data
// The BaseSize of an array includes all the fields before the array data,
// including the sync block and method table. The reference to RawData.Data
// points at the number of components, skipping over these two pointer-sized fields.

// Mono arrays instead contain the bounds in the first field for all cases
#if MONO
internal IntPtr RawBounds;
#endif
internal uint RawLength; // Array._numComponents padded to IntPtr
#if TARGET_64BIT
internal uint RawPadding;
#endif
internal byte RawData;

// This ctor exists solely to prevent C# from generating a protected .ctor that violates the surface area.
private protected Array() { }

Expand Down Expand Up @@ -369,8 +388,8 @@ public static unsafe void Copy(Array sourceArray, Array destinationArray, int le
(uint)length <= destinationArray.NativeLength)
{
nuint byteCount = (uint)length * (nuint)pMT->ComponentSize;
ref byte src = ref Unsafe.As<RawArrayData>(sourceArray).Data;
ref byte dst = ref Unsafe.As<RawArrayData>(destinationArray).Data;
ref byte src = ref sourceArray.RawData;
ref byte dst = ref destinationArray.RawData;

if (pMT->ContainsGCPointers)
Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
Expand Down Expand Up @@ -400,8 +419,8 @@ public static unsafe void Copy(Array sourceArray, int sourceIndex, Array destina
{
nuint elementSize = (nuint)pMT->ComponentSize;
nuint byteCount = (uint)length * elementSize;
ref byte src = ref Unsafe.AddByteOffset(ref Unsafe.As<RawArrayData>(sourceArray).Data, (uint)sourceIndex * elementSize);
ref byte dst = ref Unsafe.AddByteOffset(ref Unsafe.As<RawArrayData>(destinationArray).Data, (uint)destinationIndex * elementSize);
ref byte src = ref Unsafe.AddByteOffset(ref sourceArray.RawData, (uint)sourceIndex * elementSize);
ref byte dst = ref Unsafe.AddByteOffset(ref destinationArray.RawData, (uint)destinationIndex * elementSize);

if (pMT->ContainsGCPointers)
Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
Expand Down
Loading
Loading