Bug 607539 - Allow profiler to be run from interpreter (r=dmandelin,dvander)
☠☠ backed out by 80842d45466c ☠ ☠
authorBill McCloskey <wmccloskey@mozilla.com>
Wed, 17 Nov 2010 12:46:44 -0800
changeset 57837 08794e076dedf23252ac6c7f03c2cd1f5a896d0f
parent 57836 7a0bb2b04bbd9979eb8b6fdf1b6747579b711354
child 57838 78a42f77bb90c49b066023e740fc05f7c863ab37
child 57880 80842d45466cceced04ab032c07bd85bffe5f99d
child 58038 30c864b404833da3c08199a7f703c87a7b4dadd7
child 58054 1fa547033cec06f52553db88c7dc69dea31121ee
push id17032
push userrsayre@mozilla.com
push dateWed, 17 Nov 2010 21:55:39 +0000
treeherdermozilla-central@78a42f77bb90 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmandelin, dvander
bugs607539
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 607539 - Allow profiler to be run from interpreter (r=dmandelin,dvander)
js/src/jscntxt.cpp
js/src/jsinterp.cpp
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -2269,17 +2269,17 @@ JSContext::updateJITEnabled()
     methodJitEnabled = (options & JSOPTION_METHODJIT) &&
                        !IsJITBrokenHere()
 # if defined JS_CPU_X86 || defined JS_CPU_X64
                        && JSC::MacroAssemblerX86Common::getSSEState() >=
                           JSC::MacroAssemblerX86Common::HasSSE2
 # endif
                         ;
 #ifdef JS_TRACER
