Bug 1276029 - Baldr: use process-wide signal-handling-support query (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Fri, 15 Jul 2016 12:26:40 -0500
changeset 305159 606e6f6aebe26585dd35c400ce72c3032b5e3378
parent 305158 c40efa3603e05cb5a781c4ef74726363e5c0c1e2
child 305160 09461dda5ac8cf17b9504d4997fc851c172fb885
push id20029
push usercbook@mozilla.com
push dateSun, 17 Jul 2016 08:10:51 +0000
treeherderfx-team@ce145f8f4c4a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1276029
milestone50.0a1
Bug 1276029 - Baldr: use process-wide signal-handling-support query (r=bbouvier) MozReview-Commit-ID: 3bS2f00Xcu
js/src/asmjs/AsmJS.cpp
js/src/asmjs/WasmCompile.cpp
js/src/asmjs/WasmJS.cpp
js/src/asmjs/WasmSignalHandlers.cpp
js/src/asmjs/WasmSignalHandlers.h
js/src/asmjs/WasmTypes.cpp
js/src/asmjs/WasmTypes.h
js/src/builtin/TestingFunctions.cpp
js/src/jit-test/tests/asm.js/testHeapAccess.js
js/src/jit-test/tests/asm.js/testJumpRange.js
js/src/jit-test/tests/asm.js/testTimeout-deactivate-reactivate-signals.js
js/src/jit-test/tests/asm.js/testTimeout1-nosignals.js
js/src/jit-test/tests/asm.js/testTimeout2-nosignals.js
js/src/jit-test/tests/asm.js/testTimeout3-nosignals.js
js/src/jit-test/tests/asm.js/testTimeout4-nosignals.js
js/src/jit-test/tests/ion/iloop-nosignaling.js
js/src/jit-test/tests/wasm/signals-enabled.js
js/src/jit/CompileWrappers.cpp
js/src/jit/CompileWrappers.h
js/src/jit/Lowering.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -8383,17 +8383,17 @@ LookupAsmJSModuleInCache(ExclusiveContex
     asmJSMetadata->scriptSource.reset(parser.ss);
 
     bool atEnd = cursor == entry.memory + entry.serializedSize;
     MOZ_ASSERT(atEnd, "Corrupt cache file");
     if (!atEnd)
         return true;
 
     Assumptions assumptions;
-    if (!assumptions.init(SignalUsage(cx), cx->buildIdOp()))
+    if (!assumptions.init(cx->buildIdOp()))
         return true;
 
     if (assumptions != (*module)->metadata().assumptions)
         return true;
 
     if (!parser.tokenStream.advance(asmJSMetadata->srcEndBeforeCurly()))
         return false;
 
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -1384,17 +1384,17 @@ DecodeUnknownSections(Decoder& d)
 
     return true;
 }
 
 bool
 CompileArgs::init(ExclusiveContext* cx)
 {
     alwaysBaseline = cx->options().wasmAlwaysBaseline();
-    if (!assumptions.init(SignalUsage(cx), cx->buildIdOp())) {
+    if (!assumptions.init(cx->buildIdOp())) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 SharedModule
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -562,17 +562,17 @@ WasmMemoryObject::construct(JSContext* c
         return false;
 
     if (initialDbl < 0 || initialDbl > INT32_MAX / PageSize) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, "Memory", "initial");
         return false;
     }
 
     uint32_t bytes = uint32_t(initialDbl) * PageSize;
-    bool signalsForOOB = SignalUsage(cx).forOOB;
+    bool signalsForOOB = SignalUsage().forOOB;
     RootedArrayBufferObject buffer(cx, ArrayBufferObject::createForWasm(cx, bytes, signalsForOOB));
     if (!buffer)
         return false;
 
     RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory).toObject());
     RootedWasmMemoryObject memoryObj(cx, WasmMemoryObject::create(cx, buffer, proto));
     if (!memoryObj)
         return false;
