Revision: 3448 Author: [email protected] Date: Thu Dec 10 06:06:08 2009 Log: 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 http://code.google.com/p/v8/source/detail?r=3448 Modified: /branches/bleeding_edge/src/arm/fast-codegen-arm.cc /branches/bleeding_edge/src/arm/macro-assembler-arm.cc /branches/bleeding_edge/src/arm/macro-assembler-arm.h /branches/bleeding_edge/src/compiler.cc /branches/bleeding_edge/src/fast-codegen.cc /branches/bleeding_edge/src/fast-codegen.h /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h /branches/bleeding_edge/src/x64/fast-codegen-x64.cc /branches/bleeding_edge/src/x64/macro-assembler-x64.cc /branches/bleeding_edge/src/x64/macro-assembler-x64.h ======================================= --- /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Wed Dec 9 05:06:08 2009 +++ /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Thu Dec 10 06:06:08 2009 @@ -519,21 +519,6 @@ __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. } - - -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) { @@ -1667,6 +1652,8 @@ Move(expr->context(), r0); } + +Register FastCodeGenerator::result_register() { return r0; } #undef __ ======================================= --- /branches/bleeding_edge/src/arm/macro-assembler-arm.cc Wed Dec 9 03:14:45 2009 +++ /branches/bleeding_edge/src/arm/macro-assembler-arm.cc Thu Dec 10 06:06:08 2009 @@ -162,6 +162,21 @@ } +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) { @@ -626,6 +641,15 @@ str(sp, MemOperand(r7)); } } + + +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, ======================================= --- /branches/bleeding_edge/src/arm/macro-assembler-arm.h Thu Nov 26 02:28:32 2009 +++ /branches/bleeding_edge/src/arm/macro-assembler-arm.h Thu Dec 10 06:06:08 2009 @@ -64,6 +64,9 @@ 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 @@ // 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 ======================================= --- /branches/bleeding_edge/src/compiler.cc Thu Dec 10 05:12:13 2009 +++ /branches/bleeding_edge/src/compiler.cc Thu Dec 10 06:06:08 2009 @@ -690,12 +690,10 @@ void CodeGenSelector::VisitContinueStatement(ContinueStatement* stmt) { - BAILOUT("ContinueStatement"); } void CodeGenSelector::VisitBreakStatement(BreakStatement* stmt) { - BAILOUT("BreakStatement"); } ======================================= --- /branches/bleeding_edge/src/fast-codegen.cc Thu Dec 10 05:12:13 2009 +++ /branches/bleeding_edge/src/fast-codegen.cc Thu Dec 10 06:06:08 2009 @@ -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::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::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::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 @@ // 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 @@ __ CallStub(&stack_stub); __ jmp(&stack_check_success); - __ bind(&exit); + __ bind(loop_statement.break_target()); decrement_loop_depth(); } @@ -338,16 +390,18 @@ 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 @@ 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 @@ __ CallStub(&stack_stub); __ jmp(&stack_check_success); - __ bind(&exit); - + __ bind(loop_statement.break_target()); decrement_loop_depth(); } @@ -511,6 +564,23 @@ void FastCodeGenerator::VisitThrow(Throw* expr) { UNREACHABLE(); } + + +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 __ ======================================= --- /branches/bleeding_edge/src/fast-codegen.h Thu Nov 26 02:28:32 2009 +++ /branches/bleeding_edge/src/fast-codegen.h Thu Dec 10 06:06:08 2009 @@ -35,6 +35,8 @@ namespace v8 { namespace internal { +// ----------------------------------------------------------------------------- +// Fast code generator. class FastCodeGenerator: public AstVisitor { public: @@ -43,6 +45,7 @@ function_(NULL), script_(script), is_eval_(is_eval), + nesting_stack_(NULL), loop_depth_(0), true_label_(NULL), false_label_(NULL) { @@ -55,6 +58,159 @@ 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); @@ -111,12 +267,14 @@ ASSERT(loop_depth_ > 0); 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 @@ 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); }; ======================================= --- /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Wed Dec 9 05:06:08 2009 +++ /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Thu Dec 10 06:06:08 2009 @@ -513,20 +513,6 @@ __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. } - - -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) { @@ -1639,6 +1625,7 @@ // 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)); ======================================= --- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Wed Dec 9 06:54:34 2009 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Thu Dec 10 06:06:08 2009 @@ -502,6 +502,13 @@ // Link this handler as the new current one. mov(Operand::StaticVariable(ExternalReference(Top::k_handler_address)), esp); } + + +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, @@ -1348,6 +1355,18 @@ void MacroAssembler::Ret() { ret(0); } + + +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) { ======================================= --- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Wed Dec 9 06:54:34 2009 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Thu Dec 10 06:06:08 2009 @@ -149,6 +149,9 @@ // 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 @@ 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. ======================================= --- /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Wed Dec 9 05:06:08 2009 +++ /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Thu Dec 10 06:06:08 2009 @@ -523,20 +523,6 @@ __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. } - - -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) { @@ -1649,6 +1635,8 @@ Move(expr->context(), rax); } + +Register FastCodeGenerator::result_register() { return rax; } #undef __ ======================================= --- /branches/bleeding_edge/src/x64/macro-assembler-x64.cc Wed Dec 9 01:35:41 2009 +++ /branches/bleeding_edge/src/x64/macro-assembler-x64.cc Thu Dec 10 06:06:08 2009 @@ -1343,6 +1343,13 @@ push(kScratchRegister); } } + + +void MacroAssembler::Drop(int stack_elements) { + if (stack_elements > 0) { + addq(rsp, Immediate(stack_elements * kPointerSize)); + } +} void MacroAssembler::Test(const Operand& src, Smi* source) { @@ -1429,6 +1436,14 @@ // Link this handler. movq(Operand(kScratchRegister, 0), rsp); } + + +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() { ======================================= --- /branches/bleeding_edge/src/x64/macro-assembler-x64.h Wed Dec 9 01:35:41 2009 +++ /branches/bleeding_edge/src/x64/macro-assembler-x64.h Thu Dec 10 06:06:08 2009 @@ -400,7 +400,7 @@ 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 @@ 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 @@ // 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 -- v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev
