changeset 13008:cbd149ccab1b mvt

Implementation of reference field support for value types in C2
author thartmann
date Wed, 17 May 2017 09:52:46 +0200
parents b32107cabbcf
children 32d8f469730a
files src/cpu/x86/vm/sharedRuntime_x86_64.cpp src/share/vm/code/compiledMethod.cpp src/share/vm/opto/callnode.cpp src/share/vm/opto/compile.cpp src/share/vm/opto/escape.cpp src/share/vm/opto/graphKit.cpp src/share/vm/opto/output.cpp src/share/vm/opto/type.cpp src/share/vm/opto/type.hpp src/share/vm/opto/valuetypenode.cpp src/share/vm/runtime/frame.cpp src/share/vm/runtime/globals.hpp src/share/vm/runtime/sharedRuntime.cpp src/share/vm/runtime/sharedRuntime.hpp test/compiler/valhalla/valuetypes/ValueTypeTestBench.java
diffstat 15 files changed, 337 insertions(+), 158 deletions(-) [+]
line wrap: on
line diff
--- a/src/cpu/x86/vm/sharedRuntime_x86_64.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/cpu/x86/vm/sharedRuntime_x86_64.cpp	Wed May 17 09:52:46 2017 +0200
@@ -28,6 +28,7 @@
 #endif
 #include "asm/macroAssembler.hpp"
 #include "asm/macroAssembler.inline.hpp"
+#include "classfile/symbolTable.hpp"
 #include "code/debugInfoRec.hpp"
 #include "code/icBuffer.hpp"
 #include "code/vtableStubs.hpp"
@@ -609,7 +610,8 @@
                                    size_t size_in_bytes,
                                    const VMRegPair& reg_pair,
                                    const Address& to,
-                                   int extraspace) {
+                                   int extraspace,
+                                   bool is_oop) {
   assert(bt != T_VALUETYPE || !ValueTypePassFieldsAsArgs, "no value type here");
   if (bt == T_VOID) {
     assert(prev_bt == T_LONG || prev_bt == T_DOUBLE, "missing half");
@@ -637,12 +639,21 @@
     assert(!r_2->is_valid(), "must be invalid");
     return;
   }
-  if (r_1->is_stack()) {
-    int ld_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + extraspace;
-    __ load_sized_value(rax, Address(rsp, ld_off), size_in_bytes, /* is_signed */ false);
-    __ store_sized_value(to, rax, size_in_bytes);
-  } else if (r_1->is_Register()) {
-    __ store_sized_value(to, r_1->as_Register(), size_in_bytes);
+
+  if (!r_1->is_XMMRegister()) {
+    Register val = rax;
+    assert_different_registers(to.base(), val);
+    if(r_1->is_stack()) {
+      int ld_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + extraspace;
+      __ load_sized_value(val, Address(rsp, ld_off), size_in_bytes, /* is_signed */ false);
+    } else {
+      val = r_1->as_Register();
+    }
+    if (is_oop) {
+      __ store_heap_oop(to, val);
+    } else {
+      __ store_sized_value(to, val, size_in_bytes);
+    }
   } else {
     if (wide) {
       __ movdbl(to, r_1->as_XMMRegister());
@@ -669,12 +680,13 @@
 
   __ bind(skip_fixup);
 
+  bool has_value_argument = false;
   if (ValueTypePassFieldsAsArgs) {
-    // Is there a value type arguments?
-    int i = 0;
-    for (; i < sig_extended.length() && sig_extended.at(i)._bt != T_VALUETYPE; i++);
-
-    if (i < sig_extended.length()) {
+    // Is there a value type argument?
+    for (int i = 0; i < sig_extended.length() && !has_value_argument; i++) {
+      has_value_argument = (sig_extended.at(i)._bt == T_VALUETYPE);
+    }
+    if (has_value_argument) {
       // There is at least a value type argument: we're coming from
       // compiled code so we have no buffers to back the value
       // types. Allocate the buffers here with a runtime call.
@@ -708,15 +720,12 @@
       __ bind(no_exception);
 
       // We get an array of objects from the runtime call
-      int offset_in_bytes = arrayOopDesc::base_offset_in_bytes(T_OBJECT);
-      __ get_vm_result(r13, r15_thread);
+      __ get_vm_result(r13, r15_thread); // Use r13 as temporary because r10 is trashed by movptr()
       __ get_vm_result_2(rbx, r15_thread); // TODO: required to keep the callee Method live?
-      __ addptr(r13, offset_in_bytes);
       __ mov(r10, r13);
     }
   }
 
-
   // Since all args are passed on the stack, total_args_passed *
   // Interpreter::stackElementSize is the space we need. Plus 1 because
   // we also account for the return address location since
@@ -750,6 +759,7 @@
   // we allocated above and want to pass to the
   // interpreter. next_arg_int is the next argument from the
   // interpreter point of view (value types are passed by reference).
+  bool has_oop_field = false;
   for (int next_arg_comp = 0, ignored = 0, next_vt_arg = 0, next_arg_int = 0;
        next_arg_comp < sig_extended.length(); next_arg_comp++) {
     assert(ignored <= next_arg_comp, "shouldn't skip over more slot than there are arguments");
@@ -762,7 +772,7 @@
       const VMRegPair reg_pair = regs[next_arg_comp-ignored];
       size_t size_in_bytes = reg_pair.second()->is_valid() ? 8 : 4;
       gen_c2i_adapter_helper(masm, bt, next_arg_comp > 0 ? sig_extended.at(next_arg_comp-1)._bt : T_ILLEGAL,
-                             size_in_bytes, reg_pair, Address(rsp, offset), extraspace);
+                             size_in_bytes, reg_pair, Address(rsp, offset), extraspace, false);
       next_arg_int++;
 #ifdef ASSERT
       if (bt == T_LONG || bt == T_DOUBLE) {
@@ -774,7 +784,8 @@
     } else {
       ignored++;
       // get the buffer from the just allocated pool of buffers
-      __ load_heap_oop(r11, Address(r10, next_vt_arg * type2aelembytes(T_VALUETYPE)));
+      int index = arrayOopDesc::base_offset_in_bytes(T_OBJECT) + next_vt_arg * type2aelembytes(T_VALUETYPE);
+      __ load_heap_oop(r11, Address(r10, index));
       next_vt_arg++; next_arg_int++;
       int vt = 1;
       // write fields we get from compiled code in registers/stack
@@ -799,8 +810,10 @@
           int off = sig_extended.at(next_arg_comp)._offset;
           assert(off > 0, "offset in object should be positive");
           size_t size_in_bytes = is_java_primitive(bt) ? type2aelembytes(bt) : wordSize;
+          bool is_oop = (bt == T_OBJECT || bt == T_ARRAY);
+          has_oop_field = has_oop_field || is_oop;
           gen_c2i_adapter_helper(masm, bt, next_arg_comp > 0 ? sig_extended.at(next_arg_comp-1)._bt : T_ILLEGAL,
-                                 size_in_bytes, regs[next_arg_comp-ignored], Address(r11, off), extraspace);
+                                 size_in_bytes, regs[next_arg_comp-ignored], Address(r11, off), extraspace, is_oop);
         }
       } while (vt != 0);
       // pass the buffer to the interpreter
@@ -808,6 +821,23 @@
     }
   }
 
+  // If a value type was allocated and initialized, apply post barrier to all oop fields
+  if (has_value_argument && has_oop_field) {
+    __ push(r13); // save senderSP
+    __ push(rbx); // save callee
+    // Allocate argument register save area
+    if (frame::arg_reg_save_area_bytes != 0) {
+      __ subptr(rsp, frame::arg_reg_save_area_bytes);
+    }
+    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::apply_post_barriers), r15_thread, r10);
+    // De-allocate argument register save area
+    if (frame::arg_reg_save_area_bytes != 0) {
+      __ addptr(rsp, frame::arg_reg_save_area_bytes);
+    }
+    __ pop(rbx); // restore callee
+    __ pop(r13); // restore sender SP
+  }
+
   // Schedule the branch target address early.
   __ movptr(rcx, Address(rbx, in_bytes(Method::interpreter_entry_offset())));
   __ jmp(rcx);
