Bug 799185 part 1 - Rewrite ReconstructPCStack. r=jorendorff,gkw
authorNicolas 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 id23869
push useremorley@mozilla.com
push dateThu, 15 Nov 2012 16:18:16 +0000
treeherdermozilla-central@a37525d304d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff, gkw
bugs799185
milestone19.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 799185 part 1 - Rewrite ReconstructPCStack. r=jorendorff,gkw
js/src/jsopcode.cpp
--- 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 {