[INFER] Reset use count when recompiling, bug 650163.
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 09 May 2011 15:11:32 -0700
changeset 75008 32e8c937a40944a7c2666dd07839e304d9075eb2
parent 75007 cb9c34a8b2b47b0c66f7431844eecc26aaf6d1cf
child 75009 49c7baf9872c7ef3d25859f7aaa964f1930e63a3
child 75012 d1724a9944bd0454772837e72ed938fab739195e
push id1199
push userjorendorff@mozilla.com
push dateSat, 13 Aug 2011 18:32:33 +0000
treeherdermozilla-inbound@080fece621e4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs650163
milestone6.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
[INFER] Reset use count when recompiling, bug 650163.
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsinfer.cpp
js/src/jsscript.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/MethodJIT-inl.h
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/Retcon.cpp
js/src/methodjit/Retcon.h
js/src/methodjit/StubCalls.cpp
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -141,19 +141,16 @@ JSCompartment::init(JSContext *cx)
 #endif
 
 #if ENABLE_YARR_JIT
     regExpAllocator = rt->new_<JSC::ExecutableAllocator>();
     if (!regExpAllocator)
         return false;
 #endif
 
-    if (!backEdgeTable.init())
-        return false;
-
 #ifdef JS_METHODJIT
     if (!(jaegerCompartment = rt->new_<mjit::JaegerCompartment>()))
         return false;
     return jaegerCompartment->Initialize();
 #else
     return true;
 #endif
 }
@@ -647,26 +644,8 @@ MathCache *
 JSCompartment::allocMathCache(JSContext *cx)
 {
     JS_ASSERT(!mathCache);
     mathCache = cx->new_<MathCache>();
     if (!mathCache)
         js_ReportOutOfMemory(cx);
     return mathCache;
 }
