Bug 1118826 - Rewrite and simplify JitFrameIterator::baselineScriptAndPc and related code. r=shu
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 10 Jan 2015 20:05:18 +0100
changeset 223198 c213d9d53886ebc66b681fb5767cf4a267ad3056
parent 223197 a55f093defe1023db222cbd5be3a65f1bb963e39
child 223199 b55daf871dd244abd3b173815a19207378670566
push id10769
push usercbook@mozilla.com
push dateMon, 12 Jan 2015 14:15:52 +0000
treeherderfx-team@0e9765732906 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1118826
milestone37.0a1
Bug 1118826 - Rewrite and simplify JitFrameIterator::baselineScriptAndPc and related code. r=shu
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineDebugModeOSR.cpp
js/src/jit/BaselineFrame.h
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
js/src/jit/JitFrames.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jit/shared/BaselineCompiler-shared.cpp
js/src/vm/GeneratorObject.cpp
js/src/vm/GeneratorObject.h
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -120,16 +120,17 @@ struct BaselineStackBuilder
         header_->copyStackTop = buffer_ + bufferTotal_;
         header_->copyStackBottom = header_->copyStackTop;
         header_->setR0 = 0;
         header_->valueR0 = UndefinedValue();
         header_->setR1 = 0;
         header_->valueR1 = UndefinedValue();
         header_->resumeFramePtr = nullptr;
         header_->resumeAddr = nullptr;
