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 439498 8b6344a8c25c48a2180b6a5ba7050e2773f22f89
parent 439497 d1094983384c30f5aa7860e7d5ff49be625fb47a
child 439499 f2ff07352cbe9a60a0bb9c9705f3c911d4ae1f7d
push id34776
push usernerli@mozilla.com
push dateThu, 04 Oct 2018 04:03:46 +0000
treeherdermozilla-central@8b1f1ebed0f0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth, bbouvier
bugs1495149
milestone64.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 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)
     {}