[INFER] Fix code discarding for JM+TM integration, bug 685358.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 13 Sep 2011 15:01:46 -0700
changeset 78388 c51c15708dcbada41991deca4cc6c39ebbb71dca
parent 78387 e678ced82a6af95891f63d61b048db952ae7b9ea
child 78389 f933cbe46a03a44d8f318dbc71e9864320cd40b3
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs685358
milestone9.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] Fix code discarding for JM+TM integration, bug 685358.
js/src/jit-test/tests/basic/wrap-primitive-this.js
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/StubCalls.h
--- a/js/src/jit-test/tests/basic/wrap-primitive-this.js
+++ b/js/src/jit-test/tests/basic/wrap-primitive-this.js
@@ -10,19 +10,20 @@
  * primitive, then we may get:
  *
  * Assertion failure: thisObj == globalObj
  */
 
 var HOTLOOP = this.tracemonkey ? tracemonkey.HOTLOOP : 8;
 var a;
 function f(n) {
-    for (var i = 0; i < HOTLOOP; i++)
+  for (var i = 0; i < HOTLOOP; i++) {
         if (i == HOTLOOP - 2)
             a = this;
+  }
 }
 
 /*
  * Various sorts of events can cause the global to be reshaped, which
  * resets our loop counts. Furthermore, we don't record a branch off a
  * trace until it has been taken HOTEXIT times. So simply calling the
  * function twice may not be enough to ensure that the 'a = this' branch
  * gets recorded. This is probably excessive, but it'll work.
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1594,20 +1594,24 @@ IteratorNext(JSContext *cx, JSObject *it
 /*
  * For bytecodes which push values and then fall through, make sure the
  * types of the pushed values are consistent with type inference information.
  */
 static inline void
 TypeCheckNextBytecode(JSContext *cx, JSScript *script, unsigned n, const FrameRegs &regs)
 {
 #ifdef DEBUG
-    if (cx->typeInferenceEnabled() &&
-        *regs.pc != JSOP_TRAP &&
+    if (*regs.pc != JSOP_TRAP &&
         n == analyze::GetBytecodeLength(regs.pc)) {
-        TypeScript::CheckBytecode(cx, script, regs.pc, regs.sp);
+        if (script->hasAnalysis() && !regs.fp()->hasImacropc()) {
+            jsbytecode *nextpc = regs.pc + GetBytecodeLength(cx, script, regs.pc);
+            JS_ASSERT(regs.sp == regs.fp()->base() + script->analysis()->getCode(nextpc).stackDepth);
+        }
+        if (cx->typeInferenceEnabled())
+            TypeScript::CheckBytecode(cx, script, regs.pc, regs.sp);
     }
 #endif
 }
 
 JS_NEVER_INLINE bool
 js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
 {
 #ifdef MOZ_TRACEVIS
@@ -1823,40 +1827,22 @@ js::Interpret(JSContext *cx, StackFrame 
      * if it returns false.
      */
 #define CHECK_BRANCH()                                                        \
     JS_BEGIN_MACRO                                                            \
         if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
             goto error;                                                       \
     JS_END_MACRO
 
-#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));                  \
-        JS_ASSERT_IF(leaveOnSafePoint, interpMode != JSINTERP_NORMAL);        \
-        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
-# define LEAVE_ON_SAFE_POINT() /* nop */
-#endif
-
 #define BRANCH(n)                                                             \
     JS_BEGIN_MACRO                                                            \
         regs.pc += (n);                                                       \
         op = (JSOp) *regs.pc;                                                 \
         if ((n) <= 0)                                                         \
             goto check_backedge;                                              \
-        LEAVE_ON_SAFE_POINT();                                                \
         DO_OP();                                                              \
     JS_END_MACRO
 
 #define SET_SCRIPT(s)                                                         \
     JS_BEGIN_MACRO                                                            \
         script = (s);                                                         \
         if (script->stepModeEnabled())                                        \
             ENABLE_INTERRUPTS();                                              \
@@ -1882,23 +1868,16 @@ js::Interpret(JSContext *cx, StackFrame 
     JSRuntime *const rt = cx->runtime;
     JSScript *script;
     SET_SCRIPT(regs.fp()->script());
     double *pcCounts = script->pcCounters.get(JSPCCounters::INTERP);
     ENABLE_PCCOUNT_INTERRUPTS();
     Value *argv = regs.fp()->maybeFormalArgs();
     CHECK_INTERRUPT_HANDLER();
 
-#if defined(JS_TRACER) && defined(JS_METHODJIT)
-    bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT);
-# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
-#else
-# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0)
-#endif
-
     if (!entryFrame)
         entryFrame = regs.fp();
 
     /*
      * Initialize the index segment register used by LOAD_ATOM and
      * GET_FULL_INDEX macros below. As a register we use a pointer based on
      * the atom map to turn frequently executed LOAD_ATOM into simple array
      * access. For less frequent object and regexp loads we have to recover
@@ -2073,60 +2052,33 @@ js::Interpret(JSContext *cx, StackFrame 
         }
 
 #ifdef JS_TRACER
 #ifdef JS_METHODJIT
         if (TRACE_PROFILER(cx) && interpMode == JSINTERP_PROFILE) {
             LoopProfile *prof = TRACE_PROFILER(cx);
             JS_ASSERT(!TRACE_RECORDER(cx));
             LoopProfile::ProfileAction act = prof->profileOperation(cx, op);
-            switch (act) {
-                case LoopProfile::ProfComplete:
-                    if (interpMode != JSINTERP_NORMAL) {
-                        leaveOnSafePoint = true;
-                        LEAVE_ON_SAFE_POINT();
-                    }
-                    break;
-                default:
-                    moreInterrupts = true;
-                    break;
-            }
+            if (act != LoopProfile::ProfComplete)
+                moreInterrupts = true;
         }
 #endif
         if (TraceRecorder* tr = TRACE_RECORDER(cx)) {
             JS_ASSERT(!TRACE_PROFILER(cx));
             AbortableRecordingStatus status = tr->monitorRecording(op);
             JS_ASSERT_IF(cx->isExceptionPending(), status == ARECORD_ERROR);
 
-            if (interpMode != JSINTERP_NORMAL) {
-                JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT);
-                switch (status) {
-                  case ARECORD_IMACRO_ABORTED:
-                  case ARECORD_ABORTED:
-                  case ARECORD_COMPLETED:
-                  case ARECORD_STOP:
-#ifdef JS_METHODJIT
-                    leaveOnSafePoint = true;
-                    LEAVE_ON_SAFE_POINT();
-#endif
-                    break;
-                  default:
-                    break;
-                }
-            }
-
             switch (status) {
               case ARECORD_CONTINUE:
                 moreInterrupts = true;
                 break;
               case ARECORD_IMACRO:
               case ARECORD_IMACRO_ABORTED:
                 atoms = rt->atomState.commonAtomsStart();
                 op = JSOp(*regs.pc);
-                CLEAR_LEAVE_ON_TRACE_POINT();
                 if (status == ARECORD_IMACRO)
                     DO_OP();    /* keep interrupting for op. */
                 break;
               case ARECORD_ERROR:
                 // The code at 'error:' aborts the recording.
                 goto error;
               case ARECORD_ABORTED:
               case ARECORD_COMPLETED:
@@ -2161,34 +2113,33 @@ ADD_EMPTY_CASE(JSOP_TRY)
 ADD_EMPTY_CASE(JSOP_STARTXML)
 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
 #endif
 ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN)
 END_EMPTY_CASES
 
 BEGIN_CASE(JSOP_TRACE)
 BEGIN_CASE(JSOP_NOTRACE)
-    LEAVE_ON_SAFE_POINT();
+    /* No-op */
 END_CASE(JSOP_TRACE)
 
 check_backedge:
 {
     CHECK_BRANCH();
     if (op != JSOP_NOTRACE && op != JSOP_TRACE)
         DO_OP();
 
 #ifdef JS_TRACER
     if (TRACING_ENABLED(cx) && (TRACE_RECORDER(cx) || TRACE_PROFILER(cx) || (op == JSOP_TRACE && !useMethodJIT))) {
         MonitorResult r = MonitorLoopEdge(cx, interpMode);
         if (r == MONITOR_RECORDING) {
             JS_ASSERT(TRACE_RECORDER(cx));
             JS_ASSERT(!TRACE_PROFILER(cx));
             MONITOR_BRANCH_TRACEVIS;
             ENABLE_INTERRUPTS();
-            CLEAR_LEAVE_ON_TRACE_POINT();
         }
         JS_ASSERT_IF(cx->isExceptionPending(), r == MONITOR_ERROR);
         RESTORE_INTERP_VARS_CHECK_EXCEPTION();
         op = (JSOp) *regs.pc;
         DO_OP();
     }
 #endif /* JS_TRACER */
 
@@ -2303,17 +2254,16 @@ BEGIN_CASE(JSOP_STOP)
          */
         JS_ASSERT(op == JSOP_STOP);
         JS_ASSERT((uintN)(regs.sp - regs.fp()->slots()) <= script->nslots);
         jsbytecode *imacpc = regs.fp()->imacropc();
         regs.pc = imacpc + js_CodeSpec[*imacpc].length;
         if (js_CodeSpec[*imacpc].format & JOF_DECOMPOSE)
             regs.pc += GetDecomposeLength(imacpc, js_CodeSpec[*imacpc].length);
         regs.fp()->clearImacropc();
-        LEAVE_ON_SAFE_POINT();
         atoms = script->atoms;
         op = JSOp(*regs.pc);
         DO_OP();
     }
 #endif
 
     interpReturnOK = true;
     if (entryFrame != regs.fp())
@@ -5404,22 +5354,16 @@ BEGIN_CASE(JSOP_RETSUB)
     len = rval.toInt32();
     regs.pc = script->code;
 END_VARLEN_CASE
 }
 
 BEGIN_CASE(JSOP_EXCEPTION)
     PUSH_COPY(cx->getPendingException());
     cx->clearPendingException();
-#if defined(JS_TRACER) && defined(JS_METHODJIT)
-    if (interpMode == JSINTERP_PROFILE) {
-        leaveOnSafePoint = true;
-        LEAVE_ON_SAFE_POINT();
-    }
-#endif
     CHECK_BRANCH();
 END_CASE(JSOP_EXCEPTION)
 
 BEGIN_CASE(JSOP_FINALLY)
     CHECK_BRANCH();
 END_CASE(JSOP_FINALLY)
 
 BEGIN_CASE(JSOP_THROWING)
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -230,20 +230,19 @@ ExecuteKernel(JSContext *cx, JSScript *s
 extern bool
 Execute(JSContext *cx, JSScript *script, JSObject &scopeChain, Value *rval);
 
 /* Flags to toggle js::Interpret() execution. */
 enum InterpMode
 {
     JSINTERP_NORMAL    = 0, /* interpreter is running normally */
     JSINTERP_RECORD    = 1, /* interpreter has been started to record/run traces */
-    JSINTERP_SAFEPOINT = 2, /* interpreter should leave on a method JIT safe point */
-    JSINTERP_PROFILE   = 3, /* interpreter should profile a loop */
-    JSINTERP_REJOIN    = 4, /* as normal, but the frame has already started */
-    JSINTERP_SKIP_TRAP = 5  /* as REJOIN, but skip trap at first opcode */
+    JSINTERP_PROFILE   = 2, /* interpreter should profile a loop */
+    JSINTERP_REJOIN    = 3, /* as normal, but the frame has already started */
+    JSINTERP_SKIP_TRAP = 4  /* as REJOIN, but skip trap at first opcode */
 };
 
 /*
  * Execute the caller-initialized frame for a user-defined script or function
  * pointed to by cx->fp until completion or error.
  */
 extern JS_REQUIRES_STACK JS_NEVER_INLINE bool
 Interpret(JSContext *cx, StackFrame *stopFp, InterpMode mode = JSINTERP_NORMAL);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -6785,17 +6785,17 @@ mjit::Compiler::jumpAndTrace(Jump j, jsb
                                                    offsetof(TraceICInfo, loopCounter)));
 # endif
 
     /* Save and restore compiler-tracked PC, so cx->regs is right in InvokeTracer. */
     {
         jsbytecode* pc = PC;
         PC = target;
 
-        OOL_STUBCALL(stubs::InvokeTracer, REJOIN_FINISH_FRAME);
+        OOL_STUBCALL(stubs::InvokeTracer, REJOIN_RUN_TRACER);
 
         PC = pc;
     }
 
     Jump no = stubcc.masm.branchTestPtr(Assembler::Zero, Registers::ReturnReg,
                                         Registers::ReturnReg);
     if (!cx->typeInferenceEnabled())
         stubcc.masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg);
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -172,20 +172,27 @@ top:
 }
 
 /*
  * Clean up a frame and return.
  */
 static void
 InlineReturn(VMFrame &f)
 {
+    bool shiftResult = f.fp()->loweredCallOrApply();
+
     JS_ASSERT(f.fp() != f.entryfp);
     JS_ASSERT(!IsActiveWithOrBlock(f.cx, f.fp()->scopeChain(), 0));
     f.cx->stack.popInlineFrame(f.regs);
 
+    if (shiftResult) {
+        f.regs.sp[-2] = f.regs.sp[-1];
+        f.regs.sp--;
+    }
+
     DebugOnly<JSOp> op = js_GetOpcode(f.cx, f.fp()->script(), f.regs.pc);
     JS_ASSERT(op == JSOP_CALL ||
               op == JSOP_NEW ||
               op == JSOP_EVAL ||
               op == JSOP_FUNCALL ||
               op == JSOP_FUNAPPLY);
     f.regs.pc += JSOP_CALL_LENGTH;
 }
