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 4d793d17aba18f..5a66a0dd45b81c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -62,7 +62,10 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de if ((uint)(destinationIndex + length) > destinationArray.NativeLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); - if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray)) + ArrayAssignType assignType = ArrayAssignType.WrongType; + + if (sourceArray.GetType() == destinationArray.GetType() + || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == ArrayAssignType.SimpleCopy) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -86,44 +89,57 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); // Rare - CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length, assignType); } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray); + private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType) + { + Debug.Assert(elementType.IsPrimitiveType()); + + // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable + // Enums with interchangeable underlying types are interchangeable + // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2 + switch (elementType) + { + case CorElementType.ELEMENT_TYPE_U1: + case CorElementType.ELEMENT_TYPE_U2: + case CorElementType.ELEMENT_TYPE_U4: + case CorElementType.ELEMENT_TYPE_U8: + case CorElementType.ELEMENT_TYPE_U: + return elementType - 1; // normalize to signed type + default: + return elementType; + } + } // Reliability-wise, this method will either possibly corrupt your // 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. - private static unsafe 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, ArrayAssignType assignType) { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType; - void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; - AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); - - if (r == AssignArrayEnum.AssignWrongType) + if (assignType == ArrayAssignType.WrongType) ThrowHelper.ThrowArrayTypeMismatchException_CantAssignType(); if (length > 0) { - switch (r) + switch (assignType) { - case AssignArrayEnum.AssignUnboxValueClass: + case ArrayAssignType.UnboxValueClass: CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignBoxValueClassOrPrimitive: + case ArrayAssignType.BoxValueClassOrPrimitive: CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignMustCast: + case ArrayAssignType.MustCast: CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignPrimitiveWiden: + case ArrayAssignType.PrimitiveWiden: CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; @@ -134,18 +150,76 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de } } - // Must match the definition in arraynative.cpp - private enum AssignArrayEnum + private enum ArrayAssignType { - AssignWrongType, - AssignMustCast, - AssignBoxValueClassOrPrimitive, - AssignUnboxValueClass, - AssignPrimitiveWiden, + SimpleCopy, + WrongType, + MustCast, + BoxValueClassOrPrimitive, + UnboxValueClass, + PrimitiveWiden, } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] - private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); + private static unsafe ArrayAssignType CanAssignArrayType(Array sourceArray, Array destinationArray) + { + TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); + TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); + + if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions + return ArrayAssignType.SimpleCopy; + + // Value class boxing + if (srcTH.IsValueType && !destTH.IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.BoxValueClassOrPrimitive; + else + return ArrayAssignType.WrongType; + } + + // Value class unboxing. + if (!srcTH.IsValueType && destTH.IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.UnboxValueClass; + else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. + return ArrayAssignType.UnboxValueClass; + else + return ArrayAssignType.WrongType; + } + + CorElementType srcElType = srcTH.GetVerifierCorElementType(); + CorElementType destElType = destTH.GetVerifierCorElementType(); + + // Copying primitives from one type to another + if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType()) + { + if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) + return ArrayAssignType.SimpleCopy; + else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) + return ArrayAssignType.PrimitiveWiden; + else + return ArrayAssignType.WrongType; + } + + // src Object extends dest + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.SimpleCopy; + + // dest Object extends src + if (destTH.CanCastTo(srcTH)) + return ArrayAssignType.MustCast; + + // class X extends/implements src and implements dest. + if (destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + return ArrayAssignType.MustCast; + + // class X implements src and extends/implements dest + if (srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE) + return ArrayAssignType.MustCast; + + return ArrayAssignType.WrongType; + } // Unboxes from an Object[] into a value class or primitive array. private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) @@ -249,147 +323,152 @@ private static unsafe void CopyImplPrimitiveWiden(Array sourceArray, int sourceI ref byte srcElement = ref Unsafe.Add(ref srcData, (nuint)i * srcElSize); ref byte destElement = ref Unsafe.Add(ref data, (nuint)i * destElSize); - switch (srcElType) - { - 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; + PrimitiveWiden(ref srcElement, ref destElement, srcElType, destElType); + } + } - default: - Debug.Fail("Fell through outer switch in PrimitiveWiden! Unknown primitive type for source array!"); break; - } + private static void PrimitiveWiden(ref byte srcElement, ref byte destElement, CorElementType srcElType, CorElementType destElType) + { + switch (srcElType) + { + 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; } } 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 0c640dd1c2f0a4..14c8a959cda5b0 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 @@ -29,7 +29,7 @@ internal static unsafe class CastHelpers // Unlike the IsInstanceOfInterface and IsInstanceOfClass functions, // this test must deal with all kinds of type tests [DebuggerHidden] - private static object? IsInstanceOfAny(void* toTypeHnd, object? obj) + internal static object? IsInstanceOfAny(void* toTypeHnd, object? obj) { if (obj != null) { diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index b569143bbaa515..1823ffa2c07ace 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1809,6 +1809,21 @@ extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, Metho return bResult; } +extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd) +{ + QCALL_CONTRACT; + + BOOL ret = false; + + BEGIN_QCALL; + + ret = TypeHandle::FromPtr(fromTypeHnd).CanCastTo(TypeHandle::FromPtr(toTypeHnd)); + + END_QCALL; + + return ret; +} + static MethodTable * g_pStreamMT; static WORD g_slotBeginRead, g_slotEndRead; static WORD g_slotBeginWrite, g_slotEndWrite; diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 74f0c7967e7450..fb1038d7f3f5db 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -249,6 +249,7 @@ class MethodTableNative { extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb); extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable* mt); +extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo(void* fromTypeHnd, void* toTypeHnd); extern "C" INT32 QCALLTYPE ValueType_GetHashCodeStrategy(MethodTable* mt, QCall::ObjectHandleOnStack objHandle, UINT32* fieldOffset, UINT32* fieldSize, MethodTable** fieldMT); class StreamNative { diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index e038db62831d93..9cd32a2d892593 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -103,6 +103,7 @@ static const Entry s_QCall[] = DllImportEntry(QCall_FreeGCHandleForTypeHandle) DllImportEntry(MethodTable_AreTypesEquivalent) DllImportEntry(MethodTable_CanCompareBitsOrUseFastGetHashCode) + DllImportEntry(TypeHandle_CanCastTo) DllImportEntry(ValueType_GetHashCodeStrategy) DllImportEntry(RuntimeTypeHandle_MakePointer) DllImportEntry(RuntimeTypeHandle_MakeByRef) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 63471d9f919675..3715e6df74a117 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Reflection; using System.Runtime.InteropServices; @@ -109,6 +110,35 @@ internal static bool IsPrimitiveType(this CorElementType et) // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0; + private static ReadOnlySpan PrimitiveWidenTable => + [ + 0x00, // ELEMENT_TYPE_END + 0x00, // ELEMENT_TYPE_VOID + 0x0004, // ELEMENT_TYPE_BOOLEAN + 0x3F88, // ELEMENT_TYPE_CHAR (W = U2, CHAR, I4, U4, I8, U8, R4, R8) (U2 == Char) + 0x3550, // ELEMENT_TYPE_I1 (W = I1, I2, I4, I8, R4, R8) + 0x3FE8, // ELEMENT_TYPE_U1 (W = CHAR, U1, I2, U2, I4, U4, I8, U8, R4, R8) + 0x3540, // ELEMENT_TYPE_I2 (W = I2, I4, I8, R4, R8) + 0x3F88, // ELEMENT_TYPE_U2 (W = U2, CHAR, I4, U4, I8, U8, R4, R8) + 0x3500, // ELEMENT_TYPE_I4 (W = I4, I8, R4, R8) + 0x3E00, // ELEMENT_TYPE_U4 (W = U4, I8, R4, R8) + 0x3400, // ELEMENT_TYPE_I8 (W = I8, R4, R8) + 0x3800, // ELEMENT_TYPE_U8 (W = U8, R4, R8) + 0x3000, // ELEMENT_TYPE_R4 (W = R4, R8) + 0x2000, // ELEMENT_TYPE_R8 (W = R8) + ]; + + internal static bool CanPrimitiveWiden(CorElementType srcET, CorElementType dstET) + { + Debug.Assert(srcET.IsPrimitiveType() && dstET.IsPrimitiveType()); + if ((int)srcET >= PrimitiveWidenTable.Length) + { + // I or U + return srcET == dstET; + } + return (PrimitiveWidenTable[(int)srcET] & (1 << (int)dstET)) != 0; + } + /// Provide a fast way to access constant data stored in a module as a ReadOnlySpan{T} /// A field handle that specifies the location of the data to be referred to by the ReadOnlySpan{T}. The Rva of the field must be aligned on a natural boundary of type T /// A ReadOnlySpan{T} of the data stored in the field