Add JSOP_LOOPENTRY for target of initial loop jump, bug 720169. r=dvander
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 23 Jan 2012 13:51:01 -0800
changeset 86367 850ce7c81121f7731719449c974da81ee333a11f
parent 86366 735345948d326c50f68c308cf600f707c5a4c3b9
child 86368 1b5b7d538230afd714174f78d918ada69c960c93
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs720169
milestone12.0a1
Add JSOP_LOOPENTRY for target of initial loop jump, bug 720169. r=dvander
js/src/frontend/BytecodeEmitter.cpp
js/src/jsanalyze.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsopcode.cpp
js/src/jsopcode.tbl
js/src/jsxdrapi.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/LoopState.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -534,17 +534,17 @@ EmitBackPatchOp(JSContext *cx, BytecodeE
 static JSBool
 UpdateLineNumberNotes(JSContext *cx, BytecodeEmitter *bce, uintN line)
 {
     UPDATE_LINE_NUMBER_NOTES(cx, bce, line);
     return JS_TRUE;
 }
 
 static ptrdiff_t
-EmitTraceOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
+EmitLoopHead(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
 {
     if (nextpn) {
         /*
          * Try to give the JSOP_LOOPHEAD the same line number as the next
          * instruction. nextpn is often a block, in which case the next
          * instruction typically comes from the first statement inside.
          */
         JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
@@ -552,16 +552,31 @@ EmitTraceOp(JSContext *cx, BytecodeEmitt
             nextpn = nextpn->pn_head;
         if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
             return -1;
     }
 
     return Emit1(cx, bce, JSOP_LOOPHEAD);
 }
 
+static bool
+EmitLoopEntry(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
+{
+    if (nextpn) {
+        /* Update the line number, as for LOOPHEAD. */
+        JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
+        if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
+            nextpn = nextpn->pn_head;
+        if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
+            return false;
+    }
+
+    return Emit1(cx, bce, JSOP_LOOPENTRY) >= 0;
+}
+
 /*
  * If op is JOF_TYPESET (see the type barriers comment in jsinfer.h), reserve
  * a type set to store its result.
  */
 static inline void
 CheckTypeSet(JSContext *cx, BytecodeEmitter *bce, JSOp op)
 {
     if (js_CodeSpec[op].format & JOF_TYPESET) {
@@ -4826,17 +4841,17 @@ EmitForIn(JSContext *cx, BytecodeEmitter
      * least one iteration, as the other loop forms do.
      */
     ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
     if (jmp < 0)
         return false;
 
     top = bce->offset();
     SET_STATEMENT_TOP(&stmtInfo, top);
-    if (EmitTraceOp(cx, bce, NULL) < 0)
+    if (EmitLoopHead(cx, bce, NULL) < 0)
         return false;
 
 #ifdef DEBUG
     intN loopDepth = bce->stackDepth;
 #endif
 
     /*
      * Emit code to get the next enumeration value and assign it to the
@@ -4869,16 +4884,18 @@ EmitForIn(JSContext *cx, BytecodeEmitter
     do {
         stmt->update = bce->offset();
     } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
 
     /*
      * Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
      */
     SetJumpOffsetAt(bce, jmp);
+    if (!EmitLoopEntry(cx, bce, NULL))
+        return false;
     if (Emit1(cx, bce, JSOP_MOREITER) < 0)
         return false;
     ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
     if (beq < 0)
         return false;
 
     /* Set the first srcnote offset so we can find the start of the loop body. */
     if (!SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, tmp2 - jmp))
@@ -4973,17 +4990,19 @@ EmitNormalFor(JSContext *cx, BytecodeEmi
         if (jmp < 0)
             return false;
     }
 
     top = bce->offset();
     SET_STATEMENT_TOP(&stmtInfo, top);
 
     /* Emit code for the loop body. */
-    if (EmitTraceOp(cx, bce, forBody) < 0)
+    if (EmitLoopHead(cx, bce, forBody) < 0)
+        return false;
+    if (jmp == -1 && !EmitLoopEntry(cx, bce, forBody))
         return false;
     if (!EmitTree(cx, bce, forBody))
         return false;
 
     /* Set the second note offset so we can find the update part. */
     JS_ASSERT(noteIndex != -1);
     ptrdiff_t tmp2 = bce->offset();
 
@@ -5021,16 +5040,18 @@ EmitNormalFor(JSContext *cx, BytecodeEmi
     }
 
     ptrdiff_t tmp3 = bce->offset();
 
     if (forHead->pn_kid2) {
         /* Fix up the goto from top to target the loop condition. */
         JS_ASSERT(jmp >= 0);
         SetJumpOffsetAt(bce, jmp);
+        if (!EmitLoopEntry(cx, bce, forHead->pn_kid2))
+            return false;
 
         if (!EmitTree(cx, bce, forHead->pn_kid2))
             return false;
     }
 
     /* Set the first note offset so we can find the loop condition. */
     if (!SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, tmp3 - tmp))
         return false;
@@ -5186,19 +5207,21 @@ EmitDo(JSContext *cx, BytecodeEmitter *b
     if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
         return false;
 
     ptrdiff_t noteIndex2 = NewSrcNote(cx, bce, SRC_WHILE);
     if (noteIndex2 < 0)
         return false;
 
     /* Compile the loop body. */
-    ptrdiff_t top = EmitTraceOp(cx, bce, pn->pn_left);
+    ptrdiff_t top = EmitLoopHead(cx, bce, pn->pn_left);
     if (top < 0)
         return false;
+    if (!EmitLoopEntry(cx, bce, NULL))
+        return false;
 
     StmtInfo stmtInfo;
     PushStatement(bce, &stmtInfo, STMT_DO_LOOP, top);
     if (!EmitTree(cx, bce, pn->pn_left))
         return false;
 
     /* Set loop and enclosing label update offsets, for continue. */
     ptrdiff_t off = bce->offset();
@@ -5254,24 +5277,26 @@ EmitWhile(JSContext *cx, BytecodeEmitter
     ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
     if (noteIndex < 0)
         return false;
 
     ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
     if (jmp < 0)
         return false;
 
-    top = EmitTraceOp(cx, bce, pn->pn_right);
+    top = EmitLoopHead(cx, bce, pn->pn_right);
     if (top < 0)
         return false;
 
     if (!EmitTree(cx, bce, pn->pn_right))
         return false;
 
     SetJumpOffsetAt(bce, jmp);
+    if (!EmitLoopEntry(cx, bce, pn->pn_left))
+        return false;
     if (!EmitTree(cx, bce, pn->pn_left))
         return false;
 
     ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
     if (beq < 0)
         return false;
 
     if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, beq - jmp))
@@ -6492,22 +6517,24 @@ frontend::EmitTree(JSContext *cx, Byteco
       {
         JS_ASSERT(!bce->inStrictMode());
 
         if (!EmitTree(cx, bce, pn->pn_left))
             return JS_FALSE;
         ptrdiff_t jmp = EmitJump(cx, bce, JSOP_FILTER, 0);
         if (jmp < 0)
             return JS_FALSE;
-        top = EmitTraceOp(cx, bce, pn->pn_right);
+        top = EmitLoopHead(cx, bce, pn->pn_right);
         if (top < 0)
             return JS_FALSE;
         if (!EmitTree(cx, bce, pn->pn_right))
             return JS_FALSE;
         SetJumpOffsetAt(bce, jmp);
+        if (!EmitLoopEntry(cx, bce, NULL))
+            return false;
         if (EmitJump(cx, bce, JSOP_ENDFILTER, top - bce->offset()) < 0)
             return JS_FALSE;
         break;
       }
 #endif
 
       case PNK_DOT:
         /*
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -810,24 +810,26 @@ ScriptAnalysis::analyzeLifetimes(JSConte
                 uint32_t entry = targetOffset;
                 if (entry) {
                     do {
                         entry--;
                     } while (!maybeCode(entry));
 
                     jsbytecode *entrypc = script->code + entry;
 
-                    if (JSOp(*entrypc) == JSOP_GOTO)
+                    if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_FILTER)
                         loop->entry = entry + GET_JUMP_OFFSET(entrypc);
                     else
                         loop->entry = targetOffset;
                 } else {
                     /* Do-while loop at the start of the script. */
                     loop->entry = targetOffset;
                 }
+                JS_ASSERT(script->code[loop->entry] == JSOP_LOOPHEAD ||
+                          script->code[loop->entry] == JSOP_LOOPENTRY);
             } else {
                 for (unsigned i = 0; i < savedCount; i++) {
                     LifetimeVariable &var = *saved[i];
                     JS_ASSERT(!var.lifetime && var.saved);
                     if (var.live(targetOffset)) {
                         /*
                          * Jumping to a place where this variable is live. Make a new
                          * lifetime segment for the variable.
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3425,16 +3425,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
     /* Add type constraints for the various opcodes. */
     switch (op) {
 
         /* Nop bytecodes. */
       case JSOP_POP:
       case JSOP_NOP:
       case JSOP_LOOPHEAD:
+      case JSOP_LOOPENTRY:
       case JSOP_GOTO:
       case JSOP_IFEQ:
       case JSOP_IFNE:
       case JSOP_LINENO:
       case JSOP_DEFCONST:
       case JSOP_LEAVEWITH:
       case JSOP_LEAVEBLOCK:
       case JSOP_RETRVAL:
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1798,16 +1798,17 @@ ADD_EMPTY_CASE(JSOP_UNUSED12)
 ADD_EMPTY_CASE(JSOP_UNUSED13)
 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
 ADD_EMPTY_CASE(JSOP_TRY)
 #if JS_HAS_XML_SUPPORT
 ADD_EMPTY_CASE(JSOP_STARTXML)
 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
 #endif
 ADD_EMPTY_CASE(JSOP_LOOPHEAD)
+ADD_EMPTY_CASE(JSOP_LOOPENTRY)
 END_EMPTY_CASES
 
 BEGIN_CASE(JSOP_LABEL)
 END_CASE(JSOP_LABEL)
 
 check_backedge:
 {
     CHECK_BRANCH();
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -3916,16 +3916,18 @@ Decompile(SprintStack *ss, jsbytecode *p
                      * We never decompile the obligatory JSOP_POP,
                      * JSOP_MOREITER or JSOP_IFNE, though we do quick asserts
                      * to check that they are there.
                      */
                     cond = GET_JUMP_OFFSET(pc);
                     next = js_GetSrcNoteOffset(sn, 0);
                     tail = js_GetSrcNoteOffset(sn, 1);
                     JS_ASSERT(pc[next] == JSOP_POP);
+                    JS_ASSERT(pc[cond] == JSOP_LOOPENTRY);
+                    cond += JSOP_LOOPENTRY_LENGTH;
                     JS_ASSERT(pc[cond] == JSOP_MOREITER);
                     DECOMPILE_CODE(pc + oplen, next - oplen);
                     lval = POP_STR();
 
                     /*
                      * This string "<next>" comes from jsopcode.tbl. It stands
                      * for the result pushed by JSOP_ITERNEXT.
                      */
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -567,8 +567,11 @@ OPDEF(JSOP_INITMETHOD,    223,"initmetho
 
 OPDEF(JSOP_SHARPINIT,     224,"sharpinit",     NULL,  3,  0,  0,  0,  JOF_UINT16|JOF_SHARPSLOT)
 
 /* Pop the stack, convert to a jsid (int or string), and push back. */
 OPDEF(JSOP_TOID,          225, "toid",         NULL,  1,  1,  1,  0,  JOF_BYTE)
 
 /* Push the implicit 'this' value for calls to the associated name. */
 OPDEF(JSOP_IMPLICITTHIS,  226, "implicitthis", "",    3,  0,  1,  0,  JOF_ATOM)
+
+/* This opcode is the target of the entry jump for some loop. */
+OPDEF(JSOP_LOOPENTRY,     227, "loopentry",    NULL,  1,  0,  0,  0,  JOF_BYTE)
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -221,17 +221,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 103)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 104)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 JS_END_EXTERN_C
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -3221,16 +3221,19 @@ mjit::Compiler::generateMethod()
           {
             if (analysis->jumpTarget(PC)) {
                 interruptCheckHelper();
                 recompileCheckHelper();
             }
           }
           END_CASE(JSOP_LOOPHEAD)
 
+          BEGIN_CASE(JSOP_LOOPENTRY)
+          END_CASE(JSOP_LOOPENTRY)
+
           BEGIN_CASE(JSOP_DEBUGGER)
           {
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(PC), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::DebuggerStatement, REJOIN_FALLTHROUGH);
           }
           END_CASE(JSOP_DEBUGGER)
 
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -1892,16 +1892,17 @@ LoopState::analyzeLoopBody(unsigned fram
           }
 
           case JSOP_ENUMELEM:
           case JSOP_ENUMCONSTELEM:
             unknownModset = true;
             break;
 
           case JSOP_LOOPHEAD:
+          case JSOP_LOOPENTRY:
           case JSOP_POP:
           case JSOP_ZERO:
           case JSOP_ONE:
           case JSOP_INT8:
           case JSOP_INT32:
           case JSOP_UINT16:
           case JSOP_UINT24:
           case JSOP_FALSE: