Skip to content

Commit 4ce4f51

Browse files
committed
Push back changes around IsSimpleCopy and CanAssignArrayType
1 parent 5465a97 commit 4ce4f51

File tree

8 files changed

+197
-143
lines changed

8 files changed

+197
-143
lines changed

src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs

Lines changed: 24 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de
6262
if ((uint)(destinationIndex + length) > destinationArray.NativeLength)
6363
throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray));
6464

65-
ArrayAssignType assignType = ArrayAssignType.WrongType;
66-
67-
if (sourceArray.GetType() == destinationArray.GetType()
68-
|| (assignType = CanAssignArrayType(sourceArray, destinationArray)) == ArrayAssignType.SimpleCopy)
65+
if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray))
6966
{
7067
MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
7168

@@ -89,57 +86,44 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de
8986
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy);
9087

9188
// Rare
92-
CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length, assignType);
89+
CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length);
9390
}
9491

95-
private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType)
96-
{
97-
Debug.Assert(elementType.IsPrimitiveType());
98-
99-
// Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable
100-
// Enums with interchangeable underlying types are interchangeable
101-
// BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2
102-
switch (elementType)
103-
{
104-
case CorElementType.ELEMENT_TYPE_U1:
105-
case CorElementType.ELEMENT_TYPE_U2:
106-
case CorElementType.ELEMENT_TYPE_U4:
107-
case CorElementType.ELEMENT_TYPE_U8:
108-
case CorElementType.ELEMENT_TYPE_U:
109-
return elementType - 1; // normalize to signed type
110-
default:
111-
return elementType;
112-
}
113-
}
92+
[MethodImpl(MethodImplOptions.InternalCall)]
93+
private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray);
11494

11595
// Reliability-wise, this method will either possibly corrupt your
11696
// instance & might fail when called from within a CER, or if the
11797
// reliable flag is true, it will either always succeed or always
11898
// throw an exception with no side effects.
119-
private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, ArrayAssignType assignType)
99+
private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
120100
{
121101
Debug.Assert(sourceArray.Rank == destinationArray.Rank);
122102

123-
if (assignType == ArrayAssignType.WrongType)
103+
void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType;
104+
void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType;
105+
AssignArrayEnum r = CanAssignArrayType(srcTH, destTH);
106+
107+
if (r == AssignArrayEnum.AssignWrongType)
124108
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
125109

126110
if (length > 0)
127111
{
128-
switch (assignType)
112+
switch (r)
129113
{
130-
case ArrayAssignType.UnboxValueClass:
114+
case AssignArrayEnum.AssignUnboxValueClass:
131115
CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length);
132116
break;
133117

134-
case ArrayAssignType.BoxValueClassOrPrimitive:
118+
case AssignArrayEnum.AssignBoxValueClassOrPrimitive:
135119
CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length);
136120
break;
137121

138-
case ArrayAssignType.MustCast:
122+
case AssignArrayEnum.AssignMustCast:
139123
CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length);
140124
break;
141125

142-
case ArrayAssignType.PrimitiveWiden:
126+
case AssignArrayEnum.AssignPrimitiveWiden:
143127
CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length);
144128
break;
145129

@@ -150,76 +134,18 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de
150134
}
151135
}
152136

153-
private enum ArrayAssignType
137+
// Must match the definition in arraynative.cpp
138+
private enum AssignArrayEnum
154139
{
155-
SimpleCopy,
156-
WrongType,
157-
MustCast,
158-
BoxValueClassOrPrimitive,
159-
UnboxValueClass,
160-
PrimitiveWiden,
140+
AssignWrongType,
141+
AssignMustCast,
142+
AssignBoxValueClassOrPrimitive,
143+
AssignUnboxValueClass,
144+
AssignPrimitiveWiden,
161145
}
162146

