Bug 1546138 - Baldr: do failure checking in masm.wasmCallBulitinInstanceMethod (r=lth)
authorLuke Wagner <luke@mozilla.com>
Wed, 01 May 2019 16:55:05 -0500
changeset 532516 8a9dd53d55f6913cf9510a207cf4fb2d44fd4cbb
parent 532515 831cb051385509ce1c25f54e4101ed8cfa439dab
child 532517 274fb69f7d77a018cf66f0179e41fca38f9baaad
push id11268
push usercsabou@mozilla.com
push dateTue, 14 May 2019 15:24:22 +0000
treeherdermozilla-beta@5fb7fcd568d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1546138
milestone68.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 1546138 - Baldr: do failure checking in masm.wasmCallBulitinInstanceMethod (r=lth) This patch further centralizes builtin signature information into SymbolicAddressSignature, removing more than 100 lines of code and preparing the way for the next patch, which will use this eager error check to return a JSObject* from Instance::tableGet. Differential Revision: https://phabricator.services.mozilla.com/D29595
js/src/jit/CodeGenerator.cpp
js/src/jit/Lowering.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/shared/LIR-shared.h
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmBuiltins.cpp
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmIonCompile.cpp
js/src/wasm/WasmTypes.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7416,17 +7416,18 @@ void CodeGenerator::emitWasmCallBase(LWa
       break;
     case wasm::CalleeDesc::Builtin:
       masm.call(desc, callee.builtin());
       reloadRegs = false;
       switchRealm = false;
       break;
     case wasm::CalleeDesc::BuiltinInstanceMethod:
       masm.wasmCallBuiltinInstanceMethod(desc, mir->instanceArg(),
-                                         callee.builtin());
+                                         callee.builtin(),
+                                         mir->builtinMethodFailureMode());
       switchRealm = false;
       break;
   }
 
   // Note the assembler offset for the associated LSafePoint.
   markSafepointAt(masm.currentOffset(), lir);
 
   // Now that all the outbound in-memory args are on the stack, note the
@@ -14013,21 +14014,16 @@ void CodeGenerator::visitIonToWasmCall(L
 void CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) {
   emitIonToWasmCallBase(lir);
 }
 
 void CodeGenerator::visitWasmNullConstant(LWasmNullConstant* lir) {
   masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output()));
 }
 
-void CodeGenerator::visitIsNullPointer(LIsNullPointer* lir) {
-  masm.cmpPtrSet(Assembler::Equal, ToRegister(lir->value()), ImmWord(0),
-                 ToRegister(lir->output()));
-}
-
 void CodeGenerator::visitWasmCompareAndSelect(LWasmCompareAndSelect* ins) {
   bool cmpIs32bit = ins->compareType() == MCompare::Compare_Int32 ||
                     ins->compareType() == MCompare::Compare_UInt32;
   bool selIs32bit = ins->mir()->type() == MIRType::Int32;
 
   if (cmpIs32bit && selIs32bit) {
     Register out = ToRegister(ins->output());
     MOZ_ASSERT(ToRegister(ins->ifTrueExpr()) == out,
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4717,21 +4717,16 @@ void LIRGenerator::visitConstant(MConsta
       MOZ_CRASH("unexpected constant type");
   }
 }
 
 void LIRGenerator::visitWasmNullConstant(MWasmNullConstant* ins) {
   define(new (alloc()) LWasmNullConstant(), ins);
 }
 
-void LIRGenerator::visitIsNullPointer(MIsNullPointer* ins) {
-  define(new (alloc()) LIsNullPointer(useRegisterAtStart(ins->getOperand(0))),
-         ins);
-}
-
 void LIRGenerator::visitWasmFloatConstant(MWasmFloatConstant* ins) {
   switch (ins->type()) {
     case MIRType::Double:
       define(new (alloc()) LDouble(ins->toDouble()), ins);
       break;
     case MIRType::Float32:
       define(new (alloc()) LFloat32(ins->toFloat32()), ins);
       break;
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5446,27 +5446,29 @@ MWasmCall* MWasmCall::New(TempAllocator&
     call->initOperand(call->argRegs_.length(), tableIndex);
   }
 
   return call;
 }
 
 MWasmCall* MWasmCall::NewBuiltinInstanceMethodCall(
     TempAllocator& alloc, const wasm::CallSiteDesc& desc,
-    const wasm::SymbolicAddress builtin, const ABIArg& instanceArg,
-    const Args& args, MIRType resultType, uint32_t stackArgAreaSizeUnaligned) {
+    const wasm::SymbolicAddress builtin, wasm::FailureMode failureMode,
+    const ABIArg& instanceArg, const Args& args, MIRType resultType,
+    uint32_t stackArgAreaSizeUnaligned) {
   auto callee = wasm::CalleeDesc::builtinInstanceMethod(builtin);
   MWasmCall* call = MWasmCall::New(alloc, desc, callee, args, resultType,
                                    stackArgAreaSizeUnaligned, nullptr);
   if (!call) {
     return nullptr;
   }
 
   MOZ_ASSERT(instanceArg != ABIArg());
   call->instanceArg_ = instanceArg;
+  call->builtinMethodFailureMode_ = failureMode;
   return call;
 }
 
 void MSqrt::trySpecializeFloat32(TempAllocator& alloc) {
   if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
     if (input()->type() == MIRType::Float32) {
       ConvertDefinitionToDouble<0>(alloc, input(), this);
     }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1565,39 +1565,16 @@ class MWasmNullConstant : public MNullar
   bool congruentTo(const MDefinition* ins) const override {
     return ins->isWasmNullConstant();
   }
   AliasSet getAliasSet() const override { return AliasSet::None(); }
 
   ALLOW_CLONE(MWasmNullConstant)
 };
 
-class MIsNullPointer : public MUnaryInstruction, public NoTypePolicy::Data {
-  explicit MIsNullPointer(MDefinition* value)
-      : MUnaryInstruction(classOpcode, value) {
-    MOZ_ASSERT(value->type() == MIRType::Pointer);
-    setResultType(MIRType::Boolean);
-    setMovable();
-  }
-
- public:
-  INSTRUCTION_HEADER(IsNullPointer);
-
-  static MIsNullPointer* New(TempAllocator& alloc, MDefinition* value) {
-    return new (alloc) MIsNullPointer(value);
-  }
-
-  bool congruentTo(const MDefinition* ins) const override {
-    return congruentIfOperandsEqual(ins);
-  }
-  AliasSet getAliasSet() const override { return AliasSet::None(); }
-
-  ALLOW_CLONE(MIsNullPointer)
-};
-
 // Floating-point value as created by wasm. Just a constant value, used to
 // effectively inhibite all the MIR optimizations. This uses the same LIR nodes
 // as a MConstant of the same type would.
 class MWasmFloatConstant : public MNullaryInstruction {
   union {
     float f32_;
     double f64_;
     uint64_t bits_;
@@ -11892,25 +11869,27 @@ class MWasmStackArg : public MUnaryInstr
 
   uint32_t spOffset() const { return spOffset_; }
   void incrementOffset(uint32_t inc) { spOffset_ += inc; }
 };
 
 class MWasmCall final : public MVariadicInstruction, public NoTypePolicy::Data {
   wasm::CallSiteDesc desc_;
   wasm::CalleeDesc callee_;
+  wasm::FailureMode builtinMethodFailureMode_;
   FixedList<AnyRegister> argRegs_;
   uint32_t stackArgAreaSizeUnaligned_;
   ABIArg instanceArg_;
 
   MWasmCall(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee,
             uint32_t stackArgAreaSizeUnaligned)
       : MVariadicInstruction(classOpcode),
         desc_(desc),
         callee_(callee),
+        builtinMethodFailureMode_(wasm::FailureMode::Infallible),
         stackArgAreaSizeUnaligned_(stackArgAreaSizeUnaligned) {}
 
  public:
   INSTRUCTION_HEADER(WasmCall)
 
   struct Arg {
     AnyRegister reg;
     MDefinition* def;
@@ -11920,26 +11899,31 @@ class MWasmCall final : public MVariadic
 
   static MWasmCall* New(TempAllocator& alloc, const wasm::CallSiteDesc& desc,
                         const wasm::CalleeDesc& callee, const Args& args,
                         MIRType resultType, uint32_t stackArgAreaSizeUnaligned,
                         MDefinition* tableIndex = nullptr);
 
   static MWasmCall* NewBuiltinInstanceMethodCall(
       TempAllocator& alloc, const wasm::CallSiteDesc& desc,
-      const wasm::SymbolicAddress builtin, const ABIArg& instanceArg,
-      const Args& args, MIRType resultType, uint32_t stackArgAreaSizeUnaligned);
+      const wasm::SymbolicAddress builtin, wasm::FailureMode failureMode,
+      const ABIArg& instanceArg, const Args& args, MIRType resultType,
+      uint32_t stackArgAreaSizeUnaligned);
 
   size_t numArgs() const { return argRegs_.length(); }
   AnyRegister registerForArg(size_t index) const {
     MOZ_ASSERT(index < numArgs());
     return argRegs_[index];
   }
   const wasm::CallSiteDesc& desc() const { return desc_; }
   const wasm::CalleeDesc& callee() const { return callee_; }
+  wasm::FailureMode builtinMethodFailureMode() const {
+    MOZ_ASSERT(callee_.which() == wasm::CalleeDesc::BuiltinInstanceMethod);
+    return builtinMethodFailureMode_;
+  }
   uint32_t stackArgAreaSizeUnaligned() const {
     return stackArgAreaSizeUnaligned_;
   }
 
   bool possiblyCalls() const override { return true; }
 
   const ABIArg& instanceArg() const { return instanceArg_; }
 };
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -3186,33 +3186,52 @@ CodeOffset MacroAssembler::wasmCallImpor
                     WasmTlsReg);
   loadWasmPinnedRegsFromTls();
 
   return call(desc, ABINonArgReg0);
 }
 
 CodeOffset MacroAssembler::wasmCallBuiltinInstanceMethod(
     const wasm::CallSiteDesc& desc, const ABIArg& instanceArg,
-    wasm::SymbolicAddress builtin) {
+    wasm::SymbolicAddress builtin, wasm::FailureMode failureMode) {
   MOZ_ASSERT(instanceArg != ABIArg());
 
   if (instanceArg.kind() == ABIArg::GPR) {
     loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, instance)),
             instanceArg.gpr());
   } else if (instanceArg.kind() == ABIArg::Stack) {
     // Safe to use ABINonArgReg0 since it's the last thing before the call.
     Register scratch = ABINonArgReg0;
     loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, instance)), scratch);
     storePtr(scratch,
              Address(getStackPointer(), instanceArg.offsetFromArgBase()));
   } else {
     MOZ_CRASH("Unknown abi passing style for pointer");
   }
 
