Bug 939614: IonMonkey: Use logic in UsesBeforeCompile to get actual needed usecount, r=jandem
☠☠ backed out by ad9dc3482bd4 ☠ ☠
authorHannes Verschore <hv1989@gmail.com>
Thu, 12 Dec 2013 15:14:13 +0100
changeset 176241 31d0d40d4a7ce9aac74aa2f4fcad36b47e70595f
parent 176240 2b87186ddb6df93d330d948e7a9bf5eef558ba2d
child 176242 62a6568217da8eee2fd6a977659a65e2a18ddcd8
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs939614
milestone29.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 939614: IonMonkey: Use logic in UsesBeforeCompile to get actual needed usecount, r=jandem
js/src/jit/BaselineCompiler.cpp
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonOptimizationLevels.cpp
js/src/jit/IonOptimizationLevels.h
js/src/jit/JitOptions.h
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -632,17 +632,18 @@ 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 = UsesBeforeIonCompile(script, pc);
+    const OptimizationInfo *info = js_IonOptimizations.get(js_IonOptimizations.firstLevel());
+    uint32_t minUses = info->usesBeforeCompile(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/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1797,27 +1797,16 @@ CheckScript(JSContext *cx, JSScript *scr
     if (!script->compileAndGo()) {
         IonSpew(IonSpew_Abort, "not compile-and-go");
         return false;
     }
 
     return true;
 }
 
-// Longer scripts can only be compiled off thread, as these compilations
-// can be expensive and stall the main thread for too long.
-static const uint32_t MAX_OFF_THREAD_SCRIPT_SIZE = 100 * 1000;
-static const uint32_t MAX_MAIN_THREAD_SCRIPT_SIZE = 2 * 1000;
-static const uint32_t MAX_MAIN_THREAD_LOCALS_AND_ARGS = 256;
-
-// DOM Worker runtimes don't have off thread compilation, but can also compile
-// larger scripts since this doesn't stall the main thread.
-static const uint32_t MAX_DOM_WORKER_SCRIPT_SIZE = 16 * 1000;
-static const uint32_t MAX_DOM_WORKER_LOCALS_AND_ARGS = 2048;
-
 static MethodStatus
 CheckScriptSize(JSContext *cx, JSScript* script)
 {
     if (!js_JitOptions.limitScriptSize)
         return Method_Compiled;
 
     if (script->length() > MAX_OFF_THREAD_SCRIPT_SIZE) {
         // Some scripts are so large we never try to Ion compile them.
@@ -1871,24 +1860,24 @@ CanIonCompileScript(JSContext *cx, Handl
 {
     if (!script->canIonCompile() || !CheckScript(cx, script, osr))
         return false;
 
     return CheckScriptSize(cx, script) == Method_Compiled;
 }
 
 static OptimizationLevel
-GetOptimizationLevel(HandleScript script, ExecutionMode executionMode)
+GetOptimizationLevel(HandleScript script, jsbytecode *pc, ExecutionMode executionMode)
 {
     if (executionMode == ParallelExecution)
         return Optimization_Normal;
 
     JS_ASSERT(executionMode == SequentialExecution);
 
-    return js_IonOptimizations.levelForUseCount(script->getUseCount());
+    return js_IonOptimizations.levelForScript(script, pc);
 }
 
 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));
@@ -1911,17 +1900,17 @@ 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);
+    OptimizationLevel optimizationLevel = GetOptimizationLevel(script, osrPc, executionMode);
     if (optimizationLevel == Optimization_DontCompile)
         return Method_Skipped;
 
     IonScript *scriptIon = GetIonScript(script, executionMode);
     if (scriptIon) {
         if (!scriptIon->method())
             return Method_CantCompile;
 
@@ -2725,49 +2714,16 @@ jit::ForbidCompilation(JSContext *cx, JS
 
       default:
         MOZ_ASSUME_UNREACHABLE("No such execution mode");
     }
 
     MOZ_ASSUME_UNREACHABLE("No such execution mode");
 }
 
-uint32_t
-jit::UsesBeforeIonCompile(JSScript *script, jsbytecode *pc)
-{
-    JS_ASSERT(pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY);
-
-    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);
-
-    uint32_t numLocalsAndArgs = analyze::TotalSlots(script);
-    if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
-        minUses = minUses * (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS);
-
-    if (JSOp(*pc) != JSOP_LOOPENTRY || js_JitOptions.eagerCompilation)
-        return minUses;
-
-    // It's more efficient to enter outer loops, rather than inner loops, via OSR.
-    // To accomplish this, we use a slightly higher threshold for inner loops.
-    // Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
-    uint32_t loopDepth = GET_UINT8(pc);
-    JS_ASSERT(loopDepth > 0);
-    return minUses + loopDepth * 100;
-}
-
 void
 AutoFlushCache::updateTop(uintptr_t p, size_t len)
 {
     IonContext *ictx = MaybeGetIonContext();
     JitRuntime *jrt = (ictx != nullptr) ? const_cast<JitRuntime *>(ictx->runtime->jitRuntime()) : nullptr;
     if (!jrt || !jrt->flusher())
         JSC::ExecutableAllocator::cacheFlush((void*)p, len);
     else
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -174,17 +174,16 @@ IsIonInlinablePC(jsbytecode *pc) {
 inline bool
 TooManyArguments(unsigned nargs)
 {
     return (nargs >= SNAPSHOT_MAX_NARGS || nargs > js_JitOptions.maxStackArgs);
 }
 
 void ForbidCompilation(JSContext *cx, JSScript *script);
 void ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode);
-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/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6008,17 +6008,18 @@ IonBuilder::insertRecompileCheck()
     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()));