-
-size_t
-JSCompartment::backEdgeCount(jsbytecode *pc) const
-{
-    if (BackEdgeMap::Ptr p = backEdgeTable.lookup(pc))
-        return p->value;
-
-    return 0;
-}
-
-size_t
-JSCompartment::incBackEdgeCount(jsbytecode *pc)
-{
-    if (BackEdgeMap::Ptr p = backEdgeTable.lookupWithDefault(pc, 0))
-        return ++p->value;
-    return 1;  /* oom not reported by backEdgeTable, so ignore. */
-}
-
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -504,31 +504,21 @@ struct JS_FRIEND_API(JSCompartment) {
 
     js::DtoaCache dtoaCache;
 
   private:
     js::MathCache                *mathCache;
 
     js::MathCache *allocMathCache(JSContext *cx);
 
-    typedef js::HashMap<jsbytecode*,
-                        size_t,
-                        js::DefaultHasher<jsbytecode*>,
-                        js::SystemAllocPolicy> BackEdgeMap;
-
-    BackEdgeMap                  backEdgeTable;
-
     JSCompartment *thisForCtor() { return this; }
   public:
     js::MathCache *getMathCache(JSContext *cx) {
         return mathCache ? mathCache : allocMathCache(cx);
     }
-
-    size_t backEdgeCount(jsbytecode *pc) const;
-    size_t incBackEdgeCount(jsbytecode *pc);
 };
 
 #define JS_SCRIPTS_TO_GC(cx)    ((cx)->compartment->scriptsToGC)
 #define JS_PROPERTY_TREE(cx)    ((cx)->compartment->propertyTree)
 
 #ifdef DEBUG
 #define JS_COMPARTMENT_METER(x) x
 #else
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1963,44 +1963,16 @@ TypeCompartment::dynamicPush(JSContext *
      * parent has constraints listening to type changes in this one (it won't
      * necessarily, if we have condensed the constraints but not reanalyzed the
      * parent). The parent is listening for isUninlineable changes on the
      * function, so we can treat this as a state change on the function to
      * trigger any necessary reanalysis.
      */
     if (script->fun && !script->fun->getType()->unknownProperties())
         ObjectStateChange(cx, script->fun->getType(), false);
-
-    /*
-     * If we unexpectedly read a hole out of an array, mark all array reads in
-     * the script as undefined. :FIXME: bug 650163 remove hack.
-     */
-    if (script->hasAnalysis() && script->analysis(cx)->ranInference() &&
-        JSOp(*pc) == JSOP_GETELEM && type == TYPE_UNDEFINED) {
-        analyze::ScriptAnalysis *analysis = script->analysis(cx);
-        unsigned offset = 0;
-        while (offset < script->length) {
-            if (JSOp(script->code[offset]) == JSOP_GETELEM && analysis->maybeCode(offset)) {
-                TypeSet *pushed = analysis->pushedTypes(offset, 0);
-                if (!pushed->hasType(TYPE_UNDEFINED)) {
-                    pushed->addType(cx, TYPE_UNDEFINED);
-                    TypeResult *result = (TypeResult *) cx->calloc_(sizeof(TypeResult));
-                    if (!result) {
-                        setPendingNukeTypes(cx);
-                        return;
-                    }
-                    result->offset = offset;
-                    result->type = TYPE_UNDEFINED;
-                    result->next = script->typeResults;
-                    script->typeResults = result;
-                }
-            }
-            offset += analyze::GetBytecodeLength(pc);
-        }
-    }
 }
 
 void
 TypeCompartment::processPendingRecompiles(JSContext *cx)
 {
     /* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
     Vector<JSScript*> *pending = pendingRecompiles;
     pendingRecompiles = NULL;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -416,19 +416,21 @@ struct JSScript {
     static JSScript *NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg);
 
     /* FIXME: bug 586181 */
     JSCList         links;      /* Links for compartment script list */
     jsbytecode      *code;      /* bytecodes and their immediate operands */
     uint32          length;     /* length of code vector */
 
   private:
-    uint16          version;    /* JS version under which script was compiled */
+    size_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. */
 
-    size_t          callCount_; /* Number of times the script has been called. */
+    uint16          version;    /* JS version under which script was compiled */
 
   public:
     uint16          nfixed;     /* number of slots besides stack operands in
                                    slot array */
 
     /*
      * Offsets to various array structures from the end of this script, or
      * JSScript::INVALID_OFFSET if the array has length 0.
@@ -628,19 +630,20 @@ struct JSScript {
     inline void **nativeMap(bool constructing);
     inline void *maybeNativeCodeForPC(bool constructing, jsbytecode *pc);
     inline void *nativeCodeForPC(bool constructing, jsbytecode *pc);
 
     js::mjit::JITScript *getJIT(bool constructing) {
         return constructing ? jitCtor : jitNormal;
     }
 
-    size_t callCount() const  { return callCount_; }
-    size_t incCallCount() { return ++callCount_; }
-    size_t *addressOfCallCount() { return &callCount_; }
+    size_t useCount() const  { return useCount_; }
+    size_t incUseCount() { return ++useCount_; }
+    size_t *addressOfUseCount() { return &useCount_; }
+    void resetUseCount() { useCount_ = 0; }
 
     JITScriptStatus getJITStatus(bool constructing) {
         void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal;
         if (addr == NULL)
             return JITScript_None;
         if (addr == JS_UNJITTABLE_SCRIPT)
             return JITScript_Invalid;
         return JITScript_Valid;
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -83,17 +83,17 @@ static const char *OpcodeNames[] = {
 # undef OPDEF
 };
 #endif
 
 /*
  * Number of times a script must be called or had a backedge before we try to
  * inline its calls.
  */
-static const size_t CALLS_BACKEDGES_BEFORE_INLINING = 10000;
+static const size_t USES_BEFORE_INLINING = 10000;
 
 mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, bool isConstructing)
   : BaseCompiler(cx),
     outerScript(outerScript),
     isConstructing(isConstructing),
     ssa(cx, outerScript),
     globalObj(outerScript->global),
     globalSlots((globalObj && globalObj->isGlobal()) ? globalObj->getRawSlots() : NULL),
