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 305084 606e6f6aebe26585dd35c400ce72c3032b5e3378
parent 305083 c40efa3603e05cb5a781c4ef74726363e5c0c1e2
child 305085 09461dda5ac8cf17b9504d4997fc851c172fb885
push id79491
push userlwagner@mozilla.com
push dateFri, 15 Jul 2016 23:40:46 +0000
treeherdermozilla-inbound@86a5424096d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1276029
milestone50.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 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;