Bug 882111 - Don't push an interpreter frame when calling into the JITs. r=djvj
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 19 Jun 2013 19:10:04 +0200
changeset 147142 3efe3f3d2c25b1a8a86912f32c623e4e2d6fa0c1
parent 147141 60a18af6cc27a05d769e0da68973a0551466acae
child 147143 aded0559ac48b019e4106eafdfa11f95a06a7705
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj
bugs882111
milestone24.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
Bug 882111 - Don't push an interpreter frame when calling into the JITs. r=djvj
js/src/ion/BaselineFrame.h
js/src/ion/BaselineJIT.cpp
js/src/ion/BaselineJIT.h
js/src/ion/Ion.cpp
js/src/ion/Ion.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonCompartment.h
js/src/jsgc.cpp
js/src/jsiter.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/ion/BaselineFrame.h
+++ b/js/src/ion/BaselineFrame.h
@@ -145,30 +145,30 @@ class BaselineFrame
         JS_ASSERT((size % sizeof(Value)) == 0);
         return size / sizeof(Value);
     }
     Value *valueSlot(size_t slot) const {
         JS_ASSERT(slot < numValueSlots());
         return (Value *)this - (slot + 1);
     }
 
-    Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing) const {
+    Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
         JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
         JS_ASSERT(i < script()->nfixed);
         return *valueSlot(i);
     }
 
-    Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing) const {
+    Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
         JS_ASSERT(i < numFormalArgs());
         JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
         JS_ASSERT_IF(checkAliasing, !script()->formalIsAliased(i));
         return argv()[i];
     }
 
-    Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const {
+    Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
         JS_ASSERT(i < numActualArgs());
         JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
         JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i));
         return argv()[i];
     }
 
     Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
 #ifdef DEBUG
@@ -332,16 +332,19 @@ class BaselineFrame
         return isNonStrictEvalFrame() && isDirectEvalFrame();
     }
     bool isNonEvalFunctionFrame() const {
         return isFunctionFrame() && !isEvalFrame();
     }
     bool isDebuggerFrame() const {
         return false;
     }
