Bug 1495149 - Baldr: factor out trap-handling code from simulators (r=lth,bbouvier)
authorLuke Wagner <luke@mozilla.com>
Wed, 03 Oct 2018 15:44:05 -0500
changeset 487753 8b6344a8c25c48a2180b6a5ba7050e2773f22f89
parent 487752 d1094983384c30f5aa7860e7d5ff49be625fb47a
child 487754 f2ff07352cbe9a60a0bb9c9705f3c911d4ae1f7d
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewerslth, bbouvier
bugs1495149
milestone64.0a1
Bug 1495149 - Baldr: factor out trap-handling code from simulators (r=lth,bbouvier)
js/src/jit/arm/Simulator-arm.cpp
js/src/jit/arm/Simulator-arm.h
js/src/jit/arm64/vixl/Debugger-vixl.cpp
js/src/jit/arm64/vixl/Debugger-vixl.h
js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
js/src/jit/arm64/vixl/Simulator-vixl.h
js/src/jit/mips32/Simulator-mips32.cpp
js/src/jit/mips32/Simulator-mips32.h
js/src/jit/mips64/Simulator-mips64.cpp
js/src/jit/mips64/Simulator-mips64.h
js/src/vm/JSContext.cpp
js/src/wasm/WasmFrameIter.cpp
js/src/wasm/WasmFrameIter.h
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmInstance.h
js/src/wasm/WasmProcess.cpp
js/src/wasm/WasmProcess.h
js/src/wasm/WasmSignalHandlers.cpp
js/src/wasm/WasmSignalHandlers.h
js/src/wasm/WasmTypes.h
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -410,19 +410,19 @@ class AutoLockSimulatorCache : public Lo
 
 mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
     SimulatorProcess::ICacheCheckingDisableCount(1); // Checking is disabled by default.
 SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
 
 int64_t Simulator::StopSimAt = -1L;
 
 Simulator*
