Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.

Commit 4ce1c21

Browse files
authored
Wasm: add support for most of the conv_ovf operations (#8350)
* wasm: add support for most of the conv_ovf operations i and i_un still to do. * correct comment * remove masks, refactor * address feedback. Refactor to remove some conditions. Change Dbl2ULngOvf to check for negative
1 parent 151abd3 commit 4ce1c21

4 files changed

Lines changed: 595 additions & 11 deletions

File tree

src/Common/src/TypeSystem/IL/ILImporter.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -746,25 +746,25 @@ private void ImportBasicBlock(BasicBlock basicBlock)
746746
ImportConvert(WellKnownType.SByte, true, false);
747747
break;
748748
case ILOpcode.conv_ovf_u1:
749-
ImportConvert(WellKnownType.Byte, true, false);
749+
ImportConvert(WellKnownType.Byte, true, true);
750750
break;
751751
case ILOpcode.conv_ovf_i2:
752752
ImportConvert(WellKnownType.Int16, true, false);
753753
break;
754754
case ILOpcode.conv_ovf_u2:
755-
ImportConvert(WellKnownType.UInt16, true, false);
755+
ImportConvert(WellKnownType.UInt16, true, true);
756756
break;
757757
case ILOpcode.conv_ovf_i4:
758758
ImportConvert(WellKnownType.Int32, true, false);
759759
break;
760760
case ILOpcode.conv_ovf_u4:
761-
ImportConvert(WellKnownType.UInt32, true, false);
761+
ImportConvert(WellKnownType.UInt32, true, true);
762762
break;
763763
case ILOpcode.conv_ovf_i8:
764764
ImportConvert(WellKnownType.Int64, true, false);
765765
break;
766766
case ILOpcode.conv_ovf_u8:
767-
ImportConvert(WellKnownType.UInt64, true, false);
767+
ImportConvert(WellKnownType.UInt64, true, true);
768768
break;
769769
case ILOpcode.refanyval:
770770
ImportRefAnyVal(ReadILToken());
@@ -779,10 +779,10 @@ private void ImportBasicBlock(BasicBlock basicBlock)
779779
ImportLdToken(ReadILToken());
780780
break;
781781
case ILOpcode.conv_u2:
782-
ImportConvert(WellKnownType.UInt16, false, false);
782+
ImportConvert(WellKnownType.UInt16, false, true);
783783
break;
784784
case ILOpcode.conv_u1:
785-
ImportConvert(WellKnownType.Byte, false, false);
785+
ImportConvert(WellKnownType.Byte, false, true);
786786
break;
787787
case ILOpcode.conv_i:
788788
ImportConvert(WellKnownType.IntPtr, false, false);
@@ -791,7 +791,7 @@ private void ImportBasicBlock(BasicBlock basicBlock)
791791
ImportConvert(WellKnownType.IntPtr, true, false);
792792
break;
793793
case ILOpcode.conv_ovf_u:
794-
ImportConvert(WellKnownType.UIntPtr, true, false);
794+
ImportConvert(WellKnownType.UIntPtr, true, true);
795795
break;
796796
case ILOpcode.add_ovf:
797797
case ILOpcode.add_ovf_un:

src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.ComponentModel;
67
using System.Diagnostics;
78
using System.IO;
89
using System.Linq;
@@ -4150,14 +4151,135 @@ private void ImportCompareOperation(ILOpcode opcode)
41504151

41514152
private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned)
41524153
{
4153-
//TODO checkOverflow
4154+
//TODO checkOverflow - r_un & r_4, i & i_un
41544155
StackEntry value = _stack.Pop();
41554156
TypeDesc destType = GetWellKnownType(wellKnownType);
41564157

41574158
// Load the value and then convert it instead of using ValueAsType to avoid loading the incorrect size
41584159
LLVMValueRef loadedValue = value.ValueAsType(value.Type, _builder);
4159-
LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(), wellKnownType == WellKnownType.UInt64 /* unsigned is always false, so check for the type explicitly */);
4160-
PushExpression(GetStackValueKind(destType), "conv", converted, destType);
4160+
4161+
ExpressionEntry expressionEntry;
4162+
if (checkOverflow)
4163+
{
4164+
Debug.Assert(destType is EcmaType);
4165+
if (IsLlvmReal(loadedValue.TypeOf))
4166+
{
4167+
expressionEntry = BuildConvOverflowFromReal(value, loadedValue, (EcmaType)destType, wellKnownType, unsigned, value.Type);
4168+
}
4169+
else
4170+
{
4171+
expressionEntry = BuildConvOverflow(value.Name(), loadedValue, (EcmaType)destType, wellKnownType, unsigned, value.Type);
4172+
}
4173+
}
4174+
else
4175+
{
4176+
LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(), wellKnownType == WellKnownType.UInt64 /* unsigned is always false, so check for the type explicitly */);
4177+
expressionEntry = new ExpressionEntry(GetStackValueKind(destType), "conv", converted, destType);
4178+
}
4179+
_stack.Push(expressionEntry);
4180+
}
4181+
4182+
private bool IsLlvmReal(LLVMTypeRef llvmTypeRef)
4183+
{
4184+
return llvmTypeRef == LLVMTypeRef.Float || llvmTypeRef == LLVMTypeRef.Double;
4185+
}
4186+
4187+
ExpressionEntry BuildConvOverflowFromReal(StackEntry value, LLVMValueRef loadedValue, EcmaType destType, WellKnownType destWellKnownType, bool unsigned, TypeDesc sourceType)
4188+
{
4189+
//TODO: single overflow checks extend to doubles - this could be more efficient
4190+
if (value.Type == GetWellKnownType(WellKnownType.Single))
4191+
{
4192+
value = new ExpressionEntry(StackValueKind.Float, "dbl", _builder.BuildFPExt(loadedValue, LLVMTypeRef.Double), GetWellKnownType(WellKnownType.Double));
4193+
}
4194+
switch (destWellKnownType)
4195+
{
4196+
case WellKnownType.Byte:
4197+
case WellKnownType.SByte:
4198+
case WellKnownType.Int16:
4199+
case WellKnownType.UInt16:
4200+
var intExpression = CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2IntOvf", new[] {value});
4201+
return BuildConvOverflow(value.Name(), intExpression.ValueForStackKind(StackValueKind.Int32, _builder, false), destType, destWellKnownType, unsigned, sourceType);
4202+
case WellKnownType.Int32:
4203+
return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2IntOvf", new[] { value });
4204+
case WellKnownType.UInt32:
4205+
case WellKnownType.UIntPtr: // TODO : 64bit.
4206+
return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2UIntOvf", new[] { value });
4207+
case WellKnownType.Int64:
4208+
return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2LngOvf", new[] { value });
4209+
case WellKnownType.UInt64:
4210+
return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2ULngOvf", new[] { value });
4211+
default:
4212+
throw new InvalidProgramException("Unsupported destination for singled/double overflow check");
4213+
}
4214+
}
4215+
4216+
ExpressionEntry BuildConvOverflow(string name, LLVMValueRef loadedValue, EcmaType destType, WellKnownType destWellKnownType, bool unsigned, TypeDesc sourceType)
4217+
{
4218+
ulong maxValue = 0;
4219+
long minValue = 0;
4220+
switch (destWellKnownType)
4221+
{
4222+
case WellKnownType.Byte:
4223+
maxValue = byte.MaxValue;
4224+
break;
4225+
case WellKnownType.SByte:
4226+
maxValue = (ulong)sbyte.MaxValue;
4227+
minValue = sbyte.MinValue;
4228+
break;
4229+
case WellKnownType.UInt16:
4230+
maxValue = ushort.MaxValue;
4231+
break;
4232+
case WellKnownType.Int16:
4233+
maxValue = (ulong)short.MaxValue;
4234+
minValue = short.MinValue;
4235+
break;
4236+
case WellKnownType.UInt32:
4237+
case WellKnownType.UIntPtr: // TODO : 64bit.
4238+
maxValue = uint.MaxValue;
4239+
break;
4240+
case WellKnownType.Int32:
4241+
maxValue = int.MaxValue;
4242+
minValue = int.MinValue;
4243+
break;
4244+
case WellKnownType.UInt64:
4245+
maxValue = ulong.MaxValue;
4246+
break;
4247+
case WellKnownType.Int64:
4248+
maxValue = long.MaxValue;
4249+
minValue = long.MinValue;
4250+
break;
4251+
}
4252+
BuildConvOverflowCheck(loadedValue, unsigned, maxValue, minValue, sourceType, destType);
4253+
LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), name, unsigned);
4254+
return new ExpressionEntry(GetStackValueKind(destType), "conv", converted, destType);
4255+
}
4256+
4257+
private void BuildConvOverflowCheck(LLVMValueRef loadedValue, bool unsigned, ulong maxValue, long minValue, TypeDesc sourceType, EcmaType destType)
4258+
{
4259+
var maxDiff = LLVMValueRef.CreateConstInt(loadedValue.TypeOf, maxValue - (ulong)minValue);
4260+
4261+
LLVMBasicBlockRef overflowBlock = _currentFunclet.AppendBasicBlock("ovf");
4262+
LLVMBasicBlockRef noOverflowBlock = _currentFunclet.AppendBasicBlock("no_ovf");
4263+
LLVMValueRef cmp;
4264+
//special case same width signed -> unsigned, can just check for negative values
4265+
if (unsigned && (loadedValue.TypeOf.IntWidth >> 3) == destType.InstanceFieldSize.AsInt &&
4266+
(sourceType == GetWellKnownType(WellKnownType.Int16)
4267+
|| sourceType == GetWellKnownType(WellKnownType.Int32)
4268+
|| sourceType == GetWellKnownType(WellKnownType.Int64)))
4269+
{
4270+
cmp = _builder.BuildICmp(LLVMIntPredicate.LLVMIntSLT, loadedValue, LLVMValueRef.CreateConstInt(loadedValue.TypeOf, 0));
4271+
}
4272+
else
4273+
{
4274+
var valueDiff = _builder.BuildSub(loadedValue, LLVMValueRef.CreateConstInt(loadedValue.TypeOf, (ulong)minValue));
4275+
cmp = _builder.BuildICmp(LLVMIntPredicate.LLVMIntUGT, valueDiff, maxDiff);
4276+
}
4277+
_builder.BuildCondBr(cmp, overflowBlock, noOverflowBlock);
4278+
4279+
_builder.PositionAtEnd(overflowBlock);
4280+
CallOrInvokeThrowException(_builder, "ThrowHelpers", "ThrowOverflowException");
4281+
_builder.PositionAtEnd(noOverflowBlock);
4282+
AddInternalBasicBlock(noOverflowBlock);
41614283
}
41624284

41634285
private void ImportUnaryOperation(ILOpcode opCode)

src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ public static ulong Dbl2ULngOvf(double val)
255255
const double two64 = 2.0 * 2147483648.0 * 4294967296.0;
256256

257257
// Note that this expression also works properly for val = NaN case
258-
if (val < two64)
258+
if (val > -1.0 && val < two64)
259259
return unchecked((ulong)val);
260260

261261
return ThrowULngOvf();

0 commit comments

Comments
 (0)