author | Nicolas B. Pierron <nicolas.b.pierron@mozilla.com> |
Wed, 14 Nov 2012 17:38:10 -0800 | |
changeset 113337 | ae672aa46c289c9c66b70f85946106fff097add6 |
parent 113336 | 208c1637560ba23c552a90d75354fd1b8c05c285 |
child 113338 | 2e5ead73fbbc36dbfb5093ed547fd74aa8a05724 |
push id | 23869 |
push user | emorley@mozilla.com |
push date | Thu, 15 Nov 2012 16:18:16 +0000 |
treeherder | mozilla-central@a37525d304d9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jorendorff, gkw |
bugs | 799185 |
milestone | 19.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
|
--- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -6366,16 +6366,19 @@ js_ReconstructStackDepth(JSContext *cx, } #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1); static int SimulateOp(JSScript *script, JSOp op, const JSCodeSpec *cs, jsbytecode *pc, jsbytecode **pcstack, unsigned &pcdepth) { + if (cs->format & JOF_DECOMPOSE) + return pcdepth; + unsigned nuses = StackUses(script, pc); unsigned ndefs = StackDefs(script, pc); LOCAL_ASSERT(pcdepth >= nuses); pcdepth -= nuses; LOCAL_ASSERT(pcdepth + ndefs <= StackDepth(script)); /* * Fill the slots that the opcode defines withs its pc unless it just @@ -6417,144 +6420,118 @@ SimulateOp(JSScript *script, JSOp op, co pcstack[pcdepth] = tmp; } break; } pcdepth += ndefs; return pcdepth; } +/* Ensure that script analysis reports the same stack depth. */ +static void +AssertPCDepth(JSScript *script, jsbytecode *pc, unsigned pcdepth) { + /* + * If this assertion fails, run the failing test case under gdb and use the + * following gdb command to understand the execution path of this function. + * + * call js_DumpScriptDepth(cx, script, pc) + */ + JS_ASSERT_IF(script->hasAnalysis() && script->analysis()->maybeCode(pc), + script->analysis()->getCode(pc).stackDepth == pcdepth); +} + static int ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target, jsbytecode **pcstack) { /* - * Walk forward from script->main and compute the stack depth and stack of + * Walk forward from script->code and compute the stack depth and stack of * operand-generating opcode PCs in pcstack. * * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced. * FIXME: Optimize to use last empty-stack sequence point. */ LOCAL_ASSERT(script->code <= target && target < script->code + script->length); - jsbytecode *pc = script->code; - unsigned pcdepth = 0; unsigned hpcdepth = unsigned(-1); + unsigned cpcdepth = unsigned(-1); + + jsbytecode *pc; + unsigned pcdepth; ptrdiff_t oplen; - for (; pc < target; pc += oplen) { - JS_ASSERT_IF(script->hasAnalysis() && script->analysis()->maybeCode(pc), - script->analysis()->getCode(pc).stackDepth == - ((hpcdepth == unsigned(-1)) ? pcdepth : hpcdepth)); + for (pc = script->code, pcdepth = 0; ; pc += oplen) { JSOp op = JSOp(*pc); - const JSCodeSpec *cs = &js_CodeSpec[op]; oplen = GetBytecodeLength(pc); - - if (cs->format & JOF_DECOMPOSE) - continue; - - if (op == JSOP_GOTO) { - ptrdiff_t jmpoff = GET_JUMP_OFFSET(pc); - if (0 < jmpoff && pc + jmpoff <= target) { - pc += jmpoff; - oplen = 0; - /* Use the Hidden pc count if we follow the goto */ - if (hpcdepth != unsigned(-1)) { - pcdepth = hpcdepth; - hpcdepth = unsigned(-1); - } - continue; - } - - /* - * If we do not follow a goto we look for another mean to continue - * at the next PC. - */ - if (script->hasTrynotes()) { - JSTryNote *tn = script->trynotes()->vector; - JSTryNote *tnEnd = tn + script->trynotes()->length; - for (; tn != tnEnd; tn++) { - jsbytecode *start = script->main() + tn->start; - jsbytecode *end = start + tn->length; - if (start < pc && pc <= end && end <= target) - break; - } - - if (tn != tnEnd) { - pcdepth = tn->stackDepth; - hpcdepth = unsigned(-1); - oplen = 0; - pc = script->main() + tn->start + tn->length; - continue; - } - } - - /* - * JSOP_THROWING compensates for hidden JSOP_DUP at the start of the - * previous guarded catch (see EmitTry in BytecodeEmitter.cpp). - */ - if (JSOp(*(pc + oplen)) == JSOP_THROWING) - hpcdepth = pcdepth + 2; - else - /* Use the normal pc count if continue after the goto */ + const JSCodeSpec *cs = &js_CodeSpec[op]; + jssrcnote *sn = js_GetSrcNote(cx, script, pc); + + bool exitPath = + op == JSOP_GOTO || + op == JSOP_RETRVAL || + op == JSOP_THROW; + + bool isHiddenGoto = false; + + if (sn && SN_TYPE(sn) == SRC_HIDDEN) { + isHiddenGoto = op == JSOP_GOTO; + if (hpcdepth == unsigned(-1)) + hpcdepth = pcdepth; + } else if (!exitPath) { + hpcdepth = unsigned(-1); + } + + if (op == JSOP_LEAVEBLOCK && sn && SN_TYPE(sn) == SRC_CATCH) { + LOCAL_ASSERT(cpcdepth == unsigned(-1)); + cpcdepth = pcdepth; + } else if (sn && SN_TYPE(sn) == SRC_HIDDEN && + (op == JSOP_THROW || op == JSOP_THROWING)) + { + LOCAL_ASSERT(cpcdepth != unsigned(-1)); + pcdepth = cpcdepth + 1; + cpcdepth = unsigned(-1); + } else if (!(op == JSOP_GOTO && sn && SN_TYPE(sn) == SRC_HIDDEN) && + !(op == JSOP_GOSUB && cpcdepth != unsigned(-1))) + { + if (cpcdepth != unsigned(-1)) + LOCAL_ASSERT((op == JSOP_NOP && sn && SN_TYPE(sn) == SRC_ENDBRACE) || + op == JSOP_FINALLY); + cpcdepth = unsigned(-1); + } + + /* At this point, pcdepth is the stack depth *before* the insn at pc. */ + AssertPCDepth(script, pc, pcdepth); + if (pc >= target) + break; + + /* Simulate the instruction at pc. */ + if (SimulateOp(script, op, cs, pc, pcstack, pcdepth) < 0) + return -1; + + /* At this point, pcdepth is the stack depth *after* the insn at pc. */ + + if (exitPath && hpcdepth != unsigned(-1)) { + pcdepth = hpcdepth; + if (!isHiddenGoto) hpcdepth = unsigned(-1); - - continue; } /* - * A (C ? T : E) expression requires skipping either T (if target is in - * E) or both T and E (if target is after the whole expression) before - * adjusting pcdepth based on the JSOP_IFEQ at pc that tests condition - * C. We know that the stack depth can't change from what it was with - * C on top of stack. + * A (C ? T : E) expression requires skipping T if target is in E or + * after the whole expression, because this expression is pushing a + * result on the stack and the goto cannot be skipped. */ - jssrcnote *sn = js_GetSrcNote(cx, script, pc); if (sn && SN_TYPE(sn) == SRC_COND) { - ptrdiff_t jmpoff = js_GetSrcNoteOffset(sn, 0); - if (pc + jmpoff < target) { - pc += jmpoff; - op = JSOp(*pc); - JS_ASSERT(op == JSOP_GOTO); - cs = &js_CodeSpec[op]; - oplen = cs->length; - JS_ASSERT(oplen > 0); - ptrdiff_t jmplen = GET_JUMP_OFFSET(pc); - if (pc + jmplen < target) { - oplen = (unsigned) jmplen; - continue; - } - - /* - * Ok, target lies in E. Manually pop C off the model stack, - * since we have moved beyond the IFEQ now. - */ - LOCAL_ASSERT(pcdepth != 0); - --pcdepth; + ptrdiff_t jmplen = GET_JUMP_OFFSET(pc); + if (pc + jmplen <= target) { + /* Target does not lie in T. */ + oplen = jmplen; } } - - /* - * SRC_HIDDEN instructions annotate early-exit paths and do not affect - * the stack depth when not taken. However, when pc points into an - * early-exit path, hidden instructions need to be taken into account. - */ - if (sn && SN_TYPE(sn) == SRC_HIDDEN) { - if (hpcdepth == unsigned(-1)) - hpcdepth = pcdepth; - if (SimulateOp(script, op, cs, pc, pcstack, hpcdepth) < 0) - return -1; - } else { - hpcdepth = unsigned(-1); - if (SimulateOp(script, op, cs, pc, pcstack, pcdepth) < 0) - return -1; - } - } LOCAL_ASSERT(pc == target); - if (hpcdepth != unsigned(-1)) - return hpcdepth; + AssertPCDepth(script, pc, pcdepth); return pcdepth; } #undef LOCAL_ASSERT #undef LOCAL_ASSERT_RV namespace js {