@@ -133,24 +133,19 @@ mjit::Compiler::Compiler(JSContext *cx, 
     applyTricks(NoApplyTricks)
 {
     JS_ASSERT(!outerScript->isUncachedEval);
 
     /* :FIXME: bug 637856 disabling traceJit if inference is enabled */
     if (cx->typeInferenceEnabled())
         addTraceHints = false;
 
-    /*
-     * Note: we use callCount_ to count both calls and backedges in scripts
-     * after they have been compiled and we are checking to recompile a version
-     * with inline calls. :FIXME: should remove compartment->incBackEdgeCount
-     * and do the same when deciding to initially compile.
-     */
+    /* Once a script starts getting really hot we will inline calls in it. */
     if (!debugMode() && cx->typeInferenceEnabled() &&
-        (outerScript->callCount() >= CALLS_BACKEDGES_BEFORE_INLINING ||
+        (outerScript->useCount() >= USES_BEFORE_INLINING ||
          cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))) {
         inlining_ = true;
     }
 }
 
 CompileStatus
 mjit::Compiler::compile()
 {
@@ -1116,19 +1111,18 @@ mjit::Compiler::finishThisUp(JITScript *
         jitTraceICs[i].jumpTargetPC = traceICs[i].jumpTarget;
 #endif
 
         jitTraceICs[i].hasSlowTraceHint = traceICs[i].slowTraceHint.isSet();
         if (traceICs[i].slowTraceHint.isSet())
             jitTraceICs[i].slowTraceHint = stubCode.locationOf(traceICs[i].slowTraceHint.get());
 #ifdef JS_TRACER
         uint32 hotloop = GetHotloop(cx);
-        uint32 prevCount = cx->compartment->backEdgeCount(traceICs[i].jumpTarget);
         jitTraceICs[i].loopCounterStart = hotloop;
-        jitTraceICs[i].loopCounter = hotloop < prevCount ? 1 : hotloop - prevCount;
+        jitTraceICs[i].loopCounter = hotloop;
 #endif
         
         stubCode.patch(traceICs[i].addrLabel, &jitTraceICs[i]);
     }
 #endif /* JS_MONOIC */
 
     for (size_t i = 0; i < callPatches.length(); i++) {
         CallPatchInfo &patch = callPatches[i];
@@ -3124,27 +3118,27 @@ mjit::Compiler::interruptCheckHelper()
 }
 
 void
 mjit::Compiler::recompileCheckHelper()
 {
     if (inlining() || debugMode() || !analysis->hasFunctionCalls() || !cx->typeInferenceEnabled())
         return;
 
-    size_t *addr = script->addressOfCallCount();
+    size_t *addr = script->addressOfUseCount();
     masm.add32(Imm32(1), AbsoluteAddress(addr));
 #if defined(JS_CPU_X86) || defined(JS_CPU_ARM)
     Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, AbsoluteAddress(addr),
-                              Imm32(CALLS_BACKEDGES_BEFORE_INLINING));
+                              Imm32(USES_BEFORE_INLINING));
 #else
     /* Handle processors that can't load from absolute addresses. */
     RegisterID reg = frame.allocReg();
     masm.move(ImmPtr(addr), reg);
     Jump jump = masm.branch32(Assembler::GreaterThanOrEqual, Address(reg, 0),
-                              Imm32(CALLS_BACKEDGES_BEFORE_INLINING));
+                              Imm32(USES_BEFORE_INLINING));
     frame.freeReg(reg);
 #endif
     stubcc.linkExit(jump, Uses(0));
     stubcc.leave();
 
     OOL_STUBCALL(stubs::RecompileForInline, REJOIN_RESUME);
     stubcc.rejoin(Changes(0));
 }
--- a/js/src/methodjit/MethodJIT-inl.h
+++ b/js/src/methodjit/MethodJIT-inl.h
@@ -45,34 +45,34 @@ namespace js {
 namespace mjit {
 
 enum CompileRequest
 {
     CompileRequest_Interpreter,
     CompileRequest_JIT
 };
 
-/* Number of times a script must be called before we run it in the methodjit. */
-static const size_t CALLS_BEFORE_COMPILE = 16;
-
-/* Number of loop back-edges we execute in the interpreter before methodjitting. */
-static const size_t BACKEDGES_BEFORE_COMPILE = 16;
+/*
+ * Number of times a script must be called or have back edges taken before we
+ * run it in the methodjit.
+ */
+static const size_t USES_BEFORE_COMPILE = 16;
 
 static inline CompileStatus
 CanMethodJIT(JSContext *cx, JSScript *script, StackFrame *fp, CompileRequest request)
 {
     if (!cx->methodJitEnabled)
         return Compile_Abort;
     JITScriptStatus status = script->getJITStatus(fp->isConstructing());
     if (status == JITScript_Invalid)
         return Compile_Abort;
     if (request == CompileRequest_Interpreter &&
         status == JITScript_None &&
         !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
-        script->incCallCount() <= CALLS_BEFORE_COMPILE)
+        script->incUseCount() <= USES_BEFORE_COMPILE)
     {
         return Compile_Skipped;
     }
     if (status == JITScript_None)
         return TryCompile(cx, fp);
     return Compile_Okay;
 }
 
@@ -85,17 +85,17 @@ CanMethodJITAtBranch(JSContext *cx, JSSc
 {
     if (!cx->methodJitEnabled)
         return Compile_Abort;
     JITScriptStatus status = script->getJITStatus(fp->isConstructing());
     if (status == JITScript_Invalid)
         return Compile_Abort;
     if (status == JITScript_None &&
         !cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) &&
-        cx->compartment->incBackEdgeCount(pc) <= BACKEDGES_BEFORE_COMPILE)
+        script->incUseCount() <= USES_BEFORE_COMPILE)
     {
         return Compile_Skipped;
     }
     if (status == JITScript_None)
         return TryCompile(cx, fp);
     return Compile_Okay;
 }
 
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -713,16 +713,19 @@ extern "C" {
             pop edi;
             pop esi;
             pop ebp;
             xor eax, eax
             ret;
         }
     }
 
