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) {
// implementations.
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.
int PcStoreOffset();
......
......@@ -177,7 +177,6 @@ enum Hint {
taken = 0x3e
};
// The result of negating a hint is as if the corresponding condition
// were negated by NegateCondition. That is, no_hint is mapped to
// itself and not_taken and taken are mapped to each other.
......@@ -187,6 +186,7 @@ inline Hint NegateHint(Hint hint) {
: ((hint == not_taken) ? taken : not_taken);
}
// -----------------------------------------------------------------------------
// Machine instruction Immediates
......
......@@ -202,11 +202,6 @@ class BreakableStatement: public Statement {
// Code generation
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.
bool is_target_for_anonymous() const { return type_ == TARGET_FOR_ANONYMOUS; }
......@@ -220,7 +215,6 @@ class BreakableStatement: public Statement {
ZoneStringList* labels_;
Type type_;
BreakTarget break_target_;
int break_stack_height_;
};
......@@ -451,12 +445,12 @@ class CaseClause: public ZoneObject {
CHECK(!is_default());
return label_;
}
BreakTarget* body_target() { return &body_target_; }
JumpTarget* body_target() { return &body_target_; }
ZoneList<Statement*>* statements() const { return statements_; }
private:
Expression* label_;
BreakTarget body_target_;
JumpTarget body_target_;
ZoneList<Statement*>* statements_;
};
......
......@@ -83,7 +83,6 @@ CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script,
allocator_(NULL),
cc_reg_(al),
state_(NULL),
break_stack_height_(0),
function_return_is_shadowed_(false),
in_spilled_code_(false) {
}
......@@ -107,8 +106,6 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
ASSERT(frame_ == NULL);
frame_ = new VirtualFrame(this);
cc_reg_ = al;
function_return_.Initialize(this, JumpTarget::BIDIRECTIONAL);
function_return_is_shadowed_ = false;
set_in_spilled_code(false);
{
CodeGenState state(this);
......@@ -133,6 +130,10 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
// Allocate space for locals and initialize them.
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);
if (scope_->num_heap_slots() > 0) {
......@@ -1127,7 +1128,6 @@ void CodeGenerator::VisitBlock(Block* node) {
VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ Block");
CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this);
VisitStatementsAndSpill(node->statements());
if (node->break_target()->is_linked()) {
......@@ -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) {
VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ ContinueStatement");
CodeForStatementPosition(node);
CleanStack(break_stack_height_ - node->target()->break_stack_height());
node->target()->continue_target()->Jump();
}
......@@ -1361,7 +1353,6 @@ void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ BreakStatement");
CodeForStatementPosition(node);
CleanStack(break_stack_height_ - node->target()->break_stack_height());
node->target()->break_target()->Jump();
}
......@@ -1521,7 +1512,6 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ SwitchStatement");
CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this);
LoadAndSpill(node->tag());
......@@ -1615,7 +1605,6 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ LoopStatement");
CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this);
// Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a
......@@ -1805,15 +1794,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
Comment cmnt(masm_, "[ ForInStatement");
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 jsobject(this);
JumpTarget fixed_array(this);
......@@ -1902,6 +1882,11 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
// sp[2] : array or enum cache
// sp[3] : 0 or map
// 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(r1, frame_->ElementAt(1)); // load the length
__ cmp(r0, Operand(r1)); // compare to the array length
......@@ -1986,7 +1971,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
// Exit.
exit.Bind();
break_stack_height_ -= kForInStackSize;
ASSERT(frame_->height() == original_height);
}
......@@ -2259,16 +2243,11 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
frame_->EmitPush(r2);
// We keep two elements on the stack - the (possibly faked) result
// and the state - while evaluating the finally block. Record it, so
// that a break/continue crossing this statement can restore the
// stack.
const int kFinallyStackSize = 2 * kPointerSize;
break_stack_height_ += kFinallyStackSize;
// and the state - while evaluating the finally block.
//
// Generate code for the statements in the finally block.
VisitStatementsAndSpill(node->finally_block()->statements());
break_stack_height_ -= kFinallyStackSize;
if (has_valid_frame()) {
JumpTarget exit(this);
// Restore state and return value or faked TOS.
......
......@@ -330,7 +330,6 @@ class CodeGenerator: public AstVisitor {
// Control flow
void Branch(bool if_true, JumpTarget* target);
void CheckStack();
void CleanStack(int num_bytes);
bool CheckForInlineRuntimeCall(CallRuntime* node);
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
......@@ -446,7 +445,6 @@ class CodeGenerator: public AstVisitor {
RegisterAllocator* allocator_;
Condition cc_reg_;
CodeGenState* state_;
int break_stack_height_;
// Jump targets
BreakTarget function_return_;
......
......@@ -80,7 +80,6 @@ CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script,
frame_(NULL),
allocator_(NULL),
state_(NULL),
break_stack_height_(0),
loop_nesting_(0),
function_return_is_shadowed_(false),
in_spilled_code_(false) {
......@@ -107,8 +106,6 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
allocator_ = &register_allocator;
ASSERT(frame_ == NULL);
frame_ = new VirtualFrame(this);
function_return_.Initialize(this, JumpTarget::BIDIRECTIONAL);
function_return_is_shadowed_ = false;
set_in_spilled_code(false);
// Adjust for function-level loop nesting.
......@@ -125,7 +122,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
// esi: callee's context
allocator_->Initialize();
frame_->Enter();
// tos: code slot
#ifdef DEBUG
if (strlen(FLAG_stop_at) > 0 &&
fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
......@@ -136,6 +133,10 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
// Allocate space for locals and initialize them.
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.
if (scope_->arguments() != NULL) {
......@@ -1561,7 +1562,6 @@ void CodeGenerator::VisitBlock(Block* node) {
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ Block");
CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this);
VisitStatements(node->statements());
if (node->break_target()->is_linked()) {
......@@ -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) {
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ ContinueStatement");
CodeForStatementPosition(node);
CleanStack(break_stack_height_ - node->target()->break_stack_height());
node->target()->continue_target()->Jump();
}
......@@ -1782,7 +1775,6 @@ void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ BreakStatement");
CodeForStatementPosition(node);
CleanStack(break_stack_height_ - node->target()->break_stack_height());
node->target()->break_target()->Jump();
}
......@@ -2020,7 +2012,6 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ SwitchStatement");
CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this);
// Compile the switch value.
......@@ -2148,7 +2139,6 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) {
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ LoopStatement");
CodeForStatementPosition(node);
node->set_break_stack_height(break_stack_height_);
node->break_target()->Initialize(this);
// Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a
......@@ -2471,15 +2461,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
Comment cmnt(masm_, "[ ForInStatement");
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 jsobject(this);
JumpTarget fixed_array(this);
......@@ -2568,6 +2549,11 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
// Condition.
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
__ cmp(eax, frame_->ElementAt(1)); // compare to the array length
node->break_target()->Branch(above_equal);
......@@ -2650,8 +2636,6 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
// Exit.
exit.Bind();
break_stack_height_ -= kForInStackSize;
}
......@@ -2917,16 +2901,11 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) {
frame_->EmitPush(ecx);
// We keep two elements on the stack - the (possibly faked) result
// and the state - while evaluating the finally block. Record it, so
// that a break/continue crossing this statement can restore the
// stack.
const int kFinallyStackSize = 2 * kPointerSize;
break_stack_height_ += kFinallyStackSize;
// and the state - while evaluating the finally block.
//
// Generate code for the statements in the finally block.
VisitStatementsAndSpill(node->finally_block()->statements());
break_stack_height_ -= kFinallyStackSize;
if (has_valid_frame()) {
JumpTarget exit(this);
// Restore state and return value or faked TOS.
......
......@@ -501,7 +501,6 @@ class CodeGenerator: public AstVisitor {
void CallWithArguments(ZoneList<Expression*>* arguments, int position);
void CheckStack();
void CleanStack(int num_bytes);
bool CheckForInlineRuntimeCall(CallRuntime* node);
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
......@@ -617,7 +616,6 @@ class CodeGenerator: public AstVisitor {
VirtualFrame* frame_;
RegisterAllocator* allocator_;
CodeGenState* state_;
int break_stack_height_;
int loop_nesting_;
// Jump targets.
......
......@@ -37,23 +37,48 @@ namespace v8 { namespace internal {
JumpTarget::JumpTarget(CodeGenerator* cgen, Directionality direction)
: cgen_(cgen),
masm_(cgen == NULL ? NULL : cgen->masm()),
direction_(direction),
reaching_frames_(0),
merge_labels_(0),
entry_frame_(NULL),
is_bound_(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() {
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_;
entry_frame_ = NULL;
is_bound_ = false;
is_linked_ = false;
Reset();
}
......@@ -501,16 +526,10 @@ void JumpTarget::AddReachingFrame(VirtualFrame* frame) {
// -------------------------------------------------------------------------
// BreakTarget implementation.
BreakTarget::BreakTarget() : JumpTarget(NULL, FORWARD_ONLY) {
}
void BreakTarget::Initialize(CodeGenerator* cgen, Directionality direction) {
ASSERT(cgen != NULL);
ASSERT(cgen_ == NULL);
cgen_ = cgen;
masm_ = cgen->masm();
direction_ = direction;
JumpTarget::Initialize(cgen, direction);
ASSERT(cgen_->has_valid_frame());
expected_height_ = cgen_->frame()->height();
}
......@@ -530,6 +549,58 @@ void BreakTarget::CopyTo(BreakTarget* destination) {
destination->entry_label_ = entry_label_;
destination->is_bound_ = is_bound_;
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) {
// While shadowing this shadow target saves the state of the original.
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
// being used until shadowing stops.
cgen_ = 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.
explicit JumpTarget(CodeGenerator* cgen,
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(); }
// 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.
CodeGenerator* code_generator() const { return cgen_; }
......@@ -77,19 +100,9 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
bool is_linked() const { return 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
// jump and there will be no current frame after the jump.
void Jump();
virtual void Jump();
void Jump(Result* arg);
void Jump(Result* arg0, Result* arg1);
void Jump(Result* arg0, Result* arg1, Result* arg2);
......@@ -97,7 +110,7 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
// 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.
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* arg0, Result* arg1, Hint hint = no_hint);
void Branch(Condition cc,
......@@ -125,7 +138,7 @@ class JumpTarget : public ZoneObject { // Shadows are dynamically allocated.
// A mergable elements argument of kAllElements indicates that all
// frame elements must be mergable. Mergable elements are ignored
// 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* arg0, Result* arg1, int mergable_elements = kAllElements);
void Bind(Result* arg0,
......@@ -209,13 +222,13 @@ class BreakTarget : public JumpTarget {
// generator must be supplied before using the break target as a
// label. This is useful, eg, when break targets are embedded in AST
// nodes.
BreakTarget();
BreakTarget() {}
// 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.
void Initialize(CodeGenerator* cgen,
// Supply a code generator, expected expression stack height, and
// directionality to an already constructed break 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);
// Copy the state of this break target to the destination. The
......@@ -225,7 +238,28 @@ class BreakTarget : public JumpTarget {
// overwritten, without deallocating pointed-to virtual frames.
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:
// 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);
};
......
......@@ -168,8 +168,11 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) {
MergeMoveRegistersToRegisters(expected);
MergeMoveMemoryToRegisters(expected);
// Fix any sync bit problems.
for (int i = 0; i <= stack_pointer_; i++) {
// Fix any sync bit problems from the bottom-up, stopping when we
// 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 target = expected->elements_[i];
if (source.is_synced() && !target.is_synced()) {
......@@ -197,17 +200,15 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) {
// Move registers, constants, and copies to memory. Perform moves
// from the top downward in the frame in order to leave the backing
// stores of copies in registers.
//
// Moving memory-backed copies to memory requires a spare register
// for the memory-to-memory moves. Since we are performing a merge,
// we use esi (which is already saved in the frame). We keep track
// of the index of the frame element esi is caching or kIllegalIndex
// if esi has not been disturbed.
for (int i = 0; i < elements_.length(); i++) {
// On ARM, all elements are in memory.
#ifdef DEBUG
int start = Min(stack_pointer_, elements_.length() - 1);
for (int i = start; i >= 0; i--) {
ASSERT(elements_[i].is_memory());
ASSERT(expected->elements_[i].is_memory());
}
#endif
}
......
......@@ -84,6 +84,11 @@ class VirtualFrame : public Malloced {
// the frame after a runtime call). No code is emitted.
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.
void SpillAll();
......
......@@ -171,8 +171,11 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) {
MergeMoveRegistersToRegisters(expected);
MergeMoveMemoryToRegisters(expected);
// Fix any sync bit problems.
for (int i = 0; i <= stack_pointer_; i++) {
// Fix any sync bit problems from the bottom-up, stopping when we
// 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 target = expected->elements_[i];
if (source.is_synced() && !target.is_synced()) {
......@@ -209,7 +212,10 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) {
int esi_caches = kIllegalIndex;
// A "singleton" memory element.
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];
if (target.is_memory()) {
FrameElement source = elements_[i];
......
......@@ -80,10 +80,18 @@ class VirtualFrame : public Malloced {
// emitted.
void Adjust(int count);
// Forget elements from the top of the frame to match an actual frame (eg,
// the frame after a runtime call). No code is emitted.
// Forget count elements from the top of the frame all in-memory
// (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);
// 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.
void SpillAll();
......@@ -124,8 +132,7 @@ class VirtualFrame : public Malloced {
void Enter();
void Exit();
// Prepare for returning from the frame by spilling locals and
// dropping all non-locals elements in the virtual frame. This
// Prepare for returning from the frame by spilling locals. This
// avoids generating unnecessary merge code when jumping to the
// shared return site. Emits code for spills.
void PrepareForReturn();
......
......@@ -129,13 +129,27 @@ void VirtualFrame::Adjust(int count) {
void VirtualFrame::Forget(int count) {
ASSERT(count >= 0);
ASSERT(stack_pointer_ == elements_.length() - 1);
ASSERT(elements_.length() >= 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++) {
FrameElement last = elements_.RemoveLast();
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());
} else {
frame_registers_.Unuse(last.reg());
}
}
}
}
......@@ -324,15 +338,11 @@ void VirtualFrame::AttachToCodeGenerator() {
void VirtualFrame::PrepareForReturn() {
// Spill all locals. This is necessary to make sure all locals have
// 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);
// 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
# too long to run in debug mode on 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 ]
# 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