diff src/share/vm/oops/valueKlass.cpp @ 13020:4cd1b06ce224

multiple value return
author roland
date Thu, 08 Jun 2017 18:25:59 +0200
parents d16eda688d5d
children b1cf9cbe2ecb
line wrap: on
line diff
--- a/src/share/vm/oops/valueKlass.cpp	Thu Jun 01 17:13:31 2017 +0200
+++ b/src/share/vm/oops/valueKlass.cpp	Thu Jun 08 18:25:59 2017 +0200
@@ -23,6 +23,7 @@
  */
 
 #include "precompiled.hpp"
+#include "gc/shared/gcLocker.inline.hpp"
 #include "interpreter/interpreter.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/fieldStreams.hpp"
@@ -30,6 +31,7 @@
 #include "oops/objArrayKlass.hpp"
 #include "oops/valueKlass.hpp"
 #include "oops/valueArrayKlass.hpp"
+#include "runtime/signature.hpp"
 #include "utilities/copy.hpp"
 
 int ValueKlass::first_field_offset() const {
@@ -238,3 +240,264 @@
   value_store(data_for_oop(src()), data_for_oop(value), true, true);
   return value;
 }
+
+// Value type arguments are not passed by reference, instead each
+// field of the value type is passed as an argument. This helper
+// function collects the fields of the value types (including embedded
+// value type's fields) in a list. Included with the field's type is
+// the offset of each field in the value type: i2c and c2i adapters
+// need that to load or store fields. Finally, the list of fields is
+// sorted in order of increasing offsets: the adapters and the
+// compiled code need and agreed upon order of fields.
+//
+// The list of basic types that is returned starts with a T_VALUETYPE
+// and ends with an extra T_VOID. T_VALUETYPE/T_VOID are used as
+// delimiters. Every entry between the two is a field of the value
+// type. If there's an embedded value type in the list, it also starts
+// with a T_VALUETYPE and ends with a T_VOID. This is so we can
+// generate a unique fingerprint for the method's adapters and we can
+// generate the list of basic types from the interpreter point of view
+// (value types passed as reference: iterate on the list until a
+// T_VALUETYPE, drop everything until and including the closing
+// T_VOID) or the compiler point of view (each field of the value
+// types is an argument: drop all T_VALUETYPE/T_VOID from the list).
+GrowableArray<SigEntry> ValueKlass::collect_fields(int base_off) const {
+  GrowableArray<SigEntry> sig_extended;
+  sig_extended.push(SigEntry(T_VALUETYPE, base_off));
+  for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
+    if (fs.access_flags().is_static())  continue;
+    fieldDescriptor& fd = fs.field_descriptor();
+    BasicType bt = fd.field_type();
+    int offset = base_off + fd.offset() - (base_off > 0 ? first_field_offset() : 0);
+    if (bt == T_VALUETYPE) {
+      Symbol* signature = fd.signature();
+      JavaThread* THREAD = JavaThread::current();
+      oop loader = class_loader();
+      oop domain = protection_domain();
+      ResetNoHandleMark rnhm;
+      HandleMark hm;
+      NoSafepointVerifier nsv;
+      Klass* klass = SystemDictionary::resolve_or_null(signature,
+                                                       Handle(THREAD, loader), Handle(THREAD, domain),
+                                                       THREAD);
+      assert(klass != NULL && !HAS_PENDING_EXCEPTION, "lookup shouldn't fail");
+      const GrowableArray<SigEntry>& embedded = ValueKlass::cast(klass)->collect_fields(offset);
+      sig_extended.appendAll(&embedded);
+    } else {
+      sig_extended.push(SigEntry(bt, offset));
+      if (bt == T_LONG || bt == T_DOUBLE) {
+        sig_extended.push(SigEntry(T_VOID, offset));
+      }
+    }
+  }
+  int offset = base_off + size_helper()*HeapWordSize - (base_off > 0 ? first_field_offset() : 0);
+  sig_extended.push(SigEntry(T_VOID, offset)); // hack: use T_VOID to mark end of value type fields
+  if (base_off == 0) {
+    sig_extended.sort(SigEntry::compare);
+  }
+  assert(sig_extended.at(0)._bt == T_VALUETYPE && sig_extended.at(sig_extended.length()-1)._bt == T_VOID, "broken structure");
+  return sig_extended;
+}
+
+// Returns the basic types and registers for fields to return an
+// instance of this value type in registers if possible.
+GrowableArray<SigEntry> ValueKlass::return_convention(VMRegPair*& regs, int& nb_fields) const {
+  assert(ValueTypeReturnedAsFields, "inconsistent");
+  const GrowableArray<SigEntry>& sig_vk = collect_fields();
+  nb_fields = SigEntry::count_fields(sig_vk)+1;
+  BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, nb_fields);
+  sig_bt[0] = T_METADATA;
+  SigEntry::fill_sig_bt(sig_vk, sig_bt+1, nb_fields-1, true);
+  regs = NEW_RESOURCE_ARRAY(VMRegPair, nb_fields);
+  int total = SharedRuntime::java_return_convention(sig_bt, regs, nb_fields);
+
+  if (total <= 0) {
+    regs = NULL;
+  }
+  
+  return sig_vk;
+}
+
+// Create handles for all oop fields returned in registers that are
+// going to be live across a safepoint.
+bool ValueKlass::save_oop_results(RegisterMap& reg_map, GrowableArray<Handle>& handles) const {
+  if (ValueTypeReturnedAsFields) {
+    int nb_fields;
+    VMRegPair* regs;
+    const GrowableArray<SigEntry>& sig_vk = return_convention(regs, nb_fields);
+    
+    if (regs != NULL) {
+      regs++;
+      nb_fields--;
+      save_oop_fields(sig_vk, reg_map, regs, handles, nb_fields);
+      return true;
+    }
+  }
+  return false;
+}
+
+// Same as above but with pre-computed return convention
+void ValueKlass::save_oop_fields(const GrowableArray<SigEntry>& sig_vk, RegisterMap& reg_map, const VMRegPair* regs, GrowableArray<Handle>& handles, int nb_fields) const {
+  int j = 0;
+  Thread* thread = Thread::current();
+  for (int i = 0; i < sig_vk.length(); i++) {
+    BasicType bt = sig_vk.at(i)._bt;
+    if (bt == T_OBJECT || bt == T_ARRAY) {
+      int off = sig_vk.at(i)._offset;
+      VMRegPair pair = regs[j];
+      address loc = reg_map.location(pair.first());
+      oop v = *(oop*)loc;
+      assert(v == NULL || v->is_oop(), "not an oop?");
+      assert(Universe::heap()->is_in_or_null(v), "must be heap pointer");
+      handles.push(Handle(thread, v));
+    }
+    if (bt == T_VALUETYPE) {
+      continue;
+    }
+    if (bt == T_VOID &&
+        sig_vk.at(i-1)._bt != T_LONG &&
+        sig_vk.at(i-1)._bt != T_DOUBLE) {
+      continue;
+    }
+    j++;
+  }
+  assert(j == nb_fields, "missed a field?");
+}
+
+// Update oop fields in registers from handles after a safepoint
+void ValueKlass::restore_oop_results(RegisterMap& reg_map, GrowableArray<Handle>& handles) const {
+  assert(ValueTypeReturnedAsFields, "inconsistent");
+  int nb_fields;
+  VMRegPair* regs;
+  const GrowableArray<SigEntry>& sig_vk = return_convention(regs, nb_fields);
+  assert(regs != NULL, "inconsistent");
+
+  regs++;
+  nb_fields--;
+
+  int j = 0;
+  for (int i = 0, k = 0; i < sig_vk.length(); i++) {
+    BasicType bt = sig_vk.at(i)._bt;
+    if (bt == T_OBJECT || bt == T_ARRAY) {
+      int off = sig_vk.at(i)._offset;
+      VMRegPair pair = regs[j];
+      address loc = reg_map.location(pair.first());
+      *(oop*)loc = handles.at(k++)();
+    }
+    if (bt == T_VALUETYPE) {
+      continue;
+    }
+    if (bt == T_VOID &&
+        sig_vk.at(i-1)._bt != T_LONG &&
+        sig_vk.at(i-1)._bt != T_DOUBLE) {
+      continue;
+    }
+    j++;
+  }
+  assert(j == nb_fields, "missed a field?");
+}
+
+// Fields are in registers. Create an instance of the value type and
+// initialize it with the values of the fields.
+oop ValueKlass::realloc_result(const GrowableArray<SigEntry>& sig_vk, const RegisterMap& reg_map, const VMRegPair* regs,
+                               const GrowableArray<Handle>& handles, int nb_fields, TRAPS) {
+  oop new_vt = allocate_instance(CHECK_NULL);
+
+  int j = 0;
+  int k = 0;
+  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;
+    VMRegPair pair = regs[j];
+    address loc = reg_map.location(pair.first());
+    switch(bt) {
+    case T_BOOLEAN: {
+      jboolean v = *(intptr_t*)loc;
+      *(jboolean*)((address)new_vt + off) = v;
+      break;
+    }
+    case T_CHAR: {
+      jchar v = *(intptr_t*)loc;
+      *(jchar*)((address)new_vt + off) = v;
+      break;
+    }
+    case T_BYTE: {
+      jbyte v = *(intptr_t*)loc;
+      *(jbyte*)((address)new_vt + off) = v;
+      break;
+    }
+    case T_SHORT: {
+      jshort v = *(intptr_t*)loc;
+      *(jshort*)((address)new_vt + off) = v;
+      break;
+    }
+    case T_INT: {
+      jint v = *(intptr_t*)loc;
+      *(jint*)((address)new_vt + off) = v;
+      break;
+    }
+    case T_LONG: {
+#ifdef _LP64
+      jlong v = *(intptr_t*)loc;
+      *(jlong*)((address)new_vt + off) = v;
+#else
+      Unimplemented();
+#endif
+      break;
+    }
+    case T_OBJECT:
+    case T_ARRAY: {
+      Handle handle = handles.at(k++);
+      oop v = handle();
+      if (!UseCompressedOops) {
+        oop* p = (oop*)((address)new_vt + off);
+        oopDesc::store_heap_oop(p, v);
+      } else {
+        narrowOop* p = (narrowOop*)((address)new_vt + off);
+        oopDesc::encode_store_heap_oop(p, v);
+      }
+      break;
+    }
+    case T_FLOAT: {
+      jfloat v = *(jfloat*)loc;
+      *(jfloat*)((address)new_vt + off) = v;
+      break;
+    }
+    case T_DOUBLE: {
+      jdouble v = *(jdouble*)loc;
+      *(jdouble*)((address)new_vt + off) = v;
+      break;
+    }
+    default:
+      ShouldNotReachHere();
+    }
+    j++;
+  }
+  assert(j == nb_fields, "missed a field?");
+  assert(k == handles.length(), "missed an oop?");
+  return new_vt;
+}
+
+ValueKlass* ValueKlass::returned_value_type(const RegisterMap& map) {
+  BasicType bt = T_METADATA;
+  VMRegPair pair;
+  int nb = SharedRuntime::java_return_convention(&bt, &pair, 1);
+  assert(nb == 1, "broken");
+  
+  address loc = map.location(pair.first());
+  intptr_t ptr = *(intptr_t*)loc;
+  if (Universe::heap()->is_in_reserved((void*)ptr)) {
+    return NULL;
+  }
+  return (ValueKlass*)ptr;
+}