Skip to content

NativeAOT: Cover more opcodes in type preinitializer #112073

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

Merged
merged 11 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from 4 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 @@ -835,6 +835,8 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
case ILOpcode.conv_u2:
case ILOpcode.conv_u4:
case ILOpcode.conv_u8:
case ILOpcode.conv_r4:
case ILOpcode.conv_r8:
{
StackEntry popped = stack.Pop();
if (popped.ValueKind.WithNormalizedNativeInt(context) == StackValueKind.Int32)
Expand Down Expand Up @@ -874,6 +876,12 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
case ILOpcode.conv_u8:
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((uint)val));
break;
case ILOpcode.conv_r4:
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble((float)val));
break;
case ILOpcode.conv_r8:
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble((double)val));
break;
default:
return Status.Fail(methodIL.OwningMethod, opcode);
}
Expand Down Expand Up @@ -912,6 +920,12 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
case ILOpcode.conv_u8:
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(val));
break;
case ILOpcode.conv_r4:
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble((float)val));
break;
case ILOpcode.conv_r8:
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble((double)val));
break;
default:
return Status.Fail(methodIL.OwningMethod, opcode);
}
Expand All @@ -921,9 +935,44 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
double val = popped.Value.AsDouble();
switch (opcode)
{
case ILOpcode.conv_i:
stack.Push(StackValueKind.NativeInt,
context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64((long)val) : ValueTypeValue.FromInt32((int)val));
break;
case ILOpcode.conv_u:
stack.Push(StackValueKind.NativeInt,
context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64((long)(ulong)val) : ValueTypeValue.FromInt32((int)(uint)val));
break;
case ILOpcode.conv_i1:
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((sbyte)val));
break;
case ILOpcode.conv_i2:
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((short)val));
break;
case ILOpcode.conv_i4:
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((int)val));
break;
case ILOpcode.conv_i8:
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)val));
break;
case ILOpcode.conv_u1:
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((byte)val));
break;
case ILOpcode.conv_u2:
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((ushort)val));
break;
case ILOpcode.conv_u4:
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((int)(uint)val));
break;
case ILOpcode.conv_u8:
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)(ulong)val));
break;
case ILOpcode.conv_r4:
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble((float)val));
break;
case ILOpcode.conv_r8:
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(val));
break;
default:
return Status.Fail(methodIL.OwningMethod, opcode);
}
Expand Down Expand Up @@ -1390,6 +1439,10 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
StackEntry value = stack.Pop();
if (value.ValueKind == StackValueKind.Int32)
stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(-value.Value.AsInt32()));
else if (value.ValueKind == StackValueKind.Int64)
stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(-value.Value.AsInt64()));
else if (value.ValueKind == StackValueKind.Float)
stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(-value.Value.AsDouble()));
else
return Status.Fail(methodIL.OwningMethod, opcode);
}
Expand Down
249 changes: 249 additions & 0 deletions src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private static int Main()
TestIndirectLoads.Run();
TestInitBlock.Run();
TestDataflow.Run();
TestConversions.Run();
#else
Console.WriteLine("Preinitialization is disabled in multimodule builds for now. Skipping test.");
#endif
Expand Down Expand Up @@ -1429,6 +1430,236 @@ public static void Run()
}
}