@@ -769,47 +776,31 @@ HandleErrorInExcessFrame(VMFrame &f, Sta
     }
 
     JS_ASSERT(&f.regs == &cx->regs());
     JS_ASSERT_IF(!returnOK, cx->fp() == stopFp);
 
     return returnOK;
 }
 
-/* Returns whether the current PC has method JIT'd code. */
-static inline void *
-AtSafePoint(JSContext *cx)
-{
-    StackFrame *fp = cx->fp();
-    if (fp->hasImacropc())
-        return NULL;
-
-    JSScript *script = fp->script();
-    return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs().pc);
-}
-
 /*
  * Interprets until either a safe point is reached that has method JIT'd
  * code, or the current frame tries to return.
  */
 static inline JSBool
 PartialInterpret(VMFrame &f)
 {
     JSContext *cx = f.cx;
     StackFrame *fp = cx->fp();
+    JS_ASSERT(!fp->finishedInInterpreter());
 
-#ifdef DEBUG
-    JSScript *script = fp->script();
-    JS_ASSERT(!fp->finishedInInterpreter());
-    JS_ASSERT(fp->hasImacropc() ||
-              !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs().pc));
-#endif
-
-    JSBool ok = JS_TRUE;
-    ok = Interpret(cx, fp, JSINTERP_SAFEPOINT);
+    JS_ASSERT(!cx->compartment->jaegerCompartment()->finishingTracer);
+    cx->compartment->jaegerCompartment()->finishingTracer = true;
+    JSBool ok = Interpret(cx, fp, JSINTERP_REJOIN);
+    cx->compartment->jaegerCompartment()->finishingTracer = false;
 
     return ok;
 }
 
 JS_STATIC_ASSERT(JSOP_NOP == 0);
 
 /*
  * Returns whether the current PC would return, or if the frame has already
@@ -911,23 +902,16 @@ EvaluateExcessFrame(VMFrame &f, StackFra
      * A "finished" frame is when the interpreter rested on a STOP,
      * RETURN, RETRVAL, etc. We check for finished frames BEFORE looking
      * for a safe point. If the frame was finished, we could have already
      * called ScriptEpilogue(), and entering the JIT could call it twice.
      */
     if (!fp->hasImacropc() && FrameIsFinished(cx))
         return HandleFinishedFrame(f, entryFrame);
 
