Fast-codegen: Implementing try/finally on top of nesting context.
Review URL: http://codereview.chromium.org/492002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3461 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
d225baa2b5
commit
9ff5f9daed
@ -1681,6 +1681,7 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
|
|||||||
// Convert current context to test context: End post-test code.
|
// Convert current context to test context: End post-test code.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
|
void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
|
||||||
__ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
__ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
|
||||||
Move(expr->context(), r0);
|
Move(expr->context(), r0);
|
||||||
@ -1689,7 +1690,40 @@ void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
|
|||||||
|
|
||||||
Register FastCodeGenerator::result_register() { return r0; }
|
Register FastCodeGenerator::result_register() { return r0; }
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Non-local control flow support.
|
||||||
|
|
||||||
|
void FastCodeGenerator::EnterFinallyBlock() {
|
||||||
|
ASSERT(!result_register().is(r1));
|
||||||
|
// Cook return address in link register to stack (smi encoded Code* delta)
|
||||||
|
__ sub(r1, lr, Operand(masm_->CodeObject()));
|
||||||
|
ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
|
||||||
|
ASSERT_EQ(0, kSmiTag);
|
||||||
|
__ add(r1, r1, Operand(r1)); // Convert to smi.
|
||||||
|
__ push(r1);
|
||||||
|
// Store result register while executing finally block.
|
||||||
|
__ push(result_register());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FastCodeGenerator::ExitFinallyBlock() {
|
||||||
|
ASSERT(!result_register().is(r1));
|
||||||
|
// Restore result register from stack.
|
||||||
|
__ pop(result_register());
|
||||||
|
// Uncook return address and return.
|
||||||
|
__ pop(r1);
|
||||||
|
ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
|
||||||
|
__ mov(r1, Operand(r1, ASR, 1)); // Un-smi-tag value.
|
||||||
|
__ add(pc, r1, Operand(masm_->CodeObject()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FastCodeGenerator::ThrowException() {
|
||||||
|
__ push(result_register());
|
||||||
|
__ CallRuntime(Runtime::kThrow, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#undef __
|
#undef __
|
||||||
|
|
||||||
|
|
||||||
} } // namespace v8::internal
|
} } // namespace v8::internal
|
||||||
|
@ -751,7 +751,9 @@ void CodeGenSelector::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
|||||||
|
|
||||||
|
|
||||||
void CodeGenSelector::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
void CodeGenSelector::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
||||||
BAILOUT("TryFinallyStatement");
|
Visit(stmt->try_block());
|
||||||
|
CHECK_BAILOUT;
|
||||||
|
Visit(stmt->finally_block());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -443,7 +443,63 @@ void FastCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
|||||||
|
|
||||||
|
|
||||||
void FastCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
void FastCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
||||||
UNREACHABLE();
|
// Try finally is compiled by setting up a try-handler on the stack while
|
||||||
|
// 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.
|
||||||
|
// 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
|
||||||
|
// chaing 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.
|
||||||
|
Label finally_entry;
|
||||||
|
Label try_handler_setup;
|
||||||
|
|
||||||
|
// Setup the try-handler chain. Use a call to
|
||||||
|
// Jump to try-handler setup and try-block code. Use call to put try-handler
|
||||||
|
// address on stack.
|
||||||
|
__ Call(&try_handler_setup);
|
||||||
|
// Try handler code. Return address of call is pushed on handler stack.
|
||||||
|
{
|
||||||
|
// This code is only executed during stack-handler traversal when an
|
||||||
|
// exception is thrown. The execption is in the result register, which
|
||||||
|
// is retained by the finally block.
|
||||||
|
// Call the finally block and then rethrow the exception.
|
||||||
|
__ Call(&finally_entry);
|
||||||
|
ThrowException();
|
||||||
|
}
|
||||||
|
|
||||||
|
__ bind(&finally_entry);
|
||||||
|
{
|
||||||
|
// Finally block implementation.
|
||||||
|
EnterFinallyBlock();
|
||||||
|
Finally finally_block(this);
|
||||||
|
Visit(stmt->finally_block());
|
||||||
|
ExitFinallyBlock(); // Return to the calling code.
|
||||||
|
}
|
||||||
|
|
||||||
|
__ bind(&try_handler_setup);
|
||||||
|
{
|
||||||
|
// Setup try handler (stack pointer registers).
|
||||||
|
__ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER);
|
||||||
|
TryFinally try_block(this, &finally_entry);
|
||||||
|
VisitStatements(stmt->try_block()->statements());
|
||||||
|
__ PopTryHandler();
|
||||||
|
}
|
||||||
|
// Execute the finally block on the way out.
|
||||||
|
__ Call(&finally_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -276,6 +276,12 @@ class FastCodeGenerator: public AstVisitor {
|
|||||||
void SetStatementPosition(Statement* stmt);
|
void SetStatementPosition(Statement* stmt);
|
||||||
void SetSourcePosition(int pos);
|
void SetSourcePosition(int pos);
|
||||||
|
|
||||||
|
// Non-local control flow support.
|
||||||
|
void EnterFinallyBlock();
|
||||||
|
void ExitFinallyBlock();
|
||||||
|
void ThrowException();
|
||||||
|
|
||||||
|
// Loop nesting counter.
|
||||||
int loop_depth() { return loop_depth_; }
|
int loop_depth() { return loop_depth_; }
|
||||||
void increment_loop_depth() { loop_depth_++; }
|
void increment_loop_depth() { loop_depth_++; }
|
||||||
void decrement_loop_depth() {
|
void decrement_loop_depth() {
|
||||||
|
@ -1656,14 +1656,51 @@ void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
|
|||||||
// Convert current context to test context: End post-test code.
|
// Convert current context to test context: End post-test code.
|
||||||
}
|
}
|
||||||
|
|
||||||
Register FastCodeGenerator::result_register() { return eax; }
|
|
||||||
|
|
||||||
void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
|
void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
|
||||||
__ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
|
__ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
|
||||||
Move(expr->context(), eax);
|
Move(expr->context(), eax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Register FastCodeGenerator::result_register() { return eax; }
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Non-local control flow support.
|
||||||
|
|
||||||
|
void FastCodeGenerator::EnterFinallyBlock() {
|
||||||
|
// Cook return address on top of stack (smi encoded Code* delta)
|
||||||
|
ASSERT(!result_register().is(edx));
|
||||||
|
__ mov(edx, Operand(esp, 0));
|
||||||
|
__ sub(Operand(edx), Immediate(masm_->CodeObject()));
|
||||||
|
ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
|
||||||
|
ASSERT_EQ(0, kSmiTag);
|
||||||
|
__ add(edx, Operand(edx)); // Convert to smi.
|
||||||
|
__ mov(Operand(esp, 0), edx);
|
||||||
|
// Store result register while executing finally block.
|
||||||
|
__ push(result_register());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FastCodeGenerator::ExitFinallyBlock() {
|
||||||
|
ASSERT(!result_register().is(edx));
|
||||||
|
// Restore result register from stack.
|
||||||
|
__ pop(result_register());
|
||||||
|
// Uncook return address.
|
||||||
|
__ mov(edx, Operand(esp, 0));
|
||||||
|
__ sar(edx, 1); // Convert smi to int.
|
||||||
|
__ add(Operand(edx), Immediate(masm_->CodeObject()));
|
||||||
|
__ mov(Operand(esp, 0), edx);
|
||||||
|
// And return.
|
||||||
|
__ ret(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FastCodeGenerator::ThrowException() {
|
||||||
|
__ push(result_register());
|
||||||
|
__ CallRuntime(Runtime::kThrow, 1);
|
||||||
|
}
|
||||||
|
|
||||||
#undef __
|
#undef __
|
||||||
|
|
||||||
|
|
||||||
} } // namespace v8::internal
|
} } // namespace v8::internal
|
||||||
|
@ -152,7 +152,6 @@ class MacroAssembler: public Assembler {
|
|||||||
// Unlink the stack handler on top of the stack from the try handler chain.
|
// Unlink the stack handler on top of the stack from the try handler chain.
|
||||||
void PopTryHandler();
|
void PopTryHandler();
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Inline caching support
|
// Inline caching support
|
||||||
|
|
||||||
|
@ -1671,6 +1671,45 @@ void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
|
|||||||
|
|
||||||
Register FastCodeGenerator::result_register() { return rax; }
|
Register FastCodeGenerator::result_register() { return rax; }
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Non-local control flow support.
|
||||||
|
|
||||||
|
|
||||||
|
void FastCodeGenerator::EnterFinallyBlock() {
|
||||||
|
ASSERT(!result_register().is(rdx));
|
||||||
|
ASSERT(!result_register().is(rcx));
|
||||||
|
// Cook return address on top of stack (smi encoded Code* delta)
|
||||||
|
__ movq(rdx, Operand(rsp, 0));
|
||||||
|
__ Move(rcx, masm_->CodeObject());
|
||||||
|
__ subq(rdx, rcx);
|
||||||
|
__ Integer32ToSmi(rdx, rdx);
|
||||||
|
__ movq(Operand(rsp, 0), rdx);
|
||||||
|
// Store result register while executing finally block.
|
||||||
|
__ push(result_register());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FastCodeGenerator::ExitFinallyBlock() {
|
||||||
|
ASSERT(!result_register().is(rdx));
|
||||||
|
ASSERT(!result_register().is(rcx));
|
||||||
|
// Restore result register from stack.
|
||||||
|
__ pop(result_register());
|
||||||
|
// Uncook return address.
|
||||||
|
__ movq(rdx, Operand(rsp, 0));
|
||||||
|
__ SmiToInteger32(rdx, rdx);
|
||||||
|
__ Move(rcx, masm_->CodeObject());
|
||||||
|
__ addq(rdx, rcx);
|
||||||
|
__ movq(Operand(rsp, 0), rdx);
|
||||||
|
// And return.
|
||||||
|
__ ret(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FastCodeGenerator::ThrowException() {
|
||||||
|
__ push(result_register());
|
||||||
|
__ CallRuntime(Runtime::kThrow, 1);
|
||||||
|
}
|
||||||
|
|
||||||
#undef __
|
#undef __
|
||||||
|
|
||||||
|
|
||||||
|
@ -1440,8 +1440,10 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location,
|
|||||||
|
|
||||||
void MacroAssembler::PopTryHandler() {
|
void MacroAssembler::PopTryHandler() {
|
||||||
ASSERT_EQ(0, StackHandlerConstants::kNextOffset);
|
ASSERT_EQ(0, StackHandlerConstants::kNextOffset);
|
||||||
|
// Unlink this handler.
|
||||||
movq(kScratchRegister, ExternalReference(Top::k_handler_address));
|
movq(kScratchRegister, ExternalReference(Top::k_handler_address));
|
||||||
pop(Operand(kScratchRegister, 0));
|
pop(Operand(kScratchRegister, 0));
|
||||||
|
// Remove the remaining fields.
|
||||||
addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
|
addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,3 +347,48 @@ assertTrue(broke);
|
|||||||
assertFalse(caught);
|
assertFalse(caught);
|
||||||
assertTrue(finalized);
|
assertTrue(finalized);
|
||||||
|
|
||||||
|
function return_from_nested_finally_in_finally() {
|
||||||
|
try {
|
||||||
|
return 1;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
return 2;
|
||||||
|
} finally {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(42, return_from_nested_finally_in_finally());
|
||||||
|
|
||||||
|
function break_from_nested_finally_in_finally() {
|
||||||
|
L: try {
|
||||||
|
return 1;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
return 2;
|
||||||
|
} finally {
|
||||||
|
break L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(42, break_from_nested_finally_in_finally());
|
||||||
|
|
||||||
|
function continue_from_nested_finally_in_finally() {
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
return 1;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
return 2;
|
||||||
|
} finally {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(42, continue_from_nested_finally_in_finally());
|
||||||
|
Loading…
Reference in New Issue
Block a user