Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize 'ConvertOrWidenPrimitivesEnumsAndPointersIfPossible' #101858

Merged
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -128,121 +128,312 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje
}

EETypeElementType dstElementType = dstEEType->ElementType;
if (!CanPrimitiveWiden(dstElementType, srcEEType->ElementType))
{
dstObject = null;
return CreateChangeTypeArgumentException(srcEEType, dstEEType);
}
EETypeElementType srcElementType = srcEEType->ElementType;

bool boolValue;
char charValue;
sbyte sbyteValue;
byte byteValue;
short shortValue;
ushort ushortValue;
int intValue;
uint uintValue;
long longValue;
ulong ulongValue;
float floatValue;
double doubleValue;

void* rawDstValue;
ref byte rawSrcValue = ref RuntimeHelpers.GetRawData(srcObject);

switch (dstElementType)
{
case EETypeElementType.Boolean:
dstObject = Convert.ToBoolean(srcObject);
switch (srcElementType)
{
case EETypeElementType.Boolean:
boolValue = Unsafe.As<byte, bool>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &boolValue;
break;

case EETypeElementType.Char:
char charValue = Convert.ToChar(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, charValue) : charValue;
switch (srcElementType)
{
case EETypeElementType.Char:
charValue = Unsafe.As<byte, char>(ref rawSrcValue);
break;
case EETypeElementType.Byte:
charValue = (char)rawSrcValue;
break;
case EETypeElementType.UInt16:
charValue = (char)Unsafe.As<byte, ushort>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &charValue;
break;

case EETypeElementType.SByte:
sbyte sbyteValue = Convert.ToSByte(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, sbyteValue) : sbyteValue;
switch (srcElementType)
{
case EETypeElementType.SByte:
sbyteValue = Unsafe.As<byte, sbyte>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &sbyteValue;
break;

case EETypeElementType.Int16:
short shortValue = Convert.ToInt16(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, shortValue) : shortValue;
case EETypeElementType.Byte:
jkotas marked this conversation as resolved.
Show resolved Hide resolved
switch (srcElementType)
{
case EETypeElementType.Byte:
byteValue = rawSrcValue;
break;
default:
goto Failure;
}
rawDstValue = &byteValue;
break;

case EETypeElementType.Int32:
int intValue = Convert.ToInt32(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, intValue) : intValue;
case EETypeElementType.Int16:
switch (srcElementType)
{
case EETypeElementType.SByte:
shortValue = (short)Unsafe.As<byte, sbyte>(ref rawSrcValue);
break;
case EETypeElementType.Byte:
shortValue = (short)rawSrcValue;
break;
case EETypeElementType.Int16:
shortValue = Unsafe.As<byte, short>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &shortValue;
break;

case EETypeElementType.Int64:
long longValue = Convert.ToInt64(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, longValue) : longValue;
case EETypeElementType.UInt16:
Sergio0694 marked this conversation as resolved.
Show resolved Hide resolved
switch (srcElementType)
{
case EETypeElementType.Char:
ushortValue = (ushort)Unsafe.As<byte, char>(ref rawSrcValue);
break;
case EETypeElementType.Byte:
ushortValue = (ushort)rawSrcValue;
break;
case EETypeElementType.UInt16:
Sergio0694 marked this conversation as resolved.
Show resolved Hide resolved
ushortValue = Unsafe.As<byte, ushort>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &ushortValue;
break;

case EETypeElementType.Byte:
byte byteValue = Convert.ToByte(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, byteValue) : byteValue;
case EETypeElementType.Int32:
switch (srcElementType)
{
case EETypeElementType.Char:
intValue = (int)Unsafe.As<byte, char>(ref rawSrcValue);
break;
case EETypeElementType.SByte:
intValue = (int)Unsafe.As<byte, sbyte>(ref rawSrcValue);
break;
case EETypeElementType.Byte:
intValue = (int)rawSrcValue;
break;
case EETypeElementType.Int16:
intValue = (int)Unsafe.As<byte, short>(ref rawSrcValue);
break;
case EETypeElementType.UInt16:
intValue = (int)Unsafe.As<byte, ushort>(ref rawSrcValue);
break;
case EETypeElementType.Int32:
intValue = Unsafe.As<byte, int>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &intValue;
break;

case EETypeElementType.UInt16:
ushort ushortValue = Convert.ToUInt16(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, ushortValue) : ushortValue;
case EETypeElementType.UInt32:
switch (srcElementType)
{
case EETypeElementType.Char:
uintValue = (uint)Unsafe.As<byte, char>(ref rawSrcValue);
break;
case EETypeElementType.Byte:
uintValue = (uint)rawSrcValue;
break;
case EETypeElementType.UInt16:
uintValue = (uint)Unsafe.As<byte, ushort>(ref rawSrcValue);
break;
case EETypeElementType.UInt32:
uintValue = Unsafe.As<byte, uint>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &uintValue;
break;

case EETypeElementType.UInt32:
uint uintValue = Convert.ToUInt32(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, uintValue) : uintValue;
case EETypeElementType.Int64:
switch (srcElementType)
{
case EETypeElementType.Char:
longValue = (long)Unsafe.As<byte, char>(ref rawSrcValue);
break;
case EETypeElementType.SByte:
longValue = (long)Unsafe.As<byte, sbyte>(ref rawSrcValue);
break;
case EETypeElementType.Byte:
longValue = (long)rawSrcValue;
break;
case EETypeElementType.Int16:
longValue = (long)Unsafe.As<byte, short>(ref rawSrcValue);
break;
case EETypeElementType.UInt16:
longValue = (long)Unsafe.As<byte, ushort>(ref rawSrcValue);
break;
case EETypeElementType.Int32:
longValue = (long)Unsafe.As<byte, int>(ref rawSrcValue);
break;
case EETypeElementType.UInt32:
longValue = (long)Unsafe.As<byte, uint>(ref rawSrcValue);
break;
case EETypeElementType.Int64:
longValue = Unsafe.As<byte, long>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &longValue;
break;

case EETypeElementType.UInt64:
ulong ulongValue = Convert.ToUInt64(srcObject);
dstObject = dstEEType->IsEnum ? Enum.ToObject(dstEEType, (long)ulongValue) : ulongValue;
switch (srcElementType)
{
case EETypeElementType.Char:
ulongValue = (ulong)Unsafe.As<byte, char>(ref rawSrcValue);
break;
case EETypeElementType.Byte:
ulongValue = (ulong)rawSrcValue;
break;
case EETypeElementType.UInt16:
ulongValue = (ulong)Unsafe.As<byte, ushort>(ref rawSrcValue);
break;
case EETypeElementType.UInt32:
ulongValue = (ulong)Unsafe.As<byte, uint>(ref rawSrcValue);
break;
case EETypeElementType.UInt64:
ulongValue = Unsafe.As<byte, ulong>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &ulongValue;
break;

case EETypeElementType.Single:
if (new EETypePtr(srcEEType).CorElementType == CorElementType.ELEMENT_TYPE_CHAR)
{
dstObject = (float)(char)srcObject;
}
else
switch (srcElementType)
{
dstObject = Convert.ToSingle(srcObject);
case EETypeElementType.Char:
floatValue = (float)Unsafe.As<byte, char>(ref rawSrcValue);
break;
case EETypeElementType.SByte:
floatValue = (float)Unsafe.As<byte, sbyte>(ref rawSrcValue);
break;
case EETypeElementType.Byte:
floatValue = (float)rawSrcValue;
break;
case EETypeElementType.Int16:
floatValue = (float)Unsafe.As<byte, short>(ref rawSrcValue);
break;
case EETypeElementType.UInt16:
floatValue = (float)Unsafe.As<byte, ushort>(ref rawSrcValue);
break;
case EETypeElementType.Int32:
floatValue = (float)Unsafe.As<byte, int>(ref rawSrcValue);
break;
case EETypeElementType.UInt32:
Sergio0694 marked this conversation as resolved.
Show resolved Hide resolved
floatValue = (float)Unsafe.As<byte, uint>(ref rawSrcValue);
break;
case EETypeElementType.Int64:
floatValue = (float)Unsafe.As<byte, long>(ref rawSrcValue);
break;
case EETypeElementType.UInt64:
floatValue = (float)Unsafe.As<byte, ulong>(ref rawSrcValue);
break;
case EETypeElementType.Single:
floatValue = Unsafe.As<byte, float>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &floatValue;
break;

case EETypeElementType.Double:
if (new EETypePtr(srcEEType).CorElementType == CorElementType.ELEMENT_TYPE_CHAR)
switch (srcElementType)
{
dstObject = (double)(char)srcObject;
}
else
{
dstObject = Convert.ToDouble(srcObject);
case EETypeElementType.Char:
doubleValue = (double)Unsafe.As<byte, char>(ref rawSrcValue);
break;
case EETypeElementType.SByte:
doubleValue = (double)Unsafe.As<byte, sbyte>(ref rawSrcValue);
break;
case EETypeElementType.Byte:
doubleValue = (double)rawSrcValue;
break;
case EETypeElementType.Int16:
doubleValue = (double)Unsafe.As<byte, short>(ref rawSrcValue);
break;
case EETypeElementType.UInt16:
doubleValue = (double)Unsafe.As<byte, ushort>(ref rawSrcValue);
break;
case EETypeElementType.Int32:
doubleValue = (double)Unsafe.As<byte, int>(ref rawSrcValue);
break;
case EETypeElementType.UInt32:
doubleValue = (double)Unsafe.As<byte, uint>(ref rawSrcValue);
break;
case EETypeElementType.Int64:
doubleValue = (double)Unsafe.As<byte, long>(ref rawSrcValue);
break;
case EETypeElementType.UInt64:
doubleValue = (double)Unsafe.As<byte, ulong>(ref rawSrcValue);
break;
case EETypeElementType.Single:
doubleValue = (double)Unsafe.As<byte, float>(ref rawSrcValue);
break;
case EETypeElementType.Double:
doubleValue = Unsafe.As<byte, double>(ref rawSrcValue);
break;
default:
goto Failure;
}
rawDstValue = &doubleValue;
break;

default:
Debug.Fail("Unexpected CorElementType: " + dstElementType + ": Not a valid widening target.");
dstObject = null;
return CreateChangeTypeException(srcEEType, dstEEType, semantics);
goto Failure;
}

Debug.Assert(dstObject.GetMethodTable() == dstEEType);
dstObject = RuntimeImports.RhBox(dstEEType, ref *(byte*)rawDstValue);
return null;
}

private static bool CanPrimitiveWiden(EETypeElementType destType, EETypeElementType srcType)
{
Debug.Assert(destType is < EETypeElementType.ValueType and >= EETypeElementType.Boolean);
Debug.Assert(srcType is < EETypeElementType.ValueType and >= EETypeElementType.Boolean);

ReadOnlySpan<ushort> primitiveAttributes = [
0x0000, // Unknown
0x0000, // Void
0x0004, // Boolean (W = BOOL)
0xCf88, // Char (W = U2, CHAR, I4, U4, I8, U8, R4, R8)
0xC550, // SByte (W = I1, I2, I4, I8, R4, R8)
0xCFE8, // Byte (W = CHAR, U1, I2, U2, I4, U4, I8, U8, R4, R8)
0xC540, // Int16 (W = I2, I4, I8, R4, R8)
0xCF88, // UInt16 (W = U2, CHAR, I4, U4, I8, U8, R4, R8)
0xC500, // Int32 (W = I4, I8, R4, R8)
0xCE00, // UInt32 (W = U4, I8, R4, R8)
0xC400, // Int64 (W = I8, R4, R8)
0xC800, // UInt64 (W = U8, R4, R8)
0x0000, // IntPtr
0x0000, // UIntPtr
0xC000, // Single (W = R4, R8)
0x8000, // Double (W = R8)
];

ushort mask = (ushort)(1 << (byte)destType);
return (primitiveAttributes[(int)srcType & 0xF] & mask) != 0;
Failure:
dstObject = null;
return CreateChangeTypeArgumentException(srcEEType, dstEEType);
}

private static Exception ConvertPointerIfPossible(object srcObject, MethodTable* dstEEType, CheckArgumentSemantics semantics, out object dstPtr)
Expand Down
Loading