Bug 831585 - Refactor and simplify for-in bytecode and remove cx->iterValue. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 17 Sep 2014 21:07:37 +0200
changeset 205902 26d86ab7c4f35da9e7db86c1e0e7cf2203f4bbd6
parent 205901 c0021e5609b2fd656b02f2711652ddcece27bc91
child 205903 c9115053bdc6c7cdfd25daf174320f674637c357
push id27507
push userryanvm@gmail.com
push dateThu, 18 Sep 2014 02:16:54 +0000
treeherdermozilla-central@488d490da742 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs831585
milestone35.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 831585 - Refactor and simplify for-in bytecode and remove cx->iterValue. r=bhackett
js/src/frontend/BytecodeEmitter.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineInspector.cpp
js/src/jit/BaselineInspector.h
js/src/jit/BytecodeAnalysis.cpp
js/src/jit/BytecodeAnalysis.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.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/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/RangeAnalysis.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsiter.cpp
js/src/jsiter.h
js/src/vm/Debugger.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
js/src/vm/Xdr.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -599,16 +599,18 @@ NonLocalExitScope::prepareForNonLocalJum
                 return false;
             break;
 
           case STMT_FOR_OF_LOOP:
             npops += 2;
             break;
 
           case STMT_FOR_IN_LOOP:
+            /* The iterator and the current value are on the stack. */
+            npops += 1;
             FLUSH_POPS();
             if (!PopIterator(cx, bce))
                 return false;
             break;
 
           case STMT_SPREAD:
             MOZ_ASSERT_UNREACHABLE("can't break/continue/return from inside a spread");
             break;
@@ -713,20 +715,18 @@ PushLoopStatement(BytecodeEmitter *bce, 
     }
 
     stmt->stackDepth = bce->stackDepth;
     stmt->loopDepth = downLoop ? downLoop->loopDepth + 1 : 1;
 
     int loopSlots;
     if (type == STMT_SPREAD)
         loopSlots = 3;
-    else if (type == STMT_FOR_OF_LOOP)
+    else if (type == STMT_FOR_IN_LOOP || type == STMT_FOR_OF_LOOP)
         loopSlots = 2;
-    else if (type == STMT_FOR_IN_LOOP)
-        loopSlots = 1;
     else
         loopSlots = 0;
 
     MOZ_ASSERT(loopSlots <= stmt->stackDepth);
 
     if (downLoop)
         stmt->canIonOsr = (downLoop->canIonOsr &&
                            stmt->stackDepth == downLoop->stackDepth + loopSlots);
@@ -4878,16 +4878,21 @@ EmitForIn(ExclusiveContext *cx, Bytecode
      * Emit a bytecode to convert top of stack value to the iterator
      * object depending on the loop variant (for-in, for-each-in, or
      * destructuring for-in).
      */
     JS_ASSERT(pn->isOp(JSOP_ITER));
     if (Emit2(cx, bce, JSOP_ITER, (uint8_t) pn->pn_iflags) < 0)
         return false;
 
+    // For-in loops have both the iterator and the value on the stack. Push
+    // undefined to balance the stack.
+    if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
+        return false;
+
     // Enter the block before the loop body, after evaluating the obj.
     // Initialize let bindings with undefined when entering, as the name
     // assigned to is a plain assignment.
     StmtInfoBCE letStmt(cx);
     if (letDecl) {
         if (!EnterBlockScope(cx, bce, &letStmt, pn1->pn_objbox, JSOP_UNDEFINED, 0))
             return false;
     }
@@ -4912,28 +4917,21 @@ EmitForIn(ExclusiveContext *cx, Bytecode
     SET_STATEMENT_TOP(&stmtInfo, top);
     if (EmitLoopHead(cx, bce, nullptr) < 0)
         return false;
 
 #ifdef DEBUG
     int loopDepth = bce->stackDepth;
 #endif
 
-    /*
-     * Emit code to get the next enumeration value and assign it to the
-     * left hand side.
-     */
-    if (Emit1(cx, bce, JSOP_ITERNEXT) < 0)
-        return false;
+    // Emit code to assign the enumeration value to the left hand side, but
+    // also leave it on the stack.
     if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, nullptr))
         return false;
 
-    if (Emit1(cx, bce, JSOP_POP) < 0)
-        return false;
-
     /* The stack should be balanced around the assignment opcode sequence. */
     JS_ASSERT(bce->stackDepth == loopDepth);
 
     /* Emit code for the loop body. */
     if (!EmitTree(cx, bce, forBody))
         return false;
 
     /* Set loop and enclosing "update" offsets, for continue. */
@@ -4943,30 +4941,38 @@ EmitForIn(ExclusiveContext *cx, Bytecode
     } while ((stmt = stmt->down) != nullptr && stmt->type == STMT_LABEL);
 
     /*
      * Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
      */
     SetJumpOffsetAt(bce, jmp);
     if (!EmitLoopEntry(cx, bce, nullptr))
         return false;
+    if (Emit1(cx, bce, JSOP_POP) < 0)
+        return false;
     if (Emit1(cx, bce, JSOP_MOREITER) < 0)
         return false;
-    ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
+    if (Emit1(cx, bce, JSOP_ISNOITER) < 0)
+        return false;
+    ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, top - bce->offset());
     if (beq < 0)
         return false;
 
     /* Set the srcnote offset so we can find the closing jump. */
     if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, beq - jmp))
         return false;
 
     // Fix up breaks and continues.
     if (!PopStatementBCE(cx, bce))
         return false;
 
+    // Pop the enumeration value.
+    if (Emit1(cx, bce, JSOP_POP) < 0)
+        return false;
+
     if (!bce->tryNoteList.append(JSTRY_ITER, bce->stackDepth, top, bce->offset()))
         return false;
     if (Emit1(cx, bce, JSOP_ENDITER) < 0)
         return false;
 
     if (letDecl) {
         if (!LeaveNestedScope(cx, bce, &letStmt))
             return false;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -3087,26 +3087,31 @@ BaselineCompiler::emit_JSOP_MOREITER()
     if (!emitOpIC(compiler.getStub(&stubSpace_)))
         return false;
 
     frame.push(R0);
     return true;
 }
 
 bool
