Bug 606890 - Allow profile to profile short loops twice (r=dmandelin)
authorBill McCloskey <wmccloskey@mozilla.com>
Thu, 11 Nov 2010 17:25:40 -0800
changeset 57798 d2dd2db256ce2e9ca13575968eb9ce7256a07038
parent 57797 d5257e1f0c8bbf356b29f00b6d529ba16aa9e66c
child 57799 70458708c2b92698a485e1f117cfceb3c7badfc4
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersdmandelin
bugs606890
milestone2.0b8pre
Bug 606890 - Allow profile to profile short loops twice (r=dmandelin)
js/src/assembler/assembler/MacroAssemblerARM.h
js/src/jshotloop.h
js/src/jstracer.cpp
js/src/jstracer.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MonoIC.h
--- a/js/src/assembler/assembler/MacroAssemblerARM.h
+++ b/js/src/assembler/assembler/MacroAssemblerARM.h
@@ -645,16 +645,23 @@ public:
 
     Jump branchSub32(Condition cond, Address src, RegisterID dest)
     {
         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
         sub32(src, dest);
         return Jump(m_assembler.jmp(ARMCondition(cond)));
     }
 
+    Jump branchSub32(Condition cond, Imm32 imm, Address dest)
+    {
+        ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
+        sub32(imm, dest);
+        return Jump(m_assembler.jmp(ARMCondition(cond)));
+    }
+
     Jump branchNeg32(Condition cond, RegisterID srcDest)
     {
         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
         neg32(srcDest);
         return Jump(m_assembler.jmp(ARMCondition(cond)));
     }
 
     Jump branchOr32(Condition cond, RegisterID src, RegisterID dest)
new file mode 100644
--- /dev/null
+++ b/js/src/jshotloop.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SpiderMonkey JaegerMonkey implementation
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2002-2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef jshotloop_h___
+#define jshotloop_h___
+
+#include "jscntxt.h"
+
+namespace js {
+
+uint32
+GetHotloop(JSContext *cx);
+
+}
+
+#endif
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -16278,29 +16278,39 @@ RecordTracePoint(JSContext* cx, uintN& i
 }
 
 LoopProfile::LoopProfile(JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom)
     : script(entryfp->script()),
       entryfp(entryfp),
       top(top),
       bottom(bottom),
       hits(0),
-      profiled(false),
-      traceOK(false),
-      execOK(false),
-      numAllOps(0),
-      numSelfOps(0),
-      numSelfOpsMult(0),
-      branchMultiplier(1),
-      shortLoop(false),
-      maybeShortLoop(false),
-      numInnerLoops(0),
-      loopStackDepth(0),
-      sp(0)
-{
+      undecided(false),
+      expensive(false),
+      unprofitable(false)
+{
+    reset();
+}
+
+void
+LoopProfile::reset()
+{
+    profiled = false;
+    traceOK = false;
+    execOK = false;
+    numAllOps = 0;
+    numSelfOps = 0;
+    numSelfOpsMult = 0;
+    branchMultiplier = 1;
+    shortLoop = false;
+    maybeShortLoop = false;
+    numInnerLoops = 0;
+    loopStackDepth = 0;
+    sp = 0;
+
     memset(allOps, 0, sizeof(allOps));
     memset(selfOps, 0, sizeof(selfOps));
 }
 
 MonitorResult
 LoopProfile::profileLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
     if (cx->regs->pc == top) {
@@ -16391,17 +16401,17 @@ LookupOrAddProfile(JSContext *cx, TraceM
         }
     }
 
     return prof;
 }
 
 JS_REQUIRES_STACK TracePointAction
 MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist,
