Nikolay Korovaiko 9 лет назад
Родитель
Сommit
f8accc1b59

+ 12 - 0
lib/Backend/IR.cpp

@@ -2872,6 +2872,18 @@ Instr::FindRegDef(StackSym *sym)
     return nullptr;
 }
 
+Instr*
+Instr::FindSingleDefInstr(Js::OpCode opCode, Opnd* src)
+{
+    RegOpnd* src1 = src->IsRegOpnd() ? src->AsRegOpnd() : nullptr;
+
+    return  src1 &&
+        src1->m_sym->IsSingleDef() &&
+        src1->m_sym->GetInstrDef()->m_opcode == opCode ?
+        src1->m_sym->GetInstrDef() :
+        nullptr;
+}
+
 void
 Instr::TransferDstAttributesTo(Instr * instr)
 {

+ 2 - 0
lib/Backend/IR.h

@@ -271,6 +271,8 @@ public:
     RegOpnd *       FindRegUse(StackSym *sym);
     static RegOpnd *FindRegUseInRange(StackSym *sym, Instr *instrBegin, Instr *instrEnd);
     RegOpnd *       FindRegDef(StackSym *sym);
+    static Instr*   FindSingleDefInstr(Js::OpCode opCode, Opnd* src);
+
     BranchInstr *   ChangeCmCCToBranchInstr(LabelInstr *targetInstr);
 
     static void     MoveRangeAfter(Instr * instrStart, Instr * instrLast, Instr * instrAfter);

+ 33 - 2
lib/Backend/IRBuilderAsmJs.cpp

@@ -2350,6 +2350,22 @@ IRBuilderAsmJs::BuildInt2(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot
     AddInstr(instr, offset);
 }
 
+IR::RegOpnd* IRBuilderAsmJs::BuildDivideByZeroCheck(IR::RegOpnd* srcOpnd, uint32 offset)
+{
+    IR::RegOpnd* newSrc = IR::RegOpnd::New(StackSym::New(m_func), srcOpnd->GetType(), m_func);
+    newSrc->SetValueType(ValueType::GetInt(false));
+    AddInstr(IR::Instr::New(Js::OpCode::DivideByZeroCheck, newSrc, srcOpnd, m_func), offset);
+    return newSrc;
+}
+
+IR::RegOpnd* IRBuilderAsmJs::BuildOverflowCheckReg3(IR::RegOpnd* src1Opnd, IR::RegOpnd* src2Opnd, uint32 offset)
+{
+    IR::RegOpnd* newSrc = IR::RegOpnd::New(StackSym::New(m_func), src2Opnd->GetType(), m_func);
+    newSrc->SetValueType(ValueType::GetInt(false));
+    AddInstr(IR::Instr::New(Js::OpCode::OverflowCheck3, newSrc, src1Opnd, src2Opnd, m_func), offset);
+    return newSrc;
+}
+
 void
 IRBuilderAsmJs::BuildInt3(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot, Js::RegSlot src2RegSlot)
 {
@@ -2379,14 +2395,29 @@ IRBuilderAsmJs::BuildInt3(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot
     case Js::OpCodeAsmJs::Mul_Int:
         instr = IR::Instr::New(Js::OpCode::Mul_I4, dstOpnd, src1Opnd, src2Opnd, m_func);
         break;
-
+    case Js::OpCodeAsmJs::Div_Check_UInt:
+        src1Opnd->SetType(TyUint32);
+        src2Opnd->SetType(TyUint32);
+    case Js::OpCodeAsmJs::Div_Check_Int:
+    {
+        src2Opnd = BuildDivideByZeroCheck(src2Opnd, offset);
+        src1Opnd = BuildOverflowCheckReg3(src1Opnd, src2Opnd, offset);
+        instr = IR::Instr::New(Js::OpCode::Div_I4, dstOpnd, src1Opnd, src2Opnd, m_func);
+        break;
+    }
     case Js::OpCodeAsmJs::Div_UInt:
         src1Opnd->SetType(TyUint32);
         src2Opnd->SetType(TyUint32);
     case Js::OpCodeAsmJs::Div_Int:
         instr = IR::Instr::New(Js::OpCode::Div_I4, dstOpnd, src1Opnd, src2Opnd, m_func);
         break;
-
+    case Js::OpCodeAsmJs::Rem_Check_UInt:
+        src1Opnd->SetType(TyUint32);
+        src2Opnd->SetType(TyUint32);
+    case Js::OpCodeAsmJs::Rem_Check_Int:
+        src2Opnd = BuildDivideByZeroCheck(src2Opnd, offset);
+        instr = IR::Instr::New(Js::OpCode::Rem_I4, dstOpnd, src1Opnd, src2Opnd, m_func);
+        break;
     case Js::OpCodeAsmJs::Rem_UInt:
         src1Opnd->SetType(TyUint32);
         src2Opnd->SetType(TyUint32);

+ 3 - 0
lib/Backend/IRBuilderAsmJs.h

@@ -146,6 +146,9 @@ private:
     Js::PropertyId          CalculatePropertyOffset(SymID id, IRType type, bool isVar = true);
 
     IR::Instr*              GenerateStSlotForReturn(IR::RegOpnd* srcOpnd, IRType type);
+    IR::RegOpnd*            BuildDivideByZeroCheck(IR::RegOpnd* srcOpnd, uint32 offset);
+    IR::RegOpnd*            BuildOverflowCheckReg3(IR::RegOpnd* src1Opnd, IR::RegOpnd* src2Opnd, uint32 offset);
+    
     JitArenaAllocator *     m_tempAlloc;
     JitArenaAllocator *     m_funcAlloc;
     Func *                  m_func;

+ 55 - 8
lib/Backend/Lower.cpp

@@ -1100,11 +1100,14 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
                 }
             }
             break;
-
+        case Js::OpCode::OverflowCheck3:
+            instr->UnlinkSrc2();
+        case Js::OpCode::DivideByZeroCheck:
+            instr->m_opcode = Js::OpCode::MOV;
+            break;
         case Js::OpCode::Div_I4:
             this->LowerDivI4(instr);
             break;
-
         case Js::OpCode::Add_Ptr:
             m_lowererMD.EmitPtrInstr(instr);
             break;
@@ -13020,7 +13023,6 @@ Lowerer::LowerInstrWithBailOnResultCondition(
         case Js::OpCode::Rem_I4:
             m_lowererMD.LowerInt4RemWithBailOut(instr, bailOutKind, bailOutLabel, skipBailOutLabel);
             break;
-
         default:
             Assert(false); // not implemented
             __assume(false);
@@ -22361,18 +22363,43 @@ Lowerer::LowerDivI4Common(IR::Instr * instr)
     IR::Opnd * src1 = instr->GetSrc1();
     IR::Opnd * src2 = instr->GetSrc2();
 
-    InsertTestBranch(src2, src2, Js::OpCode::BrEq_A, div0Label, div0Label);
+    bool isWasmFunc = m_func->GetJITFunctionBody()->IsWasmFunction();
 
-    InsertMove(dst, IR::IntConstOpnd::NewFromType(0, dst->GetType(), m_func), divLabel);
+    IR::Instr* divideByZeroInstr = IR::Instr::FindSingleDefInstr(Js::OpCode::DivideByZeroCheck, instr->GetSrc2());
+
+    if (divideByZeroInstr || !isWasmFunc)
+    {
+        InsertTestBranch(src2, src2, Js::OpCode::BrEq_A, div0Label, div0Label);
+
+        if (divideByZeroInstr)
+        {
+            IR::Opnd* errReg = IR::RegOpnd::New(TyInt32, m_func);
+            InsertMove(errReg, IR::IntConstOpnd::NewFromType(SCODE_CODE(WASMERR_DivideByZero), TyInt32, m_func), divLabel);
+            GenerateThrow(errReg, divLabel);
+        }
+        else
+        {
+            InsertMove(dst, IR::IntConstOpnd::NewFromType(0, dst->GetType(), m_func), divLabel);
+        }
     InsertBranch(Js::OpCode::Br, doneLabel, divLabel);
+    }
+
 
     if (instr->GetSrc1()->IsSigned())
     {
         IR::LabelInstr * minIntLabel = nullptr;
         // we need to check for INT_MIN/-1 if divisor is either -1 or variable, and dividend is either INT_MIN or variable
-        
-        bool needsMinOverNeg1Check = !(src2->IsImmediateOpnd() && src2->GetImmediateValue(m_func) != -1);
         int64 intMin = IRType_IsInt64(src1->GetType()) ? LONGLONG_MIN : INT_MIN;
+        IR::Instr* overflowReg3 = IR::Instr::FindSingleDefInstr(Js::OpCode::OverflowCheck3, instr->GetSrc1());
+        bool needsMinOverNeg1Check = true;
+        if (isWasmFunc && instr->m_opcode != Js::OpCode::Rem_I4)
+        {
+            needsMinOverNeg1Check = overflowReg3 != nullptr;
+        }
+        else
+        {
+            needsMinOverNeg1Check = !(src2->IsImmediateOpnd() && src2->GetImmediateValue(m_func) != -1);
+        }
         if (src1->IsImmediateOpnd())
         {
             if (needsMinOverNeg1Check && src1->GetImmediateValue(m_func) == intMin)
@@ -22398,7 +22425,17 @@ Lowerer::LowerDivI4Common(IR::Instr * instr)
             {
                 InsertCompareBranch(src2, IR::IntConstOpnd::NewFromType(-1, src2->GetType(), m_func), Js::OpCode::BrNeq_A, divLabel, divLabel);
             }
-            InsertMove(dst, instr->m_opcode == Js::OpCode::Div_I4 ? src1 : IR::IntConstOpnd::NewFromType(0, dst->GetType(), m_func), divLabel);
+            
+            if (overflowReg3)
+            {
+                IR::Opnd* errReg = IR::RegOpnd::New(TyInt32, m_func);
+                InsertMove(errReg, IR::IntConstOpnd::NewFromType(SCODE_CODE(VBSERR_Overflow), TyInt32, m_func), divLabel);
+                GenerateThrow(errReg, divLabel);
+            }
+            else
+            {
+                InsertMove(dst, instr->m_opcode == Js::OpCode::Div_I4 ? src1 : IR::IntConstOpnd::NewFromType(0, dst->GetType(), m_func), divLabel);
+            }
             InsertBranch(Js::OpCode::Br, doneLabel, divLabel);
         }
     }
@@ -22422,6 +22459,16 @@ Lowerer::LowerRemI4(IR::Instr * instr)
     }
 }
 
