changeset 1886:29be67755005

2009-06-10 Gary Benson <gbenson@redhat.com> * ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp (SharkTopLevelBlock::CallType): Removed. (SharkTopLevelBlock::get_call_type): Likewise. (SharkTopLevelBlock::get_callee): Likewise. (SharkTopLevelBlock::improve_virtual_call): New method. (SharkTopLevelBlock::get_virtual_callee): Updated. * ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp (SharkTopLevelBlock::scan_for_traps): Updated call traps. (SharkTopLevelBlock::get_call_type): Removed. (SharkTopLevelBlock::get_callee): Likewise. (SharkTopLevelBlock::improve_virtual_call): New method. (SharkTopLevelBlock::get_virtual_callee): Updated. (SharkTopLevelBlock::do_call): Replaced callee selection.
author Gary Benson <gbenson@redhat.com>
date Wed, 10 Jun 2009 13:22:21 +0100
parents 19e7ed3a153a
children 05f411adf2f1
files ChangeLog ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp
diffstat 3 files changed, 151 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Jun 10 18:10:29 2009 +0100
+++ b/ChangeLog	Wed Jun 10 13:22:21 2009 +0100
@@ -1,3 +1,19 @@
+2009-06-10  Gary Benson  <gbenson@redhat.com>
+
+	* ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp
+	(SharkTopLevelBlock::CallType): Removed.
+	(SharkTopLevelBlock::get_call_type): Likewise.
+	(SharkTopLevelBlock::get_callee): Likewise.
+	(SharkTopLevelBlock::improve_virtual_call): New method.
+	(SharkTopLevelBlock::get_virtual_callee): Updated.
+	* ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp
+	(SharkTopLevelBlock::scan_for_traps): Updated call traps.
+	(SharkTopLevelBlock::get_call_type): Removed.
+	(SharkTopLevelBlock::get_callee): Likewise.
+	(SharkTopLevelBlock::improve_virtual_call): New method.
+	(SharkTopLevelBlock::get_virtual_callee): Updated.
+	(SharkTopLevelBlock::do_call): Replaced callee selection.
+
 2009-06-09  Gary Benson  <gbenson@redhat.com>
 
 	* patches/hotspot/default/icedtea-shark.patch
--- a/ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp	Wed Jun 10 18:10:29 2009 +0100
+++ b/ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp	Wed Jun 10 13:22:21 2009 +0100
@@ -75,37 +75,13 @@
       }
       break;
 
+    case Bytecodes::_invokestatic:
+    case Bytecodes::_invokespecial:
     case Bytecodes::_invokevirtual:
-      method = iter()->get_method(will_link);
-      assert(will_link, "typeflow responsibility");
-
-      // If this is a non-final invokevirtual then we need to
-      // check that its holder is linked, because its vtable
-      // won't have been set up otherwise.
-      if (!method->is_final_method() && !method->holder()->is_linked()) {
-        set_trap(
-          Deoptimization::make_trap_request(
-            Deoptimization::Reason_uninitialized,
-            Deoptimization::Action_reinterpret), bci());
-          return;
-      }
-      break;
-
     case Bytecodes::_invokeinterface:
       method = iter()->get_method(will_link);
       assert(will_link, "typeflow responsibility");
 
