First stage of loop optimization (371802, r=igor).

This commit is contained in:
brendan%mozilla.org 2007-02-28 03:12:01 +00:00
parent 7bf5f8b7a1
commit 187827fa7f
3 changed files with 72 additions and 34 deletions

View File

@ -4155,32 +4155,52 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
break;
case TOK_WHILE:
/*
* Minimize bytecodes issued for one or more iterations by jumping to
* the condition below the body and closing the loop if the condition
* is true with a backward branch. For iteration count i:
*
* i test at the top test at the bottom
* = =============== ==================
* 0 ifeq-pass goto; ifne-fail
* 1 ifeq-fail; goto; ifne-pass goto; ifne-pass; ifne-fail
* 2 2*(ifeq-fail; goto); ifeq-pass goto; 2*ifne-pass; ifne-fail
* . . .
* N N*(ifeq-fail; goto); ifeq-pass goto; N*ifne-pass; ifne-fail
*
* SpiderMonkey, pre-mozilla.org, emitted while parsing and so used
* test at the top. When JSParseNode trees were added during the ES3
* work (1998-9), the code generation scheme was not optimized, and
* the decompiler continued to take advantage of the branch and jump
* that bracketed the body. But given the SRC_WHILE note, it is easy
* to support the more efficient scheme.
*/
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top);
if (!js_EmitTree(cx, cg, pn->pn_left))
return JS_FALSE;
noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
if (noteIndex < 0)
return JS_FALSE;
beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
if (beq < 0)
return JS_FALSE;
if (!js_EmitTree(cx, cg, pn->pn_right))
return JS_FALSE;
jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
if (jmp < 0)
return JS_FALSE;
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
top = CG_OFFSET(cg);
if (!js_EmitTree(cx, cg, pn->pn_right))
return JS_FALSE;
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
if (!js_EmitTree(cx, cg, pn->pn_left))
return JS_FALSE;
beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg));
if (beq < 0)
return JS_FALSE;
if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, beq - jmp))
return JS_FALSE;
ok = js_PopStatementCG(cx, cg);
break;
case TOK_DO:
/* Emit an annotated nop so we know to decompile a 'do' keyword. */
if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 ||
js_Emit1(cx, cg, JSOP_NOP) < 0) {
noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0)
return JS_FALSE;
}
/* Compile the loop body. */
top = CG_OFFSET(cg);
@ -4199,12 +4219,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
/*
* No source note needed, because JSOP_IFNE is used only for do-while.
* If we ever use JSOP_IFNE for other purposes, we can still avoid yet
* another note here, by storing (jmp - top) in the SRC_WHILE note's
* offset, and fetching that delta in order to decompile recursively.
* Since we use JSOP_IFNE for other purposes as well as for do-while
* loops, we must store 1 + (beq - top) in the SRC_WHILE note offset,
* and the decompiler must get that delta and decompile recursively.
*/
if (EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)) < 0)
beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg));
if (beq < 0)
return JS_FALSE;
if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, 1 + (beq - top)))
return JS_FALSE;
ok = js_PopStatementCG(cx, cg);
break;

View File

@ -1791,8 +1791,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
todo = -2;
switch (sn ? SN_TYPE(sn) : SRC_NULL) {
case SRC_WHILE:
++pc;
tail = js_GetSrcNoteOffset(sn, 0) - 1;
LOCAL_ASSERT(pc[tail] == JSOP_IFNE ||
pc[tail] == JSOP_IFNEX);
js_printf(SET_MAYBE_BRACE(jp), "\tdo {\n");
jp->indent += 4;
DECOMPILE_CODE(pc, tail);
jp->indent -= 4;
js_printf(jp, "\t} while (%s);\n", POP_STR());
pc += tail;
len = js_CodeSpec[*pc].length;
todo = -2;
break;
case SRC_FOR:
@ -2571,6 +2581,22 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
case JSOP_GOTOX:
sn = js_GetSrcNote(jp->script, pc);
switch (sn ? SN_TYPE(sn) : SRC_NULL) {
case SRC_WHILE:
cond = GetJumpOffset(pc, pc);
tail = js_GetSrcNoteOffset(sn, 0);
DECOMPILE_CODE(pc + cond, tail - cond);
rval = POP_STR();
js_printf(SET_MAYBE_BRACE(jp), "\twhile (%s) {\n", rval);
jp->indent += 4;
DECOMPILE_CODE(pc + oplen, cond - oplen);
jp->indent -= 4;
js_printf(jp, "\t}\n");
pc += tail;
LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
len = js_CodeSpec[*pc].length;
todo = -2;
break;
case SRC_CONT2LABEL:
atom = js_GetAtom(cx, &jp->script->atomMap,
(jsatomid) js_GetSrcNoteOffset(sn, 0));
@ -2580,9 +2606,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
RETRACT(&ss->sprinter, rval);
js_printf(jp, "\tcontinue %s;\n", rval);
break;
case SRC_CONTINUE:
js_printf(jp, "\tcontinue;\n");
break;
case SRC_BREAK2LABEL:
atom = js_GetAtom(cx, &jp->script->atomMap,
(jsatomid) js_GetSrcNoteOffset(sn, 0));
@ -2592,8 +2620,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
RETRACT(&ss->sprinter, rval);
js_printf(jp, "\tbreak %s;\n", rval);
break;
case SRC_HIDDEN:
break;
default:
js_printf(jp, "\tbreak;\n");
break;
@ -2667,17 +2697,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
todo = -2;
break;
case SRC_WHILE:
rval = POP_STR();
js_printf(SET_MAYBE_BRACE(jp), "\twhile (%s) {\n", rval);
jp->indent += 4;
tail = js_GetSrcNoteOffset(sn, 0);
DECOMPILE_CODE(pc + oplen, tail - oplen);
jp->indent -= 4;
js_printf(jp, "\t}\n");
todo = -2;
break;
case SRC_COND:
xval = JS_strdup(cx, POP_STR());
if (!xval)
@ -2709,10 +2728,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
case JSOP_IFNE:
case JSOP_IFNEX:
/* Currently, this must be a do-while loop's upward branch. */
jp->indent -= 4;
js_printf(jp, "\t} while (%s);\n", POP_STR());
todo = -2;
LOCAL_ASSERT(0);
break;
case JSOP_OR:

View File

@ -200,7 +200,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 5)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 6)
/*
* Library-private functions.