Bug 1255772: Implement wasm::Unreachable; r=sunfish
authorBenjamin Bouvier <benj@benj.me>
Thu, 10 Mar 2016 21:00:07 +0100
changeset 339913 e3a45833901b5d826b1586d78aa11e7f453d47ad
parent 339912 eceb5235a54da29b7280153f0a130eece3682d4e
child 339914 fe5bc1369d3efe3501995a8a061a8862bcb10723
push id12823
push userjmaher@mozilla.com
push dateMon, 14 Mar 2016 10:52:22 +0000
reviewerssunfish
bugs1255772
milestone48.0a1
Bug 1255772: Implement wasm::Unreachable; r=sunfish MozReview-Commit-ID: BFDrX72K7Vk
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmBinaryToText.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/asmjs/WasmStubs.cpp
js/src/asmjs/WasmTextToBinary.cpp
js/src/asmjs/WasmTypes.cpp
js/src/asmjs/WasmTypes.h
js/src/jit-test/tests/wasm/basic-control-flow.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/js.msg
js/src/vm/Xdr.h
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -180,16 +180,23 @@ DecodeExpr(FunctionDecoder& f, ExprType*
 static bool
 DecodeNop(FunctionDecoder& f, ExprType* type)
 {
     *type = ExprType::Void;
     return true;
 }
 
 static bool
+DecodeUnreachable(FunctionDecoder& f, ExprType* type)
+{
+    *type = AnyType;
+    return true;
+}
+
+static bool
 DecodeCallWithSig(FunctionDecoder& f, const Sig& sig, ExprType* type)
 {
     for (ValType argType : sig.args()) {
         ExprType exprType;
         if (!DecodeExpr(f, &exprType))
             return false;
 
         if (!CheckType(f, exprType, argType))
@@ -838,16 +845,18 @@ DecodeExpr(FunctionDecoder& f, ExprType*
       case Expr::Br:
         return DecodeBranch(f, expr, type);
       case Expr::BrIf:
         return DecodeBranch(f, expr, type);
       case Expr::BrTable:
         return DecodeBrTable(f, type);
       case Expr::Return:
         return DecodeReturn(f, type);
+      case Expr::Unreachable:
+        return DecodeUnreachable(f, type);
       default:
         // Note: it's important not to remove this default since readExpr()
         // can return Expr values for which there is no enumerator.
         break;
     }
 
     return f.fail("bad expression code");
 }
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -154,16 +154,22 @@ RenderFullLine(WasmRenderContext& c)
 
 static bool
 RenderNop(WasmRenderContext& c)
 {
     return c.buffer.append("(nop)");
 }
 
 static bool
+RenderUnreachable(WasmRenderContext& c)
+{
+    return c.buffer.append("(trap)");
+}
+
+static bool
 RenderCallWithSig(WasmRenderContext& c, uint32_t sigIndex)
 {
     const DeclaredSig& sig = c.signatures[sigIndex];
     for (uint32_t i = 0; i < sig.args().length(); i++) {
         if (!c.buffer.append(" "))
             return false;
         if (!RenderExpr(c))
             return false;
@@ -872,16 +878,18 @@ RenderExpr(WasmRenderContext& c)
 {
     Expr expr;
     if (!c.d.readExpr(&expr))
         return RenderFail(c, "unable to read expression");
 
     switch (expr) {
       case Expr::Nop:
         return RenderNop(c);
+      case Expr::Unreachable:
+        return RenderUnreachable(c);
       case Expr::Call:
         return RenderCall(c);
       case Expr::CallImport:
         return RenderCallImport(c);
       case Expr::CallIndirect:
         return RenderCallIndirect(c);
       case Expr::I32Const:
         return RenderConstI32(c);
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -860,16 +860,27 @@ class FunctionCompiler
     {
         if (inDeadCode())
             return;
         MAsmJSVoidReturn* ins = MAsmJSVoidReturn::New(alloc());
         curBlock_->end(ins);
         curBlock_ = nullptr;
     }
 
+    bool unreachableTrap()
+    {
+        if (inDeadCode())
+            return true;
+
+        auto* ins = MAsmThrowUnreachable::New(alloc());
+        curBlock_->end(ins);
+        curBlock_ = nullptr;
+        return true;
+    }
+
     bool branchAndStartThen(MDefinition* cond, MBasicBlock** thenBlock, MBasicBlock** elseBlock)
     {
         if (inDeadCode())
             return true;
 
         bool hasThenBlock = *thenBlock != nullptr;
         bool hasElseBlock = *elseBlock != nullptr;
 
@@ -2613,16 +2624,23 @@ EmitReturn(FunctionCompiler& f, MDefinit
 
     f.returnExpr(retVal);
 
     *def = nullptr;
     return true;
 }
 
 static bool
+EmitUnreachable(FunctionCompiler& f, MDefinition** def)
+{
+    *def = nullptr;
+    return f.unreachableTrap();
+}
+
+static bool
 EmitBlock(FunctionCompiler& f, MDefinition** def)
 {
     if (!f.startBlock())
         return false;
     if (uint32_t numStmts = f.readVarU32()) {
         for (uint32_t i = 0; i < numStmts - 1; i++) {
             MDefinition* _;
             if (!EmitExpr(f, &_))
@@ -2685,26 +2703,27 @@ EmitExpr(FunctionCompiler& f, MDefinitio
         return EmitLoop(f, def);
       case Expr::Br:
       case Expr::BrIf:
         return EmitBranch(f, op, def);
       case Expr::BrTable:
         return EmitBrTable(f, def);
       case Expr::Return:
         return EmitReturn(f, def);
+      case Expr::Unreachable:
+        return EmitUnreachable(f, def);
 
       // Calls
       case Expr::Call:
         return EmitCall(f, exprOffset, def);
       case Expr::CallIndirect:
         return EmitCallIndirect(f, exprOffset, def);
       case Expr::CallImport:
         return EmitCallImport(f, exprOffset, def);
 
-
       // Locals and globals
       case Expr::GetLocal:
         return EmitGetLocal(f, def);
       case Expr::SetLocal:
         return EmitSetLocal(f, def);
       case Expr::LoadGlobal:
         return EmitLoadGlobal(f, def);
       case Expr::StoreGlobal:
@@ -3028,17 +3047,16 @@ EmitExpr(FunctionCompiler& f, MDefinitio
       case Expr::I64Popcnt:
       case Expr::I64Eqz:
       case Expr::I32Rotr:
       case Expr::I32Rotl:
       case Expr::I64Rotr:
       case Expr::I64Rotl:
       case Expr::MemorySize:
       case Expr::GrowMemory:
-      case Expr::Unreachable:
         MOZ_CRASH("NYI");
         break;
       case Expr::Limit:;
     }
 
     MOZ_CRASH("unexpected wasm opcode");
 }
 
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -771,17 +771,18 @@ static Offsets
 GenerateErrorStub(MacroAssembler& masm, SymbolicAddress address)
 {
     masm.haltingAlign(CodeAlignment);
 
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
     // sp can be anything at this point, so ensure it is aligned when calling
-    // into C++.  We unconditionally jump to throw so don't worry about restoring sp.
+    // into C++.  We unconditionally jump to throw so don't worry about
+    // restoring sp.
     masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
 
     masm.assertStackAlignment(ABIStackAlignment);
     masm.call(address);
     masm.jump(JumpTarget::Throw);
 
     offsets.end = masm.currentOffset();
     return offsets;
@@ -827,16 +828,18 @@ wasm::GenerateJumpTarget(MacroAssembler&
       case JumpTarget::StackOverflow:
         return GenerateStackOverflow(masm);
       case JumpTarget::ConversionError:
         return GenerateErrorStub(masm, SymbolicAddress::OnImpreciseConversion);
       case JumpTarget::OutOfBounds:
         return GenerateErrorStub(masm, SymbolicAddress::OnOutOfBounds);
       case JumpTarget::BadIndirectCall:
         return GenerateErrorStub(masm, SymbolicAddress::BadIndirectCall);
+      case JumpTarget::UnreachableTrap:
+        return GenerateErrorStub(masm, SymbolicAddress::UnreachableTrap);
       case JumpTarget::Throw:
         return GenerateThrow(masm);
       case JumpTarget::Limit:
         break;
     }
     MOZ_CRASH("bad JumpTarget");
 }
 
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -204,16 +204,17 @@ enum class WasmAstExprKind
     ConversionOperator,
     GetLocal,
     If,
     Load,
     Nop,
     Return,
     SetLocal,
     Store,
+    Trap,
     UnaryOperator,
 };
 
 class WasmAstExpr : public WasmAstNode
 {
     const WasmAstExprKind kind_;
 
   protected:
@@ -233,16 +234,23 @@ class WasmAstExpr : public WasmAstNode
 
 struct WasmAstNop : WasmAstExpr
 {
     WasmAstNop()
       : WasmAstExpr(WasmAstExprKind::Nop)
     {}
 };
 
+struct WasmAstTrap : WasmAstExpr
+{
+    WasmAstTrap()
+      : WasmAstExpr(WasmAstExprKind::Trap)
+    {}
+};
+
 class WasmAstConst : public WasmAstExpr
 {
     const Val val_;
 
   public:
     static const WasmAstExprKind Kind = WasmAstExprKind::Const;
     explicit WasmAstConst(Val val)
       : WasmAstExpr(Kind),
@@ -791,16 +799,17 @@ class WasmToken
         Param,
         Result,
         Return,
         Segment,
         SetLocal,
         Store,
         Table,
         Text,
+        Trap,
         Type,
         UnaryOpcode,
         ValueType
     };
   private:
     Kind kind_;
     const char16_t* begin_;
     const char16_t* end_;
@@ -1926,16 +1935,18 @@ WasmTokenStream::next()
             return WasmToken(WasmToken::Segment, begin, cur_);
         break;
 
       case 't':
         if (consume(MOZ_UTF16("table")))
             return WasmToken(WasmToken::Table, begin, cur_);
         if (consume(MOZ_UTF16("type")))
             return WasmToken(WasmToken::Type, begin, cur_);
+        if (consume(MOZ_UTF16("trap")))
+            return WasmToken(WasmToken::Trap, begin, cur_);
         break;
 
       default:
         break;
     }
 
     return fail(begin);
 }
@@ -2664,16 +2675,18 @@ ParseBranchTable(WasmParseContext& c, Wa
 static WasmAstExpr*
 ParseExprInsideParens(WasmParseContext& c)
 {
     WasmToken token = c.ts.get();
 
     switch (token.kind()) {
       case WasmToken::Nop:
         return new(c.lifo) WasmAstNop;
+      case WasmToken::Trap:
+        return new(c.lifo) WasmAstTrap;
       case WasmToken::BinaryOpcode:
         return ParseBinaryOperator(c, token.expr());
       case WasmToken::Block:
         return ParseBlock(c, Expr::Block);
       case WasmToken::Br:
         return ParseBranch(c, Expr::Br);
       case WasmToken::BrIf:
         return ParseBranch(c, Expr::BrIf);
@@ -3368,16 +3381,17 @@ ResolveBranchTable(Resolver& r, WasmAstB
     return ResolveExpr(r, bt.index());
 }
 
 static bool
 ResolveExpr(Resolver& r, WasmAstExpr& expr)
 {
     switch (expr.kind()) {
       case WasmAstExprKind::Nop:
+      case WasmAstExprKind::Trap:
         return true;
       case WasmAstExprKind::BinaryOperator:
         return ResolveBinaryOperator(r, expr.as<WasmAstBinaryOperator>());
       case WasmAstExprKind::Block:
         return ResolveBlock(r, expr.as<WasmAstBlock>());
       case WasmAstExprKind::Branch:
         return ResolveBranch(r, expr.as<WasmAstBranch>());
       case WasmAstExprKind::Call:
@@ -3698,16 +3712,18 @@ EncodeBranchTable(Encoder& e, WasmAstBra
 }
 
 static bool
 EncodeExpr(Encoder& e, WasmAstExpr& expr)
 {
     switch (expr.kind()) {
       case WasmAstExprKind::Nop:
         return e.writeExpr(Expr::Nop);
+      case WasmAstExprKind::Trap:
+        return e.writeExpr(Expr::Unreachable);
       case WasmAstExprKind::BinaryOperator:
         return EncodeBinaryOperator(e, expr.as<WasmAstBinaryOperator>());
       case WasmAstExprKind::Block:
         return EncodeBlock(e, expr.as<WasmAstBlock>());
       case WasmAstExprKind::Branch:
         return EncodeBranch(e, expr.as<WasmAstBranch>());
       case WasmAstExprKind::Call:
         return EncodeCall(e, expr.as<WasmAstCall>());
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -74,16 +74,23 @@ OnImpreciseConversion()
 
 static void
 BadIndirectCall()
 {
     JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IND_CALL);
 }
 
+static void
+UnreachableTrap()
+{
+    JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
+    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNREACHABLE);
+}
+
 static int32_t
 CoerceInPlace_ToInt32(MutableHandleValue val)
 {
     JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
 
     int32_t i32;
     if (!ToInt32(cx, val, &i32))
         return false;
@@ -181,16 +188,18 @@ wasm::AddressOf(SymbolicAddress imm, Exc
       case SymbolicAddress::ReportOverRecursed:
         return FuncCast(WasmReportOverRecursed, Args_General0);
       case SymbolicAddress::OnOutOfBounds:
         return FuncCast(OnOutOfBounds, Args_General0);
       case SymbolicAddress::OnImpreciseConversion:
         return FuncCast(OnImpreciseConversion, Args_General0);
       case SymbolicAddress::BadIndirectCall:
         return FuncCast(BadIndirectCall, Args_General0);
+      case SymbolicAddress::UnreachableTrap:
+        return FuncCast(UnreachableTrap, Args_General0);
       case SymbolicAddress::HandleExecutionInterrupt:
         return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
       case SymbolicAddress::InvokeImport_Void:
         return FuncCast(InvokeImport_Void, Args_General3);
       case SymbolicAddress::InvokeImport_I32:
         return FuncCast(InvokeImport_I32, Args_General3);
       case SymbolicAddress::InvokeImport_F64:
         return FuncCast(InvokeImport_F64, Args_General3);
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -530,16 +530,17 @@ enum class SymbolicAddress
     ATan2D,
     Runtime,
     RuntimeInterruptUint32,
     StackLimit,
     ReportOverRecursed,
     OnOutOfBounds,
     OnImpreciseConversion,
     BadIndirectCall,
+    UnreachableTrap,
     HandleExecutionInterrupt,
     InvokeImport_Void,
     InvokeImport_I32,
     InvokeImport_F64,
     CoerceInPlace_ToInt32,
     CoerceInPlace_ToNumber,
     Limit
 };
@@ -553,16 +554,17 @@ AddressOf(SymbolicAddress imm, Exclusive
 // and patched specially by the MacroAssembler and ModuleGenerator.
 
 enum class JumpTarget
 {
     StackOverflow,
     OutOfBounds,
     ConversionError,
     BadIndirectCall,
+    UnreachableTrap,
     Throw,
     Limit
 };
 
 typedef EnumeratedArray<JumpTarget, JumpTarget::Limit, Uint32Vector> JumpSiteArray;
 
 // The CompileArgs struct captures global parameters that affect all wasm code
 // generation. It also currently is the single source of truth for whether or
--- a/js/src/jit-test/tests/wasm/basic-control-flow.js
+++ b/js/src/jit-test/tests/wasm/basic-control-flow.js
@@ -401,8 +401,18 @@ var f = wasmEvalText(`(module (func (res
 
 assertEq(f(-2), -1);
 assertEq(f(-1), -1);
 assertEq(f(0), 0);
 assertEq(f(1), 0);
 assertEq(f(2), 2);
 assertEq(f(3), -1);
 
+// ----------------------------------------------------------------------------
+// unreachable
+
+const UNREACHABLE = /unreachable/;
+assertErrorMessage(wasmEvalText(`(module (func (trap)) (export "" 0))`), Error, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (if (trap) (i32.const 0))) (export "" 0))`), Error, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (block (br_if 0 (trap)))) (export "" 0))`), Error, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (block (br_table 0 (trap)))) (export "" 0))`), Error, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (result i32) (i32.add (i32.const 0) (trap))) (export "" 0))`), Error, UNREACHABLE);
+
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -10622,16 +10622,23 @@ CodeGenerator::visitAsmJSInterruptCheck(
 
     MOZ_ASSERT((sizeof(AsmJSFrame) + masm.framePushed()) % ABIStackAlignment == 0);
     masm.call(wasm::SymbolicAddress::HandleExecutionInterrupt);
     masm.branchIfFalseBool(ReturnReg, wasm::JumpTarget::Throw);
 
     masm.bind(&rejoin);
 }
 
+void
+CodeGenerator::visitAsmThrowUnreachable(LAsmThrowUnreachable* lir)
+{
+    MOZ_ASSERT(gen->compilingAsmJS());
+    masm.jump(wasm::JumpTarget::UnreachableTrap);
+}
+
 typedef bool (*RecompileFn)(JSContext*);
 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile);
 
 typedef bool (*ForcedRecompileFn)(JSContext*);
 static const VMFunction ForcedRecompileFnInfo = FunctionInfo<ForcedRecompileFn>(ForcedRecompile);
 
 void
 CodeGenerator::visitRecompileCheck(LRecompileCheck* ins)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -390,16 +390,17 @@ class CodeGenerator : public CodeGenerat
     void visitAssertResultV(LAssertResultV* ins);
     void visitAssertResultT(LAssertResultT* ins);
     void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset);
     void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset);
 
     void visitInterruptCheck(LInterruptCheck* lir);
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
     void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir);
+    void visitAsmThrowUnreachable(LAsmThrowUnreachable* lir);
     void visitRecompileCheck(LRecompileCheck* ins);
 
     void visitRandom(LRandom* ins);
 
     IonScriptCounts* extractScriptCounts() {
         IonScriptCounts* counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
         return counts;
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2462,16 +2462,22 @@ LIRGenerator::visitInterruptCheck(MInter
 void
 LIRGenerator::visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins)
 {
     gen->setPerformsCall();
     add(new(alloc()) LAsmJSInterruptCheck, ins);
 }
 
 void
+LIRGenerator::visitAsmThrowUnreachable(MAsmThrowUnreachable* ins)
+{
+    add(new(alloc()) LAsmThrowUnreachable, ins);
+}
+
+void
 LIRGenerator::visitStoreSlot(MStoreSlot* ins)
 {
     LInstruction* lir;
 
     switch (ins->value()->type()) {
       case MIRType_Value:
         lir = new(alloc()) LStoreSlotV(useRegister(ins->slots()), useBox(ins->value()));
         add(lir, ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -178,16 +178,17 @@ class LIRGenerator : public LIRGenerator
     void visitConvertElementsToDoubles(MConvertElementsToDoubles* ins);
     void visitMaybeToDoubleElement(MMaybeToDoubleElement* ins);
     void visitMaybeCopyElementsForWrite(MMaybeCopyElementsForWrite* ins);
     void visitLoadSlot(MLoadSlot* ins);
     void visitLoadFixedSlotAndUnbox(MLoadFixedSlotAndUnbox* ins);
     void visitFunctionEnvironment(MFunctionEnvironment* ins);
     void visitInterruptCheck(MInterruptCheck* ins);
     void visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins);
+    void visitAsmThrowUnreachable(MAsmThrowUnreachable* ins);
     void visitStoreSlot(MStoreSlot* ins);
     void visitFilterTypeSet(MFilterTypeSet* ins);
     void visitTypeBarrier(MTypeBarrier* ins);
     void visitMonitorTypes(MMonitorTypes* ins);
     void visitPostWriteBarrier(MPostWriteBarrier* ins);
     void visitPostWriteElementBarrier(MPostWriteElementBarrier* ins);
     void visitArrayLength(MArrayLength* ins);
     void visitSetArrayLength(MSetArrayLength* ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7587,16 +7587,32 @@ class MAsmJSInterruptCheck
 {
   public:
     INSTRUCTION_HEADER(AsmJSInterruptCheck)
     static MAsmJSInterruptCheck* New(TempAllocator& alloc) {
         return new(alloc) MAsmJSInterruptCheck;
     }
 };
 
+// Directly jumps to the unreachable trap handler.
+class MAsmThrowUnreachable
+  : public MAryControlInstruction<0, 0>,
+    public NoTypePolicy::Data
+{
+  public:
+    INSTRUCTION_HEADER(AsmThrowUnreachable)
+    static MAsmThrowUnreachable* New(TempAllocator& alloc) {
+        return new(alloc) MAsmThrowUnreachable;
+    }
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+};
+
 // Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
 // it to baseline to throw at the correct pc.
 class MLexicalCheck
   : public MUnaryInstruction,
     public BoxPolicy<0>::Data
 {
     BailoutKind kind_;
     explicit MLexicalCheck(MDefinition* input, BailoutKind kind)
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -248,16 +248,17 @@ namespace jit {
     _(Floor)                                                                \
     _(Ceil)                                                                 \
     _(Round)                                                                \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
     _(AsmJSInterruptCheck)                                                  \
+    _(AsmThrowUnreachable)                                                  \
     _(GetDOMProperty)                                                       \
     _(GetDOMMember)                                                         \
     _(SetDOMProperty)                                                       \
     _(IsCallable)                                                           \
     _(IsObject)                                                             \
     _(HasClass)                                                             \
     _(AsmJSNeg)                                                             \
     _(AsmJSUnsignedToDouble)                                                \
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1242,16 +1242,25 @@ class LAsmJSInterruptCheck : public LIns
     LAsmJSInterruptCheck()
     { }
 
     bool isCall() const {
         return true;
     }
 };
 
+class LAsmThrowUnreachable : public LInstructionHelper<0, 0, 0>
+{
+  public:
+    LIR_HEADER(AsmThrowUnreachable);
+
+    LAsmThrowUnreachable()
+    { }
+};
+
 class LInterruptCheck : public LInstructionHelper<0, 0, 0>
 {
     Label* oolEntry_;
 
     // Whether this is an implicit interrupt check. Implicit interrupt checks
     // use a patchable backedge and signal handlers instead of an explicit
     // rt->interrupt check.
     bool implicit_;
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -339,16 +339,17 @@
     _(RoundF)                       \
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
     _(AsmJSInterruptCheck)          \
+    _(AsmThrowUnreachable)          \
     _(GetDOMProperty)               \
     _(GetDOMMemberV)                \
     _(GetDOMMemberT)                \
     _(SetDOMProperty)               \
     _(CallDOMNative)                \
     _(IsCallable)                   \
     _(IsObject)                     \
     _(IsObjectAndBranch)            \
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -344,16 +344,17 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_OK,         1
 
 // wasm
 MSG_DEF(JSMSG_WASM_FAIL,               1, JSEXN_TYPEERR, "wasm error: {0}")
 MSG_DEF(JSMSG_WASM_DECODE_FAIL,        2, JSEXN_TYPEERR, "wasm validation error at offset {0}: {1}")
 MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1, JSEXN_SYNTAXERR, "wasm text error: {0}")
 MSG_DEF(JSMSG_WASM_BAD_IND_CALL,       0, JSEXN_ERR,     "wasm indirect call signature mismatch")
 MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR, "first argument must be a typed array")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG,     0, JSEXN_TYPEERR, "second argument, if present, must be an object")
+MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_ERR,     "reached unreachable trap")
 
 // Proxy
 MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE,   2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
 MSG_DEF(JSMSG_CANT_CHANGE_EXTENSIBILITY, 0, JSEXN_TYPEERR, "can't change object's extensibility")
 MSG_DEF(JSMSG_CANT_DEFINE_INVALID,     0, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor")
 MSG_DEF(JSMSG_CANT_DEFINE_NEW,         0, JSEXN_TYPEERR, "proxy can't define a new property on a non-extensible object")
 MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC,    0, JSEXN_TYPEERR, "proxy can't define a non-existent property as non-configurable")
 MSG_DEF(JSMSG_PROXY_DEFINE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy defineProperty handler returned false for property '{0}'")
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -26,21 +26,21 @@ namespace js {
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  *
  * (If you're wondering, 0xb973c0de is used because it looks like "bytecode".)
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 351;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 352;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 418,
+static_assert(JSErr_Limit == 419,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)