163-
private static unsafe ArrayAssignType CanAssignArrayType(Array sourceArray, Array destinationArray)
164-
{
165-
TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle();
166-
TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle();
167-
168-
if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions
169-
return ArrayAssignType.SimpleCopy;
170-
171-
// Value class boxing
172-
if (srcTH.IsValueType && !destTH.IsValueType)
173-
{
174-
if (srcTH.CanCastTo(destTH))
175-
return ArrayAssignType.BoxValueClassOrPrimitive;
176-
else
177-
return ArrayAssignType.WrongType;
178-
}
179-
180-
// Value class unboxing.
181-
if (!srcTH.IsValueType && destTH.IsValueType)
182-
{
183-
if (srcTH.CanCastTo(destTH))
184-
return ArrayAssignType.UnboxValueClass;
185-
else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V.
186-
return ArrayAssignType.UnboxValueClass;
187-
else
188-
return ArrayAssignType.WrongType;
189-
}
190-
191-
CorElementType srcElType = srcTH.GetVerifierCorElementType();
192-
CorElementType destElType = destTH.GetVerifierCorElementType();
193-
194-
// Copying primitives from one type to another
195-
if (srcElType.IsPrimitiveType() && destElType.IsPrimitiveType())
196-
{
197-
if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType))
198-
return ArrayAssignType.SimpleCopy;
199-
else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType))
200-
return ArrayAssignType.PrimitiveWiden;
201-
else
202-
return ArrayAssignType.WrongType;
203-
}
204-
205-
// src Object extends dest
206-
if (srcTH.CanCastTo(destTH))
207-
return ArrayAssignType.SimpleCopy;
208-
209-
// dest Object extends src
210-
if (destTH.CanCastTo(srcTH))
211-
return ArrayAssignType.MustCast;
212-
213-
// class X extends/implements src and implements dest.
214-
if (destTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE)
215-
return ArrayAssignType.MustCast;
216-
217-
// class X implements src and extends/implements dest
218-
if (srcTH.IsInterface && srcElType != CorElementType.ELEMENT_TYPE_VALUETYPE)
219-
return ArrayAssignType.MustCast;
220-
221-
return ArrayAssignType.WrongType;
222-
}
147+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")]
148+
private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH);
223149

224150
// Unboxes from an Object[] into a value class or primitive array.
225151
private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -862,34 +862,6 @@ public static TypeHandle TypeHandleOf<T>()
862862

863863
public static bool AreSameType(TypeHandle left, TypeHandle right) => left.m_asTAddr == right.m_asTAddr;
864864

865-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
866-
public bool CanCastTo(TypeHandle destTH)
867-
{
868-
CastResult result = CastCache.TryGet(CastHelpers.s_table!, (nuint)m_asTAddr, (nuint)destTH.m_asTAddr);
869-
870-
if (result != CastResult.MaybeCast)
871-
return result == CastResult.CanCast;
872-
873-
return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr);
874-
}
875-
876-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeHandle_CanCastTo")]
877-
[return: MarshalAs(UnmanagedType.Bool)]
878-
private static partial bool CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd);
879-
880-
public bool IsValueType
881-
{
882-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
883-
get
884-
{
885-
return IsTypeDesc
886-
? AsTypeDesc()->GetInternalCorElementType() == CorElementType.ELEMENT_TYPE_VALUETYPE
887-
: AsMethodTable()->IsValueType;
888-
}
889-
}
890-
891-
public bool IsInterface => !IsTypeDesc && AsMethodTable()->IsInterface;
892-
893865
public CorElementType GetVerifierCorElementType() => IsTypeDesc
894866
? AsTypeDesc()->GetInternalCorElementType()
895867
: AsMethodTable()->GetVerifierCorElementType();

src/coreclr/classlibnative/bcltype/arraynative.cpp

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,165 @@ extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHand
3636
return ctorEntrypoint;
3737
}
3838