-      // Handle the case where we encounter an invokeinterface but
-      // should really have an invokevirtual since the resolved
-      // method is a virtual method in java.lang.Object.  This is
-      // a legal corner case in the spec, and while javac does not
-      // generate this code there's no reason a compliant Java
-      // compiler should not.  See cpCacheOop.cpp and
-      // interpreterRuntime.cpp for more details.
-      if (method->holder() == function()->env()->Object_klass())
-        Unimplemented();
-
-      // Bail out if the holder is unloaded
       if (!method->holder()->is_linked()) {
         set_trap(
           Deoptimization::make_trap_request(
@@ -841,39 +817,78 @@
   }
 }
 
-// Figure out what type of call this is.
-//  - Direct calls are where the callee is fixed.
-//  - Interface and Virtual calls require lookup at runtime.
-// NB some invokevirtuals can be resolved to direct calls.
-SharkTopLevelBlock::CallType SharkTopLevelBlock::get_call_type(ciMethod* method)
+ciMethod* SharkTopLevelBlock::improve_virtual_call(ciMethod*   caller,
+                                              ciInstanceKlass* klass,
+                                              ciMethod*        dest_method,
+                                              ciType*          receiver_type)
 {
-  if (bc() == Bytecodes::_invokeinterface)
-    return CALL_INTERFACE;
-  else if (bc() == Bytecodes::_invokevirtual && !method->is_final_method())
-    return CALL_VIRTUAL;
-  else
-    return CALL_DIRECT;
+  // If the method is obviously final then we are already done
+  if (dest_method->can_be_statically_bound())
+    return dest_method;
+
+  // Array methods are all inherited from Object and are monomorphic
+  if (receiver_type->is_array_klass() &&
+      dest_method->holder() == function()->env()->Object_klass())
+    return dest_method;
+
+  // All other interesting cases are instance classes
+  if (!receiver_type->is_instance_klass())
+    return NULL;
+
+  // Attempt to improve the receiver
+  ciInstanceKlass* actual_receiver = klass;
+  ciInstanceKlass *improved_receiver = receiver_type->as_instance_klass();
+  if (improved_receiver->is_loaded() &&
+      improved_receiver->is_initialized() &&
+      !improved_receiver->is_interface() &&
+      improved_receiver->is_subtype_of(actual_receiver)) {
+    actual_receiver = improved_receiver;
+  }
+
+  // Attempt to find a monomorphic target for this call using
+  // class heirachy analysis.  
+  ciInstanceKlass *calling_klass = caller->holder();
+  ciMethod* monomorphic_target =
+    dest_method->find_monomorphic_target(calling_klass, klass, actual_receiver);
+  if (monomorphic_target != NULL) {
+    assert(!monomorphic_target->is_abstract(), "shouldn't be");
+
+    // Opto has a bunch of type checking here that I don't
+    // understand.  It's to inhibit casting in one direction,
+    // possibly because objects in Opto can have inexact
+    // types, but I can't even tell which direction it
+    // doesn't like.  For now I'm going to block *any* cast.
+    if (monomorphic_target != dest_method) {
+#ifndef PRODUCT
+      tty->print_cr("found monomorphic target, but inhibited cast:");
+      tty->print("  dest_method = ");
+      dest_method->print_short_name(tty);
+      tty->cr();
+      tty->print("  monomorphic_target = ");
+      monomorphic_target->print_short_name(tty);
+      tty->cr();
+#endif // !PRODUCT
+      monomorphic_target = NULL;
+    }
+  }
+
+  // Replace the virtual call with a direct one.  This makes
+  // us dependent on that target method not getting overridden
+  // by dynamic class loading.
+  if (monomorphic_target != NULL) {
+    function()->env()->dependencies()->assert_unique_concrete_method(
+      actual_receiver, monomorphic_target);
+    return monomorphic_target;
+  }
+
+  // Because Opto distinguishes exact types from inexact ones
+  // it can perform a further optimization to replace calls
+  // with non-monomorphic targets if the receiver has an exact
+  // type.  We don't mark types this way, so we can't do this.
+
+  return NULL;
 }
 
-Value *SharkTopLevelBlock::get_callee(CallType    call_type,
-                                      ciMethod*   method,
-                                      SharkValue* receiver)
-{
-  switch (call_type) {
-  case CALL_DIRECT:
-    return get_direct_callee(method);
-  case CALL_VIRTUAL:
-    return get_virtual_callee(receiver, method);
-  case CALL_INTERFACE:
-    return get_interface_callee(receiver, method);
-  default:
-    ShouldNotReachHere();
-  } 
-}
-
-// Direct calls can be made when the callee is fixed.
-// invokestatic and invokespecial are always direct;
-// invokevirtual is direct in some circumstances.
 Value *SharkTopLevelBlock::get_direct_callee(ciMethod* method)
 {
   return builder()->CreateBitCast(
@@ -882,9 +897,8 @@
     "callee");
 }
 
-// Non-direct virtual calls are handled here
 Value *SharkTopLevelBlock::get_virtual_callee(SharkValue* receiver,
-                                              ciMethod*   method)
+                                              int vtable_index)
 {
   Value *klass = builder()->CreateValueOfStructEntry(
     receiver->jobject_value(),
@@ -898,11 +912,10 @@
       SharkType::methodOop_type(),
       vtableEntry::size() * wordSize,
       in_ByteSize(instanceKlass::vtable_start_offset() * wordSize),
-      LLVMValue::intptr_constant(method->vtable_index())),
+      LLVMValue::intptr_constant(vtable_index)),
     "callee");
 }
 
