From 15b6ca2b98fddb114fdf5192d18418f57abb9848 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Wed, 18 Apr 2018 18:47:12 -0700 Subject: [PATCH 1/4] add nontrapping float-to-int conversions --- lib/Backend/GlobOpt.cpp | 1 + lib/Backend/GlobOptExpr.cpp | 2 + lib/Backend/IRBuilderAsmJs.cpp | 60 +++++- lib/Backend/JnHelperMethodList.h | 15 +- lib/Backend/Lower.cpp | 25 ++- lib/Backend/Lower.h | 2 +- lib/Backend/LowerMDShared.cpp | 59 ++++- lib/Backend/LowerMDShared.h | 4 +- lib/Backend/arm/LowerMD.h | 1 + lib/Backend/arm64/LowerMD.h | 1 + lib/Common/ConfigFlagsList.h | 1 + lib/Runtime/ByteCode/OpCodes.h | 1 + lib/Runtime/ByteCode/OpCodesAsmJs.h | 9 + .../Language/InterpreterHandlerAsmJs.inl | 26 ++- lib/Runtime/Language/JavascriptConversion.cpp | 96 --------- lib/Runtime/Language/JavascriptConversion.h | 8 - lib/Runtime/Library/WabtInterface.cpp | 1 + lib/Runtime/Math/Chakra.Runtime.Math.vcxproj | 2 +- lib/Runtime/Math/WasmMath.h | 33 ++- lib/Runtime/Math/WasmMath.inl | 201 ++++++++++++++++-- lib/WasmReader/WasmBinaryOpCodes.h | 19 +- lib/WasmReader/WasmParseTree.cpp | 14 ++ lib/WasmReader/WasmParseTree.h | 5 + lib/wabt/chakra/wabtapi.cc | 4 + lib/wabt/chakra/wabtapi.h | 1 + 25 files changed, 429 insertions(+), 162 deletions(-) diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 4c21a47a8d2..43f55774ac3 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -13884,6 +13884,7 @@ GlobOpt::PreLowerCanonicalize(IR::Instr *instr, Value **pSrc1Val, Value **pSrc2V case Js::OpCode::TrapIfUnalignedAccess: case Js::OpCode::FromVar: case Js::OpCode::Conv_Prim: + case Js::OpCode::Conv_Prim_Sat: case Js::OpCode::LdC_A_I4: case Js::OpCode::LdStr: case Js::OpCode::InitFld: diff --git a/lib/Backend/GlobOptExpr.cpp b/lib/Backend/GlobOptExpr.cpp index c27fd903547..938bbe6b12c 100644 --- a/lib/Backend/GlobOptExpr.cpp +++ b/lib/Backend/GlobOptExpr.cpp @@ -278,6 +278,7 @@ GlobOpt::CSEAddInstr( break; case Js::OpCode::Conv_Prim: + case Js::OpCode::Conv_Prim_Sat: exprAttributes = ConvAttributes(instr->GetDst()->IsUnsigned(), instr->GetSrc1()->IsUnsigned()); break; } @@ -534,6 +535,7 @@ GlobOpt::CSEOptimize(BasicBlock *block, IR::Instr * *const instrRef, Value **pSr break; case Js::OpCode::Conv_Prim: + case Js::OpCode::Conv_Prim_Sat: exprAttributes = ConvAttributes(instr->GetDst()->IsUnsigned(), instr->GetSrc1()->IsUnsigned()); break; diff --git a/lib/Backend/IRBuilderAsmJs.cpp b/lib/Backend/IRBuilderAsmJs.cpp index 5bb566bb8b4..4627d8666f0 100644 --- a/lib/Backend/IRBuilderAsmJs.cpp +++ b/lib/Backend/IRBuilderAsmJs.cpp @@ -2054,18 +2054,30 @@ IRBuilderAsmJs::BuildInt1Double1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::R IR::RegOpnd * srcOpnd = BuildSrcOpnd(srcRegSlot, TyFloat64); srcOpnd->SetValueType(ValueType::Float); IR::RegOpnd * dstOpnd = nullptr; + Js::OpCode op = Js::OpCode::Nop; switch (newOpcode) { case Js::OpCodeAsmJs::Conv_DTI: dstOpnd = BuildDstOpnd(dstRegSlot, TyInt32); + op = Js::OpCode::Conv_Prim; break; case Js::OpCodeAsmJs::Conv_DTU: dstOpnd = BuildDstOpnd(dstRegSlot, TyUint32); + op = Js::OpCode::Conv_Prim; + break; + case Js::OpCodeAsmJs::Conv_Sat_DTI: + dstOpnd = BuildDstOpnd(dstRegSlot, TyInt32); + op = Js::OpCode::Conv_Prim_Sat; + break; + case Js::OpCodeAsmJs::Conv_Sat_DTU: + dstOpnd = BuildDstOpnd(dstRegSlot, TyUint32); + op = Js::OpCode::Conv_Prim_Sat; break; case Js::OpCodeAsmJs::Conv_Check_DTI: case Js::OpCodeAsmJs::Conv_Check_DTU: { IR::RegOpnd* tmpDst = IR::RegOpnd::New(TyFloat64, m_func); + op = Js::OpCode::Conv_Prim; tmpDst->SetValueType(ValueType::Float); AddInstr(IR::Instr::New(Js::OpCode::TrapIfTruncOverflow, tmpDst, srcOpnd, m_func), offset); dstOpnd = BuildDstOpnd(dstRegSlot, newOpcode == Js::OpCodeAsmJs::Conv_Check_DTI ? TyInt32 : TyUint32); @@ -2077,7 +2089,7 @@ IRBuilderAsmJs::BuildInt1Double1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::R Assume(UNREACHED); } dstOpnd->SetValueType(ValueType::GetInt(false)); - IR::Instr * instr = IR::Instr::New(Js::OpCode::Conv_Prim, dstOpnd, srcOpnd, m_func); + IR::Instr * instr = IR::Instr::New(op, dstOpnd, srcOpnd, m_func); AddInstr(instr, offset); } @@ -2098,6 +2110,14 @@ IRBuilderAsmJs::BuildInt1Float1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::Re dstOpnd = BuildDstOpnd(dstRegSlot, TyUint32); op = Js::OpCode::Conv_Prim; break; + case Js::OpCodeAsmJs::Conv_Sat_FTI: + dstOpnd = BuildDstOpnd(dstRegSlot, TyInt32); + op = Js::OpCode::Conv_Prim_Sat; + break; + case Js::OpCodeAsmJs::Conv_Sat_FTU: + dstOpnd = BuildDstOpnd(dstRegSlot, TyUint32); + op = Js::OpCode::Conv_Prim_Sat; + break; case Js::OpCodeAsmJs::Reinterpret_FTI: dstOpnd = BuildDstOpnd(dstRegSlot, TyInt32); op = Js::OpCode::Reinterpret_Prim; @@ -3338,25 +3358,43 @@ IRBuilderAsmJs::BuildInt1Long1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::Reg void IRBuilderAsmJs::BuildLong1Float1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot) { - IR::RegOpnd * src1Opnd = BuildSrcOpnd(src1RegSlot, TyFloat32); + IR::RegOpnd * srcOpnd = BuildSrcOpnd(src1RegSlot, TyFloat32); IR::RegOpnd * dstOpnd = nullptr; + Js::OpCode op = Js::OpCode::Nop; + bool trapping = false; switch (newOpcode) { case Js::OpCodeAsmJs::Conv_Check_FTL: dstOpnd = BuildDstOpnd(dstRegSlot, TyInt64); + op = Js::OpCode::Conv_Prim; + trapping = true; break; case Js::OpCodeAsmJs::Conv_Check_FTUL: dstOpnd = BuildDstOpnd(dstRegSlot, TyUint64); + op = Js::OpCode::Conv_Prim; + trapping = true; + break; + case Js::OpCodeAsmJs::Conv_Sat_FTL: + dstOpnd = BuildDstOpnd(dstRegSlot, TyInt64); + op = Js::OpCode::Conv_Prim_Sat; + break; + case Js::OpCodeAsmJs::Conv_Sat_FTUL: + dstOpnd = BuildDstOpnd(dstRegSlot, TyUint64); + op = Js::OpCode::Conv_Prim_Sat; break; default: Assume(UNREACHED); } - IR::RegOpnd* tmpDst = IR::RegOpnd::New(src1Opnd->GetType(), m_func); - tmpDst->SetValueType(ValueType::Float); - AddInstr(IR::Instr::New(Js::OpCode::TrapIfTruncOverflow, tmpDst, src1Opnd, m_func), offset); - dstOpnd->m_dontDeadStore = true; - IR::Instr * instr = IR::Instr::New(Js::OpCode::Conv_Prim, dstOpnd, tmpDst, m_func); + if (trapping) + { + IR::RegOpnd* tmpDst = IR::RegOpnd::New(srcOpnd->GetType(), m_func); + tmpDst->SetValueType(ValueType::Float); + AddInstr(IR::Instr::New(Js::OpCode::TrapIfTruncOverflow, tmpDst, srcOpnd, m_func), offset); + dstOpnd->m_dontDeadStore = true; + srcOpnd = tmpDst; + } + IR::Instr * instr = IR::Instr::New(op, dstOpnd, srcOpnd, m_func); AddInstr(instr, offset); } @@ -3401,6 +3439,14 @@ IRBuilderAsmJs::BuildLong1Double1(Js::OpCodeAsmJs newOpcode, uint32 offset, Js:: dstType = TyUint64; doTruncTrapCheck = true; break; + case Js::OpCodeAsmJs::Conv_Sat_DTL: + op = Js::OpCode::Conv_Prim_Sat; + dstType = TyInt64; + break; + case Js::OpCodeAsmJs::Conv_Sat_DTUL: + op = Js::OpCode::Conv_Prim_Sat; + dstType = TyUint64; + break; case Js::OpCodeAsmJs::Reinterpret_DTL: op = Js::OpCode::Reinterpret_Prim; dstType = TyInt64; diff --git a/lib/Backend/JnHelperMethodList.h b/lib/Backend/JnHelperMethodList.h index 1220d5f0c24..8529911f164 100644 --- a/lib/Backend/JnHelperMethodList.h +++ b/lib/Backend/JnHelperMethodList.h @@ -547,12 +547,15 @@ HELPERCALL(DirectMath_NearestFlt, (float(*)(float)) Wasm::WasmMath::Nearest, 0) -#define CONVERSION_HELPER(HELPER_TYPE) HELPERCALL(HELPER_TYPE, Js::JavascriptConversion::##HELPER_TYPE, AttrCanThrow) -CONVERSION_HELPER(F32TOI64) -CONVERSION_HELPER(F32TOU64) -CONVERSION_HELPER(F64TOI64) -CONVERSION_HELPER(F64TOU64) -#undef CONVERSION_HELPER +HELPERCALL(F32ToI64, (int64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToI64, AttrCanThrow) +HELPERCALL(F32ToU64, (uint64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToU64, AttrCanThrow) +HELPERCALL(F64ToI64, (int64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToI64, AttrCanThrow) +HELPERCALL(F64ToU64, (uint64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToU64, AttrCanThrow) + +HELPERCALL(F32ToI64Sat, (int64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToI64, AttrCanThrow) +HELPERCALL(F32ToU64Sat, (uint64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToU64, AttrCanThrow) +HELPERCALL(F64ToI64Sat, (int64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToI64, AttrCanThrow) +HELPERCALL(F64ToU64Sat, (uint64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToU64, AttrCanThrow) HELPERCALL(I64TOF64, Js::JavascriptConversion::LongToDouble, 0) HELPERCALL(UI64TOF64, Js::JavascriptConversion::ULongToDouble, 0) diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index 0b46d85974d..20270cf3182 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -1914,11 +1914,16 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa Assert(UNREACHED); } break; + case Js::OpCode::Conv_Prim_Sat: + { + GenerateTruncWithCheck(instr); + break; + } case Js::OpCode::Conv_Prim: { if (IR::Instr::FindSingleDefInstr(Js::OpCode::TrapIfTruncOverflow, instr->GetSrc1())) { - GenerateTruncWithCheck(instr); + GenerateTruncWithCheck(instr); break; } @@ -19789,13 +19794,14 @@ IR::RegOpnd * Lowerer::GetRegOpnd(IR::Opnd* opnd, IR::Instr* insertInstr, Func* return regOpnd; } -void Lowerer::GenerateTruncWithCheck(IR::Instr* instr) +template +void Lowerer::GenerateTruncWithCheck(_In_ IR::Instr* instr) { Assert(instr->GetSrc1()->IsFloat()); if (instr->GetDst()->IsInt32() || instr->GetDst()->IsUInt32()) { - m_lowererMD.GenerateTruncWithCheck(instr); + m_lowererMD.GenerateTruncWithCheck(instr); } else { @@ -19810,8 +19816,17 @@ void Lowerer::GenerateTruncWithCheck(IR::Instr* instr) { m_lowererMD.LoadDoubleHelperArgument(instr, instr->GetSrc1()); } - IR::JnHelperMethod helperList[2][2] = { IR::HelperF32TOI64, IR::HelperF32TOU64, IR::HelperF64TOI64 ,IR::HelperF64TOU64 }; - IR::JnHelperMethod helper = helperList[instr->GetSrc1()->GetType() != TyFloat32][instr->GetDst()->GetType() == TyUint64]; + IR::JnHelperMethod helper; + if (Saturate) + { + IR::JnHelperMethod helperList[2][2] = { IR::HelperF32ToI64Sat, IR::HelperF32ToU64Sat, IR::HelperF64ToI64Sat ,IR::HelperF64ToU64Sat }; + helper = helperList[instr->GetSrc1()->GetType() != TyFloat32][instr->GetDst()->GetType() == TyUint64]; + } + else + { + IR::JnHelperMethod helperList[2][2] = { IR::HelperF32ToI64, IR::HelperF32ToU64, IR::HelperF64ToI64 ,IR::HelperF64ToU64 }; + helper = helperList[instr->GetSrc1()->GetType() != TyFloat32][instr->GetDst()->GetType() == TyUint64]; + } instr->UnlinkSrc1(); this->m_lowererMD.ChangeToHelperCall(instr, helper); } diff --git a/lib/Backend/Lower.h b/lib/Backend/Lower.h index 1fdd9a0d913..020aedf3e4b 100644 --- a/lib/Backend/Lower.h +++ b/lib/Backend/Lower.h @@ -514,7 +514,7 @@ class Lowerer void GenerateFastInlineMathClz(IR::Instr* instr); void GenerateCtz(IR::Instr* instr); void GeneratePopCnt(IR::Instr* instr); - void GenerateTruncWithCheck(IR::Instr* instr); + template void GenerateTruncWithCheck(_In_ IR::Instr* instr); void GenerateFastInlineMathFround(IR::Instr* instr); void GenerateFastInlineRegExpExec(IR::Instr * instr); bool GenerateFastPush(IR::Opnd *baseOpndParam, IR::Opnd *src, IR::Instr *callInstr, IR::Instr *insertInstr, IR::LabelInstr *labelHelper, IR::LabelInstr *doneLabel, IR::LabelInstr * bailOutLabelHelper, bool returnLength = false); diff --git a/lib/Backend/LowerMDShared.cpp b/lib/Backend/LowerMDShared.cpp index 795661aaa8e..ee4a61b91e4 100644 --- a/lib/Backend/LowerMDShared.cpp +++ b/lib/Backend/LowerMDShared.cpp @@ -4722,11 +4722,17 @@ IR::Opnd* LowererMD::Subtract2To31(IR::Opnd* src1, IR::Opnd* intMinFP, IRType ty return adjSrc; } -IR::Opnd* LowererMD::GenerateTruncChecks(IR::Instr* instr) +template +IR::Opnd* +LowererMD::GenerateTruncChecks(_In_ IR::Instr* instr, _In_opt_ IR::LabelInstr* doneLabel) { + AnalysisAssert(!Saturate || doneLabel); + IR::LabelInstr * conversion = IR::LabelInstr::New(Js::OpCode::Label, m_func); - IR::LabelInstr * throwLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func, true); + IR::LabelInstr * nanLabel = Saturate ? IR::LabelInstr::New(Js::OpCode::Label, m_func, true) : nullptr; + IR::LabelInstr * oobLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func, true); IR::Opnd* src1 = instr->GetSrc1(); + IR::Opnd* dst = instr->GetDst(); IR::Opnd * src64 = nullptr; if (src1->IsFloat32()) @@ -4739,31 +4745,57 @@ IR::Opnd* LowererMD::GenerateTruncChecks(IR::Instr* instr) src64 = src1; } - IR::RegOpnd* limitReg = MaterializeDoubleConstFromInt(instr->GetDst()->IsUInt32() ? + IR::RegOpnd* limitReg = MaterializeDoubleConstFromInt(dst->IsUInt32() ? m_func->GetThreadContextInfo()->GetDoubleNegOneAddr() : m_func->GetThreadContextInfo()->GetDoubleIntMinMinusOneAddr(), instr); - m_lowerer->InsertCompareBranch(src64, limitReg, Js::OpCode::BrLe_A, throwLabel, instr); + m_lowerer->InsertCompareBranch(src64, limitReg, Js::OpCode::BrLe_A, oobLabel, instr); - limitReg = MaterializeDoubleConstFromInt(instr->GetDst()->IsUInt32() ? + limitReg = MaterializeDoubleConstFromInt(dst->IsUInt32() ? m_func->GetThreadContextInfo()->GetDoubleUintMaxPlusOneAddr() : m_func->GetThreadContextInfo()->GetDoubleIntMaxPlusOneAddr(), instr); m_lowerer->InsertCompareBranch(limitReg, src64, Js::OpCode::BrGt_A, conversion, instr, true /*no NaN check*/); - instr->InsertBefore(throwLabel); - this->m_lowerer->GenerateThrow(IR::IntConstOpnd::New(SCODE_CODE(VBSERR_Overflow), TyInt32, m_func), instr); - //no jump here we aren't coming back + + instr->InsertBefore(oobLabel); + if (Saturate) + { + IR::LabelInstr * tooBigLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func, true); + IR::RegOpnd* zeroReg = IR::RegOpnd::New(TyFloat64, m_func); + LoadFloatZero(zeroReg, instr); + + m_lowerer->InsertCompareBranch(src64, zeroReg, Js::OpCode::BrGt_A, tooBigLabel, instr, true /*no NaN check*/); + instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::JP, nanLabel, m_func)); + + m_lowerer->InsertMove(dst, IR::IntConstOpnd::New(dst->IsUnsigned() ? 0 : INT32_MIN, dst->GetType(), m_func), instr); + m_lowerer->InsertBranch(Js::OpCode::Br, doneLabel, instr); + + instr->InsertBefore(tooBigLabel); + m_lowerer->InsertMove(dst, IR::IntConstOpnd::New(dst->IsUnsigned() ? UINT32_MAX : INT32_MAX, dst->GetType(), m_func), instr); + m_lowerer->InsertBranch(Js::OpCode::Br, doneLabel, instr); + + instr->InsertBefore(nanLabel); + m_lowerer->InsertMove(dst, IR::IntConstOpnd::New(0, dst->GetType(), m_func), instr); + m_lowerer->InsertBranch(Js::OpCode::Br, doneLabel, instr); + } + else + { + m_lowerer->GenerateThrow(IR::IntConstOpnd::New(SCODE_CODE(VBSERR_Overflow), TyInt32, m_func), instr); + //no jump here we aren't coming back + } instr->InsertBefore(conversion); return src64; } +template void -LowererMD::GenerateTruncWithCheck(IR::Instr * instr) +LowererMD::GenerateTruncWithCheck(_In_ IR::Instr * instr) { Assert(AutoSystemInfo::Data.SSE2Available()); - IR::Opnd* src64 = GenerateTruncChecks(instr); //converts src to double and checks if MIN <= src <= MAX + IR::LabelInstr * doneLabel = Saturate ? IR::LabelInstr::New(Js::OpCode::Label, m_func) : nullptr; + IR::Opnd* src64 = GenerateTruncChecks(instr, doneLabel); //converts src to double and checks if MIN <= src <= MAX IR::Opnd* dst = instr->GetDst(); @@ -4784,12 +4816,17 @@ LowererMD::GenerateTruncWithCheck(IR::Instr * instr) { instr->InsertBefore(IR::Instr::New(Js::OpCode::CVTTSD2SI, dst, src64, m_func)); } - + if (Saturate) + { + instr->InsertBefore(doneLabel); + } instr->UnlinkSrc1(); instr->UnlinkDst(); instr->Remove(); } +template void LowererMD::GenerateTruncWithCheck(_In_ IR::Instr * instr); +template void LowererMD::GenerateTruncWithCheck(_In_ IR::Instr * instr); void LowererMD::GenerateCtz(IR::Instr * instr) diff --git a/lib/Backend/LowerMDShared.h b/lib/Backend/LowerMDShared.h index b8c97702121..945dcb41a86 100644 --- a/lib/Backend/LowerMDShared.h +++ b/lib/Backend/LowerMDShared.h @@ -176,8 +176,8 @@ class LowererMD void GenerateClz(IR::Instr * instr); void GenerateCtz(IR::Instr * instr); void GeneratePopCnt(IR::Instr * instr); - void GenerateTruncWithCheck(IR::Instr * instr); - IR::Opnd* GenerateTruncChecks(IR::Instr* instr); + template void GenerateTruncWithCheck(_In_ IR::Instr * instr); + template IR::Opnd* GenerateTruncChecks(_In_ IR::Instr* instr, _In_opt_ IR::LabelInstr* doneLabel); IR::RegOpnd* MaterializeDoubleConstFromInt(intptr_t constAddr, IR::Instr* instr); IR::RegOpnd* MaterializeConstFromBits(int intConst, IRType type, IR::Instr* instr); IR::Opnd* Subtract2To31(IR::Opnd* src1, IR::Opnd* intMinFP, IRType type, IR::Instr* instr); diff --git a/lib/Backend/arm/LowerMD.h b/lib/Backend/arm/LowerMD.h index 79bbae40406..64463ba1bbe 100644 --- a/lib/Backend/arm/LowerMD.h +++ b/lib/Backend/arm/LowerMD.h @@ -101,6 +101,7 @@ class LowererMD void GenerateClz(IR::Instr * instr); void GenerateCtz(IR::Instr * instr) { Assert(UNREACHED); } void GeneratePopCnt(IR::Instr * instr) { Assert(UNREACHED); } + template void GenerateTruncWithCheck(IR::Instr * instr) { Assert(UNREACHED); } void GenerateFastDivByPow2(IR::Instr *instr); bool GenerateFastAdd(IR::Instr * instrAdd); diff --git a/lib/Backend/arm64/LowerMD.h b/lib/Backend/arm64/LowerMD.h index 4992ce7d2f3..7f10b321056 100644 --- a/lib/Backend/arm64/LowerMD.h +++ b/lib/Backend/arm64/LowerMD.h @@ -99,6 +99,7 @@ class LowererMD void GenerateClz(IR::Instr * instr); void GenerateCtz(IR::Instr * instr) { Assert(UNREACHED); } void GeneratePopCnt(IR::Instr * instr) { Assert(UNREACHED); } + template void GenerateTruncWithCheck(IR::Instr * instr) { Assert(UNREACHED); } void GenerateFastDivByPow2(IR::Instr *instr); bool GenerateFastDivAndRem(IR::Instr* instrDiv, IR::LabelInstr* bailOutLabel = false); diff --git a/lib/Common/ConfigFlagsList.h b/lib/Common/ConfigFlagsList.h index 9461a0109fa..776fd1cf215 100644 --- a/lib/Common/ConfigFlagsList.h +++ b/lib/Common/ConfigFlagsList.h @@ -923,6 +923,7 @@ FLAGR(Boolean, WasmExperimental, "Enable WebAssembly experimental features", DEF // Not having the DEFAULT_CONFIG_XXXX macro ensures we use CONFIG_FLAG_RELEASE instead of CONFIG_FLAG FLAGPR_EXPERIMENTAL_WASM(Boolean, WasmSignExtends , "Use new WebAssembly sign extension operators") FLAGPR_EXPERIMENTAL_WASM(Boolean, WasmSimd , "Enable SIMD in WebAssembly") +FLAGPR_EXPERIMENTAL_WASM(Boolean, NontrappingConversions, "Enable non-trapping float-to-int conversions in WebAssembly") FLAGNR(Boolean, AssertBreak , "Debug break on assert", false) FLAGNR(Boolean, AssertPopUp , "Pop up asserts (default: false)", false) diff --git a/lib/Runtime/ByteCode/OpCodes.h b/lib/Runtime/ByteCode/OpCodes.h index 8ae4c061352..3750dcf0a6f 100755 --- a/lib/Runtime/ByteCode/OpCodes.h +++ b/lib/Runtime/ByteCode/OpCodes.h @@ -331,6 +331,7 @@ MACRO_BACKEND_ONLY( ToVar, Reg2, OpTempNumberProducin // TODO: Consider changing the code so we don't have mark this as CallsValueOf MACRO_BACKEND_ONLY( FromVar, Reg2, OpTempNumberSources|OpTempObjectSources|OpCanCSE) MACRO_BACKEND_ONLY( Conv_Prim, Reg2, OpTempNumberProducing|OpTempNumberSources|OpCanCSE|OpPostOpDbgBailOut) // Convert between primitives (int32/float64) +MACRO_BACKEND_ONLY( Conv_Prim_Sat, Reg2, OpTempNumberProducing|OpTempNumberSources|OpCanCSE) // Convert between primitives (int/float), saturating OOB values MACRO_BACKEND_ONLY( Conv_Bool, Reg2, OpTempNumberSources|OpCanCSE) // Convert from i4 to bool MACRO_BACKEND_ONLY( Reinterpret_Prim, Reg2, OpTempNumberProducing|OpTempNumberSources|OpCanCSE) // Reinterpret bits between primitives (int32/float32) MACRO_BACKEND_ONLY( TrapIfTruncOverflow, Reg2, OpSideEffect) diff --git a/lib/Runtime/ByteCode/OpCodesAsmJs.h b/lib/Runtime/ByteCode/OpCodesAsmJs.h index 5719e37d3d4..80c087b3934 100755 --- a/lib/Runtime/ByteCode/OpCodesAsmJs.h +++ b/lib/Runtime/ByteCode/OpCodesAsmJs.h @@ -314,6 +314,15 @@ MACRO_EXTEND_WMS( Conv_Check_FTUL , Long1Float1 , None MACRO_EXTEND_WMS( Conv_Check_DTL , Long1Double1 , None ) MACRO_EXTEND_WMS( Conv_Check_DTUL , Long1Double1 , None ) +MACRO_EXTEND_WMS( Conv_Sat_DTI , Int1Double1 , None ) +MACRO_EXTEND_WMS( Conv_Sat_FTI , Int1Float1 , None ) +MACRO_EXTEND_WMS( Conv_Sat_DTU , Int1Double1 , None ) +MACRO_EXTEND_WMS( Conv_Sat_FTU , Int1Float1 , None ) +MACRO_EXTEND_WMS( Conv_Sat_FTL , Long1Float1 , None ) +MACRO_EXTEND_WMS( Conv_Sat_FTUL , Long1Float1 , None ) +MACRO_EXTEND_WMS( Conv_Sat_DTL , Long1Double1 , None ) +MACRO_EXTEND_WMS( Conv_Sat_DTUL , Long1Double1 , None ) + // InOut tracing opcodes MACRO_EXTEND_WMS( PrintFuncName , Int2, None) diff --git a/lib/Runtime/Language/InterpreterHandlerAsmJs.inl b/lib/Runtime/Language/InterpreterHandlerAsmJs.inl index 40b73452bb8..812a06da63c 100755 --- a/lib/Runtime/Language/InterpreterHandlerAsmJs.inl +++ b/lib/Runtime/Language/InterpreterHandlerAsmJs.inl @@ -259,14 +259,24 @@ EXDEF2_WMS( D1toD1Mem , Nearest_Db , Wasm::WasmMath::Nearest ) +EXDEF2_WMS( F1toI1Ctx , Conv_Check_FTI , Wasm::WasmMath::F32ToI32 ) +EXDEF2_WMS( D1toI1Ctx , Conv_Check_DTU , Wasm::WasmMath::F64ToU32 ) +EXDEF2_WMS( F1toI1Ctx , Conv_Check_FTU , Wasm::WasmMath::F32ToU32 ) +EXDEF2_WMS( F1toL1Ctx , Conv_Check_FTL , Wasm::WasmMath::F32ToI64 ) +EXDEF2_WMS( F1toL1Ctx , Conv_Check_FTUL , Wasm::WasmMath::F32ToU64 ) +EXDEF2_WMS( D1toL1Ctx , Conv_Check_DTL , Wasm::WasmMath::F64ToI64 ) +EXDEF2_WMS( D1toL1Ctx , Conv_Check_DTUL , Wasm::WasmMath::F64ToU64 ) + + +EXDEF2_WMS( D1toI1Ctx , Conv_Sat_DTI , Wasm::WasmMath::F64ToI32 ) +EXDEF2_WMS( F1toI1Ctx , Conv_Sat_FTI , Wasm::WasmMath::F32ToI32 ) +EXDEF2_WMS( D1toI1Ctx , Conv_Sat_DTU , Wasm::WasmMath::F64ToU32 ) +EXDEF2_WMS( F1toI1Ctx , Conv_Sat_FTU , Wasm::WasmMath::F32ToU32 ) +EXDEF2_WMS( F1toL1Ctx , Conv_Sat_FTL , Wasm::WasmMath::F32ToI64 ) +EXDEF2_WMS( F1toL1Ctx , Conv_Sat_FTUL , Wasm::WasmMath::F32ToU64 ) +EXDEF2_WMS( D1toL1Ctx , Conv_Sat_DTL , Wasm::WasmMath::F64ToI64 ) +EXDEF2_WMS( D1toL1Ctx , Conv_Sat_DTUL , Wasm::WasmMath::F64ToU64 ) DEF2_WMS( IP_TARG_ASM , AsmJsLoopBodyStart, OP_ProfiledLoopBodyStart ) DEF2_WMS( IP_TARG_ASM , WasmLoopBodyStart , OP_ProfiledWasmLoopBodyStart ) diff --git a/lib/Runtime/Language/JavascriptConversion.cpp b/lib/Runtime/Language/JavascriptConversion.cpp index dcbc1d8a104..a5ac7a5ab00 100644 --- a/lib/Runtime/Language/JavascriptConversion.cpp +++ b/lib/Runtime/Language/JavascriptConversion.cpp @@ -1412,102 +1412,6 @@ namespace Js return static_cast(aValue); } - int32 JavascriptConversion::F32TOI32(float src, ScriptContext * ctx) - { - if (Wasm::WasmMath::isInRange, &Wasm::WasmMath::LessOrEqual>(src) && - !Wasm::WasmMath::isNaN(src)) - { - return (int32)src; - } - - JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow); - } - - uint32 JavascriptConversion::F32TOU32(float src, ScriptContext * ctx) - { - if (Wasm::WasmMath::isInRange, &Wasm::WasmMath::LessThan>(src) && - !Wasm::WasmMath::isNaN(src)) - { - return (uint32)src; - } - - JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow); - } - - int32 JavascriptConversion::F64TOI32(double src, ScriptContext * ctx) - { - if (Wasm::WasmMath::isInRange, &Wasm::WasmMath::LessOrEqual>(src) && - !Wasm::WasmMath::isNaN(src)) - { - return (int32)src; - } - - JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow); - } - - uint32 JavascriptConversion::F64TOU32(double src, ScriptContext * ctx) - { - if (Wasm::WasmMath::isInRange, &Wasm::WasmMath::LessThan>(src) - && !Wasm::WasmMath::isNaN(src)) - { - return (uint32)src; - } - - JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow); - } - - int64 JavascriptConversion::F32TOI64(float src, ScriptContext * ctx) - { - if (Wasm::WasmMath::isInRange, &Wasm::WasmMath::LessOrEqual>(src) && - !Wasm::WasmMath::isNaN(src)) - { - return (int64)src; - } - - JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow); - } - - uint64 JavascriptConversion::F32TOU64(float src, ScriptContext * ctx) - { - if (Wasm::WasmMath::isInRange, &Wasm::WasmMath::LessThan>(src) && - !Wasm::WasmMath::isNaN(src)) - { - return (uint64)src; - } - - JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow); - } - - int64 JavascriptConversion::F64TOI64(double src, ScriptContext * ctx) - { - if (Wasm::WasmMath::isInRange, &Wasm::WasmMath::LessOrEqual>(src) && - !Wasm::WasmMath::isNaN(src)) - { - return (int64)src; - } - - JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow); - } - - uint64 JavascriptConversion::F64TOU64(double src, ScriptContext * ctx) - { - if (Wasm::WasmMath::isInRange, &Wasm::WasmMath::LessThan>(src) && - !Wasm::WasmMath::isNaN(src)) - { - return (uint64)src; - } - - JavascriptError::ThrowWebAssemblyRuntimeError(ctx, VBSERR_Overflow); - } - int64 JavascriptConversion::ToLength(Var aValue, ScriptContext* scriptContext) { if (TaggedInt::Is(aValue)) diff --git a/lib/Runtime/Language/JavascriptConversion.h b/lib/Runtime/Language/JavascriptConversion.h index b2900b81804..b091f387e0f 100644 --- a/lib/Runtime/Language/JavascriptConversion.h +++ b/lib/Runtime/Language/JavascriptConversion.h @@ -82,14 +82,6 @@ namespace Js { static JavascriptString * ToPrimitiveString(Var aValue, ScriptContext * scriptContext); static int64 ToLength(Var aValue, ScriptContext* scriptContext); - static int64 F32TOI64(float src, ScriptContext * ctx); - static uint64 F32TOU64(float src, ScriptContext * ctx); - static int64 F64TOI64(double src, ScriptContext * ctx); - static uint64 F64TOU64(double src, ScriptContext * ctx); - static int32 F32TOI32(float src, ScriptContext * ctx); - static uint32 F32TOU32(float src, ScriptContext * ctx); - static int32 F64TOI32(double src, ScriptContext * ctx); - static uint32 F64TOU32(double src, ScriptContext * ctx); static float LongToFloat(__int64 aValue); static float ULongToFloat(unsigned __int64 aValue); diff --git a/lib/Runtime/Library/WabtInterface.cpp b/lib/Runtime/Library/WabtInterface.cpp index c73aa415d67..f7ec7f5f71f 100644 --- a/lib/Runtime/Library/WabtInterface.cpp +++ b/lib/Runtime/Library/WabtInterface.cpp @@ -154,6 +154,7 @@ Js::Var WabtInterface::EntryConvertWast2Wasm(RecyclableObject* function, CallInf wabtCtx.features.sign_extends = CONFIG_FLAG(WasmSignExtends); wabtCtx.features.threads = Wasm::Threads::IsEnabled(); wabtCtx.features.simd = Wasm::Simd::IsEnabled(); + wabtCtx.features.sat_float_to_int = Wasm::NontrappingConversions::IsEnabled(); if (isSpecText) { wabtCtx.spec = &spec; diff --git a/lib/Runtime/Math/Chakra.Runtime.Math.vcxproj b/lib/Runtime/Math/Chakra.Runtime.Math.vcxproj index db7f3340d0a..3f7347b27f1 100644 --- a/lib/Runtime/Math/Chakra.Runtime.Math.vcxproj +++ b/lib/Runtime/Math/Chakra.Runtime.Math.vcxproj @@ -68,4 +68,4 @@ - + \ No newline at end of file diff --git a/lib/Runtime/Math/WasmMath.h b/lib/Runtime/Math/WasmMath.h index bc7179defa6..afe54a81981 100644 --- a/lib/Runtime/Math/WasmMath.h +++ b/lib/Runtime/Math/WasmMath.h @@ -25,10 +25,39 @@ class WasmMath template bool static LessThan(T aLeft, T aRight); template bool static LessOrEqual(T aLeft, T aRight); template using CmpPtr = bool(*)(T a, T b); - template CMP1, CmpPtr CMP2> static bool isInRange(STYPE srcVal); - template static bool isNaN(STYPE src); + template < + typename SourceType, + typename DstType, + typename ReinterpretType, + ReinterpretType Max, + ReinterpretType NegZero, + ReinterpretType NegOne, + CmpPtr MaxCmp, + CmpPtr NegOneCmp, + bool Saturate, + DstType MinResult, + DstType MaxResult> + static DstType ConvertFloatToInt(SourceType srcVal, _In_ Js::ScriptContext* scriptContext); + template static bool IsNaN(STYPE src); template static To SignExtend(To value); + + template + static int64 F32ToI64(float src, _In_ Js::ScriptContext* scriptContext); + template + static uint64 F32ToU64(float src, _In_ Js::ScriptContext* scriptContext); + template + static int64 F64ToI64(double src, _In_ Js::ScriptContext* scriptContext); + template + static uint64 F64ToU64(double src, _In_ Js::ScriptContext* scriptContext); + template + static int32 F32ToI32(float src, _In_ Js::ScriptContext* scriptContext); + template + static uint32 F32ToU32(float src, _In_ Js::ScriptContext* scriptContext); + template + static int32 F64ToI32(double src, _In_ Js::ScriptContext* scriptContext); + template + static uint32 F64ToU32(double src, _In_ Js::ScriptContext* scriptContext); }; } //namespace Wasm diff --git a/lib/Runtime/Math/WasmMath.inl b/lib/Runtime/Math/WasmMath.inl index fc0959025d3..483c7b51ec5 100644 --- a/lib/Runtime/Math/WasmMath.inl +++ b/lib/Runtime/Math/WasmMath.inl @@ -4,8 +4,8 @@ //------------------------------------------------------------------------------------------------------- #pragma once -namespace Wasm -{ +using namespace Wasm; + template inline T WasmMath::Shl( T aLeft, T aRight ) { @@ -130,21 +130,44 @@ template bool WasmMath::LessOrEqual(T aLeft, T aRight) return aLeft <= aRight; } -template CMP1, - WasmMath::CmpPtr CMP2> -bool WasmMath::isInRange(STYPE srcVal) +template < + typename SrcType, + typename DstType, + typename ReinterpretType, + ReinterpretType Max, + ReinterpretType NegZero, + ReinterpretType NegOne, + WasmMath::CmpPtr MaxCmp, + WasmMath::CmpPtr NegOneCmp, + bool Saturate, + DstType MinResult, + DstType MaxResult> +DstType WasmMath::ConvertFloatToInt(SrcType srcVal, _In_ Js::ScriptContext * scriptContext) { - Assert(sizeof(STYPE) == sizeof(UTYPE)); - UTYPE val = *reinterpret_cast (&srcVal); - return (CMP1(val, MAX)) || (val >= NEG_ZERO && CMP2(val, NEG_ONE)); + CompileAssert(sizeof(SrcType) == sizeof(ReinterpretType)); + + if (IsNaN(srcVal)) + { + if (!Saturate) + { + Js::JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, VBSERR_Overflow); + } + return 0; + } + + ReinterpretType val = *reinterpret_cast (&srcVal); + if (MaxCmp(val, Max) || (val >= NegZero && NegOneCmp(val, NegOne))) + { + return static_cast(srcVal); + } + if (!Saturate) + { + Js::JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, VBSERR_Overflow); + } + return (srcVal < 0) ? MinResult : MaxResult; } -template bool WasmMath::isNaN(STYPE src) +template bool WasmMath::IsNaN(STYPE src) { return src != src; } @@ -229,4 +252,154 @@ To WasmMath::SignExtend(To value) return static_cast(static_cast(value)); } +template +int32 WasmMath::F32ToI32(float src, _In_ Js::ScriptContext* scriptContext) +{ + return WasmMath::ConvertFloatToInt< + float, // SrcType + int32, // DstType + uint32, // ReinterpretType + Js::NumberConstants::k_Float32TwoTo31, + Js::NumberConstants::k_Float32NegZero, + Js::NumberConstants::k_Float32NegTwoTo31, + &WasmMath::LessThan, + &WasmMath::LessOrEqual, + Saturate, + INT32_MIN, + INT32_MAX>( + src, + scriptContext); +} + +template +uint32 WasmMath::F32ToU32(float src, _In_ Js::ScriptContext* scriptContext) +{ + return WasmMath::ConvertFloatToInt< + float, // SrcType + uint32, // DstType + uint32, // ReinterpretType + Js::NumberConstants::k_Float32TwoTo32, + Js::NumberConstants::k_Float32NegZero, + Js::NumberConstants::k_Float32NegOne, + &WasmMath::LessThan, + &WasmMath::LessThan, + Saturate, + 0, + UINT32_MAX>( + src, + scriptContext); +} + +template +int32 WasmMath::F64ToI32(double src, _In_ Js::ScriptContext* scriptContext) +{ + return WasmMath::ConvertFloatToInt< + double, // SrcType + int32, // DstType + uint64, // ReinterpretType + Js::NumberConstants::k_TwoTo31, + Js::NumberConstants::k_NegZero, + Js::NumberConstants::k_NegTwoTo31, + &WasmMath::LessOrEqual, + &WasmMath::LessOrEqual, + Saturate, + INT32_MIN, + INT32_MAX>( + src, + scriptContext); +} + +template +uint32 WasmMath::F64ToU32(double src, _In_ Js::ScriptContext* scriptContext) +{ + return WasmMath::ConvertFloatToInt< + double, // SrcType + uint32, // DstType + uint64, // ReinterpretType + Js::NumberConstants::k_TwoTo32, + Js::NumberConstants::k_NegZero, + Js::NumberConstants::k_NegOne, + &WasmMath::LessOrEqual, + &WasmMath::LessThan, + Saturate, + 0, + UINT32_MAX>( + src, + scriptContext); +} + +template +int64 WasmMath::F32ToI64(float src, _In_ Js::ScriptContext* scriptContext) +{ + return WasmMath::ConvertFloatToInt< + float, // SrcType + int64, // DstType + uint32, // ReinterpretType + Js::NumberConstants::k_Float32TwoTo63, + Js::NumberConstants::k_Float32NegZero, + Js::NumberConstants::k_Float32NegTwoTo63, + &WasmMath::LessThan, + &WasmMath::LessOrEqual, + Saturate, + INT64_MIN, + INT64_MAX>( + src, + scriptContext); +} + +template +uint64 WasmMath::F32ToU64(float src, _In_ Js::ScriptContext* scriptContext) +{ + return WasmMath::ConvertFloatToInt< + float, // SrcType + uint64, // DstType + uint32, // ReinterpretType + Js::NumberConstants::k_Float32TwoTo64, + Js::NumberConstants::k_Float32NegZero, + Js::NumberConstants::k_Float32NegOne, + &WasmMath::LessThan, + &WasmMath::LessThan, + Saturate, + 0, + UINT64_MAX>( + src, + scriptContext); +} + +template +int64 WasmMath::F64ToI64(double src, _In_ Js::ScriptContext* scriptContext) +{ + return WasmMath::ConvertFloatToInt< + double, // SrcType + int64, // DstType + uint64, // ReinterpretType + Js::NumberConstants::k_TwoTo63, + Js::NumberConstants::k_NegZero, + Js::NumberConstants::k_NegTwoTo63, + &WasmMath::LessThan, + &WasmMath::LessOrEqual, + Saturate, + INT64_MIN, + INT64_MAX>( + src, + scriptContext); +} + +template +uint64 WasmMath::F64ToU64(double src, _In_ Js::ScriptContext* scriptContext) +{ + return WasmMath::ConvertFloatToInt< + double, // SrcType + uint64, // DstType + uint64, // ReinterpretType + Js::NumberConstants::k_TwoTo64, + Js::NumberConstants::k_NegZero, + Js::NumberConstants::k_NegOne, + &WasmMath::LessThan, + &WasmMath::LessThan, + Saturate, + 0, + UINT64_MAX>( + src, + scriptContext); } diff --git a/lib/WasmReader/WasmBinaryOpCodes.h b/lib/WasmReader/WasmBinaryOpCodes.h index 9b38543c95d..bec75834bf9 100755 --- a/lib/WasmReader/WasmBinaryOpCodes.h +++ b/lib/WasmReader/WasmBinaryOpCodes.h @@ -60,9 +60,11 @@ #define WASM_PREFIX(prefixname, op, imp, errorMsg) #endif -#define WASM_PREFIX_THREADS 0xfe #define WASM_PREFIX_TRACING 0xf0 +#define WASM_PREFIX_NUMERIC 0xfc +#define WASM_PREFIX_THREADS 0xfe +WASM_PREFIX(Numeric, WASM_PREFIX_NUMERIC, Wasm::NontrappingConversions::IsEnabled(), "WebAssembly nontrapping float-to-int conversion support is not enabled") WASM_PREFIX(Threads, WASM_PREFIX_THREADS, Wasm::Threads::IsEnabled(), "WebAssembly Threads support is not enabled") #if ENABLE_DEBUG_CONFIG_OPTIONS // We won't even look at that prefix in release builds @@ -303,6 +305,20 @@ WASM_UNARY__OPCODE(I64TruncU_F32, 0xaf, L_F , Conv_Check_FTUL, true, "i64.tr WASM_UNARY__OPCODE(I64TruncS_F64, 0xb0, L_D , Conv_Check_DTL , true, "i64.trunc_s/f64") WASM_UNARY__OPCODE(I64TruncU_F64, 0xb1, L_D , Conv_Check_DTUL, true, "i64.trunc_u/f64") +#define __has_nontrapping (Wasm::NontrappingConversions::IsEnabled()) +#define __prefix (WASM_PREFIX_NUMERIC << 8) +WASM_UNARY__OPCODE(I32SatTruncS_F32, __prefix | 0x00, I_F, Conv_Sat_FTI, __has_nontrapping, "i32.trunc_s:sat/f32") +WASM_UNARY__OPCODE(I32SatTruncU_F32, __prefix | 0x01, I_F, Conv_Sat_FTU, __has_nontrapping, "i32.trunc_u:sat/f32") +WASM_UNARY__OPCODE(I32SatTruncS_F64, __prefix | 0x02, I_D, Conv_Sat_DTI, __has_nontrapping, "i32.trunc_s:sat/f64") +WASM_UNARY__OPCODE(I32SatTruncU_F64, __prefix | 0x03, I_D, Conv_Sat_DTU, __has_nontrapping, "i32.trunc_u:sat/f64") + +WASM_UNARY__OPCODE(I64SatTruncS_F32, __prefix | 0x04, L_F, Conv_Sat_FTL, __has_nontrapping, "i64.trunc_s:sat/f32") +WASM_UNARY__OPCODE(I64SatTruncU_F32, __prefix | 0x05, L_F, Conv_Sat_FTUL, __has_nontrapping, "i64.trunc_u:sat/f32") +WASM_UNARY__OPCODE(I64SatTruncS_F64, __prefix | 0x06, L_D, Conv_Sat_DTL, __has_nontrapping, "i64.trunc_s:sat/f64") +WASM_UNARY__OPCODE(I64SatTruncU_F64, __prefix | 0x07, L_D, Conv_Sat_DTUL, __has_nontrapping, "i64.trunc_u:sat/f64") +#undef __has_nontrapping +#undef __prefix + WASM_UNARY__OPCODE(F32SConvertI32, 0xb2, F_I , Fround_Int , true, "f32.convert_s/i32") WASM_UNARY__OPCODE(F32UConvertI32, 0xb3, F_I , Conv_UTF , true, "f32.convert_u/i32") WASM_UNARY__OPCODE(F32SConvertI64, 0xb4, F_L , Conv_LTF , true, "f32.convert_s/i64") @@ -417,6 +433,7 @@ WASM_UNARY__OPCODE(PrintF64 , __prefix | 0x0f, D_D , PrintF64 , #endif #undef WASM_PREFIX_THREADS +#undef WASM_PREFIX_NUMERIC #undef WASM_PREFIX_TRACING #undef WASM_PREFIX #undef WASM_OPCODE diff --git a/lib/WasmReader/WasmParseTree.cpp b/lib/WasmReader/WasmParseTree.cpp index e801186eba1..2707fc6972b 100644 --- a/lib/WasmReader/WasmParseTree.cpp +++ b/lib/WasmReader/WasmParseTree.cpp @@ -31,8 +31,22 @@ bool IsEnabled() #endif } } + +namespace NontrappingConversions +{ + bool IsEnabled() + { +#ifdef ENABLE_WASM + return CONFIG_FLAG_RELEASE(NontrappingConversions); +#else + return false; +#endif + } } +} + + #ifdef ENABLE_WASM namespace Wasm diff --git a/lib/WasmReader/WasmParseTree.h b/lib/WasmReader/WasmParseTree.h index 9deef566c29..253db4ab611 100644 --- a/lib/WasmReader/WasmParseTree.h +++ b/lib/WasmReader/WasmParseTree.h @@ -21,6 +21,11 @@ namespace Wasm bool IsEnabled(); }; + namespace NontrappingConversions + { + bool IsEnabled(); + }; + namespace WasmTypes { enum WasmType diff --git a/lib/wabt/chakra/wabtapi.cc b/lib/wabt/chakra/wabtapi.cc index a04abb3fe3b..ad8969e5c3a 100644 --- a/lib/wabt/chakra/wabtapi.cc +++ b/lib/wabt/chakra/wabtapi.cc @@ -76,6 +76,10 @@ Features GetWabtFeatures(const ChakraContext& ctx) { features.enable_simd(); } + if (ctx.features.sat_float_to_int) + { + features.enable_sat_float_to_int(); + } return features; } diff --git a/lib/wabt/chakra/wabtapi.h b/lib/wabt/chakra/wabtapi.h index 5b68e06c217..eda7a634894 100644 --- a/lib/wabt/chakra/wabtapi.h +++ b/lib/wabt/chakra/wabtapi.h @@ -72,6 +72,7 @@ namespace ChakraWabt bool sign_extends : 1; bool threads : 1; bool simd : 1; + bool sat_float_to_int : 1; } features; }; From e1053fa2c1923e0e4377fc205bc52a6e4db779d0 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Thu, 19 Apr 2018 14:37:00 -0700 Subject: [PATCH 2/4] add spec tests; rename flag --- lib/Common/ConfigFlagsList.h | 2 +- lib/Runtime/Library/WabtInterface.cpp | 2 +- lib/WasmReader/WasmBinaryOpCodes.h | 4 +- lib/WasmReader/WasmParseTree.cpp | 4 +- lib/WasmReader/WasmParseTree.h | 2 +- .../nontrapping_conversions.baseline | 1 + .../features/nontrapping/conversions.wast | 200 ++++++++++++++++++ test/WasmSpec/rlexe.xml | 7 + 8 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 test/WasmSpec/baselines/nontrapping_conversions.baseline create mode 100644 test/WasmSpec/features/nontrapping/conversions.wast diff --git a/lib/Common/ConfigFlagsList.h b/lib/Common/ConfigFlagsList.h index 776fd1cf215..51d3afc33b1 100644 --- a/lib/Common/ConfigFlagsList.h +++ b/lib/Common/ConfigFlagsList.h @@ -923,7 +923,7 @@ FLAGR(Boolean, WasmExperimental, "Enable WebAssembly experimental features", DEF // Not having the DEFAULT_CONFIG_XXXX macro ensures we use CONFIG_FLAG_RELEASE instead of CONFIG_FLAG FLAGPR_EXPERIMENTAL_WASM(Boolean, WasmSignExtends , "Use new WebAssembly sign extension operators") FLAGPR_EXPERIMENTAL_WASM(Boolean, WasmSimd , "Enable SIMD in WebAssembly") -FLAGPR_EXPERIMENTAL_WASM(Boolean, NontrappingConversions, "Enable non-trapping float-to-int conversions in WebAssembly") +FLAGPR_EXPERIMENTAL_WASM(Boolean, WasmNontrapping, "Enable non-trapping float-to-int conversions in WebAssembly") FLAGNR(Boolean, AssertBreak , "Debug break on assert", false) FLAGNR(Boolean, AssertPopUp , "Pop up asserts (default: false)", false) diff --git a/lib/Runtime/Library/WabtInterface.cpp b/lib/Runtime/Library/WabtInterface.cpp index f7ec7f5f71f..4129a752f1c 100644 --- a/lib/Runtime/Library/WabtInterface.cpp +++ b/lib/Runtime/Library/WabtInterface.cpp @@ -154,7 +154,7 @@ Js::Var WabtInterface::EntryConvertWast2Wasm(RecyclableObject* function, CallInf wabtCtx.features.sign_extends = CONFIG_FLAG(WasmSignExtends); wabtCtx.features.threads = Wasm::Threads::IsEnabled(); wabtCtx.features.simd = Wasm::Simd::IsEnabled(); - wabtCtx.features.sat_float_to_int = Wasm::NontrappingConversions::IsEnabled(); + wabtCtx.features.sat_float_to_int = Wasm::WasmNontrapping::IsEnabled(); if (isSpecText) { wabtCtx.spec = &spec; diff --git a/lib/WasmReader/WasmBinaryOpCodes.h b/lib/WasmReader/WasmBinaryOpCodes.h index bec75834bf9..b3979de3970 100755 --- a/lib/WasmReader/WasmBinaryOpCodes.h +++ b/lib/WasmReader/WasmBinaryOpCodes.h @@ -64,7 +64,7 @@ #define WASM_PREFIX_NUMERIC 0xfc #define WASM_PREFIX_THREADS 0xfe -WASM_PREFIX(Numeric, WASM_PREFIX_NUMERIC, Wasm::NontrappingConversions::IsEnabled(), "WebAssembly nontrapping float-to-int conversion support is not enabled") +WASM_PREFIX(Numeric, WASM_PREFIX_NUMERIC, Wasm::WasmNontrapping::IsEnabled(), "WebAssembly nontrapping float-to-int conversion support is not enabled") WASM_PREFIX(Threads, WASM_PREFIX_THREADS, Wasm::Threads::IsEnabled(), "WebAssembly Threads support is not enabled") #if ENABLE_DEBUG_CONFIG_OPTIONS // We won't even look at that prefix in release builds @@ -305,7 +305,7 @@ WASM_UNARY__OPCODE(I64TruncU_F32, 0xaf, L_F , Conv_Check_FTUL, true, "i64.tr WASM_UNARY__OPCODE(I64TruncS_F64, 0xb0, L_D , Conv_Check_DTL , true, "i64.trunc_s/f64") WASM_UNARY__OPCODE(I64TruncU_F64, 0xb1, L_D , Conv_Check_DTUL, true, "i64.trunc_u/f64") -#define __has_nontrapping (Wasm::NontrappingConversions::IsEnabled()) +#define __has_nontrapping (Wasm::WasmNontrapping::IsEnabled()) #define __prefix (WASM_PREFIX_NUMERIC << 8) WASM_UNARY__OPCODE(I32SatTruncS_F32, __prefix | 0x00, I_F, Conv_Sat_FTI, __has_nontrapping, "i32.trunc_s:sat/f32") WASM_UNARY__OPCODE(I32SatTruncU_F32, __prefix | 0x01, I_F, Conv_Sat_FTU, __has_nontrapping, "i32.trunc_u:sat/f32") diff --git a/lib/WasmReader/WasmParseTree.cpp b/lib/WasmReader/WasmParseTree.cpp index 2707fc6972b..ee54758230c 100644 --- a/lib/WasmReader/WasmParseTree.cpp +++ b/lib/WasmReader/WasmParseTree.cpp @@ -32,12 +32,12 @@ bool IsEnabled() } } -namespace NontrappingConversions +namespace WasmNontrapping { bool IsEnabled() { #ifdef ENABLE_WASM - return CONFIG_FLAG_RELEASE(NontrappingConversions); + return CONFIG_FLAG_RELEASE(WasmNontrapping); #else return false; #endif diff --git a/lib/WasmReader/WasmParseTree.h b/lib/WasmReader/WasmParseTree.h index 253db4ab611..9e3e612dceb 100644 --- a/lib/WasmReader/WasmParseTree.h +++ b/lib/WasmReader/WasmParseTree.h @@ -21,7 +21,7 @@ namespace Wasm bool IsEnabled(); }; - namespace NontrappingConversions + namespace WasmNontrapping { bool IsEnabled(); }; diff --git a/test/WasmSpec/baselines/nontrapping_conversions.baseline b/test/WasmSpec/baselines/nontrapping_conversions.baseline new file mode 100644 index 00000000000..45c68e777b7 --- /dev/null +++ b/test/WasmSpec/baselines/nontrapping_conversions.baseline @@ -0,0 +1 @@ +181/181 tests passed. diff --git a/test/WasmSpec/features/nontrapping/conversions.wast b/test/WasmSpec/features/nontrapping/conversions.wast new file mode 100644 index 00000000000..4ca491177f2 --- /dev/null +++ b/test/WasmSpec/features/nontrapping/conversions.wast @@ -0,0 +1,200 @@ +(module + (func (export "i32.trunc_s:sat_f32") (param $x f32) (result i32) (i32.trunc_s:sat/f32 (get_local $x))) + (func (export "i32.trunc_u:sat_f32") (param $x f32) (result i32) (i32.trunc_u:sat/f32 (get_local $x))) + (func (export "i32.trunc_s:sat_f64") (param $x f64) (result i32) (i32.trunc_s:sat/f64 (get_local $x))) + (func (export "i32.trunc_u:sat_f64") (param $x f64) (result i32) (i32.trunc_u:sat/f64 (get_local $x))) + (func (export "i64.trunc_s:sat_f32") (param $x f32) (result i64) (i64.trunc_s:sat/f32 (get_local $x))) + (func (export "i64.trunc_u:sat_f32") (param $x f32) (result i64) (i64.trunc_u:sat/f32 (get_local $x))) + (func (export "i64.trunc_s:sat_f64") (param $x f64) (result i64) (i64.trunc_s:sat/f64 (get_local $x))) + (func (export "i64.trunc_u:sat_f64") (param $x f64) (result i64) (i64.trunc_u:sat/f64 (get_local $x))) +) + +;; Saturating conversions: test all the same values as the non-saturating conversions. + +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -0x1.19999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const 2147483520.0)) (i32.const 2147483520)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -2147483648.0)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const 2147483648.0)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -2147483904.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const inf)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -inf)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f32" (f32.const -nan:0x200000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const -0x1p-149)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 0x1.19999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 4294967040.0)) (i32.const -256)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const -0x1.ccccccp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const -0x1.fffffep-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const 4294967296.0)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const -1.0)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const inf)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const -inf)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const nan:0x200000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f32" (f32.const -nan:0x200000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -1.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -0x1.199999999999ap+0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -1.5)) (i32.const -1)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -1.9)) (i32.const -1)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -2.0)) (i32.const -2)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const 2147483647.0)) (i32.const 2147483647)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -2147483648.0)) (i32.const -2147483648)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const 2147483648.0)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -2147483649.0)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const inf)) (i32.const 0x7fffffff)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -inf)) (i32.const 0x80000000)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_s:sat_f64" (f64.const -nan:0x4000000000000)) (i32.const 0)) + +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const -0.0)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const -0x0.0000000000001p-1022)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 1.0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 0x1.199999999999ap+0)) (i32.const 1)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 1.5)) (i32.const 1)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 1.9)) (i32.const 1)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 2.0)) (i32.const 2)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 2147483648)) (i32.const -2147483648)) ;; 0x1.00000p+31 -> 8000 0000 +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 4294967295.0)) (i32.const -1)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const -0x1.ccccccccccccdp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const -0x1.fffffffffffffp-1)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 1e8)) (i32.const 100000000)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 4294967296.0)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const -1.0)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 1e16)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 1e30)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const 9223372036854775808)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const inf)) (i32.const 0xffffffff)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const -inf)) (i32.const 0x00000000)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const nan:0x4000000000000)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const -nan)) (i32.const 0)) +(assert_return (invoke "i32.trunc_u:sat_f64" (f64.const -nan:0x4000000000000)) (i32.const 0)) + +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -0x1.19999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const 9223371487098961920.0)) (i64.const 9223371487098961920)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const 9223372036854775808.0)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -9223373136366403584.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const inf)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -inf)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const nan:0x200000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f32" (f32.const -nan:0x200000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const 0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const -0x1p-149)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const 0x1.19999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const 4294967296)) (i64.const 4294967296)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const 18446742974197923840.0)) (i64.const -1099511627776)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const -0x1.ccccccp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const -0x1.fffffep-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const 18446744073709551616.0)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const -1.0)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const inf)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const -inf)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const nan:0x200000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f32" (f32.const -nan:0x200000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -1.0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -0x1.199999999999ap+0)) (i64.const -1)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -1.5)) (i64.const -1)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -1.9)) (i64.const -1)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -2.0)) (i64.const -2)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const 4294967296)) (i64.const 4294967296)) ;; 0x1.00000p+32 -> 1 0000 0000 +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -4294967296)) (i64.const -4294967296)) ;; -0x1.00000p+32 -> ffff ffff 0000 0000 +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const 9223372036854774784.0)) (i64.const 9223372036854774784)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -9223372036854775808.0)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const 9223372036854775808.0)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -9223372036854777856.0)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const inf)) (i64.const 0x7fffffffffffffff)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -inf)) (i64.const 0x8000000000000000)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const nan:0x4000000000000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_s:sat_f64" (f64.const -nan:0x4000000000000)) (i64.const 0)) + +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const -0.0)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const -0x0.0000000000001p-1022)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 1.0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 0x1.199999999999ap+0)) (i64.const 1)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 1.5)) (i64.const 1)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 4294967295)) (i64.const 0xffffffff)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 4294967296)) (i64.const 0x100000000)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 18446744073709549568.0)) (i64.const -2048)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const -0x1.ccccccccccccdp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const -0x1.fffffffffffffp-1)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 1e8)) (i64.const 100000000)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 1e16)) (i64.const 10000000000000000)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 9223372036854775808)) (i64.const -9223372036854775808)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const 18446744073709551616.0)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const -1.0)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const inf)) (i64.const 0xffffffffffffffff)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const -inf)) (i64.const 0x0000000000000000)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const nan:0x4000000000000)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const -nan)) (i64.const 0)) +(assert_return (invoke "i64.trunc_u:sat_f64" (f64.const -nan:0x4000000000000)) (i64.const 0)) diff --git a/test/WasmSpec/rlexe.xml b/test/WasmSpec/rlexe.xml index dd1eadf8099..7c6f22c0a8e 100644 --- a/test/WasmSpec/rlexe.xml +++ b/test/WasmSpec/rlexe.xml @@ -1260,6 +1260,13 @@ exclude_dynapogo + + + spec.js + baselines/nontrapping_conversions.baseline + -wasm -args features/nontrapping/conversions.wast -endargs -WasmNontrapping + + spec.js From 9ce2f13c724d16758d67890ae10357061c15567a Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Fri, 20 Apr 2018 18:02:15 -0700 Subject: [PATCH 3/4] cleanup lowering code; update convert-test-suite --- lib/Backend/LowerMDShared.cpp | 38 +++++++++++-------- test/WasmSpec/convert-test-suite/config.json | 10 +++++ ...ions.wast => nontrapping_conversions.wast} | 0 test/WasmSpec/rlexe.xml | 10 ++++- 4 files changed, 41 insertions(+), 17 deletions(-) rename test/WasmSpec/features/nontrapping/{conversions.wast => nontrapping_conversions.wast} (100%) diff --git a/lib/Backend/LowerMDShared.cpp b/lib/Backend/LowerMDShared.cpp index ee4a61b91e4..f0918069f24 100644 --- a/lib/Backend/LowerMDShared.cpp +++ b/lib/Backend/LowerMDShared.cpp @@ -4728,11 +4728,13 @@ LowererMD::GenerateTruncChecks(_In_ IR::Instr* instr, _In_opt_ IR::LabelInstr* d { AnalysisAssert(!Saturate || doneLabel); + IR::Opnd* dst = instr->GetDst(); + Assert(dst->IsInt32() || dst->IsUInt32()); + + IR::LabelInstr * nanLabel = (Saturate && dst->IsSigned()) ? IR::LabelInstr::New(Js::OpCode::Label, m_func, true) : nullptr; IR::LabelInstr * conversion = IR::LabelInstr::New(Js::OpCode::Label, m_func); - IR::LabelInstr * nanLabel = Saturate ? IR::LabelInstr::New(Js::OpCode::Label, m_func, true) : nullptr; - IR::LabelInstr * oobLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func, true); + IR::LabelInstr * tooSmallLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func, true); IR::Opnd* src1 = instr->GetSrc1(); - IR::Opnd* dst = instr->GetDst(); IR::Opnd * src64 = nullptr; if (src1->IsFloat32()) @@ -4749,7 +4751,7 @@ LowererMD::GenerateTruncChecks(_In_ IR::Instr* instr, _In_opt_ IR::LabelInstr* d m_func->GetThreadContextInfo()->GetDoubleNegOneAddr() : m_func->GetThreadContextInfo()->GetDoubleIntMinMinusOneAddr(), instr); - m_lowerer->InsertCompareBranch(src64, limitReg, Js::OpCode::BrLe_A, oobLabel, instr); + m_lowerer->InsertCompareBranch(src64, limitReg, Js::OpCode::BrLe_A, tooSmallLabel, instr); limitReg = MaterializeDoubleConstFromInt(dst->IsUInt32() ? m_func->GetThreadContextInfo()->GetDoubleUintMaxPlusOneAddr() : @@ -4757,29 +4759,33 @@ LowererMD::GenerateTruncChecks(_In_ IR::Instr* instr, _In_opt_ IR::LabelInstr* d m_lowerer->InsertCompareBranch(limitReg, src64, Js::OpCode::BrGt_A, conversion, instr, true /*no NaN check*/); - instr->InsertBefore(oobLabel); if (Saturate) { - IR::LabelInstr * tooBigLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func, true); - IR::RegOpnd* zeroReg = IR::RegOpnd::New(TyFloat64, m_func); - LoadFloatZero(zeroReg, instr); + // Insert a label to mark this as the start of a helper block, so layout knows to move it + m_lowerer->InsertLabel(true, instr); - m_lowerer->InsertCompareBranch(src64, zeroReg, Js::OpCode::BrGt_A, tooBigLabel, instr, true /*no NaN check*/); - instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::JP, nanLabel, m_func)); - - m_lowerer->InsertMove(dst, IR::IntConstOpnd::New(dst->IsUnsigned() ? 0 : INT32_MIN, dst->GetType(), m_func), instr); - m_lowerer->InsertBranch(Js::OpCode::Br, doneLabel, instr); + // NaN case is same as too small case for unsigned, so combine them + instr->InsertBefore(IR::BranchInstr::New(Js::OpCode::JP, dst->IsSigned() ? nanLabel : tooSmallLabel, m_func)); - instr->InsertBefore(tooBigLabel); + // Overflow case m_lowerer->InsertMove(dst, IR::IntConstOpnd::New(dst->IsUnsigned() ? UINT32_MAX : INT32_MAX, dst->GetType(), m_func), instr); m_lowerer->InsertBranch(Js::OpCode::Br, doneLabel, instr); - instr->InsertBefore(nanLabel); - m_lowerer->InsertMove(dst, IR::IntConstOpnd::New(0, dst->GetType(), m_func), instr); + instr->InsertBefore(tooSmallLabel); + m_lowerer->InsertMove(dst, IR::IntConstOpnd::New(dst->IsUnsigned() ? 0 : INT32_MIN, dst->GetType(), m_func), instr); m_lowerer->InsertBranch(Js::OpCode::Br, doneLabel, instr); + + + if (dst->IsSigned()) + { + instr->InsertBefore(nanLabel); + m_lowerer->InsertMove(dst, IR::IntConstOpnd::New(0, dst->GetType(), m_func), instr); + m_lowerer->InsertBranch(Js::OpCode::Br, doneLabel, instr); + } } else { + instr->InsertBefore(tooSmallLabel); m_lowerer->GenerateThrow(IR::IntConstOpnd::New(SCODE_CODE(VBSERR_Overflow), TyInt32, m_func), instr); //no jump here we aren't coming back } diff --git a/test/WasmSpec/convert-test-suite/config.json b/test/WasmSpec/convert-test-suite/config.json index 013b5f419be..39dc650caae 100644 --- a/test/WasmSpec/convert-test-suite/config.json +++ b/test/WasmSpec/convert-test-suite/config.json @@ -6,6 +6,7 @@ "testsuite/js-api", "features/extends", + "features/nontrapping", "features/threads" ], "features": [{ @@ -39,6 +40,15 @@ "chakra_extends_i32", "chakra_extends_i64" ] + }, { + "required": true, + "flags": ["-WasmNontrapping"], + "folders": [ + "features/nontrapping" + ], + "files": [ + "nontrapping_conversions" + ] }, { "required": true, "flags": ["-WasmThreads", "-ESSharedArrayBuffer"], diff --git a/test/WasmSpec/features/nontrapping/conversions.wast b/test/WasmSpec/features/nontrapping/nontrapping_conversions.wast similarity index 100% rename from test/WasmSpec/features/nontrapping/conversions.wast rename to test/WasmSpec/features/nontrapping/nontrapping_conversions.wast diff --git a/test/WasmSpec/rlexe.xml b/test/WasmSpec/rlexe.xml index 7c6f22c0a8e..5f7d3403eb8 100644 --- a/test/WasmSpec/rlexe.xml +++ b/test/WasmSpec/rlexe.xml @@ -1264,7 +1264,15 @@ spec.js baselines/nontrapping_conversions.baseline - -wasm -args features/nontrapping/conversions.wast -endargs -WasmNontrapping + -wasm -args features/nontrapping/nontrapping_conversions.wast -endargs -WasmNontrapping + + + + + spec.js + baselines/nontrapping_conversions.baseline + -wasm -args features/nontrapping/nontrapping_conversions.wast -endargs -nonative -WasmNontrapping + exclude_dynapogo From 7487cda937ea8aee4f85a61ee96ecd885cbc8f10 Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Tue, 24 Apr 2018 12:51:13 -0700 Subject: [PATCH 4/4] update bytecode GUID --- lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h b/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h index eac4552b699..736f6bf60e6 100644 --- a/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h +++ b/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h @@ -4,6 +4,6 @@ //------------------------------------------------------------------------------------------------------- // NOTE: If there is a merge conflict the correct fix is to make a new GUID. -// {B3776BBD-797D-4535-A15F-3BA14DB291DF} +// {752B5975-74CF-499D-8438-F7028EB3CAC4} const GUID byteCodeCacheReleaseFileVersion = -{ 0xB3776BBD, 0x797D, 0x4535, { 0xA1, 0x5F, 0x3B, 0xA1, 0x4D, 0xB2, 0x91, 0xDF } }; +{ 0x752B5975, 0x74CF, 0x499D, { 0x84, 0x38, 0xF7, 0x02, 0x8E, 0xB3, 0xCA, 0xC4 } };