Bug 1435360 - Baldr: remove wasm async interrupt support (r=jandem)
☠☠ backed out by edcbf982b033 ☠ ☠
authorLuke Wagner <luke@mozilla.com>
Mon, 12 Mar 2018 11:12:54 -0500
changeset 461339 7d6183aa40da6492ec595939a0169a9709657fb0
parent 461338 f3410beb9eb1515c041a343ed077f3b2399ecee2
child 461340 eb7a4a96c3331e2353a61ca5d234df10dde374fa
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1435360
milestone60.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 1435360 - Baldr: remove wasm async interrupt support (r=jandem)
js/src/jit/AsyncInterrupt.cpp
js/src/jit/AsyncInterrupt.h
js/src/jit/Ion.cpp
js/src/jit/JitOptions.cpp
js/src/jit/JitOptions.h
js/src/jit/Lowering.cpp
js/src/jit/arm/Simulator-arm.cpp
js/src/jit/arm/Simulator-arm.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/jsapi.cpp
js/src/moz.build
js/src/vm/JSCompartment.cpp
js/src/vm/JSContext.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmBuiltins.cpp
js/src/wasm/WasmCode.cpp
js/src/wasm/WasmCode.h
js/src/wasm/WasmFrameIter.cpp
js/src/wasm/WasmFrameIter.h
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmIonCompile.cpp
js/src/wasm/WasmModule.h
js/src/wasm/WasmProcess.cpp
js/src/wasm/WasmProcess.h
js/src/wasm/WasmSignalHandlers.cpp
js/src/wasm/WasmSignalHandlers.h
js/src/wasm/WasmStubs.cpp
js/src/wasm/WasmTypes.cpp
js/src/wasm/WasmTypes.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit/AsyncInterrupt.cpp
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jit/AsyncInterrupt.h"
+
+#include "jit/JitCompartment.h"
+#include "util/Windows.h"
+
+#if defined(ANDROID)
+# include <sys/system_properties.h>
+#endif
+
+using namespace js;
+using namespace js::jit;
+
+using mozilla::PodArrayZero;
+
+static void
+RedirectIonBackedgesToInterruptCheck(JSContext* cx)
+{
+    // Jitcode may only be modified on the runtime's active thread.
+    if (cx != cx->runtime()->activeContext())
+        return;
+
+    // The faulting thread is suspended so we can access cx fields that can
+    // normally only be accessed by the cx's active thread.
+    AutoNoteSingleThreadedRegion anstr;
+
+    Zone* zone = cx->zoneRaw();
+    if (zone && !zone->isAtomsZone()) {
+        jit::JitRuntime* jitRuntime = cx->runtime()->jitRuntime();
+        if (!jitRuntime)
+            return;
+
+        // If the backedge list is being mutated, the pc must be in C++ code and
+        // thus not in a JIT iloop. We assume that the interrupt flag will be
+        // checked at least once before entering JIT code (if not, no big deal;
+        // the browser will just request another interrupt in a second).
+        if (!jitRuntime->preventBackedgePatching()) {
+            jit::JitZoneGroup* jzg = zone->group()->jitZoneGroup;
+            jzg->patchIonBackedges(cx, jit::JitZoneGroup::BackedgeInterruptCheck);
+        }
+    }
+}
+
+#if !defined(XP_WIN)
+// For the interrupt signal, pick a signal number that:
+//  - is not otherwise used by mozilla or standard libraries
+//  - defaults to nostop and noprint on gdb/lldb so that noone is bothered
+// SIGVTALRM a relative of SIGALRM, so intended for user code, but, unlike
+// SIGALRM, not used anywhere else in Mozilla.
+static const int sJitAsyncInterruptSignal = SIGVTALRM;
+
+static void
+JitAsyncInterruptHandler(int signum, siginfo_t*, void*)
+{
+    MOZ_RELEASE_ASSERT(signum == sJitAsyncInterruptSignal);
+
+    JSContext* cx = TlsContext.get();
+    if (!cx)
+        return;
+
+#if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
+    SimulatorProcess::ICacheCheckingDisableCount++;
+#endif
+
+    RedirectIonBackedgesToInterruptCheck(cx);
+
+#if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
+    SimulatorProcess::cacheInvalidatedBySignalHandler_ = true;
+    SimulatorProcess::ICacheCheckingDisableCount--;
+#endif
+
+    cx->finishHandlingJitInterrupt();
+}
+#endif
+
+static bool sTriedInstallAsyncInterrupt = false;
+static bool sHaveAsyncInterrupt = false;
+
+void
+jit::EnsureAsyncInterrupt(JSContext* cx)
+{
+    // We assume that there are no races creating the first JSRuntime of the process.
+    if (sTriedInstallAsyncInterrupt)
+        return;
+    sTriedInstallAsyncInterrupt = true;
+
+#if defined(ANDROID) && !defined(__aarch64__)
+    // Before Android 4.4 (SDK version 19), there is a bug
+    //   https://android-review.googlesource.com/#/c/52333
+    // in Bionic's pthread_join which causes pthread_join to return early when
+    // pthread_kill is used (on any thread). Nobody expects the pthread_cond_wait
+    // EINTRquisition.
+    char version_string[PROP_VALUE_MAX];
+    PodArrayZero(version_string);
+    if (__system_property_get("ro.build.version.sdk", version_string) > 0) {
+        if (atol(version_string) < 19)
+            return;
+    }
+#endif
+
+#if defined(XP_WIN)
+    // Windows uses SuspendThread to stop the active thread from another thread.
+#else
+    struct sigaction interruptHandler;
+    interruptHandler.sa_flags = SA_SIGINFO;
+    interruptHandler.sa_sigaction = &JitAsyncInterruptHandler;
+    sigemptyset(&interruptHandler.sa_mask);
+    struct sigaction prev;
+    if (sigaction(sJitAsyncInterruptSignal, &interruptHandler, &prev))
+        MOZ_CRASH("unable to install interrupt handler");
+
+    // There shouldn't be any other handlers installed for
+    // sJitAsyncInterruptSignal. If there are, we could always forward, but we
+    // need to understand what we're doing to avoid problematic interference.
+    if ((prev.sa_flags & SA_SIGINFO && prev.sa_sigaction) ||
+        (prev.sa_handler != SIG_DFL && prev.sa_handler != SIG_IGN))
+    {
+        MOZ_CRASH("contention for interrupt signal");
+    }
+#endif // defined(XP_WIN)
+
+    sHaveAsyncInterrupt = true;
+}
+
+bool
+jit::HaveAsyncInterrupt()
+{
+    MOZ_ASSERT(sTriedInstallAsyncInterrupt);
+    return sHaveAsyncInterrupt;
+}
+
+// JSRuntime::requestInterrupt sets interrupt_ (which is checked frequently by
+// C++ code at every Baseline JIT loop backedge) and jitStackLimit_ (which is
+// checked at every Baseline and Ion JIT function prologue). The remaining
+// sources of potential iloops (Ion loop backedges) are handled by this
+// function: Ion loop backedges are patched to instead point to a stub that
+// handles the interrupt;
+void
+jit::InterruptRunningCode(JSContext* cx)
+{
+    // If signal handlers weren't installed, then Ion emit normal interrupt
+    // checks and don't need asynchronous interruption.
+    MOZ_ASSERT(sTriedInstallAsyncInterrupt);
+    if (!sHaveAsyncInterrupt)
+        return;
+
+    // Do nothing if we're already handling an interrupt here, to avoid races
+    // below and in JitRuntime::patchIonBackedges.
+    if (!cx->startHandlingJitInterrupt())
+        return;
+
+    // If we are on context's thread, then we can patch Ion backedges without
+    // any special synchronization.
+    if (cx == TlsContext.get()) {
+        RedirectIonBackedgesToInterruptCheck(cx);
+        cx->finishHandlingJitInterrupt();
+        return;
+    }
+
+    // We are not on the runtime's active thread, so we need to halt the
+    // runtime's active thread first.
+#if defined(XP_WIN)
+    // On Windows, we can simply suspend the active thread. SuspendThread can
+    // sporadically fail if the thread is in the middle of a syscall. Rather
+    // than retrying in a loop, just wait for the next request for interrupt.
+    HANDLE thread = (HANDLE)cx->threadNative();
+    if (SuspendThread(thread) != (DWORD)-1) {
+        RedirectIonBackedgesToInterruptCheck(cx);
+        ResumeThread(thread);
+    }
+    cx->finishHandlingJitInterrupt();
+#else
+    // On Unix, we instead deliver an async signal to the active thread which
+    // halts the thread and callers our JitAsyncInterruptHandler (which has
+    // already been installed by EnsureSignalHandlersInstalled).
+    pthread_t thread = (pthread_t)cx->threadNative();
+    pthread_kill(thread, sJitAsyncInterruptSignal);
+#endif
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit/AsyncInterrupt.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_AsyncInterrupt_h
+#define jit_AsyncInterrupt_h
+
+#include "NamespaceImports.h"
+
+namespace js {
+namespace jit {
+
+// Ensure the given JSRuntime is set up to use async interrupts. Failure to
+// enable signal handlers indicates some catastrophic failure and creation of
+// the runtime must fail.
+void
+EnsureAsyncInterrupt(JSContext* cx);
+
+// Return whether the async interrupt can be used to interrupt Ion code.
+bool
+HaveAsyncInterrupt();
+
+// Force any currently-executing JIT code to call HandleExecutionInterrupt.
+extern void
+InterruptRunningCode(JSContext* cx);
+
+} // namespace jit
+} // namespace js
+
+#endif // jit_AsyncInterrupt_h
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -387,17 +387,17 @@ void
 JitZoneGroup::patchIonBackedges(JSContext* cx, BackedgeTarget target)
 {
     if (target == BackedgeLoopHeader) {
         // We must be on the active thread. The caller must use
         // AutoPreventBackedgePatching to ensure we don't reenter.
         MOZ_ASSERT(cx->runtime()->jitRuntime()->preventBackedgePatching());
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
     } else {
-        // We must be called from InterruptRunningJitCode, or a signal handler
+        // We must be called from jit::InterruptRunningCode, or a signal handler
         // triggered there. rt->handlingJitInterrupt() ensures we can't reenter
         // this code.
         MOZ_ASSERT(!cx->runtime()->jitRuntime()->preventBackedgePatching());
         MOZ_ASSERT(cx->handlingJitInterrupt());
     }
 
     // Do nothing if we know all backedges are already jumping to `target`.
     if (backedgeTarget_ == target)
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -182,20 +182,16 @@ DefaultJitOptions::DefaultJitOptions()
 
     // The bytecode length limit for small function.
     SET_DEFAULT(smallFunctionMaxBytecodeLength_, 130);
 
     // An artificial testing limit for the maximum supported offset of
     // pc-relative jump and call instructions.
     SET_DEFAULT(jumpThreshold, UINT32_MAX);
 
-    // Whether the (ARM) simulators should always interrupt before executing any
-    // instruction.
-    SET_DEFAULT(simulatorAlwaysInterrupt, false);
-
     // Branch pruning heuristic is based on a scoring system, which is look at
     // different metrics and provide a score. The score is computed as a
     // projection where each factor defines the weight of each metric. Then this
     // score is compared against a threshold to prevent a branch from being
     // removed.
     SET_DEFAULT(branchPruningHitCountFactor, 1);
     SET_DEFAULT(branchPruningInstFactor, 10);
     SET_DEFAULT(branchPruningBlockSpanFactor, 100);
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -71,17 +71,16 @@ struct DefaultJitOptions
     bool forceInlineCaches;
     bool fullDebugChecks;
     bool limitScriptSize;
     bool osr;
     bool asmJSAtomicsEnable;
     bool wasmFoldOffsets;
     bool wasmDelayTier2;
     bool ionInterruptWithoutSignals;
-    bool simulatorAlwaysInterrupt;
     uint32_t baselineWarmUpThreshold;
     uint32_t exceptionBailoutThreshold;
     uint32_t frequentBailoutThreshold;
     uint32_t maxStackArgs;
     uint32_t osrPcMismatchesBeforeRecompile;
     uint32_t smallFunctionMaxBytecodeLength_;
     uint32_t jumpThreshold;
     uint32_t branchPruningHitCountFactor;
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4,21 +4,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/Lowering.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/EndianUtils.h"
 
+#include "jit/AsyncInterrupt.h"
 #include "jit/JitSpewer.h"
 #include "jit/LIR.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
-#include "wasm/WasmSignalHandlers.h"
 
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/BytecodeUtil-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 using namespace jit;
 
@@ -88,18 +88,18 @@ void
 LIRGenerator::visitIsConstructing(MIsConstructing* ins)
 {
     define(new(alloc()) LIsConstructing(), ins);
 }
 
 static void
 TryToUseImplicitInterruptCheck(MIRGraph& graph, MBasicBlock* backedge)
 {
-    // Implicit interrupt checks require wasm signal handlers to be installed.
-    if (!wasm::HaveSignalHandlers() || JitOptions.ionInterruptWithoutSignals)
+    // Implicit interrupt checks require JIT async interrupt support.
+    if (!jit::HaveAsyncInterrupt() || JitOptions.ionInterruptWithoutSignals)
         return;
 
     // To avoid triggering expensive interrupts (backedge patching) in
     // requestMajorGC and requestMinorGC, use an implicit interrupt check only
     // if the loop body can not trigger GC or affect GC state like the store
     // buffer. We do this by checking there are no safepoints attached to LIR
     // instructions inside the loop.
 
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -1155,17 +1155,16 @@ Simulator::Simulator(JSContext* cx)
 
     // Note, allocation and anything that depends on allocated memory is
     // deferred until init(), in order to handle OOM properly.
 
     stack_ = nullptr;
     stackLimit_ = 0;
     pc_modified_ = false;
     icount_ = 0L;
-    wasm_interrupt_ = false;
     break_pc_ = nullptr;
     break_instr_ = 0;
     single_stepping_ = false;
     single_step_callback_ = nullptr;
     single_step_callback_arg_ = nullptr;
     skipCalleeSavedRegsCheck = false;
 
     // Set up architecture state.
@@ -1589,39 +1588,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;
 }
 
-// The signal handler only redirects the PC to the interrupt stub when the PC is
-// in function code. However, this guard is racy for the ARM simulator since the
-// signal handler samples PC in the middle of simulating an instruction and thus
-// the current PC may have advanced once since the signal handler's guard. So we
-// re-check here.
-void
-Simulator::handleWasmInterrupt()
-{
-    if (!wasm::CodeExists)
-        return;
-
-    uint8_t* pc = (uint8_t*)get_pc();
-
-    const wasm::ModuleSegment* ms = nullptr;
-    if (!wasm::InInterruptibleCode(cx_, pc, &ms))
-        return;
-
-    if (!cx_->activation()->asJit()->startWasmInterrupt(registerState()))
-        return;
-
-    set_pc(int32_t(ms->interruptCode()));
-}
-
 static inline JitActivation*
 GetJitActivation(JSContext* cx)
 {
     if (!wasm::CodeExists)
         return nullptr;
     if (!cx->activation() || !cx->activation()->isJit())
         return nullptr;
     return cx->activation()->asJit();
@@ -1654,17 +1630,17 @@ Simulator::handleWasmSegFault(int32_t ad
 
     MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
 
     if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
         return false;
 
     const wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
     if (!memoryAccess) {
-        MOZ_ALWAYS_TRUE(act->asJit()->startWasmInterrupt(registerState()));
+        act->asJit()->startWasmTrap(wasm::Trap::OutOfBounds, 0, registerState());
         if (!instance->code().containsCodePC(pc))
             MOZ_CRASH("Cannot map PC to trap handler");
         set_pc(int32_t(moduleSegment->outOfBoundsCode()));
         return true;
     }
 
     MOZ_ASSERT(memoryAccess->hasTrapOutOfLineCode());
     set_pc(int32_t(memoryAccess->trapOutOfLineCode(moduleSegment->base())));
@@ -4920,29 +4896,16 @@ Simulator::disable_single_stepping()
     if (!single_stepping_)
         return;
     single_step_callback_(single_step_callback_arg_, this, (void*)get_pc());
     single_stepping_ = false;
     single_step_callback_ = nullptr;
     single_step_callback_arg_ = nullptr;
 }
 
-static void
-FakeInterruptHandler()
-{
-    JSContext* cx = TlsContext.get();
-    uint8_t* pc = cx->simulator()->get_pc_as<uint8_t*>();
-
-    const wasm::ModuleSegment* ms= nullptr;
-    if (!wasm::InInterruptibleCode(cx, pc, &ms))
-        return;
-
-    cx->simulator()->trigger_wasm_interrupt();
-}
-
 template<bool EnableStopSimAt>
 void
 Simulator::execute()
 {
     if (single_stepping_)
         single_step_callback_(single_step_callback_arg_, this, nullptr);
 
     // Get the PC to simulate. Cannot use the accessor here as we need the raw
@@ -4952,26 +4915,19 @@ Simulator::execute()
     while (program_counter != end_sim_pc) {
         if (EnableStopSimAt && (icount_ == Simulator::StopSimAt)) {
             fprintf(stderr, "\nStopped simulation at icount %lld\n", icount_);
             ArmDebugger dbg(this);
             dbg.debug();
         } else {
             if (single_stepping_)
                 single_step_callback_(single_step_callback_arg_, this, (void*)program_counter);
-            if (MOZ_UNLIKELY(JitOptions.simulatorAlwaysInterrupt))
-                FakeInterruptHandler();
             SimInstruction* instr = reinterpret_cast<SimInstruction*>(program_counter);
             instructionDecode(instr);
             icount_++;
-
-            if (MOZ_UNLIKELY(wasm_interrupt_)) {
-                handleWasmInterrupt();
-                wasm_interrupt_ = false;
-            }
         }
         program_counter = get_pc();
     }
 
     if (single_stepping_)
         single_step_callback_(single_step_callback_arg_, this, nullptr);
 }
 
--- a/js/src/jit/arm/Simulator-arm.h
+++ b/js/src/jit/arm/Simulator-arm.h
@@ -192,23 +192,16 @@ class Simulator
 
     // Special case of set_register and get_register to access the raw PC value.
     void set_pc(int32_t value);
     int32_t get_pc() const;
 
     template <typename T>
     T get_pc_as() const { return reinterpret_cast<T>(get_pc()); }
 
-    void trigger_wasm_interrupt() {
-        // This can be called several times if a single interrupt isn't caught
-        // and handled by the simulator, but this is fine; once the current
-        // instruction is done executing, the interrupt will be handled anyhow.
-        wasm_interrupt_ = true;
-    }
-
     void enable_single_stepping(SingleStepCallback cb, void* arg);
     void disable_single_stepping();
 
     uintptr_t stackLimit() const;
     bool overRecursed(uintptr_t newsp = 0) const;
     bool overRecursedWithExtra(uint32_t extra) const;
 
     // Executes ARM instructions until the PC reaches end_sim_pc.
@@ -288,17 +281,16 @@ class Simulator
     inline bool isWatchedStop(uint32_t bkpt_code);
     inline bool isEnabledStop(uint32_t bkpt_code);
     inline void enableStop(uint32_t bkpt_code);
     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.
-    void handleWasmInterrupt();
     JS::ProfilingFrameIterator::RegisterState registerState();
 
     // Handle any wasm faults, returning true if the fault was handled.
     bool handleWasmSegFault(int32_t addr, unsigned numBytes);
     bool handleWasmIllFault();
 
     // Read and write memory.
     inline uint8_t readBU(int32_t addr);
@@ -421,19 +413,16 @@ class Simulator
     bool inexact_vfp_flag_;
 
     // Simulator support.
     char* stack_;
     uintptr_t stackLimit_;
     bool pc_modified_;
     int64_t icount_;
 
-    // wasm async interrupt / fault support
-    bool wasm_interrupt_;
-
     // Debugger input.
     char* lastDebuggerInput_;
 
     // Registered breakpoints.
     SimInstruction* break_pc_;
     Instr break_instr_;
 
     // Single-stepping support
--- a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
+++ b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp
@@ -81,17 +81,16 @@ Simulator::~Simulator() {
 void Simulator::ResetState() {
   // Reset the system registers.
   nzcv_ = SimSystemRegister::DefaultValueFor(NZCV);
   fpcr_ = SimSystemRegister::DefaultValueFor(FPCR);
 
   // Reset registers to 0.
   pc_ = nullptr;
   pc_modified_ = false;
-  wasm_interrupt_ = false;
   for (unsigned i = 0; i < kNumberOfRegisters; i++) {
     set_xreg(i, 0xbadbeef);
   }
   // Set FP registers to a value that is a NaN in both 32-bit and 64-bit FP.
   uint64_t nan_bits = UINT64_C(0x7ff0dead7f8beef1);
   VIXL_ASSERT(IsSignallingNaN(rawbits_to_double(nan_bits & kDRegMask)));
   VIXL_ASSERT(IsSignallingNaN(rawbits_to_float(nan_bits & kSRegMask)));
   for (unsigned i = 0; i < kNumberOfFPRegisters; i++) {
@@ -190,25 +189,16 @@ void Simulator::Destroy(Simulator* sim) 
 }
 
 
 void Simulator::ExecuteInstruction() {
   // The program counter should always be aligned.
   VIXL_ASSERT(IsWordAligned(pc_));
   decoder_->Decode(pc_);
   increment_pc();
-
-  if (MOZ_UNLIKELY(wasm_interrupt_)) {
-    handle_wasm_interrupt();
-    // Just calling set_pc turns the pc_modified_ flag on, which means it doesn't
-    // auto-step after executing the next instruction.  Force that to off so it
-    // will auto-step after executing the first instruction of the handler.
-    pc_modified_ = false;
-    wasm_interrupt_ = false;
-  }
 }
 
 
 uintptr_t Simulator::stackLimit() const {
   return reinterpret_cast<uintptr_t>(stack_limit_);
 }
 
 
@@ -225,22 +215,16 @@ bool Simulator::overRecursed(uintptr_t n
 
 
 bool Simulator::overRecursedWithExtra(uint32_t extra) const {
   uintptr_t newsp = get_sp() - extra;
   return newsp <= stackLimit();
 }
 
 
-void Simulator::trigger_wasm_interrupt() {
-  MOZ_ASSERT(!wasm_interrupt_);
-  wasm_interrupt_ = true;
-}
-
-
 static inline JitActivation*
 GetJitActivation(JSContext* cx)
 {
     if (!js::wasm::CodeExists)
         return nullptr;
     if (!cx->activation() || !cx->activation()->isJit())
         return nullptr;
     return cx->activation()->asJit();
@@ -252,42 +236,16 @@ 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;
 }
 
-// The signal handler only redirects the PC to the interrupt stub when the PC is
-// in function code. However, this guard is racy for the ARM simulator since the
-// signal handler samples PC in the middle of simulating an instruction and thus
-// the current PC may have advanced once since the signal handler's guard. So we
-// re-check here.
-void Simulator::handle_wasm_interrupt()
-{
-  if (!js::wasm::CodeExists)
-    return;
-
-  uint8_t* pc = (uint8_t*)get_pc();
-
-  const js::wasm::ModuleSegment* ms = nullptr;
-  if (!js::wasm::InInterruptibleCode(cx_, pc, &ms))
-      return;
-
-  JitActivation* act = GetJitActivation(cx_);
-  if (!act)
-      return;
-
-  if (!act->startWasmInterrupt(registerState()))
-      return;
-
-  set_pc((Instruction*)ms->interruptCode());
-}
-
 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();
@@ -304,18 +262,17 @@ Simulator::handle_wasm_seg_fault(uintptr
 
     MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
 
     if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
         return false;
 
     const js::wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
     if (!memoryAccess) {
-        if (!act->startWasmInterrupt(registerState()))
-	    MOZ_CRASH("Cannot start interrupt");
+        act->startWasmTrap(wasm::Trap::OutOfBounds, 0, registerState());
         if (!instance->code().containsCodePC(pc))
             MOZ_CRASH("Cannot map PC to trap handler");
         set_pc((Instruction*)moduleSegment->outOfBoundsCode());
         return true;
     }
 
     MOZ_ASSERT(memoryAccess->hasTrapOutOfLineCode());
     set_pc((Instruction*)memoryAccess->trapOutOfLineCode(moduleSegment->base()));
--- a/js/src/jit/arm64/vixl/Simulator-vixl.h
+++ b/js/src/jit/arm64/vixl/Simulator-vixl.h
@@ -741,18 +741,16 @@ 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;
   }
 
-  void trigger_wasm_interrupt();
-  void handle_wasm_interrupt();
   bool handle_wasm_ill_fault();
   bool handle_wasm_seg_fault(uintptr_t addr, unsigned numBytes);
 
   void increment_pc() {
     if (!pc_modified_) {
       pc_ = pc_->NextInstruction();
     }
 
@@ -2578,17 +2576,16 @@ class Simulator : public DecoderVisitor 
   static const int stack_size_ = (2 * MBytes) + (2 * stack_protection_size_);
   byte* stack_limit_;
 
   Decoder* decoder_;
   // Indicates if the pc has been modified by the instruction and should not be
   // automatically incremented.
   bool pc_modified_;
   const Instruction* pc_;
-  bool wasm_interrupt_;
 
   static const char* xreg_names[];
   static const char* wreg_names[];
   static const char* sreg_names[];
   static const char* dreg_names[];
   static const char* vreg_names[];
 
   static const Instruction* kEndOfSimAddress;
--- a/js/src/jit/mips32/Simulator-mips32.cpp
+++ b/js/src/jit/mips32/Simulator-mips32.cpp
@@ -1264,17 +1264,16 @@ Simulator::Simulator()
     // Note, allocation and anything that depends on allocated memory is
     // deferred until init(), in order to handle OOM properly.
 
     stack_ = nullptr;
     stackLimit_ = 0;
     pc_modified_ = false;
     icount_ = 0;
     break_count_ = 0;
-    wasm_interrupt_ = false;
     break_pc_ = nullptr;
     break_instr_ = 0;
 
     // Set up architecture state.
     // All registers are initialized to zero to start with.
     for (int i = 0; i < Register::kNumSimuRegisters; i++) {
         registers_[i] = 0;
     }
@@ -1637,42 +1636,16 @@ 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;
 }
 
-// The signal handler only redirects the PC to the interrupt stub when the PC is
-// in function code. However, this guard is racy for the simulator since the
-// signal handler samples PC in the middle of simulating an instruction and thus
-// the current PC may have advanced once since the signal handler's guard. So we
-// re-check here.
-void
-Simulator::handleWasmInterrupt()
-{
-    if (!wasm::CodeExists)
-        return;
-
-    void* pc = (void*)get_pc();
-    void* fp = (void*)getRegister(Register::fp);
-
-    JitActivation* activation = TlsContext.get()->activation()->asJit();
-    const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule() || !segment->containsCodePC(pc))
-        return;
-
-    if (!activation->startWasmInterrupt(registerState()))
-         return;
-
-    set_pc(int32_t(segment->asModule()->interruptCode()));
-}
-
-
 // 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)
@@ -1701,17 +1674,17 @@ Simulator::handleWasmFault(int32_t addr,
 
     if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
          return false;
 
     LLBit_ = false;
 
     const wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
     if (!memoryAccess) {
-        MOZ_ALWAYS_TRUE(act->startWasmInterrupt(registerState()));
+        act->startWasmTrap(wasm::Trap::OutOfBounds, 0, registerState());
         if (!instance->code().containsCodePC(pc))
             MOZ_CRASH("Cannot map PC to trap handler");
         set_pc(int32_t(moduleSegment->outOfBoundsCode()));
         return true;
     }
 
     MOZ_ASSERT(memoryAccess->hasTrapOutOfLineCode());
     set_pc(int32_t(memoryAccess->trapOutOfLineCode(moduleSegment->base())));
@@ -3668,52 +3641,32 @@ Simulator::branchDelayInstructionDecode(
     }
 
     if (instr->isForbiddenInBranchDelay()) {
         MOZ_CRASH("Eror:Unexpected opcode in a branch delay slot.");
     }
     instructionDecode(instr);
 }
 
-static void
-FakeInterruptHandler()
-{
-    JSContext* cx = TlsContext.get();
-    uint8_t* pc = cx->simulator()->get_pc_as<uint8_t*>();
-
-    const wasm::ModuleSegment* ms = nullptr;
-    if (!wasm::InInterruptibleCode(cx, pc, &ms))
-        return;
-
-    cx->simulator()->trigger_wasm_interrupt();
-}
-
 template<bool enableStopSimAt>
 void
 Simulator::execute()
 {
     // Get the PC to simulate. Cannot use the accessor here as we need the
     // raw PC value and not the one used as input to arithmetic instructions.
     int program_counter = get_pc();
 
     while (program_counter != end_sim_pc) {
         if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) {
             MipsDebugger dbg(this);
             dbg.debug();
         } else {
-            if (MOZ_UNLIKELY(JitOptions.simulatorAlwaysInterrupt))
-                FakeInterruptHandler();
             SimInstruction* instr = reinterpret_cast<SimInstruction*>(program_counter);
             instructionDecode(instr);
             icount_++;
-
-            if (MOZ_UNLIKELY(wasm_interrupt_)) {
-                handleWasmInterrupt();
-                wasm_interrupt_ = false;
-            }
         }
         program_counter = get_pc();
     }
 }
 
 void
 Simulator::callInternal(uint8_t* entry)
 {
--- a/js/src/jit/mips32/Simulator-mips32.h
+++ b/js/src/jit/mips32/Simulator-mips32.h
@@ -197,23 +197,16 @@ class Simulator {
 
     // Special case of set_register and get_register to access the raw PC value.
     void set_pc(int32_t value);
     int32_t get_pc() const;
 
     template <typename T>
     T get_pc_as() const { return reinterpret_cast<T>(get_pc()); }
 
-    void trigger_wasm_interrupt() {
-        // This can be called several times if a single interrupt isn't caught
-        // and handled by the simulator, but this is fine; once the current
-        // instruction is done executing, the interrupt will be handled anyhow.
-        wasm_interrupt_ = true;
-    }
-
     // Accessor to the internal simulator stack area.
     uintptr_t stackLimit() const;
     bool overRecursed(uintptr_t newsp = 0) const;
     bool overRecursedWithExtra(uint32_t extra) const;
 
     // Executes MIPS instructions until the PC reaches end_sim_pc.
     template<bool enableStopSimAt>
     void execute();
@@ -299,18 +292,16 @@ class Simulator {
     void handleStop(uint32_t code, SimInstruction* instr);
     bool isStopInstruction(SimInstruction* instr);
     bool isEnabledStop(uint32_t code);
     void enableStop(uint32_t code);
     void disableStop(uint32_t code);
     void increaseStopCounter(uint32_t code);
     void printStopInfo(uint32_t code);
 
-    // Handle a wasm interrupt triggered by an async signal handler.
-    void handleWasmInterrupt();
     JS::ProfilingFrameIterator::RegisterState registerState();
 
     // Handle any wasm faults, returning true if the fault was handled.
     bool handleWasmFault(int32_t addr, unsigned numBytes);
     bool handleWasmTrapFault();
 
     // Executes one instruction.
     void instructionDecode(SimInstruction* instr);
@@ -360,19 +351,16 @@ class Simulator {
 
     // Simulator support.
     char* stack_;
     uintptr_t stackLimit_;
     bool pc_modified_;
     int icount_;
     int break_count_;
 
-    // wasm async interrupt / fault support
-    bool wasm_interrupt_;
-
     // Debugger input.
     char* lastDebuggerInput_;
 
     // Registered breakpoints.
     SimInstruction* break_pc_;
     Instr break_instr_;
 
     // A stop is watched if its code is less than kNumOfWatchedStops.
--- a/js/src/jit/mips64/Simulator-mips64.cpp
+++ b/js/src/jit/mips64/Simulator-mips64.cpp
@@ -1273,17 +1273,16 @@ Simulator::Simulator()
     // Note, allocation and anything that depends on allocated memory is
     // deferred until init(), in order to handle OOM properly.
 
     stack_ = nullptr;
     stackLimit_ = 0;
     pc_modified_ = false;
     icount_ = 0;
     break_count_ = 0;
-    wasm_interrupt_ = false;
     break_pc_ = nullptr;
     break_instr_ = 0;
     single_stepping_ = false;
     single_step_callback_ = nullptr;
     single_step_callback_arg_ = nullptr;
 
     // Set up architecture state.
     // All registers are initialized to zero to start with.
@@ -1640,45 +1639,16 @@ 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;
 }
 
-// The signal handler only redirects the PC to the interrupt stub when the PC is
-// in function code. However, this guard is racy for the simulator since the
-// signal handler samples PC in the middle of simulating an instruction and thus
-// the current PC may have advanced once since the signal handler's guard. So we
-// re-check here.
-void
-Simulator::handleWasmInterrupt()
-{
-    if (!wasm::CodeExists)
-        return;
-
-    void* pc = (void*)get_pc();
-    void* fp = (void*)getRegister(Register::fp);
-
-    JitActivation* activation = TlsContext.get()->activation()->asJit();
-    const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc);
-    if (!segment || !segment->isModule() || !segment->containsCodePC(pc))
-        return;
-
-    // fp can be null during the prologue/epilogue of the entry function.
-    if (!fp)
-        return;
-
-    if (!activation->startWasmInterrupt(registerState()))
-         return;
-
-    set_pc(int64_t(segment->asModule()->interruptCode()));
-}
-
 // 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)
@@ -1707,17 +1677,17 @@ Simulator::handleWasmFault(uint64_t addr
 
     if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
          return false;
 
     LLBit_ = false;
 
     const wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
     if (!memoryAccess) {
-        MOZ_ALWAYS_TRUE(act->startWasmInterrupt(registerState()));
+        act->startWasmTrap(wasm::Trap::OutOfBounds, 0, registerState());
         if (!instance->code().containsCodePC(pc))
             MOZ_CRASH("Cannot map PC to trap handler");
         set_pc(int64_t(moduleSegment->outOfBoundsCode()));
         return true;
     }
 
     MOZ_ASSERT(memoryAccess->hasTrapOutOfLineCode());
     set_pc(int64_t(memoryAccess->trapOutOfLineCode(moduleSegment->base())));
@@ -4057,29 +4027,16 @@ Simulator::disable_single_stepping()
     if (!single_stepping_)
         return;
     single_step_callback_(single_step_callback_arg_, this, (void*)get_pc());
     single_stepping_ = false;
     single_step_callback_ = nullptr;
     single_step_callback_arg_ = nullptr;
 }
 
-static void
-FakeInterruptHandler()
-{
-    JSContext* cx = TlsContext.get();
-    uint8_t* pc = cx->simulator()->get_pc_as<uint8_t*>();
-
-    const wasm::ModuleSegment* ms = nullptr;
-    if (!wasm::InInterruptibleCode(cx, pc, &ms))
-        return;
-
-    cx->simulator()->trigger_wasm_interrupt();
-}
-
 template<bool enableStopSimAt>
 void
 Simulator::execute()
 {
     if (single_stepping_)
         single_step_callback_(single_step_callback_arg_, this, nullptr);
 
     // Get the PC to simulate. Cannot use the accessor here as we need the
@@ -4088,26 +4045,19 @@ Simulator::execute()
 
     while (program_counter != end_sim_pc) {
         if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) {
             MipsDebugger dbg(this);
             dbg.debug();
         } else {
             if (single_stepping_)
                 single_step_callback_(single_step_callback_arg_, this, (void*)program_counter);
-            if (MOZ_UNLIKELY(JitOptions.simulatorAlwaysInterrupt))
-                FakeInterruptHandler();
             SimInstruction* instr = reinterpret_cast<SimInstruction *>(program_counter);
             instructionDecode(instr);
             icount_++;
-
-            if (MOZ_UNLIKELY(wasm_interrupt_)) {
-                handleWasmInterrupt();
-                wasm_interrupt_ = false;
-            }
         }
         program_counter = get_pc();
     }
 
     if (single_stepping_)
         single_step_callback_(single_step_callback_arg_, this, nullptr);
 }
 
--- a/js/src/jit/mips64/Simulator-mips64.h
+++ b/js/src/jit/mips64/Simulator-mips64.h
@@ -201,23 +201,16 @@ class Simulator {
 
     // Special case of set_register and get_register to access the raw PC value.
     void set_pc(int64_t value);
     int64_t get_pc() const;
 
     template <typename T>
     T get_pc_as() const { return reinterpret_cast<T>(get_pc()); }
 
-    void trigger_wasm_interrupt() {
-        // This can be called several times if a single interrupt isn't caught
-        // and handled by the simulator, but this is fine; once the current
-        // instruction is done executing, the interrupt will be handled anyhow.
-        wasm_interrupt_ = true;
-    }
-
     void enable_single_stepping(SingleStepCallback cb, void* arg);
     void disable_single_stepping();
 
     // Accessor to the internal simulator stack area.
     uintptr_t stackLimit() const;
     bool overRecursed(uintptr_t newsp = 0) const;
     bool overRecursedWithExtra(uint32_t extra) const;
 
@@ -314,18 +307,16 @@ class Simulator {
     void handleStop(uint32_t code, SimInstruction* instr);
     bool isStopInstruction(SimInstruction* instr);
     bool isEnabledStop(uint32_t code);
     void enableStop(uint32_t code);
     void disableStop(uint32_t code);
     void increaseStopCounter(uint32_t code);
     void printStopInfo(uint32_t code);
 
-    // Handle a wasm interrupt triggered by an async signal handler.
-    void handleWasmInterrupt();
     JS::ProfilingFrameIterator::RegisterState registerState();
 
     // Handle any wasm faults, returning true if the fault was handled.
     bool handleWasmFault(uint64_t addr, unsigned numBytes);
     bool handleWasmTrapFault();
 
     // Executes one instruction.
     void instructionDecode(SimInstruction* instr);
@@ -373,19 +364,16 @@ class Simulator {
 
     // Simulator support.
     char* stack_;
     uintptr_t stackLimit_;
     bool pc_modified_;
     int64_t icount_;
     int64_t break_count_;
 
-    // wasm async interrupt support
-    bool wasm_interrupt_;
-
     // Debugger input.
     char* lastDebuggerInput_;
 
     // Registered breakpoints.
     SimInstruction* break_pc_;
     Instr break_instr_;
 
     // Single-stepping support
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -7267,19 +7267,16 @@ JS_SetGlobalJitCompilerOption(JSContext*
         break;
       case JSJITCOMPILER_JUMP_THRESHOLD:
         if (value == uint32_t(-1)) {
             jit::DefaultJitOptions defaultValues;
             value = defaultValues.jumpThreshold;
         }
         jit::JitOptions.jumpThreshold = value;
         break;
-      case JSJITCOMPILER_SIMULATOR_ALWAYS_INTERRUPT:
-        jit::JitOptions.simulatorAlwaysInterrupt = !!value;
-        break;
       case JSJITCOMPILER_SPECTRE_INDEX_MASKING:
         jit::JitOptions.spectreIndexMasking = !!value;
         break;
       case JSJITCOMPILER_SPECTRE_OBJECT_MITIGATIONS_BARRIERS:
         jit::JitOptions.spectreObjectMitigationsBarriers = !!value;
         break;
       case JSJITCOMPILER_SPECTRE_OBJECT_MITIGATIONS_MISC:
         jit::JitOptions.spectreObjectMitigationsMisc = !!value;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -208,16 +208,17 @@ UNIFIED_SOURCES += [
     'irregexp/RegExpEngine.cpp',
     'irregexp/RegExpInterpreter.cpp',
     'irregexp/RegExpMacroAssembler.cpp',
     'irregexp/RegExpParser.cpp',
     'irregexp/RegExpStack.cpp',
     'jit/AliasAnalysis.cpp',
     'jit/AliasAnalysisShared.cpp',
     'jit/AlignmentMaskAnalysis.cpp',
+    'jit/AsyncInterrupt.cpp',
     'jit/BacktrackingAllocator.cpp',
     'jit/Bailouts.cpp',
     'jit/BaselineBailouts.cpp',
     'jit/BaselineCacheIRCompiler.cpp',
     'jit/BaselineCompiler.cpp',
     'jit/BaselineDebugModeOSR.cpp',
     'jit/BaselineFrame.cpp',
     'jit/BaselineFrameInfo.cpp',
--- a/js/src/vm/JSCompartment.cpp
+++ b/js/src/vm/JSCompartment.cpp
@@ -178,17 +178,17 @@ JSRuntime::createJitRuntime(JSContext* c
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     jit::JitRuntime* jrt = cx->new_<jit::JitRuntime>(cx->runtime());
     if (!jrt)
         return nullptr;
 
-    // Protect jitRuntime_ from being observed (by InterruptRunningJitCode)
+    // Protect jitRuntime_ from being observed (by jit::InterruptRunningCode)
     // while it is being initialized. Unfortunately, initialization depends on
     // jitRuntime_ being non-null, so we can't just wait to assign jitRuntime_.
     JitRuntime::AutoPreventBackedgePatching apbp(cx->runtime(), jrt);
     jitRuntime_ = jrt;
 
     AutoEnterOOMUnsafeRegion noOOM;
     if (!jitRuntime_->initialize(cx, atomsLock)) {
         // Handling OOM here is complicated: if we delete jitRuntime_ now, we
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -30,16 +30,17 @@
 
 #include "jsexn.h"
 #include "jspubtd.h"
 #include "jstypes.h"
 
 #include "builtin/String.h"
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
+#include "jit/AsyncInterrupt.h"
 #include "jit/Ion.h"
 #include "jit/PcScriptCache.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #include "util/DoubleToString.h"
 #include "util/NativeStack.h"
 #include "util/Windows.h"
 #include "vm/BytecodeUtil.h"
@@ -47,17 +48,16 @@
 #include "vm/HelperThreads.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Shape.h"
-#include "wasm/WasmSignalHandlers.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
@@ -97,17 +97,17 @@ js::AutoCycleDetector::~AutoCycleDetecto
     }
 }
 
 bool
 JSContext::init(ContextKind kind)
 {
     // Skip most of the initialization if this thread will not be running JS.
     if (kind == ContextKind::Cooperative) {
-        // Get a platform-native handle for this thread, used by js::InterruptRunningJitCode.
+        // Get a platform-native handle for this thread, used by jit::InterruptRunningCode.
 #ifdef XP_WIN
         size_t openFlags = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME |
                            THREAD_QUERY_INFORMATION;
         HANDLE self = OpenThread(openFlags, false, GetCurrentThreadId());
         if (!self)
         return false;
         static_assert(sizeof(HANDLE) <= sizeof(threadNative_), "need bigger field");
         threadNative_ = (size_t)self;
@@ -118,21 +118,22 @@ JSContext::init(ContextKind kind)
 
         if (!regexpStack.ref().init())
             return false;
 
         if (!fx.initInstance())
             return false;
 
 #ifdef JS_SIMULATOR
-        simulator_ = js::jit::Simulator::Create(this);
+        simulator_ = jit::Simulator::Create(this);
         if (!simulator_)
             return false;
 #endif
 
+        jit::EnsureAsyncInterrupt(this);
         if (!wasm::EnsureSignalHandlers(this))
             return false;
     }
 
     // Set the ContextKind last, so that ProtectedData checks will allow us to
     // initialize this context before it becomes the runtime's active context.
     kind_ = kind;
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -25,31 +25,31 @@
 #include "jsmath.h"
 
 #include "builtin/Promise.h"
 #include "gc/FreeOp.h"
 #include "gc/GCInternals.h"
 #include "gc/PublicIterators.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/arm64/vixl/Simulator-vixl.h"
+#include "jit/AsyncInterrupt.h"
 #include "jit/JitCompartment.h"
 #include "jit/mips32/Simulator-mips32.h"
 #include "jit/mips64/Simulator-mips64.h"
 #include "js/Date.h"
 #include "js/MemoryMetrics.h"
 #include "js/SliceBudget.h"
 #include "js/Wrapper.h"
 #include "util/Windows.h"
 #include "vm/Debugger.h"
 #include "vm/JSAtom.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/TraceLogging.h"
 #include "vm/TraceLoggingGraph.h"
-#include "wasm/WasmSignalHandlers.h"
 
 #include "gc/GC-inl.h"
 #include "vm/JSContext-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::Atomic;
@@ -593,17 +593,17 @@ JSContext::requestInterrupt(InterruptMod
         // additional steps to interrupt corner cases where the above fields are
         // not regularly polled. Wake ilooping Ion code, irregexp JIT code and
         // Atomics.wait()
         interruptRegExpJit_ = true;
         fx.lock();
         if (fx.isWaiting())
             fx.wake(FutexThread::WakeForJSInterrupt);
         fx.unlock();
-        InterruptRunningJitCode(this);
+        jit::InterruptRunningCode(this);
     }
 }
 
 bool
 JSContext::handleInterrupt()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
     if (interrupt_ || jitStackLimit == UINTPTR_MAX) {
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -994,28 +994,19 @@ struct JSRuntime : public js::MallocProv
     friend class js::gc::AutoTraceSession;
     friend class JS::AutoEnterCycleCollection;
 
   private:
     js::ActiveThreadData<js::RuntimeCaches> caches_;
   public:
     js::RuntimeCaches& caches() { return caches_.ref(); }
 
-    // When wasm traps or is interrupted, the signal handler records some data
-    // for unwinding purposes. Wasm code can't interrupt or trap reentrantly.
-    js::ActiveThreadData<
-        mozilla::MaybeOneOf<js::wasm::TrapData, js::wasm::InterruptData>
-    > wasmUnwindData;
-
-    js::wasm::TrapData& wasmTrapData() {
-        return wasmUnwindData.ref().ref<js::wasm::TrapData>();
-    }
-    js::wasm::InterruptData& wasmInterruptData() {
-        return wasmUnwindData.ref().ref<js::wasm::InterruptData>();
-    }
+    // When wasm traps, the signal handler records some data for unwinding
+    // purposes. Wasm code can't trap reentrantly.
+    js::ActiveThreadData<mozilla::Maybe<js::wasm::TrapData>> wasmTrapData;
 
   public:
 #if defined(NIGHTLY_BUILD)
     // Support for informing the embedding of any error thrown.
     // This mechanism is designed to let the embedding
     // log/report/fail in case certain errors are thrown
     // (e.g. SyntaxError, ReferenceError or TypeError
     // in critical code).
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1569,17 +1569,17 @@ jit::JitActivation::~JitActivation()
 
     // All reocvered value are taken from activation during the bailout.
     MOZ_ASSERT(ionRecovery_.empty());
 
     // The BailoutFrameInfo should have unregistered itself from the
     // JitActivations.
     MOZ_ASSERT(!bailoutData_);
 
-    MOZ_ASSERT(!isWasmInterrupted());
+    // Traps get handled immediately.
     MOZ_ASSERT(!isWasmTrapping());
 
     clearRematerializedFrames();
     js_delete(rematerializedFrames_);
 }
 
 void
 jit::JitActivation::setBailoutData(jit::BailoutFrameInfo* bailoutData)
@@ -1737,96 +1737,16 @@ jit::JitActivation::removeIonFrameRecove
 
 void
 jit::JitActivation::traceIonRecovery(JSTracer* trc)
 {
     for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end(); it++)
         it->trace(trc);
 }
 
-bool
-jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::RegisterState& state)
-{
-    // fp may be null when first entering wasm code from an interpreter entry
-    // stub.
-    if (!state.fp)
-        return false;
-
-    MOZ_ASSERT(state.pc);
-
-    // Execution can only be interrupted in function code. Afterwards, control
-    // flow does not reenter function code and thus there can be no
-    // interrupt-during-interrupt.
-
-    bool unwound;
-    wasm::UnwindState unwindState;
-    MOZ_ALWAYS_TRUE(wasm::StartUnwinding(state, &unwindState, &unwound));
-
-    void* pc = unwindState.pc;
-
-    if (unwound) {
-        // In the prologue/epilogue, FP might have been fixed up to the
-        // caller's FP, and the caller could be the jit entry. Ignore this
-        // interrupt, in this case, because FP points to a jit frame and not a
-        // wasm one.
-        if (!wasm::LookupCode(pc)->lookupFuncRange(pc))
-            return false;
-    }
-
-    cx_->runtime()->wasmUnwindData.ref().construct<wasm::InterruptData>(pc, state.pc);
-    setWasmExitFP(unwindState.fp);
-
-    MOZ_ASSERT(compartment() == unwindState.fp->tls->instance->compartment());
-    MOZ_ASSERT(isWasmInterrupted());
-    return true;
-}
-
-void
-jit::JitActivation::finishWasmInterrupt()
-{
-    MOZ_ASSERT(isWasmInterrupted());
-
-    cx_->runtime()->wasmUnwindData.ref().destroy();
-    packedExitFP_ = nullptr;
-}
-
-bool
-jit::JitActivation::isWasmInterrupted() const
-{
-    JSRuntime* rt = cx_->runtime();
-    if (!rt->wasmUnwindData.ref().constructed<wasm::InterruptData>())
-        return false;
-
-    Activation* act = cx_->activation();
-    while (act && !act->hasWasmExitFP())
-        act = act->prev();
-
-    if (act != this)
-        return false;
-
-    DebugOnly<const wasm::Frame*> fp = wasmExitFP();
-    DebugOnly<void*> unwindPC = rt->wasmInterruptData().unwindPC;
-    MOZ_ASSERT(fp->instance()->code().containsCodePC(unwindPC));
-    return true;
-}
-
-void*
-jit::JitActivation::wasmInterruptUnwindPC() const
-{
-    MOZ_ASSERT(isWasmInterrupted());
-    return cx_->runtime()->wasmInterruptData().unwindPC;
-}
-
-void*
-jit::JitActivation::wasmInterruptResumePC() const
-{
-    MOZ_ASSERT(isWasmInterrupted());
-    return cx_->runtime()->wasmInterruptData().resumePC;
-}
-
 void
 jit::JitActivation::startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset,
                                   const wasm::RegisterState& state)
 {
     bool unwound;
     wasm::UnwindState unwindState;
     MOZ_ALWAYS_TRUE(wasm::StartUnwinding(state, &unwindState, &unwound));
     MOZ_ASSERT(unwound == (trap == wasm::Trap::IndirectCallBadSig));
@@ -1837,61 +1757,61 @@ jit::JitActivation::startWasmTrap(wasm::
     const wasm::Code& code = fp->tls->instance->code();
     MOZ_RELEASE_ASSERT(&code == wasm::LookupCode(pc));
 
     // If the frame was unwound, the bytecodeOffset must be recovered from the
     // callsite so that it is accurate.
     if (unwound)
         bytecodeOffset = code.lookupCallSite(pc)->lineOrBytecode();
 
-    cx_->runtime()->wasmUnwindData.ref().construct<wasm::TrapData>(pc, trap, bytecodeOffset);
+    cx_->runtime()->wasmTrapData.ref().emplace(pc, trap, bytecodeOffset);
     setWasmExitFP(fp);
 }
 
 void
 jit::JitActivation::finishWasmTrap()
 {
     MOZ_ASSERT(isWasmTrapping());
 
-    cx_->runtime()->wasmUnwindData.ref().destroy();
+    cx_->runtime()->wasmTrapData.ref().reset();
     packedExitFP_ = nullptr;
 }
 
 bool
 jit::JitActivation::isWasmTrapping() const
 {
     JSRuntime* rt = cx_->runtime();
-    if (!rt->wasmUnwindData.ref().constructed<wasm::TrapData>())
+    if (!rt->wasmTrapData.ref())
         return false;
 
     Activation* act = cx_->activation();
     while (act && !act->hasWasmExitFP())
         act = act->prev();
 
     if (act != this)
         return false;
 
     DebugOnly<const wasm::Frame*> fp = wasmExitFP();
-    DebugOnly<void*> unwindPC = rt->wasmTrapData().pc;
+    DebugOnly<void*> unwindPC = rt->wasmTrapData->pc;
     MOZ_ASSERT(fp->instance()->code().containsCodePC(unwindPC));
     return true;
 }
 
 void*
 jit::JitActivation::wasmTrapPC() const
 {
     MOZ_ASSERT(isWasmTrapping());
-    return cx_->runtime()->wasmTrapData().pc;
+    return cx_->runtime()->wasmTrapData->pc;
 }
 
 uint32_t
 jit::JitActivation::wasmTrapBytecodeOffset() const
 {
     MOZ_ASSERT(isWasmTrapping());
-    return cx_->runtime()->wasmTrapData().bytecodeOffset;
+    return cx_->runtime()->wasmTrapData->bytecodeOffset;
 }
 
 InterpreterFrameIterator&
 InterpreterFrameIterator::operator++()
 {
     MOZ_ASSERT(!done());
     if (fp_ != activation_->entryFrame_) {
         pc_ = fp_->prevpc();
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1800,27 +1800,16 @@ class JitActivation : public Activation
     wasm::ExitReason wasmExitReason() const {
         MOZ_ASSERT(hasWasmExitFP());
         return wasm::ExitReason::Decode(encodedWasmExitReason_);
     }
     static size_t offsetOfEncodedWasmExitReason() {
         return offsetof(JitActivation, encodedWasmExitReason_);
     }
 
-    // Interrupts are started from the interrupt signal handler (or the ARM
-    // simulator) and cleared by WasmHandleExecutionInterrupt or WasmHandleThrow
-    // when the interrupt is handled.
-
-    // Returns true iff we've entered interrupted state.
-    bool startWasmInterrupt(const wasm::RegisterState& state);
-    void finishWasmInterrupt();
-    bool isWasmInterrupted() const;
-    void* wasmInterruptUnwindPC() const;
-    void* wasmInterruptResumePC() const;
-
     void startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset, const wasm::RegisterState& state);
     void finishWasmTrap();
     bool isWasmTrapping() const;
     void* wasmTrapPC() const;
     uint32_t wasmTrapBytecodeOffset() const;
 };
 
 // A filtering of the ActivationIterator to only stop at JitActivations.
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -3368,18 +3368,17 @@ class BaseCompiler final : public BaseCo
     }
 
     void moveImmF64(double d, RegF64 dest) {
         masm.loadConstantDouble(d, dest);
     }
 
     void addInterruptCheck()
     {
-        // Always use signals for interrupts with Asm.JS/Wasm
-        MOZ_RELEASE_ASSERT(HaveSignalHandlers());
+        // TODO
     }
 
     void jumpTable(const LabelVector& labels, Label* theTable) {
         // Flush constant pools to ensure that the table is never interrupted by
         // constant pool entries.
         masm.flush();
 
         masm.bind(theTable);
@@ -9486,18 +9485,16 @@ BaseCompiler::init()
         !SigPILL_.append(MIRType::Int64) || !SigPILL_.append(MIRType::Int64))
     {
         return false;
     }
 
     if (!fr.setupLocals(locals_, sig().args(), debugEnabled_, &localInfo_))
         return false;
 
-    addInterruptCheck();
-
     return true;
 }
 
 FuncOffsets
 BaseCompiler::finish()
 {
     MOZ_ASSERT(done(), "all bytes must be consumed");
     MOZ_ASSERT(func_.callSiteLineNums.length() == lastReadCallSite_);
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -64,38 +64,16 @@ extern MOZ_EXPORT int64_t
 static JitActivation*
 CallingActivation()
 {
     Activation* act = TlsContext.get()->activation();
     MOZ_ASSERT(act->asJit()->hasWasmExitFP());
     return act->asJit();
 }
 
-static void*
-WasmHandleExecutionInterrupt()
-{
-    JitActivation* activation = CallingActivation();
-    MOZ_ASSERT(activation->isWasmInterrupted());
-
-    if (!CheckForInterrupt(activation->cx())) {
-        // If CheckForInterrupt failed, it is time to interrupt execution.
-        // Returning nullptr to the caller will jump to the throw stub which
-        // will call HandleThrow. The JitActivation must stay in the
-        // interrupted state until then so that stack unwinding works in
-        // HandleThrow.
-        return nullptr;
-    }
-
-    // If CheckForInterrupt succeeded, then execution can proceed and the
-    // interrupt is over.
-    void* resumePC = activation->wasmInterruptResumePC();
-    activation->finishWasmInterrupt();
-    return resumePC;
-}
-
 static bool
 WasmHandleDebugTrap()
 {
     JitActivation* activation = CallingActivation();
     JSContext* cx = activation->cx();
     Frame* fp = activation->wasmExitFP();
     Instance* instance = fp->tls->instance;
     const Code& code = instance->code();
@@ -214,17 +192,16 @@ wasm::HandleThrow(JSContext* cx, WasmFra
             // Unexpected success from the handler onLeaveFrame -- raising error
             // since throw recovery is not yet implemented in the wasm baseline.
             // TODO properly handle success and resume wasm execution.
             JS_ReportErrorASCII(cx, "Unexpected success from onLeaveFrame");
         }
         frame->leave(cx);
     }
 
-    MOZ_ASSERT(!cx->activation()->asJit()->isWasmInterrupted(), "unwinding clears the interrupt");
     MOZ_ASSERT(!cx->activation()->asJit()->isWasmTrapping(), "unwinding clears the trapping state");
 
     return iter.unwoundAddressOfReturnAddress();
 }
 
 static void*
 WasmHandleThrow()
 {
@@ -282,17 +259,17 @@ WasmOldReportTrap(int32_t trapIndex)
     }
 
     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
 }
 
 static void
 WasmReportTrap()
 {
-    Trap trap = TlsContext.get()->runtime()->wasmTrapData().trap;
+    Trap trap = TlsContext.get()->runtime()->wasmTrapData->trap;
     WasmOldReportTrap(int32_t(trap));
 }
 
 static void
 WasmReportOutOfBounds()
 {
     JSContext* cx = TlsContext.get();
     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS);
@@ -506,19 +483,16 @@ FuncCast(F* funcPtr, ABIFunctionType abi
 #endif
     return pf;
 }
 
 void*
 wasm::AddressOf(SymbolicAddress imm, ABIFunctionType* abiType)
 {
     switch (imm) {
-      case SymbolicAddress::HandleExecutionInterrupt:
-        *abiType = Args_General0;
-        return FuncCast(WasmHandleExecutionInterrupt, *abiType);
       case SymbolicAddress::HandleDebugTrap:
         *abiType = Args_General0;
         return FuncCast(WasmHandleDebugTrap, *abiType);
       case SymbolicAddress::HandleThrow:
         *abiType = Args_General0;
         return FuncCast(WasmHandleThrow, *abiType);
       case SymbolicAddress::ReportTrap:
         *abiType = Args_General0;
@@ -687,17 +661,16 @@ wasm::AddressOf(SymbolicAddress imm, ABI
 }
 
 bool
 wasm::NeedsBuiltinThunk(SymbolicAddress sym)
 {
     // Some functions don't want to a thunk, because they already have one or
     // they don't have frame info.
     switch (sym) {
-      case SymbolicAddress::HandleExecutionInterrupt: // GenerateInterruptExit
       case SymbolicAddress::HandleDebugTrap:          // GenerateDebugTrapStub
       case SymbolicAddress::HandleThrow:              // GenerateThrowStub
       case SymbolicAddress::ReportTrap:               // GenerateTrapExit
       case SymbolicAddress::OldReportTrap:            // GenerateOldTrapExit
       case SymbolicAddress::ReportOutOfBounds:        // GenerateOutOfBoundsExit
       case SymbolicAddress::ReportUnalignedAccess:    // GenerateUnalignedExit
       case SymbolicAddress::CallImport_Void:          // GenerateImportInterpExit
       case SymbolicAddress::CallImport_I32:
@@ -869,18 +842,18 @@ PopulateTypedNatives(TypedNativeToFuncPt
 
 // ============================================================================
 // Process-wide builtin thunk set
 //
 // Thunks are inserted between wasm calls and the C++ callee and achieve two
 // things:
 //  - bridging the few differences between the internal wasm ABI and the external
 //    native ABI (viz. float returns on x86 and soft-fp ARM)
-//  - executing an exit prologue/epilogue which in turn allows any asynchronous
-//    interrupt to see the full stack up to the wasm operation that called out
+//  - executing an exit prologue/epilogue which in turn allows any profiling
+//    iterator to see the full stack up to the wasm operation that called out
 //
 // Thunks are created for two kinds of C++ callees, enumerated above:
 //  - SymbolicAddress: for statically compiled calls in the wasm module
 //  - Imported JS builtins: optimized calls to imports
 //
 // All thunks are created up front, lazily, when the first wasm module is
 // compiled in the process. Thunks are kept alive until the JS engine shuts down
 // in the process. No thunks are created at runtime after initialization. This
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -330,25 +330,23 @@ ModuleSegment::initialize(Tier tier,
                           UniqueCodeBytes codeBytes,
                           uint32_t codeLength,
                           const ShareableBytes& bytecode,
                           const LinkDataTier& linkData,
                           const Metadata& metadata,
                           const CodeRangeVector& codeRanges)
 {
     MOZ_ASSERT(bytes_ == nullptr);
-    MOZ_ASSERT(linkData.interruptOffset);
     MOZ_ASSERT(linkData.outOfBoundsOffset);
     MOZ_ASSERT(linkData.unalignedAccessOffset);
     MOZ_ASSERT(linkData.trapOffset);
 
     tier_ = tier;
     bytes_ = Move(codeBytes);
     length_ = codeLength;
-    interruptCode_ = bytes_.get() + linkData.interruptOffset;
     outOfBoundsCode_ = bytes_.get() + linkData.outOfBoundsOffset;
     unalignedAccessCode_ = bytes_.get() + linkData.unalignedAccessOffset;
     trapCode_ = bytes_.get() + linkData.trapOffset;
 
     if (!StaticallyLink(*this, linkData))
         return false;
 
     ExecutableAllocator::cacheFlush(bytes_.get(), RoundupCodeLength(codeLength));
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -139,19 +139,18 @@ class CodeSegment
 
 typedef UniquePtr<ModuleSegment> UniqueModuleSegment;
 typedef UniquePtr<const ModuleSegment> UniqueConstModuleSegment;
 
 class ModuleSegment : public CodeSegment
 {
     Tier            tier_;
 
-    // These are pointers into code for stubs used for asynchronous
-    // signal-handler control-flow transfer.
-    uint8_t*        interruptCode_;
+    // These are pointers into code for stubs used for signal-handler
+    // control-flow transfer.
     uint8_t*        outOfBoundsCode_;
     uint8_t*        unalignedAccessCode_;
     uint8_t*        trapCode_;
 
     bool initialize(Tier tier,
                     UniqueCodeBytes bytes,
                     uint32_t codeLength,
                     const ShareableBytes& bytecode,
@@ -168,17 +167,16 @@ class ModuleSegment : public CodeSegment
                                       const CodeRangeVector& codeRanges);
   public:
     ModuleSegment(const ModuleSegment&) = delete;
     void operator=(const ModuleSegment&) = delete;
 
     ModuleSegment()
       : CodeSegment(),
         tier_(Tier(-1)),
-        interruptCode_(nullptr),
         outOfBoundsCode_(nullptr),
         unalignedAccessCode_(nullptr),
         trapCode_(nullptr)
     {}
 
     static UniqueModuleSegment create(Tier tier,
                                       jit::MacroAssembler& masm,
                                       const ShareableBytes& bytecode,
@@ -190,17 +188,16 @@ class ModuleSegment : public CodeSegment
                                       const Bytes& unlinkedBytes,
                                       const ShareableBytes& bytecode,
                                       const LinkDataTier& linkData,
                                       const Metadata& metadata,
                                       const CodeRangeVector& codeRanges);
 
     Tier tier() const { return tier_; }
 
-    uint8_t* interruptCode() const { return interruptCode_; }
     uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
     uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
     uint8_t* trapCode() const { return trapCode_; }
 
     // Structured clone support:
 
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor, const LinkDataTier& linkDataTier) const;
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -56,37 +56,16 @@ WasmFrameIter::WasmFrameIter(JitActivati
         MOZ_ASSERT(codeRange_);
 
         lineOrBytecode_ = activation->wasmTrapBytecodeOffset();
 
         MOZ_ASSERT(!done());
         return;
     }
 
-    // When asynchronously interrupted, exitFP is set to the interrupted frame
-    // itself and so we do not want to skip it. Instead, we can recover the
-    // Code and CodeRange from the JitActivation, which are set when control
-    // flow was interrupted. There is no CallSite (b/c the interrupt was
-    // async), but this is fine because CallSite is only used for line number
-    // for which we can use the beginning of the function from the CodeRange
-    // instead.
-
-    if (activation->isWasmInterrupted()) {
-        code_ = &fp_->tls->instance->code();
-        MOZ_ASSERT(code_ == LookupCode(activation->wasmInterruptUnwindPC()));
-
-        codeRange_ = code_->lookupFuncRange(activation->wasmInterruptUnwindPC());
-        MOZ_ASSERT(codeRange_);
-
-        lineOrBytecode_ = codeRange_->funcLineOrBytecode();
-
-        MOZ_ASSERT(!done());
-        return;
-    }
-
     // Otherwise, execution exits wasm code via an exit stub which sets exitFP
     // to the exit stub's frame. Thus, in this case, we want to start iteration
     // at the caller of the exit frame, whose Code, CodeRange and CallSite are
     // indicated by the returnAddress of the exit stub's frame. If the caller
     // was Ion, we can just skip the wasm frames.
 
     popFrame();
     MOZ_ASSERT(!done() || unwoundIonCallerFP_);
@@ -106,24 +85,22 @@ WasmFrameIter::operator++()
     MOZ_ASSERT(!done());
 
     // When the iterator is set to unwind, each time the iterator pops a frame,
     // the JitActivation is updated so that the just-popped frame is no longer
     // visible. This is necessary since Debugger::onLeaveFrame is called before
     // popping each frame and, once onLeaveFrame is called for a given frame,
     // that frame must not be visible to subsequent stack iteration (or it
     // could be added as a "new" frame just as it becomes garbage).  When the
-    // frame is "interrupted", then exitFP is included in the callstack
-    // (otherwise, it is skipped, as explained above). So to unwind the
-    // innermost frame, we just clear the interrupt state.
+    // frame is trapping, then exitFP is included in the callstack (otherwise,
+    // it is skipped, as explained above). So to unwind the innermost frame, we
+    // just clear the trapping state.
 
     if (unwind_ == Unwind::True) {
-        if (activation_->isWasmInterrupted())
-            activation_->finishWasmInterrupt();
-        else if (activation_->isWasmTrapping())
+        if (activation_->isWasmTrapping())
             activation_->finishWasmTrap();
         activation_->setWasmExitFP(fp_);
     }
 
     popFrame();
 }
 
 void
@@ -727,20 +704,18 @@ ProfilingFrameIterator::initFromExitFP(c
     code_ = LookupCode(pc, &codeRange_);
     MOZ_ASSERT(code_);
     MOZ_ASSERT(codeRange_);
 
     // Since we don't have the pc for fp, start unwinding at the caller of fp.
     // This means that the innermost frame is skipped. This is fine because:
     //  - for import exit calls, the innermost frame is a thunk, so the first
     //    frame that shows up is the function calling the import;
-    //  - for Math and other builtin calls as well as interrupts, we note the
-    //    absence of an exit reason and inject a fake "builtin" frame; and
-    //  - for async interrupts, we just accept that we'll lose the innermost
-    //    frame.
+    //  - for Math and other builtin calls, we note the absence of an exit
+    //    reason and inject a fake "builtin" frame; and
     switch (codeRange_->kind()) {
       case CodeRange::InterpEntry:
         callerPC_ = nullptr;
         callerFP_ = nullptr;
         codeRange_ = nullptr;
         exitReason_ = ExitReason(ExitReason::Fixed::FakeInterpEntry);
         break;
       case CodeRange::JitEntry:
@@ -758,17 +733,16 @@ ProfilingFrameIterator::initFromExitFP(c
       case CodeRange::ImportInterpExit:
       case CodeRange::BuiltinThunk:
       case CodeRange::TrapExit:
       case CodeRange::OldTrapExit:
       case CodeRange::DebugTrap:
       case CodeRange::OutOfBoundsExit:
       case CodeRange::UnalignedExit:
       case CodeRange::Throw:
-      case CodeRange::Interrupt:
       case CodeRange::FarJumpIsland:
         MOZ_CRASH("Unexpected CodeRange kind");
     }
 
     MOZ_ASSERT(!done());
 }
 
 bool
@@ -956,21 +930,16 @@ js::wasm::StartUnwinding(const RegisterS
         if (intptr_t(fixedFP) == (FailFP & ~JitActivation::ExitFpWasmBit))
             return false;
         break;
       case CodeRange::Throw:
         // The throw stub executes a small number of instructions before popping
         // the entire activation. To simplify testing, we simply pretend throw
         // stubs have already popped the entire stack.
         return false;
-      case CodeRange::Interrupt:
-        // When the PC is in the async interrupt stub, the fp may be garbage and
-        // so we cannot blindly unwind it. Since the percent of time spent in
-        // the interrupt stub is extremely small, just ignore the stack.
-        return false;
     }
 
     unwindState->code = code;
     unwindState->codeRange = codeRange;
     unwindState->fp = fixedFP;
     unwindState->pc = fixedPC;
     return true;
 }
@@ -1086,30 +1055,28 @@ ProfilingFrameIterator::operator++()
         callerPC_ = callerFP_->returnAddress;
         AssertMatchesCallSite(callerPC_, callerFP_->callerFP);
         callerFP_ = callerFP_->callerFP;
         break;
       case CodeRange::InterpEntry:
         MOZ_CRASH("should have had null caller fp");
       case CodeRange::JitEntry:
         MOZ_CRASH("should have been guarded above");
-      case CodeRange::Interrupt:
       case CodeRange::Throw:
         MOZ_CRASH("code range doesn't have frame");
     }
 
     MOZ_ASSERT(!done());
 }
 
 static const char*
 ThunkedNativeToDescription(SymbolicAddress func)
 {
     MOZ_ASSERT(NeedsBuiltinThunk(func));
     switch (func) {
-      case SymbolicAddress::HandleExecutionInterrupt:
       case SymbolicAddress::HandleDebugTrap:
       case SymbolicAddress::HandleThrow:
       case SymbolicAddress::ReportTrap:
       case SymbolicAddress::OldReportTrap:
       case SymbolicAddress::ReportOutOfBounds:
       case SymbolicAddress::ReportUnalignedAccess:
       case SymbolicAddress::CallImport_Void:
       case SymbolicAddress::CallImport_I32:
@@ -1255,18 +1222,17 @@ ProfilingFrameIterator::label() const
       case CodeRange::BuiltinThunk:      return builtinNativeDescription;
       case CodeRange::ImportInterpExit:  return importInterpDescription;
       case CodeRange::TrapExit:          return trapDescription;
       case CodeRange::OldTrapExit:       return trapDescription;
       case CodeRange::DebugTrap:         return debugTrapDescription;
       case CodeRange::OutOfBoundsExit:   return "out-of-bounds stub (in wasm)";
       case CodeRange::UnalignedExit:     return "unaligned trap stub (in wasm)";
       case CodeRange::FarJumpIsland:     return "interstitial (in wasm)";
-      case CodeRange::Throw:             MOZ_FALLTHROUGH;
-      case CodeRange::Interrupt:         MOZ_CRASH("does not have a frame");
+      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)
 {
--- a/js/src/wasm/WasmFrameIter.h
+++ b/js/src/wasm/WasmFrameIter.h
@@ -44,22 +44,16 @@ struct FuncOffsets;
 struct CallableOffsets;
 
 // 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.
-//
-// The one exception is that this iterator may be called from the interrupt
-// callback which may be called asynchronously from asm.js code; in this case,
-// the backtrace may not be correct. That being said, we try our best printing
-// an informative message to the user and at least the name of the innermost
-// function stack frame.
 
 class WasmFrameIter
 {
   public:
     enum class Unwind { True, False };
 
   private:
     jit::JitActivation* activation_;
@@ -153,17 +147,17 @@ class ExitReason
     }
     SymbolicAddress symbolic() const {
         MOZ_ASSERT(!isFixed());
         return SymbolicAddress(payload_ >> 1);
     }
 };
 
 // Iterates over the frames of a single wasm JitActivation, given an
-// asynchronously-interrupted thread's state.
+// asynchronously-profiled thread's state.
 class ProfilingFrameIterator
 {
     const Code* code_;
     const CodeRange* codeRange_;
     Frame* callerFP_;
     void* callerPC_;
     void* stackAddress_;
     uint8_t* unwoundIonCallerFP_;
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -545,20 +545,16 @@ ModuleGenerator::noteCodeRange(uint32_t 
       case CodeRange::OutOfBoundsExit:
         MOZ_ASSERT(!linkDataTier_->outOfBoundsOffset);
         linkDataTier_->outOfBoundsOffset = codeRange.begin();
         break;
       case CodeRange::UnalignedExit:
         MOZ_ASSERT(!linkDataTier_->unalignedAccessOffset);
         linkDataTier_->unalignedAccessOffset = codeRange.begin();
         break;
-      case CodeRange::Interrupt:
-        MOZ_ASSERT(!linkDataTier_->interruptOffset);
-        linkDataTier_->interruptOffset = codeRange.begin();
-        break;
       case CodeRange::TrapExit:
         MOZ_ASSERT(!linkDataTier_->trapOffset);
         linkDataTier_->trapOffset = codeRange.begin();
         break;
       case CodeRange::Throw:
         // Jumped to by other stubs, so nothing to do.
         break;
       case CodeRange::FarJumpIsland:
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -246,18 +246,16 @@ class FunctionCompiler
             }
 
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(i), ins);
             if (!mirGen_.ensureBallast())
                 return false;
         }
 
-        addInterruptCheck();
-
         return true;
     }
 
     void finish()
     {
         mirGen().initWasmMaxStackArgBytes(maxStackArgBytes_);
 
         MOZ_ASSERT(callStack_.empty());
@@ -1030,18 +1028,17 @@ class FunctionCompiler
     {
         if (inDeadCode())
             return;
         curBlock_->add(MWasmStoreGlobalVar::New(alloc(), globalDataOffset, v, tlsPointer_));
     }
 
     void addInterruptCheck()
     {
-        // We rely on signal handlers for interrupts on Asm.JS/Wasm
-        MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
+        // TODO
     }
 
     MDefinition* extractSimdElement(unsigned lane, MDefinition* base, MIRType type, SimdSign sign)
     {
         if (inDeadCode())
             return nullptr;
 
         MOZ_ASSERT(IsSimdType(base->type()));
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -37,17 +37,16 @@ struct CompileArgs;
 //
 // LinkData is built incrementally by ModuleGenerator and then stored immutably
 // in Module. LinkData is distinct from Metadata in that LinkData is owned and
 // destroyed by the Module since it is not needed after instantiation; Metadata
 // is needed at runtime.
 
 struct LinkDataTierCacheablePod
 {
-    uint32_t interruptOffset;
     uint32_t outOfBoundsOffset;
     uint32_t unalignedAccessOffset;
     uint32_t trapOffset;
 
     LinkDataTierCacheablePod() { mozilla::PodZero(this); }
 };
 
 struct LinkDataTier : LinkDataTierCacheablePod
--- a/js/src/wasm/WasmProcess.cpp
+++ b/js/src/wasm/WasmProcess.cpp
@@ -45,25 +45,24 @@ class ProcessCodeSegmentMap
     // Since writes (insertions or removals) can happen on any background
     // thread at the same time, we need a lock here.
 
     Mutex mutatorsMutex_;
 
     CodeSegmentVector segments1_;
     CodeSegmentVector segments2_;
 
-    // Because of sampling/interruptions/stack iteration in general, the
-    // thread running wasm might need to know to which CodeSegment the
-    // current PC belongs, during a call to lookup(). A lookup is a
-    // read-only operation, and we don't want to take a lock then
+    // Because of profiling, the thread running wasm might need to know to which
+    // CodeSegment the current PC belongs, during a call to lookup(). A lookup
+    // is a read-only operation, and we don't want to take a lock then
     // (otherwise, we could have a deadlock situation if an async lookup
     // happened on a given thread that was holding mutatorsMutex_ while getting
-    // interrupted/sampled). Since the writer could be modifying the data that
-    // is getting looked up, the writer functions use spin-locks to know if
-    // there are any observers (i.e. calls to lookup()) of the atomic data.
+    // sampled). Since the writer could be modifying the data that is getting
+    // looked up, the writer functions use spin-locks to know if there are any
+    // observers (i.e. calls to lookup()) of the atomic data.
 
     Atomic<size_t> observers_;
 
     // Except during swapAndWait(), there are no lookup() observers of the
     // vector pointed to by mutableCodeSegments_
 
     CodeSegmentVector* mutableCodeSegments_;
     Atomic<const CodeSegmentVector*> readonlyCodeSegments_;
--- a/js/src/wasm/WasmProcess.h
+++ b/js/src/wasm/WasmProcess.h
@@ -25,17 +25,17 @@ namespace js {
 namespace wasm {
 
 class Code;
 class CodeRange;
 class CodeSegment;
 
 // These methods return the wasm::CodeSegment (resp. wasm::Code) containing
 // the given pc, if any exist in the process. These methods do not take a lock,
-// and thus are safe to use in a profiling or async interrupt context.
+// 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);
 
 // A bool member that can be used as a very fast lookup to know if there is any
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -26,30 +26,42 @@
 #include "jit/AtomicOperations.h"
 #include "jit/Disassembler.h"
 #include "vm/Runtime.h"
 #include "wasm/WasmBuiltins.h"
 #include "wasm/WasmInstance.h"
 
 #include "vm/ArrayBufferObject-inl.h"
 
+#if defined(XP_WIN)
+# include "util/Windows.h"
+#else
+# include <signal.h>
+# include <sys/mman.h>
+#endif
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+# include <sys/ucontext.h> // for ucontext_t, mcontext_t
+#endif
+
+#if defined(__x86_64__)
+# if defined(__DragonFly__)
+#  include <machine/npx.h> // for union savefpu
+# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
+       defined(__NetBSD__) || defined(__OpenBSD__)
+#  include <machine/fpu.h> // for struct savefpu/fxsave64
+# endif
+#endif
+
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 using JS::GenericNaN;
 using mozilla::DebugOnly;
-using mozilla::PodArrayZero;
-
-#if defined(ANDROID)
-# include <sys/system_properties.h>
-# if defined(MOZ_LINKER)
-extern "C" MFBT_API bool IsSignalHandlingBroken();
-# endif
-#endif
 
 // Crashing inside the signal handler can cause the handler to be recursively
 // invoked, eventually blowing the stack without actually showing a crash
 // report dialog via Breakpad. To guard against this we watch for such
 // recursion and fall through to the next handler immediately rather than
 // trying to handle it.
 
 static MOZ_THREAD_LOCAL(bool) sAlreadyInSignalHandler;
@@ -252,48 +264,30 @@ struct AutoSignalHandler
 #  define RLR_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_lr)
 #  define R31_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_sp)
 # endif
 # if defined(__FreeBSD__) && defined(__mips__)
 #  define EPC_sig(p) ((p)->uc_mcontext.mc_pc)
 #  define RFP_sig(p) ((p)->uc_mcontext.mc_regs[30])
 # endif
 #elif defined(XP_DARWIN)
-# define EIP_sig(p) ((p)->uc_mcontext->__ss.__eip)
-# define EBP_sig(p) ((p)->uc_mcontext->__ss.__ebp)
-# define ESP_sig(p) ((p)->uc_mcontext->__ss.__esp)
-# define RIP_sig(p) ((p)->uc_mcontext->__ss.__rip)
-# define RBP_sig(p) ((p)->uc_mcontext->__ss.__rbp)
-# define RSP_sig(p) ((p)->uc_mcontext->__ss.__rsp)
-# define R14_sig(p) ((p)->uc_mcontext->__ss.__lr)
-# define R15_sig(p) ((p)->uc_mcontext->__ss.__pc)
+# define EIP_sig(p) ((p)->thread.uts.ts32.__eip)
+# define EBP_sig(p) ((p)->thread.uts.ts32.__ebp)
+# define ESP_sig(p) ((p)->thread.uts.ts32.__esp)
+# define RIP_sig(p) ((p)->thread.__rip)
+# define RBP_sig(p) ((p)->thread.__rbp)
+# define RSP_sig(p) ((p)->thread.__rsp)
+# define R11_sig(p) ((p)->thread.__r[11])
+# define R13_sig(p) ((p)->thread.__sp)
+# define R14_sig(p) ((p)->thread.__lr)
+# define R15_sig(p) ((p)->thread.__pc)
 #else
 # error "Don't know how to read/write to the thread state via the mcontext_t."
 #endif
 
-#if defined(XP_WIN)
-# include "util/Windows.h"
-#else
-# include <signal.h>
-# include <sys/mman.h>
-#endif
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-# include <sys/ucontext.h> // for ucontext_t, mcontext_t
-#endif
-
-#if defined(__x86_64__)
-# if defined(__DragonFly__)
-#  include <machine/npx.h> // for union savefpu
-# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
-       defined(__NetBSD__) || defined(__OpenBSD__)
-#  include <machine/fpu.h> // for struct savefpu/fxsave64
-# endif
-#endif
-
 #if defined(ANDROID)
 // Not all versions of the Android NDK define ucontext_t or mcontext_t.
 // Detect this and provide custom but compatible definitions. Note that these
 // follow the GLibc naming convention to access register values from
 // mcontext_t.
 //
 // See: https://chromiumcodereview.appspot.com/10829122/
 // See: http://code.google.com/p/android/issues/detail?id=34784
@@ -364,48 +358,40 @@ typedef struct ucontext {
     mcontext_t uc_mcontext;
     // Other fields are not used by V8, don't define them here.
 } ucontext_t;
 enum { REG_EIP = 14 };
 #  endif  // defined(__i386__)
 # endif  // !defined(__BIONIC_HAVE_UCONTEXT_T)
 #endif // defined(ANDROID)
 
-#if !defined(XP_WIN)
-# define CONTEXT ucontext_t
-#endif
-
-// Define a context type for use in the emulator code. This is usually just
-// the same as CONTEXT, but on Mac we use a different structure since we call
-// into the emulator code from a Mach exception handler rather than a
-// sigaction-style signal handler.
 #if defined(XP_DARWIN)
 # if defined(__x86_64__)
 struct macos_x64_context {
     x86_thread_state64_t thread;
     x86_float_state64_t float_;
 };
-#  define EMULATOR_CONTEXT macos_x64_context
+#  define CONTEXT macos_x64_context
 # elif defined(__i386__)
 struct macos_x86_context {
     x86_thread_state_t thread;
     x86_float_state_t float_;
 };
-#  define EMULATOR_CONTEXT macos_x86_context
+#  define CONTEXT macos_x86_context
 # elif defined(__arm__)
 struct macos_arm_context {
     arm_thread_state_t thread;
     arm_neon_state_t float_;
 };
-#  define EMULATOR_CONTEXT macos_arm_context
+#  define CONTEXT macos_arm_context
 # else
 #  error Unsupported architecture
 # endif
-#else
-# define EMULATOR_CONTEXT CONTEXT
+#elif !defined(XP_WIN)
+# define CONTEXT ucontext_t
 #endif
 
 #if defined(_M_X64) || defined(__x86_64__)
 # define PC_sig(p) RIP_sig(p)
 # define FP_sig(p) RBP_sig(p)
 # define SP_sig(p) RSP_sig(p)
 #elif defined(_M_IX86) || defined(__i386__)
 # define PC_sig(p) EIP_sig(p)
@@ -423,144 +409,72 @@ struct macos_arm_context {
 # define LR_sig(p) RLR_sig(p)
 #elif defined(__mips__)
 # define PC_sig(p) EPC_sig(p)
 # define FP_sig(p) RFP_sig(p)
 # define SP_sig(p) RSP_sig(p)
 # define LR_sig(p) R31_sig(p)
 #endif
 
-#if defined(PC_sig) && defined(FP_sig) && defined(SP_sig)
-# define KNOWS_MACHINE_STATE
-#endif
-
 static uint8_t**
 ContextToPC(CONTEXT* context)
 {
-#ifdef KNOWS_MACHINE_STATE
+#ifdef PC_sig
     return reinterpret_cast<uint8_t**>(&PC_sig(context));
 #else
     MOZ_CRASH();
 #endif
 }
 
 static uint8_t*
 ContextToFP(CONTEXT* context)
 {
-#ifdef KNOWS_MACHINE_STATE
+#ifdef FP_sig
     return reinterpret_cast<uint8_t*>(FP_sig(context));
 #else
     MOZ_CRASH();
 #endif
 }
 
-#ifdef KNOWS_MACHINE_STATE
 static uint8_t*
 ContextToSP(CONTEXT* context)
 {
+#ifdef SP_sig
     return reinterpret_cast<uint8_t*>(SP_sig(context));
+#else
+    MOZ_CRASH();
+#endif
 }
 
-# if defined(__arm__) || defined(__aarch64__) || defined(__mips__)
+#if defined(__arm__) || defined(__aarch64__) || defined(__mips__)
 static uint8_t*
 ContextToLR(CONTEXT* context)
 {
+# ifdef LR_sig
     return reinterpret_cast<uint8_t*>(LR_sig(context));
-}
-# endif
-#endif // KNOWS_MACHINE_STATE
-
-#if defined(XP_DARWIN)
-
-static uint8_t**
-ContextToPC(EMULATOR_CONTEXT* context)
-{
-# if defined(__x86_64__)
-    static_assert(sizeof(context->thread.__rip) == sizeof(void*),
-                  "stored IP should be compile-time pointer-sized");
-    return reinterpret_cast<uint8_t**>(&context->thread.__rip);
-# elif defined(__i386__)
-    static_assert(sizeof(context->thread.uts.ts32.__eip) == sizeof(void*),
-                  "stored IP should be compile-time pointer-sized");
-    return reinterpret_cast<uint8_t**>(&context->thread.uts.ts32.__eip);
-# elif defined(__arm__)
-    static_assert(sizeof(context->thread.__pc) == sizeof(void*),
-                  "stored IP should be compile-time pointer-sized");
-    return reinterpret_cast<uint8_t**>(&context->thread.__pc);
 # else
-#  error Unsupported architecture
+    MOZ_CRASH();
 # endif
 }
-
-static uint8_t*
-ContextToFP(EMULATOR_CONTEXT* context)
-{
-# if defined(__x86_64__)
-    return (uint8_t*)context->thread.__rbp;
-# elif defined(__i386__)
-    return (uint8_t*)context->thread.uts.ts32.__ebp;
-# elif defined(__arm__)
-    return (uint8_t*)context->thread.__r[11];
-# else
-#  error Unsupported architecture
-# endif
-}
-
-# if defined(__arm__) || defined(__aarch64__)
-static uint8_t*
-ContextToLR(EMULATOR_CONTEXT* context)
-{
-    return (uint8_t*)context->thread.__lr;
-}
-# endif
-
-static uint8_t*
-ContextToSP(EMULATOR_CONTEXT* context)
-{
-# if defined(__x86_64__)
-    return (uint8_t*)context->thread.__rsp;
-# elif defined(__i386__)
-    return (uint8_t*)context->thread.uts.ts32.__esp;
-# elif defined(__arm__)
-    return (uint8_t*)context->thread.__sp;
-# else
-#  error Unsupported architecture
-# endif
-}
+#endif
 
 static JS::ProfilingFrameIterator::RegisterState
-ToRegisterState(EMULATOR_CONTEXT* context)
+ToRegisterState(CONTEXT* context)
 {
     JS::ProfilingFrameIterator::RegisterState state;
     state.fp = ContextToFP(context);
     state.pc = *ContextToPC(context);
     state.sp = ContextToSP(context);
-# if defined(__arm__) || defined(__aarch64__)
+#if defined(__arm__) || defined(__aarch64__) || defined(__mips__)
     state.lr = ContextToLR(context);
-# endif
+#else
+    state.lr = (void*)UINTPTR_MAX;
+#endif
     return state;
 }
-#endif // XP_DARWIN
-
-static JS::ProfilingFrameIterator::RegisterState
-ToRegisterState(CONTEXT* context)
-{
-#ifdef KNOWS_MACHINE_STATE
-    JS::ProfilingFrameIterator::RegisterState state;
-    state.fp = ContextToFP(context);
-    state.pc = *ContextToPC(context);
-    state.sp = ContextToSP(context);
-# if defined(__arm__) || defined(__aarch64__) || defined(__mips__)
-    state.lr = ContextToLR(context);
-# endif
-    return state;
-#else
-    MOZ_CRASH();
-#endif
-}
 
 #if defined(WASM_HUGE_MEMORY)
 MOZ_COLD static void
 SetFPRegToNaN(size_t size, void* fp_reg)
 {
     MOZ_RELEASE_ASSERT(size <= Simd128DataSize);
     memset(fp_reg, 0, Simd128DataSize);
     switch (size) {
@@ -648,17 +562,17 @@ AddressOfFPRegisterSlot(CONTEXT* context
       case X86Encoding::xmm14: return &XMM_sig(context, 14);
       case X86Encoding::xmm15: return &XMM_sig(context, 15);
       default: break;
     }
     MOZ_CRASH();
 }
 
 MOZ_COLD static void*
-AddressOfGPRegisterSlot(EMULATOR_CONTEXT* context, Registers::Code code)
+AddressOfGPRegisterSlot(CONTEXT* context, Registers::Code code)
 {
     switch (code) {
       case X86Encoding::rax: return &RAX_sig(context);
       case X86Encoding::rcx: return &RCX_sig(context);
       case X86Encoding::rdx: return &RDX_sig(context);
       case X86Encoding::rbx: return &RBX_sig(context);
       case X86Encoding::rsp: return &RSP_sig(context);
       case X86Encoding::rbp: return &RBP_sig(context);
@@ -673,17 +587,17 @@ AddressOfGPRegisterSlot(EMULATOR_CONTEXT
       case X86Encoding::r14: return &R14_sig(context);
       case X86Encoding::r15: return &R15_sig(context);
       default: break;
     }
     MOZ_CRASH();
 }
 # else
 MOZ_COLD static void*
-AddressOfFPRegisterSlot(EMULATOR_CONTEXT* context, FloatRegisters::Encoding encoding)
+AddressOfFPRegisterSlot(CONTEXT* context, FloatRegisters::Encoding encoding)
 {
     switch (encoding) {
       case X86Encoding::xmm0:  return &context->float_.__fpu_xmm0;
       case X86Encoding::xmm1:  return &context->float_.__fpu_xmm1;
       case X86Encoding::xmm2:  return &context->float_.__fpu_xmm2;
       case X86Encoding::xmm3:  return &context->float_.__fpu_xmm3;
       case X86Encoding::xmm4:  return &context->float_.__fpu_xmm4;
       case X86Encoding::xmm5:  return &context->float_.__fpu_xmm5;
@@ -698,17 +612,17 @@ AddressOfFPRegisterSlot(EMULATOR_CONTEXT
       case X86Encoding::xmm14: return &context->float_.__fpu_xmm14;
       case X86Encoding::xmm15: return &context->float_.__fpu_xmm15;
       default: break;
     }
     MOZ_CRASH();
 }
 
 MOZ_COLD static void*
-AddressOfGPRegisterSlot(EMULATOR_CONTEXT* context, Registers::Code code)
+AddressOfGPRegisterSlot(CONTEXT* context, Registers::Code code)
 {
     switch (code) {
       case X86Encoding::rax: return &context->thread.__rax;
       case X86Encoding::rcx: return &context->thread.__rcx;
       case X86Encoding::rdx: return &context->thread.__rdx;
       case X86Encoding::rbx: return &context->thread.__rbx;
       case X86Encoding::rsp: return &context->thread.__rsp;
       case X86Encoding::rbp: return &context->thread.__rbp;
@@ -724,69 +638,69 @@ AddressOfGPRegisterSlot(EMULATOR_CONTEXT
       case X86Encoding::r15: return &context->thread.__r15;
       default: break;
     }
     MOZ_CRASH();
 }
 # endif  // !XP_DARWIN
 #elif defined(JS_CODEGEN_ARM64)
 MOZ_COLD static void*
-AddressOfFPRegisterSlot(EMULATOR_CONTEXT* context, FloatRegisters::Encoding encoding)
+AddressOfFPRegisterSlot(CONTEXT* context, FloatRegisters::Encoding encoding)
 {
     MOZ_CRASH("NYI - asm.js not supported yet on this platform");
 }
 
 MOZ_COLD static void*
-AddressOfGPRegisterSlot(EMULATOR_CONTEXT* context, Registers::Code code)
+AddressOfGPRegisterSlot(CONTEXT* context, Registers::Code code)
 {
     MOZ_CRASH("NYI - asm.js not supported yet on this platform");
 }
 #endif
 
 MOZ_COLD static void
-SetRegisterToCoercedUndefined(EMULATOR_CONTEXT* context, size_t size,
+SetRegisterToCoercedUndefined(CONTEXT* context, size_t size,
                               const Disassembler::OtherOperand& value)
 {
     if (value.kind() == Disassembler::OtherOperand::FPR)
         SetFPRegToNaN(size, AddressOfFPRegisterSlot(context, value.fpr()));
     else
         SetGPRegToZero(AddressOfGPRegisterSlot(context, value.gpr()));
 }
 
 MOZ_COLD static void
-SetRegisterToLoadedValue(EMULATOR_CONTEXT* context, SharedMem<void*> addr, size_t size,
+SetRegisterToLoadedValue(CONTEXT* context, SharedMem<void*> addr, size_t size,
                          const Disassembler::OtherOperand& value)
 {
     if (value.kind() == Disassembler::OtherOperand::FPR)
         SetFPRegToLoadedValue(addr, size, AddressOfFPRegisterSlot(context, value.fpr()));
     else
         SetGPRegToLoadedValue(addr, size, AddressOfGPRegisterSlot(context, value.gpr()));
 }
 
 MOZ_COLD static void
-SetRegisterToLoadedValueSext32(EMULATOR_CONTEXT* context, SharedMem<void*> addr, size_t size,
+SetRegisterToLoadedValueSext32(CONTEXT* context, SharedMem<void*> addr, size_t size,
                                const Disassembler::OtherOperand& value)
 {
     SetGPRegToLoadedValueSext32(addr, size, AddressOfGPRegisterSlot(context, value.gpr()));
 }
 
 MOZ_COLD static void
-StoreValueFromRegister(EMULATOR_CONTEXT* context, SharedMem<void*> addr, size_t size,
+StoreValueFromRegister(CONTEXT* context, SharedMem<void*> addr, size_t size,
                        const Disassembler::OtherOperand& value)
 {
     if (value.kind() == Disassembler::OtherOperand::FPR)
         StoreValueFromFPReg(addr, size, AddressOfFPRegisterSlot(context, value.fpr()));
     else if (value.kind() == Disassembler::OtherOperand::GPR)
         StoreValueFromGPReg(addr, size, AddressOfGPRegisterSlot(context, value.gpr()));
     else
         StoreValueFromGPImm(addr, size, value.imm());
 }
 
 MOZ_COLD static uint8_t*
-ComputeAccessAddress(EMULATOR_CONTEXT* context, const Disassembler::ComplexAddress& address)
+ComputeAccessAddress(CONTEXT* context, const Disassembler::ComplexAddress& address)
 {
     MOZ_RELEASE_ASSERT(!address.isPCRelative(), "PC-relative addresses not supported yet");
 
     uintptr_t result = address.disp();
 
     if (address.hasBase()) {
         uintptr_t base;
         StoreValueFromGPReg(SharedMem<void*>::unshared(&base), sizeof(uintptr_t),
@@ -801,29 +715,29 @@ ComputeAccessAddress(EMULATOR_CONTEXT* c
         MOZ_ASSERT(address.scale() < 32, "address shift overflow");
         result += index * (uintptr_t(1) << address.scale());
     }
 
     return reinterpret_cast<uint8_t*>(result);
 }
 
 MOZ_COLD static void
-HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
+HandleMemoryAccess(CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
                    const ModuleSegment* segment, const Instance& instance, JitActivation* activation,
                    uint8_t** ppc)
 {
     MOZ_RELEASE_ASSERT(instance.code().containsCodePC(pc));
 
     const MemoryAccess* memoryAccess = instance.code().lookupMemoryAccess(pc);
     if (!memoryAccess) {
         // If there is no associated MemoryAccess for the faulting PC, this must be
         // experimental SIMD.js or Atomics. When these are converted to
         // non-experimental wasm features, this case, as well as outOfBoundsCode,
         // can be removed.
-        MOZ_ALWAYS_TRUE(activation->startWasmInterrupt(ToRegisterState(context)));
+        activation->startWasmTrap(wasm::Trap::OutOfBounds, 0, ToRegisterState(context));
         *ppc = segment->outOfBoundsCode();
         return;
     }
 
     MOZ_RELEASE_ASSERT(memoryAccess->insnOffset() == (pc - segment->base()));
 
     // On WASM_HUGE_MEMORY platforms, asm.js code may fault. asm.js does not
     // trap on fault and so has no trap out-of-line path. Instead, stores are
@@ -957,26 +871,26 @@ HandleMemoryAccess(EMULATOR_CONTEXT* con
     }
 
     *ppc = end;
 }
 
 #else // WASM_HUGE_MEMORY
 
 MOZ_COLD static void
-HandleMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
+HandleMemoryAccess(CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
                    const ModuleSegment* segment, const Instance& instance, JitActivation* activation,
                    uint8_t** ppc)
 {
     MOZ_RELEASE_ASSERT(instance.code().containsCodePC(pc));
 
     const MemoryAccess* memoryAccess = instance.code().lookupMemoryAccess(pc);
     if (!memoryAccess) {
         // See explanation in the WASM_HUGE_MEMORY HandleMemoryAccess.
-        MOZ_ALWAYS_TRUE(activation->startWasmInterrupt(ToRegisterState(context)));
+        activation->startWasmTrap(wasm::Trap::OutOfBounds, 0, ToRegisterState(context));
         *ppc = segment->outOfBoundsCode();
         return;
     }
 
     MOZ_RELEASE_ASSERT(memoryAccess->hasTrapOutOfLineCode());
     *ppc = memoryAccess->trapOutOfLineCode(segment->base());
 }
 
@@ -1014,43 +928,18 @@ HandleFault(PEXCEPTION_POINTERS exceptio
         return false;
 
     const ModuleSegment* moduleSegment = codeSegment->asModule();
 
     JitActivation* activation = TlsContext.get()->activation()->asJit();
     MOZ_ASSERT(activation);
 
     const Instance* instance = LookupFaultingInstance(*moduleSegment, pc, ContextToFP(context));
-    if (!instance) {
-        // On Windows, it is possible for InterruptRunningJitCode to execute
-        // between a faulting instruction and the handling of the fault due
-        // to InterruptRunningJitCode's use of SuspendThread. When this happens,
-        // after ResumeThread, the exception handler is called with pc equal to
-        // ModuleSegment.interrupt, which is logically wrong. The Right Thing would
-        // be for the OS to make fault-handling atomic (so that CONTEXT.pc was
-        // always the logically-faulting pc). Fortunately, we can detect this
-        // case and silence the exception ourselves (the exception will
-        // retrigger after the interrupt jumps back to resumePC).
-        return activation->isWasmInterrupted() &&
-               pc == moduleSegment->interruptCode() &&
-               moduleSegment->containsCodePC(activation->wasmInterruptResumePC());
-    }
-
-    // In the same race-with-interrupt situation above, it's *also* possible
-    // that the reported 'pc' is the pre-interrupt pc, not post-interrupt
-    // moduleSegment->interruptCode (this may be windows-version-specific). In
-    // this case, lookupTrap()/lookupMemoryAccess() will all succeed causing the
-    // pc to be redirected *again* (to a trap stub), leading to the interrupt
-    // stub never being called. Since the goal of the async interrupt is to break
-    // out iloops and trapping does just that, this is fine, we just clear the
-    // "interrupted" state.
-    if (activation->isWasmInterrupted()) {
-        MOZ_ASSERT(activation->wasmInterruptResumePC() == pc);
-        activation->finishWasmInterrupt();
-    }
+    if (!instance)
+        return false;
 
     if (record->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) {
         Trap trap;
         BytecodeOffset bytecode;
         if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode))
             return false;
 
         activation->startWasmTrap(trap, bytecode.offset, ToRegisterState(context));
@@ -1120,17 +1009,17 @@ struct ExceptionRequest
 
 static bool
 HandleMachException(JSContext* cx, const ExceptionRequest& request)
 {
     // Get the port of the JSContext's thread from the message.
     mach_port_t cxThread = request.body.thread.name;
 
     // Read out the JSRuntime thread's register state.
-    EMULATOR_CONTEXT context;
+    CONTEXT context;
 # if defined(__x86_64__)
     unsigned int thread_state_count = x86_THREAD_STATE64_COUNT;
     unsigned int float_state_count = x86_FLOAT_STATE64_COUNT;
     int thread_state = x86_THREAD_STATE64;
     int float_state = x86_FLOAT_STATE64;
 # elif defined(__i386__)
     unsigned int thread_state_count = x86_THREAD_STATE_COUNT;
     unsigned int float_state_count = x86_FLOAT_STATE_COUNT;
@@ -1441,17 +1330,17 @@ HandleFault(int signum, siginfo_t* info,
     }
 
 #ifdef JS_CODEGEN_ARM
     if (signum == SIGBUS) {
         // TODO: We may see a bus error for something that is an unaligned access that
         // partly overlaps the end of the heap.  In this case, it is an out-of-bounds
         // error and we should signal that properly, but to do so we must inspect
         // the operand of the failed access.
-        MOZ_ALWAYS_TRUE(activation->startWasmInterrupt(ToRegisterState(context)));
+        activation->startWasmTrap(wasm::Trap::UnalignedAccess, 0, ToRegisterState(context));
         *ppc = moduleSegment->unalignedAccessCode();
         return true;
     }
 #endif
 
     HandleMemoryAccess(context, pc, faultingAddress, moduleSegment, *instance, activation, ppc);
     return true;
 }
@@ -1490,240 +1379,91 @@ WasmFaultHandler(int signum, siginfo_t* 
         previousSignal->sa_sigaction(signum, info, context);
     else if (previousSignal->sa_handler == SIG_DFL || previousSignal->sa_handler == SIG_IGN)
         sigaction(signum, previousSignal, nullptr);
     else
         previousSignal->sa_handler(signum);
 }
 # endif // XP_WIN || XP_DARWIN || assume unix
 
-static void
-RedirectIonBackedgesToInterruptCheck(JSContext* cx)
-{
-    if (!cx->runtime()->hasJitRuntime())
-        return;
-    jit::JitRuntime* jitRuntime = cx->runtime()->jitRuntime();
-    Zone* zone = cx->zoneRaw();
-    if (zone && !zone->isAtomsZone()) {
-        // If the backedge list is being mutated, the pc must be in C++ code and
-        // thus not in a JIT iloop. We assume that the interrupt flag will be
-        // checked at least once before entering JIT code (if not, no big deal;
-        // the browser will just request another interrupt in a second).
-        if (!jitRuntime->preventBackedgePatching()) {
-            jit::JitZoneGroup* jzg = zone->group()->jitZoneGroup;
-            jzg->patchIonBackedges(cx, jit::JitZoneGroup::BackedgeInterruptCheck);
-        }
-    }
-}
-
-bool
-wasm::InInterruptibleCode(JSContext* cx, uint8_t* pc, const ModuleSegment** ms)
-{
-    // Only interrupt in function code so that the frame iterators have the
-    // invariant that resumePC always has a function CodeRange and we can't
-    // get into any weird interrupt-during-interrupt-stub cases.
-
-    if (!cx->compartment())
-        return false;
-
-    const CodeSegment* cs = LookupCodeSegment(pc);
-    if (!cs || !cs->isModule())
-        return false;
-
-    *ms = cs->asModule();
-    return !!(*ms)->code().lookupFuncRange(pc);
-}
-
-// The return value indicates whether the PC was changed, not whether there was
-// a failure.
-static bool
-RedirectJitCodeToInterruptCheck(JSContext* cx, CONTEXT* context)
-{
-    // Jitcode may only be modified on the runtime's active thread.
-    if (cx != cx->runtime()->activeContext())
-        return false;
-
-    // The faulting thread is suspended so we can access cx fields that can
-    // normally only be accessed by the cx's active thread.
-    AutoNoteSingleThreadedRegion anstr;
-
-    RedirectIonBackedgesToInterruptCheck(cx);
-
-#ifdef JS_SIMULATOR
-    uint8_t* pc = cx->simulator()->get_pc_as<uint8_t*>();
-#else
-    uint8_t* pc = *ContextToPC(context);
-#endif
-
-    const ModuleSegment* moduleSegment = nullptr;
-    if (!InInterruptibleCode(cx, pc, &moduleSegment))
-        return false;
-
-#ifdef JS_SIMULATOR
-    // The checks performed by the !JS_SIMULATOR path happen in
-    // Simulator::handleWasmInterrupt.
-    cx->simulator()->trigger_wasm_interrupt();
-#else
-    // Only probe cx->activation() after we know the pc is in wasm code. This
-    // way we don't depend on signal-safe update of cx->activation().
-    JitActivation* activation = cx->activation()->asJit();
-
-    // The out-of-bounds/unaligned trap paths which call startWasmInterrupt() go
-    // through function code, so test if already interrupted. These paths are
-    // temporary though, so this case can be removed later.
-    if (activation->isWasmInterrupted())
-        return false;
-
-    if (!activation->startWasmInterrupt(ToRegisterState(context)))
-        return false;
-
-    *ContextToPC(context) = moduleSegment->interruptCode();
-#endif
-
-    return true;
-}
-
-#if !defined(XP_WIN)
-// For the interrupt signal, pick a signal number that:
-//  - is not otherwise used by mozilla or standard libraries
-//  - defaults to nostop and noprint on gdb/lldb so that noone is bothered
-// SIGVTALRM a relative of SIGALRM, so intended for user code, but, unlike
-// SIGALRM, not used anywhere else in Mozilla.
-static const int sInterruptSignal = SIGVTALRM;
-
-static void
-JitInterruptHandler(int signum, siginfo_t* info, void* context)
-{
-    if (JSContext* cx = TlsContext.get()) {
-
-#if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
-        SimulatorProcess::ICacheCheckingDisableCount++;
-#endif
-
-        RedirectJitCodeToInterruptCheck(cx, (CONTEXT*)context);
-
-#if defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
-        SimulatorProcess::cacheInvalidatedBySignalHandler_ = true;
-        SimulatorProcess::ICacheCheckingDisableCount--;
-#endif
-
-        cx->finishHandlingJitInterrupt();
-    }
-}
+#if defined(ANDROID) && defined(MOZ_LINKER)
+extern "C" MFBT_API bool IsSignalHandlingBroken();
 #endif
 
 static bool sTriedInstallSignalHandlers = false;
 static bool sHaveSignalHandlers = false;
 
 static bool
 ProcessHasSignalHandlers()
 {
     // We assume that there are no races creating the first JSRuntime of the process.
     if (sTriedInstallSignalHandlers)
         return sHaveSignalHandlers;
     sTriedInstallSignalHandlers = true;
 
-#if defined(ANDROID)
-# if !defined(__aarch64__)
-    // Before Android 4.4 (SDK version 19), there is a bug
-    //   https://android-review.googlesource.com/#/c/52333
-    // in Bionic's pthread_join which causes pthread_join to return early when
-    // pthread_kill is used (on any thread). Nobody expects the pthread_cond_wait
-    // EINTRquisition.
-    char version_string[PROP_VALUE_MAX];
-    PodArrayZero(version_string);
-    if (__system_property_get("ro.build.version.sdk", version_string) > 0) {
-        if (atol(version_string) < 19)
-            return false;
-    }
-# endif
-# if defined(MOZ_LINKER)
+#if defined(ANDROID) && defined(MOZ_LINKER)
     // Signal handling is broken on some android systems.
     if (IsSignalHandlingBroken())
         return false;
-# endif
 #endif
 
-    // The interrupt handler allows the active thread to be paused from another
-    // thread (see InterruptRunningJitCode).
-#if defined(XP_WIN)
-    // Windows uses SuspendThread to stop the active thread from another thread.
-#else
-    struct sigaction interruptHandler;
-    interruptHandler.sa_flags = SA_SIGINFO;
-    interruptHandler.sa_sigaction = &JitInterruptHandler;
-    sigemptyset(&interruptHandler.sa_mask);
-    struct sigaction prev;
-    if (sigaction(sInterruptSignal, &interruptHandler, &prev))
-        MOZ_CRASH("unable to install interrupt handler");
-
-    // There shouldn't be any other handlers installed for sInterruptSignal. If
-    // there are, we could always forward, but we need to understand what we're
-    // doing to avoid problematic interference.
-    if ((prev.sa_flags & SA_SIGINFO && prev.sa_sigaction) ||
-        (prev.sa_handler != SIG_DFL && prev.sa_handler != SIG_IGN))
-    {
-        MOZ_CRASH("contention for interrupt signal");
-    }
-#endif // defined(XP_WIN)
-
     // Initalize ThreadLocal flag used by WasmFaultHandler
     sAlreadyInSignalHandler.infallibleInit();
 
     // Install a SIGSEGV handler to handle safely-out-of-bounds asm.js heap
     // access and/or unaligned accesses.
-# if defined(XP_WIN)
-#  if defined(MOZ_ASAN)
+#if defined(XP_WIN)
+# if defined(MOZ_ASAN)
     // Under ASan we need to let the ASan runtime's ShadowExceptionHandler stay
     // in the first handler position. This requires some coordination with
     // MemoryProtectionExceptionHandler::isDisabled().
     const bool firstHandler = false;
-#  else
+# else
     // Otherwise, WasmFaultHandler needs to go first, so that we can recover
     // from wasm faults and continue execution without triggering handlers
     // such as MemoryProtectionExceptionHandler that assume we are crashing.
     const bool firstHandler = true;
-#  endif
+# endif
     if (!AddVectoredExceptionHandler(firstHandler, WasmFaultHandler))
         return false;
-# elif defined(XP_DARWIN)
+#elif defined(XP_DARWIN)
     // OSX handles seg faults via the Mach exception handler above, so don't
     // install WasmFaultHandler.
-# else
+#else
     // SA_NODEFER allows us to reenter the signal handler if we crash while
     // handling the signal, and fall through to the Breakpad handler by testing
     // handlingSegFault.
 
     // Allow handling OOB with signals on all architectures
     struct sigaction faultHandler;
     faultHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
     faultHandler.sa_sigaction = WasmFaultHandler;
     sigemptyset(&faultHandler.sa_mask);
     if (sigaction(SIGSEGV, &faultHandler, &sPrevSEGVHandler))
         MOZ_CRASH("unable to install segv handler");
 
-#  if defined(JS_CODEGEN_ARM)
+# if defined(JS_CODEGEN_ARM)
     // On Arm Handle Unaligned Accesses
     struct sigaction busHandler;
     busHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
     busHandler.sa_sigaction = WasmFaultHandler;
     sigemptyset(&busHandler.sa_mask);
     if (sigaction(SIGBUS, &busHandler, &sPrevSIGBUSHandler))
         MOZ_CRASH("unable to install sigbus handler");
-#  endif
+# endif
 
     // Install a handler to handle the instructions that are emitted to implement
     // wasm traps.
     struct sigaction wasmTrapHandler;
     wasmTrapHandler.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK;
     wasmTrapHandler.sa_sigaction = WasmFaultHandler;
     sigemptyset(&wasmTrapHandler.sa_mask);
     if (sigaction(kWasmTrapSignal, &wasmTrapHandler, &sPrevWasmTrapHandler))
         MOZ_CRASH("unable to install wasm trap handler");
-# endif
+#endif
 
     sHaveSignalHandlers = true;
     return true;
 }
 
 bool
 wasm::EnsureSignalHandlers(JSContext* cx)
 {
@@ -1741,66 +1481,8 @@ wasm::EnsureSignalHandlers(JSContext* cx
 }
 
 bool
 wasm::HaveSignalHandlers()
 {
     MOZ_ASSERT(sTriedInstallSignalHandlers);
     return sHaveSignalHandlers;
 }
-
-// JSRuntime::requestInterrupt sets interrupt_ (which is checked frequently by
-// C++ code at every Baseline JIT loop backedge) and jitStackLimit_ (which is
-// checked at every Baseline and Ion JIT function prologue). The remaining
-// sources of potential iloops (Ion loop backedges and all wasm code) are
-// handled by this function:
-//  1. Ion loop backedges are patched to instead point to a stub that handles
-//     the interrupt;
-//  2. if the active thread's pc is inside wasm code, the pc is updated to point
-//     to a stub that handles the interrupt.
-void
-js::InterruptRunningJitCode(JSContext* cx)
-{
-    // If signal handlers weren't installed, then Ion and wasm emit normal
-    // interrupt checks and don't need asynchronous interruption.
-    if (!HaveSignalHandlers())
-        return;
-
-    // Do nothing if we're already handling an interrupt here, to avoid races
-    // below and in JitRuntime::patchIonBackedges.
-    if (!cx->startHandlingJitInterrupt())
-        return;
-
-    // If we are on context's thread, then: pc is not in wasm code (so nothing
-    // to do for wasm) and we can patch Ion backedges without any special
-    // synchronization.
-    if (cx == TlsContext.get()) {
-        RedirectIonBackedgesToInterruptCheck(cx);
-        cx->finishHandlingJitInterrupt();
-        return;
-    }
-
-    // We are not on the runtime's active thread, so to do 1 and 2 above, we need
-    // to halt the runtime's active thread first.
-#if defined(XP_WIN)
-    // On Windows, we can simply suspend the active thread and work directly on
-    // its context from this thread. SuspendThread can sporadically fail if the
-    // thread is in the middle of a syscall. Rather than retrying in a loop,
-    // just wait for the next request for interrupt.
-    HANDLE thread = (HANDLE)cx->threadNative();
-    if (SuspendThread(thread) != (DWORD)-1) {
-        CONTEXT context;
-        context.ContextFlags = CONTEXT_FULL;
-        if (GetThreadContext(thread, &context)) {
-            if (RedirectJitCodeToInterruptCheck(cx, &context))
-                SetThreadContext(thread, &context);
-        }
-        ResumeThread(thread);
-    }
-    cx->finishHandlingJitInterrupt();
-#else
-    // On Unix, we instead deliver an async signal to the active thread which
-    // halts the thread and callers our JitInterruptHandler (which has already
-    // been installed by EnsureSignalHandlersInstalled).
-    pthread_t thread = (pthread_t)cx->threadNative();
-    pthread_kill(thread, sInterruptSignal);
-#endif
-}
--- a/js/src/wasm/WasmSignalHandlers.h
+++ b/js/src/wasm/WasmSignalHandlers.h
@@ -25,41 +25,29 @@
 # include <mach/mach.h>
 #endif
 
 #include "js/TypeDecls.h"
 #include "threading/Thread.h"
 #include "wasm/WasmTypes.h"
 
 namespace js {
-
-// Force any currently-executing asm.js/ion code to call HandleExecutionInterrupt.
-extern void
-InterruptRunningJitCode(JSContext* cx);
-
 namespace wasm {
 
 // 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 interrupts or
-// asm.js/wasm out-of-bounds.
+// Return whether signals can be used in this process for asm.js/wasm
+// out-of-bounds.
 bool
 HaveSignalHandlers();
 
-class ModuleSegment;
-
-// Returns true if wasm code is on top of the activation stack (and fills out
-// the code segment outparam in this case), or false otherwise.
-bool
-InInterruptibleCode(JSContext* cx, uint8_t* pc, const ModuleSegment** ms);
-
 #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
@@ -74,33 +62,17 @@ class MachExceptionHandler
     MachExceptionHandler();
     ~MachExceptionHandler() { uninstall(); }
     mach_port_t port() const { return port_; }
     bool installed() const { return installed_; }
     bool install(JSContext* cx);
 };
 #endif
 
-// Typed wrappers encapsulating the data saved by the signal handler on async
-// interrupt or trap. On interrupt, the PC at which to resume is saved. On trap,
-// the bytecode offset to be reported in callstacks is saved.
-
-struct InterruptData
-{
-    // The pc to use for unwinding purposes which is kept consistent with fp at
-    // call boundaries.
-    void* unwindPC;
-
-    // The pc at which we should return if the interrupt doesn't stop execution.
-    void* resumePC;
-
-    InterruptData(void* unwindPC, void* resumePC)
-      : unwindPC(unwindPC), resumePC(resumePC)
-    {}
-};
+// On trap, the bytecode offset to be reported in callstacks is saved.
 
 struct TrapData
 {
     void* pc;
     Trap trap;
     uint32_t bytecodeOffset;
 
     TrapData(void* pc, Trap trap, uint32_t bytecodeOffset)
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -1125,18 +1125,16 @@ GenerateImportJitExit(MacroAssembler& ma
     masm.callJitNoProfiler(callee);
 
     // Note that there might be a GC thing in the JSReturnOperand now.
     // In all the code paths from here:
     // - either the value is unboxed because it was a primitive and we don't
     //   need to worry about rooting anymore.
     // - or the value needs to be rooted, but nothing can cause a GC between
     //   here and CoerceInPlace, which roots before coercing to a primitive.
-    //   In particular, this is true because wasm::InInterruptibleCode will
-    //   return false when PC is in the jit exit.
 
     // The JIT callee clobbers all registers, including WasmTlsReg and
     // FramePointer, so restore those here. During this sequence of
     // instructions, FP can't be trusted by the profiling frame iterator.
     offsets->untrustedFPStart = masm.currentOffset();
     AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
 
     masm.loadWasmTlsRegFromFrame();
@@ -1477,189 +1475,30 @@ static const LiveRegisterSet AllUserRegs
     FloatRegisterSet(FloatRegisters::AllDoubleMask));
 static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
 #else
 static const LiveRegisterSet AllRegsExceptSP(
     GeneralRegisterSet(Registers::AllMask & ~(uint32_t(1) << Registers::StackPointer)),
     FloatRegisterSet(FloatRegisters::AllMask));
 #endif
 
-// The async interrupt-callback exit is called from arbitrarily-interrupted wasm
-// code. It calls into the WasmHandleExecutionInterrupt to determine whether we must
-// really halt execution which can reenter the VM (e.g., to display the slow
-// script dialog). If execution is not interrupted, this stub must carefully
-// preserve *all* register state. If execution is interrupted, the entire
-// activation will be popped by the throw stub, so register state does not need
-// to be restored.
-static bool
-GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
-{
-    masm.haltingAlign(CodeAlignment);
-
-    offsets->begin = masm.currentOffset();
-
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-    // Be very careful here not to perturb the machine state before saving it
-    // to the stack. In particular, add/sub instructions may set conditions in
-    // the flags register.
-    masm.push(Imm32(0));            // space used as return address, updated below
-    masm.setFramePushed(0);         // set to 0 now so that framePushed is offset of return address
-    masm.PushFlags();               // after this we are safe to use sub
-    masm.PushRegsInMask(AllRegsExceptSP); // save all GP/FP registers (except SP)
-
-    // We know that StackPointer is word-aligned, but not necessarily
-    // stack-aligned, so we need to align it dynamically.
-    masm.moveStackPtrTo(ABINonVolatileReg);
-    masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
-    if (ShadowStackSpace)
-        masm.subFromStackPtr(Imm32(ShadowStackSpace));
-
-    // Make the call to C++, which preserves ABINonVolatileReg.
-    masm.assertStackAlignment(ABIStackAlignment);
-    masm.call(SymbolicAddress::HandleExecutionInterrupt);
-
-    // HandleExecutionInterrupt returns null if execution is interrupted and
-    // the resumption pc otherwise.
-    masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
-
-    // Restore the stack pointer then store resumePC into the stack slow that
-    // will be popped by the 'ret' below.
-    masm.moveToStackPtr(ABINonVolatileReg);
-    masm.storePtr(ReturnReg, Address(StackPointer, masm.framePushed()));
-
-    // Restore the machine state to before the interrupt. After popping flags,
-    // no instructions can be executed which set flags.
-    masm.PopRegsInMask(AllRegsExceptSP);
-    masm.PopFlags();
-
-    // Return to the resumePC stored into this stack slot above.
-    MOZ_ASSERT(masm.framePushed() == 0);
-    masm.ret();
-#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
-    // Reserve space to store resumePC and HeapReg.
-    masm.subFromStackPtr(Imm32(2 * sizeof(intptr_t)));
-    // Set to zero so we can use masm.framePushed() below.
-    masm.setFramePushed(0);
-
-    // Save all registers, except sp.
-    masm.PushRegsInMask(AllUserRegsExceptSP);
-
-    // Save the stack pointer and FCSR in a non-volatile registers.
-    masm.moveStackPtrTo(s0);
-    masm.as_cfc1(s1, Assembler::FCSR);
-
-    // Align the stack.
-    masm.ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
-
-    // Store HeapReg into the reserved space.
-    masm.storePtr(HeapReg, Address(s0, masm.framePushed() + sizeof(intptr_t)));
-
-# ifdef USES_O32_ABI
-    // MIPS ABI requires rewserving stack for registes $a0 to $a3.
-    masm.subFromStackPtr(Imm32(4 * sizeof(intptr_t)));
-# endif
-
-    masm.assertStackAlignment(ABIStackAlignment);
-    masm.call(SymbolicAddress::HandleExecutionInterrupt);
-
-    masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
-
-    // This will restore stack to the address before the call.
-    masm.moveToStackPtr(s0);
-
-    // Restore FCSR.
-    masm.as_ctc1(s1, Assembler::FCSR);
-
-    // Store resumePC into the reserved space.
-    masm.storePtr(ReturnReg, Address(s0, masm.framePushed()));
-
-    masm.PopRegsInMask(AllUserRegsExceptSP);
-
-    // Pop resumePC into PC. Clobber HeapReg to make the jump and restore it
-    // during jump delay slot.
-    masm.loadPtr(Address(StackPointer, 0), HeapReg);
-    // Reclaim the reserve space.
-    masm.addToStackPtr(Imm32(2 * sizeof(intptr_t)));
-    masm.as_jr(HeapReg);
-    masm.loadPtr(Address(StackPointer, -int32_t(sizeof(intptr_t))), HeapReg);
-#elif defined(JS_CODEGEN_ARM)
-    {
-        // Be careful not to clobber scratch registers before they are saved.
-        ScratchRegisterScope scratch(masm);
-        SecondScratchRegisterScope secondScratch(masm);
-
-        // Reserve a word to receive the return address.
-        masm.as_alu(StackPointer, StackPointer, Imm8(4), OpSub);
-
-        // Set framePushed to 0 now so that framePushed can be used later as the
-        // stack offset to the return-address space reserved above.
-        masm.setFramePushed(0);
-
-        // Save all GP/FP registers (except PC and SP).
-        masm.PushRegsInMask(AllRegsExceptPCSP);
-    }
-
-    // Save SP, APSR and FPSCR in non-volatile registers.
-    masm.as_mrs(r4);
-    masm.as_vmrs(r5);
-    masm.mov(sp, r6);
-
-    // We know that StackPointer is word-aligned, but not necessarily
-    // stack-aligned, so we need to align it dynamically.
-    masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
-
-    // Make the call to C++, which preserves the non-volatile registers.
-    masm.assertStackAlignment(ABIStackAlignment);
-    masm.call(SymbolicAddress::HandleExecutionInterrupt);
-
-    // HandleExecutionInterrupt returns null if execution is interrupted and
-    // the resumption pc otherwise.
-    masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
-
-    // Restore the stack pointer then store resumePC into the stack slot that
-    // will be popped by the 'ret' below.
-    masm.mov(r6, sp);
-    masm.storePtr(ReturnReg, Address(sp, masm.framePushed()));
-
-    // Restore the machine state to before the interrupt. After popping flags,
-    // no instructions can be executed which set flags.
-    masm.as_vmsr(r5);
-    masm.as_msr(r4);
-    masm.PopRegsInMask(AllRegsExceptPCSP);
-
-    // Return to the resumePC stored into this stack slot above.
-    MOZ_ASSERT(masm.framePushed() == 0);
-    masm.ret();
-#elif defined(JS_CODEGEN_ARM64)
-    MOZ_CRASH();
-#elif defined (JS_CODEGEN_NONE)
-    MOZ_CRASH();
-#else
-# error "Unknown architecture!"
-#endif
-
-    return FinishOffsets(masm, offsets);
-}
-
 // Generate a stub that restores the stack pointer to what it was on entry to
 // the wasm activation, sets the return register to 'false' and then executes a
 // return which will return from this wasm activation to the caller. This stub
-// should only be called after the caller has reported an error (or, in the case
-// of the interrupt stub, intends to interrupt execution).
+// should only be called after the caller has reported an error.
 static bool
 GenerateThrowStub(MacroAssembler& masm, Label* throwLabel, Offsets* offsets)
 {
     masm.haltingAlign(CodeAlignment);
 
     masm.bind(throwLabel);
 
     offsets->begin = masm.currentOffset();
 
-    // The throw stub can be jumped to from an async interrupt that is halting
-    // execution. Thus the stack pointer can be unaligned and we must align it
+    // Conservatively, the stack pointer can be unaligned and we must align it
     // dynamically.
     masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
     if (ShadowStackSpace)
         masm.subFromStackPtr(Imm32(ShadowStackSpace));
 
     // WasmHandleThrow unwinds JitActivation::wasmExitFP() and returns the
     // address of the return address on the stack this stub should return to.
     // Set the FramePointer to a magic value to indicate a return by throw.
@@ -1830,28 +1669,21 @@ wasm::GenerateStubs(const ModuleEnvironm
     if (!code->codeRanges.emplaceBack(CodeRange::UnalignedExit, offsets))
         return false;
 
     if (!GenerateTrapExit(masm, &throwLabel, &offsets))
         return false;
     if (!code->codeRanges.emplaceBack(CodeRange::TrapExit, offsets))
         return false;
 
-    if (!GenerateInterruptExit(masm, &throwLabel, &offsets))
-        return false;
-    if (!code->codeRanges.emplaceBack(CodeRange::Interrupt, offsets))
+    CallableOffsets callableOffsets;
+    if (!GenerateDebugTrapStub(masm, &throwLabel, &callableOffsets))
         return false;
-
-    {
-        CallableOffsets offsets;
-        if (!GenerateDebugTrapStub(masm, &throwLabel, &offsets))
-            return false;
-        if (!code->codeRanges.emplaceBack(CodeRange::DebugTrap, offsets))
-            return false;
-    }
+    if (!code->codeRanges.emplaceBack(CodeRange::DebugTrap, callableOffsets))
+        return false;
 
     if (!GenerateThrowStub(masm, &throwLabel, &offsets))
         return false;
     if (!code->codeRanges.emplaceBack(CodeRange::Throw, offsets))
         return false;
 
     masm.finish();
     if (masm.oom())
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -779,17 +779,16 @@ CodeRange::CodeRange(Kind kind, Offsets 
     PodZero(&u);
 #ifdef DEBUG
     switch (kind_) {
       case FarJumpIsland:
       case OutOfBoundsExit:
       case UnalignedExit:
       case TrapExit:
       case Throw:
-      case Interrupt:
         break;
       default:
         MOZ_CRASH("should use more specific constructor");
     }
 #endif
 }
 
 CodeRange::CodeRange(Kind kind, uint32_t funcIndex, Offsets offsets)
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1059,17 +1059,16 @@ class CodeRange
         BuiltinThunk,      // fast-path calling from wasm into a C++ native
         TrapExit,          // calls C++ to report and jumps to throw stub
         OldTrapExit,       // calls C++ to report and jumps to throw stub
         DebugTrap,         // calls C++ to handle debug event
         FarJumpIsland,     // inserted to connect otherwise out-of-range insns
         OutOfBoundsExit,   // stub jumped to by non-standard asm.js SIMD/Atomics
         UnalignedExit,     // stub jumped to by wasm Atomics and non-standard
                            // ARM unaligned trap
-        Interrupt,         // stub executes asynchronously to interrupt wasm
         Throw              // special stack-unwinding stub jumped to by other stubs
     };
 
   private:
     // All fields are treated as cacheable POD:
     uint32_t begin_;
     uint32_t ret_;
     uint32_t end_;
@@ -1368,17 +1367,16 @@ enum class SymbolicAddress
     TruncD,
     TruncF,
     NearbyIntD,
     NearbyIntF,
     ExpD,
     LogD,
     PowD,
     ATan2D,
-    HandleExecutionInterrupt,
     HandleDebugTrap,
     HandleThrow,
     ReportTrap,
     OldReportTrap,
     ReportOutOfBounds,
     ReportUnalignedAccess,
     ReportInt64JSCall,
     CallImport_Void,