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

Commit

Permalink
Wasm: add support for most of the conv_ovf operations (#8350)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
yowl authored Oct 5, 2020
1 parent 151abd3 commit 4ce1c21
Show file tree
Hide file tree
Showing 4 changed files with 595 additions and 11 deletions.
14 changes: 7 additions & 7 deletions src/Common/src/TypeSystem/IL/ILImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -746,25 +746,25 @@ private void ImportBasicBlock(BasicBlock basicBlock)
ImportConvert(WellKnownType.SByte, true, false);
break;
case ILOpcode.conv_ovf_u1:
ImportConvert(WellKnownType.Byte, true, false);
ImportConvert(WellKnownType.Byte, true, true);
break;
case ILOpcode.conv_ovf_i2:
ImportConvert(WellKnownType.Int16, true, false);
break;
case ILOpcode.conv_ovf_u2:
ImportConvert(WellKnownType.UInt16, true, false);
ImportConvert(WellKnownType.UInt16, true, true);
break;
case ILOpcode.conv_ovf_i4:
ImportConvert(WellKnownType.Int32, true, false);
break;
case ILOpcode.conv_ovf_u4:
ImportConvert(WellKnownType.UInt32, true, false);
ImportConvert(WellKnownType.UInt32, true, true);
break;
case ILOpcode.conv_ovf_i8:
ImportConvert(WellKnownType.Int64, true, false);
break;
case ILOpcode.conv_ovf_u8:
ImportConvert(WellKnownType.UInt64, true, false);
ImportConvert(WellKnownType.UInt64, true, true);
break;
case ILOpcode.refanyval:
ImportRefAnyVal(ReadILToken());
Expand All @@ -779,10 +779,10 @@ private void ImportBasicBlock(BasicBlock basicBlock)
ImportLdToken(ReadILToken());
break;
case ILOpcode.conv_u2:
ImportConvert(WellKnownType.UInt16, false, false);
ImportConvert(WellKnownType.UInt16, false, true);
break;
case ILOpcode.conv_u1:
ImportConvert(WellKnownType.Byte, false, false);
ImportConvert(WellKnownType.Byte, false, true);
break;
case ILOpcode.conv_i:
ImportConvert(WellKnownType.IntPtr, false, false);
Expand All @@ -791,7 +791,7 @@ private void ImportBasicBlock(BasicBlock basicBlock)
ImportConvert(WellKnownType.IntPtr, true, false);
break;
case ILOpcode.conv_ovf_u:
ImportConvert(WellKnownType.UIntPtr, true, false);
ImportConvert(WellKnownType.UIntPtr, true, true);
break;
case ILOpcode.add_ovf:
case ILOpcode.add_ovf_un:
Expand Down
128 changes: 125 additions & 3 deletions src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -4150,14 +4151,135 @@ private void ImportCompareOperation(ILOpcode opcode)

private void ImportConvert(WellKnownType wellKnownType, bool checkOverflow, bool unsigned)
{
//TODO checkOverflow
//TODO checkOverflow - r_un & r_4, i & i_un
StackEntry value = _stack.Pop();
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 converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), value.Name(), wellKnownType == WellKnownType.UInt64 /* unsigned is always false, so check for the type explicitly */);
PushExpression(GetStackValueKind(destType), "conv", converted, destType);

ExpressionEntry expressionEntry;
if (checkOverflow)
{
Debug.Assert(destType is EcmaType);
if (IsLlvmReal(loadedValue.TypeOf))
{
expressionEntry = BuildConvOverflowFromReal(value, loadedValue, (EcmaType)destType, wellKnownType, unsigned, value.Type);
}
else
{
expressionEntry = BuildConvOverflow(value.Name(), loadedValue, (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 */);
expressionEntry = new ExpressionEntry(GetStackValueKind(destType), "conv", converted, destType);
}
_stack.Push(expressionEntry);
}

private bool IsLlvmReal(LLVMTypeRef llvmTypeRef)
{
return llvmTypeRef == LLVMTypeRef.Float || llvmTypeRef == LLVMTypeRef.Double;
}

ExpressionEntry BuildConvOverflowFromReal(StackEntry value, LLVMValueRef loadedValue, EcmaType destType, WellKnownType destWellKnownType, bool unsigned, TypeDesc sourceType)
{
//TODO: single overflow checks extend to doubles - this could be more efficient
if (value.Type == GetWellKnownType(WellKnownType.Single))
{
value = new ExpressionEntry(StackValueKind.Float, "dbl", _builder.BuildFPExt(loadedValue, LLVMTypeRef.Double), GetWellKnownType(WellKnownType.Double));
}
switch (destWellKnownType)
{
case WellKnownType.Byte:
case WellKnownType.SByte:
case WellKnownType.Int16:
case WellKnownType.UInt16:
var intExpression = CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2IntOvf", new[] {value});
return BuildConvOverflow(value.Name(), intExpression.ValueForStackKind(StackValueKind.Int32, _builder, false), destType, destWellKnownType, unsigned, sourceType);
case WellKnownType.Int32:
return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2IntOvf", new[] { value });
case WellKnownType.UInt32:
case WellKnownType.UIntPtr: // TODO : 64bit.
return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2UIntOvf", new[] { value });
case WellKnownType.Int64:
return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2LngOvf", new[] { value });
case WellKnownType.UInt64:
return CallRuntime("Internal.Runtime.CompilerHelpers", _method.Context, "MathHelpers", "Dbl2ULngOvf", new[] { value });
default:
throw new InvalidProgramException("Unsupported destination for singled/double overflow check");
}
}

ExpressionEntry BuildConvOverflow(string name, LLVMValueRef loadedValue, EcmaType destType, WellKnownType destWellKnownType, bool unsigned, TypeDesc sourceType)
{
ulong maxValue = 0;
long minValue = 0;
switch (destWellKnownType)
{
case WellKnownType.Byte:
maxValue = byte.MaxValue;
break;
case WellKnownType.SByte:
maxValue = (ulong)sbyte.MaxValue;
minValue = sbyte.MinValue;
break;
case WellKnownType.UInt16:
maxValue = ushort.MaxValue;
break;
case WellKnownType.Int16:
maxValue = (ulong)short.MaxValue;
minValue = short.MinValue;
break;
case WellKnownType.UInt32:
case WellKnownType.UIntPtr: // TODO : 64bit.
maxValue = uint.MaxValue;
break;
case WellKnownType.Int32:
maxValue = int.MaxValue;
minValue = int.MinValue;
break;
case WellKnownType.UInt64:
maxValue = ulong.MaxValue;
break;
case WellKnownType.Int64:
maxValue = long.MaxValue;
minValue = long.MinValue;
break;
}
BuildConvOverflowCheck(loadedValue, unsigned, maxValue, minValue, sourceType, destType);
LLVMValueRef converted = CastIfNecessary(loadedValue, GetLLVMTypeForTypeDesc(destType), name, unsigned);
return new ExpressionEntry(GetStackValueKind(destType), "conv", converted, destType);
}

private void BuildConvOverflowCheck(LLVMValueRef loadedValue, bool unsigned, ulong maxValue, long minValue, TypeDesc sourceType, EcmaType destType)
{
var maxDiff = LLVMValueRef.CreateConstInt(loadedValue.TypeOf, maxValue - (ulong)minValue);

LLVMBasicBlockRef overflowBlock = _currentFunclet.AppendBasicBlock("ovf");
LLVMBasicBlockRef noOverflowBlock = _currentFunclet.AppendBasicBlock("no_ovf");
LLVMValueRef cmp;
//special case same width signed -> unsigned, can just check for negative values
if (unsigned && (loadedValue.TypeOf.IntWidth >> 3) == destType.InstanceFieldSize.AsInt &&
(sourceType == GetWellKnownType(WellKnownType.Int16)
|| sourceType == GetWellKnownType(WellKnownType.Int32)
|| sourceType == GetWellKnownType(WellKnownType.Int64)))
{
cmp = _builder.BuildICmp(LLVMIntPredicate.LLVMIntSLT, loadedValue, LLVMValueRef.CreateConstInt(loadedValue.TypeOf, 0));
}
else
{
var valueDiff = _builder.BuildSub(loadedValue, LLVMValueRef.CreateConstInt(loadedValue.TypeOf, (ulong)minValue));
cmp = _builder.BuildICmp(LLVMIntPredicate.LLVMIntUGT, valueDiff, maxDiff);
}
_builder.BuildCondBr(cmp, overflowBlock, noOverflowBlock);

_builder.PositionAtEnd(overflowBlock);
CallOrInvokeThrowException(_builder, "ThrowHelpers", "ThrowOverflowException");
_builder.PositionAtEnd(noOverflowBlock);
AddInternalBasicBlock(noOverflowBlock);
}

private void ImportUnaryOperation(ILOpcode opCode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ public static ulong Dbl2ULngOvf(double val)
const double two64 = 2.0 * 2147483648.0 * 4294967296.0;

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

return ThrowULngOvf();
Expand Down
Loading

0 comments on commit 4ce1c21

Please sign in to comment.