Bug 927782 - Part 11: Optimize block scopes without aliased locals. r=luke

This commit is contained in:
Andy Wingo 2013-11-26 12:07:02 +01:00
parent 52ca0ec4da
commit 4ba25cc8a5
18 changed files with 234 additions and 317 deletions

View File

@ -141,15 +141,6 @@ EmitCheck(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
return offset; return offset;
} }
static StaticBlockObject &
LastBlockAdded(BytecodeEmitter *bce, jsbytecode *pc)
{
DebugOnly<uint32_t> index = GET_UINT32_INDEX(pc);
JS_ASSERT(index < bce->objectList.length);
JS_ASSERT(index == bce->objectList.length - 1);
return bce->objectList.lastbox->object->as<StaticBlockObject>();
}
static void static void
UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target) UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
{ {
@ -168,26 +159,8 @@ UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
bce->maxStackDepth = depth; bce->maxStackDepth = depth;
} }
/* int nuses = StackUses(nullptr, pc);
* Specially handle any case in which StackUses or StackDefs would call int ndefs = StackDefs(nullptr, pc);
* NumBlockSlots, since that requires a well-formed script. This allows us
* to safely pass nullptr as the 'script' parameter to StackUses and
* StackDefs.
*/
int nuses, ndefs;
if (op == JSOP_ENTERBLOCK) {
nuses = 0;
ndefs = LastBlockAdded(bce, pc).slotCount();
} else if (op == JSOP_ENTERLET0) {
nuses = ndefs = LastBlockAdded(bce, pc).slotCount();
} else if (op == JSOP_ENTERLET1) {
nuses = ndefs = LastBlockAdded(bce, pc).slotCount() + 1;
} else if (op == JSOP_ENTERLET2) {
nuses = ndefs = LastBlockAdded(bce, pc).slotCount() + 2;
} else {
nuses = StackUses(nullptr, pc);
ndefs = StackDefs(nullptr, pc);
}
bce->stackDepth -= nuses; bce->stackDepth -= nuses;
JS_ASSERT(bce->stackDepth >= 0); JS_ASSERT(bce->stackDepth >= 0);
@ -568,7 +541,10 @@ class NonLocalExitScope {
return false; return false;
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent)) if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
return false; return false;
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObj.slotCount()); if (blockObj.needsClone()) {
if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
return false;
}
openScopeIndex = bce->blockScopeList.length() - 1; openScopeIndex = bce->blockScopeList.length() - 1;
return true; return true;
} }
@ -623,11 +599,11 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
} }
if (stmt->isBlockScope) { if (stmt->isBlockScope) {
FLUSH_POPS();
JS_ASSERT(stmt->blockObj); JS_ASSERT(stmt->blockObj);
StaticBlockObject &blockObj = *stmt->blockObj; StaticBlockObject &blockObj = *stmt->blockObj;
if (!popScopeForNonLocalExit(blockObj, stmt->blockScopeIndex)) if (!popScopeForNonLocalExit(blockObj, stmt->blockScopeIndex))
return false; return false;
npops += blockObj.slotCount();
} }
} }
@ -773,6 +749,55 @@ ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, StaticBlockObjec
static bool static bool
EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmitter *bce); EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmitter *bce);
// ~ Block Scopes ~
//
// A block scope is a region of a script with an additional set of named
// variables. Those variables may be local and thus accessible directly from
// the stack, or "aliased" and only accessible through the scope chain.
//
// A block scope may or may not have a corresponding link on the scope chain.
// If no variable declared in the scope is "aliased", then no scope chain node
// is allocated.
//
// To help debuggers, the bytecode emitter arranges to record the PC ranges
// comprehended by a block scope, and ultimately attach them to the JSScript.
// An element in the "block scope array" specifies the PC range, and links to a
// StaticBlockObject in the object list of the script. That block is linked to
// the previous block in the scope, if any. The static block chain at any
// pre-retire PC can be retrieved using JSScript::getBlockScope(jsbytecode *pc).
//
// When PUSHBLOCKSCOPE is executed, it assumes that the block's locals are
// already on the stack. Initial values of "aliased" locals are copied from the
// stack to the ClonedBlockObject, and no further access is made to the stack
// slot.
//
// Likewise after leaving a POPBLOCKSCOPE, we will need to emit code to pop the
// stack values.
//
// Finally, to assist the debugger, we also emit a DEBUGLEAVEBLOCK opcode before
// POPBLOCKSCOPE in all cases -- even if the block has no aliased locals. This
// allows DebugScopes to invalidate any association between a debugger scope
// object, which can proxy access to unaliased stack locals, and the actual live
// frame. In normal, non-debug mode, this opcode does not cause any baseline
// code to be emitted.
//
// In this function "extraSlots" indicates the number of slots that are
// "floating" on the stack above the scope's slots. This will only be nonzero
// in the case of for-let-in and for-let-of loops, where loop iterator state
// floats above the block scopes. It would be nice to fix this eventually so
// that loop iterator state gets assigned to block-scoped fp-addressable
// temporaries, instead of being addressable only via the sp. This would also
// make generators more efficient, as the loop state could be heap-allocated, so
// that the value stack would likely be empty at yield points inside for-of /
// for-in loops.
//
// Summary: Enter block scopes with EnterBlockScope. It will emit
// PUSHBLOCKSCOPE if needed. Leave them with LeaveBlockScope, which will emit
// DEBUGLEAVEBLOCK and may emit POPBLOCKSCOPE. Pass EnterBlockScope a fresh
// StmtInfoBCE object, and pass that same object to the corresponding
// LeaveBlockScope. Push locals before entering a scope, and pop them
// afterwards. Brush your teeth, and clean behind your ears!
//
static bool static bool
EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox, EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
unsigned extraSlots) unsigned extraSlots)
@ -792,19 +817,13 @@ EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, O
JS_ASSERT(depth >= 0); JS_ASSERT(depth >= 0);
blockObj.setStackDepth(depth); blockObj.setStackDepth(depth);
JSOp op;
switch (extraSlots) {
case 0: op = JSOP_ENTERLET0; break;
case 1: op = JSOP_ENTERLET1; break;
case 2: op = JSOP_ENTERLET2; break;
default: MOZ_ASSUME_UNREACHABLE("unexpected extraSlots");
}
if (!ComputeAliasedSlots(cx, bce, blockObj)) if (!ComputeAliasedSlots(cx, bce, blockObj))
return false; return false;
if (!EmitInternedObjectOp(cx, scopeObjectIndex, op, bce)) if (blockObj.needsClone()) {
return false; if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_PUSHBLOCKSCOPE, bce))
return false;
}
stmt->blockScopeIndex = bce->blockScopeList.length(); stmt->blockScopeIndex = bce->blockScopeList.length();
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent)) if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
@ -837,7 +856,7 @@ PopStatementBCE(ExclusiveContext *cx, BytecodeEmitter *bce)
} }
static bool static bool
LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op) LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce)
{ {
StmtInfoBCE *stmt = bce->topStmt; StmtInfoBCE *stmt = bce->topStmt;
JS_ASSERT(stmt->isBlockScope); JS_ASSERT(stmt->isBlockScope);
@ -852,7 +871,7 @@ LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
JS_ASSERT(blockObj == bce->blockChain); JS_ASSERT(blockObj == bce->blockChain);
#endif #endif
uint32_t slotCount = bce->blockChain->slotCount(); bool blockOnChain = bce->blockChain->needsClone();
if (!PopStatementBCE(cx, bce)) if (!PopStatementBCE(cx, bce))
return false; return false;
@ -862,8 +881,10 @@ LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
bce->blockScopeList.recordEnd(blockScopeIndex, bce->offset()); bce->blockScopeList.recordEnd(blockScopeIndex, bce->offset());
JS_ASSERT(op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR); if (blockOnChain) {
EMIT_UINT16_IMM_OP(op, slotCount); if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
return false;
}
return true; return true;
} }
@ -2684,10 +2705,10 @@ EmitSwitch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
} }
} }
if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) { if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) {
if (!LeaveBlockScope(cx, bce, JSOP_LEAVEBLOCK)) if (!LeaveBlockScope(cx, bce))
return false; return false;
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObj->slotCount());
} else { } else {
if (!PopStatementBCE(cx, bce)) if (!PopStatementBCE(cx, bce))
return false; return false;
@ -3983,7 +4004,8 @@ EmitTry(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (ParseNode *pn2 = pn->pn_kid2) { if (ParseNode *pn2 = pn->pn_kid2) {
// The emitted code for a catch block looks like: // The emitted code for a catch block looks like:
// //
// enterblock // undefined... as many as there are locals in the catch block
// [pushblockscope] only if any local aliased
// exception // exception
// if there is a catchguard: // if there is a catchguard:
// dup // dup
@ -3991,14 +4013,16 @@ EmitTry(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
// if there is a catchguard: // if there is a catchguard:
// < catchguard code > // < catchguard code >
// ifne POST // ifne POST
// throwing pop exception to cx->exception
// debugleaveblock // debugleaveblock
// leaveblock // [popblockscope] only if any local aliased
// popnv <num block locals> leave exception on top
// throwing pop exception to cx->exception
// goto <next catch block> // goto <next catch block>
// POST: pop // POST: pop
// < catch block contents > // < catch block contents >
// debugleaveblock // debugleaveblock
// leaveblock // [popblockscope] only if any local aliased
// popn <num block locals>
// goto <end of catch blocks> non-local; finally applies // goto <end of catch blocks> non-local; finally applies
// //
// If there's no catch block without a catchguard, the last <next catch // If there's no catch block without a catchguard, the last <next catch
@ -4194,11 +4218,13 @@ EmitIf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
* destructure z * destructure z
* pick 1 * pick 1
* pop -1 * pop -1
* enterlet0 * pushblockscope (if needed)
* evaluate e +1 * evaluate e +1
* leaveblockexpr -3 * debugleaveblock
* popblockscope (if needed)
* popnv 3 -3
* *
* Note that, since enterlet0 simply changes fp->blockChain and does not * Note that, since pushblockscope simply changes fp->scopeChain and does not
* otherwise touch the stack, evaluation of the let-var initializers must leave * otherwise touch the stack, evaluation of the let-var initializers must leave
* the initial value in the let-var's future slot. * the initial value in the let-var's future slot.
*/ */
@ -4236,9 +4262,13 @@ EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
if (!EmitTree(cx, bce, letBody->pn_expr)) if (!EmitTree(cx, bce, letBody->pn_expr))
return false; return false;
if (!LeaveBlockScope(cx, bce, letBody->getOp())) if (!LeaveBlockScope(cx, bce))
return false; return false;
JSOp leaveOp = letBody->getOp();
JS_ASSERT(leaveOp == JSOP_POPN || leaveOp == JSOP_POPNV);
EMIT_UINT16_IMM_OP(leaveOp, blockObj->slotCount());
return true; return true;
} }
@ -4250,7 +4280,7 @@ MOZ_NEVER_INLINE static bool
EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
{ {
JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE)); JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
JS_ASSERT(pn->getOp() == JSOP_LEAVEBLOCK); JS_ASSERT(pn->getOp() == JSOP_POPN);
StmtInfoBCE stmtInfo(cx); StmtInfoBCE stmtInfo(cx);
ObjectBox *objbox = pn->pn_objbox; ObjectBox *objbox = pn->pn_objbox;
@ -4268,9 +4298,11 @@ EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!EmitTree(cx, bce, pn->pn_expr)) if (!EmitTree(cx, bce, pn->pn_expr))
return false; return false;
if (!LeaveBlockScope(cx, bce, JSOP_LEAVEBLOCK)) if (!LeaveBlockScope(cx, bce))
return false; return false;
EMIT_UINT16_IMM_OP(JSOP_POPN, slots);
return true; return true;
} }
@ -4440,14 +4472,14 @@ EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
if (!PopStatementBCE(cx, bce)) if (!PopStatementBCE(cx, bce))
return false; return false;
// Pop result and iter.
EMIT_UINT16_IMM_OP(JSOP_POPN, 2);
if (letDecl) { if (letDecl) {
if (!LeaveBlockScope(cx, bce, JSOP_LEAVEBLOCK)) if (!LeaveBlockScope(cx, bce))
return false; return false;
} }
// Pop result, iter, and slots from the lexical block (if any).
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount + 2);
return true; return true;
} }
@ -4468,20 +4500,24 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
if (letDecl) { if (letDecl) {
/* /*
* The let's slot(s) will be under the iterator, but the block must not * The let's slot(s) will be under the iterator, but the block must not
* be entered (i.e. fp->blockChain set) until after evaluating the rhs. * be entered until after evaluating the rhs. So, we reserve space for
* Thus, push to reserve space and enterblock after. The same argument * the block scope now, and only push the block onto the scope chain
* applies when leaving the loop. Thus, a for-let-in loop looks like: * later. Thus, a for-let-in loop looks like:
* *
* push x N * push x N
* eval rhs * eval rhs
* iter * iter
* enterlet1 * pushblockscope (if needed)
* goto * goto
* ... loop body * ... loop body
* ifne * ifne
* leaveforinlet * debugleaveblock
* popblockscope (if needed)
* enditer * enditer
* popn(N) * popn(N)
*
* Note that pushblockscope and popblockscope only get emitted if some
* of the variables in the block are captured.
*/ */
for (uint32_t i = 0; i < blockObjCount; ++i) { for (uint32_t i = 0; i < blockObjCount; ++i) {
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
@ -4600,8 +4636,9 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
return false; return false;
if (letDecl) { if (letDecl) {
if (!LeaveBlockScope(cx, bce, JSOP_LEAVEBLOCK)) if (!LeaveBlockScope(cx, bce))
return false; return false;
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
} }
return true; return true;

View File

@ -603,9 +603,9 @@ FullParseHandler::addCatchBlock(ParseNode *catchList, ParseNode *letBlock,
inline void inline void
FullParseHandler::setLeaveBlockResult(ParseNode *block, ParseNode *kid, bool leaveBlockExpr) FullParseHandler::setLeaveBlockResult(ParseNode *block, ParseNode *kid, bool leaveBlockExpr)
{ {
JS_ASSERT(block->isOp(JSOP_LEAVEBLOCK)); JS_ASSERT(block->isOp(JSOP_POPN));
if (leaveBlockExpr) if (leaveBlockExpr)
block->setOp(JSOP_LEAVEBLOCKEXPR); block->setOp(JSOP_POPNV);
block->pn_expr = kid; block->pn_expr = kid;
} }
@ -637,7 +637,7 @@ FullParseHandler::newLexicalScope(ObjectBox *blockbox)
if (!pn) if (!pn)
return nullptr; return nullptr;
pn->setOp(JSOP_LEAVEBLOCK); pn->setOp(JSOP_POPN);
pn->pn_objbox = blockbox; pn->pn_objbox = blockbox;
pn->pn_cookie.makeFree(); pn->pn_cookie.makeFree();
pn->pn_dflags = 0; pn->pn_dflags = 0;

View File

@ -407,7 +407,7 @@ enum ParseNodeKind
* PNK_NULL, * PNK_NULL,
* PNK_THIS * PNK_THIS
* *
* PNK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR * PNK_LEXICALSCOPE name pn_op: JSOP_POPN or JSOP_POPNV
* pn_objbox: block object in ObjectBox holder * pn_objbox: block object in ObjectBox holder
* pn_expr: block body * pn_expr: block body
* PNK_ARRAYCOMP list pn_count: 1 * PNK_ARRAYCOMP list pn_count: 1

View File

@ -3231,7 +3231,6 @@ Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInf
if (!pn) if (!pn)
return null(); return null();
/* Tell codegen to emit JSOP_ENTERLETx (not JSOP_ENTERBLOCK). */
pn->pn_dflags |= PND_LET; pn->pn_dflags |= PND_LET;
/* Populate the new scope with decls found in the head with updated blockid. */ /* Populate the new scope with decls found in the head with updated blockid. */
@ -3601,7 +3600,7 @@ Parser<FullParseHandler>::letDeclaration()
if (!pn1) if (!pn1)
return null(); return null();
pn1->setOp(JSOP_LEAVEBLOCK); pn1->setOp(JSOP_POPN);
pn1->pn_pos = pc->blockNode->pn_pos; pn1->pn_pos = pc->blockNode->pn_pos;
pn1->pn_objbox = blockbox; pn1->pn_objbox = blockbox;
pn1->pn_expr = pc->blockNode; pn1->pn_expr = pc->blockNode;
@ -3637,8 +3636,8 @@ Parser<FullParseHandler>::letStatement()
if (tokenStream.peekToken() == TOK_LP) { if (tokenStream.peekToken() == TOK_LP) {
pn = letBlock(LetStatement); pn = letBlock(LetStatement);
JS_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI)); JS_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
JS_ASSERT_IF(pn && pn->isKind(PNK_LET) && pn->pn_expr->getOp() != JSOP_LEAVEBLOCK, JS_ASSERT_IF(pn && pn->isKind(PNK_LET) && pn->pn_expr->getOp() != JSOP_POPNV,
pn->isOp(JSOP_NOP)); pn->pn_expr->isOp(JSOP_POPN));
} else } else
pn = letDeclaration(); pn = letDeclaration();
return pn; return pn;
@ -6718,16 +6717,21 @@ Parser<ParseHandler>::arrayInitializer()
* *
* Each let () {...} or for (let ...) ... compiles to: * Each let () {...} or for (let ...) ... compiles to:
* *
* JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n> * JSOP_PUSHN <N> // Push space for block-scoped locals.
* (JSOP_PUSHBLOCKSCOPE <O>) // If a local is aliased, push on scope
* // chain.
* ...
* JSOP_DEBUGLEAVEBLOCK // Invalidate any DebugScope proxies.
* JSOP_POPBLOCKSCOPE? // Pop off scope chain, if needed.
* JSOP_POPN <N> // Pop space for block-scoped locals.
* *
* where <o> is a literal object representing the block scope, * where <o> is a literal object representing the block scope,
* with <n> properties, naming each var declared in the block. * with <n> properties, naming each var declared in the block.
* *
* Each var declaration in a let-block binds a name in <o> at * Each var declaration in a let-block binds a name in <o> at compile
* compile time, and allocates a slot on the operand stack at * time. A block-local var is accessed by the JSOP_GETLOCAL and
* runtime via JSOP_ENTERBLOCK. A block-local var is accessed by * JSOP_SETLOCAL ops. These ops have an immediate operand, the local
* the JSOP_GETLOCAL and JSOP_SETLOCAL ops. These ops have an * slot's stack index from fp->spbase.
* immediate operand, the local slot's stack index from fp->spbase.
* *
* The array comprehension iteration step, array.push(i * j) in * The array comprehension iteration step, array.push(i * j) in
* the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>, * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>,

View File

@ -860,6 +860,15 @@ BaselineCompiler::emit_JSOP_POPN()
return true; return true;
} }
bool
BaselineCompiler::emit_JSOP_POPNV()
{
frame.popRegsAndSync(1);
frame.popn(GET_UINT16(pc));
frame.push(R0);
return true;
}
bool bool
BaselineCompiler::emit_JSOP_DUP() BaselineCompiler::emit_JSOP_DUP()
{ {
@ -2575,23 +2584,14 @@ BaselineCompiler::emit_JSOP_RETSUB()
return emitOpIC(stubCompiler.getStub(&stubSpace_)); return emitOpIC(stubCompiler.getStub(&stubSpace_));
} }
typedef bool (*EnterBlockFn)(JSContext *, BaselineFrame *, Handle<StaticBlockObject *>); typedef bool (*PushBlockScopeFn)(JSContext *, BaselineFrame *, Handle<StaticBlockObject *>);
static const VMFunction EnterBlockInfo = FunctionInfo<EnterBlockFn>(jit::EnterBlock); static const VMFunction PushBlockScopeInfo = FunctionInfo<PushBlockScopeFn>(jit::PushBlockScope);
bool bool
BaselineCompiler::emitEnterBlock() BaselineCompiler::emit_JSOP_PUSHBLOCKSCOPE()
{ {
StaticBlockObject &blockObj = script->getObject(pc)->as<StaticBlockObject>(); StaticBlockObject &blockObj = script->getObject(pc)->as<StaticBlockObject>();
if (JSOp(*pc) == JSOP_ENTERBLOCK) {
for (size_t i = 0; i < blockObj.slotCount(); i++)
frame.push(UndefinedValue());
// Pushed values will be accessed using GETLOCAL and SETLOCAL, so ensure
// they are synced.
frame.syncStack(0);
}
// Call a stub to push the block on the block chain. // Call a stub to push the block on the block chain.
prepareVMCall(); prepareVMCall();
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
@ -2599,31 +2599,22 @@ BaselineCompiler::emitEnterBlock()
pushArg(ImmGCPtr(&blockObj)); pushArg(ImmGCPtr(&blockObj));
pushArg(R0.scratchReg()); pushArg(R0.scratchReg());
return callVM(EnterBlockInfo); return callVM(PushBlockScopeInfo);
} }
bool typedef bool (*PopBlockScopeFn)(JSContext *, BaselineFrame *);
BaselineCompiler::emit_JSOP_ENTERBLOCK() static const VMFunction PopBlockScopeInfo = FunctionInfo<PopBlockScopeFn>(jit::PopBlockScope);
{
return emitEnterBlock();
}
bool bool
BaselineCompiler::emit_JSOP_ENTERLET0() BaselineCompiler::emit_JSOP_POPBLOCKSCOPE()
{ {
return emitEnterBlock(); // Call a stub to pop the block from the block chain.
} prepareVMCall();
bool masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
BaselineCompiler::emit_JSOP_ENTERLET1() pushArg(R0.scratchReg());
{
return emitEnterBlock();
}
bool return callVM(PopBlockScopeInfo);
BaselineCompiler::emit_JSOP_ENTERLET2()
{
return emitEnterBlock();
} }
typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *, jsbytecode *); typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *, jsbytecode *);
@ -2643,56 +2634,6 @@ BaselineCompiler::emit_JSOP_DEBUGLEAVEBLOCK()
return callVM(DebugLeaveBlockInfo); 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();
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
pushArg(R0.scratchReg());
return callVM(LeaveBlockInfo);
}
bool
BaselineCompiler::emit_JSOP_LEAVEBLOCK()
{
if (!emitLeaveBlock())
return false;
// Pop slots pushed by JSOP_ENTERBLOCK.
frame.popn(GET_UINT16(pc));
return true;
}
bool
BaselineCompiler::emit_JSOP_LEAVEBLOCKEXPR()
{
if (!emitLeaveBlock())
return false;
// Pop slots pushed by JSOP_ENTERBLOCK, but leave the topmost value
// on the stack.
frame.popRegsAndSync(1);
frame.popn(GET_UINT16(pc));
frame.push(R0);
return true;
}
bool
BaselineCompiler::emit_JSOP_LEAVEFORLETIN()
{
if (!emitLeaveBlock())
return false;
// Another op will pop the slots (after the enditer).
return true;
}
typedef bool (*GetAndClearExceptionFn)(JSContext *, MutableHandleValue); typedef bool (*GetAndClearExceptionFn)(JSContext *, MutableHandleValue);
static const VMFunction GetAndClearExceptionInfo = static const VMFunction GetAndClearExceptionInfo =
FunctionInfo<GetAndClearExceptionFn>(GetAndClearException); FunctionInfo<GetAndClearExceptionFn>(GetAndClearException);

View File

@ -27,6 +27,7 @@ namespace jit {
_(JSOP_NOTEARG) \ _(JSOP_NOTEARG) \
_(JSOP_POP) \ _(JSOP_POP) \
_(JSOP_POPN) \ _(JSOP_POPN) \
_(JSOP_POPNV) \
_(JSOP_DUP) \ _(JSOP_DUP) \
_(JSOP_DUP2) \ _(JSOP_DUP2) \
_(JSOP_SWAP) \ _(JSOP_SWAP) \
@ -145,13 +146,8 @@ namespace jit {
_(JSOP_FINALLY) \ _(JSOP_FINALLY) \
_(JSOP_GOSUB) \ _(JSOP_GOSUB) \
_(JSOP_RETSUB) \ _(JSOP_RETSUB) \
_(JSOP_ENTERBLOCK) \ _(JSOP_PUSHBLOCKSCOPE) \
_(JSOP_ENTERLET0) \ _(JSOP_POPBLOCKSCOPE) \
_(JSOP_ENTERLET1) \
_(JSOP_ENTERLET2) \
_(JSOP_LEAVEBLOCK) \
_(JSOP_LEAVEBLOCKEXPR) \
_(JSOP_LEAVEFORLETIN) \
_(JSOP_DEBUGLEAVEBLOCK) \ _(JSOP_DEBUGLEAVEBLOCK) \
_(JSOP_EXCEPTION) \ _(JSOP_EXCEPTION) \
_(JSOP_DEBUGGER) \ _(JSOP_DEBUGGER) \
@ -255,9 +251,6 @@ class BaselineCompiler : public BaselineCompilerSpecific
bool emitFormalArgAccess(uint32_t arg, bool get); bool emitFormalArgAccess(uint32_t arg, bool get);
bool emitEnterBlock();
bool emitLeaveBlock();
bool addPCMappingEntry(bool addIndexEntry); bool addPCMappingEntry(bool addIndexEntry);
void getScopeCoordinateObject(Register reg); void getScopeCoordinateObject(Register reg);

View File

@ -36,31 +36,22 @@ BaselineFrame::popOffScopeChain()
inline bool inline bool
BaselineFrame::pushBlock(JSContext *cx, Handle<StaticBlockObject *> block) BaselineFrame::pushBlock(JSContext *cx, Handle<StaticBlockObject *> block)
{ {
JS_ASSERT_IF(hasBlockChain(), blockChain() == *block->enclosingBlock()); JS_ASSERT(block->needsClone());
if (block->needsClone()) { ClonedBlockObject *clone = ClonedBlockObject::create(cx, block, this);
ClonedBlockObject *clone = ClonedBlockObject::create(cx, block, this); if (!clone)
if (!clone) return false;
return false; pushOnScopeChain(*clone);
pushOnScopeChain(*clone);
}
setBlockChain(*block);
return true; return true;
} }
inline void inline void
BaselineFrame::popBlock(JSContext *cx) BaselineFrame::popBlock(JSContext *cx)
{ {
JS_ASSERT(hasBlockChain()); JS_ASSERT(scopeChain_->is<ClonedBlockObject>());
if (blockChain_->needsClone()) { popOffScopeChain();
JS_ASSERT(scopeChain_->as<ClonedBlockObject>().staticBlock() == *blockChain_);
popOffScopeChain();
}
setBlockChain(*blockChain_->enclosingBlock());
} }
inline CallObject & inline CallObject &

View File

@ -169,7 +169,7 @@ class BaselineFrame
Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
#ifdef DEBUG #ifdef DEBUG
CheckLocalUnaliased(checkAliasing, script(), maybeBlockChain(), i); CheckLocalUnaliased(checkAliasing, script(), i);
#endif #endif
return *valueSlot(i); return *valueSlot(i);
} }