class TestConversions
{
private static int GetInt() => -42;
private static long GetLong() => -42;
private static float GetFloat() => -42;
private static double GetDouble() => -42;
private static nint GetNativeInt() => -42;

class IntConversions
{
internal static byte s_byte;
internal static sbyte s_sbyte;
internal static short s_short;
internal static ushort s_ushort;
internal static uint s_uint;
internal static long s_long;
internal static ulong s_ulong;
internal static float s_float;
internal static double s_double;
internal static nint s_nint;
internal static nuint s_nuint;

static IntConversions()
{
s_byte = unchecked((byte)GetInt());
s_sbyte = unchecked((sbyte)GetInt());
s_short = unchecked((short)GetInt());
s_ushort = unchecked((ushort)GetInt());
s_uint = unchecked((uint)GetInt());
s_long = GetInt();
s_ulong = unchecked((ulong)GetInt());
s_float = unchecked((float)GetInt());
s_double = unchecked(GetInt());
s_nint = GetInt();
s_nuint = unchecked((nuint)GetInt());
}
}

class LongConversions
{
internal static byte s_byte;
internal static sbyte s_sbyte;
internal static short s_short;
internal static ushort s_ushort;
internal static int s_int;
internal static uint s_uint;
internal static ulong s_ulong;
internal static float s_float;
internal static double s_double;
internal static nint s_nint;
internal static nuint s_nuint;

static LongConversions()
{
s_byte = unchecked((byte)GetLong());
s_sbyte = unchecked((sbyte)GetLong());
s_short = unchecked((short)GetLong());
s_ushort = unchecked((ushort)GetLong());
s_int = unchecked((int)GetLong());
s_uint = unchecked((uint)GetLong());
s_ulong = unchecked((ulong)GetLong());
s_float = unchecked((float)GetLong());
s_double = unchecked(GetLong());
s_nint = unchecked((nint)GetLong());
s_nuint = unchecked((nuint)GetLong());
}
}

class FloatConversions
{
internal static byte s_byte;
internal static sbyte s_sbyte;
internal static short s_short;
internal static ushort s_ushort;
internal static int s_int;
internal static uint s_uint;
internal static long s_long;
internal static ulong s_ulong;
internal static double s_double;
internal static nint s_nint;
internal static nuint s_nuint;

static FloatConversions()
{
s_byte = (byte)GetFloat();
s_sbyte = (sbyte)GetFloat();
s_short = (short)GetFloat();
s_ushort = (ushort)GetFloat();
s_int = (int)GetFloat();
s_uint = (uint)GetFloat();
s_long = (long)GetFloat();
s_ulong = (ulong)GetFloat();
s_double = GetFloat();
s_nint = (nint)GetFloat();
s_nuint = (nuint)GetFloat();
}
}

class DoubleConversions
{
internal static byte s_byte;
internal static sbyte s_sbyte;
internal static short s_short;
internal static ushort s_ushort;
internal static int s_int;
internal static uint s_uint;
internal static long s_long;
internal static ulong s_ulong;
internal static float s_float;
internal static nint s_nint;
internal static nuint s_nuint;

static DoubleConversions()
{
s_byte = unchecked((byte)GetDouble());
s_sbyte = unchecked((sbyte)GetDouble());
s_short = unchecked((short)GetDouble());
s_ushort = unchecked((ushort)GetDouble());
s_int = unchecked((int)GetDouble());
s_uint = unchecked((uint)GetDouble());
s_long = unchecked((long)GetDouble());
s_ulong = unchecked((ulong)GetDouble());
s_float = unchecked((float)GetDouble());
s_nint = unchecked((nint)GetDouble());
s_nuint = unchecked((nuint)GetDouble());
}
}

class NativeIntConversions
{
internal static byte s_byte;
internal static sbyte s_sbyte;
internal static short s_short;
internal static ushort s_ushort;
internal static int s_int;
internal static uint s_uint;
internal static long s_long;
internal static ulong s_ulong;
internal static float s_float;
internal static double s_double;
internal static decimal s_decimal;
internal static nuint s_nuint;

static NativeIntConversions()
{
s_byte = unchecked((byte)GetNativeInt());
s_sbyte = unchecked((sbyte)GetNativeInt());
s_short = unchecked((short)GetNativeInt());
s_ushort = unchecked((ushort)GetNativeInt());
s_int = unchecked((int)GetNativeInt());
s_uint = unchecked((uint)GetNativeInt());
s_long = unchecked((long)GetNativeInt());
s_ulong = unchecked((ulong)GetNativeInt());
s_float = unchecked((float)GetNativeInt());
s_double = unchecked((double)GetNativeInt());
s_decimal = unchecked((decimal)GetNativeInt());
s_nuint = unchecked((nuint)GetNativeInt());
}
}

public static void Run()
{
Assert.IsPreinitialized(typeof(IntConversions));
Assert.AreEqual(unchecked((byte)GetInt()), IntConversions.s_byte);
Assert.AreEqual(unchecked((sbyte)GetInt()), IntConversions.s_sbyte);
Assert.AreEqual(unchecked((short)GetInt()), IntConversions.s_short);
Assert.AreEqual(unchecked((ushort)GetInt()), IntConversions.s_ushort);
Assert.AreEqual(unchecked((uint)GetInt()), IntConversions.s_uint);
Assert.AreEqual(unchecked((long)GetInt()), IntConversions.s_long);
Assert.AreEqual(unchecked((ulong)GetInt()), IntConversions.s_ulong);
Assert.AreEqual(unchecked((float)GetInt()), IntConversions.s_float);
Assert.AreEqual(unchecked((double)GetInt()), IntConversions.s_double);
Assert.AreEqual(unchecked((nint)GetInt()), IntConversions.s_nint);
Assert.AreEqual(unchecked((nuint)GetInt()), IntConversions.s_nuint);

Assert.IsPreinitialized(typeof(LongConversions));
Assert.AreEqual(unchecked((byte)GetLong()), LongConversions.s_byte);
Assert.AreEqual(unchecked((sbyte)GetLong()), LongConversions.s_sbyte);
Assert.AreEqual(unchecked((short)GetLong()), LongConversions.s_short);
Assert.AreEqual(unchecked((ushort)GetLong()), LongConversions.s_ushort);
Assert.AreEqual(unchecked((int)GetLong()), LongConversions.s_int);
Assert.AreEqual(unchecked((uint)GetLong()), LongConversions.s_uint);
Assert.AreEqual(unchecked((ulong)GetLong()), LongConversions.s_ulong);
Assert.AreEqual(unchecked((float)GetLong()), LongConversions.s_float);
Assert.AreEqual(unchecked((double)GetLong()), LongConversions.s_double);
Assert.AreEqual(unchecked((nint)GetLong()), LongConversions.s_nint);
Assert.AreEqual(unchecked((nuint)GetLong()), LongConversions.s_nuint);

Assert.IsPreinitialized(typeof(FloatConversions));
Assert.AreEqual(unchecked((byte)GetFloat()), FloatConversions.s_byte);
Assert.AreEqual(unchecked((sbyte)GetFloat()), FloatConversions.s_sbyte);
Assert.AreEqual(unchecked((short)GetFloat()), FloatConversions.s_short);
Assert.AreEqual(unchecked((ushort)GetFloat()), FloatConversions.s_ushort);
Assert.AreEqual(unchecked((int)GetFloat()), FloatConversions.s_int);
Assert.AreEqual(unchecked((uint)GetFloat()), FloatConversions.s_uint);
Assert.AreEqual(unchecked((long)GetFloat()), FloatConversions.s_long);
Assert.AreEqual(unchecked((ulong)GetFloat()), FloatConversions.s_ulong);
Assert.AreEqual(unchecked((double)GetFloat()), FloatConversions.s_double);
Assert.AreEqual(unchecked((nint)GetFloat()), FloatConversions.s_nint);
Assert.AreEqual(unchecked((nuint)GetFloat()), FloatConversions.s_nuint);

Assert.IsPreinitialized(typeof(DoubleConversions));
Assert.AreEqual(unchecked((byte)GetDouble()), DoubleConversions.s_byte);
Assert.AreEqual(unchecked((sbyte)GetDouble()), DoubleConversions.s_sbyte);
Assert.AreEqual(unchecked((short)GetDouble()), DoubleConversions.s_short);
Assert.AreEqual(unchecked((ushort)GetDouble()), DoubleConversions.s_ushort);
Assert.AreEqual(unchecked((int)GetDouble()), DoubleConversions.s_int);
Assert.AreEqual(unchecked((uint)GetDouble()), DoubleConversions.s_uint);
Assert.AreEqual(unchecked((long)GetDouble()), DoubleConversions.s_long);
Assert.AreEqual(unchecked((ulong)GetDouble()), DoubleConversions.s_ulong);
Assert.AreEqual(unchecked((float)GetDouble()), DoubleConversions.s_float);
Assert.AreEqual(unchecked((nint)GetDouble()), DoubleConversions.s_nint);
Assert.AreEqual(unchecked((nuint)GetDouble()), DoubleConversions.s_nuint);

Assert.IsPreinitialized(typeof(NativeIntConversions));
Assert.AreEqual(unchecked((byte)GetNativeInt()), NativeIntConversions.s_byte);
Assert.AreEqual(unchecked((sbyte)GetNativeInt()), NativeIntConversions.s_sbyte);
Assert.AreEqual(unchecked((short)GetNativeInt()), NativeIntConversions.s_short);
Assert.AreEqual(unchecked((ushort)GetNativeInt()), NativeIntConversions.s_ushort);
Assert.AreEqual(unchecked((int)GetNativeInt()), NativeIntConversions.s_int);
Assert.AreEqual(unchecked((uint)GetNativeInt()), NativeIntConversions.s_uint);
Assert.AreEqual(unchecked((long)GetNativeInt()), NativeIntConversions.s_long);
Assert.AreEqual(unchecked((ulong)GetNativeInt()), NativeIntConversions.s_ulong);
Assert.AreEqual(unchecked((float)GetNativeInt()), NativeIntConversions.s_float);
Assert.AreEqual(unchecked((double)GetNativeInt()), NativeIntConversions.s_double);
Assert.AreEqual(unchecked((nuint)GetNativeInt()), NativeIntConversions.s_nuint);
}
}


static class Assert
{
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Expand Down Expand Up @@ -1480,6 +1711,12 @@ public static unsafe void AreEqual(long v1, long v2)
throw new Exception();
}

public static unsafe void AreEqual(ulong v1, ulong v2)
{
if (v1 != v2)
throw new Exception();
}

public static unsafe void AreEqual(float v1, float v2)
{
if (v1 != v2)
Expand All @@ -1492,6 +1729,18 @@ public static unsafe void AreEqual(double v1, double v2)
throw new Exception();
}

public static unsafe void AreEqual(nint v1, nint v2)
{
if (v1 != v2)
throw new Exception();
}

public static unsafe void AreEqual(nuint v1, nuint v2)
{
if (v1 != v2)
throw new Exception();
}

public static void True(bool v)
{
if (!v)
Expand Down
Loading