Revision: 3461 Author: [email protected] Date: Mon Dec 14 03:09:25 2009 Log: Fast-codegen: Implementing try/finally on top of nesting context.
Review URL: http://codereview.chromium.org/492002 http://code.google.com/p/v8/source/detail?r=3461 Modified: /branches/bleeding_edge/src/arm/fast-codegen-arm.cc /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.h /branches/bleeding_edge/src/x64/fast-codegen-x64.cc /branches/bleeding_edge/src/x64/macro-assembler-x64.cc /branches/bleeding_edge/test/mjsunit/try.js ======================================= --- /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Fri Dec 11 08:09:16 2009 +++ /branches/bleeding_edge/src/arm/fast-codegen-arm.cc Mon Dec 14 03:09:25 2009 @@ -1680,6 +1680,7 @@ false_label_ = saved_false; // Convert current context to test context: End post-test code. } + void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) { __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); @@ -1689,7 +1690,40 @@ Register FastCodeGenerator::result_register() { return r0; } -#undef __ - +// ---------------------------------------------------------------------------- +// 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 __ } } // namespace v8::internal ======================================= --- /branches/bleeding_edge/src/compiler.cc Fri Dec 11 08:09:16 2009 +++ /branches/bleeding_edge/src/compiler.cc Mon Dec 14 03:09:25 2009 @@ -751,7 +751,9 @@ void CodeGenSelector::VisitTryFinallyStatement(TryFinallyStatement* stmt) { - BAILOUT("TryFinallyStatement"); + Visit(stmt->try_block()); + CHECK_BAILOUT; + Visit(stmt->finally_block()); } ======================================= --- /branches/bleeding_edge/src/fast-codegen.cc Fri Dec 11 08:09:16 2009 +++ /branches/bleeding_edge/src/fast-codegen.cc Mon Dec 14 03:09:25 2009 @@ -443,7 +443,63 @@ 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); } ======================================= --- /branches/bleeding_edge/src/fast-codegen.h Fri Dec 11 08:09:16 2009 +++ /branches/bleeding_edge/src/fast-codegen.h Mon Dec 14 03:09:25 2009 @@ -276,6 +276,12 @@ void SetStatementPosition(Statement* stmt); void SetSourcePosition(int pos); + // Non-local control flow support. + void EnterFinallyBlock(); + void ExitFinallyBlock(); + void ThrowException(); + + // Loop nesting counter. int loop_depth() { return loop_depth_; } void increment_loop_depth() { loop_depth_++; } void decrement_loop_depth() { ======================================= --- /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Fri Dec 11 14:48:14 2009 +++ /branches/bleeding_edge/src/ia32/fast-codegen-ia32.cc Mon Dec 14 03:09:25 2009 @@ -1656,14 +1656,51 @@ // 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)); Move(expr->context(), eax); } -#undef __ - + +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 __ } } // namespace v8::internal ======================================= --- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Thu Dec 10 06:06:08 2009 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Mon Dec 14 03:09:25 2009 @@ -152,7 +152,6 @@ // Unlink the stack handler on top of the stack from the try handler chain. void PopTryHandler(); - // --------------------------------------------------------------------------- // Inline caching support ======================================= --- /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Fri Dec 11 08:09:16 2009 +++ /branches/bleeding_edge/src/x64/fast-codegen-x64.cc Mon Dec 14 03:09:25 2009 @@ -1670,6 +1670,45 @@ 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 __ ======================================= --- /branches/bleeding_edge/src/x64/macro-assembler-x64.cc Thu Dec 10 06:06:08 2009 +++ /branches/bleeding_edge/src/x64/macro-assembler-x64.cc Mon Dec 14 03:09:25 2009 @@ -1440,8 +1440,10 @@ void MacroAssembler::PopTryHandler() { ASSERT_EQ(0, StackHandlerConstants::kNextOffset); + // Unlink this handler. movq(kScratchRegister, ExternalReference(Top::k_handler_address)); pop(Operand(kScratchRegister, 0)); + // Remove the remaining fields. addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize)); } ======================================= --- /branches/bleeding_edge/test/mjsunit/try.js Fri Mar 6 00:28:56 2009 +++ /branches/bleeding_edge/test/mjsunit/try.js Mon Dec 14 03:09:25 2009 @@ -347,3 +347,48 @@ assertFalse(caught); 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()); -- v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev
