changeset 2254:d7d97061baf3

PR icedtea/494: exception handlers not installed Speed up exception handling This commit fixes PR icedtea/494, a bug where some exception handlers were not installed. Shark relied on exception handler tables supplied by the TypeFlow pass, but it turns out that TypeFlow does not record all exceptions in its tables. Shark now builds its own tables. While implementing this I realised that the VM call carried out to figure out which exception handler to use is unnecessary in most cases. I've left the original code in, but added a fast path which bypasses a lot of the junk. 2010-05-20 Gary Benson <gbenson@redhat.com> PR icedtea/494 * ports/hotspot/src/share/vm/shark/sharkInvariants.hpp (SharkCompileInvariants::java_lang_Throwable_klass): New method. * ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp (SharkTopLevelBlock::_exc_handlers): New field. (SharkTopLevelBlock::_exceptions): Likewise. (SharkTopLevelBlock::compute_exceptions): New method. (SharkTopLevelBlock::num_exceptions): Rewritten. (SharkTopLevelBlock::exc_handler): New method. (SharkTopLevelBlock::exception): Rewritten. (SharkTopLevelBlock::marshal_exception_fast): New method. (SharkTopLevelBlock::marshal_exception_slow): Likewise. (SharkTopLevelBlock::handler_for_exception): Likewise. (SharkTopLevelBlock::make_trap): Likewise. * ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp (SharkTopLevelBlock::enter): Compute exceptions on entry, and skip over any null handlers. (SharkTopLevelBlock::compute_exceptions): New method. (SharkTopLevelBlock::handle_exception): Rewritten. (SharkTopLevelBlock::marshal_exception_fast): New method. (SharkTopLevelBlock::marshal_exception_slow): Likewise. (SharkTopLevelBlock::handler_for_exception): Likewise. (SharkTopLevelBlock::make_trap): Likewise. (SharkTopLevelBlock::can_reach_helper): Skip over null handlers.
author Gary Benson <gbenson@redhat.com>
date Thu, 20 May 2010 12:40:31 +0100
parents 494ed9fb8215
children 85ec20ad5e63
files ChangeLog ports/hotspot/src/share/vm/shark/sharkInvariants.hpp ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp
diffstat 4 files changed, 218 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri May 14 17:37:29 2010 +0100
+++ b/ChangeLog	Thu May 20 12:40:31 2010 +0100
@@ -1,3 +1,30 @@
+2010-05-20  Gary Benson  <gbenson@redhat.com>
+
+	PR icedtea/494
+	* ports/hotspot/src/share/vm/shark/sharkInvariants.hpp
+	(SharkCompileInvariants::java_lang_Throwable_klass): New method.
+	* ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp
+	(SharkTopLevelBlock::_exc_handlers): New field.
+	(SharkTopLevelBlock::_exceptions): Likewise.
+	(SharkTopLevelBlock::compute_exceptions): New method.
+	(SharkTopLevelBlock::num_exceptions): Rewritten.
+	(SharkTopLevelBlock::exc_handler): New method.
+	(SharkTopLevelBlock::exception): Rewritten.
+	(SharkTopLevelBlock::marshal_exception_fast): New method.
+	(SharkTopLevelBlock::marshal_exception_slow): Likewise.
+	(SharkTopLevelBlock::handler_for_exception): Likewise.
+	(SharkTopLevelBlock::make_trap): Likewise.
+	* ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp
+	(SharkTopLevelBlock::enter): Compute exceptions on entry,
+	and skip over any null handlers.
+	(SharkTopLevelBlock::compute_exceptions): New method.
+	(SharkTopLevelBlock::handle_exception): Rewritten.
+	(SharkTopLevelBlock::marshal_exception_fast): New method.
+	(SharkTopLevelBlock::marshal_exception_slow): Likewise.
+	(SharkTopLevelBlock::handler_for_exception): Likewise.
+	(SharkTopLevelBlock::make_trap): Likewise.
+	(SharkTopLevelBlock::can_reach_helper): Skip over null handlers.
+
 2010-05-14  Gary Benson  <gbenson@redhat.com>
 
 	* ports/hotspot/src/share/vm/shark/sharkCodeBuffer.hpp