-    if (void *ncode = AtSafePoint(cx)) {
-        if (!JaegerShotAtSafePoint(cx, ncode, false))
-            return false;
-        InlineReturn(f);
-        return true;
-    }
-
     return PartialInterpret(f);
 }
 
 /*
  * Evaluate frames newer than the entry frame until all are gone. This will
  * always leave f.regs.fp == entryFrame.
  */
 static bool
@@ -1008,30 +992,37 @@ js::mjit::ResetTraceHint(JSScript *scrip
         ResetTraceHintAt(script, script->jitNormal, pc, index, full);
 
     if (script->jitCtor)
         ResetTraceHintAt(script, script->jitCtor, pc, index, full);
 #endif
 }
 
 #if JS_MONOIC
-void *
+JSBool
 RunTracer(VMFrame &f, ic::TraceICInfo &ic)
 #else
-void *
+JSBool
 RunTracer(VMFrame &f)
 #endif
 {
     JSContext *cx = f.cx;
     StackFrame *entryFrame = f.fp();
     TracePointAction tpa;
 
     /* :TODO: nuke PIC? */
     if (!cx->traceJitEnabled)
-        return NULL;
+        return false;
+
+    /*
+     * Don't reenter the tracer while finishing frames we bailed out from,
+     * to avoid over-recursing.
+     */
+    if (cx->compartment->jaegerCompartment()->finishingTracer)
+        return false;
 
     /*
      * Force initialization of the entry frame's scope chain and return value,
      * if necessary.  The tracer can query the scope chain without needing to
      * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the
      * correct return value stored if we trace/interpret through to the end
      * of the frame.
      */
@@ -1068,16 +1059,19 @@ RunTracer(VMFrame &f)
          * garbage, confusing the recompiler.
          */
         FrameRegs regs = f.regs;
         PreserveRegsGuard regsGuard(cx, regs);
 
         tpa = MonitorTracePoint(f.cx, &blacklist, traceData, traceEpoch,
                                 loopCounter, hits);
         JS_ASSERT(!TRACE_RECORDER(cx));
+
+        if (tpa != TPA_Nothing)
+            ClearAllFrames(cx->compartment);
     }
 
 #if JS_MONOIC
     if (!monitor.recompiled()) {
         ic.loopCounterStart = *loopCounter;
         if (blacklist)
             DisableTraceHint(entryFrame->jit(), ic);
     }