-BaselineCompiler::emit_JSOP_ITERNEXT()
+BaselineCompiler::emit_JSOP_ISNOITER()
 {
     frame.syncStack(0);
-    masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
-
-    ICIteratorNext_Fallback::Compiler compiler(cx);
-    if (!emitOpIC(compiler.getStub(&stubSpace_)))
-        return false;
-
-    frame.push(R0);
+
+    Label isMagic, done;
+    masm.branchTestMagic(Assembler::Equal, frame.addressOfStackValue(frame.peek(-1)),
+                         &isMagic);
+    masm.moveValue(BooleanValue(false), R0);
+    masm.jump(&done);
+
+    masm.bind(&isMagic);
+    masm.moveValue(BooleanValue(true), R0);
+
+    masm.bind(&done);
+    frame.push(R0, JSVAL_TYPE_BOOLEAN);
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_ENDITER()
 {
     frame.popRegsAndSync(1);
 
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -165,17 +165,17 @@ namespace jit {
     _(JSOP_DEBUGGER)           \
     _(JSOP_ARGUMENTS)          \
     _(JSOP_RUNONCE)            \
     _(JSOP_REST)               \
     _(JSOP_TOID)               \
     _(JSOP_TABLESWITCH)        \
     _(JSOP_ITER)               \
     _(JSOP_MOREITER)           \
-    _(JSOP_ITERNEXT)           \
+    _(JSOP_ISNOITER)           \
     _(JSOP_ENDITER)            \
     _(JSOP_CALLEE)             \
     _(JSOP_SETRVAL)            \
     _(JSOP_RETRVAL)            \
     _(JSOP_RETURN)
 
 class BaselineCompiler : public BaselineCompilerSpecific
 {
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -9839,25 +9839,26 @@ static bool
 DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallback *stub_,
                        HandleObject iterObj, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICIteratorMore_Fallback *> stub(frame, stub_);
 
     FallbackICSpew(cx, stub, "IteratorMore");
 
-    bool cond;
-    if (!IteratorMore(cx, iterObj, &cond))
-        return false;
-    res.setBoolean(cond);
+    if (!IteratorMore(cx, iterObj, res))
+        return false;
 
     // Check if debug mode toggling made the stub invalid.
     if (stub.invalid())
         return true;
 
+    if (!res.isMagic(JS_NO_ITER_VALUE) && !res.isString())
+        stub->setHasNonStringResult();
+
     if (iterObj->is<PropertyIteratorObject>() &&
         !stub->hasStub(ICStub::IteratorMore_Native))
     {
         ICIteratorMore_Native::Compiler compiler(cx);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
         if (!newStub)
             return false;
         stub->addNewStub(newStub);
@@ -9901,117 +9902,37 @@ ICIteratorMore_Native::Compiler::generat
 
     masm.branchTestObjClass(Assembler::NotEqual, obj, scratch,
                             &PropertyIteratorObject::class_, &failure);
     masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, nativeIterator);
 
     masm.branchTest32(Assembler::NonZero, Address(nativeIterator, offsetof(NativeIterator, flags)),
                       Imm32(JSITER_FOREACH), &failure);
 
-    // Set output to true if props_cursor < props_end.
-    masm.loadPtr(Address(nativeIterator, offsetof(NativeIterator, props_end)), scratch);
-    Address cursorAddr = Address(nativeIterator, offsetof(NativeIterator, props_cursor));
-    masm.cmpPtrSet(Assembler::LessThan, cursorAddr, scratch, scratch);
-
-    masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch, R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-//
-// IteratorNext_Fallback
-//
-
-static bool
-DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallback *stub_,
-                       HandleValue iterValue, MutableHandleValue res)
-{
-    // This fallback stub may trigger debug mode toggling.
-    DebugModeOSRVolatileStub<ICIteratorNext_Fallback *> stub(frame, stub_);
-
-    FallbackICSpew(cx, stub, "IteratorNext");
-
-    RootedObject iteratorObject(cx, &iterValue.toObject());
-    if (!IteratorNext(cx, iteratorObject, res))
-        return false;
-
-    // Check if debug mode toggling made the stub invalid.
-    if (stub.invalid())
-        return true;
-
-    if (!res.isString() && !stub->hasNonStringResult())
-        stub->setHasNonStringResult();
-
-    if (iteratorObject->is<PropertyIteratorObject>() &&
-        !stub->hasStub(ICStub::IteratorNext_Native))
-    {
-        ICIteratorNext_Native::Compiler compiler(cx);
-        ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
-        if (!newStub)
-            return false;
-        stub->addNewStub(newStub);
-    }
-
-    return true;
-}
-
-typedef bool (*DoIteratorNextFallbackFn)(JSContext *, BaselineFrame *, ICIteratorNext_Fallback *,
-                                         HandleValue, MutableHandleValue);
-static const VMFunction DoIteratorNextFallbackInfo =
-    FunctionInfo<DoIteratorNextFallbackFn>(DoIteratorNextFallback);
-
-bool
-ICIteratorNext_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
-{
-    EmitRestoreTailCallReg(masm);
-
-    masm.pushValue(R0);
-    masm.push(BaselineStubReg);
-    masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
-
-    return tailCallVM(DoIteratorNextFallbackInfo, masm);
-}
-
-//
-// IteratorNext_Native
-//
-
-bool
-ICIteratorNext_Native::Compiler::generateStubCode(MacroAssembler &masm)
-{
-    Label failure;
-
-    Register obj = masm.extractObject(R0, ExtractTemp0);
-
-    GeneralRegisterSet regs(availableGeneralRegs(1));
-    Register nativeIterator = regs.takeAny();
-    Register scratch = regs.takeAny();
-
-    masm.branchTestObjClass(Assembler::NotEqual, obj, scratch,
-                            &PropertyIteratorObject::class_, &failure);
-    masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, nativeIterator);
-
-    masm.branchTest32(Assembler::NonZero, Address(nativeIterator, offsetof(NativeIterator, flags)),
-                      Imm32(JSITER_FOREACH), &failure);
-
-    // Get cursor, next string.
-    masm.loadPtr(Address(nativeIterator, offsetof(NativeIterator, props_cursor)), scratch);
+    // If props_cursor < props_end, load the next string and advance the cursor.
+    // Else, return MagicValue(JS_NO_ITER_VALUE).
+    Label iterDone;
+    Address cursorAddr(nativeIterator, offsetof(NativeIterator, props_cursor));
+    Address cursorEndAddr(nativeIterator, offsetof(NativeIterator, props_end));
+    masm.loadPtr(cursorAddr, scratch);
+    masm.branchPtr(Assembler::BelowOrEqual, cursorEndAddr, scratch, &iterDone);
+
+    // Get next string.
     masm.loadPtr(Address(scratch, 0), scratch);
 
     // Increase the cursor.
-    masm.addPtr(Imm32(sizeof(JSString *)),
-                Address(nativeIterator, offsetof(NativeIterator, props_cursor)));
+    masm.addPtr(Imm32(sizeof(JSString *)), cursorAddr);
 
     masm.tagValue(JSVAL_TYPE_STRING, scratch, R0);
     EmitReturnFromIC(masm);
 
+    masm.bind(&iterDone);
+    masm.moveValue(MagicValue(JS_NO_ITER_VALUE), R0);
+    EmitReturnFromIC(masm);
+
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 //
 // IteratorClose_Fallback
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -433,18 +433,16 @@ class ICEntry
     _(SetProp_CallScripted)     \
     _(SetProp_CallNative)       \
                                 \
     _(TableSwitch)              \
                                 \
     _(IteratorNew_Fallback)     \
     _(IteratorMore_Fallback)    \
     _(IteratorMore_Native)      \
-    _(IteratorNext_Fallback)    \
-    _(IteratorNext_Native)      \
     _(IteratorClose_Fallback)   \
                                 \
     _(InstanceOf_Fallback)      \
                                 \
     _(TypeOf_Fallback)          \
     _(TypeOf_Typed)             \
                                 \
     _(Rest_Fallback)            \
@@ -6133,16 +6131,24 @@ class ICIteratorMore_Fallback : public I
 
   public:
     static inline ICIteratorMore_Fallback *New(ICStubSpace *space, JitCode *code) {
         if (!code)
             return nullptr;
         return space->allocate<ICIteratorMore_Fallback>(code);
     }
 
+    void setHasNonStringResult() {
+        extra_ = 1;
+    }
+    bool hasNonStringResult() const {
+        MOZ_ASSERT(extra_ <= 1);
+        return extra_;
+    }
+
     class Compiler : public ICStubCompiler {
       protected:
         bool generateStubCode(MacroAssembler &masm);
 
       public:
         explicit Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::IteratorMore_Fallback)
         { }
@@ -6179,86 +6185,16 @@ class ICIteratorMore_Native : public ICS
         { }
 
         ICStub *getStub(ICStubSpace *space) {
             return ICIteratorMore_Native::New(space, getStubCode());
         }
     };
 };
 
-// IC for getting the next value in an iterator.
-class ICIteratorNext_Fallback : public ICFallbackStub
-{
-    friend class ICStubSpace;
-
-    explicit ICIteratorNext_Fallback(JitCode *stubCode)
-      : ICFallbackStub(ICStub::IteratorNext_Fallback, stubCode)
-    { }
-
-  public:
-    static inline ICIteratorNext_Fallback *New(ICStubSpace *space, JitCode *code) {
-        if (!code)
-            return nullptr;
-        return space->allocate<ICIteratorNext_Fallback>(code);
-    }
-
-    void setHasNonStringResult() {
-        JS_ASSERT(extra_ == 0);
-        extra_ = 1;
-    }
-    bool hasNonStringResult() const {
-        return extra_;
-    }
-
-    class Compiler : public ICStubCompiler {
-      protected:
-        bool generateStubCode(MacroAssembler &masm);
-
-      public:
-        explicit Compiler(JSContext *cx)
-          : ICStubCompiler(cx, ICStub::IteratorNext_Fallback)
-        { }
-
-        ICStub *getStub(ICStubSpace *space) {
-            return ICIteratorNext_Fallback::New(space, getStubCode());
-        }
-    };
-};
-
-// IC for getting the next value in a native iterator.
-class ICIteratorNext_Native : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICIteratorNext_Native(JitCode *stubCode)
-      : ICStub(ICStub::IteratorNext_Native, stubCode)
-    { }
-
-  public:
-    static inline ICIteratorNext_Native *New(ICStubSpace *space, JitCode *code) {
-        if (!code)
-            return nullptr;
-        return space->allocate<ICIteratorNext_Native>(code);
-    }
-
-    class Compiler : public ICStubCompiler {
-      protected:
-        bool generateStubCode(MacroAssembler &masm);
-
-      public:
-        explicit Compiler(JSContext *cx)
-          : ICStubCompiler(cx, ICStub::IteratorNext_Native)
-        { }
-
-        ICStub *getStub(ICStubSpace *space) {
-            return ICIteratorNext_Native::New(space, getStubCode());
-        }
-    };
-};
-
 // IC for closing an iterator.
 class ICIteratorClose_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
     explicit ICIteratorClose_Fallback(JitCode *stubCode)
       : ICFallbackStub(ICStub::IteratorClose_Fallback, stubCode)
     { }
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -376,27 +376,27 @@ BaselineInspector::hasSeenAccessedGetter
     ICStub *stub = entry.fallbackStub();
 
     if (stub->isGetProp_Fallback())
         return stub->toGetProp_Fallback()->hasAccessedGetter();
     return false;
 }
 
 bool
-BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc)
+BaselineInspector::hasSeenNonStringIterMore(jsbytecode *pc)
 {
-    JS_ASSERT(JSOp(*pc) == JSOP_ITERNEXT);
+    JS_ASSERT(JSOp(*pc) == JSOP_MOREITER);
 
     if (!hasBaselineScript())
         return false;
 
     const ICEntry &entry = icEntryFromPC(pc);
     ICStub *stub = entry.fallbackStub();
 
-    return stub->toIteratorNext_Fallback()->hasNonStringResult();
+    return stub->toIteratorMore_Fallback()->hasNonStringResult();
 }
 
 bool
 BaselineInspector::hasSeenDoubleResult(jsbytecode *pc)
 {
     if (!hasBaselineScript())
         return false;
 
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -102,17 +102,17 @@ class BaselineInspector
     MIRType expectedResultType(jsbytecode *pc);
     MCompare::CompareType expectedCompareType(jsbytecode *pc);
     MIRType expectedBinaryArithSpecialization(jsbytecode *pc);
 
     bool hasSeenNonNativeGetElement(jsbytecode *pc);
     bool hasSeenNegativeIndexGetElement(jsbytecode *pc);
     bool hasSeenAccessedGetter(jsbytecode *pc);
     bool hasSeenDoubleResult(jsbytecode *pc);
-    bool hasSeenNonStringIterNext(jsbytecode *pc);
+    bool hasSeenNonStringIterMore(jsbytecode *pc);
 
     JSObject *getTemplateObject(jsbytecode *pc);
     JSObject *getTemplateObjectForNative(jsbytecode *pc, Native native);
 
     DeclEnvObject *templateDeclEnvObject();
     CallObject *templateCallObject();
 
     JSObject *commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter);
--- a/js/src/jit/BytecodeAnalysis.cpp
+++ b/js/src/jit/BytecodeAnalysis.cpp
@@ -48,28 +48,29 @@ BytecodeAnalysis::init(TempAllocator &al
     jsbytecode *end = script_->codeEnd();
 
     // Clear all BytecodeInfo.
     mozilla::PodZero(infos_.begin(), infos_.length());
     infos_[0].init(/*stackDepth=*/0);
 
     Vector<CatchFinallyRange, 0, IonAllocPolicy> catchFinallyRanges(alloc);
 
-    for (jsbytecode *pc = script_->code(); pc < end; pc += GetBytecodeLength(pc)) {
+    jsbytecode *nextpc;
+    for (jsbytecode *pc = script_->code(); pc < end; pc = nextpc) {
         JSOp op = JSOp(*pc);
+        nextpc = pc + GetBytecodeLength(pc);
         unsigned offset = script_->pcToOffset(pc);
 
         JitSpew(JitSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s",
                 int(script_->pcToOffset(pc)), int(script_->length()), js_CodeName[op]);
 
         // If this bytecode info has not yet been initialized, it's not reachable.
         if (!infos_[offset].initialized)
             continue;
 
-
         unsigned stackDepth = infos_[offset].stackDepth;
 #ifdef DEBUG
         for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++)
             JS_ASSERT(!infos_[script_->pcToOffset(chkpc)].initialized);
 #endif
 
         unsigned nuses = GetUseCount(script_, offset);
         unsigned ndefs = GetDefCount(script_, offset);
@@ -185,32 +186,27 @@ BytecodeAnalysis::init(TempAllocator &al
 
             // If this is a a backedge to an un-analyzed segment, analyze from there.
             bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized;
 
             infos_[targetOffset].init(newStackDepth);
             infos_[targetOffset].jumpTarget = true;
 
             if (jumpBack)
-                pc = script_->offsetToPC(targetOffset);
+                nextpc = script_->offsetToPC(targetOffset);
         }
 
         // Handle any fallthrough from this opcode.
         if (BytecodeFallsThrough(op)) {
-            jsbytecode *nextpc = pc + GetBytecodeLength(pc);
-            JS_ASSERT(nextpc < end);
-            unsigned nextOffset = script_->pcToOffset(nextpc);
+            jsbytecode *fallthrough = pc + GetBytecodeLength(pc);
+            JS_ASSERT(fallthrough < end);
+            unsigned fallthroughOffset = script_->pcToOffset(fallthrough);
 
-            infos_[nextOffset].init(stackDepth);
-
-            if (jump)
-                infos_[nextOffset].jumpFallthrough = true;
+            infos_[fallthroughOffset].init(stackDepth);
 
             // Treat the fallthrough of a branch instruction as a jump target.
             if (jump)
-                infos_[nextOffset].jumpTarget = true;
-            else
-                infos_[nextOffset].fallthrough = true;
+                infos_[fallthroughOffset].jumpTarget = true;
         }
     }
 
     return true;
 }
--- a/js/src/jit/BytecodeAnalysis.h
+++ b/js/src/jit/BytecodeAnalysis.h
@@ -16,18 +16,16 @@ namespace jit {
 
 // Basic information about bytecodes in the script.  Used to help baseline compilation.
 struct BytecodeInfo
 {
     static const uint16_t MAX_STACK_DEPTH = 0xffffU;
     uint16_t stackDepth;
     bool initialized : 1;
     bool jumpTarget : 1;
-    bool jumpFallthrough : 1;
-    bool fallthrough : 1;
 
     // If true, this is a JSOP_LOOPENTRY op inside a catch or finally block.
     bool loopEntryInCatchOrFinally : 1;
 
     void init(unsigned depth) {
         JS_ASSERT(depth <= MAX_STACK_DEPTH);
         JS_ASSERT_IF(initialized, stackDepth == depth);
         initialized = true;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6364,76 +6364,74 @@ LoadNativeIterator(MacroAssembler &masm,
 
     // Test class.
     masm.branchTestObjClass(Assembler::NotEqual, obj, dest, &PropertyIteratorObject::class_, failures);
 
     // Load NativeIterator object.
     masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, dest);
 }
 
-typedef bool (*IteratorNextFn)(JSContext *, HandleObject, MutableHandleValue);
-static const VMFunction IteratorNextInfo = FunctionInfo<IteratorNextFn>(IteratorNext);
-
-bool
-CodeGenerator::visitIteratorNext(LIteratorNext *lir)
-{
-    const Register obj = ToRegister(lir->object());
-    const Register temp = ToRegister(lir->temp());
-    const ValueOperand output = ToOutValue(lir);
-
-    OutOfLineCode *ool = oolCallVM(IteratorNextInfo, lir, (ArgList(), obj), StoreValueTo(output));
-    if (!ool)
-        return false;
-
-    LoadNativeIterator(masm, obj, temp, ool->entry());
-
-    masm.branchTest32(Assembler::NonZero, Address(temp, offsetof(NativeIterator, flags)),
-                      Imm32(JSITER_FOREACH), ool->entry());
-
-    // Get cursor, next string.
-    masm.loadPtr(Address(temp, offsetof(NativeIterator, props_cursor)), output.scratchReg());
-    masm.loadPtr(Address(output.scratchReg(), 0), output.scratchReg());
-    masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
-
-    // Increase the cursor.
-    masm.addPtr(Imm32(sizeof(JSString *)), Address(temp, offsetof(NativeIterator, props_cursor)));
-
-    masm.bind(ool->rejoin());
-    return true;
-}
-
-typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, bool *);
+typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, MutableHandleValue);
 static const VMFunction IteratorMoreInfo = FunctionInfo<IteratorMoreFn>(IteratorMore);
 
 bool
 CodeGenerator::visitIteratorMore(LIteratorMore *lir)
 {
     const Register obj = ToRegister(lir->object());
-    const Register output = ToRegister(lir->output());
+    const ValueOperand output = ToOutValue(lir);
     const Register temp = ToRegister(lir->temp());
 
-    OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir,
-                                   (ArgList(), obj), StoreRegisterTo(output));
+    OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir, (ArgList(), obj), StoreValueTo(output));
     if (!ool)
         return false;
 
-    LoadNativeIterator(masm, obj, output, ool->entry());
-
-    masm.branchTest32(Assembler::NonZero, Address(output, offsetof(NativeIterator, flags)),
+    Register outputScratch = output.scratchReg();
+    LoadNativeIterator(masm, obj, outputScratch, ool->entry());
+
+    masm.branchTest32(Assembler::NonZero, Address(outputScratch, offsetof(NativeIterator, flags)),
                       Imm32(JSITER_FOREACH), ool->entry());
 
-    // Set output to true if props_cursor < props_end.
-    masm.loadPtr(Address(output, offsetof(NativeIterator, props_end)), temp);
-    masm.cmpPtrSet(Assembler::LessThan, Address(output, offsetof(NativeIterator, props_cursor)),
-                   temp, output);
+    // If props_cursor < props_end, load the next string and advance the cursor.
+    // Else, return MagicValue(JS_NO_ITER_VALUE).
+    Label iterDone;
+    Address cursorAddr(outputScratch, offsetof(NativeIterator, props_cursor));
+    Address cursorEndAddr(outputScratch, offsetof(NativeIterator, props_end));
+    masm.loadPtr(cursorAddr, temp);
+    masm.branchPtr(Assembler::BelowOrEqual, cursorEndAddr, temp, &iterDone);
+
+    // Get next string.
+    masm.loadPtr(Address(temp, 0), temp);
+
+    // Increase the cursor.
+    masm.addPtr(Imm32(sizeof(JSString *)), cursorAddr);
+
+    masm.tagValue(JSVAL_TYPE_STRING, temp, output);
+    masm.jump(ool->rejoin());
+
+    masm.bind(&iterDone);
+    masm.moveValue(MagicValue(JS_NO_ITER_VALUE), output);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
+bool
+CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch *lir)
+{
+    ValueOperand input = ToValue(lir, LIsNoIterAndBranch::Input);
+    Label *ifTrue = getJumpLabelForBranch(lir->ifTrue());
+    Label *ifFalse = getJumpLabelForBranch(lir->ifFalse());
+
+    masm.branchTestMagic(Assembler::Equal, input, ifTrue);
+
+    if (!isNextBlock(lir->ifFalse()->lir()))
+        masm.jump(ifFalse);
+    return true;
+}
+
 typedef bool (*CloseIteratorFn)(JSContext *, HandleObject);
 static const VMFunction CloseIteratorInfo = FunctionInfo<CloseIteratorFn>(CloseIterator);
 
 bool
 CodeGenerator::visitIteratorEnd(LIteratorEnd *lir)
 {
     const Register obj = ToRegister(lir->object());
     const Register temp1 = ToRegister(lir->temp1());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -260,18 +260,18 @@ class CodeGenerator : public CodeGenerat
     bool visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir);
     bool visitStoreTypedArrayElement(LStoreTypedArrayElement *lir);
     bool visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir);
     bool visitClampIToUint8(LClampIToUint8 *lir);
     bool visitClampDToUint8(LClampDToUint8 *lir);
     bool visitClampVToUint8(LClampVToUint8 *lir);
     bool visitCallIteratorStart(LCallIteratorStart *lir);
     bool visitIteratorStart(LIteratorStart *lir);
-    bool visitIteratorNext(LIteratorNext *lir);
     bool visitIteratorMore(LIteratorMore *lir);
