Commit 1c5a9435 authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

ARM: Change code generation for function return

Generating code for function return on ARM is now aligned with the other platforms. The first non-shadowed return statement encountered will emit code for function return and all other returns including the fall through at the bottom of a function will jump to that.
Review URL: http://codereview.chromium.org/2815003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4856 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 142de628
...@@ -342,56 +342,27 @@ void CodeGenerator::Generate(CompilationInfo* info) { ...@@ -342,56 +342,27 @@ void CodeGenerator::Generate(CompilationInfo* info) {
} }
} }
// Generate the return sequence if necessary. // Handle the return from the function.
if (has_valid_frame() || function_return_.is_linked()) { if (has_valid_frame()) {
if (!function_return_.is_linked()) { // If there is a valid frame, control flow can fall off the end of
CodeForReturnPosition(info->function()); // the body. In that case there is an implicit return statement.
} ASSERT(!function_return_is_shadowed_);
// exit frame_->PrepareForReturn();
// r0: result
// sp: stack pointer
// fp: frame pointer
// cp: callee's context
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex); __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
if (function_return_.is_bound()) {
function_return_.Jump();
} else {
function_return_.Bind();
GenerateReturnSequence();
}
} else if (function_return_.is_linked()) {
// If the return target has dangling jumps to it, then we have not
// yet generated the return sequence. This can happen when (a)
// control does not flow off the end of the body so we did not
// compile an artificial return statement just above, and (b) there
// are return statements in the body but (c) they are all shadowed.
function_return_.Bind(); function_return_.Bind();
if (FLAG_trace) { GenerateReturnSequence();
// Push the return value on the stack as the parameter.
// Runtime::TraceExit returns the parameter as it is.
frame_->EmitPush(r0);
frame_->CallRuntime(Runtime::kTraceExit, 1);
}
#ifdef DEBUG
// Add a label for checking the size of the code used for returning.
Label check_exit_codesize;
masm_->bind(&check_exit_codesize);
#endif
// Make sure that the constant pool is not emitted inside of the return
// sequence.
{ Assembler::BlockConstPoolScope block_const_pool(masm_);
// Tear down the frame which will restore the caller's frame pointer and
// the link register.
frame_->Exit();
// Here we use masm_-> instead of the __ macro to avoid the code coverage
// tool from instrumenting as we rely on the code size here.
int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
masm_->add(sp, sp, Operand(sp_delta));
masm_->Jump(lr);
#ifdef DEBUG
// Check that the size of the code used for returning matches what is
// expected by the debugger. If the sp_delts above cannot be encoded in
// the add instruction the add will generate two instructions.
int return_sequence_length =
masm_->InstructionsGeneratedSince(&check_exit_codesize);
CHECK(return_sequence_length ==
Assembler::kJSReturnSequenceInstructions ||
return_sequence_length ==
Assembler::kJSReturnSequenceInstructions + 1);
#endif
}
} }
// Adjust for function-level loop nesting. // Adjust for function-level loop nesting.
...@@ -1958,8 +1929,56 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) { ...@@ -1958,8 +1929,56 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
// returning thus making it easier to merge. // returning thus making it easier to merge.
frame_->EmitPop(r0); frame_->EmitPop(r0);
frame_->PrepareForReturn(); frame_->PrepareForReturn();
if (function_return_.is_bound()) {
// If the function return label is already bound we reuse the
// code by jumping to the return site.
function_return_.Jump();
} else {
function_return_.Bind();
GenerateReturnSequence();
}
}
}
function_return_.Jump();
void CodeGenerator::GenerateReturnSequence() {
if (FLAG_trace) {
// Push the return value on the stack as the parameter.
// Runtime::TraceExit returns the parameter as it is.
frame_->EmitPush(r0);
frame_->CallRuntime(Runtime::kTraceExit, 1);
}
#ifdef DEBUG
// Add a label for checking the size of the code used for returning.
Label check_exit_codesize;
masm_->bind(&check_exit_codesize);
#endif
// Make sure that the constant pool is not emitted inside of the return
// sequence.
{ Assembler::BlockConstPoolScope block_const_pool(masm_);
// Tear down the frame which will restore the caller's frame pointer and
// the link register.
frame_->Exit();
// Here we use masm_-> instead of the __ macro to avoid the code coverage
// tool from instrumenting as we rely on the code size here.
int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
masm_->add(sp, sp, Operand(sp_delta));
masm_->Jump(lr);
DeleteFrame();
#ifdef DEBUG
// Check that the size of the code used for returning matches what is
// expected by the debugger. If the sp_delts above cannot be encoded in
// the add instruction the add will generate two instructions.
int return_sequence_length =
masm_->InstructionsGeneratedSince(&check_exit_codesize);
CHECK(return_sequence_length ==
Assembler::kJSReturnSequenceInstructions ||
return_sequence_length ==
Assembler::kJSReturnSequenceInstructions + 1);
#endif
} }
} }
......
...@@ -315,6 +315,12 @@ class CodeGenerator: public AstVisitor { ...@@ -315,6 +315,12 @@ class CodeGenerator: public AstVisitor {
// Main code generation function // Main code generation function
void Generate(CompilationInfo* info); void Generate(CompilationInfo* info);
// Generate the return sequence code. Should be called no more than
// once per compiled function, immediately after binding the return
// target (which can not be done more than once). The return value should
// be in r0.
void GenerateReturnSequence();
// Returns the arguments allocation mode. // Returns the arguments allocation mode.
ArgumentsAllocationMode ArgumentsMode(); ArgumentsAllocationMode ArgumentsMode();
......
...@@ -212,10 +212,9 @@ class VirtualFrame : public ZoneObject { ...@@ -212,10 +212,9 @@ class VirtualFrame : public ZoneObject {
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 elements in the virtual frame. 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. No spill code emitted. Value to return should be in r0.
inline void PrepareForReturn(); inline void PrepareForReturn();
// Number of local variables after when we use a loop for allocating. // Number of local variables after when we use a loop for allocating.
......
...@@ -3278,6 +3278,9 @@ void CodeGenerator::VisitAndSpill(Statement* statement) { ...@@ -3278,6 +3278,9 @@ void CodeGenerator::VisitAndSpill(Statement* statement) {
void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) { void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) {
#ifdef DEBUG
int original_height = frame_->height();
#endif
ASSERT(in_spilled_code()); ASSERT(in_spilled_code());
set_in_spilled_code(false); set_in_spilled_code(false);
VisitStatements(statements); VisitStatements(statements);
...@@ -3285,14 +3288,20 @@ void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) { ...@@ -3285,14 +3288,20 @@ void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) {
frame_->SpillAll(); frame_->SpillAll();
} }
set_in_spilled_code(true); set_in_spilled_code(true);
ASSERT(!has_valid_frame() || frame_->height() == original_height);
} }
void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) { void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
#ifdef DEBUG
int original_height = frame_->height();
#endif
ASSERT(!in_spilled_code()); ASSERT(!in_spilled_code());
for (int i = 0; has_valid_frame() && i < statements->length(); i++) { for (int i = 0; has_valid_frame() && i < statements->length(); i++) {
Visit(statements->at(i)); Visit(statements->at(i));
} }
ASSERT(!has_valid_frame() || frame_->height() == original_height);
} }
......
...@@ -74,7 +74,9 @@ bool VirtualFrame::Equals(const VirtualFrame* other) { ...@@ -74,7 +74,9 @@ bool VirtualFrame::Equals(const VirtualFrame* other) {
void VirtualFrame::PrepareForReturn() { void VirtualFrame::PrepareForReturn() {
SpillAll(); // Don't bother flushing tos registers as returning does not require more
// access to the expression stack.
top_of_stack_state_ = NO_TOS_REGISTERS;
} }
......
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