Fix issue 265 by handling extra statement state on the frame based on

the expectation at the break, continue, and return labels (including
shadowed ones) instead of based on the AST nodes.

See http://code.google.com/p/v8/issues/detail?id=265

Review URL: http://codereview.chromium.org/42017

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1482 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 1a9dc2d0
...@@ -218,6 +218,9 @@ inline Condition ReverseCondition(Condition cc) { ...@@ -218,6 +218,9 @@ inline Condition ReverseCondition(Condition cc) {
// implementations. // implementations.
enum Hint { no_hint }; enum Hint { no_hint };
// Hints are not used on the arm. Negating is trivial.
inline Hint NegateHint(Hint ignored) { return no_hint; }
// The pc store offset may be 8 or 12 depending on the processor implementation. // The pc store offset may be 8 or 12 depending on the processor implementation.
int PcStoreOffset(); int PcStoreOffset();
......
...@@ -177,7 +177,6 @@ enum Hint { ...@@ -177,7 +177,6 @@ enum Hint {
taken = 0x3e taken = 0x3e
}; };
// The result of negating a hint is as if the corresponding condition // The result of negating a hint is as if the corresponding condition
// were negated by NegateCondition. That is, no_hint is mapped to // were negated by NegateCondition. That is, no_hint is mapped to
// itself and not_taken and taken are mapped to each other. // itself and not_taken and taken are mapped to each other.
...@@ -187,6 +186,7 @@ inline Hint NegateHint(Hint hint) { ...@@ -187,6 +186,7 @@ inline Hint NegateHint(Hint hint) {
: ((hint == not_taken) ? taken : not_taken); : ((hint == not_taken) ? taken : not_taken);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Machine instruction Immediates // Machine instruction Immediates
......
...@@ -202,11 +202,6 @@ class BreakableStatement: public Statement { ...@@ -202,11 +202,6 @@ class BreakableStatement: public Statement {
// Code generation // Code generation
BreakTarget* break_target() { return &break_target_; } BreakTarget* break_target() { return &break_target_; }
// Used during code generation for restoring the stack when a
// break/continue crosses a statement that keeps stuff on the stack.
int break_stack_height() { return break_stack_height_; }
void set_break_stack_height(int height) { break_stack_height_ = height; }
// Testers. // Testers.
bool is_target_for_anonymous() const { return type_ == TARGET_FOR_ANONYMOUS; } bool is_target_for_anonymous() const { return type_ == TARGET_FOR_ANONYMOUS; }
...@@ -220,7 +215,6 @@ class BreakableStatement: public Statement { ...@@ -220,7 +215,6 @@ class BreakableStatement: public Statement {
ZoneStringList* labels_; ZoneStringList* labels_;
Type type_; Type type_;
BreakTarget break_target_; BreakTarget break_target_;
int break_stack_height_;
}; };
...@@ -451,12 +445,12 @@ class CaseClause: public ZoneObject { ...@@ -451,12 +445,12 @@ class CaseClause: public ZoneObject {
CHECK(!is_default()); CHECK(!is_default());
return label_; return label_;
} }
BreakTarget* body_target() { return &body_target_; } JumpTarget* body_target() { return &body_target_; }
ZoneList<Statement*>* statements() const { return statements_; } ZoneList<Statement*>* statements() const { return statements_; }
private: private:
Expression* label_; Expression* label_;
BreakTarget body_target_; JumpTarget body_target_;
ZoneList<Statement*>* statements_; ZoneList<Statement*>* statements_;
}; };
......
...@@ -83,7 +83,6 @@ CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script, ...@@ -83,7 +83,6 @@ CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script,
allocator_(NULL), allocator_(NULL),
cc_reg_(al), cc_reg_(al),
state_(NULL), state_(NULL),
break_stack_height_(0),
function_return_is_shadowed_(false), function_return_is_shadowed_(false),
in_spilled_code_(false) { in_spilled_code_(false) {
} }
...@@ -107,8 +106,6 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { ...@@ -107,8 +106,6 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
ASSERT(frame_ == NULL); ASSERT(frame_ == NULL);
frame_ = new VirtualFrame(this); frame_ = new VirtualFrame(this);
cc_reg_ = al; cc_reg_ = al;
function_return_.Initialize(this, JumpTarget::BIDIRECTIONAL);
function_return_is_shadowed_ = false;
set_in_spilled_code(false); set_in_spilled_code(false);
{ {
CodeGenState state(this); CodeGenState state(this);
...@@ -133,6 +130,10 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { ...@@ -133,6 +130,10 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
// Allocate space for locals and initialize them. // Allocate space for locals and initialize them.
frame_->AllocateStackSlots(scope_->num_stack_slots()); frame_->AllocateStackSlots(scope_->num_stack_slots());
// Initialize the function return target after the locals are set
// up, because it needs the expected frame height from the frame.
function_return_.Initialize(this, JumpTarget::BIDIRECTIONAL);
function_return_is_shadowed_ = false;
VirtualFrame::SpilledScope spilled_scope(this); VirtualFrame::SpilledScope spilled_scope(this);
if (scope_->num_heap_slots() > 0) { if (scope_->num_heap_slots() > 0) {
...@@ -1127,7 +1128,6 @@ void CodeGenerator::VisitBlock(Block* node) { ...@@ -1127,7 +1128,6 @@ void CodeGenerator::VisitBlock(Block* node) {
VirtualFrame::SpilledScope spilled_scope(this); VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ Block"); Comment cmnt(masm_, "[ Block");
CodeForStatementPosition(node); CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this); node->break_target()->Initialize(this);
VisitStatementsAndSpill(node->statements()); VisitStatementsAndSpill(node->statements());
if (node->break_target()->is_linked()) { if (node->break_target()->is_linked()) {
...@@ -1341,18 +1341,10 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { ...@@ -1341,18 +1341,10 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) {
} }
void CodeGenerator::CleanStack(int num_bytes) {
VirtualFrame::SpilledScope spilled_scope(this);
ASSERT(num_bytes % kPointerSize == 0);
frame_->Drop(num_bytes / kPointerSize);
}
void CodeGenerator::VisitContinueStatement(ContinueStatement* node) { void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
VirtualFrame::SpilledScope spilled_scope(this); VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ ContinueStatement"); Comment cmnt(masm_, "[ ContinueStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
CleanStack(break_stack_height_ - node->target()->break_stack_height());
node->target()->continue_target()->Jump(); node->target()->continue_target()->Jump();
} }
...@@ -1361,7 +1353,6 @@ void CodeGenerator::VisitBreakStatement(BreakStatement* node) { ...@@ -1361,7 +1353,6 @@ void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
VirtualFrame::SpilledScope spilled_scope(this); VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ BreakStatement"); Comment cmnt(masm_, "[ BreakStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
CleanStack(break_stack_height_ - node->target()->break_stack_height());
node->target()->break_target()->Jump(); node->target()->break_target()->Jump();
} }
...@@ -1521,7 +1512,6 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { ...@@ -1521,7 +1512,6 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
VirtualFrame::SpilledScope spilled_scope(this); VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ SwitchStatement"); Comment cmnt(masm_, "[ SwitchStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this); node->break_target()->Initialize(this);
LoadAndSpill(node->tag()); LoadAndSpill(node->tag());
...@@ -1615,7 +1605,6 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { ...@@ -1615,7 +1605,6 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
VirtualFrame::SpilledScope spilled_scope(this); VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ LoopStatement"); Comment cmnt(masm_, "[ LoopStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this); node->break_target()->Initialize(this);
// Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a // Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a
...@@ -1805,15 +1794,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { ...@@ -1805,15 +1794,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
Comment cmnt(masm_, "[ ForInStatement"); Comment cmnt(masm_, "[ ForInStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
// We keep stuff on the stack while the body is executing.
// Record it, so that a break/continue crossing this statement
// can restore the stack.
const int kForInStackSize = 5 * kPointerSize;
break_stack_height_ += kForInStackSize;
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this);
node->continue_target()->Initialize(this);
JumpTarget primitive(this); JumpTarget primitive(this);
JumpTarget jsobject(this); JumpTarget jsobject(this);
JumpTarget fixed_array(this); JumpTarget fixed_array(this);
...@@ -1902,6 +1882,11 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { ...@@ -1902,6 +1882,11 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
// sp[2] : array or enum cache // sp[2] : array or enum cache
// sp[3] : 0 or map // sp[3] : 0 or map
// sp[4] : enumerable // sp[4] : enumerable
// Grab the current frame's height for the break and continue
// targets only after all the state is pushed on the frame.
node->break_target()->Initialize(this);
node->continue_target()->Initialize(this);
__ ldr(r0, frame_->ElementAt(0)); // load the current count __ ldr(r0, frame_->ElementAt(0)); // load the current count
__ ldr(r1, frame_->ElementAt(1)); // load the length __ ldr(r1, frame_->ElementAt(1)); // load the length
__ cmp(r0, Operand(r1)); // compare to the array length __ cmp(r0, Operand(r1)); // compare to the array length
...@@ -1986,7 +1971,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { ...@@ -1986,7 +1971,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
// Exit. // Exit.
exit.Bind(); exit.Bind();
break_stack_height_ -= kForInStackSize;
ASSERT(frame_->height() == original_height); ASSERT(frame_->height() == original_height);
} }
...@@ -2259,16 +2243,11 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { ...@@ -2259,16 +2243,11 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
frame_->EmitPush(r2); frame_->EmitPush(r2);
// We keep two elements on the stack - the (possibly faked) result // We keep two elements on the stack - the (possibly faked) result
// and the state - while evaluating the finally block. Record it, so // and the state - while evaluating the finally block.
// that a break/continue crossing this statement can restore the //
// stack.
const int kFinallyStackSize = 2 * kPointerSize;
break_stack_height_ += kFinallyStackSize;
// Generate code for the statements in the finally block. // Generate code for the statements in the finally block.
VisitStatementsAndSpill(node->finally_block()->statements()); VisitStatementsAndSpill(node->finally_block()->statements());
break_stack_height_ -= kFinallyStackSize;
if (has_valid_frame()) { if (has_valid_frame()) {
JumpTarget exit(this); JumpTarget exit(this);
// Restore state and return value or faked TOS. // Restore state and return value or faked TOS.
......
...@@ -330,7 +330,6 @@ class CodeGenerator: public AstVisitor { ...@@ -330,7 +330,6 @@ class CodeGenerator: public AstVisitor {
// Control flow // Control flow
void Branch(bool if_true, JumpTarget* target); void Branch(bool if_true, JumpTarget* target);
void CheckStack(); void CheckStack();
void CleanStack(int num_bytes);
bool CheckForInlineRuntimeCall(CallRuntime* node); bool CheckForInlineRuntimeCall(CallRuntime* node);
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node); Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
...@@ -446,7 +445,6 @@ class CodeGenerator: public AstVisitor { ...@@ -446,7 +445,6 @@ class CodeGenerator: public AstVisitor {
RegisterAllocator* allocator_; RegisterAllocator* allocator_;
Condition cc_reg_; Condition cc_reg_;
CodeGenState* state_; CodeGenState* state_;
int break_stack_height_;
// Jump targets // Jump targets
BreakTarget function_return_; BreakTarget function_return_;
......
...@@ -80,7 +80,6 @@ CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script, ...@@ -80,7 +80,6 @@ CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script,
frame_(NULL), frame_(NULL),
allocator_(NULL), allocator_(NULL),
state_(NULL), state_(NULL),
break_stack_height_(0),
loop_nesting_(0), loop_nesting_(0),
function_return_is_shadowed_(false), function_return_is_shadowed_(false),
in_spilled_code_(false) { in_spilled_code_(false) {
...@@ -107,8 +106,6 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { ...@@ -107,8 +106,6 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
allocator_ = &register_allocator; allocator_ = &register_allocator;
ASSERT(frame_ == NULL); ASSERT(frame_ == NULL);
frame_ = new VirtualFrame(this); frame_ = new VirtualFrame(this);
function_return_.Initialize(this, JumpTarget::BIDIRECTIONAL);
function_return_is_shadowed_ = false;
set_in_spilled_code(false); set_in_spilled_code(false);
// Adjust for function-level loop nesting. // Adjust for function-level loop nesting.
...@@ -125,7 +122,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { ...@@ -125,7 +122,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
// esi: callee's context // esi: callee's context
allocator_->Initialize(); allocator_->Initialize();
frame_->Enter(); frame_->Enter();
// tos: code slot
#ifdef DEBUG #ifdef DEBUG
if (strlen(FLAG_stop_at) > 0 && if (strlen(FLAG_stop_at) > 0 &&
fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) { fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
...@@ -136,6 +133,10 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { ...@@ -136,6 +133,10 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
// Allocate space for locals and initialize them. // Allocate space for locals and initialize them.
frame_->AllocateStackSlots(scope_->num_stack_slots()); frame_->AllocateStackSlots(scope_->num_stack_slots());
// Initialize the function return target after the locals are set
// up, because it needs the expected frame height from the frame.
function_return_.Initialize(this, JumpTarget::BIDIRECTIONAL);
function_return_is_shadowed_ = false;
// Allocate the arguments object and copy the parameters into it. // Allocate the arguments object and copy the parameters into it.
if (scope_->arguments() != NULL) { if (scope_->arguments() != NULL) {
...@@ -1561,7 +1562,6 @@ void CodeGenerator::VisitBlock(Block* node) { ...@@ -1561,7 +1562,6 @@ void CodeGenerator::VisitBlock(Block* node) {
ASSERT(!in_spilled_code()); ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ Block"); Comment cmnt(masm_, "[ Block");
CodeForStatementPosition(node); CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this); node->break_target()->Initialize(this);
VisitStatements(node->statements()); VisitStatements(node->statements());
if (node->break_target()->is_linked()) { if (node->break_target()->is_linked()) {
...@@ -1763,17 +1763,10 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { ...@@ -1763,17 +1763,10 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) {
} }
void CodeGenerator::CleanStack(int num_bytes) {
ASSERT(num_bytes % kPointerSize == 0);
frame_->Drop(num_bytes / kPointerSize);
}
void CodeGenerator::VisitContinueStatement(ContinueStatement* node) { void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
ASSERT(!in_spilled_code()); ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ ContinueStatement"); Comment cmnt(masm_, "[ ContinueStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
CleanStack(break_stack_height_ - node->target()->break_stack_height());
node->target()->continue_target()->Jump(); node->target()->continue_target()->Jump();
} }
...@@ -1782,7 +1775,6 @@ void CodeGenerator::VisitBreakStatement(BreakStatement* node) { ...@@ -1782,7 +1775,6 @@ void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
ASSERT(!in_spilled_code()); ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ BreakStatement"); Comment cmnt(masm_, "[ BreakStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
CleanStack(break_stack_height_ - node->target()->break_stack_height());
node->target()->break_target()->Jump(); node->target()->break_target()->Jump();
} }
...@@ -2020,7 +2012,6 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { ...@@ -2020,7 +2012,6 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
ASSERT(!in_spilled_code()); ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ SwitchStatement"); Comment cmnt(masm_, "[ SwitchStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this); node->break_target()->Initialize(this);
// Compile the switch value. // Compile the switch value.
...@@ -2148,7 +2139,6 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { ...@@ -2148,7 +2139,6 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
ASSERT(!in_spilled_code()); ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ LoopStatement"); Comment cmnt(masm_, "[ LoopStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this); node->break_target()->Initialize(this);
// Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a // Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a
...@@ -2471,15 +2461,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { ...@@ -2471,15 +2461,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
Comment cmnt(masm_, "[ ForInStatement"); Comment cmnt(masm_, "[ ForInStatement");
CodeForStatementPosition(node); CodeForStatementPosition(node);
// We keep stuff on the stack while the body is executing.
// Record it, so that a break/continue crossing this statement
// can restore the stack.
const int kForInStackSize = 5 * kPointerSize;
break_stack_height_ += kForInStackSize;
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this);
node->continue_target()->Initialize(this);
JumpTarget primitive(this); JumpTarget primitive(this);
JumpTarget jsobject(this); JumpTarget jsobject(this);
JumpTarget fixed_array(this); JumpTarget fixed_array(this);
...@@ -2568,6 +2549,11 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { ...@@ -2568,6 +2549,11 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
// Condition. // Condition.
entry.Bind(); entry.Bind();
// Grab the current frame's height for the break and continue
// targets only after all the state is pushed on the frame.
node->break_target()->Initialize(this);
node->continue_target()->Initialize(this);
__ mov(eax, frame_->ElementAt(0)); // load the current count __ mov(eax, frame_->ElementAt(0)); // load the current count
__ cmp(eax, frame_->ElementAt(1)); // compare to the array length __ cmp(eax, frame_->ElementAt(1)); // compare to the array length
node->break_target()->Branch(above_equal); node->break_target()->Branch(above_equal);
...@@ -2650,8 +2636,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { ...@@ -2650,8 +2636,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
// Exit. // Exit.
exit.Bind(); exit.Bind();
break_stack_height_ -= kForInStackSize;
} }
...@@ -2917,16 +2901,11 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { ...@@ -2917,16 +2901,11 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
frame_->EmitPush(ecx); frame_->EmitPush(ecx);
// We keep two elements on the stack - the (possibly faked) result // We keep two elements on the stack - the (possibly faked) result
// and the state - while evaluating the finally block. Record it, so // and the state - while evaluating the finally block.
// that a break/continue crossing this statement can restore the //
// stack.
const int kFinallyStackSize = 2 * kPointerSize;
break_stack_height_ += kFinallyStackSize;
// Generate code for the statements in the finally block. // Generate code for the statements in the finally block.
VisitStatementsAndSpill(node->finally_block()->statements()); VisitStatementsAndSpill(node->finally_block()->statements());
break_stack_height_ -= kFinallyStackSize;
if (has_valid_frame()) { if (has_valid_frame()) {
JumpTarget exit(this); JumpTarget exit(this);
// Restore state and return value or faked TOS. // Restore state and return value or faked TOS.
......
...@@ -501,7 +501,6 @@ class CodeGenerator: public AstVisitor { ...@@ -501,7 +501,6 @@ class CodeGenerator: public AstVisitor {
void CallWithArguments(ZoneList<Expression*>* arguments, int position); void CallWithArguments(ZoneList<Expression*>* arguments, int position);
void CheckStack(); void CheckStack();
void CleanStack(int num_bytes);
bool CheckForInlineRuntimeCall(CallRuntime* node); bool CheckForInlineRuntimeCall(CallRuntime* node);
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node); Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
...@@ -617,7 +616,6 @@ class CodeGenerator: public AstVisitor { ...@@ -617,7 +616,6 @@ class CodeGenerator: public AstVisitor {
VirtualFrame* frame_; VirtualFrame* frame_;
RegisterAllocator* allocator_; RegisterAllocator* allocator_;
CodeGenState* state_; CodeGenState* state_;
int break_stack_height_;
int loop_nesting_; int loop_nesting_;
// Jump targets. // Jump targets.
......
...@@ -37,23 +37,48 @@ namespace v8 { namespace internal { ...@@ -37,23 +37,48 @@ namespace v8 { namespace internal {
JumpTarget::JumpTarget(CodeGenerator* cgen, Directionality direction) JumpTarget::JumpTarget(CodeGenerator* cgen, Directionality direction)
: cgen_(cgen), : cgen_(cgen),
masm_(cgen == NULL ? NULL : cgen->masm()),
direction_(direction), direction_(direction),
reaching_frames_(0), reaching_frames_(0),
merge_labels_(0), merge_labels_(0),
entry_frame_(NULL), entry_frame_(NULL),
is_bound_(false), is_bound_(false),
is_linked_(false) { is_linked_(false) {
ASSERT(cgen != NULL);
masm_ = cgen->masm();
}
JumpTarget::JumpTarget()
: cgen_(NULL),
masm_(NULL),
direction_(FORWARD_ONLY),
reaching_frames_(0),
merge_labels_(0),
entry_frame_(NULL),
is_bound_(false),
is_linked_(false) {
}
void JumpTarget::Initialize(CodeGenerator* cgen, Directionality direction) {
ASSERT(cgen != NULL);
ASSERT(cgen_ == NULL);
cgen_ = cgen;
masm_ = cgen->masm();
direction_ = direction;
} }
void JumpTarget::Unuse() { void JumpTarget::Unuse() {
ASSERT(!is_linked()); ASSERT(!is_linked());
entry_label_.Unuse(); #ifdef DEBUG
for (int i = 0; i < reaching_frames_.length(); i++) {
ASSERT(reaching_frames_[i] == NULL);
}
#endif
delete entry_frame_; delete entry_frame_;
entry_frame_ = NULL;
is_bound_ = false; Reset();
is_linked_ = false;
} }
...@@ -501,16 +526,10 @@ void JumpTarget::AddReachingFrame(VirtualFrame* frame) { ...@@ -501,16 +526,10 @@ void JumpTarget::AddReachingFrame(VirtualFrame* frame) {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// BreakTarget implementation. // BreakTarget implementation.
BreakTarget::BreakTarget() : JumpTarget(NULL, FORWARD_ONLY) {
}
void BreakTarget::Initialize(CodeGenerator* cgen, Directionality direction) { void BreakTarget::Initialize(CodeGenerator* cgen, Directionality direction) {
ASSERT(cgen != NULL); JumpTarget::Initialize(cgen, direction);
ASSERT(cgen_ == NULL); ASSERT(cgen_->has_valid_frame());
cgen_ = cgen; expected_height_ = cgen_->frame()->height();
masm_ = cgen->masm();
direction_ = direction;
} }
...@@ -530,6 +549,58 @@ void BreakTarget::CopyTo(BreakTarget* destination) { ...@@ -530,6 +549,58 @@ void BreakTarget::CopyTo(BreakTarget* destination) {
destination->entry_label_ = entry_label_; destination->entry_label_ = entry_label_;
destination->is_bound_ = is_bound_; destination->is_bound_ = is_bound_;
destination->is_linked_ = is_linked_; destination->is_linked_ = is_linked_;
destination->expected_height_ = expected_height_;
}
void BreakTarget::Jump() {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
// This is a break target so drop leftover statement state from the
// frame before merging.
cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_);
JumpTarget::Jump();
}
void BreakTarget::Branch(Condition cc, Hint hint) {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
int count = cgen_->frame()->height() - expected_height_;
if (count > 0) {
// We negate and branch here rather than using
// JumpTarget::Branch's negate and branch. This gives us a hook
// to remove statement state from the frame.
JumpTarget fall_through(cgen_);
// Branch to fall through will not negate, because it is a
// forward-only target.
fall_through.Branch(NegateCondition(cc), NegateHint(hint));
Jump(); // May emit merge code here.
fall_through.Bind();
} else {
JumpTarget::Branch(cc, hint);
}
}
void BreakTarget::Bind(int mergable_elements) {
#ifdef DEBUG
ASSERT(mergable_elements == kAllElements);
ASSERT(cgen_ != NULL);
for (int i = 0; i < reaching_frames_.length(); i++) {
ASSERT(reaching_frames_[i]->height() == expected_height_);
}
#endif
// This is a break target so drop leftover statement state from the
// frame before merging.
if (cgen_->has_valid_frame()) {
int count = cgen_->frame()->height() - expected_height_;
cgen_->frame()->ForgetElements(count);
}
JumpTarget::Bind(mergable_elements);
} }
...@@ -546,15 +617,19 @@ ShadowTarget::ShadowTarget(BreakTarget* shadowed) { ...@@ -546,15 +617,19 @@ ShadowTarget::ShadowTarget(BreakTarget* shadowed) {
// While shadowing this shadow target saves the state of the original. // While shadowing this shadow target saves the state of the original.
shadowed->CopyTo(this); shadowed->CopyTo(this);
// The original's state is reset. We do not Unuse it because that
// would delete the expected frame and assert that the target is not
// linked.
shadowed->Reset();
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
shadowed->set_expected_height(cgen_->frame()->height());
// Setting the code generator to null prevents the shadow target from // Setting the code generator to null prevents the shadow target from
// being used until shadowing stops. // being used until shadowing stops.
cgen_ = NULL; cgen_ = NULL;
masm_ = NULL; masm_ = NULL;
// The original's state is reset. We do not Unuse it because that
// would delete the expected frame and assert that the target is not
// linked.
shadowed->Reset();
} }
......
...@@ -58,8 +58,31 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. ...@@ -58,8 +58,31 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
explicit JumpTarget(CodeGenerator* cgen, explicit JumpTarget(CodeGenerator* cgen,
Directionality direction = FORWARD_ONLY); Directionality direction = FORWARD_ONLY);
// Construct a jump target without a code generator. A code
// generator must be supplied before using the jump target as a
// label. This is useful, eg, when break targets are embedded in
// AST nodes.
JumpTarget();
// Supply a code generator and directionality to an already
// constructed jump target. This function expects to be given a
// non-null code generator, and to be called only when the code
// generator is not yet set.
virtual void Initialize(CodeGenerator* cgen,
Directionality direction = FORWARD_ONLY);
virtual ~JumpTarget() { Unuse(); } virtual ~JumpTarget() { Unuse(); }
// Treat the jump target as a fresh one. The state is reset and
// pointed-to virtual frames are deallocated. There should be no
// dangling jumps to the target.
void Unuse();
// Reset the internal state of this jump target. Pointed-to virtual
// frames are not deallocated and dangling jumps to the target are
// left dangling.
void Reset();
// Accessors. // Accessors.
CodeGenerator* code_generator() const { return cgen_; } CodeGenerator* code_generator() const { return cgen_; }
...@@ -77,19 +100,9 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. ...@@ -77,19 +100,9 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
bool is_linked() const { return is_linked_; } bool is_linked() const { return is_linked_; }
bool is_unused() const { return !is_bound() && !is_linked(); } bool is_unused() const { return !is_bound() && !is_linked(); }
// Treat the jump target as a fresh one. The expected frame if any
// will be deallocated and there should be no dangling jumps to the
// target (thus no reaching frames).
void Unuse();
// Reset the internal state of this jump target. Pointed-to virtual
// frames are not deallocated and dangling jumps to the target are
// left dangling.
void Reset();
// Emit a jump to the target. There must be a current frame at the // Emit a jump to the target. There must be a current frame at the
// jump and there will be no current frame after the jump. // jump and there will be no current frame after the jump.
void Jump(); virtual void Jump();
void Jump(Result* arg); void Jump(Result* arg);
void Jump(Result* arg0, Result* arg1); void Jump(Result* arg0, Result* arg1);
void Jump(Result* arg0, Result* arg1, Result* arg2); void Jump(Result* arg0, Result* arg1, Result* arg2);
...@@ -97,7 +110,7 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. ...@@ -97,7 +110,7 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
// Emit a conditional branch to the target. There must be a current // Emit a conditional branch to the target. There must be a current
// frame at the branch. The current frame will fall through to the // frame at the branch. The current frame will fall through to the
// code after the branch. // code after the branch.
void Branch(Condition cc, Hint hint = no_hint); virtual void Branch(Condition cc, Hint hint = no_hint);
void Branch(Condition cc, Result* arg, Hint hint = no_hint); void Branch(Condition cc, Result* arg, Hint hint = no_hint);
void Branch(Condition cc, Result* arg0, Result* arg1, Hint hint = no_hint); void Branch(Condition cc, Result* arg0, Result* arg1, Hint hint = no_hint);
void Branch(Condition cc, void Branch(Condition cc,
...@@ -125,7 +138,7 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. ...@@ -125,7 +138,7 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
// A mergable elements argument of kAllElements indicates that all // A mergable elements argument of kAllElements indicates that all
// frame elements must be mergable. Mergable elements are ignored // frame elements must be mergable. Mergable elements are ignored
// completely for forward-only jump targets. // completely for forward-only jump targets.
void Bind(int mergable_elements = kAllElements); virtual void Bind(int mergable_elements = kAllElements);
void Bind(Result* arg, int mergable_elements = kAllElements); void Bind(Result* arg, int mergable_elements = kAllElements);
void Bind(Result* arg0, Result* arg1, int mergable_elements = kAllElements); void Bind(Result* arg0, Result* arg1, int mergable_elements = kAllElements);
void Bind(Result* arg0, void Bind(Result* arg0,
...@@ -209,13 +222,13 @@ class BreakTarget : public JumpTarget { ...@@ -209,13 +222,13 @@ class BreakTarget : public JumpTarget {
// generator must be supplied before using the break target as a // generator must be supplied before using the break target as a
// label. This is useful, eg, when break targets are embedded in AST // label. This is useful, eg, when break targets are embedded in AST
// nodes. // nodes.
BreakTarget(); BreakTarget() {}
// Supply a code generator and directionality to an already // Supply a code generator, expected expression stack height, and
// constructed jump target. This function expects to be given a // directionality to an already constructed break target. This
// non-null code generator, and to be called only when the code // function expects to be given a non-null code generator, and to be
// generator is not yet set. // called only when the code generator is not yet set.
void Initialize(CodeGenerator* cgen, virtual void Initialize(CodeGenerator* cgen,
Directionality direction = FORWARD_ONLY); Directionality direction = FORWARD_ONLY);
// Copy the state of this break target to the destination. The // Copy the state of this break target to the destination. The
...@@ -225,7 +238,28 @@ class BreakTarget : public JumpTarget { ...@@ -225,7 +238,28 @@ class BreakTarget : public JumpTarget {
// overwritten, without deallocating pointed-to virtual frames. // overwritten, without deallocating pointed-to virtual frames.
void CopyTo(BreakTarget* destination); void CopyTo(BreakTarget* destination);
// Emit a jump to the target. There must be a current frame at the
// jump and there will be no current frame after the jump.
virtual void Jump();
// Emit a conditional branch to the target. There must be a current
// frame at the branch. The current frame will fall through to the
// code after the branch.
virtual void Branch(Condition cc, Hint hint = no_hint);
// Bind a break target. If there is no current frame at the binding
// site, there must be at least one frame reaching via a forward
// jump.
virtual void Bind(int mergable_elements = kAllElements);
// Setter for expected height.
void set_expected_height(int expected) { expected_height_ = expected; }
private: private:
// The expected height of the expression stack where the target will
// be bound, statically known at initialization time.
int expected_height_;
DISALLOW_COPY_AND_ASSIGN(BreakTarget); DISALLOW_COPY_AND_ASSIGN(BreakTarget);
}; };
......
...@@ -168,8 +168,11 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) { ...@@ -168,8 +168,11 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) {
MergeMoveRegistersToRegisters(expected); MergeMoveRegistersToRegisters(expected);
MergeMoveMemoryToRegisters(expected); MergeMoveMemoryToRegisters(expected);
// Fix any sync bit problems. // Fix any sync bit problems from the bottom-up, stopping when we
for (int i = 0; i <= stack_pointer_; i++) { // hit the stack pointer or the top of the frame if the stack
// pointer is floating above the frame.
int limit = Min(stack_pointer_, elements_.length() - 1);
for (int i = 0; i <= limit; i++) {
FrameElement source = elements_[i]; FrameElement source = elements_[i];
FrameElement target = expected->elements_[i]; FrameElement target = expected->elements_[i];
if (source.is_synced() && !target.is_synced()) { if (source.is_synced() && !target.is_synced()) {
...@@ -197,17 +200,15 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) { ...@@ -197,17 +200,15 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) {
// Move registers, constants, and copies to memory. Perform moves // Move registers, constants, and copies to memory. Perform moves
// from the top downward in the frame in order to leave the backing // from the top downward in the frame in order to leave the backing
// stores of copies in registers. // stores of copies in registers.
// // On ARM, all elements are in memory.
// Moving memory-backed copies to memory requires a spare register
// for the memory-to-memory moves. Since we are performing a merge, #ifdef DEBUG
// we use esi (which is already saved in the frame). We keep track int start = Min(stack_pointer_, elements_.length() - 1);
// of the index of the frame element esi is caching or kIllegalIndex for (int i = start; i >= 0; i--) {
// if esi has not been disturbed.
for (int i = 0; i < elements_.length(); i++) {
ASSERT(elements_[i].is_memory()); ASSERT(elements_[i].is_memory());
ASSERT(expected->elements_[i].is_memory()); ASSERT(expected->elements_[i].is_memory());
} }
#endif
} }
......
...@@ -84,6 +84,11 @@ class VirtualFrame : public Malloced { ...@@ -84,6 +84,11 @@ class VirtualFrame : public Malloced {
// the frame after a runtime call). No code is emitted. // the frame after a runtime call). No code is emitted.
void Forget(int count); void Forget(int count);
// Forget count elements from the top of the frame without adjusting
// the stack pointer downward. This is used, for example, before
// merging frames at break, continue, and return targets.
void ForgetElements(int count);
// Spill all values from the frame to memory. // Spill all values from the frame to memory.
void SpillAll(); void SpillAll();
......
...@@ -171,8 +171,11 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) { ...@@ -171,8 +171,11 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) {
MergeMoveRegistersToRegisters(expected); MergeMoveRegistersToRegisters(expected);
MergeMoveMemoryToRegisters(expected); MergeMoveMemoryToRegisters(expected);
// Fix any sync bit problems. // Fix any sync bit problems from the bottom-up, stopping when we
for (int i = 0; i <= stack_pointer_; i++) { // hit the stack pointer or the top of the frame if the stack
// pointer is floating above the frame.
int limit = Min(stack_pointer_, elements_.length() - 1);
for (int i = 0; i <= limit; i++) {
FrameElement source = elements_[i]; FrameElement source = elements_[i];
FrameElement target = expected->elements_[i]; FrameElement target = expected->elements_[i];
if (source.is_synced() && !target.is_synced()) { if (source.is_synced() && !target.is_synced()) {
...@@ -209,7 +212,10 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) { ...@@ -209,7 +212,10 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) {
int esi_caches = kIllegalIndex; int esi_caches = kIllegalIndex;
// A "singleton" memory element. // A "singleton" memory element.
FrameElement memory_element = FrameElement::MemoryElement(); FrameElement memory_element = FrameElement::MemoryElement();
for (int i = stack_pointer_; i >= 0; i--) { // Loop downward from the stack pointer or the top of the frame if
// the stack pointer is floating above the frame.
int start = Min(stack_pointer_, elements_.length() - 1);
for (int i = start; i >= 0; i--) {
FrameElement target = expected->elements_[i]; FrameElement target = expected->elements_[i];
if (target.is_memory()) { if (target.is_memory()) {
FrameElement source = elements_[i]; FrameElement source = elements_[i];
......
...@@ -80,10 +80,18 @@ class VirtualFrame : public Malloced { ...@@ -80,10 +80,18 @@ class VirtualFrame : public Malloced {
// emitted. // emitted.
void Adjust(int count); void Adjust(int count);
// Forget elements from the top of the frame to match an actual frame (eg, // Forget count elements from the top of the frame all in-memory
// the frame after a runtime call). No code is emitted. // (including synced) and adjust the stack pointer downward, to
// match an external frame effect (examples include a call removing
// its arguments, and exiting a try/catch removing an exception
// handler). No code will be emitted.
void Forget(int count); void Forget(int count);
// Forget count elements from the top of the frame without adjusting
// the stack pointer downward. This is used, for example, before
// merging frames at break, continue, and return targets.
void ForgetElements(int count);
// Spill all values from the frame to memory. // Spill all values from the frame to memory.
void SpillAll(); void SpillAll();
...@@ -124,8 +132,7 @@ class VirtualFrame : public Malloced { ...@@ -124,8 +132,7 @@ class VirtualFrame : public Malloced {
void Enter(); void Enter();
void Exit(); void Exit();
// Prepare for returning from the frame by spilling locals and // Prepare for returning from the frame by spilling locals. This
// dropping all non-locals elements in the virtual frame. This
// avoids generating unnecessary merge code when jumping to the // avoids generating unnecessary merge code when jumping to the
// shared return site. Emits code for spills. // shared return site. Emits code for spills.
void PrepareForReturn(); void PrepareForReturn();
......
...@@ -129,13 +129,27 @@ void VirtualFrame::Adjust(int count) { ...@@ -129,13 +129,27 @@ void VirtualFrame::Adjust(int count) {
void VirtualFrame::Forget(int count) { void VirtualFrame::Forget(int count) {
ASSERT(count >= 0); ASSERT(count >= 0);
ASSERT(stack_pointer_ == elements_.length() - 1); ASSERT(stack_pointer_ == elements_.length() - 1);
ASSERT(elements_.length() >= count);
stack_pointer_ -= count; stack_pointer_ -= count;
ForgetElements(count);
}
void VirtualFrame::ForgetElements(int count) {
ASSERT(count >= 0);
ASSERT(elements_.length() >= count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
FrameElement last = elements_.RemoveLast(); FrameElement last = elements_.RemoveLast();
if (last.is_register()) { if (last.is_register()) {
// A hack to properly count register references for the code
// generator's current frame and also for other frames. The
// same code appears in PrepareMergeTo.
if (cgen_->frame() == this) {
Unuse(last.reg()); Unuse(last.reg());
} else {
frame_registers_.Unuse(last.reg());
}
} }
} }
} }
...@@ -324,15 +338,11 @@ void VirtualFrame::AttachToCodeGenerator() { ...@@ -324,15 +338,11 @@ void VirtualFrame::AttachToCodeGenerator() {
void VirtualFrame::PrepareForReturn() { void VirtualFrame::PrepareForReturn() {
// Spill all locals. This is necessary to make sure all locals have // Spill all locals. This is necessary to make sure all locals have
// the right value when breaking at the return site in the debugger. // the right value when breaking at the return site in the debugger.
//
// TODO(203): It is also necessary to ensure that merging at the
// return site does not generate code to overwrite eax, where the
// return value is kept in a non-refcounted register reference.
for (int i = 0; i < expression_base_index(); i++) SpillElementAt(i); for (int i = 0; i < expression_base_index(); i++) SpillElementAt(i);
// Drop all non-local stack elements.
Drop(height());
// Validate state: The expression stack should be empty and the
// stack pointer should have been updated to reflect this.
ASSERT(height() == 0);
ASSERT(stack_pointer_ == expression_base_index() - 1);
} }
......
...@@ -34,10 +34,6 @@ bugs: FAIL ...@@ -34,10 +34,6 @@ bugs: FAIL
# too long to run in debug mode on ARM. # too long to run in debug mode on ARM.
fuzz-natives: PASS, SKIP if ($mode == release || $arch == arm) fuzz-natives: PASS, SKIP if ($mode == release || $arch == arm)
# Bug on IA32, realiably triggers a debug assertion. Unpredictable in
# release mode on IA32, currently passes on ARM.
bugs/bug-265: (PASS || FAIL || CRASH), FAIL if $mode == debug
[ $arch == arm ] [ $arch == arm ]
# Slow tests which times out in debug mode. # Slow tests which times out in debug mode.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment