Bug 927782 - Part 3: Add DEBUGLEAVEBLOCK opcode to invalidate live DebugScopes. r=luke
☠☠ backed out by 94cdaced90bf ☠ ☠
authorAndy Wingo <wingo@igalia.com>
Mon, 25 Nov 2013 12:19:58 +0100
changeset 173966 fc7a979712fc7b52f35125e8c15a85ed33f18c6c
parent 173965 c8304ccf88e90c17beed5c26fbae1782704c3cd6
child 173967 cbdd50c96b858458ea7b3bfd6b5335ddde65b4c9
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs927782
milestone28.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;
@@ -2615,18 +2619,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
@@ -3934,16 +3941,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).
@@ -4193,16 +4202,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);
@@ -4226,16 +4237,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)
 {
@@ -4403,16 +4416,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;
@@ -4562,16 +4577,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
@@ -2594,16 +2594,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
@@ -906,16 +906,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);
 
 } // namespace jit
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -435,19 +435,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();
 }