--- a/js/src/asmjs/WasmSignalHandlers.cpp
+++ b/js/src/asmjs/WasmSignalHandlers.cpp
@@ -1256,34 +1256,31 @@ JitInterruptHandler(int signum, siginfo_
 {
     if (JSRuntime* rt = RuntimeForCurrentThread()) {
         RedirectJitCodeToInterruptCheck(rt, (CONTEXT*)context);
         rt->finishHandlingJitInterrupt();
     }
 }
 #endif
 
-bool
-wasm::EnsureSignalHandlersInstalled(JSRuntime* rt)
+static bool
+ProcessHasSignalHandlers()
 {
-#if defined(XP_DARWIN) && defined(ASMJS_MAY_USE_SIGNAL_HANDLERS)
-    // On OSX, each JSRuntime gets its own handler thread.
-    if (!rt->wasmMachExceptionHandler.installed() && !rt->wasmMachExceptionHandler.install(rt))
-        return false;
-#endif
-
-    // All the rest of the handlers are process-wide and thus must only be
-    // installed once. We assume that there are no races creating the first
-    // JSRuntime of the process.
+    // We assume that there are no races creating the first JSRuntime of the process.
     static bool sTried = false;
     static bool sResult = false;
     if (sTried)
         return sResult;
     sTried = true;
 
+    // Developers might want to forcibly disable signals to avoid seeing
+    // spurious SIGSEGVs in the debugger.
+    if (getenv("JS_DISABLE_SLOW_SCRIPT_SIGNALS") || getenv("JS_NO_SIGNALS"))
+        return false;
+
 #if defined(ANDROID)
     // 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);
@@ -1352,31 +1349,64 @@ wasm::EnsureSignalHandlersInstalled(JSRu
 #  endif
 # endif
 #endif // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS)
 
     sResult = true;
     return true;
 }
 
+bool
+wasm::EnsureSignalHandlers(JSRuntime* rt)
+{
+    // Nothing to do if the platform doesn't support it.
+    if (!ProcessHasSignalHandlers())
+        return true;
+
+#if defined(XP_DARWIN) && defined(ASMJS_MAY_USE_SIGNAL_HANDLERS)
+    // On OSX, each JSRuntime gets its own handler thread.
+    if (!rt->wasmMachExceptionHandler.installed() && !rt->wasmMachExceptionHandler.install(rt))
+        return false;
+#endif
+
+    return true;
+}
+
+static bool sHandlersSuppressedForTesting = false;
+
+bool
+wasm::HaveSignalHandlers()
+{
+    if (!ProcessHasSignalHandlers())
+        return false;
+
+    return !sHandlersSuppressedForTesting;
+}
+
+void
+wasm::SuppressSignalHandlersForTesting(bool suppress)
+{
+    sHandlersSuppressedForTesting = suppress;
+}
+
 // 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 main thread's pc is inside wasm code, the pc is updated to point
 //     to a stub that handles the interrupt.
 void
 js::InterruptRunningJitCode(JSRuntime* rt)
 {
     // If signal handlers weren't installed, then Ion and wasm emit normal
     // interrupt checks and don't need asynchronous interruption.
-    if (!rt->canUseSignalHandlers())
+    if (!HaveSignalHandlers())
         return;
 
     // Do nothing if we're already handling an interrupt here, to avoid races
     // below and in JitRuntime::patchIonBackedges.
     if (!rt->startHandlingJitInterrupt())
         return;
 
     // If we are on runtime's main thread, then: pc is not in wasm code (so
--- a/js/src/asmjs/WasmSignalHandlers.h
+++ b/js/src/asmjs/WasmSignalHandlers.h
@@ -32,22 +32,31 @@ struct JSRuntime;
 namespace js {
 
 // Force any currently-executing asm.js/ion code to call HandleExecutionInterrupt.
 extern void
 InterruptRunningJitCode(JSRuntime* rt);
 
 namespace wasm {
 
-// Set up any signal/exception handlers needed to execute code in the given
-// runtime. Return whether runtime can:
-//  - rely on fault handler support for avoiding asm.js heap bounds checks
-//  - rely on InterruptRunningJitCode to halt running Ion/asm.js from any thread
+// 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
-EnsureSignalHandlersInstalled(JSRuntime* rt);
+EnsureSignalHandlers(JSRuntime* rt);
+
+// Return whether signals can be used in this process for interrupts or, ifdef
+// ASMJS_MAY_USE_SIGNAL_HANDLERS, asm.js/wasm out-of-bounds. This value can
+// change over time solely due to DisableSignalHandlersForTesting.
+bool
+HaveSignalHandlers();
+
+// Artificially suppress signal handler support, for testing purposes.
+void
+SuppressSignalHandlersForTesting(bool suppress);
 
 #if defined(XP_DARWIN) && defined(ASMJS_MAY_USE_SIGNAL_HANDLERS)
 // 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 JSRuntime (upon the first use of asm.js in the JSRuntime). This thread
 // and related resources are owned by AsmJSMachExceptionHandler which is owned
 // by JSRuntime.
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -20,16 +20,17 @@
 
 #include "fdlibm.h"
 
 #include "jslibmath.h"
 #include "jsmath.h"
 
 #include "asmjs/WasmInstance.h"
 #include "asmjs/WasmSerialize.h"
+#include "asmjs/WasmSignalHandlers.h"
 #include "jit/MacroAssembler.h"
 #include "js/Conversions.h"
 #include "vm/Interpreter.h"
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::jit;
@@ -267,28 +268,28 @@ wasm::AddressOf(SymbolicAddress imm, Exc
         return FuncCast(ecmaAtan2, Args_Double_DoubleDouble);
       case SymbolicAddress::Limit:
         break;
     }
 
     MOZ_CRASH("Bad SymbolicAddress");
 }
 
-SignalUsage::SignalUsage(ExclusiveContext* cx)
+SignalUsage::SignalUsage()
   :
 #ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
     // Signal-handling is only used to eliminate bounds checks when the OS page
     // size is an even divisor of the WebAssembly page size.
-    forOOB(cx->canUseSignalHandlers() &&
+    forOOB(HaveSignalHandlers() &&
            gc::SystemPageSize() <= PageSize &&
            PageSize % gc::SystemPageSize() == 0),
 #else
     forOOB(false),
 #endif
-    forInterrupt(cx->canUseSignalHandlers())
+    forInterrupt(HaveSignalHandlers())
 {}
 
 bool
 SignalUsage::operator==(SignalUsage rhs) const
 {
     return forOOB == rhs.forOOB && forInterrupt == rhs.forInterrupt;
 }
 
@@ -325,20 +326,18 @@ GetCPUID(uint32_t* cpuId)
     *cpuId = MIPS64 | (jit::GetMIPSFlags() << ARCH_BITS);
     return true;
 #else
     return false;
 #endif
 }
 
 MOZ_MUST_USE bool
-Assumptions::init(SignalUsage usesSignal, JS::BuildIdOp buildIdOp)
+Assumptions::init(JS::BuildIdOp buildIdOp)
 {
-    this->usesSignal = usesSignal;
-
     if (!GetCPUID(&cpuId))
         return false;
 
     if (!buildIdOp || !buildIdOp(&buildId))
         return false;
 
     return true;
 }
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -796,35 +796,34 @@ typedef EnumeratedArray<JumpTarget, Jump
 // not to use signal handlers for different purposes.
 
 struct SignalUsage
 {
     // NB: these fields are serialized as a POD in Assumptions.
     bool forOOB;
     bool forInterrupt;
 
-    SignalUsage() = default;
-    explicit SignalUsage(ExclusiveContext* cx);
+    SignalUsage();
     bool operator==(SignalUsage rhs) const;
     bool operator!=(SignalUsage rhs) const { return !(*this == rhs); }
 };
 
 // Assumptions captures ambient state that must be the same when compiling and
 // deserializing a module for the compiled code to be valid. If it's not, then
 // the module must be recompiled from scratch.
 
 struct Assumptions
 {
     SignalUsage           usesSignal;
     uint32_t              cpuId;
     JS::BuildIdCharVector buildId;
     bool                  newFormat;
 
-    Assumptions() : cpuId(0), newFormat(false) {}
-    MOZ_MUST_USE bool init(SignalUsage usesSignal, JS::BuildIdOp buildIdOp);
+    Assumptions() : usesSignal(), cpuId(0), newFormat(false) {}
+    MOZ_MUST_USE bool init(JS::BuildIdOp buildIdOp);
 
     bool operator==(const Assumptions& rhs) const;
     bool operator!=(const Assumptions& rhs) const { return !(*this == rhs); }
 
     WASM_DECLARE_SERIALIZABLE(Assumptions)
 };
 
 // A Module can either be asm.js or wasm.
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -18,16 +18,17 @@
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
 #include "asmjs/AsmJS.h"
 #include "asmjs/WasmBinaryToExperimentalText.h"
 #include "asmjs/WasmBinaryToText.h"
 #include "asmjs/WasmJS.h"
+#include "asmjs/WasmSignalHandlers.h"
 #include "asmjs/WasmTextToBinary.h"
 #include "builtin/Promise.h"
 #include "builtin/SelfHostingDefines.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitFrameIterator.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
@@ -510,17 +511,31 @@ WasmIsSupported(JSContext* cx, unsigned 
     args.rval().setBoolean(wasm::HasCompilerSupport(cx) && cx->options().wasm());
     return true;
 }
 
 static bool
 WasmUsesSignalForOOB(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setBoolean(wasm::SignalUsage(cx).forOOB);
+    args.rval().setBoolean(wasm::SignalUsage().forOOB);
+    return true;
+}
+
+static bool
+SuppressSignalHandlers(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!args.requireAtLeast(cx, "suppressSignalHandlers", 1))
+        return false;
+
+    wasm::SuppressSignalHandlersForTesting(ToBoolean(args[0]));
+
+    args.rval().setUndefined();
     return true;
 }
 
 static bool
 WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject callee(cx, &args.callee());
@@ -3781,16 +3796,21 @@ gc::ZealModeHelpText),
     JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
 "wasmIsSupported()",
 "  Returns a boolean indicating whether WebAssembly is supported on the current device."),
 
     JS_FN_HELP("wasmUsesSignalForOOB", WasmUsesSignalForOOB, 0, 0,
 "wasmUsesSignalForOOB()",
 "  Return whether wasm and asm.js use a signal handler for detecting out-of-bounds."),
 
+    JS_FN_HELP("suppressSignalHandlers", SuppressSignalHandlers, 1, 0,
+"suppressSignalHandlers(suppress)",
+"  This function allows artificially suppressing signal handler support, even if the underlying "
+"  platform supports it."),
+
     JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
 "wasmTextToBinary(str)",
 "  Translates the given text wasm module into its binary encoding."),
 
     JS_FN_HELP("wasmBinaryToText", WasmBinaryToText, 1, 0,
 "wasmBinaryToText(bin)",
 "  Translates binary encoding to text format"),
 
--- a/js/src/jit-test/tests/asm.js/testHeapAccess.js
+++ b/js/src/jit-test/tests/asm.js/testHeapAccess.js
@@ -21,39 +21,34 @@ setCachingEnabled(true);
 var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return i8[0]|0}; return f');
 var f = asmLink(code, this, null, new ArrayBuffer(BUF_MIN));
 assertEq(f(0),0);
 assertEq(f(0x7f),0x7f);
 assertEq(f(0xff),-1);
 assertEq(f(0x100),0);
 
 // Test signal handlers deactivation
-(function() {
-    var jco = getJitCompilerOptions();
-    var signalHandlersBefore = jco["signals.enable"];
-    if (signalHandlersBefore == 1) {
-        setJitCompilerOption("signals.enable", 0);
+if (wasmUsesSignalForOOB()) {
+    suppressSignalHandlers(true);
+    assertEq(wasmUsesSignalForOOB(), false);
 
-        var buf = new ArrayBuffer(BUF_MIN);
-        var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + '/* not a clone */ function f(i) {i=i|0; i32[0] = i; return i8[0]|0}; return f');
-        var f = asmLink(code, this, null, buf);
-        assertEq(f(0),0);
-        assertEq(f(0x7f),0x7f);
-        assertEq(f(0xff),-1);
-        assertEq(f(0x100),0);
+    var buf = new ArrayBuffer(BUF_MIN);
+    var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + '/* not a clone */ function f(i) {i=i|0; i32[0] = i; return i8[0]|0}; return f');
+    var f = asmLink(code, this, null, buf);
+    assertEq(f(0),0);
+    assertEq(f(0x7f),0x7f);
+    assertEq(f(0xff),-1);
+    assertEq(f(0x100),0);
 
-        // Bug 1088655
-        assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + 'var i32=new stdlib.Int32Array(heap); function f(i) {i=i|0;var j=0x10000;return (i32[j>>2] = i)|0 } return f'), this, null, buf)(1), 1);
+    // Bug 1088655
+    assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + 'var i32=new stdlib.Int32Array(heap); function f(i) {i=i|0;var j=0x10000;return (i32[j>>2] = i)|0 } return f'), this, null, buf)(1), 1);
 