+        header_->resumePC = nullptr;
         header_->monitorStub = nullptr;
         header_->numFrames = 0;
         return true;
     }
 
     bool enlarge() {
         MOZ_ASSERT(buffer_ != nullptr);
         if (bufferTotal_ & mozilla::tl::MulOverflowMask<2>::value)
@@ -270,16 +271,20 @@ struct BaselineStackBuilder
     void setResumeFramePtr(void *resumeFramePtr) {
         header_->resumeFramePtr = resumeFramePtr;
     }
 
     void setResumeAddr(void *resumeAddr) {
         header_->resumeAddr = resumeAddr;
     }
 
+    void setResumePC(jsbytecode *pc) {
+        header_->resumePC = pc;
+    }
+
     void setMonitorStub(ICStub *stub) {
         header_->monitorStub = stub;
     }
 
     template <typename T>
     BufferPointer<T> pointerAtStackOffset(size_t offset) {
         if (offset < bufferUsed_) {
             // Calculate offset from copyStackTop.
@@ -978,16 +983,17 @@ InitFromBailout(JSContext *cx, HandleScr
                 enterMonitorChain = true;
         }
 
         uint32_t numCallArgs = isCall ? GET_ARGC(pc) : 0;
 
         if (resumeAfter && !enterMonitorChain)
             pc = GetNextPc(pc);
 
+        builder.setResumePC(pc);
         builder.setResumeFramePtr(prevFramePtr);
 
         if (enterMonitorChain) {
             ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
             ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback();
             MOZ_ASSERT(fallbackStub->isMonitoredFallback());
             JitSpew(JitSpew_BaselineBailouts, "      [TYPE-MONITOR CHAIN]");
             ICMonitoredFallbackStub *monFallbackStub = fallbackStub->toMonitoredFallbackStub();
@@ -1029,38 +1035,40 @@ InitFromBailout(JSContext *cx, HandleScr
 
         } else {
             // If needed, initialize BaselineBailoutInfo's valueR0 and/or valueR1 with the
             // top stack values.
             //
             // Note that we use the 'maybe' variant of nativeCodeForPC because
             // of exception propagation for debug mode. See note below.
             PCMappingSlotInfo slotInfo;
-            uint8_t *nativeCodeForPC = baselineScript->maybeNativeCodeForPC(script, pc, &slotInfo);
+            uint8_t *nativeCodeForPC;
+
+            if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) {
+                // When propagating an exception for debug mode, set the
+                // resume pc to the throwing pc, so that Debugger hooks report
+                // the correct pc offset of the throwing op instead of its
+                // successor (this pc will be used as the BaselineFrame's
+                // override pc).
+                //
+                // Note that we never resume at this pc, it is set for the sake
+                // of frame iterators giving the correct answer.
+                //
+                // We also set nativeCodeForPC to nullptr as this address
+                // won't be used anywhere.
+                jsbytecode *throwPC = script->offsetToPC(iter.pcOffset());
+                builder.setResumePC(throwPC);
+                nativeCodeForPC = nullptr;
+            } else {
+                nativeCodeForPC = baselineScript->nativeCodeForPC(script, pc, &slotInfo);
+                MOZ_ASSERT(nativeCodeForPC);
+            }
+
             unsigned numUnsynced = slotInfo.numUnsynced();
 
-            if (excInfo && excInfo->propagatingIonExceptionForDebugMode() && resumeAfter) {
-                // When propagating an exception for debug mode, set the
-                // return address as native code for the throwing op, so that
-                // Debugger hooks report the correct pc offset of the throwing
-                // op instead of its successor.
-                //
-                // This should not be done if we are at a resume-at point, as
-                // might be the case when propagating an exception thrown from
-                // an interrupt handler. That interrupt could have happened to
-                // interrupt at a loop head, which would have no ICEntry at
-                // that point.
-                //
-                // Note that we never resume into this address, it is set for
-                // the sake of frame iterators giving the correct answer.
-                jsbytecode *throwPC = script->offsetToPC(iter.pcOffset());
-                nativeCodeForPC = baselineScript->nativeCodeForPC(script, throwPC);
-            }
-
-            MOZ_ASSERT(nativeCodeForPC);
             MOZ_ASSERT(numUnsynced <= 2);
             PCMappingSlotInfo::SlotLocation loc1, loc2;
             if (numUnsynced > 0) {
                 loc1 = slotInfo.topSlotLocation();
                 JitSpew(JitSpew_BaselineBailouts, "      Popping top stack value into %d.",
                         (int) loc1);
                 builder.popValueInto(loc1);
             }
@@ -1678,35 +1686,34 @@ jit::FinishBailoutToBaseline(BaselineBai
 {
     // The caller pushes R0 and R1 on the stack without rooting them.
     // Since GC here is very unlikely just suppress it.
     JSContext *cx = GetJSContextFromJitCode();
     js::gc::AutoSuppressGC suppressGC(cx);
 
     JitSpew(JitSpew_BaselineBailouts, "  Done restoring frames");
 
-    // Check that we can get the current script's PC.
-#ifdef DEBUG
-    jsbytecode *pc;
-    cx->currentScript(&pc);
-    JitSpew(JitSpew_BaselineBailouts, "  Got pc=%p", pc);
-#endif
+    // The current native code pc may not have a corresponding ICEntry, so we
+    // store the bytecode pc in the frame for frame iterators. This pc is
+    // cleared at the end of this function. If we return false, we don't clear
+    // it: the exception handler also needs it and will clear it for us.
+    BaselineFrame *topFrame = GetTopBaselineFrame(cx);
+    topFrame->setOverridePc(bailoutInfo->resumePC);
 
     uint32_t numFrames = bailoutInfo->numFrames;
     MOZ_ASSERT(numFrames > 0);
     BailoutKind bailoutKind = bailoutInfo->bailoutKind;
 
     // Free the bailout buffer.
     js_free(bailoutInfo);
     bailoutInfo = nullptr;
 
     // Ensure the frame has a call object if it needs one. If the scope chain
     // is nullptr, we will enter baseline code at the prologue so no need to do
     // anything in that case.
-    BaselineFrame *topFrame = GetTopBaselineFrame(cx);
     if (topFrame->scopeChain() && !EnsureHasScopeObjects(cx, topFrame))
         return false;
 
     // Create arguments objects for bailed out frames, to maintain the invariant
     // that script->needsArgsObj() implies frame->hasArgsObj().
     RootedScript innerScript(cx, nullptr);
     RootedScript outerScript(cx, nullptr);
 
@@ -1845,10 +1852,12 @@ jit::FinishBailoutToBaseline(BaselineBai
         return false;
       default:
         MOZ_CRASH("Unknown bailout kind!");
     }
 
     if (!CheckFrequentBailouts(cx, outerScript))
         return false;
 
+    // We're returning to JIT code, so we should clear the override pc.
+    topFrame->clearOverridePc();
     return true;
 }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -3564,17 +3564,18 @@ BaselineCompiler::emit_JSOP_FINALYIELDRV
     masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
     return emitReturn();
 }
 
 typedef bool (*InterpretResumeFn)(JSContext *, HandleObject, HandleValue, HandlePropertyName,
                                   MutableHandleValue);
 static const VMFunction InterpretResumeInfo = FunctionInfo<InterpretResumeFn>(jit::InterpretResume);
 
-typedef bool (*GeneratorThrowFn)(JSContext *, BaselineFrame *, HandleObject, HandleValue, uint32_t);
+typedef bool (*GeneratorThrowFn)(JSContext *, BaselineFrame *, Handle<GeneratorObject*>,
+                                 HandleValue, uint32_t);
 static const VMFunction GeneratorThrowInfo = FunctionInfo<GeneratorThrowFn>(jit::GeneratorThrowOrClose, TailCall);
 
 bool
 BaselineCompiler::emit_JSOP_RESUME()
 {
     GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(pc);
 
     frame.syncStack(0);
@@ -3596,25 +3597,18 @@ BaselineCompiler::emit_JSOP_RESUME()
     Register scratch1 = regs.takeAny();
     masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), scratch1);
 
     // Load the BaselineScript or call a stub if we don't have one.
     Label interpret;
     masm.loadPtr(Address(scratch1, JSScript::offsetOfBaselineScript()), scratch1);
     masm.branchPtr(Assembler::BelowOrEqual, scratch1, ImmPtr(BASELINE_DISABLED_SCRIPT), &interpret);
 
-    // Determine the resume address based on the yieldIndex and the
-    // yieldIndex -> native table in the BaselineScript.
+    // Push |undefined| for all formals.
     Register scratch2 = regs.takeAny();
-    masm.load32(Address(scratch1, BaselineScript::offsetOfYieldEntriesOffset()), scratch2);
-    masm.addPtr(scratch2, scratch1);
-    masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), scratch2);
-    masm.loadPtr(BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))), scratch1);
-
-    // Push |undefined| for all formals.
     Label loop, loopDone;
     masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch2);
     masm.bind(&loop);
     masm.branchTest32(Assembler::Zero, scratch2, scratch2, &loopDone);
     {
         masm.pushValue(UndefinedValue());
         masm.sub32(Imm32(1), scratch2);
         masm.jump(&loop);
@@ -3702,53 +3696,58 @@ BaselineCompiler::emit_JSOP_RESUME()
         masm.storeValue(NullValue(), exprStackSlot);
         regs.add(initLength);
     }
 
     masm.bind(&noExprStack);
     masm.pushValue(retVal);
 
     if (resumeKind == GeneratorObject::NEXT) {
+        // Determine the resume address based on the yieldIndex and the
+        // yieldIndex -> native table in the BaselineScript.
+        masm.load32(Address(scratch1, BaselineScript::offsetOfYieldEntriesOffset()), scratch2);
+        masm.addPtr(scratch2, scratch1);
+        masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), scratch2);
+        masm.loadPtr(BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))), scratch1);
+
         // Mark as running and jump to the generator's JIT code.
         masm.storeValue(Int32Value(GeneratorObject::YIELD_INDEX_RUNNING),
                         Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()));
         masm.jump(scratch1);
     } else {
         MOZ_ASSERT(resumeKind == GeneratorObject::THROW || resumeKind == GeneratorObject::CLOSE);
 
         // Update the frame's frameSize field.
-        Register scratch3 = regs.takeAny();
         masm.computeEffectiveAddress(Address(BaselineFrameReg, BaselineFrame::FramePointerOffset),
                                      scratch2);
-        masm.movePtr(scratch2, scratch3);
+        masm.movePtr(scratch2, scratch1);
         masm.subPtr(BaselineStackReg, scratch2);
         masm.store32(scratch2, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
         masm.loadBaselineFramePtr(BaselineFrameReg, scratch2);
 
         prepareVMCall();
         pushArg(Imm32(resumeKind));
         pushArg(retVal);
         pushArg(genObj);
         pushArg(scratch2);
 
         JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(GeneratorThrowInfo);
         if (!code)
             return false;
 
         // Create the frame descriptor.
-        masm.subPtr(BaselineStackReg, scratch3);
-        masm.makeFrameDescriptor(scratch3, JitFrame_BaselineJS);
-
-        // Push the frame descriptor and the native address of the op after the
-        // YIELD. The frame iteration code relies on this address to determine
-        // where we threw the exception.
-        masm.push(scratch3);
+        masm.subPtr(BaselineStackReg, scratch1);
+        masm.makeFrameDescriptor(scratch1, JitFrame_BaselineJS);
+
+        // Push the frame descriptor and a dummy return address (it doesn't
+        // matter what we push here, frame iterators will use the frame pc
+        // set in jit::GeneratorThrowOrClose).
         masm.push(scratch1);
+        masm.push(ImmWord(0));
         masm.jump(code);
-        regs.add(scratch3);
     }
 
     // If the generator script has no JIT code, call into the VM.
     masm.bind(&interpret);
 
     prepareVMCall();
     if (resumeKind == GeneratorObject::NEXT) {
         pushArg(ImmGCPtr(cx->names().next));
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -188,44 +188,44 @@ CollectJitStackScripts(JSContext *cx, co
           case JitFrame_BaselineJS: {
             JSScript *script = iter.script();
 
             if (!obs.shouldRecompileOrInvalidate(script)) {
                 prevFrameStubPtr = nullptr;
                 break;
             }
 
-            if (BaselineDebugModeOSRInfo *info = iter.baselineFrame()->getDebugModeOSRInfo()) {
+            BaselineFrame *frame = iter.baselineFrame();
+
+            if (BaselineDebugModeOSRInfo *info = frame->getDebugModeOSRInfo()) {
                 // If patching a previously patched yet unpopped frame, we can
                 // use the BaselineDebugModeOSRInfo on the frame directly to
                 // patch. Indeed, we cannot use iter.returnAddressToFp(), as
                 // it points into the debug mode OSR handler and cannot be
                 // used to look up a corresponding ICEntry.
                 //
                 // See cases F and G in PatchBaselineFramesForDebugMode.
                 if (!entries.append(DebugModeOSREntry(script, info)))
                     return false;
+            } else if (frame->isDebuggerHandlingException() && frame->maybeOverridePc()) {
+                // We are in the middle of handling an exception and the frame
+                // has an override pc. This happens since we could have bailed
+                // out in place from Ion after a throw, settling on the pc which
+                // may have no ICEntry (e.g., Ion is free to insert resume
+                // points after non-effectful ops for better register
+                // allocation).
+                uint32_t offset = script->pcToOffset(frame->overridePc());
+                if (!entries.append(DebugModeOSREntry(script, offset)))
+                    return false;
             } else {
+                // The frame must be settled on a pc with an ICEntry.
                 uint8_t *retAddr = iter.returnAddressToFp();
-                if (iter.baselineFrame()->isDebuggerHandlingException()) {
-                    // We are in the middle of handling an exception. This
-                    // happens since we could have bailed out in place from
-                    // Ion after a throw, settling on the pc which may have no
-                    // ICEntry (e.g., Ion is free to insert resume points
-                    // after non-effectful ops for better register
-                    // allocation).
-                    jsbytecode *pc = script->baselineScript()->pcForNativeAddress(script, retAddr);
-                    if (!entries.append(DebugModeOSREntry(script, script->pcToOffset(pc))))
-                        return false;
-                } else {
-                    // Normally, the frame is settled on a pc with an ICEntry.
-                    ICEntry &icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
-                    if (!entries.append(DebugModeOSREntry(script, icEntry)))
-                        return false;
-                }
+                ICEntry &icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
+                if (!entries.append(DebugModeOSREntry(script, icEntry)))
+                    return false;
             }
 
             if (entries.back().needsRecompileInfo()) {
                 if (!entries.back().allocateRecompileInfo(cx))
                     return false;
 
                 needsRecompileHandler |= true;
             }
@@ -418,25 +418,24 @@ PatchBaselineFramesForDebugMode(JSContex
                 break;
             }
 
             if (kind == ICEntry::Kind_Invalid) {
                 // Case H above.
                 //
                 // We are recompiling on-stack scripts from inside the
                 // exception handler, by way of an onExceptionUnwind
-                // invocation, on a pc without an ICEntry. Since execution
-                // cannot continue after the exception anyways, it doesn't
-                // matter what we patch the resume address with, nor do we
-                // need to fix up the stack and register state.
+                // invocation, on a pc without an ICEntry. This means the
+                // frame must have an override pc.
                 //
-                // So that frame iterators may continue working, we patch the
-                // resume address.
+                // Patch the resume address to nullptr, to ensure the old
+                // address is not used anywhere.
                 MOZ_ASSERT(iter.baselineFrame()->isDebuggerHandlingException());
-                uint8_t *retAddr = bl->nativeCodeForPC(script, pc);
+                MOZ_ASSERT(iter.baselineFrame()->overridePc() == pc);
+                uint8_t *retAddr = nullptr;
                 SpewPatchBaselineFrameFromExceptionHandler(prev->returnAddress(), retAddr,
                                                            script, pc);
                 DebugModeOSRVolatileJitFrameIterator::forwardLiveIterators(
                     cx, prev->returnAddress(), retAddr);
                 prev->setReturnAddress(retAddr);
                 entryIndex++;
                 break;
             }
@@ -477,22 +476,16 @@ PatchBaselineFramesForDebugMode(JSContex
                 //
                 // Patching returns from a VM call. After fixing up the the
                 // continuation for unsynced values (the frame register is
                 // popped by the callVM trampoline), we resume at the
                 // return-from-callVM address. The assumption here is that all
                 // callVMs which can trigger debug mode OSR are the *only*
                 // callVMs generated for their respective pc locations in the
                 // baseline JIT code.
-                //
-                // Get the slot info for the next pc, but ignore the code
-                // address. We get the code address from the
-                // return-from-callVM entry instead.
-                (void) bl->maybeNativeCodeForPC(script, pc + GetBytecodeLength(pc),
-                                                &recompInfo->slotInfo);
                 ICEntry &callVMEntry = bl->callVMEntryFromPCOffset(pcOffset);
                 recompInfo->resumeAddr = bl->returnAddressForIC(callVMEntry);
                 popFrameReg = false;
                 break;
               }
 
               case ICEntry::Kind_DebugTrap:
                 // Case C above.
@@ -532,16 +525,17 @@ PatchBaselineFramesForDebugMode(JSContex
             // The recompile handler must already be created so that this
             // function may be infallible.
             JitRuntime *rt = cx->runtime()->jitRuntime();
             void *handlerAddr = rt->getBaselineDebugModeOSRHandlerAddress(cx, popFrameReg);
             MOZ_ASSERT(handlerAddr);
 
             prev->setReturnAddress(reinterpret_cast<uint8_t *>(handlerAddr));
             iter.baselineFrame()->setDebugModeOSRInfo(recompInfo);
+            iter.baselineFrame()->setOverridePc(recompInfo->pc);
 
             entryIndex++;
             break;
           }
 
           case JitFrame_BaselineStub: {
             JitFrameIterator prev(iter);
             ++prev;
@@ -949,16 +943,19 @@ SyncBaselineDebugModeOSRInfo(BaselineFra
     // Scale stackAdjust.
     info->stackAdjust *= sizeof(Value);
 }
 
 static void
 FinishBaselineDebugModeOSR(BaselineFrame *frame)
 {
     frame->deleteDebugModeOSRInfo();
+
+    // We will return to JIT code now so we have to clear the override pc.
+    frame->clearOverridePc();
 }
 
 void
 BaselineFrame::deleteDebugModeOSRInfo()
 {
     js_delete(getDebugModeOSRInfo());
     flags_ &= ~HAS_DEBUG_MODE_OSR_INFO;
 }
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -61,24 +61,26 @@ class BaselineFrame
 
         // Frame has over-recursed on an early check.
         OVER_RECURSED    = 1 << 9,
 
         // Frame has a BaselineRecompileInfo stashed in the scratch value
         // slot. See PatchBaselineFramesForDebugMode.
         HAS_DEBUG_MODE_OSR_INFO = 1 << 10,
 
-        // Frame has had its scope chain unwound to a pc during exception
-        // handling that is different from its current pc.
+        // This flag is intended for use whenever the frame is settled on a
+        // native code address without a corresponding ICEntry. In this case,
+        // the frame contains an explicit bytecode offset for frame iterators.
         //
-        // This flag is intended for use in the DebugEpilogue. Once it is set,
-        // the only way to clear it is to pop the frame. Do *not* set this if
-        // we will resume execution on the frame, such as in a catch or
-        // finally block.
-        HAS_UNWOUND_SCOPE_OVERRIDE_PC = 1 << 11,
+        // There can also be an override pc if the frame has had its scope chain
+        // unwound to a pc during exception handling that is different from its
+        // current pc.
+        //
+        // This flag should never be set when we're executing JIT code.
+        HAS_OVERRIDE_PC = 1 << 11,
 
         // Frame has called out to Debugger code from
         // HandleExceptionBaseline. This is set for debug mode OSR sanity
         // checking when it handles corner cases which only arise during
         // exception handling.
         DEBUGGER_HANDLING_EXCEPTION = 1 << 12
     };
 
@@ -95,17 +97,17 @@ class BaselineFrame
     };
     uint32_t loReturnValue_;              // If HAS_RVAL, the frame's return value.
     uint32_t hiReturnValue_;
     uint32_t frameSize_;
     JSObject *scopeChain_;                // Scope chain (always initialized).
     JSScript *evalScript_;                // If isEvalFrame(), the current eval script.
     ArgumentsObject *argsObj_;            // If HAS_ARGS_OBJ, the arguments object.
     void *unused;                         // See static assertion re: sizeof, below.
-    uint32_t unwoundScopeOverrideOffset_; // If HAS_UNWOUND_SCOPE_OVERRIDE_PC.
+    uint32_t overrideOffset_;             // If HAS_OVERRIDE_PC, the bytecode offset.
     uint32_t flags_;
 
   public:
     // Distance between the frame pointer and the frame header (return address).
     // This is the old frame pointer saved in the prologue.
     static const uint32_t FramePointerOffset = sizeof(void *);
 
     bool initForOsr(InterpreterFrame *fp, uint32_t numStackValues);
@@ -336,30 +338,35 @@ class BaselineFrame
 
     void setDebugModeOSRInfo(BaselineDebugModeOSRInfo *info) {
         flags_ |= HAS_DEBUG_MODE_OSR_INFO;
         debugModeOSRInfo_ = info;
     }
 
     void deleteDebugModeOSRInfo();
 
-    jsbytecode *unwoundScopeOverridePc() {
-        MOZ_ASSERT(flags_ & HAS_UNWOUND_SCOPE_OVERRIDE_PC);
-        return script()->offsetToPC(unwoundScopeOverrideOffset_);
+    // See the HAS_OVERRIDE_PC comment.
+    jsbytecode *overridePc() const {
+        MOZ_ASSERT(flags_ & HAS_OVERRIDE_PC);
+        return script()->offsetToPC(overrideOffset_);
     }
 
-    jsbytecode *getUnwoundScopeOverridePc() {
-        if (flags_ & HAS_UNWOUND_SCOPE_OVERRIDE_PC)
-            return unwoundScopeOverridePc();
+    jsbytecode *maybeOverridePc() const {
+        if (flags_ & HAS_OVERRIDE_PC)
+            return overridePc();
         return nullptr;
     }
 
-    void setUnwoundScopeOverridePc(jsbytecode *pc) {
-        flags_ |= HAS_UNWOUND_SCOPE_OVERRIDE_PC;
-        unwoundScopeOverrideOffset_ = script()->pcToOffset(pc);
+    void setOverridePc(jsbytecode *pc) {
+        flags_ |= HAS_OVERRIDE_PC;
+        overrideOffset_ = script()->pcToOffset(pc);
+    }
+
+    void clearOverridePc() {
+        flags_ &= ~HAS_OVERRIDE_PC;
     }
 
     void trace(JSTracer *trc, JitFrameIterator &frame);
 
     bool isFunctionFrame() const {
         return CalleeTokenIsFunction(calleeToken());
     }
     bool isGlobalFrame() const {
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -521,45 +521,35 @@ BaselineScript::pcMappingReader(size_t i
     uint8_t *dataStart = pcMappingData() + entry.bufferOffset;
     uint8_t *dataEnd = (indexEntry == numPCMappingIndexEntries() - 1)
         ? pcMappingData() + pcMappingSize_
         : pcMappingData() + pcMappingIndexEntry(indexEntry + 1).bufferOffset;
 
     return CompactBufferReader(dataStart, dataEnd);
 }
 
-ICEntry *
-BaselineScript::maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset)
+ICEntry &
+BaselineScript::icEntryFromReturnOffset(CodeOffsetLabel returnOffset)
 {
     size_t bottom = 0;
     size_t top = numICEntries();
     size_t mid = bottom + (top - bottom) / 2;
     while (mid < top) {
         ICEntry &midEntry = icEntry(mid);
         if (midEntry.returnOffset().offset() < returnOffset.offset())
             bottom = mid + 1;
-        else // if (midEntry.returnOffset().offset() >= returnOffset.offset())
+        else
             top = mid;
         mid = bottom + (top - bottom) / 2;
     }
-    if (mid >= numICEntries())
-        return nullptr;
-
-    if (icEntry(mid).returnOffset().offset() != returnOffset.offset())
-        return nullptr;
 
-    return &icEntry(mid);
-}
+    MOZ_ASSERT(mid < numICEntries());
+    MOZ_ASSERT(icEntry(mid).returnOffset().offset() == returnOffset.offset());
 
-ICEntry &
-BaselineScript::icEntryFromReturnOffset(CodeOffsetLabel returnOffset)
-{
-    ICEntry *result = maybeICEntryFromReturnOffset(returnOffset);
-    MOZ_ASSERT(result);
-    return *result;
+    return icEntry(mid);
 }
 
 uint8_t *
 BaselineScript::returnAddressForIC(const ICEntry &ent)
 {
     return method()->raw() + ent.returnOffset().offset();
 }
 
@@ -639,25 +629,16 @@ BaselineScript::callVMEntryFromPCOffset(
     }
     for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++) {
         if (icEntry(i).kind() == ICEntry::Kind_CallVM)
             return icEntry(i);
     }
     MOZ_CRASH("Invalid PC offset for callVM entry.");
 }
 
-ICEntry *
-BaselineScript::maybeICEntryFromReturnAddress(uint8_t *returnAddr)
-{
-    MOZ_ASSERT(returnAddr > method_->raw());
-    MOZ_ASSERT(returnAddr < method_->raw() + method_->instructionsSize());
-    CodeOffsetLabel offset(returnAddr - method_->raw());
-    return maybeICEntryFromReturnOffset(offset);
-}
-
 ICEntry &
 BaselineScript::icEntryFromReturnAddress(uint8_t *returnAddr)
 {
     MOZ_ASSERT(returnAddr > method_->raw());
     MOZ_ASSERT(returnAddr < method_->raw() + method_->instructionsSize());
     CodeOffsetLabel offset(returnAddr - method_->raw());
     return icEntryFromReturnOffset(offset);
 }
@@ -723,17 +704,17 @@ BaselineScript::copyPCMappingEntries(con
 void
 BaselineScript::copyPCMappingIndexEntries(const PCMappingIndexEntry *entries)
 {
     for (uint32_t i = 0; i < numPCMappingIndexEntries(); i++)
         pcMappingIndexEntry(i) = entries[i];
 }
 
 uint8_t *
-BaselineScript::maybeNativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo)
+BaselineScript::nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo)
 {
     MOZ_ASSERT_IF(script->hasBaselineScript(), script->baselineScript() == this);
 
     uint32_t pcOffset = script->pcToOffset(pc);
 
     // Look for the first PCMappingIndexEntry with pc > the pc we are
     // interested in.
     uint32_t i = 1;
@@ -767,17 +748,17 @@ BaselineScript::maybeNativeCodeForPC(JSS
             if (slotInfo)
                 *slotInfo = PCMappingSlotInfo(b & ~0x80);
             return method_->raw() + nativeOffset;
         }
 
         curPC += GetBytecodeLength(curPC);
     }
 
-    return nullptr;
+    MOZ_CRASH("No native code for this pc");
 }
 
 jsbytecode *
 BaselineScript::pcForReturnOffset(JSScript *script, uint32_t nativeOffset)
 {
     return pcForNativeOffset(script, nativeOffset, true);
 }
 
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -337,22 +337,20 @@ struct BaselineScript
         method()->togglePreBarriers(enabled);
     }
 
     bool containsCodeAddress(uint8_t *addr) const {
         return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize();
     }
 
     ICEntry &icEntry(size_t index);
-    ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromPCOffset(uint32_t pcOffset);
     ICEntry &icEntryFromPCOffset(uint32_t pcOffset, ICEntry *prevLookedUpEntry);
     ICEntry &callVMEntryFromPCOffset(uint32_t pcOffset);
-    ICEntry *maybeICEntryFromReturnAddress(uint8_t *returnAddr);
     ICEntry &icEntryFromReturnAddress(uint8_t *returnAddr);
     uint8_t *returnAddressForIC(const ICEntry &ent);
 
     size_t numICEntries() const {
         return icEntries_;
     }
 
     void copyICEntries(JSScript *script, const ICEntry *entries, MacroAssembler &masm);
@@ -363,27 +361,20 @@ struct BaselineScript
     PCMappingIndexEntry &pcMappingIndexEntry(size_t index);
     CompactBufferReader pcMappingReader(size_t indexEntry);
 
     size_t numPCMappingIndexEntries() const {
         return pcMappingIndexEntries_;
     }
 
     void copyPCMappingIndexEntries(const PCMappingIndexEntry *entries);
+    void copyPCMappingEntries(const CompactBufferWriter &entries);
 
-    void copyPCMappingEntries(const CompactBufferWriter &entries);
-    uint8_t *maybeNativeCodeForPC(JSScript *script, jsbytecode *pc,
-                                  PCMappingSlotInfo *slotInfo = nullptr);
     uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc,
-                             PCMappingSlotInfo *slotInfo = nullptr)
-    {
-        uint8_t *code = maybeNativeCodeForPC(script, pc, slotInfo);
-        MOZ_ASSERT(code);
-        return code;
-    }
+                             PCMappingSlotInfo *slotInfo = nullptr);
 
     jsbytecode *pcForReturnOffset(JSScript *script, uint32_t nativeOffset);
     jsbytecode *pcForReturnAddress(JSScript *script, uint8_t *nativeAddress);
 
     jsbytecode *pcForNativeAddress(JSScript *script, uint8_t *nativeAddress);
     jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset);
 
     bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);