@@ -831,7 +861,8 @@
                                    BasicType prev_bt,
                                    size_t size_in_bytes,
                                    const VMRegPair& reg_pair,
-                                   const Address& from) {
+                                   const Address& from,
+                                   bool is_oop) {
   assert(bt != T_VALUETYPE || !ValueTypePassFieldsAsArgs, "no value type here");
   if (bt == T_VOID) {
     // Longs and doubles are passed in native word order, but misaligned
@@ -852,18 +883,21 @@
   }
 
   bool is_signed = (bt != T_CHAR) && (bt != T_BOOLEAN);
-  if (r_1->is_stack()) {
-    // Convert stack slot to an SP offset (+ wordSize to account for return address)
-    int st_off = reg_pair.first()->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+  if (!r_1->is_XMMRegister()) {
     // We can use r13 as a temp here because compiled code doesn't need r13 as an input
     // and if we end up going thru a c2i because of a miss a reasonable value of r13
     // will be generated.
-    __ load_sized_value(r13, from, size_in_bytes, is_signed);
-    __ movq(Address(rsp, st_off), r13);
-  } else if (r_1->is_Register()) {
-    Register r = r_1->as_Register();
-    assert(r != rax, "must be different");
-    __ load_sized_value(r, from, size_in_bytes, is_signed);
+    Register dst = r_1->is_stack() ? r13 : r_1->as_Register();
+    if (is_oop) {
+      __ load_heap_oop(dst, from);
+    } else {
+      __ load_sized_value(dst, from, size_in_bytes, is_signed);
+    }
+    if (r_1->is_stack()) {
+      // Convert stack slot to an SP offset (+ wordSize to account for return address)
+      int st_off = reg_pair.first()->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+      __ movq(Address(rsp, st_off), dst);
+    }
   } else {
     if (wide) {
       __ movdbl(r_1->as_XMMRegister(), from);
@@ -1005,7 +1039,7 @@
       const VMRegPair reg_pair = regs[next_arg_comp-ignored];
       size_t size_in_bytes = reg_pair.second()->is_valid() ? 8 : 4;
       gen_i2c_adapter_helper(masm, bt, next_arg_comp > 0 ? sig_extended.at(next_arg_comp-1)._bt : T_ILLEGAL,
-                             size_in_bytes, reg_pair, Address(saved_sp, offset));
+                             size_in_bytes, reg_pair, Address(saved_sp, offset), false);
       next_arg_int++;
     } else {
       next_arg_int++;
@@ -1035,7 +1069,8 @@
           int off = sig_extended.at(next_arg_comp)._offset;
           assert(off > 0, "offset in object should be positive");
           size_t size_in_bytes = is_java_primitive(bt) ? type2aelembytes(bt) : wordSize;
-          gen_i2c_adapter_helper(masm, bt, prev_bt, size_in_bytes, regs[next_arg_comp - ignored], Address(r10, off));
+          bool is_oop = (bt == T_OBJECT || bt == T_ARRAY);
+          gen_i2c_adapter_helper(masm, bt, prev_bt, size_in_bytes, regs[next_arg_comp - ignored], Address(r10, off), is_oop);
         }
       } while (vt != 0);
     }
@@ -1113,7 +1148,41 @@
 
   __ flush();
   new_adapter = AdapterBlob::create(masm->code(), frame_complete, frame_size_in_words, oop_maps);
-  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+
+  // If value types are passed as fields, save the extended signature as symbol in
+  // the AdapterHandlerEntry to be used by nmethod::preserve_callee_argument_oops().
+  Symbol* extended_signature = NULL;
+  if (ValueTypePassFieldsAsArgs) {
+    bool has_value_argument = false;
+    Thread* THREAD = Thread::current();
+    ResourceMark rm(THREAD);
+    int length = sig_extended.length();
+    char* sig_str = NEW_RESOURCE_ARRAY(char, 2*length + 3);
+    int idx = 0;
+    sig_str[idx++] = '(';
+    for (int index = 0; index < length; index++) {
+      BasicType bt = sig_extended.at(index)._bt;
+      if (bt == T_VALUETYPE || bt == T_VOID) {
+        has_value_argument = true;
+        continue; // Ignore wrapper types
+      }
+      sig_str[idx++] = type2char(bt);
+      if (bt == T_OBJECT) {
+        sig_str[idx++] = ';';
+      } else if (bt == T_ARRAY) {
+        // We don't know the array element type, put void as placeholder
+        sig_str[idx++] = 'V';
+      }
+    }
+    sig_str[idx++] = ')';
+    sig_str[idx++] = '\0';
+    if (has_value_argument) {
+      // Extended signature is only required if a value type argument is passed
+      extended_signature = SymbolTable::new_permanent_symbol(sig_str, THREAD);
+    }
+  }
+
+  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, extended_signature);
 }
 
 int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
--- a/src/share/vm/code/compiledMethod.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/code/compiledMethod.cpp	Wed May 17 09:52:46 2017 +0200
@@ -30,6 +30,7 @@
 #include "interpreter/bytecode.hpp"
 #include "memory/resourceArea.hpp"
 #include "runtime/mutexLocker.hpp"
+#include "runtime/sharedRuntime.hpp"
 
 CompiledMethod::CompiledMethod(Method* method, const char* name, CompilerType type, const CodeBlobLayout& layout, int frame_complete_offset, int frame_size, ImmutableOopMapSet* oop_maps, bool caller_must_gc_arguments)
   : CodeBlob(name, type, layout, frame_complete_offset, frame_size, oop_maps, caller_must_gc_arguments),
@@ -310,6 +311,27 @@
       signature = callee->signature();
     }
 
+    // If value types are passed as fields, use the extended signature
+    // which contains the types of all (oop) fields of the value type.
+    if (ValueTypePassFieldsAsArgs) {
+      // Check if receiver or one of the arguments is a value type
+      bool has_value_receiver = (callee != NULL && callee->method_holder()->is_value());
+      bool has_value_argument = has_value_receiver;
+      const int len = signature->utf8_length();
+      for (int i = 0; i < len && !has_value_argument; ++i) {
+        if (signature->byte_at(i) == 'Q') {
+          has_value_argument = true;
+        }
+      }
+      if (has_value_argument) {
+        // Get the extended signature from the callee's adapter through the attached method
+        assert(callee != NULL, "must have attached method");
+        signature = callee->adapter()->get_sig_extended();
+        assert(signature != NULL, "signature is null");
+        has_receiver = false; // The extended signature contains the receiver type
+      }
+    }
+
     fr.oops_compiled_arguments_do(signature, has_receiver, has_appendix, reg_map, f);
   }
 #endif // !SHARK
