Skip to content

Commit

Permalink
Optimize 'ConvertOrWidenPrimitivesEnumsAndPointersIfPossible' (#101858)
Browse files Browse the repository at this point in the history
* Optimize 'ConvertOrWidenPrimitivesEnumsAndPointersIfPossible'

* Add missing U4 -> U8 widening flag

* Skip type checks when unboxing

* Centralized boxing operations

* Remove unnecessary code to handle enums

* Combine 'Char' cases with 'UInt16' ones

* Deduplicate the implementation between array and reflection

---------

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
  • Loading branch information
Sergio0694 and jkotas authored May 6, 2024
1 parent c5c7f0d commit f501153
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 306 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -526,255 +526,26 @@ private static unsafe void CopyImplPrimitiveTypeWithWidening(Array sourceArray,
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy);
}

fixed (byte* pSrcArray = &MemoryMarshal.GetArrayDataReference(sourceArray), pDstArray = &MemoryMarshal.GetArrayDataReference(destinationArray))
{
byte* srcData = pSrcArray + (nuint)sourceIndex * srcElementSize;
byte* data = pDstArray + (nuint)destinationIndex * destElementSize;
ref byte srcData = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(sourceArray), (nuint)sourceIndex * srcElementSize);
ref byte dstData = ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(destinationArray), (nuint)destinationIndex * destElementSize);

if (sourceElementType == destElementType)
{
// Multidim arrays and enum->int copies can still reach this path.
SpanHelpers.Memmove(ref *data, ref *srcData, (nuint)length * srcElementSize);
return;
}
if (sourceElementType == destElementType)
{
// Multidim arrays and enum->int copies can still reach this path.
SpanHelpers.Memmove(ref dstData, ref srcData, (nuint)length * srcElementSize);
return;
}

ulong dummyElementForZeroLengthCopies = 0;
// If the element types aren't identical and the length is zero, we're still obliged to check the types for widening compatibility.
// We do this by forcing the loop below to copy one dummy element.
if (length == 0)
{
srcData = (byte*)&dummyElementForZeroLengthCopies;
data = (byte*)&dummyElementForZeroLengthCopies;
length = 1;
}
if (!InvokeUtils.CanPrimitiveWiden(destElementType, sourceElementType))
{
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}

for (int i = 0; i < length; i++, srcData += srcElementSize, data += destElementSize)
{
// We pretty much have to do some fancy datatype mangling every time here, for
// converting w/ sign extension and floating point conversions.
switch (sourceElementType)
{
case EETypeElementType.Byte:
{
switch (destElementType)
{
case EETypeElementType.Single:
*(float*)data = *(byte*)srcData;
break;

case EETypeElementType.Double:
*(double*)data = *(byte*)srcData;
break;

case EETypeElementType.Char:
case EETypeElementType.Int16:
case EETypeElementType.UInt16:
*(short*)data = *(byte*)srcData;
break;

case EETypeElementType.Int32:
case EETypeElementType.UInt32:
*(int*)data = *(byte*)srcData;
break;

case EETypeElementType.Int64:
case EETypeElementType.UInt64:
*(long*)data = *(byte*)srcData;
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
break;
}

case EETypeElementType.SByte:
switch (destElementType)
{
case EETypeElementType.Int16:
*(short*)data = *(sbyte*)srcData;
break;

case EETypeElementType.Int32:
*(int*)data = *(sbyte*)srcData;
break;

case EETypeElementType.Int64:
*(long*)data = *(sbyte*)srcData;
break;

case EETypeElementType.Single:
*(float*)data = *(sbyte*)srcData;
break;

case EETypeElementType.Double:
*(double*)data = *(sbyte*)srcData;
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
break;

case EETypeElementType.UInt16:
case EETypeElementType.Char:
switch (destElementType)
{
case EETypeElementType.Single:
*(float*)data = *(ushort*)srcData;
break;

case EETypeElementType.Double:
*(double*)data = *(ushort*)srcData;
break;

case EETypeElementType.UInt16:
case EETypeElementType.Char:
*(ushort*)data = *(ushort*)srcData;
break;

case EETypeElementType.Int32:
case EETypeElementType.UInt32:
*(uint*)data = *(ushort*)srcData;
break;

case EETypeElementType.Int64:
case EETypeElementType.UInt64:
*(ulong*)data = *(ushort*)srcData;
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
break;

case EETypeElementType.Int16:
switch (destElementType)
{
case EETypeElementType.Int32:
*(int*)data = *(short*)srcData;
break;

case EETypeElementType.Int64:
*(long*)data = *(short*)srcData;
break;

case EETypeElementType.Single:
*(float*)data = *(short*)srcData;
break;

case EETypeElementType.Double:
*(double*)data = *(short*)srcData;
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
break;

case EETypeElementType.Int32:
switch (destElementType)
{
case EETypeElementType.Int64:
*(long*)data = *(int*)srcData;
break;

case EETypeElementType.Single:
*(float*)data = (float)*(int*)srcData;
break;

case EETypeElementType.Double:
*(double*)data = *(int*)srcData;
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
break;

case EETypeElementType.UInt32:
switch (destElementType)
{
case EETypeElementType.Int64:
case EETypeElementType.UInt64:
*(long*)data = *(uint*)srcData;
break;

case EETypeElementType.Single:
*(float*)data = (float)*(uint*)srcData;
break;

case EETypeElementType.Double:
*(double*)data = *(uint*)srcData;
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
break;


case EETypeElementType.Int64:
switch (destElementType)
{
case EETypeElementType.Single:
*(float*)data = (float)*(long*)srcData;
break;

case EETypeElementType.Double:
*(double*)data = (double)*(long*)srcData;
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
break;

case EETypeElementType.UInt64:
switch (destElementType)
{
case EETypeElementType.Single:

//*(float*) data = (float) *(Ulong*)srcData;
long srcValToFloat = *(long*)srcData;
float f = (float)srcValToFloat;
if (srcValToFloat < 0)
f += 4294967296.0f * 4294967296.0f; // This is 2^64

*(float*)data = f;
break;

case EETypeElementType.Double:
//*(double*) data = (double) *(Ulong*)srcData;
long srcValToDouble = *(long*)srcData;
double d = (double)srcValToDouble;
if (srcValToDouble < 0)
d += 4294967296.0 * 4294967296.0; // This is 2^64

*(double*)data = d;
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
break;

case EETypeElementType.Single:
switch (destElementType)
{
case EETypeElementType.Double:
*(double*)data = *(float*)srcData;
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
break;

default:
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType);
}
}
for (int i = 0; i < length; i++)
{
InvokeUtils.PrimitiveWiden(destElementType, sourceElementType, ref dstData, ref srcData);
srcData = ref Unsafe.AddByteOffset(ref srcData, srcElementSize);
dstData = ref Unsafe.AddByteOffset(ref dstData, destElementSize);
}
}

Expand Down
Loading

0 comments on commit f501153

Please sign in to comment.