Bug 1255772: Implement wasm::Unreachable; r=sunfish
authorBenjamin Bouvier <benj@benj.me>
Thu, 10 Mar 2016 21:00:07 +0100
changeset 288550 e3a45833901b5d826b1586d78aa11e7f453d47ad
parent 288549 eceb5235a54da29b7280153f0a130eece3682d4e
child 288551 fe5bc1369d3efe3501995a8a061a8862bcb10723
push id30084
push userkwierso@gmail.com
push dateTue, 15 Mar 2016 00:39:07 +0000
treeherdermozilla-central@422077f61bcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish
bugs1255772
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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)