--- a/src/share/vm/opto/callnode.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/opto/callnode.cpp	Wed May 17 09:52:46 2017 +0200
@@ -369,6 +369,7 @@
       st->print(" %s%d]=#NULL",msg,i);
       break;
     case Type::AryPtr:
+    case Type::ValueTypePtr:
     case Type::InstPtr:
       st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,p2i(t->isa_oopptr()->const_oop()));
       break;
--- a/src/share/vm/opto/compile.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/opto/compile.cpp	Wed May 17 09:52:46 2017 +0200
@@ -1418,7 +1418,7 @@
     if ( offset != Type::OffsetBot &&
          offset > arrayOopDesc::length_offset_in_bytes() ) {
       offset = Type::OffsetBot; // Flatten constant access into array body only
-      tj = ta = TypeAryPtr::make(ptr, ta->ary(), ta->klass(), true, Type::Offset(offset), ta->_field_offset, ta->instance_id());
+      tj = ta = TypeAryPtr::make(ptr, ta->ary(), ta->klass(), true, Type::Offset(offset), ta->field_offset(), ta->instance_id());
     }
   } else if( ta && _AliasLevel >= 2 ) {
     // For arrays indexed by constant indices, we flatten the alias
@@ -1429,7 +1429,7 @@
     if( offset != Type::OffsetBot ) {
       if( ta->const_oop() ) { // MethodData* or Method*
         offset = Type::OffsetBot;   // Flatten constant access into array body
-        tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),ta->ary(),ta->klass(),false,Type::Offset(offset), ta->_field_offset);
+        tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),ta->ary(),ta->klass(),false,Type::Offset(offset), ta->field_offset());
       } else if( offset == arrayOopDesc::length_offset_in_bytes() ) {
         // range is OK as-is.
         tj = ta = TypeAryPtr::RANGE;
@@ -1443,35 +1443,35 @@
         ptr = TypePtr::BotPTR;
       } else {                  // Random constant offset into array body
         offset = Type::OffsetBot;   // Flatten constant access into array body
-        tj = ta = TypeAryPtr::make(ptr,ta->ary(),ta->klass(),false,Type::Offset(offset), ta->_field_offset);
+        tj = ta = TypeAryPtr::make(ptr,ta->ary(),ta->klass(),false,Type::Offset(offset), ta->field_offset());
       }
     }
     // Arrays of fixed size alias with arrays of unknown size.
     if (ta->size() != TypeInt::POS) {
       const TypeAry *tary = TypeAry::make(ta->elem(), TypeInt::POS);
-      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,ta->klass(),false,Type::Offset(offset), ta->_field_offset);
+      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,ta->klass(),false,Type::Offset(offset), ta->field_offset());
     }
     // Arrays of known objects become arrays of unknown objects.
     if (ta->elem()->isa_narrowoop() && ta->elem() != TypeNarrowOop::BOTTOM) {
       const TypeAry *tary = TypeAry::make(TypeNarrowOop::BOTTOM, ta->size());
-      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,NULL,false,Type::Offset(offset), ta->_field_offset);
+      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,NULL,false,Type::Offset(offset), ta->field_offset());
     }
     if (ta->elem()->isa_oopptr() && ta->elem() != TypeInstPtr::BOTTOM) {
       const TypeAry *tary = TypeAry::make(TypeInstPtr::BOTTOM, ta->size());
-      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,NULL,false,Type::Offset(offset), ta->_field_offset);
+      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,NULL,false,Type::Offset(offset), ta->field_offset());
     }
     // Arrays of bytes and of booleans both use 'bastore' and 'baload' so
     // cannot be distinguished by bytecode alone.
     if (ta->elem() == TypeInt::BOOL) {
       const TypeAry *tary = TypeAry::make(TypeInt::BYTE, ta->size());
       ciKlass* aklass = ciTypeArrayKlass::make(T_BYTE);
-      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,aklass,false,Type::Offset(offset), ta->_field_offset);
+      tj = ta = TypeAryPtr::make(ptr,ta->const_oop(),tary,aklass,false,Type::Offset(offset), ta->field_offset());
     }
     // During the 2nd round of IterGVN, NotNull castings are removed.
     // Make sure the Bottom and NotNull variants alias the same.
     // Also, make sure exact and non-exact variants alias the same.
     if (ptr == TypePtr::NotNull || ta->klass_is_exact() || ta->speculative() != NULL) {
-      tj = ta = TypeAryPtr::make(TypePtr::BotPTR,ta->ary(),ta->klass(),false,Type::Offset(offset), ta->_field_offset);
+      tj = ta = TypeAryPtr::make(TypePtr::BotPTR,ta->ary(),ta->klass(),false,Type::Offset(offset), ta->field_offset());
     }
   }
 
--- a/src/share/vm/opto/escape.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/opto/escape.cpp	Wed May 17 09:52:46 2017 +0200
@@ -884,7 +884,7 @@
         ptnode_adr(call_idx)->set_scalar_replaceable(false);
       } else {
         // Determine whether any arguments are returned.
-        const TypeTuple* d = call->tf()->domain_sig();
+        const TypeTuple* d = call->tf()->domain_cc();
         bool ret_arg = false;
         for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
           if (d->field_at(i)->isa_ptr() != NULL &&
@@ -1059,16 +1059,11 @@
       // fall-through if not a Java method or no analyzer information
       if (call_analyzer != NULL) {
         PointsToNode* call_ptn = ptnode_adr(call->_idx);
-        const TypeTuple* d = call->tf()->domain_sig();
-        int extra = 0;
+        const TypeTuple* d = call->tf()->domain_cc();
         for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
           const Type* at = d->field_at(i);
-          if (at->isa_valuetypeptr()) {
-            extra += at->is_valuetypeptr()->value_type()->value_klass()->field_count() - 1;
-            continue;
-          }
           int k = i - TypeFunc::Parms;
-          Node* arg = call->in(i + extra);
+          Node* arg = call->in(i);
           PointsToNode* arg_ptn = ptnode_adr(arg->_idx);
           if (at->isa_ptr() != NULL &&
               call_analyzer->is_arg_returned(k)) {
@@ -1108,7 +1103,7 @@
       // Fall-through here if not a Java method or no analyzer information
       // or some other type of call, assume the worst case: all arguments
       // globally escape.
-      const TypeTuple* d = call->tf()->domain_sig();
+      const TypeTuple* d = call->tf()->domain_cc();
       for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
         const Type* at = d->field_at(i);
         if (at->isa_oopptr() != NULL) {
@@ -2054,8 +2049,9 @@
 
 bool ConnectionGraph::is_oop_field(Node* n, int offset, bool* unsafe) {
   const Type* adr_type = n->as_AddP()->bottom_type();
+  int field_offset = adr_type->isa_aryptr() ? adr_type->isa_aryptr()->field_offset().get() : Type::OffsetBot;
   BasicType bt = T_INT;
-  if (offset == Type::OffsetBot) {
+  if (offset == Type::OffsetBot && field_offset == Type::OffsetBot) {
     // Check only oop fields.
     if (!adr_type->isa_aryptr() ||
         (adr_type->isa_aryptr()->klass() == NULL) ||
@@ -2086,7 +2082,14 @@
         // Ignore first AddP.
       } else {
         const Type* elemtype = adr_type->isa_aryptr()->elem();
-        bt = elemtype->array_element_basic_type();
+        if (elemtype->isa_valuetype()) {
+          assert(field_offset != Type::OffsetBot, "invalid field offset");
+          ciValueKlass* vk = elemtype->is_valuetype()->value_klass();
+          field_offset += vk->first_field_offset();
+          bt = vk->get_field_by_offset(field_offset, false)->layout_type();
+        } else {
+          bt = elemtype->array_element_basic_type();
+        }
       }
     } else if (adr_type->isa_rawptr() || adr_type->isa_klassptr()) {
       // Allocation initialization, ThreadLocal field access, unsafe access
@@ -2411,7 +2414,7 @@
   if (tinst->isa_aryptr() && t->isa_aryptr()) {
     // In the case of a flattened value type array, each field has its
     // own slice so we need to keep track of the field being accessed.
-    tinst = tinst->is_aryptr()->with_field_offset(t->is_aryptr()->field_offset());
+    tinst = tinst->is_aryptr()->with_field_offset(t->is_aryptr()->field_offset().get());
   }
 
   // Do NOT remove the next line: ensure a new alias index is allocated
--- a/src/share/vm/opto/graphKit.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/opto/graphKit.cpp	Wed May 17 09:52:46 2017 +0200
@@ -1737,6 +1737,10 @@
           call->init_req(idx, arg);
           idx++;
         }
+        // If a value type argument is passed as fields, attach the Method* to the call site
+        // to be able to access the extended signature later via attached_method_before_pc().
+        // For example, see CompiledMethod::preserve_callee_argument_oops().
+        call->set_override_symbolic_info(true);
       } else {
         call->init_req(idx, arg);
         idx++;
--- a/src/share/vm/opto/output.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/opto/output.cpp	Wed May 17 09:52:46 2017 +0200
@@ -632,6 +632,7 @@
     array->append(new ConstantOopWriteValue(NULL));
     break;
   case Type::AryPtr:
+  case Type::ValueTypePtr:
   case Type::InstPtr:          // fall through
     array->append(new ConstantOopWriteValue(t->isa_oopptr()->const_oop()->constant_encoding()));
     break;
--- a/src/share/vm/opto/type.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/opto/type.cpp	Wed May 17 09:52:46 2017 +0200
@@ -3089,7 +3089,7 @@
 const TypeOopPtr *TypeOopPtr::BOTTOM;
 
 //------------------------------TypeOopPtr-------------------------------------
-TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, Offset offset,
+TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, Offset offset, Offset field_offset,
                        int instance_id, const TypePtr* speculative, int inline_depth)
   : TypePtr(t, ptr, offset, speculative, inline_depth),
     _const_oop(o), _klass(k),
@@ -3110,9 +3110,19 @@
       // Array with unknown body type
       assert(this->isa_aryptr(), "only arrays without klass");
       _is_ptr_to_narrowoop = UseCompressedOops;
-    } else if (this->isa_aryptr()) {
-      _is_ptr_to_narrowoop = (UseCompressedOops && klass()->is_obj_array_klass() &&
-                              this->offset() != arrayOopDesc::length_offset_in_bytes());
+    } else if (UseCompressedOops && this->isa_aryptr() && this->offset() != arrayOopDesc::length_offset_in_bytes()) {
+      if (klass()->is_obj_array_klass()) {
+        _is_ptr_to_narrowoop = true;
+      } else if (klass()->is_value_array_klass() && field_offset != Offset::top && field_offset != Offset::bottom) {
+        // Check if the field of the value type array element contains oops
+        ciValueKlass* vk = klass()->as_value_array_klass()->element_klass()->as_value_klass();
+        int foffset = field_offset.get() + vk->first_field_offset();
+        ciField* field = vk->get_field_by_offset(foffset, false);
+        assert(field != NULL, "missing field");
+        BasicType bt = field->layout_type();
+        assert(bt != T_VALUETYPE, "should be flattened");
+        _is_ptr_to_narrowoop = (bt == T_OBJECT || bt == T_ARRAY);
+      }
     } else if (klass()->is_instance_klass()) {
       ciInstanceKlass* ik = klass()->as_instance_klass();
       ciField* field = NULL;
@@ -3169,7 +3179,7 @@
   ciKlass*  k = Compile::current()->env()->Object_klass();
   bool      xk = false;
   ciObject* o = NULL;
-  return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, xk, o, offset, instance_id, speculative, inline_depth))->hashcons();
+  return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, xk, o, offset, Offset::bottom, instance_id, speculative, inline_depth))->hashcons();
 }
 
 
@@ -3285,7 +3295,7 @@
 const Type *TypeOopPtr::xdual() const {
   assert(klass() == Compile::current()->env()->Object_klass(), "no klasses here");
   assert(const_oop() == NULL,             "no constants here");
-  return new TypeOopPtr(_base, dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance_id(), dual_speculative(), dual_inline_depth());
+  return new TypeOopPtr(_base, dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), Offset::bottom, dual_instance_id(), dual_speculative(), dual_inline_depth());
 }
 
 //--------------------------make_from_klass_common-----------------------------
@@ -3620,7 +3630,7 @@
 //------------------------------TypeInstPtr-------------------------------------
 TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, bool xk, ciObject* o, Offset off,
                          int instance_id, const TypePtr* speculative, int inline_depth)
-  : TypeOopPtr(InstPtr, ptr, k, xk, o, off, instance_id, speculative, inline_depth),
+  : TypeOopPtr(InstPtr, ptr, k, xk, o, off, Offset::bottom, instance_id, speculative, inline_depth),
     _name(k->name()) {
    assert(k != NULL &&
           (k->is_loaded() || o == NULL),
@@ -3804,7 +3814,7 @@
       // below the centerline when the superclass is exact. We need to
       // do the same here.
       if (klass()->equals(ciEnv::current()->Object_klass()) && !klass_is_exact()) {
-        return TypeAryPtr::make(ptr, tp->ary(), tp->klass(), tp->klass_is_exact(), offset, tp->_field_offset, instance_id, speculative, depth);
+        return TypeAryPtr::make(ptr, tp->ary(), tp->klass(), tp->klass_is_exact(), offset, tp->field_offset(), instance_id, speculative, depth);
       } else {
         // cannot subclass, so the meet has to fall badly below the centerline
         ptr = NotNull;
@@ -3824,7 +3834,7 @@
         if (klass()->equals(ciEnv::current()->Object_klass()) && !klass_is_exact()) {
           // that is, tp's array type is a subtype of my klass
           return TypeAryPtr::make(ptr, (ptr == Constant ? tp->const_oop() : NULL),
-                                  tp->ary(), tp->klass(), tp->klass_is_exact(), offset, tp->_field_offset, instance_id, speculative, depth);
+                                  tp->ary(), tp->klass(), tp->klass_is_exact(), offset, tp->field_offset(), instance_id, speculative, depth);
         }
       }
       // The other case cannot happen, since I cannot be a subtype of an array.
@@ -4347,7 +4357,7 @@
 //------------------------------hash-------------------------------------------
 // Type-specific hashing function.
 int TypeAryPtr::hash(void) const {
-  return (intptr_t)_ary + TypeOopPtr::hash() + field_offset();
+  return (intptr_t)_ary + TypeOopPtr::hash() + _field_offset.get();
 }
 
 //------------------------------meet-------------------------------------------
@@ -4533,7 +4543,7 @@
         // cannot subclass, so the meet has to fall badly below the centerline
         ptr = NotNull;
         instance_id = InstanceBot;
-        return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), false, NULL,offset, instance_id, speculative, depth);
+        return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id, speculative, depth);
       }
     case Constant:
     case NotNull:
@@ -4567,11 +4577,11 @@
 //------------------------------xdual------------------------------------------
 // Dual: compute field-by-field dual
 const Type *TypeAryPtr::xdual() const {
-  return new TypeAryPtr(dual_ptr(), _const_oop, _ary->dual()->is_ary(),_klass, _klass_is_exact, dual_offset(), dual_field_offset(), dual_instance_id(), is_autobox_cache(), dual_speculative(), dual_inline_depth());
-}
-
-Type::Offset TypeAryPtr::meet_field_offset(int field_offset) const {
-  return _field_offset.meet(Offset(field_offset));
+  return new TypeAryPtr(dual_ptr(), _const_oop, _ary->dual()->is_ary(), _klass, _klass_is_exact, dual_offset(), dual_field_offset(), dual_instance_id(), is_autobox_cache(), dual_speculative(), dual_inline_depth());
+}
+
+Type::Offset TypeAryPtr::meet_field_offset(const Type::Offset offset) const {
+  return _field_offset.meet(offset);
 }
 
 //------------------------------dual_offset------------------------------------
--- a/src/share/vm/opto/type.hpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/opto/type.hpp	Wed May 17 09:52:46 2017 +0200
@@ -1001,8 +1001,8 @@
 // Some kind of oop (Java pointer), either instance or array.
 class TypeOopPtr : public TypePtr {
 protected:
-  TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, Offset offset, int instance_id,
-             const TypePtr* speculative, int inline_depth);
+  TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, Offset offset, Offset field_offset,
+             int instance_id, const TypePtr* speculative, int inline_depth);
 public:
   virtual bool eq( const Type *t ) const;
   virtual int  hash() const;             // Type specific hashing
@@ -1193,10 +1193,10 @@
 //------------------------------TypeAryPtr-------------------------------------
 // Class of Java array pointers
 class TypeAryPtr : public TypeOopPtr {
-  TypeAryPtr( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk,
-              Offset offset, Offset field_offset, int instance_id, bool is_autobox_cache,
-              const TypePtr* speculative, int inline_depth)
-    : TypeOopPtr(AryPtr,ptr,k,xk,o,offset, instance_id, speculative, inline_depth),
+  TypeAryPtr(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk,
+             Offset offset, Offset field_offset, int instance_id, bool is_autobox_cache,
+             const TypePtr* speculative, int inline_depth)
+    : TypeOopPtr(AryPtr, ptr, k, xk, o, offset, field_offset, instance_id, speculative, inline_depth),
     _ary(ary),
     _is_autobox_cache(is_autobox_cache),
     _field_offset(field_offset)
@@ -1222,16 +1222,16 @@
   virtual int hash() const;     // Type specific hashing
   const TypeAry *_ary;          // Array we point into
   const bool     _is_autobox_cache;
-  Offset meet_field_offset(int offset) const;
+  // For flattened value type arrays, each field of the value type in
+  // the array has its own memory slice so we need to keep track of
+  // which field is accessed
+  const Offset _field_offset;
+  Offset meet_field_offset(const Type::Offset offset) const;
   Offset dual_field_offset() const;
 
   ciKlass* compute_klass(DEBUG_ONLY(bool verify = false)) const;
 
 public:
-  // For flattened value type arrays, each field of the value type in
-  // the array has its own memory slice so we need to keep track of
-  // which field is accessed
-  const Offset   _field_offset;
   // Accessors
   ciKlass* klass() const;
   const TypeAry* ary() const  { return _ary; }
@@ -1280,7 +1280,7 @@
 
   const TypeAryPtr* cast_to_autobox_cache(bool cache) const;
 
-  const int field_offset() const { return _field_offset.get(); }
+  const Offset field_offset() const { return _field_offset; }
   const TypeAryPtr* with_field_offset(int offset) const;
   const TypePtr* with_field_offset_and_offset(intptr_t offset) const;
 
@@ -1315,7 +1315,7 @@
 // Class of value type pointers
 class TypeValueTypePtr : public TypeOopPtr {
   TypeValueTypePtr(const TypeValueType* vt, PTR ptr, ciObject* o, Offset offset, int instance_id, const TypePtr* speculative, int inline_depth)
-    : TypeOopPtr(ValueTypePtr, ptr, vt->value_klass(), true, o, offset, instance_id, speculative, inline_depth) {
+    : TypeOopPtr(ValueTypePtr, ptr, vt->value_klass(), true, o, offset, Offset::bottom, instance_id, speculative, inline_depth) {
     _vt = vt;
   }
 
--- a/src/share/vm/opto/valuetypenode.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/opto/valuetypenode.cpp	Wed May 17 09:52:46 2017 +0200
@@ -43,10 +43,10 @@
   for (uint i = 0; i < vt->field_count(); ++i) {
     ciType* field_type = vt->field_type(i);
     Node* value = NULL;
-    if (field_type->is_primitive_type()) {
+    if (field_type->is_valuetype()) {
+      value = ValueTypeNode::make_default(gvn, field_type->as_value_klass());
+    } else {
       value = gvn.zerocon(field_type->basic_type());
-    } else {
-      value = ValueTypeNode::make_default(gvn, field_type->as_value_klass());
     }
     vt->set_field_value(i, value);
   }
@@ -110,7 +110,8 @@
           adr_type = gvn.C->alias_type(field)->adr_type();
         }
         Node* adr = gvn.transform(new AddPNode(base, ptr, gvn.MakeConX(offset)));
-        value = LoadNode::make(gvn, NULL, mem, adr, adr_type, Type::get_const_type(ftype), ftype->basic_type(), MemNode::unordered);
+        BasicType bt = type2field[ftype->basic_type()];
+        value = LoadNode::make(gvn, NULL, mem, adr, adr_type, Type::get_const_type(ftype), bt, MemNode::unordered);
       }
     }
     set_field_value(i, gvn.transform(value));
@@ -143,7 +144,16 @@
         adr_type = kit->C->alias_type(field)->adr_type();
       }
       Node* adr = kit->basic_plus_adr(base, ptr, offset);
-      kit->store_to_memory(kit->control(), adr, value, field_type(i)->basic_type(), adr_type, MemNode::unordered);
+      BasicType bt = type2field[field_type(i)->basic_type()];
+      if (is_java_primitive(bt)) {
+        kit->store_to_memory(kit->control(), adr, value, bt, adr_type, MemNode::unordered);
+      } else {
+        const TypeOopPtr* ft = TypeOopPtr::make_from_klass(field_type(i)->as_klass());
+        assert(adr->bottom_type()->is_ptr_to_narrowoop() == UseCompressedOops, "inconsistent");
+        bool is_array = base_type->isa_aryptr() != NULL;
+        kit->store_oop(kit->control(), base, adr, adr_type, value, ft, bt, is_array, MemNode::unordered);
+      }
+
     }
   }
 }