+    bool visitIsNoIterAndBranch(LIsNoIterAndBranch *lir);
     bool visitIteratorEnd(LIteratorEnd *lir);
     bool visitArgumentsLength(LArgumentsLength *lir);
     bool visitGetFrameArgument(LGetFrameArgument *lir);
     bool visitSetFrameArgumentT(LSetFrameArgumentT *lir);
     bool visitSetFrameArgumentC(LSetFrameArgumentC *lir);
     bool visitSetFrameArgumentV(LSetFrameArgumentV *lir);
     bool visitRunOncePrologue(LRunOncePrologue *lir);
     bool emitRest(LInstruction *lir, Register array, Register numActuals,
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -486,16 +486,18 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
         else if (*pc == JSOP_SETARG)
             slot = info().argSlotUnchecked(GET_ARGNO(pc));
         else
             continue;
         if (slot >= info().firstStackSlot())
             continue;
         if (!analysis().maybeInfo(pc))
             continue;
+        if (!last)
+            continue;
 
         MPhi *phi = entry->getSlot(slot)->toPhi();
 
         if (*last == JSOP_POS)
             last = earlier;
 
         if (js_CodeSpec[*last].format & JOF_TYPESET) {
             types::TemporaryTypeSet *typeSet = bytecodeTypes(last);
@@ -556,17 +558,16 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
                 type = MIRType_Boolean;
                 break;
               case JSOP_DOUBLE:
                 type = MIRType_Double;
                 break;
               case JSOP_STRING:
               case JSOP_TYPEOF:
               case JSOP_TYPEOFEXPR:
-              case JSOP_ITERNEXT:
                 type = MIRType_String;
                 break;
               case JSOP_ADD:
               case JSOP_SUB:
               case JSOP_MUL:
               case JSOP_DIV:
               case JSOP_MOD:
               case JSOP_NEG:
@@ -1778,22 +1779,22 @@ IonBuilder::inspectOpcode(JSOp op)
         return jsop_lambda(info().getFunction(pc));
 
       case JSOP_LAMBDA_ARROW:
         return jsop_lambda_arrow(info().getFunction(pc));
 
       case JSOP_ITER:
         return jsop_iter(GET_INT8(pc));
 
-      case JSOP_ITERNEXT:
-        return jsop_iternext();
-
       case JSOP_MOREITER:
         return jsop_itermore();
 
+      case JSOP_ISNOITER:
+        return jsop_isnoiter();
+
       case JSOP_ENDITER:
         return jsop_iterend();
 
       case JSOP_IN:
         return jsop_in();
 
       case JSOP_SETRVAL:
         JS_ASSERT(!script()->noScriptRval());
@@ -2253,16 +2254,33 @@ IonBuilder::processWhileCondEnd(CFGState
         test = newTest(ins, state.loop.successor, body);
     current->end(test);
 
     state.state = CFGState::WHILE_LOOP_BODY;
     state.stopAt = state.loop.bodyEnd;
     pc = state.loop.bodyStart;
     if (!setCurrentAndSpecializePhis(body))
         return ControlStatus_Error;
+
+    // If this is a for-in loop, unbox the current value as string if possible.
+    if (ins->isIsNoIter()) {
+        MIteratorMore *iterMore = ins->toIsNoIter()->input()->toIteratorMore();
+        jsbytecode *iterMorePc = iterMore->resumePoint()->pc();
+        MOZ_ASSERT(*iterMorePc == JSOP_MOREITER);
+
+        if (!nonStringIteration_ && !inspector->hasSeenNonStringIterMore(iterMorePc)) {
+            MDefinition *val = current->peek(-1);
+            MOZ_ASSERT(val == iterMore);
+            MInstruction *ins = MUnbox::New(alloc(), val, MIRType_String, MUnbox::Fallible,
+                                            Bailout_NonStringInputInvalidate);
+            current->add(ins);
+            current->rewriteAtDepth(-1, ins);
+        }
+    }
+
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processWhileBodyEnd(CFGState &state)
 {
     if (!processDeferredContinues(state))
         return ControlStatus_Error;
@@ -10449,50 +10467,41 @@ IonBuilder::jsop_iter(uint8_t flags)
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
-IonBuilder::jsop_iternext()
-{
-    MDefinition *iter = current->peek(-1);
-    MInstruction *ins = MIteratorNext::New(alloc(), iter);
-
-    current->add(ins);
-    current->push(ins);
-
-    if (!resumeAfter(ins))
-        return false;
-
-    if (!nonStringIteration_ && !inspector->hasSeenNonStringIterNext(pc)) {
-        ins = MUnbox::New(alloc(), ins, MIRType_String, MUnbox::Fallible,
-                          Bailout_NonStringInputInvalidate);
-        current->add(ins);
-        current->rewriteAtDepth(-1, ins);
-    }
-
-    return true;
-}
-
-bool
 IonBuilder::jsop_itermore()
 {
     MDefinition *iter = current->peek(-1);
     MInstruction *ins = MIteratorMore::New(alloc(), iter);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
+IonBuilder::jsop_isnoiter()
+{
+    MDefinition *def = current->peek(-1);
+    MOZ_ASSERT(def->isIteratorMore());
+
+    MInstruction *ins = MIsNoIter::New(alloc(), def);
+    current->add(ins);
+    current->push(ins);
+
+    return true;
+}
+
+bool
 IonBuilder::jsop_iterend()
 {
     MDefinition *iter = current->pop();
     MInstruction *ins = MIteratorEnd::New(alloc(), iter);
 
     current->add(ins);
 
     return resumeAfter(ins);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -646,18 +646,18 @@ class IonBuilder
     bool jsop_regexp(RegExpObject *reobj);
     bool jsop_object(JSObject *obj);
     bool jsop_lambda(JSFunction *fun);
     bool jsop_lambda_arrow(JSFunction *fun);
     bool jsop_this();
     bool jsop_typeof();
     bool jsop_toid();
     bool jsop_iter(uint8_t flags);
-    bool jsop_iternext();
     bool jsop_itermore();
+    bool jsop_isnoiter();
     bool jsop_iterend();
     bool jsop_in();
     bool jsop_in_dense();
     bool jsop_instanceof();
     bool jsop_getaliasedvar(ScopeCoordinate sc);
     bool jsop_setaliasedvar(ScopeCoordinate sc);
 
     /* Inlining. */
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -5566,37 +5566,17 @@ class LIteratorStart : public LInstructi
     const LDefinition *temp3() {
         return getTemp(2);
     }
     MIteratorStart *mir() const {
         return mir_->toIteratorStart();
     }
 };
 
-class LIteratorNext : public LInstructionHelper<BOX_PIECES, 1, 1>
-{
-  public:
-    LIR_HEADER(IteratorNext)
-
-    LIteratorNext(const LAllocation &iterator, const LDefinition &temp) {
-        setOperand(0, iterator);
-        setTemp(0, temp);
-    }
-    const LAllocation *object() {
-        return getOperand(0);
-    }
-    const LDefinition *temp() {
-        return getTemp(0);
-    }
-    MIteratorNext *mir() const {
-        return mir_->toIteratorNext();
-    }
-};
-
-class LIteratorMore : public LInstructionHelper<1, 1, 1>
+class LIteratorMore : public LInstructionHelper<BOX_PIECES, 1, 1>
 {
   public:
     LIR_HEADER(IteratorMore)
 
     LIteratorMore(const LAllocation &iterator, const LDefinition &temp) {
         setOperand(0, iterator);
         setTemp(0, temp);
     }
@@ -5606,16 +5586,36 @@ class LIteratorMore : public LInstructio
     const LDefinition *temp() {
         return getTemp(0);
     }
     MIteratorMore *mir() const {
         return mir_->toIteratorMore();
     }
 };
 
+class LIsNoIterAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(IsNoIterAndBranch)
+
+    LIsNoIterAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse) {
+        setSuccessor(0, ifTrue);
+        setSuccessor(1, ifFalse);
+    }
+
+    static const size_t Input = 0;
+
+    MBasicBlock *ifTrue() const {
+        return getSuccessor(0);
+    }
+    MBasicBlock *ifFalse() const {
+        return getSuccessor(1);
+    }
+};
+
 class LIteratorEnd : public LInstructionHelper<0, 1, 3>
 {
   public:
     LIR_HEADER(IteratorEnd)
 
     LIteratorEnd(const LAllocation &iterator, const LDefinition &temp1,
                  const LDefinition &temp2, const LDefinition &temp3) {
         setOperand(0, iterator);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -254,18 +254,18 @@
     _(SetPropertyCacheV)            \
     _(SetPropertyCacheT)            \
     _(SetElementCacheV)             \
     _(SetElementCacheT)             \
     _(SetPropertyPolymorphicV)      \
     _(SetPropertyPolymorphicT)      \
     _(CallIteratorStart)            \
     _(IteratorStart)                \
-    _(IteratorNext)                 \
     _(IteratorMore)                 \
+    _(IsNoIterAndBranch)            \
     _(IteratorEnd)                  \
     _(ArrayLength)                  \
     _(SetArrayLength)               \
     _(TypedArrayLength)             \
     _(TypedArrayElements)           \
     _(TypedObjectProto)             \
     _(TypedObjectElements)          \
     _(SetTypedObjectOffset)         \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -869,16 +869,28 @@ LIRGenerator::visitTest(MTest *test)
         MDefinition *lhs = opd->getOperand(0);
         MDefinition *rhs = opd->getOperand(1);
         if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
             ReorderCommutative(&lhs, &rhs, test);
             return lowerForBitAndAndBranch(new(alloc()) LBitAndAndBranch(ifTrue, ifFalse), test, lhs, rhs);
         }
     }
 
+    if (opd->isIsNoIter()) {
+        MOZ_ASSERT(opd->isEmittedAtUses());
+
+        MDefinition *input = opd->toIsNoIter()->input();
+        MOZ_ASSERT(input->type() == MIRType_Value);
+
+        LIsNoIterAndBranch *lir = new(alloc()) LIsNoIterAndBranch(ifTrue, ifFalse);
+        if (!useBox(lir, LIsNoIterAndBranch::Input, input))
+            return false;
+        return add(lir, test);
+    }
+
     if (opd->type() == MIRType_Double)
         return add(new(alloc()) LTestDAndBranch(useRegister(opd), ifTrue, ifFalse));
 
     if (opd->type() == MIRType_Float32)
         return add(new(alloc()) LTestFAndBranch(useRegister(opd), ifTrue, ifFalse));
 
     JS_ASSERT(opd->type() == MIRType_Int32 || opd->type() == MIRType_Boolean);
     return add(new(alloc()) LTestIAndBranch(useRegister(opd), ifTrue, ifFalse));
@@ -3327,27 +3339,27 @@ LIRGenerator::visitIteratorStart(MIterat
         return defineReturn(lir, ins) && assignSafepoint(lir, ins);
     }
 
     LIteratorStart *lir = new(alloc()) LIteratorStart(useRegister(ins->object()), temp(), temp(), temp());
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
-LIRGenerator::visitIteratorNext(MIteratorNext *ins)
+LIRGenerator::visitIteratorMore(MIteratorMore *ins)
 {
-    LIteratorNext *lir = new(alloc()) LIteratorNext(useRegister(ins->iterator()), temp());
+    LIteratorMore *lir = new(alloc()) LIteratorMore(useRegister(ins->iterator()), temp());
     return defineBox(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
-LIRGenerator::visitIteratorMore(MIteratorMore *ins)
+LIRGenerator::visitIsNoIter(MIsNoIter *ins)
 {
-    LIteratorMore *lir = new(alloc()) LIteratorMore(useRegister(ins->iterator()), temp());
-    return define(lir, ins) && assignSafepoint(lir, ins);
+    MOZ_ASSERT(ins->hasOneUse());
+    return emitAtUses(ins);
 }
 
 bool
 LIRGenerator::visitIteratorEnd(MIteratorEnd *ins)
 {
     LIteratorEnd *lir = new(alloc()) LIteratorEnd(useRegister(ins->iterator()), temp(), temp(), temp());
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -231,18 +231,18 @@ class LIRGenerator : public LIRGenerator
     bool visitCallsiteCloneCache(MCallsiteCloneCache *ins);
     bool visitCallGetElement(MCallGetElement *ins);
     bool visitCallSetElement(MCallSetElement *ins);
     bool visitCallInitElementArray(MCallInitElementArray *ins);
     bool visitSetPropertyCache(MSetPropertyCache *ins);
     bool visitSetElementCache(MSetElementCache *ins);
     bool visitCallSetProperty(MCallSetProperty *ins);
     bool visitIteratorStart(MIteratorStart *ins);
-    bool visitIteratorNext(MIteratorNext *ins);
     bool visitIteratorMore(MIteratorMore *ins);
+    bool visitIsNoIter(MIsNoIter *ins);
     bool visitIteratorEnd(MIteratorEnd *ins);
     bool visitStringLength(MStringLength *ins);
     bool visitArgumentsLength(MArgumentsLength *ins);
     bool visitGetFrameArgument(MGetFrameArgument *ins);
     bool visitSetFrameArgument(MSetFrameArgument *ins);
     bool visitRunOncePrologue(MRunOncePrologue *ins);
     bool visitRest(MRest *ins);
     bool visitRestPar(MRestPar *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -10208,49 +10208,24 @@ class MIteratorStart
     MDefinition *object() const {
         return getOperand(0);
     }
     uint8_t flags() const {
         return flags_;
     }
 };
 
-class MIteratorNext
-  : public MUnaryInstruction,
-    public SingleObjectPolicy
-{
-    explicit MIteratorNext(MDefinition *iter)
-      : MUnaryInstruction(iter)
-    {
-        setResultType(MIRType_Value);
-    }
-
-  public:
-    INSTRUCTION_HEADER(IteratorNext)
-
-    static MIteratorNext *New(TempAllocator &alloc, MDefinition *iter) {
-        return new(alloc) MIteratorNext(iter);
-    }
-
-    TypePolicy *typePolicy() {
-        return this;
-    }
-    MDefinition *iterator() const {
-        return getOperand(0);
-    }
-};
-
 class MIteratorMore
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
     explicit MIteratorMore(MDefinition *iter)
       : MUnaryInstruction(iter)
     {
-        setResultType(MIRType_Boolean);
+        setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(IteratorMore)
 
     static MIteratorMore *New(TempAllocator &alloc, MDefinition *iter) {
         return new(alloc) MIteratorMore(iter);
     }
@@ -10258,16 +10233,38 @@ class MIteratorMore
     TypePolicy *typePolicy() {
         return this;
     }
     MDefinition *iterator() const {
         return getOperand(0);
     }
 };
 
+class MIsNoIter
+  : public MUnaryInstruction
+{
+    MIsNoIter(MDefinition *def)
+      : MUnaryInstruction(def)
+    {
+        setResultType(MIRType_Boolean);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(IsNoIter)
+
+    static MIsNoIter *New(TempAllocator &alloc, MDefinition *def) {
+        return new(alloc) MIsNoIter(def);
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+};
+
 class MIteratorEnd
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
     explicit MIteratorEnd(MDefinition *iter)
       : MUnaryInstruction(iter)
     { }
 
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -187,18 +187,18 @@ namespace jit {
     _(CallGetElement)                                                       \
     _(CallSetElement)                                                       \
     _(CallSetProperty)                                                      \
     _(CallInitElementArray)                                                 \
     _(DeleteProperty)                                                       \
     _(DeleteElement)                                                        \
     _(SetPropertyCache)                                                     \
     _(IteratorStart)                                                        \
-    _(IteratorNext)                                                         \
     _(IteratorMore)                                                         \
+    _(IsNoIter)                                                             \
     _(IteratorEnd)                                                          \
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
     _(GetFrameArgument)                                                     \
     _(SetFrameArgument)                                                     \
     _(RunOncePrologue)                                                      \
     _(Rest)                                                                 \
     _(Floor)                                                                \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -269,18 +269,18 @@ class ParallelSafetyVisitor : public MDe
     UNSAFE_OP(CallGetElement)
     WRITE_GUARDED_OP(CallSetElement, object)
     UNSAFE_OP(CallInitElementArray)
     WRITE_GUARDED_OP(CallSetProperty, object)
     UNSAFE_OP(DeleteProperty)
     UNSAFE_OP(DeleteElement)
     WRITE_GUARDED_OP(SetPropertyCache, object)
     UNSAFE_OP(IteratorStart)
-    UNSAFE_OP(IteratorNext)
     UNSAFE_OP(IteratorMore)
+    UNSAFE_OP(IsNoIter)
     UNSAFE_OP(IteratorEnd)
     SAFE_OP(StringLength)
     SAFE_OP(ArgumentsLength)
     SAFE_OP(GetFrameArgument)
     UNSAFE_OP(SetFrameArgument)
     UNSAFE_OP(RunOncePrologue)
     CUSTOM_OP(Rest)
     SAFE_OP(RestPar)
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2057,16 +2057,22 @@ RangeAnalysis::addRangeAssertions()
             // Perform range checking for all numeric and numeric-like types.
             if (!IsNumberType(ins->type()) &&
                 ins->type() != MIRType_Boolean &&
                 ins->type() != MIRType_Value)
             {
                 continue;
             }
 
+            // MIsNoIter is fused with the MTest that follows it and emitted as
+            // LIsNoIterAndBranch. Skip it to avoid complicating MIsNoIter
+            // lowering.
+            if (ins->isIsNoIter())
+                continue;
+
             Range r(ins);
 
             // Don't insert assertions if there's nothing interesting to assert.
             if (r.isUnknown() || (ins->type() == MIRType_Int32 && r.isUnknownInt32()))
                 continue;
 
             MAssertRange *guard = MAssertRange::New(alloc(), ins, new(alloc()) Range(r));
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1104,17 +1104,16 @@ JSContext::JSContext(JSRuntime *rt)
     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
     resolvingList(nullptr),
     generatingError(false),
     savedFrameChains_(),
     cycleDetectorSet(MOZ_THIS_IN_INITIALIZER_LIST()),
     data(nullptr),
     data2(nullptr),
     outstandingRequests(0),
-    iterValue(MagicValue(JS_NO_ITER_VALUE)),
     jitIsBroken(false),
 #ifdef MOZ_TRACE_JSCALLS
     functionCallback(nullptr),
 #endif
     innermostGenerator_(nullptr)
 {
     JS_ASSERT(static_cast<ContextFriendFields*>(this) ==
               ContextFriendFields::get(this));
@@ -1299,18 +1298,16 @@ JSContext::mark(JSTracer *trc)
 {
     /* Stack frames and slots are traced by StackSpace::mark. */
 
     /* Mark other roots-by-definition in the JSContext. */
     if (isExceptionPending())
         MarkValueRoot(trc, &unwrappedException_, "unwrapped exception");
 
     TraceCycleDetectionSet(trc, cycleDetectorSet);
-
-    MarkValueRoot(trc, &iterValue, "iterValue");
 }
 
 void *
 ThreadSafeContext::stackLimitAddressForJitCode(StackKind kind)
 {
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     return runtime_->mainThread.addressOfSimulatorStackLimit();
 #endif
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -485,19 +485,16 @@ struct JSContext : public js::ExclusiveC
     }
 
     js::LifoAlloc &tempLifoAlloc() { return runtime()->tempLifoAlloc; }
 
     unsigned            outstandingRequests;/* number of JS_BeginRequest calls
                                                without the corresponding
                                                JS_EndRequest. */
 
-    /* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */
-    js::Value           iterValue;
-
     bool jitIsBroken;
 
     void updateJITEnabled();
 
     /* Whether this context has JS frames on the stack. */
     bool currentlyRunning() const;
 
     bool currentlyRunningInInterpreter() const {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -860,26 +860,25 @@ IsIterator(HandleValue v)
 
 MOZ_ALWAYS_INLINE bool
 iterator_next_impl(JSContext *cx, CallArgs args)
 {
     JS_ASSERT(IsIterator(args.thisv()));
 
     RootedObject thisObj(cx, &args.thisv().toObject());
 
-    bool more;
-    if (!IteratorMore(cx, thisObj, &more))
+    if (!IteratorMore(cx, thisObj, args.rval()))
         return false;
 
-    if (!more) {
+    if (args.rval().isMagic(JS_NO_ITER_VALUE)) {
         ThrowStopIteration(cx);
         return false;
     }
 
-    return IteratorNext(cx, thisObj, args.rval());
+    return true;
 }
 
 static bool
 iterator_next(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsIterator, iterator_next_impl>(cx, args);
 }
@@ -1012,23 +1011,16 @@ static bool
 CloseLegacyGenerator(JSContext *cx, HandleObject genobj);
 
 bool
 js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
 {
     /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
     JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
 
-    /*
-     * Make sure the more/next state machine doesn't get stuck. A value might
-     * be left in iterValue when a trace is left due to an interrupt after
-     * JSOP_MOREITER but before the value is picked up by FOR*.
-     */
-    cx->iterValue.setMagic(JS_NO_ITER_VALUE);
-
     RootedObject obj(cx);
     if (vp.isObject()) {
         /* Common case. */
         obj = &vp.toObject();
     } else {
         /*
          * Enumerating over null and undefined gives an empty enumerator, so
          * that |for (var p in <null or undefined>) <loop>;| never executes
@@ -1042,18 +1034,16 @@ js::ValueToIterator(JSContext *cx, unsig
     }
 
     return GetIterator(cx, obj, flags, vp);
 }
 
 bool
 js::CloseIterator(JSContext *cx, HandleObject obj)
 {
-    cx->iterValue.setMagic(JS_NO_ITER_VALUE);
-
     if (obj->is<PropertyIteratorObject>()) {
         /* Remove enumerators from the active list, which is a stack. */
         NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator();
 
         if (ni->flags & JSITER_ENUMERATE) {
             ni->unlink();
 
             JS_ASSERT(ni->flags & JSITER_ACTIVE);
@@ -1250,103 +1240,71 @@ class IndexRangePredicate {
 
 bool
 js::SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end)
 {
     return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
 }
 
 bool
-js::IteratorMore(JSContext *cx, HandleObject iterobj, bool *res)
+js::IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
 {
     /* Fast path for native iterators */
     NativeIterator *ni = nullptr;
     if (iterobj->is<PropertyIteratorObject>()) {
         /* Key iterators are handled by fast-paths. */
         ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
-        bool more = ni->props_cursor < ni->props_end;
-        if (ni->isKeyIter() || !more) {
-            *res = more;
+        if (ni->props_cursor >= ni->props_end) {
+            rval.setMagic(JS_NO_ITER_VALUE);
             return true;
         }
-    }
-
-    /* We might still have a pending value. */
-    if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
-        *res = true;
-        return true;
+        if (ni->isKeyIter()) {
+            rval.setString(*ni->current());
+            ni->incCursor();
+            return true;
+        }
     }
 
     /* We're reentering below and can call anything. */
     JS_CHECK_RECURSION(cx, return false);
 
     /* Fetch and cache the next value from the iterator. */
-    RootedValue val(cx);
     if (ni) {
         JS_ASSERT(!ni->isKeyIter());
         RootedId id(cx);
         RootedValue current(cx, StringValue(*ni->current()));
         if (!ValueToId<CanGC>(cx, current, &id))
             return false;
         ni->incCursor();
         RootedObject obj(cx, ni->obj);
-        if (!JSObject::getGeneric(cx, obj, obj, id, &val))
-            return false;
-        if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, val, &val))
-            return false;
-    } else {
-        /* Call the iterator object's .next method. */
-        if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, &val))
+        if (!JSObject::getGeneric(cx, obj, obj, id, rval))
             return false;
-        if (!Invoke(cx, ObjectValue(*iterobj), val, 0, nullptr, &val)) {
-            /* Check for StopIteration. */
-            if (!cx->isExceptionPending())
-                return false;
-            RootedValue exception(cx);
-            if (!cx->getPendingException(&exception))
-                return false;
-            if (!JS_IsStopIteration(exception))
-                return false;
-
-            cx->clearPendingException();
-            cx->iterValue.setMagic(JS_NO_ITER_VALUE);
-            *res = false;
-            return true;
-        }
+        if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, rval, rval))
+            return false;
+        return true;
     }
 
-    /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
-    JS_ASSERT(!val.isMagic(JS_NO_ITER_VALUE));
-    cx->iterValue = val;
-    *res = true;
-    return true;
-}
+    /* Call the iterator object's .next method. */
+    if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, rval))
+        return false;
+    if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) {
+        /* Check for StopIteration. */
+        if (!cx->isExceptionPending())
+            return false;
+        RootedValue exception(cx);
+        if (!cx->getPendingException(&exception))
+            return false;
+        if (!JS_IsStopIteration(exception))
+            return false;
 
-bool
-js::IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
-{
-    /* Fast path for native iterators */
-    if (iterobj->is<PropertyIteratorObject>()) {
-        /*
-         * Implement next directly as all the methods of the native iterator are
-         * read-only and permanent.
-         */
-        NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
-        if (ni->isKeyIter()) {
-            JS_ASSERT(ni->props_cursor < ni->props_end);
-            rval.setString(*ni->current());
-            ni->incCursor();
-            return true;
-        }
+        cx->clearPendingException();
+        rval.setMagic(JS_NO_ITER_VALUE);
+        return true;
     }
 
-    JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
-    rval.set(cx->iterValue);
-    cx->iterValue.setMagic(JS_NO_ITER_VALUE);
-
     return true;
 }
 
 static bool
 stopiter_hasInstance(JSContext *cx, HandleObject obj, MutableHandleValue v, bool *bp)
 {
     *bp = JS_IsStopIteration(v);
     return true;
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -196,25 +196,21 @@ SuppressDeletedProperty(JSContext *cx, H
 
 extern bool
 SuppressDeletedElement(JSContext *cx, HandleObject obj, uint32_t index);
 
 extern bool
 SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end);
 
 /*
- * IteratorMore() indicates whether another value is available. It might
- * internally call iterobj.next() and then cache the value until its
- * picked up by IteratorNext(). The value is cached in the current context.
+ * IteratorMore() returns the next iteration value. If no value is available,
+ * MagicValue(JS_NO_ITER_VALUE) is returned.
  */
 extern bool
-IteratorMore(JSContext *cx, HandleObject iterobj, bool *res);
-
-extern bool
-IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval);
+IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval);
 
 extern bool
 ThrowStopIteration(JSContext *cx);
 
 /*
  * Create an object of the form { value: VALUE, done: DONE }.
  * ES6 draft from 2013-09-05, section 25.4.3.4.
  */
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1335,31 +1335,16 @@ Debugger::onSingleStep(JSContext *cx, Mu
         }
         if (trappingScript->compileAndGo())
             JS_ASSERT(stepperCount == trappingScript->stepModeCount());
         else
             JS_ASSERT(stepperCount <= trappingScript->stepModeCount());
     }
 #endif
 
-    /* Preserve the debuggee's iterValue while handlers run. */
-    class PreserveIterValue {
-        JSContext *cx;
-        RootedValue savedIterValue;
-
-      public:
-        explicit PreserveIterValue(JSContext *cx) : cx(cx), savedIterValue(cx, cx->iterValue) {
-            cx->iterValue.setMagic(JS_NO_ITER_VALUE);
-        }
-        ~PreserveIterValue() {
-            cx->iterValue = savedIterValue;
-        }
-    };
-    PreserveIterValue piv(cx);
-
     /* Call all the onStep handlers we found. */
     for (JSObject **p = frames.begin(); p != frames.end(); p++) {
         RootedObject frame(cx, *p);
         Debugger *dbg = Debugger::fromChildJSObject(frame);
 
         Maybe<AutoCompartment> ac;
         ac.emplace(cx, dbg->object);
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -450,19 +450,16 @@ struct AutoGCIfNeeded
  * when done.  Then push the return value.
  */
 bool
 js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct)
 {
     JS_ASSERT(args.length() <= ARGS_LENGTH_MAX);
     JS_ASSERT(!cx->compartment()->activeAnalysis);
 
-    /* We should never enter a new script while cx->iterValue is live. */
-    JS_ASSERT(cx->iterValue.isMagic(JS_NO_ITER_VALUE));
-
     /* Perform GC if necessary on exit from the function. */
     AutoGCIfNeeded gcIfNeeded(cx);
 
     /* MaybeConstruct is a subset of InitialFrameFlags */
     InitialFrameFlags initial = (InitialFrameFlags) construct;
 
     if (args.calleev().isPrimitive())
         return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, construct);
@@ -1926,34 +1923,27 @@ END_CASE(JSOP_ITER)
 
 CASE(JSOP_MOREITER)
 {
     JS_ASSERT(REGS.stackDepth() >= 1);
     JS_ASSERT(REGS.sp[-1].isObject());
     PUSH_NULL();
     RootedObject &obj = rootObject0;
     obj = &REGS.sp[-2].toObject();
-    bool cond;
-    if (!IteratorMore(cx, obj, &cond))
+    if (!IteratorMore(cx, obj, REGS.stackHandleAt(-1)))
         goto error;
-    REGS.sp[-1].setBoolean(cond);
 }
 END_CASE(JSOP_MOREITER)
 
-CASE(JSOP_ITERNEXT)
+CASE(JSOP_ISNOITER)
 {
-    JS_ASSERT(REGS.sp[-1].isObject());
-    PUSH_NULL();
-    MutableHandleValue res = REGS.stackHandleAt(-1);
-    RootedObject &obj = rootObject0;
-    obj = &REGS.sp[-2].toObject();
-    if (!IteratorNext(cx, obj, res))
-        goto error;
+    bool b = REGS.sp[-1].isMagic(JS_NO_ITER_VALUE);
+    PUSH_BOOLEAN(b);
 }
-END_CASE(JSOP_ITERNEXT)
+END_CASE(JSOP_ISNOITER)
 
 CASE(JSOP_ENDITER)
 {
     JS_ASSERT(REGS.stackDepth() >= 1);
     RootedObject &obj = rootObject0;
     obj = &REGS.sp[-1].toObject();
     bool ok = CloseIterator(cx, obj);
     REGS.sp--;
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -610,40 +610,35 @@ 1234567890123456789012345678901234567890
      * 'val' and pushes 'iter' which is an iterator for 'val'.
      *   Category: Statements
      *   Type: For-In Statement
      *   Operands: uint8_t flags
      *   Stack: val => iter
      */ \
     macro(JSOP_ITER,      75, "iter",       NULL,         2,  1,  1,  JOF_UINT8) \
     /*
-     * Stores the next iterated value into 'cx->iterValue' and pushes 'true'
-     * onto the stack if another value is available, and 'false' otherwise.
-     * It is followed immediately by JSOP_IFNE.
+     * Pushes the next iterated value onto the stack. If no value is available,
+     * MagicValue(JS_NO_ITER_VALUE) is pushed.
      *
-     * This opcode increments iterator cursor if current iteration has
-     * JSITER_FOREACH flag.
-     *   Category: Statements
-     *   Type: For-In Statement
-     *   Operands:
-     *   Stack: iter => iter, cond
-     */ \
-    macro(JSOP_MOREITER,  76, "moreiter",   NULL,         1,  1,  2,  JOF_BYTE) \
-    /*
-     * Pushes the value produced by the preceding JSOP_MOREITER operation
-     * ('cx->iterValue') onto the stack
-     *
-     * This opcode increments iterator cursor if current iteration does not have
-     * JSITER_FOREACH flag.
      *   Category: Statements
      *   Type: For-In Statement
      *   Operands:
      *   Stack: iter => iter, val
      */ \
-    macro(JSOP_ITERNEXT,  77, "iternext",   "<next>",     1,  0,  1,  JOF_BYTE) \
+    macro(JSOP_MOREITER,  76, "moreiter",   NULL,         1,  1,  2,  JOF_BYTE) \
+    /*
+     * Pushes a boolean indicating whether the value on top of the stack is
+     * MagicValue(JS_NO_ITER_VALUE).
+     *
+     *   Category: Statements
+     *   Type: For-In Statement
+     *   Operands:
+     *   Stack: val => val, res
+     */ \
+    macro(JSOP_ISNOITER,  77, "isnoiter",   NULL,         1,  1,  2,  JOF_BYTE) \
     /*
      * Exits a for-in loop by popping the iterator object from the stack and
      * closing it.
      *   Category: Statements
      *   Type: For-In Statement
      *   Operands:
      *   Stack: iter =>
      */ \
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -23,17 +23,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 183);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 184);
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext *cx)
       : context(cx), base(nullptr), cursor(nullptr), limit(nullptr) { }
 
     JSContext *cx() const {
         return context;