| Index: src/full-codegen/full-codegen.cc
|
| diff --git a/src/full-codegen/full-codegen.cc b/src/full-codegen/full-codegen.cc
|
| index 4b1a552391566a3d27fdd65895fd1299e41cfb61..49a6fc0a8e2a40b58852ef7f3e2c2a4495a6d9a9 100644
|
| --- a/src/full-codegen/full-codegen.cc
|
| +++ b/src/full-codegen/full-codegen.cc
|
| @@ -853,10 +853,7 @@ void FullCodeGenerator::VisitIfStatement(IfStatement* stmt) {
|
| PrepareForBailoutForId(stmt->IfId(), NO_REGISTERS);
|
| }
|
|
|
| -
|
| -void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
|
| - Comment cmnt(masm_, "[ ContinueStatement");
|
| - SetStatementPosition(stmt);
|
| +void FullCodeGenerator::EmitContinue(Statement* target) {
|
| NestedStatement* current = nesting_stack_;
|
| int stack_depth = 0;
|
| int context_length = 0;
|
| @@ -865,7 +862,15 @@ void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
|
| // try...finally on our way out, we will unconditionally preserve the
|
| // accumulator on the stack.
|
| ClearAccumulator();
|
| - while (!current->IsContinueTarget(stmt->target())) {
|
| + while (!current->IsContinueTarget(target)) {
|
| + if (current->IsTryFinally()) {
|
| + Comment cmnt(masm(), "[ Deferred continue through finally");
|
| + current->Exit(&stack_depth, &context_length);
|
| + DCHECK_EQ(0, stack_depth);
|
| + DCHECK_EQ(0, context_length);
|
| + current->AsTryFinally()->deferred_commands()->RecordContinue(target);
|
| + return;
|
| + }
|
| current = current->Exit(&stack_depth, &context_length);
|
| }
|
| __ Drop(stack_depth);
|
| @@ -881,10 +886,13 @@ void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
|
| __ jmp(current->AsIteration()->continue_label());
|
| }
|
|
|
| -
|
| -void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
|
| - Comment cmnt(masm_, "[ BreakStatement");
|
| +void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
|
| + Comment cmnt(masm_, "[ ContinueStatement");
|
| SetStatementPosition(stmt);
|
| + EmitContinue(stmt->target());
|
| +}
|
| +
|
| +void FullCodeGenerator::EmitBreak(Statement* target) {
|
| NestedStatement* current = nesting_stack_;
|
| int stack_depth = 0;
|
| int context_length = 0;
|
| @@ -893,7 +901,15 @@ void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
|
| // try...finally on our way out, we will unconditionally preserve the
|
| // accumulator on the stack.
|
| ClearAccumulator();
|
| - while (!current->IsBreakTarget(stmt->target())) {
|
| + while (!current->IsBreakTarget(target)) {
|
| + if (current->IsTryFinally()) {
|
| + Comment cmnt(masm(), "[ Deferred break through finally");
|
| + current->Exit(&stack_depth, &context_length);
|
| + DCHECK_EQ(0, stack_depth);
|
| + DCHECK_EQ(0, context_length);
|
| + current->AsTryFinally()->deferred_commands()->RecordBreak(target);
|
| + return;
|
| + }
|
| current = current->Exit(&stack_depth, &context_length);
|
| }
|
| __ Drop(stack_depth);
|
| @@ -909,15 +925,29 @@ void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
|
| __ jmp(current->AsBreakable()->break_label());
|
| }
|
|
|
| +void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
|
| + Comment cmnt(masm_, "[ BreakStatement");
|
| + SetStatementPosition(stmt);
|
| + EmitBreak(stmt->target());
|
| +}
|
|
|
| -void FullCodeGenerator::EmitUnwindBeforeReturn() {
|
| +void FullCodeGenerator::EmitUnwindAndReturn() {
|
| NestedStatement* current = nesting_stack_;
|
| int stack_depth = 0;
|
| int context_length = 0;
|
| while (current != NULL) {
|
| + if (current->IsTryFinally()) {
|
| + Comment cmnt(masm(), "[ Deferred return through finally");
|
| + current->Exit(&stack_depth, &context_length);
|
| + DCHECK_EQ(0, stack_depth);
|
| + DCHECK_EQ(0, context_length);
|
| + current->AsTryFinally()->deferred_commands()->RecordReturn();
|
| + return;
|
| + }
|
| current = current->Exit(&stack_depth, &context_length);
|
| }
|
| __ Drop(stack_depth);
|
| + EmitReturnSequence();
|
| }
|
|
|
|
|
| @@ -935,8 +965,7 @@ void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
|
| SetStatementPosition(stmt);
|
| Expression* expr = stmt->expression();
|
| VisitForAccumulatorValue(expr);
|
| - EmitUnwindBeforeReturn();
|
| - EmitReturnSequence();
|
| + EmitUnwindAndReturn();
|
| }
|
|
|
|
|
| @@ -1188,24 +1217,22 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| // executing the try body, and removing it again afterwards.
|
| //
|
| // The try-finally construct can enter the finally block in three ways:
|
| - // 1. By exiting the try-block normally. This removes the try-handler and
|
| - // calls the finally block code before continuing.
|
| + // 1. By exiting the try-block normally. This exits the try block,
|
| + // pushes the continuation token and falls through to the finally
|
| + // block.
|
| // 2. By exiting the try-block with a function-local control flow transfer
|
| - // (break/continue/return). The site of the, e.g., break removes the
|
| - // try handler and calls the finally block code before continuing
|
| - // its outward control transfer.
|
| - // 3. By exiting the try-block with a thrown exception.
|
| - // This can happen in nested function calls. It traverses the try-handler
|
| - // chain and consumes the try-handler entry before jumping to the
|
| - // handler code. The handler code then calls the finally-block before
|
| - // rethrowing the exception.
|
| - //
|
| - // The finally block must assume a return address on top of the stack
|
| - // (or in the link register on ARM chips) and a value (return value or
|
| - // exception) in the result register (rax/eax/r0), both of which must
|
| - // be preserved. The return address isn't GC-safe, so it should be
|
| - // cooked before GC.
|
| + // (break/continue/return). The site of the, e.g., break exits the
|
| + // try block, pushes the continuation token and jumps to the
|
| + // finally block. After the finally block executes, the execution
|
| + // continues based on the continuation token to a block that
|
| + // continues with the control flow transfer.
|
| + // 3. By exiting the try-block with a thrown exception. In the handler,
|
| + // we push the exception and continuation token and jump to the
|
| + // finally block (which will again dispatch based on the token once
|
| + // it is finished).
|
| +
|
| Label try_entry, handler_entry, finally_entry;
|
| + DeferredCommands deferred(this, &finally_entry);
|
|
|
| // Jump to try-handler setup and try-block code.
|
| __ jmp(&try_entry);
|
| @@ -1213,26 +1240,18 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| PrepareForBailoutForId(stmt->HandlerId(), NO_REGISTERS);
|
|
|
| // Exception handler code. This code is only executed when an exception
|
| - // is thrown. The exception is in the result register, and must be
|
| - // preserved by the finally block. Call the finally block and then
|
| - // rethrow the exception if it returns.
|
| - __ Call(&finally_entry);
|
| - __ Push(result_register());
|
| - __ CallRuntime(Runtime::kReThrow);
|
| -
|
| - // Finally block implementation.
|
| - __ bind(&finally_entry);
|
| - EnterFinallyBlock();
|
| - { Finally finally_body(this);
|
| - Visit(stmt->finally_block());
|
| + // is thrown. Record the continuation and jump to the finally block.
|
| + {
|
| + Comment cmt_handler(masm(), "[ Finally handler");
|
| + deferred.RecordThrow();
|
| }
|
| - ExitFinallyBlock(); // Return to the calling code.
|
|
|
| // Set up try handler.
|
| __ bind(&try_entry);
|
| int handler_index = NewHandlerTableEntry();
|
| EnterTryBlock(handler_index, &handler_entry);
|
| - { TryFinally try_body(this, &finally_entry);
|
| + {
|
| + TryFinally try_body(this, &deferred);
|
| Visit(stmt->try_block());
|
| }
|
| ExitTryBlock(handler_index);
|
| @@ -1241,7 +1260,23 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| // finally block will unconditionally preserve the result register on the
|
| // stack.
|
| ClearAccumulator();
|
| - __ Call(&finally_entry);
|
| + deferred.EmitFallThrough();
|
| + // Fall through to the finally block.
|
| +
|
| + // Finally block implementation.
|
| + __ bind(&finally_entry);
|
| + Comment cmnt_finally(masm(), "[ Finally block");
|
| + EnterFinallyBlock();
|
| + {
|
| + Finally finally_body(this);
|
| + Visit(stmt->finally_block());
|
| + }
|
| + ExitFinallyBlock(); // Return to the calling code.
|
| +
|
| + {
|
| + Comment cmnt_deferred(masm(), "[ Post-finally dispatch");
|
| + deferred.EmitCommands();
|
| + }
|
| }
|
|
|
|
|
| @@ -1485,13 +1520,49 @@ FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
|
| // Down to the handler block and also drop context.
|
| __ Drop(*stack_depth + kElementCount);
|
| }
|
| - __ Call(finally_entry_);
|
| -
|
| *stack_depth = 0;
|
| *context_length = 0;
|
| return previous_;
|
| }
|
|
|
| +void FullCodeGenerator::DeferredCommands::RecordBreak(Statement* target) {
|
| + TokenId token = dispenser_.GetBreakContinueToken();
|
| + commands_.push_back({kBreak, token, target});
|
| + EmitJumpToFinally(token);
|
| +}
|
| +
|
| +void FullCodeGenerator::DeferredCommands::RecordContinue(Statement* target) {
|
| + TokenId token = dispenser_.GetBreakContinueToken();
|
| + commands_.push_back({kContinue, token, target});
|
| + EmitJumpToFinally(token);
|
| +}
|
| +
|
| +void FullCodeGenerator::DeferredCommands::RecordReturn() {
|
| + if (return_token_ == TokenDispenserForFinally::kInvalidToken) {
|
| + return_token_ = TokenDispenserForFinally::kReturnToken;
|
| + commands_.push_back({kReturn, return_token_, nullptr});
|
| + }
|
| + EmitJumpToFinally(return_token_);
|
| +}
|
| +
|
| +void FullCodeGenerator::DeferredCommands::RecordThrow() {
|
| + if (throw_token_ == TokenDispenserForFinally::kInvalidToken) {
|
| + throw_token_ = TokenDispenserForFinally::kThrowToken;
|
| + commands_.push_back({kThrow, throw_token_, nullptr});
|
| + }
|
| + EmitJumpToFinally(throw_token_);
|
| +}
|
| +
|
| +void FullCodeGenerator::DeferredCommands::EmitFallThrough() {
|
| + __ Push(Smi::FromInt(TokenDispenserForFinally::kFallThroughToken));
|
| + __ Push(result_register());
|
| +}
|
| +
|
| +void FullCodeGenerator::DeferredCommands::EmitJumpToFinally(TokenId token) {
|
| + __ Push(Smi::FromInt(token));
|
| + __ Push(result_register());
|
| + __ jmp(finally_entry_);
|
| +}
|
|
|
| bool FullCodeGenerator::TryLiteralCompare(CompareOperation* expr) {
|
| Expression* sub_expr;
|
|
|