Commit aef02ccc authored by lrn@chromium.org's avatar lrn@chromium.org

Fast codegen: Working break and continue.

Started framework for all intra-functional outward control transfers,
including handling of try/finally.

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


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3448 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent b4e74720
......@@ -521,21 +521,6 @@ void FastCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
}
void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
Comment cmnt(masm_, "[ ReturnStatement");
Expression* expr = stmt->expression();
// Complete the statement based on the type of the subexpression.
if (expr->AsLiteral() != NULL) {
__ mov(r0, Operand(expr->AsLiteral()->handle()));
} else {
ASSERT_EQ(Expression::kValue, expr->context());
Visit(expr);
__ pop(r0);
}
EmitReturnSequence(stmt->statement_pos());
}
void FastCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
Comment cmnt(masm_, "[ FunctionLiteral");
......@@ -1668,6 +1653,8 @@ void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
}
Register FastCodeGenerator::result_register() { return r0; }
#undef __
......
......@@ -162,6 +162,21 @@ void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) {
}
void MacroAssembler::Drop(int stack_elements, Condition cond) {
if (stack_elements > 0) {
add(sp, sp, Operand(stack_elements * kPointerSize), LeaveCC, cond);
}
}
void MacroAssembler::Call(Label* target) {
bl(target);
}
void MacroAssembler::Move(Register dst, Handle<Object> value) {
mov(dst, Operand(value));
}
void MacroAssembler::SmiJumpTable(Register index, Vector<Label*> targets) {
......@@ -628,6 +643,15 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location,
}
void MacroAssembler::PopTryHandler() {
ASSERT_EQ(0, StackHandlerConstants::kNextOffset);
pop(r1);
mov(ip, Operand(ExternalReference(Top::k_handler_address)));
add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
str(r1, MemOperand(ip));
}
Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
JSObject* holder, Register holder_reg,
Register scratch,
......
......@@ -64,6 +64,9 @@ class MacroAssembler: public Assembler {
void Call(byte* target, RelocInfo::Mode rmode, Condition cond = al);
void Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
void Ret(Condition cond = al);
void Drop(int stack_elements, Condition cond = al);
void Call(Label* target);
void Move(Register dst, Handle<Object> value);
// Jumps to the label at the index given by the Smi in "index".
void SmiJumpTable(Register index, Vector<Label*> targets);
// Load an object from the root table.
......@@ -148,6 +151,9 @@ class MacroAssembler: public Assembler {
// On exit, r0 contains TOS (code slot).
void PushTryHandler(CodeLocation try_location, HandlerType type);
// Unlink the stack handler on top of the stack from the try handler chain.
// Must preserve the result register.
void PopTryHandler();
// ---------------------------------------------------------------------------
// Inline caching support
......
......@@ -690,12 +690,10 @@ void CodeGenSelector::VisitIfStatement(IfStatement* stmt) {
void CodeGenSelector::VisitContinueStatement(ContinueStatement* stmt) {
BAILOUT("ContinueStatement");
}
void CodeGenSelector::VisitBreakStatement(BreakStatement* stmt) {
BAILOUT("BreakStatement");
}
......
......@@ -36,7 +36,7 @@
namespace v8 {
namespace internal {
#define __ ACCESS_MASM(masm_)
#define __ ACCESS_MASM(masm())
Handle<Code> FastCodeGenerator::MakeCode(FunctionLiteral* fun,
Handle<Script> script,
......@@ -232,8 +232,10 @@ void FastCodeGenerator::EmitLogicalOperation(BinaryOperation* expr) {
void FastCodeGenerator::VisitBlock(Block* stmt) {
Comment cmnt(masm_, "[ Block");
Breakable nested_statement(this, stmt);
SetStatementPosition(stmt);
VisitStatements(stmt->statements());
__ bind(nested_statement.break_target());
}
......@@ -278,15 +280,62 @@ void FastCodeGenerator::VisitIfStatement(IfStatement* stmt) {
void FastCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
UNREACHABLE();
Comment cmnt(masm_, "[ ContinueStatement");
NestedStatement* current = nesting_stack_;
int stack_depth = 0;
while (!current->IsContinueTarget(stmt->target())) {
stack_depth = current->Exit(stack_depth);
current = current->outer();
}
__ Drop(stack_depth);
Iteration* loop = current->AsIteration();
__ jmp(loop->continue_target());
}
void FastCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
UNREACHABLE();
Comment cmnt(masm_, "[ BreakStatement");
NestedStatement* current = nesting_stack_;
int stack_depth = 0;
while (!current->IsBreakTarget(stmt->target())) {
stack_depth = current->Exit(stack_depth);
current = current->outer();
}
__ Drop(stack_depth);
Breakable* target = current->AsBreakable();
__ jmp(target->break_target());
}
void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
Comment cmnt(masm_, "[ ReturnStatement");
Expression* expr = stmt->expression();
// Complete the statement based on the type of the subexpression.
if (expr->AsLiteral() != NULL) {
__ Move(result_register(), expr->AsLiteral()->handle());
} else {
ASSERT_EQ(Expression::kValue, expr->context());
Visit(expr);
__ pop(result_register());
}
// Exit all nested statements.
NestedStatement* current = nesting_stack_;
int stack_depth = 0;
while (current != NULL) {
stack_depth = current->Exit(stack_depth);
current = current->outer();
}
__ Drop(stack_depth);
EmitReturnSequence(stmt->statement_pos());
}
void FastCodeGenerator::VisitWithEnterStatement(WithEnterStatement* stmt) {
UNREACHABLE();
}
......@@ -304,8 +353,10 @@ void FastCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
Comment cmnt(masm_, "[ DoWhileStatement");
Label body, stack_limit_hit, stack_check_success;
Iteration loop_statement(this, stmt);
increment_loop_depth();
Label body, exit, stack_limit_hit, stack_check_success;
__ bind(&body);
Visit(stmt->body());
......@@ -316,10 +367,11 @@ void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
// We are not in an expression context because we have been compiling
// statements. Set up a test expression context for the condition.
__ bind(loop_statement.continue_target());
ASSERT_EQ(NULL, true_label_);
ASSERT_EQ(NULL, false_label_);
true_label_ = &body;
false_label_ = &exit;
false_label_ = loop_statement.break_target();
ASSERT(stmt->cond()->context() == Expression::kTest);
Visit(stmt->cond());
true_label_ = NULL;
......@@ -330,7 +382,7 @@ void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
__ CallStub(&stack_stub);
__ jmp(&stack_check_success);
__ bind(&exit);
__ bind(loop_statement.break_target());
decrement_loop_depth();
}
......@@ -338,16 +390,18 @@ void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
Comment cmnt(masm_, "[ WhileStatement");
Label body, stack_limit_hit, stack_check_success;
Iteration loop_statement(this, stmt);
increment_loop_depth();
Label test, body, exit, stack_limit_hit, stack_check_success;
// Emit the test at the bottom of the loop.
__ jmp(&test);
__ jmp(loop_statement.continue_target());
__ bind(&body);
Visit(stmt->body());
__ bind(&test);
__ bind(loop_statement.continue_target());
// Check stack before looping.
__ StackLimitCheck(&stack_limit_hit);
__ bind(&stack_check_success);
......@@ -357,7 +411,7 @@ void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
ASSERT_EQ(NULL, true_label_);
ASSERT_EQ(NULL, false_label_);
true_label_ = &body;
false_label_ = &exit;
false_label_ = loop_statement.break_target();
ASSERT(stmt->cond()->context() == Expression::kTest);
Visit(stmt->cond());
true_label_ = NULL;
......@@ -368,8 +422,7 @@ void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
__ CallStub(&stack_stub);
__ jmp(&stack_check_success);
__ bind(&exit);
__ bind(loop_statement.break_target());
decrement_loop_depth();
}
......@@ -513,6 +566,23 @@ void FastCodeGenerator::VisitThrow(Throw* expr) {
}
int FastCodeGenerator::TryFinally::Exit(int stack_depth) {
// The macros used here must preserve the result register.
__ Drop(stack_depth);
__ PopTryHandler();
__ Call(finally_entry_);
return 0;
}
int FastCodeGenerator::TryCatch::Exit(int stack_depth) {
// The macros used here must preserve the result register.
__ Drop(stack_depth);
__ PopTryHandler();
return 0;
}
#undef __
......
......@@ -35,6 +35,8 @@
namespace v8 {
namespace internal {
// -----------------------------------------------------------------------------
// Fast code generator.
class FastCodeGenerator: public AstVisitor {
public:
......@@ -43,6 +45,7 @@ class FastCodeGenerator: public AstVisitor {
function_(NULL),
script_(script),
is_eval_(is_eval),
nesting_stack_(NULL),
loop_depth_(0),
true_label_(NULL),
false_label_(NULL) {
......@@ -55,6 +58,159 @@ class FastCodeGenerator: public AstVisitor {
void Generate(FunctionLiteral* fun);
private:
class Breakable;
class Iteration;
class TryCatch;
class TryFinally;
class Finally;
class ForIn;
class NestedStatement BASE_EMBEDDED {
public:
explicit NestedStatement(FastCodeGenerator* codegen) : codegen_(codegen) {
// Link into codegen's nesting stack.
previous_ = codegen->nesting_stack_;
codegen->nesting_stack_ = this;
}
virtual ~NestedStatement() {
// Unlink from codegen's nesting stack.
ASSERT_EQ(this, codegen_->nesting_stack_);
codegen_->nesting_stack_ = previous_;
}
virtual Breakable* AsBreakable() { return NULL; }
virtual Iteration* AsIteration() { return NULL; }
virtual TryCatch* AsTryCatch() { return NULL; }
virtual TryFinally* AsTryFinally() { return NULL; }
virtual Finally* AsFinally() { return NULL; }
virtual ForIn* AsForIn() { return NULL; }
virtual bool IsContinueTarget(Statement* target) { return false; }
virtual bool IsBreakTarget(Statement* target) { return false; }
// Generate code to leave the nested statement. This includes
// cleaning up any stack elements in use and restoring the
// stack to the expectations of the surrounding statements.
// Takes a number of stack elements currently on top of the
// nested statement's stack, and returns a number of stack
// elements left on top of the surrounding statement's stack.
// The generated code must preserve the result register (which
// contains the value in case of a return).
virtual int Exit(int stack_depth) {
// Default implementation for the case where there is
// nothing to clean up.
return stack_depth;
}
NestedStatement* outer() { return previous_; }
protected:
MacroAssembler* masm() { return codegen_->masm(); }
private:
FastCodeGenerator* codegen_;
NestedStatement* previous_;
DISALLOW_COPY_AND_ASSIGN(NestedStatement);
};
class Breakable : public NestedStatement {
public:
Breakable(FastCodeGenerator* codegen,
BreakableStatement* break_target)
: NestedStatement(codegen),
target_(break_target) {}
virtual ~Breakable() {}
virtual Breakable* AsBreakable() { return this; }
virtual bool IsBreakTarget(Statement* statement) {
return target_ == statement;
}
BreakableStatement* statement() { return target_; }
Label* break_target() { return &break_target_label_; }
private:
BreakableStatement* target_;
Label break_target_label_;
DISALLOW_COPY_AND_ASSIGN(Breakable);
};
class Iteration : public Breakable {
public:
Iteration(FastCodeGenerator* codegen,
IterationStatement* iteration_statement)
: Breakable(codegen, iteration_statement) {}
virtual ~Iteration() {}
virtual Iteration* AsIteration() { return this; }
virtual bool IsContinueTarget(Statement* statement) {
return this->statement() == statement;
}
Label* continue_target() { return &continue_target_label_; }
private:
Label continue_target_label_;
DISALLOW_COPY_AND_ASSIGN(Iteration);
};
// The environment inside the try block of a try/catch statement.
class TryCatch : public NestedStatement {
public:
explicit TryCatch(FastCodeGenerator* codegen, Label* catch_entry)
: NestedStatement(codegen), catch_entry_(catch_entry) { }
virtual ~TryCatch() {}
virtual TryCatch* AsTryCatch() { return this; }
Label* catch_entry() { return catch_entry_; }
virtual int Exit(int stack_depth);
private:
Label* catch_entry_;
DISALLOW_COPY_AND_ASSIGN(TryCatch);
};
// The environment inside the try block of a try/finally statement.
class TryFinally : public NestedStatement {
public:
explicit TryFinally(FastCodeGenerator* codegen, Label* finally_entry)
: NestedStatement(codegen), finally_entry_(finally_entry) { }
virtual ~TryFinally() {}
virtual TryFinally* AsTryFinally() { return this; }
Label* finally_entry() { return finally_entry_; }
virtual int Exit(int stack_depth);
private:
Label* finally_entry_;
DISALLOW_COPY_AND_ASSIGN(TryFinally);
};
// A FinallyEnvironment represents being inside a finally block.
// Abnormal termination of the finally block needs to clean up
// the block's parameters from the stack.
class Finally : public NestedStatement {
public:
explicit Finally(FastCodeGenerator* codegen) : NestedStatement(codegen) { }
virtual ~Finally() {}
virtual Finally* AsFinally() { return this; }
virtual int Exit(int stack_depth) {
return stack_depth + kFinallyStackElementCount;
}
private:
// Number of extra stack slots occupied during a finally block.
static const int kFinallyStackElementCount = 2;
DISALLOW_COPY_AND_ASSIGN(Finally);
};
// A ForInEnvironment represents being inside a for-in loop.
// Abnormal termination of the for-in block needs to clean up
// the block's temporary storage from the stack.
class ForIn : public Iteration {
public:
ForIn(FastCodeGenerator* codegen,
ForInStatement* statement)
: Iteration(codegen, statement) { }
virtual ~ForIn() {}
virtual ForIn* AsForIn() { return this; }
virtual int Exit(int stack_depth) {
return stack_depth + kForInStackElementCount;
}
private:
// TODO(lrn): Check that this value is correct when implementing
// for-in.
static const int kForInStackElementCount = 5;
DISALLOW_COPY_AND_ASSIGN(ForIn);
};
int SlotOffset(Slot* slot);
void Move(Expression::Context destination, Register source);
void Move(Expression::Context destination, Slot* source, Register scratch);
......@@ -112,11 +268,13 @@ class FastCodeGenerator: public AstVisitor {
loop_depth_--;
}
MacroAssembler* masm() { return masm_; }
static Register result_register();
// AST node visit functions.
#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
// Handles the shortcutted logical binary operations in VisitBinaryOperation.
void EmitLogicalOperation(BinaryOperation* expr);
......@@ -125,11 +283,14 @@ class FastCodeGenerator: public AstVisitor {
Handle<Script> script_;
bool is_eval_;
Label return_label_;
NestedStatement* nesting_stack_;
int loop_depth_;
Label* true_label_;
Label* false_label_;
friend class NestedStatement;
DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator);
};
......
......@@ -515,20 +515,6 @@ void FastCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
}
void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
Comment cmnt(masm_, "[ ReturnStatement");
Expression* expr = stmt->expression();
if (expr->AsLiteral() != NULL) {
__ mov(eax, expr->AsLiteral()->handle());
} else {
ASSERT_EQ(Expression::kValue, expr->context());
Visit(expr);
__ pop(eax);
}
EmitReturnSequence(stmt->statement_pos());
}
void FastCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
Comment cmnt(masm_, "[ FunctionLiteral");
......@@ -1639,6 +1625,7 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Convert current context to test context: End post-test code.
}
Register FastCodeGenerator::result_register() { return eax; }
void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
__ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
......
......@@ -504,6 +504,13 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location,
}
void MacroAssembler::PopTryHandler() {
ASSERT_EQ(0, StackHandlerConstants::kNextOffset);
pop(Operand::StaticVariable(ExternalReference(Top::k_handler_address)));
add(Operand(esp), Immediate(StackHandlerConstants::kSize - kPointerSize));
}
Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
JSObject* holder, Register holder_reg,
Register scratch,
......@@ -1350,6 +1357,18 @@ void MacroAssembler::Ret() {
}
void MacroAssembler::Drop(int stack_elements) {
if (stack_elements > 0) {
add(Operand(esp), Immediate(stack_elements * kPointerSize));
}
}
void MacroAssembler::Move(Register dst, Handle<Object> value) {
mov(dst, value);
}
void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
if (FLAG_native_code_counters && counter->Enabled()) {
mov(Operand::StaticVariable(ExternalReference(counter)), Immediate(value));
......
......@@ -149,6 +149,9 @@ class MacroAssembler: public Assembler {
// address must be pushed before calling this helper.
void PushTryHandler(CodeLocation try_location, HandlerType type);
// Unlink the stack handler on top of the stack from the try handler chain.
void PopTryHandler();
// ---------------------------------------------------------------------------
// Inline caching support
......@@ -333,6 +336,12 @@ class MacroAssembler: public Assembler {
void Ret();
void Drop(int element_count);
void Call(Label* target) { call(target); }
void Move(Register target, Handle<Object> value);
struct Unresolved {
int pc;
uint32_t flags; // see Bootstrapper::FixupFlags decoders/encoders.
......
......@@ -525,20 +525,6 @@ void FastCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
}
void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
Comment cmnt(masm_, "[ ReturnStatement");
Expression* expr = stmt->expression();
if (expr->AsLiteral() != NULL) {
__ Move(rax, expr->AsLiteral()->handle());
} else {
Visit(expr);
ASSERT_EQ(Expression::kValue, expr->context());
__ pop(rax);
}
EmitReturnSequence(stmt->statement_pos());
}
void FastCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
Comment cmnt(masm_, "[ FunctionLiteral");
......@@ -1650,6 +1636,8 @@ void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
}
Register FastCodeGenerator::result_register() { return rax; }
#undef __
......
......@@ -1345,6 +1345,13 @@ void MacroAssembler::Push(Smi* source) {
}
void MacroAssembler::Drop(int stack_elements) {
if (stack_elements > 0) {
addq(rsp, Immediate(stack_elements * kPointerSize));
}
}
void MacroAssembler::Test(const Operand& src, Smi* source) {
intptr_t smi = reinterpret_cast<intptr_t>(source);
if (is_int32(smi)) {
......@@ -1431,6 +1438,14 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location,
}
void MacroAssembler::PopTryHandler() {
ASSERT_EQ(0, StackHandlerConstants::kNextOffset);
movq(kScratchRegister, ExternalReference(Top::k_handler_address));
pop(Operand(kScratchRegister, 0));
addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
}
void MacroAssembler::Ret() {
ret(0);
}
......
......@@ -400,7 +400,7 @@ class MacroAssembler: public Assembler {
void Test(const Operand& dst, Smi* source);
// ---------------------------------------------------------------------------
// Macro instructions
// Macro instructions.
// Load a register with a long value as efficiently as possible.
void Set(Register dst, int64_t x);
......@@ -412,6 +412,8 @@ class MacroAssembler: public Assembler {
void Cmp(Register dst, Handle<Object> source);
void Cmp(const Operand& dst, Handle<Object> source);
void Push(Handle<Object> source);
void Drop(int stack_elements);
void Call(Label* target) { call(target); }
// Control Flow
void Jump(Address destination, RelocInfo::Mode rmode);
......@@ -443,6 +445,8 @@ class MacroAssembler: public Assembler {
// address must be pushed before calling this helper.
void PushTryHandler(CodeLocation try_location, HandlerType type);
// Unlink the stack handler on top of the stack from the try handler chain.
void PopTryHandler();
// ---------------------------------------------------------------------------
// Inline caching support
......
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