< prev index next >

src/hotspot/share/runtime/sharedRuntime.cpp

Print this page

        

@@ -42,16 +42,21 @@
 #include "interpreter/interpreter.hpp"
 #include "interpreter/interpreterRuntime.hpp"
 #include "jfr/jfrEvents.hpp"
 #include "logging/log.hpp"
 #include "memory/metaspaceShared.hpp"
+#include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
+#include "oops/access.hpp"
+#include "oops/fieldStreams.hpp"
 #include "oops/klass.hpp"
 #include "oops/method.inline.hpp"
 #include "oops/objArrayKlass.hpp"
+#include "oops/objArrayOop.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/valueKlass.hpp"
 #include "prims/forte.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/methodHandles.hpp"
 #include "prims/nativeLookup.hpp"
 #include "runtime/arguments.hpp"

@@ -1114,10 +1119,16 @@
           }
           break;
         default:
           break;
       }
+    } else {
+      assert(attached_method->has_scalarized_args(), "invalid use of attached method");
+      if (!attached_method->method_holder()->is_value()) {
+        // Ignore the attached method in this case to not confuse below code
+        attached_method = NULL;
+      }
     }
   }
 
   assert(bc != Bytecodes::_illegal, "not initialized");
 

@@ -1132,24 +1143,39 @@
     RegisterMap reg_map2(thread);
     frame stubFrame   = thread->last_frame();
     // Caller-frame is a compiled frame
     frame callerFrame = stubFrame.sender(&reg_map2);
 
-    if (attached_method.is_null()) {
-      methodHandle callee = bytecode.static_target(CHECK_NH);
+    methodHandle callee = attached_method;
+    if (callee.is_null()) {
+      callee = bytecode.static_target(CHECK_NH);
       if (callee.is_null()) {
         THROW_(vmSymbols::java_lang_NoSuchMethodException(), nullHandle);
       }
     }
-
+    if (callee->has_scalarized_args() && callee->method_holder()->is_value()) {
+      // If the receiver is a value type that is passed as fields, no oop is available.
+      // Resolve the call without receiver null checking.
+      assert(!attached_method.is_null(), "must have attached method");
+      if (bc == Bytecodes::_invokevirtual) {
+        LinkInfo link_info(attached_method->method_holder(), attached_method->name(), attached_method->signature());
+        LinkResolver::resolve_virtual_call(callinfo, receiver, callee->method_holder(), link_info, /*check_null_and_abstract=*/ false, CHECK_NH);
+      } else {
+        assert(bc == Bytecodes::_invokeinterface, "anything else?");
+        LinkInfo link_info(constantPoolHandle(THREAD, caller->constants()), bytecode_index, CHECK_NH);
+        LinkResolver::resolve_interface_call(callinfo, receiver, callee->method_holder(), link_info, /*check_null_and_abstract=*/ false, CHECK_NH);
+      }
+      return receiver; // is null
+    } else {
     // Retrieve from a compiled argument list
     receiver = Handle(THREAD, callerFrame.retrieve_receiver(&reg_map2));
 
     if (receiver.is_null()) {
       THROW_(vmSymbols::java_lang_NullPointerException(), nullHandle);
     }
   }
+  }
 
   // Resolve method
   if (attached_method.not_null()) {
     // Parameterized by attached method.
     LinkResolver::resolve_invoke(callinfo, receiver, attached_method, bc, CHECK_NH);

@@ -1271,14 +1297,20 @@
 #endif
 
   bool is_nmethod = caller_nm->is_nmethod();
 
   if (is_virtual) {
+    Klass* receiver_klass = NULL;
+    if (ValueTypePassFieldsAsArgs && callee_method->method_holder()->is_value()) {
+      // If the receiver is a value type that is passed as fields, no oop is available
+      receiver_klass = callee_method->method_holder();
+    } else {
     assert(receiver.not_null() || invoke_code == Bytecodes::_invokehandle, "sanity check");
+      receiver_klass = invoke_code == Bytecodes::_invokehandle ? NULL : receiver->klass();
+    }
     bool static_bound = call_info.resolved_method()->can_be_statically_bound();
-    Klass* klass = invoke_code == Bytecodes::_invokehandle ? NULL : receiver->klass();
-    CompiledIC::compute_monomorphic_entry(callee_method, klass,
+    CompiledIC::compute_monomorphic_entry(callee_method, receiver_klass,
                      is_optimized, static_bound, is_nmethod, virtual_call_info,
                      CHECK_false);
   } else {
     // static call
     CompiledStaticCall::compute_entry(callee_method, is_nmethod, static_call_info);

@@ -1339,10 +1371,20 @@
   // make sure caller is not getting deoptimized
   // and removed before we are done with it.
   // CLEANUP - with lazy deopt shouldn't need this lock
   nmethodLocker caller_lock(caller_nm);
 
+  if (!is_virtual && !is_optimized) {
+    SimpleScopeDesc ssd(caller_nm, caller_frame.pc());
+    Bytecode bc(ssd.method(), ssd.method()->bcp_from(ssd.bci()));
+    // Substitutability test implementation piggy backs on static call resolution
+    if (bc.code() == Bytecodes::_if_acmpeq || bc.code() == Bytecodes::_if_acmpne) {
+      SystemDictionary::ValueBootstrapMethods_klass()->initialize(CHECK_NULL);
+      return SystemDictionary::ValueBootstrapMethods_klass()->find_method(vmSymbols::isSubstitutable_name(), vmSymbols::object_object_boolean_signature());
+    }
+  }
+
   // determine call info & receiver
   // note: a) receiver is NULL for static calls
   //       b) an exception is thrown if receiver is NULL for non-static calls
   CallInfo call_info;
   Bytecodes::Code invoke_code = Bytecodes::_illegal;

@@ -1426,18 +1468,20 @@
   frame caller_frame = stub_frame.sender(&reg_map);
   assert(!caller_frame.is_interpreted_frame() && !caller_frame.is_entry_frame(), "unexpected frame");
 #endif /* ASSERT */
 
   methodHandle callee_method;
+  bool is_optimized = false;
   JRT_BLOCK
-    callee_method = SharedRuntime::handle_ic_miss_helper(thread, CHECK_NULL);
+    callee_method = SharedRuntime::handle_ic_miss_helper(thread, is_optimized, CHECK_NULL);
     // Return Method* through TLS
     thread->set_vm_result_2(callee_method());
   JRT_BLOCK_END
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != NULL, " Jump to zero!");
-  return callee_method->verified_code_entry();
+  assert(callee_method->verified_code_entry() != NULL, "Jump to zero!");
+  assert(callee_method->verified_value_ro_code_entry() != NULL, "Jump to zero!");
+  return is_optimized ? callee_method->verified_code_entry() : callee_method->verified_value_ro_code_entry();
 JRT_END
 
 
 // Handle call site that has been made non-entrant
 JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method(JavaThread* thread))

@@ -1464,18 +1508,20 @@
     return callee->get_c2i_entry();
   }
 
   // Must be compiled to compiled path which is safe to stackwalk
   methodHandle callee_method;