@@ -1085,21 +1079,21 @@ RunTracer(VMFrame &f)
 
     // Even though ExecuteTree() bypasses the interpreter, it should propagate
     // error failures correctly.
     JS_ASSERT_IF(cx->isExceptionPending(), tpa == TPA_Error);
 
     JS_ASSERT(f.fp() == cx->fp());
     switch (tpa) {
       case TPA_Nothing:
-        return NULL;
+        return false;
 
       case TPA_Error:
         if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter()))
-            THROWV(NULL);
+            THROWV(false);
         JS_ASSERT(!cx->fp()->hasImacropc());
         break;
 
       case TPA_RanStuff:
       case TPA_Recorded:
         break;
     }
 
@@ -1125,58 +1119,51 @@ RunTracer(VMFrame &f)
      * trampoline. This trampoline simulates the frame-popping portion of
      * emitReturn (except without the benefit of the FrameState) and will
      * produce the necessary register state to return to the caller.
      */
 
   restart:
     /* Step 1. Finish frames created after the entry frame. */
     if (!FinishExcessFrames(f, entryFrame))
-        THROWV(NULL);
+        THROWV(false);
 
     /* IMacros are guaranteed to have been removed by now. */
     JS_ASSERT(f.fp() == entryFrame);
     JS_ASSERT(!entryFrame->hasImacropc());
 
     /* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */
     if (FrameIsFinished(cx)) {
         if (!HandleFinishedFrame(f, entryFrame))
-            THROWV(NULL);
-        void *addr = *f.returnAddressLocation();
-        if (addr != JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline))
-            *f.returnAddressLocation() = cx->jaegerCompartment()->forceReturnFromFastCall();
-        return NULL;
+            THROWV(false);
+        return true;
     }
 