--- a/ports/hotspot/src/share/vm/shark/sharkInvariants.hpp	Fri May 14 17:37:29 2010 +0100
+++ b/ports/hotspot/src/share/vm/shark/sharkInvariants.hpp	Thu May 20 12:40:31 2010 +0100
@@ -1,6 +1,6 @@
 /*
  * Copyright 1999-2007 Sun Microsystems, Inc.  All Rights Reserved.
- * Copyright 2008, 2009 Red Hat, Inc.
+ * Copyright 2008, 2009, 2010 Red Hat, Inc.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -93,11 +93,14 @@
     return builder()->code_buffer();
   }
 
-  // That well-known class...
+  // Commonly used classes
  protected:
   ciInstanceKlass* java_lang_Object_klass() const {
     return env()->Object_klass();
   }
+  ciInstanceKlass* java_lang_Throwable_klass() const {
+    return env()->Throwable_klass();
+  }
 };
 
 class SharkTargetInvariants : public SharkCompileInvariants {
--- a/ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp	Fri May 14 17:37:29 2010 +0100
+++ b/ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp	Thu May 20 12:40:31 2010 +0100
@@ -221,8 +221,11 @@
         successor(i)->enter(this, false);
       }
     }
+    compute_exceptions();
     for (int i = 0; i < num_exceptions(); i++) {
-      exception(i)->enter(this, true);
+      SharkTopLevelBlock *handler = exception(i);
+      if (handler)
+        handler->enter(this, true);
     }
   }
 }
@@ -438,67 +441,93 @@
   builder()->SetInsertPoint(no_exception);
 }
 
+void SharkTopLevelBlock::compute_exceptions() {
+  ciExceptionHandlerStream str(target(), start());
+
+  int exc_count = str.count();
+  _exc_handlers = new GrowableArray<ciExceptionHandler*>(exc_count);
+  _exceptions   = new GrowableArray<SharkTopLevelBlock*>(exc_count);
+
+  int index = 0;
+  for (; !str.is_done(); str.next()) {
+    ciExceptionHandler *handler = str.handler();
+    if (handler->handler_bci() == -1)
+      break;
+    _exc_handlers->append(handler);
+
+    // Try and get this exception's handler from typeflow.  We should
+    // do it this way always, really, except that typeflow sometimes
+    // doesn't record exceptions, even loaded ones, and sometimes it
+    // returns them with a different handler bci.  Why???
+    SharkTopLevelBlock *block = NULL;
+    ciInstanceKlass* klass;
+    if (handler->is_catch_all()) {
+      klass = java_lang_Throwable_klass();
+    }
+    else {
+      klass = handler->catch_klass();
+    }
+    for (int i = 0; i < ciblock()->exceptions()->length(); i++) {
+      if (klass == ciblock()->exc_klasses()->at(i)) {
+        block = function()->block(ciblock()->exceptions()->at(i)->pre_order());
+        if (block->start() == handler->handler_bci())
+          break;
+        else
+          block = NULL;
+      }
+    }
+
+    // If typeflow let us down then try and figure it out ourselves
+    if (block == NULL) {
+      for (int i = 0; i < function()->block_count(); i++) {
+        SharkTopLevelBlock *candidate = function()->block(i);
+        if (candidate->start() == handler->handler_bci()) {
+          if (block != NULL) {
+            NOT_PRODUCT(warning("there may be trouble ahead"));
+            block = NULL;
+            break;
+          }
+          block = candidate;
+        }
+      }
+    }
+    _exceptions->append(block);
+  }
+}
+
 void SharkTopLevelBlock::handle_exception(Value* exception, int action) {
   if (action & EAM_HANDLE && num_exceptions() != 0) {
-    // Clear the stack and push the exception onto it.
-    // We do this now to protect it across the VM call
-    // we may be about to make.
+    // Clear the stack and push the exception onto it
     while (xstack_depth())
       pop();
     push(SharkValue::create_jobject(exception, true));
 
-    int *indexes = NEW_RESOURCE_ARRAY(int, num_exceptions());
-    bool has_catch_all = false;
-
-    ciExceptionHandlerStream eh_iter(target(), bci());
-    for (int i = 0; i < num_exceptions(); i++, eh_iter.next()) {
-      ciExceptionHandler* handler = eh_iter.handler();
-
-      if (handler->is_catch_all()) {
-        assert(i == num_exceptions() - 1, "catch-all should be last");
-        has_catch_all = true;
-      }
-      else {
-        indexes[i] = handler->catch_klass_index();
-      }
-    }
-
+    // Work out how many options we have to check
+    bool has_catch_all = exc_handler(num_exceptions() - 1)->is_catch_all();
     int num_options = num_exceptions();
     if (has_catch_all)
       num_options--;
 
-    // Drop into the runtime if there are non-catch-all options
+    // Marshal any non-catch-all handlers
     if (num_options > 0) {
-      Value *index = call_vm(
-        builder()->find_exception_handler(),
-        builder()->CreateInlineData(
-          indexes,
-          num_options * sizeof(int),
-          PointerType::getUnqual(SharkType::jint_type())),
-        LLVMValue::jint_constant(num_options),
-        EX_CHECK_NO_CATCH);
-
-      // Jump to the exception handler, if found
-      BasicBlock *no_handler = function()->CreateBlock("no_handler");
-      SwitchInst *switchinst = builder()->CreateSwitch(
-        index, no_handler, num_options);
-
+      bool all_loaded = true;
       for (int i = 0; i < num_options; i++) {
-        SharkTopLevelBlock* handler = this->exception(i);
-
-        switchinst->addCase(
-          LLVMValue::jint_constant(i),
-          handler->entry_block());
-
-        handler->add_incoming(current_state());
+        if (!exc_handler(i)->catch_klass()->is_loaded()) {
+          all_loaded = false;
+          break;
+        }
       }
 
-      builder()->SetInsertPoint(no_handler);
+      if (all_loaded)
+        marshal_exception_fast(num_options);
+      else
+        marshal_exception_slow(num_options);
     }
 
-    // No specific handler exists, but maybe there's a catch-all
+    // Install the catch-all handler, if present
     if (has_catch_all) {
       SharkTopLevelBlock* handler = this->exception(num_options);
+      assert(handler != NULL, "catch-all handler cannot be unloaded");
 
       builder()->CreateBr(handler->entry_block());
       handler->add_incoming(current_state());
@@ -510,6 +539,78 @@
   handle_return(T_VOID, exception);
 }
 
+void SharkTopLevelBlock::marshal_exception_fast(int num_options) {
+  Value *exception_klass = builder()->CreateValueOfStructEntry(
+    xstack(0)->jobject_value(),
+    in_ByteSize(oopDesc::klass_offset_in_bytes()),
+    SharkType::oop_type(),
+    "exception_klass");
+
+  for (int i = 0; i < num_options; i++) {
+    Value *check_klass =
+      builder()->CreateInlineOop(exc_handler(i)->catch_klass());
+
+    BasicBlock *not_exact   = function()->CreateBlock("not_exact");
+    BasicBlock *not_subtype = function()->CreateBlock("not_subtype");
+
+    builder()->CreateCondBr(
+      builder()->CreateICmpEQ(check_klass, exception_klass),
+      handler_for_exception(i), not_exact);
+
+    builder()->SetInsertPoint(not_exact);
+    builder()->CreateCondBr(
+      builder()->CreateICmpNE(
+        builder()->CreateCall2(
+          builder()->is_subtype_of(), check_klass, exception_klass),
+        LLVMValue::jbyte_constant(0)),
+      handler_for_exception(i), not_subtype);
+
+    builder()->SetInsertPoint(not_subtype);
+  }
+}
+
+void SharkTopLevelBlock::marshal_exception_slow(int num_options) {
+  int *indexes = NEW_RESOURCE_ARRAY(int, num_options);
+  for (int i = 0; i < num_options; i++)
+    indexes[i] = exc_handler(i)->catch_klass_index();
+
+  Value *index = call_vm(
+    builder()->find_exception_handler(),
+    builder()->CreateInlineData(
+      indexes,
+      num_options * sizeof(int),
+      PointerType::getUnqual(SharkType::jint_type())),
+    LLVMValue::jint_constant(num_options),
+    EX_CHECK_NO_CATCH);
+
+  BasicBlock *no_handler = function()->CreateBlock("no_handler");
+  SwitchInst *switchinst = builder()->CreateSwitch(
+    index, no_handler, num_options);
+
+  for (int i = 0; i < num_options; i++) {
+    switchinst->addCase(
+      LLVMValue::jint_constant(i),
+      handler_for_exception(i));
+  }
+
+  builder()->SetInsertPoint(no_handler);
+}
+
+BasicBlock* SharkTopLevelBlock::handler_for_exception(int index) {
+  SharkTopLevelBlock *successor = this->exception(index);
+  if (successor) {
+    successor->add_incoming(current_state());
+    return successor->entry_block();
+  }
+  else {
+    return make_trap(
+      exc_handler(index)->handler_bci(),
+      Deoptimization::make_trap_request(
+        Deoptimization::Reason_unhandled,
+        Deoptimization::Action_reinterpret));
+  }
+}
+
 void SharkTopLevelBlock::maybe_add_safepoint() {
   if (current_state()->has_safepointed())
     return;
@@ -579,13 +680,30 @@
   }
 
   for (int i = 0; i < num_exceptions(); i++) {
-    if (exception(i)->can_reach_helper(other))
+    SharkTopLevelBlock *handler = exception(i);
+    if (handler && handler->can_reach_helper(other))
       return true;
   }
 
   return false;
 }
 
+BasicBlock* SharkTopLevelBlock::make_trap(int trap_bci, int trap_request) {
+  BasicBlock *trap_block = function()->CreateBlock("trap");
+  BasicBlock *orig_block = builder()->GetInsertBlock();
+  builder()->SetInsertPoint(trap_block);
+
+  int orig_bci = bci();
+  iter()->force_bci(trap_bci);
+
+  do_trap(trap_request);
+
+  builder()->SetInsertPoint(orig_block);
+  iter()->force_bci(orig_bci);
+
+  return trap_block;
+}
+
 void SharkTopLevelBlock::do_trap(int trap_request) {
   decache_for_trap();
   builder()->CreateRet(
--- a/ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp	Fri May 14 17:37:29 2010 +0100
+++ b/ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp	Thu May 20 12:40:31 2010 +0100
@@ -79,12 +79,6 @@
   bool falls_through() const {
     return ciblock()->control() == ciBlock::fall_through_bci;
   }
-  int num_exceptions() const {
-    return ciblock()->exceptions()->length();
-  }
-  SharkTopLevelBlock* exception(int index) const {
-    return function()->block(ciblock()->exceptions()->at(index)->pre_order());
-  }
   int num_successors() const {
     return ciblock()->successors()->length();
   }
@@ -93,6 +87,25 @@
   }
   SharkTopLevelBlock* bci_successor(int bci) const;
 
+  // Exceptions
+ private:
+  GrowableArray<ciExceptionHandler*>* _exc_handlers;
+  GrowableArray<SharkTopLevelBlock*>* _exceptions;
+
+ private:
+  void compute_exceptions();
+
+ private:
+  int num_exceptions() const {
+    return _exc_handlers->length();
+  }
+  ciExceptionHandler* exc_handler(int index) const {
+    return _exc_handlers->at(index);
+  }
+  SharkTopLevelBlock* exception(int index) const {
+    return _exceptions->at(index);
+  }
+
   // Traps
  private:
   bool _has_trap;
@@ -250,6 +263,9 @@
   };
   void check_pending_exception(int action);
   void handle_exception(llvm::Value* exception, int action);
+  void marshal_exception_fast(int num_options);
+  void marshal_exception_slow(int num_options);
+  llvm::BasicBlock* handler_for_exception(int index);
 
   // VM calls
  private:
@@ -335,6 +351,7 @@
 
   // Traps
  private:
+  llvm::BasicBlock* make_trap(int trap_bci, int trap_request);
   void do_trap(int trap_request);
 
   // Returns