Bug 606650 - Treat typeof() tests as constant in JIT profiler (r=dmandelin)
authorBill McCloskey <wmccloskey@mozilla.com>
Tue, 26 Oct 2010 12:03:16 -0700
changeset 56603 81fa7d0b8f00981f5fa0665d68e40211e67e2ab8
parent 56602 ceec1a6b5566b5cae142c2bf13f7f6f87a58cea6
child 56604 94080153175cf2c571557195853074aed8f32811
push id16602
push userrsayre@mozilla.com
push dateWed, 27 Oct 2010 01:10:03 +0000
treeherdermozilla-central@7b83033bb6f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmandelin
bugs606650
milestone2.0b8pre
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 606650 - Treat typeof() tests as constant in JIT profiler (r=dmandelin)
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -16768,21 +16768,21 @@ LoopProfile::LoopProfile(JSScript *scrip
       bottom(bottom),
       hits(0),
       profiled(false),
       traceOK(false),
       numAllOps(0),
       numSelfOps(0),
       numSelfOpsMult(0),
       branchMultiplier(1),
-      prevOp(JSOP_LIMIT),
       shortLoop(false),
       maybeShortLoop(false),
       numInnerLoops(0),
-      loopStackDepth(0)
+      loopStackDepth(0),
+      sp(0)
 {
     memset(allOps, 0, sizeof(allOps));
     memset(selfOps, 0, sizeof(selfOps));
 }
 
 MonitorResult
 LoopProfile::profileLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
@@ -16937,17 +16937,16 @@ PCWithinLoop(JSScript *script, jsbytecod
 {
     return script != loop.script || (pc >= loop.top && pc <= loop.bottom);
 }
 
 LoopProfile::ProfileAction
 LoopProfile::profileOperation(JSContext* cx, JSOp op)
 {
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
-    JSOp prev = prevOp;
 
     if (profiled) {
         tm->profile = NULL;
         return ProfComplete;
     }
 
     jsbytecode *pc = cx->regs->pc;
     JSScript *script = cx->fp()->maybeScript();
@@ -17007,27 +17006,22 @@ LoopProfile::profileOperation(JSContext*
     }
 
     if (op == JSOP_EVAL)
         increment(OP_EVAL);
 
     if (op == JSOP_NEW)
         increment(OP_NEW);
 
-    if (op == JSOP_INT8)
-        prevConst = GET_INT8(cx->regs->pc);
-
     if (op == JSOP_GETELEM || op == JSOP_SETELEM) {
         Value& lval = cx->regs->sp[op == JSOP_GETELEM ? -2 : -3];
         if (lval.isObject() && js_IsTypedArray(&lval.toObject()))
             increment(OP_TYPED_ARRAY);
     }
 
-    prevOp = op;
-
     if (op == JSOP_CALL) {
         increment(OP_CALL);
 
         uintN argc = GET_ARGC(cx->regs->pc);
         Value &v = cx->regs->sp[-((int)argc + 2)];
         JSObject *callee;
         if (IsFunctionObject(v, &callee)) {
             JSFunction *fun = callee->getFunctionPrivate();
@@ -17058,42 +17052,70 @@ LoopProfile::profileOperation(JSContext*
         || op == JSOP_LE || op == JSOP_GE || op == JSOP_IN || op == JSOP_MOREITER)
     {
         const JSCodeSpec *cs = &js_CodeSpec[op];
         ptrdiff_t oplen = cs->length;
         JSScript *script = cx->fp()->script();
         JS_ASSERT(oplen != -1);
 
         if (cx->regs->pc - script->code + oplen < ptrdiff_t(script->length))
-            testPC = cx->regs->pc + oplen;
+            if (cx->regs->pc[oplen] == JSOP_IFEQ || cx->regs->pc[oplen] == JSOP_IFNE)
+                testPC = cx->regs->pc + oplen;
     }
 
     /* Check if we're exiting the loop being profiled. */
     JSOp testOp = js_GetOpcode(cx, cx->fp()->script(), testPC);
     if (testOp == JSOP_IFEQ || testOp == JSOP_IFNE || testOp == JSOP_GOTO
         || testOp == JSOP_AND || testOp == JSOP_OR)
     {
         ptrdiff_t len = GET_JUMP_OFFSET(testPC);
-        if (testPC + len == top && prev == JSOP_INT8
-            && (op == JSOP_LT || op == JSOP_LE) && prevConst < 8)
-        {
-            shortLoop = true;
-        }
+        if (testPC + len == top && (op == JSOP_LT || op == JSOP_LE)) {
+            StackValue v = stackAt(-1);
+            if (v.hasValue && v.value < 8)
+                shortLoop = true;
+        }
+
         if (testPC + len == top && (op == JSOP_LT || op == JSOP_LE)
             && cx->regs->sp[-2].isInt32() && cx->regs->sp[-2].toInt32() < 16)
         {
             maybeShortLoop = true;
         }
+
         if (testOp != JSOP_GOTO && len > 0) {
+            bool isConst;
+            if (testOp == JSOP_IFEQ || testOp == JSOP_IFNE)
+                isConst = stackAt(-1).isConst && stackAt(-2).isConst;
+            else
+                isConst = stackAt(-1).isConst;
+
             increment(OP_FWDJUMP);
-            if (loopStackDepth == 0)
+            if (loopStackDepth == 0 && !isConst)
                 branchMultiplier *= 2;
         }
     }
 
+    if (op == JSOP_INT8) {
+        stackPush(StackValue(true, GET_INT8(cx->regs->pc)));
+    } else if (op == JSOP_STRING) {
+        stackPush(StackValue(true));
+    } else if (op == JSOP_TYPEOF || op == JSOP_TYPEOFEXPR) {
+        stackPush(StackValue(true));
+    } else if (op == JSOP_EQ || op == JSOP_NE) {
+        StackValue v1 = stackAt(-1);
+        StackValue v2 = stackAt(-2);
+        stackPush(StackValue(v1.isConst && v2.isConst));
+    } else if (op == JSOP_AND) {
+        bool b = !!js_ValueToBoolean(cx->regs->sp[-1]);
+        StackValue v = stackAt(-1);
+        if (b)
+            stackPop();
+    } else {
+        stackClear();
+    }
+    
     return ProfContinue;
 }
 
 static LoopProfile *
 LookupLoopProfile(JSContext *cx, jsbytecode *pc)
 {
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     LoopProfileMap &table = *tm->loopProfiles;
@@ -17201,17 +17223,17 @@ LoopProfile::decide(JSContext *cx)
     if (count(OP_RECURSIVE)) {
         /* don't trace */
     } else if (count(OP_EVAL)) {
         /* don't trace */
     } else if (numInnerLoops > 3) {
         /* don't trace */
     } else if (shortLoop) {
         /* don't trace */
-    } else if (maybeShortLoop) {
+    } else if (maybeShortLoop && numInnerLoops < 2) {
         /* don't trace */
     } else if (isCompilationExpensive(cx, 4)) {
         /* don't trace */
     } else if (isCompilationUnprofitable(cx, 4)) {
         /* don't trace */
     } else {
         uintN goodOps = 0;
 
@@ -17226,17 +17248,16 @@ LoopProfile::decide(JSContext *cx)
 
         debug_only_printf(LC_TMProfiler, "FEATURE goodOps %u\n", goodOps);
         
         if (goodOps >= numAllOps)
             traceOK = true;
     }
 
     debug_only_printf(LC_TMProfiler, "TRACE %s:%d = %d\n", script->filename, line, traceOK);
-    debug_only_print0(LC_TMProfiler, "\n");
 
     if (traceOK) {
         /* Unblacklist the inner loops. */
         for (uintN i=0; i<numInnerLoops; i++) {
             InnerLoop &loop = innerLoops[i];
             LoopProfile *prof = LookupLoopProfile(cx, loop.top);
             if (prof) {
                 /*
@@ -17254,16 +17275,18 @@ LoopProfile::decide(JSContext *cx)
         }
     }
 
     if (!traceOK) {
         debug_only_printf(LC_TMProfiler, "Blacklisting at %d\n", line);
         Blacklist(top);
     }
 
+    debug_only_print0(LC_TMProfiler, "\n");
+
     execOK = traceOK;
 }
 
 JS_REQUIRES_STACK MonitorResult
 MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
     if (tm->profile)
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -662,16 +662,17 @@ VMFragment::toTreeFragment()
 
 enum MonitorResult {
     MONITOR_RECORDING,
     MONITOR_NOT_RECORDING,
     MONITOR_ERROR
 };
 
 const uintN PROFILE_MAX_INNER_LOOPS = 8;
+const uintN PROFILE_MAX_STACK = 6;
 
 /*
  * A loop profile keeps track of the instruction mix of a hot loop. We use this
  * information to predict whether tracing would be beneficial for the loop.
  */
 class LoopProfile
 {
 public:
@@ -732,25 +733,16 @@ public:
     /*
      * This keeps track of the number of times that every succeeding instruction
      * in the trace will have to be compiled. Every time we hit a branch, we
      * double this number. Polymorphic calls multiply it by n (for n-way
      * polymorphism).
      */
     double branchMultiplier;
 
-    /*
-     * Tracks the value of the constant in the most recent INT8 instruction.
-     * This is used to detect for loops with small upper bounds (i.e., short loops).
-     */
-    uintN prevConst;
-    
-    /* Tracks the previous opcode. */
-    JSOp prevOp;
-
     /* Set to true if the loop is short (i.e., has fewer than 8 iterations). */
     bool shortLoop;
 
     /* Set to true if the loop may be short (has few iterations at profiling time). */
     bool maybeShortLoop;
 
     /*
      * When we hit a nested loop while profiling, we record where it occurs
@@ -772,16 +764,52 @@ public:
 
     /*
      * These two variables track the loops that we are currently nested
      * inside while profiling. Loops get popped off here when they exit.
      */
     InnerLoop loopStack[PROFILE_MAX_INNER_LOOPS];
     uintN loopStackDepth;
 
+    /*
+     * These fields keep track of values on the JS stack. If the stack grows larger
+     * than PROFILE_MAX_STACK, we continue to track sp, but we return conservative results
+     * for stackTop().
+     */
+    struct StackValue {
+        bool isConst;
+        bool hasValue;
+        int value;
+
+        StackValue() : isConst(false), hasValue(false) {}
+        StackValue(bool isConst) : isConst(isConst), hasValue(false) {}
+        StackValue(bool isConst, int value) : isConst(isConst), hasValue(true), value(value) {}
+    };
+    StackValue stack[PROFILE_MAX_STACK];
+    uintN sp;
+
+    inline void stackClear() { sp = 0; }
+    
+    inline void stackPush(const StackValue &v) {
+        if (sp < PROFILE_MAX_STACK)
+            stack[sp++] = v;
+        else
+            stackClear();
+    }
+
+    inline void stackPop() { if (sp > 0) sp--; }
+
+    inline StackValue stackAt(int pos) {
+        pos += sp;
+        if (pos >= 0 && uintN(pos) < PROFILE_MAX_STACK)
+            return stack[pos];
+        else
+            return StackValue(false);
+    }
+    
     LoopProfile(JSScript *script, jsbytecode *top, jsbytecode *bottom);
 
     enum ProfileAction {
         ProfContinue,
         ProfComplete
     };
 
     /* These two functions track the instruction mix. */