-// Interface calls are handled here
 Value* SharkTopLevelBlock::get_interface_callee(SharkValue *receiver,
                                                 ciMethod*   method)
 {
@@ -1013,31 +1026,78 @@
 
 void SharkTopLevelBlock::do_call()
 {
+  // Set frequently used booleans
+  bool is_static = bc() == Bytecodes::_invokestatic;
+  bool is_virtual = bc() == Bytecodes::_invokevirtual;
+  bool is_interface = bc() == Bytecodes::_invokeinterface;
+
+  // Find the method being called
   bool will_link;
-  ciMethod *method = iter()->get_method(will_link);
+  ciMethod *dest_method = iter()->get_method(will_link);
   assert(will_link, "typeflow responsibility");
+  assert(dest_method->is_static() == is_static, "must match bc");
 
-  // Figure out what type of call this is
-  CallType call_type = get_call_type(method);
+  // Find the class of the method being called.  Note
+  // that the superclass check in the second assertion
+  // is to cope with a hole in the spec that allows for
+  // invokeinterface instructions where the resolved
+  // method is a virtual method in java.lang.Object.
+  // javac doesn't generate code like that, but there's
+  // no reason a compliant Java compiler might not.
+  ciInstanceKlass *holder_klass  = dest_method->holder();
+  assert(holder_klass->is_loaded(), "scan_for_traps responsibility");
+  assert(holder_klass->is_interface() ||
+         holder_klass->super() == NULL ||
+         !is_interface, "must match bc");
+  ciKlass *holder = iter()->get_declared_method_holder();
+  ciInstanceKlass *klass =
+    ciEnv::get_instance_klass_for_declared_method_holder(holder);
 
   // Find the receiver in the stack.  We do this before
   // trying to inline because the inliner can only use
   // zero-checked values, not being able to perform the
   // check itself.
   SharkValue *receiver = NULL;
-  if (bc() != Bytecodes::_invokestatic) {
-    receiver = xstack(method->arg_size() - 1);
+  if (!is_static) {
+    receiver = xstack(dest_method->arg_size() - 1);
     check_null(receiver);
   }
 
+  // Try to improve non-direct calls
+  bool call_is_virtual = is_virtual || is_interface;
+  ciMethod *call_method = dest_method;
+  if (call_is_virtual) {
+    ciMethod *optimized_method = improve_virtual_call(
+      target(), klass, dest_method, receiver->type());
+    if (optimized_method) {
+      call_method = optimized_method;
+      call_is_virtual = false;
+    }
+  }
+
   // Try to inline the call
-  if (call_type == CALL_DIRECT) {
-    if (SharkInliner::attempt_inline(method, current_state(), thread()))
+  if (!call_is_virtual) {
+    if (SharkInliner::attempt_inline(call_method, current_state(), thread()))
       return;
   }
 
   // Find the method we are calling
-  Value *callee = get_callee(call_type, method, receiver);
+  Value *callee;
+  if (call_is_virtual) {
+    if (is_virtual) {
+      int vtable_index = call_method->resolve_vtable_index(
+        target()->holder(), klass);
+      assert(vtable_index >= 0, "should be");
+      callee = get_virtual_callee(receiver, vtable_index);
+    }
+    else {
+      assert(is_interface, "should be");
+      callee = get_interface_callee(receiver, call_method);
+    }
+  }
+  else {
+    callee = get_direct_callee(call_method);
+  }
 
   // Load the SharkEntry from the callee
   Value *base_pc = builder()->CreateValueOfStructEntry(
@@ -1056,9 +1116,9 @@
     "entry_point");
 
   // Make the call
-  current_state()->decache_for_Java_call(method);
+  current_state()->decache_for_Java_call(call_method);
   builder()->CreateCall3(entry_point, callee, base_pc, thread());
-  current_state()->cache_after_Java_call(method);
+  current_state()->cache_after_Java_call(call_method);
 
   // Check for pending exceptions
   check_pending_exception(EX_CHECK_FULL);
--- a/ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp	Wed Jun 10 18:10:29 2009 +0100
+++ b/ports/hotspot/src/share/vm/shark/sharkTopLevelBlock.hpp	Wed Jun 10 13:22:21 2009 +0100
@@ -371,18 +371,12 @@
 
   // invoke*
  private:
-  enum CallType {
-    CALL_DIRECT,
-    CALL_VIRTUAL,
-    CALL_INTERFACE
-  };
-  CallType get_call_type(ciMethod* method);
-  llvm::Value* get_callee(CallType    call_type,
-                          ciMethod*   method,
-                          SharkValue* receiver);
-
+  ciMethod* improve_virtual_call(ciMethod*        caller,
+                                 ciInstanceKlass* klass,
+                                 ciMethod*        dest_method,
+                                 ciType*          receiver_type);
   llvm::Value* get_direct_callee(ciMethod* method);
-  llvm::Value* get_virtual_callee(SharkValue* receiver, ciMethod* method);
+  llvm::Value* get_virtual_callee(SharkValue* receiver, int vtable_index);
   llvm::Value* get_interface_callee(SharkValue* receiver, ciMethod* method);
 
   void do_call();