+    uint32_t useCount = info->usesBeforeCompile(topBuilder->script());
+    current->add(MRecompileCheck::New(alloc(), topBuilder->script(), useCount));
 }
 
 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/IonOptimizationLevels.cpp
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -51,58 +51,90 @@ OptimizationInfo::initAsmjsOptimizationI
     // Take normal option values for not specified values.
     initNormalOptimizationInfo();
 
     level_ = Optimization_AsmJS;
     edgeCaseAnalysis_ = false;
     eliminateRedundantChecks_ = false;
 }
 
+uint32_t
+OptimizationInfo::usesBeforeCompile(JSScript *script, jsbytecode *pc) const
+{
+    JS_ASSERT(pc == nullptr || pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY);
+
+    if (pc == script->code())
+        pc = nullptr;
+
+    uint32_t minUses = usesBeforeCompile_;
+    if (js_JitOptions.forceDefaultIonUsesBeforeCompile)
+        minUses = js_JitOptions.forcedDefaultIonUsesBeforeCompile;
+
+    // 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);
+
+    uint32_t numLocalsAndArgs = analyze::TotalSlots(script);
+    if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
+        minUses = minUses * (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS);
+
+    if (!pc || js_JitOptions.eagerCompilation)
+        return minUses;
+
+    // It's more efficient to enter outer loops, rather than inner loops, via OSR.
+    // To accomplish this, we use a slightly higher threshold for inner loops.
+    // Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
+    uint32_t loopDepth = GET_UINT8(pc);
+    JS_ASSERT(loopDepth > 0);
+    return minUses + loopDepth * 100;
+}
+
 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)
+OptimizationInfos::nextLevel(OptimizationLevel level) const
 {
     JS_ASSERT(!isLastLevel(level));
     switch (level) {
       case Optimization_DontCompile:
         return Optimization_Normal;
       default:
         MOZ_ASSUME_UNREACHABLE("Unknown optimization level.");
     }
 }
 
+OptimizationLevel
+OptimizationInfos::firstLevel() const
+{
+    return nextLevel(Optimization_DontCompile);
+}
+
 bool
-OptimizationInfos::isLastLevel(OptimizationLevel level)
+OptimizationInfos::isLastLevel(OptimizationLevel level) const
 {
     return level == Optimization_Normal;
 }
 
 OptimizationLevel