+  bool is_optimized = false;
   JRT_BLOCK
     // Force resolving of caller (if we called from compiled frame)
-    callee_method = SharedRuntime::reresolve_call_site(thread, CHECK_NULL);
+    callee_method = SharedRuntime::reresolve_call_site(thread, is_optimized, CHECK_NULL);
     thread->set_vm_result_2(callee_method());
   JRT_BLOCK_END
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != NULL, " Jump to zero!");
-  return callee_method->verified_code_entry();
+  assert(callee_method->verified_code_entry() != NULL, "Jump to zero!");
+  assert(callee_method->verified_value_ro_code_entry() != NULL, "Jump to zero!");
+  return is_optimized ? callee_method->verified_code_entry() : callee_method->verified_value_ro_code_entry();
 JRT_END
 
 // Handle abstract method call
 JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method_abstract(JavaThread* thread))
   // Verbose error message for AbstractMethodError.

@@ -1514,11 +1560,11 @@
   JRT_BLOCK
     callee_method = SharedRuntime::resolve_helper(thread, false, false, CHECK_NULL);
     thread->set_vm_result_2(callee_method());
   JRT_BLOCK_END
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != NULL, " Jump to zero!");
+  assert(callee_method->verified_code_entry() != NULL, "Jump to zero!");
   return callee_method->verified_code_entry();
 JRT_END
 
 
 // resolve virtual call and update inline cache to monomorphic

@@ -1527,12 +1573,12 @@
   JRT_BLOCK
     callee_method = SharedRuntime::resolve_helper(thread, true, false, CHECK_NULL);
     thread->set_vm_result_2(callee_method());
   JRT_BLOCK_END
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != NULL, " Jump to zero!");
-  return callee_method->verified_code_entry();
+  assert(callee_method->verified_value_ro_code_entry() != NULL, "Jump to zero!");
+  return callee_method->verified_value_ro_code_entry();
 JRT_END
 
 
 // Resolve a virtual call that can be statically bound (e.g., always
 // monomorphic, so it has no inline cache).  Patch code to resolved target.

@@ -1541,11 +1587,11 @@
   JRT_BLOCK
     callee_method = SharedRuntime::resolve_helper(thread, true, true, CHECK_NULL);
     thread->set_vm_result_2(callee_method());
   JRT_BLOCK_END
   // return compiled code entry point after potential safepoints
-  assert(callee_method->verified_code_entry() != NULL, " Jump to zero!");
+  assert(callee_method->verified_code_entry() != NULL, "Jump to zero!");
   return callee_method->verified_code_entry();
 JRT_END
 
 // The handle_ic_miss_helper_internal function returns false if it failed due
 // to either running out of vtable stubs or ic stubs due to IC transitions

