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

Reply via email to