@@ -170,10 +180,7 @@
 
   // Oop is non-NULL, use it
   region->init_req(1, kit->control());
-  // Fixme if we cast oop to not null we fail if the control path is not folded
-  // castnode.cpp:69: #  assert(ft == Type::TOP) failed: special case #3
-  //oop   ->init_req(1, not_null_oop);
-  oop   ->init_req(1, in_oop);
+  oop   ->init_req(1, not_null_oop);
   io    ->init_req(1, kit->i_o());
   mem   ->init_req(1, kit->merged_memory());
 
@@ -183,9 +190,6 @@
   ciValueKlass* vk = value_klass();
   Node* klass_node = kit->makecon(TypeKlassPtr::make(vk));
   Node* alloc_oop  = kit->new_instance(klass_node);
-  AllocateNode* alloc = AllocateNode::Ideal_allocation(alloc_oop, &kit->gvn());
-  // TODO enable/fix this
-  // alloc->initialization()->set_complete_with_arraycopy();
   // Write field values to memory
   store_values(kit, alloc_oop, alloc_oop, vk);
   region->init_req(2, kit->control());
--- a/src/share/vm/runtime/frame.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/runtime/frame.cpp	Wed May 17 09:52:46 2017 +0200
@@ -1006,13 +1006,14 @@
   virtual void handle_oop_offset() {
     // Extract low order register number from register array.
     // In LP64-land, the high-order bits are valid but unhelpful.
+    assert(_offset < _arg_size, "out of bounds");
     VMReg reg = _regs[_offset].first();
     oop *loc = _fr.oopmapreg_to_location(reg, _reg_map);
     _f->do_oop(loc);
   }
 
  public:
-  CompiledArgumentOopFinder(Symbol* signature, bool has_receiver, bool has_appendix, OopClosure* f, frame fr,  const RegisterMap* reg_map)
+  CompiledArgumentOopFinder(Symbol* signature, bool has_receiver, bool has_appendix, OopClosure* f, frame fr, const RegisterMap* reg_map)
     : SignatureInfo(signature) {
 
     // initialize CompiledArgumentOopFinder
@@ -1022,11 +1023,7 @@
     _has_appendix = has_appendix;
     _fr        = fr;
     _reg_map   = (RegisterMap*)reg_map;
-    _arg_size  = ArgumentSizeComputer(signature).size() + (has_receiver ? 1 : 0) + (has_appendix ? 1 : 0);
-
-    int arg_size;
-    _regs = SharedRuntime::find_callee_arguments(signature, has_receiver, has_appendix, &arg_size);
-    assert(arg_size == _arg_size, "wrong arg size");
+    _regs = SharedRuntime::find_callee_arguments(signature, has_receiver, has_appendix, &_arg_size);
   }
 
   void oops_do() {
--- a/src/share/vm/runtime/globals.hpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/runtime/globals.hpp	Wed May 17 09:52:46 2017 +0200
@@ -4038,10 +4038,6 @@
   experimental(bool, AlwaysAtomicAccesses, false,                           \
           "Accesses to all variables should always be atomic")              \
                                                                             \
-  develop_pd(bool, ValueTypePassFieldsAsArgs,                               \
-             "Pass each field as an argument at calls")                     \
-                                                                            \
-                                                                            \
   product(bool, EnableTracing, false,                                       \
           "Enable event-based tracing")                                     \
                                                                             \
@@ -4084,10 +4080,16 @@
              "Limit on number of compiler directives.")                     \
                                                                             \
   product(bool, EnableValhalla, false,                                      \
-             "Enable experimental Valhalla features")                       \
+          "Enable experimental Valhalla features")                          \
                                                                             \
   product(bool, EnableMVT, false,                                           \
-             "Enable experimental Minimal Value Types")                     \
+          "Enable experimental Minimal Value Types")                        \
+                                                                            \
+  develop_pd(bool, ValueTypePassFieldsAsArgs,                               \
+             "Pass each value type field as an argument at calls")          \
+                                                                            \
+  develop(bool, FullGCALotWithValueTypes, false,                            \
+          "Force full GCs to stress test handling of value types")          \
 
 
 /*
--- a/src/share/vm/runtime/sharedRuntime.cpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/runtime/sharedRuntime.cpp	Wed May 17 09:52:46 2017 +0200
@@ -1108,6 +1108,7 @@
 
   Bytecode_invoke bytecode(caller, bci);
   int bytecode_index = bytecode.index();
+  bc = bytecode.invoke_code();
 
   methodHandle attached_method = extract_attached_method(vfst);
   if (attached_method.not_null()) {
@@ -1133,9 +1134,9 @@
           }
           break;
       }
+    } else {
+      assert(ValueTypePassFieldsAsArgs, "invalid use of attached methods");
     }
-  } else {
-    bc = bytecode.invoke_code();
   }
 
   bool has_receiver = bc != Bytecodes::_invokestatic &&
@@ -1162,8 +1163,9 @@
       // If the receiver is a value type that is passed as fields, no oop is available.
       // Resolve the call without receiver null checking.
       assert(bc == Bytecodes::_invokevirtual, "only allowed with invokevirtual");
+      assert(!attached_method.is_null(), "must have attached method");
       constantPoolHandle constants(THREAD, caller->constants());
-      LinkInfo link_info(constants, bytecode_index, CHECK_NH);
+      LinkInfo link_info(attached_method->method_holder(), attached_method->name(), attached_method->signature());
       LinkResolver::resolve_virtual_call(callinfo, receiver, NULL, link_info, /*check_null_or_abstract*/ false, CHECK_NH);
       return receiver; // is null
     } else {
@@ -1372,6 +1374,10 @@
     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();
+      if (FullGCALotWithValueTypes) {
+        // Trigger a full GC to verify that the GC knows about the contents of oop fields
+        Universe::heap()->collect(GCCause::_full_gc_alot);
+      }
     } else {
       assert(receiver.not_null() || invoke_code == Bytecodes::_invokehandle, "sanity check");
       receiver_klass = invoke_code == Bytecodes::_invokehandle ? NULL : receiver->klass();
@@ -2456,9 +2462,9 @@
     : 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_unverified_entry, Symbol* sig_extended) {
     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_unverified_entry, sig_extended);
     if (DumpSharedSpaces) {
       ((CDSAdapterHandlerEntry*)entry)->init();
     }
@@ -2607,8 +2613,9 @@
 AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint,
                                                       address i2c_entry,
                                                       address c2i_entry,
-                                                      address c2i_unverified_entry) {
-  return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+                                                      address c2i_unverified_entry,
+                                                      Symbol* sig_extended) {
+  return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, sig_extended);
 }
 
 // Value type arguments are not passed by reference, instead each
@@ -3353,3 +3360,26 @@
   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(array->is_oop(), "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();
+        oopDesc::bs()->write_ref_array((HeapWord*) doop_address, map->count());
+        map++;
+      }
+    }
+  }
+}
+JRT_END
--- a/src/share/vm/runtime/sharedRuntime.hpp	Wed May 17 09:01:40 2017 +0200
+++ b/src/share/vm/runtime/sharedRuntime.hpp	Wed May 17 09:52:46 2017 +0200
@@ -554,6 +554,7 @@
   static address handle_wrong_method_abstract(JavaThread* thread);
   static address handle_wrong_method_ic_miss(JavaThread* thread);
   static void allocate_value_types(JavaThread* thread, Method* callee);
