Bug 696108 - hoist EmitFor, EmitForIn, EmitNormalFor out of EmitTree (r=jorendorff)

--HG--
extra : rebase_source : bec844e4a62f0492c33d3447fb31ca460768c62a
This commit is contained in:
Luke Wagner 2011-10-20 08:27:25 -07:00
parent 4c1a947967
commit f53995c346

View File

@ -5556,13 +5556,301 @@ EmitWith(JSContext *cx, CodeGenerator *cg, ParseNode *pn, JSBool &ok)
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;
@ -5860,280 +6148,7 @@ EmitTree(JSContext *cx, CodeGenerator *cg, ParseNode *pn)
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: {