Bug 696108 - hoist EmitFor, EmitForIn, EmitNormalFor out of EmitTree (r=jorendorff)
authorLuke Wagner <luke@mozilla.com>
Thu, 20 Oct 2011 08:27:25 -0700
changeset 79089 2d6f586ee5b002c9ada0c1782c678f6aae4b557d
parent 79088 838ec216947e40d88b442f9bd0a922b6388b7592
child 79090 15e15fead382a9fbfaa9b12ea049b24089b3555f
push id21359
push userbmo@edmorley.co.uk
push dateSat, 22 Oct 2011 12:59:01 +0000
treeherdermozilla-central@7ba4cea5382d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs696108
milestone10.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 696108 - hoist EmitFor, EmitForIn, EmitNormalFor out of EmitTree (r=jorendorff)
js/src/frontend/BytecodeGenerator.cpp
--- a/js/src/frontend/BytecodeGenerator.cpp
+++ b/js/src/frontend/BytecodeGenerator.cpp
@@ -5551,23 +5551,311 @@ EmitWith(JSContext *cx, CodeGenerator *c
     if (!EmitTree(cx, cg, pn->pn_right))
         return false;
     if (Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
         return false;
     ok = PopStatementCG(cx, cg);
     return true;
 }
 
+static bool
+EmitForIn(JSContext *cx, CodeGenerator *cg, ParseNode *pn, ptrdiff_t top)
+{
+    StmtInfo stmtInfo;
+    PushStatement(cg, &stmtInfo, STMT_FOR_IN_LOOP, top);
+
+    ParseNode *forHead = pn->pn_left;
+    ParseNode *forBody = pn->pn_right;
+
+    /*
+     * If the left part is 'var x', emit code to define x if necessary
+     * using a prolog opcode, but do not emit a pop. If the left part
+     * was originally 'var x = i', the parser will have rewritten it;
+     * see Parser::forStatement. 'for (let x = i in o)' is mercifully
+     * banned.
+     */
+    bool forLet = false;
+    if (ParseNode *decl = forHead->pn_kid1) {
+        JS_ASSERT(TokenKindIsDecl(decl->getKind()));
+        forLet = decl->isKind(TOK_LET);
+        cg->flags |= TCF_IN_FOR_INIT;
+        if (!EmitTree(cx, cg, decl))
+            return false;
+        cg->flags &= ~TCF_IN_FOR_INIT;
+    }
+
+    /* Compile the object expression to the right of 'in'. */
+    {
+        TempPopScope tps;
+        if (forLet && !tps.popBlock(cx, cg))
+            return false;
+        if (!EmitTree(cx, cg, forHead->pn_kid3))
+            return false;
+        if (forLet && !tps.repushBlock(cx, cg))
+            return false;
+    }
+
+    /*
+     * 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, cg, JSOP_ITER, (uint8) pn->pn_iflags) < 0)
+        return false;
+
+    /* Annotate so the decompiler can find the loop-closing jump. */
+    intN noteIndex = NewSrcNote(cx, cg, SRC_FOR_IN);
+    if (noteIndex < 0)
+        return false;
+
+    /*
+     * Jump down to the loop condition to minimize overhead assuming at
+     * least one iteration, as the other loop forms do.
+     */
+    ptrdiff_t jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
+    if (jmp < 0)
+        return false;
+
+    intN noteIndex2 = NewSrcNote(cx, cg, SRC_TRACE);
+    if (noteIndex2 < 0)
+        return false;
+
+    top = CG_OFFSET(cg);
+    SET_STATEMENT_TOP(&stmtInfo, top);
+    if (EmitTraceOp(cx, cg, NULL) < 0)
+        return false;
+
+#ifdef DEBUG
+    intN loopDepth = cg->stackDepth;
+#endif
+
+    /*
+     * Emit code to get the next enumeration value and assign it to the
+     * left hand side. The JSOP_POP after this assignment is annotated
+     * so that the decompiler can distinguish 'for (x in y)' from
+     * 'for (var x in y)'.
+     */
+    if (!EmitAssignment(cx, cg, forHead->pn_kid2, JSOP_NOP, NULL))
+        return false;
+    ptrdiff_t tmp2 = CG_OFFSET(cg);
+    if (forHead->pn_kid1 && NewSrcNote2(cx, cg, SRC_DECL,
+                                        (forHead->pn_kid1->isOp(JSOP_DEFVAR))
+                                        ? SRC_DECL_VAR
+                                        : SRC_DECL_LET) < 0) {
+        return false;
+    }
+    if (Emit1(cx, cg, JSOP_POP) < 0)
+        return false;
+
+    /* The stack should be balanced around the assignment opcode sequence. */
+    JS_ASSERT(cg->stackDepth == loopDepth);
+
+    /* Emit code for the loop body. */
+    if (!EmitTree(cx, cg, forBody))
+        return false;
+
+    /* Set loop and enclosing "update" offsets, for continue. */
+    StmtInfo *stmt = &stmtInfo;
+    do {
+        stmt->update = CG_OFFSET(cg);
+    } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
+
+    /*
+     * Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
+     */
+    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
+    if (Emit1(cx, cg, JSOP_MOREITER) < 0)
+        return false;
+    ptrdiff_t beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg));
+    if (beq < 0)
+        return false;
+
+    /*
+     * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
+     * note gets bigger.
+     */
+    if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, beq - top))
+        return false;
+    /* Set the first srcnote offset so we can find the start of the loop body. */
+    if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp2 - jmp))
+        return false;
+    /* Set the second srcnote offset so we can find the closing jump. */
+    if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, beq - jmp))
+        return false;
+
+    /* Now fixup all breaks and continues (before the JSOP_ENDITER). */
+    if (!PopStatementCG(cx, cg))
+        return false;
+
+    if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, CG_OFFSET(cg)))
+        return false;
+
+    return Emit1(cx, cg, JSOP_ENDITER) >= 0;
+}
+
+static bool
+EmitNormalFor(JSContext *cx, CodeGenerator *cg, ParseNode *pn, ptrdiff_t top)
+{
+    StmtInfo stmtInfo;
+    PushStatement(cg, &stmtInfo, STMT_FOR_LOOP, top);
+
+    ParseNode *forHead = pn->pn_left;
+    ParseNode *forBody = pn->pn_right;
+
+    /* C-style for (init; cond; update) ... loop. */
+    JSOp op = JSOP_POP;
+    ParseNode *pn3 = forHead->pn_kid1;
+    if (!pn3) {
+        /* No initializer: emit an annotated nop for the decompiler. */
+        op = JSOP_NOP;
+    } else {
+        cg->flags |= TCF_IN_FOR_INIT;
+#if JS_HAS_DESTRUCTURING
+        if (pn3->isKind(TOK_ASSIGN) &&
+            !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
+            return false;
+        }
+#endif
+        if (op == JSOP_POP) {
+            if (!EmitTree(cx, cg, pn3))
+                return false;
+            if (TokenKindIsDecl(pn3->getKind())) {
+                /*
+                 * Check whether a destructuring-initialized var decl
+                 * was optimized to a group assignment.  If so, we do
+                 * not need to emit a pop below, so switch to a nop,
+                 * just for the decompiler.
+                 */
+                JS_ASSERT(pn3->isArity(PN_LIST));
+                if (pn3->pn_xflags & PNX_GROUPINIT)
+                    op = JSOP_NOP;
+            }
+        }
+        cg->flags &= ~TCF_IN_FOR_INIT;
+    }
+
+    /*
+     * NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
+     * Use tmp to hold the biased srcnote "top" offset, which differs
+     * from the top local variable by the length of the JSOP_GOTO{,X}
+     * emitted in between tmp and top if this loop has a condition.
+     */
+    intN noteIndex = NewSrcNote(cx, cg, SRC_FOR);
+    if (noteIndex < 0 || Emit1(cx, cg, op) < 0)
+        return false;
+    ptrdiff_t tmp = CG_OFFSET(cg);
+
+    ptrdiff_t jmp = -1;
+    if (forHead->pn_kid2) {
+        /* Goto the loop condition, which branches back to iterate. */
+        jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
+        if (jmp < 0)
+            return false;
+    }
+
+    top = CG_OFFSET(cg);
+    SET_STATEMENT_TOP(&stmtInfo, top);
+
+    intN noteIndex2 = NewSrcNote(cx, cg, SRC_TRACE);
+    if (noteIndex2 < 0)
+        return false;
+
+    /* Emit code for the loop body. */
+    if (EmitTraceOp(cx, cg, forBody) < 0)
+        return false;
+    if (!EmitTree(cx, cg, forBody))
+        return false;
+
+    /* Set the second note offset so we can find the update part. */
+    JS_ASSERT(noteIndex != -1);
+    ptrdiff_t tmp2 = CG_OFFSET(cg);
+
+    /* Set loop and enclosing "update" offsets, for continue. */
+    StmtInfo *stmt = &stmtInfo;
+    do {
+        stmt->update = CG_OFFSET(cg);
+    } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
+
+    /* Check for update code to do before the condition (if any). */
+    pn3 = forHead->pn_kid3;
+    if (pn3) {
+        op = JSOP_POP;
+#if JS_HAS_DESTRUCTURING
+        if (pn3->isKind(TOK_ASSIGN) &&
+            !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
+            return false;
+        }
+#endif
+        if (op == JSOP_POP && !EmitTree(cx, cg, pn3))
+            return false;
+
+        /* Always emit the POP or NOP, to help the decompiler. */
+        if (Emit1(cx, cg, op) < 0)
+            return false;
+
+        /* Restore the absolute line number for source note readers. */
+        ptrdiff_t lineno = pn->pn_pos.end.lineno;
+        if (CG_CURRENT_LINE(cg) != (uintN) lineno) {
+            if (NewSrcNote2(cx, cg, SRC_SETLINE, lineno) < 0)
+                return false;
+            CG_CURRENT_LINE(cg) = (uintN) lineno;
+        }
+    }
+
+    ptrdiff_t tmp3 = CG_OFFSET(cg);
+
+    if (forHead->pn_kid2) {
+        /* Fix up the goto from top to target the loop condition. */
+        JS_ASSERT(jmp >= 0);
+        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
+
+        if (!EmitTree(cx, cg, forHead->pn_kid2))
+            return false;
+    }
+
+    /*
+     * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
+     * note gets bigger.
+     */
+    if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, CG_OFFSET(cg) - top))
+        return false;
+    /* Set the first note offset so we can find the loop condition. */
+    if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp3 - tmp))
+        return false;
+    if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, tmp2 - tmp))
+        return false;
+    /* The third note offset helps us find the loop-closing jump. */
+    if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, CG_OFFSET(cg) - tmp))
+        return false;
+
+    /* If no loop condition, just emit a loop-closing jump. */
+    op = forHead->pn_kid2 ? JSOP_IFNE : JSOP_GOTO;
+    if (EmitJump(cx, cg, op, top - CG_OFFSET(cg)) < 0)
+        return false;
+
+    /* Now fixup all breaks and continues. */
+    return PopStatementCG(cx, cg);
+}
+
+static inline bool
+EmitFor(JSContext *cx, CodeGenerator *cg, ParseNode *pn, ptrdiff_t top)
+{
+    return pn->pn_left->isKind(TOK_IN)
+           ? EmitForIn(cx, cg, pn, top)
+           : EmitNormalFor(cx, cg, pn, top);
+}
+
 JSBool
 EmitTree(JSContext *cx, CodeGenerator *cg, ParseNode *pn)
 {
     JSBool useful, wantval;
     StmtInfo stmtInfo;
     StmtInfo *stmt;
-    ptrdiff_t top, off, tmp, beq, jmp, tmp2, tmp3;
+    ptrdiff_t top, off, tmp, beq, jmp;
     ParseNode *pn2, *pn3;
     JSAtom *atom;
     jsatomid atomIndex;
     uintN index;
     ptrdiff_t noteIndex, noteIndex2;
     SrcNoteType noteType;
     jsbytecode *pc;
     JSOp op;
@@ -5855,290 +6143,17 @@ EmitTree(JSContext *cx, CodeGenerator *c
         if (!SetSrcNoteOffset(cx, cg, noteIndex2, 0, beq - top))
             return JS_FALSE;
         if (!SetSrcNoteOffset(cx, cg, noteIndex, 0, 1 + (off - top)))
             return JS_FALSE;
         ok = PopStatementCG(cx, cg);
         break;
 
       case TOK_FOR:
-        beq = 0;                /* suppress gcc warnings */
-        jmp = -1;
-        pn2 = pn->pn_left;
-        PushStatement(cg, &stmtInfo, STMT_FOR_LOOP, top);
-
-        if (pn2->isKind(TOK_IN)) {
-            /* Set stmtInfo type for later testing. */
-            stmtInfo.type = STMT_FOR_IN_LOOP;
-
-            /*
-             * If the left part is 'var x', emit code to define x if necessary
-             * using a prolog opcode, but do not emit a pop. If the left part
-             * was originally 'var x = i', the parser will have rewritten it;
-             * see Parser::forStatement. 'for (let x = i in o)' is mercifully
-             * banned.
-             */
-            bool forLet = false;
-            if (ParseNode *decl = pn2->pn_kid1) {
-                JS_ASSERT(TokenKindIsDecl(decl->getKind()));
-                forLet = decl->isKind(TOK_LET);
-                cg->flags |= TCF_IN_FOR_INIT;
-                if (!EmitTree(cx, cg, decl))
-                    return JS_FALSE;
-                cg->flags &= ~TCF_IN_FOR_INIT;
-            }
-
-            /* Compile the object expression to the right of 'in'. */
-            {
-                TempPopScope tps;
-                if (forLet && !tps.popBlock(cx, cg))
-                    return JS_FALSE;
-                if (!EmitTree(cx, cg, pn2->pn_kid3))
-                    return JS_FALSE;
-                if (forLet && !tps.repushBlock(cx, cg))
-                    return JS_FALSE;
-            }
-
-            /*
-             * 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, cg, JSOP_ITER, (uint8) pn->pn_iflags) < 0)
-                return JS_FALSE;
-
-            /* Annotate so the decompiler can find the loop-closing jump. */
-            noteIndex = NewSrcNote(cx, cg, SRC_FOR_IN);
-            if (noteIndex < 0)
-                return JS_FALSE;
-
-            /*
-             * Jump down to the loop condition to minimize overhead assuming at
-             * least one iteration, as the other loop forms do.
-             */
-            jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
-            if (jmp < 0)
-                return JS_FALSE;
-
-            noteIndex2 = NewSrcNote(cx, cg, SRC_TRACE);
-            if (noteIndex2 < 0)
-                return JS_FALSE;
-            
-            top = CG_OFFSET(cg);
-            SET_STATEMENT_TOP(&stmtInfo, top);
-            if (EmitTraceOp(cx, cg, NULL) < 0)
-                return JS_FALSE;
-
-#ifdef DEBUG
-            intN loopDepth = cg->stackDepth;
-#endif
-
-            /*
-             * Emit code to get the next enumeration value and assign it to the
-             * left hand side. The JSOP_POP after this assignment is annotated
-             * so that the decompiler can distinguish 'for (x in y)' from
-             * 'for (var x in y)'.
-             */
-            if (!EmitAssignment(cx, cg, pn2->pn_kid2, JSOP_NOP, NULL))
-                return false;
-            tmp2 = CG_OFFSET(cg);
-            if (pn2->pn_kid1 && NewSrcNote2(cx, cg, SRC_DECL, (pn2->pn_kid1->isOp(JSOP_DEFVAR))
-                                                              ? SRC_DECL_VAR
-                                                              : SRC_DECL_LET) < 0)
-            {
-                return false;
-            }
-            if (Emit1(cx, cg, JSOP_POP) < 0)
-                return false;
-
-            /* The stack should be balanced around the assignment opcode sequence. */
-            JS_ASSERT(cg->stackDepth == loopDepth);
-
-            /* Emit code for the loop body. */
-            if (!EmitTree(cx, cg, pn->pn_right))
-                return JS_FALSE;
-
-            /* Set loop and enclosing "update" offsets, for continue. */
-            stmt = &stmtInfo;
-            do {
-                stmt->update = CG_OFFSET(cg);
-            } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
-
-            /*
-             * Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
-             */
-            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
-            if (Emit1(cx, cg, JSOP_MOREITER) < 0)
-                return JS_FALSE;
-            beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg));
-            if (beq < 0)
-                return JS_FALSE;
-
-            /*
-             * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
-             * note gets bigger.
-             */
-            if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, beq - top))
-                return JS_FALSE;
-            /* Set the first srcnote offset so we can find the start of the loop body. */
-            if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp2 - jmp))
-                return JS_FALSE;
-            /* Set the second srcnote offset so we can find the closing jump. */
-            if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, beq - jmp))
-                return JS_FALSE;
-        } else {
-            /* C-style for (init; cond; update) ... loop. */
-            op = JSOP_POP;
-            pn3 = pn2->pn_kid1;
-            if (!pn3) {
-                /* No initializer: emit an annotated nop for the decompiler. */
-                op = JSOP_NOP;
-            } else {
-                cg->flags |= TCF_IN_FOR_INIT;
-#if JS_HAS_DESTRUCTURING
-                if (pn3->isKind(TOK_ASSIGN) &&
-                    !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
-                    return JS_FALSE;
-                }
-#endif
-                if (op == JSOP_POP) {
-                    if (!EmitTree(cx, cg, pn3))
-                        return JS_FALSE;
-                    if (TokenKindIsDecl(pn3->getKind())) {
-                        /*
-                         * Check whether a destructuring-initialized var decl
-                         * was optimized to a group assignment.  If so, we do
-                         * not need to emit a pop below, so switch to a nop,
-                         * just for the decompiler.
-                         */
-                        JS_ASSERT(pn3->isArity(PN_LIST));
-                        if (pn3->pn_xflags & PNX_GROUPINIT)
-                            op = JSOP_NOP;
-                    }
-                }
-                cg->flags &= ~TCF_IN_FOR_INIT;
-            }
-
-            /*
-             * NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
-             * Use tmp to hold the biased srcnote "top" offset, which differs
-             * from the top local variable by the length of the JSOP_GOTO{,X}
-             * emitted in between tmp and top if this loop has a condition.
-             */
-            noteIndex = NewSrcNote(cx, cg, SRC_FOR);
-            if (noteIndex < 0 || Emit1(cx, cg, op) < 0)
-                return JS_FALSE;
-            tmp = CG_OFFSET(cg);
-
-            if (pn2->pn_kid2) {
-                /* Goto the loop condition, which branches back to iterate. */
-                jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
-                if (jmp < 0)
-                    return JS_FALSE;
-            }
-
-            top = CG_OFFSET(cg);
-            SET_STATEMENT_TOP(&stmtInfo, top);
-
-            noteIndex2 = NewSrcNote(cx, cg, SRC_TRACE);
-            if (noteIndex2 < 0)
-                return JS_FALSE;
-            
-            /* Emit code for the loop body. */
-            if (EmitTraceOp(cx, cg, pn->pn_right) < 0)
-                return JS_FALSE;
-            if (!EmitTree(cx, cg, pn->pn_right))
-                return JS_FALSE;
-
-            /* Set the second note offset so we can find the update part. */
-            JS_ASSERT(noteIndex != -1);
-            tmp2 = CG_OFFSET(cg);
-
-            /* Set loop and enclosing "update" offsets, for continue. */
-            stmt = &stmtInfo;
-            do {
-                stmt->update = CG_OFFSET(cg);
-            } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
-
-            /* Check for update code to do before the condition (if any). */
-            pn3 = pn2->pn_kid3;
-            if (pn3) {
-                op = JSOP_POP;
-#if JS_HAS_DESTRUCTURING
-                if (pn3->isKind(TOK_ASSIGN) &&
-                    !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
-                    return JS_FALSE;
-                }
-#endif
-                if (op == JSOP_POP && !EmitTree(cx, cg, pn3))
-                    return JS_FALSE;
-
-                /* Always emit the POP or NOP, to help the decompiler. */
-                if (Emit1(cx, cg, op) < 0)
-                    return JS_FALSE;
-
-                /* Restore the absolute line number for source note readers. */
-                off = (ptrdiff_t) pn->pn_pos.end.lineno;
-                if (CG_CURRENT_LINE(cg) != (uintN) off) {
-                    if (NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)
-                        return JS_FALSE;
-                    CG_CURRENT_LINE(cg) = (uintN) off;
-                }
-            }
-
-            tmp3 = CG_OFFSET(cg);
-
-            if (pn2->pn_kid2) {
-                /* Fix up the goto from top to target the loop condition. */
-                JS_ASSERT(jmp >= 0);
-                CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
-
-                if (!EmitTree(cx, cg, pn2->pn_kid2))
-                    return JS_FALSE;
-            }
-
-            /*
-             * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
-             * note gets bigger.
-             */
-            if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, CG_OFFSET(cg) - top))
-                return JS_FALSE;
-            /* Set the first note offset so we can find the loop condition. */
-            if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp3 - tmp))
-                return JS_FALSE;
-            if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, tmp2 - tmp))
-                return JS_FALSE;
-            /* The third note offset helps us find the loop-closing jump. */
-            if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, CG_OFFSET(cg) - tmp))
-                return JS_FALSE;
-
-            if (pn2->pn_kid2) {
-                beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg));
-                if (beq < 0)
-                    return JS_FALSE;
-            } else {
-                /* No loop condition -- emit the loop-closing jump. */
-                jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
-                if (jmp < 0)
-                    return JS_FALSE;
-            }
-        }
-
-        /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */
-        if (!PopStatementCG(cx, cg))
-            return JS_FALSE;
-
-        if (pn2->isKind(TOK_IN)) {
-            if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, CG_OFFSET(cg)) ||
-                Emit1(cx, cg, JSOP_ENDITER) < 0)
-            {
-                return JS_FALSE;
-            }
-        }
+        ok = EmitFor(cx, cg, pn, top);
         break;
 
       case TOK_BREAK: {
         stmt = cg->topStmt;
         atom = pn->pn_atom;
 
         jsatomid labelIndex;
         if (atom) {