diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index a814bd06a242..06289cd059a0 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -5316,8 +5316,10 @@ CodeGenerator::generateBody() extendTrackedOptimizationsEntry(iter->mirRaw()->trackedOptimizations()); #ifdef DEBUG - if (!counts) - emitDebugResultChecks(*iter); + if (!counts) { + if (JitOptions.fullDebugChecks) + emitDebugResultChecks(*iter); + } #endif } if (masm.oom()) diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 6325185a9bca..94920d302314 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1906,6 +1906,7 @@ OptimizeMIR(MIRGenerator* mir) if (mir->shouldCancel("Make loops contiguous")) return false; } + AssertExtendedGraphCoherency(graph, /* underValueNumberer = */ false, /* force = */ true); // Passes after this point must not move instructions; these analyses // depend on knowing the final order in which instructions will execute. @@ -1949,6 +1950,8 @@ OptimizeMIR(MIRGenerator* mir) AssertGraphCoherency(graph); } + AssertGraphCoherency(graph, /* force = */ true); + DumpMIRExpressions(graph); return true; @@ -1992,8 +1995,10 @@ GenerateLIR(MIRGenerator* mir) case RegisterAllocator_Backtracking: case RegisterAllocator_Testbed: { #ifdef DEBUG - if (!integrity.record()) - return nullptr; + if (JitOptions.fullDebugChecks) { + if (!integrity.record()) + return nullptr; + } #endif BacktrackingAllocator regalloc(mir, &lirgen, *lir, @@ -2002,8 +2007,10 @@ GenerateLIR(MIRGenerator* mir) return nullptr; #ifdef DEBUG - if (!integrity.check(false)) - return nullptr; + if (JitOptions.fullDebugChecks) { + if (!integrity.check(false)) + return nullptr; + } #endif gs.spewPass("Allocate Registers [Backtracking]"); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index d607d8209b4e..f9377557155f 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2701,9 +2701,12 @@ AssertOperandsBeforeSafeInsertTop(MResumePoint* resume) #endif // DEBUG void -jit::AssertBasicGraphCoherency(MIRGraph& graph) +jit::AssertBasicGraphCoherency(MIRGraph& graph, bool force) { #ifdef DEBUG + if (!JitOptions.fullDebugChecks && !force) + return; + MOZ_ASSERT(graph.entryBlock()->numPredecessors() == 0); MOZ_ASSERT(graph.entryBlock()->phisEmpty()); MOZ_ASSERT(!graph.entryBlock()->unreachable()); @@ -2886,12 +2889,14 @@ AssertDominatorTree(MIRGraph& graph) #endif void -jit::AssertGraphCoherency(MIRGraph& graph) +jit::AssertGraphCoherency(MIRGraph& graph, bool force) { #ifdef DEBUG if (!JitOptions.checkGraphConsistency) return; - AssertBasicGraphCoherency(graph); + if (!JitOptions.fullDebugChecks && !force) + return; + AssertBasicGraphCoherency(graph, force); AssertReversePostorder(graph); #endif } @@ -2975,7 +2980,7 @@ AssertResumePointDominatedByOperands(MResumePoint* resume) #endif // DEBUG void -jit::AssertExtendedGraphCoherency(MIRGraph& graph, bool underValueNumberer) +jit::AssertExtendedGraphCoherency(MIRGraph& graph, bool underValueNumberer, bool force) { // Checks the basic GraphCoherency but also other conditions that // do not hold immediately (such as the fact that critical edges @@ -2984,8 +2989,10 @@ jit::AssertExtendedGraphCoherency(MIRGraph& graph, bool underValueNumberer) #ifdef DEBUG if (!JitOptions.checkGraphConsistency) return; + if (!JitOptions.fullDebugChecks && !force) + return; - AssertGraphCoherency(graph); + AssertGraphCoherency(graph, force); AssertDominatorTree(graph); diff --git a/js/src/jit/IonAnalysis.h b/js/src/jit/IonAnalysis.h index 3069fc4fcf94..512c6bdc21ea 100644 --- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -85,13 +85,13 @@ MOZ_MUST_USE bool BuildPhiReverseMapping(MIRGraph& graph); void -AssertBasicGraphCoherency(MIRGraph& graph); +AssertBasicGraphCoherency(MIRGraph& graph, bool force = false); void -AssertGraphCoherency(MIRGraph& graph); +AssertGraphCoherency(MIRGraph& graph, bool force = false); void -AssertExtendedGraphCoherency(MIRGraph& graph, bool underValueNumberer = false); +AssertExtendedGraphCoherency(MIRGraph& graph, bool underValueNumberer = false, bool force = false); MOZ_MUST_USE bool EliminateRedundantChecks(MIRGraph& graph); diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index 91370bc4f351..82fd77b0be1d 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -164,6 +164,10 @@ DefaultJitOptions::DefaultJitOptions() // JSScript::hadFrequentBailouts and invalidate. SET_DEFAULT(frequentBailoutThreshold, 10); + // Whether to run all debug checks in debug builds. + // Disabling might make it more enjoyable to run JS in debug builds. + SET_DEFAULT(fullDebugChecks, true); + // How many actual arguments are accepted on the C stack. SET_DEFAULT(maxStackArgs, 4096); diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index 2c37021e26c6..6c226422b459 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -68,6 +68,7 @@ struct DefaultJitOptions bool disableSink; bool eagerCompilation; bool forceInlineCaches; + bool fullDebugChecks; bool limitScriptSize; bool osr; bool asmJSAtomicsEnable; diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index ae0ca19e9925..fe83f5b124b1 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -359,7 +359,7 @@ class MNode : public TempObject protected: // Need visibility on getUseFor to avoid O(n^2) complexity. - friend void AssertBasicGraphCoherency(MIRGraph& graph); + friend void AssertBasicGraphCoherency(MIRGraph& graph, bool force); // Gets the MUse corresponding to given operand. virtual MUse* getUseFor(size_t index) = 0; @@ -12923,7 +12923,7 @@ class MResumePoint final : private: friend class MBasicBlock; - friend void AssertBasicGraphCoherency(MIRGraph& graph); + friend void AssertBasicGraphCoherency(MIRGraph& graph, bool force); // List of stack slots needed to reconstruct the frame corresponding to the // function which is compiled by IonBuilder. diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 289dc8c4b320..2405779ffc1d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6423,6 +6423,11 @@ JS_SetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t v case JSJITCOMPILER_ION_INTERRUPT_WITHOUT_SIGNAL: jit::JitOptions.ionInterruptWithoutSignals = !!value; break; +#ifdef DEBUG + case JSJITCOMPILER_FULL_DEBUG_CHECKS: + jit::JitOptions.fullDebugChecks = !!value; + break; +#endif default: break; } @@ -6470,6 +6475,11 @@ JS_GetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t* case JSJITCOMPILER_ION_INTERRUPT_WITHOUT_SIGNAL: *valueOut = jit::JitOptions.ionInterruptWithoutSignals ? 1 : 0; break; +#ifdef DEBUG + case JSJITCOMPILER_FULL_DEBUG_CHECKS: + *valueOut = jit::JitOptions.fullDebugChecks ? 1 : 0; + break; +#endif default: return false; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 92694de60ed4..5e4a25d61288 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5847,6 +5847,7 @@ JS_SetOffthreadIonCompilationEnabled(JSContext* cx, bool enabled); Register(ION_CHECK_RANGE_ANALYSIS, "ion.check-range-analysis") \ Register(BASELINE_ENABLE, "baseline.enable") \ Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \ + Register(FULL_DEBUG_CHECKS, "jit.full-debug-checks") \ Register(JUMP_THRESHOLD, "jump-threshold") \ Register(ASMJS_ATOMICS_ENABLE, "asmjs.atomics.enable") \ Register(WASM_TEST_MODE, "wasm.test-mode") \ diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index 2c1b178b2ae7..cbf94e3ab72d 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -1449,6 +1449,7 @@ ReloadPrefsCallback(const char* pref, void* data) bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit.unsafe_eager_compilation"); bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation"); + bool fullJitDebugChecks = Preferences::GetBool(JS_OPTIONS_DOT_STR "jit.full_debug_checks"); int32_t baselineThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR "baselinejit.threshold", -1); int32_t ionThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR "ion.threshold", -1); @@ -1509,6 +1510,9 @@ ReloadPrefsCallback(const char* pref, void* data) useBaselineEager ? 0 : baselineThreshold); JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_WARMUP_TRIGGER, useIonEager ? 0 : ionThreshold); +#ifdef DEBUG + JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_FULL_DEBUG_CHECKS, fullJitDebugChecks); +#endif } XPCJSContext::~XPCJSContext() diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 2c97e13b713f..db5e3f896d1a 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1259,6 +1259,9 @@ pref("javascript.options.asyncstack", false); #endif pref("javascript.options.throw_on_asmjs_validation_failure", false); pref("javascript.options.ion.offthread_compilation", true); +#ifdef DEBUG +pref("javascript.options.jit.full_debug_checks", true); +#endif // This preference instructs the JS engine to discard the // source of any privileged JS after compilation. This saves // memory, but makes things like Function.prototype.toSource()