Bug 890722: IonMonkey: Implement JSOP_RETRVAL, JSOP_SETRVAL, JSOP_POPV, r=jandem
☠☠ backed out by 1718a2f065c6 ☠ ☠
authorHannes Verschore <hv1989@gmail.com>
Tue, 20 Aug 2013 17:34:45 +0200
changeset 156144 ef139b6034a551d4b1f3d01c0f3bef6945be40cb
parent 156143 9fb952a6e85eb86b405bcfeb4a315e388d282684
child 156145 9586120a2290139d0df6f6606e36d2c126f9a62a
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs890722
milestone26.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 890722: IonMonkey: Implement JSOP_RETRVAL, JSOP_SETRVAL, JSOP_POPV, r=jandem
js/src/jit-test/tests/ion/bug890722.js
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/CompileInfo.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonFrameIterator.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MIRGraph.cpp
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/CodeGenerator-x64.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/jit/x86/CodeGenerator-x86.h
js/src/vm/Stack.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug890722.js
@@ -0,0 +1,29 @@
+
+// Test setting return value;
+
+function bail() { bailout(); }
+function bail2() { bailout(); return 2; }
+
+// Test 1: Test setting/getting return value in ionmonkey
+function test() {
+    return evalcx("1;");
+}
+assertEq(test(), 1)
+
+// Test 3: Test ion -> baseline
+function test2() {
+    return evaluate("1; bail2();");
+}
+assertEq(test2(), 2)
+
+// Test 3: Test ion -> baseline
+function test3() {
+    return evaluate("1; bail2(); 3");
+}
+assertEq(test3(), 3)
+
+// Test4: Test baseline -> ion entering (very fragile, since iterations need to be precise, before it gets tested)
+function test4() {
+    return evaluate("1; for(var i=0; i<1097; i++) { 3; };");
+}
+assertEq(test4(), 3)
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -533,26 +533,31 @@ InitFromBailout(JSContext *cx, HandleScr
     // that will be fixed later.
     if (cx->runtime()->spsProfiler.enabled() && ionScript->hasSPSInstrumentation()) {
         IonSpew(IonSpew_BaselineBailouts, "      Setting SPS flag on frame!");
         flags |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
     }
 
     // Initialize BaselineFrame's scopeChain and argsObj
     JSObject *scopeChain = NULL;
+    Value returnValue;
     ArgumentsObject *argsObj = NULL;
     BailoutKind bailoutKind = iter.bailoutKind();
     if (bailoutKind == Bailout_ArgumentCheck) {
         // Temporary hack -- skip the (unused) scopeChain, because it could be
         // bogus (we can fail before the scope chain slot is set). Strip the
         // hasScopeChain flag and this will be fixed up later in |FinishBailoutToBaseline|,
         // which calls |EnsureHasScopeObjects|.
         IonSpew(IonSpew_BaselineBailouts, "      Bailout_ArgumentCheck! (no valid scopeChain)");
         iter.skip();
 
+        // skip |return value|
+        iter.skip();
+        returnValue = UndefinedValue();
+
         // Scripts with |argumentsHasVarBinding| have an extra slot.
         if (script->argumentsHasVarBinding()) {
             IonSpew(IonSpew_BaselineBailouts,
                     "      Bailout_ArgumentCheck for script with argumentsHasVarBinding!"
                     "Using empty arguments object");
             iter.skip();
         }
     } else {
@@ -578,26 +583,31 @@ InitFromBailout(JSContext *cx, HandleScr
                 // prologue in this case because the prologue expects the scope
                 // chain in R1 for eval and global scripts.
                 JS_ASSERT(!script->isForEval());
                 JS_ASSERT(script->compileAndGo);
                 scopeChain = &(script->global());
             }
         }
 
-        // If script maybe has an arguments object, the second slot will hold it.
+        // Second slot holds the return value.
+        returnValue = iter.read();
+
+        // If script maybe has an arguments object, the third slot will hold it.
         if (script->argumentsHasVarBinding()) {
             v = iter.read();
             JS_ASSERT(v.isObject() || v.isUndefined());
             if (v.isObject())
                 argsObj = &v.toObject().as<ArgumentsObject>();
         }
     }
     IonSpew(IonSpew_BaselineBailouts, "      ScopeChain=%p", scopeChain);
     blFrame->setScopeChain(scopeChain);