-                  void** traceData, uintN *traceEpoch)
+                  void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits)
 {
     if (!cx->profilingEnabled)
         return RecordTracePoint(cx, inlineCallCount, blacklist, true);
 
     *blacklist = false;
 
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
     /*
@@ -16412,17 +16422,18 @@ MonitorTracePoint(JSContext *cx, uintN& 
         return TPA_Nothing;
 
     LoopProfile *prof = LookupOrAddProfile(cx, tm, traceData, traceEpoch);
     if (!prof) {
         *blacklist = true;
         return TPA_Nothing;
     }
 
-    if (prof->hits++ < PROFILE_HOTLOOP)
+    prof->hits += hits;
+    if (prof->hits < PROFILE_HOTLOOP)
         return TPA_Nothing;
 
     AutoRetBlacklist autoRetBlacklist(cx->regs->pc, blacklist);
 
     if (prof->profiled) {
         if (prof->traceOK) {
             return RecordTracePoint(cx, inlineCallCount, blacklist, prof->execOK);
         } else {
@@ -16434,16 +16445,21 @@ MonitorTracePoint(JSContext *cx, uintN& 
                       js_FramePCToLineNumber(cx, cx->fp()));
 
     tm->profile = prof;
 
     if (!Interpret(cx, cx->fp(), inlineCallCount, JSINTERP_PROFILE))
         return TPA_Error;
 
     JS_ASSERT(!cx->throwing);
+
+    if (prof->undecided) {
+        *loopCounter = 5000;
+        prof->reset();
+    }
     
     return TPA_RanStuff;
 }
 
 /*
  * Returns true if pc is within the given loop. Also returns true if
  * we are in some function called by the loop.
  */
@@ -16464,39 +16480,40 @@ LoopProfile::profileOperation(JSContext*
         return ProfComplete;
     }
 
     jsbytecode *pc = cx->regs->pc;
     JSStackFrame *fp = cx->fp();
     JSScript *script = fp->script();
 
     if (!PCWithinLoop(fp, pc, *this)) {
-        debug_only_printf(LC_TMProfiler, "Profiling complete (loop exit) at %d (line %u)\n",
-                          (int)(cx->regs->pc - script->code),
+        debug_only_printf(LC_TMProfiler, "Profiling complete (loop exit) at line %u\n",
                           js_FramePCToLineNumber(cx, cx->fp()));
         tm->profile->decide(cx);
         tm->profile = NULL;
         return ProfComplete;
     }
 
     while (loopStackDepth > 0 && !PCWithinLoop(fp, pc, loopStack[loopStackDepth-1])) {
-        debug_only_print0(LC_TMProfiler, "Profiler: Exiting inner loop\n");
+        debug_only_printf(LC_TMProfiler, "Profiler: Exiting inner loop at line %u\n",
+                          js_FramePCToLineNumber(cx, cx->fp()));
         loopStackDepth--;
     }
 
     if (op == JSOP_TRACE || op == JSOP_NOTRACE) {
         if (pc != top && (loopStackDepth == 0 || pc != loopStack[loopStackDepth-1].top)) {
             if (loopStackDepth == PROFILE_MAX_INNER_LOOPS) {
                 debug_only_print0(LC_TMProfiler, "Profiling complete (maxnest)\n");
                 tm->profile->decide(cx);
                 tm->profile = NULL;
                 return ProfComplete;
             }
 
-            debug_only_print0(LC_TMProfiler, "Profiler: Entering inner loop\n");
+            debug_only_printf(LC_TMProfiler, "Profiler: Entering inner loop at line %d\n",
+                              js_FramePCToLineNumber(cx, cx->fp()));
             loopStack[loopStackDepth++] = InnerLoop(fp, pc, GetLoopBottom(cx));
         }
     }
 
     numAllOps++;
     if (loopStackDepth == 0) {
         numSelfOps++;
         numSelfOpsMult += branchMultiplier;
@@ -16527,16 +16544,18 @@ LoopProfile::profileOperation(JSContext*
 
     if (op == JSOP_NEW)
         increment(OP_NEW);
 
     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);
+        else if (lval.isObject() && lval.toObject().isDenseArray() && op == JSOP_GETELEM)
+            increment(OP_ARRAY_READ);
     }
 
     if (op == JSOP_CALL) {
         increment(OP_CALL);
 
         uintN argc = GET_ARGC(cx->regs->pc);
         Value &v = cx->regs->sp[-((int)argc + 2)];
         JSObject *callee;
@@ -16637,79 +16656,71 @@ LookupLoopProfile(JSContext *cx, jsbytec
     LoopProfileMap &table = *tm->loopProfiles;
     if (LoopProfileMap::Ptr p = table.lookup(pc)) {
         JS_ASSERT(p->value->top == pc);
         return p->value;
     } else
         return NULL;
 }
 
-/*
- * Returns true if the loop would probably take a long time to
- * compile. Since this function recurses into nested loops, we
- * pass a depth argument to ensure that we don't recurse too
- * far and overflow the stack. Mostly, we're guarding against
- * the possibility that we (incorrectly) track a loop as being
- * nested inside itself, leading to infinite recursion.
- */
+/* Returns true if the loop would probably take a long time to compile. */
 bool
-LoopProfile::isCompilationExpensive(JSContext *cx, uintN depth)
-{
-    if (depth == 0)
-        return true;
-
+LoopProfile::isCompilationExpensive(JSContext *cx)
+{
     /* Too many ops to compile? */
     if (numSelfOps == MAX_PROFILE_OPS)
-        return true;
+        expensive = true;
 
     /* Is the code too branchy? */
     if (numSelfOpsMult > numSelfOps*100000)
-        return true;
+        expensive = true;
 
     /* Ensure that inner loops aren't too expensive. */
     for (uintN i=0; i<numInnerLoops; i++) {
         LoopProfile *prof = LookupLoopProfile(cx, innerLoops[i].top);
-        if (prof && prof->isCompilationExpensive(cx, depth-1))
-            return true;
-    }
-
-    return false;
+        if (prof && prof->expensive)
+            expensive = true;
+    }
+
+    return expensive;
 }
 
 /*
  * This function recognizes loops that are short and that contain
  * jumps. The tracer does badly with these loops because it
  * needs to do a lot of side exits, which are somewhat
  * expensive.
  */
 bool
-LoopProfile::isCompilationUnprofitable(JSContext *cx, uintN depth)
-{
-    if (depth == 0)
-        return true;
-
-    if (numAllOps < 15 && allOps[OP_FWDJUMP])
-        return true;
+LoopProfile::isCompilationUnprofitable(JSContext *cx, uintN goodOps)
+{
+    if (goodOps <= 20 && allOps[OP_FWDJUMP])
+        unprofitable = true;
     
     /* Ensure that inner loops aren't fleeting. */
     for (uintN i=0; i<numInnerLoops; i++) {
         LoopProfile *prof = LookupLoopProfile(cx, innerLoops[i].top);
-        if (prof && prof->isCompilationUnprofitable(cx, depth-1))
-            return true;
-    }
-
-    return false;
+        if (prof && prof->unprofitable)
+            unprofitable = true;
+    }
+
+    return unprofitable;
 }
 
 /* After profiling is done, this method decides whether to trace the loop. */
 void
 LoopProfile::decide(JSContext *cx)
 {
+    bool wasUndecided = undecided;
     profiled = true;
-
+    undecided = false;
+
+    if (traceOK)
+        return; /* We must have enabled it from an outer loop already. */
+    
 #ifdef DEBUG
     uintN line = js_PCToLineNumber(cx, script, top);
 
     debug_only_printf(LC_TMProfiler, "LOOP %s:%d\n", script->filename, line);
 
     for (uintN i=0; i<numInnerLoops; i++) {
         InnerLoop &loop = innerLoops[i];
         if (LoopProfile *prof = LookupLoopProfile(cx, loop.top)) {
@@ -16720,16 +16731,17 @@ LoopProfile::decide(JSContext *cx)
     }
     debug_only_printf(LC_TMProfiler, "FEATURE float %d\n", allOps[OP_FLOAT]);
     debug_only_printf(LC_TMProfiler, "FEATURE int %d\n", allOps[OP_INT]);
     debug_only_printf(LC_TMProfiler, "FEATURE bit %d\n", allOps[OP_BIT]);
     debug_only_printf(LC_TMProfiler, "FEATURE equality %d\n", allOps[OP_EQ]);
     debug_only_printf(LC_TMProfiler, "FEATURE eval %d\n", allOps[OP_EVAL]);
     debug_only_printf(LC_TMProfiler, "FEATURE new %d\n", allOps[OP_NEW]);
     debug_only_printf(LC_TMProfiler, "FEATURE call %d\n", allOps[OP_CALL]);
+    debug_only_printf(LC_TMProfiler, "FEATURE arrayread %d\n", allOps[OP_ARRAY_READ]);
     debug_only_printf(LC_TMProfiler, "FEATURE typedarray %d\n", allOps[OP_TYPED_ARRAY]);
     debug_only_printf(LC_TMProfiler, "FEATURE fwdjump %d\n", allOps[OP_FWDJUMP]);
     debug_only_printf(LC_TMProfiler, "FEATURE recursive %d\n", allOps[OP_RECURSIVE]);
     debug_only_printf(LC_TMProfiler, "FEATURE shortLoop %d\n", shortLoop);
     debug_only_printf(LC_TMProfiler, "FEATURE maybeShortLoop %d\n", maybeShortLoop);
     debug_only_printf(LC_TMProfiler, "FEATURE numAllOps %d\n", numAllOps);
     debug_only_printf(LC_TMProfiler, "FEATURE selfOps %d\n", numSelfOps);
     debug_only_printf(LC_TMProfiler, "FEATURE selfOpsMult %g\n", numSelfOpsMult);
@@ -16739,37 +16751,45 @@ LoopProfile::decide(JSContext *cx)
     if (count(OP_RECURSIVE)) {
         debug_only_print0(LC_TMProfiler, "NOTRACE: recursive\n");
     } else if (count(OP_EVAL)) {
         debug_only_print0(LC_TMProfiler, "NOTRACE: eval\n");
     } else if (numInnerLoops > 3) {
         debug_only_print0(LC_TMProfiler, "NOTRACE: >3 inner loops\n");
     } else if (shortLoop) {
         debug_only_print0(LC_TMProfiler, "NOTRACE: short\n");
+    } else if (isCompilationExpensive(cx)) {
+        debug_only_print0(LC_TMProfiler, "NOTRACE: expensive\n");
     } else if (maybeShortLoop && numInnerLoops < 2) {
-        debug_only_print0(LC_TMProfiler, "NOTRACE: maybe short\n");
-    } else if (isCompilationExpensive(cx, 4)) {
-        debug_only_print0(LC_TMProfiler, "NOTRACE: expensive\n");
-    } else if (isCompilationUnprofitable(cx, 4)) {
-        debug_only_print0(LC_TMProfiler, "NOTRACE: unprofitable\n");
+        if (wasUndecided) {
+            debug_only_print0(LC_TMProfiler, "NOTRACE: maybe short\n");
+        } else {
+            debug_only_print0(LC_TMProfiler, "UNDECIDED: maybe short\n");
+            undecided = true; /* Profile the loop again to see if it's still short. */
+        }
     } else {
         uintN goodOps = 0;
 
         /* The tracer handles these ops well because of type specialization. */
-        goodOps += count(OP_FLOAT)*10 + count(OP_BIT)*10 + count(OP_INT)*5;
+        goodOps += count(OP_FLOAT)*10 + count(OP_BIT)*10 + count(OP_INT)*5 + count(OP_EQ)*15;
 
         /* The tracer handles these ops well because of inlining. */
         goodOps += (count(OP_CALL) + count(OP_NEW))*20;
 
         /* The tracer specialized typed array access. */
         goodOps += count(OP_TYPED_ARRAY)*10;
 
+        /* The methodjit is faster at array writes, but the tracer is faster for reads. */
+        goodOps += count(OP_ARRAY_READ)*15;
+
         debug_only_printf(LC_TMProfiler, "FEATURE goodOps %u\n", goodOps);
-        
-        if (goodOps >= numAllOps)
+
+        if (isCompilationUnprofitable(cx, goodOps))
+            debug_only_print0(LC_TMProfiler, "NOTRACE: unprofitable\n");
+        else if (goodOps >= numAllOps)
             traceOK = true;
     }
 
     debug_only_printf(LC_TMProfiler, "TRACE %s:%d = %d\n", script->filename, line, traceOK);
 
     if (traceOK) {
         /* Unblacklist the inner loops. */
         for (uintN i=0; i<numInnerLoops; i++) {
@@ -16786,17 +16806,17 @@ LoopProfile::decide(JSContext *cx)
                 if (IsBlacklisted(loop.top))
                     debug_only_printf(LC_TMProfiler, "Unblacklisting at %d\n",
                                       js_PCToLineNumber(cx, prof->script, loop.top));
                 Unblacklist(prof->script, loop.top);
             }
         }
     }
 
-    if (!traceOK) {
+    if (!traceOK && !undecided) {
         debug_only_printf(LC_TMProfiler, "Blacklisting at %d\n", line);
         Blacklist(top);
     }
 
     debug_only_print0(LC_TMProfiler, "\n");
 
     execOK = traceOK;
 }
@@ -16814,17 +16834,17 @@ MonitorLoopEdge(JSContext* cx, uintN& in
 
     LoopProfile *prof = LookupOrAddProfile(cx, tm, NULL, NULL);
     if (!prof)
         return MONITOR_NOT_RECORDING;
 
     if (prof->hits++ < PROFILE_HOTLOOP)
         return MONITOR_NOT_RECORDING;
 
-    if (prof->profiled) {
+    if (prof->profiled || prof->undecided) {
         if (prof->traceOK)
             return RecordLoopEdge(cx, inlineCallCount, prof->execOK);
         return MONITOR_NOT_RECORDING;
     }
 
     debug_only_printf(LC_TMProfiler, "Profiling at line %d\n",
                       js_FramePCToLineNumber(cx, cx->fp()));
 
@@ -16836,23 +16856,35 @@ MonitorLoopEdge(JSContext* cx, uintN& in
 void
 AbortProfiling(JSContext *cx)
 {
     debug_only_print0(LC_TMProfiler, "Profiling complete (aborted)\n");
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
     tm->profile->profiled = true;
     tm->profile->traceOK = false;
     tm->profile->execOK = false;
+    tm->profile->undecided = false;
     tm->profile = NULL;
 }
 
 #else /* JS_METHODJIT */
 
 JS_REQUIRES_STACK MonitorResult
 MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
     return RecordLoopEdge(cx, inlineCallCount, true);
 }
 
 #endif /* JS_METHODJIT */
 
+uint32
+GetHotloop(JSContext *cx)
+{
+#ifdef JS_METHODJIT
+    if (cx->profilingEnabled)
+        return PROFILE_HOTLOOP;
+    else
+#endif
+        return 1;
+}
+
 } /* namespace js */
 
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -661,16 +661,17 @@ public:
         OP_INT, // Integer arithmetic
         OP_BIT, // Bit operations
         OP_EQ, // == and !=
         OP_EVAL, // Calls to eval()
         OP_CALL, // JSOP_CALL instructions
         OP_FWDJUMP, // Jumps with positive delta
         OP_NEW, // JSOP_NEW instructions
         OP_RECURSIVE, // Recursive calls
+        OP_ARRAY_READ, // Reads from dense arrays
         OP_TYPED_ARRAY, // Accesses to typed arrays
         OP_LIMIT
     };
 
     /* The script in which the loop header lives. */
     JSScript *script;
 
     /* The stack frame where we started profiling. Only valid while profiling! */
@@ -680,16 +681,19 @@ public:
     jsbytecode *top, *bottom;
 
     /* Number of times we have seen this loop executed; used to decide when to profile. */
     uintN hits;
 
     /* Whether we have run a complete profile of the loop. */
     bool profiled;
 
+    /* Sometimes we can't decide in one profile run whether to trace, so we set undecided. */
+    bool undecided;
+
     /* If we have profiled the loop, this saves the decision of whether to trace it. */
     bool traceOK;
 
     /*
      * Sometimes loops are not good tracing opportunities, but they are nested inside
      * loops that we want to trace. In that case, we set their traceOK flag to true,
      * but we set execOK to false. That way, the loop is traced so that it can be
      * integrated into the outer trace. But we never execute the trace on its only.
@@ -721,16 +725,20 @@ public:
     double branchMultiplier;
 
     /* 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;
 
+    /* These are memoized versions of isCompilationExpensive/Unprofitable. */
+    bool expensive;
+    bool unprofitable;
+
     /*
      * When we hit a nested loop while profiling, we record where it occurs
      * and how many iterations we execute it.
      */
     struct InnerLoop {
         JSStackFrame *entryfp;
         jsbytecode *top, *bottom;
         uintN iters;
@@ -784,16 +792,18 @@ public:
         if (pos >= 0 && uintN(pos) < PROFILE_MAX_STACK)
             return stack[pos];
         else
             return StackValue(false);
     }
     
     LoopProfile(JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom);
 
+    void reset();
+    
     enum ProfileAction {
         ProfContinue,
         ProfComplete
     };
 
     /* These two functions track the instruction mix. */
     inline void increment(OpKind kind)
     {
@@ -806,18 +816,18 @@ public:
 
     /* Called for every back edge being profiled. */
     MonitorResult profileLoopEdge(JSContext* cx, uintN& inlineCallCount);
     
     /* Called for every instruction being profiled. */
     ProfileAction profileOperation(JSContext *cx, JSOp op);
 
     /* Once a loop's profile is done, these decide whether it should be traced. */
-    bool isCompilationExpensive(JSContext *cx, uintN depth);
-    bool isCompilationUnprofitable(JSContext *cx, uintN depth);
+    bool isCompilationExpensive(JSContext *cx);
+    bool isCompilationUnprofitable(JSContext *cx, uintN goodOps);
     void decide(JSContext *cx);
 };
 
 /*
  * BUILTIN_NO_FIXUP_NEEDED indicates that after the initial LeaveTree of a deep
  * bail, the builtin call needs no further fixup when the trace exits and calls
  * LeaveTree the second time.
  */
@@ -1656,17 +1666,17 @@ MonitorLoopEdge(JSContext* cx, uintN& in
 extern JS_REQUIRES_STACK MonitorResult
 ProfileLoopEdge(JSContext* cx, uintN& inlineCallCount);
 
 extern JS_REQUIRES_STACK TracePointAction
 RecordTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist);
 
 extern JS_REQUIRES_STACK TracePointAction
 MonitorTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist,
-                  void** traceData, uintN *traceEpoch);
+                  void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits);
 
 extern JS_REQUIRES_STACK TraceRecorder::AbortResult
 AbortRecording(JSContext* cx, const char* reason);
 
 extern void
 InitJIT(TraceMonitor *tm);
 
 extern void
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -52,16 +52,17 @@
 #include "assembler/assembler/LinkBuffer.h"
 #include "FrameState-inl.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 #include "InlineFrameAssembler.h"
 #include "jscompartment.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
+#include "jshotloop.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
 using namespace js::mjit;
 #if defined(JS_POLYIC) || defined(JS_MONOIC)
 using namespace js::mjit::ic;
 #endif
@@ -595,16 +596,18 @@ mjit::Compiler::finishThisUp(JITScript *
             scriptTICs[i].stubEntry = stubCode.locationOf(traceICs[i].stubEntry);
             scriptTICs[i].traceData = NULL;
 #ifdef DEBUG
             scriptTICs[i].jumpTargetPC = traceICs[i].jumpTarget;
 #endif
             scriptTICs[i].hasSlowTraceHint = traceICs[i].slowTraceHint.isSet();
             if (traceICs[i].slowTraceHint.isSet())
                 scriptTICs[i].slowTraceHint = stubCode.locationOf(traceICs[i].slowTraceHint.get());
+            scriptTICs[i].loopCounterStart = GetHotloop(cx);
+            scriptTICs[i].loopCounter = scriptTICs[i].loopCounterStart;
             
             stubCode.patch(traceICs[i].addrLabel, &scriptTICs[i]);
         }
     }
 #endif /* JS_MONOIC */
 
     for (size_t i = 0; i < callPatches.length(); i++) {
         CallPatchInfo &patch = callPatches[i];
@@ -4749,35 +4752,39 @@ mjit::Compiler::jumpAndTrace(Jump j, jsb
     Label traceStart = stubcc.masm.label();
 
     stubcc.linkExitDirect(j, traceStart);
     if (slow)
         slow->linkTo(traceStart, &stubcc.masm);
 # if JS_MONOIC
     ic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
     traceICs[index] = ic;
+
+    Jump nonzero = stubcc.masm.branchSub32(Assembler::NonZero, Imm32(1),
+                                           Address(Registers::ArgReg1,
+                                                   offsetof(TraceICInfo, loopCounter)));
+    stubcc.jumpInScript(nonzero, target);
 # endif
 
     /* Save and restore compiler-tracked PC, so cx->regs is right in InvokeTracer. */
     {
         jsbytecode* pc = PC;
         PC = target;
 
         OOL_STUBCALL(stubs::InvokeTracer);
 
         PC = pc;
     }
 
     Jump no = stubcc.masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg,
                                         Registers::ReturnReg);
+    if (!stubcc.jumpInScript(no, target))
+        return false;
     restoreFrameRegs(stubcc.masm);
     stubcc.masm.jump(Registers::ReturnReg);
-    no.linkTo(stubcc.masm.label(), &stubcc.masm);
-    if (!stubcc.jumpInScript(stubcc.masm.jump(), target))
-        return false;
 #endif
     return true;
 }
 
 void
 mjit::Compiler::enterBlock(JSObject *obj)
 {
     // If this is an exception entry point, then jsl_InternalThrow has set
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -967,27 +967,36 @@ RunTracer(VMFrame &f)
      */
     entryFrame->scopeChain();
     entryFrame->returnValue();
 
     bool blacklist;
     uintN inlineCallCount = 0;
     void **traceData;
     uintN *traceEpoch;
+    uint32 *loopCounter;
+    uint32 hits;
 #if JS_MONOIC
     traceData = &tic.traceData;
     traceEpoch = &tic.traceEpoch;
+    loopCounter = &tic.loopCounter;
+    *loopCounter = 1;
+    hits = tic.loopCounterStart;
 #else
     traceData = NULL;
     traceEpoch = NULL;
+    loopCounter = NULL;
+    hits = 1;
 #endif
-    tpa = MonitorTracePoint(f.cx, inlineCallCount, &blacklist, traceData, traceEpoch);
+    tpa = MonitorTracePoint(f.cx, inlineCallCount, &blacklist, traceData, traceEpoch,
+                            loopCounter, hits);
     JS_ASSERT(!TRACE_RECORDER(cx));
 
 #if JS_MONOIC
+    tic.loopCounterStart = *loopCounter;
     if (blacklist)
         DisableTraceHint(f, tic);
 #endif
 
     // Even though ExecuteTree() bypasses the interpreter, it should propagate
     // error failures correctly.
     JS_ASSERT_IF(cx->throwing, tpa == TPA_Error);
 
--- a/js/src/methodjit/MonoIC.h
+++ b/js/src/methodjit/MonoIC.h
@@ -148,16 +148,18 @@ struct TraceICInfo {
     JSC::CodeLocationJump slowTraceHint;
 #ifdef DEBUG
     jsbytecode *jumpTargetPC;
 #endif
     
     /* This data is used by the tracing JIT. */
     void *traceData;
     uintN traceEpoch;
+    uint32 loopCounter;
+    uint32 loopCounterStart;
 
     bool hasSlowTraceHint : 1;
 };
 
 static const uint16 BAD_TRACEIC_INDEX = (uint16)0xffff;
 
 void JS_FASTCALL GetGlobalName(VMFrame &f, ic::MICInfo *ic);
 void JS_FASTCALL SetGlobalName(VMFrame &f, ic::MICInfo *ic);