-        setJitCompilerOption("signals.enable", 1);
-    }
-    jco = getJitCompilerOptions();
-    var signalHandlersAfter = jco["signals.enable"];
-    assertEq(signalHandlersBefore, signalHandlersAfter);
-})();
+    suppressSignalHandlers(false);
+    assertEq(wasmUsesSignalForOOB(), true);
+}
 
 setCachingEnabled(false);
 
 var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) {i=i|0; i32[0] = i; return u8[0]|0}; return f');
 var f = asmLink(code, this, null, new ArrayBuffer(BUF_MIN));
 assertEq(f(0),0);
 assertEq(f(0x7f),0x7f);
 assertEq(f(0xff),0xff);
--- a/js/src/jit-test/tests/asm.js/testJumpRange.js
+++ b/js/src/jit-test/tests/asm.js/testJumpRange.js
@@ -3,17 +3,17 @@ load(libdir + "asserts.js");
 
 var fatFunc = USE_ASM + '\n';
 for (var i = 0; i < 100; i++)
     fatFunc += "function f" + i + "() { return ((f" + (i+1) + "()|0)+1)|0 }\n";
 fatFunc += "function f100() { return 42 }\n";
 fatFunc += "return f0";
 
 for (var signals = 0; signals <= 1; signals++) {
-    setJitCompilerOption("signals.enable", signals);
+    suppressSignalHandlers(Boolean(signals));
 
     for (let threshold of [0, 50, 100, 5000, -1]) {
         setJitCompilerOption("jump-threshold", threshold);
 
         assertEq(asmCompile(
             USE_ASM + `
                 function h() { return ((g()|0)+2)|0 }
                 function g() { return ((f()|0)+1)|0 }
--- a/js/src/jit-test/tests/asm.js/testTimeout-deactivate-reactivate-signals.js
+++ b/js/src/jit-test/tests/asm.js/testTimeout-deactivate-reactivate-signals.js
@@ -1,29 +1,29 @@
 // |jit-test| exitstatus: 6;
 
 load(libdir + "asm.js");
 
 setCachingEnabled(true);
 
 var jco = getJitCompilerOptions();
-if (jco["signals.enable"] === 0 || !isCachingEnabled() || !isAsmJSCompilationAvailable())
+if (!isCachingEnabled() || !isAsmJSCompilationAvailable())
     quit(6);
 
 // Modules compiled without signal handlers should still work even if signal
 // handlers have been reactivated.
-setJitCompilerOption("signals.enable", 0);
+suppressSignalHandlers(true);
 
 var code = USE_ASM + "function f() {} function g() { while(1) { f() } } return g";
 
 var m = asmCompile(code);
 assertEq(isAsmJSModule(m), true);
 assertEq(isAsmJSModuleLoadedFromCache(m), false);
 
-setJitCompilerOption("signals.enable", 1);
+suppressSignalHandlers(false);
 
 var m = asmCompile(code);
 assertEq(isAsmJSModule(m), true);
 assertEq(isAsmJSModuleLoadedFromCache(m), false);
 
 var g = asmLink(m);
 timeout(1);
 g();
--- a/js/src/jit-test/tests/asm.js/testTimeout1-nosignals.js
+++ b/js/src/jit-test/tests/asm.js/testTimeout1-nosignals.js
@@ -1,9 +1,9 @@
 // |jit-test| exitstatus: 6;
 
 load(libdir + "asm.js");
 
-setJitCompilerOption("signals.enable", 0);
+suppressSignalHandlers(true);
 var g = asmLink(asmCompile(USE_ASM + "function f() {} function g() { while(1) { f() } } return g"));
 timeout(1);
 g();
 assertEq(true, false);
--- a/js/src/jit-test/tests/asm.js/testTimeout2-nosignals.js
+++ b/js/src/jit-test/tests/asm.js/testTimeout2-nosignals.js
@@ -1,9 +1,9 @@
 // |jit-test| exitstatus: 6;
 
 load(libdir + "asm.js");
 
-setJitCompilerOption("signals.enable", 0);
+suppressSignalHandlers(true);
 var g = asmLink(asmCompile(USE_ASM + "function g() { while(1) {} } return g"));
 timeout(1);
 g();
 assertEq(true, false);
--- a/js/src/jit-test/tests/asm.js/testTimeout3-nosignals.js
+++ b/js/src/jit-test/tests/asm.js/testTimeout3-nosignals.js
@@ -1,9 +1,9 @@
 // |jit-test| exitstatus: 6;
 
 load(libdir + "asm.js");
 
-setJitCompilerOption("signals.enable", 0);
+suppressSignalHandlers(true);
 var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; if (!i) return; f((i-1)|0); f((i-1)|0); f((i-1)|0); f((i-1)|0); f((i-1)|0); } return f"));
 timeout(1);
 f(100);
 assertEq(true, false);
--- a/js/src/jit-test/tests/asm.js/testTimeout4-nosignals.js
+++ b/js/src/jit-test/tests/asm.js/testTimeout4-nosignals.js
@@ -1,9 +1,9 @@
 // |jit-test| exitstatus: 6;
 
 load(libdir + "asm.js");
 
-setJitCompilerOption("signals.enable", 0);
+suppressSignalHandlers(true);
 var g = asmLink(asmCompile(USE_ASM + "function f(d) { d=+d; d=d*.1; d=d/.4; return +d } function g() { while(1) { +f(1.1) } } return g"));
 timeout(1);
 g();
 assertEq(true, false);
--- a/js/src/jit-test/tests/ion/iloop-nosignaling.js
+++ b/js/src/jit-test/tests/ion/iloop-nosignaling.js
@@ -1,5 +1,5 @@
 // |jit-test| exitstatus: 6;
 
-setJitCompilerOption('signals.enable', 0);
+suppressSignalHandlers(true);
 timeout(1);
 for(;;);
--- a/js/src/jit-test/tests/wasm/signals-enabled.js
+++ b/js/src/jit-test/tests/wasm/signals-enabled.js
@@ -3,36 +3,27 @@ load(libdir + 'asserts.js');
 
 // Explicitly opt into the new binary format for imports and exports until it
 // is used by default everywhere.
 const textToBinary = str => wasmTextToBinary(str, 'new-format');
 
 if (!wasmUsesSignalForOOB())
     quit();
 
-function enable() {
-    assertEq(getJitCompilerOptions()["signals.enable"], 0);
-    setJitCompilerOption("signals.enable", 1);
-}
-function disable() {
-    assertEq(getJitCompilerOptions()["signals.enable"], 1);
-    setJitCompilerOption("signals.enable", 0);
-}
-
 const Module = WebAssembly.Module;
 const Instance = WebAssembly.Instance;
 const Memory = WebAssembly.Memory;
 const code = textToBinary('(module (import "x" "y" (memory 1 1)))');
 
-disable();
+suppressSignalHandlers(true);
 var mem = new Memory({initial:1});
-enable();
+suppressSignalHandlers(false);
 var m = new Module(code);
-disable();
+suppressSignalHandlers(true);
 assertErrorMessage(() => new Instance(m, {x:{y:mem}}), Error, /signals/);
 var m = new Module(code);
-enable();
+suppressSignalHandlers(false);
 assertEq(new Instance(m, {x:{y:mem}}) instanceof Instance, true);
 var mem = new Memory({initial:1});
-disable();
+suppressSignalHandlers(true);
 var m = new Module(code);
-enable();
+suppressSignalHandlers(false);
 assertEq(new Instance(m, {x:{y:mem}}) instanceof Instance, true);
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -108,22 +108,16 @@ CompileRuntime::jitRuntime()
 
 SPSProfiler&
 CompileRuntime::spsProfiler()
 {
     return runtime()->spsProfiler;
 }
 
 bool
-CompileRuntime::canUseSignalHandlers()
-{
-    return runtime()->canUseSignalHandlers();
-}
-
-bool
 CompileRuntime::jitSupportsFloatingPoint()
 {
     return runtime()->jitSupportsFloatingPoint;
 }
 
 bool
 CompileRuntime::hadOutOfMemory()
 {
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -64,17 +64,16 @@ class CompileRuntime
     // used/dereferenced on the background thread so we return it as void*.
     const void* getJSContext();
 
     const JitRuntime* jitRuntime();
 
     // Compilation does not occur off thread when the SPS profiler is enabled.
     SPSProfiler& spsProfiler();
 
-    bool canUseSignalHandlers();
     bool jitSupportsFloatingPoint();
     bool hadOutOfMemory();
     bool profilingScripts();
 
     const JSAtomState& names();
     const PropertyName* emptyString();
     const StaticStrings& staticStrings();
     const Value& NaNValue();
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3,16 +3,17 @@
  * 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/Lowering.h"
 
 #include "mozilla/DebugOnly.h"
 
+#include "asmjs/WasmSignalHandlers.h"
 #include "jit/JitSpewer.h"
 #include "jit/LIR.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 
@@ -88,17 +89,17 @@ LIRGenerator::visitIsConstructing(MIsCon
 {
     define(new(alloc()) LIsConstructing(), ins);
 }
 
 static void
 TryToUseImplicitInterruptCheck(MIRGraph& graph, MBasicBlock* backedge)
 {
     // Implicit interrupt checks require asm.js signal handlers to be installed.
-    if (!GetJitContext()->runtime->canUseSignalHandlers())
+    if (!wasm::HaveSignalHandlers())
         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/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6102,25 +6102,16 @@ JS_SetGlobalJitCompilerOption(JSContext*
         if (value == 1) {
             rt->setOffthreadIonCompilationEnabled(true);
             JitSpew(js::jit::JitSpew_IonScripts, "Enable offthread compilation");
         } else if (value == 0) {
             rt->setOffthreadIonCompilationEnabled(false);
             JitSpew(js::jit::JitSpew_IonScripts, "Disable offthread compilation");
         }
         break;
-      case JSJITCOMPILER_SIGNALS_ENABLE:
-        if (value == 1) {
-            rt->setCanUseSignalHandlers(true);
-            JitSpew(js::jit::JitSpew_IonScripts, "Enable signals");
-        } else if (value == 0) {
-            rt->setCanUseSignalHandlers(false);
-            JitSpew(js::jit::JitSpew_IonScripts, "Disable signals");
-        }
-        break;
       case JSJITCOMPILER_JUMP_THRESHOLD:
         if (value == uint32_t(-1)) {
             jit::DefaultJitOptions defaultValues;
             value = defaultValues.jumpThreshold;
         }
         jit::JitOptions.jumpThreshold = value;
         break;
       case JSJITCOMPILER_WASM_TEST_MODE:
@@ -6146,18 +6137,16 @@ JS_GetGlobalJitCompilerOption(JSContext*
       case JSJITCOMPILER_ION_FORCE_IC:
         return jit::JitOptions.forceInlineCaches;
       case JSJITCOMPILER_ION_ENABLE:
         return JS::ContextOptionsRef(cx).ion();
       case JSJITCOMPILER_BASELINE_ENABLE:
         return JS::ContextOptionsRef(cx).baseline();
       case JSJITCOMPILER_OFFTHREAD_COMPILATION_ENABLE:
         return rt->canUseOffthreadIonCompilation();
-      case JSJITCOMPILER_SIGNALS_ENABLE:
-        return rt->canUseSignalHandlers();
       case JSJITCOMPILER_WASM_TEST_MODE:
         return jit::JitOptions.wasmTestMode ? 1 : 0;
       default:
         break;
     }
 #endif
     return 0;
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5536,17 +5536,16 @@ JS_SetOffthreadIonCompilationEnabled(JSC
 #define JIT_COMPILER_OPTIONS(Register)                                     \
     Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger")           \
     Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger")                     \
     Register(ION_GVN_ENABLE, "ion.gvn.enable")                             \
     Register(ION_FORCE_IC, "ion.forceinlineCaches")                        \
     Register(ION_ENABLE, "ion.enable")                                     \
     Register(BASELINE_ENABLE, "baseline.enable")                           \
     Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \
-    Register(SIGNALS_ENABLE, "signals.enable")                             \
     Register(JUMP_THRESHOLD, "jump-threshold")                             \
     Register(WASM_TEST_MODE, "wasm.test-mode")
 
 typedef enum JSJitCompilerOption {
 #define JIT_COMPILER_DECLARE(key, str) \
     JSJITCOMPILER_ ## key,
 
     JIT_COMPILER_OPTIONS(JIT_COMPILER_DECLARE)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -213,17 +213,16 @@ class ExclusiveContext : public ContextF
     PropertyName* emptyString() { return runtime_->emptyString; }
     FreeOp* defaultFreeOp() { return runtime_->defaultFreeOp(); }
     void* runtimeAddressForJit() { return runtime_; }
     void* runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
     void* stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void* stackLimitAddressForJitCode(StackKind kind);
     uintptr_t stackLimit(StackKind kind) { return runtime_->mainThread.nativeStackLimit[kind]; }
     size_t gcSystemPageSize() { return gc::SystemPageSize(); }
-    bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); }
     bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
     bool jitSupportsUnalignedAccesses() const { return runtime_->jitSupportsUnalignedAccesses; }
     bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; }
     bool lcovEnabled() const { return runtime_->lcovOutput.isEnabled(); }
 
     // Thread local data that may be accessed freely.
     DtoaState* dtoaState() {
         return perThreadData->dtoaState;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -204,18 +204,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
 #ifdef DEBUG
     handlingInitFailure(false),
 #endif
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     runningOOMTest(false),
 #endif
     allowRelazificationForTesting(false),
     data(nullptr),
-    signalHandlersInstalled_(false),
-    canUseSignalHandlers_(false),
     defaultFreeOp_(thisFromCtor()),
     debuggerMutations(0),
     securityCallbacks(&NullSecurityCallbacks),
     DOMcallbacks(nullptr),
     destroyPrincipals(nullptr),
     readPrincipals(nullptr),
     warningReporter(nullptr),
     buildIdOp(nullptr),
@@ -264,24 +262,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&onNewGlobalObjectWatchers);
 
     PodArrayZero(nativeStackQuota);
     PodZero(&asmJSCacheOps);
     lcovOutput.init();
 }
 
-static bool
-SignalBasedTriggersDisabled()
-{
-  // Don't bother trying to cache the getenv lookup; this should be called
-  // infrequently.
-  return !!getenv("JS_DISABLE_SLOW_SCRIPT_SIGNALS") || !!getenv("JS_NO_SIGNALS");
-}
-
 bool
 JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
 {
     ownerThread_ = PR_GetCurrentThread();
 
     // Get a platform-native handle for the owner thread, used by
     // js::InterruptRunningJitCode to halt the runtime's main thread.
 #ifdef XP_WIN
@@ -349,18 +339,18 @@ JSRuntime::init(uint32_t maxbytes, uint3
     if (!simulator_)
         return false;
 #endif
 
     jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
     jitSupportsUnalignedAccesses = js::jit::JitSupportsUnalignedAccesses();
     jitSupportsSimd = js::jit::JitSupportsSimd();
 
-    signalHandlersInstalled_ = wasm::EnsureSignalHandlersInstalled(this);
-    canUseSignalHandlers_ = signalHandlersInstalled_ && !SignalBasedTriggersDisabled();
+    if (!wasm::EnsureSignalHandlers(this))
+        return false;
 
     if (!spsProfiler.init())
         return false;
 
     if (!fx.initInstance())
         return false;
 
     if (!parentRuntime) {
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -973,33 +973,16 @@ struct JSRuntime : public JS::shadow::Ru
     /* Client opaque pointers */
     void*               data;
 
 #if defined(XP_DARWIN) && defined(ASMJS_MAY_USE_SIGNAL_HANDLERS)
     js::wasm::MachExceptionHandler wasmMachExceptionHandler;
 #endif
 
   private:
-    // Whether EnsureSignalHandlersInstalled succeeded in installing all the
-    // relevant handlers for this platform.
-    bool signalHandlersInstalled_;
-
-    // Whether we should use them or they have been disabled for making
-    // debugging easier. If signal handlers aren't installed, it is set to false.
-    bool canUseSignalHandlers_;
-
-  public:
-    bool canUseSignalHandlers() const {
-        return canUseSignalHandlers_;
-    }
-    void setCanUseSignalHandlers(bool enable) {
-        canUseSignalHandlers_ = signalHandlersInstalled_ && enable;
-    }
-
-  private:
     js::FreeOp          defaultFreeOp_;
 
   public:
     js::FreeOp* defaultFreeOp() {
         return &defaultFreeOp_;
     }
 
     uint32_t            debuggerMutations;