+    IonSpew(IonSpew_BaselineBailouts, "      ReturnValue=%p", returnValue);
+    blFrame->setReturnValue(returnValue);
 
     // Do not need to initialize scratchValue or returnValue fields in BaselineFrame.
 
     blFrame->setFlags(flags);
 
     // initArgsObjUnchecked modifies the frame's flags, so call it after setFlags.
     if (argsObj)
         blFrame->initArgsObjUnchecked(*argsObj);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -830,16 +830,22 @@ PrepareOsrTempData(JSContext *cx, ICUseC
         // Store the function in exec field, and StackFrame::FUNCTION for flags.
         *((JSFunction **) (stackFrame + StackFrame::offsetOfExec())) = frame->fun();
         *((uint32_t *) (stackFrame + StackFrame::offsetOfFlags())) = StackFrame::FUNCTION;
     } else {
         *((JSScript **) (stackFrame + StackFrame::offsetOfExec())) = frame->script();
         *((uint32_t *) (stackFrame + StackFrame::offsetOfFlags())) = 0;
     }
 
+    // Set return value.
+    if (frame->hasReturnValue()) {
+        *((uint32_t *) (stackFrame + StackFrame::offsetOfFlags())) |= StackFrame::HAS_RVAL;
+        *((Value *) (stackFrame + StackFrame::offsetOfReturnValue())) = *(frame->returnValue());
+    }
+
     // Do locals and stack values.  Note that in the fake StackFrame, these go from
     // low to high addresses, while on the C stack, they go from high to low addresses.
     // So we can't use memcpy on this, but must copy the values in reverse order.
     Value *stackFrameLocalsStart = (Value *) (stackFrame + sizeof(StackFrame));
     for (size_t i = 0; i < numLocalsAndStackVals; i++)
         stackFrameLocalsStart[i] = *(frame->valueSlot(i));
 
     IonSpew(IonSpew_BaselineOSR, "Allocated IonOsrTempData at %p", (void *) info);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1019,16 +1019,47 @@ CodeGenerator::visitOsrScopeChain(LOsrSc
 
     const ptrdiff_t frameOffset = StackFrame::offsetOfScopeChain();
 
     masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
     return true;
 }
 
 bool
+CodeGenerator::visitOsrValue(LOsrValue *value)
+{
+    const LAllocation *frame   = value->getOperand(0);
+    const ValueOperand out     = ToOutValue(value);
+
+    const ptrdiff_t frameOffset = value->mir()->frameOffset();
+
+    masm.loadValue(Address(ToRegister(frame), frameOffset), out);
+    return true;
+}
+
+bool
+CodeGenerator::visitOsrReturnValue(LOsrReturnValue *lir)
+{
+    const LAllocation *frame   = lir->getOperand(0);
+    const ValueOperand out     = ToOutValue(lir);
+
+    Address flags = Address(ToRegister(frame), StackFrame::offsetOfFlags());
+    Address retval = Address(ToRegister(frame), StackFrame::offsetOfReturnValue());
+
+    masm.moveValue(UndefinedValue(), out);
+
+    Label done;
+    masm.branchTest32(Assembler::Zero, flags, Imm32(StackFrame::HAS_RVAL), &done);
+    masm.loadValue(retval, out);
+    masm.bind(&done);
+
+    return true;
+}
+
+bool
 CodeGenerator::visitStackArgT(LStackArgT *lir)
 {
     const LAllocation *arg = lir->getArgument();
     MIRType argType = lir->mir()->getArgument()->type();
     uint32_t argslot = lir->argslot();
 
     int32_t stack_offset = StackOffsetOfPassedArg(argslot);
     Address dest(StackPointer, stack_offset);
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -61,16 +61,18 @@ class CodeGenerator : public CodeGenerat
     bool visitParameter(LParameter *lir);
     bool visitCallee(LCallee *lir);
     bool visitStart(LStart *lir);
     bool visitReturn(LReturn *ret);
     bool visitDefVar(LDefVar *lir);
     bool visitDefFun(LDefFun *lir);
     bool visitOsrEntry(LOsrEntry *lir);
     bool visitOsrScopeChain(LOsrScopeChain *lir);
+    bool visitOsrValue(LOsrValue *lir);
+    bool visitOsrReturnValue(LOsrReturnValue *lir);
     bool visitStackArgT(LStackArgT *lir);
     bool visitStackArgV(LStackArgV *lir);
     bool visitMoveGroup(LMoveGroup *group);
     bool visitValueToInt32(LValueToInt32 *lir);
     bool visitValueToDouble(LValueToDouble *lir);
     bool visitInt32ToDouble(LInt32ToDouble *lir);
     void emitOOLTestObject(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch);
     bool visitTestOAndBranch(LTestOAndBranch *lir);
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -12,24 +12,33 @@
 #include "jit/Registers.h"
 
 namespace js {
 namespace ion {
 
 inline unsigned
 StartArgSlot(JSScript *script, JSFunction *fun)
 {
-    // First slot is for scope chain.
-    // Second one may be for arguments object.
-    return 1 + (script->argumentsHasVarBinding() ? 1 : 0);
+    // Reserved slots:
+    // Slot 0: Scope chain.
+    // Slot 1: Return value.
+
+    // When needed:
+    // Slot 2: Argumentsobject.
+
+    return 2 + (script->argumentsHasVarBinding() ? 1 : 0);
 }
 
 inline unsigned
 CountArgSlots(JSScript *script, JSFunction *fun)
 {
+    // Slot x + 0: This value.
+    // Slot x + 1: Argument 1.
+    // ...
+    // Slot x + n: Argument n.
     return StartArgSlot(script, fun) + (fun ? fun->nargs + 1 : 0);
 }
 
 enum ExecutionMode {
     // Normal JavaScript execution
     SequentialExecution = 0,
 
     // JavaScript code to be executed in parallel worker threads,
@@ -140,37 +149,49 @@ class CompileInfo
         return js_GetSrcNote(cx, script(), pc);
     }
 
     // Total number of slots: args, locals, and stack.
     unsigned nslots() const {
         return nslots_;
     }
 
+    // Number of slots needed for Scope chain, return value,
+    // maybe argumentsobject and this value.
+    unsigned nimplicit() const {
+        return nimplicit_;
+    }
+    // Number of arguments (without counting this value).
     unsigned nargs() const {
         return nargs_;
     }
+    // Number of slots needed for local variables.
     unsigned nlocals() const {
         return nlocals_;
     }
     unsigned ninvoke() const {
         return nslots_ - nstack_;
     }
 
     uint32_t scopeChainSlot() const {
         JS_ASSERT(script());
         return 0;
     }
+    uint32_t returnValueSlot() const {
+        JS_ASSERT(script());
+        return 1;
+    }
     uint32_t argsObjSlot() const {
         JS_ASSERT(hasArguments());
-        return 1;
+        return 2;
     }
     uint32_t thisSlot() const {
         JS_ASSERT(fun());
-        return hasArguments() ? 2 : 1;
+        JS_ASSERT(nimplicit_ > 0);
+        return nimplicit_ - 1;
     }
     uint32_t firstArgSlot() const {
         return nimplicit_;
     }
     uint32_t argSlotUnchecked(uint32_t i) const {
         // During initialization, some routines need to get at arg
         // slots regardless of how regular argument access is done.
         JS_ASSERT(i < nargs_);
@@ -192,26 +213,27 @@ class CompileInfo
     uint32_t firstStackSlot() const {
         return firstLocalSlot() + nlocals();
     }
     uint32_t stackSlot(uint32_t i) const {
         return firstStackSlot() + i;
     }
 
     uint32_t startArgSlot() const {
-        JS_ASSERT(scopeChainSlot() == 0);
+        JS_ASSERT(script());
         return StartArgSlot(script(), fun());
     }
     uint32_t endArgSlot() const {
-        JS_ASSERT(scopeChainSlot() == 0);
+        JS_ASSERT(script());
         return CountArgSlots(script(), fun());
     }
 
     uint32_t totalSlots() const {
-        return 2 + (hasArguments() ? 1 : 0) + nargs() + nlocals();
+        JS_ASSERT(script() && fun());
+        return nimplicit() + nargs() + nlocals();
     }
 
     bool hasArguments() const {
         return script()->argumentsHasVarBinding();
     }
     bool needsArgsObj() const {
         return script()->needsArgsObj();
     }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -511,21 +511,24 @@ IonBuilder::build()
         current->add(undef);
         current->initSlot(info().localSlot(i), undef);
     }
 
     // Initialize something for the scope chain. We can bail out before the
     // start instruction, but the snapshot is encoded *at* the start
     // instruction, which means generating any code that could load into
     // registers is illegal.
-    {
-        MInstruction *scope = MConstant::New(UndefinedValue());
-        current->add(scope);
-        current->initSlot(info().scopeChainSlot(), scope);
-    }
+    MInstruction *scope = MConstant::New(UndefinedValue());
+    current->add(scope);
+    current->initSlot(info().scopeChainSlot(), scope);
+
+    // Initialize the return value.
+    MInstruction *returnValue = MConstant::New(UndefinedValue());
+    current->add(returnValue);
+    current->initSlot(info().returnValueSlot(), returnValue);
 
     // Initialize the arguments object slot to undefined if necessary.
     if (info().hasArguments()) {
         MInstruction *argsObj = MConstant::New(UndefinedValue());
         current->add(argsObj);
         current->initSlot(info().argsObjSlot(), argsObj);
     }
 
@@ -672,21 +675,24 @@ IonBuilder::buildInline(IonBuilder *call
                                                 MFunctionBoundary::Inline_Enter,
                                                 inliningDepth_));
 
     predecessor->end(MGoto::New(current));
     if (!current->addPredecessorWithoutPhis(predecessor))
         return false;
 
     // Initialize scope chain slot to Undefined.  It's set later by |initScopeChain|.
-    {
-        MInstruction *scope = MConstant::New(UndefinedValue());
-        current->add(scope);
-        current->initSlot(info().scopeChainSlot(), scope);
-    }
+    MInstruction *scope = MConstant::New(UndefinedValue());
+    current->add(scope);
+    current->initSlot(info().scopeChainSlot(), scope);
+
+    // Initialize |return value| slot.
+    MInstruction *returnValue = MConstant::New(UndefinedValue());
+    current->add(returnValue);
+    current->initSlot(info().returnValueSlot(), returnValue);
 
     // Initialize |arguments| slot.
     if (info().hasArguments()) {
         MInstruction *argsObj = MConstant::New(UndefinedValue());
         current->add(argsObj);
         current->initSlot(info().argsObjSlot(), argsObj);
     }
 
@@ -1119,16 +1125,17 @@ IonBuilder::snoopControlFlow(JSOp op)
       case JSOP_NOP:
         return maybeLoop(op, info().getNote(cx, pc));
 
       case JSOP_POP:
         return maybeLoop(op, info().getNote(cx, pc));
 
       case JSOP_RETURN:
       case JSOP_STOP:
+      case JSOP_RETRVAL:
         return processReturn(op);
 
       case JSOP_THROW:
         return processThrow();
 
       case JSOP_GOTO:
       {
         jssrcnote *sn = info().getNote(cx, pc);
@@ -1571,16 +1578,22 @@ IonBuilder::inspectOpcode(JSOp op)
         return jsop_iterend();
 
       case JSOP_IN:
         return jsop_in();
 
       case JSOP_INSTANCEOF:
         return jsop_instanceof();
 
+      case JSOP_SETRVAL:
+      case JSOP_POPV:
+        JS_ASSERT(!script()->noScriptRval);
+        current->setSlot(info().returnValueSlot(), current->pop());
+        return true;
+
       default:
 #ifdef DEBUG
         return abort("Unsupported opcode: %s (line %d)", js_CodeName[op], info().lineno(cx, pc));
 #else
         return abort("Unsupported opcode: %d (line %d)", op, info().lineno(cx, pc));
 #endif
     }
 }
@@ -3350,26 +3363,34 @@ IonBuilder::jsop_try()
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processReturn(JSOp op)
 {
     MDefinition *def;
     switch (op) {
       case JSOP_RETURN:
+        // Return the last instruction.
         def = current->pop();
         break;
 
       case JSOP_STOP:
-      {
-        MInstruction *ins = MConstant::New(UndefinedValue());
-        current->add(ins);
-        def = ins;
+        // Return undefined eagerly if script doesn't use return value.
+        if (script()->noScriptRval) {
+            MInstruction *ins = MConstant::New(UndefinedValue());
+            current->add(ins);
+            def = ins;
+            break;
+        }
+
+        // Fall through
+      case JSOP_RETRVAL:
+        // Return the value in the return value slot.
+        def = current->getSlot(info().returnValueSlot());
         break;
-      }
 
       default:
         def = NULL;
         MOZ_ASSUME_UNREACHABLE("unknown return op");
     }
 
     if (instrumentedProfiling())
         current->add(MFunctionBoundary::New(script(), MFunctionBoundary::Exit));
@@ -5741,16 +5762,27 @@ IonBuilder::newOsrPreheader(MBasicBlock 
             // slot.
             scopev = MConstant::New(UndefinedValue());
         }
 
         osrBlock->add(scopev);
         osrBlock->initSlot(slot, scopev);
     }
 
+    // Initialize |return value|
+    {
+        MInstruction *returnValue;
+        if (!script()->noScriptRval)
+            returnValue = MOsrReturnValue::New(entry);
+        else
+            returnValue = MConstant::New(UndefinedValue());
+        osrBlock->add(returnValue);
+        osrBlock->initSlot(info().returnValueSlot(), returnValue);
+    }
+
     // Initialize arguments object.  Ion will not allow OSR-ing into scripts
     // with |needsArgsObj| set, so this can be undefined.
     JS_ASSERT(!info().needsArgsObj());
     if (info().hasArguments()) {
         MInstruction *argsObj = MConstant::New(UndefinedValue());
         osrBlock->add(argsObj);
         osrBlock->initSlot(info().argsObjSlot(), argsObj);
     }
--- a/js/src/jit/IonFrameIterator.h
+++ b/js/src/jit/IonFrameIterator.h
@@ -266,16 +266,19 @@ class SnapshotIterator : public Snapshot
     void readFrameArgs(Op &op, const Value *argv, Value *scopeChain, Value *thisv,
                        unsigned start, unsigned formalEnd, unsigned iterEnd, JSScript *script)
     {
         if (scopeChain)
             *scopeChain = read();
         else
             skip();
 
+        // Skip slot for return value.
+        skip();
+
         // Skip slot for arguments object.
         if (script->argumentsHasVarBinding())
             skip();
 
         if (thisv)
             *thisv = read();
         else
             skip();
@@ -405,19 +408,19 @@ class InlineFrameIteratorMaybeGC
             // The overflown arguments are not available in current frame.
             // They are the last pushed arguments in the parent frame of this inlined frame.
             InlineFrameIteratorMaybeGC it(cx, this);
             ++it;
             unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
             SnapshotIterator parent_s(it.snapshotIterator());
 
             // Skip over all slots untill we get to the last slots (= arguments slots of callee)
-            // the +2 is for [this] and [scopechain], and maybe +1 for [argsObj]
-            JS_ASSERT(parent_s.slots() >= nactual + 2 + argsObjAdj);
-            unsigned skip = parent_s.slots() - nactual - 2 - argsObjAdj;
+            // the +2 is for [this], [returnvalue], [scopechain], and maybe +1 for [argsObj]
+            JS_ASSERT(parent_s.slots() >= nactual + 3 + argsObjAdj);
+            unsigned skip = parent_s.slots() - nactual - 3 - argsObjAdj;
             for (unsigned j = 0; j < skip; j++)
                 parent_s.skip();
 
             // Get the overflown arguments
             parent_s.readFrameArgs(op, NULL, NULL, NULL, nformal, nactual, end, it.script());
         } else {
             SnapshotIterator s(si_);
             Value *argv = frame_->actualArgs();
@@ -452,16 +455,19 @@ class InlineFrameIteratorMaybeGC
 
     JSObject *thisObject() const {
         // JS_ASSERT(isConstructing(...));
         SnapshotIterator s(si_);
 
         // scopeChain
         s.skip();
 
+        // return value
+        s.skip();
+
         // Arguments object.
         if (script()->argumentsHasVarBinding())
             s.skip();
 
         // In strict modes, |this| may not be an object and thus may not be
         // readable which can either segv in read or trigger the assertion.
         Value v = s.read();
         JS_ASSERT(v.isObject());
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -2677,16 +2677,32 @@ class LOsrScopeChain : public LInstructi
         setOperand(0, entry);
     }
 
     const MOsrScopeChain *mir() {
         return mir_->toOsrScopeChain();
     }
 };
 
+// Materialize a JSObject scope chain stored in an interpreter frame for OSR.
+class LOsrReturnValue : public LInstructionHelper<BOX_PIECES, 1, 0>
+{
+  public:
+    LIR_HEADER(OsrReturnValue)
+
+    LOsrReturnValue(const LAllocation &entry)
+    {
+        setOperand(0, entry);
+    }
+
+    const MOsrReturnValue *mir() {
+        return mir_->toOsrReturnValue();
+    }
+};
+
 class LRegExp : public LCallInstructionHelper<1, 0, 0>
 {
   public:
     LIR_HEADER(RegExp)
 
     const MRegExp *mir() const {
         return mir_->toRegExp();
     }
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -121,16 +121,17 @@
     _(DoubleToInt32)                \
     _(TruncateDToInt32)             \
     _(IntToString)                  \
     _(DoubleToString)               \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
+    _(OsrReturnValue)               \
     _(RegExp)                       \
     _(RegExpTest)                   \
     _(Lambda)                       \
     _(LambdaForSingleton)           \
     _(LambdaPar)                    \
     _(ImplicitThis)                 \
     _(Slots)                        \
     _(Elements)                     \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1474,16 +1474,23 @@ LIRGenerator::visitOsrValue(MOsrValue *v
 bool
 LIRGenerator::visitOsrScopeChain(MOsrScopeChain *object)
 {
     LOsrScopeChain *lir = new LOsrScopeChain(useRegister(object->entry()));
     return define(lir, object);
 }
 
 bool
+LIRGenerator::visitOsrReturnValue(MOsrReturnValue *value)
+{
+    LOsrReturnValue *lir = new LOsrReturnValue(useRegister(value->entry()));
+    return defineBox(lir, value);
+}
+
+bool
 LIRGenerator::visitToDouble(MToDouble *convert)
 {
     MDefinition *opd = convert->input();
     mozilla::DebugOnly<MToDouble::ConversionKind> conversion = convert->conversion();
 
     switch (opd->type()) {
       case MIRType_Value:
       {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -148,16 +148,17 @@ class LIRGenerator : public LIRGenerator
     bool visitConcatPar(MConcatPar *ins);
     bool visitCharCodeAt(MCharCodeAt *ins);
     bool visitFromCharCode(MFromCharCode *ins);
     bool visitStart(MStart *start);
     bool visitOsrEntry(MOsrEntry *entry);
     bool visitNop(MNop *nop);
     bool visitOsrValue(MOsrValue *value);
     bool visitOsrScopeChain(MOsrScopeChain *object);
+    bool visitOsrReturnValue(MOsrReturnValue *value);
     bool visitToDouble(MToDouble *convert);
     bool visitToInt32(MToInt32 *convert);
     bool visitTruncateToInt32(MTruncateToInt32 *truncate);
     bool visitToString(MToString *convert);
     bool visitRegExp(MRegExp *ins);
     bool visitRegExpTest(MRegExpTest *ins);
     bool visitLambda(MLambda *ins);
     bool visitLambdaPar(MLambdaPar *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -3986,16 +3986,38 @@ class MOsrScopeChain : public MUnaryInst
         return new MOsrScopeChain(entry);
     }
 
     MOsrEntry *entry() {
         return getOperand(0)->toOsrEntry();
     }
 };
 
+// MIR representation of the return value on the OSR StackFrame.
+// The Value is indexed off of OsrFrameReg.
+class MOsrReturnValue : public MUnaryInstruction
+{
+  private:
+    MOsrReturnValue(MOsrEntry *entry)
+      : MUnaryInstruction(entry)
+    {
+        setResultType(MIRType_Value);
+    }
+
+  public:
+    INSTRUCTION_HEADER(OsrReturnValue)
+    static MOsrReturnValue *New(MOsrEntry *entry) {
+        return new MOsrReturnValue(entry);
+    }
+
+    MOsrEntry *entry() {
+        return getOperand(0)->toOsrEntry();
+    }
+};
+
 // Check the current frame for over-recursion past the global stack limit.
 class MCheckOverRecursed : public MNullaryInstruction
 {
   public:
     INSTRUCTION_HEADER(CheckOverRecursed)
 };
 
 // Check the current frame for over-recursion past the global stack limit.
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -415,16 +415,19 @@ MBasicBlock::linkOsrValues(MStart *start
 
     MResumePoint *res = start->resumePoint();
 
     for (uint32_t i = 0; i < stackDepth(); i++) {
         MDefinition *def = slots_[i];
         if (i == info().scopeChainSlot()) {
             if (def->isOsrScopeChain())
                 def->toOsrScopeChain()->setResumePoint(res);
+        } else if (i == info().returnValueSlot()) {
+            if (def->isOsrReturnValue())
+                def->toOsrReturnValue()->setResumePoint(res);
         } else if (info().hasArguments() && i == info().argsObjSlot()) {
             JS_ASSERT(def->isConstant() && def->toConstant()->value() == UndefinedValue());
         } else {
             def->toOsrValue()->setResumePoint(res);
         }
     }
 }
 
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -20,16 +20,17 @@ namespace ion {
     _(TypeObjectDispatch)                                                   \
     _(FunctionDispatch)                                                     \
     _(PolyInlineDispatch)                                                   \
     _(Compare)                                                              \
     _(Phi)                                                                  \
     _(Beta)                                                                 \
     _(OsrValue)                                                             \
     _(OsrScopeChain)                                                        \
+    _(OsrReturnValue)                                                       \
     _(ReturnFromCtor)                                                       \
     _(CheckOverRecursed)                                                    \
     _(DefVar)                                                               \
     _(DefFun)                                                               \
     _(CreateThis)                                                           \
     _(CreateThisWithProto)                                                  \
     _(CreateThisWithTemplate)                                               \
     _(CreateArgumentsObject)                                                \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -111,16 +111,17 @@ class ParallelSafetyVisitor : public MIn
     SAFE_OP(TableSwitch)
     SAFE_OP(Goto)
     SAFE_OP(Test)
     SAFE_OP(Compare)
     SAFE_OP(Phi)
     SAFE_OP(Beta)
     UNSAFE_OP(OsrValue)
     UNSAFE_OP(OsrScopeChain)
+    UNSAFE_OP(OsrReturnValue)
     UNSAFE_OP(ReturnFromCtor)
     CUSTOM_OP(CheckOverRecursed)
     UNSAFE_OP(DefVar)
     UNSAFE_OP(DefFun)
     UNSAFE_OP(CreateThis)
     UNSAFE_OP(CreateThisWithTemplate)
     UNSAFE_OP(CreateThisWithProto)
     UNSAFE_OP(CreateArgumentsObject)
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1235,28 +1235,16 @@ CodeGeneratorARM::visitValue(LValue *val
 {
     const ValueOperand out = ToOutValue(value);
 
     masm.moveValue(value->value(), out);
     return true;
 }
 
 bool
-CodeGeneratorARM::visitOsrValue(LOsrValue *value)
-{
-    const LAllocation *frame   = value->getOperand(0);
-    const ValueOperand out     = ToOutValue(value);
-
-    const ptrdiff_t frameOffset = value->mir()->frameOffset();
-
-    masm.loadValue(Address(ToRegister(frame), frameOffset), out);
-    return true;
-}
-
-bool
 CodeGeneratorARM::visitBox(LBox *box)
 {
     const LDefinition *type = box->getDef(TYPE_INDEX);
 
     JS_ASSERT(!box->getOperand(0)->isConstant());
 
     // On x86, the input operand and the output payload have the same
     // virtual register. All that needs to be written is the type tag for
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -128,17 +128,16 @@ class CodeGeneratorARM : public CodeGene
   public:
     CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     bool visitBox(LBox *box);
     bool visitBoxDouble(LBoxDouble *box);
     bool visitUnbox(LUnbox *unbox);
     bool visitValue(LValue *value);
-    bool visitOsrValue(LOsrValue *value);
     bool visitDouble(LDouble *ins);
 
     bool visitLoadSlotV(LLoadSlotV *load);
     bool visitLoadSlotT(LLoadSlotT *load);
     bool visitStoreSlotT(LStoreSlotT *load);
 
     bool visitLoadElementT(LLoadElementT *load);
 
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -61,29 +61,16 @@ bool
 CodeGeneratorX64::visitValue(LValue *value)
 {
     LDefinition *reg = value->getDef(0);
     masm.moveValue(value->value(), ToRegister(reg));
     return true;
 }
 
 bool
-CodeGeneratorX64::visitOsrValue(LOsrValue *value)
-{
-    const LAllocation *frame  = value->getOperand(0);
-    const LDefinition *target = value->getDef(0);
-
-    const ptrdiff_t valueOffset = value->mir()->frameOffset();
-
-    masm.movq(Operand(ToRegister(frame), valueOffset), ToRegister(target));
-
-    return true;
-}
-
-bool
 CodeGeneratorX64::visitBox(LBox *box)
 {
     const LAllocation *in = box->getOperand(0);
     const LDefinition *result = box->getDef(0);
 
     if (box->type() != MIRType_Double)
         masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result));
     else
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -32,17 +32,16 @@ class CodeGeneratorX64 : public CodeGene
     void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
                            const Register &elements, const LAllocation *index);
 
   public:
     CodeGeneratorX64(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     bool visitValue(LValue *value);
-    bool visitOsrValue(LOsrValue *value);
     bool visitBox(LBox *box);
     bool visitUnbox(LUnbox *unbox);
     bool visitLoadSlotV(LLoadSlotV *ins);
     bool visitLoadSlotT(LLoadSlotT *load);
     bool visitStoreSlotT(LStoreSlotT *store);
     bool visitLoadElementT(LLoadElementT *load);
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitInterruptCheck(LInterruptCheck *lir);
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -87,27 +87,16 @@ bool
 CodeGeneratorX86::visitValue(LValue *value)
 {
     const ValueOperand out = ToOutValue(value);
     masm.moveValue(value->value(), out);
     return true;
 }
 
 bool
-CodeGeneratorX86::visitOsrValue(LOsrValue *value)
-{
-    const LAllocation *frame   = value->getOperand(0);
-    const ValueOperand out     = ToOutValue(value);
-    const ptrdiff_t frameOffset = value->mir()->frameOffset();
-
-    masm.loadValue(Operand(ToRegister(frame), frameOffset), out);
-    return true;
-}
-
-bool
 CodeGeneratorX86::visitBox(LBox *box)
 {
     const LDefinition *type = box->getDef(TYPE_INDEX);
 
     DebugOnly<const LAllocation *> a = box->getOperand(0);
     JS_ASSERT(!a->isConstant());
 
     // On x86, the input operand and the output payload have the same
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -38,17 +38,16 @@ class CodeGeneratorX86 : public CodeGene
   public:
     CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     bool visitBox(LBox *box);
     bool visitBoxDouble(LBoxDouble *box);
     bool visitUnbox(LUnbox *unbox);
     bool visitValue(LValue *value);
-    bool visitOsrValue(LOsrValue *value);
     bool visitLoadSlotV(LLoadSlotV *load);
     bool visitLoadSlotT(LLoadSlotT *load);
     bool visitStoreSlotT(LStoreSlotT *store);
     bool visitLoadElementT(LLoadElementT *load);
     bool visitImplicitThis(LImplicitThis *lir);
     bool visitInterruptCheck(LInterruptCheck *lir);
     bool visitCompareB(LCompareB *lir);
     bool visitCompareBAndBranch(LCompareBAndBranch *lir);
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -916,16 +916,20 @@ class StackFrame
     static size_t offsetOfNumActual() {
         return offsetof(StackFrame, u.nactual);
     }
 
     static size_t offsetOfScopeChain() {
         return offsetof(StackFrame, scopeChain_);
     }
 
+    static size_t offsetOfReturnValue() {
+        return offsetof(StackFrame, rval_);
+    }
+
     static ptrdiff_t offsetOfThis(JSFunction *fun) {
         return fun == NULL
                ? -1 * ptrdiff_t(sizeof(Value))
                : -(fun->nargs + 1) * ptrdiff_t(sizeof(Value));
     }
 
     static ptrdiff_t offsetOfFormalArg(JSFunction *fun, unsigned i) {
         JS_ASSERT(i < fun->nargs);