From 6629e07d7244c49f16903b335839adcebe17b2a2 Mon Sep 17 00:00:00 2001 From: yowl Date: Sat, 1 Oct 2022 13:23:18 -0500 Subject: [PATCH] NativeAOT-LLVM: cast unsigned ints to floats using uitofp (#1994) * if the source is an unsigned int, then make the cast unsigned * u4 should be unsigned * widen small ints before conv * add boolean to widening check (and a couple of more tests). --- .../tools/Common/TypeSystem/IL/ILImporter.cs | 4 +-- .../CodeGen/ILToLLVMImporter.cs | 19 ++++++++++---- .../SmokeTests/HelloWasm/HelloWasm.cs | 25 +++++++++++++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/IL/ILImporter.cs b/src/coreclr/tools/Common/TypeSystem/IL/ILImporter.cs index ad44ab85d099..09cf9fc3e77e 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/ILImporter.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/ILImporter.cs @@ -581,7 +581,7 @@ private void ImportBasicBlock(BasicBlock basicBlock) ImportConvert(WellKnownType.Double, false, false); break; case ILOpcode.conv_u4: - ImportConvert(WellKnownType.UInt32, false, false); + ImportConvert(WellKnownType.UInt32, false, true); break; case ILOpcode.conv_u8: ImportConvert(WellKnownType.UInt64, false, true); @@ -825,7 +825,7 @@ private void ImportBasicBlock(BasicBlock basicBlock) ImportStoreIndirect(WellKnownType.IntPtr); break; case ILOpcode.conv_u: - ImportConvert(WellKnownType.UIntPtr, false, false); + ImportConvert(WellKnownType.UIntPtr, false, true); break; case ILOpcode.prefix1: opCode = (ILOpcode)(0x100 + ReadILByte()); diff --git a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs index b038f6db36c2..be9e8421756d 100644 --- a/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs +++ b/src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/ILToLLVMImporter.cs @@ -1227,8 +1227,9 @@ internal static LLVMValueRef CastIfNecessary(LLVMBuilderRef builder, LLVMValueRe } else if (toStoreKind == LLVMTypeKind.LLVMIntegerTypeKind && (valueTypeKind == LLVMTypeKind.LLVMDoubleTypeKind || valueTypeKind == LLVMTypeKind.LLVMFloatTypeKind)) { - //TODO: keep track of the TypeDesc so we can call BuildUIToFP when the integer is unsigned - typedToStore = builder.BuildSIToFP(source, valueType, "CastSIToFloat" + (name ?? "")); + typedToStore = unsigned + ? builder.BuildUIToFP(source, valueType, "CastUIToFloat" + (name ?? "")) + : builder.BuildSIToFP(source, valueType, "CastSIToFloat" + (name ?? "")); } else if ((toStoreKind == LLVMTypeKind.LLVMDoubleTypeKind || toStoreKind == LLVMTypeKind.LLVMFloatTypeKind) && valueTypeKind == LLVMTypeKind.LLVMIntegerTypeKind) @@ -4026,10 +4027,18 @@ private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool { //TODO checkOverflow - r_un & r_4, i & i_un StackEntry value = _stack.Pop(); + TypeDesc stackType = value.Type; + TypeDesc destType = GetWellKnownType(wellKnownType); // Load the value and then convert it instead of using ValueAsType to avoid loading the incorrect size LLVMValueRef loadedValue = value.ValueAsType(value.Type, _builder); + LLVMValueRef widenedStackValue = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(WidenBytesAndShorts(value.Type)), value.Name(), + stackType.IsWellKnownType(WellKnownType.Boolean) || + stackType.IsWellKnownType(WellKnownType.Byte) || + stackType.IsWellKnownType(WellKnownType.UInt16) || + stackType.IsWellKnownType(WellKnownType.UInt32) + ); ExpressionEntry expressionEntry; if (checkOverflow) @@ -4037,16 +4046,16 @@ private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool Debug.Assert(destType is EcmaType); if (IsLlvmReal(loadedValue.TypeOf)) { - expressionEntry = BuildConvOverflowFromReal(value, loadedValue, (EcmaType)destType, wellKnownType, unsigned, value.Type); + expressionEntry = BuildConvOverflowFromReal(value, widenedStackValue, (EcmaType)destType, wellKnownType, unsigned, value.Type); } else { - expressionEntry = BuildConvOverflow(value.Name(), loadedValue, (EcmaType)destType, wellKnownType, unsigned, value.Type); + expressionEntry = BuildConvOverflow(value.Name(), widenedStackValue, (EcmaType)destType, wellKnownType, unsigned, value.Type); } } else { - LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(), wellKnownType == WellKnownType.UInt64 /* unsigned is always false, so check for the type explicitly */); + LLVMValueRef converted = CastIfNecessary(widenedStackValue, GetLLVMTypeForTypeDesc(destType), value.Name(), unsigned); expressionEntry = new ExpressionEntry(GetStackValueKind(destType), "conv", converted, destType); } _stack.Push(expressionEntry); diff --git a/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs b/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs index bb88076722ac..24a1405c9fde 100644 --- a/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs +++ b/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs @@ -229,6 +229,8 @@ private static unsafe int Main(string[] args) PrintLine(((BoxStubTest[])arrayCastingTest)[2].Value); EndTest(!(arrayCastingTest is CastingTestClass[])); + ConvUTest(); + CastByteForIndex(); ldindTest(); @@ -1081,6 +1083,29 @@ private static void IntToStringTest() EndTest(intString == "42"); } + private static void ConvUTest() + { + StartTest("Implicit casting using ConvU"); + byte alpha = 0xFF; + float f = alpha / 255f; + if (f != 1f) + { + FailTest("Expected 1f but didn't get it"); // TODO: float.ToString() is failing in DiyFP + } + + byte msbByte = 0xff; + nuint nativeUnsignedFromByte = msbByte; + if (nativeUnsignedFromByte != 0xff) + { + FailTest($"Expected 0xff but got {nativeUnsignedFromByte}"); + return; + } + + ushort msbUshort = 0x8000; + nuint nativeUnsignedFromUshort = msbUshort; + EndTest(nativeUnsignedFromUshort == 0x8000, $"Expected 0x8000 but got {nativeUnsignedFromUshort}"); + } + private static void CastByteForIndex() { StartTest("Implicit casting of byte for an index");