+  static void apply_post_barriers(JavaThread* thread, objArrayOopDesc* array);
 
   static address handle_unsafe_access(JavaThread* thread, address next_pc);
 
@@ -672,6 +673,7 @@
   address _i2c_entry;
   address _c2i_entry;
   address _c2i_unverified_entry;
+  Symbol* _sig_extended;
 
 #ifdef ASSERT
   // Captures code and signature used to generate this adapter when
@@ -680,11 +682,12 @@
   int            _saved_code_length;
 #endif
 
-  void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry) {
+  void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry, Symbol* sig_extended) {
     _fingerprint = fingerprint;
     _i2c_entry = i2c_entry;
     _c2i_entry = c2i_entry;
     _c2i_unverified_entry = c2i_unverified_entry;
+    _sig_extended = sig_extended;
 #ifdef ASSERT
     _saved_code = NULL;
     _saved_code_length = 0;
@@ -700,6 +703,7 @@
   address get_i2c_entry()            const { return _i2c_entry; }
   address get_c2i_entry()            const { return _c2i_entry; }
   address get_c2i_unverified_entry() const { return _c2i_unverified_entry; }
+  Symbol* get_sig_extended()         const { return _sig_extended; }
   address base_address();
   void relocate(address new_base);
 
@@ -745,7 +749,8 @@
  public:
 
   static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint,
-                                        address i2c_entry, address c2i_entry, address c2i_unverified_entry);
+                                        address i2c_entry, address c2i_entry, address c2i_unverified_entry,
+                                        Symbol* sig_extended = NULL);
   static void create_native_wrapper(const methodHandle& method);
   static AdapterHandlerEntry* get_adapter(const methodHandle& method);
 
--- a/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java	Wed May 17 09:01:40 2017 +0200
+++ b/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java	Wed May 17 09:52:46 2017 +0200
@@ -28,13 +28,13 @@
  * @library /testlibrary /test/lib /compiler/whitebox /
  * @requires os.simpleArch == "x64"
  * @modules java.base/jdk.internal.misc:+open
- * @modules java.base/jvm.internal.value
  * @compile -XDenableValueTypes ValueCapableClass1.java ValueCapableClass2.java ValueTypeTestBench.java
  * @run main ClassFileInstaller sun.hotspot.WhiteBox
  * @run main ClassFileInstaller jdk.test.lib.Platform
  * @run main/othervm -Xbootclasspath/a:. -ea -noverify -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
  *                   -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-TieredCompilation -XX:+VerifyAdapterSharing
  *                   -XX:+EnableValhalla -XX:+EnableMVT -XX:+ValueTypePassFieldsAsArgs -XX:+ValueArrayFlatten
+ *                   -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 -XX:+FullGCALotWithValueTypes
  *                   compiler.valhalla.valuetypes.ValueTypeTestBench
  * @run main/othervm -Xbootclasspath/a:. -ea -noverify -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
  *                   -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-TieredCompilation
@@ -43,6 +43,7 @@
  * @run main/othervm -Xbootclasspath/a:. -ea -noverify -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
  *                   -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-TieredCompilation -XX:+AlwaysIncrementalInline
  *                   -XX:+EnableValhalla -XX:+EnableMVT -XX:+ValueTypePassFieldsAsArgs -XX:+ValueArrayFlatten
+ *                   -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1
  *                   compiler.valhalla.valuetypes.ValueTypeTestBench
  */
 
@@ -78,16 +79,20 @@
     final int x;
     final long y;
     final short z;
+    final Integer o;
+    final int[] oa;
     final MyValue2 v1;
     final MyValue2 v2;
     static final MyValue2 v3 = MyValue2.createWithFieldsInline(ValueTypeTestBench.rI, true);
     final int c;
 
-    private MyValue1(int x, long y, short z, MyValue2 v1, MyValue2 v2, int c) {
+    private MyValue1(int x, long y, short z, Integer o, int[] oa, MyValue2 v1, MyValue2 v2, int c) {
         s = x;
         this.x = x;
         this.y = y;
         this.z = z;
+        this.o = o;
+        this.oa = oa;
         this.v1 = v1;
         this.v2 = v2;
         this.c = c;
@@ -98,6 +103,8 @@
         this.x = 0;
         this.y = 0;
         this.z = 0;
+        this.o = null;
+        this.oa = null;
         this.v1 = MyValue2.createDefaultInline();
         this.v2 = MyValue2.createDefaultInline();
         this.c = 0;
@@ -119,6 +126,9 @@
         v = setX(v, x);
         v = setY(v, y);
         v = setZ(v, (short)x);
+        v = setO(v, new Integer(x));
+        int[] oa = {x};
+        v = setOA(v, oa);
         v = setV1(v, MyValue2.createWithFieldsInline(x, x < y));
         v = setV2(v, MyValue2.createWithFieldsInline(x, x > y));
         v = setC(v, ValueTypeTestBench.rI);
@@ -131,25 +141,34 @@
         v = setX(v, x);
         v = setY(v, y);
         v = setZ(v, (short)x);
+        v = setO(v, new Integer(x));
+        int[] oa = {x};
+        v = setOA(v, oa);
         v = setV1(v, MyValue2.createWithFieldsInline(x, x < y));
         v = setV2(v, MyValue2.createWithFieldsInline(x, x > y));
         v = setC(v, ValueTypeTestBench.rI);
         return v;
     }
 
+    // Hash only primitive and value type fields to avoid NullPointerException
+    @ForceInline
+    public long hashPrimitive() {
+        return s + sf + x + y + z + c + v1.hash() + v2.hash() + v3.hash();
+    }
+
     @ForceInline
     public long hash() {
-        return s + sf + x + y + z + c + v1.hash() + v2.hash() + v3.hash();
+        return hashPrimitive() + o + oa[0];
     }
 
     @DontCompile
     public long hashInterpreted() {
-        return s + sf + x + y + z + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted();
+        return s + sf + x + y + z + o + oa[0] + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted();
     }
 
     @ForceInline
     public void print() {
-        System.out.print("s=" + s + ", sf=" + sf + ", x=" + x + ", y=" + y + ", z=" + z + ", v1[");
+        System.out.print("s=" + s + ", sf=" + sf + ", x=" + x + ", y=" + y + ", z=" + z + ", o=" + (o != null ? (Integer)o : "NULL") + ", v1[");
         v1.print();
         System.out.print("], v2[");
         v2.print();
@@ -177,6 +196,18 @@
     }
 
     @ForceInline
+    __ValueFactory static MyValue1 setO(MyValue1 v, Integer o) {
+        v.o = o;
+        return v;
+    }
+
+    @ForceInline
+    __ValueFactory static MyValue1 setOA(MyValue1 v, int[] oa) {
+        v.oa = oa;
+        return v;
+    }
+
+    @ForceInline
     __ValueFactory static MyValue1 setC(MyValue1 v, int c) {
         v.c = c;
         return v;
@@ -478,7 +509,7 @@
     }
 
     // Merge value types created in a loop (inlined)
-    @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
+    @Test(failOn = ALLOC + LOAD + STORE + TRAP)
     public long test11(int x, long y) {
         MyValue1 v = MyValue1.createWithFieldsInline(x, y);
         for (int i = 0; i < 10; ++i) {
@@ -657,7 +688,7 @@
     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {2}, failOn = LOAD)
     public long test20(boolean deopt) {
         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
-        MyValue1[] va = new MyValue1[3];
+        MyValue2[] va = new MyValue2[3];
         if (deopt) {
             // uncommon trap
             WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20"));
@@ -668,7 +699,7 @@
 
     @DontCompile
     public void test20_verifier(boolean warmup) {
-        MyValue1[] va = new MyValue1[42];
+        MyValue2[] va = new MyValue2[42];
         long result = test20(!warmup);
         Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash());
     }
@@ -1161,6 +1192,7 @@
         }
     }
 
+    // Merge value type arrays created from two branches
     @Test(failOn = (TRAP))
     public MyValue1[] test45(boolean b) {
         MyValue1[] va;
@@ -1212,7 +1244,7 @@
     public void test46_verifier(boolean warmup) {
         MyValue1[] va = new MyValue1[1];
         MyValue1 v = test46();
-        Asserts.assertEQ(v.hash(), va[0].hash());
+        Asserts.assertEQ(v.hashPrimitive(), va[0].hashPrimitive());
     }
 
     // Test default initialization of value type arrays
@@ -1227,7 +1259,7 @@
         MyValue1[] va = new MyValue1[len];
         MyValue1[] var = test47(len);
         for (int i = 0; i < len; ++i) {
-            Asserts.assertEQ(va[i].hash(), var[i].hash());
+            Asserts.assertEQ(va[i].hashPrimitive(), var[i].hashPrimitive());
         }
     }
 
@@ -1277,13 +1309,13 @@
     public long test51() {
         MyValue1 v1 = MyValue1.createDefaultInline();
         MyValue1 v2 = MyValue1.createDefaultDontInline();
-        return v1.hash() + v2.hash();
+        return v1.hashPrimitive() + v2.hashPrimitive();
     }
 
     @DontCompile
     public void test51_verifier(boolean warmup) {
         long result = test51();
-        Asserts.assertEQ(result, 2 * MyValue1.createDefaultInline().hash());
+        Asserts.assertEQ(result, 2 * MyValue1.createDefaultInline().hashPrimitive());
     }
 
     // test vwithfield
@@ -1435,8 +1467,7 @@
     }
 
     // When calling a method on __Value, passing fields as arguments is impossible