-    profilingEnabled = (options & JSOPTION_PROFILING) && traceJitEnabled && methodJitEnabled;
+    profilingEnabled = (options & JSOPTION_PROFILING) && traceJitEnabled;
 #endif
 #endif
 }
 
 namespace js {
 
 void
 SetPendingException(JSContext *cx, const Value &v)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -744,16 +744,20 @@ Invoke(JSContext *cx, const CallArgs &ar
 }
 
 bool
 InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc)
 {
 #ifdef JS_TRACER
     if (TRACE_RECORDER(cx))
         AbortRecording(cx, "attempt to reenter VM while recording");
+#ifdef JS_METHODJIT
+    if (TRACE_PROFILER(cx))
+        AbortProfiling(cx);
+#endif
     LeaveTrace(cx);
 #endif
 
     /* Always push arguments, regardless of optimized/normal invoke. */
     StackSpace &stack = cx->stack();
     if (!stack.pushInvokeArgs(cx, argc, &args_))
         return false;
 
@@ -2332,21 +2336,31 @@ Interpret(JSContext *cx, JSStackFrame *e
 # define END_VARLEN_CASE    goto advance_pc;
 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)
 # define END_EMPTY_CASES    goto advance_pc_by_one;
 
 #endif /* !JS_THREADED_INTERP */
 
     /* Check for too deep of a native thread stack. */
 #ifdef JS_TRACER
+#ifdef JS_METHODJIT
+    JS_CHECK_RECURSION(cx, do {
+            if (TRACE_RECORDER(cx))
+                AbortRecording(cx, "too much recursion");
+            if (TRACE_PROFILER(cx))
+                AbortProfiling(cx);
+            return JS_FALSE;
+        } while (0););
+#else
     JS_CHECK_RECURSION(cx, do {
             if (TRACE_RECORDER(cx))
                 AbortRecording(cx, "too much recursion");
             return JS_FALSE;
         } while (0););
+#endif
 #else
     JS_CHECK_RECURSION(cx, return JS_FALSE);
 #endif
 
 #define LOAD_ATOM(PCOFF, atom)                                                \
     JS_BEGIN_MACRO                                                            \
         JS_ASSERT(regs.fp->hasImacropc()                                      \
                   ? atoms == COMMON_ATOMS_START(&rt->atomState) &&            \
@@ -2399,19 +2413,25 @@ Interpret(JSContext *cx, JSStackFrame *e
     JS_END_MACRO
 
 #define MONITOR_BRANCH()                                                      \
     JS_BEGIN_MACRO                                                            \
         if (TRACING_ENABLED(cx)) {                                            \
             MonitorResult r = MonitorLoopEdge(cx, inlineCallCount);           \
             if (r == MONITOR_RECORDING) {                                     \
                 JS_ASSERT(TRACE_RECORDER(cx));                                \
+                JS_ASSERT(!TRACE_PROFILER(cx));                               \
                 MONITOR_BRANCH_TRACEVIS;                                      \
                 ENABLE_INTERRUPTS();                                          \
                 CLEAR_LEAVE_ON_TRACE_POINT();                                 \
+            } else if (r == MONITOR_PROFILING) {                              \
+                JS_ASSERT(TRACE_PROFILER(cx));                                \
+                JS_ASSERT(!TRACE_RECORDER(cx));                               \
+                ENABLE_INTERRUPTS();                                          \
+                CLEAR_LEAVE_ON_TRACE_POINT();                                 \
             }                                                                 \
             RESTORE_INTERP_VARS();                                            \
             JS_ASSERT_IF(cx->throwing, r == MONITOR_ERROR);                   \
             if (r == MONITOR_ERROR)                                           \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
@@ -2435,16 +2455,17 @@ Interpret(JSContext *cx, JSStackFrame *e
 #define TRACE_RECORDER(cx) (false)
 #define TRACE_PROFILER(cx) (false)
 #endif
 
 #if defined(JS_TRACER) && defined(JS_METHODJIT)
 # define LEAVE_ON_SAFE_POINT()                                                \
     do {                                                                      \
         JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx));                  \
+        JS_ASSERT_IF(leaveOnSafePoint, !TRACE_PROFILER(cx));                  \
         if (leaveOnSafePoint && !regs.fp->hasImacropc() &&                    \
             script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \
             JS_ASSERT(!TRACE_RECORDER(cx));                                   \
             interpReturnOK = true;                                            \
             goto leave_on_safe_point;                                         \
         }                                                                     \
     } while (0)
 #else
@@ -2546,21 +2567,30 @@ Interpret(JSContext *cx, JSStackFrame *e
 #ifdef JS_TRACER
     /*
      * The method JIT may have already initiated a recording, in which case
      * there should already be a valid recorder. Otherwise...
      * we cannot reenter the interpreter while recording.
      */
     if (interpMode == JSINTERP_RECORD) {
         JS_ASSERT(TRACE_RECORDER(cx));
+        JS_ASSERT(!TRACE_PROFILER(cx));
         ENABLE_INTERRUPTS();
+#ifdef JS_METHODJIT
     } else if (interpMode == JSINTERP_PROFILE) {
+        JS_ASSERT(TRACE_PROFILER(cx));
+        JS_ASSERT(!TRACE_RECORDER(cx));
         ENABLE_INTERRUPTS();
+#endif
     } else if (TRACE_RECORDER(cx)) {
         AbortRecording(cx, "attempt to reenter interpreter while recording");
+#ifdef JS_METHODJIT
+    } else if (TRACE_PROFILER(cx)) {
+        AbortProfiling(cx);
+#endif
     }
 
     if (regs.fp->hasImacropc())
         atoms = COMMON_ATOMS_START(&rt->atomState);
 #endif
 
     /* Don't call the script prologue if executing between Method and Trace JIT. */
     if (interpMode == JSINTERP_NORMAL) {
@@ -2625,16 +2655,20 @@ Interpret(JSContext *cx, JSStackFrame *e
 #endif /* !JS_THREADED_INTERP */
     {
         bool moreInterrupts = false;
         JSInterruptHook hook = cx->debugHooks->interruptHook;
         if (hook) {
 #ifdef JS_TRACER
             if (TRACE_RECORDER(cx))
                 AbortRecording(cx, "interrupt hook");
+#ifdef JS_METHODJIT
+            if (TRACE_PROFILER(cx))
+                AbortProfiling(cx);
+#endif
 #endif
             Value rval;
             switch (hook(cx, script, regs.pc, Jsvalify(&rval),
                          cx->debugHooks->interruptHookData)) {
               case JSTRAP_ERROR:
                 goto error;
               case JSTRAP_CONTINUE:
                 break;
@@ -2649,29 +2683,31 @@ Interpret(JSContext *cx, JSStackFrame *e
               default:;
             }
             moreInterrupts = true;
         }
 
 #ifdef JS_TRACER
 #ifdef JS_METHODJIT
         if (LoopProfile *prof = TRACE_PROFILER(cx)) {
+            JS_ASSERT(!TRACE_RECORDER(cx));
             LoopProfile::ProfileAction act = prof->profileOperation(cx, op);
             switch (act) {
                 case LoopProfile::ProfComplete:
                     leaveOnSafePoint = true;
                     LEAVE_ON_SAFE_POINT();
                     break;
                 default:
                     moreInterrupts = true;
                     break;
             }
         }
 #endif
         if (TraceRecorder* tr = TRACE_RECORDER(cx)) {
+            JS_ASSERT(!TRACE_PROFILER(cx));
             AbortableRecordingStatus status = tr->monitorRecording(op);
             JS_ASSERT_IF(cx->throwing, status == ARECORD_ERROR);
 
             if (interpMode != JSINTERP_NORMAL) {
                 JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT);
                 switch (status) {
                   case ARECORD_IMACRO_ABORTED:
                   case ARECORD_ABORTED:
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2407,16 +2407,17 @@ TraceMonitor::outOfMemory() const
 
 /*
  * This function destroys the recorder after a successful recording, possibly
  * starting a suspended outer recorder.
  */
 AbortableRecordingStatus
 TraceRecorder::finishSuccessfully()
 {
+    JS_ASSERT(!traceMonitor->profile);
     JS_ASSERT(traceMonitor->recorder == this);
     JS_ASSERT(fragment->lastIns && fragment->code());
 
     AUDIT(traceCompleted);
     mark.commit();
 
     /* Grab local copies of members needed after |delete this|. */
     JSContext* localcx = cx;
@@ -2432,16 +2433,17 @@ TraceRecorder::finishSuccessfully()
     }
     return ARECORD_COMPLETED;
 }
 
 /* This function aborts a recorder and any pending outer recorders. */
 JS_REQUIRES_STACK TraceRecorder::AbortResult
 TraceRecorder::finishAbort(const char* reason)
 {
+    JS_ASSERT(!traceMonitor->profile);
     JS_ASSERT(traceMonitor->recorder == this);
     JS_ASSERT(!fragment->code());
 
     AUDIT(recorderAborted);
 #ifdef DEBUG
     debug_only_printf(LC_TMMinimal,
                       "Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n",
                       tree->treeFileName,
@@ -2702,16 +2704,17 @@ ValueToNative(const Value &v, JSValueTyp
 #endif
 }
 
 void
 TraceMonitor::flush()
 {
     /* flush should only be called after all recorders have been aborted. */
     JS_ASSERT(!recorder);
+    JS_ASSERT(!profile);
     AUDIT(cacheFlushed);
 
     // recover profiling data from expiring Fragments
     verbose_only(
         for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
             for (TreeFragment *f = vmfragments[i]; f; f = f->next) {
                 JS_ASSERT(f->root == f);
                 for (TreeFragment *p = f; p; p = p->peer)
@@ -4395,16 +4398,20 @@ ResetJITImpl(JSContext* cx)
     if (!cx->traceJitEnabled)
         return;
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     debug_only_print0(LC_TMTracer, "Flushing cache.\n");
     if (tm->recorder) {
         JS_ASSERT_NOT_ON_TRACE(cx);
         AbortRecording(cx, "flush cache");
     }
+#if JS_METHODJIT
+    if (tm->profile)
+        AbortProfiling(cx);
+#endif
     if (ProhibitFlush(cx)) {
         debug_only_print0(LC_TMTracer, "Deferring JIT flush due to deep bail.\n");
         tm->needFlush = JS_TRUE;
         return;
     }
     tm->flush();
 }
 
@@ -5493,16 +5500,17 @@ CheckGlobalObjectShape(JSContext* cx, Tr
 bool JS_REQUIRES_STACK
 TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f,
                              unsigned stackSlots, unsigned ngslots,
                              JSValueType* typeMap, VMSideExit* expectedInnerExit,
                              JSScript* outerScript, jsbytecode* outerPC, uint32 outerArgc,
                              bool speculate)
 {
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
+    JS_ASSERT(!tm->profile);
     JS_ASSERT(!tm->needFlush);
     JS_ASSERT_IF(cx->fp()->hasImacropc(), f->root != f);
 
     tm->recorder = new TraceRecorder(cx, anchor, f, stackSlots, ngslots, typeMap,
                                      expectedInnerExit, outerScript, outerPC, outerArgc,
                                      speculate);
 
     if (!tm->recorder || tm->outOfMemory() || OverfullJITCache(tm)) {
@@ -6387,16 +6395,19 @@ TracerState::~TracerState()
     JS_TRACE_MONITOR(cx).tracecx = NULL;
 }
 
 /* Call |f|, return the exit taken. */
 static JS_ALWAYS_INLINE VMSideExit*
 ExecuteTrace(JSContext* cx, Fragment* f, TracerState& state)
 {
     JS_ASSERT(!cx->bailExit);
+#ifdef JS_METHODJIT
+    JS_ASSERT(!TRACE_PROFILER(cx));
+#endif
     union { NIns *code; GuardRecord* (FASTCALL *func)(TracerState*); } u;
     u.code = f->code();
     GuardRecord* rec;
 #if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32)
     SIMULATE_FASTCALL(rec, state, NULL, u.func);
 #else
     rec = u.func(&state);
 #endif
@@ -6454,17 +6465,18 @@ static JS_REQUIRES_STACK bool
 ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount,
             VMSideExit** innermostNestedGuardp, VMSideExit **lrp)
 {
 #ifdef MOZ_TRACEVIS
     TraceVisStateObj tvso(cx, S_EXECUTE);
 #endif
     JS_ASSERT(f->root == f && f->code());
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
-
+    JS_ASSERT(!tm->profile);
+    
     if (!ScopeChainCheck(cx, f) || !cx->stack().ensureEnoughSpaceToEnterTrace() ||
         inlineCallCount + f->maxCallDepth > JS_MAX_INLINE_CALL_COUNT) {
         *lrp = NULL;
         return true;
     }
 
     /* Make sure the global object is sane. */
     JS_ASSERT(f->globalObj->numSlots() <= MAX_GLOBAL_SLOTS);
@@ -6918,24 +6930,26 @@ TraceRecorder::assertInsideLoop()
      * immediately preceeding a loop (the one that jumps to the loop
      * condition).
      */
     JS_ASSERT(pc >= beg - JSOP_GOTO_LENGTH && pc <= end);
 #endif
 }
 
 JS_REQUIRES_STACK MonitorResult
-RecordLoopEdge(JSContext* cx, uintN& inlineCallCount)
+RecordLoopEdge(JSContext* cx, uintN& inlineCallCount, bool execOK)
 {
 #ifdef MOZ_TRACEVIS
     TraceVisStateObj tvso(cx, S_MONITOR);
 #endif
 
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
 
+    JS_ASSERT(!tm->profile);
+
     /* Is the recorder currently active? */
     if (tm->recorder) {
         tm->recorder->assertInsideLoop();
         jsbytecode* pc = cx->regs->pc;
         if (pc == tm->recorder->tree->ip) {
             tm->recorder->closeLoop();
         } else {
             MonitorResult r = TraceRecorder::recordLoopEdge(cx, tm->recorder, inlineCallCount);
@@ -7041,16 +7055,19 @@ RecordLoopEdge(JSContext* cx, uintN& inl
         debug_only_print0(LC_TMTracer, "Blacklisted: too many peer trees.\n");
         Blacklist((jsbytecode*) f->root->ip);
 #ifdef MOZ_TRACEVIS
         tvso.r = R_MAX_PEERS;
 #endif
         return MONITOR_NOT_RECORDING;
     }
 
+    if (!execOK)
+        return MONITOR_NOT_RECORDING;
+    
     VMSideExit* lr = NULL;
     VMSideExit* innermostNestedGuard = NULL;
 
     if (!ExecuteTree(cx, match, inlineCallCount, &innermostNestedGuard, &lr))
         return MONITOR_ERROR;
 
     if (!lr) {
 #ifdef MOZ_TRACEVIS
@@ -7135,16 +7152,17 @@ RecordLoopEdge(JSContext* cx, uintN& inl
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::monitorRecording(JSOp op)
 {
     TraceMonitor &localtm = JS_TRACE_MONITOR(cx);
     debug_only_stmt( JSContext *localcx = cx; )
     assertInsideLoop();
+    JS_ASSERT(!localtm.profile);
 
     /* Process needFlush requests now. */
     if (localtm.needFlush) {
         ResetJIT(cx, FR_DEEP_BAIL);
         return ARECORD_ABORTED;
     }
     JS_ASSERT(!fragment->lastIns);
 
@@ -7620,16 +7638,17 @@ InitJIT(TraceMonitor *tm)
 #endif
 #endif
 }
 
 void
 FinishJIT(TraceMonitor *tm)
 {
     JS_ASSERT(!tm->recorder);
+    JS_ASSERT(!tm->profile);
 
 #ifdef JS_JIT_SPEW
     if (jitstats.recorderStarted) {
         char sep = ':';
         debug_only_print0(LC_TMStats, "recorder");
 #define RECORDER_JITSTAT(_ident, _name)                             \
         debug_only_printf(LC_TMStats, "%c " _name "(%llu)", sep,    \
                           (unsigned long long int)jitstats._ident); \
@@ -16202,16 +16221,17 @@ class AutoRetBlacklist
 JS_REQUIRES_STACK TracePointAction
 RecordTracePoint(JSContext* cx, uintN& inlineCallCount, bool* blacklist, bool execAllowed)
 {
     JSStackFrame* fp = cx->fp();
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     jsbytecode* pc = cx->regs->pc;
 
     JS_ASSERT(!TRACE_RECORDER(cx));
+    JS_ASSERT(!tm->profile);
 
     JSObject* globalObj = cx->fp()->scopeChain().getGlobal();
     uint32 globalShape = -1;
     SlotList* globalSlots = NULL;
 
     AutoRetBlacklist autoRetBlacklist(pc, blacklist);
 
     if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) {
@@ -16303,23 +16323,25 @@ RecordTracePoint(JSContext* cx, uintN& i
     if (!Interpret(cx, fp, inlineCallCount, JSINTERP_RECORD))
         return TPA_Error;
 
     JS_ASSERT(!cx->throwing);
     
     return TPA_RanStuff;
 }
 
-LoopProfile::LoopProfile(JSScript *script, jsbytecode *top, jsbytecode *bottom)
-    : script(script),
+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),
@@ -16332,30 +16354,31 @@ LoopProfile::LoopProfile(JSScript *scrip
 MonitorResult
 LoopProfile::profileLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
     if (cx->regs->pc == top) {
         debug_only_print0(LC_TMProfiler, "Profiling complete (edge)\n");
         decide(cx);
     } else {
         /* Record an inner loop invocation. */
-        JSScript *script = cx->fp()->script();
+        JSStackFrame *fp = cx->fp();
         jsbytecode *pc = cx->regs->pc;
         bool found = false;
 
         /* We started with the most deeply nested one first, since it gets hit most often.*/
         for (int i = int(numInnerLoops)-1; i >= 0; i--) {
-            if (innerLoops[i].script == script && innerLoops[i].top == pc) {
+            if (innerLoops[i].entryfp == fp && innerLoops[i].top == pc) {
                 innerLoops[i].iters++;
                 found = true;
+                break;
             }
         }
 
         if (!found && numInnerLoops < PROFILE_MAX_INNER_LOOPS)
-            innerLoops[numInnerLoops++] = InnerLoop(script, pc, NULL);
+            innerLoops[numInnerLoops++] = InnerLoop(fp, pc, NULL);
     }
 
     return MONITOR_NOT_RECORDING;
 }
 
 
 static const uintN PROFILE_HOTLOOP = 61;
 static const uintN MAX_PROFILE_OPS = 4096;
@@ -16382,42 +16405,42 @@ LookupOrAddProfile(JSContext *cx, TraceM
      * follows. Every time the trace monitor is flushed,
      * |tm->flushEpoch| is incremented. When storing the profile in
      * the IC, we store the current |tm->flushEpoch| along with it.
      * Before pulling a profile out of the IC, we check that its
      * stored epoch is still up-to-date with |tm->flushEpoch|.
      * This ensures that no flush has happened in between.
      */
 
-#if JS_MONOIC
-    if (*traceData && *traceEpoch == tm->flushEpoch) {
-        prof = (LoopProfile *)*traceData;
-    } else {
+    if (traceData) {
+        if (*traceData && *traceEpoch == tm->flushEpoch) {
+            prof = (LoopProfile *)*traceData;
+        } else {
+            jsbytecode* pc = cx->regs->pc;
+            jsbytecode* bottom = GetLoopBottom(cx);
+            if (!bottom)
+                return NULL;
+            prof = new (*tm->dataAlloc) LoopProfile(cx->fp(), pc, bottom);
+            *traceData = prof;
+            *traceEpoch = tm->flushEpoch;
+            tm->loopProfiles->put(pc, prof);
+        }
+    } else {
+        LoopProfileMap &table = *tm->loopProfiles;
         jsbytecode* pc = cx->regs->pc;
-        jsbytecode* bottom = GetLoopBottom(cx);
-        if (!bottom)
-            return NULL;
-        prof = new (*tm->dataAlloc) LoopProfile(cx->fp()->script(), pc, bottom);
-        *traceData = prof;
-        *traceEpoch = tm->flushEpoch;
-        tm->loopProfiles->put(pc, prof);
-    }
-#else
-    LoopProfileMap &table = *tm->loopProfiles;
-    jsbytecode* pc = cx->regs->pc;
-    if (LoopProfileMap::AddPtr p = table.lookupForAdd(pc)) {
-        prof = p->value;
-    } else {
-        jsbytecode* bottom = GetLoopBottom(cx);
-        if (!bottom)
-            return NULL;
-        prof = new (*tm->dataAlloc) LoopProfile(cx->fp()->script(), pc, bottom);
-        table.add(p, pc, prof);
-    }
-#endif
+        if (LoopProfileMap::AddPtr p = table.lookupForAdd(pc)) {
+            prof = p->value;
+        } else {
+            jsbytecode* bottom = GetLoopBottom(cx);
+            if (!bottom)
+                return NULL;
+            prof = new (*tm->dataAlloc) LoopProfile(cx->fp(), pc, bottom);
+            table.add(p, pc, prof);
+        }
+    }
 
     return prof;
 }
 
 JS_REQUIRES_STACK TracePointAction
 MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist,
                   void** traceData, uintN *traceEpoch)
 {
@@ -16448,80 +16471,80 @@ MonitorTracePoint(JSContext *cx, uintN& 
     if (prof->profiled) {
         if (prof->traceOK) {
             return RecordTracePoint(cx, inlineCallCount, blacklist, prof->execOK);
         } else {
             return TPA_Nothing;
         }
     }
 
-    debug_only_printf(LC_TMProfiler, "Profiling at line %d\n",
+    debug_only_printf(LC_TMProfiler, "Profiling at line %d from methodjit\n",
                       js_FramePCToLineNumber(cx, cx->fp()));
 
+    prof->entryfp = cx->fp();
     tm->profile = prof;
 
     if (!Interpret(cx, cx->fp(), inlineCallCount, JSINTERP_PROFILE))
         return TPA_Error;
 
     JS_ASSERT(!cx->throwing);
     
     return TPA_RanStuff;
 }
 
 /*
- * Returns true if pc is within the given loop.
- * If we're in a different script, then we must have come from
- * a call instruction within the loop (since we check if we're within
- * the loop before each instruction) so we're still in the loop.
+ * Returns true if pc is within the given loop. Also returns true if
+ * we are in some function called by the loop.
  */
 template<class T>
 static inline bool
-PCWithinLoop(JSScript *script, jsbytecode *pc, T& loop)
-{
-    return script != loop.script || (pc >= loop.top && pc <= loop.bottom);
+PCWithinLoop(JSStackFrame *fp, jsbytecode *pc, T& loop)
+{
+    return fp > loop.entryfp || (fp == loop.entryfp && pc >= loop.top && pc <= loop.bottom);
 }
 
 LoopProfile::ProfileAction
 LoopProfile::profileOperation(JSContext* cx, JSOp op)
 {
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
 
     if (profiled) {
         tm->profile = NULL;
         return ProfComplete;
     }
 
     jsbytecode *pc = cx->regs->pc;
-    JSScript *script = cx->fp()->maybeScript();
-
-    if (!PCWithinLoop(script, pc, *this)) {
+    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 - cx->fp()->script()->code),
+                          (int)(cx->regs->pc - script->code),
                           js_FramePCToLineNumber(cx, cx->fp()));
         tm->profile->decide(cx);
         tm->profile = NULL;
         return ProfComplete;
     }
 
-    while (loopStackDepth > 0 && !PCWithinLoop(script, pc, loopStack[loopStackDepth-1])) {
+    while (loopStackDepth > 0 && !PCWithinLoop(fp, pc, loopStack[loopStackDepth-1])) {
         debug_only_print0(LC_TMProfiler, "Profiler: Exiting inner loop\n");
         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");
-            loopStack[loopStackDepth++] = InnerLoop(script, pc, GetLoopBottom(cx));
+            loopStack[loopStackDepth++] = InnerLoop(fp, pc, GetLoopBottom(cx));
         }
     }
 
     numAllOps++;
     if (loopStackDepth == 0) {
         numSelfOps++;
         numSelfOpsMult += branchMultiplier;
     }
@@ -16573,17 +16596,17 @@ LoopProfile::profileOperation(JSContext*
                 js::Native native = fun->u.n.native;
                 if (js_IsMathFunction(JS_JSVALIFY_NATIVE(native)))
                     increment(OP_FLOAT);
             }
         }
     }
 
     if (op == JSOP_CALLPROP && loopStackDepth == 0)
-        branchMultiplier *= mjit::GetCallTargetCount(cx->fp()->script(), cx->regs->pc);
+        branchMultiplier *= mjit::GetCallTargetCount(script, cx->regs->pc);
 
     if (op == JSOP_TABLESWITCH) {
         jsint low = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN);
         jsint high = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN*2);
         branchMultiplier *= high - low + 1;
     }
 
     if (op == JSOP_LOOKUPSWITCH)
@@ -16607,17 +16630,17 @@ LoopProfile::profileOperation(JSContext*
         JS_ASSERT(oplen != -1);
 
         if (cx->regs->pc - script->code + oplen < ptrdiff_t(script->length))
             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);
+    JSOp testOp = js_GetOpcode(cx, 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 && (op == JSOP_LT || op == JSOP_LE)) {
             StackValue v = stackAt(-1);
             if (v.hasValue && v.value < 8)
                 shortLoop = true;
@@ -16655,16 +16678,25 @@ LoopProfile::profileOperation(JSContext*
     } else if (op == JSOP_AND) {
         bool b = !!js_ValueToBoolean(cx->regs->sp[-1]);
         StackValue v = stackAt(-1);
         if (b)
             stackPop();
     } else {
         stackClear();
     }
+
+    if (count(OP_RECURSIVE) || count(OP_EVAL)) {
+        debug_only_printf(LC_TMProfiler, "Profiling complete (early exit) at %d (line %u)\n",
+                          (int)(cx->regs->pc - script->code),
+                          js_FramePCToLineNumber(cx, cx->fp()));
+        tm->profile->decide(cx);
+        tm->profile = NULL;
+        return ProfComplete;
+    }
     
     return ProfContinue;
 }
 
 static LoopProfile *
 LookupLoopProfile(JSContext *cx, jsbytecode *pc)
 {
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
@@ -16690,17 +16722,17 @@ LoopProfile::isCompilationExpensive(JSCo
     if (depth == 0)
         return true;
 
     /* Too many ops to compile? */
     if (numSelfOps == MAX_PROFILE_OPS)
         return true;
 
     /* Is the code too branchy? */
-    if (numSelfOpsMult >= numSelfOps*100000)
+    if (numSelfOpsMult > numSelfOps*100000)
         return 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;
     }
@@ -16765,30 +16797,34 @@ LoopProfile::decide(JSContext *cx)
     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);
 #endif
 
     traceOK = false;
+    /*
+     * The first two cases are "early exits"; if these change, need
+     * to change profileOperation too.
+    */
     if (count(OP_RECURSIVE)) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: recursive\n");
     } else if (count(OP_EVAL)) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: eval\n");
     } else if (numInnerLoops > 3) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: >3 inner loops\n");
     } else if (shortLoop) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: short\n");
     } else if (maybeShortLoop && numInnerLoops < 2) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: maybe short\n");
     } else if (isCompilationExpensive(cx, 4)) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: expensive\n");
     } else if (isCompilationUnprofitable(cx, 4)) {
-        /* don't trace */
+        debug_only_print0(LC_TMProfiler, "NOTRACE: unprofitable\n");
     } 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;
 
         /* The tracer handles these ops well because of inlining. */
         goodOps += (count(OP_CALL) + count(OP_NEW))*20;
@@ -16833,37 +16869,62 @@ LoopProfile::decide(JSContext *cx)
     debug_only_print0(LC_TMProfiler, "\n");
 
     execOK = traceOK;
 }
 
 JS_REQUIRES_STACK MonitorResult
 MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
+    if (!cx->profilingEnabled || TRACE_RECORDER(cx))
+        return RecordLoopEdge(cx, inlineCallCount, true);
+
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
+
     if (tm->profile)
         return tm->profile->profileLoopEdge(cx, inlineCallCount);
-    else
-        return RecordLoopEdge(cx, inlineCallCount);
+
+    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->traceOK)
+            return RecordLoopEdge(cx, inlineCallCount, prof->execOK);
+        return MONITOR_NOT_RECORDING;
+    }
+
+    debug_only_printf(LC_TMProfiler, "Profiling at line %d from interpreter\n",
+                      js_FramePCToLineNumber(cx, cx->fp()));
+
+    prof->entryfp = cx->fp();
+    tm->profile = prof;
+
+    return MONITOR_PROFILING;
 }
 
 void
 AbortProfiling(JSContext *cx)
 {
     debug_only_print0(LC_TMProfiler, "Profiling complete (aborted)\n");
     TraceMonitor *tm = &JS_TRACE_MONITOR(cx);
-    tm->profile->numSelfOps = MAX_PROFILE_OPS;
-    tm->profile->decide(cx);
+    tm->profile->profiled = true;
+    tm->profile->traceOK = false;
+    tm->profile->execOK = false;
+    Blacklist(tm->profile->top);
     tm->profile = NULL;
 }
 
 #else /* JS_METHODJIT */
 
 JS_REQUIRES_STACK MonitorResult
 MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount)
 {
-    return RecordLoopEdge(cx, inlineCallCount);
+    return RecordLoopEdge(cx, inlineCallCount, true);
 }
 
 #endif /* JS_METHODJIT */
 
 } /* namespace js */
 
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -635,16 +635,17 @@ inline TreeFragment*
 VMFragment::toTreeFragment()
 {
     JS_ASSERT(root == this);
     return static_cast<TreeFragment*>(this);
 }
 
 enum MonitorResult {
     MONITOR_RECORDING,
+    MONITOR_PROFILING,
     MONITOR_NOT_RECORDING,
     MONITOR_ERROR
 };
 
 const uintN PROFILE_MAX_INNER_LOOPS = 8;
 const uintN PROFILE_MAX_STACK = 6;
 
 /*
@@ -667,16 +668,19 @@ public:
         OP_RECURSIVE, // Recursive calls
         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! */
+    JSStackFrame *entryfp;
+    
     /* The bytecode locations of the loop header and the back edge. */
     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;
@@ -722,23 +726,24 @@ public:
     /* 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
      * and how many iterations we execute it.
      */
     struct InnerLoop {
+        JSStackFrame *entryfp;
         JSScript *script;
         jsbytecode *top, *bottom;
         uintN iters;
 
         InnerLoop() {}
-        InnerLoop(JSScript *script, jsbytecode *top, jsbytecode *bottom)
-            : script(script), top(top), bottom(bottom), iters(0) {}
+        InnerLoop(JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom)
+            : entryfp(entryfp), script(entryfp->script()), top(top), bottom(bottom), iters(0) {}
     };
 
     /* These two variables track all the inner loops seen while profiling (up to a limit). */
     InnerLoop innerLoops[PROFILE_MAX_INNER_LOOPS];
     uintN numInnerLoops;
 
     /*
      * These two variables track the loops that we are currently nested
@@ -778,17 +783,17 @@ public:
     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);
+    LoopProfile(JSStackFrame *entryfp, jsbytecode *top, jsbytecode *bottom);
 
     enum ProfileAction {
         ProfContinue,
         ProfComplete
     };
 
     /* These two functions track the instruction mix. */
     inline void increment(OpKind kind)
@@ -1555,17 +1560,17 @@ class TraceRecorder
     friend class AdjustCallerStackTypesVisitor;
     friend class TypeCompatibilityVisitor;
     friend class ImportFrameSlotsVisitor;
     friend class SlotMap;
     friend class DefaultSlotMap;
     friend class DetermineTypesVisitor;
     friend class RecursiveSlotMap;
     friend class UpRecursiveSlotMap;
-    friend MonitorResult RecordLoopEdge(JSContext*, uintN&);
+    friend MonitorResult RecordLoopEdge(JSContext*, uintN&, bool);
     friend TracePointAction RecordTracePoint(JSContext*, uintN &inlineCallCount,
                                              bool *blacklist);
     friend AbortResult AbortRecording(JSContext*, const char*);
     friend class BoxArg;
     friend void TraceMonitor::sweep();
 
   public:
     static bool JS_REQUIRES_STACK