View File

@ -1252,6 +1252,7 @@ IonBuilder::traverseBytecode()
switch (op) { switch (op) {
case JSOP_POP: case JSOP_POP:
case JSOP_POPN: case JSOP_POPN:
case JSOP_POPNV:
case JSOP_DUP: case JSOP_DUP:
case JSOP_DUP2: case JSOP_DUP2:
case JSOP_PICK: case JSOP_PICK:
@ -1504,6 +1505,15 @@ IonBuilder::inspectOpcode(JSOp op)
current->pop(); current->pop();
return true; return true;
case JSOP_POPNV:
{
MDefinition *mins = current->pop();
for (uint32_t i = 0, n = GET_UINT16(pc); i < n; i++)
current->pop();
current->push(mins);
return true;
}
case JSOP_NEWINIT: case JSOP_NEWINIT:
if (GET_UINT8(pc) == JSProto_Array) if (GET_UINT8(pc) == JSProto_Array)
return jsop_newarray(0); return jsop_newarray(0);

View File

@ -883,13 +883,13 @@ OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *m
} }
bool bool
EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block) PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block)
{ {
return frame->pushBlock(cx, block); return frame->pushBlock(cx, block);
} }
bool bool
LeaveBlock(JSContext *cx, BaselineFrame *frame) PopBlockScope(JSContext *cx, BaselineFrame *frame)
{ {
frame->popBlock(cx); frame->popBlock(cx);
return true; return true;

View File

@ -652,8 +652,8 @@ JSObject *InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleO
bool HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, bool *mustReturn); bool HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, bool *mustReturn);
bool OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn); bool OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn);
bool EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block); bool PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
bool LeaveBlock(JSContext *cx, BaselineFrame *frame); bool PopBlockScope(JSContext *cx, BaselineFrame *frame);
bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc); bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc);
bool InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, bool InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame,