@@ -487,16 +478,19 @@ struct BaselineBailoutInfo
     Value valueR1;
 
     // The value of the frame pointer register on resume.
     void *resumeFramePtr;
 
     // The native code address to resume into.
     void *resumeAddr;
 
+    // The bytecode pc where we will resume.
+    jsbytecode *resumePC;
+
     // If resuming into a TypeMonitor IC chain, this field holds the
     // address of the first stub in that chain.  If this field is
     // set, then the actual jitcode resumed into is the jitcode for
     // the first stub, not the resumeAddr above.  The resumeAddr
     // above, in this case, is pushed onto the stack so that the
     // TypeMonitor chain can tail-return into the main jitcode when done.
     ICStub *monitorStub;
 
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -209,53 +209,30 @@ JitFrameIterator::script() const
 void
 JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
 {
     MOZ_ASSERT(isBaselineJS());
     JSScript *script = this->script();
     if (scriptRes)
         *scriptRes = script;
 
-    // If we have unwound the scope due to exception handling to a different
-    // pc, the frame should behave as if it were settled on that pc.
-    if (jsbytecode *overridePc = baselineFrame()->getUnwoundScopeOverridePc()) {
+    MOZ_ASSERT(pcRes);
+
+    // Use the frame's override pc, if we have one. This should only happen
+    // when we're in FinishBailoutToBaseline, handling an exception or toggling
+    // debug mode.
+    if (jsbytecode *overridePc = baselineFrame()->maybeOverridePc()) {
         *pcRes = overridePc;
         return;
     }
 
-    // If we are settled on a patched BaselineFrame due to debug mode OSR, get
-    // the stashed pc.
-    if (baselineFrame()->getDebugModeOSRInfo()) {
-        *pcRes = baselineFrame()->debugModeOSRInfo()->pc;
-        return;
-    }
-
+    // Else, there must be an ICEntry for the current return address.
     uint8_t *retAddr = returnAddressToFp();
-    if (pcRes) {
-        // If the return address is into the prologue entry address or just
-        // after the debug prologue, then assume start of script.
-        if (retAddr == script->baselineScript()->prologueEntryAddr() ||
-            retAddr == script->baselineScript()->postDebugPrologueAddr())
-        {
-            *pcRes = script->code();
-            return;
-        }
-
-        // The return address _may_ be a return from a callVM or IC chain call done for
-        // some op.
-        ICEntry *icEntry = script->baselineScript()->maybeICEntryFromReturnAddress(retAddr);
-        if (icEntry) {
-            *pcRes = icEntry->pc(script);
-            return;
-        }
-
-        // If not, the return address _must_ be the start address of an op, which can
-        // be computed from the pc mapping table.
-        *pcRes = script->baselineScript()->pcForReturnAddress(script, retAddr);
-    }
+    ICEntry &icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
+    *pcRes = icEntry.pc(script);
 }
 
 Value *
 JitFrameIterator::actualArgs() const
 {
     return jsFrame()->argv() + 1;
 }
 
@@ -528,17 +505,17 @@ HandleClosingGeneratorReturn(JSContext *
     if (!exception.isMagic(JS_GENERATOR_CLOSING))
         return;
 
     cx->clearPendingException();
     frame.baselineFrame()->setReturnValue(UndefinedValue());
 
     if (unwoundScopeToPc) {
         if (frame.baselineFrame()->isDebuggee())
-            frame.baselineFrame()->setUnwoundScopeOverridePc(unwoundScopeToPc);
+            frame.baselineFrame()->setOverridePc(unwoundScopeToPc);
         pc = unwoundScopeToPc;
     }
 
     ForcedReturn(cx, frame, pc, rfe, calledDebugEpilogue);
 }
 
 struct AutoDebuggerHandlingException
 {
@@ -693,16 +670,23 @@ HandleExceptionBaseline(JSContext *cx, c
 
 struct AutoDeleteDebugModeOSRInfo
 {
     BaselineFrame *frame;
     explicit AutoDeleteDebugModeOSRInfo(BaselineFrame *frame) : frame(frame) { MOZ_ASSERT(frame); }
     ~AutoDeleteDebugModeOSRInfo() { frame->deleteDebugModeOSRInfo(); }
 };
 
+struct AutoClearBaselineOverridePc
+{
+    BaselineFrame *frame;
+    explicit AutoClearBaselineOverridePc(BaselineFrame *frame) : frame(frame) { MOZ_ASSERT(frame); }
+    ~AutoClearBaselineOverridePc() { frame->clearOverridePc(); }
+};
+
 void
 HandleException(ResumeFromException *rfe)
 {
     JSContext *cx = GetJSContextFromJitCode();
     TraceLoggerThread *logger = TraceLoggerForMainThread(cx->runtime());
 
     rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
 
@@ -784,16 +768,27 @@ HandleException(ResumeFromException *rfe
 
         } else if (iter.isBaselineJS()) {
             // It's invalid to call DebugEpilogue twice for the same frame.
             bool calledDebugEpilogue = false;
 
             // Remember the pc we unwound the scope to.
             jsbytecode *unwoundScopeToPc = nullptr;
 
+            // Clear the frame's override pc when we leave this block. This is
+            // fine because we're either:
+            // (1) Going to enter a catch or finally block. We don't want to
+            //     keep the old pc when we're executing JIT code.
+            // (2) Going to pop the frame, either here or a forced return.
+            //     In this case nothing will observe the frame's pc.
+            // (3) Performing an exception bailout. In this case
+            //     FinishBailoutToBaseline will set the pc to the resume pc
+            //     and clear it before it returns to JIT code.
+            AutoClearBaselineOverridePc clearPc(iter.baselineFrame());
+
             HandleExceptionBaseline(cx, iter, rfe, &unwoundScopeToPc, &calledDebugEpilogue);
 
             // If we are propagating an exception through a frame with
             // on-stack recompile info, we should free the allocated
             // RecompileInfo struct before we leave this block, as we will not
             // be returning to the recompile handler.
             //
             // We cannot delete it immediately because of the call to
@@ -815,24 +810,23 @@ HandleException(ResumeFromException *rfe
             // it doesn't try to pop the SPS frame again.
             iter.baselineFrame()->unsetPushedSPSFrame();
 
             if (iter.baselineFrame()->isDebuggee() && !calledDebugEpilogue) {
                 // If we still need to call the DebugEpilogue, we must
                 // remember the pc we unwound the scope chain to, as it will
                 // be out of sync with the frame's actual pc.
                 if (unwoundScopeToPc)
-                    iter.baselineFrame()->setUnwoundScopeOverridePc(unwoundScopeToPc);
+                    iter.baselineFrame()->setOverridePc(unwoundScopeToPc);
 
                 // If DebugEpilogue returns |true|, we have to perform a forced
                 // return, e.g. return frame->returnValue() to the caller.
                 BaselineFrame *frame = iter.baselineFrame();
-                RootedScript script(cx);
                 jsbytecode *pc;
-                iter.baselineScriptAndPc(script.address(), &pc);
+                iter.baselineScriptAndPc(nullptr, &pc);
                 if (jit::DebugEpilogue(cx, frame, pc, false)) {
                     MOZ_ASSERT(frame->hasReturnValue());
                     rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
                     rfe->framePointer = iter.fp() - BaselineFrame::FramePointerOffset;
                     rfe->stackPointer = reinterpret_cast<uint8_t *>(frame);
                     return;
                 }
             }
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -783,17 +783,17 @@ DebugEpilogueOnBaselineReturn(JSContext 
 
 bool
 DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok)
 {
     // Unwind scope chain to stack depth 0.
     ScopeIter si(frame, pc, cx);
     UnwindAllScopes(cx, si);
     jsbytecode *unwindPc = frame->script()->main();
-    frame->setUnwoundScopeOverridePc(unwindPc);
+    frame->setOverridePc(unwindPc);
 
     // If Debugger::onLeaveFrame returns |true| we have to return the frame's
     // return value. If it returns |false|, the debugger threw an exception.
     // In both cases we have to pop debug scopes.
     ok = Debugger::onLeaveFrame(cx, frame, ok);
 
     if (frame->isNonEvalFunctionFrame()) {
         MOZ_ASSERT_IF(ok, frame->hasReturnValue());
@@ -814,19 +814,24 @@ DebugEpilogue(JSContext *cx, BaselineFra
 
     if (!ok) {
         // Pop this frame by updating jitTop, so that the exception handling
         // code will start at the previous frame.
 
         JitFrameLayout *prefix = frame->framePrefix();
         EnsureExitFrame(prefix);
         cx->mainThread().jitTop = (uint8_t *)prefix;
+        return false;
     }
 
-    return ok;
+    // Clear the override pc. This is not necessary for correctness: the frame
+    // will return immediately, but this simplifies the check we emit in debug
+    // builds after each callVM, to ensure this flag is not set.
+    frame->clearOverridePc();
+    return true;
 }
 
 JSObject *
 CreateGenerator(JSContext *cx, BaselineFrame *frame)
 {
     return GeneratorObject::create(cx, frame);
 }
 
@@ -907,21 +912,29 @@ DebugAfterYield(JSContext *cx, BaselineF
     // The BaselineFrame has just been constructed by JSOP_RESUME in the
     // caller. We need to set its debuggee flag as necessary.
     if (frame->script()->isDebuggee())
         frame->setIsDebuggee();
     return true;
 }
 
 bool
-GeneratorThrowOrClose(JSContext *cx, BaselineFrame *frame, HandleObject obj, HandleValue arg,
-                      uint32_t resumeKind)
+GeneratorThrowOrClose(JSContext *cx, BaselineFrame *frame, Handle<GeneratorObject*> genObj,
+                      HandleValue arg, uint32_t resumeKind)
 {
+    // Set the frame's pc to the current resume pc, so that frame iterators
+    // work. This function always returns false, so we're guaranteed to enter
+    // the exception handler where we will clear the pc.
+    JSScript *script = frame->script();
+    uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()];
+    frame->setOverridePc(script->offsetToPC(offset));
+
     MOZ_ALWAYS_TRUE(DebugAfterYield(cx, frame));
-    return js::GeneratorThrowOrClose(cx, obj, arg, resumeKind);
+    MOZ_ALWAYS_FALSE(js::GeneratorThrowOrClose(cx, genObj, arg, resumeKind));
+    return false;
 }
 
 bool
 StrictEvalPrologue(JSContext *cx, BaselineFrame *frame)
 {
     return frame->strictEvalPrologue(cx);
 }
 
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -12,16 +12,17 @@
 #include "jit/CompileInfo.h"
 #include "jit/JitFrames.h"
 
 namespace js {
 
 class DeclEnvObject;
 class StaticWithObject;
 class InlineTypedObject;
+class GeneratorObject;
 
 namespace jit {
 
 enum DataType {
     Type_Void,
     Type_Bool,
     Type_Int32,
     Type_Double,
@@ -308,16 +309,17 @@ template <> struct TypeToDataType<JSStri
 template <> struct TypeToDataType<JSFlatString *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<HandleObject> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleString> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandlePropertyName> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleFunction> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<NativeObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<InlineTypedObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<ArrayObject *> > { static const DataType result = Type_Handle; };
+template <> struct TypeToDataType<Handle<GeneratorObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<PlainObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<StaticWithObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<StaticBlockObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleScript> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleValue> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<MutableHandleValue> { static const DataType result = Type_Handle; };
 
 // Convert argument types to properties of the argument known by the jit.
@@ -344,16 +346,19 @@ template <> struct TypeToArgProperties<H
     static const uint32_t result = TypeToArgProperties<NativeObject *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<Handle<InlineTypedObject *> > {
     static const uint32_t result = TypeToArgProperties<InlineTypedObject *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<Handle<ArrayObject *> > {
     static const uint32_t result = TypeToArgProperties<ArrayObject *>::result | VMFunction::ByRef;
 };
+template <> struct TypeToArgProperties<Handle<GeneratorObject *> > {
+    static const uint32_t result = TypeToArgProperties<GeneratorObject *>::result | VMFunction::ByRef;
+};
 template <> struct TypeToArgProperties<Handle<PlainObject *> > {
     static const uint32_t result = TypeToArgProperties<PlainObject *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<Handle<StaticWithObject *> > {
     static const uint32_t result = TypeToArgProperties<StaticWithObject *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<Handle<StaticBlockObject *> > {
     static const uint32_t result = TypeToArgProperties<StaticBlockObject *>::result | VMFunction::ByRef;
@@ -418,16 +423,19 @@ template <> struct TypeToRootType<Handle
     static const uint32_t result = VMFunction::RootObject;
 };
 template <> struct TypeToRootType<Handle<InlineTypedObject *> > {
     static const uint32_t result = VMFunction::RootObject;
 };
 template <> struct TypeToRootType<Handle<ArrayObject *> > {
     static const uint32_t result = VMFunction::RootObject;
 };
+template <> struct TypeToRootType<Handle<GeneratorObject *> > {
+    static const uint32_t result = VMFunction::RootObject;
+};
 template <> struct TypeToRootType<Handle<PlainObject *> > {
     static const uint32_t result = VMFunction::RootObject;
 };
 template <> struct TypeToRootType<Handle<StaticBlockObject *> > {
     static const uint32_t result = VMFunction::RootObject;
 };
 template <> struct TypeToRootType<Handle<StaticWithObject *> > {
     static const uint32_t result = VMFunction::RootCell;
@@ -734,18 +742,18 @@ bool DebugEpilogueOnBaselineReturn(JSCon
 
 JSObject *CreateGenerator(JSContext *cx, BaselineFrame *frame);
 bool NormalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc,
                    uint32_t stackDepth);
 bool FinalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc);
 bool InterpretResume(JSContext *cx, HandleObject obj, HandleValue val, HandlePropertyName kind,
                      MutableHandleValue rval);
 bool DebugAfterYield(JSContext *cx, BaselineFrame *frame);
-bool GeneratorThrowOrClose(JSContext *cx, BaselineFrame *frame, HandleObject obj, HandleValue arg,
-                           uint32_t resumeKind);
+bool GeneratorThrowOrClose(JSContext *cx, BaselineFrame *frame, Handle<GeneratorObject*> genObj,
+                           HandleValue arg, uint32_t resumeKind);
 
 bool StrictEvalPrologue(JSContext *cx, BaselineFrame *frame);
 bool HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame);
 
 bool NewArgumentsObject(JSContext *cx, BaselineFrame *frame, MutableHandleValue res);
 
 JSObject *InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleObject templateObj,
                             HandleObject res);
--- a/js/src/jit/shared/BaselineCompiler-shared.cpp
+++ b/js/src/jit/shared/BaselineCompiler-shared.cpp
@@ -44,16 +44,27 @@ BaselineCompilerShared::callVM(const VMF
         return false;
 
 #ifdef DEBUG
     // Assert prepareVMCall() has been called.
     MOZ_ASSERT(inCall_);
     inCall_ = false;
 #endif
 
+#ifdef DEBUG
+    // Assert the frame does not have an override pc when we're executing JIT code.
+    {
+        Label ok;
+        masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
+                          Imm32(BaselineFrame::HAS_OVERRIDE_PC), &ok);
+        masm.assumeUnreachable("BaselineFrame shouldn't override pc when executing JIT code");
+        masm.bind(&ok);
+    }
+#endif
+
     // Compute argument size. Note that this include the size of the frame pointer
     // pushed by prepareVMCall.
     uint32_t argSize = fun.explicitStackSlots() * sizeof(void *) + sizeof(void *);
 
     // Assert all arguments were pushed.
     MOZ_ASSERT(masm.framePushed() - pushedBeforeCall_ == argSize);
 
     Address frameSizeAddress(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize());
@@ -94,15 +105,26 @@ BaselineCompilerShared::callVM(const VMF
         masm.push(BaselineTailCallReg);
     }
     MOZ_ASSERT(fun.expectTailCall == NonTailCall);
     // Perform the call.
     masm.call(code);
     uint32_t callOffset = masm.currentOffset();
     masm.pop(BaselineFrameReg);
 
+#ifdef DEBUG
+    // Assert the frame does not have an override pc when we're executing JIT code.
+    {
+        Label ok;
+        masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
+                          Imm32(BaselineFrame::HAS_OVERRIDE_PC), &ok);
+        masm.assumeUnreachable("BaselineFrame shouldn't override pc after VM call");
+        masm.bind(&ok);
+    }
+#endif
+
     // Add a fake ICEntry (without stubs), so that the return offset to
     // pc mapping works.
     ICEntry entry(script->pcToOffset(pc), ICEntry::Kind_CallVM);
     entry.setReturnOffset(CodeOffsetLabel(callOffset));
 
     return icEntries_.append(entry);
 }
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -100,19 +100,19 @@ GeneratorObject::finalSuspend(JSContext 
 
     if (genObj->is<LegacyGeneratorObject>() && !closing)
         return ThrowStopIteration(cx);
 
     return true;
 }
 
 bool
-js::GeneratorThrowOrClose(JSContext *cx, HandleObject obj, HandleValue arg, uint32_t resumeKind)
+js::GeneratorThrowOrClose(JSContext *cx, Handle<GeneratorObject*> genObj, HandleValue arg,
+                          uint32_t resumeKind)
 {
-    GeneratorObject *genObj = &obj->as<GeneratorObject>();
     if (resumeKind == GeneratorObject::THROW) {
         cx->setPendingException(arg);
         genObj->setRunning();
     } else {
         MOZ_ASSERT(resumeKind == GeneratorObject::CLOSE);
         MOZ_ASSERT(genObj->is<LegacyGeneratorObject>());
         cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
         genObj->setClosing();
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -203,17 +203,18 @@ class LegacyGeneratorObject : public Gen
 };
 
 class StarGeneratorObject : public GeneratorObject
 {
   public:
     static const Class class_;
 };
 
-bool GeneratorThrowOrClose(JSContext *cx, HandleObject obj, HandleValue val, uint32_t resumeKind);
+bool GeneratorThrowOrClose(JSContext *cx, Handle<GeneratorObject*> obj, HandleValue val,
+                           uint32_t resumeKind);
 
 } // namespace js
 
 template<>
 inline bool
 JSObject::is<js::GeneratorObject>() const
 {
     return is<js::LegacyGeneratorObject>() || is<js::StarGeneratorObject>();