-  return call(desc, builtin);
+  CodeOffset ret = call(desc, builtin);
+
+  if (failureMode != wasm::FailureMode::Infallible) {
+    Label noTrap;
+    switch (failureMode) {
+      case wasm::FailureMode::Infallible:
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE();
+      case wasm::FailureMode::FailOnNegI32:
+        branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &noTrap);
+        break;
+      case wasm::FailureMode::FailOnNullPtr:
+        branchTestPtr(Assembler::NonZero, ReturnReg, ReturnReg, &noTrap);
+        break;
+    }
+    wasmTrap(wasm::Trap::ThrowReported,
+             wasm::BytecodeOffset(desc.lineOrBytecode()));
+    bind(&noTrap);
+  }
+
+  return ret;
 }
 
 CodeOffset MacroAssembler::wasmCallIndirect(const wasm::CallSiteDesc& desc,
                                             const wasm::CalleeDesc& callee,
                                             bool needsBoundsCheck) {
   Register scratch = WasmTableCallScratchReg0;
   Register index = WasmTableCallIndexReg;
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1922,17 +1922,18 @@ class MacroAssembler : public MacroAssem
                               const wasm::CalleeDesc& callee,
                               bool needsBoundsCheck);
 
   // This function takes care of loading the pointer to the current instance
   // as the implicit first argument. It preserves TLS and pinned registers.
   // (TLS & pinned regs are non-volatile registers in the system ABI).
   CodeOffset wasmCallBuiltinInstanceMethod(const wasm::CallSiteDesc& desc,
                                            const ABIArg& instanceArg,
-                                           wasm::SymbolicAddress builtin);
+                                           wasm::SymbolicAddress builtin,
+                                           wasm::FailureMode failureMode);
 
   // As enterFakeExitFrame(), but using register conventions appropriate for
   // wasm stubs.
   void enterFakeExitFrameForWasm(Register cxreg, Register scratch,
                                  ExitFrameType type) PER_SHARED_ARCH;
 
  public:
   // ========================================================================
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -6777,27 +6777,16 @@ inline bool IsWasmCall(LNode::Opcode op)
 }
 
 class LWasmNullConstant : public LInstructionHelper<1, 0, 0> {
  public:
   LIR_HEADER(WasmNullConstant);
   explicit LWasmNullConstant() : LInstructionHelper(classOpcode) {}
 };
 
-class LIsNullPointer : public LInstructionHelper<1, 1, 0> {
- public:
-  LIR_HEADER(IsNullPointer);
-  explicit LIsNullPointer(const LAllocation& value)
-      : LInstructionHelper(classOpcode) {
-    setOperand(0, value);
-  }
-  MIsNullPointer* mir() const { return mirRaw()->toIsNullPointer(); }
-  const LAllocation* value() { return getOperand(0); }
-};
-
 template <size_t Defs>
 class LWasmCallBase : public LVariadicInstruction<Defs, 0> {
   using Base = LVariadicInstruction<Defs, 0>;
 
   bool needsBoundsCheck_;
 
  public:
   LWasmCallBase(LNode::Opcode opcode, uint32_t numOperands,
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -4643,24 +4643,25 @@ class BaseCompiler final : public BaseCo
     CalleeDesc callee = CalleeDesc::import(globalDataOffset);
     return masm.wasmCallImport(desc, callee);
   }
 
   CodeOffset builtinCall(SymbolicAddress builtin, const FunctionCall& call) {
     return callSymbolic(builtin, call);
   }
 
-  CodeOffset builtinInstanceMethodCall(SymbolicAddress builtin,
+  CodeOffset builtinInstanceMethodCall(const SymbolicAddressSignature& builtin,
                                        const ABIArg& instanceArg,
                                        const FunctionCall& call) {
     // Builtin method calls assume the TLS register has been set.
     masm.loadWasmTlsRegFromFrame();
 
     CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
-    return masm.wasmCallBuiltinInstanceMethod(desc, instanceArg, builtin);
+    return masm.wasmCallBuiltinInstanceMethod(
+        desc, instanceArg, builtin.identity, builtin.failureMode);
   }
 
   //////////////////////////////////////////////////////////////////////
   //
   // Sundry low-level code generators.
 
   // The compiler depends on moveImm32() clearing the high bits of a 64-bit
   // register on 64-bit systems except MIPS64 where high bits are sign extended
@@ -9796,17 +9797,17 @@ bool BaseCompiler::emitInstanceCall(uint
         t = sizeof(void*) == 4 ? ValType::I32 : ValType::I64;
         break;
       default:
         MOZ_CRASH("Unexpected type");
     }
     passArg(t, peek(numNonInstanceArgs - i), &baselineCall);
   }
   CodeOffset raOffset =
-      builtinInstanceMethodCall(builtin.identity, instanceArg, baselineCall);
+      builtinInstanceMethodCall(builtin, instanceArg, baselineCall);
   if (!createStackMap("emitInstanceCall", raOffset)) {
     return false;
   }
 
   endCall(baselineCall, stackSpace);
 
   popValueStackBy(numNonInstanceArgs);
 
@@ -10158,64 +10159,48 @@ bool BaseCompiler::emitWait(ValType type
   if (!iter_.readWait(&addr, type, byteSize, &nothing, &nothing)) {
     return false;
   }
 
   if (deadCode_) {
     return true;
   }
 
-  // Returns -1 on trap, otherwise nonnegative result.
   switch (type.code()) {
     case ValType::I32:
       if (!emitInstanceCall(lineOrBytecode, SASigWaitI32)) {
         return false;
       }
       break;
     case ValType::I64:
       if (!emitInstanceCall(lineOrBytecode, SASigWaitI64)) {
         return false;
       }
       break;
     default:
       MOZ_CRASH();
   }
 
-  Label ok;
-  masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
-  trap(Trap::ThrowReported);
-  masm.bind(&ok);
-
   return true;
 }
 
 bool BaseCompiler::emitWake() {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
   Nothing nothing;
   LinearMemoryAddress<Nothing> addr;
   if (!iter_.readWake(&addr, &nothing)) {
     return false;
   }
 
   if (deadCode_) {
     return true;
   }
 
-  // Returns -1 on trap, otherwise nonnegative result.
-  if (!emitInstanceCall(lineOrBytecode, SASigWake)) {
-    return false;
-  }
-
-  Label ok;
-  masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
-  trap(Trap::ThrowReported);
-  masm.bind(&ok);
-
-  return true;
+  return emitInstanceCall(lineOrBytecode, SASigWake);
 }
 
 #ifdef ENABLE_WASM_BULKMEM_OPS
 bool BaseCompiler::emitMemOrTableCopy(bool isMem) {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
   uint32_t dstMemOrTableIndex = 0;
   uint32_t srcMemOrTableIndex = 0;
@@ -10224,95 +10209,69 @@ bool BaseCompiler::emitMemOrTableCopy(bo
                                 &srcMemOrTableIndex, &nothing, &nothing)) {
     return false;
   }
 
   if (deadCode_) {
     return true;
   }
 
-  // Returns -1 on trap, otherwise 0.
   if (isMem) {
     MOZ_ASSERT(srcMemOrTableIndex == 0);
     MOZ_ASSERT(dstMemOrTableIndex == 0);
     if (!emitInstanceCall(lineOrBytecode, SASigMemCopy,
                           /*pushReturnedValue=*/false)) {
       return false;
     }
   } else {
     pushI32(dstMemOrTableIndex);
     pushI32(srcMemOrTableIndex);
     if (!emitInstanceCall(lineOrBytecode, SASigTableCopy,
                           /*pushReturnedValue=*/false)) {
       return false;
     }
   }
 
-  Label ok;
-  masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
-  trap(Trap::ThrowReported);
-  masm.bind(&ok);
-
   return true;
 }
 
 bool BaseCompiler::emitDataOrElemDrop(bool isData) {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
   uint32_t segIndex = 0;
   if (!iter_.readDataOrElemDrop(isData, &segIndex)) {
     return false;
   }
 
   if (deadCode_) {
     return true;
   }
 
   // Despite the cast to int32_t, the callee regards the value as unsigned.
-  //
-  // Returns -1 on trap, otherwise 0.
   pushI32(int32_t(segIndex));
-  const SymbolicAddressSignature& callee =
-      isData ? SASigDataDrop : SASigElemDrop;
-  if (!emitInstanceCall(lineOrBytecode, callee, /*pushReturnedValue=*/false)) {
-    return false;
-  }
-
-  Label ok;
-  masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
-  trap(Trap::ThrowReported);
-  masm.bind(&ok);
-
-  return true;
+
+  return emitInstanceCall(lineOrBytecode,
+                          isData ? SASigDataDrop : SASigElemDrop,
+                          /*pushReturnedValue=*/false);
 }
 
 bool BaseCompiler::emitMemFill() {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
   Nothing nothing;
   if (!iter_.readMemFill(&nothing, &nothing, &nothing)) {
     return false;
   }
 
   if (deadCode_) {
     return true;
   }
 
-  // Returns -1 on trap, otherwise 0.
-  if (!emitInstanceCall(lineOrBytecode, SASigMemFill,
-                        /*pushReturnedValue=*/false)) {
-    return false;
-  }
-
-  Label ok;
-  masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
-  trap(Trap::ThrowReported);
-  masm.bind(&ok);
-
-  return true;
+  return emitInstanceCall(lineOrBytecode, SASigMemFill,
+                          /*pushReturnedValue=*/false);
 }
 
 bool BaseCompiler::emitMemOrTableInit(bool isMem) {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
   uint32_t segIndex = 0;
   uint32_t dstTableIndex = 0;
   Nothing nothing;
@@ -10320,36 +10279,30 @@ bool BaseCompiler::emitMemOrTableInit(bo
                                 &nothing, &nothing)) {
     return false;
   }
 
   if (deadCode_) {
     return true;
   }
 