View File

@ -253,7 +253,6 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_EVAL: case JSOP_EVAL:
case JSOP_SPREADEVAL: case JSOP_SPREADEVAL:
case JSOP_ENTERLET2:
case JSOP_ENTERWITH: case JSOP_ENTERWITH:
canTrackVars = false; canTrackVars = false;
break; break;

View File

@ -114,18 +114,6 @@ js_GetVariableBytecodeLength(jsbytecode *pc)
} }
} }
static uint32_t
NumBlockSlots(JSScript *script, jsbytecode *pc)
{
JS_ASSERT(*pc == JSOP_ENTERBLOCK ||
*pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1 || *pc == JSOP_ENTERLET2);
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH);
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET2_LENGTH);
return script->getObject(GET_UINT32_INDEX(pc))->as<StaticBlockObject>().propertyCountForCompilation();
}
unsigned unsigned
js::StackUses(JSScript *script, jsbytecode *pc) js::StackUses(JSScript *script, jsbytecode *pc)
{ {
@ -138,16 +126,8 @@ js::StackUses(JSScript *script, jsbytecode *pc)
switch (op) { switch (op) {
case JSOP_POPN: case JSOP_POPN:
return GET_UINT16(pc); return GET_UINT16(pc);
case JSOP_LEAVEBLOCK: case JSOP_POPNV:
return GET_UINT16(pc);
case JSOP_LEAVEBLOCKEXPR:
return GET_UINT16(pc) + 1; return GET_UINT16(pc) + 1;
case JSOP_ENTERLET0:
return NumBlockSlots(script, pc);
case JSOP_ENTERLET1:
return NumBlockSlots(script, pc) + 1;
case JSOP_ENTERLET2:
return NumBlockSlots(script, pc) + 2;
default: default:
/* stack: fun, this, [argc arguments] */ /* stack: fun, this, [argc arguments] */
JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL || JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL ||
@ -161,15 +141,8 @@ js::StackDefs(JSScript *script, jsbytecode *pc)
{ {
JSOp op = (JSOp) *pc; JSOp op = (JSOp) *pc;
const JSCodeSpec &cs = js_CodeSpec[op]; const JSCodeSpec &cs = js_CodeSpec[op];
if (cs.ndefs >= 0) JS_ASSERT (cs.ndefs >= 0);
return cs.ndefs; return cs.ndefs;
uint32_t n = NumBlockSlots(script, pc);
if (op == JSOP_ENTERLET1)
return n + 1;
if (op == JSOP_ENTERLET2)
return n + 2;
return n;
} }
static const char * const countBaseNames[] = { static const char * const countBaseNames[] = {

View File

@ -97,7 +97,9 @@ OPDEF(JSOP_SPREADNEW, 42, "spreadnew", NULL, 1, 3, 1, JOF_BYTE|JOF_IN
/* spreadcall variant of JSOP_EVAL */ /* spreadcall variant of JSOP_EVAL */
OPDEF(JSOP_SPREADEVAL,43, "spreadeval", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) OPDEF(JSOP_SPREADEVAL,43, "spreadeval", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET)
OPDEF(JSOP_UNUSED44, 44, "unused44", NULL, 1, 0, 0, JOF_BYTE) /* Pop N values, preserving top value. */
OPDEF(JSOP_POPNV, 44, "popnv", NULL, 3, -1, 1, JOF_UINT16)
OPDEF(JSOP_UNUSED45, 45, "unused45", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED45, 45, "unused45", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED46, 46, "unused46", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED46, 46, "unused46", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED47, 47, "unused47", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED47, 47, "unused47", NULL, 1, 0, 0, JOF_BYTE)
@ -220,9 +222,7 @@ OPDEF(JSOP_UNUSED101, 101, "unused101", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED102, 102, "unused102", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED102, 102, "unused102", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED103, 103, "unused103", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED103, 103, "unused103", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED104, 104, "unused104", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED104, 104, "unused104", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED105, 105, "unused105", NULL, 1, 0, 0, JOF_BYTE)
/* Leave a for-let-in block leaving its storage pushed (to be popped after enditer). */
OPDEF(JSOP_LEAVEFORLETIN, 105,"leaveforletin",NULL, 1, 0, 0, JOF_BYTE)
/* The argument is the offset to the next statement and is used by IonMonkey. */ /* The argument is the offset to the next statement and is used by IonMonkey. */
OPDEF(JSOP_LABEL, 106,"label", NULL, 5, 0, 0, JOF_JUMP) OPDEF(JSOP_LABEL, 106,"label", NULL, 5, 0, 0, JOF_JUMP)
@ -398,13 +398,9 @@ OPDEF(JSOP_UNUSED183, 183,"unused183", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3)
/* Enter a let block/expr whose slots are at the top of the stack. */ OPDEF(JSOP_UNUSED185, 185,"unused185", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_ENTERLET0, 185,"enterlet0", NULL, 5, -1, -1, JOF_OBJECT) OPDEF(JSOP_UNUSED186, 186,"unused186", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED187, 187,"unused187", NULL, 1, 0, 0, JOF_BYTE)
/* Enter a let block/expr whose slots are 1 below the top of the stack. */
OPDEF(JSOP_ENTERLET1, 186,"enterlet1", NULL, 5, -1, -1, JOF_OBJECT)
/* Enter a let block/expr whose slots are 2 below the top of the stack. */
OPDEF(JSOP_ENTERLET2, 187,"enterlet2", NULL, 5, -1, -1, JOF_OBJECT)
/* /*
* Opcode to hold 24-bit immediate integer operands. * Opcode to hold 24-bit immediate integer operands.
@ -436,8 +432,8 @@ OPDEF(JSOP_TYPEOFEXPR, 197,"typeofexpr", NULL, 1, 1, 1, JOF_BYTE|JOF_DE
/* /*
* Block-local scope support. * Block-local scope support.
*/ */
OPDEF(JSOP_ENTERBLOCK, 198,"enterblock", NULL, 5, 0, -1, JOF_OBJECT) OPDEF(JSOP_PUSHBLOCKSCOPE,198,"pushblockscope", NULL, 5, 0, 0, JOF_OBJECT)
OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 3, -1, 0, JOF_UINT16) OPDEF(JSOP_POPBLOCKSCOPE, 199,"popblockscope", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_DEBUGLEAVEBLOCK, 200,"debugleaveblock", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_DEBUGLEAVEBLOCK, 200,"debugleaveblock", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED201, 201,"unused201", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED201, 201,"unused201", NULL, 1, 0, 0, JOF_BYTE)
@ -459,12 +455,7 @@ OPDEF(JSOP_GETFUNNS, 205,"getfunns", NULL, 1, 0, 1, JOF_BYTE)
*/ */
OPDEF(JSOP_ENUMCONSTELEM, 206,"enumconstelem",NULL, 1, 3, 0, JOF_BYTE|JOF_SET) OPDEF(JSOP_ENUMCONSTELEM, 206,"enumconstelem",NULL, 1, 3, 0, JOF_BYTE|JOF_SET)
/* OPDEF(JSOP_UNUSED207, 207, "unused207", NULL, 1, 0, 0, JOF_BYTE)
* Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals,
* which must be moved down when the block pops.
*/
OPDEF(JSOP_LEAVEBLOCKEXPR,207,"leaveblockexpr",NULL, 3, -1, 1, JOF_UINT16)
OPDEF(JSOP_UNUSED208, 208, "unused208", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED208, 208, "unused208", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE)

View File

@ -854,8 +854,8 @@ js::UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth)
return; return;
if (cx->compartment()->debugMode()) if (cx->compartment()->debugMode())
DebugScopes::onPopBlock(cx, si); DebugScopes::onPopBlock(cx, si);
JS_ASSERT(&si.staticBlock() == si.frame().maybeBlockChain()); if (si.staticBlock().needsClone())
si.frame().popBlock(cx); si.frame().popBlock(cx);
break; break;
case ScopeIter::With: case ScopeIter::With:
if (si.scope().as<WithObject>().stackDepth() < stackDepth) if (si.scope().as<WithObject>().stackDepth() < stackDepth)
@ -1589,7 +1589,6 @@ CASE(EnableInterruptsPseudoOpcode)
/* Various 1-byte no-ops. */ /* Various 1-byte no-ops. */
CASE(JSOP_NOP) CASE(JSOP_NOP)
CASE(JSOP_UNUSED2) CASE(JSOP_UNUSED2)
CASE(JSOP_UNUSED44)
CASE(JSOP_UNUSED45) CASE(JSOP_UNUSED45)
CASE(JSOP_UNUSED46) CASE(JSOP_UNUSED46)
CASE(JSOP_UNUSED47) CASE(JSOP_UNUSED47)
@ -1602,6 +1601,7 @@ CASE(JSOP_UNUSED101)
CASE(JSOP_UNUSED102) CASE(JSOP_UNUSED102)
CASE(JSOP_UNUSED103) CASE(JSOP_UNUSED103)
CASE(JSOP_UNUSED104) CASE(JSOP_UNUSED104)
CASE(JSOP_UNUSED105)
CASE(JSOP_UNUSED107) CASE(JSOP_UNUSED107)
CASE(JSOP_UNUSED125) CASE(JSOP_UNUSED125)
CASE(JSOP_UNUSED126) CASE(JSOP_UNUSED126)
@ -1641,6 +1641,9 @@ CASE(JSOP_UNUSED180)
CASE(JSOP_UNUSED181) CASE(JSOP_UNUSED181)
CASE(JSOP_UNUSED182) CASE(JSOP_UNUSED182)
CASE(JSOP_UNUSED183) CASE(JSOP_UNUSED183)
CASE(JSOP_UNUSED185)
CASE(JSOP_UNUSED186)
CASE(JSOP_UNUSED187)
CASE(JSOP_UNUSED189) CASE(JSOP_UNUSED189)
CASE(JSOP_UNUSED190) CASE(JSOP_UNUSED190)
CASE(JSOP_UNUSED191) CASE(JSOP_UNUSED191)
@ -1649,6 +1652,7 @@ CASE(JSOP_UNUSED194)
CASE(JSOP_UNUSED196) CASE(JSOP_UNUSED196)
CASE(JSOP_UNUSED201) CASE(JSOP_UNUSED201)
CASE(JSOP_GETFUNNS) CASE(JSOP_GETFUNNS)
CASE(JSOP_UNUSED207)
CASE(JSOP_UNUSED208) CASE(JSOP_UNUSED208)
CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED209)
CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED210)
@ -1714,11 +1718,24 @@ CASE(JSOP_POPN)
JS_ASSERT(GET_UINT16(REGS.pc) <= REGS.stackDepth()); JS_ASSERT(GET_UINT16(REGS.pc) <= REGS.stackDepth());
REGS.sp -= GET_UINT16(REGS.pc); REGS.sp -= GET_UINT16(REGS.pc);
#ifdef DEBUG #ifdef DEBUG
if (StaticBlockObject *block = REGS.fp()->maybeBlockChain()) if (StaticBlockObject *block = script->getBlockScope(REGS.pc + JSOP_POPN_LENGTH))
JS_ASSERT(REGS.stackDepth() >= block->stackDepth() + block->slotCount()); JS_ASSERT(REGS.stackDepth() >= block->stackDepth() + block->slotCount());
#endif #endif
END_CASE(JSOP_POPN) END_CASE(JSOP_POPN)
CASE(JSOP_POPNV)
{
JS_ASSERT(GET_UINT16(REGS.pc) < REGS.stackDepth());
Value val = REGS.sp[-1];
REGS.sp -= GET_UINT16(REGS.pc);
REGS.sp[-1] = val;
#ifdef DEBUG
if (StaticBlockObject *block = script->getBlockScope(REGS.pc + JSOP_POPNV_LENGTH))
JS_ASSERT(REGS.stackDepth() >= block->stackDepth() + block->slotCount());
#endif
}
END_CASE(JSOP_POPNV)
CASE(JSOP_SETRVAL) CASE(JSOP_SETRVAL)
POP_RETURN_VALUE(); POP_RETURN_VALUE();
END_CASE(JSOP_SETRVAL) END_CASE(JSOP_SETRVAL)
@ -3336,52 +3353,36 @@ CASE(JSOP_DEBUGGER)
} }
END_CASE(JSOP_DEBUGGER) END_CASE(JSOP_DEBUGGER)
CASE(JSOP_ENTERBLOCK) CASE(JSOP_PUSHBLOCKSCOPE)
CASE(JSOP_ENTERLET0)
CASE(JSOP_ENTERLET1)
CASE(JSOP_ENTERLET2)
{ {
StaticBlockObject &blockObj = script->getObject(REGS.pc)->as<StaticBlockObject>(); StaticBlockObject &blockObj = script->getObject(REGS.pc)->as<StaticBlockObject>();
if (*REGS.pc == JSOP_ENTERBLOCK) { JS_ASSERT(blockObj.needsClone());
JS_ASSERT(REGS.stackDepth() == blockObj.stackDepth()); // FIXME: "Aliased" slots don't need to be on the stack.
JS_ASSERT(REGS.stackDepth() + blockObj.slotCount() <= script->nslots()); JS_ASSERT(REGS.stackDepth() >= blockObj.stackDepth() + blockObj.slotCount());
Value *vp = REGS.sp + blockObj.slotCount();
SetValueRangeToUndefined(REGS.sp, vp);
REGS.sp = vp;
}
/* Clone block iff there are any closed-over variables. */ // Clone block and push on scope chain.
if (!REGS.fp()->pushBlock(cx, blockObj)) if (!REGS.fp()->pushBlock(cx, blockObj))
goto error; goto error;
} }
END_CASE(JSOP_ENTERBLOCK) END_CASE(JSOP_PUSHBLOCKSCOPE)
CASE(JSOP_LEAVEBLOCK) CASE(JSOP_POPBLOCKSCOPE)
CASE(JSOP_LEAVEFORLETIN)
CASE(JSOP_LEAVEBLOCKEXPR)
{ {
blockDepth = REGS.fp()->blockChain().stackDepth(); #ifdef DEBUG
// Pop block from scope chain.
JS_ASSERT(*(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH) == JSOP_DEBUGLEAVEBLOCK);
StaticBlockObject *blockObj = script->getBlockScope(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH);
JS_ASSERT(blockObj && blockObj->needsClone());
// FIXME: "Aliased" slots don't need to be on the stack.
JS_ASSERT(REGS.stackDepth() >= blockObj->stackDepth() + blockObj->slotCount());
#endif
// Pop block from scope chain. // Pop block from scope chain.
REGS.fp()->popBlock(cx); REGS.fp()->popBlock(cx);
if (*REGS.pc == JSOP_LEAVEBLOCK) {
/* Pop the block's slots. */
REGS.sp -= GET_UINT16(REGS.pc);
JS_ASSERT(REGS.stackDepth() == blockDepth);
} else if (*REGS.pc == JSOP_LEAVEBLOCKEXPR) {
/* Pop the block's slots maintaining the topmost expr. */
Value *vp = &REGS.sp[-1];
REGS.sp -= GET_UINT16(REGS.pc);
JS_ASSERT(REGS.stackDepth() == blockDepth + 1);
REGS.sp[-1] = *vp;
} else {
/* Another op will pop; nothing to do here. */
ADVANCE_AND_DISPATCH(JSOP_LEAVEFORLETIN_LENGTH);
}
} }
END_CASE(JSOP_LEAVEBLOCK) END_CASE(JSOP_POPBLOCKSCOPE)
CASE(JSOP_DEBUGLEAVEBLOCK) CASE(JSOP_DEBUGLEAVEBLOCK)
{ {

View File

@ -110,7 +110,7 @@ inline Value &
StackFrame::unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing) StackFrame::unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing)
{ {
#ifdef DEBUG #ifdef DEBUG
CheckLocalUnaliased(checkAliasing, script(), maybeBlockChain(), i); CheckLocalUnaliased(checkAliasing, script(), i);
#endif #endif
return slots()[i]; return slots()[i];
} }

View File

@ -327,20 +327,14 @@ StackFrame::epilogue(JSContext *cx)
bool bool
StackFrame::pushBlock(JSContext *cx, StaticBlockObject &block) StackFrame::pushBlock(JSContext *cx, StaticBlockObject &block)
{ {
JS_ASSERT_IF(hasBlockChain(), blockChain_ == block.enclosingBlock()); JS_ASSERT (block.needsClone());
if (block.needsClone()) { Rooted<StaticBlockObject *> blockHandle(cx, &block);
Rooted<StaticBlockObject *> blockHandle(cx, &block); ClonedBlockObject *clone = ClonedBlockObject::create(cx, blockHandle, this);
ClonedBlockObject *clone = ClonedBlockObject::create(cx, blockHandle, this); if (!clone)
if (!clone) return false;
return false;
pushOnScopeChain(*clone); pushOnScopeChain(*clone);
blockChain_ = blockHandle;
} else {
blockChain_ = &block;
}
return true; return true;
} }
@ -348,12 +342,8 @@ StackFrame::pushBlock(JSContext *cx, StaticBlockObject &block)
void void
StackFrame::popBlock(JSContext *cx) StackFrame::popBlock(JSContext *cx)
{ {
if (blockChain_->needsClone()) { JS_ASSERT(scopeChain_->is<ClonedBlockObject>());
JS_ASSERT(scopeChain_->as<ClonedBlockObject>().staticBlock() == *blockChain_); popOffScopeChain();
popOffScopeChain();
}
blockChain_ = blockChain_->enclosingBlock();
} }
void void
@ -1254,8 +1244,7 @@ AbstractFramePtr::hasPushedSPSFrame() const
#ifdef DEBUG #ifdef DEBUG
void void
js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, unsigned i)
StaticBlockObject *maybeBlock, unsigned i)
{ {
if (!checkAliasing) if (!checkAliasing)
return; return;
@ -1264,13 +1253,8 @@ js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
if (i < script->nfixed()) { if (i < script->nfixed()) {
JS_ASSERT(!script->varIsAliased(i)); JS_ASSERT(!script->varIsAliased(i));
} else { } else {
unsigned depth = i - script->nfixed(); // FIXME: The callers of this function do not easily have the PC of the
for (StaticBlockObject *b = maybeBlock; b; b = b->enclosingBlock()) { // current frame, and so they do not know the block scope.
if (b->containsVarAtDepth(depth)) {
JS_ASSERT(!b->isAliased(depth - b->stackDepth()));
break;
}
}
} }
} }
#endif #endif

View File

@ -75,8 +75,7 @@ enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
#ifdef DEBUG #ifdef DEBUG
extern void extern void
CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, unsigned i);
StaticBlockObject *maybeBlock, unsigned i);
#endif #endif
namespace jit { namespace jit {
@ -591,14 +590,8 @@ class StackFrame
inline void popOffScopeChain(); inline void popOffScopeChain();
/* /*
* Block chain * For blocks with aliased locals, these interfaces push and pop entries on
* * the scope chain.
* Entering/leaving a let (or exception) block may do 1 or 2 things: First,
* a static block object (created at compiled time and stored in the
* script) is pushed on StackFrame::blockChain. Second, if the static block
* may be cloned to hold the dynamic values if this is needed for dynamic
* scope access. A clone is created for a static block iff
* StaticBlockObject::needsClone.
*/ */
bool hasBlockChain() const { bool hasBlockChain() const {