Bug 927782 - Part 3: Add DEBUGLEAVEBLOCK opcode to invalidate live DebugScopes. r=luke
authorAndy Wingo <wingo@igalia.com>
Mon, 25 Nov 2013 12:19:58 +0100
changeset 177433 98190772bfebf794b2e590b6a42a225435521d3a
parent 177432 963a687c1cca3e3350a59b346b83be1b1589162c
child 177434 e2c007db70cf8e164a2650d7be780f2f2411bcef
push id462
push userraliiev@mozilla.com
push dateTue, 22 Apr 2014 00:22:30 +0000
treeherdermozilla-release@ac5db8c74ac0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs927782
milestone29.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 927782 - Part 3: Add DEBUGLEAVEBLOCK opcode to invalidate live DebugScopes. r=luke
js/src/frontend/BytecodeEmitter.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/BaselineFrame-inl.h
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jsopcode.tbl
js/src/vm/Interpreter.cpp
js/src/vm/Stack.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -583,28 +583,32 @@ EmitNonLocalJumpFixup(ExclusiveContext *
                  * For a for-let-in/of statement, pushing/popping the block is
                  * interleaved with JSOP_(END)ITER. Just handle both together
                  * here and skip over the enclosing STMT_FOR_IN_LOOP.
                  */
                 unsigned popCount = blockObjCount;
                 stmt = stmt->down;
                 if (stmt == toStmt)
                     break;
+                if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
+                    return false;
                 if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
                     return false;
                 if (stmt->type == STMT_FOR_OF_LOOP) {
                     popCount += 2;
                 } else {
                     JS_ASSERT(stmt->type == STMT_FOR_IN_LOOP);
                     if (!PopIterator(cx, bce))
                         return false;
                 }
                 EMIT_UINT16_IMM_OP(JSOP_POPN, popCount);
             } else {
                 /* There is a Block object with locals on the stack to pop. */
+                if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
+                    return false;
                 EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
             }
         }
     }
 
     FLUSH_POPS();
     bce->stackDepth = depth;
     return true;
@@ -2617,18 +2621,21 @@ EmitSwitch(ExclusiveContext *cx, Bytecod
             SET_JUMP_OFFSET(pc, off);
             pc += JUMP_OFFSET_LEN;
         }
     }
 
     if (!PopStatementBCE(cx, bce))
         return false;
 
-    if (pn->pn_right->isKind(PNK_LEXICALSCOPE))
+    if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) {
+        if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
+            return false;
         EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
+    }
 
     return true;
 }
 
 bool
 BytecodeEmitter::isRunOnceLambda()
 {
     // The run once lambda flags set by the parser are approximate, and we look
@@ -3936,16 +3943,18 @@ EmitTry(ExclusiveContext *cx, BytecodeEm
                 bce->stackDepth = depth + count + 1;
 
                 /*
                  * Move exception back to cx->exception to prepare for
                  * the next catch.
                  */
                 if (Emit1(cx, bce, JSOP_THROWING) < 0)
                     return false;
+                if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
+                    return false;
                 EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
                 JS_ASSERT(bce->stackDepth == depth);
             }
 
             /*
              * Emit the lexical scope and catch body.  Save the catch's
              * block object population via count, for use when targeting
              * guardJump at the next catch (the guard mismatch case).
@@ -4195,16 +4204,18 @@ EmitLet(ExclusiveContext *cx, BytecodeEm
 
     DebugOnly<ptrdiff_t> bodyBegin = bce->offset();
     if (!EmitEnterBlock(cx, bce, letBody, JSOP_ENTERLET0))
         return false;
 
     if (!EmitTree(cx, bce, letBody->pn_expr))
         return false;
 
+    if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
+        return false;
     JSOp leaveOp = letBody->getOp();
     JS_ASSERT(leaveOp == JSOP_LEAVEBLOCK || leaveOp == JSOP_LEAVEBLOCKEXPR);
     EMIT_UINT16_IMM_OP(leaveOp, blockObj->slotCount());
 
     DebugOnly<ptrdiff_t> bodyEnd = bce->offset();
     JS_ASSERT(bodyEnd > bodyBegin);
 
     return PopStatementBCE(cx, bce);
@@ -4228,16 +4239,18 @@ EmitLexicalScope(ExclusiveContext *cx, B
         return false;
 
     if (!EmitEnterBlock(cx, bce, pn, JSOP_ENTERBLOCK))
         return false;
 
     if (!EmitTree(cx, bce, pn->pn_expr))
         return false;
 
+    if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
+        return false;
     EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, slots);
 
     return PopStatementBCE(cx, bce);
 }
 
 static bool
 EmitWith(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
@@ -4405,16 +4418,18 @@ EmitForOf(ExclusiveContext *cx, Bytecode
 
     // Fixup breaks and continues.
     if (!PopStatementBCE(cx, bce))
         return false;
 
     if (letDecl) {
         if (!PopStatementBCE(cx, bce))
             return false;
+        if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
+            return false;
         if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
             return false;
     }
 
     // Pop result, iter, and slots from the lexical block (if any).
     EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount + 2);
 
     return true;
@@ -4564,16 +4579,18 @@ EmitForIn(ExclusiveContext *cx, Bytecode
 
     /* Fixup breaks and continues before JSOP_ITER (and JSOP_LEAVEFORINLET). */
     if (!PopStatementBCE(cx, bce))
         return false;
 
     if (letDecl) {
         if (!PopStatementBCE(cx, bce))
             return false;
+        if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
+            return false;
         if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
             return false;
     }
 
     if (!bce->tryNoteList.append(JSTRY_ITER, bce->stackDepth, top, bce->offset()))
         return false;
     if (Emit1(cx, bce, JSOP_ENDITER) < 0)
         return false;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2620,16 +2620,31 @@ BaselineCompiler::emit_JSOP_ENTERLET1()
 }
 
 bool
 BaselineCompiler::emit_JSOP_ENTERLET2()
 {
     return emitEnterBlock();
 }
 
+typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *);
+static const VMFunction DebugLeaveBlockInfo = FunctionInfo<DebugLeaveBlockFn>(jit::DebugLeaveBlock);
+
+bool
+BaselineCompiler::emit_JSOP_DEBUGLEAVEBLOCK()
+{
+    // Call a stub to pop the block from the block chain.
+    prepareVMCall();
+
+    masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
+    pushArg(R0.scratchReg());
+
+    return callVM(DebugLeaveBlockInfo);
+}
+
 typedef bool (*LeaveBlockFn)(JSContext *, BaselineFrame *);
 static const VMFunction LeaveBlockInfo = FunctionInfo<LeaveBlockFn>(jit::LeaveBlock);
 
 bool
 BaselineCompiler::emitLeaveBlock()
 {
     // Call a stub to pop the block from the block chain.
     prepareVMCall();
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -147,16 +147,17 @@ namespace jit {
     _(JSOP_RETSUB)             \
     _(JSOP_ENTERBLOCK)         \
     _(JSOP_ENTERLET0)          \
     _(JSOP_ENTERLET1)          \
     _(JSOP_ENTERLET2)          \
     _(JSOP_LEAVEBLOCK)         \
     _(JSOP_LEAVEBLOCKEXPR)     \
     _(JSOP_LEAVEFORLETIN)      \
+    _(JSOP_DEBUGLEAVEBLOCK)    \
     _(JSOP_EXCEPTION)          \
     _(JSOP_DEBUGGER)           \
     _(JSOP_ARGUMENTS)          \
     _(JSOP_RUNONCE)            \
     _(JSOP_REST)               \
     _(JSOP_TOID)               \
     _(JSOP_TABLESWITCH)        \
     _(JSOP_ITER)               \
--- a/js/src/jit/BaselineFrame-inl.h
+++ b/js/src/jit/BaselineFrame-inl.h
@@ -50,19 +50,16 @@ BaselineFrame::pushBlock(JSContext *cx, 
     return true;
 }
 
 inline void
 BaselineFrame::popBlock(JSContext *cx)
 {
     JS_ASSERT(hasBlockChain());
 
-    if (cx->compartment()->debugMode())
-        DebugScopes::onPopBlock(cx, this);
-
     if (blockChain_->needsClone()) {
         JS_ASSERT(scopeChain_->as<ClonedBlockObject>().staticBlock() == *blockChain_);
         popOffScopeChain();
     }
 
     setBlockChain(*blockChain_->enclosingBlock());
 }
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -890,16 +890,27 @@ EnterBlock(JSContext *cx, BaselineFrame 
 bool
 LeaveBlock(JSContext *cx, BaselineFrame *frame)
 {
     frame->popBlock(cx);
     return true;
 }
 
 bool
+DebugLeaveBlock(JSContext *cx, BaselineFrame *frame)
+{
+    JS_ASSERT(frame->hasBlockChain());
+
+    if (JS_UNLIKELY(cx->compartment()->debugMode()))
+        DebugScopes::onPopBlock(cx, frame);
+
+    return true;
+}
+
+bool
 InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t numStackValues)
 {
     return frame->initForOsr(interpFrame, numStackValues);
 }
 
 JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject type,
                                 HandleObject owner, int32_t offset)
 {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -649,16 +649,17 @@ bool NewArgumentsObject(JSContext *cx, B
 JSObject *InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleObject templateObj,
                             HandleObject res);
 
 bool HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, bool *mustReturn);
 bool OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn);
 
 bool EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
 bool LeaveBlock(JSContext *cx, BaselineFrame *frame);
+bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame);
 
 bool InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame,
                              uint32_t numStackValues);
 
 JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject type,
                                 HandleObject owner, int32_t offset);
 
 bool Recompile(JSContext *cx);
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -433,19 +433,18 @@ OPDEF(JSOP_UNUSED196,     196,"unused196
  */
 OPDEF(JSOP_TYPEOFEXPR,    197,"typeofexpr",  NULL,    1,  1,  1, JOF_BYTE|JOF_DETECTING)
 
 /*
  * Block-local scope support.
  */
 OPDEF(JSOP_ENTERBLOCK,    198,"enterblock",  NULL,    5,  0, -1,  JOF_OBJECT)
 OPDEF(JSOP_LEAVEBLOCK,    199,"leaveblock",  NULL,    3, -1,  0,  JOF_UINT16)
-
+OPDEF(JSOP_DEBUGLEAVEBLOCK, 200,"debugleaveblock", NULL, 1,  0,  0,  JOF_BYTE)
 
-OPDEF(JSOP_UNUSED200,     200,"unused200",  NULL,     1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED201,     201,"unused201",  NULL,     1,  0,  0,  JOF_BYTE)
 
 /*
  * Generator and array comprehension support.
  */
 OPDEF(JSOP_GENERATOR,     202,"generator",   NULL,    1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_YIELD,         203,"yield",       NULL,    1,  1,  1,  JOF_BYTE)
 OPDEF(JSOP_ARRAYPUSH,     204,"arraypush",   NULL,    3,  1,  0,  JOF_LOCAL)
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -850,16 +850,18 @@ js::UnwindScope(JSContext *cx, AbstractF
     JS_ASSERT_IF(frame.isStackFrame(), frame.asStackFrame() == cx->interpreterFrame());
     JS_ASSERT_IF(frame.isStackFrame(), stackDepth <= cx->interpreterRegs().stackDepth());
 
     for (ScopeIter si(frame, cx); !si.done(); ++si) {
         switch (si.type()) {
           case ScopeIter::Block:
             if (si.staticBlock().stackDepth() < stackDepth)
                 return;
+            if (cx->compartment()->debugMode())
+                DebugScopes::onPopBlock(cx, frame);
             frame.popBlock(cx);
             break;
           case ScopeIter::With:
             if (si.scope().as<WithObject>().stackDepth() < stackDepth)
                 return;
             frame.popWith(cx);
             break;
           case ScopeIter::Call:
@@ -1528,17 +1530,16 @@ CASE(JSOP_UNUSED181)
 CASE(JSOP_UNUSED182)
 CASE(JSOP_UNUSED183)
 CASE(JSOP_UNUSED189)
 CASE(JSOP_UNUSED190)
 CASE(JSOP_UNUSED191)
 CASE(JSOP_UNUSED192)
 CASE(JSOP_UNUSED194)
 CASE(JSOP_UNUSED196)
-CASE(JSOP_UNUSED200)
 CASE(JSOP_UNUSED201)
 CASE(JSOP_GETFUNNS)
 CASE(JSOP_UNUSED208)
 CASE(JSOP_UNUSED209)
 CASE(JSOP_UNUSED210)
 CASE(JSOP_UNUSED219)
 CASE(JSOP_UNUSED220)
 CASE(JSOP_UNUSED221)
@@ -3263,16 +3264,28 @@ CASE(JSOP_LEAVEBLOCKEXPR)
         REGS.sp[-1] = *vp;
     } else {
         /* Another op will pop; nothing to do here. */
         ADVANCE_AND_DISPATCH(JSOP_LEAVEFORLETIN_LENGTH);
     }
 }
 END_CASE(JSOP_LEAVEBLOCK)
 
+CASE(JSOP_DEBUGLEAVEBLOCK)
+{
+    JS_ASSERT(REGS.fp()->hasBlockChain());
+
+    // FIXME: This opcode should not be necessary.  The debugger shouldn't need
+    // help from bytecode to do its job.  See bug 927782.
+
+    if (JS_UNLIKELY(cx->compartment()->debugMode()))
+        DebugScopes::onPopBlock(cx, REGS.fp());
+}
+END_CASE(JSOP_DEBUGLEAVEBLOCK)
+
 CASE(JSOP_GENERATOR)
 {
     JS_ASSERT(!cx->isExceptionPending());
     REGS.fp()->initGeneratorFrame();
     REGS.pc += JSOP_GENERATOR_LENGTH;
     JSObject *obj = js_NewGenerator(cx, REGS);
     if (!obj)
         goto error;
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -367,21 +367,16 @@ StackFrame::pushBlock(JSContext *cx, Sta
     }
 
     return true;
 }
 
 void
 StackFrame::popBlock(JSContext *cx)
 {
-    JS_ASSERT(hasBlockChain());
-
-    if (JS_UNLIKELY(cx->compartment()->debugMode()))
-        DebugScopes::onPopBlock(cx, this);
-
     if (blockChain_->needsClone()) {
         JS_ASSERT(scopeChain_->as<ClonedBlockObject>().staticBlock() == *blockChain_);
         popOffScopeChain();
     }
 
     blockChain_ = blockChain_->enclosingBlock();
 }