-  // Returns -1 on trap, otherwise 0.
   pushI32(int32_t(segIndex));
   if (isMem) {
     if (!emitInstanceCall(lineOrBytecode, SASigMemInit,
                           /*pushReturnedValue=*/false)) {
       return false;
     }
   } else {
     pushI32(dstTableIndex);
     if (!emitInstanceCall(lineOrBytecode, SASigTableInit,
                           /*pushReturnedValue=*/false)) {
       return false;
     }
   }
 
-  Label ok;
-  masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
-  trap(Trap::ThrowReported);
-  masm.bind(&ok);
-
   return true;
 }
 #endif
 
 MOZ_MUST_USE
 bool BaseCompiler::emitTableFill() {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
@@ -10359,56 +10312,41 @@ bool BaseCompiler::emitTableFill() {
     return false;
   }
 
   if (deadCode_) {
     return true;
   }
 
   // fill(start:u32, val:ref, len:u32, table:u32) -> u32
-  //
-  // Returns -1 on trap, otherwise 0.
   pushI32(tableIndex);
-  if (!emitInstanceCall(lineOrBytecode, SASigTableFill,
-                        /*pushReturnedValue=*/false)) {
-    return false;
-  }
-
-  Label ok;
-  masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &ok);
-  trap(Trap::ThrowReported);
-  masm.bind(&ok);
-
-  return true;
+  return emitInstanceCall(lineOrBytecode, SASigTableFill,
+                          /*pushReturnedValue=*/false);
 }
 
 MOZ_MUST_USE
 bool BaseCompiler::emitTableGet() {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
   Nothing index;
   uint32_t tableIndex;
   if (!iter_.readTableGet(&tableIndex, &index)) {
     return false;
   }
   if (deadCode_) {
     return true;
   }
   // get(index:u32, table:u32) -> void*
   //
-  // Returns nullptr for error, otherwise a pointer to a nonmoveable memory
-  // location that holds the anyref value.
+  // Returns a pointer to a nonmoveable memory location that holds the anyref
+  // value.
   pushI32(tableIndex);
   if (!emitInstanceCall(lineOrBytecode, SASigTableGet,
                         /*pushReturnedValue=*/false)) {
     return false;
   }
-  Label noTrap;
-  masm.branchTestPtr(Assembler::NonZero, ReturnReg, ReturnReg, &noTrap);
-  trap(Trap::ThrowReported);
-  masm.bind(&noTrap);
 
   masm.loadPtr(Address(ReturnReg, 0), ReturnReg);
 
   // Push the resulting anyref back on the eval stack.  NOTE: needRef() must
   // not kill the value in the register.
   RegPtr r = RegPtr(ReturnReg);
   needRef(r);
   pushRef(r);
@@ -10424,61 +10362,48 @@ bool BaseCompiler::emitTableGrow() {
   uint32_t tableIndex;
   if (!iter_.readTableGrow(&tableIndex, &initValue, &delta)) {
     return false;
   }
   if (deadCode_) {
     return true;
   }
   // grow(initValue:anyref, delta:u32, table:u32) -> u32
-  //
-  // infallible.
   pushI32(tableIndex);
   return emitInstanceCall(lineOrBytecode, SASigTableGrow);
 }
 
 MOZ_MUST_USE
 bool BaseCompiler::emitTableSet() {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
   Nothing index, value;
   uint32_t tableIndex;
   if (!iter_.readTableSet(&tableIndex, &index, &value)) {
     return false;
   }
   if (deadCode_) {
     return true;
   }
   // set(index:u32, value:ref, table:u32) -> i32
-  //
-  // Returns -1 on range error, otherwise 0 (which is then ignored).
   pushI32(tableIndex);
-  if (!emitInstanceCall(lineOrBytecode, SASigTableSet,
-                        /*pushReturnedValue=*/false)) {
-    return false;
-  }
-  Label noTrap;
-  masm.branchTest32(Assembler::NotSigned, ReturnReg, ReturnReg, &noTrap);
-  trap(Trap::ThrowReported);
-  masm.bind(&noTrap);
-  return true;
+  return emitInstanceCall(lineOrBytecode, SASigTableSet,
+                          /*pushReturnedValue=*/false);
 }
 
 MOZ_MUST_USE
 bool BaseCompiler::emitTableSize() {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
   uint32_t tableIndex;
   if (!iter_.readTableSize(&tableIndex)) {
     return false;
   }
   if (deadCode_) {
     return true;
   }
   // size(table:u32) -> u32
-  //
-  // infallible.
   pushI32(tableIndex);
   return emitInstanceCall(lineOrBytecode, SASigTableSize);
 }
 
 bool BaseCompiler::emitStructNew() {
   uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
   uint32_t typeIndex;
@@ -10498,23 +10423,16 @@ bool BaseCompiler::emitStructNew() {
 
   const StructType& structType = env_.types[typeIndex].structType();
 
   pushI32(structType.moduleIndex_);
   if (!emitInstanceCall(lineOrBytecode, SASigStructNew)) {
     return false;
   }
 
-  // Null pointer check.
-
-  Label ok;
-  masm.branchTestPtr(Assembler::NonZero, ReturnReg, ReturnReg, &ok);
-  trap(Trap::ThrowReported);
-  masm.bind(&ok);
-
   // As many arguments as there are fields.
 
   MOZ_ASSERT(args.length() == structType.fields_.length());
 
   // Optimization opportunity: Iterate backward to pop arguments off the
   // stack.  This will generate more instructions than we want, since we
   // really only need to pop the stack once at the end, not for every element,
   // but to do better we need a bit more machinery to load elements off the
@@ -10834,18 +10752,16 @@ bool BaseCompiler::emitStructNarrow() {
 
   RegPtr rp = popRef();
 
   // AnyRef -> (ref T) must first unbox; leaves rp or null
 
   bool mustUnboxAnyref = inputType == ValType::AnyRef;
 
   // Dynamic downcast (ref T) -> (ref U), leaves rp or null
-  //
-  // Infallible.
   const StructType& outputStruct =
       env_.types[outputType.refTypeIndex()].structType();
 
   pushI32(mustUnboxAnyref);
   pushI32(outputStruct.moduleIndex_);
   pushRef(rp);
   return emitInstanceCall(lineOrBytecode, SASigStructNarrow);
 }
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -51,116 +51,162 @@ static const unsigned BUILTIN_THUNK_LIFO
 #define _F64 MIRType::Double
 #define _F32 MIRType::Float32
 #define _I32 MIRType::Int32
 #define _I64 MIRType::Int64
 #define _PTR MIRType::Pointer
 #define _RoN MIRType::RefOrNull
 #define _VOID MIRType::None
 #define _END MIRType::None
+#define _Infallible FailureMode::Infallible
+#define _FailOnNegI32 FailureMode::FailOnNegI32
+#define _FailOnNullPtr FailureMode::FailOnNullPtr
 
 namespace js {
 namespace wasm {
 
 const SymbolicAddressSignature SASigSinD = {
-    SymbolicAddress::SinD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::SinD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigCosD = {
-    SymbolicAddress::CosD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::CosD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigTanD = {
-    SymbolicAddress::TanD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::TanD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigASinD = {
-    SymbolicAddress::ASinD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::ASinD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigACosD = {
-    SymbolicAddress::ACosD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::ACosD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigATanD = {
-    SymbolicAddress::ATanD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::ATanD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigCeilD = {
-    SymbolicAddress::CeilD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::CeilD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigCeilF = {
-    SymbolicAddress::CeilF, _F32, 1, {_F32, _END}};
+    SymbolicAddress::CeilF, _F32, _Infallible, 1, {_F32, _END}};
 const SymbolicAddressSignature SASigFloorD = {
-    SymbolicAddress::FloorD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::FloorD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigFloorF = {
-    SymbolicAddress::FloorF, _F32, 1, {_F32, _END}};
+    SymbolicAddress::FloorF, _F32, _Infallible, 1, {_F32, _END}};
 const SymbolicAddressSignature SASigTruncD = {
-    SymbolicAddress::TruncD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::TruncD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigTruncF = {
-    SymbolicAddress::TruncF, _F32, 1, {_F32, _END}};
+    SymbolicAddress::TruncF, _F32, _Infallible, 1, {_F32, _END}};
 const SymbolicAddressSignature SASigNearbyIntD = {
-    SymbolicAddress::NearbyIntD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::NearbyIntD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigNearbyIntF = {
-    SymbolicAddress::NearbyIntF, _F32, 1, {_F32, _END}};
+    SymbolicAddress::NearbyIntF, _F32, _Infallible, 1, {_F32, _END}};
 const SymbolicAddressSignature SASigExpD = {
-    SymbolicAddress::ExpD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::ExpD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigLogD = {
-    SymbolicAddress::LogD, _F64, 1, {_F64, _END}};
+    SymbolicAddress::LogD, _F64, _Infallible, 1, {_F64, _END}};
 const SymbolicAddressSignature SASigPowD = {
-    SymbolicAddress::PowD, _F64, 2, {_F64, _F64, _END}};
+    SymbolicAddress::PowD, _F64, _Infallible, 2, {_F64, _F64, _END}};
 const SymbolicAddressSignature SASigATan2D = {
-    SymbolicAddress::ATan2D, _F64, 2, {_F64, _F64, _END}};
+    SymbolicAddress::ATan2D, _F64, _Infallible, 2, {_F64, _F64, _END}};
 const SymbolicAddressSignature SASigMemoryGrow = {
-    SymbolicAddress::MemoryGrow, _I32, 2, {_PTR, _I32, _END}};
+    SymbolicAddress::MemoryGrow, _I32, _Infallible, 2, {_PTR, _I32, _END}};
 const SymbolicAddressSignature SASigMemorySize = {
-    SymbolicAddress::MemorySize, _I32, 1, {_PTR, _END}};
-const SymbolicAddressSignature SASigWaitI32 = {
-    SymbolicAddress::WaitI32, _I32, 4, {_PTR, _I32, _I32, _I64, _END}};
-const SymbolicAddressSignature SASigWaitI64 = {
-    SymbolicAddress::WaitI64, _I32, 4, {_PTR, _I32, _I64, _I64, _END}};
+    SymbolicAddress::MemorySize, _I32, _Infallible, 1, {_PTR, _END}};
+const SymbolicAddressSignature SASigWaitI32 = {SymbolicAddress::WaitI32,
+                                               _I32,
+                                               _FailOnNegI32,
+                                               4,
+                                               {_PTR, _I32, _I32, _I64, _END}};
+const SymbolicAddressSignature SASigWaitI64 = {SymbolicAddress::WaitI64,
+                                               _I32,
+                                               _FailOnNegI32,
+                                               4,
+                                               {_PTR, _I32, _I64, _I64, _END}};
 const SymbolicAddressSignature SASigWake = {
-    SymbolicAddress::Wake, _I32, 3, {_PTR, _I32, _I32, _END}};
-const SymbolicAddressSignature SASigMemCopy = {
-    SymbolicAddress::MemCopy, _I32, 4, {_PTR, _I32, _I32, _I32, _END}};
+    SymbolicAddress::Wake, _I32, _FailOnNegI32, 3, {_PTR, _I32, _I32, _END}};
+const SymbolicAddressSignature SASigMemCopy = {SymbolicAddress::MemCopy,
+                                               _VOID,
+                                               _FailOnNegI32,
+                                               4,
+                                               {_PTR, _I32, _I32, _I32, _END}};
 const SymbolicAddressSignature SASigDataDrop = {
-    SymbolicAddress::DataDrop, _I32, 2, {_PTR, _I32, _END}};
-const SymbolicAddressSignature SASigMemFill = {
-    SymbolicAddress::MemFill, _I32, 4, {_PTR, _I32, _I32, _I32, _END}};
+    SymbolicAddress::DataDrop, _VOID, _FailOnNegI32, 2, {_PTR, _I32, _END}};
+const SymbolicAddressSignature SASigMemFill = {SymbolicAddress::MemFill,
+                                               _VOID,
+                                               _FailOnNegI32,
+                                               4,
+                                               {_PTR, _I32, _I32, _I32, _END}};
 const SymbolicAddressSignature SASigMemInit = {
-    SymbolicAddress::MemInit, _I32, 5, {_PTR, _I32, _I32, _I32, _I32, _END}};
+    SymbolicAddress::MemInit,
+    _VOID,
+    _FailOnNegI32,
+    5,
+    {_PTR, _I32, _I32, _I32, _I32, _END}};
 const SymbolicAddressSignature SASigTableCopy = {
     SymbolicAddress::TableCopy,
-    _I32,
+    _VOID,
+    _FailOnNegI32,
     6,
     {_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
 const SymbolicAddressSignature SASigElemDrop = {
-    SymbolicAddress::ElemDrop, _I32, 2, {_PTR, _I32, _END}};
+    SymbolicAddress::ElemDrop, _VOID, _FailOnNegI32, 2, {_PTR, _I32, _END}};
 const SymbolicAddressSignature SASigTableFill = {
-    SymbolicAddress::TableFill, _I32, 5, {_PTR, _I32, _RoN, _I32, _I32, _END}};
-const SymbolicAddressSignature SASigTableGet = {
-    SymbolicAddress::TableGet, _PTR, 3, {_PTR, _I32, _I32, _END}};
+    SymbolicAddress::TableFill,
+    _VOID,
+    _FailOnNegI32,
+    5,
+    {_PTR, _I32, _RoN, _I32, _I32, _END}};
+const SymbolicAddressSignature SASigTableGet = {SymbolicAddress::TableGet,
+                                                _PTR,
+                                                _FailOnNullPtr,
+                                                3,
+                                                {_PTR, _I32, _I32, _END}};
 const SymbolicAddressSignature SASigTableGrow = {
-    SymbolicAddress::TableGrow, _I32, 4, {_PTR, _RoN, _I32, _I32, _END}};
+    SymbolicAddress::TableGrow,
+    _I32,
+    _Infallible,
+    4,
+    {_PTR, _RoN, _I32, _I32, _END}};
 const SymbolicAddressSignature SASigTableInit = {
     SymbolicAddress::TableInit,
-    _I32,
+    _VOID,
+    _FailOnNegI32,
     6,
     {_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
-const SymbolicAddressSignature SASigTableSet = {
-    SymbolicAddress::TableSet, _I32, 4, {_PTR, _I32, _RoN, _I32, _END}};
+const SymbolicAddressSignature SASigTableSet = {SymbolicAddress::TableSet,
+                                                _VOID,
+                                                _FailOnNegI32,
+                                                4,
+                                                {_PTR, _I32, _RoN, _I32, _END}};
 const SymbolicAddressSignature SASigTableSize = {
-    SymbolicAddress::TableSize, _I32, 2, {_PTR, _I32, _END}};
+    SymbolicAddress::TableSize, _I32, _Infallible, 2, {_PTR, _I32, _END}};
 const SymbolicAddressSignature SASigPostBarrier = {
-    SymbolicAddress::PostBarrier, _VOID, 2, {_PTR, _PTR, _END}};
+    SymbolicAddress::PostBarrier, _VOID, _Infallible, 2, {_PTR, _PTR, _END}};
 const SymbolicAddressSignature SASigPostBarrierFiltering = {
-    SymbolicAddress::PostBarrierFiltering, _VOID, 2, {_PTR, _PTR, _END}};
+    SymbolicAddress::PostBarrierFiltering,
+    _VOID,
+    _Infallible,
+    2,
+    {_PTR, _PTR, _END}};
 const SymbolicAddressSignature SASigStructNew = {
-    SymbolicAddress::StructNew, _RoN, 2, {_PTR, _I32, _END}};
+    SymbolicAddress::StructNew, _RoN, _FailOnNullPtr, 2, {_PTR, _I32, _END}};
 const SymbolicAddressSignature SASigStructNarrow = {
-    SymbolicAddress::StructNarrow, _RoN, 4, {_PTR, _I32, _I32, _RoN, _END}};
+    SymbolicAddress::StructNarrow,
+    _RoN,
+    _Infallible,
+    4,
+    {_PTR, _I32, _I32, _RoN, _END}};
 
 }  // namespace wasm
 }  // namespace js
 
 #undef _F64
 #undef _F32
 #undef _I32
 #undef _I64
 #undef _PTR
 #undef _RoN
 #undef _VOID
 #undef _END