-    /* Step 3. If entryFrame is at a safe point, just leave. */
-    if (void *ncode = AtSafePoint(cx))
-        return ncode;
-
-    /* Step 4. Do a partial interp, then restart the whole process. */
+    /* Step 3. Do a partial interp, then restart the whole process. */
     if (!PartialInterpret(f)) {
         if (!HandleErrorInExcessFrame(f, entryFrame))
-            THROWV(NULL);
+            THROWV(false);
     }
 
     goto restart;
 }
 
 #endif /* JS_TRACER */
 
 #if defined JS_TRACER
 # if defined JS_MONOIC
-void *JS_FASTCALL
+JSBool JS_FASTCALL
 stubs::InvokeTracer(VMFrame &f, ic::TraceICInfo *ic)
 {
     return RunTracer(f, *ic);
 }
 
 # else
 
-void *JS_FASTCALL
+JSBool JS_FASTCALL
 stubs::InvokeTracer(VMFrame &f)
 {
     return RunTracer(f);
 }
 # endif /* JS_MONOIC */
 #endif /* JS_TRACER */
 
 /* :XXX: common out with identical copy in Compiler.cpp */
@@ -1567,20 +1554,31 @@ js_InternalInterpret(void *returnData, v
         }
         if (takeBranch)
             f.regs.pc = nextpc + GET_JUMP_OFFSET(nextpc);
         else
             f.regs.pc = nextpc + analyze::GetBytecodeLength(nextpc);
         break;
       }
 