+void
+Lowerer::GenerateThrow(IR::Opnd* errorCode, IR::Instr * instr) const
+{
+    IR::Instr *throwInstr = IR::Instr::New(Js::OpCode::RuntimeTypeError, IR::RegOpnd::New(TyMachReg, m_func), errorCode, m_func);
+    instr->InsertBefore(throwInstr);
+    Lowerer* lw = const_cast<Lowerer*> (this); //LowerUnaryHelperMem unlinks src1 of throwInstr,
+                                               //local and self-contained mutation
+    lw->LowerUnaryHelperMem(throwInstr, IR::HelperOp_RuntimeTypeError);
+}
+
 void
 Lowerer::LowerDivI4(IR::Instr * instr)
 {

+ 1 - 1
lib/Backend/Lower.h

@@ -435,7 +435,7 @@ private:
     void            GenerateObjectTestAndTypeLoad(IR::Instr *instrLdSt, IR::RegOpnd *opndBase, IR::RegOpnd *opndType, IR::LabelInstr *labelHelper);
     IR::LabelInstr *GenerateBailOut(IR::Instr * instr, IR::BranchInstr * branchInstr = nullptr, IR::LabelInstr * labelBailOut = nullptr, IR::LabelInstr * collectRuntimeStatsLabel = nullptr);
     void            GenerateJumpToEpilogForBailOut(BailOutInfo * bailOutInfo, IR::Instr *instrAfter);
-
+    void            GenerateThrow(IR::Opnd* errorCode, IR::Instr * instr) const;
     void            LowerDivI4(IR::Instr * const instr);
     void            LowerRemI4(IR::Instr * const instr);
     void            LowerDivI4Common(IR::Instr * const instr);

+ 1 - 0
lib/Parser/rterrors.h

@@ -373,3 +373,4 @@ RT_ERROR_MSG(WASMERR_InvalidImport, 5672, "", "Import is invalid", kjstTypeError
 RT_ERROR_MSG(WASMERR_InvalidGlobalRef, 5673, "", "Global initialization does not support forward reference", kjstTypeError, 0)
 RT_ERROR_MSG(WASMERR_NeedMemoryObject, 5674, "%s is not a WebAssembly.Memory", "WebAssembly.Memory object expected", kjstTypeError, 0)
 RT_ERROR_MSG(WASMERR_InvalidTypeConversion, 5675, "Invalid WebAssembly type conversion %s to %s", "Invalid WebAssembly type conversion", kjstTypeError, 0)
+RT_ERROR_MSG(WASMERR_DivideByZero, 5676,    "",  "Division by zero", kjstError, 0)

+ 2 - 0
lib/Runtime/ByteCode/OpCodes.h

@@ -736,6 +736,8 @@ MACRO_BACKEND_ONLY(     Copysign_A,         Empty,          OpTempNumberSources|
 MACRO_BACKEND_ONLY(     Trunc_A,            Empty,          OpTempNumberSources|OpCanCSE|OpProducesNumber)
 MACRO_BACKEND_ONLY(     Nearest_A,          Empty,          OpTempNumberSources|OpCanCSE|OpProducesNumber)
 MACRO_BACKEND_ONLY(     Unreachable_Void,   Empty,          OpSideEffect|OpNoFallThrough)
+MACRO_BACKEND_ONLY(     OverflowCheck3,     Reg3,           OpSideEffect)
+MACRO_BACKEND_ONLY(     DivideByZeroCheck,  Reg3,           OpSideEffect)
 
 MACRO_BACKEND_ONLY(     AsmJsEntryTracing, Empty, None)
 

+ 4 - 0
lib/Runtime/ByteCode/OpCodesAsmJs.h

@@ -134,7 +134,9 @@ MACRO_WMS       ( Add_Int                    , Int3            , None
 MACRO_WMS       ( Sub_Int                    , Int3            , None            ) // int32 Arithmetic '-' (subtract)
 MACRO_WMS       ( Mul_Int                    , Int3            , None            ) // int32 Arithmetic '*'
 MACRO_WMS       ( Div_Int                    , Int3            , None            ) // int32 Arithmetic '/'
+MACRO_WMS       ( Div_Check_Int              , Int3            , None            ) // (checked) int32 Arithmetic '/'
 MACRO_WMS       ( Rem_Int                    , Int3            , None            ) // int32 Arithmetic '%'
+MACRO_WMS       ( Rem_Check_Int              , Int3            , None            ) // (checked) int32 Arithmetic '%'
 MACRO_WMS       ( And_Int                    , Int3            , None            ) // int32 Bitwise '&'
 MACRO_WMS       ( Or_Int                     , Int3            , None            ) // int32 Bitwise '|'
 MACRO_WMS       ( Xor_Int                    , Int3            , None            ) // int32 Bitwise '^'
@@ -148,7 +150,9 @@ MACRO_WMS       ( PopCnt_Int                 , Int2            , None
 // Unsigned int math
 MACRO_WMS       ( Mul_UInt                   , Int3            , None            ) // uint32 Arithmetic '*'
 MACRO_WMS       ( Div_UInt                   , Int3            , None            ) // uint32 Arithmetic '/'
+MACRO_WMS       ( Div_Check_UInt             , Int3            , None            ) // (checked) uint32 Arithmetic '/'
 MACRO_WMS       ( Rem_UInt                   , Int3            , None            ) // uint32 Arithmetic '%'
+MACRO_WMS       ( Rem_Check_UInt             , Int3            , None            ) // (checked) uint32 Arithmetic '%'
 
 // Int64 Math
 MACRO_WMS       ( Ld_LongConst               , Long1Const1      , None            ) // Sets an int64 register from a const int64

+ 5 - 0
lib/Runtime/Language/InterpreterHandlerAsmJs.inl

@@ -99,6 +99,8 @@ EXDEF2    (NOPASMJS          , InvalidOpCode, Empty
   DEF2_WMS( I2toI1Mem        , Mul_Int      , AsmJsMath::Mul<int>                                )
   DEF2_WMS( I2toI1Mem        , Div_Int      , AsmJsMath::Div<int>                                )
   DEF2_WMS( I2toI1Mem        , Rem_Int      , AsmJsMath::Rem<int>                                )
+  DEF2_WMS( I2toI1Mem        , Div_Check_Int , OP_DivOverflow                                    )
+  DEF2_WMS( I2toI1Mem        , Rem_Check_Int , (OP_DivRemCheck<int, &AsmJsMath::Rem<int>>)       )
   DEF2_WMS( I2toI1Mem        , And_Int      , AsmJsMath::And                                     )
   DEF2_WMS( I2toI1Mem        , Or_Int       , AsmJsMath::Or                                      )
   DEF2_WMS( I2toI1Mem        , Xor_Int      , AsmJsMath::Xor                                     )
@@ -132,6 +134,9 @@ EXDEF2    (NOPASMJS          , InvalidOpCode, Empty
   DEF2_WMS( L1toL1Mem        , Clz_Long     , Wasm::WasmMath::Clz<int64>)
   DEF2_WMS( L1toL1Mem        , Ctz_Long     , Wasm::WasmMath::Ctz<int64>)
   DEF2_WMS( L1toL1Mem        , PopCnt_Long  , Wasm::WasmMath::PopCnt<int64>)
+  DEF2_WMS( I2toI1Mem        , Div_Check_UInt , (OP_DivRemCheck<uint32, &AsmJsMath::Div<uint32>>) )
+  DEF2_WMS( I2toI1Mem        , Rem_Check_UInt , (OP_DivRemCheck<uint32, &AsmJsMath::Rem<uint32>>) )
+
 
   DEF2_WMS( D1toD1Mem        , Neg_Db       , AsmJsMath::Neg<double>                             ) // double unary '-'
   DEF2_WMS( D2toD1Mem        , Add_Db       , AsmJsMath::Add<double>                             )

+ 25 - 0
lib/Runtime/Language/InterpreterStackFrame.cpp

@@ -7984,6 +7984,31 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
 #endif
     }
 
+    template <typename T, InterpreterStackFrame::AsmJsMathPtr<T> func> T InterpreterStackFrame::OP_DivRemCheck(T a, T b)
+    {
+        if (b == 0)
+        {
+            JavascriptError::ThrowError(scriptContext, WASMERR_DivideByZero);
+        }
+
+        return func(a, b);
+    }
+
+    int InterpreterStackFrame::OP_DivOverflow(int aLeft, int aRight)
+    {
+        if (aRight == 0)
+        {
+            JavascriptError::ThrowError(scriptContext, WASMERR_DivideByZero);
+        }
+
+        if (aLeft == INT_MIN && aRight == -1)
+        {
+            JavascriptError::ThrowError(scriptContext, VBSERR_Overflow);
+        }
+
+        return AsmJsMath::Div(aLeft, aRight);
+    }
+
     void InterpreterStackFrame::OP_Unreachable()
     {
         JavascriptError::ThrowUnreachable(scriptContext);

+ 3 - 1
lib/Runtime/Language/InterpreterStackFrame.h

@@ -187,7 +187,9 @@ namespace Js
         void ValidateRegValue(Var value, bool allowStackVar = false, bool allowStackVarOnDisabledStackNestedFunc = true) const;
         int OP_GetMemorySize();
         void OP_Unreachable();
-
+        template <typename T> using AsmJsMathPtr = T(*)(T a, T b);
+        int OP_DivOverflow(int a, int b);
+        template <typename T, AsmJsMathPtr<T> func> T OP_DivRemCheck(T a, T b);
         void ValidateSetRegValue(Var value, bool allowStackVar = false, bool allowStackVarOnDisabledStackNestedFunc = true) const;
         template <typename RegSlotType> Var GetReg(RegSlotType localRegisterID) const;
         template <typename RegSlotType> void SetReg(RegSlotType localRegisterID, Var bValue);

+ 4 - 4
lib/WasmReader/WasmBinaryOpCodes.h

@@ -184,10 +184,10 @@ WASM_UNARY__OPCODE(I32Popcnt,         0x69, I_I , PopCnt_Int     , false)
 WASM_BINARY_OPCODE(I32Add,            0x6a, I_II, Add_Int        , false)
 WASM_BINARY_OPCODE(I32Sub,            0x6b, I_II, Sub_Int        , false)
 WASM_BINARY_OPCODE(I32Mul,            0x6c, I_II, Mul_Int        , false)
-WASM_BINARY_OPCODE(I32DivS,           0x6d, I_II, Div_Int        , false)
-WASM_BINARY_OPCODE(I32DivU,           0x6e, I_II, Div_UInt       , false)
-WASM_BINARY_OPCODE(I32RemS,           0x6f, I_II, Rem_Int        , false)
-WASM_BINARY_OPCODE(I32RemU,           0x70, I_II, Rem_UInt       , false)
+WASM_BINARY_OPCODE(I32DivS,           0x6d, I_II, Div_Check_Int  , false)
+WASM_BINARY_OPCODE(I32DivU,           0x6e, I_II, Div_Check_UInt , false)
+WASM_BINARY_OPCODE(I32RemS,           0x6f, I_II, Rem_Check_Int  , false)
+WASM_BINARY_OPCODE(I32RemU,           0x70, I_II, Rem_Check_UInt , false)
 WASM_BINARY_OPCODE(I32And,            0x71, I_II, And_Int        , false)
 WASM_BINARY_OPCODE(I32Or,             0x72, I_II, Or_Int         , false)
 WASM_BINARY_OPCODE(I32Xor,            0x73, I_II, Xor_Int        , false)

+ 10 - 0
test/wasm/exceptions.baseline

@@ -0,0 +1,10 @@
+Division by zero
+Overflow
+Division by zero
+Division by zero
+Division by zero
+-2
+2
+0
+0
+1

+ 27 - 0
test/wasm/exceptions.js

@@ -0,0 +1,27 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function trap (f, a, b)
+{
+    try { return f(a, b); }
+    catch (e) { return e.message; };
+}
+
+const blob = WScript.LoadBinaryFile('exceptions.wasm');
+const moduleBytesView = new Uint8Array(blob);
+var a = Wasm.instantiateModule(moduleBytesView, {}).exports;
+
+print(trap(a.i32_div_s, 5, 0));
+print(trap(a.i32_div_s, -2147483648, -1));
+print(trap(a.i32_div_u, 5, 0));
+print(trap(a.i32_rem_u, 5, 0));
+print(trap(a.i32_rem_s, 5, 0));
+
+print(trap(a.i32_div_s, 5, -2));
+print(trap(a.i32_div_u, 5, 2));
+
+print(trap(a.i32_rem_u, 5, 1));
+print(trap(a.i32_rem_s, 5, -1));
+print(trap(a.i32_rem_s, 5, 2));

BIN
test/wasm/exceptions.wasm


+ 17 - 0
test/wasm/exceptions.wast

@@ -0,0 +1,17 @@
+;;-------------------------------------------------------------------------------------------------------
+;; Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+;; Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+;;-------------------------------------------------------------------------------------------------------
+
+(module
+  (func (export "i32_div_s") (param $x i32) (param $y i32) (result i32)
+    (i32.div_s (get_local $x) (get_local $y)))
+  (func (export "i32_div_u") (param $x i32) (param $y i32) (result i32)
+    (i32.div_u (get_local $x) (get_local $y)))
+    
+  (func (export "i32_rem_s") (param $x i32) (param $y i32) (result i32)
+    (i32.rem_s (get_local $x) (get_local $y)))
+  (func (export "i32_rem_u") (param $x i32) (param $y i32) (result i32)
+    (i32.rem_u (get_local $x) (get_local $y)))
+)
+

+ 8 - 1
test/wasm/rlexe.xml

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <regress-exe>
 <test>
   <default>
@@ -102,4 +102,11 @@
        <compile-flags>-on:Wasm</compile-flags>
     </default>
 </test>
+<test>
+    <default>
+       <files>exceptions.js</files>
+       <baseline>exceptions.baseline</baseline>
+       <compile-flags>-on:Wasm</compile-flags>
+    </default>
+</test>
 </regress-exe>