+    extern "C" void *
+    js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VMFrame &f);
+
     __declspec(naked) void JaegerInterpoline() {
         __asm {
             /* Align the stack to 16 bytes. */
             push esp;
             push eax;
             push edi;
             push esi;
             call js_InternalInterpret;
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -47,16 +47,18 @@
 #include "jsdbgapi.h"
 #include "jsnum.h"
 #include "assembler/assembler/LinkBuffer.h"
 #include "assembler/assembler/RepatchBuffer.h"
 
 #include "jscntxtinlines.h"
 #include "jsinterpinlines.h"
 
+#include "MethodJIT-inl.h"
+
 using namespace js;
 using namespace js::mjit;
 
 namespace js {
 namespace mjit {
 
 AutoScriptRetrapper::~AutoScriptRetrapper()
 {
@@ -320,17 +322,17 @@ Recompiler::Recompiler(JSContext *cx, JS
  * - For VMFrames with a stub call return address in the original script,
  *   redirect to the interpoline.
  *
  * - For VMFrames whose entryncode address (the value of entryfp->ncode before
  *   being clobbered with JaegerTrampolineReturn) is in the original script,
  *   redirect that entryncode to the interpoline.
  */
 void
-Recompiler::recompile()
+Recompiler::recompile(bool resetUses)
 {
     JS_ASSERT(script->hasJITCode());
 
     JaegerSpew(JSpew_Recompile, "recompiling script (file \"%s\") (line \"%d\") (length \"%d\")\n",
                script->filename, script->lineno, script->length);
 
     types::AutoEnterTypeInference enter(cx, true);
 
@@ -413,16 +415,24 @@ Recompiler::recompile()
         cleanup(script->jitNormal);
         ReleaseScriptCode(cx, script, true);
     }
     if (script->jitCtor) {
         cleanup(script->jitCtor);
         ReleaseScriptCode(cx, script, false);
     }
 
+    if (resetUses) {
+        /*
+         * Wait for the script to get warm again before doing another compile,
+         * unless we are recompiling *because* the script got hot.
+         */
+        script->resetUseCount();
+    }
+
     cx->compartment->types.recompilations++;
 }
 
 void
 Recompiler::cleanup(JITScript *jit)
 {
     while (!JS_CLIST_IS_EMPTY(&jit->callers)) {
         JaegerSpew(JSpew_Recompile, "Purging IC caller\n");
--- a/js/src/methodjit/Retcon.h
+++ b/js/src/methodjit/Retcon.h
@@ -81,17 +81,17 @@ class AutoScriptRetrapper
  * ever change the code associated with a JSScript, or otherwise would cause
  * existing JITed code to be incorrect, you /must/ use this to invalidate the
  * JITed code, fixing up the stack in the process.
  */
 class Recompiler {
 public:
     Recompiler(JSContext *cx, JSScript *script);
 
-    void recompile();
+    void recompile(bool resetUses = true);
 
     static void
     expandInlineFrames(JSContext *cx, StackFrame *fp, mjit::CallSite *inlined,
                        StackFrame *next, VMFrame *f);
 
 private:
     JSContext *cx;
     JSScript *script;
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1297,17 +1297,17 @@ stubs::Interrupt(VMFrame &f, jsbytecode 
         THROW();
 }
 
 void JS_FASTCALL
 stubs::RecompileForInline(VMFrame &f)
 {
     ExpandInlineFrames(f.cx, true);
     Recompiler recompiler(f.cx, f.script());
-    recompiler.recompile();
+    recompiler.recompile(/* resetUses */ false);
 }
 
 void JS_FASTCALL
 stubs::Trap(VMFrame &f, uint32 trapTypes)
 {
     Value rval;
 
     /*