+#undef _Infallible
+#undef _FailOnNegI32
+#undef _FailOnNullPtr
 
 // ============================================================================
 // WebAssembly builtin C++ functions called from wasm code to implement internal
 // wasm operations: implementations.
 
 #if defined(JS_CODEGEN_ARM)
 extern "C" {
 
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -323,34 +323,36 @@ Instance::callImport_funcref(Instance* i
   if (!CheckFuncRefValue(cx, rval, &fun)) {
     return false;
   }
 
   *(void**)argv = fun;
   return true;
 }
 
-/* static */ uint32_t /* infallible */
-Instance::memoryGrow_i32(Instance* instance, uint32_t delta) {
+/* static */ uint32_t Instance::memoryGrow_i32(Instance* instance,
+                                               uint32_t delta) {
+  MOZ_ASSERT(SASigMemoryGrow.failureMode == FailureMode::Infallible);
   MOZ_ASSERT(!instance->isAsmJS());
 
   JSContext* cx = TlsContext.get();
   RootedWasmMemoryObject memory(cx, instance->memory_);
 
   uint32_t ret = WasmMemoryObject::grow(memory, delta, cx);
 
   // If there has been a moving grow, this Instance should have been notified.
   MOZ_RELEASE_ASSERT(instance->tlsData()->memoryBase ==
                      instance->memory_->buffer().dataPointerEither());
 
   return ret;
 }
 
-/* static */ uint32_t /* infallible */
-Instance::memorySize_i32(Instance* instance) {
+/* static */ uint32_t Instance::memorySize_i32(Instance* instance) {
+  MOZ_ASSERT(SASigMemorySize.failureMode == FailureMode::Infallible);
+
   // This invariant must hold when running Wasm code. Assert it here so we can
   // write tests for cross-realm calls.
   MOZ_ASSERT(TlsContext.get()->realm() == instance->realm());
 
   uint32_t byteLength = instance->memory()->volatileMemoryLength();
   MOZ_ASSERT(byteLength % wasm::PageSize == 0);
   return byteLength / wasm::PageSize;
 }
@@ -388,30 +390,32 @@ static int32_t PerformWait(Instance* ins
       return 2;
     case FutexThread::WaitResult::Error:
       return -1;
     default:
       MOZ_CRASH();
   }
 }
 
-/* static */ int32_t /* -1 to signal trap; nonnegative result for ok */
-Instance::wait_i32(Instance* instance, uint32_t byteOffset, int32_t value,
-                   int64_t timeout_ns) {
+/* static */ int32_t Instance::wait_i32(Instance* instance, uint32_t byteOffset,
+                                        int32_t value, int64_t timeout_ns) {
+  MOZ_ASSERT(SASigWaitI32.failureMode == FailureMode::FailOnNegI32);
   return PerformWait<int32_t>(instance, byteOffset, value, timeout_ns);
 }
 
-/* static */ int32_t /* -1 to signal trap; nonnegative result for ok */
-Instance::wait_i64(Instance* instance, uint32_t byteOffset, int64_t value,
-                   int64_t timeout_ns) {
+/* static */ int32_t Instance::wait_i64(Instance* instance, uint32_t byteOffset,
+                                        int64_t value, int64_t timeout_ns) {
+  MOZ_ASSERT(SASigWaitI64.failureMode == FailureMode::FailOnNegI32);
   return PerformWait<int64_t>(instance, byteOffset, value, timeout_ns);
 }
 
-/* static */ int32_t /* -1 to signal trap; nonnegative for ok */
-Instance::wake(Instance* instance, uint32_t byteOffset, int32_t count) {
+/* static */ int32_t Instance::wake(Instance* instance, uint32_t byteOffset,
+                                    int32_t count) {
+  MOZ_ASSERT(SASigWake.failureMode == FailureMode::FailOnNegI32);
+
   JSContext* cx = TlsContext.get();
 
   // The alignment guard is not in the wasm spec as of 2017-11-02, but is
   // considered likely to appear, as 4-byte alignment is required for WAKE by
   // the spec's validation algorithm.
 
   if (byteOffset & 3) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
@@ -432,19 +436,21 @@ Instance::wake(Instance* instance, uint3
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_WAKE_OVERFLOW);
     return -1;
   }
 
   return int32_t(woken);
 }
 
-/* static */ int32_t /* -1 to signal trap; 0 for ok */
-Instance::memCopy(Instance* instance, uint32_t dstByteOffset,
-                  uint32_t srcByteOffset, uint32_t len) {
+/* static */ int32_t Instance::memCopy(Instance* instance,
+                                       uint32_t dstByteOffset,
+                                       uint32_t srcByteOffset, uint32_t len) {
+  MOZ_ASSERT(SASigMemCopy.failureMode == FailureMode::FailOnNegI32);
+
   WasmMemoryObject* mem = instance->memory();
   uint32_t memLen = mem->volatileMemoryLength();
 
   if (len == 0) {
     // Even though the length is zero, we must check for a valid offset.  But
     // zero-length operations at the edge of the memory are allowed.
     if (dstByteOffset <= memLen && srcByteOffset <= memLen) {
       return 0;
@@ -500,18 +506,19 @@ Instance::memCopy(Instance* instance, ui
   }
 
   JSContext* cx = TlsContext.get();
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             JSMSG_WASM_OUT_OF_BOUNDS);
   return -1;
 }
 
-/* static */ int32_t /* -1 to signal trap; 0 for ok */
-Instance::dataDrop(Instance* instance, uint32_t segIndex) {
+/* static */ int32_t Instance::dataDrop(Instance* instance, uint32_t segIndex) {
+  MOZ_ASSERT(SASigDataDrop.failureMode == FailureMode::FailOnNegI32);
+
   MOZ_RELEASE_ASSERT(size_t(segIndex) < instance->passiveDataSegments_.length(),
                      "ensured by validation");
 
   if (!instance->passiveDataSegments_[segIndex]) {
     JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                               JSMSG_WASM_DROPPED_DATA_SEG);
     return -1;
   }
@@ -519,19 +526,20 @@ Instance::dataDrop(Instance* instance, u
   SharedDataSegment& segRefPtr = instance->passiveDataSegments_[segIndex];
   MOZ_RELEASE_ASSERT(!segRefPtr->active());
 
   // Drop this instance's reference to the DataSegment so it can be released.
   segRefPtr = nullptr;
   return 0;
 }
 
-/* static */ int32_t /* -1 to signal trap; 0 for ok */
-Instance::memFill(Instance* instance, uint32_t byteOffset, uint32_t value,
-                  uint32_t len) {
+/* static */ int32_t Instance::memFill(Instance* instance, uint32_t byteOffset,
+                                       uint32_t value, uint32_t len) {
+  MOZ_ASSERT(SASigMemFill.failureMode == FailureMode::FailOnNegI32);
+
   WasmMemoryObject* mem = instance->memory();
   uint32_t memLen = mem->volatileMemoryLength();
 
   if (len == 0) {
     // Even though the length is zero, we must check for a valid offset.  But
     // zero-length operations at the edge of the memory are allowed.
     if (byteOffset <= memLen) {
       return 0;
@@ -572,19 +580,21 @@ Instance::memFill(Instance* instance, ui
   }
 
   JSContext* cx = TlsContext.get();
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             JSMSG_WASM_OUT_OF_BOUNDS);
   return -1;
 }
 
-/* static */ int32_t /* -1 to signal trap; 0 for ok */
-Instance::memInit(Instance* instance, uint32_t dstOffset, uint32_t srcOffset,
-                  uint32_t len, uint32_t segIndex) {
+/* static */ int32_t Instance::memInit(Instance* instance, uint32_t dstOffset,
+                                       uint32_t srcOffset, uint32_t len,
+                                       uint32_t segIndex) {
+  MOZ_ASSERT(SASigMemInit.failureMode == FailureMode::FailOnNegI32);
+
   MOZ_RELEASE_ASSERT(size_t(segIndex) < instance->passiveDataSegments_.length(),
                      "ensured by validation");
 
   if (!instance->passiveDataSegments_[segIndex]) {
     JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                               JSMSG_WASM_DROPPED_DATA_SEG);
     return -1;
   }
@@ -650,20 +660,22 @@ Instance::memInit(Instance* instance, ui
     }
   }
 
   JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                             JSMSG_WASM_OUT_OF_BOUNDS);
   return -1;
 }
 
-/* static */ int32_t /* -1 to signal trap; 0 for ok */
-Instance::tableCopy(Instance* instance, uint32_t dstOffset, uint32_t srcOffset,
-                    uint32_t len, uint32_t dstTableIndex,
-                    uint32_t srcTableIndex) {
+/* static */ int32_t Instance::tableCopy(Instance* instance, uint32_t dstOffset,
+                                         uint32_t srcOffset, uint32_t len,
+                                         uint32_t dstTableIndex,
+                                         uint32_t srcTableIndex) {
+  MOZ_ASSERT(SASigMemCopy.failureMode == FailureMode::FailOnNegI32);
+
   const SharedTable& srcTable = instance->tables()[srcTableIndex];
   uint32_t srcTableLen = srcTable->length();
 
   const SharedTable& dstTable = instance->tables()[dstTableIndex];
   uint32_t dstTableLen = dstTable->length();
 
   if (len == 0) {
     // Even though the number of items to copy is zero, we must check for valid
@@ -727,18 +739,19 @@ Instance::tableCopy(Instance* instance, 
     }
   }
 
   JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                             JSMSG_WASM_OUT_OF_BOUNDS);
   return -1;
 }
 
-/* static */ int32_t /* -1 to signal trap; 0 for ok */
-Instance::elemDrop(Instance* instance, uint32_t segIndex) {
+/* static */ int32_t Instance::elemDrop(Instance* instance, uint32_t segIndex) {
+  MOZ_ASSERT(SASigDataDrop.failureMode == FailureMode::FailOnNegI32);
+
   MOZ_RELEASE_ASSERT(size_t(segIndex) < instance->passiveElemSegments_.length(),
                      "ensured by validation");
 
   if (!instance->passiveElemSegments_[segIndex]) {
     JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                               JSMSG_WASM_DROPPED_ELEM_SEG);
     return -1;
   }
@@ -796,19 +809,22 @@ void Instance::initElems(uint32_t tableI
       }
       void* code = codeBaseTier +
                    codeRanges[funcToCodeRange[funcIndex]].funcTableEntry();
       table.setFuncRef(dstOffset + i, code, this);
     }
   }
 }
 
-/* static */ int32_t /* -1 to signal trap; 0 for ok */
-Instance::tableInit(Instance* instance, uint32_t dstOffset, uint32_t srcOffset,
-                    uint32_t len, uint32_t segIndex, uint32_t tableIndex) {
+/* static */ int32_t Instance::tableInit(Instance* instance, uint32_t dstOffset,
+                                         uint32_t srcOffset, uint32_t len,
+                                         uint32_t segIndex,
+                                         uint32_t tableIndex) {
+  MOZ_ASSERT(SASigTableInit.failureMode == FailureMode::FailOnNegI32);
+
   MOZ_RELEASE_ASSERT(size_t(segIndex) < instance->passiveElemSegments_.length(),
                      "ensured by validation");
 
   if (!instance->passiveElemSegments_[segIndex]) {
     JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                               JSMSG_WASM_DROPPED_ELEM_SEG);
     return -1;
   }
@@ -865,19 +881,21 @@ Instance::tableInit(Instance* instance, 
     }
   }
 
   JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                             JSMSG_WASM_OUT_OF_BOUNDS);
   return -1;
 }
 
-/* static */ int32_t /* -1 to signal trap; 0 for ok */
-Instance::tableFill(Instance* instance, uint32_t start, void* value,
-                    uint32_t len, uint32_t tableIndex) {
+/* static */ int32_t Instance::tableFill(Instance* instance, uint32_t start,
+                                         void* value, uint32_t len,
+                                         uint32_t tableIndex) {
+  MOZ_ASSERT(SASigTableFill.failureMode == FailureMode::FailOnNegI32);
+
   Table& table = *instance->tables()[tableIndex];
   MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
 
   if (len == 0) {
     // Even though the length is zero, we must check for a valid offset.  But
     // zero-length operations at the edge of the table are allowed.
     if (start <= table.length()) {
       return 0;
@@ -920,96 +938,105 @@ Instance::tableFill(Instance* instance, 
 // Ion code has to hold a value that may or may not be a pointer to GC'd
 // storage, or where Ion has to pass in a pointer to storage where a return
 // value can be written.
 //
 // Note carefully that the pointer that is returned may not be valid past
 // operations that change the size of the table or cause GC work; it is strictly
 // to be used to retrieve the return value.
 
-/* static */ void* /* nullptr to signal trap; pointer to table location
-                      otherwise */
-Instance::tableGet(Instance* instance, uint32_t index, uint32_t tableIndex) {
+/* static */ void* Instance::tableGet(Instance* instance, uint32_t index,
+                                      uint32_t tableIndex) {
+  MOZ_ASSERT(SASigTableGet.failureMode == FailureMode::FailOnNullPtr);
+
   const Table& table = *instance->tables()[tableIndex];
   MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
   if (index >= table.length()) {
     JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                               JSMSG_WASM_TABLE_OUT_OF_BOUNDS);
     return nullptr;
   }
   return const_cast<void*>(table.getShortlivedAnyRefLocForCompiledCode(index));
 }
 
-/* static */ uint32_t /* infallible */
-Instance::tableGrow(Instance* instance, void* initValue, uint32_t delta,
-                    uint32_t tableIndex) {
+/* static */ uint32_t Instance::tableGrow(Instance* instance, void* initValue,
+                                          uint32_t delta, uint32_t tableIndex) {
+  MOZ_ASSERT(SASigTableGrow.failureMode == FailureMode::Infallible);
+
   RootedAnyRef obj(TlsContext.get(), AnyRef::fromCompiledCode(initValue));
   Table& table = *instance->tables()[tableIndex];
   MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
 
   uint32_t oldSize = table.grow(delta, TlsContext.get());
   if (oldSize != uint32_t(-1) && initValue != nullptr) {
     for (uint32_t i = 0; i < delta; i++) {
       table.setAnyRef(oldSize + i, obj.get());
     }
   }
   return oldSize;
 }
 
-/* static */ int32_t /* -1 to signal trap; 0 for ok */
-Instance::tableSet(Instance* instance, uint32_t index, void* value,
-                   uint32_t tableIndex) {
+/* static */ int32_t Instance::tableSet(Instance* instance, uint32_t index,
+                                        void* value, uint32_t tableIndex) {
+  MOZ_ASSERT(SASigTableSet.failureMode == FailureMode::FailOnNegI32);
+
   Table& table = *instance->tables()[tableIndex];
   MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
   if (index >= table.length()) {
     JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
                               JSMSG_WASM_TABLE_OUT_OF_BOUNDS);
     return -1;
   }
   table.setAnyRef(index, AnyRef::fromCompiledCode(value));
   return 0;
 }
 
-/* static */ uint32_t /* infallible */
-Instance::tableSize(Instance* instance, uint32_t tableIndex) {
+/* static */ uint32_t Instance::tableSize(Instance* instance,
+                                          uint32_t tableIndex) {
+  MOZ_ASSERT(SASigTableSize.failureMode == FailureMode::Infallible);
   Table& table = *instance->tables()[tableIndex];
   return table.length();
 }
 
-/* static */ void /* infallible */
-Instance::postBarrier(Instance* instance, gc::Cell** location) {
+/* static */ void Instance::postBarrier(Instance* instance,
+                                        gc::Cell** location) {
+  MOZ_ASSERT(SASigPostBarrier.failureMode == FailureMode::Infallible);
   MOZ_ASSERT(location);
   TlsContext.get()->runtime()->gc.storeBuffer().putCell(location);
 }
 
-/* static */ void /* infallible */
-Instance::postBarrierFiltering(Instance* instance, gc::Cell** location) {
+/* static */ void Instance::postBarrierFiltering(Instance* instance,
+                                                 gc::Cell** location) {
+  MOZ_ASSERT(SASigPostBarrier.failureMode == FailureMode::Infallible);
   MOZ_ASSERT(location);
   if (*location == nullptr || !gc::IsInsideNursery(*location)) {
     return;
   }
   TlsContext.get()->runtime()->gc.storeBuffer().putCell(location);
 }
 
 // The typeIndex is an index into the structTypeDescrs_ table in the instance.
 // That table holds TypeDescr objects.
 //
 // When we fail to allocate we return a nullptr; the wasm side must check this
 // and propagate it as an error.
 
-/* static */ void* /* null on OOM, otherwise a pointer */
-Instance::structNew(Instance* instance, uint32_t typeIndex) {
+/* static */ void* Instance::structNew(Instance* instance, uint32_t typeIndex) {
+  MOZ_ASSERT(SASigStructNew.failureMode == FailureMode::FailOnNullPtr);
   JSContext* cx = TlsContext.get();
   Rooted<TypeDescr*> typeDescr(cx, instance->structTypeDescrs_[typeIndex]);
   return TypedObject::createZeroed(cx, typeDescr);
 }
 
-/* static */ void* /* infallible */
-Instance::structNarrow(Instance* instance, uint32_t mustUnboxAnyref,
-                       uint32_t outputTypeIndex, void* maybeNullPtr) {
+/* static */ void* Instance::structNarrow(Instance* instance,
+                                          uint32_t mustUnboxAnyref,
+                                          uint32_t outputTypeIndex,
+                                          void* maybeNullPtr) {
+  MOZ_ASSERT(SASigStructNarrow.failureMode == FailureMode::Infallible);
+
   JSContext* cx = TlsContext.get();
 
   Rooted<TypedObject*> obj(cx);
   Rooted<StructTypeDescr*> typeDescr(cx);
 
   if (maybeNullPtr == nullptr) {
     return maybeNullPtr;
   }
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -676,66 +676,16 @@ class FunctionCompiler {
     }
     auto* ins =
         MWasmAddOffset::New(alloc(), base, access->offset(), bytecodeOffset());
     curBlock_->add(ins);
     access->clearOffset();
     return ins;
   }
 
-  bool checkI32NegativeMeansFailedResult(MDefinition* value) {
-    if (inDeadCode()) {
-      return true;
-    }
-
-    auto* zero = constant(Int32Value(0), MIRType::Int32);
-    auto* cond = compare(value, zero, JSOP_LT, MCompare::Compare_Int32);
-
-    MBasicBlock* failBlock;
-    if (!newBlock(curBlock_, &failBlock)) {
-      return false;
-    }
-
-    MBasicBlock* okBlock;
-    if (!newBlock(curBlock_, &okBlock)) {
-      return false;
-    }
-
-    curBlock_->end(MTest::New(alloc(), cond, failBlock, okBlock));
-    failBlock->end(
-        MWasmTrap::New(alloc(), wasm::Trap::ThrowReported, bytecodeOffset()));
-    curBlock_ = okBlock;
-    return true;
-  }
-
-  bool checkPointerNullMeansFailedResult(MDefinition* value) {
-    if (inDeadCode()) {
-      return true;
-    }
-
-    auto* cond = MIsNullPointer::New(alloc(), value);
-    curBlock_->add(cond);
-
-    MBasicBlock* failBlock;
-    if (!newBlock(curBlock_, &failBlock)) {
-      return false;
-    }
-
-    MBasicBlock* okBlock;
-    if (!newBlock(curBlock_, &okBlock)) {
-      return false;
-    }
-
-    curBlock_->end(MTest::New(alloc(), cond, failBlock, okBlock));
-    failBlock->end(
-        MWasmTrap::New(alloc(), wasm::Trap::ThrowReported, bytecodeOffset()));
-    curBlock_ = okBlock;
-    return true;
-  }
-
   MDefinition* derefTableElementPointer(MDefinition* base) {
     // Table element storage may be moved by GC operations, so reads from that
     // storage are not movable.
     MWasmLoadRef* load =
         MWasmLoadRef::New(alloc(), base, AliasSet::WasmTableElement,
                           /*isMovable=*/false);
     curBlock_->add(load);
     return load;
@@ -1153,16 +1103,18 @@ class FunctionCompiler {
   bool builtinCall(const SymbolicAddressSignature& builtin,
                    uint32_t lineOrBytecode, const CallCompileState& call,
                    MDefinition** def) {
     if (inDeadCode()) {
       *def = nullptr;
       return true;
     }
 
+    MOZ_ASSERT(builtin.failureMode == FailureMode::Infallible);
+
     CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Symbolic);
     auto callee = CalleeDesc::builtin(builtin.identity);
     auto* ins =
         MWasmCall::New(alloc(), desc, callee, call.regArgs_, builtin.retType,
                        StackArgAreaSizeUnaligned(builtin));
     if (!ins) {
       return false;
     }
@@ -1170,32 +1122,37 @@ class FunctionCompiler {
     curBlock_->add(ins);
     *def = ins;
     return true;
   }
 
   bool builtinInstanceMethodCall(const SymbolicAddressSignature& builtin,
                                  uint32_t lineOrBytecode,
                                  const CallCompileState& call,
-                                 MDefinition** def) {
+                                 MDefinition** def = nullptr) {
+    MOZ_ASSERT_IF(!def, builtin.retType == MIRType::None);
     if (inDeadCode()) {
-      *def = nullptr;
+      if (def) {
+        *def = nullptr;
+      }
       return true;
     }
 
     CallSiteDesc desc(lineOrBytecode, CallSiteDesc::Symbolic);
     auto* ins = MWasmCall::NewBuiltinInstanceMethodCall(
-        alloc(), desc, builtin.identity, call.instanceArg_, call.regArgs_,
-        builtin.retType, StackArgAreaSizeUnaligned(builtin));
+        alloc(), desc, builtin.identity, builtin.failureMode, call.instanceArg_,
+        call.regArgs_, builtin.retType, StackArgAreaSizeUnaligned(builtin));
     if (!ins) {
       return false;
     }
 
     curBlock_->add(ins);
-    *def = ins;
+    if (def) {
+      *def = ins;
+    }
     return true;
   }
 
   /*********************************************** Control flow generation */
 
   inline bool inDeadCode() const { return curBlock_ == nullptr; }
 
   void returnExpr(MDefinition* operand) {
@@ -2220,18 +2177,17 @@ static bool EmitSetGlobal(FunctionCompil
     CallCompileState args;
     if (!f.passInstance(callee.argTypes[0], &args)) {
       return false;
     }
     if (!f.passArg(barrierAddr, callee.argTypes[1], &args)) {
       return false;
     }
     f.finishCall(&args);
-    MDefinition* ret;
-    if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
+    if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args)) {
       return false;
     }
   }
 
   return true;
 }
 
 static bool EmitTeeGlobal(FunctionCompiler& f) {
@@ -2845,20 +2801,16 @@ static bool EmitWait(FunctionCompiler& f
     return false;
   }
 
   MDefinition* ret;
   if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
     return false;
   }
 
-  if (!f.checkI32NegativeMeansFailedResult(ret)) {
-    return false;
-  }
-
   f.iter().setResult(ret);
   return true;
 }
 
 static bool EmitWake(FunctionCompiler& f) {
   uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
 
   const SymbolicAddressSignature& callee = SASigWake;
@@ -2892,20 +2844,16 @@ static bool EmitWake(FunctionCompiler& f
     return false;
   }
 
   MDefinition* ret;
   if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
     return false;
   }
 
-  if (!f.checkI32NegativeMeansFailedResult(ret)) {
-    return false;
-  }
-
   f.iter().setResult(ret);
   return true;
 }
 
 static bool EmitAtomicXchg(FunctionCompiler& f, ValType type,
                            Scalar::Type viewType) {
   LinearMemoryAddress<MDefinition*> addr;
   MDefinition* value;
@@ -2971,26 +2919,17 @@ static bool EmitMemOrTableCopy(FunctionC
     if (!f.passArg(sti, callee.argTypes[5], &args)) {
       return false;
     }
   }
   if (!f.finishCall(&args)) {
     return false;
   }
 
-  MDefinition* ret;
-  if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
-    return false;
-  }
-
-  if (!f.checkI32NegativeMeansFailedResult(ret)) {
-    return false;
-  }
-
-  return true;
+  return f.builtinInstanceMethodCall(callee, lineOrBytecode, args);
 }
 
 static bool EmitDataOrElemDrop(FunctionCompiler& f, bool isData) {
   uint32_t segIndexVal = 0;
   if (!f.iter().readDataOrElemDrop(isData, &segIndexVal)) {
     return false;
   }
 
@@ -3012,26 +2951,17 @@ static bool EmitDataOrElemDrop(FunctionC
   if (!f.passArg(segIndex, callee.argTypes[1], &args)) {
     return false;
   }
 
   if (!f.finishCall(&args)) {
     return false;
   }
 
-  MDefinition* ret;
-  if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
-    return false;
-  }
-
-  if (!f.checkI32NegativeMeansFailedResult(ret)) {
-    return false;
-  }
-
-  return true;
+  return f.builtinInstanceMethodCall(callee, lineOrBytecode, args);
 }
 
 static bool EmitMemFill(FunctionCompiler& f) {
   MDefinition *start, *val, *len;
   if (!f.iter().readMemFill(&start, &val, &len)) {
     return false;
   }
 
@@ -3056,26 +2986,17 @@ static bool EmitMemFill(FunctionCompiler
   if (!f.passArg(len, callee.argTypes[3], &args)) {
     return false;
   }
 
   if (!f.finishCall(&args)) {
     return false;
   }
 
-  MDefinition* ret;
-  if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
-    return false;
-  }
-
-  if (!f.checkI32NegativeMeansFailedResult(ret)) {
-    return false;
-  }
-
-  return true;
+  return f.builtinInstanceMethodCall(callee, lineOrBytecode, args);
 }
 
 static bool EmitMemOrTableInit(FunctionCompiler& f, bool isMem) {
   uint32_t segIndexVal = 0, dstTableIndex = 0;
   MDefinition *dstOff, *srcOff, *len;
   if (!f.iter().readMemOrTableInit(isMem, &segIndexVal, &dstTableIndex, &dstOff,
                                    &srcOff, &len)) {
     return false;
@@ -3117,26 +3038,17 @@ static bool EmitMemOrTableInit(FunctionC
     if (!f.passArg(dti, callee.argTypes[5], &args)) {
       return false;
     }
   }
   if (!f.finishCall(&args)) {
     return false;
   }
 
-  MDefinition* ret;
-  if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
-    return false;
-  }
-
-  if (!f.checkI32NegativeMeansFailedResult(ret)) {
-    return false;
-  }
-
-  return true;
+  return f.builtinInstanceMethodCall(callee, lineOrBytecode, args);
 }
 #endif  // ENABLE_WASM_BULKMEM_OPS
 
 #ifdef ENABLE_WASM_REFTYPES
 // Note, table.{get,grow,set} on table(funcref) are currently rejected by the
 // verifier.
 
 static bool EmitTableFill(FunctionCompiler& f) {
@@ -3176,26 +3088,17 @@ static bool EmitTableFill(FunctionCompil
   if (!f.passArg(tableIndexArg, callee.argTypes[4], &args)) {
     return false;
   }
 
   if (!f.finishCall(&args)) {
     return false;
   }
 
-  MDefinition* ret;
-  if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
-    return false;
-  }
-
-  if (!f.checkI32NegativeMeansFailedResult(ret)) {
-    return false;
-  }
-
-  return true;
+  return f.builtinInstanceMethodCall(callee, lineOrBytecode, args);
 }
 
 static bool EmitTableGet(FunctionCompiler& f) {
   uint32_t tableIndex;
   MDefinition* index;
   if (!f.iter().readTableGet(&tableIndex, &index)) {
     return false;
   }
@@ -3230,19 +3133,16 @@ static bool EmitTableGet(FunctionCompile
   }
 
   // The return value here is either null, denoting an error, or a short-lived
   // pointer to a location containing a possibly-null ref.
   MDefinition* result;
   if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &result)) {
     return false;
   }
-  if (!f.checkPointerNullMeansFailedResult(result)) {
-    return false;
-  }
 
   MDefinition* ret = f.derefTableElementPointer(result);
   if (!ret) {
     return false;
   }
 
   f.iter().setResult(ret);
   return true;
@@ -3334,24 +3234,17 @@ static bool EmitTableSet(FunctionCompile
   if (!f.passArg(tableIndexArg, callee.argTypes[3], &args)) {
     return false;
   }
 
   if (!f.finishCall(&args)) {
     return false;
   }
 
-  MDefinition* ret;
-  if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
-    return false;
-  }
-  if (!f.checkI32NegativeMeansFailedResult(ret)) {
-    return false;
-  }
-  return true;
+  return f.builtinInstanceMethodCall(callee, lineOrBytecode, args);
 }
 
 static bool EmitTableSize(FunctionCompiler& f) {
   uint32_t tableIndex;
   if (!f.iter().readTableSize(&tableIndex)) {
     return false;
   }
 
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1895,31 +1895,40 @@ enum class SymbolicAddress {
   PrintPtr,
   PrintF32,
   PrintF64,
   PrintText,
 #endif
   Limit
 };
 
+// The FailureMode indicates whether, immediately after a call to a builtin
+// returns, the return value should be checked against an error condition
+// (and if so, which one) which signals that the C++ calle has already
+// reported an error and thus wasm needs to wasmTrap(Trap::ThrowReported).
+
+enum class FailureMode : uint8_t { Infallible, FailOnNegI32, FailOnNullPtr };
+
 // SymbolicAddressSignature carries type information for a function referred
 // to by a SymbolicAddress.  In order that |argTypes| can be written out as a
 // static initialiser, it has to have fixed length.  At present
 // SymbolicAddressType is used to describe functions with at most 6 arguments,
 // so |argTypes| has 7 entries in order to allow the last value to be
 // MIRType::None, in the hope of catching any accidental overruns of the
 // defined section of the array.
 
 static constexpr size_t SymbolicAddressSignatureMaxArgs = 6;
 
 struct SymbolicAddressSignature {
   // The SymbolicAddress that is described.
   const SymbolicAddress identity;
   // The return type, or MIRType::None to denote 'void'.
   const jit::MIRType retType;
+  // The failure mode, which is checked by masm.wasmCallBuiltinInstanceMethod.
+  const FailureMode failureMode;
   // The number of arguments, 0 .. SymbolicAddressSignatureMaxArgs only.
   const uint8_t numArgs;
   // The argument types; SymbolicAddressSignatureMaxArgs + 1 guard, which
   // should be MIRType::None.
   const jit::MIRType argTypes[SymbolicAddressSignatureMaxArgs + 1];
 };
 
 // The 16 in this assertion is derived as follows: SymbolicAddress is probably