-      case REJOIN_FINISH_FRAME:
-        /* InvokeTracer finishes the frame it is given, including the epilogue. */
-        if (fp->isFunctionFrame())
-            fp->markFunctionEpilogueDone();
+      case REJOIN_RUN_TRACER:
+        if (returnReg) {
+            /* InvokeTracer finishes the frame it is given, including the epilogue. */
+            if (fp->isFunctionFrame())
+                fp->markFunctionEpilogueDone();
+            if (fp != f.entryfp) {
+                InlineReturn(f);
+                cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished);
+                *f.oldregs = f.regs;
+                return NULL;
+            } else {
+                cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Returned);
+                return NULL;
+            }
+        }
         break;
 
       default:
         JS_NOT_REACHED("Missing rejoin");
     }
 
     if (nextDepth == uint32(-1))
         nextDepth = analysis->getCode(f.regs.pc).stackDepth;
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -889,17 +889,17 @@ mjit::EnterMethodJIT(JSContext *cx, Stac
     JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms());
 #endif
 
     /* Undo repointRegs in SetVMFrameRegs. */
     cx->stack.repointRegs(&oldRegs);
 
     JaegerStatus status = cx->compartment->jaegerCompartment()->lastUnfinished();
     if (status) {
-        if (partial) {
+        if (partial || status == Jaeger_Returned) {
             /*
              * Being called from the interpreter, which will resume execution
              * where the JIT left off.
              */
             return status;
         }
 
         /*
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -314,18 +314,18 @@ enum RejoinState {
     REJOIN_BINARY,
 
     /*
      * For an opcode fused with IFEQ/IFNE, call returns a boolean indicating
      * the result of the comparison and whether to take or not take the branch.
      */
     REJOIN_BRANCH,
 
-    /* Calls to RunTracer which finished the given frame. */
-    REJOIN_FINISH_FRAME
+    /* Calls to RunTracer which either finished the frame or did nothing. */
+    REJOIN_RUN_TRACER
 };
 
 /* Helper to watch for recompilation and frame expansion activity on a compartment. */
 struct RecompilationMonitor
 {
     JSContext *cx;
 
     /*
@@ -458,16 +458,19 @@ class JaegerCompartment {
 
     /*
      * References held on pools created for native ICs, where the IC was
      * destroyed and we are waiting for the pool to finish use and jump
      * into the interpoline.
      */
     Vector<StackFrame *, 8, SystemAllocPolicy> orphanedNativeFrames;
     Vector<JSC::ExecutablePool *, 8, SystemAllocPolicy> orphanedNativePools;
+
+    /* Whether frames pushed after bailing out in RunTracer are unwinding. */
+    bool finishingTracer;
 };
 
 /*
  * Allocation policy for compiler jstl objects. The goal is to free the
  * compiler from having to check and propagate OOM after every time we
  * append to a vector. We do this by reporting OOM to the engine and
  * setting a flag on the compiler when OOM occurs. The compiler is required
  * to check for OOM only before trying to use the contents of the list.
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -109,19 +109,19 @@ struct UncachedCallResult {
  * pointer that can be used to call the function, or throw.
  */
 void UncachedCallHelper(VMFrame &f, uint32 argc, bool lowered, UncachedCallResult *ucr);
 void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
 
 void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
 void JS_FASTCALL Throw(VMFrame &f);
 #if JS_MONOIC
-void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic);
+JSBool JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic);
 #else
-void * JS_FASTCALL InvokeTracer(VMFrame &f);
+JSBool JS_FASTCALL InvokeTracer(VMFrame &f);
 #endif
 
 void * JS_FASTCALL LookupSwitch(VMFrame &f, jsbytecode *pc);
 void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc);
 
 void JS_FASTCALL BindName(VMFrame &f);
 void JS_FASTCALL BindNameNoCache(VMFrame &f, JSAtom *atom);
 JSObject * JS_FASTCALL BindGlobalName(VMFrame &f);