Răsfoiți Sursa

On windows, do not emit TrapIfChecks for div/rem instead, catch the win32 exception and throw a WebAssembly exception instead
Add flag -WasmMathExFilter

Michael Ferris 8 ani în urmă
părinte
comite
3adb1df769

+ 1 - 1
lib/Backend/GlobOpt.cpp

@@ -12216,7 +12216,7 @@ bool GlobOpt::OptConstFoldBinaryWasm(
     }
 
     int64 tmpValueOut;
-    if (!instr->BinaryCalculatorT<T>(src1IntConstantValue, src2IntConstantValue, &tmpValueOut))
+    if (!instr->BinaryCalculatorT<T>(src1IntConstantValue, src2IntConstantValue, &tmpValueOut, func->GetJITFunctionBody()->IsWasmFunction()))
     {
         return false;
     }

+ 16 - 11
lib/Backend/IR.cpp

@@ -3744,19 +3744,24 @@ bool Instr::IsNeq()
 }
 
 template <typename T>
-bool Instr::BinaryCalculatorT(T src1Const, T src2Const, int64 *pResult)
+bool Instr::BinaryCalculatorT(T src1Const, T src2Const, int64 *pResult, bool checkWouldTrap)
 {
     T value = 0;
     switch (this->m_opcode)
     {
-#define BINARY_U(OPCODE,HANDLER) \
+#define DO_HANDLER(HANDLER, type) HANDLER(type##src1Const, type##src2Const)
+#define BINARY_CASE_CHECK(OPCODE,HANDLER,CHECK_HANDLER,type) \
     case Js::OpCode::##OPCODE: \
-        value = HANDLER((typename SignedTypeTraits<T>::UnsignedType)src1Const, (typename SignedTypeTraits<T>::UnsignedType)src2Const); \
+        if (checkWouldTrap && DO_HANDLER(CHECK_HANDLER,type)) { return false; } \
+        value = DO_HANDLER(HANDLER,type); \
         break;
-#define BINARY(OPCODE,HANDLER) \
+#define BINARY_CASE(OPCODE,HANDLER,type) \
     case Js::OpCode::##OPCODE: \
-        value = HANDLER(src1Const, src2Const); \
+        value = DO_HANDLER(HANDLER,type); \
         break;
+#define BINARY_U(OPCODE,HANDLER) BINARY_CASE(OPCODE,HANDLER,(typename SignedTypeTraits<T>::UnsignedType))
+#define BINARY(OPCODE,HANDLER)  BINARY_CASE(OPCODE,HANDLER,)
+
         BINARY(CmEq_I4, Js::AsmJsMath::CmpEq)
         BINARY(CmNeq_I4, Js::AsmJsMath::CmpNe)
         BINARY(CmLt_I4, Js::AsmJsMath::CmpLt)
@@ -3776,10 +3781,10 @@ bool Instr::BinaryCalculatorT(T src1Const, T src2Const, int64 *pResult)
         BINARY(Shl_I4, Wasm::WasmMath::Shl)
         BINARY(Shr_I4, Wasm::WasmMath::Shr)
         BINARY_U(ShrU_I4, Wasm::WasmMath::ShrU)
-        BINARY(Div_I4, Js::AsmJsMath::DivChecked)
-        BINARY_U(DivU_I4, Js::AsmJsMath::DivChecked)
-        BINARY(Rem_I4, Js::AsmJsMath::RemChecked)
-        BINARY_U(RemU_I4, Js::AsmJsMath::RemChecked)
+        BINARY_CASE_CHECK(DivU_I4, Js::AsmJsMath::DivChecked, Js::AsmJsMath::DivWouldTrap, (typename SignedTypeTraits<T>::UnsignedType))
+        BINARY_CASE_CHECK(Div_I4, Js::AsmJsMath::DivChecked, Js::AsmJsMath::DivWouldTrap, )
+        BINARY_CASE_CHECK(RemU_I4, Js::AsmJsMath::RemChecked, Js::AsmJsMath::RemWouldTrap, (typename SignedTypeTraits<T>::UnsignedType))
+        BINARY_CASE_CHECK(Rem_I4, Js::AsmJsMath::RemChecked, Js::AsmJsMath::RemWouldTrap, )
         default:
             return false;
 #undef BINARY
@@ -3790,8 +3795,8 @@ bool Instr::BinaryCalculatorT(T src1Const, T src2Const, int64 *pResult)
     return true;
 }
 
-template bool Instr::BinaryCalculatorT<int>(int src1Const64, int src2Const64, int64 *pResult);
-template bool Instr::BinaryCalculatorT<int64>(int64 src1Const64, int64 src2Const64, int64 *pResult);
+template bool Instr::BinaryCalculatorT<int>(int src1Const64, int src2Const64, int64 *pResult, bool checkWouldTrap);
+template bool Instr::BinaryCalculatorT<int64>(int64 src1Const64, int64 src2Const64, int64 *pResult, bool checkWouldTrap);
 
 bool Instr::BinaryCalculator(IntConstType src1Const, IntConstType src2Const, IntConstType *pResult)
 {

+ 1 - 1
lib/Backend/IR.h

@@ -334,7 +334,7 @@ public:
     bool            IsNeq();
     bool            BinaryCalculator(IntConstType src1Const, IntConstType src2Const, IntConstType *pResult);
     template <typename T>     
-    bool            BinaryCalculatorT(T src1Const, T src2Const, int64 *pResult);
+    bool            BinaryCalculatorT(T src1Const, T src2Const, int64 *pResult, bool checkWouldTrap);
     bool            UnaryCalculator(IntConstType src1Const, IntConstType *pResult);
     IR::Instr*      GetNextArg();
 

+ 44 - 12
lib/Backend/IRBuilderAsmJs.cpp

@@ -2408,13 +2408,21 @@ IRBuilderAsmJs::BuildInt3(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot
         src2Opnd->SetType(TyUint32);
         // Fall through for trap
     case Js::OpCodeAsmJs::Div_Trap_Int:
-#ifdef _M_X64
-        src2Opnd = BuildTrapIfZero(src2Opnd, offset);
-        if (newOpcode == Js::OpCodeAsmJs::Div_Trap_Int)
+#ifdef _WIN32
+        if (CONFIG_FLAG(WasmMathExFilter))
         {
-            src1Opnd = BuildTrapIfMinIntOverNegOne(src1Opnd, src2Opnd, offset);
+            // Do not emit traps, but make sure we don't remove the div
+            dstOpnd->m_dontDeadStore = true;
         }
+        else
 #endif
+        {
+            src2Opnd = BuildTrapIfZero(src2Opnd, offset);
+            if (newOpcode == Js::OpCodeAsmJs::Div_Trap_Int)
+            {
+                src1Opnd = BuildTrapIfMinIntOverNegOne(src1Opnd, src2Opnd, offset);
+            }
+        }
         instr = IR::Instr::New(newOpcode == Js::OpCodeAsmJs::Div_Trap_UInt ? Js::OpCode::DivU_I4 : Js::OpCode::Div_I4, dstOpnd, src1Opnd, src2Opnd, m_func);
         break;
     case Js::OpCodeAsmJs::Div_UInt:
@@ -2430,9 +2438,17 @@ IRBuilderAsmJs::BuildInt3(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot
         src2Opnd->SetType(TyUint32);
         // Fall through for trap
     case Js::OpCodeAsmJs::Rem_Trap_Int:
-#ifdef _M_X64
-        src2Opnd = BuildTrapIfZero(src2Opnd, offset);
+#ifdef _WIN32
+        if (CONFIG_FLAG(WasmMathExFilter))
+        {
+            // Do not emit traps, but make sure we don't remove the rem
+            dstOpnd->m_dontDeadStore = true;
+        }
+        else
 #endif
+        {
+            src2Opnd = BuildTrapIfZero(src2Opnd, offset);
+        }
         instr = IR::Instr::New(newOpcode == Js::OpCodeAsmJs::Rem_Trap_UInt ? Js::OpCode::RemU_I4 : Js::OpCode::Rem_I4, dstOpnd, src1Opnd, src2Opnd, m_func);
         break;
     case Js::OpCodeAsmJs::Rem_UInt:
@@ -3023,13 +3039,21 @@ IRBuilderAsmJs::BuildLong3(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot
         // Fall Through for trap
     case Js::OpCodeAsmJs::Div_Trap_Long:
     {
-#ifdef _M_X64
-        src2Opnd = BuildTrapIfZero(src2Opnd, offset);
-        if (newOpcode == Js::OpCodeAsmJs::Div_Trap_Long)
+#ifdef _WIN32
+        if (CONFIG_FLAG(WasmMathExFilter))
         {
-            src1Opnd = BuildTrapIfMinIntOverNegOne(src1Opnd, src2Opnd, offset);
+            // Do not emit traps, but make sure we don't remove the div
+            dstOpnd->m_dontDeadStore = true;
         }
+        else
 #endif
+        {
+            src2Opnd = BuildTrapIfZero(src2Opnd, offset);
+            if (newOpcode == Js::OpCodeAsmJs::Div_Trap_Long)
+            {
+                src1Opnd = BuildTrapIfMinIntOverNegOne(src1Opnd, src2Opnd, offset);
+            }
+        }
         Js::OpCode op = newOpcode == Js::OpCodeAsmJs::Div_Trap_ULong ? Js::OpCode::DivU_I4 : Js::OpCode::Div_I4;
         instr = IR::Instr::New(op, dstOpnd, src1Opnd, src2Opnd, m_func);
         break;
@@ -3040,9 +3064,17 @@ IRBuilderAsmJs::BuildLong3(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot
         // Fall Through for trap
     case Js::OpCodeAsmJs::Rem_Trap_Long:
     {
-#ifdef _M_X64
-        src2Opnd = BuildTrapIfZero(src2Opnd, offset);
+#ifdef _WIN32
+        if (CONFIG_FLAG(WasmMathExFilter))
+        {
+            // Do not emit traps, but make sure we don't remove the rem
+            dstOpnd->m_dontDeadStore = true;
+        }
+        else
 #endif
+        {
+            src2Opnd = BuildTrapIfZero(src2Opnd, offset);
+        }
         Js::OpCode op = newOpcode == Js::OpCodeAsmJs::Rem_Trap_ULong ? Js::OpCode::RemU_I4 : Js::OpCode::Rem_I4;
         instr = IR::Instr::New(op, dstOpnd, src1Opnd, src2Opnd, m_func);
         break;

+ 2 - 0
lib/Common/ConfigFlagsList.h

@@ -396,6 +396,7 @@ PHASE(All)
 #endif
 #define DEFAULT_CONFIG_WasmCheckVersion     (true)
 #define DEFAULT_CONFIG_WasmFold             (true)
+#define DEFAULT_CONFIG_WasmMathExFilter     (true)
 #define DEFAULT_CONFIG_WasmIgnoreResponse   (false)
 #define DEFAULT_CONFIG_WasmMaxTableSize     (10000000)
 #define DEFAULT_CONFIG_BgJitDelayFgBuffer   (0)
@@ -863,6 +864,7 @@ FLAGNR(Boolean, AsmJsEdge             , "Enable asm.js features which may have b
 FLAGNR(Boolean, Wasm                  , "Enable WebAssembly", DEFAULT_CONFIG_Wasm)
 FLAGNR(Boolean, WasmI64               , "Enable Int64 testing for WebAssembly. ArgIns can be [number,string,{low:number,high:number}]. Return values will be {low:number,high:number}", DEFAULT_CONFIG_WasmI64)
 FLAGNR(Boolean, WasmFastArray         , "Enable fast array implementation for WebAssembly", DEFAULT_CONFIG_WasmFastArray)
+FLAGNR(Boolean, WasmMathExFilter      , "Enable Math exception filter for WebAssembly", DEFAULT_CONFIG_WasmMathExFilter)
 FLAGNR(Boolean, WasmCheckVersion      , "Check the binary version for WebAssembly", DEFAULT_CONFIG_WasmCheckVersion)
 FLAGNR(Boolean, WasmFold              , "Enable i32/i64 const folding", DEFAULT_CONFIG_WasmFold)
 FLAGNR(Boolean, WasmIgnoreResponse    , "Ignore the type of the Response object", DEFAULT_CONFIG_WasmIgnoreResponse)

+ 105 - 33
lib/Runtime/Library/JavascriptFunction.cpp

@@ -1854,7 +1854,98 @@ LABEL1:
     9)  Return EXCEPTION_CONTINUE_EXECUTION
 
     */
-#if ENABLE_NATIVE_CODEGEN && defined(_M_X64)
+#if ENABLE_NATIVE_CODEGEN
+#if defined(_M_IX86) || defined(_M_X64)
+    class ExceptionFilterHelper
+    {
+        Js::ScriptFunction* m_func = nullptr;
+        bool m_checkedForFunc = false;
+        PEXCEPTION_POINTERS exceptionInfo;
+    public:
+        ExceptionFilterHelper(PEXCEPTION_POINTERS exceptionInfo) : exceptionInfo(exceptionInfo) {}
+        PEXCEPTION_POINTERS GetExceptionInfo() const
+        {
+            return exceptionInfo;
+        }
+        Var GetIPAddress() const
+        {
+#if _M_IX86
+            return (Var)exceptionInfo->ContextRecord->Eip;
+#elif _M_X64
+            return (Var)exceptionInfo->ContextRecord->Rip;
+#else
+#error Not yet Implemented
+#endif
+        }
+        Var* GetAddressOfFuncObj() const
+        {
+#if _M_IX86
+            return (Var*)(exceptionInfo->ContextRecord->Ebp + 2 * sizeof(Var));
+#elif _M_X64
+            return (Var*)(exceptionInfo->ContextRecord->Rbp + 2 * sizeof(Var));
+#else
+#error Not yet Implemented
+#endif
+        }
+        Js::ScriptFunction* GetScriptFunction()
+        {
+            if (m_checkedForFunc)
+            {
+                return m_func;
+            }
+            m_checkedForFunc = true;
+            ThreadContext* threadContext = ThreadContext::GetContextForCurrentThread();
+
+            // AV should come from JITed code, since we don't eliminate bound checks in interpreter
+            if (!threadContext->IsNativeAddress(GetIPAddress()))
+            {
+                return nullptr;
+            }
+
+            Var* addressOfFuncObj = GetAddressOfFuncObj();
+            if (!addressOfFuncObj || *addressOfFuncObj == nullptr || !ScriptFunction::Is(*addressOfFuncObj))
+            {
+                return nullptr;
+            }
+
+            Js::ScriptFunction* func = (Js::ScriptFunction*)(*addressOfFuncObj);
+
+            RecyclerHeapObjectInfo heapObject;
+            Recycler* recycler = threadContext->GetRecycler();
+
+            bool isFuncObjHeapAllocated = recycler->FindHeapObject(func, FindHeapObjectFlags_NoFlags, heapObject); // recheck if this needs to be removed
+            bool isEntryPointHeapAllocated = recycler->FindHeapObject(func->GetEntryPointInfo(), FindHeapObjectFlags_NoFlags, heapObject);
+            bool isFunctionBodyHeapAllocated = recycler->FindHeapObject(func->GetFunctionBody(), FindHeapObjectFlags_NoFlags, heapObject);
+
+            // ensure that all our objects are heap allocated
+            if (!(isFuncObjHeapAllocated && isEntryPointHeapAllocated && isFunctionBodyHeapAllocated))
+            {
+                return nullptr;
+            }
+            m_func = func;
+            return m_func;
+        }
+    };
+
+    void CheckWasmMathException(int exceptionCode, ExceptionFilterHelper& helper)
+    {
+        if (CONFIG_FLAG(WasmMathExFilter) && (exceptionCode == STATUS_INTEGER_DIVIDE_BY_ZERO || exceptionCode == STATUS_INTEGER_OVERFLOW))
+        {
+            Js::ScriptFunction* func = helper.GetScriptFunction();
+            if (func)
+            {
+                Js::FunctionBody* funcBody = func->GetFunctionBody();
+                if (funcBody && funcBody->IsWasmFunction())
+                {
+                    int32 code = exceptionCode == STATUS_INTEGER_DIVIDE_BY_ZERO ? WASMERR_DivideByZero : VBSERR_Overflow;
+                    JavascriptError::ThrowWebAssemblyRuntimeError(func->GetScriptContext(), code);
+                }
+            }
+        }
+    }
+
+    // x64 specific exception filters
+#ifdef _M_X64
     ArrayAccessDecoder::InstructionData ArrayAccessDecoder::CheckValidInstr(BYTE* &pc, PEXCEPTION_POINTERS exceptionInfo) // get the reg operand and isLoad and
     {
         InstructionData instrData;
@@ -2187,45 +2278,17 @@ LABEL1:
         return instrData;
     }
 
-    bool JavascriptFunction::ResumeForOutOfBoundsArrayRefs(int exceptionCode, PEXCEPTION_POINTERS exceptionInfo)
+    bool ResumeForOutOfBoundsArrayRefs(int exceptionCode, ExceptionFilterHelper& helper)
     {
         if (exceptionCode != STATUS_ACCESS_VIOLATION)
         {
             return false;
         }
-
-        ThreadContext* threadContext = ThreadContext::GetContextForCurrentThread();
-
-        // AV should come from JITed code, since we don't eliminate bound checks in interpreter
-        if (!threadContext->IsNativeAddress((Var)exceptionInfo->ContextRecord->Rip))
-        {
-            return false;
-        }
-
-        Var* addressOfFuncObj = (Var*)(exceptionInfo->ContextRecord->Rbp + 2 * sizeof(Var));
-        if (!addressOfFuncObj)
-        {
-            return false;
-        }
-
-        Js::ScriptFunction* func = (ScriptFunction::Is(*addressOfFuncObj))?(Js::ScriptFunction*)(*addressOfFuncObj):nullptr;
+        Js::ScriptFunction* func = helper.GetScriptFunction();
         if (!func)
         {
             return false;
         }
-
-        RecyclerHeapObjectInfo heapObject;
-        Recycler* recycler = threadContext->GetRecycler();
-
-        bool isFuncObjHeapAllocated = recycler->FindHeapObject(func, FindHeapObjectFlags_NoFlags, heapObject); // recheck if this needs to be removed
-        bool isEntryPointHeapAllocated = recycler->FindHeapObject(func->GetEntryPointInfo(), FindHeapObjectFlags_NoFlags, heapObject);
-        bool isFunctionBodyHeapAllocated = recycler->FindHeapObject(func->GetFunctionBody(), FindHeapObjectFlags_NoFlags, heapObject);
-
-        // ensure that all our objects are heap allocated
-        if (!(isFuncObjHeapAllocated && isEntryPointHeapAllocated && isFunctionBodyHeapAllocated))
-        {
-            return false;
-        }
         Js::FunctionBody* funcBody = func->GetFunctionBody();
         bool isWAsmJs = funcBody->GetIsAsmJsFunction();
         bool isWasmOnly = funcBody->IsWasmFunction();
@@ -2267,6 +2330,7 @@ LABEL1:
             }
         }
 
+        PEXCEPTION_POINTERS exceptionInfo = helper.GetExceptionInfo();
         BYTE* pc = (BYTE*)exceptionInfo->ExceptionRecord->ExceptionAddress;
         ArrayAccessDecoder::InstructionData instrData = ArrayAccessDecoder::CheckValidInstr(pc, exceptionInfo);
         // Check If the instruction is valid
@@ -2334,15 +2398,23 @@ LABEL1:
 
         return true;
     }
+#endif
+#endif
 #endif
 
     int JavascriptFunction::CallRootEventFilter(int exceptionCode, PEXCEPTION_POINTERS exceptionInfo)
     {
-#if ENABLE_NATIVE_CODEGEN && defined(_M_X64)
-        if (ResumeForOutOfBoundsArrayRefs(exceptionCode, exceptionInfo))
+#if ENABLE_NATIVE_CODEGEN
+#if defined(_M_IX86) || defined(_M_X64)
+        ExceptionFilterHelper helper(exceptionInfo);
+        CheckWasmMathException(exceptionCode, helper);
+#ifdef _M_X64
+        if (ResumeForOutOfBoundsArrayRefs(exceptionCode, helper))
         {
             return EXCEPTION_CONTINUE_EXECUTION;
         }
+#endif
+#endif
 #endif
         return EXCEPTION_CONTINUE_SEARCH;
     }

+ 0 - 3
lib/Runtime/Library/JavascriptFunction.h

@@ -216,9 +216,6 @@ namespace Js
 #endif
         private:
             static int CallRootEventFilter(int exceptionCode, PEXCEPTION_POINTERS exceptionInfo);
-#if ENABLE_NATIVE_CODEGEN && defined(_M_X64)
-            static bool ResumeForOutOfBoundsArrayRefs(int exceptionCode, PEXCEPTION_POINTERS exceptionInfo);
-#endif
     };
 #if ENABLE_NATIVE_CODEGEN && defined(_M_X64)
     class ArrayAccessDecoder

+ 8 - 0
lib/Runtime/Math/AsmJsMath.cpp

@@ -33,12 +33,14 @@ namespace Js
     {
         return aRight == 0 ? 0 : (aLeft == INT_MIN && aRight == -1) ? INT_MIN : aLeft / aRight;
     }
+    template<> bool AsmJsMath::DivWouldTrap(int32 aLeft, int32 aRight) { return aRight == 0 || (aLeft == INT_MIN && aRight == -1); }
     template<> int32 AsmJsMath::RemUnsafe<int32>(int32 aLeft, int32 aRight) { return aLeft % aRight; }
     template<>
     int32 AsmJsMath::RemChecked<int32>(int32 aLeft, int32 aRight)
     {
         return ((aRight == 0) || (aLeft == INT_MIN && aRight == -1)) ? 0 : aLeft % aRight;
     }
+    template<> bool AsmJsMath::RemWouldTrap(int32 aLeft, int32 aRight) { return aRight == 0 || (aLeft == INT_MIN && aRight == -1); }
 
     // Unsigned Int32
     template<> uint32 AsmJsMath::Mul<uint32>(uint32 aLeft, uint32 aRight) { return aLeft * aRight; }
@@ -48,12 +50,14 @@ namespace Js
     {
         return aRight == 0 ? 0 : aLeft / aRight;
     }
+    template<> bool AsmJsMath::DivWouldTrap(uint32 aLeft, uint32 aRight) { return aRight == 0; }
     template<> uint32 AsmJsMath::RemUnsafe<uint32>(uint32 aLeft, uint32 aRight) { return aLeft % aRight; }
     template<>
     uint32 AsmJsMath::RemChecked<uint32>(uint32 aLeft, uint32 aRight)
     {
         return aRight == 0 ? 0 : (aLeft == INT_MIN && aRight == -1) ? INT_MIN : aLeft % aRight;
     }
+    template<> bool AsmJsMath::RemWouldTrap(uint32 aLeft, uint32 aRight) { return aRight == 0 || (aLeft == INT_MIN && aRight == -1); }
 
     // Int64
     template<> int64 AsmJsMath::Mul<int64>(int64 aLeft, int64 aRight) { return aLeft * aRight; }
@@ -63,12 +67,14 @@ namespace Js
     {
         return aRight == 0 ? 0 : (aLeft == LONGLONG_MIN && aRight == -1) ? LONGLONG_MIN : aLeft / aRight;
     }
+    template<> bool AsmJsMath::DivWouldTrap(int64 aLeft, int64 aRight) { return aRight == 0 || (aLeft == LONGLONG_MIN && aRight == -1); }
     template<> int64 AsmJsMath::RemUnsafe<int64>(int64 aLeft, int64 aRight) { return aLeft % aRight; }
     template<>
     int64 AsmJsMath::RemChecked<int64>(int64 aLeft, int64 aRight)
     {
         return ((aRight == 0) || (aLeft == LONGLONG_MIN && aRight == -1)) ? 0 : aLeft % aRight;
     }
+    template<> bool AsmJsMath::RemWouldTrap(int64 aLeft, int64 aRight) { return aRight == 0 || (aLeft == LONGLONG_MIN && aRight == -1); }
 
     // Unsigned Int64
     template<> uint64 AsmJsMath::Mul<uint64>(uint64 aLeft, uint64 aRight) { return aLeft * aRight; }
@@ -78,10 +84,12 @@ namespace Js
     {
         return aRight == 0 ? 0 : aLeft / aRight;
     }
+    template<> bool AsmJsMath::DivWouldTrap(uint64 aLeft, uint64 aRight) { return aRight == 0; }
     template<> uint64 AsmJsMath::RemUnsafe<uint64>(uint64 aLeft, uint64 aRight) { return aLeft % aRight; }
     template<>
     uint64 AsmJsMath::RemChecked<uint64>(uint64 aLeft, uint64 aRight)
     {
         return aRight == 0 ? 0 : (aLeft == LONGLONG_MIN && aRight == -1) ? LONGLONG_MIN : aLeft % aRight;
     }
+    template<> bool AsmJsMath::RemWouldTrap(uint64 aLeft, uint64 aRight) { return aRight == 0 || (aLeft == LONGLONG_MIN && aRight == -1); }
 }

+ 2 - 0
lib/Runtime/Math/AsmJsMath.h

@@ -12,8 +12,10 @@ namespace Js
         template<typename T> static T Add( T aLeft, T aRight );
         template<typename T> static T Sub( T aLeft, T aRight );
         template<typename T> static T Mul( T aLeft, T aRight );
+        template<typename T> static bool DivWouldTrap(T aLeft, T aRight);
         template<typename T> static T DivUnsafe(T aLeft, T aRight);
         template<typename T> static T DivChecked(T aLeft, T aRight);
+        template<typename T> static bool RemWouldTrap(T aLeft, T aRight);
         template<typename T> static T RemUnsafe(T aLeft, T aRight);
         template<typename T> static T RemChecked(T aLeft, T aRight);
         template<typename T> static T Min( T aLeft, T aRight );