From 6ee9d7bdad91a1e038109d54ffbe406f5c86e466 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 19 Jan 2024 02:48:25 +0800 Subject: [PATCH 01/25] Body of CopySlow and setup QCall for CanAssignArrayType --- .../src/System/Array.CoreCLR.cs | 47 +++++++++- .../RuntimeHelpers.CoreCLR.cs | 2 +- .../classlibnative/bcltype/arraynative.cpp | 93 ++++--------------- .../classlibnative/bcltype/arraynative.h | 7 +- src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 1 + .../src/System/ThrowHelper.cs | 6 ++ 7 files changed, 74 insertions(+), 83 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index d18caf9a39410..4da45dfe0d4e1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -95,8 +95,51 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de // instance & might fail when called from within a CER, or if the // reliable flag is true, it will either always succeed or always // throw an exception with no side effects. - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length); + private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + { + Debug.Assert(sourceArray.Rank == destinationArray.Rank); + + TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); + TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); + AssignArrayEnum r = CanAssignArrayType(srcTH.m_asTAddr, destTH.m_asTAddr); + + if (r == AssignArrayEnum.AssignWrongType) + ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); + + if (length > 0) + { + switch (r) + { + case AssignArrayEnum.AssignUnboxValueClass: + throw null; + + case AssignArrayEnum.AssignBoxValueClassOrPrimitive: + throw null; + + case AssignArrayEnum.AssignMustCast: + throw null; + + case AssignArrayEnum.AssignPrimitiveWiden: + throw null; + + default: + Debug.Fail("Fell through switch in Array.Copy!"); + break; + } + } + } + + private enum AssignArrayEnum + { + AssignWrongType, + AssignMustCast, + AssignBoxValueClassOrPrimitive, + AssignUnboxValueClass, + AssignPrimitiveWiden, + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] + private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); // Provides a strong exception guarantee - either it succeeds, or // it throws an exception with no side effects. The arrays must be diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 112729d383ac1..408bc0750f62c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -622,7 +622,7 @@ internal unsafe struct TypeHandle /// /// The address of the current type handle object. /// - private readonly void* m_asTAddr; + internal readonly void* m_asTAddr; [MethodImpl(MethodImplOptions.AggressiveInlining)] public TypeHandle(void* tAddr) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 0c1afdcbfd8b7..b8f1fd9a4b705 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -115,26 +115,18 @@ FCIMPLEND // Returns an enum saying whether you can copy an array of srcType into destType. -ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF pSrc, const BASEARRAYREF pDest) +ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; - PRECONDITION(pSrc != NULL); - PRECONDITION(pDest != NULL); + PRECONDITION(srcTH != NULL); + PRECONDITION(destTH != NULL); } CONTRACTL_END; - // This first bit is a minor optimization: e.g. when copying byte[] to byte[] - // we do not need to call GetArrayElementTypeHandle(). - MethodTable *pSrcMT = pSrc->GetMethodTable(); - MethodTable *pDestMT = pDest->GetMethodTable(); - _ASSERTE(pSrcMT != pDestMT); // Handled by fast path - - TypeHandle srcTH = pSrcMT->GetArrayElementTypeHandle(); - TypeHandle destTH = pDestMT->GetArrayElementTypeHandle(); _ASSERTE(srcTH != destTH); // Handled by fast path // Value class boxing @@ -190,6 +182,20 @@ ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF return AssignWrongType; } +extern "C" int QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH) +{ + QCALL_CONTRACT; + + INT32 ret = 0; + + BEGIN_QCALL; + + ret = ArrayNative::CanAssignArrayType(TypeHandle::FromPtr(srcTH), TypeHandle::FromPtr(destTH)); + + END_QCALL; + + return ret; +} // Casts and assigns each element of src array to the dest array type. void ArrayNative::CastCheckEachElement(const BASEARRAYREF pSrcUnsafe, const unsigned int srcIndex, BASEARRAYREF pDestUnsafe, unsigned int destIndex, const unsigned int len) @@ -653,71 +659,6 @@ void memmoveGCRefs(void *dest, const void *src, size_t len) } } -FCIMPL5(void, ArrayNative::CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength) -{ - FCALL_CONTRACT; - - struct _gc - { - BASEARRAYREF pSrc; - BASEARRAYREF pDst; - } gc; - - gc.pSrc = (BASEARRAYREF)pSrc; - gc.pDst = (BASEARRAYREF)pDst; - - // cannot pass null for source or destination - _ASSERTE(gc.pSrc != NULL && gc.pDst != NULL); - - // source and destination must be arrays - _ASSERTE(gc.pSrc->GetMethodTable()->IsArray()); - _ASSERTE(gc.pDst->GetMethodTable()->IsArray()); - - _ASSERTE(gc.pSrc->GetRank() == gc.pDst->GetRank()); - - // array bounds checking - _ASSERTE(iLength >= 0); - _ASSERTE(iSrcIndex >= 0); - _ASSERTE(iDstIndex >= 0); - _ASSERTE((DWORD)(iSrcIndex + iLength) <= gc.pSrc->GetNumComponents()); - _ASSERTE((DWORD)(iDstIndex + iLength) <= gc.pDst->GetNumComponents()); - - HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); - - int r = CanAssignArrayType(gc.pSrc, gc.pDst); - - if (r == AssignWrongType) - COMPlusThrow(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType")); - - if (iLength > 0) - { - switch (r) - { - case AssignUnboxValueClass: - UnBoxEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength); - break; - - case AssignBoxValueClassOrPrimitive: - BoxEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength); - break; - - case AssignMustCast: - CastCheckEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength); - break; - - case AssignPrimitiveWiden: - PrimitiveWiden(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength); - break; - - default: - _ASSERTE(!"Fell through switch in Array.Copy!"); - } - } - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - // Check we're allowed to create an array with the given element type. static void CheckElementType(TypeHandle elementType) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index fae3038469368..105b471c969e3 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -28,7 +28,6 @@ class ArrayNative static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); - static FCDECL5(void, CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength); // This set of methods will set a value in an array static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); @@ -41,7 +40,6 @@ class ArrayNative // to a field. static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); -private: // Return values for CanAssignArrayType enum AssignArrayEnum { @@ -53,7 +51,9 @@ class ArrayNative }; // The following functions are all helpers for ArrayCopy - static AssignArrayEnum CanAssignArrayType(const BASEARRAYREF pSrc, const BASEARRAYREF pDest); + static AssignArrayEnum CanAssignArrayType(const TypeHandle pSrc, const TypeHandle pDest); + +private: static void CastCheckEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); static void BoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); static void UnBoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); @@ -63,5 +63,6 @@ class ArrayNative extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd); +extern "C" INT32 QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH); #endif // _ARRAYNATIVE_H_ diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index e6b1ae38cd77b..cdc834caf46c8 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -390,7 +390,6 @@ FCFuncEnd() FCFuncStart(gArrayFuncs) FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) - FCFuncElement("CopySlow", ArrayNative::CopySlow) FCFuncElement("InternalSetValue", ArrayNative::SetValue) FCFuncEnd() diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 3a7264543a671..3e149e1a763a2 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -166,6 +166,7 @@ static const Entry s_QCall[] = DllImportEntry(MdUtf8String_EqualsCaseInsensitive) DllImportEntry(Array_CreateInstance) DllImportEntry(Array_GetElementConstructorEntrypoint) + DllImportEntry(Array_CanAssignArrayType) DllImportEntry(AssemblyName_InitializeAssemblySpec) DllImportEntry(AssemblyNative_GetFullName) DllImportEntry(AssemblyNative_GetLocation) diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index 100222ef29deb..b66755e495051 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -65,6 +65,12 @@ internal static void ThrowArrayTypeMismatchException() throw new ArrayTypeMismatchException(); } + [DoesNotReturn] + internal static void ThrowArrayTypeMismatchException_CantAssignType() + { + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } + [DoesNotReturn] internal static void ThrowInvalidTypeWithPointersNotSupported(Type targetType) { From 220ba579b08360e271be3d9e4c2a223efd374c19 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 19 Jan 2024 03:08:38 +0800 Subject: [PATCH 02/25] UnBoxEachElement --- .../src/System/Array.CoreCLR.cs | 30 ++++++++++++++++++- .../Runtime/CompilerServices/CastHelpers.cs | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 4da45dfe0d4e1..af33b9c5e86a5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -111,7 +111,8 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de switch (r) { case AssignArrayEnum.AssignUnboxValueClass: - throw null; + UnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + break; case AssignArrayEnum.AssignBoxValueClassOrPrimitive: throw null; @@ -141,6 +142,33 @@ private enum AssignArrayEnum [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); + // Unboxes from an Object[] into a value class or primitive array. + private static unsafe void UnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + { + MethodTable* pDestArrayMT = RuntimeHelpers.GetMethodTable(destinationArray); + TypeHandle destTH = pDestArrayMT->GetArrayElementTypeHandle(); + // _ASSERTE(destTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || destTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pDest->GetArrayElementType())); + // Debug.Assert(!RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle().IsValueType); + + MethodTable* pDestMT = destTH.AsMethodTable(); + nuint destSize = pDestArrayMT->ComponentSize; + ref object srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(ref sourceArray)), sourceIndex); + ref byte data = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(destinationArray), (nuint)destinationIndex * destSize); + + for (int i = 0; i < length; i++) + { + object obj = Unsafe.Add(ref srcData, i); + + // Now that we have retrieved the element, we are no longer subject to race + // conditions from another array mutator. + + Buffer.Memmove( + ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref CastHelpers.Unbox(pDestMT, obj), + destSize); + } + } + // Provides a strong exception guarantee - either it succeeds, or // it throws an exception with no side effects. The arrays must be // compatible array types based on the array element type - this diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs index 053490540ffc2..df4d82b2ec80b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs @@ -386,7 +386,7 @@ internal static unsafe class CastHelpers [DebuggerHidden] [StackTraceHidden] [DebuggerStepThrough] - private static ref byte Unbox(void* toTypeHnd, object obj) + internal static ref byte Unbox(void* toTypeHnd, object obj) { // this will throw NullReferenceException if obj is null, attributed to the user code, as expected. if (RuntimeHelpers.GetMethodTable(obj) == toTypeHnd) From a7fa4db2af68009ae5a4129c029a1740c864f5ae Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 19 Jan 2024 03:23:13 +0800 Subject: [PATCH 03/25] BoxEachElement --- .../src/System/Array.CoreCLR.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index af33b9c5e86a5..c890848ced693 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -115,7 +115,8 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de break; case AssignArrayEnum.AssignBoxValueClassOrPrimitive: - throw null; + BoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + break; case AssignArrayEnum.AssignMustCast: throw null; @@ -169,6 +170,29 @@ ref CastHelpers.Unbox(pDestMT, obj), } } + // Will box each element in an array of value classes or primitives into an array of Objects. + private static unsafe void BoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + { + MethodTable* pSrcArrayMT = RuntimeHelpers.GetMethodTable(sourceArray); + TypeHandle srcTH = pSrcArrayMT->GetArrayElementTypeHandle(); + // _ASSERTE(destTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || destTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pDest->GetArrayElementType())); + // Debug.Assert(!RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle().IsValueType); + + MethodTable* pSrcMT = srcTH.AsMethodTable(); + + RuntimeHelpers.RunClassConstructor(RuntimeTypeHandle.FromIntPtr((nint)pSrcMT)); // pSrcMT->CheckRunClassInitThrowing + + nuint srcSize = pSrcArrayMT->ComponentSize; + ref byte data = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(sourceArray), (nuint)sourceIndex * srcSize); + ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(ref destinationArray)), destinationIndex); + + for (int i = 0; i < length; i++) + { + object? obj = RuntimeHelpers.Box(pSrcMT, ref Unsafe.AddByteOffset(ref data, (nuint)i * srcSize)); + Unsafe.Add(ref destData, i) = obj; + } + } + // Provides a strong exception guarantee - either it succeeds, or // it throws an exception with no side effects. The arrays must be // compatible array types based on the array element type - this From 887c54a44536d016a00dbcc6095a47b5edd7fb57 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 19 Jan 2024 03:58:20 +0800 Subject: [PATCH 04/25] CastCheckEachElement --- .../src/System/Array.CoreCLR.cs | 22 ++++++++++++++++++- .../Runtime/CompilerServices/CastHelpers.cs | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index c890848ced693..6ef6a13a7d852 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -119,7 +119,8 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de break; case AssignArrayEnum.AssignMustCast: - throw null; + CastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + break; case AssignArrayEnum.AssignPrimitiveWiden: throw null; @@ -193,6 +194,25 @@ private static unsafe void BoxEachElement(Array sourceArray, int sourceIndex, Ar } } + // Casts and assigns each element of src array to the dest array type. + private static unsafe void CastCheckEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + { + TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); + + ref object? srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(ref sourceArray)), sourceIndex); + ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(ref destinationArray)), destinationIndex); + + for (int i = 0; i < length; i++) + { + object? obj = Unsafe.Add(ref srcData, i); + + // Now that we have grabbed obj, we are no longer subject to races from another + // mutator thread. + + Unsafe.Add(ref destData, i) = CastHelpers.ChkCastAny(destTH.AsMethodTable(), obj); + } + } + // Provides a strong exception guarantee - either it succeeds, or // it throws an exception with no side effects. The arrays must be // compatible array types based on the array element type - this diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs index df4d82b2ec80b..1f62427397a8d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs @@ -209,7 +209,7 @@ internal static unsafe class CastHelpers [DebuggerHidden] [StackTraceHidden] [DebuggerStepThrough] - private static object? ChkCastAny(void* toTypeHnd, object? obj) + internal static object? ChkCastAny(void* toTypeHnd, object? obj) { CastResult result; From 43b99ce739238a2d4f268dccc4e9413713763bd6 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 19 Jan 2024 04:14:19 +0800 Subject: [PATCH 05/25] Simplify Unsafe.As --- .../System.Private.CoreLib/src/System/Array.CoreCLR.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 6ef6a13a7d852..473c6ec3be115 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -154,7 +154,7 @@ private static unsafe void UnBoxEachElement(Array sourceArray, int sourceIndex, MethodTable* pDestMT = destTH.AsMethodTable(); nuint destSize = pDestArrayMT->ComponentSize; - ref object srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(ref sourceArray)), sourceIndex); + ref object srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(sourceArray)), sourceIndex); ref byte data = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(destinationArray), (nuint)destinationIndex * destSize); for (int i = 0; i < length; i++) @@ -185,7 +185,7 @@ private static unsafe void BoxEachElement(Array sourceArray, int sourceIndex, Ar nuint srcSize = pSrcArrayMT->ComponentSize; ref byte data = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(sourceArray), (nuint)sourceIndex * srcSize); - ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(ref destinationArray)), destinationIndex); + ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(destinationArray)), destinationIndex); for (int i = 0; i < length; i++) { @@ -199,8 +199,8 @@ private static unsafe void CastCheckEachElement(Array sourceArray, int sourceInd { TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); - ref object? srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(ref sourceArray)), sourceIndex); - ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(ref destinationArray)), destinationIndex); + ref object? srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(sourceArray)), sourceIndex); + ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(destinationArray)), destinationIndex); for (int i = 0; i < length; i++) { From ad0e7f671cc8a29632feb86949b6f2eab72f0df0 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 19 Jan 2024 11:57:56 +0800 Subject: [PATCH 06/25] PrimitiveWiden with scalar implementation --- .../src/System/Array.CoreCLR.cs | 168 +++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 473c6ec3be115..ec23ea36c914e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -123,7 +124,8 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de break; case AssignArrayEnum.AssignPrimitiveWiden: - throw null; + PrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + break; default: Debug.Fail("Fell through switch in Array.Copy!"); @@ -213,6 +215,170 @@ private static unsafe void CastCheckEachElement(Array sourceArray, int sourceInd } } + // Widen primitive types to another primitive type. + private static void PrimitiveWiden(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + { + // Get appropriate sizes, which requires method tables. + + CorElementType srcElType = sourceArray.GetCorElementTypeOfElementType(); + CorElementType destElType = destinationArray.GetCorElementTypeOfElementType(); + + switch (srcElType) + { + case CorElementType.ELEMENT_TYPE_U1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_CHAR: + case CorElementType.ELEMENT_TYPE_I2: + case CorElementType.ELEMENT_TYPE_U2: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + default: + Debug.Fail("Array.Copy from U1 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I2: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_I4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_I8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + default: + Debug.Fail("Array.Copy from I1 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + // U2 and CHAR are identical in conversion + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + default: + Debug.Fail("Array.Copy from U2 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I2: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_I8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + default: + Debug.Fail("Array.Copy from I2 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + default: + Debug.Fail("Array.Copy from U4 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + default: + Debug.Fail("Array.Copy from I4 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + default: + Debug.Fail("Array.Copy from U8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + case CorElementType.ELEMENT_TYPE_R8: + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + default: + Debug.Fail("Array.Copy from I8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_R4: + Debug.Assert(destElType == CorElementType.ELEMENT_TYPE_R8); + GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; + + default: + Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; + } + + static void GenericScalarWiden(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + where TFrom : unmanaged, INumber + where TTo : unmanaged, INumber + { + ref TFrom src = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(sourceArray)), sourceIndex); + ref TTo dst = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(destinationArray)), destinationIndex); + + for (int i = 0; i < length; i++) + { + Unsafe.Add(ref dst, i) = TTo.CreateTruncating(Unsafe.Add(ref src, i)); + } + } + } + // Provides a strong exception guarantee - either it succeeds, or // it throws an exception with no side effects. The arrays must be // compatible array types based on the array element type - this From 7ee8f426a27ff5f3e62b1921f1de5cc433366a9d Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 19 Jan 2024 12:01:59 +0800 Subject: [PATCH 07/25] Remove native implementation --- .../classlibnative/bcltype/arraynative.cpp | 427 ------------------ .../classlibnative/bcltype/arraynative.h | 7 - 2 files changed, 434 deletions(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index b8f1fd9a4b705..b5fef6968fc27 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -197,433 +197,6 @@ extern "C" int QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH) return ret; } -// Casts and assigns each element of src array to the dest array type. -void ArrayNative::CastCheckEachElement(const BASEARRAYREF pSrcUnsafe, const unsigned int srcIndex, BASEARRAYREF pDestUnsafe, unsigned int destIndex, const unsigned int len) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(pSrcUnsafe != NULL); - PRECONDITION(srcIndex >= 0); - PRECONDITION(pDestUnsafe != NULL); - PRECONDITION(len > 0); - } - CONTRACTL_END; - - // pSrc is either a PTRARRAYREF or a multidimensional array. - TypeHandle destTH = pDestUnsafe->GetArrayElementTypeHandle(); - - struct _gc - { - OBJECTREF obj; - BASEARRAYREF pDest; - BASEARRAYREF pSrc; - } gc; - - gc.obj = NULL; - gc.pDest = pDestUnsafe; - gc.pSrc = pSrcUnsafe; - - GCPROTECT_BEGIN(gc); - - for(unsigned int i=srcIndex; iGetDataPtr() + i)); - - // Now that we have grabbed obj, we are no longer subject to races from another - // mutator thread. - if (gc.obj != NULL && !ObjIsInstanceOf(OBJECTREFToObject(gc.obj), destTH)) - COMPlusThrow(kInvalidCastException, W("InvalidCast_DownCastArrayElement")); - - OBJECTREF * destData = (OBJECTREF*)(gc.pDest->GetDataPtr()) + i - srcIndex + destIndex; - SetObjectReference(destData, gc.obj); - } - - GCPROTECT_END(); - - return; -} - - -// Will box each element in an array of value classes or primitives into an array of Objects. -void ArrayNative::BoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(pSrc != NULL); - PRECONDITION(srcIndex >= 0); - PRECONDITION(pDest != NULL); - PRECONDITION(length > 0); - } - CONTRACTL_END; - - // pDest is either a PTRARRAYREF or a multidimensional array. - _ASSERTE(pSrc!=NULL && srcIndex>=0 && pDest!=NULL && destIndex>=0 && length>=0); - TypeHandle srcTH = pSrc->GetArrayElementTypeHandle(); -#ifdef _DEBUG - TypeHandle destTH = pDest->GetArrayElementTypeHandle(); -#endif - _ASSERTE(srcTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || srcTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pSrc->GetArrayElementType())); - _ASSERTE(!destTH.GetMethodTable()->IsValueType()); - - // Get method table of type we're copying from - we need to allocate objects of that type. - MethodTable * pSrcMT = srcTH.AsMethodTable(); - PREFIX_ASSUME(pSrcMT != NULL); - - if (!pSrcMT->IsClassInited()) - { - BASEARRAYREF pSrcTmp = pSrc; - BASEARRAYREF pDestTmp = pDest; - GCPROTECT_BEGIN (pSrcTmp); - GCPROTECT_BEGIN (pDestTmp); - pSrcMT->CheckRunClassInitThrowing(); - pSrc = pSrcTmp; - pDest = pDestTmp; - GCPROTECT_END (); - GCPROTECT_END (); - } - - const unsigned int srcSize = pSrcMT->GetNumInstanceFieldBytes(); - unsigned int srcArrayOffset = srcIndex * srcSize; - - struct _gc - { - BASEARRAYREF src; - BASEARRAYREF dest; - OBJECTREF obj; - } gc; - - gc.src = pSrc; - gc.dest = pDest; - gc.obj = NULL; - - void* srcPtr = 0; - GCPROTECT_BEGIN(gc); - GCPROTECT_BEGININTERIOR(srcPtr); - for (unsigned int i=destIndex; i < destIndex+length; i++, srcArrayOffset += srcSize) - { - srcPtr = (BYTE*)gc.src->GetDataPtr() + srcArrayOffset; - gc.obj = pSrcMT->FastBox(&srcPtr); - - OBJECTREF * destData = (OBJECTREF*)((gc.dest)->GetDataPtr()) + i; - SetObjectReference(destData, gc.obj); - } - GCPROTECT_END(); - GCPROTECT_END(); -} - - -// Unboxes from an Object[] into a value class or primitive array. -void ArrayNative::UnBoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(pSrc != NULL); - PRECONDITION(srcIndex >= 0); - PRECONDITION(pDest != NULL); - PRECONDITION(destIndex >= 0); - PRECONDITION(length > 0); - } - CONTRACTL_END; - -#ifdef _DEBUG - TypeHandle srcTH = pSrc->GetArrayElementTypeHandle(); -#endif - TypeHandle destTH = pDest->GetArrayElementTypeHandle(); - _ASSERTE(destTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || destTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pDest->GetArrayElementType())); - _ASSERTE(!srcTH.GetMethodTable()->IsValueType()); - - MethodTable * pDestMT = destTH.AsMethodTable(); - PREFIX_ASSUME(pDestMT != NULL); - - SIZE_T destSize = pDest->GetComponentSize(); - BYTE* srcData = (BYTE*) pSrc->GetDataPtr() + srcIndex * sizeof(OBJECTREF); - BYTE* data = (BYTE*) pDest->GetDataPtr() + destIndex * destSize; - - for(; length>0; length--, srcData += sizeof(OBJECTREF), data += destSize) - { - OBJECTREF obj = ObjectToOBJECTREF(*(Object**)srcData); - - // Now that we have retrieved the element, we are no longer subject to race - // conditions from another array mutator. - - if (!pDestMT->UnBoxInto(data, obj)) - goto fail; - } - return; - -fail: - COMPlusThrow(kInvalidCastException, W("InvalidCast_DownCastArrayElement")); -} - - -// Widen primitive types to another primitive type. -void ArrayNative::PrimitiveWiden(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - PRECONDITION(pSrc != NULL); - PRECONDITION(srcIndex >= 0); - PRECONDITION(pDest != NULL); - PRECONDITION(destIndex >= 0); - PRECONDITION(length > 0); - } - CONTRACTL_END; - - // Get appropriate sizes, which requires method tables. - TypeHandle srcTH = pSrc->GetArrayElementTypeHandle(); - TypeHandle destTH = pDest->GetArrayElementTypeHandle(); - - const CorElementType srcElType = srcTH.GetVerifierCorElementType(); - const CorElementType destElType = destTH.GetVerifierCorElementType(); - const unsigned int srcSize = GetSizeForCorElementType(srcElType); - const unsigned int destSize = GetSizeForCorElementType(destElType); - - BYTE* srcData = (BYTE*) pSrc->GetDataPtr() + srcIndex * srcSize; - BYTE* data = (BYTE*) pDest->GetDataPtr() + destIndex * destSize; - - _ASSERTE(srcElType != destElType); // We shouldn't be here if these are the same type. - _ASSERTE(CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)); - - for(; length>0; length--, srcData += srcSize, data += destSize) - { - // We pretty much have to do some fancy datatype mangling every time here, for - // converting w/ sign extension and floating point conversions. - switch (srcElType) - { - case ELEMENT_TYPE_U1: - switch (destElType) - { - case ELEMENT_TYPE_R4: - *(float*)data = *(UINT8*)srcData; - break; - - case ELEMENT_TYPE_R8: - *(double*)data = *(UINT8*)srcData; - break; -#ifndef BIGENDIAN - default: - *(UINT8*)data = *(UINT8*)srcData; - memset(data+1, 0, destSize - 1); - break; -#else // BIGENDIAN - case ELEMENT_TYPE_CHAR: - case ELEMENT_TYPE_I2: - case ELEMENT_TYPE_U2: - *(INT16*)data = *(UINT8*)srcData; - break; - - case ELEMENT_TYPE_I4: - case ELEMENT_TYPE_U4: - *(INT32*)data = *(UINT8*)srcData; - break; - - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - *(INT64*)data = *(UINT8*)srcData; - break; - - default: - _ASSERTE(!"Array.Copy from U1 to another type hit unsupported widening conversion"); -#endif // BIGENDIAN - } - break; - - - case ELEMENT_TYPE_I1: - switch (destElType) - { - case ELEMENT_TYPE_I2: - *(INT16*)data = *(INT8*)srcData; - break; - - case ELEMENT_TYPE_I4: - *(INT32*)data = *(INT8*)srcData; - break; - - case ELEMENT_TYPE_I8: - *(INT64*)data = *(INT8*)srcData; - break; - - case ELEMENT_TYPE_R4: - *(float*)data = *(INT8*)srcData; - break; - - case ELEMENT_TYPE_R8: - *(double*)data = *(INT8*)srcData; - break; - - default: - _ASSERTE(!"Array.Copy from I1 to another type hit unsupported widening conversion"); - } - break; - - - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_CHAR: - switch (destElType) - { - case ELEMENT_TYPE_R4: - *(float*)data = *(UINT16*)srcData; - break; - - case ELEMENT_TYPE_R8: - *(double*)data = *(UINT16*)srcData; - break; -#ifndef BIGENDIAN - default: - *(UINT16*)data = *(UINT16*)srcData; - memset(data+2, 0, destSize - 2); - break; -#else // BIGENDIAN - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_CHAR: - *(UINT16*)data = *(UINT16*)srcData; - break; - - case ELEMENT_TYPE_I4: - case ELEMENT_TYPE_U4: - *(UINT32*)data = *(UINT16*)srcData; - break; - - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - *(UINT64*)data = *(UINT16*)srcData; - break; - - default: - _ASSERTE(!"Array.Copy from U1 to another type hit unsupported widening conversion"); -#endif // BIGENDIAN - } - break; - - - case ELEMENT_TYPE_I2: - switch (destElType) - { - case ELEMENT_TYPE_I4: - *(INT32*)data = *(INT16*)srcData; - break; - - case ELEMENT_TYPE_I8: - *(INT64*)data = *(INT16*)srcData; - break; - - case ELEMENT_TYPE_R4: - *(float*)data = *(INT16*)srcData; - break; - - case ELEMENT_TYPE_R8: - *(double*)data = *(INT16*)srcData; - break; - - default: - _ASSERTE(!"Array.Copy from I2 to another type hit unsupported widening conversion"); - } - break; - - - case ELEMENT_TYPE_I4: - switch (destElType) - { - case ELEMENT_TYPE_I8: - *(INT64*)data = *(INT32*)srcData; - break; - - case ELEMENT_TYPE_R4: - *(float*)data = (float)*(INT32*)srcData; - break; - - case ELEMENT_TYPE_R8: - *(double*)data = *(INT32*)srcData; - break; - - default: - _ASSERTE(!"Array.Copy from I4 to another type hit unsupported widening conversion"); - } - break; - - - case ELEMENT_TYPE_U4: - switch (destElType) - { - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - *(INT64*)data = *(UINT32*)srcData; - break; - - case ELEMENT_TYPE_R4: - *(float*)data = (float)*(UINT32*)srcData; - break; - - case ELEMENT_TYPE_R8: - *(double*)data = *(UINT32*)srcData; - break; - - default: - _ASSERTE(!"Array.Copy from U4 to another type hit unsupported widening conversion"); - } - break; - - - case ELEMENT_TYPE_I8: - if (destElType == ELEMENT_TYPE_R4) - { - *(float*) data = (float) *(INT64*)srcData; - } - else - { - _ASSERTE(destElType==ELEMENT_TYPE_R8); - *(double*) data = (double) *(INT64*)srcData; - } - break; - - - case ELEMENT_TYPE_U8: - if (destElType == ELEMENT_TYPE_R4) - { - //*(float*) data = (float) *(UINT64*)srcData; - INT64 srcVal = *(INT64*)srcData; - float f = (float) srcVal; - if (srcVal < 0) - f += 4294967296.0f * 4294967296.0f; // This is 2^64 - - *(float*) data = f; - } - else - { - _ASSERTE(destElType==ELEMENT_TYPE_R8); - //*(double*) data = (double) *(UINT64*)srcData; - INT64 srcVal = *(INT64*)srcData; - double d = (double) srcVal; - if (srcVal < 0) - d += 4294967296.0 * 4294967296.0; // This is 2^64 - - *(double*) data = d; - } - break; - - - case ELEMENT_TYPE_R4: - *(double*) data = *(float*)srcData; - break; - - default: - _ASSERTE(!"Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); - } - } -} // // This is a GC safe variant of the memmove intrinsic. It sets the cards, and guarantees that the object references in the GC heap are diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 105b471c969e3..94eaca51ab51e 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -52,13 +52,6 @@ class ArrayNative // The following functions are all helpers for ArrayCopy static AssignArrayEnum CanAssignArrayType(const TypeHandle pSrc, const TypeHandle pDest); - -private: - static void CastCheckEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); - static void BoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); - static void UnBoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); - static void PrimitiveWiden(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length); - }; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); From 2e6305454643dd1c8160a4294d13f5f84fef3f5c Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 19 Jan 2024 12:05:22 +0800 Subject: [PATCH 08/25] Suppress trimming warning --- src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index ec23ea36c914e..d65300a90237f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; @@ -174,6 +175,7 @@ ref CastHelpers.Unbox(pDestMT, obj), } // Will box each element in an array of value classes or primitives into an array of Objects. + [UnconditionalSuppressMessage("Trimming", "IL2059:The type passed to the RunClassConstructor is not statically known, Trimmer can't make sure that its static constructor is available.", Justification = "The type handle is retrieved from existing array.")] private static unsafe void BoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { MethodTable* pSrcArrayMT = RuntimeHelpers.GetMethodTable(sourceArray); From 8062ec4e3010328902a30286d47c325ec280c837 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 19 Jan 2024 17:53:10 +0800 Subject: [PATCH 09/25] Fix GC mode for CanAssignArrayType --- src/coreclr/classlibnative/bcltype/arraynative.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index b5fef6968fc27..82f77d7cdd6f2 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -121,7 +121,7 @@ ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const TypeHandle sr { THROWS; GC_TRIGGERS; - MODE_COOPERATIVE; + MODE_ANY; PRECONDITION(srcTH != NULL); PRECONDITION(destTH != NULL); } From dafbc243a369d11edee4d1ef078b7fb2f31f2bf1 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 20 Jan 2024 10:29:51 +0800 Subject: [PATCH 10/25] Rename each routine --- .../src/System/Array.CoreCLR.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index d65300a90237f..1783f8138525d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -113,19 +113,19 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de switch (r) { case AssignArrayEnum.AssignUnboxValueClass: - UnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; case AssignArrayEnum.AssignBoxValueClassOrPrimitive: - BoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; case AssignArrayEnum.AssignMustCast: - CastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; case AssignArrayEnum.AssignPrimitiveWiden: - PrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; default: @@ -148,7 +148,7 @@ private enum AssignArrayEnum private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); // Unboxes from an Object[] into a value class or primitive array. - private static unsafe void UnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { MethodTable* pDestArrayMT = RuntimeHelpers.GetMethodTable(destinationArray); TypeHandle destTH = pDestArrayMT->GetArrayElementTypeHandle(); @@ -176,7 +176,7 @@ ref CastHelpers.Unbox(pDestMT, obj), // Will box each element in an array of value classes or primitives into an array of Objects. [UnconditionalSuppressMessage("Trimming", "IL2059:The type passed to the RunClassConstructor is not statically known, Trimmer can't make sure that its static constructor is available.", Justification = "The type handle is retrieved from existing array.")] - private static unsafe void BoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + private static unsafe void CopyImplBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { MethodTable* pSrcArrayMT = RuntimeHelpers.GetMethodTable(sourceArray); TypeHandle srcTH = pSrcArrayMT->GetArrayElementTypeHandle(); @@ -199,7 +199,7 @@ private static unsafe void BoxEachElement(Array sourceArray, int sourceIndex, Ar } // Casts and assigns each element of src array to the dest array type. - private static unsafe void CastCheckEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + private static unsafe void CopyImplCastCheckEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); @@ -218,7 +218,7 @@ private static unsafe void CastCheckEachElement(Array sourceArray, int sourceInd } // Widen primitive types to another primitive type. - private static void PrimitiveWiden(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + private static void CopyImplPrimitiveWiden(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { // Get appropriate sizes, which requires method tables. From 41f3fa81eecf9640ce487ba66b8708018059142c Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 22 Jan 2024 00:34:56 +0800 Subject: [PATCH 11/25] Reduce code bloat of generic math --- .../src/System/Array.CoreCLR.cs | 298 +++++++++--------- 1 file changed, 148 insertions(+), 150 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 1783f8138525d..f5582395cd701 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -218,165 +217,164 @@ private static unsafe void CopyImplCastCheckEachElement(Array sourceArray, int s } // Widen primitive types to another primitive type. - private static void CopyImplPrimitiveWiden(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + private static unsafe void CopyImplPrimitiveWiden(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { // Get appropriate sizes, which requires method tables. CorElementType srcElType = sourceArray.GetCorElementTypeOfElementType(); CorElementType destElType = destinationArray.GetCorElementTypeOfElementType(); - switch (srcElType) - { - case CorElementType.ELEMENT_TYPE_U1: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_CHAR: - case CorElementType.ELEMENT_TYPE_I2: - case CorElementType.ELEMENT_TYPE_U2: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_I4: - case CorElementType.ELEMENT_TYPE_U4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - default: - Debug.Fail("Array.Copy from U1 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I1: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I2: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_I4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_I8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - default: - Debug.Fail("Array.Copy from I1 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_CHAR: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_U2: - case CorElementType.ELEMENT_TYPE_CHAR: - // U2 and CHAR are identical in conversion - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_I4: - case CorElementType.ELEMENT_TYPE_U4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - default: - Debug.Fail("Array.Copy from U2 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I2: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_I8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - default: - Debug.Fail("Array.Copy from I2 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U4: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I8: - case CorElementType.ELEMENT_TYPE_U8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - default: - Debug.Fail("Array.Copy from U4 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I4: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_I8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - default: - Debug.Fail("Array.Copy from I4 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_U8: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_R4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - default: - Debug.Fail("Array.Copy from U8 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_I8: - switch (destElType) - { - case CorElementType.ELEMENT_TYPE_R4: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case CorElementType.ELEMENT_TYPE_R8: - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - default: - Debug.Fail("Array.Copy from I8 to another type hit unsupported widening conversion"); break; - } - break; - - case CorElementType.ELEMENT_TYPE_R4: - Debug.Assert(destElType == CorElementType.ELEMENT_TYPE_R8); - GenericScalarWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - - default: - Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; - } + nuint srcElSize = RuntimeHelpers.GetMethodTable(sourceArray)->ComponentSize; + nuint destElSize = RuntimeHelpers.GetMethodTable(destinationArray)->ComponentSize; + + ref byte srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(sourceArray), (nuint)sourceIndex * srcElSize); + ref byte data = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(destinationArray), (nuint)destinationIndex * destElSize); - static void GenericScalarWiden(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) - where TFrom : unmanaged, INumber - where TTo : unmanaged, INumber + for (int i = 0; i < length; i++) { - ref TFrom src = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(sourceArray)), sourceIndex); - ref TTo dst = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(destinationArray)), destinationIndex); + ref byte srcElement = ref Unsafe.Add(ref srcData, (nuint)i * srcElSize); + ref byte destElement = ref Unsafe.Add(ref data, (nuint)i * srcElSize); - for (int i = 0; i < length; i++) + switch (srcElType) { - Unsafe.Add(ref dst, i) = TTo.CreateTruncating(Unsafe.Add(ref src, i)); + case CorElementType.ELEMENT_TYPE_U1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_CHAR: + case CorElementType.ELEMENT_TYPE_I2: + case CorElementType.ELEMENT_TYPE_U2: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = srcElement; break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = srcElement; break; + default: + Debug.Fail("Array.Copy from U1 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I1: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I2: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I1 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_CHAR: + // U2 and CHAR are identical in conversion + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I4: + case CorElementType.ELEMENT_TYPE_U4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U2 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I2: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I2 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + case CorElementType.ELEMENT_TYPE_U8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U4 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I4: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_I8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I4 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_U8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from U8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_I8: + switch (destElType) + { + case CorElementType.ELEMENT_TYPE_R4: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + case CorElementType.ELEMENT_TYPE_R8: + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + default: + Debug.Fail("Array.Copy from I8 to another type hit unsupported widening conversion"); break; + } + break; + + case CorElementType.ELEMENT_TYPE_R4: + Debug.Assert(destElType == CorElementType.ELEMENT_TYPE_R8); + Unsafe.As(ref destElement) = Unsafe.As(ref srcElement); break; + + default: + Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; } } } From 5456631f9c89721407db8c4c9f5fccd52322e757 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 22 Jan 2024 00:51:50 +0800 Subject: [PATCH 12/25] GC barrier --- .../src/System/Array.CoreCLR.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index f5582395cd701..c7f65d1db654b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -166,10 +166,20 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc // Now that we have retrieved the element, we are no longer subject to race // conditions from another array mutator. - Buffer.Memmove( - ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), - ref CastHelpers.Unbox(pDestMT, obj), - destSize); + if (pDestMT->ContainsGCPointers) + { + Buffer.BulkMoveWithWriteBarrier( + ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref CastHelpers.Unbox(pDestMT, obj), + destSize); + } + else + { + Buffer.Memmove( + ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref CastHelpers.Unbox(pDestMT, obj), + destSize); + } } } From 0dddd004562ebcd29c3c8dbe8c8f6f86e501d075 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 22 Jan 2024 02:16:24 +0800 Subject: [PATCH 13/25] Unbox helper for nullable --- .../src/System/Array.CoreCLR.cs | 26 +++++++++++++------ .../Runtime/CompilerServices/CastHelpers.cs | 3 +++ src/coreclr/vm/ecalllist.h | 1 + src/coreclr/vm/jitinterface.h | 1 + 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index c7f65d1db654b..78bdd0644998b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -166,19 +166,29 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc // Now that we have retrieved the element, we are no longer subject to race // conditions from another array mutator. - if (pDestMT->ContainsGCPointers) + if (pDestMT->IsNullable) { - Buffer.BulkMoveWithWriteBarrier( + CastHelpers.Unbox_Nullable( ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), - ref CastHelpers.Unbox(pDestMT, obj), - destSize); + pDestMT, + obj); } else { - Buffer.Memmove( - ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), - ref CastHelpers.Unbox(pDestMT, obj), - destSize); + if (pDestMT->ContainsGCPointers) + { + Buffer.BulkMoveWithWriteBarrier( + ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref CastHelpers.Unbox(pDestMT, obj), + destSize); + } + else + { + Buffer.Memmove( + ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref CastHelpers.Unbox(pDestMT, obj), + destSize); + } } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs index 1f62427397a8d..c66ec2a632d7c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs @@ -23,6 +23,9 @@ internal static unsafe class CastHelpers [MethodImpl(MethodImplOptions.InternalCall)] private static extern ref byte Unbox_Helper(void* toTypeHnd, object obj); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void Unbox_Nullable(ref byte destination, void* toTypeHnd, object? obj); + [MethodImpl(MethodImplOptions.InternalCall)] private static extern void WriteBarrier(ref object? dst, object obj); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index cdc834caf46c8..2f81be9bce2f9 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -384,6 +384,7 @@ FCFuncStart(gCastHelpers) FCFuncElement("IsInstanceOfAny_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup) FCFuncElement("ChkCastAny_NoCacheLookup", ::ChkCastAny_NoCacheLookup) FCFuncElement("Unbox_Helper", ::Unbox_Helper) + FCFuncElement("Unbox_Nullable", ::JIT_Unbox_Nullable) FCFuncElement("WriteBarrier", ::WriteBarrier_Helper) FCFuncEnd() diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index d1ae48e2df5ca..f8aa5ea3e7e16 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -241,6 +241,7 @@ extern "C" FCDECL2(VOID, JIT_WriteBarrierEnsureNonHeapTarget, Object **dst, Obje extern "C" FCDECL2(Object*, ChkCastAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj); extern "C" FCDECL2(Object*, IsInstanceOfAny_NoCacheLookup, CORINFO_CLASS_HANDLE type, Object* obj); extern "C" FCDECL2(LPVOID, Unbox_Helper, CORINFO_CLASS_HANDLE type, Object* obj); +extern "C" FCDECL3(void, JIT_Unbox_Nullable, void * destPtr, CORINFO_CLASS_HANDLE type, Object* obj); // ARM64 JIT_WriteBarrier uses speciall ABI and thus is not callable directly // Copied write barriers must be called at a different location From 4965cb56a0ee66c8811f0451cb9df97a54c8b04e Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 22 Jan 2024 20:07:12 +0800 Subject: [PATCH 14/25] Throw InvalidCastException --- .../src/System/Array.CoreCLR.cs | 29 ++++++++++--------- .../src/System/ThrowHelper.cs | 6 ++++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 78bdd0644998b..fdac4b2651c2f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -173,22 +173,23 @@ ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), pDestMT, obj); } + else if (obj is null) + { + ThrowHelper.ThrowInvalidCastException_DownCastArrayElement(); + } + else if (pDestMT->ContainsGCPointers) + { + Buffer.BulkMoveWithWriteBarrier( + ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref CastHelpers.Unbox(pDestMT, obj), + destSize); + } else { - if (pDestMT->ContainsGCPointers) - { - Buffer.BulkMoveWithWriteBarrier( - ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), - ref CastHelpers.Unbox(pDestMT, obj), - destSize); - } - else - { - Buffer.Memmove( - ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), - ref CastHelpers.Unbox(pDestMT, obj), - destSize); - } + Buffer.Memmove( + ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref CastHelpers.Unbox(pDestMT, obj), + destSize); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index b66755e495051..5108a8b22fb9a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -71,6 +71,12 @@ internal static void ThrowArrayTypeMismatchException_CantAssignType() throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); } + [DoesNotReturn] + internal static void ThrowInvalidCastException_DownCastArrayElement() + { + throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement); + } + [DoesNotReturn] internal static void ThrowInvalidTypeWithPointersNotSupported(Type targetType) { From dc4724d65aac3ae41394454f8ec3bcf116fd7e41 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 22 Jan 2024 21:52:47 +0800 Subject: [PATCH 15/25] Simplify --- .../src/System/Array.CoreCLR.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index fdac4b2651c2f..16da49a4e3c01 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -156,20 +156,22 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc MethodTable* pDestMT = destTH.AsMethodTable(); nuint destSize = pDestArrayMT->ComponentSize; - ref object srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(sourceArray)), sourceIndex); + ref object? srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(sourceArray)), sourceIndex); ref byte data = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(destinationArray), (nuint)destinationIndex * destSize); for (int i = 0; i < length; i++) { - object obj = Unsafe.Add(ref srcData, i); + object? obj = Unsafe.Add(ref srcData, i); // Now that we have retrieved the element, we are no longer subject to race // conditions from another array mutator. + ref byte dest = ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize); + if (pDestMT->IsNullable) { CastHelpers.Unbox_Nullable( - ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref dest, pDestMT, obj); } @@ -180,14 +182,14 @@ ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), else if (pDestMT->ContainsGCPointers) { Buffer.BulkMoveWithWriteBarrier( - ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref dest, ref CastHelpers.Unbox(pDestMT, obj), destSize); } else { Buffer.Memmove( - ref Unsafe.AddByteOffset(ref data, (nuint)i * destSize), + ref dest, ref CastHelpers.Unbox(pDestMT, obj), destSize); } From 879c3cf70fdc64e0a195b72ebc24b4c20d1d70b5 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 22 Jan 2024 22:21:59 +0800 Subject: [PATCH 16/25] Add asserts --- .../System.Private.CoreLib/src/System/Array.CoreCLR.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 16da49a4e3c01..87d1503a03c2f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -151,8 +151,9 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc { MethodTable* pDestArrayMT = RuntimeHelpers.GetMethodTable(destinationArray); TypeHandle destTH = pDestArrayMT->GetArrayElementTypeHandle(); - // _ASSERTE(destTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || destTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pDest->GetArrayElementType())); - // Debug.Assert(!RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle().IsValueType); + + Debug.Assert(!destTH.IsTypeDesc && destTH.AsMethodTable()->IsValueType); + Debug.Assert(!RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle().AsMethodTable()->IsValueType); MethodTable* pDestMT = destTH.AsMethodTable(); nuint destSize = pDestArrayMT->ComponentSize; @@ -202,8 +203,9 @@ private static unsafe void CopyImplBoxEachElement(Array sourceArray, int sourceI { MethodTable* pSrcArrayMT = RuntimeHelpers.GetMethodTable(sourceArray); TypeHandle srcTH = pSrcArrayMT->GetArrayElementTypeHandle(); - // _ASSERTE(destTH.GetSignatureCorElementType() == ELEMENT_TYPE_CLASS || destTH.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE || CorTypeInfo::IsPrimitiveType(pDest->GetArrayElementType())); - // Debug.Assert(!RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle().IsValueType); + + Debug.Assert(!srcTH.IsTypeDesc && srcTH.AsMethodTable()->IsValueType); + Debug.Assert(!RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle().AsMethodTable()->IsValueType); MethodTable* pSrcMT = srcTH.AsMethodTable(); From 17c9b2359a57446f5c40986dd9aef879aab43fce Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 22 Jan 2024 23:07:01 +0800 Subject: [PATCH 17/25] Try remove cctor run --- src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 87d1503a03c2f..e012d95cd9c55 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -198,7 +198,6 @@ ref CastHelpers.Unbox(pDestMT, obj), } // Will box each element in an array of value classes or primitives into an array of Objects. - [UnconditionalSuppressMessage("Trimming", "IL2059:The type passed to the RunClassConstructor is not statically known, Trimmer can't make sure that its static constructor is available.", Justification = "The type handle is retrieved from existing array.")] private static unsafe void CopyImplBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { MethodTable* pSrcArrayMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -209,8 +208,6 @@ private static unsafe void CopyImplBoxEachElement(Array sourceArray, int sourceI MethodTable* pSrcMT = srcTH.AsMethodTable(); - RuntimeHelpers.RunClassConstructor(RuntimeTypeHandle.FromIntPtr((nint)pSrcMT)); // pSrcMT->CheckRunClassInitThrowing - nuint srcSize = pSrcArrayMT->ComponentSize; ref byte data = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(sourceArray), (nuint)sourceIndex * srcSize); ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(destinationArray)), destinationIndex); From 79ff323fabef90000cfc726cfd39596693b28a41 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 24 Jan 2024 22:01:11 +0800 Subject: [PATCH 18/25] Use void* for ElementType --- .../System.Private.CoreLib/src/System/Array.CoreCLR.cs | 10 +++++----- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index e012d95cd9c55..81295efe934df 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -100,9 +100,9 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); - TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); - AssignArrayEnum r = CanAssignArrayType(srcTH.m_asTAddr, destTH.m_asTAddr); + void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType; + void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; + AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); if (r == AssignArrayEnum.AssignWrongType) ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); @@ -222,7 +222,7 @@ private static unsafe void CopyImplBoxEachElement(Array sourceArray, int sourceI // Casts and assigns each element of src array to the dest array type. private static unsafe void CopyImplCastCheckEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { - TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); + void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; ref object? srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(sourceArray)), sourceIndex); ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(destinationArray)), destinationIndex); @@ -234,7 +234,7 @@ private static unsafe void CopyImplCastCheckEachElement(Array sourceArray, int s // Now that we have grabbed obj, we are no longer subject to races from another // mutator thread. - Unsafe.Add(ref destData, i) = CastHelpers.ChkCastAny(destTH.AsMethodTable(), obj); + Unsafe.Add(ref destData, i) = CastHelpers.ChkCastAny(destTH, obj); } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 408bc0750f62c..112729d383ac1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -622,7 +622,7 @@ internal unsafe struct TypeHandle /// /// The address of the current type handle object. /// - internal readonly void* m_asTAddr; + private readonly void* m_asTAddr; [MethodImpl(MethodImplOptions.AggressiveInlining)] public TypeHandle(void* tAddr) From 491dd2e30fb7d7f9d31d75b8599c1d651aa74279 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 24 Jan 2024 22:14:49 +0800 Subject: [PATCH 19/25] UnBoxEachElement only allows exact type match --- .../System.Private.CoreLib/src/System/Array.CoreCLR.cs | 7 ++++--- .../src/System/Runtime/CompilerServices/CastHelpers.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 81295efe934df..3bf95e2dabfe8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -176,22 +176,23 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc pDestMT, obj); } - else if (obj is null) + else if (obj is null || RuntimeHelpers.GetMethodTable(obj) != pDestMT) { + GC.KeepAlive(obj); ThrowHelper.ThrowInvalidCastException_DownCastArrayElement(); } else if (pDestMT->ContainsGCPointers) { Buffer.BulkMoveWithWriteBarrier( ref dest, - ref CastHelpers.Unbox(pDestMT, obj), + ref obj.GetRawData(), destSize); } else { Buffer.Memmove( ref dest, - ref CastHelpers.Unbox(pDestMT, obj), + ref obj.GetRawData(), destSize); } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs index c66ec2a632d7c..9dd209631f53f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs @@ -389,7 +389,7 @@ internal static unsafe class CastHelpers [DebuggerHidden] [StackTraceHidden] [DebuggerStepThrough] - internal static ref byte Unbox(void* toTypeHnd, object obj) + private static ref byte Unbox(void* toTypeHnd, object obj) { // this will throw NullReferenceException if obj is null, attributed to the user code, as expected. if (RuntimeHelpers.GetMethodTable(obj) == toTypeHnd) From c96d08d8230dacb6a70668dd1658c1ea8e4d7a47 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 25 Jan 2024 20:30:14 +0800 Subject: [PATCH 20/25] Fix MD array --- .../System.Private.CoreLib/src/System/Array.CoreCLR.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 3bf95e2dabfe8..ae39f9eb41678 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -157,7 +157,7 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc MethodTable* pDestMT = destTH.AsMethodTable(); nuint destSize = pDestArrayMT->ComponentSize; - ref object? srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(sourceArray)), sourceIndex); + ref object? srcData = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(sourceArray)), sourceIndex); ref byte data = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(destinationArray), (nuint)destinationIndex * destSize); for (int i = 0; i < length; i++) @@ -211,7 +211,7 @@ private static unsafe void CopyImplBoxEachElement(Array sourceArray, int sourceI nuint srcSize = pSrcArrayMT->ComponentSize; ref byte data = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(sourceArray), (nuint)sourceIndex * srcSize); - ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(destinationArray)), destinationIndex); + ref object? destData = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(destinationArray)), destinationIndex); for (int i = 0; i < length; i++) { @@ -225,8 +225,8 @@ private static unsafe void CopyImplCastCheckEachElement(Array sourceArray, int s { void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; - ref object? srcData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(sourceArray)), sourceIndex); - ref object? destData = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(destinationArray)), destinationIndex); + ref object? srcData = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(sourceArray)), sourceIndex); + ref object? destData = ref Unsafe.Add(ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(destinationArray)), destinationIndex); for (int i = 0; i < length; i++) { From c27846ba2bd42ffd81ab8a1bdfec545aad3f5b1a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 25 Jan 2024 20:44:06 +0800 Subject: [PATCH 21/25] Nit --- .../src/System/Array.CoreCLR.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index ae39f9eb41678..04a5e40c2884c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -171,29 +171,19 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc if (pDestMT->IsNullable) { - CastHelpers.Unbox_Nullable( - ref dest, - pDestMT, - obj); + CastHelpers.Unbox_Nullable(ref dest, pDestMT, obj); } else if (obj is null || RuntimeHelpers.GetMethodTable(obj) != pDestMT) { - GC.KeepAlive(obj); ThrowHelper.ThrowInvalidCastException_DownCastArrayElement(); } else if (pDestMT->ContainsGCPointers) { - Buffer.BulkMoveWithWriteBarrier( - ref dest, - ref obj.GetRawData(), - destSize); + Buffer.BulkMoveWithWriteBarrier(ref dest, ref obj.GetRawData(), destSize); } else { - Buffer.Memmove( - ref dest, - ref obj.GetRawData(), - destSize); + Buffer.Memmove(ref dest, ref obj.GetRawData(), destSize); } } } From 224adf5f0d6b49d8003f2c8282b6e9dfd79249db Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 25 Jan 2024 21:27:37 +0800 Subject: [PATCH 22/25] Move Unbox_Nullable to RuntimeHelpers --- src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs | 2 +- .../src/System/Runtime/CompilerServices/CastHelpers.cs | 3 --- .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 3 +++ src/coreclr/vm/ecalllist.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 04a5e40c2884c..7a22d48119cd7 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -171,7 +171,7 @@ private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourc if (pDestMT->IsNullable) { - CastHelpers.Unbox_Nullable(ref dest, pDestMT, obj); + RuntimeHelpers.Unbox_Nullable(ref dest, pDestMT, obj); } else if (obj is null || RuntimeHelpers.GetMethodTable(obj) != pDestMT) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs index 9dd209631f53f..5f6c7070958ac 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs @@ -23,9 +23,6 @@ internal static unsafe class CastHelpers [MethodImpl(MethodImplOptions.InternalCall)] private static extern ref byte Unbox_Helper(void* toTypeHnd, object obj); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Unbox_Nullable(ref byte destination, void* toTypeHnd, object? obj); - [MethodImpl(MethodImplOptions.InternalCall)] private static extern void WriteBarrier(ref object? dst, object obj); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 112729d383ac1..534a251630cda 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -301,6 +301,9 @@ internal static unsafe bool ObjectHasComponentSize(object obj) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern unsafe object? Box(MethodTable* methodTable, ref byte data); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe void Unbox_Nullable(ref byte destination, MethodTable* toTypeHnd, object? obj); + // Given an object reference, returns its MethodTable*. // // WARNING: The caller has to ensure that MethodTable* does not get unloaded. The most robust way diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 2f81be9bce2f9..cbaaf8456c882 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -384,7 +384,6 @@ FCFuncStart(gCastHelpers) FCFuncElement("IsInstanceOfAny_NoCacheLookup", ::IsInstanceOfAny_NoCacheLookup) FCFuncElement("ChkCastAny_NoCacheLookup", ::ChkCastAny_NoCacheLookup) FCFuncElement("Unbox_Helper", ::Unbox_Helper) - FCFuncElement("Unbox_Nullable", ::JIT_Unbox_Nullable) FCFuncElement("WriteBarrier", ::WriteBarrier_Helper) FCFuncEnd() @@ -482,6 +481,7 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo) FCFuncElement("Box", JIT_Box) + FCFuncElement("Unbox_Nullable", JIT_Unbox_Nullable) FCFuncEnd() FCFuncStart(gMethodTableFuncs) From d67643ef7745d1b05516a74095dff86da2a66228 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 26 Jan 2024 05:05:06 +0800 Subject: [PATCH 23/25] Fix PrimitiveWiden --- src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 7a22d48119cd7..bac7744e0b3e7 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -246,7 +246,7 @@ private static unsafe void CopyImplPrimitiveWiden(Array sourceArray, int sourceI for (int i = 0; i < length; i++) { ref byte srcElement = ref Unsafe.Add(ref srcData, (nuint)i * srcElSize); - ref byte destElement = ref Unsafe.Add(ref data, (nuint)i * srcElSize); + ref byte destElement = ref Unsafe.Add(ref data, (nuint)i * destElSize); switch (srcElType) { From 297d5155b59e7ba7ddd91d2cc6fcfa09480d247a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Fri, 26 Jan 2024 23:15:11 +0800 Subject: [PATCH 24/25] Cleanup native code --- .../src/System/Array.CoreCLR.cs | 1 + .../classlibnative/bcltype/arraynative.cpp | 19 ++++++++++++++----- .../classlibnative/bcltype/arraynative.h | 13 ------------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index bac7744e0b3e7..16d9067567ee5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -134,6 +134,7 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de } } + // Must match the definition in arraynative.cpp private enum AssignArrayEnum { AssignWrongType, diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 82f77d7cdd6f2..079416d6b81b9 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -114,16 +114,25 @@ FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst FCIMPLEND +// Return values for CanAssignArrayType +enum AssignArrayEnum +{ + AssignWrongType, + AssignMustCast, + AssignBoxValueClassOrPrimitive, + AssignUnboxValueClass, + AssignPrimitiveWiden, +}; + // Returns an enum saying whether you can copy an array of srcType into destType. -ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) +AssignArrayEnum CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) { CONTRACTL { THROWS; GC_TRIGGERS; - MODE_ANY; - PRECONDITION(srcTH != NULL); - PRECONDITION(destTH != NULL); + PRECONDITION(!srcTH.IsNull()); + PRECONDITION(!destTH.IsNull()); } CONTRACTL_END; @@ -190,7 +199,7 @@ extern "C" int QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH) BEGIN_QCALL; - ret = ArrayNative::CanAssignArrayType(TypeHandle::FromPtr(srcTH), TypeHandle::FromPtr(destTH)); + ret = CanAssignArrayType(TypeHandle::FromPtr(srcTH), TypeHandle::FromPtr(destTH)); END_QCALL; diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 94eaca51ab51e..aeb264a9b28ce 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -39,19 +39,6 @@ class ArrayNative // This method will acquire data to create a span from a TypeHandle // to a field. static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); - - // Return values for CanAssignArrayType - enum AssignArrayEnum - { - AssignWrongType, - AssignMustCast, - AssignBoxValueClassOrPrimitive, - AssignUnboxValueClass, - AssignPrimitiveWiden, - }; - - // The following functions are all helpers for ArrayCopy - static AssignArrayEnum CanAssignArrayType(const TypeHandle pSrc, const TypeHandle pDest); }; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); From 4f442b1a76e821c838117434ee1701eb99e24a87 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 27 Jan 2024 00:30:32 +0800 Subject: [PATCH 25/25] Update src/coreclr/classlibnative/bcltype/arraynative.cpp Co-authored-by: Aaron Robinson --- src/coreclr/classlibnative/bcltype/arraynative.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 079416d6b81b9..32bc52b96f1a1 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -125,7 +125,7 @@ enum AssignArrayEnum }; // Returns an enum saying whether you can copy an array of srcType into destType. -AssignArrayEnum CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) +static AssignArrayEnum CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) { CONTRACTL {