+    bool isGeneratorFrame() const {
+        return false;
+    }
 
     IonJSFrameLayout *framePrefix() const {
         uint8_t *fp = (uint8_t *)this + Size() + FramePointerOffset;
         return (IonJSFrameLayout *)fp;
     }
 
     // Methods below are used by the compiler.
     static size_t offsetOfCalleeToken() {
--- a/js/src/ion/BaselineJIT.cpp
+++ b/js/src/ion/BaselineJIT.cpp
@@ -38,150 +38,162 @@ BaselineScript::BaselineScript(uint32_t 
 #ifdef DEBUG
     spsOn_(false),
 #endif
     spsPushToggleOffset_(spsPushToggleOffset),
     flags_(0)
 { }
 
 static const size_t BASELINE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4096;
+static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000;
 
 static bool
 CheckFrame(StackFrame *fp)
 {
     if (fp->isGeneratorFrame()) {
         IonSpew(IonSpew_BaselineAbort, "generator frame");
         return false;
     }
 
     if (fp->isDebuggerFrame()) {
         // Debugger eval-in-frame. These are likely short-running scripts so
         // don't bother compiling them for now.
         IonSpew(IonSpew_BaselineAbort, "debugger frame");
         return false;
     }
 
-    static const unsigned MAX_ARGS_LENGTH = 20000;
-
-    if (fp->isNonEvalFunctionFrame() && fp->numActualArgs() > MAX_ARGS_LENGTH) {
+    if (fp->isNonEvalFunctionFrame() && fp->numActualArgs() > BASELINE_MAX_ARGS_LENGTH) {
         // Fall back to the interpreter to avoid running out of stack space.
         IonSpew(IonSpew_BaselineAbort, "Too many arguments (%u)", fp->numActualArgs());
         return false;
     }
 
     return true;
 }
 
 static bool
 IsJSDEnabled(JSContext *cx)
 {
     return cx->compartment()->debugMode() && cx->runtime()->debugHooks.callHook;
 }
 
 static IonExecStatus
-EnterBaseline(JSContext *cx, StackFrame *fp, void *jitcode, bool osr)
+EnterBaseline(JSContext *cx, EnterJitData &data)
 {
     JS_CHECK_RECURSION(cx, return IonExec_Aborted);
     JS_ASSERT(ion::IsBaselineEnabled(cx));
-    JS_ASSERT(CheckFrame(fp));
+    JS_ASSERT_IF(data.osrFrame, CheckFrame(data.osrFrame));
 
     EnterIonCode enter = cx->compartment()->ionCompartment()->enterBaselineJIT();
 
-    // maxArgc is the maximum of arguments between the number of actual
-    // arguments and the number of formal arguments. It accounts for |this|.
-    int maxArgc = 0;
-    Value *maxArgv = NULL;
-    unsigned numActualArgs = 0;
-    RootedValue thisv(cx);
+    // Caller must construct |this| before invoking the Ion function.
+    JS_ASSERT_IF(data.constructing, data.maxArgv[0].isObject());
 
-    void *calleeToken;
-    if (fp->isNonEvalFunctionFrame()) {
-        numActualArgs = fp->numActualArgs();
-        maxArgc = Max(numActualArgs, fp->numFormalArgs()) + 1; // +1 = include |this|
-        maxArgv = fp->argv() - 1; // -1 = include |this|
-        calleeToken = CalleeToToken(&fp->callee());
-    } else {
-        // For eval function frames, set the callee token to the enclosing function.
-        if (fp->isFunctionFrame())
-            calleeToken = CalleeToToken(&fp->callee());
-        else
-            calleeToken = CalleeToToken(fp->script());
-        thisv = fp->thisValue();
-        maxArgc = 1;
-        maxArgv = thisv.address();
-    }
-
-    // Caller must construct |this| before invoking the Ion function.
-    JS_ASSERT_IF(fp->isConstructing(), fp->functionThis().isObject());
-
-    RootedValue result(cx, Int32Value(numActualArgs));
+    data.result.setInt32(data.numActualArgs);
     {
         AssertCompartmentUnchanged pcc(cx);
         IonContext ictx(cx, NULL);
-        JitActivation activation(cx, fp->isConstructing());
+        JitActivation activation(cx, data.constructing);
         JSAutoResolveFlags rf(cx, RESOLVE_INFER);
-
-        fp->setRunningInJit();
+        AutoFlushInhibitor afi(cx->compartment()->ionCompartment());
 
-        // Pass the scope chain for global and eval frames.
-        JSObject *scopeChain = NULL;
-        if (!fp->isNonEvalFunctionFrame())
-            scopeChain = fp->scopeChain();
+        if (data.osrFrame)
+            data.osrFrame->setRunningInJit();
 
-        // For OSR, pass the number of locals + stack values.
-        uint32_t numStackValues = osr ? fp->script()->nfixed + cx->stack.regs().stackDepth() : 0;
-        JS_ASSERT_IF(osr, !IsJSDEnabled(cx));
+        JS_ASSERT_IF(data.osrFrame, !IsJSDEnabled(cx));
 
-        AutoFlushInhibitor afi(cx->compartment()->ionCompartment());
         // Single transition point from Interpreter to Baseline.
-        enter(jitcode, maxArgc, maxArgv, osr ? fp : NULL, calleeToken, scopeChain, numStackValues,
-              result.address());
+        enter(data.jitcode, data.maxArgc, data.maxArgv, data.osrFrame, data.calleeToken,
+              data.scopeChain, data.osrNumStackValues, data.result.address());
 
-        fp->clearRunningInJit();
+        if (data.osrFrame)
+            data.osrFrame->clearRunningInJit();
     }
 
     JS_ASSERT(!cx->runtime()->hasIonReturnOverride());
 
-    // The trampoline wrote the return value but did not set the HAS_RVAL flag.
-    fp->setReturnValue(result);
-
-    // Ion callers wrap primitive constructor return.
-    if (!result.isMagic() && fp->isConstructing() && fp->returnValue().isPrimitive())
-        fp->setReturnValue(ObjectValue(fp->constructorThis()));
+    // Jit callers wrap primitive constructor return.
+    if (!data.result.isMagic() && data.constructing && data.result.isPrimitive())
+        data.result = data.maxArgv[0];
 
     // Release temporary buffer used for OSR into Ion.
     cx->runtime()->getIonRuntime(cx)->freeOsrTempData();
 
-    JS_ASSERT_IF(result.isMagic(), result.isMagic(JS_ION_ERROR));
-    return result.isMagic() ? IonExec_Error : IonExec_Ok;
+    JS_ASSERT_IF(data.result.isMagic(), data.result.isMagic(JS_ION_ERROR));
+    return data.result.isMagic() ? IonExec_Error : IonExec_Ok;
 }
 
 IonExecStatus
-ion::EnterBaselineMethod(JSContext *cx, StackFrame *fp)
+ion::EnterBaselineMethod(JSContext *cx, RunState &state)
 {
-    BaselineScript *baseline = fp->script()->baselineScript();
-    void *jitcode = baseline->method()->raw();
+    BaselineScript *baseline = state.script()->baselineScript();
+
+    EnterJitData data(cx);
+    data.jitcode = baseline->method()->raw();
 
-    return EnterBaseline(cx, fp, jitcode, /* osr = */false);
+    AutoValueVector vals(cx);
+    if (!SetEnterJitData(cx, data, state, vals))
+        return IonExec_Error;
+
+    IonExecStatus status = EnterBaseline(cx, data);
+    if (status != IonExec_Ok)
+        return status;
+
+    state.setReturnValue(data.result);
+    return IonExec_Ok;
 }
 
 IonExecStatus
 ion::EnterBaselineAtBranch(JSContext *cx, StackFrame *fp, jsbytecode *pc)
 {
     JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
 
     BaselineScript *baseline = fp->script()->baselineScript();
-    uint8_t *jitcode = baseline->nativeCodeForPC(fp->script(), pc);
+
+    EnterJitData data(cx);
+    data.jitcode = baseline->nativeCodeForPC(fp->script(), pc);
 
     // Skip debug breakpoint/trap handler, the interpreter already handled it
     // for the current op.
     if (cx->compartment()->debugMode())
-        jitcode += MacroAssembler::ToggledCallSize();
+        data.jitcode += MacroAssembler::ToggledCallSize();
+
+    data.osrFrame = fp;
+    data.osrNumStackValues = fp->script()->nfixed + cx->interpreterRegs().stackDepth();
+
+    RootedValue thisv(cx);
 
-    return EnterBaseline(cx, fp, jitcode, /* osr = */true);
+    if (fp->isNonEvalFunctionFrame()) {
+        data.constructing = fp->isConstructing();
+        data.numActualArgs = fp->numActualArgs();
+        data.maxArgc = Max(fp->numActualArgs(), fp->numFormalArgs()) + 1; // +1 = include |this|
+        data.maxArgv = fp->argv() - 1; // -1 = include |this|
+        data.scopeChain = NULL;
+        data.calleeToken = CalleeToToken(&fp->callee());
+    } else {
+        thisv = fp->thisValue();
+        data.constructing = false;
+        data.numActualArgs = 0;
+        data.maxArgc = 1;
+        data.maxArgv = thisv.address();
+        data.scopeChain = fp->scopeChain();
+
+        // For eval function frames, set the callee token to the enclosing function.
+        if (fp->isFunctionFrame())
+            data.calleeToken = CalleeToToken(&fp->callee());
+        else
+            data.calleeToken = CalleeToToken(fp->script());
+    }
+
+    IonExecStatus status = EnterBaseline(cx, data);
+    if (status != IonExec_Ok)
+        return status;
+
+    fp->setReturnValue(data.result);
+    return IonExec_Ok;
 }
 
 static MethodStatus
 BaselineCompile(JSContext *cx, HandleScript script)
 {
     JS_ASSERT(!script->hasBaselineScript());
     JS_ASSERT(script->canBaselineCompile());
 
@@ -204,54 +216,39 @@ BaselineCompile(JSContext *cx, HandleScr
     JS_ASSERT_IF(status != Method_Compiled, !script->hasBaselineScript());
 
     if (status == Method_CantCompile)
         script->setBaselineScript(BASELINE_DISABLED_SCRIPT);
 
     return status;
 }
 
-MethodStatus
-ion::CanEnterBaselineJIT(JSContext *cx, JSScript *scriptArg, StackFrame *fp, bool newType)
+static MethodStatus
+CanEnterBaselineJIT(JSContext *cx, HandleScript script, bool osr)
 {
-    // Skip if baseline compilation is disabled in options.
     JS_ASSERT(ion::IsBaselineEnabled(cx));
 
     // Skip if the script has been disabled.
-    if (!scriptArg->canBaselineCompile())
+    if (!script->canBaselineCompile())
         return Method_Skipped;
 
-    if (scriptArg->length > BaselineScript::MAX_JSSCRIPT_LENGTH)
-        return Method_CantCompile;
-
-    RootedScript script(cx, scriptArg);
-
-    // If constructing, allocate a new |this| object.
-    if (fp->isConstructing() && fp->functionThis().isPrimitive()) {
-        RootedObject callee(cx, &fp->callee());
-        RootedObject obj(cx, CreateThisForFunction(cx, callee, newType));
-        if (!obj)
-            return Method_Skipped;
-        fp->functionThis().setObject(*obj);
-    }
-
-    if (!CheckFrame(fp))
+    if (script->length > BaselineScript::MAX_JSSCRIPT_LENGTH)
         return Method_CantCompile;
 
     if (!cx->compartment()->ensureIonCompartmentExists(cx))
         return Method_Error;
 
     if (script->hasBaselineScript())
         return Method_Compiled;
 
     // Check script use count. However, always eagerly compile scripts if JSD
     // is enabled, so that we don't have to OSR and don't have to update the
     // frame pointer stored in JSD's frames list.
     if (IsJSDEnabled(cx)) {
-        if (JSOp(*cx->stack.regs().pc) == JSOP_LOOPENTRY) // No OSR.
+        if (osr)
             return Method_Skipped;
     } else if (script->incUseCount() <= js_IonOptions.baselineUsesBeforeCompile) {
         return Method_Skipped;
     }
 
     if (script->isCallsiteClone) {
         // Ensure the original function is compiled too, so that bailouts from
         // Ion code have a BaselineScript to resume into.
@@ -266,16 +263,70 @@ ion::CanEnterBaselineJIT(JSContext *cx, 
             if (status != Method_Compiled)
                 return status;
         }
     }
 
     return BaselineCompile(cx, script);
 }
 
+MethodStatus
+ion::CanEnterBaselineAtBranch(JSContext *cx, StackFrame *fp, bool newType)
+{
+   // If constructing, allocate a new |this| object.
+   if (fp->isConstructing() && fp->functionThis().isPrimitive()) {
+       RootedObject callee(cx, &fp->callee());
+       RootedObject obj(cx, CreateThisForFunction(cx, callee, newType));
+       if (!obj)
+           return Method_Skipped;
+       fp->functionThis().setObject(*obj);
+   }
+
+   if (!CheckFrame(fp))
+       return Method_CantCompile;
+
+   RootedScript script(cx, fp->script());
+   return CanEnterBaselineJIT(cx, script, /* osr = */true);
+}
+
+MethodStatus
+ion::CanEnterBaselineMethod(JSContext *cx, RunState &state)
+{
+    if (state.isInvoke()) {
+        InvokeState &invoke = *state.asInvoke();
+
+        if (invoke.args().length() > BASELINE_MAX_ARGS_LENGTH) {
+            IonSpew(IonSpew_BaselineAbort, "Too many arguments (%u)", invoke.args().length());
+            return Method_CantCompile;
+        }
+
+        // If constructing, allocate a new |this| object.
+        if (invoke.constructing() && invoke.args().thisv().isPrimitive()) {
+            RootedObject callee(cx, &invoke.args().callee());
+            RootedObject obj(cx, CreateThisForFunction(cx, callee, invoke.useNewType()));
+            if (!obj)
+                return Method_Skipped;
+            invoke.args().setThis(ObjectValue(*obj));
+        }
+    } else if (state.isExecute()) {
+        ExecuteType type = state.asExecute()->type();
+        if (type == EXECUTE_DEBUG || type == EXECUTE_DEBUG_GLOBAL) {
+            IonSpew(IonSpew_BaselineAbort, "debugger frame");
+            return Method_CantCompile;
+        }
+    } else {
+        JS_ASSERT(state.isGenerator());
+        IonSpew(IonSpew_BaselineAbort, "generator frame");
+        return Method_CantCompile;
+    }
+
+    RootedScript script(cx, state.script());
+    return CanEnterBaselineJIT(cx, script, /* osr = */false);
+};
+
 // Be safe, align IC entry list to 8 in all cases.
 static const unsigned DataAlignment = sizeof(uintptr_t);
 
 BaselineScript *
 BaselineScript::New(JSContext *cx, uint32_t prologueOffset,
                     uint32_t spsPushToggleOffset, size_t icEntries,
                     size_t pcMappingIndexEntries, size_t pcMappingSize)
 {
--- a/js/src/ion/BaselineJIT.h
+++ b/js/src/ion/BaselineJIT.h
@@ -258,20 +258,23 @@ struct BaselineScript
 
 inline bool
 IsBaselineEnabled(JSContext *cx)
 {
     return cx->hasOption(JSOPTION_BASELINE);
 }
 
 MethodStatus
-CanEnterBaselineJIT(JSContext *cx, JSScript *scriptArg, StackFrame *fp, bool newType);
+CanEnterBaselineMethod(JSContext *cx, RunState &state);
+
+MethodStatus
+CanEnterBaselineAtBranch(JSContext *cx, StackFrame *fp, bool newType);
 
 IonExecStatus
-EnterBaselineMethod(JSContext *cx, StackFrame *fp);
+EnterBaselineMethod(JSContext *cx, RunState &state);
 
 IonExecStatus
 EnterBaselineAtBranch(JSContext *cx, StackFrame *fp, jsbytecode *pc);
 
 void
 FinishDiscardBaselineScript(FreeOp *fop, JSScript *script);
 
 void
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -1337,17 +1337,17 @@ OffThreadCompilationAvailable(JSContext 
     return OffThreadCompilationEnabled(cx)
         && cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL
         && !cx->runtime()->profilingScripts
         && !cx->runtime()->spsProfiler.enabled();
 }
 
 static AbortReason
 IonCompile(JSContext *cx, JSScript *script,
-           AbstractFramePtr fp, jsbytecode *osrPc, bool constructing,
+           BaselineFrame *baselineFrame, jsbytecode *osrPc, bool constructing,
            ExecutionMode executionMode)
 {
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
@@ -1386,22 +1386,22 @@ IonCompile(JSContext *cx, JSScript *scri
     AutoFlushCache afc("IonCompile");
 
     types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode));
     if (!enterCompiler.init(script))
         return AbortReason_Disable;
 
     AutoTempAllocatorRooter root(cx, temp);
 
-    IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &inspector, info, fp);
+    IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &inspector, info, baselineFrame);
     if (!builder)
         return AbortReason_Alloc;
 
     JS_ASSERT(!GetIonScript(builder->script(), executionMode));
-    JS_ASSERT(builder->script()->canIonCompile());
+    JS_ASSERT(CanIonCompile(builder->script(), executionMode));
 
     RootedScript builderScript(cx, builder->script());
     IonSpewNewFunction(graph, builderScript);
 
     if (!builder->build()) {
         IonSpew(IonSpew_Abort, "Builder failed to build.");
         return builder->abortReason();
     }
@@ -1432,53 +1432,47 @@ IonCompile(JSContext *cx, JSScript *scri
     bool success = codegen->link();
 
     IonSpewEndFunction();
 
     return success ? AbortReason_NoAbort : AbortReason_Disable;
 }
 
 static bool
-CheckFrame(AbstractFramePtr fp)
+TooManyArguments(unsigned nargs)
 {
-    if (fp.isEvalFrame()) {
-        // Eval frames are not yet supported. Supporting this will require new
-        // logic in pushBailoutFrame to deal with linking prev.
-        // Additionally, JSOP_DEFVAR support will require baking in isEvalFrame().
-        IonSpew(IonSpew_Abort, "eval frame");
-        return false;
-    }
-
-    if (fp.isGeneratorFrame()) {
-        // Err... no.
-        IonSpew(IonSpew_Abort, "generator frame");
-        return false;
-    }
-
-    if (fp.isDebuggerFrame()) {
-        IonSpew(IonSpew_Abort, "debugger frame");
-        return false;
-    }
-
-    // This check is to not overrun the stack. Eventually, we will want to
-    // handle this when we support JSOP_ARGUMENTS or function calls.
-    if (fp.isFunctionFrame() &&
-        (fp.numActualArgs() >= SNAPSHOT_MAX_NARGS ||
-         fp.numActualArgs() > js_IonOptions.maxStackArgs))
-    {
+    return (nargs >= SNAPSHOT_MAX_NARGS || nargs > js_IonOptions.maxStackArgs);
+}
+
+static bool
+CheckFrame(BaselineFrame *frame)
+{
+    JS_ASSERT(!frame->isGeneratorFrame());
+    JS_ASSERT(!frame->isDebuggerFrame());
+
+    // This check is to not overrun the stack.
+    if (frame->isFunctionFrame() && TooManyArguments(frame->numActualArgs())) {
         IonSpew(IonSpew_Abort, "too many actual args");
         return false;
     }
 
     return true;
 }
 
 static bool
 CheckScript(JSContext *cx, JSScript *script, bool osr)
 {
+    if (script->isForEval()) {
+        // Eval frames are not yet supported. Supporting this will require new
+        // logic in pushBailoutFrame to deal with linking prev.
+        // Additionally, JSOP_DEFVAR support will require baking in isEvalFrame().
+        IonSpew(IonSpew_Abort, "eval script");
+        return false;
+    }
+
     if (!script->analyzedArgsUsage() && !script->ensureRanAnalysis(cx)) {
         IonSpew(IonSpew_Abort, "OOM under ensureRanAnalysis");
         return false;
     }
 
     if (osr && script->needsArgsObj()) {
         // OSR-ing into functions with arguments objects is not supported.
         IonSpew(IonSpew_Abort, "OSR script has argsobj");
@@ -1541,17 +1535,17 @@ CanIonCompileScript(JSContext *cx, Handl
 {
     if (!script->canIonCompile() || !CheckScript(cx, script, osr))
         return false;
 
     return CheckScriptSize(cx, script) == Method_Compiled;
 }
 
 static MethodStatus
-Compile(JSContext *cx, HandleScript script, AbstractFramePtr fp, jsbytecode *osrPc,
+Compile(JSContext *cx, HandleScript script, BaselineFrame *osrFrame, jsbytecode *osrPc,
         bool constructing, ExecutionMode executionMode)
 {
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT(ion::IsBaselineEnabled(cx));
     JS_ASSERT_IF(osrPc != NULL, (JSOp)*osrPc == JSOP_LOOPENTRY);
 
     if (executionMode == SequentialExecution && !script->hasBaselineScript())
         return Method_Skipped;
@@ -1581,31 +1575,31 @@ Compile(JSContext *cx, HandleScript scri
 
     if (executionMode == SequentialExecution) {
         // Use getUseCount instead of incUseCount to avoid bumping the
         // use count twice.
         if (script->getUseCount() < js_IonOptions.usesBeforeCompile)
             return Method_Skipped;
     }
 
-    AbortReason reason = IonCompile(cx, script, fp, osrPc, constructing, executionMode);
+    AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode);
     if (reason == AbortReason_Disable)
         return Method_CantCompile;
 
     // Compilation succeeded or we invalidated right away or an inlining/alloc abort
     return HasIonScript(script, executionMode) ? Method_Compiled : Method_Skipped;
 }
 
 } // namespace ion
 } // namespace js
 
 // Decide if a transition from interpreter execution to Ion code should occur.
 // May compile or recompile the target JSScript.
 MethodStatus
-ion::CanEnterAtBranch(JSContext *cx, JSScript *script, AbstractFramePtr fp,
+ion::CanEnterAtBranch(JSContext *cx, JSScript *script, BaselineFrame *osrFrame,
                       jsbytecode *pc, bool isConstructing)
 {
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT((JSOp)*pc == JSOP_LOOPENTRY);
 
     // Skip if the script has been disabled.
     if (!script->canIonCompile())
         return Method_Skipped;
@@ -1618,24 +1612,24 @@ ion::CanEnterAtBranch(JSContext *cx, JSS
     if (script->hasIonScript() && script->ionScript()->bailoutExpected())
         return Method_Skipped;
 
     // Optionally ignore on user request.
     if (!js_IonOptions.osr)
         return Method_Skipped;
 
     // Mark as forbidden if frame can't be handled.
-    if (!CheckFrame(fp)) {
+    if (!CheckFrame(osrFrame)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
     RootedScript rscript(cx, script);
-    MethodStatus status = Compile(cx, rscript, fp, pc, isConstructing, SequentialExecution);
+    MethodStatus status = Compile(cx, rscript, osrFrame, pc, isConstructing, SequentialExecution);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     if (script->ionScript()->osrPc() != pc) {
         // If we keep failing to enter the script due to an OSR pc mismatch,
@@ -1650,88 +1644,100 @@ ion::CanEnterAtBranch(JSContext *cx, JSS
     }
 
     script->ionScript()->resetOsrPcMismatchCounter();
 
     return Method_Compiled;
 }
 
 MethodStatus
-ion::CanEnter(JSContext *cx, HandleScript script, AbstractFramePtr fp, bool isConstructing)
+ion::CanEnter(JSContext *cx, RunState &state)
 {
     JS_ASSERT(ion::IsEnabled(cx));
 
+    JSScript *script = state.script();
+
     // Skip if the script has been disabled.
     if (!script->canIonCompile())
         return Method_Skipped;
 
     // Skip if the script is being compiled off thread.
     if (script->isIonCompilingOffThread())
         return Method_Skipped;
 
     // Skip if the code is expected to result in a bailout.
     if (script->hasIonScript() && script->ionScript()->bailoutExpected())
         return Method_Skipped;
 
     // If constructing, allocate a new |this| object before building Ion.
     // Creating |this| is done before building Ion because it may change the
     // type information and invalidate compilation results.
-    if (isConstructing && fp.thisValue().isPrimitive()) {
-        RootedObject callee(cx, fp.callee());
-        RootedObject obj(cx, CreateThisForFunction(cx, callee, fp.useNewType()));
-        if (!obj || !ion::IsEnabled(cx)) // Note: OOM under CreateThis can disable TI.
-            return Method_Skipped;
-        fp.thisValue().setObject(*obj);
-    }
-
-    // Mark as forbidden if frame can't be handled.
-    if (!CheckFrame(fp)) {
+    if (state.isInvoke()) {
+        InvokeState &invoke = *state.asInvoke();
+
+        if (TooManyArguments(invoke.args().length())) {
+            IonSpew(IonSpew_Abort, "too many actual args");
+            ForbidCompilation(cx, script);
+            return Method_CantCompile;
+        }
+
+        if (invoke.constructing() && invoke.args().thisv().isPrimitive()) {
+            RootedScript scriptRoot(cx, script);
+            RootedObject callee(cx, &invoke.args().callee());
+            RootedObject obj(cx, CreateThisForFunction(cx, callee, invoke.useNewType()));
+            if (!obj || !ion::IsEnabled(cx)) // Note: OOM under CreateThis can disable TI.
+                return Method_Skipped;
+            invoke.args().setThis(ObjectValue(*obj));
+            script = scriptRoot;
+        }
+    } else if (state.isGenerator()) {
+        IonSpew(IonSpew_Abort, "generator frame");
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // If --ion-eager is used, compile with Baseline first, so that we
     // can directly enter IonMonkey.
     if (js_IonOptions.eagerCompilation && !script->hasBaselineScript()) {
-        bool newType = isConstructing && fp.asStackFrame()->useNewType();
-        MethodStatus status = CanEnterBaselineJIT(cx, script, fp.asStackFrame(), newType);
+        MethodStatus status = CanEnterBaselineMethod(cx, state);
         if (status != Method_Compiled)
             return status;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
     RootedScript rscript(cx, script);
-    MethodStatus status = Compile(cx, rscript, fp, NULL, isConstructing, SequentialExecution);
+    bool constructing = state.isInvoke() && state.asInvoke()->constructing();
+    MethodStatus status = Compile(cx, rscript, NULL, NULL, constructing, SequentialExecution);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
 
 MethodStatus
-ion::CompileFunctionForBaseline(JSContext *cx, HandleScript script, AbstractFramePtr fp,
+ion::CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame,
                                 bool isConstructing)
 {
     JS_ASSERT(ion::IsEnabled(cx));
-    JS_ASSERT(fp.fun()->nonLazyScript()->canIonCompile());
-    JS_ASSERT(!fp.fun()->nonLazyScript()->isIonCompilingOffThread());
-    JS_ASSERT(!fp.fun()->nonLazyScript()->hasIonScript());
-    JS_ASSERT(fp.isFunctionFrame());
+    JS_ASSERT(frame->fun()->nonLazyScript()->canIonCompile());
+    JS_ASSERT(!frame->fun()->nonLazyScript()->isIonCompilingOffThread());
+    JS_ASSERT(!frame->fun()->nonLazyScript()->hasIonScript());
+    JS_ASSERT(frame->isFunctionFrame());
 
     // Mark as forbidden if frame can't be handled.
-    if (!CheckFrame(fp)) {
+    if (!CheckFrame(frame)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    MethodStatus status = Compile(cx, script, fp, NULL, isConstructing, SequentialExecution);
+    MethodStatus status = Compile(cx, script, frame, NULL, isConstructing, SequentialExecution);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
 }
@@ -1746,18 +1752,17 @@ ion::CanEnterInParallel(JSContext *cx, H
     // condition differently treats it more like an error.
     if (!script->canParallelIonCompile())
         return Method_Skipped;
 
     // Skip if the script is being compiled off thread.
     if (script->isParallelIonCompilingOffThread())
         return Method_Skipped;
 
-    MethodStatus status = Compile(cx, script, AbstractFramePtr(), NULL, false,
-                                  ParallelExecution);
+    MethodStatus status = Compile(cx, script, NULL, NULL, false, ParallelExecution);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script, ParallelExecution);
         return status;
     }
 
     // This can GC, so afterward, script->parallelIon is
     // not guaranteed to be valid.
@@ -1803,105 +1808,134 @@ ion::CanEnterUsingFastInvoke(JSContext *
 
     if (!script->hasIonScript())
         return Method_Skipped;
 
     return Method_Compiled;
 }
 
 static IonExecStatus
-EnterIon(JSContext *cx, StackFrame *fp, void *jitcode)
+EnterIon(JSContext *cx, EnterJitData &data)
 {
     JS_CHECK_RECURSION(cx, return IonExec_Aborted);
     JS_ASSERT(ion::IsEnabled(cx));
-    JS_ASSERT(CheckFrame(fp));
-    JS_ASSERT(!fp->script()->ionScript()->bailoutExpected());
+    JS_ASSERT(!data.osrFrame);
 
     EnterIonCode enter = cx->compartment()->ionCompartment()->enterJIT();
 
-    // maxArgc is the maximum of arguments between the number of actual
-    // arguments and the number of formal arguments. It accounts for |this|.
-    int maxArgc = 0;
-    Value *maxArgv = NULL;
-    unsigned numActualArgs = 0;
-    RootedValue thisv(cx);
-
-    void *calleeToken;
-    if (fp->isFunctionFrame()) {
-        numActualArgs = fp->numActualArgs();
-        maxArgc = Max(numActualArgs, fp->numFormalArgs()) + 1; // +1 = include |this|
-        maxArgv = fp->argv() - 1; // -1 = include |this|
-        calleeToken = CalleeToToken(&fp->callee());
-    } else {
-        calleeToken = CalleeToToken(fp->script());
-        thisv = fp->thisValue();
-        maxArgc = 1;
-        maxArgv = thisv.address();
-    }
-
     // Caller must construct |this| before invoking the Ion function.
-    JS_ASSERT_IF(fp->isConstructing(), fp->functionThis().isObject());
-    RootedValue result(cx, Int32Value(numActualArgs));
+    JS_ASSERT_IF(data.constructing, data.maxArgv[0].isObject());
+
+    data.result.setInt32(data.numActualArgs);
     {
         AssertCompartmentUnchanged pcc(cx);
         IonContext ictx(cx, NULL);
-        JitActivation activation(cx, fp->isConstructing());
+        JitActivation activation(cx, data.constructing);
         JSAutoResolveFlags rf(cx, RESOLVE_INFER);
         AutoFlushInhibitor afi(cx->compartment()->ionCompartment());
 
-        fp->setRunningInJit();
-
-        // Single transition point from Interpreter to Ion.
-        enter(jitcode, maxArgc, maxArgv, fp, calleeToken, /* scopeChain = */ NULL, 0,
-              result.address());
-
-        fp->clearRunningInJit();
+        // Single transition point from Interpreter to Baseline.
+        enter(data.jitcode, data.maxArgc, data.maxArgv, /* osrFrame = */NULL, data.calleeToken,
+              /* scopeChain = */ NULL, 0, data.result.address());
     }
 
     JS_ASSERT(!cx->runtime()->hasIonReturnOverride());
 
-    // The trampoline wrote the return value but did not set the HAS_RVAL flag.
-    fp->setReturnValue(result);
-
-    // Ion callers wrap primitive constructor return.
-    if (!result.isMagic() && fp->isConstructing() && fp->returnValue().isPrimitive())
-        fp->setReturnValue(ObjectValue(fp->constructorThis()));
-
-    JS_ASSERT_IF(result.isMagic(), result.isMagic(JS_ION_ERROR));
-    return result.isMagic() ? IonExec_Error : IonExec_Ok;
+    // Jit callers wrap primitive constructor return.
+    if (!data.result.isMagic() && data.constructing && data.result.isPrimitive())
+        data.result = data.maxArgv[0];
+
+    // Release temporary buffer used for OSR into Ion.
+    cx->runtime()->getIonRuntime(cx)->freeOsrTempData();
+
+    JS_ASSERT_IF(data.result.isMagic(), data.result.isMagic(JS_ION_ERROR));
+    return data.result.isMagic() ? IonExec_Error : IonExec_Ok;
+}
+
+bool
+ion::SetEnterJitData(JSContext *cx, EnterJitData &data, RunState &state, AutoValueVector &vals)
+{
+    data.osrFrame = NULL;
+
+    if (state.isInvoke()) {
+        CallArgs &args = state.asInvoke()->args();
+        unsigned numFormals = state.script()->function()->nargs;
+        data.constructing = state.asInvoke()->constructing();
+        data.numActualArgs = args.length();
+        data.maxArgc = Max(args.length(), numFormals) + 1;
+        data.scopeChain = NULL;
+        data.calleeToken = CalleeToToken(args.callee().toFunction());
+
+        if (data.numActualArgs >= numFormals) {
+            data.maxArgv = args.base() + 1;
+        } else {
+            // Pad missing arguments with |undefined|.
+            for (size_t i = 1; i < args.length() + 2; i++) {
+                if (!vals.append(args.base()[i]))
+                    return false;
+            }
+
+            while (vals.length() < numFormals + 1) {
+                if (!vals.append(UndefinedValue()))
+                    return false;
+            }
+
+            JS_ASSERT(vals.length() >= numFormals + 1);
+            data.maxArgv = vals.begin();
+        }
+    } else {
+        data.constructing = false;
+        data.numActualArgs = 0;
+        data.maxArgc = 1;
+        data.maxArgv = state.asExecute()->addressOfThisv();
+        data.scopeChain = state.asExecute()->scopeChain();
+
+        data.calleeToken = CalleeToToken(state.script());
+
+        if (state.script()->isForEval() &&
+            !(state.asExecute()->type() & StackFrame::GLOBAL))
+        {
+            ScriptFrameIter iter(cx);
+            if (iter.isFunctionFrame())
+                data.calleeToken = CalleeToToken(iter.callee());
+        }
+    }
+
+    return true;
 }
 
 IonExecStatus
-ion::Cannon(JSContext *cx, StackFrame *fp)
+ion::Cannon(JSContext *cx, RunState &state)
 {
-    RootedScript script(cx, fp->script());
-    IonScript *ion = script->ionScript();
-    IonCode *code = ion->method();
-    void *jitcode = code->raw();
+    IonScript *ion = state.script()->ionScript();
+
+    EnterJitData data(cx);
+    data.jitcode = ion->method()->raw();
+
+    AutoValueVector vals(cx);
+    if (!SetEnterJitData(cx, data, state, vals))
+        return IonExec_Error;
 
 #if JS_TRACE_LOGGING
     TraceLog(TraceLogging::defaultLogger(),
              TraceLogging::ION_CANNON_START,
              script);
 #endif
 
-    IonExecStatus status = EnterIon(cx, fp, jitcode);
+    IonExecStatus status = EnterIon(cx, data);
 
 #if JS_TRACE_LOGGING
-    if (status == IonExec_Bailout) {
-        TraceLog(TraceLogging::defaultLogger(),
-                 TraceLogging::ION_CANNON_BAIL,
-                 script);
-    } else {
-        TraceLog(TraceLogging::defaultLogger(),
-                 TraceLogging::ION_CANNON_STOP,
-                 script);
-    }
+    TraceLog(TraceLogging::defaultLogger(),
+             TraceLogging::ION_CANNON_STOP,
+             script);
 #endif
 
+    if (status == IonExec_Ok)
+        state.setReturnValue(data.result);
+
     return status;
 }
 
 IonExecStatus
 ion::FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args)
 {
     JS_CHECK_RECURSION(cx, return IonExec_Error);
 
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -269,19 +269,19 @@ bool InitializeIon();
 IonContext *GetIonContext();
 IonContext *MaybeGetIonContext();
 
 bool SetIonContext(IonContext *ctx);
 
 bool CanIonCompileScript(JSContext *cx, HandleScript script, bool osr);
 
 MethodStatus CanEnterAtBranch(JSContext *cx, JSScript *script,
-                              AbstractFramePtr fp, jsbytecode *pc, bool isConstructing);
-MethodStatus CanEnter(JSContext *cx, HandleScript script, AbstractFramePtr fp, bool isConstructing);
-MethodStatus CompileFunctionForBaseline(JSContext *cx, HandleScript script, AbstractFramePtr fp,
+                              BaselineFrame *frame, jsbytecode *pc, bool isConstructing);
+MethodStatus CanEnter(JSContext *cx, RunState &state);
+MethodStatus CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame,
                                         bool isConstructing);
 MethodStatus CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs);
 
 MethodStatus CanEnterInParallel(JSContext *cx, HandleScript script);
 
 enum IonExecStatus
 {
     // The method call had to be aborted due to a stack limit check. This
@@ -297,17 +297,21 @@ enum IonExecStatus
 };
 
 static inline bool
 IsErrorStatus(IonExecStatus status)
 {
     return status == IonExec_Error || status == IonExec_Aborted;
 }
 
-IonExecStatus Cannon(JSContext *cx, StackFrame *fp);
+struct EnterJitData;
+
+bool SetEnterJitData(JSContext *cx, EnterJitData &data, RunState &state, AutoValueVector &vals);
+
+IonExecStatus Cannon(JSContext *cx, RunState &state);
 
 // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard.
 IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args);
 
 // Walk the stack and invalidate active Ion frames for the invalid scripts.
 void Invalidate(types::TypeCompartment &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
 void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -29,23 +29,23 @@
 #endif
 
 using namespace js;
 using namespace js::ion;
 
 using mozilla::DebugOnly;
 
 IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
-                       BaselineInspector *inspector, CompileInfo *info, AbstractFramePtr fp,
+                       BaselineInspector *inspector, CompileInfo *info, BaselineFrame *baselineFrame,
                        size_t inliningDepth, uint32_t loopDepth)
   : MIRGenerator(cx->compartment(), temp, graph, info),
     backgroundCodegen_(NULL),
     recompileInfo(cx->compartment()->types.compiledInfo),
     cx(cx),
-    fp(fp),
+    baselineFrame_(baselineFrame),
     abortReason_(AbortReason_Disable),
     loopDepth_(loopDepth),
     callerResumePoint_(NULL),
     callerBuilder_(NULL),
     inspector(inspector),
     inliningDepth_(inliningDepth),
     numLoopRestarts_(0),
     failedBoundsCheck_(info->script()->failedBoundsCheck),
@@ -57,17 +57,17 @@ IonBuilder::IonBuilder(JSContext *cx, Te
     script_.init(info->script());
     pc = info->startPC();
 }
 
 void
 IonBuilder::clearForBackEnd()
 {
     cx = NULL;
-    fp = AbstractFramePtr();
+    baselineFrame_ = NULL;
 }
 
 bool
 IonBuilder::abort(const char *message, ...)
 {
     // Don't call PCToLineNumber in release builds.
 #ifdef DEBUG
     va_list ap;
@@ -3455,17 +3455,17 @@ IonBuilder::inlineScriptedCall(CallInfo 
                                                  this->info().executionMode());
     if (!info)
         return false;
 
     MIRGraphExits saveExits;
     AutoAccumulateExits aae(graph(), saveExits);
 
     // Build the graph.
-    IonBuilder inlineBuilder(cx, &temp(), &graph(), &inspector, info, AbstractFramePtr(),
+    IonBuilder inlineBuilder(cx, &temp(), &graph(), &inspector, info, NULL,
                              inliningDepth_ + 1, loopDepth_);
     if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) {
         JS_ASSERT(calleeScript->hasAnalysis());
 
         // Inlining the callee failed. Disable inlining the function
         if (inlineBuilder.abortReason_ == AbortReason_Disable)
             calleeScript->analysis()->setIonUninlineable();
 
@@ -5597,29 +5597,29 @@ IonBuilder::newPendingLoopHeader(MBasicB
         // Unbox the MOsrValue if it is known to be unboxable.
         for (uint32_t i = info().startArgSlot(); i < block->stackDepth(); i++) {
             MPhi *phi = block->getSlot(i)->toPhi();
 
             bool haveValue = false;
             Value existingValue;
             if (info().fun() && i == info().thisSlot()) {
                 haveValue = true;
-                existingValue = fp.thisValue();
+                existingValue = baselineFrame_->thisValue();
             } else {
                 uint32_t arg = i - info().firstArgSlot();
                 uint32_t var = i - info().firstLocalSlot();
                 if (arg < info().nargs()) {
                     if (!script()->formalIsAliased(arg)) {
                         haveValue = true;
-                        existingValue = fp.unaliasedFormal(arg);
+                        existingValue = baselineFrame_->unaliasedFormal(arg);
                     }
                 } else if (var < info().nlocals()) {
                     if (!script()->varIsAliased(var)) {
                         haveValue = true;
-                        existingValue = fp.unaliasedVar(var);
+                        existingValue = baselineFrame_->unaliasedVar(var);
                     }
                 }
             }
             if (haveValue) {
                 MIRType type = existingValue.isDouble()
                              ? MIRType_Double
                              : MIRTypeFromValueType(existingValue.extractNonDoubleType());
                 types::Type ntype = types::GetValueType(cx, existingValue);
@@ -8143,17 +8143,17 @@ IonBuilder::jsop_this()
 
     if (script()->strict) {
         current->pushSlot(info().thisSlot());
         return true;
     }
 
     types::StackTypeSet *types = types::TypeScript::ThisTypes(script());
     if (types && (types->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
-                  (types->empty() && fp && fp.thisValue().isObject())))
+                  (types->empty() && baselineFrame_ && baselineFrame_->thisValue().isObject())))
     {
         // This is safe, because if the entry type of |this| is an object, it
         // will necessarily be an object throughout the entire function. OSR
         // can introduce a phi, but this phi will be specialized.
         current->pushSlot(info().thisSlot());
         return true;
     }
 
@@ -8304,18 +8304,18 @@ IonBuilder::hasStaticScopeObject(ScopeCo
         environment = environment->enclosingScope();
     }
 
     // Look for the call object on the current frame, if we are compiling the
     // outer script itself. Don't do this if we are at entry to the outer
     // script, as the call object we see will not be the real one --- after
     // entering the Ion code a different call object will be created.
 
-    if (script() == outerScript && fp && info().osrPc()) {
-        JSObject *scope = fp.scopeChain();
+    if (script() == outerScript && baselineFrame_ && info().osrPc()) {
+        JSObject *scope = baselineFrame_->scopeChain();
         if (scope->is<CallObject>() &&
             scope->as<CallObject>().callee().nonLazyScript() == outerScript)
         {
             JS_ASSERT(scope->hasSingletonType());
             pcall.set(scope);
             return true;
         }
     }
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -193,17 +193,17 @@ class IonBuilder : public MIRGenerator
         static CFGState CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget);
         static CFGState Label(jsbytecode *exitpc);
     };
 
     static int CmpSuccessors(const void *a, const void *b);
 
   public:
     IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
-               BaselineInspector *inspector, CompileInfo *info, AbstractFramePtr fp,
+               BaselineInspector *inspector, CompileInfo *info, BaselineFrame *baselineFrame,
                size_t inliningDepth = 0, uint32_t loopDepth = 0);
 
     bool build();
     bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
                      CallInfo &callInfo);
 
   private:
     bool traverseBytecode();
@@ -581,17 +581,17 @@ class IonBuilder : public MIRGenerator
 
     CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
 
     AbortReason abortReason() { return abortReason_; }
 
   private:
     JSContext *cx;
-    AbstractFramePtr fp;
+    BaselineFrame *baselineFrame_;
     AbortReason abortReason_;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32_t loopDepth_;
 
     /* Information used for inline-call builders. */
     MResumePoint *callerResumePoint_;
--- a/js/src/ion/IonCompartment.h
+++ b/js/src/ion/IonCompartment.h
@@ -21,17 +21,40 @@ namespace ion {
 
 class FrameSizeClass;
 
 enum EnterJitType {
     EnterJitBaseline = 0,
     EnterJitOptimized = 1
 };
 
-typedef void (*EnterIonCode)(void *code, int argc, Value *argv, StackFrame *fp,
+struct EnterJitData
+{
+    explicit EnterJitData(JSContext *cx)
+      : scopeChain(cx),
+        result(cx)
+    {}
+
+    uint8_t *jitcode;
+    StackFrame *osrFrame;
+
+    void *calleeToken;
+
+    Value *maxArgv;
+    unsigned maxArgc;
+    unsigned numActualArgs;
+    unsigned osrNumStackValues;
+
+    RootedObject scopeChain;
+    RootedValue result;
+
+    bool constructing;
+};
+
+typedef void (*EnterIonCode)(void *code, unsigned argc, Value *argv, StackFrame *fp,
                              CalleeToken calleeToken, JSObject *scopeChain,
                              size_t numStackValues, Value *vp);
 
 class IonBuilder;
 
 typedef Vector<IonBuilder*, 0, SystemAllocPolicy> OffThreadCompilationVector;
 
 // ICStubSpace is an abstraction for allocation policy and storage for stub data.
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4142,16 +4142,19 @@ AutoGCSlice::AutoGCSlice(JSRuntime *rt)
     /*
      * During incremental GC, the compartment's active flag determines whether
      * there are stack frames active for any of its scripts. Normally this flag
      * is set at the beginning of the mark phase. During incremental GC, we also
      * set it at the start of every phase.
      */
     rt->stackSpace.markActiveCompartments();
 
+    for (ActivationIterator iter(rt); !iter.done(); ++iter)
+        iter.activation()->compartment()->zone()->active = true;
+
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         /*
          * Clear needsBarrier early so we don't do any write barriers during
          * GC. We don't need to update the Ion barriers (which is expensive)
          * because Ion code doesn't run during GC. If need be, we'll update the
          * Ion barriers in ~AutoGCSlice.
          */
         if (zone->isGCMarking()) {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1368,16 +1368,65 @@ static void
 SetGeneratorClosed(JSContext *cx, JSGenerator *gen)
 {
     JS_ASSERT(gen->state != JSGEN_CLOSED);
     if (GeneratorHasMarkableFrame(gen))
         GeneratorWriteBarrierPre(cx, gen);
     gen->state = JSGEN_CLOSED;
 }
 
+GeneratorState::GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState)
+  : RunState(cx, Generator, gen->fp->script()),
+    cx_(cx),
+    gen_(gen),
+    futureState_(futureState),
+    entered_(false)
+{ }
+
+GeneratorState::~GeneratorState()
+{
+    if (entered_)
+        cx_->leaveGenerator(gen_);
+}
+
+StackFrame *
+GeneratorState::pushInterpreterFrame(JSContext *cx)
+{
+    gfg_.construct();
+
+    /*
+     * Write barrier is needed since the generator stack can be updated,
+     * and it's not barriered in any other way. We need to do it before
+     * gen->state changes, which can cause us to trace the generator
+     * differently.
+     *
+     * We could optimize this by setting a bit on the generator to signify
+     * that it has been marked. If this bit has already been set, there is no
+     * need to mark again. The bit would have to be reset before the next GC,
+     * or else some kind of epoch scheme would have to be used.
+     */
+    GeneratorWriteBarrierPre(cx, gen_);
+
+    if (!cx->stack.pushGeneratorFrame(cx, gen_, gfg_.addr())) {
+        SetGeneratorClosed(cx, gen_);
+        return NULL;
+    }
+
+    /*
+     * Don't change the state until after the frame is successfully pushed
+     * or else we might fail to scan some generator values.
+     */
+    gen_->state = futureState_;
+    gen_->regs = cx->stack.regs();
+
+    cx->enterGenerator(gen_);   /* OOM check above. */
+    entered_ = true;
+    return gfg_.ref().fp();
+}
+
 static void
 generator_trace(JSTracer *trc, JSObject *obj)
 {
     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
     if (!gen)
         return;
 
     if (GeneratorHasMarkableFrame(gen))
@@ -1488,29 +1537,16 @@ static JSBool
 SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj,
                 JSGenerator *gen, const Value &arg)
 {
     if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NESTING_GENERATOR);
         return false;
     }
 
-    /*
-     * Write barrier is needed since the generator stack can be updated,
-     * and it's not barriered in any other way. We need to do it before
-     * gen->state changes, which can cause us to trace the generator
-     * differently.
-     *
-     * We could optimize this by setting a bit on the generator to signify
-     * that it has been marked. If this bit has already been set, there is no
-     * need to mark again. The bit would have to be reset before the next GC,
-     * or else some kind of epoch scheme would have to be used.
-     */
-    GeneratorWriteBarrierPre(cx, gen);
-
     JSGeneratorState futureState;
     JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
     switch (op) {
       case JSGENOP_NEXT:
       case JSGENOP_SEND:
         if (gen->state == JSGEN_OPEN) {
             /*
              * Store the argument to send as the result of the yield
@@ -1530,34 +1566,20 @@ SendToGenerator(JSContext *cx, JSGenerat
         JS_ASSERT(op == JSGENOP_CLOSE);
         cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
         futureState = JSGEN_CLOSING;
         break;
     }
 
     JSBool ok;
     {
-        GeneratorFrameGuard gfg;
-        if (!cx->stack.pushGeneratorFrame(cx, gen, &gfg)) {
-            SetGeneratorClosed(cx, gen);
+        GeneratorState state(cx, gen, futureState);
+        ok = RunScript(cx, state);
+        if (!ok && gen->state == JSGEN_CLOSED)
             return false;
-        }
-
-        /*
-         * Don't change the state until after the frame is successfully pushed
-         * or else we might fail to scan some generator values.
-         */
-        gen->state = futureState;
-
-        StackFrame *fp = gfg.fp();
-        gen->regs = cx->stack.regs();
-
-        cx->enterGenerator(gen);   /* OOM check above. */
-        ok = RunScript(cx, fp);
-        cx->leaveGenerator(gen);
     }
 
     if (gen->fp->isYielding()) {
         /*
          * Yield is ordinarily infallible, but ok can be false here if a
          * Debugger.Frame.onPop hook fails.
          */
         JS_ASSERT(gen->state == JSGEN_RUNNING);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -342,81 +342,78 @@ js::ValueToCallable(JSContext *cx, const
             return callable;
     }
 
     ReportIsNotFunction(cx, v, numToSkip, construct);
     return NULL;
 }
 
 static JS_NEVER_INLINE bool
-Interpret(JSContext *cx, StackFrame *entryFrame);
+Interpret(JSContext *cx, RunState &state);
+
+StackFrame *
+InvokeState::pushInterpreterFrame(JSContext *cx)
+{
+    ifg_.construct();
+
+    if (!cx->stack.pushInvokeFrame(cx, args_, initial_, ifg_.addr()))
+        return NULL;
+
+    return ifg_.ref().fp();
+}
+
+StackFrame *
+ExecuteState::pushInterpreterFrame(JSContext *cx)
+{
+    efg_.construct();
+
+    if (!cx->stack.pushExecuteFrame(cx, script_, thisv_, scopeChain_, type_, evalInFrame_,
+                                    efg_.addr()))
+    {
+        return NULL;
+    }
+
+    return efg_.ref().fp();
+}
 
 bool
-js::RunScript(JSContext *cx, StackFrame *fp)
+js::RunScript(JSContext *cx, RunState &state)
 {
-    JS_ASSERT(fp == cx->stack.fp());
-    RootedScript script(cx, fp->script());
-
-    JS_ASSERT_IF(!fp->isGeneratorFrame(), cx->stack.regs().pc == script->code);
-    JS_ASSERT_IF(fp->isEvalFrame(), script->isActiveEval);
-
     JS_CHECK_RECURSION(cx, return false);
 
-    // Check to see if useNewType flag should be set for this frame.
-    if (fp->isFunctionFrame() && fp->isConstructing() && !fp->isGeneratorFrame() &&
-        cx->typeInferenceEnabled())
-    {
-        ScriptFrameIter iter(cx);
-        if (!iter.done()) {
-            JSScript *script = iter.script();
-            jsbytecode *pc = iter.pc();
-            if (UseNewType(cx, script, pc))
-                fp->setUseNewType();
-        }
-    }
-
-#ifdef DEBUG
-    struct CheckStackBalance {
-        JSContext *cx;
-        StackFrame *fp;
-        CheckStackBalance(JSContext *cx)
-          : cx(cx), fp(cx->stack.fp())
-        {}
-        ~CheckStackBalance() {
-            JS_ASSERT(fp == cx->stack.fp());
-        }
-    } check(cx);
-#endif
-
     SPSEntryMarker marker(cx->runtime());
 
 #ifdef JS_ION
     if (ion::IsEnabled(cx)) {
-        ion::MethodStatus status = ion::CanEnter(cx, script, AbstractFramePtr(fp),
-                                                 fp->isConstructing());
+        ion::MethodStatus status = ion::CanEnter(cx, state);
         if (status == ion::Method_Error)
             return false;
         if (status == ion::Method_Compiled) {
-            ion::IonExecStatus status = ion::Cannon(cx, fp);
+            ion::IonExecStatus status = ion::Cannon(cx, state);
             return !IsErrorStatus(status);
         }
     }
 
     if (ion::IsBaselineEnabled(cx)) {
-        ion::MethodStatus status = ion::CanEnterBaselineJIT(cx, script, fp, false);
+        ion::MethodStatus status = ion::CanEnterBaselineMethod(cx, state);
         if (status == ion::Method_Error)
             return false;
         if (status == ion::Method_Compiled) {
-            ion::IonExecStatus status = ion::EnterBaselineMethod(cx, fp);
+            ion::IonExecStatus status = ion::EnterBaselineMethod(cx, state);
             return !IsErrorStatus(status);
         }
     }
 #endif
 
-    return Interpret(cx, fp);
+    if (state.isInvoke()) {
+        InvokeState &invoke = *state.asInvoke();
+        TypeMonitorCall(cx, invoke.args(), invoke.constructing());
+    }
+
+    return Interpret(cx, state);
 }
 
 /*
  * Find a function reference and its 'this' value implicit first parameter
  * under argc arguments on cx's stack, and call the function.  Push missing
  * required arguments, allocate declared local variables, and pop everything
  * when done.  Then push the return value.
  */
@@ -454,28 +451,32 @@ js::Invoke(JSContext *cx, CallArgs args,
     JSFunction *fun = callee.toFunction();
     JS_ASSERT_IF(construct, !fun->isNativeConstructor());
     if (fun->isNative())
         return CallJSNative(cx, fun->native(), args);
 
     if (!fun->getOrCreateScript(cx))
         return false;
 
-    TypeMonitorCall(cx, args, construct);
-
-    /* Get pointer to new frame/slots, prepare arguments. */
-    InvokeFrameGuard ifg;
-    if (!cx->stack.pushInvokeFrame(cx, args, initial, &ifg))
-        return false;
-
     /* Run function until JSOP_STOP, JSOP_RETURN or error. */
-    JSBool ok = RunScript(cx, ifg.fp());
-
-    /* Propagate the return value out. */
-    args.rval().set(ifg.fp()->returnValue());
+    InvokeState state(cx, args, initial);
+
+    // Check to see if useNewType flag should be set for this frame.
+    if (construct && cx->typeInferenceEnabled()) {
+        ScriptFrameIter iter(cx);
+        if (!iter.done()) {
+            JSScript *script = iter.script();
+            jsbytecode *pc = iter.pc();
+            if (UseNewType(cx, script, pc))
+                state.setUseNewType();
+        }
+    }
+
+    JSBool ok = RunScript(cx, state);
+
     JS_ASSERT_IF(ok && construct, !args.rval().isPrimitive());
     return ok;
 }
 
 bool
 js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv,
            Value *rval)
 {
@@ -573,40 +574,32 @@ js::InvokeGetterOrSetter(JSContext *cx, 
 
     return Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
 }
 
 bool
 js::ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChainArg, const Value &thisv,
                   ExecuteType type, AbstractFramePtr evalInFrame, Value *result)
 {
-    RootedObject scopeChain(cx, &scopeChainArg);
-
     JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG);
-    JS_ASSERT_IF(type == EXECUTE_GLOBAL, !scopeChain->isScope());
+    JS_ASSERT_IF(type == EXECUTE_GLOBAL, !scopeChainArg.isScope());
 
     if (script->isEmpty()) {
         if (result)
             result->setUndefined();
         return true;
     }
 
-    ExecuteFrameGuard efg;
-    if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
-        return false;
-
-    TypeScript::SetThis(cx, script, efg.fp()->thisValue());
+    TypeScript::SetThis(cx, script, thisv);
 
     Probes::startExecution(script);
-    bool ok = RunScript(cx, efg.fp());
+    ExecuteState state(cx, script, thisv, scopeChainArg, type, evalInFrame, result);
+    bool ok = RunScript(cx, state);
     Probes::stopExecution(script);
 
-    /* Propgate the return value out. */
-    if (result)
-        *result = efg.fp()->returnValue();
     return ok;
 }
 
 bool
 js::Execute(JSContext *cx, HandleScript script, JSObject &scopeChainArg, Value *rval)
 {
     /* The scope chain could be anything, so innerize just in case. */
     RootedObject scopeChain(cx, &scopeChainArg);
@@ -1021,17 +1014,17 @@ js::IteratorNext(JSContext *cx, HandleOb
             ni->incCursor();
             return true;
         }
     }
     return js_IteratorNext(cx, iterobj, rval);
 }
 
 static JS_NEVER_INLINE bool
-Interpret(JSContext *cx, StackFrame *entryFrame)
+Interpret(JSContext *cx, RunState &state)
 {
     JSAutoResolveFlags rf(cx, RESOLVE_INFER);
 
     gc::MaybeVerifyBarriers(cx, true);
 
     JS_ASSERT(!cx->compartment()->activeAnalysis);
 
 #define CHECK_PCCOUNT_INTERRUPTS() JS_ASSERT_IF(script->hasScriptCounts, switchMask == -1)
@@ -1096,20 +1089,29 @@ Interpret(JSContext *cx, StackFrame *ent
 
 #define SET_SCRIPT(s)                                                         \
     JS_BEGIN_MACRO                                                            \
         script = (s);                                                         \
         if (script->hasAnyBreakpointsOrStepMode() || script->hasScriptCounts) \
             interrupts.enable();                                              \
     JS_END_MACRO
 
+    StackFrame *entryFrame = state.pushInterpreterFrame(cx);
+    if (!entryFrame)
+        return false;
+
+    JS_ASSERT_IF(!state.isGenerator(), cx->stack.regs().pc == state.script()->code);
+    JS_ASSERT_IF(entryFrame->isEvalFrame(), state.script()->isActiveEval);
+
     /* Repoint cx->regs to a local variable for faster access. */
     FrameRegs regs = cx->stack.regs();
     PreserveRegsGuard interpGuard(cx, regs);
 
+    InterpreterActivation activation(cx, entryFrame, regs);
+
     /*
      * Help Debugger find frames running scripts that it has put in
      * single-step mode.
      */
     InterpreterFrames interpreterFrame(cx, &regs, interrupts);
 
     /* Copy in hot values that change infrequently. */
     JSRuntime *const rt = cx->runtime();
@@ -1136,21 +1138,16 @@ Interpret(JSContext *cx, StackFrame *ent
     RootedFunction rootFunction0(cx);
     RootedTypeObject rootType0(cx);
     RootedPropertyName rootName0(cx);
     RootedId rootId0(cx);
     RootedShape rootShape0(cx);
     RootedScript rootScript0(cx);
     DebugOnly<uint32_t> blockDepth;
 
-    if (!entryFrame)
-        entryFrame = regs.fp();
-
-    InterpreterActivation activation(cx, entryFrame, regs);
-
 #if JS_HAS_GENERATORS
     if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) {
         JS_ASSERT(size_t(regs.pc - script->code) <= script->length);
         JS_ASSERT(regs.stackDepth() <= script->nslots);
 
         /*
          * To support generator_throw and to catch ignored exceptions,
          * fail if cx->isExceptionPending() is true.
@@ -1345,32 +1342,30 @@ check_backedge:
     DO_OP();
 }
 
 BEGIN_CASE(JSOP_LOOPENTRY)
 
 #ifdef JS_ION
     // Attempt on-stack replacement with Baseline code.
     if (ion::IsBaselineEnabled(cx)) {
-        ion::MethodStatus status = ion::CanEnterBaselineJIT(cx, script, regs.fp(), false);
+        ion::MethodStatus status = ion::CanEnterBaselineAtBranch(cx, regs.fp(), false);
         if (status == ion::Method_Error)
             goto error;
         if (status == ion::Method_Compiled) {
             ion::IonExecStatus maybeOsr = ion::EnterBaselineAtBranch(cx, regs.fp(), regs.pc);
 
             // We failed to call into baseline at all, so treat as an error.
             if (maybeOsr == ion::IonExec_Aborted)
                 goto error;
 
             interpReturnOK = (maybeOsr == ion::IonExec_Ok);
 
             if (entryFrame != regs.fp())
-                goto jit_return;
-
-            regs.fp()->setFinishedInInterpreter();
+                goto jit_return_pop_frame;
             goto leave_on_safe_point;
         }
     }
 #endif /* JS_ION */
 
 END_CASE(JSOP_LOOPENTRY)
 
 BEGIN_CASE(JSOP_NOTEARG)
@@ -1449,25 +1444,28 @@ BEGIN_CASE(JSOP_STOP)
         if (cx->compartment()->debugMode())
             interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
 
         if (!regs.fp()->isYielding())
             regs.fp()->epilogue(cx);
         else
             Probes::exitScript(cx, script, script->function(), regs.fp());
 
-        /* The JIT inlines the epilogue. */
 #if defined(JS_ION)
-  jit_return:
+  jit_return_pop_frame:
 #endif
 
         activation.popFrame(regs.fp());
         cx->stack.popInlineFrame(regs);
         SET_SCRIPT(regs.fp()->script());
 
+#if defined(JS_ION)
+  jit_return:
+#endif
+
         JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_INVOKE);
 
         /* Resume execution in the calling frame. */
         if (JS_LIKELY(interpReturnOK)) {
             TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
 
             len = JSOP_CALL_LENGTH;
             DO_NEXT_OP(len);
@@ -2251,58 +2249,64 @@ BEGIN_CASE(JSOP_FUNCALL)
         }
         Value *newsp = args.spAfterCall();
         TypeScript::Monitor(cx, script, regs.pc, newsp[-1]);
         regs.sp = newsp;
         len = JSOP_CALL_LENGTH;
         DO_NEXT_OP(len);
     }
 
-    TypeMonitorCall(cx, args, construct);
-
     InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
     bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
+
+#ifdef JS_ION
+    InvokeState state(cx, args, initial);
+    if (newType)
+        state.setUseNewType();
+
+    if (!newType && ion::IsEnabled(cx)) {
+        ion::MethodStatus status = ion::CanEnter(cx, state);
+        if (status == ion::Method_Error)
+            goto error;
+        if (status == ion::Method_Compiled) {
+            ion::IonExecStatus exec = ion::Cannon(cx, state);
+            CHECK_BRANCH();
+            regs.sp = args.spAfterCall();
+            interpReturnOK = !IsErrorStatus(exec);
+            goto jit_return;
+        }
+    }
+
+    if (ion::IsBaselineEnabled(cx)) {
+        ion::MethodStatus status = ion::CanEnterBaselineMethod(cx, state);
+        if (status == ion::Method_Error)
+            goto error;
+        if (status == ion::Method_Compiled) {
+            ion::IonExecStatus exec = ion::EnterBaselineMethod(cx, state);
+            CHECK_BRANCH();
+            regs.sp = args.spAfterCall();
+            interpReturnOK = !IsErrorStatus(exec);
+            goto jit_return;
+        }
+    }
+#endif
+
+    TypeMonitorCall(cx, args, construct);
+
     funScript = fun->nonLazyScript();
     if (!cx->stack.pushInlineFrame(cx, regs, args, fun, funScript, initial))
         goto error;
 
     activation.pushFrame(regs.fp());
 
     if (newType)
         regs.fp()->setUseNewType();
 
     SET_SCRIPT(regs.fp()->script());
 
-#ifdef JS_ION
-    if (!newType && ion::IsEnabled(cx)) {
-        ion::MethodStatus status = ion::CanEnter(cx, script, AbstractFramePtr(regs.fp()),
-                                                 regs.fp()->isConstructing());
-        if (status == ion::Method_Error)
-            goto error;
-        if (status == ion::Method_Compiled) {
-            ion::IonExecStatus exec = ion::Cannon(cx, regs.fp());
-            CHECK_BRANCH();
-            interpReturnOK = !IsErrorStatus(exec);
-            goto jit_return;
-        }
-    }
-
-    if (ion::IsBaselineEnabled(cx)) {
-        ion::MethodStatus status = ion::CanEnterBaselineJIT(cx, script, regs.fp(), newType);
-        if (status == ion::Method_Error)
-            goto error;
-        if (status == ion::Method_Compiled) {
-            ion::IonExecStatus exec = ion::EnterBaselineMethod(cx, regs.fp());
-            CHECK_BRANCH();
-            interpReturnOK = !IsErrorStatus(exec);
-            goto jit_return;
-        }
-    }
-#endif
-
     if (!regs.fp()->prologue(cx))
         goto error;
     if (cx->compartment()->debugMode()) {
         switch (ScriptDebugPrologue(cx, regs.fp())) {
           case JSTRAP_CONTINUE:
             break;
           case JSTRAP_RETURN:
             interpReturnOK = true;
@@ -3166,28 +3170,30 @@ END_CASE(JSOP_ARRAYPUSH)
 
   exit:
     if (cx->compartment()->debugMode())
         interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
     if (!regs.fp()->isYielding())
         regs.fp()->epilogue(cx);
     else
         Probes::exitScript(cx, script, script->function(), regs.fp());
-    regs.fp()->setFinishedInInterpreter();
 
     gc::MaybeVerifyBarriers(cx, true);
 
 #ifdef JS_ION
     /*
      * This path is used when it's guaranteed the method can be finished
      * inside the JIT.
      */
   leave_on_safe_point:
 #endif
 
+    if (interpReturnOK)
+        state.setReturnValue(entryFrame->returnValue());
+
     return interpReturnOK;
 }
 
 bool
 js::Throw(JSContext *cx, HandleValue v)
 {
     JS_ASSERT(!cx->isExceptionPending());
     cx->setPendingException(v);
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef Interpreter_h___
 #define Interpreter_h___
 /*
  * JS interpreter interface.
  */
+#include "jsiter.h"
 #include "jsprvtd.h"
 #include "jspubtd.h"
 
 #include "vm/Stack.h"
 
 namespace js {
 
 /* Implemented in jsdbgapi: */
@@ -158,18 +159,150 @@ InvokeConstructor(JSContext *cx, const V
 extern bool
 ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChain, const Value &thisv,
               ExecuteType type, AbstractFramePtr evalInFrame, Value *result);
 
 /* Execute a script with the given scopeChain as global code. */
 extern bool
 Execute(JSContext *cx, HandleScript script, JSObject &scopeChain, Value *rval);
 
+class ExecuteState;
+class InvokeState;
+class GeneratorState;
+
+// RunState is passed to RunScript and RunScript then eiter passes it to the
+// interpreter or to the JITs. RunState contains all information we need to
+// construct an interpreter or JIT frame.
+class RunState
+{
+  protected:
+    enum Kind { Execute, Invoke, Generator };
+    Kind kind_;
+
+    RootedScript script_;
+
+    explicit RunState(JSContext *cx, Kind kind, JSScript *script)
+      : kind_(kind),
+        script_(cx, script)
+    { }
+
+  public:
+    bool isExecute() const { return kind_ == Execute; }
+    bool isInvoke() const { return kind_ == Invoke; }
+    bool isGenerator() const { return kind_ == Generator; }
+
+    ExecuteState *asExecute() const {
+        JS_ASSERT(isExecute());
+        return (ExecuteState *)this;
+    }
+    InvokeState *asInvoke() const {
+        JS_ASSERT(isInvoke());
+        return (InvokeState *)this;
+    }
+    GeneratorState *asGenerator() const {
+        JS_ASSERT(isGenerator());
+        return (GeneratorState *)this;
+    }
+
+    JSScript *script() const { return script_; }
+
+    virtual StackFrame *pushInterpreterFrame(JSContext *cx) = 0;
+    virtual void setReturnValue(Value v) = 0;
+
+  private:
+    RunState(const RunState &other) MOZ_DELETE;
+    RunState(const ExecuteState &other) MOZ_DELETE;
+    RunState(const InvokeState &other) MOZ_DELETE;
+    RunState(const GeneratorState &other) MOZ_DELETE;
+    void operator=(const RunState &other) MOZ_DELETE;
+};
+
+// Eval or global script.
+class ExecuteState : public RunState
+{
+    mozilla::Maybe<ExecuteFrameGuard> efg_;
+    ExecuteType type_;
+
+    RootedValue thisv_;
+    RootedObject scopeChain_;
+
+    AbstractFramePtr evalInFrame_;
+    Value *result_;
+
+  public:
+    ExecuteState(JSContext *cx, JSScript *script, const Value &thisv, JSObject &scopeChain,
+                 ExecuteType type, AbstractFramePtr evalInFrame, Value *result)
+      : RunState(cx, Execute, script),
+        type_(type),
+        thisv_(cx, thisv),
+        scopeChain_(cx, &scopeChain),
+        evalInFrame_(evalInFrame),
+        result_(result)
+    { }
+
+    Value *addressOfThisv() { return thisv_.address(); }
+    JSObject *scopeChain() const { return scopeChain_; }
+    ExecuteType type() const { return type_; }
+
+    virtual StackFrame *pushInterpreterFrame(JSContext *cx);
+
+    virtual void setReturnValue(Value v) {
+        if (result_)
+            *result_ = v;
+    }
+};
+
+// Data to invoke a function.
+class InvokeState : public RunState
+{
+    mozilla::Maybe<InvokeFrameGuard> ifg_;
+    CallArgs &args_;
+    InitialFrameFlags initial_;
+    bool useNewType_;
+
+  public:
+    InvokeState(JSContext *cx, CallArgs &args, InitialFrameFlags initial)
+      : RunState(cx, Invoke, args.callee().toFunction()->nonLazyScript()),
+        args_(args),
+        initial_(initial),
+        useNewType_(false)
+    { }
+
+    bool useNewType() const { return useNewType_; }
+    void setUseNewType() { useNewType_ = true; }
+
+    bool constructing() const { return InitialFrameFlagsAreConstructing(initial_); }
+    CallArgs &args() const { return args_; }
+
+    virtual StackFrame *pushInterpreterFrame(JSContext *cx);
+
+    virtual void setReturnValue(Value v) {
+        args_.rval().set(v);
+    }
+};
+
+// Generator script.
+class GeneratorState : public RunState
+{
+    mozilla::Maybe<GeneratorFrameGuard> gfg_;
+    JSContext *cx_;
+    JSGenerator *gen_;
+    JSGeneratorState futureState_;
+    bool entered_;
+
+  public:
+    GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState);
+    ~GeneratorState();
+
+    virtual StackFrame *pushInterpreterFrame(JSContext *cx);
+    virtual void setReturnValue(Value) { }
+};
+
 extern bool
-RunScript(JSContext *cx, StackFrame *fp);
+RunScript(JSContext *cx, RunState &state);
 
 extern bool
 StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal);
 
 extern bool
 LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal);
 
 /* === except that NaN is the same as NaN and -0 is not the same as +0. */
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -933,20 +933,19 @@ ContextStack::pushExecuteFrame(JSContext
     if (evalInFrame) {
         JS_ASSERT_IF(evalInFrame.isStackFrame(), !evalInFrame.asStackFrame()->runningInJit());
         prevLink = NULL;
         prev = evalInFrame;
         extend = CANT_EXTEND;
     } else {
         prevLink = maybefp();
         extend = CAN_EXTEND;
-        if (maybefp()) {
-            ScriptFrameIter iter(cx);
+        ScriptFrameIter iter(cx);
+        if (!iter.done())
             prev = iter.isIon() ? maybefp() : iter.abstractFramePtr();
-        }
     }
 
     unsigned nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots;
     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, extend, &efg->pushedSeg_);
     if (!firstUnused)
         return false;
 
     StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused + 2);
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -278,43 +278,42 @@ class StackFrame
         /* Frame subtypes */
         EVAL               =        0x4,  /* frame pushed for eval() or debugger eval */
         DEBUGGER           =        0x8,  /* frame pushed for debugger eval */
         GENERATOR          =       0x10,  /* frame is associated with a generator */
         CONSTRUCTING       =       0x20,  /* frame is for a constructor invocation */
 
         /* Temporary frame states */
         YIELDING           =       0x40,  /* Interpret dispatched JSOP_YIELD */
-        FINISHED_IN_INTERP =       0x80,  /* set if frame finished in Interpret() */
 
         /* Function prologue state */
-        HAS_CALL_OBJ       =      0x100,  /* CallObject created for heavyweight fun */
-        HAS_ARGS_OBJ       =      0x200,  /* ArgumentsObject created for needsArgsObj script */
+        HAS_CALL_OBJ       =       0x80,  /* CallObject created for heavyweight fun */
+        HAS_ARGS_OBJ       =      0x100,  /* ArgumentsObject created for needsArgsObj script */
 
         /* Lazy frame initialization */
-        HAS_HOOK_DATA      =      0x400,  /* frame has hookData_ set */
-        HAS_RVAL           =      0x800,  /* frame has rval_ set */
-        HAS_SCOPECHAIN     =     0x1000,  /* frame has scopeChain_ set */
-        HAS_PREVPC         =     0x2000,  /* frame has prevpc_ and prevInline_ set */
-        HAS_BLOCKCHAIN     =     0x4000,  /* frame has blockChain_ set */
+        HAS_HOOK_DATA      =      0x200,  /* frame has hookData_ set */
+        HAS_RVAL           =      0x400,  /* frame has rval_ set */
+        HAS_SCOPECHAIN     =      0x800,  /* frame has scopeChain_ set */
+        HAS_PREVPC         =     0x1000,  /* frame has prevpc_ and prevInline_ set */
+        HAS_BLOCKCHAIN     =     0x2000,  /* frame has blockChain_ set */
 
         /* Debugger state */
-        PREV_UP_TO_DATE    =     0x8000,  /* see DebugScopes::updateLiveScopes */
+        PREV_UP_TO_DATE    =     0x4000,  /* see DebugScopes::updateLiveScopes */
 
         /* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */
-        HAS_PUSHED_SPS_FRAME =  0x10000,  /* SPS was notified of enty */
+        HAS_PUSHED_SPS_FRAME =   0x8000,  /* SPS was notified of enty */
 
         /*
          * If set, we entered one of the JITs and ScriptFrameIter should skip
          * this frame.
          */
-        RUNNING_IN_JIT     =    0x20000,
+        RUNNING_IN_JIT     =    0x10000,
 
         /* Miscellaneous state. */
-        USE_NEW_TYPE       =    0x40000   /* Use new type for constructed |this| object. */
+        USE_NEW_TYPE       =    0x20000   /* Use new type for constructed |this| object. */
     };
 
   private:
     mutable uint32_t    flags_;         /* bits described by Flags */
     union {                             /* describes what code is executing in a */
         JSScript        *script;        /*   global frame */
         JSFunction      *fun;           /*   function frame, pre GetScopeChain */
     } exec;
@@ -930,24 +929,16 @@ class StackFrame
     void setYielding() {
         flags_ |= YIELDING;
     }
 
     void clearYielding() {
         flags_ &= ~YIELDING;
     }
 
-    void setFinishedInInterpreter() {
-        flags_ |= FINISHED_IN_INTERP;
-    }
-
-    bool finishedInInterpreter() const {
-        return !!(flags_ & FINISHED_IN_INTERP);
-    }
-
   public:
     static size_t offsetOfFlags() {
         return offsetof(StackFrame, flags_);
     }
 
     static size_t offsetOfExec() {
         return offsetof(StackFrame, exec);
     }