-Simulator::Create(JSContext* cx)
+Simulator::Create()
 {
-    auto sim = MakeUnique<Simulator>(cx);
+    auto sim = MakeUnique<Simulator>();
     if (!sim) {
         return nullptr;
     }
 
     if (!sim->init()) {
         return nullptr;
     }
 
@@ -1147,18 +1147,17 @@ SimulatorProcess::FlushICache(void* star
 {
     JitSpewCont(JitSpew_CacheFlush, "[%p %zx]", start_addr, size);
     if (!ICacheCheckingDisableCount) {
         AutoLockSimulatorCache als;
         js::jit::FlushICacheLocked(icache(), start_addr, size);
     }
 }
 
-Simulator::Simulator(JSContext* cx)
-  : cx_(cx)
+Simulator::Simulator()
 {
     // Set up simulator support first. Some of this information is needed to
     // setup the architecture state.
 
     // Note, allocation and anything that depends on allocated memory is
     // deferred until init(), in order to handle OOM properly.
 
     stack_ = nullptr;
@@ -1594,100 +1593,16 @@ Simulator::registerState()
     wasm::RegisterState state;
     state.pc = (void*) get_pc();
     state.fp = (void*) get_register(fp);
     state.sp = (void*) get_register(sp);
     state.lr = (void*) get_register(lr);
     return state;
 }
 
-static inline JitActivation*
-GetJitActivation(JSContext* cx)
-{
-    if (!wasm::CodeExists) {
-        return nullptr;
-    }
-    if (!cx->activation() || !cx->activation()->isJit()) {
-        return nullptr;
-    }
-    return cx->activation()->asJit();
-}
-
-// WebAssembly memories contain an extra region of guard pages (see
-// WasmArrayRawBuffer comment). The guard pages catch out-of-bounds accesses
-// using a signal handler that redirects PC to a stub that safely reports an
-// error. However, if the handler is hit by the simulator, the PC is in C++ code
-// and cannot be redirected. Therefore, we must avoid hitting the handler by
-// redirecting in the simulator before the real handler would have been hit.
-bool
-Simulator::handleWasmSegFault(int32_t addr, unsigned numBytes)
-{
-    JitActivation* act = GetJitActivation(cx_);
-    if (!act) {
-        return false;
-    }
-
-    void* pc = reinterpret_cast<void*>(get_pc());
-    uint8_t* fp = reinterpret_cast<uint8_t*>(get_register(r11));
-
-    const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule()) {
-        return false;
-    }
-    const wasm::ModuleSegment* moduleSegment = segment->asModule();
-
-    wasm::Instance* instance = wasm::LookupFaultingInstance(*moduleSegment, pc, fp);
-    if (!instance) {
-        return false;
-    }
-
-    MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
-
-    if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes)) {
-        return false;
-    }
-
-    wasm::Trap trap;
-    wasm::BytecodeOffset bytecode;
-    MOZ_ALWAYS_TRUE(moduleSegment->code().lookupTrap(pc, &trap, &bytecode));
-
-    MOZ_RELEASE_ASSERT(trap == wasm::Trap::OutOfBounds);
-
-    act->startWasmTrap(wasm::Trap::OutOfBounds, bytecode.offset(), registerState());
-    set_pc(int32_t(moduleSegment->trapCode()));
-    return true;
-}
-
-bool
-Simulator::handleWasmIllFault()
-{
-    JitActivation* act = GetJitActivation(cx_);
-    if (!act) {
-        return false;
-    }
-
-    void* pc = reinterpret_cast<void*>(get_pc());
-
-    const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule()) {
-        return false;
-    }
-    const wasm::ModuleSegment* moduleSegment = segment->asModule();
-
-    wasm::Trap trap;
-    wasm::BytecodeOffset bytecode;
-    if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode)) {
-        return false;
-    }
-
-    act->startWasmTrap(trap, bytecode.offset(), registerState());
-    set_pc(int32_t(moduleSegment->trapCode()));
-    return true;
-}
-
 uint64_t
 Simulator::readQ(int32_t addr, SimInstruction* instr, UnalignedPolicy f)
 {
     if (handleWasmSegFault(addr, 8)) {
         return UINT64_MAX;
     }
 
     if ((addr & 3) == 0 || (f == AllowUnaligned && !HasAlignmentFault())) {
@@ -3672,17 +3587,19 @@ rotateBytes(uint32_t val, int32_t rotate
         return (val >> 24) | (val << 8);
     }
 }
 
 void
 Simulator::decodeType3(SimInstruction* instr)
 {
     if (MOZ_UNLIKELY(instr->isUDF())) {
-        if (handleWasmIllFault()) {
+        uint8_t* newPC;
+        if (wasm::HandleIllegalInstruction(registerState(), &newPC)) {
+            set_pc((int32_t)newPC);
             return;
         }
         MOZ_CRASH("illegal instruction encountered");
     }
 
     int rd = instr->rdValue();
     int rn = instr->rnValue();
     int32_t rn_val = get_register(rn);
--- a/js/src/jit/arm/Simulator-arm.h
+++ b/js/src/jit/arm/Simulator-arm.h
@@ -34,17 +34,17 @@
 #include "mozilla/Atomics.h"
 
 #include "jit/arm/Architecture-arm.h"
 #include "jit/arm/disasm/Disasm-arm.h"
 #include "jit/IonTypes.h"
 #include "js/ProfilingFrameIterator.h"
 #include "threading/Thread.h"
 #include "vm/MutexIDs.h"
-#include "wasm/WasmCode.h"
+#include "wasm/WasmSignalHandlers.h"
 
 namespace js {
 namespace jit {
 
 class JitActivation;
 class Simulator;
 class Redirection;
 class CachePage;
@@ -100,22 +100,22 @@ class Simulator
         d24, d25, d26, d27, d28, d29, d30, d31,
         num_d_registers = 32,
         q0 = 0, q1, q2, q3, q4, q5, q6, q7,
         q8, q9, q10, q11, q12, q13, q14, q15,
         num_q_registers = 16
     };
 
     // Returns nullptr on OOM.
-    static Simulator* Create(JSContext* cx);
+    static Simulator* Create();
 
     static void Destroy(Simulator* simulator);
 
     // Constructor/destructor are for internal use only; use the static methods above.
-    explicit Simulator(JSContext* cx);
+    Simulator();
     ~Simulator();
 
     static bool supportsAtomics() { return HasLDSTREXBHD(); }
 
     // The currently executing Simulator instance. Potentially there can be one
     // for each native thread.
     static Simulator* Current();
 
@@ -284,18 +284,30 @@ class Simulator
     inline void disableStop(uint32_t bkpt_code);
     inline void increaseStopCounter(uint32_t bkpt_code);
     void printStopInfo(uint32_t code);
 
     // Handle a wasm interrupt triggered by an async signal handler.
     JS::ProfilingFrameIterator::RegisterState registerState();
 
     // Handle any wasm faults, returning true if the fault was handled.
-    bool handleWasmSegFault(int32_t addr, unsigned numBytes);
-    bool handleWasmIllFault();
+    // This method is rather hot so inline the normal (no-wasm) case.
+    bool MOZ_ALWAYS_INLINE handleWasmSegFault(int32_t addr, unsigned numBytes) {
+        if (MOZ_LIKELY(!wasm::CodeExists)) {
+            return false;
+        }
+
+        uint8_t* newPC;
+        if (!wasm::MemoryAccessTraps(registerState(), (uint8_t*)addr, numBytes, &newPC)) {
+            return false;
+        }
+
+        set_pc(int32_t(newPC));
+        return true;
+    }
 
     // Read and write memory.
     inline uint8_t readBU(int32_t addr);
     inline int8_t readB(int32_t addr);
     inline void writeB(int32_t addr, uint8_t value);
     inline void writeB(int32_t addr, int8_t value);
 
     inline uint8_t readExBU(int32_t addr);
@@ -377,18 +389,16 @@ class Simulator
     template<class ReturnType, int register_size>
     void getFromVFPRegister(int reg_index, ReturnType* out);
 
     template<class InputType, int register_size>
     void setVFPRegister(int reg_index, const InputType& value);
 
     void callInternal(uint8_t* entry);
 
-    JSContext* const cx_;
-
     // Architecture state.
     // Saturating instructions require a Q flag to indicate saturation.
     // There is currently no way to read the CPSR directly, and thus read the Q
     // flag, so this is left unimplemented.
     int32_t registers_[16];
     bool n_flag_;
     bool z_flag_;
     bool c_flag_;
--- a/js/src/jit/arm64/vixl/Debugger-vixl.cpp
+++ b/js/src/jit/arm64/vixl/Debugger-vixl.cpp
@@ -540,18 +540,18 @@ const char* RegisterToken::kWAliases[kNu
   { "w27", NULL },
   { "w28", NULL },
   { "w29", NULL },
   { "w30", NULL },
   { "wsp", NULL }
 };
 
 
-Debugger::Debugger(JSContext* cx, Decoder* decoder, FILE* stream)
-    : Simulator(cx, decoder, stream),
+Debugger::Debugger(Decoder* decoder, FILE* stream)
+    : Simulator(decoder, stream),
       debug_parameters_(DBG_INACTIVE),
       pending_request_(false),
       steps_(0),
       last_command_(NULL) {
   disasm_ = js_new<PrintDisassembler>(stdout);
   printer_ = js_new<Decoder>();
   printer_->AppendVisitor(disasm_);
 }
--- a/js/src/jit/arm64/vixl/Debugger-vixl.h
+++ b/js/src/jit/arm64/vixl/Debugger-vixl.h
@@ -49,17 +49,17 @@ enum DebugParameters {
 
 // Forward declarations.
 class DebugCommand;
 class Token;
 class FormatToken;
 
 class Debugger : public Simulator {
  public:
-  explicit Debugger(JSContext* cx, Decoder* decoder, FILE* stream = stdout);
+  explicit Debugger(Decoder* decoder, FILE* stream = stdout);
   ~Debugger();
 
   virtual void Run() override;
   virtual void VisitException(const Instruction* instr) override;
 
   int debug_parameters() const { return debug_parameters_; }
   void set_debug_parameters(int parameters) {
     debug_parameters_ = parameters;
--- a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
+++ b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
@@ -28,32 +28,28 @@
 
 #include "jit/arm64/vixl/Debugger-vixl.h"
 #include "jit/arm64/vixl/Simulator-vixl.h"
 #include "jit/IonTypes.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "threading/LockGuard.h"
 #include "vm/Runtime.h"
-#include "wasm/WasmInstance.h"
-#include "wasm/WasmProcess.h"
-#include "wasm/WasmSignalHandlers.h"
 
 js::jit::SimulatorProcess* js::jit::SimulatorProcess::singleton_ = nullptr;
 
 namespace vixl {
 
 using mozilla::DebugOnly;
 using js::jit::ABIFunctionType;
 using js::jit::JitActivation;
 using js::jit::SimulatorProcess;
 
-Simulator::Simulator(JSContext* cx, Decoder* decoder, FILE* stream)
-  : cx_(cx)
-  , stream_(nullptr)
+Simulator::Simulator(Decoder* decoder, FILE* stream)
+  : stream_(nullptr)
   , print_disasm_(nullptr)
   , instrumentation_(nullptr)
   , stack_(nullptr)
   , stack_limit_(nullptr)
   , decoder_(nullptr)
   , oom_(false)
 {
     this->init(decoder, stream);
@@ -156,29 +152,29 @@ void Simulator::init(Decoder* decoder, F
 
 Simulator* Simulator::Current() {
   JSContext* cx = js::TlsContext.get();
   MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(cx->runtime()));
   return cx->simulator();
 }
 
 
-Simulator* Simulator::Create(JSContext* cx) {
+Simulator* Simulator::Create() {
   Decoder *decoder = js_new<vixl::Decoder>();
   if (!decoder)
     return nullptr;
 
   // FIXME: This just leaks the Decoder object for now, which is probably OK.
   // FIXME: We should free it at some point.
   // FIXME: Note that it can't be stored in the SimulatorRuntime due to lifetime conflicts.
   js::UniquePtr<Simulator> sim;
   if (getenv("USE_DEBUGGER") != nullptr)
-    sim.reset(js_new<Debugger>(cx, decoder, stdout));
+    sim.reset(js_new<Debugger>(decoder, stdout));
   else
-    sim.reset(js_new<Simulator>(cx, decoder, stdout));
+    sim.reset(js_new<Simulator>(decoder, stdout));
 
   // Check if Simulator:init ran out of memory.
   if (sim && sim->oom())
     return nullptr;
 
   return sim.release();
 }
 
@@ -214,72 +210,27 @@ bool Simulator::overRecursed(uintptr_t n
 
 
 bool Simulator::overRecursedWithExtra(uint32_t extra) const {
   uintptr_t newsp = get_sp() - extra;
   return newsp <= stackLimit();
 }
 
 
-static inline JitActivation*
-GetJitActivation(JSContext* cx)
-{
-    if (!js::wasm::CodeExists)
-        return nullptr;
-    if (!cx->activation() || !cx->activation()->isJit())
-        return nullptr;
-    return cx->activation()->asJit();
-}
-
 JS::ProfilingFrameIterator::RegisterState
 Simulator::registerState()
 {
   JS::ProfilingFrameIterator::RegisterState state;
   state.pc = (uint8_t*) get_pc();
   state.fp = (uint8_t*) get_fp();
   state.lr = (uint8_t*) get_lr();
   state.sp = (uint8_t*) get_sp();
   return state;
 }
 
-bool
-Simulator::handle_wasm_seg_fault(uintptr_t addr, unsigned numBytes)
-{
-    JitActivation* act = GetJitActivation(cx_);
-    if (!act)
-        return false;
-
-    uint8_t* pc = (uint8_t*)get_pc();
-    uint8_t* fp = (uint8_t*)get_fp();
-
-    const js::wasm::CodeSegment* segment = js::wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule())
-        return false;
-    const js::wasm::ModuleSegment* moduleSegment = segment->asModule();
-
-    js::wasm::Instance* instance = js::wasm::LookupFaultingInstance(*moduleSegment, pc, fp);
-    if (!instance)
-	return false;
-
-    MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
-
-    if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
-        return false;
-
-    js::wasm::Trap trap;
-    js::wasm::BytecodeOffset bytecode;
-    MOZ_ALWAYS_TRUE(moduleSegment->code().lookupTrap(pc, &trap, &bytecode));
-
-    MOZ_RELEASE_ASSERT(trap == js::wasm::Trap::OutOfBounds);
-
-    act->startWasmTrap(js::wasm::Trap::OutOfBounds, bytecode.offset(), registerState());
-    set_pc((Instruction*)moduleSegment->trapCode());
-    return true;
-}
-
 int64_t Simulator::call(uint8_t* entry, int argument_count, ...) {
   va_list parameters;
   va_start(parameters, argument_count);
 
   // First eight arguments passed in registers.
   VIXL_ASSERT(argument_count <= 8);
   // This code should use the type of the called function
   // (with templates, like the callVM machinery), but since the
@@ -413,54 +364,35 @@ class Redirection
 
 
 
 void* Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType type) {
   Redirection* redirection = Redirection::Get(nativeFunction, type);
   return redirection->addressOfSvcInstruction();
 }
 
-bool
-Simulator::handle_wasm_ill_fault()
-{
-    JitActivation* act = GetJitActivation(cx_);
-    if (!act)
-        return false;
-
-    uint8_t* pc = (uint8_t*)get_pc();
-
-    const js::wasm::CodeSegment* segment = js::wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule())
-        return false;
-    const js::wasm::ModuleSegment* moduleSegment = segment->asModule();
-
-    js::wasm::Trap trap;
-    js::wasm::BytecodeOffset bytecode;
-    if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode))
-        return false;
-
-    act->startWasmTrap(trap, bytecode.offset(), registerState());
-    set_pc((Instruction*)moduleSegment->trapCode());
-    return true;
-}
-
 void Simulator::VisitException(const Instruction* instr) {
   switch (instr->Mask(ExceptionMask)) {
     case BRK: {
       int lowbit  = ImmException_offset;
       int highbit = ImmException_offset + ImmException_width - 1;
       HostBreakpoint(instr->Bits(highbit, lowbit));
       break;
     }
     case HLT:
       switch (instr->ImmException()) {
-        case kUnreachableOpcode:
-          if (!handle_wasm_ill_fault())
-              DoUnreachable(instr);
+        case kUnreachableOpcode: {
+          uint8_t* newPC;
+          if (js::wasm::HandleIllegalInstruction(registerState(), &newPC)) {
+            set_pc((Instruction*)newPC);
+            return;
+          }
+          DoUnreachable(instr);
           return;
+        }
         case kTraceOpcode:
           DoTrace(instr);
           return;
         case kLogOpcode:
           DoLog(instr);
           return;
         case kPrintfOpcode:
           DoPrintf(instr);
--- a/js/src/jit/arm64/vixl/Simulator-vixl.h
+++ b/js/src/jit/arm64/vixl/Simulator-vixl.h
@@ -39,16 +39,17 @@
 #include "jit/arm64/vixl/Instructions-vixl.h"
 #include "jit/arm64/vixl/Instrument-vixl.h"
 #include "jit/arm64/vixl/Simulator-Constants-vixl.h"
 #include "jit/arm64/vixl/Utils-vixl.h"
 #include "jit/IonTypes.h"
 #include "js/AllocPolicy.h"
 #include "vm/MutexIDs.h"
 #include "vm/PosixNSPR.h"
+#include "wasm/WasmSignalHandlers.h"
 
 namespace vixl {
 
 // Assemble the specified IEEE-754 components into the target type and apply
 // appropriate rounding.
 //  sign:     0 = positive, 1 = negative
 //  exponent: Unbiased IEEE-754 exponent.
 //  mantissa: The mantissa of the input. The top bit (which is not encoded for
@@ -691,23 +692,23 @@ class SimExclusiveGlobalMonitor {
   const int kPassProbability;
   uint32_t seed_;
 };
 
 class Redirection;
 
 class Simulator : public DecoderVisitor {
  public:
-  explicit Simulator(JSContext* cx, Decoder* decoder, FILE* stream = stdout);
+  explicit Simulator(Decoder* decoder, FILE* stream = stdout);
   ~Simulator();
 
   // Moz changes.
   void init(Decoder* decoder, FILE* stream);
   static Simulator* Current();
-  static Simulator* Create(JSContext* cx);
+  static Simulator* Create();
   static void Destroy(Simulator* sim);
   uintptr_t stackLimit() const;
   uintptr_t* addressOfStackLimit();
   bool overRecursed(uintptr_t newsp = 0) const;
   bool overRecursedWithExtra(uint32_t extra) const;
   int64_t call(uint8_t* entry, int argument_count, ...);
   static void* RedirectNativeFunction(void* nativeFunction, js::jit::ABIFunctionType type);
   void setGPR32Result(int32_t result);
@@ -741,18 +742,31 @@ class Simulator : public DecoderVisitor 
   template <typename T>
   T get_pc_as() const { return reinterpret_cast<T>(const_cast<Instruction*>(pc())); }
 
   void set_pc(const Instruction* new_pc) {
     pc_ = Memory::AddressUntag(new_pc);
     pc_modified_ = true;
   }
 
-  bool handle_wasm_ill_fault();
-  bool handle_wasm_seg_fault(uintptr_t addr, unsigned numBytes);
+  // Handle any wasm faults, returning true if the fault was handled.
+  // This method is rather hot so inline the normal (no-wasm) case.
+  bool MOZ_ALWAYS_INLINE handle_wasm_seg_fault(uintptr_t addr, unsigned numBytes) {
+    if (MOZ_LIKELY(!js::wasm::CodeExists)) {
+      return false;
+    }
+
+    uint8_t* newPC;
+    if (!js::wasm::MemoryAccessTraps(registerState(), (uint8_t*)addr, numBytes, &newPC)) {
+      return false;
+    }
+
+    set_pc((Instruction*)newPC);
+    return true;
+  }
 
   void increment_pc() {
     if (!pc_modified_) {
       pc_ = pc_->NextInstruction();
     }
 
     pc_modified_ = false;
   }
@@ -2513,18 +2527,16 @@ class Simulator : public DecoderVisitor 
 
   bool FPProcessNaNs(const Instruction* instr);
 
   // Pseudo Printf instruction
   void DoPrintf(const Instruction* instr);
 
   // Processor state ---------------------------------------
 
-  JSContext* const cx_;
-
   // Simulated monitors for exclusive access instructions.
   SimExclusiveLocalMonitor local_monitor_;
   SimExclusiveGlobalMonitor global_monitor_;
 
   // Output stream.
   FILE* stream_;
   PrintDisassembler* print_disasm_;
 
--- a/js/src/jit/mips32/Simulator-mips32.cpp
+++ b/js/src/jit/mips32/Simulator-mips32.cpp
@@ -518,17 +518,17 @@ class AutoLockSimulatorCache : public Lo
 
 mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
     SimulatorProcess::ICacheCheckingDisableCount(1); // Checking is disabled by default.
 SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
 
 int Simulator::StopSimAt = -1;
 
 Simulator*
-Simulator::Create(JSContext* cx)
+Simulator::Create()
 {
     auto sim = MakeUnique<Simulator>();
     if (!sim) {
         return nullptr;
     }
 
     if (!sim->init()) {
         return nullptr;
@@ -1634,113 +1634,29 @@ Simulator::registerState()
     wasm::RegisterState state;
     state.pc = (void*) get_pc();
     state.fp = (void*) getRegister(fp);
     state.sp = (void*) getRegister(sp);
     state.lr = (void*) getRegister(ra);
     return state;
 }
 
-// WebAssembly memories contain an extra region of guard pages (see
-// WasmArrayRawBuffer comment). The guard pages catch out-of-bounds accesses
-// using a signal handler that redirects PC to a stub that safely reports an
-// error. However, if the handler is hit by the simulator, the PC is in C++ code
-// and cannot be redirected. Therefore, we must avoid hitting the handler by
-// redirecting in the simulator before the real handler would have been hit.
-bool
-Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
-{
-    if (!wasm::CodeExists) {
-        return false;
-    }
-
-    JSContext* cx = TlsContext.get();
-    if (!cx->activation() || !cx->activation()->isJit()) {
-        return false;
-    }
-    JitActivation* act = cx->activation()->asJit();
-
-    void* pc = reinterpret_cast<void*>(get_pc());
-    uint8_t* fp = reinterpret_cast<uint8_t*>(getRegister(Register::fp));
-
-    const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule()) {
-        return false;
-    }
-    const wasm::ModuleSegment* moduleSegment = segment->asModule();
-
-    wasm::Instance* instance = wasm::LookupFaultingInstance(*moduleSegment, pc, fp);
-    if (!instance) {
-        return false;
-    }
-
-    MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
-
-    if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes)) {
-         return false;
-    }
-
-    LLBit_ = false;
-
-    wasm::Trap trap;
-    wasm::BytecodeOffset bytecode;
-    MOZ_ALWAYS_TRUE(moduleSegment->code().lookupTrap(pc, &trap, &bytecode));
-
-    MOZ_RELEASE_ASSERT(trap == wasm::Trap::OutOfBounds);
-
-    act->startWasmTrap(wasm::Trap::OutOfBounds, bytecode.offset(), registerState());
-    set_pc(int32_t(moduleSegment->trapCode()));
-    return true;
-}
-
-bool
-Simulator::handleWasmTrapFault()
-{
-    if (!wasm::CodeExists) {
-        return false;
-    }
-
-    JSContext* cx = TlsContext.get();
-    if (!cx->activation() || !cx->activation()->isJit()) {
-        return false;
-    }
-    JitActivation* act = cx->activation()->asJit();
-
-    void* pc = reinterpret_cast<void*>(get_pc());
-
-    const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule()) {
-        return false;
-    }
-    const wasm::ModuleSegment* moduleSegment = segment->asModule();
-
-    wasm::Trap trap;
-    wasm::BytecodeOffset bytecode;
-    if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode)) {
-        return false;
-    }
-
-    act->startWasmTrap(trap, bytecode.offset(), registerState());
-    set_pc(int32_t(moduleSegment->trapCode()));
-    return true;
-}
-
 // MIPS memory instructions (except lwl/r and swl/r) trap on unaligned memory
 // access enabling the OS to handle them via trap-and-emulate.
 // Note that simulator runs have the runtime system running directly on the host
 // system and only generated code is executed in the simulator.
 // Since the host is typically IA32 it will not trap on unaligned memory access.
 // We assume that that executing correct generated code will not produce unaligned
 // memory access, so we explicitly check for address alignment and trap.
 // Note that trapping does not occur when executing wasm code, which requires that
 // unaligned memory access provides correct result.
 int
 Simulator::readW(uint32_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 4)) {
+    if (handleWasmSegFault(addr, 4)) {
         return -1;
     }
 
     if ((addr & kPointerAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
         return *ptr;
     }
     printf("Unaligned read at 0x%08x, pc=0x%08" PRIxPTR "\n",
@@ -1748,17 +1664,17 @@ Simulator::readW(uint32_t addr, SimInstr
            reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 void
 Simulator::writeW(uint32_t addr, int value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 4)) {
+    if (handleWasmSegFault(addr, 4)) {
         return;
     }
 
     if ((addr & kPointerAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1767,17 +1683,17 @@ Simulator::writeW(uint32_t addr, int val
            addr,
            reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
 }
 
 double
 Simulator::readD(uint32_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 8)) {
+    if (handleWasmSegFault(addr, 8)) {
         return NAN;
     }
 
     if ((addr & kDoubleAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         double* ptr = reinterpret_cast<double*>(addr);
         return *ptr;
     }
     printf("Unaligned (double) read at 0x%08x, pc=0x%08" PRIxPTR "\n",
@@ -1785,17 +1701,17 @@ Simulator::readD(uint32_t addr, SimInstr
            reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 void
 Simulator::writeD(uint32_t addr, double value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 8)) {
+    if (handleWasmSegFault(addr, 8)) {
         return;
     }
 
     if ((addr & kDoubleAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         double* ptr = reinterpret_cast<double*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1804,17 +1720,17 @@ Simulator::writeD(uint32_t addr, double 
            addr,
            reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
 }
 
 uint16_t
 Simulator::readHU(uint32_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 2)) {
+    if (handleWasmSegFault(addr, 2)) {
         return 0xffff;
     }
 
     if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
         return *ptr;
     }
     printf("Unaligned unsigned halfword read at 0x%08x, pc=0x%08" PRIxPTR "\n",
@@ -1822,17 +1738,17 @@ Simulator::readHU(uint32_t addr, SimInst
            reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 int16_t
 Simulator::readH(uint32_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 2)) {
+    if (handleWasmSegFault(addr, 2)) {
         return -1;
     }
 
     if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         int16_t* ptr = reinterpret_cast<int16_t*>(addr);
         return *ptr;
     }
     printf("Unaligned signed halfword read at 0x%08x, pc=0x%08" PRIxPTR "\n",
@@ -1840,17 +1756,17 @@ Simulator::readH(uint32_t addr, SimInstr
            reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 void
 Simulator::writeH(uint32_t addr, uint16_t value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 2)) {
+    if (handleWasmSegFault(addr, 2)) {
         return;
     }
 
     if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1859,17 +1775,17 @@ Simulator::writeH(uint32_t addr, uint16_
            addr,
            reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
 }
 
 void
 Simulator::writeH(uint32_t addr, int16_t value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 2)) {
+    if (handleWasmSegFault(addr, 2)) {
         return;
     }
 
     if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         int16_t* ptr = reinterpret_cast<int16_t*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1878,65 +1794,65 @@ Simulator::writeH(uint32_t addr, int16_t
            addr,
            reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
 }
 
 uint32_t
 Simulator::readBU(uint32_t addr)
 {
-    if (handleWasmFault(addr, 1)) {
+    if (handleWasmSegFault(addr, 1)) {
         return 0xff;
     }
 
     uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
     return *ptr;
 }
 
 int32_t
 Simulator::readB(uint32_t addr)
 {
-    if (handleWasmFault(addr, 1)) {
+    if (handleWasmSegFault(addr, 1)) {
         return -1;
     }
 
     int8_t* ptr = reinterpret_cast<int8_t*>(addr);
     return *ptr;
 }
 
 void
 Simulator::writeB(uint32_t addr, uint8_t value)
 {
-    if (handleWasmFault(addr, 1)) {
+    if (handleWasmSegFault(addr, 1)) {
         return;
     }
 
     uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
     LLBit_ = false;
     *ptr = value;
 }
 
 void
 Simulator::writeB(uint32_t addr, int8_t value)
 {
-    if (handleWasmFault(addr, 1)) {
+    if (handleWasmSegFault(addr, 1)) {
         return;
     }
 
     int8_t* ptr = reinterpret_cast<int8_t*>(addr);
     LLBit_ = false;
     *ptr = value;
 }
 
 int
 Simulator::loadLinkedW(uint32_t addr, SimInstruction* instr)
 {
     if ((addr & kPointerAlignmentMask) == 0) {
 
-        if (handleWasmFault(addr, 1)) {
+        if (handleWasmSegFault(addr, 1)) {
             return -1;
         }
 
         volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(addr);
         int32_t value = *ptr;
         lastLLValue_ = value;
         LLAddr_ = addr;
         // Note that any memory write or "external" interrupt should reset this value to false.
@@ -2341,18 +2257,22 @@ Simulator::softwareInterrupt(SimInstruct
     } else {
           switch (func) {
             case ff_tge:
             case ff_tgeu:
             case ff_tlt:
             case ff_tltu:
             case ff_teq:
             case ff_tne:
-            if (instr->bits(15, 6) == kWasmTrapCode && handleWasmTrapFault()) {
-                return;
+            if (instr->bits(15, 6) == kWasmTrapCode) {
+                uint8_t* newPC;
+                if (wasm::HandleIllegalInstruction(registerState(), &newPC)) {
+                    set_pc(int32_t(newPC));
+                    return;
+                }
             }
         };
         // All remaining break_ codes, and all traps are handled here.
         MipsDebugger dbg(this);
         dbg.debug();
     }
 }
 
--- a/js/src/jit/mips32/Simulator-mips32.h
+++ b/js/src/jit/mips32/Simulator-mips32.h
@@ -32,17 +32,17 @@
 #ifdef JS_SIMULATOR_MIPS32
 
 #include "mozilla/Atomics.h"
 
 #include "jit/IonTypes.h"
 #include "js/ProfilingFrameIterator.h"
 #include "threading/Thread.h"
 #include "vm/MutexIDs.h"
-#include "wasm/WasmCode.h"
+#include "wasm/WasmSignalHandlers.h"
 
 namespace js {
 
 namespace jit {
 
 class JitActivation;
 
 class Simulator;
@@ -155,17 +155,17 @@ class Simulator {
         f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11,
         f12, f13, f14, f15,   // f12 and f14 are arguments FPURegisters.
         f16, f17, f18, f19, f20, f21, f22, f23, f24, f25,
         f26, f27, f28, f29, f30, f31,
         kNumFPURegisters
     };
 
     // Returns nullptr on OOM.
-    static Simulator* Create(JSContext* cx);
+    static Simulator* Create();
 
     static void Destroy(Simulator* simulator);
 
     // Constructor/destructor are for internal use only; use the static methods above.
     Simulator();
     ~Simulator();
 
     static bool supportsAtomics() { return true; }
@@ -303,18 +303,31 @@ class Simulator {
     void enableStop(uint32_t code);
     void disableStop(uint32_t code);
     void increaseStopCounter(uint32_t code);
     void printStopInfo(uint32_t code);
 
     JS::ProfilingFrameIterator::RegisterState registerState();
 
     // Handle any wasm faults, returning true if the fault was handled.
-    bool handleWasmFault(int32_t addr, unsigned numBytes);
-    bool handleWasmTrapFault();
+    // This method is rather hot so inline the normal (no-wasm) case.
+    bool MOZ_ALWAYS_INLINE handleWasmSegFault(int32_t addr, unsigned numBytes) {
+        if (MOZ_LIKELY(!js::wasm::CodeExists)) {
+            return false;
+        }
+
+        uint8_t* newPC;
+        if (!js::wasm::MemoryAccessTraps(registerState(), (uint8_t*)addr, numBytes, &newPC)) {
+            return false;
+        }
+
+        LLBit_ = false;
+        set_pc(int32_t(newPC));
+        return true;
+    }
 
     // Executes one instruction.
     void instructionDecode(SimInstruction* instr);
     // Execute one instruction placed in a branch delay slot.
     void branchDelayInstructionDecode(SimInstruction* instr);
 
   public:
     static int StopSimAt;
--- a/js/src/jit/mips64/Simulator-mips64.cpp
+++ b/js/src/jit/mips64/Simulator-mips64.cpp
@@ -554,17 +554,17 @@ class AutoLockSimulatorCache : public Lo
 
 mozilla::Atomic<size_t, mozilla::ReleaseAcquire>
     SimulatorProcess::ICacheCheckingDisableCount(1);  // Checking is disabled by default.
 SimulatorProcess* SimulatorProcess::singleton_ = nullptr;
 
 int64_t Simulator::StopSimAt = -1;
 
 Simulator *
-Simulator::Create(JSContext* cx)
+Simulator::Create()
 {
     auto sim = MakeUnique<Simulator>();
     if (!sim) {
         return nullptr;
     }
 
     if (!sim->init()) {
         return nullptr;
@@ -1646,192 +1646,108 @@ Simulator::registerState()
     wasm::RegisterState state;
     state.pc = (void*) get_pc();
     state.fp = (void*) getRegister(fp);
     state.sp = (void*) getRegister(sp);
     state.lr = (void*) getRegister(ra);
     return state;
 }
 
-// WebAssembly memories contain an extra region of guard pages (see
-// WasmArrayRawBuffer comment). The guard pages catch out-of-bounds accesses
-// using a signal handler that redirects PC to a stub that safely reports an
-// error. However, if the handler is hit by the simulator, the PC is in C++ code
-// and cannot be redirected. Therefore, we must avoid hitting the handler by
-// redirecting in the simulator before the real handler would have been hit.
-bool
-Simulator::handleWasmFault(uint64_t addr, unsigned numBytes)
-{
-    if (!wasm::CodeExists) {
-        return false;
-    }
-
-    JSContext* cx = TlsContext.get();
-    if (!cx->activation() || !cx->activation()->isJit()) {
-        return false;
-    }
-    JitActivation* act = cx->activation()->asJit();
-
-    void* pc = reinterpret_cast<void*>(get_pc());
-    uint8_t* fp = reinterpret_cast<uint8_t*>(getRegister(Register::fp));
-
-    const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule()) {
-        return false;
-    }
-    const wasm::ModuleSegment* moduleSegment = segment->asModule();
-
-    wasm::Instance* instance = wasm::LookupFaultingInstance(*moduleSegment, pc, fp);
-    if (!instance) {
-        return false;
-    }
-
-    MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
-
-    if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes)) {
-         return false;
-    }
-
-    LLBit_ = false;
-
-    wasm::Trap trap;
-    wasm::BytecodeOffset bytecode;
-    MOZ_ALWAYS_TRUE(moduleSegment->code().lookupTrap(pc, &trap, &bytecode));
-
-    MOZ_RELEASE_ASSERT(trap == wasm::Trap::OutOfBounds);
-
-    act->startWasmTrap(wasm::Trap::OutOfBounds, bytecode.offset(), registerState());
-    set_pc(int64_t(moduleSegment->trapCode()));
-    return true;
-}
-
-bool
-Simulator::handleWasmTrapFault()
-{
-    if (!wasm::CodeExists) {
-        return false;
-    }
-
-    JSContext* cx = TlsContext.get();
-    if (!cx->activation() || !cx->activation()->isJit()) {
-        return false;
-    }
-    JitActivation* act = cx->activation()->asJit();
-
-    void* pc = reinterpret_cast<void*>(get_pc());
-
-    const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule()) {
-        return false;
-    }
-    const wasm::ModuleSegment* moduleSegment = segment->asModule();
-
-    wasm::Trap trap;
-    wasm::BytecodeOffset bytecode;
-    if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode)) {
-        return false;
-    }
-
-    act->startWasmTrap(trap, bytecode.offset(), registerState());
-    set_pc(int64_t(moduleSegment->trapCode()));
-    return true;
-}
-
 // MIPS memory instructions (except lw(d)l/r , sw(d)l/r) trap on unaligned memory
 // access enabling the OS to handle them via trap-and-emulate.
 // Note that simulator runs have the runtime system running directly on the host
 // system and only generated code is executed in the simulator.
 // Since the host is typically IA32 it will not trap on unaligned memory access.
 // We assume that that executing correct generated code will not produce unaligned
 // memory access, so we explicitly check for address alignment and trap.
 // Note that trapping does not occur when executing wasm code, which requires that
 // unaligned memory access provides correct result.
 
 uint8_t
 Simulator::readBU(uint64_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 1)) {
+    if (handleWasmSegFault(addr, 1)) {
         return 0xff;
     }
 
     uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
     return* ptr;
 }
 
 int8_t
 Simulator::readB(uint64_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 1)) {
+    if (handleWasmSegFault(addr, 1)) {
         return -1;
     }
 
     int8_t* ptr = reinterpret_cast<int8_t*>(addr);
     return* ptr;
 }
 
 void
 Simulator::writeB(uint64_t addr, uint8_t value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 1)) {
+    if (handleWasmSegFault(addr, 1)) {
         return;
     }
 
     uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
     *ptr = value;
 }
 
 void
 Simulator::writeB(uint64_t addr, int8_t value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 1)) {
+    if (handleWasmSegFault(addr, 1)) {
         return;
     }
 
     int8_t* ptr = reinterpret_cast<int8_t*>(addr);
     *ptr = value;
 }
 
 uint16_t
 Simulator::readHU(uint64_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 2)) {
+    if (handleWasmSegFault(addr, 2)) {
         return 0xffff;
     }
 
     if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
         return *ptr;
     }
     printf("Unaligned unsigned halfword read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 int16_t
 Simulator::readH(uint64_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 2)) {
+    if (handleWasmSegFault(addr, 2)) {
         return -1;
     }
 
     if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         int16_t* ptr = reinterpret_cast<int16_t*>(addr);
         return *ptr;
     }
     printf("Unaligned signed halfword read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 void
 Simulator::writeH(uint64_t addr, uint16_t value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 2)) {
+    if (handleWasmSegFault(addr, 2)) {
         return;
     }
 
     if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1839,17 +1755,17 @@ Simulator::writeH(uint64_t addr, uint16_
     printf("Unaligned unsigned halfword write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
 }
 
 void
 Simulator::writeH(uint64_t addr, int16_t value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 2)) {
+    if (handleWasmSegFault(addr, 2)) {
         return;
     }
 
     if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         int16_t* ptr = reinterpret_cast<int16_t*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1857,51 +1773,51 @@ Simulator::writeH(uint64_t addr, int16_t
     printf("Unaligned halfword write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
 }
 
 uint32_t
 Simulator::readWU(uint64_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 4)) {
+    if (handleWasmSegFault(addr, 4)) {
         return -1;
     }
 
     if ((addr & 3) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
         return *ptr;
     }
     printf("Unaligned read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 int32_t
 Simulator::readW(uint64_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 4)) {
+    if (handleWasmSegFault(addr, 4)) {
         return -1;
     }
 
     if ((addr & 3) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         int32_t* ptr = reinterpret_cast<int32_t*>(addr);
         return *ptr;
     }
     printf("Unaligned read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 void
 Simulator::writeW(uint64_t addr, uint32_t value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 4)) {
+    if (handleWasmSegFault(addr, 4)) {
         return;
     }
 
     if ((addr & 3) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1909,17 +1825,17 @@ Simulator::writeW(uint64_t addr, uint32_
     printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
 }
 
 void
 Simulator::writeW(uint64_t addr, int32_t value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 4)) {
+    if (handleWasmSegFault(addr, 4)) {
         return;
     }
 
     if ((addr & 3) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         int32_t* ptr = reinterpret_cast<int32_t*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1927,34 +1843,34 @@ Simulator::writeW(uint64_t addr, int32_t
     printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
 }
 
 int64_t
 Simulator::readDW(uint64_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 8)) {
+    if (handleWasmSegFault(addr, 8)) {
         return -1;
     }
 
     if ((addr & kPointerAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
         return *ptr;
     }
     printf("Unaligned read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 void
 Simulator::writeDW(uint64_t addr, int64_t value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 8)) {
+    if (handleWasmSegFault(addr, 8)) {
         return;
     }
 
     if ((addr & kPointerAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         int64_t* ptr = reinterpret_cast<int64_t*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1962,34 +1878,34 @@ Simulator::writeDW(uint64_t addr, int64_
     printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
 }
 
 double
 Simulator::readD(uint64_t addr, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 8)) {
+    if (handleWasmSegFault(addr, 8)) {
         return NAN;
     }
 
     if ((addr & kDoubleAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         double* ptr = reinterpret_cast<double*>(addr);
         return *ptr;
     }
     printf("Unaligned (double) read at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n",
            addr, reinterpret_cast<intptr_t>(instr));
     MOZ_CRASH();
     return 0;
 }
 
 void
 Simulator::writeD(uint64_t addr, double value, SimInstruction* instr)
 {
-    if (handleWasmFault(addr, 8)) {
+    if (handleWasmSegFault(addr, 8)) {
         return;
     }
 
     if ((addr & kDoubleAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
         double* ptr = reinterpret_cast<double*>(addr);
         LLBit_ = false;
         *ptr = value;
         return;
@@ -1999,17 +1915,17 @@ Simulator::writeD(uint64_t addr, double 
     MOZ_CRASH();
 }
 
 int
 Simulator::loadLinkedW(uint64_t addr, SimInstruction* instr)
 {
     if ((addr & 3) == 0) {
 
-        if (handleWasmFault(addr, 4)) {
+        if (handleWasmSegFault(addr, 4)) {
             return -1;
         }
 
         volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(addr);
         int32_t value = *ptr;
         lastLLValue_ = value;
         LLAddr_ = addr;
         // Note that any memory write or "external" interrupt should reset this value to false.
@@ -2052,17 +1968,17 @@ Simulator::storeConditionalW(uint64_t ad
     return 0;
 }
 
 int64_t
 Simulator::loadLinkedD(uint64_t addr, SimInstruction* instr)
 {
     if ((addr & kPointerAlignmentMask) == 0) {
 
-        if (handleWasmFault(addr, 8)) {
+        if (handleWasmSegFault(addr, 8)) {
             return -1;
         }
 
         volatile int64_t* ptr = reinterpret_cast<volatile int64_t*>(addr);
         int64_t value = *ptr;
         lastLLValue_ = value;
         LLAddr_ = addr;
         // Note that any memory write or "external" interrupt should reset this value to false.
@@ -2437,18 +2353,22 @@ Simulator::softwareInterrupt(SimInstruct
     } else {
         switch (func) {
             case ff_tge:
             case ff_tgeu:
             case ff_tlt:
             case ff_tltu:
             case ff_teq:
             case ff_tne:
-            if (instr->bits(15, 6) == kWasmTrapCode && handleWasmTrapFault()) {
-                return;
+            if (instr->bits(15, 6) == kWasmTrapCode) {
+                uint8_t* newPC;
+                if (wasm::HandleIllegalInstruction(registerState(), &newPC)) {
+                    set_pc(int64_t(newPC));
+                    return;
+                }
             }
         };
         // All remaining break_ codes, and all traps are handled here.
         MipsDebugger dbg(this);
         dbg.debug();
     }
 }
 
--- a/js/src/jit/mips64/Simulator-mips64.h
+++ b/js/src/jit/mips64/Simulator-mips64.h
@@ -33,16 +33,17 @@
 #ifdef JS_SIMULATOR_MIPS64
 
 #include "mozilla/Atomics.h"
 
 #include "jit/IonTypes.h"
 #include "js/ProfilingFrameIterator.h"
 #include "threading/Thread.h"
 #include "vm/MutexIDs.h"
+#include "wasm/WasmSignalHandlers.h"
 
 namespace js {
 
 namespace jit {
 
 class JitActivation;
 
 class Simulator;
@@ -155,17 +156,17 @@ class Simulator {
     enum FPURegister {
         f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11,
         f12, f13, f14, f15, f16, f17, f18, f19, f20, f21,
         f22, f23, f24, f25, f26, f27, f28, f29, f30, f31,
         kNumFPURegisters
     };
 
     // Returns nullptr on OOM.
-    static Simulator* Create(JSContext* cx);
+    static Simulator* Create();
 
     static void Destroy(Simulator* simulator);
 
     // Constructor/destructor are for internal use only; use the static methods above.
     Simulator();
     ~Simulator();
 
     static bool supportsAtomics() { return true; }
@@ -312,18 +313,31 @@ class Simulator {
     void enableStop(uint32_t code);
     void disableStop(uint32_t code);
     void increaseStopCounter(uint32_t code);
     void printStopInfo(uint32_t code);
 
     JS::ProfilingFrameIterator::RegisterState registerState();
 
     // Handle any wasm faults, returning true if the fault was handled.
-    bool handleWasmFault(uint64_t addr, unsigned numBytes);
-    bool handleWasmTrapFault();
+    // This method is rather hot so inline the normal (no-wasm) case.
+    bool MOZ_ALWAYS_INLINE handleWasmSegFault(uint64_t addr, unsigned numBytes) {
+        if (MOZ_LIKELY(!js::wasm::CodeExists)) {
+            return false;
+        }
+
+        uint8_t* newPC;
+        if (!js::wasm::MemoryAccessTraps(registerState(), (uint8_t*)addr, numBytes, &newPC)) {
+            return false;
+        }
+
+        LLBit_ = false;
+        set_pc(int64_t(newPC));
+        return true;
+    }
 
     // Executes one instruction.
     void instructionDecode(SimInstruction* instr);
     // Execute one instruction placed in a branch delay slot.
     void branchDelayInstructionDecode(SimInstruction* instr);
 
   public:
     static int64_t StopSimAt;
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -113,17 +113,17 @@ JSContext::init(ContextKind kind)
             return false;
         }
 
         if (!fx.initInstance()) {
             return false;
         }
 
 #ifdef JS_SIMULATOR
-        simulator_ = jit::Simulator::Create(this);
+        simulator_ = jit::Simulator::Create();
         if (!simulator_) {
             return false;
         }
 #endif
 
         if (!wasm::EnsureSignalHandlers(this)) {
             return false;
         }
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -1462,54 +1462,8 @@ ProfilingFrameIterator::label() const
       case CodeRange::TrapExit:          return trapDescription;
       case CodeRange::DebugTrap:         return debugTrapDescription;
       case CodeRange::FarJumpIsland:     return "interstitial (in wasm)";
       case CodeRange::Throw:             MOZ_CRASH("does not have a frame");
     }
 
     MOZ_CRASH("bad code range kind");
 }
-
-Instance*
-wasm::LookupFaultingInstance(const ModuleSegment& codeSegment, void* pc, void* fp)
-{
-    // Assume bug-caused faults can be raised at any PC and apply the logic of
-    // ProfilingFrameIterator to reject any pc outside the (post-prologue,
-    // pre-epilogue) body of a wasm function. This is exhaustively tested by the
-    // simulators which call this function at every load/store before even
-    // knowing whether there is a fault.
-
-    const CodeRange* codeRange = codeSegment.code().lookupFuncRange(pc);
-    if (!codeRange) {
-        return nullptr;
-    }
-
-    size_t offsetInModule = ((uint8_t*)pc) - codeSegment.base();
-    if ((offsetInModule >= codeRange->funcNormalEntry() &&
-         offsetInModule < codeRange->funcNormalEntry() + SetFP) ||
-        (offsetInModule >= codeRange->ret() - PoppedFP &&
-         offsetInModule <= codeRange->ret()))
-    {
-        return nullptr;
-    }
-
-    Instance* instance = reinterpret_cast<Frame*>(fp)->tls->instance;
-
-    // TODO: In the special case of a cross-instance indirect call bad-signature
-    // fault, fp can point to the caller frame which is in a different
-    // instance/module than pc. This special case should go away when old-style
-    // traps go away and signal handling is reworked.
-    //MOZ_RELEASE_ASSERT(&instance->code() == &codeSegment.code());
-
-    return instance;
-}
-
-bool
-wasm::InCompiledCode(void* pc)
-{
-    if (LookupCodeSegment(pc)) {
-        return true;
-    }
-
-    const CodeRange* codeRange;
-    uint8_t* codeBase;
-    return LookupBuiltinThunk(pc, &codeRange, &codeBase);
-}
--- a/js/src/wasm/WasmFrameIter.h
+++ b/js/src/wasm/WasmFrameIter.h
@@ -40,16 +40,18 @@ class DebugFrame;
 class FuncTypeIdDesc;
 class Instance;
 class ModuleSegment;
 
 struct CallableOffsets;
 struct FuncOffsets;
 struct Frame;
 
+typedef JS::ProfilingFrameIterator::RegisterState RegisterState;
+
 // Iterates over a linear group of wasm frames of a single wasm JitActivation,
 // called synchronously from C++ in the wasm thread. It will stop at the first
 // frame that is not of the same kind, or at the end of an activation.
 //
 // If you want to handle every kind of frames (including JS jit frames), use
 // JitFrameIter.
 
 class WasmFrameIter
@@ -184,17 +186,17 @@ class ProfilingFrameIterator
 
     // Start unwinding at a group of wasm frames after unwinding an inner group
     // of JSJit frames.
     ProfilingFrameIterator(const jit::JitActivation& activation, const Frame* fp);
 
     // Start unwinding at the innermost activation given the register state when
     // the thread was suspended.
     ProfilingFrameIterator(const jit::JitActivation& activation,
-                           const JS::ProfilingFrameIterator::RegisterState& state);
+                           const RegisterState& state);
 
     void operator++();
     bool done() const { return !codeRange_ && exitReason_.isNone(); }
 
     void* stackAddress() const { MOZ_ASSERT(!done()); return stackAddress_; }
     uint8_t* unwoundIonCallerFP() const { MOZ_ASSERT(done()); return unwoundIonCallerFP_; }
     const char* label() const;
 };
@@ -223,40 +225,27 @@ GenerateJitEntryPrologue(jit::MacroAssem
 
 void
 GenerateFunctionPrologue(jit::MacroAssembler& masm, const FuncTypeIdDesc& funcTypeId,
                          const mozilla::Maybe<uint32_t>& tier1FuncIndex,
                          FuncOffsets* offsets);
 void
 GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed, FuncOffsets* offsets);
 
-// Given a fault at pc with register fp, return the faulting instance if there
-// is such a plausible instance, and otherwise null.
-
-Instance*
-LookupFaultingInstance(const ModuleSegment& codeSegment, void* pc, void* fp);
-
-// Return whether the given PC is in wasm code.
-
-bool
-InCompiledCode(void* pc);
-
 // Describes register state and associated code at a given call frame.
 
 struct UnwindState
 {
     Frame* fp;
     void* pc;
     const Code* code;
     const CodeRange* codeRange;
     UnwindState() : fp(nullptr), pc(nullptr), code(nullptr), codeRange(nullptr) {}
 };
 
-typedef JS::ProfilingFrameIterator::RegisterState RegisterState;
-
 // Ensures the register state at a call site is consistent: pc must be in the
 // code range of the code described by fp. This prevents issues when using
 // the values of pc/fp, especially at call sites boundaries, where the state
 // hasn't fully transitioned from the caller's to the callee's.
 //
 // unwoundCaller is set to true if we were in a transitional state and had to
 // rewind to the caller's frame instead of the current frame.
 //
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -980,17 +980,16 @@ Instance::~Instance()
 }
 
 size_t
 Instance::memoryMappedSize() const
 {
     return memory_->buffer().wasmMappedSize();
 }
 
-#ifdef JS_SIMULATOR
 bool
 Instance::memoryAccessInGuardRegion(uint8_t* addr, unsigned numBytes) const
 {
     MOZ_ASSERT(numBytes > 0);
 
     if (!metadata().usesMemory()) {
         return false;
     }
@@ -998,17 +997,16 @@ Instance::memoryAccessInGuardRegion(uint
     uint8_t* base = memoryBase().unwrap(/* comparison */);
     if (addr < base) {
         return false;
     }
 
     size_t lastByteOffset = addr - base + (numBytes - 1);
     return lastByteOffset >= memory()->volatileMemoryLength() && lastByteOffset < memoryMappedSize();
 }
-#endif
 
 void
 Instance::tracePrivate(JSTracer* trc)
 {
     // This method is only called from WasmInstanceObject so the only reason why
     // TraceEdge is called is so that the pointer can be updated during a moving
     // GC. TraceWeakEdge may sound better, but it is less efficient given that
     // we know object_ is already marked.
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -100,19 +100,17 @@ class Instance
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
     const Metadata& metadata() const { return code_->metadata(); }
     bool isAsmJS() const { return metadata().isAsmJS(); }
     const SharedTableVector& tables() const { return tables_; }
     SharedMem<uint8_t*> memoryBase() const;
     WasmMemoryObject* memory() const;
     size_t memoryMappedSize() const;
     SharedArrayRawBuffer* sharedMemoryBuffer() const; // never null
-#ifdef JS_SIMULATOR
     bool memoryAccessInGuardRegion(uint8_t* addr, unsigned numBytes) const;
-#endif
     const StructTypeVector& structTypes() const { return code_->structTypes(); }
 
     static constexpr size_t offsetOfJSJitArgsRectifier() {
         return offsetof(Instance, jsJitArgsRectifier_);
     }
     static constexpr size_t offsetOfJSJitExceptionHandler() {
         return offsetof(Instance, jsJitExceptionHandler_);
     }
--- a/js/src/wasm/WasmProcess.cpp
+++ b/js/src/wasm/WasmProcess.cpp
@@ -273,16 +273,28 @@ const Code*
 wasm::LookupCode(const void* pc, const CodeRange** codeRange /* = nullptr */)
 {
     const CodeSegment* found = LookupCodeSegment(pc, codeRange);
     MOZ_ASSERT_IF(!found && codeRange, !*codeRange);
     return found ? &found->code() : nullptr;
 }
 
 bool
+wasm::InCompiledCode(void* pc)
+{
+    if (LookupCodeSegment(pc)) {
+        return true;
+    }
+
+    const CodeRange* codeRange;
+    uint8_t* codeBase;
+    return LookupBuiltinThunk(pc, &codeRange, &codeBase);
+}
+
+bool
 wasm::Init()
 {
     MOZ_RELEASE_ASSERT(!sProcessCodeSegmentMap);
 
 #ifdef ENABLE_WASM_CRANELIFT
     cranelift_initialize();
 #endif
 
--- a/js/src/wasm/WasmProcess.h
+++ b/js/src/wasm/WasmProcess.h
@@ -33,16 +33,21 @@ class CodeSegment;
 // and thus are safe to use in a profiling context.
 
 const CodeSegment*
 LookupCodeSegment(const void* pc, const CodeRange** codeRange = nullptr);
 
 const Code*
 LookupCode(const void* pc, const CodeRange** codeRange = nullptr);
 
+// Return whether the given PC is in any type of wasm code (module or builtin).
+
+bool
+InCompiledCode(void* pc);
+
 // A bool member that can be used as a very fast lookup to know if there is any
 // code segment at all.
 
 extern mozilla::Atomic<bool> CodeExists;
 
 // These methods allow to (un)register CodeSegments so they can be looked up
 // via pc in the methods described above.
 
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -901,8 +901,59 @@ wasm::EnsureSignalHandlers(JSContext* cx
 }
 
 bool
 wasm::HaveSignalHandlers()
 {
     MOZ_ASSERT(sTriedInstallSignalHandlers);
     return sHaveSignalHandlers;
 }
+
+bool
+wasm::MemoryAccessTraps(const RegisterState& regs, uint8_t* addr, uint32_t numBytes, uint8_t** newPC)
+{
+    const wasm::CodeSegment* codeSegment = wasm::LookupCodeSegment(regs.pc);
+    if (!codeSegment || !codeSegment->isModule()) {
+        return false;
+    }
+
+    const wasm::ModuleSegment& segment = *codeSegment->asModule();
+
+    Trap trap;
+    BytecodeOffset bytecode;
+    if (!segment.code().lookupTrap(regs.pc, &trap, &bytecode) || trap != Trap::OutOfBounds) {
+        return false;
+    }
+
+    Instance& instance = *reinterpret_cast<Frame*>(regs.fp)->tls->instance;
+    MOZ_ASSERT(&instance.code() == &segment.code());
+
+    if (!instance.memoryAccessInGuardRegion((uint8_t*)addr, numBytes)) {
+        return false;
+    }
+
+    jit::JitActivation* activation = TlsContext.get()->activation()->asJit();
+    activation->startWasmTrap(Trap::OutOfBounds, bytecode.offset(), regs);
+    *newPC = segment.trapCode();
+    return true;
+}
+
+bool
+wasm::HandleIllegalInstruction(const RegisterState& regs, uint8_t** newPC)
+{
+    const wasm::CodeSegment* codeSegment = wasm::LookupCodeSegment(regs.pc);
+    if (!codeSegment || !codeSegment->isModule()) {
+        return false;
+    }
+
+    const wasm::ModuleSegment& segment = *codeSegment->asModule();
+
+    Trap trap;
+    BytecodeOffset bytecode;
+    if (!segment.code().lookupTrap(regs.pc, &trap, &bytecode)) {
+        return false;
+    }
+
+    jit::JitActivation* activation = TlsContext.get()->activation()->asJit();
+    activation->startWasmTrap(trap, bytecode.offset(), regs);
+    *newPC = segment.trapCode();
+    return true;
+}
--- a/js/src/wasm/WasmSignalHandlers.h
+++ b/js/src/wasm/WasmSignalHandlers.h
@@ -20,34 +20,47 @@
 #define wasm_signal_handlers_h
 
 #include "mozilla/Attributes.h"
 
 #if defined(XP_DARWIN)
 # include <mach/mach.h>
 #endif
 
-#include "js/TypeDecls.h"
+#include "js/ProfilingFrameIterator.h"
 #include "threading/Thread.h"
-#include "wasm/WasmTypes.h"
+#include "wasm/WasmProcess.h"
 
 namespace js {
 namespace wasm {
 
+typedef JS::ProfilingFrameIterator::RegisterState RegisterState;
+
 // Ensure the given JSRuntime is set up to use signals. Failure to enable signal
 // handlers indicates some catastrophic failure and creation of the runtime must
 // fail.
 MOZ_MUST_USE bool
 EnsureSignalHandlers(JSContext* cx);
 
 // Return whether signals can be used in this process for asm.js/wasm
 // out-of-bounds.
 bool
 HaveSignalHandlers();
 
+// Return whether, with the given simulator register state, a memory access to
+// 'addr' of size 'numBytes' needs to trap and, if so, where the simulator
+// should redirect pc to.
+bool
+MemoryAccessTraps(const RegisterState& regs, uint8_t* addr, uint32_t numBytes, uint8_t** newPC);
+
+// Return whether, with the given simulator register state, an illegal
+// instruction fault is expected and, if so, the value of the next PC.
+bool
+HandleIllegalInstruction(const RegisterState& regs, uint8_t** newPC);
+
 #if defined(XP_DARWIN)
 // On OSX we are forced to use the lower-level Mach exception mechanism instead
 // of Unix signals. Mach exceptions are not handled on the victim's stack but
 // rather require an extra thread. For simplicity, we create one such thread
 // per JSContext (upon the first use of wasm in the JSContext). This thread
 // and related resources are owned by AsmJSMachExceptionHandler which is owned
 // by JSContext.
 class MachExceptionHandler
@@ -62,22 +75,12 @@ class MachExceptionHandler
     MachExceptionHandler();
     ~MachExceptionHandler() { uninstall(); }
     mach_port_t port() const { return port_; }
     bool installed() const { return installed_; }
     bool install(JSContext* cx);
 };
 #endif
 
-// On trap, the bytecode offset to be reported in callstacks is saved.
-
-struct TrapData
-{
-    void* resumePC;
-    void* unwoundPC;
-    Trap trap;
-    uint32_t bytecodeOffset;
-};
-
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_signal_handlers_h
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1469,16 +1469,34 @@ struct TrapSiteVectorArray : EnumeratedA
     bool empty() const;
     void clear();
     void swap(TrapSiteVectorArray& rhs);
     void podResizeToFit();
 
     WASM_DECLARE_SERIALIZABLE(TrapSiteVectorArray)
 };
 
+// On trap, the bytecode offset to be reported in callstacks is saved.
+
+struct TrapData
+{
+    // The resumePC indicates where, if the trap doesn't throw, the trap stub
+    // should jump to after restoring all register state.
+    void* resumePC;
+
+    // The unwoundPC is the PC after adjustment by wasm::StartUnwinding(), which
+    // basically unwinds partially-construted wasm::Frames when pc is in the
+    // prologue/epilogue. Stack traces during a trap should use this PC since
+    // it corresponds to the JitActivation::wasmExitFP.
+    void* unwoundPC;
+
+    Trap trap;
+    uint32_t bytecodeOffset;
+};
+
 // The (,Callable,Func)Offsets classes are used to record the offsets of
 // different key points in a CodeRange during compilation.
 
 struct Offsets
 {
     explicit Offsets(uint32_t begin = 0, uint32_t end = 0)
       : begin(begin), end(end)
     {}