-OptimizationInfos::levelForUseCount(uint32_t useCount)
+OptimizationInfos::levelForScript(JSScript *script, jsbytecode *pc) const
 {
     OptimizationLevel prev = Optimization_DontCompile;
 
     while (!isLastLevel(prev)) {
         OptimizationLevel level = nextLevel(prev);
         const OptimizationInfo *info = get(level);
-        if (useCount < info->usesBeforeCompile())
+        if (script->getUseCount() < info->usesBeforeCompile(script, pc))
             return prev;
 
         prev = level;
     }
 
     return prev;
 }
 
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -101,21 +101,17 @@ class OptimizationInfo
     bool inlineInterpreted() const {
         return inlineInterpreted_ && !js_JitOptions.disableInlining;
     }
 
     bool inlineNative() const {
         return inlineNative_ && !js_JitOptions.disableInlining;
     }
 
-    uint32_t usesBeforeCompile() const {
-        if (js_JitOptions.forceDefaultIonUsesBeforeCompile)
-            return js_JitOptions.forcedDefaultIonUsesBeforeCompile;
-        return usesBeforeCompile_;
-    }
+    uint32_t usesBeforeCompile(JSScript *script, jsbytecode *pc = nullptr) const;
 
     bool gvnEnabled() const {
         return gvn_ && !js_JitOptions.disableGvn;
     }
 
     bool licmEnabled() const {
         return licm_ && !js_JitOptions.disableLicm;
     }
@@ -166,38 +162,42 @@ class OptimizationInfo
         return inlineMaxTotalBytecodeLength_;
     }
 
     uint32_t inliningMaxCallerBytecodeLength() const {
         return inlineMaxTotalBytecodeLength_;
     }
 
     uint32_t usesBeforeInlining() const {
-        return usesBeforeCompile() * usesBeforeInliningFactor_;
+        uint32_t usesBeforeCompile = usesBeforeCompile_;
+        if (js_JitOptions.forceDefaultIonUsesBeforeCompile)
+            usesBeforeCompile = js_JitOptions.forcedDefaultIonUsesBeforeCompile;
+        return usesBeforeCompile * usesBeforeInliningFactor_;
     }
 };
 
 class OptimizationInfos
 {
   private:
     OptimizationInfo infos_[Optimization_Count - 1];
 
   public:
     OptimizationInfos();
 
-    const OptimizationInfo *get(OptimizationLevel level) {
+    const OptimizationInfo *get(OptimizationLevel level) const {
         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);
+    OptimizationLevel nextLevel(OptimizationLevel level) const;
+    OptimizationLevel firstLevel() const;
+    bool isLastLevel(OptimizationLevel level) const;
+    OptimizationLevel levelForScript(JSScript *script, jsbytecode *pc = nullptr) const;
 };
 
 extern OptimizationInfos js_IonOptimizations;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonOptimizationLevels_h */
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -10,16 +10,27 @@
 #include "jit/IonTypes.h"
 #include "js/TypeDecls.h"
 
 #ifdef JS_ION
 
 namespace js {
 namespace jit {
 
+// Longer scripts can only be compiled off thread, as these compilations
+// can be expensive and stall the main thread for too long.
+static const uint32_t MAX_OFF_THREAD_SCRIPT_SIZE = 100 * 1000;
+static const uint32_t MAX_MAIN_THREAD_SCRIPT_SIZE = 2 * 1000;
+static const uint32_t MAX_MAIN_THREAD_LOCALS_AND_ARGS = 256;
+
+// DOM Worker runtimes don't have off thread compilation, but can also compile
+// larger scripts since this doesn't stall the main thread.
+static const uint32_t MAX_DOM_WORKER_SCRIPT_SIZE = 16 * 1000;
+static const uint32_t MAX_DOM_WORKER_LOCALS_AND_ARGS = 2048;
+
 // Possible register allocators which may be used.
 enum IonRegisterAllocator {
     RegisterAllocator_LSRA,
     RegisterAllocator_Backtracking,
     RegisterAllocator_Stupid
 };
 
 enum IonGvnKind {