39+
// Returns whether you can directly copy an array of srcType into destType.
40+
FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst)
41+
{
42+
FCALL_CONTRACT;
43+
44+
_ASSERTE(pSrc != NULL);
45+
_ASSERTE(pDst != NULL);
46+
47+
// This case is expected to be handled by the fast path
48+
_ASSERTE(pSrc->GetMethodTable() != pDst->GetMethodTable());
49+
50+
TypeHandle srcTH = pSrc->GetMethodTable()->GetArrayElementTypeHandle();
51+
TypeHandle destTH = pDst->GetMethodTable()->GetArrayElementTypeHandle();
52+
if (srcTH == destTH) // This check kicks for different array kind or dimensions
53+
FC_RETURN_BOOL(true);
54+
55+
if (srcTH.IsValueType())
56+
{
57+
// Value class boxing
58+
if (!destTH.IsValueType())
59+
FC_RETURN_BOOL(false);
60+
61+
const CorElementType srcElType = srcTH.GetVerifierCorElementType();
62+
const CorElementType destElType = destTH.GetVerifierCorElementType();
63+
_ASSERTE(srcElType < ELEMENT_TYPE_MAX);
64+
_ASSERTE(destElType < ELEMENT_TYPE_MAX);
65+
66+
// Copying primitives from one type to another
67+
if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
68+
{
69+
if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType))
70+
FC_RETURN_BOOL(true);
71+
}
72+
}
73+
else
74+
{
75+
// Value class unboxing
76+
if (destTH.IsValueType())
77+
FC_RETURN_BOOL(false);
78+
}
79+
80+
TypeHandle::CastResult r = srcTH.CanCastToCached(destTH);
81+
if (r != TypeHandle::MaybeCast)
82+
{
83+
FC_RETURN_BOOL(r);
84+
}
85+
86+
struct
87+
{
88+
OBJECTREF src;
89+
OBJECTREF dst;
90+
} gc;
91+
92+
gc.src = ObjectToOBJECTREF(pSrc);
93+
gc.dst = ObjectToOBJECTREF(pDst);
94+
95+
BOOL iRetVal = FALSE;
96+
97+
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
98+
iRetVal = srcTH.CanCastTo(destTH);
99+
HELPER_METHOD_FRAME_END();
100+
101+
FC_RETURN_BOOL(iRetVal);
102+
}
103+
FCIMPLEND
104+
105+
106+
// Return values for CanAssignArrayType
107+
enum AssignArrayEnum
108+
{
109+
AssignWrongType,
110+
AssignMustCast,
111+
AssignBoxValueClassOrPrimitive,
112+
AssignUnboxValueClass,
113+
AssignPrimitiveWiden,
114+
};
115+
116+
// Returns an enum saying whether you can copy an array of srcType into destType.
117+
static AssignArrayEnum CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH)
118+
{
119+
CONTRACTL
120+
{
121+
THROWS;
122+
GC_TRIGGERS;
123+
PRECONDITION(!srcTH.IsNull());
124+
PRECONDITION(!destTH.IsNull());
125+
}
126+
CONTRACTL_END;
127+
128+
_ASSERTE(srcTH != destTH); // Handled by fast path
129+
130+
// Value class boxing
131+
if (srcTH.IsValueType() && !destTH.IsValueType())
132+
{
133+
if (srcTH.CanCastTo(destTH))
134+
return AssignBoxValueClassOrPrimitive;
135+
else
136+
return AssignWrongType;
137+
}
138+
139+
// Value class unboxing.
140+
if (!srcTH.IsValueType() && destTH.IsValueType())
141+
{
142+
if (srcTH.CanCastTo(destTH))
143+
return AssignUnboxValueClass;
144+
else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V.
145+
return AssignUnboxValueClass;
146+
else
147+
return AssignWrongType;
148+
}
149+
150+
const CorElementType srcElType = srcTH.GetVerifierCorElementType();
151+
const CorElementType destElType = destTH.GetVerifierCorElementType();
152+
_ASSERTE(srcElType < ELEMENT_TYPE_MAX);
153+
_ASSERTE(destElType < ELEMENT_TYPE_MAX);
154+
155+
// Copying primitives from one type to another
156+
if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
157+
{
158+
_ASSERTE(srcElType != destElType); // Handled by fast path
159+
if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType))
160+
return AssignPrimitiveWiden;
161+
else
162+
return AssignWrongType;
163+
}
164+
165+
// dest Object extends src
166+
_ASSERTE(!srcTH.CanCastTo(destTH)); // Handled by fast path
167+
168+
// src Object extends dest
169+
if (destTH.CanCastTo(srcTH))
170+
return AssignMustCast;
171+
172+
// class X extends/implements src and implements dest.
173+
if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE)
174+
return AssignMustCast;
175+
176+
// class X implements src and extends/implements dest
177+
if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE)
178+
return AssignMustCast;
179+
180+
return AssignWrongType;
181+
}
182+
183+
extern "C" int QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH)
184+
{
185+
QCALL_CONTRACT;
186+
187+
INT32 ret = 0;
188+
189+
BEGIN_QCALL;
190+
191+
ret = CanAssignArrayType(TypeHandle::FromPtr(srcTH), TypeHandle::FromPtr(destTH));
192+
193+
END_QCALL;
194+
195+
return ret;
196+
}
197+
39198

40199
//
41200
// 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

src/coreclr/classlibnative/bcltype/arraynative.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,17 @@
1313
#ifndef _ARRAYNATIVE_H_
1414
#define _ARRAYNATIVE_H_
1515

16+
#include "fcall.h"
1617
#include "qcall.h"
1718

19+
class ArrayNative
20+
{
21+
public:
22+
static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst);
23+
};
24+
1825
extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray);
1926
extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd);
27+
extern "C" INT32 QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH);
2028

2129
#endif // _ARRAYNATIVE_H_

0 commit comments

Comments
 (0)