☠☠ backed out by ad9dc3482bd4 ☠ ☠ | |
author | Hannes Verschore <hv1989@gmail.com> |
Thu, 12 Dec 2013 15:14:12 +0100 | |
changeset 160229 | fa2005c9ca0220a8c4c073b485a64fa2b25a9271 |
parent 160228 | 7d3c745a811f383c2a3ddf8c48126cbcd3740fa7 |
child 160230 | 2b87186ddb6df93d330d948e7a9bf5eef558ba2d |
push id | 25827 |
push user | kwierso@gmail.com |
push date | Fri, 13 Dec 2013 03:13:04 +0000 |
treeherder | mozilla-central@1bc33fa19b24 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 939614 |
milestone | 29.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
|
--- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -1931,17 +1931,19 @@ class FunctionCompiler { JS_ASSERT(locals_.count() == argTypes.length() + varInitializers_.length()); alloc_ = lifo_.new_<TempAllocator>(&lifo_); ionContext_.construct(m_.cx(), alloc_); graph_ = lifo_.new_<MIRGraph>(alloc_); info_ = lifo_.new_<CompileInfo>(locals_.count(), SequentialExecution); - mirGen_ = lifo_.new_<MIRGenerator>(CompileCompartment::get(cx()->compartment()), alloc_, graph_, info_); + const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS); + mirGen_ = lifo_.new_<MIRGenerator>(CompileCompartment::get(cx()->compartment()), alloc_, + graph_, info_, optimizationInfo); if (!newBlock(/* pred = */ nullptr, &curBlock_, fn_)) return false; curBlock_->add(MAsmJSCheckOverRecursed::New(alloc(), &m_.stackOverflowLabel())); for (ABIArgTypeIter i = argTypes; !i.done(); i++) { MAsmJSParameter *ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -632,17 +632,17 @@ BaselineCompiler::emitUseCountIncrement( // count but don't attempt OSR (Ion only compiles the try block). if (analysis_.info(pc).loopEntryInCatchOrFinally) { JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY); return true; } Label skipCall; - uint32_t minUses = UsesBeforeIonRecompile(script, pc); + uint32_t minUses = UsesBeforeIonCompile(script, pc); masm.branch32(Assembler::LessThan, countReg, Imm32(minUses), &skipCall); masm.branchPtr(Assembler::Equal, Address(scriptReg, JSScript::offsetOfIonScript()), ImmPtr(ION_COMPILING_SCRIPT), &skipCall); // Call IC. ICUseCount_Fallback::Compiler stubCompiler(cx);
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -17,16 +17,17 @@ #include "builtin/Eval.h" #include "builtin/TypedObject.h" #ifdef JSGC_GENERATIONAL # include "gc/Nursery.h" #endif #include "jit/ExecutionModeInlines.h" #include "jit/IonCaches.h" #include "jit/IonLinker.h" +#include "jit/IonOptimizationLevels.h" #include "jit/IonSpewer.h" #include "jit/MIRGenerator.h" #include "jit/MoveEmitter.h" #include "jit/ParallelFunctions.h" #include "jit/ParallelSafetyAnalysis.h" #include "jit/RangeAnalysis.h" #include "vm/ForkJoin.h" @@ -5852,17 +5853,19 @@ CodeGenerator::generate() return !masm.oom(); } bool CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints) { RootedScript script(cx, gen->info().script()); ExecutionMode executionMode = gen->info().executionMode(); - JS_ASSERT(!HasIonScript(script, executionMode)); + OptimizationLevel optimizationLevel = gen->optimizationInfo().level(); + + JS_ASSERT_IF(HasIonScript(script, executionMode), executionMode == SequentialExecution); // Check to make sure we didn't have a mid-build invalidation. If so, we // will trickle to jit::Compile() and return Method_Skipped. types::RecompileInfo recompileInfo; if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo)) return true; uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None() @@ -5880,17 +5883,17 @@ CodeGenerator::link(JSContext *cx, types IonScript *ionScript = IonScript::New(cx, recompileInfo, graph.totalSlotCount(), scriptFrameSize, snapshots_.size(), bailouts_.length(), graph.numConstants(), safepointIndices_.length(), osiIndices_.length(), cacheList_.length(), runtimeData_.length(), safepoints_.size(), callTargets.length(), - patchableBackedges_.length()); + patchableBackedges_.length(), optimizationLevel); if (!ionScript) { recompileInfo.compilerOutput(cx->compartment()->types)->invalidate(); return false; } // Lock the runtime against operation callbacks during the link. // We don't want an operation callback to protect the code for the script // before it has been filled in, as we could segv before the runtime's
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -22,16 +22,17 @@ #include "jit/BaselineInspector.h" #include "jit/BaselineJIT.h" #include "jit/CodeGenerator.h" #include "jit/EdgeCaseAnalysis.h" #include "jit/EffectiveAddressAnalysis.h" #include "jit/ExecutionModeInlines.h" #include "jit/IonAnalysis.h" #include "jit/IonBuilder.h" +#include "jit/IonOptimizationLevels.h" #include "jit/IonSpewer.h" #include "jit/JitCompartment.h" #include "jit/LICM.h" #include "jit/LinearScan.h" #include "jit/LIR.h" #include "jit/Lowering.h" #include "jit/ParallelSafetyAnalysis.h" #include "jit/PerfSpewer.h" @@ -44,19 +45,16 @@ #include "jscompartmentinlines.h" #include "jsgcinlines.h" #include "jsinferinlines.h" #include "jsobjinlines.h" using namespace js; using namespace js::jit; -// Global variables. -IonOptions jit::js_IonOptions; - // Assert that IonCode is gc::Cell aligned. JS_STATIC_ASSERT(sizeof(IonCode) % gc::CellSize == 0); #ifdef JS_THREADSAFE static bool IonTLSInitialized = false; static unsigned IonTLSIndex; static inline IonContext * @@ -756,17 +754,18 @@ IonScript::IonScript() { } IonScript * IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo, uint32_t frameSlots, uint32_t frameSize, size_t snapshotsSize, size_t bailoutEntries, size_t constants, size_t safepointIndices, size_t osiIndices, size_t cacheEntries, size_t runtimeSize, - size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries) + size_t safepointsSize, size_t callTargetEntries, size_t backedgeEntries, + OptimizationLevel optimizationLevel) { static const int DataAlignment = sizeof(void *); if (snapshotsSize >= MAX_BUFFER_SIZE || (bailoutEntries >= MAX_BUFFER_SIZE / sizeof(uint32_t))) { js_ReportOutOfMemory(cx); return nullptr; @@ -843,16 +842,17 @@ IonScript::New(JSContext *cx, types::Rec script->backedgeList_ = offsetCursor; script->backedgeEntries_ = backedgeEntries; offsetCursor += paddedBackedgeSize; script->frameSlots_ = frameSlots; script->frameSize_ = frameSize; script->recompileInfo_ = recompileInfo; + script->optimizationLevel_ = optimizationLevel; return script; } void IonScript::trace(JSTracer *trc) { if (method_) @@ -1228,17 +1228,19 @@ OptimizeMIR(MIRGenerator *mir) if (graph.entryBlock()->info().executionMode() == ParallelExecution) { ParallelSafetyAnalysis analysis(mir, graph); if (!analysis.analyze()) return false; } // Alias analysis is required for LICM and GVN so that we don't move // loads across stores. - if (js_IonOptions.licm || js_IonOptions.gvn) { + if (mir->optimizationInfo().licmEnabled() || + mir->optimizationInfo().gvnEnabled()) + { AliasAnalysis analysis(mir, graph); if (!analysis.analyze()) return false; IonSpewPass("Alias analysis"); AssertExtendedGraphCoherency(graph); if (mir->shouldCancel("Alias analysis")) return false; @@ -1248,56 +1250,56 @@ OptimizeMIR(MIRGenerator *mir) // alias analysis. if (!EliminateDeadResumePointOperands(mir, graph)) return false; if (mir->shouldCancel("Eliminate dead resume point operands")) return false; } - if (js_IonOptions.gvn) { - ValueNumberer gvn(mir, graph, js_IonOptions.gvnIsOptimistic); + if (mir->optimizationInfo().gvnEnabled()) { + ValueNumberer gvn(mir, graph, mir->optimizationInfo().gvnKind() == GVN_Optimistic); if (!gvn.analyze()) return false; IonSpewPass("GVN"); AssertExtendedGraphCoherency(graph); if (mir->shouldCancel("GVN")) return false; } - if (js_IonOptions.uce) { + if (mir->optimizationInfo().uceEnabled()) { UnreachableCodeElimination uce(mir, graph); if (!uce.analyze()) return false; IonSpewPass("UCE"); AssertExtendedGraphCoherency(graph); } if (mir->shouldCancel("UCE")) return false; - if (js_IonOptions.licm) { + if (mir->optimizationInfo().licmEnabled()) { // LICM can hoist instructions from conditional branches and trigger // repeated bailouts. Disable it if this script is known to bailout // frequently. JSScript *script = mir->info().script(); if (!script || !script->hadFrequentBailouts()) { LICM licm(mir, graph); if (!licm.analyze()) return false; IonSpewPass("LICM"); AssertExtendedGraphCoherency(graph); if (mir->shouldCancel("LICM")) return false; } } - if (js_IonOptions.rangeAnalysis) { + if (mir->optimizationInfo().rangeAnalysisEnabled()) { RangeAnalysis r(mir, graph); if (!r.addBetaNodes()) return false; IonSpewPass("Beta"); AssertExtendedGraphCoherency(graph); if (mir->shouldCancel("RA Beta")) return false; @@ -1313,17 +1315,17 @@ OptimizeMIR(MIRGenerator *mir) if (!r.removeBetaNodes()) return false; IonSpewPass("De-Beta"); AssertExtendedGraphCoherency(graph); if (mir->shouldCancel("RA De-Beta")) return false; - if (js_IonOptions.uce) { + if (mir->optimizationInfo().uceEnabled()) { bool shouldRunUCE = false; if (!r.prepareForUCE(&shouldRunUCE)) return false; IonSpewPass("RA check UCE"); AssertExtendedGraphCoherency(graph); if (mir->shouldCancel("RA check UCE")) return false; @@ -1345,17 +1347,17 @@ OptimizeMIR(MIRGenerator *mir) return false; IonSpewPass("Truncate Doubles"); AssertExtendedGraphCoherency(graph); if (mir->shouldCancel("Truncate Doubles")) return false; } - if (js_IonOptions.eaa) { + if (mir->optimizationInfo().eaaEnabled()) { EffectiveAddressAnalysis eaa(graph); if (!eaa.analyze()) return false; IonSpewPass("Effective Address Analysis"); AssertExtendedGraphCoherency(graph); if (mir->shouldCancel("Effective Address Analysis")) return false; @@ -1367,28 +1369,28 @@ OptimizeMIR(MIRGenerator *mir) AssertExtendedGraphCoherency(graph); if (mir->shouldCancel("DCE")) return false; // Passes after this point must not move instructions; these analyses // depend on knowing the final order in which instructions will execute. - if (js_IonOptions.edgeCaseAnalysis && !mir->compilingAsmJS()) { + if (mir->optimizationInfo().edgeCaseAnalysisEnabled()) { EdgeCaseAnalysis edgeCaseAnalysis(mir, graph); if (!edgeCaseAnalysis.analyzeLate()) return false; IonSpewPass("Edge Case Analysis (Late)"); AssertGraphCoherency(graph); if (mir->shouldCancel("Edge Case Analysis (Late)")) return false; } - if (!mir->compilingAsmJS()) { + if (mir->optimizationInfo().eliminateRedundantChecksEnabled()) { // Note: check elimination has to run after all other passes that move // instructions. Since check uses are replaced with the actual index, // code motion after this pass could incorrectly move a load or store // before its bounds check. if (!EliminateRedundantChecks(graph)) return false; IonSpewPass("Bounds Check Elimination"); AssertGraphCoherency(graph); @@ -1411,17 +1413,17 @@ GenerateLIR(MIRGenerator *mir) return nullptr; IonSpewPass("Generate LIR"); if (mir->shouldCancel("Generate LIR")) return nullptr; AllocationIntegrityState integrity(*lir); - switch (js_IonOptions.registerAllocator) { + switch (mir->optimizationInfo().registerAllocator()) { case RegisterAllocator_LSRA: { #ifdef DEBUG if (!integrity.record()) return nullptr; #endif LinearScanAllocator regalloc(mir, &lirgen, *lir); if (!regalloc.go()) @@ -1623,24 +1625,27 @@ TrackPropertiesForSingletonScopes(JSCont if (scope->is<CallObject>() && scope->hasSingletonType()) TrackAllProperties(cx, scope); } } static AbortReason IonCompile(JSContext *cx, JSScript *script, BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing, - ExecutionMode executionMode, bool recompile) + ExecutionMode executionMode, bool recompile, + OptimizationLevel optimizationLevel) { #if JS_TRACE_LOGGING AutoTraceLog logger(TraceLogging::defaultLogger(), TraceLogging::ION_COMPILE_START, TraceLogging::ION_COMPILE_STOP, script); #endif + JS_ASSERT(optimizationLevel > Optimization_DontCompile); + TrackPropertiesForSingletonScopes(cx, script, baselineFrame); LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); if (!alloc) return AbortReason_Alloc; ScopedJSDeletePtr<LifoAlloc> autoDelete(alloc); @@ -1678,20 +1683,23 @@ IonCompile(JSContext *cx, JSScript *scri AutoFlushCache afc("IonCompile", cx->runtime()->jitRuntime()); AutoTempAllocatorRooter root(cx, temp); types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(*temp); if (!constraints) return AbortReason_Alloc; + const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(optimizationLevel); + IonBuilder *builder = alloc->new_<IonBuilder>((JSContext *) nullptr, CompileCompartment::get(cx->compartment()), temp, graph, constraints, - &inspector, info, baselineFrameInspector); + &inspector, info, optimizationInfo, + baselineFrameInspector); if (!builder) return AbortReason_Alloc; JS_ASSERT(recompile == HasIonScript(builder->script(), executionMode)); JS_ASSERT(CanIonCompile(builder->script(), executionMode)); RootedScript builderScript(cx, builder->script()); IonSpewNewFunction(graph, builderScript); @@ -1862,23 +1870,36 @@ bool CanIonCompileScript(JSContext *cx, HandleScript script, bool osr) { if (!script->canIonCompile() || !CheckScript(cx, script, osr)) return false; return CheckScriptSize(cx, script) == Method_Compiled; } +static OptimizationLevel +GetOptimizationLevel(HandleScript script, ExecutionMode executionMode) +{ + if (executionMode == ParallelExecution) + return Optimization_Normal; + + JS_ASSERT(executionMode == SequentialExecution); + + return js_IonOptimizations.levelForUseCount(script->getUseCount()); +} + static MethodStatus Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc, bool constructing, ExecutionMode executionMode) { JS_ASSERT(jit::IsIonEnabled(cx)); JS_ASSERT(jit::IsBaselineEnabled(cx)); JS_ASSERT_IF(osrPc != nullptr, (JSOp)*osrPc == JSOP_LOOPENTRY); + JS_ASSERT_IF(executionMode == ParallelExecution, !osrFrame && !osrPC); + JS_ASSERT_IF(executionMode == ParallelExecution, !HasIonScript(script, executionMode)); if (!script->hasBaselineScript()) return Method_Skipped; if (cx->compartment()->debugMode()) { IonSpew(IonSpew_Abort, "debugging"); return Method_CantCompile; } @@ -1890,33 +1911,54 @@ Compile(JSContext *cx, HandleScript scri MethodStatus status = CheckScriptSize(cx, script); if (status != Method_Compiled) { IonSpew(IonSpew_Abort, "Aborted compilation of %s:%d", script->filename(), script->lineno()); return status; } bool recompile = false; + OptimizationLevel optimizationLevel = GetOptimizationLevel(script, executionMode); + if (optimizationLevel == Optimization_DontCompile) + return Method_Skipped; IonScript *scriptIon = GetIonScript(script, executionMode); if (scriptIon) { if (!scriptIon->method()) return Method_CantCompile; - return Method_Compiled; - } - - if (executionMode == SequentialExecution) { - // Use getUseCount instead of incUseCount to avoid bumping the - // use count twice. - if (script->getUseCount() < UsesBeforeIonRecompile(script, osrPc ? osrPc : script->code())) - return Method_Skipped; + + MethodStatus failedState = Method_Compiled; + + // If we keep failing to enter the script due to an OSR pc mismatch, + // recompile with the right pc. + if (osrPc && script->ionScript()->osrPc() != osrPc) { + uint32_t count = script->ionScript()->incrOsrPcMismatchCounter(); + if (count <= js_IonOptions.osrPcMismatchesBeforeRecompile) + return Method_Skipped; + + failedState = Method_Skipped; + } + + // Don't recompile/overwrite higher optimized code, + // with a lower optimization level. + if (optimizationLevel <= scriptIon->optimizationLevel()) + return failedState; + + // Don't start compiling if already compiling + if (scriptIon->isRecompiling()) + return failedState; + + if (osrPc) + script->ionScript()->resetOsrPcMismatchCounter(); + + recompile = true; } AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode, - recompile); + recompile, optimizationLevel); if (reason == AbortReason_Error) return Method_Error; if (reason == AbortReason_Disable) return Method_CantCompile; if (reason == AbortReason_Alloc) { js_ReportOutOfMemory(cx); @@ -1956,39 +1998,29 @@ jit::CanEnterAtBranch(JSContext *cx, JSS return Method_Skipped; // Mark as forbidden if frame can't be handled. if (!CheckFrame(osrFrame)) { ForbidCompilation(cx, script); return Method_CantCompile; } - // Attempt compilation. Returns Method_Compiled if already compiled. + // Attempt compilation. + // - Returns Method_Compiled if the right ionscript is present + // (Meaning it was present or a sequantial compile finished) + // - Returns Method_Skipped if pc doesn't match + // (This means a background thread compilation with that pc could have started or not.) RootedScript rscript(cx, script); MethodStatus status = Compile(cx, rscript, osrFrame, pc, isConstructing, SequentialExecution); if (status != Method_Compiled) { if (status == Method_CantCompile) ForbidCompilation(cx, script); return status; } - if (script->ionScript()->osrPc() != pc) { - // If we keep failing to enter the script due to an OSR pc mismatch, - // invalidate the script to force a recompile. - uint32_t count = script->ionScript()->incrOsrPcMismatchCounter(); - - if (count > js_IonOptions.osrPcMismatchesBeforeRecompile) { - if (!Invalidate(cx, script, SequentialExecution, true)) - return Method_Error; - } - return Method_Skipped; - } - - script->ionScript()->resetOsrPcMismatchCounter(); - return Method_Compiled; } MethodStatus jit::CanEnter(JSContext *cx, RunState &state) { JS_ASSERT(jit::IsIonEnabled(cx)); @@ -2694,21 +2726,24 @@ jit::ForbidCompilation(JSContext *cx, JS default: MOZ_ASSUME_UNREACHABLE("No such execution mode"); } MOZ_ASSUME_UNREACHABLE("No such execution mode"); } uint32_t -jit::UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc) +jit::UsesBeforeIonCompile(JSScript *script, jsbytecode *pc) { JS_ASSERT(pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY); - uint32_t minUses = js_IonOptions.usesBeforeCompile; + OptimizationLevel level = js_IonOptimizations.nextLevel(Optimization_DontCompile); + const OptimizationInfo *info = js_IonOptimizations.get(level); + + uint32_t minUses = info->usesBeforeCompile(); // If the script is too large to compile on the main thread, we can still // compile it off thread. In these cases, increase the use count threshold // to improve the compilation's type information and hopefully avoid later // recompilation. if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE) minUses = minUses * (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE);
--- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -11,258 +11,23 @@ #include "mozilla/MemoryReporting.h" #include "jscntxt.h" #include "jscompartment.h" #include "jit/CompileInfo.h" #include "jit/CompileWrappers.h" +#include "jit/IonOptions.h" namespace js { namespace jit { class TempAllocator; -// Possible register allocators which may be used. -enum IonRegisterAllocator { - RegisterAllocator_LSRA, - RegisterAllocator_Backtracking, - RegisterAllocator_Stupid -}; - -struct IonOptions -{ - // Toggles whether global value numbering is used. - // - // Default: true - bool gvn; - - // Toggles whether global value numbering is optimistic (true) or - // pessimistic (false). - // - // Default: true - bool gvnIsOptimistic; - - // Toggles whether loop invariant code motion is performed. - // - // Default: true - bool licm; - - // Toggles whether functions may be entered at loop headers. - // - // Default: true - bool osr; - - // Toggles whether large scripts are rejected. - // - // Default: true - bool limitScriptSize; - - // Describes which register allocator to use. - // - // Default: LSRA - IonRegisterAllocator registerAllocator; - - // Toggles whether inlining is performed. - // - // Default: true - bool inlining; - - // Toggles whether Edge Case Analysis is used. - // - // Default: true - bool edgeCaseAnalysis; - - // Toggles whether Range Analysis is used. - // - // Default: true - bool rangeAnalysis; - - // Whether to enable extra code to perform dynamic validation of - // RangeAnalysis results. - // - // Default: false - bool checkRangeAnalysis; - - // Whether to protect the GC heap during Ion compilation and ensure that - // only threadsafe operations are performed on it. - // - // Default: false - bool checkThreadSafety; - - // Whether to perform expensive graph-consistency DEBUG-only assertions. - // It can be useful to disable this to reduce DEBUG-compile time of large - // asm.js programs. - // - // Default: true - bool checkGraphConsistency; - - // Toggles whether Unreachable Code Elimination is performed. - // - // Default: true - bool uce; - - // Toggles whether Effective Address Analysis is performed. - // - // Default: true - bool eaa; - -#ifdef CHECK_OSIPOINT_REGISTERS - // Emit extra code to verify live regs at the start of a VM call - // are not modified before its OsiPoint. - // - // Default: false - bool checkOsiPointRegisters; -#endif - - // How many invocations or loop iterations are needed before functions - // are compiled with the baseline compiler. - // - // Default: 10 - uint32_t baselineUsesBeforeCompile; - - // How many invocations or loop iterations are needed before functions - // are compiled. - // - // Default: 1,000 - uint32_t usesBeforeCompile; - - // How many invocations or loop iterations are needed before calls - // are inlined, as a fraction of usesBeforeCompile. - // - // Default: .125 - double usesBeforeInliningFactor; - - // How many times we will try to enter a script via OSR before - // invalidating the script. - // - // Default: 6,000 - uint32_t osrPcMismatchesBeforeRecompile; - - // Number of bailouts without invalidation before we set - // JSScript::hadFrequentBailouts and invalidate. - // - // Default: 10 - uint32_t frequentBailoutThreshold; - - // Number of exception bailouts (resuming into catch/finally block) before - // we invalidate and forbid Ion compilation. - // - // Default: 10 - uint32_t exceptionBailoutThreshold; - - // Whether Ion should compile try-catch statements. - // - // Default: true - bool compileTryCatch; - - // How many actual arguments are accepted on the C stack. - // - // Default: 4,096 - uint32_t maxStackArgs; - - // The maximum inlining depth. - // - // Default: 3 - uint32_t maxInlineDepth; - - // The maximum inlining depth for functions. - // - // Inlining small functions has almost no compiling overhead - // and removes the otherwise needed call overhead. - // The value is currently very low. - // Actually it is only needed to make sure we don't blow out the stack. - // - // Default: 10 - uint32_t smallFunctionMaxInlineDepth; - - // The bytecode length limit for small function. - // - // The default for this was arrived at empirically via benchmarking. - // We may want to tune it further after other optimizations have gone - // in. - // - // Default: 100 - uint32_t smallFunctionMaxBytecodeLength; - - // The maximum number of functions to polymorphically inline at a call site. - // - // Default: 4 - uint32_t polyInlineMax; - - // The maximum total bytecode size of an inline call site. - // - // Default: 1000 - uint32_t inlineMaxTotalBytecodeLength; - - // Whether functions are compiled immediately. - // - // Default: false - bool eagerCompilation; - - // How many uses of a parallel kernel before we attempt compilation. - // - // Default: 1 - uint32_t usesBeforeCompilePar; - - // The maximum bytecode length the caller may have, - // before we stop inlining large functions in that caller. - // - // Default: 10000 - uint32_t inliningMaxCallerBytecodeLength; - - void setEagerCompilation() { - eagerCompilation = true; - usesBeforeCompile = 0; - baselineUsesBeforeCompile = 0; - } - - MOZ_CONSTEXPR IonOptions() - : gvn(true), - gvnIsOptimistic(true), - licm(true), - osr(true), - limitScriptSize(true), - registerAllocator(RegisterAllocator_LSRA), - inlining(true), - edgeCaseAnalysis(true), - rangeAnalysis(true), - checkRangeAnalysis(false), - checkThreadSafety(false), - checkGraphConsistency(true), - uce(true), - eaa(true), -#ifdef CHECK_OSIPOINT_REGISTERS - checkOsiPointRegisters(false), -#endif - baselineUsesBeforeCompile(10), - usesBeforeCompile(1000), - usesBeforeInliningFactor(.125), - osrPcMismatchesBeforeRecompile(6000), - frequentBailoutThreshold(10), - exceptionBailoutThreshold(10), - compileTryCatch(true), - maxStackArgs(4096), - maxInlineDepth(3), - smallFunctionMaxInlineDepth(10), - smallFunctionMaxBytecodeLength(100), - polyInlineMax(4), - inlineMaxTotalBytecodeLength(1000), - eagerCompilation(false), - usesBeforeCompilePar(1), - inliningMaxCallerBytecodeLength(10000) - { - } - - uint32_t usesBeforeInlining() { - return usesBeforeCompile * usesBeforeInliningFactor; - } -}; - enum MethodStatus { Method_Error, Method_CantCompile, Method_Skipped, Method_Compiled }; @@ -303,18 +68,16 @@ class IonContext int getNextAssemblerId() { return assemblerCount_++; } private: IonContext *prev_; int assemblerCount_; }; -extern IonOptions js_IonOptions; - // Initialize Ion statically for all JSRuntimes. bool InitializeIon(); // Get and set the current Ion context. IonContext *GetIonContext(); IonContext *MaybeGetIonContext(); bool SetIonContext(IonContext *ctx); @@ -411,17 +174,17 @@ IsIonInlinablePC(jsbytecode *pc) { inline bool TooManyArguments(unsigned nargs) { return (nargs >= SNAPSHOT_MAX_NARGS || nargs > js_IonOptions.maxStackArgs); } void ForbidCompilation(JSContext *cx, JSScript *script); void ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode); -uint32_t UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc); +uint32_t UsesBeforeIonCompile(JSScript *script, jsbytecode *pc); void PurgeCaches(JSScript *script, JS::Zone *zone); size_t SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf); void DestroyIonScripts(FreeOp *fop, JSScript *script); void TraceIonScripts(JSTracer* trc, JSScript *script); void TriggerOperationCallbackForIonCode(JSRuntime *rt, JSRuntime::OperationCallbackTrigger trigger);
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -7,16 +7,17 @@ #include "jit/IonAnalysis.h" #include "jsanalyze.h" #include "jit/BaselineInspector.h" #include "jit/BaselineJIT.h" #include "jit/Ion.h" #include "jit/IonBuilder.h" +#include "jit/IonOptimizationLevels.h" #include "jit/LIR.h" #include "jit/Lowering.h" #include "jit/MIRGraph.h" #include "jsinferinlines.h" #include "jsobjinlines.h" using namespace js; @@ -2153,20 +2154,22 @@ jit::AnalyzeNewScriptProperties(JSContex MIRGraph graph(&temp); CompileInfo info(script, fun, /* osrPc = */ nullptr, /* constructing = */ false, DefinitePropertiesAnalysis); AutoTempAllocatorRooter root(cx, &temp); + const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_Normal); + types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp); BaselineInspector inspector(script); IonBuilder builder(cx, CompileCompartment::get(cx->compartment()), &temp, &graph, constraints, - &inspector, &info, /* baselineFrame = */ nullptr); + &inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr); if (!builder.build()) { if (builder.abortReason() == AbortReason_Alloc) return false; return true; } if (!SplitCriticalEdges(graph))
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -13,16 +13,17 @@ #include "builtin/Eval.h" #include "builtin/TypedObject.h" #include "builtin/TypeRepresentation.h" #include "frontend/SourceNotes.h" #include "jit/BaselineFrame.h" #include "jit/BaselineInspector.h" #include "jit/ExecutionModeInlines.h" #include "jit/Ion.h" +#include "jit/IonOptimizationLevels.h" #include "jit/IonSpewer.h" #include "jit/Lowering.h" #include "jit/MIRGraph.h" #include "vm/ArgumentsObject.h" #include "vm/RegExpStatics.h" #include "jsinferinlines.h" @@ -96,21 +97,23 @@ jit::NewBaselineFrameInspector(TempAlloc inspector->varTypes.infallibleAppend(types::Type::UndefinedType()); else inspector->varTypes.infallibleAppend(types::GetValueType(frame->unaliasedVar(i))); } return inspector; } -IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, MIRGraph *graph, - types::CompilerConstraintList *constraints, - BaselineInspector *inspector, CompileInfo *info, BaselineFrameInspector *baselineFrame, - size_t inliningDepth, uint32_t loopDepth) - : MIRGenerator(comp, temp, graph, info), +IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, + MIRGraph *graph, types::CompilerConstraintList *constraints, + BaselineInspector *inspector, CompileInfo *info, + const OptimizationInfo *optimizationInfo, + BaselineFrameInspector *baselineFrame, size_t inliningDepth, + uint32_t loopDepth) + : MIRGenerator(comp, temp, graph, info, optimizationInfo), backgroundCodegen_(nullptr), analysisContext(analysisContext), baselineFrame_(baselineFrame), abortReason_(AbortReason_Disable), reprSetHash_(nullptr), constraints_(constraints), analysis_(*temp, info->script()), thisTypes(nullptr), @@ -322,16 +325,19 @@ IonBuilder::DontInline(JSScript *targetS } return InliningDecision_DontInline; } IonBuilder::InliningDecision IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo) { + if (!optimizationInfo().inlineInterpreted()) + return InliningDecision_DontInline; + if (!target->isInterpreted()) return DontInline(nullptr, "Non-interpreted target"); // Allow constructing lazy scripts when performing the definite properties // analysis, as baseline has not been used to warm the caller up yet. if (target->isInterpreted() && info().executionMode() == DefinitePropertiesAnalysis) { if (!target->getOrCreateScript(analysisContext)) return InliningDecision_Error; @@ -3846,19 +3852,19 @@ IonBuilder::inlineScriptedCall(CallInfo this->info().executionMode()); if (!info) return false; MIRGraphReturns returns(alloc()); AutoAccumulateReturns aar(graph(), returns); // Build the graph. - IonBuilder inlineBuilder(analysisContext, compartment, - &alloc(), &graph(), constraints(), &inspector, info, nullptr, - inliningDepth_ + 1, loopDepth_); + IonBuilder inlineBuilder(analysisContext, compartment, &alloc(), &graph(), constraints(), + &inspector, info, &optimizationInfo(), nullptr, inliningDepth_ + 1, + loopDepth_); if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) { if (analysisContext && analysisContext->isExceptionPending()) { IonSpew(IonSpew_Abort, "Inline builder raised exception."); abortReason_ = AbortReason_Error; return false; } // Inlining the callee failed. Mark the callee as uninlineable only if @@ -3960,29 +3966,19 @@ IonBuilder::patchInlinedReturns(CallInfo return nullptr; phi->addInput(rdef); } bottom->addPhi(phi); return phi; } -static bool -IsSmallFunction(JSScript *script) -{ - return script->length() <= js_IonOptions.smallFunctionMaxBytecodeLength; -} - IonBuilder::InliningDecision IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo) { - // Only inline when inlining is enabled. - if (!inliningEnabled()) - return InliningDecision_DontInline; - // When there is no target, inlining is impossible. if (target == nullptr) return InliningDecision_DontInline; // Native functions provide their own detection in inlineNativeCall(). if (target->isNative()) return InliningDecision_Inline; @@ -3992,40 +3988,40 @@ IonBuilder::makeInliningDecision(JSFunct return decision; // Heuristics! JSScript *targetScript = target->nonLazyScript(); // Skip heuristics if we have an explicit hint to inline. if (!targetScript->shouldInline()) { // Cap the inlining depth. - if (IsSmallFunction(targetScript)) { - if (inliningDepth_ >= js_IonOptions.smallFunctionMaxInlineDepth) + if (js_IonOptions.isSmallFunction(targetScript)) { + if (inliningDepth_ >= optimizationInfo().smallFunctionMaxInlineDepth()) return DontInline(targetScript, "Vetoed: exceeding allowed inline depth"); } else { - if (inliningDepth_ >= js_IonOptions.maxInlineDepth) + if (inliningDepth_ >= optimizationInfo().maxInlineDepth()) return DontInline(targetScript, "Vetoed: exceeding allowed inline depth"); if (targetScript->hasLoops()) return DontInline(targetScript, "Vetoed: big function that contains a loop"); // Caller must not be excessively large. - if (script()->length() >= js_IonOptions.inliningMaxCallerBytecodeLength) + if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) return DontInline(targetScript, "Vetoed: caller excessively large"); } // Callee must not be excessively large. // This heuristic also applies to the callsite as a whole. - if (targetScript->length() > js_IonOptions.inlineMaxTotalBytecodeLength) + if (targetScript->length() > optimizationInfo().inlineMaxTotalBytecodeLength()) return DontInline(targetScript, "Vetoed: callee excessively large"); // Callee must have been called a few times to have somewhat stable // type information, except for definite properties analysis, // as the caller has not run yet. - if (targetScript->getUseCount() < js_IonOptions.usesBeforeInlining() && + if (targetScript->getUseCount() < optimizationInfo().usesBeforeInlining() && info().executionMode() != DefinitePropertiesAnalysis) { return DontInline(targetScript, "Vetoed: callee is insufficiently hot."); } } // TI calls ObjectStateChange to trigger invalidation of the caller. types::TypeObjectKey *targetType = types::TypeObjectKey::get(target); @@ -4058,17 +4054,17 @@ IonBuilder::selectInliningTargets(Object case InliningDecision_Inline: inlineable = true; break; } // Enforce a maximum inlined bytecode limit at the callsite. if (inlineable && target->isInterpreted()) { totalSize += target->nonLazyScript()->length(); - if (totalSize > js_IonOptions.inlineMaxTotalBytecodeLength) + if (totalSize > optimizationInfo().inlineMaxTotalBytecodeLength()) inlineable = false; } choiceSet.append(inlineable); if (inlineable) *numInlineable += 1; } @@ -4152,19 +4148,16 @@ IonBuilder::inlineSingleCall(CallInfo &c return InliningStatus_Error; return InliningStatus_Inlined; } IonBuilder::InliningStatus IonBuilder::inlineCallsite(ObjectVector &targets, ObjectVector &originals, bool lambda, CallInfo &callInfo) { - if (!inliningEnabled()) - return InliningStatus_NotInlined; - if (targets.length() == 0) return InliningStatus_NotInlined; // Is the function provided by an MGetPropertyCache? // If so, the cache may be movable to a fallback path, with a dispatch // instruction guarding on the incoming TypeObject. MGetPropertyCache *propCache = getInlineableGetPropertyCache(callInfo); @@ -5994,20 +5987,38 @@ ClassHasResolveHook(CompileCompartment * return FunctionHasResolveHook(comp->runtime()->names(), name); return true; } void IonBuilder::insertRecompileCheck() { + // PJS doesn't recompile and doesn't need recompile checks. if (info().executionMode() != SequentialExecution) return; - return; + // No need for recompile checks if this is the highest optimization level. + OptimizationLevel curLevel = optimizationInfo().level(); + if (js_IonOptimizations.isLastLevel(curLevel)) + return; + + // Add recompile check. + + // Get the topmost builder. The topmost script will get recompiled when + // usecount is high enough to justify a higher optimization level. + IonBuilder *topBuilder = this; + while (topBuilder->callerBuilder_) + topBuilder = topBuilder->callerBuilder_; + + // Add recompile check to recompile when the usecount reaches the usecount + // of the next optimization level. + OptimizationLevel nextLevel = js_IonOptimizations.nextLevel(curLevel); + const OptimizationInfo *info = js_IonOptimizations.get(nextLevel); + current->add(MRecompileCheck::New(alloc(), topBuilder->script(), info->usesBeforeCompile())); } JSObject * IonBuilder::testSingletonProperty(JSObject *obj, PropertyName *name) { // We would like to completely no-op property/global accesses which can // produce only a particular JSObject. When indicating the access result is // definitely an object, type inference does not account for the
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -8,16 +8,17 @@ #define jit_IonBuilder_h #ifdef JS_ION // This file declares the data structures for building a MIRGraph from a // JSScript. #include "jit/BytecodeAnalysis.h" +#include "jit/IonOptimizationLevels.h" #include "jit/MIR.h" #include "jit/MIRGenerator.h" #include "jit/MIRGraph.h" #include "jit/TypeRepresentationSet.h" namespace js { namespace jit { @@ -206,39 +207,36 @@ class IonBuilder : public MIRGenerator static CFGState CondSwitch(IonBuilder *builder, jsbytecode *exitpc, jsbytecode *defaultTarget); static CFGState Label(jsbytecode *exitpc); static CFGState Try(jsbytecode *exitpc, MBasicBlock *successor); }; static int CmpSuccessors(const void *a, const void *b); public: - IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, MIRGraph *graph, - types::CompilerConstraintList *constraints, - BaselineInspector *inspector, CompileInfo *info, BaselineFrameInspector *baselineFrame, + IonBuilder(JSContext *analysisContext, CompileCompartment *comp, TempAllocator *temp, + MIRGraph *graph, types::CompilerConstraintList *constraints, + BaselineInspector *inspector, CompileInfo *info, + const OptimizationInfo *optimizationInfo, BaselineFrameInspector *baselineFrame, size_t inliningDepth = 0, uint32_t loopDepth = 0); bool build(); bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint, CallInfo &callInfo); private: bool traverseBytecode(); ControlStatus snoopControlFlow(JSOp op); bool processIterators(); bool inspectOpcode(JSOp op); uint32_t readIndex(jsbytecode *pc); JSAtom *readAtom(jsbytecode *pc); bool abort(const char *message, ...); void spew(const char *message); - static bool inliningEnabled() { - return js_IonOptions.inlining; - } - JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes); bool getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing, ObjectVector &targets, uint32_t maxTargets, bool *gotLambda); void popCfgStack(); DeferredEdge *filterDeadDeferredEdges(DeferredEdge *edge); bool processDeferredContinues(CFGState &state); ControlStatus processControlEnd();
--- a/js/src/jit/IonCode.h +++ b/js/src/jit/IonCode.h @@ -9,16 +9,17 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" #include "jsinfer.h" #include "jstypes.h" #include "gc/Heap.h" +#include "jit/IonOptimizationLevels.h" #include "jit/IonTypes.h" namespace JSC { class ExecutablePool; } namespace js { @@ -256,16 +257,19 @@ struct IonScript uint32_t backedgeEntries_; // Number of references from invalidation records. size_t refcount_; // Identifier of the compilation which produced this code. types::RecompileInfo recompileInfo_; + // The optimization level this script was compiled in. + OptimizationLevel optimizationLevel_; + // Number of times we tried to enter this script via OSR but failed due to // a LOOPENTRY pc other than osrPc_. uint32_t osrPcMismatchCounter_; // If non-null, the list of AsmJSModules // that contain an optimized call directly into this IonScript. Vector<DependentAsmJSModuleExit> *dependentAsmJSModules; @@ -330,17 +334,18 @@ struct IonScript // Do not call directly, use IonScript::New. This is public for cx->new_. IonScript(); static IonScript *New(JSContext *cx, types::RecompileInfo recompileInfo, uint32_t frameLocals, uint32_t frameSize, size_t snapshotsSize, size_t snapshotEntries, size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries, size_t cacheEntries, size_t runtimeSize, size_t safepointsSize, - size_t callTargetEntries, size_t backedgeEntries); + size_t callTargetEntries, size_t backedgeEntries, + OptimizationLevel optimizationLevel); static void Trace(JSTracer *trc, IonScript *script); static void Destroy(FreeOp *fop, IonScript *script); static inline size_t offsetOfMethod() { return offsetof(IonScript, method_); } static inline size_t offsetOfOsrEntryOffset() { return offsetof(IonScript, osrEntryOffset_); @@ -521,16 +526,19 @@ struct IonScript JS_ASSERT(refcount_); refcount_--; if (!refcount_) Destroy(fop, this); } const types::RecompileInfo& recompileInfo() const { return recompileInfo_; } + OptimizationLevel optimizationLevel() const { + return optimizationLevel_; + } uint32_t incrOsrPcMismatchCounter() { return ++osrPcMismatchCounter_; } void resetOsrPcMismatchCounter() { osrPcMismatchCounter_ = 0; } void setRecompiling() {
new file mode 100644 --- /dev/null +++ b/js/src/jit/IonOptimizationLevels.cpp @@ -0,0 +1,110 @@ +/* -*- 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/IonOptimizationLevels.h" + +#include "jsanalyze.h" +#include "jsscript.h" + +using namespace js; +using namespace js::jit; + +namespace js { +namespace jit { + +OptimizationInfos js_IonOptimizations; + +void +OptimizationInfo::initNormalOptimizationInfo() +{ + level_ = Optimization_Normal; + + eaa_ = true; + edgeCaseAnalysis_ = true; + eliminateRedundantChecks_ = true; + inlineInterpreted_ = true; + inlineNative_ = true; + gvn_ = true; + gvnKind_ = GVN_Optimistic; + licm_ = true; + uce_ = true; + rangeAnalysis_ = true; + registerAllocator_ = RegisterAllocator_LSRA; + + inlineMaxTotalBytecodeLength_ = 1000; + inliningMaxCallerBytecodeLength_ = 10000; + maxInlineDepth_ = 3; + smallFunctionMaxInlineDepth_ = 10; + usesBeforeCompile_ = 1000; + usesBeforeInliningFactor_ = 0.125; +} + +void +OptimizationInfo::initAsmjsOptimizationInfo() +{ + // The AsmJS optimization level + // Disables some passes that don't work well with asmjs. + + // Take normal option values for not specified values. + initNormalOptimizationInfo(); + + level_ = Optimization_AsmJS; + edgeCaseAnalysis_ = false; + eliminateRedundantChecks_ = false; +} + +OptimizationInfos::OptimizationInfos() +{ + infos_[Optimization_Normal - 1].initNormalOptimizationInfo(); + infos_[Optimization_AsmJS - 1].initAsmjsOptimizationInfo(); + +#ifdef DEBUG + OptimizationLevel prev = nextLevel(Optimization_DontCompile); + while (!isLastLevel(prev)) { + OptimizationLevel next = nextLevel(prev); + JS_ASSERT(get(prev)->usesBeforeCompile() < get(next)->usesBeforeCompile()); + prev = next; + } +#endif +} + +OptimizationLevel +OptimizationInfos::nextLevel(OptimizationLevel level) +{ + JS_ASSERT(!isLastLevel(level)); + switch (level) { + case Optimization_DontCompile: + return Optimization_Normal; + default: + MOZ_ASSUME_UNREACHABLE("Unknown optimization level."); + } +} + +bool +OptimizationInfos::isLastLevel(OptimizationLevel level) +{ + return level == Optimization_Normal; +} + +OptimizationLevel +OptimizationInfos::levelForUseCount(uint32_t useCount) +{ + OptimizationLevel prev = Optimization_DontCompile; + + while (!isLastLevel(prev)) { + OptimizationLevel level = nextLevel(prev); + const OptimizationInfo *info = get(level); + if (useCount < info->usesBeforeCompile()) + return prev; + + prev = level; + } + + return prev; +} + +} // namespace jit +} // namespace js
new file mode 100644 --- /dev/null +++ b/js/src/jit/IonOptimizationLevels.h @@ -0,0 +1,203 @@ +/* -*- 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_IonOptimizationLevels_h +#define jit_IonOptimizationLevels_h + +#include "jsbytecode.h" +#include "jstypes.h" + +#include "jit/IonOptions.h" +#include "js/TypeDecls.h" + +namespace js { +namespace jit { + +enum OptimizationLevel +{ + Optimization_DontCompile, + Optimization_Normal, + Optimization_AsmJS, + Optimization_Count +}; + +class OptimizationInfo +{ + public: + OptimizationLevel level_; + + // Toggles whether Effective Address Analysis is performed. + bool eaa_; + + // Toggles whether Edge Case Analysis is used. + bool edgeCaseAnalysis_; + + // Toggles whether redundant checks get removed. + bool eliminateRedundantChecks_; + + // Toggles whether interpreted scripts get inlined. + bool inlineInterpreted_; + + // Toggles whether native scripts get inlined. + bool inlineNative_; + + // Toggles whether global value numbering is used. + bool gvn_; + + // Toggles whether global value numbering is optimistic or pessimistic. + IonGvnKind gvnKind_; + + // Toggles whether loop invariant code motion is performed. + bool licm_; + + // Toggles whether Unreachable Code Elimination is performed. + bool uce_; + + // Toggles whether Range Analysis is used. + bool rangeAnalysis_; + + // Describes which register allocator to use. + IonRegisterAllocator registerAllocator_; + + // The maximum total bytecode size of an inline call site. + uint32_t inlineMaxTotalBytecodeLength_; + + // The maximum bytecode length the caller may have, + // before we stop inlining large functions in that caller. + uint32_t inliningMaxCallerBytecodeLength_; + + // The maximum inlining depth. + uint32_t maxInlineDepth_; + + // The maximum inlining depth for functions. + // + // Inlining small functions has almost no compiling overhead + // and removes the otherwise needed call overhead. + // The value is currently very low. + // Actually it is only needed to make sure we don't blow out the stack. + uint32_t smallFunctionMaxInlineDepth_; + + // How many invocations or loop iterations are needed before functions + // are compiled. + uint32_t usesBeforeCompile_; + + // How many invocations or loop iterations are needed before calls + // are inlined, as a fraction of usesBeforeCompile. + double usesBeforeInliningFactor_; + + OptimizationInfo() + { } + + void initNormalOptimizationInfo(); + void initAsmjsOptimizationInfo(); + + OptimizationLevel level() const { + return level_; + } + + bool inlineInterpreted() const { + return inlineInterpreted_ && !js_IonOptions.disableInlining; + } + + bool inlineNative() const { + return inlineNative_ && !js_IonOptions.disableInlining; + } + + uint32_t usesBeforeCompile() const { + if (js_IonOptions.forceDefaultIonUsesBeforeCompile) + return js_IonOptions.forcedDefaultIonUsesBeforeCompile; + return usesBeforeCompile_; + } + + bool gvnEnabled() const { + return gvn_ && !js_IonOptions.disableGvn; + } + + bool licmEnabled() const { + return licm_ && !js_IonOptions.disableLicm; + } + + bool uceEnabled() const { + return uce_ && !js_IonOptions.disableUce; + } + + bool rangeAnalysisEnabled() const { + return rangeAnalysis_ && !js_IonOptions.disableRangeAnalysis; + } + + bool eaaEnabled() const { + return eaa_ && !js_IonOptions.disableEaa; + } + + bool edgeCaseAnalysisEnabled() const { + return edgeCaseAnalysis_ && !js_IonOptions.disableEdgeCaseAnalysis; + } + + bool eliminateRedundantChecksEnabled() const { + return eliminateRedundantChecks_; + } + + IonGvnKind gvnKind() const { + if (!js_IonOptions.forceGvnKind) + return gvnKind_; + return js_IonOptions.forcedGvnKind; + } + + IonRegisterAllocator registerAllocator() const { + if (!js_IonOptions.forceRegisterAllocator) + return registerAllocator_; + return js_IonOptions.forcedRegisterAllocator; + } + + uint32_t smallFunctionMaxInlineDepth() const { + return smallFunctionMaxInlineDepth_; + } + + bool isSmallFunction(JSScript *script) const; + + uint32_t maxInlineDepth() const { + return maxInlineDepth_; + } + + uint32_t inlineMaxTotalBytecodeLength() const { + return inlineMaxTotalBytecodeLength_; + } + + uint32_t inliningMaxCallerBytecodeLength() const { + return inlineMaxTotalBytecodeLength_; + } + + uint32_t usesBeforeInlining() const { + return usesBeforeCompile() * usesBeforeInliningFactor_; + } +}; + +class OptimizationInfos +{ + private: + OptimizationInfo infos_[Optimization_Count - 1]; + + public: + OptimizationInfos(); + + const OptimizationInfo *get(OptimizationLevel level) { + JS_ASSERT(level < Optimization_Count); + JS_ASSERT(level != Optimization_DontCompile); + + return &infos_[level - 1]; + } + + OptimizationLevel nextLevel(OptimizationLevel level); + bool isLastLevel(OptimizationLevel level); + OptimizationLevel levelForUseCount(uint32_t useCount); +}; + +extern OptimizationInfos js_IonOptimizations; + +} // namespace jit +} // namespace js + +#endif /* jit_IonOptimizationLevels_h */
new file mode 100644 --- /dev/null +++ b/js/src/jit/JitOptions.cpp @@ -0,0 +1,160 @@ +/* -*- 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/IonOptions.h" + +using namespace js; +using namespace js::jit; + +namespace js { +namespace jit { + +IonOptions js_IonOptions; + +IonOptions::IonOptions() +{ + // Whether to perform expensive graph-consistency DEBUG-only assertions. + // It can be useful to disable this to reduce DEBUG-compile time of large + // asm.js programs. + checkGraphConsistency = true; + +#ifdef CHECK_OSIPOINT_REGISTERS + // Emit extra code to verify live regs at the start of a VM call + // are not modified before its OsiPoint. + checkOsiPointRegisters = false; +#endif + + // Whether to enable extra code to perform dynamic validation of + // RangeAnalysis results. + checkRangeAnalysis = false; + + // Whether to protect the GC heap during Ion compilation and ensure that + // only threadsafe operations are performed on it. + checkThreadSafety = false; + + // Whether Ion should compile try-catch statements. + compileTryCatch = true; + + // Toggle whether global value numbering is globally disabled. + disableGvn = false; + + // Toggles whether loop invariant code motion is globally disabled. + disableLicm = false; + + // Toggles whether inlining is globally disabled. + disableInlining = false; + + // Toggles whether Edge Case Analysis is gobally disabled. + disableEdgeCaseAnalysis = false; + + // Toggles whether Range Analysis is globally disabled. + disableRangeAnalysis = false; + + // Toggles whether Unreachable Code Elimination is globally disabled. + disableUce = false; + + // Toggles whether Effective Address Analysis is globally disabled. + disableEaa = false; + + // Whether functions are compiled immediately. + eagerCompilation = false; + + // Force how many invocation or loop iterations are needed before compiling + // a function with the highest ionmonkey optimization level. + // (i.e. OptimizationLevel_Normal) + forceDefaultIonUsesBeforeCompile = false; + forcedDefaultIonUsesBeforeCompile = 1000; + + // Force the GVN kind to be optimistic or pessimistic instead of letting + // the optimization pass decide. + forceGvnKind = false; + forcedGvnKind = GVN_Optimistic; + + // Force the used register allocator instead of letting the + // optimization pass decide. + forceRegisterAllocator = false; + forcedRegisterAllocator = RegisterAllocator_LSRA; + + // Toggles whether large scripts are rejected. + limitScriptSize = true; + + // Toggles whether functions may be entered at loop headers. + osr = true; + + // How many invocations or loop iterations are needed before functions + // are compiled with the baseline compiler. + baselineUsesBeforeCompile = 10; + + // Number of exception bailouts (resuming into catch/finally block) before + // we invalidate and forbid Ion compilation. + exceptionBailoutThreshold = 10; + + // Number of bailouts without invalidation before we set + // JSScript::hadFrequentBailouts and invalidate. + frequentBailoutThreshold = 10; + + // How many actual arguments are accepted on the C stack. + maxStackArgs = 4096; + + // How many times we will try to enter a script via OSR before + // invalidating the script. + osrPcMismatchesBeforeRecompile = 6000; + + // The bytecode length limit for small function. + // + // The default for this was arrived at empirically via benchmarking. + // We may want to tune it further after other optimizations have gone + // in. + smallFunctionMaxBytecodeLength_ = 100; + + // How many uses of a parallel kernel before we attempt compilation. + usesBeforeCompilePar = 1; +} + +bool +IonOptions::isSmallFunction(JSScript *script) const +{ + return script->length() <= smallFunctionMaxBytecodeLength_; +} + +void +IonOptions::setEagerCompilation() +{ + eagerCompilation = true; + baselineUsesBeforeCompile = 0; + forceDefaultIonUsesBeforeCompile = true; + forcedDefaultIonUsesBeforeCompile = 0; +} + +void +IonOptions::setUsesBeforeCompile(uint32_t useCount) +{ + forceDefaultIonUsesBeforeCompile = true; + forcedDefaultIonUsesBeforeCompile = useCount; + + // Undo eager compilation + if (eagerCompilation && useCount != 0) { + jit::IonOptions defaultValues; + eagerCompilation = false; + baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile; + } +} + +void +IonOptions::resetUsesBeforeCompile() +{ + forceDefaultIonUsesBeforeCompile = false; + + // Undo eager compilation + if (eagerCompilation) { + jit::IonOptions defaultValues; + eagerCompilation = false; + baselineUsesBeforeCompile = defaultValues.baselineUsesBeforeCompile; + } +} + +} // namespace jit +} // namespace js
new file mode 100644 --- /dev/null +++ b/js/src/jit/JitOptions.h @@ -0,0 +1,76 @@ +/* -*- 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_IonOptions_h +#define jit_IonOptions_h + +#include "jit/IonTypes.h" + +#ifdef JS_ION + +namespace js { +namespace jit { + +// Possible register allocators which may be used. +enum IonRegisterAllocator { + RegisterAllocator_LSRA, + RegisterAllocator_Backtracking, + RegisterAllocator_Stupid +}; + +enum IonGvnKind { + GVN_Optimistic, + GVN_Pessimistic +}; + +struct IonOptions +{ + bool checkGraphConsistency; +#ifdef CHECK_OSIPOINT_REGISTERS + bool checkOsiPointRegisters; +#endif + bool checkRangeAnalysis; + bool checkThreadSafety; + bool compileTryCatch; + bool disableGvn; + bool disableLicm; + bool disableInlining; + bool disableEdgeCaseAnalysis; + bool disableRangeAnalysis; + bool disableUce; + bool disableEaa; + bool eagerCompilation; + bool forceDefaultIonUsesBeforeCompile; + uint32_t forcedDefaultIonUsesBeforeCompile; + bool forceGvnKind; + IonGvnKind forcedGvnKind; + bool forceRegisterAllocator; + IonRegisterAllocator forcedRegisterAllocator; + bool limitScriptSize; + bool osr; + uint32_t baselineUsesBeforeCompile; + uint32_t exceptionBailoutThreshold; + uint32_t frequentBailoutThreshold; + uint32_t maxStackArgs; + uint32_t osrPcMismatchesBeforeRecompile; + uint32_t smallFunctionMaxBytecodeLength_; + uint32_t usesBeforeCompilePar; + + IonOptions(); + bool isSmallFunction(JSScript *script) const; + void setEagerCompilation(); + void setUsesBeforeCompile(uint32_t useCount); + void resetUsesBeforeCompile(); +}; + +extern IonOptions js_IonOptions; + +} // namespace jit +} // namespace js + +#endif // JS_ION + +#endif /* jit_IonOptions_h */
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3545,17 +3545,17 @@ bool LIRGenerator::visitBlock(MBasicBlock *block) { current = block->lir(); updateResumeState(block); if (!definePhis()) return false; - if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) { + if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) { if (!add(new(alloc()) LLabel())) return false; } for (MInstructionIterator iter = block->begin(); *iter != block->lastIns(); iter++) { if (!visitInstruction(*iter)) return false; }
--- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -19,16 +19,19 @@ #include "vm/StringObject-inl.h" namespace js { namespace jit { IonBuilder::InliningStatus IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) { + if (!optimizationInfo().inlineNative()) + return InliningStatus_NotInlined; + // Array natives. if (native == js_Array) return inlineArray(callInfo); if (native == js::array_pop) return inlineArrayPopShift(callInfo, MArrayPopShift::Pop); if (native == js::array_shift) return inlineArrayPopShift(callInfo, MArrayPopShift::Shift); if (native == js::array_push)
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9199,23 +9199,26 @@ class MHaveSameClass TypePolicy *typePolicy() { return this; } AliasSet getAliasSet() const { return AliasSet::None(); } }; +// Increase the usecount of the provided script upon execution and test if +// the usecount surpasses the threshold. Upon hit it will recompile the +// outermost script (i.e. not the inlined script). class MRecompileCheck : public MNullaryInstruction { JSScript *script_; uint32_t recompileThreshold_; - MRecompileCheck(JSScript *script_, uint32_t recompileThreshold) - : script_(script_), + MRecompileCheck(JSScript *script, uint32_t recompileThreshold) + : script_(script), recompileThreshold_(recompileThreshold) { setGuard(); } public: INSTRUCTION_HEADER(RecompileCheck);
--- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -24,37 +24,42 @@ #include "jit/RegisterSets.h" namespace js { namespace jit { class MBasicBlock; class MIRGraph; class MStart; +class OptimizationInfo; class MIRGenerator { public: - MIRGenerator(CompileCompartment *compartment, TempAllocator *alloc, MIRGraph *graph, CompileInfo *info); + MIRGenerator(CompileCompartment *compartment, TempAllocator *alloc, MIRGraph *graph, + CompileInfo *info, const OptimizationInfo *optimizationInfo); TempAllocator &alloc() { return *alloc_; } MIRGraph &graph() { return *graph_; } bool ensureBallast() { return alloc().ensureBallast(); } const JitRuntime *jitRuntime() const { return GetIonContext()->runtime->jitRuntime(); } CompileInfo &info() { return *info_; } + const OptimizationInfo &optimizationInfo() const { + return *optimizationInfo_; + } template <typename T> T * allocate(size_t count = 1) { return reinterpret_cast<T *>(alloc().allocate(sizeof(T) * count)); } // Set an error state and prints a message. Returns false so errors can be // propagated up. @@ -122,16 +127,17 @@ class MIRGenerator return asmJSGlobalAccesses_; } public: CompileCompartment *compartment; protected: CompileInfo *info_; + const OptimizationInfo *optimizationInfo_; TempAllocator *alloc_; JSFunction *fun_; uint32_t nslots_; MIRGraph *graph_; bool error_; size_t cancelBuild_; uint32_t maxAsmJSStackArgBytes_;
--- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -12,19 +12,21 @@ #include "jit/IonSpewer.h" #include "jit/MIR.h" #include "jit/MIRGenerator.h" using namespace js; using namespace js::jit; MIRGenerator::MIRGenerator(CompileCompartment *compartment, - TempAllocator *alloc, MIRGraph *graph, CompileInfo *info) + TempAllocator *alloc, MIRGraph *graph, CompileInfo *info, + const OptimizationInfo *optimizationInfo) : compartment(compartment), info_(info), + optimizationInfo_(optimizationInfo), alloc_(alloc), graph_(graph), error_(false), cancelBuild_(0), maxAsmJSStackArgBytes_(0), performsAsmJSCall_(false), asmJSHeapAccesses_(*alloc), asmJSGlobalAccesses_(*alloc),
--- a/js/src/jit/UnreachableCodeElimination.cpp +++ b/js/src/jit/UnreachableCodeElimination.cpp @@ -77,18 +77,18 @@ UnreachableCodeElimination::removeUnmark if (rerunAliasAnalysis_) { AliasAnalysis analysis(mir_, graph_); if (!analysis.analyze()) return false; } // Pass 5: It's important for optimizations to re-run GVN (and in // turn alias analysis) after UCE if we eliminated branches. - if (rerunAliasAnalysis_ && js_IonOptions.gvn) { - ValueNumberer gvn(mir_, graph_, js_IonOptions.gvnIsOptimistic); + if (rerunAliasAnalysis_ && mir_->optimizationInfo().gvnEnabled()) { + ValueNumberer gvn(mir_, graph_, mir_->optimizationInfo().gvnKind() == GVN_Optimistic); if (!gvn.clear() || !gvn.analyze()) return false; IonSpewPass("GVN-after-UCE"); AssertExtendedGraphCoherency(graph_); if (mir_->shouldCancel("GVN-after-UCE")) return false; }
--- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -71,17 +71,17 @@ LIRGeneratorShared::defineFixed(LInstruc LDefinition def(type, LDefinition::PRESET); def.setOutput(output); // Add an LNop to avoid regalloc problems if the next op uses this value // with a fixed or at-start policy. if (!define(lir, mir, def)) return false; - if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) { + if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) { if (!add(new(alloc()) LNop)) return false; } return true; } template <size_t Ops, size_t Temps> bool @@ -158,17 +158,17 @@ LIRGeneratorShared::defineReturn(LInstru lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg))); break; } mir->setVirtualRegister(vreg); if (!add(lir)) return false; - if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) { + if (gen->optimizationInfo().registerAllocator() == RegisterAllocator_LSRA) { if (!add(new(alloc()) LNop)) return false; } return true; } // In LIR, we treat booleans and integers as the same low-level type (INTEGER).
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5975,28 +5975,31 @@ JS_SetParallelIonCompilationEnabled(JSCo cx->runtime()->setParallelIonCompilationEnabled(enabled); #endif } JS_PUBLIC_API(void) JS_SetGlobalJitCompilerOption(JSContext *cx, JSJitCompilerOption opt, uint32_t value) { #ifdef JS_ION - jit::IonOptions defaultValues; switch (opt) { case JSJITCOMPILER_BASELINE_USECOUNT_TRIGGER: - if (value == uint32_t(-1)) + if (value == uint32_t(-1)) { + jit::IonOptions defaultValues; value = defaultValues.baselineUsesBeforeCompile; + } jit::js_IonOptions.baselineUsesBeforeCompile = value; break; case JSJITCOMPILER_ION_USECOUNT_TRIGGER: - if (value == uint32_t(-1)) - value = defaultValues.usesBeforeCompile; - jit::js_IonOptions.usesBeforeCompile = value; + if (value == uint32_t(-1)) { + jit::js_IonOptions.resetUsesBeforeCompile(); + break; + } + jit::js_IonOptions.setUsesBeforeCompile(value); if (value == 0) jit::js_IonOptions.setEagerCompilation(); break; case JSJITCOMPILER_ION_ENABLE: if (value == 1) { JS::ContextOptionsRef(cx).setIon(true); IonSpew(js::jit::IonSpew_Scripts, "Enable ion"); } else if (value == 0) {
--- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -585,18 +585,20 @@ class JSScript : public js::gc::Barriere uint32_t natoms_; /* length of atoms array */ /* Range of characters in scriptSource which contains this script's source. */ uint32_t sourceStart_; uint32_t sourceEnd_; uint32_t useCount; /* Number of times the script has been called - * or has had backedges taken. Reset if the - * script's JIT code is forcibly discarded. */ + * or has had backedges taken. When running in + * ion, also increased for any inlined scripts. + * Reset if the script's JIT code is forcibly + * discarded. */ #ifdef DEBUG // Unique identifier within the compartment for this script, used for // printing analysis information. uint32_t id_; uint32_t idpad; #endif
--- a/js/src/jsworkers.cpp +++ b/js/src/jsworkers.cpp @@ -741,17 +741,16 @@ WorkerThread::handleIonWorkload(WorkerTh { JS_ASSERT(state.isLocked()); JS_ASSERT(state.canStartIonCompile()); JS_ASSERT(idle()); ionBuilder = state.ionWorklist.popCopy(); DebugOnly<ExecutionMode> executionMode = ionBuilder->info().executionMode(); - JS_ASSERT(jit::GetIonScript(ionBuilder->script(), executionMode) == ION_COMPILING_SCRIPT); #if JS_TRACE_LOGGING AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::ION_BACKGROUND_COMPILER), TraceLogging::ION_COMPILE_START, TraceLogging::ION_COMPILE_STOP, ionBuilder->script()); #endif
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -252,16 +252,18 @@ if CONFIG['ENABLE_ION']: 'jit/EdgeCaseAnalysis.cpp', 'jit/EffectiveAddressAnalysis.cpp', 'jit/Ion.cpp', 'jit/IonAnalysis.cpp', 'jit/IonBuilder.cpp', 'jit/IonCaches.cpp', 'jit/IonFrames.cpp', 'jit/IonMacroAssembler.cpp', + 'jit/IonOptimizationLevels.cpp', + 'jit/IonOptions.cpp', 'jit/IonSpewer.cpp', 'jit/JSONSpewer.cpp', 'jit/LICM.cpp', 'jit/LinearScan.cpp', 'jit/LIR.cpp', 'jit/LiveRangeAllocator.cpp', 'jit/Lowering.cpp', 'jit/MCallOptimize.cpp',
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5435,64 +5435,67 @@ ProcessArgs(JSContext *cx, JSObject *obj } if (op->getBoolOption("no-baseline")) { enableBaseline = false; JS::ContextOptionsRef(cx).toggleBaseline(); } if (const char *str = op->getStringOption("ion-gvn")) { - if (strcmp(str, "off") == 0) - jit::js_IonOptions.gvn = false; - else if (strcmp(str, "pessimistic") == 0) - jit::js_IonOptions.gvnIsOptimistic = false; - else if (strcmp(str, "optimistic") == 0) - jit::js_IonOptions.gvnIsOptimistic = true; - else + if (strcmp(str, "off") == 0) { + jit::js_IonOptions.disableGvn = true; + } else if (strcmp(str, "pessimistic") == 0) { + jit::js_IonOptions.forceGvnKind = true; + jit::js_IonOptions.forcedGvnKind = jit::GVN_Pessimistic; + } else if (strcmp(str, "optimistic") == 0) { + jit::js_IonOptions.forceGvnKind = true; + jit::js_IonOptions.forcedGvnKind = jit::GVN_Optimistic; + } else { return OptionFailure("ion-gvn", str); + } } if (const char *str = op->getStringOption("ion-licm")) { if (strcmp(str, "on") == 0) - jit::js_IonOptions.licm = true; + jit::js_IonOptions.disableLicm = false; else if (strcmp(str, "off") == 0) - jit::js_IonOptions.licm = false; + jit::js_IonOptions.disableLicm = true; else return OptionFailure("ion-licm", str); } if (const char *str = op->getStringOption("ion-edgecase-analysis")) { if (strcmp(str, "on") == 0) - jit::js_IonOptions.edgeCaseAnalysis = true; + jit::js_IonOptions.disableEdgeCaseAnalysis = false; else if (strcmp(str, "off") == 0) - jit::js_IonOptions.edgeCaseAnalysis = false; + jit::js_IonOptions.disableEdgeCaseAnalysis = true; else return OptionFailure("ion-edgecase-analysis", str); } if (const char *str = op->getStringOption("ion-range-analysis")) { if (strcmp(str, "on") == 0) - jit::js_IonOptions.rangeAnalysis = true; + jit::js_IonOptions.disableRangeAnalysis = false; else if (strcmp(str, "off") == 0) - jit::js_IonOptions.rangeAnalysis = false; + jit::js_IonOptions.disableRangeAnalysis = true; else return OptionFailure("ion-range-analysis", str); } if (op->getBoolOption("ion-check-range-analysis")) jit::js_IonOptions.checkRangeAnalysis = true; if (op->getBoolOption("ion-check-thread-safety")) jit::js_IonOptions.checkThreadSafety = true; if (const char *str = op->getStringOption("ion-inlining")) { if (strcmp(str, "on") == 0) - jit::js_IonOptions.inlining = true; + jit::js_IonOptions.disableInlining = false; else if (strcmp(str, "off") == 0) - jit::js_IonOptions.inlining = false; + jit::js_IonOptions.disableInlining = true; else return OptionFailure("ion-inlining", str); } if (const char *str = op->getStringOption("ion-osr")) { if (strcmp(str, "on") == 0) jit::js_IonOptions.osr = true; else if (strcmp(str, "off") == 0) @@ -5507,34 +5510,38 @@ ProcessArgs(JSContext *cx, JSObject *obj else if (strcmp(str, "off") == 0) jit::js_IonOptions.limitScriptSize = false; else return OptionFailure("ion-limit-script-size", str); } int32_t useCount = op->getIntOption("ion-uses-before-compile"); if (useCount >= 0) - jit::js_IonOptions.usesBeforeCompile = useCount; + jit::js_IonOptions.setUsesBeforeCompile(useCount); useCount = op->getIntOption("baseline-uses-before-compile"); if (useCount >= 0) jit::js_IonOptions.baselineUsesBeforeCompile = useCount; if (op->getBoolOption("baseline-eager")) jit::js_IonOptions.baselineUsesBeforeCompile = 0; if (const char *str = op->getStringOption("ion-regalloc")) { - if (strcmp(str, "lsra") == 0) - jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_LSRA; - else if (strcmp(str, "backtracking") == 0) - jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_Backtracking; - else if (strcmp(str, "stupid") == 0) - jit::js_IonOptions.registerAllocator = jit::RegisterAllocator_Stupid; - else + if (strcmp(str, "lsra") == 0) { + jit::js_IonOptions.forceRegisterAllocator = true; + jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_LSRA; + } else if (strcmp(str, "backtracking") == 0) { + jit::js_IonOptions.forceRegisterAllocator = true; + jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_Backtracking; + } else if (strcmp(str, "stupid") == 0) { + jit::js_IonOptions.forceRegisterAllocator = true; + jit::js_IonOptions.forcedRegisterAllocator = jit::RegisterAllocator_Stupid; + } else { return OptionFailure("ion-regalloc", str); + } } if (op->getBoolOption("ion-eager")) jit::js_IonOptions.setEagerCompilation(); if (op->getBoolOption("ion-compile-try-catch")) jit::js_IonOptions.compileTryCatch = true;