From: Jan Kotas Date: Sat, 2 Nov 2019 04:39:41 +0000 (-0700) Subject: Rewrite a few more array FCalls in C# (dotnet/coreclr#27603) X-Git-Tag: submit/tizen/20210909.063632~11030^2~148 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6a0c9813d3b9d44b2af29714fe0ac036f6b1914e;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Rewrite a few more array FCalls in C# (dotnet/coreclr#27603) Commit migrated from https://github.com/dotnet/coreclr/commit/cbe5ac16fedcccba1d4d90fd7329d92835265b97 --- diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs index c9154fa..1a65a8f 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -178,24 +178,34 @@ namespace System if (array == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - ref byte p = ref GetRawArrayGeometry(array, out uint numComponents, out uint elementSize, out int lowerBound, out bool containsGCPointers); + ref byte p = ref Unsafe.As(array).Data; + int lowerBound = 0; + + MethodTable* pMT = RuntimeHelpers.GetMethodTable(array); + if (pMT->IsMultiDimensionalArray) + { + int rank = pMT->MultiDimensionalArrayRank; + lowerBound = Unsafe.Add(ref Unsafe.As(ref p), rank); + p = ref Unsafe.Add(ref p, 2 * sizeof(int) * rank); // skip the bounds + } int offset = index - lowerBound; - if (index < lowerBound || offset < 0 || length < 0 || (uint)(offset + length) > numComponents) + if (index < lowerBound || offset < 0 || length < 0 || (uint)(offset + length) > (uint)array.Length) ThrowHelper.ThrowIndexOutOfRangeException(); + uint elementSize = pMT->ComponentSize; + ref byte ptr = ref Unsafe.AddByteOffset(ref p, (uint)offset * (nuint)elementSize); nuint byteLength = (uint)length * (nuint)elementSize; - if (containsGCPointers) + if (pMT->ContainsGCPointers) SpanHelpers.ClearWithReferences(ref Unsafe.As(ref ptr), byteLength / (uint)sizeof(IntPtr)); else SpanHelpers.ClearWithoutReferences(ref ptr, byteLength); - } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern ref byte GetRawArrayGeometry(Array array, out uint numComponents, out uint elementSize, out int lowerBound, out bool containsGCPointers); + // GC.KeepAlive(array) not required. pMT kept alive via `ptr` + } // The various Get values... public unsafe object? GetValue(params int[] indices) @@ -342,20 +352,51 @@ namespace System public long LongLength => Unsafe.As(this).Length; - [MethodImpl(MethodImplOptions.InternalCall)] - public extern int GetLength(int dimension); + public unsafe int Rank + { + get + { + int rank = RuntimeHelpers.GetMultiDimensionalArrayRank(this); + return (rank != 0) ? rank : 1; + } + } - public extern int Rank + public unsafe int GetLength(int dimension) { - [MethodImpl(MethodImplOptions.InternalCall)] - get; + int rank = RuntimeHelpers.GetMultiDimensionalArrayRank(this); + if (rank == 0 && dimension == 0) + return Length; + + if ((uint)dimension >= (uint)rank) + throw new IndexOutOfRangeException(SR.IndexOutOfRange_ArrayRankIndex); + + return Unsafe.Add(ref RuntimeHelpers.GetMultiDimensionalArrayBounds(this), dimension); } - [MethodImpl(MethodImplOptions.InternalCall)] - public extern int GetUpperBound(int dimension); + public unsafe int GetUpperBound(int dimension) + { + int rank = RuntimeHelpers.GetMultiDimensionalArrayRank(this); + if (rank == 0 && dimension == 0) + return Length - 1; - [MethodImpl(MethodImplOptions.InternalCall)] - public extern int GetLowerBound(int dimension); + if ((uint)dimension >= (uint)rank) + throw new IndexOutOfRangeException(SR.IndexOutOfRange_ArrayRankIndex); + + ref int bounds = ref RuntimeHelpers.GetMultiDimensionalArrayBounds(this); + return Unsafe.Add(ref bounds, dimension) + Unsafe.Add(ref bounds, rank + dimension) - 1; + } + + public unsafe int GetLowerBound(int dimension) + { + int rank = RuntimeHelpers.GetMultiDimensionalArrayRank(this); + if (rank == 0 && dimension == 0) + return 0; + + if ((uint)dimension >= (uint)rank) + throw new IndexOutOfRangeException(SR.IndexOutOfRange_ArrayRankIndex); + + return Unsafe.Add(ref RuntimeHelpers.GetMultiDimensionalArrayBounds(this), rank + dimension); + } [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool TrySZBinarySearch(Array sourceArray, int sourceIndex, int count, object? value, out int retVal); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 02b2934..0ea8682 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -143,6 +143,9 @@ namespace System.Runtime.CompilerServices [MethodImpl(MethodImplOptions.InternalCall)] public static extern bool TryEnsureSufficientExecutionStack(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object GetUninitializedObjectInternal(Type type); + /// true if given type is reference type or value type that contains references [Intrinsic] public static bool IsReferenceOrContainsReferences() @@ -171,51 +174,55 @@ namespace System.Runtime.CompilerServices internal static ref byte GetRawSzArrayData(this Array array) => ref Unsafe.As(array).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(array).Data - // \-- array - // 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. - // So substrate those from BaseSize before adding to the RawData.Data reference. [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe ref byte GetRawArrayData(this Array array) => - ref Unsafe.AddByteOffset(ref Unsafe.As(array).Data, (nuint)GetObjectMethodTablePointer(array)->BaseSize - (nuint)(2 * sizeof(IntPtr))); + // See comment on RawArrayData for details + ref Unsafe.AddByteOffset(ref Unsafe.As(array).Data, (nuint)GetMethodTable(array)->BaseSize - (nuint)(2 * sizeof(IntPtr))); internal static unsafe ushort GetElementSize(this Array array) { Debug.Assert(ObjectHasComponentSize(array)); - return GetObjectMethodTablePointer(array)->ComponentSize; + return GetMethodTable(array)->ComponentSize; } - // Returns true iff the object has a component size; - // i.e., is variable length like System.String or Array. + // Returns pointer to the multi-dimensional array bounds. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe bool ObjectHasComponentSize(object obj) + internal static unsafe ref int GetMultiDimensionalArrayBounds(Array array) { - // The Flags field of the method table class will have its high bit set if the - // method table has component size info stored somewhere. See member - // MethodTable:IsStringOrArray in src\vm\methodtable.h for full details. - return (int)GetObjectMethodTablePointer(obj)->Flags < 0; + Debug.Assert(GetMultiDimensionalArrayRank(array) > 0); + // See comment on RawArrayData for details + return ref Unsafe.As(ref Unsafe.As(array).Data); } - // Subset of src\vm\methodtable.h - [StructLayout(LayoutKind.Explicit)] - private struct MethodTable + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe int GetMultiDimensionalArrayRank(Array array) { - [FieldOffset(0)] - public ushort ComponentSize; - [FieldOffset(0)] - public uint Flags; - [FieldOffset(4)] - public uint BaseSize; + int rank = GetMethodTable(array)->MultiDimensionalArrayRank; + GC.KeepAlive(array); // Keep MethodTable alive + return rank; + } + + // Returns true iff the object has a component size; + // i.e., is variable length like System.String or Array. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe bool ObjectHasComponentSize(object obj) + { + return GetMethodTable(obj)->HasComponentSize; } // Given an object reference, returns its MethodTable*. + // + // WARNING: The caller has to ensure that MethodTable* does not get unloaded. The most robust way + // to achieve this is by using GC.KeepAlive on the object that the MethodTable* was fetched from, e.g.: + // + // MethodTable* pMT = GetMethodTable(o); + // + // ... work with pMT ... + // + // GC.KeepAlive(o); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe MethodTable* GetObjectMethodTablePointer(object obj) + internal static unsafe MethodTable* GetMethodTable(object obj) { Debug.Assert(obj != null); @@ -232,8 +239,84 @@ namespace System.Runtime.CompilerServices // Ideally this would just be a single dereference: // mov tmp, qword ptr [rax] ; rax = obj ref, tmp = MethodTable* pointer } + } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object GetUninitializedObjectInternal(Type type); + // Helper class to assist with unsafe pinning of arbitrary objects. + // It's used by VM code. + internal 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(array).Data + // \-- array + // 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. + internal class RawArrayData + { + public uint Length; // Array._numComponents padded to IntPtr +#if BIT64 + public uint Padding; +#endif + public byte Data; + } + + // Subset of src\vm\methodtable.h + [StructLayout(LayoutKind.Explicit)] + internal unsafe struct MethodTable + { + [FieldOffset(0)] + public ushort ComponentSize; + [FieldOffset(0)] + private uint Flags; + [FieldOffset(4)] + public uint BaseSize; + + // WFLAGS_HIGH_ENUM + private const uint enum_flag_ContainsPointers = 0x01000000; + private const uint enum_flag_HasComponentSize = 0x80000000; + + public bool HasComponentSize + { + get + { + return (Flags & enum_flag_HasComponentSize) != 0; + } + } + + public bool ContainsGCPointers + { + get + { + return (Flags & enum_flag_ContainsPointers) != 0; + } + } + + public bool IsMultiDimensionalArray + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Debug.Assert(HasComponentSize); + // See comment on RawArrayData for details + return BaseSize > (uint)(3 * sizeof(IntPtr)); + } + } + + // Returns rank of multi-dimensional array rank, 0 for sz arrays + public int MultiDimensionalArrayRank + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Debug.Assert(HasComponentSize); + // See comment on RawArrayData for details + return (int)((BaseSize - (uint)(3 * sizeof(IntPtr))) / (uint)(2 * sizeof(int))); + } + } } } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/jithelpers.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/jithelpers.cs index 5170499..ca6d188 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/jithelpers.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/jithelpers.cs @@ -87,22 +87,6 @@ namespace System.Runtime.CompilerServices } } - // Helper class to assist with unsafe pinning of arbitrary objects. - // It's used by VM code. - internal class RawData - { - public byte Data; - } - - internal class RawArrayData - { - public uint Length; // Array._numComponents padded to IntPtr -#if BIT64 - public uint Padding; -#endif - public byte Data; - } - internal static unsafe class JitHelpers { // The special dll name to be used for DllImport of QCalls diff --git a/src/coreclr/src/classlibnative/bcltype/arraynative.cpp b/src/coreclr/src/classlibnative/bcltype/arraynative.cpp index a02048a..34b7cb6 100644 --- a/src/coreclr/src/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/src/classlibnative/bcltype/arraynative.cpp @@ -18,89 +18,6 @@ #include "arraynative.inl" -FCIMPL1(INT32, ArrayNative::GetRank, ArrayBase* array) -{ - FCALL_CONTRACT; - - VALIDATEOBJECT(array); - - if (array == NULL) - FCThrow(kNullReferenceException); - - return array->GetRank(); -} -FCIMPLEND - - -FCIMPL2(INT32, ArrayNative::GetLowerBound, ArrayBase* array, unsigned int dimension) -{ - FCALL_CONTRACT; - - VALIDATEOBJECT(array); - - if (array == NULL) - FCThrow(kNullReferenceException); - - if (dimension != 0) - { - // Check the dimension is within our rank - unsigned int rank = array->GetRank(); - - if (dimension >= rank) - FCThrowRes(kIndexOutOfRangeException, W("IndexOutOfRange_ArrayRankIndex")); - } - - return array->GetLowerBoundsPtr()[dimension]; -} -FCIMPLEND - - -// Get inclusive upper bound -FCIMPL2(INT32, ArrayNative::GetUpperBound, ArrayBase* array, unsigned int dimension) -{ - FCALL_CONTRACT; - - VALIDATEOBJECT(array); - - if (array == NULL) - FCThrow(kNullReferenceException); - - if (dimension != 0) - { - // Check the dimension is within our rank - unsigned int rank = array->GetRank(); - - if (dimension >= rank) - FCThrowRes(kIndexOutOfRangeException, W("IndexOutOfRange_ArrayRankIndex")); - } - - return array->GetBoundsPtr()[dimension] + array->GetLowerBoundsPtr()[dimension] - 1; -} -FCIMPLEND - - -FCIMPL2(INT32, ArrayNative::GetLength, ArrayBase* array, unsigned int dimension) -{ - FCALL_CONTRACT; - - VALIDATEOBJECT(array); - - if (array==NULL) - FCThrow(kNullReferenceException); - - if (dimension != 0) - { - // Check the dimension is within our rank - unsigned int rank = array->GetRank(); - if (dimension >= rank) - FCThrow(kIndexOutOfRangeException); - } - - return array->GetBoundsPtr()[dimension]; -} -FCIMPLEND - - // array is GC protected by caller void ArrayInitializeWorker(ARRAYBASEREF * arrayRef, MethodTable* pArrayMT, @@ -1011,25 +928,6 @@ FCIMPL6(void, ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, Arra FCIMPLEND -FCIMPL5(void*, ArrayNative::GetRawArrayGeometry, ArrayBase* pArray, UINT32* pNumComponents, UINT32* pElementSize, INT32* pLowerBound, CLR_BOOL* pContainsGCPointers) -{ - VALIDATEOBJECT(pArray); - - _ASSERTE(pArray != NULL); - - MethodTable *pMT = pArray->GetMethodTable(); - - *pNumComponents = pArray->GetNumComponents(); - *pElementSize = pMT->RawGetComponentSize(); - *pLowerBound = pArray->GetLowerBoundsPtr()[0]; - *pContainsGCPointers = !!pMT->ContainsPointers(); - - return (BYTE*)pArray + ArrayBase::GetDataPtrOffset(pMT); -} -FCIMPLEND - - - // Check we're allowed to create an array with the given element type. void ArrayNative::CheckElementType(TypeHandle elementType) { diff --git a/src/coreclr/src/classlibnative/bcltype/arraynative.h b/src/coreclr/src/classlibnative/bcltype/arraynative.h index b3135cb..02197bf 100644 --- a/src/coreclr/src/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/src/classlibnative/bcltype/arraynative.h @@ -25,16 +25,10 @@ struct FCALLRuntimeFieldHandle class ArrayNative { public: - static FCDECL1(INT32, GetRank, ArrayBase* pArray); - static FCDECL2(INT32, GetLowerBound, ArrayBase* pArray, unsigned int dimension); - static FCDECL2(INT32, GetUpperBound, ArrayBase* pArray, unsigned int dimension); - static FCDECL2(INT32, GetLength, ArrayBase* pArray, unsigned int dimension); static FCDECL1(void, Initialize, ArrayBase* pArray); static FCDECL6(void, ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable); - static FCDECL5(void*, GetRawArrayGeometry, ArrayBase* pArray, UINT32* pNumComponents, UINT32* pElementSize, INT32* pLowerBound, CLR_BOOL* pContainsGCPointers); - // This method will create a new array of type type, with zero lower // bounds and rank. static FCDECL4(Object*, CreateInstance, void* elementTypeHandle, INT32 rank, INT32* pLengths, INT32* pBounds); diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 72201fa..56de255 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -715,13 +715,8 @@ FCFuncStart(gClrConfig) FCFuncEnd() FCFuncStart(gArrayFuncs) - FCFuncElement("get_Rank", ArrayNative::GetRank) - FCFuncElement("GetLowerBound", ArrayNative::GetLowerBound) - FCFuncElement("GetUpperBound", ArrayNative::GetUpperBound) - FCIntrinsicSig("GetLength", &gsig_IM_Int_RetInt, ArrayNative::GetLength, CORINFO_INTRINSIC_Array_GetDimLength) FCFuncElement("Initialize", ArrayNative::Initialize) FCFuncElement("Copy", ArrayNative::ArrayCopy) - FCFuncElement("GetRawArrayGeometry", ArrayNative::GetRawArrayGeometry) FCFuncElement("InternalCreate", ArrayNative::CreateInstance) FCFuncElement("InternalGetReference", ArrayNative::GetReference) FCFuncElement("InternalSetValue", ArrayNative::SetValue) @@ -1141,8 +1136,6 @@ FCFuncStart(gPalKernel32Funcs) QCFuncElement("OpenMutex", OpenMutexW) QCFuncElement("OpenSemaphore", OpenSemaphoreW) QCFuncElement("OutputDebugString", OutputDebugStringW) - QCFuncElement("QueryPerformanceCounter", QueryPerformanceCounter) - QCFuncElement("QueryPerformanceFrequency", QueryPerformanceFrequency) QCFuncElement("ReleaseMutex", ReleaseMutex) QCFuncElement("ReleaseSemaphore", ReleaseSemaphore) QCFuncElement("ResetEvent", ResetEvent)