@@ -1553,21 +1599,22 @@
 // the failure was due to running out of IC stubs, in which case handle_ic_miss_helper
 // refills the IC stubs and tries again.
 bool SharedRuntime::handle_ic_miss_helper_internal(Handle receiver, CompiledMethod* caller_nm,
                                                    const frame& caller_frame, methodHandle callee_method,
                                                    Bytecodes::Code bc, CallInfo& call_info,
-                                                   bool& needs_ic_stub_refill, TRAPS) {
+                                                   bool& needs_ic_stub_refill, bool& is_optimized, TRAPS) {
   CompiledICLocker ml(caller_nm);
   CompiledIC* inline_cache = CompiledIC_before(caller_nm, caller_frame.pc());
   bool should_be_mono = false;
   if (inline_cache->is_optimized()) {
     if (TraceCallFixup) {
       ResourceMark rm(THREAD);
       tty->print("OPTIMIZED IC miss (%s) call to", Bytecodes::name(bc));
       callee_method->print_short_name(tty);
       tty->print_cr(" code: " INTPTR_FORMAT, p2i(callee_method->code()));
     }
+    is_optimized = true;
     should_be_mono = true;
   } else if (inline_cache->is_icholder_call()) {
     CompiledICHolder* ic_oop = inline_cache->cached_icholder();
     if (ic_oop != NULL) {
       if (!ic_oop->is_loader_alive()) {

@@ -1625,11 +1672,11 @@
     // Either clean or megamorphic
   }
   return true;
 }
 
-methodHandle SharedRuntime::handle_ic_miss_helper(JavaThread *thread, TRAPS) {
+methodHandle SharedRuntime::handle_ic_miss_helper(JavaThread *thread, bool& is_optimized, TRAPS) {
   ResourceMark rm(thread);
   CallInfo call_info;
   Bytecodes::Code bc;
 
   // receiver is NULL for static calls. An exception is thrown for NULL

@@ -1645,11 +1692,11 @@
   // plain ic_miss) and the site will be converted to an optimized virtual call site
   // never to miss again. I don't believe C2 will produce code like this but if it
   // did this would still be the correct thing to do for it too, hence no ifdef.
   //
   if (call_info.resolved_method()->can_be_statically_bound()) {
-    methodHandle callee_method = SharedRuntime::reresolve_call_site(thread, CHECK_(methodHandle()));
+    methodHandle callee_method = SharedRuntime::reresolve_call_site(thread, is_optimized, CHECK_(methodHandle()));
     if (TraceCallFixup) {
       RegisterMap reg_map(thread, false);
       frame caller_frame = thread->last_frame().sender(&reg_map);
       ResourceMark rm(thread);
       tty->print("converting IC miss to reresolve (%s) call to", Bytecodes::name(bc));

@@ -1700,11 +1747,11 @@
 
   for (;;) {
     ICRefillVerifier ic_refill_verifier;
     bool needs_ic_stub_refill = false;
     bool successful = handle_ic_miss_helper_internal(receiver, caller_nm, caller_frame, callee_method,
-                                                     bc, call_info, needs_ic_stub_refill, CHECK_(methodHandle()));
+                                                     bc, call_info, needs_ic_stub_refill, is_optimized, CHECK_(methodHandle()));
     if (successful || !needs_ic_stub_refill) {
       return callee_method;
     } else {
       InlineCacheBuffer::refill_ic_stubs();
     }

@@ -1732,11 +1779,11 @@
 // Resets a call-site in compiled code so it will get resolved again.
 // This routines handles both virtual call sites, optimized virtual call
 // sites, and static call sites. Typically used to change a call sites
 // destination from compiled to interpreted.
 //
-methodHandle SharedRuntime::reresolve_call_site(JavaThread *thread, TRAPS) {
+methodHandle SharedRuntime::reresolve_call_site(JavaThread *thread, bool& is_optimized, TRAPS) {
   ResourceMark rm(thread);
   RegisterMap reg_map(thread, false);
   frame stub_frame = thread->last_frame();
   assert(stub_frame.is_runtime_frame(), "must be a runtimeStub");
   frame caller = stub_frame.sender(&reg_map);

@@ -1793,10 +1840,11 @@
           is_static_call = true;
         } else {
           assert(iter.type() == relocInfo::virtual_call_type ||
                  iter.type() == relocInfo::opt_virtual_call_type
                 , "unexpected relocInfo. type");
+          is_optimized = (iter.type() == relocInfo::opt_virtual_call_type);
         }
       } else {
         assert(!UseInlineCaches, "relocation info. must exist for this address");
       }
 

@@ -1818,11 +1866,10 @@
     }
   }
 
   methodHandle callee_method = find_callee_method(thread, CHECK_(methodHandle()));
 
-
 #ifndef PRODUCT
   Atomic::inc(&_wrong_method_ctr);
 
   if (TraceCallFixup) {
     ResourceMark rm(thread);

@@ -2314,18 +2361,31 @@
                // Otherwise _value._fingerprint is the array.
 
   // Remap BasicTypes that are handled equivalently by the adapters.
   // These are correct for the current system but someday it might be
   // necessary to make this mapping platform dependent.
-  static int adapter_encoding(BasicType in) {
+  static int adapter_encoding(BasicType in, bool is_valuetype) {
     switch (in) {
       case T_BOOLEAN:
       case T_BYTE:
       case T_SHORT:
-      case T_CHAR:
-        // There are all promoted to T_INT in the calling convention
+      case T_CHAR: {
+        if (is_valuetype) {
+          // Do not widen value type field types
+          assert(ValueTypePassFieldsAsArgs, "must be enabled");
+          return in;
+        } else {
+          // They are all promoted to T_INT in the calling convention
         return T_INT;
+        }
+      }
+
+      case T_VALUETYPE: {
+        // If value types are passed as fields, return 'in' to differentiate
+        // between a T_VALUETYPE and a T_OBJECT in the signature.
+        return ValueTypePassFieldsAsArgs ? in : adapter_encoding(T_OBJECT, false);
+      }
 
       case T_OBJECT:
       case T_ARRAY:
         // In other words, we assume that any register good enough for
         // an int or long is good enough for a managed pointer.

@@ -2347,13 +2407,14 @@
         return T_CONFLICT;
     }
   }
 
  public:
-  AdapterFingerPrint(int total_args_passed, BasicType* sig_bt) {
+  AdapterFingerPrint(const GrowableArray<SigEntry>* sig, bool has_ro_adapter = false) {
     // The fingerprint is based on the BasicType signature encoded
     // into an array of ints with eight entries per int.
+    int total_args_passed = (sig != NULL) ? sig->length() : 0;
     int* ptr;
     int len = (total_args_passed + (_basic_types_per_int-1)) / _basic_types_per_int;
     if (len <= _compact_int_count) {
       assert(_compact_int_count == 3, "else change next line");
       _value._compact[0] = _value._compact[1] = _value._compact[2] = 0;

@@ -2367,21 +2428,41 @@
       ptr = _value._fingerprint;
     }
 
     // Now pack the BasicTypes with 8 per int
     int sig_index = 0;
+    BasicType prev_sbt = T_ILLEGAL;
+    int vt_count = 0;
     for (int index = 0; index < len; index++) {
       int value = 0;
       for (int byte = 0; byte < _basic_types_per_int; byte++) {
-        int bt = ((sig_index < total_args_passed)
-                  ? adapter_encoding(sig_bt[sig_index++])
-                  : 0);
+        int bt = 0;
+        if (sig_index < total_args_passed) {
+          BasicType sbt = sig->at(sig_index++)._bt;
+          if (ValueTypePassFieldsAsArgs && sbt == T_VALUETYPE) {
+            // Found start of value type in signature
+            vt_count++;
+            if (sig_index == 1 && has_ro_adapter) {
+              // With a ro_adapter, replace receiver value type delimiter by T_VOID to prevent matching
+              // with other adapters that have the same value type as first argument and no receiver.
+              sbt = T_VOID;
+            }
+          } else if (ValueTypePassFieldsAsArgs && sbt == T_VOID &&
+                     prev_sbt != T_LONG && prev_sbt != T_DOUBLE) {
+            // Found end of value type in signature
+            vt_count--;
+            assert(vt_count >= 0, "invalid vt_count");
+          }
+          bt = adapter_encoding(sbt, vt_count > 0);
+          prev_sbt = sbt;
+        }
         assert((bt & _basic_type_mask) == bt, "must fit in 4 bits");
         value = (value << _basic_type_bits) | bt;
       }
       ptr[index] = value;
     }
+    assert(vt_count == 0, "invalid vt_count");
   }
 
   ~AdapterFingerPrint() {
     if (_length > 0) {
       FREE_C_HEAP_ARRAY(int, _value._fingerprint);

@@ -2463,13 +2544,13 @@
  public:
   AdapterHandlerTable()
     : BasicHashtable<mtCode>(293, (DumpSharedSpaces ? sizeof(CDSAdapterHandlerEntry) : sizeof(AdapterHandlerEntry))) { }
 
   // Create a new entry suitable for insertion in the table
-  AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry) {
+  AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_value_entry, address c2i_value_ro_entry, address c2i_unverified_entry) {
     AdapterHandlerEntry* entry = (AdapterHandlerEntry*)BasicHashtable<mtCode>::new_entry(fingerprint->compute_hash());
-    entry->init(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+    entry->init(fingerprint, i2c_entry, c2i_entry, c2i_value_entry, c2i_value_ro_entry, c2i_unverified_entry);
     if (DumpSharedSpaces) {
       ((CDSAdapterHandlerEntry*)entry)->init();
     }
     return entry;
   }

@@ -2484,13 +2565,13 @@
     entry->deallocate();
     BasicHashtable<mtCode>::free_entry(entry);
   }
 
   // Find a entry with the same fingerprint if it exists
-  AdapterHandlerEntry* lookup(int total_args_passed, BasicType* sig_bt) {
+  AdapterHandlerEntry* lookup(const GrowableArray<SigEntry>* sig, bool has_ro_adapter = false) {
     NOT_PRODUCT(_lookups++);
-    AdapterFingerPrint fp(total_args_passed, sig_bt);
+    AdapterFingerPrint fp(sig, has_ro_adapter);
     unsigned int hash = fp.compute_hash();
     int index = hash_to_index(hash);
     for (AdapterHandlerEntry* e = bucket(index); e != NULL; e = e->next()) {
       NOT_PRODUCT(_buckets++);
       if (e->hash() == hash) {

@@ -2606,20 +2687,22 @@
   // are never compiled so an i2c entry is somewhat meaningless, but
   // throw AbstractMethodError just in case.
   // Pass wrong_method_abstract for the c2i transitions to return
   // AbstractMethodError for invalid invocations.
   address wrong_method_abstract = SharedRuntime::get_handle_wrong_method_abstract_stub();
-  _abstract_method_handler = AdapterHandlerLibrary::new_entry(new AdapterFingerPrint(0, NULL),
+  _abstract_method_handler = AdapterHandlerLibrary::new_entry(new AdapterFingerPrint(NULL),
                                                               StubRoutines::throw_AbstractMethodError_entry(),
-                                                              wrong_method_abstract, wrong_method_abstract);
+                                                              wrong_method_abstract, wrong_method_abstract, wrong_method_abstract, wrong_method_abstract);
 }
 
 AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint,
                                                       address i2c_entry,
                                                       address c2i_entry,
+                                                      address c2i_value_entry,
+                                                      address c2i_value_ro_entry,
                                                       address c2i_unverified_entry) {
-  return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+  return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_value_entry, c2i_value_ro_entry, c2i_unverified_entry);
 }
 
 AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& method) {
   AdapterHandlerEntry* entry = get_adapter0(method);
   if (method->is_shared()) {

@@ -2642,48 +2725,182 @@
   }
 
   return entry;
 }
 
+static int compute_scalarized_cc(const methodHandle& method, GrowableArray<SigEntry>& sig_cc, VMRegPair*& regs_cc, bool scalar_receiver) {
+  InstanceKlass* holder = method->method_holder();
+  sig_cc = GrowableArray<SigEntry>(method->size_of_parameters());
+  if (!method->is_static()) {
+    if (holder->is_value() && scalar_receiver) {
+      sig_cc.appendAll(ValueKlass::cast(holder)->extended_sig());
+    } else {
+      SigEntry::add_entry(&sig_cc, T_OBJECT);
+    }
+  }
+  Thread* THREAD = Thread::current();
+  for (SignatureStream ss(method->signature()); !ss.at_return_type(); ss.next()) {
+    if (ss.type() == T_VALUETYPE) {
+      Klass* k = ss.as_klass(Handle(THREAD, holder->class_loader()),
+                             Handle(THREAD, holder->protection_domain()),
+                             SignatureStream::ReturnNull, THREAD);
+      assert(k != NULL && !HAS_PENDING_EXCEPTION, "value klass should have been pre-loaded");
+      sig_cc.appendAll(ValueKlass::cast(k)->extended_sig());
+    } else {
+      SigEntry::add_entry(&sig_cc, ss.type());
+    }
+  }
+  regs_cc = NEW_RESOURCE_ARRAY(VMRegPair, sig_cc.length() + 2);
+  return SharedRuntime::java_calling_convention(&sig_cc, regs_cc);
+}
+
+static int insert_reserved_entry(GrowableArray<SigEntry>& sig_cc, VMRegPair*& regs_cc, int ret_off) {
+  // Find index in signature that belongs to return address slot
+  BasicType bt = T_ILLEGAL;
+  int i = 0;
+  for (uint off = 0; i < sig_cc.length(); ++i) {
+    if (SigEntry::skip_value_delimiters(&sig_cc, i)) {
+      VMReg first = regs_cc[off++].first();
+      if (first->is_valid() && first->is_stack()) {
+        // Select a type for the reserved entry that will end up on the stack
+        bt = sig_cc.at(i)._bt;
+        if (((int)first->reg2stack() + VMRegImpl::slots_per_word) == ret_off) {
+          break; // Index of the return address found
+        }
+      }
+    }
+  }
+  // Insert reserved entry and re-compute calling convention
+  SigEntry::insert_reserved_entry(&sig_cc, i, bt);
+  return SharedRuntime::java_calling_convention(&sig_cc, regs_cc);
+}
+
 AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter0(const methodHandle& method) {
   // Use customized signature handler.  Need to lock around updates to
   // the AdapterHandlerTable (it is not safe for concurrent readers
   // and a single writer: this could be fixed if it becomes a
   // problem).
 
   ResourceMark rm;
 
-  NOT_PRODUCT(int insts_size);
+  NOT_PRODUCT(int insts_size = 0);
   AdapterBlob* new_adapter = NULL;
   AdapterHandlerEntry* entry = NULL;
   AdapterFingerPrint* fingerprint = NULL;
+
   {
     MutexLocker mu(AdapterHandlerLibrary_lock);
     // make sure data structure is initialized
     initialize();
 
-    if (method->is_abstract()) {
+    bool has_value_arg = false;
+    bool has_value_recv = false;
+    GrowableArray<SigEntry> sig(method->size_of_parameters());
+    if (!method->is_static()) {
+      has_value_recv = method->method_holder()->is_value();
+      has_value_arg = has_value_recv;
+      SigEntry::add_entry(&sig, T_OBJECT);
+    }
+    for (SignatureStream ss(method->signature()); !ss.at_return_type(); ss.next()) {
+      BasicType bt = ss.type();
+      if (bt == T_VALUETYPE) {
+        has_value_arg = true;
+        bt = T_OBJECT;
+      }
+      SigEntry::add_entry(&sig, bt);
+    }
+
+    // Process abstract method if it has value type args to set has_scalarized_args accordingly
+    if (method->is_abstract() && !(ValueTypePassFieldsAsArgs && has_value_arg)) {
       return _abstract_method_handler;
     }
 
-    // Fill in the signature array, for the calling-convention call.
-    int total_args_passed = method->size_of_parameters(); // All args on stack
+    // Get a description of the compiled java calling convention and the largest used (VMReg) stack slot usage
+    VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, sig.length());
+    int args_on_stack = SharedRuntime::java_calling_convention(&sig, regs);
 
-    BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed);
-    VMRegPair* regs   = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed);
-    int i = 0;
-    if (!method->is_static())  // Pass in receiver first
-      sig_bt[i++] = T_OBJECT;
-    for (SignatureStream ss(method->signature()); !ss.at_return_type(); ss.next()) {
-      sig_bt[i++] = ss.type();  // Collect remaining bits of signature
-      if (ss.type() == T_LONG || ss.type() == T_DOUBLE)
-        sig_bt[i++] = T_VOID;   // Longs & doubles take 2 Java slots
+    // Now compute the scalarized calling convention if there are value types in the signature
+    GrowableArray<SigEntry> sig_cc = sig;
+    GrowableArray<SigEntry> sig_cc_ro = sig;
+    VMRegPair* regs_cc = regs;
+    VMRegPair* regs_cc_ro = regs;
+    int args_on_stack_cc = args_on_stack;
+    int args_on_stack_cc_ro = args_on_stack;
+
+    if (ValueTypePassFieldsAsArgs && has_value_arg && !method->is_native()) {
+      MutexUnlocker mul(AdapterHandlerLibrary_lock);
+      args_on_stack_cc = compute_scalarized_cc(method, sig_cc, regs_cc, /* scalar_receiver = */ true);
+
+      sig_cc_ro = sig_cc;
+      regs_cc_ro = regs_cc;
+      args_on_stack_cc_ro = args_on_stack_cc;
+      if (has_value_recv || args_on_stack_cc > args_on_stack) {
+        // For interface calls, we need another entry point / adapter to unpack the receiver
+        args_on_stack_cc_ro = compute_scalarized_cc(method, sig_cc_ro, regs_cc_ro, /* scalar_receiver = */ false);
+      }
+
+      // Compute the stack extension that is required to convert between the calling conventions.
+      // The stack slots at these offsets are occupied by the return address with the unscalarized
+      // calling convention. Don't use them for arguments with the scalarized calling convention.
+      int ret_off    = args_on_stack_cc - args_on_stack;
+      int ret_off_ro = args_on_stack_cc - args_on_stack_cc_ro;
+      assert(ret_off_ro <= 0 || ret_off > 0, "receiver unpacking requires more stack space than expected");
+
+      if (ret_off > 0) {
+        // Make sure the stack of the scalarized calling convention with the reserved
+        // entries (2 slots each) remains 16-byte (4 slots) aligned after stack extension.
+        int alignment = StackAlignmentInBytes / VMRegImpl::stack_slot_size;
+        if (ret_off_ro != ret_off && ret_off_ro >= 0) {
+          ret_off    += 4; // Account for two reserved entries (4 slots)
+          ret_off_ro += 4;
+          ret_off     = align_up(ret_off, alignment);
+          ret_off_ro  = align_up(ret_off_ro, alignment);
+          // TODO can we avoid wasting a stack slot here?
+          //assert(ret_off != ret_off_ro, "fail");
+          if (ret_off > ret_off_ro) {
+            swap(ret_off, ret_off_ro); // Sort by offset
+          }
+          args_on_stack_cc = insert_reserved_entry(sig_cc, regs_cc, ret_off);
+          args_on_stack_cc = insert_reserved_entry(sig_cc, regs_cc, ret_off_ro);
+        } else {
+          ret_off += 2; // Account for one reserved entry (2 slots)
+          ret_off = align_up(ret_off, alignment);
+          args_on_stack_cc = insert_reserved_entry(sig_cc, regs_cc, ret_off);
+        }
+      }
+
+      // Upper bound on stack arguments to avoid hitting the argument limit and
+      // bailing out of compilation ("unsupported incoming calling sequence").
+      // TODO we need a reasonable limit (flag?) here
+      if (args_on_stack_cc > 50) {
+        // Don't scalarize value type arguments
+        sig_cc = sig;
+        sig_cc_ro = sig;
+        regs_cc = regs;
+        regs_cc_ro = regs;
+        args_on_stack_cc = args_on_stack;
+      } else {
+        method->set_has_scalarized_args(true);
+        method->set_needs_stack_repair(args_on_stack_cc > args_on_stack);
+      }
+    }
+
+    if (method->is_abstract()) {
+      // Save a C heap allocated version of the signature for abstract methods with scalarized value type arguments
+      assert(ValueTypePassFieldsAsArgs && has_value_arg, "must have scalarized value type args");
+      address wrong_method_abstract = SharedRuntime::get_handle_wrong_method_abstract_stub();
+      entry = AdapterHandlerLibrary::new_entry(new AdapterFingerPrint(NULL),
+                                               StubRoutines::throw_AbstractMethodError_entry(),
+                                               wrong_method_abstract, wrong_method_abstract, wrong_method_abstract, wrong_method_abstract);
+      GrowableArray<SigEntry>* heap_sig = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<SigEntry>(sig_cc_ro.length(), true);
+      heap_sig->appendAll(&sig_cc_ro);
+      entry->set_sig_cc(heap_sig);
+      return entry;
     }
-    assert(i == total_args_passed, "");
 
     // Lookup method signature's fingerprint
-    entry = _adapters->lookup(total_args_passed, sig_bt);
+    entry = _adapters->lookup(&sig_cc, regs_cc != regs_cc_ro);
 
 #ifdef ASSERT
     AdapterHandlerEntry* shared_entry = NULL;
     // Start adapter sharing verification only after the VM is booted.
     if (VerifyAdapterSharing && (entry != NULL)) {

@@ -2694,15 +2911,12 @@
 
     if (entry != NULL) {
       return entry;
     }
 
-    // Get a description of the compiled java calling convention and the largest used (VMReg) stack slot usage
-    int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, false);
-
     // Make a C heap allocated version of the fingerprint to store in the adapter
-    fingerprint = new AdapterFingerPrint(total_args_passed, sig_bt);
+    fingerprint = new AdapterFingerPrint(&sig_cc, regs_cc != regs_cc_ro);
 
     // StubRoutines::code2() is initialized after this function can be called. As a result,
     // VerifyAdapterCalls and VerifyAdapterSharing can fail if we re-use code that generated
     // prior to StubRoutines::code2() being set. Checks refer to checks generated in an I2C
     // stub that ensure that an I2C stub is called from an interpreter frame.

@@ -2716,29 +2930,44 @@
       buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs,
                                              sizeof(buffer_locs)/sizeof(relocInfo));
 
       MacroAssembler _masm(&buffer);
       entry = SharedRuntime::generate_i2c2i_adapters(&_masm,
-                                                     total_args_passed,
-                                                     comp_args_on_stack,
-                                                     sig_bt,
+                                                     args_on_stack,
+                                                     args_on_stack_cc,
+                                                     &sig,
                                                      regs,
-                                                     fingerprint);
+                                                     &sig_cc,
+                                                     regs_cc,
+                                                     &sig_cc_ro,
+                                                     regs_cc_ro,
+                                                     fingerprint,
+                                                     new_adapter);
+
+      if (regs != regs_cc) {
+        // Save a C heap allocated version of the scalarized signature and store it in the adapter
+        GrowableArray<SigEntry>* heap_sig = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<SigEntry>(sig_cc.length(), true);
+        heap_sig->appendAll(&sig_cc);
+        entry->set_sig_cc(heap_sig);
+      }
+
 #ifdef ASSERT
       if (VerifyAdapterSharing) {
         if (shared_entry != NULL) {
+          if (!shared_entry->compare_code(buf->code_begin(), buffer.insts_size())) {
+            method->print();
+          }
           assert(shared_entry->compare_code(buf->code_begin(), buffer.insts_size()), "code must match");
           // Release the one just created and return the original
           _adapters->free_entry(entry);
           return shared_entry;
         } else  {
           entry->save_code(buf->code_begin(), buffer.insts_size());
         }
       }
 #endif
 
-      new_adapter = AdapterBlob::create(&buffer);
       NOT_PRODUCT(insts_size = buffer.insts_size());
     }
     if (new_adapter == NULL) {
       // CodeCache is full, disable compilation
       // Ought to log this but compile log is only per compile thread

@@ -2790,10 +3019,12 @@
 
 address AdapterHandlerEntry::base_address() {
   address base = _i2c_entry;
   if (base == NULL)  base = _c2i_entry;
   assert(base <= _c2i_entry || _c2i_entry == NULL, "");
+  assert(base <= _c2i_value_entry || _c2i_value_entry == NULL, "");
+  assert(base <= _c2i_value_ro_entry || _c2i_value_ro_entry == NULL, "");
   assert(base <= _c2i_unverified_entry || _c2i_unverified_entry == NULL, "");
   return base;
 }
 
 void AdapterHandlerEntry::relocate(address new_base) {

@@ -2802,18 +3033,25 @@
   ptrdiff_t delta = new_base - old_base;
   if (_i2c_entry != NULL)
     _i2c_entry += delta;
   if (_c2i_entry != NULL)
     _c2i_entry += delta;
+  if (_c2i_value_entry != NULL)
+    _c2i_value_entry += delta;
+  if (_c2i_value_ro_entry != NULL)
+    _c2i_value_ro_entry += delta;
   if (_c2i_unverified_entry != NULL)
     _c2i_unverified_entry += delta;
   assert(base_address() == new_base, "");
 }
 
 
 void AdapterHandlerEntry::deallocate() {
   delete _fingerprint;
+  if (_sig_cc != NULL) {
+    delete _sig_cc;
+  }
 #ifdef ASSERT
   if (_saved_code) FREE_C_HEAP_ARRAY(unsigned char, _saved_code);
 #endif
 }
 

@@ -2881,11 +3119,12 @@
       int i=0;
       if (!method->is_static())  // Pass in receiver first
         sig_bt[i++] = T_OBJECT;
       SignatureStream ss(method->signature());
       for (; !ss.at_return_type(); ss.next()) {
-        sig_bt[i++] = ss.type();  // Collect remaining bits of signature
+        BasicType bt = ss.type();
+        sig_bt[i++] = bt;  // Collect remaining bits of signature
         if (ss.type() == T_LONG || ss.type() == T_DOUBLE)
           sig_bt[i++] = T_VOID;   // Longs & doubles take 2 Java slots
       }
       assert(i == total_args_passed, "");
       BasicType ret_type = ss.type();

@@ -2996,16 +3235,20 @@
     case 'V': sig_bt[cnt++] = T_VOID;    break;
     case 'L':                   // Oop
       while (*s++ != ';');   // Skip signature
       sig_bt[cnt++] = T_OBJECT;
       break;
+    case 'Q':                // Value type
+      while (*s++ != ';');   // Skip signature
+      sig_bt[cnt++] = T_VALUETYPE;
+      break;
     case '[': {                 // Array
       do {                      // Skip optional size
         while (*s >= '0' && *s <= '9') s++;
       } while (*s++ == '[');   // Nested arrays?
       // Skip element type
-      if (s[-1] == 'L')
+      if (s[-1] == 'L' || s[-1] == 'Q')
         while (*s++ != ';'); // Skip signature
       sig_bt[cnt++] = T_ARRAY;
       break;
     }
     default : ShouldNotReachHere();

@@ -3142,13 +3385,13 @@
   }
   assert(false, "Should have found handler");
 }
 
 void AdapterHandlerEntry::print_adapter_on(outputStream* st) const {
-  st->print_cr("AHE@" INTPTR_FORMAT ": %s i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iUV: " INTPTR_FORMAT,
+  st->print_cr("AHE@" INTPTR_FORMAT ": %s i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iVE: " INTPTR_FORMAT " c2iVROE: " INTPTR_FORMAT " c2iUE: " INTPTR_FORMAT,
                p2i(this), fingerprint()->as_string(),
-               p2i(get_i2c_entry()), p2i(get_c2i_entry()), p2i(get_c2i_unverified_entry()));
+               p2i(get_i2c_entry()), p2i(get_c2i_entry()), p2i(get_c2i_value_entry()), p2i(get_c2i_value_ro_entry()), p2i(get_c2i_unverified_entry()));
 
 }
 
 #if INCLUDE_CDS
 

@@ -3238,5 +3481,212 @@
   if (new_obj == NULL) return;
 
   BarrierSet *bs = BarrierSet::barrier_set();
   bs->on_slowpath_allocation_exit(thread, new_obj);
 }
+
+// We are at a compiled code to interpreter call. We need backing
+// buffers for all value type arguments. Allocate an object array to
+// hold them (convenient because once we're done with it we don't have
+// to worry about freeing it).
+JRT_ENTRY(void, SharedRuntime::allocate_value_types(JavaThread* thread, Method* callee_method, bool allocate_receiver))
+{
+  assert(ValueTypePassFieldsAsArgs, "no reason to call this");
+  ResourceMark rm;
+  JavaThread* THREAD = thread;
+  methodHandle callee(callee_method);
+
+  int nb_slots = 0;
+  InstanceKlass* holder = callee->method_holder();
+  allocate_receiver &= !callee->is_static() && holder->is_value();
+  if (allocate_receiver) {
+    nb_slots++;
+  }
+  Handle class_loader(THREAD, holder->class_loader());
+  Handle protection_domain(THREAD, holder->protection_domain());
+  for (SignatureStream ss(callee->signature()); !ss.at_return_type(); ss.next()) {
+    if (ss.type() == T_VALUETYPE) {
+      nb_slots++;
+    }
+  }
+  objArrayOop array_oop = oopFactory::new_objectArray(nb_slots, CHECK);
+  objArrayHandle array(THREAD, array_oop);
+  int i = 0;
+  if (allocate_receiver) {
+    ValueKlass* vk = ValueKlass::cast(holder);
+    oop res = vk->allocate_instance(CHECK);
+    array->obj_at_put(i, res);
+    i++;
+  }
+  for (SignatureStream ss(callee->signature()); !ss.at_return_type(); ss.next()) {
+    if (ss.type() == T_VALUETYPE) {
+      Klass* k = ss.as_klass(class_loader, protection_domain, SignatureStream::ReturnNull, THREAD);
+      assert(k != NULL && !HAS_PENDING_EXCEPTION, "can't resolve klass");
+      ValueKlass* vk = ValueKlass::cast(k);
+      oop res = vk->allocate_instance(CHECK);
+      array->obj_at_put(i, res);
+      i++;
+    }
+  }
+  thread->set_vm_result(array());
+  thread->set_vm_result_2(callee()); // TODO: required to keep callee live?
+}
+JRT_END
+
+// Iterate of the array of heap allocated value types and apply the GC post barrier to all reference fields.
+// This is called from the C2I adapter after value type arguments are heap allocated and initialized.
+JRT_LEAF(void, SharedRuntime::apply_post_barriers(JavaThread* thread, objArrayOopDesc* array))
+{
+  assert(ValueTypePassFieldsAsArgs, "no reason to call this");
+  assert(oopDesc::is_oop(array), "should be oop");
+  for (int i = 0; i < array->length(); ++i) {
+    instanceOop valueOop = (instanceOop)array->obj_at(i);
+    ValueKlass* vk = ValueKlass::cast(valueOop->klass());
+    if (vk->contains_oops()) {
+      const address dst_oop_addr = ((address) (void*) valueOop);
+      OopMapBlock* map = vk->start_of_nonstatic_oop_maps();
+      OopMapBlock* const end = map + vk->nonstatic_oop_map_count();
+      while (map != end) {
+        address doop_address = dst_oop_addr + map->offset();
+        barrier_set_cast<ModRefBarrierSet>(BarrierSet::barrier_set())->
+          write_ref_array((HeapWord*) doop_address, map->count());
+        map++;
+      }
+    }
+  }
+}
+JRT_END
+
+// We're returning from an interpreted method: load each field into a
+// register following the calling convention
+JRT_LEAF(void, SharedRuntime::load_value_type_fields_in_regs(JavaThread* thread, oopDesc* res))
+{
+  assert(res->klass()->is_value(), "only value types here");
+  ResourceMark rm;
+  RegisterMap reg_map(thread);
+  frame stubFrame = thread->last_frame();
+  frame callerFrame = stubFrame.sender(&reg_map);
+  assert(callerFrame.is_interpreted_frame(), "should be coming from interpreter");
+
+  ValueKlass* vk = ValueKlass::cast(res->klass());
+
+  const Array<SigEntry>* sig_vk = vk->extended_sig();
+  const Array<VMRegPair>* regs = vk->return_regs();
+
+  if (regs == NULL) {
+    // The fields of the value klass don't fit in registers, bail out
+    return;
+  }
+
+  int j = 1;
+  for (int i = 0; i < sig_vk->length(); i++) {
+    BasicType bt = sig_vk->at(i)._bt;
+    if (bt == T_VALUETYPE) {
+      continue;
+    }
+    if (bt == T_VOID) {
+      if (sig_vk->at(i-1)._bt == T_LONG ||
+          sig_vk->at(i-1)._bt == T_DOUBLE) {
+        j++;
+      }
+      continue;
+    }
+    int off = sig_vk->at(i)._offset;
+    assert(off > 0, "offset in object should be positive");
+    VMRegPair pair = regs->at(j);
+    address loc = reg_map.location(pair.first());
+    switch(bt) {
+    case T_BOOLEAN:
+      *(jboolean*)loc = res->bool_field(off);
+      break;
+    case T_CHAR:
+      *(jchar*)loc = res->char_field(off);
+      break;
+    case T_BYTE:
+      *(jbyte*)loc = res->byte_field(off);
+      break;
+    case T_SHORT:
+      *(jshort*)loc = res->short_field(off);
+      break;
+    case T_INT: {
+      *(jint*)loc = res->int_field(off);
+      break;
+    }
+    case T_LONG:
+#ifdef _LP64
+      *(intptr_t*)loc = res->long_field(off);
+#else
+      Unimplemented();
+#endif
+      break;
+    case T_OBJECT:
+    case T_ARRAY: {
+      *(oop*)loc = res->obj_field(off);
+      break;
+    }
+    case T_FLOAT:
+      *(jfloat*)loc = res->float_field(off);
+      break;
+    case T_DOUBLE:
+      *(jdouble*)loc = res->double_field(off);
+      break;
+    default:
+      ShouldNotReachHere();
+    }
+    j++;
+  }
+  assert(j == regs->length(), "missed a field?");
+
+#ifdef ASSERT
+  VMRegPair pair = regs->at(0);
+  address loc = reg_map.location(pair.first());
+  assert(*(oopDesc**)loc == res, "overwritten object");
+#endif
+
+  thread->set_vm_result(res);
+}
+JRT_END
+
+// We've returned to an interpreted method, the interpreter needs a
+// reference to a value type instance. Allocate it and initialize it
+// from field's values in registers.
+JRT_BLOCK_ENTRY(void, SharedRuntime::store_value_type_fields_to_buf(JavaThread* thread, intptr_t res))
+{
+  ResourceMark rm;
+  RegisterMap reg_map(thread);
+  frame stubFrame = thread->last_frame();
+  frame callerFrame = stubFrame.sender(&reg_map);
+
+#ifdef ASSERT
+  ValueKlass* verif_vk = ValueKlass::returned_value_klass(reg_map);
+#endif
+
+  if (!is_set_nth_bit(res, 0)) {
+    // We're not returning with value type fields in registers (the
+    // calling convention didn't allow it for this value klass)
+    assert(!Metaspace::contains((void*)res), "should be oop or pointer in buffer area");
+    thread->set_vm_result((oopDesc*)res);
+    assert(verif_vk == NULL, "broken calling convention");
+    return;
+  }
+
+  clear_nth_bit(res, 0);
+  ValueKlass* vk = (ValueKlass*)res;
+  assert(verif_vk == vk, "broken calling convention");
+  assert(Metaspace::contains((void*)res), "should be klass");
+
+  // Allocate handles for every oop field so they are safe in case of
+  // a safepoint when allocating
+  GrowableArray<Handle> handles;
+  vk->save_oop_fields(reg_map, handles);
+
+  // It's unsafe to safepoint until we are here
+  JRT_BLOCK;
+  {
+    Thread* THREAD = thread;
+    oop vt = vk->realloc_result(reg_map, handles, CHECK);
+    thread->set_vm_result(vt);
+  }
+  JRT_BLOCK_END;
+}
+JRT_END
+
< prev index next >