6621084: ciMethodBlocks::split_block_at() is broken for methods with exception handler
authorkvn
Thu, 21 Feb 2008 14:03:41 -0800
changeset 199 fb51d01039ff
parent 198 8601165a33c3
child 200 88d83617f912
6621084: ciMethodBlocks::split_block_at() is broken for methods with exception handler Summary: After an exception handler block is split the exception information is not moved to the new block which starts in exception handler BCI. Reviewed-by: jrose
hotspot/src/share/vm/ci/ciMethodBlocks.cpp
hotspot/src/share/vm/ci/ciMethodBlocks.hpp
--- a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp	Wed Feb 20 17:23:43 2008 -0800
+++ b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp	Thu Feb 21 14:03:41 2008 -0800
@@ -67,6 +67,14 @@
       break;
     }
   }
+  // Move an exception handler information if needed.
+  if (former_block->is_handler()) {
+    int ex_start = former_block->ex_start_bci();
+    int ex_end = former_block->ex_limit_bci();
+    new_block->set_exception_range(ex_start, ex_end);
+    // Clear information in former_block.
+    former_block->clear_exception_handler();
+  }
   return former_block;
 }
 
@@ -102,7 +110,7 @@
     // one and end the old one.
     assert(cur_block != NULL, "must always have a current block");
     ciBlock *new_block = block_containing(bci);
-    if (new_block == NULL) {
+    if (new_block == NULL || new_block == cur_block) {
       // We have not marked this bci as the start of a new block.
       // Keep interpreting the current_range.
       _bci_to_block[bci] = cur_block;
@@ -254,9 +262,33 @@
     for(ciExceptionHandlerStream str(meth); !str.is_done(); str.next()) {
       ciExceptionHandler* handler = str.handler();
       ciBlock *eb = make_block_at(handler->handler_bci());
-      eb->set_handler();
+      //
+      // Several exception handlers can have the same handler_bci:
+      //
+      //  try {
+      //    if (a.foo(b) < 0) {
+      //      return a.error();
+      //    }
+      //    return CoderResult.UNDERFLOW;
+      //  } finally {
+      //      a.position(b);
+      //  }
+      //
+      //  The try block above is divided into 2 exception blocks
+      //  separated by 'areturn' bci.
+      //
       int ex_start = handler->start();
       int ex_end = handler->limit();
+      if (eb->is_handler()) {
+        // Extend old handler exception range to cover additional range.
+        int old_ex_start = eb->ex_start_bci();
+        int old_ex_end   = eb->ex_limit_bci();
+        if (ex_start > old_ex_start)
+          ex_start = old_ex_start;
+        if (ex_end < old_ex_end)
+          ex_end = old_ex_end;
+        eb->clear_exception_handler(); // Reset exception information
+      }
       eb->set_exception_range(ex_start, ex_end);
       // ensure a block at the start of exception range and start of following code
       (void) make_block_at(ex_start);
@@ -312,9 +344,10 @@
 
 void ciBlock::set_exception_range(int start_bci, int limit_bci)  {
    assert(limit_bci >= start_bci, "valid range");
-   assert(is_handler(), "must be handler");
+   assert(!is_handler() && _ex_start_bci == -1 && _ex_limit_bci == -1, "must not be handler");
    _ex_start_bci = start_bci;
    _ex_limit_bci = limit_bci;
+   set_handler();
 }
 
 #ifndef PRODUCT
--- a/hotspot/src/share/vm/ci/ciMethodBlocks.hpp	Wed Feb 20 17:23:43 2008 -0800
+++ b/hotspot/src/share/vm/ci/ciMethodBlocks.hpp	Thu Feb 21 14:03:41 2008 -0800
@@ -110,9 +110,10 @@
   void  set_does_jsr()              { _flags |= DoesJsr; }
   void  clear_does_jsr()            { _flags &= ~DoesJsr; }
   void  set_does_ret()              { _flags |= DoesRet; }
-  void  clear_does_ret()            { _flags |= DoesRet; }
+  void  clear_does_ret()            { _flags &= ~DoesRet; }
   void  set_is_ret_target()         { _flags |= RetTarget; }
   void  set_has_handler()           { _flags |= HasHandler; }
+  void  clear_exception_handler()   { _flags &= ~Handler; _ex_start_bci = -1; _ex_limit_bci = -1; }
 #ifndef PRODUCT
   ciMethod *method() const          { return _method; }
   void dump();