-    @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount={1, 0})
-    @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = STORE + LOAD)
+    @Test(failOn = ALLOC + STORE + LOAD)
     public String test59(MyValue1 v) {
         return v.toString();
     }
@@ -1491,16 +1522,16 @@
 
     public static MethodHandle generateNullVCCUnboxLoadLongMH() {
         return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
-                                            "nullvccUnboxLoadLong",
-                                            MethodType.methodType(long.class),
-                                            CODE -> {
-                                                CODE.
-                                                    aconst_null().
-                                                    vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
-                                                    getfield(ValueCapableClass1.class, "t", "J").
-                                                    lreturn();
-                                            }
-                                            );
+                "nullvccUnboxLoadLong",
+                MethodType.methodType(long.class),
+                CODE -> {
+                    CODE.
+                    aconst_null().
+                    vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
+                    getfield(ValueCapableClass1.class, "t", "J").
+                    lreturn();
+                }
+                );
     }
 
     /* The compiler is supposed to determine that the allocated
@@ -1521,16 +1552,16 @@
 
     public static MethodHandle generateCheckedVCCUnboxLoadLongMH() {
         return MethodHandleBuilder.loadCode(MethodHandles.lookup(),
-                                            "checkedVCCUnboxLoadLongMH",
-                                            MethodType.methodType(long.class),
-                                            CODE -> {
-                                                CODE.
-                                                    invokestatic(ValueCapableClass1.class, "createInline", "()Lcompiler/valhalla/valuetypes/ValueCapableClass1;", false).
-                                                    vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
-                                                    vgetfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J").
-                                                    lreturn();
-                                            }
-                                            );
+                "checkedVCCUnboxLoadLongMH",
+                MethodType.methodType(long.class),
+                CODE -> {
+                    CODE.
+                    invokestatic(ValueCapableClass1.class, "createInline", "()Lcompiler/valhalla/valuetypes/ValueCapableClass1;", false).
+                    vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()).
+                    vgetfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J").
+                    lreturn();
+                }
+                );
     }
 
     /* The compiler is supposed to emit a runtime null check because
@@ -1643,7 +1674,7 @@
                     vbox(ValueCapableClass1.class).
                     getfield(ValueCapableClass1.class, "t", "J").
                     lreturn();
-               }
+                }
                 );
     }
 
@@ -1672,14 +1703,14 @@
     private static final String START = "(\\d+\\t(.*";
     private static final String MID = ".*)+\\t===.*";
     private static final String END = ")|";
-    private static final String ALLOC  = START + "CallStaticJava" + MID + "_new_instance_Java" + END;
-    private static final String ALLOCA = START + "CallStaticJava" + MID + "_new_array_Java" + END;
+    private static final String ALLOC  = "(.*precise klass compiler/valhalla/valuetypes/MyValue.*\\R(.*nop.*\\R)*.*_new_instance_Java" + END;
+    private static final String ALLOCA = "(.*precise klass \\[Qcompiler/valhalla/valuetypes/MyValue.*\\R(.*nop.*\\R)*.*_new_array_Java" + END;
     private static final String LOAD   = START + "Load(B|S|I|L|F|D)" + MID + "valuetype\\*" + END;
     private static final String LOADP  = START + "Load(P|N)" + MID + "valuetype\\*" + END;
     private static final String STORE  = START + "Store(B|S|I|L|F|D)" + MID + "valuetype\\*" + END;
     private static final String STOREP = START + "Store(P|N)" + MID + "valuetype\\*" + END;
     private static final String LOOP   = START + "Loop" + MID + "" + END;
-    private static final String TRAP   = START + "CallStaticJava" + MID + "uncommon_trap" + END;
+    private static final String TRAP   = START + "CallStaticJava" + MID + "uncommon_trap.*(unstable_if|predicate)" + END;
     private static final String RETURN = START + "Return" + MID + "returns" + END;
     private static final String LINKTOSTATIC = START + "CallStaticJava" + MID + "linkToStatic" + END;
     private static final String NPE = START + "CallStaticJava" + MID + "null_check" + END;
@@ -1723,12 +1754,12 @@
         //tests.values().removeIf(p -> !p.getName().equals("test64")); // Run single test
         if (args.length == 0) {
             execute_vm("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-BackgroundCompilation",
-                       "-XX:+PrintCompilation", "-XX:+PrintInlining", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly",
-                       "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*",
-                       "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*",
-                       "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*",
-                       "-XX:CompileCommand=compileonly,java.lang.Object::<init>",
-                       "-XX:CompileCommand=inline,java.lang.__Value::hashCode");
+                    "-XX:+PrintCompilation", "-XX:+PrintInlining", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly",
+                    "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*",
+                    "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*",
+                    "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*",
+                    "-XX:CompileCommand=compileonly,java.lang.Object::<init>",
+                    "-XX:CompileCommand=inline,java.lang.__Value::hashCode");
         } else {
             // Execute tests
             ValueTypeTestBench bench = new ValueTypeTestBench();