changeset 53186:29b97940a7fb lworld

8206139: [lworld] Re-enable calling convention optimizations Reviewed-by: roland
author thartmann
date Tue, 18 Dec 2018 16:05:31 +0100
parents 127ed6c19242
children a39dfab56ea5
files src/hotspot/cpu/x86/frame_x86.cpp src/hotspot/cpu/x86/frame_x86.hpp src/hotspot/cpu/x86/globals_x86.hpp src/hotspot/cpu/x86/interp_masm_x86.cpp src/hotspot/cpu/x86/interp_masm_x86.hpp src/hotspot/cpu/x86/macroAssembler_x86.cpp src/hotspot/cpu/x86/macroAssembler_x86.hpp src/hotspot/cpu/x86/methodHandles_x86.cpp src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp src/hotspot/cpu/x86/templateTable_x86.cpp src/hotspot/cpu/x86/x86_32.ad src/hotspot/cpu/x86/x86_64.ad src/hotspot/share/aot/aotCompiledMethod.hpp src/hotspot/share/asm/codeBuffer.hpp src/hotspot/share/c1/c1_LIRAssembler.cpp src/hotspot/share/ci/ciEnv.cpp src/hotspot/share/classfile/classFileParser.cpp src/hotspot/share/code/codeBlob.cpp src/hotspot/share/code/codeBlob.hpp src/hotspot/share/code/compiledMethod.cpp src/hotspot/share/code/compiledMethod.hpp src/hotspot/share/code/nmethod.cpp src/hotspot/share/code/nmethod.hpp src/hotspot/share/compiler/compileBroker.cpp src/hotspot/share/jvmci/jvmciCodeInstaller.cpp src/hotspot/share/oops/method.cpp src/hotspot/share/oops/method.hpp src/hotspot/share/oops/valueKlass.cpp src/hotspot/share/oops/valueKlass.hpp src/hotspot/share/opto/callGenerator.cpp src/hotspot/share/opto/callnode.hpp src/hotspot/share/opto/chaitin.cpp src/hotspot/share/opto/compile.cpp src/hotspot/share/opto/compile.hpp src/hotspot/share/opto/connode.cpp src/hotspot/share/opto/escape.cpp src/hotspot/share/opto/graphKit.cpp src/hotspot/share/opto/machnode.hpp src/hotspot/share/opto/macro.cpp src/hotspot/share/opto/matcher.cpp src/hotspot/share/opto/memnode.cpp src/hotspot/share/opto/node.hpp src/hotspot/share/opto/output.cpp src/hotspot/share/opto/parse1.cpp src/hotspot/share/opto/type.cpp src/hotspot/share/opto/type.hpp src/hotspot/share/opto/valuetypenode.cpp src/hotspot/share/opto/valuetypenode.hpp src/hotspot/share/runtime/arguments.cpp src/hotspot/share/runtime/deoptimization.cpp src/hotspot/share/runtime/globals.hpp src/hotspot/share/runtime/safepoint.cpp src/hotspot/share/runtime/sharedRuntime.cpp src/hotspot/share/runtime/sharedRuntime.hpp src/hotspot/share/runtime/signature.cpp src/hotspot/share/runtime/signature.hpp src/hotspot/share/utilities/globalDefinitions.cpp src/hotspot/share/utilities/globalDefinitions.hpp test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMethodHandles.java test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnresolvedValueClass.java test/hotspot/jtreg/compiler/valhalla/valuetypes/ValueTypeTest.java
diffstat 64 files changed, 2040 insertions(+), 1002 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/cpu/x86/frame_x86.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/frame_x86.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -144,14 +144,17 @@
       if ((address)sender_sp >= thread->stack_base()) {
         return false;
       }
-      sender_unextended_sp = sender_sp;
       // On Intel the return_address is always the word on the stack
       sender_pc = (address) *(sender_sp-1);
       // Note: frame::sender_sp_offset is only valid for compiled frame
-      saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset);
+      intptr_t** saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset);
+      saved_fp = *saved_fp_addr;
+
+      // Repair the sender sp if this is a method with scalarized value type args
+      sender_sp = repair_sender_sp(sender_sp, saved_fp_addr);
+      sender_unextended_sp = sender_sp;
     }
 
-
     // If the potential sender is the interpreter then we can do some more checking
     if (Interpreter::contains(sender_pc)) {
 
@@ -454,7 +457,6 @@
   // frame owned by optimizing compiler
   assert(_cb->frame_size() >= 0, "must have non-zero frame size");
   intptr_t* sender_sp = unextended_sp() + _cb->frame_size();
-  intptr_t* unextended_sp = sender_sp;
 
   // On Intel the return_address is always the word on the stack
   address sender_pc = (address) *(sender_sp-1);
@@ -463,6 +465,9 @@
   // It is only an FP if the sender is an interpreter frame (or C1?).
   intptr_t** saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset);
 
+  // Repair the sender sp if this is a method with scalarized value type args
+  sender_sp = repair_sender_sp(sender_sp, saved_fp_addr);
+
   if (map->update_map()) {
     // Tell GC to use argument oopmaps for some runtime stubs that need it.
     // For C1, the runtime stub might not have oop maps, so set this flag
@@ -479,7 +484,7 @@
   }
 
   assert(sender_sp != sp(), "must have changed");
-  return frame(sender_sp, unextended_sp, *saved_fp_addr, sender_pc);
+  return frame(sender_sp, sender_sp, *saved_fp_addr, sender_pc);
 }
 
 
@@ -687,6 +692,22 @@
 void frame::pd_ps() {}
 #endif
 
+// Check for a method with scalarized value type arguments that needs
+// a stack repair and return the repaired sender stack pointer.
+intptr_t* frame::repair_sender_sp(intptr_t* sender_sp, intptr_t** saved_fp_addr) const {
+  CompiledMethod* cm = _cb->as_compiled_method_or_null();
+  if (cm != NULL && cm->method()->needs_stack_repair()) {
+    // The stack increment resides just below the saved rbp on the stack
+    // and does not account for the return address.
+    intptr_t* sp_inc_addr = (intptr_t*) (saved_fp_addr - 1);
+    int sp_inc = (*sp_inc_addr) / wordSize;
+    int real_frame_size = sp_inc + 1; // Add size of return address
+    assert(real_frame_size >= _cb->frame_size(), "invalid frame size");
+    sender_sp = unextended_sp() + real_frame_size;
+  }
+  return sender_sp;
+}
+
 void JavaFrameAnchor::make_walkable(JavaThread* thread) {
   // last frame set?
   if (last_Java_sp() == NULL) return;
--- a/src/hotspot/cpu/x86/frame_x86.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/frame_x86.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -123,6 +123,9 @@
     return (intptr_t*) addr_at(offset);
   }
 
+  // Support for scalarized value type calling convention
+  intptr_t* repair_sender_sp(intptr_t* sender_sp, intptr_t** saved_fp_addr) const;
+
 #ifdef ASSERT
   // Used in frame::sender_for_{interpreter,compiled}_frame
   static void verify_deopt_original_pc(CompiledMethod* nm, intptr_t* unextended_sp);
--- a/src/hotspot/cpu/x86/globals_x86.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/globals_x86.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -102,8 +102,8 @@
 define_pd_global(bool, ThreadLocalHandshakes, false);
 #endif
 
-define_pd_global(bool, ValueTypePassFieldsAsArgs, LP64_ONLY(false) NOT_LP64(false));
-define_pd_global(bool, ValueTypeReturnedAsFields, LP64_ONLY(false) NOT_LP64(false));
+define_pd_global(bool, ValueTypePassFieldsAsArgs, LP64_ONLY(true) NOT_LP64(false));
+define_pd_global(bool, ValueTypeReturnedAsFields, LP64_ONLY(true) NOT_LP64(false));
 
 #define ARCH_FLAGS(develop, \
                    product, \
--- a/src/hotspot/cpu/x86/interp_masm_x86.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -956,8 +956,7 @@
         Register ret_addr,
         bool throw_monitor_exception,
         bool install_monitor_exception,
-        bool notify_jvmdi,
-        bool load_values) {
+        bool notify_jvmdi) {
   // Note: Registers rdx xmm0 may be in use for the
   // result check if synchronized method
   Label unlocked, unlock, no_unlock;
@@ -975,7 +974,7 @@
   movbool(rbx, do_not_unlock_if_synchronized);
   movbool(do_not_unlock_if_synchronized, false); // reset the flag
 
- // get method access flags
+  // get method access flags
   movptr(rcx, Address(rbp, frame::interpreter_frame_method_offset * wordSize));
   movl(rcx, Address(rcx, Method::access_flags_offset()));
   testl(rcx, JVM_ACC_SYNCHRONIZED);
@@ -1128,26 +1127,32 @@
   movptr(rbx,
          Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize));
 
-  if (load_values) {
+  if (state == atos && ValueTypeReturnedAsFields) {
+    Label skip;
+    // Test if the return type is a value type
+    movptr(rdi, Address(rbp, frame::interpreter_frame_method_offset * wordSize));
+    movptr(rdi, Address(rdi, Method::const_offset()));
+    load_unsigned_byte(rdi, Address(rdi, ConstMethod::result_type_offset()));
+    cmpl(rdi, T_VALUETYPE);
+    jcc(Assembler::notEqual, skip);
+
     // We are returning a value type, load its fields into registers
 #ifndef _LP64
     super_call_VM_leaf(StubRoutines::load_value_type_fields_in_regs());
 #else
+    // Load fields from a buffered value with a value class specific handler
     load_klass(rdi, rax);
+    movptr(rdi, Address(rdi, InstanceKlass::adr_valueklass_fixed_block_offset()));
     movptr(rdi, Address(rdi, ValueKlass::unpack_handler_offset()));
 
-    Label skip;
     testptr(rdi, rdi);
     jcc(Assembler::equal, skip);
 
-    // Load fields from a buffered value with a value class specific
-    // handler
     call(rdi);
-
-    bind(skip);
 #endif
     // call above kills the value in rbx. Reload it.
     movptr(rbx, Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize));
+    bind(skip);
   }
   leave();                           // remove frame anchor
   pop(ret_addr);                     // get return address
--- a/src/hotspot/cpu/x86/interp_masm_x86.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -215,8 +215,7 @@
   void remove_activation(TosState state, Register ret_addr,
                          bool throw_monitor_exception = true,
                          bool install_monitor_exception = true,
-                         bool notify_jvmdi = true,
-                         bool load_values = false);
+                         bool notify_jvmdi = true);
   void get_method_counters(Register method, Register mcs, Label& skip);
 
   // Object locking
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -47,6 +47,7 @@
 #include "runtime/stubRoutines.hpp"
 #include "runtime/thread.hpp"
 #include "utilities/macros.hpp"
+#include "vmreg_x86.inline.hpp"
 #include "crc32c.h"
 #ifdef COMPILER2
 #include "opto/intrinsicnode.hpp"
@@ -5494,7 +5495,12 @@
 #endif // _LP64
 
 // C2 compiled method's prolog code.
-void MacroAssembler::verified_entry(int framesize, int stack_bang_size, bool fp_mode_24b, bool is_stub) {
+void MacroAssembler::verified_entry(Compile* C, int sp_inc) {
+  int framesize = C->frame_size_in_bytes();
+  int bangsize = C->bang_size_in_bytes();
+  bool fp_mode_24b = C->in_24_bit_fp_mode();
+  int stack_bang_size = C->need_stack_bang(bangsize) ? bangsize : 0;
+  bool is_stub = C->stub_function() != NULL;
 
   // WARNING: Initial instruction MUST be 5 bytes or longer so that
   // NativeJump::patch_verified_entry will be able to patch out the entry
@@ -5547,6 +5553,12 @@
     }
   }
 
+  if (C->needs_stack_repair()) {
+    // Save stack increment (also account for fixed framesize and rbp)
+    assert((sp_inc & (StackAlignmentInBytes-1)) == 0, "stack increment not aligned");
+    movptr(Address(rsp, C->sp_inc_offset()), sp_inc + framesize + wordSize);
+  }
+
   if (VerifyStackAtCalls) { // Majik cookie to verify stack depth
     framesize -= wordSize;
     movptr(Address(rsp, framesize), (int32_t)0xbadb100d);
@@ -5636,6 +5648,271 @@
   BIND(L_end);
 }
 
+// Move a value between registers/stack slots and update the reg_state
+bool MacroAssembler::move_helper(VMReg from, VMReg to, BasicType bt, RegState reg_state[]) {
+  if (reg_state[to->value()] == reg_written) {
+    return true; // Already written
+  }
+  if (from != to && bt != T_VOID) {
+    if (reg_state[to->value()] == reg_readonly) {
+      return false; // Not yet writable
+    }
+    if (from->is_reg()) {
+      if (to->is_reg()) {
+        if (from->is_XMMRegister()) {
+          if (bt == T_DOUBLE) {
+            movdbl(to->as_XMMRegister(), from->as_XMMRegister());
+          } else {
+            assert(bt == T_FLOAT, "must be float");
+            movflt(to->as_XMMRegister(), from->as_XMMRegister());
+          }
+        } else {
+          movq(to->as_Register(), from->as_Register());
+        }
+      } else {
+        Address to_addr = Address(rsp, to->reg2stack() * VMRegImpl::stack_slot_size + wordSize);
+        if (from->is_XMMRegister()) {
+          if (bt == T_DOUBLE) {
+            movdbl(to_addr, from->as_XMMRegister());
+          } else {
+            assert(bt == T_FLOAT, "must be float");
+            movflt(to_addr, from->as_XMMRegister());
+          }
+        } else {
+          movq(to_addr, from->as_Register());
+        }
+      }
+    } else {
+      Address from_addr = Address(rsp, from->reg2stack() * VMRegImpl::stack_slot_size + wordSize);
+      if (to->is_reg()) {
+        if (to->is_XMMRegister()) {
+          if (bt == T_DOUBLE) {
+            movdbl(to->as_XMMRegister(), from_addr);
+          } else {
+            assert(bt == T_FLOAT, "must be float");
+            movflt(to->as_XMMRegister(), from_addr);
+          }
+        } else {
+          movq(to->as_Register(), from_addr);
+        }
+      } else {
+        movq(r13, from_addr);
+        movq(Address(rsp, to->reg2stack() * VMRegImpl::stack_slot_size + wordSize), r13);
+      }
+    }
+  }
+  // Update register states
+  reg_state[from->value()] = reg_writable;
+  reg_state[to->value()] = reg_written;
+  return true;
+}
+
+// Read all fields from a value type oop and store the values in registers/stack slots
+bool MacroAssembler::unpack_value_helper(const GrowableArray<SigEntry>* sig, int& sig_index, VMReg from, VMRegPair* regs_to, int& to_index, RegState reg_state[]) {
+  Register fromReg = from->is_reg() ? from->as_Register() : noreg;
+
+  int vt = 1;
+  bool done = true;
+  bool mark_done = true;
+  do {
+    sig_index--;
+    BasicType bt = sig->at(sig_index)._bt;
+    if (bt == T_VALUETYPE) {
+      vt--;
+    } else if (bt == T_VOID &&
+               sig->at(sig_index-1)._bt != T_LONG &&
+               sig->at(sig_index-1)._bt != T_DOUBLE) {
+      vt++;
+    } else if (SigEntry::is_reserved_entry(sig, sig_index)) {
+      to_index--; // Ignore this
+    } else {
+      assert(to_index >= 0, "invalid to_index");
+      VMRegPair pair_to = regs_to[to_index--];
+      VMReg r_1 = pair_to.first();
+
+      if (bt == T_VOID) continue;
+
+      int idx = (int)r_1->value();
+      assert(idx >= 0 && idx <= 1000, "out of bounds");
+      if (reg_state[idx] == reg_readonly) {
+         if (idx != from->value()) {
+           mark_done = false;
+         }
+         done = false;
+         continue;
+      } else if (reg_state[idx] == reg_written) {
+        continue;
+      } else {
+        assert(reg_state[idx] == reg_writable, "must be writable");
+        reg_state[idx] = reg_written;
+       }
+
+      if (fromReg == noreg) {
+        int st_off = from->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+        movq(r10, Address(rsp, st_off));
+        fromReg = r10;
+      }
+
+      int off = sig->at(sig_index)._offset;
+      assert(off > 0, "offset in object should be positive");
+      bool is_oop = (bt == T_OBJECT || bt == T_ARRAY);
+
+      Address fromAddr = Address(fromReg, off);
+      bool is_signed = (bt != T_CHAR) && (bt != T_BOOLEAN);
+      if (!r_1->is_XMMRegister()) {
+        Register dst = r_1->is_stack() ? r13 : r_1->as_Register();
+        if (is_oop) {
+          load_heap_oop(dst, fromAddr);
+        } else {
+          load_sized_value(dst, fromAddr, type2aelembytes(bt), is_signed);
+        }
+        if (r_1->is_stack()) {
+          int st_off = r_1->reg2stack() * VMRegImpl::stack_slot_size + wordSize;
+          movq(Address(rsp, st_off), dst);
+        }
+      } else {
+        if (bt == T_DOUBLE) {
+          movdbl(r_1->as_XMMRegister(), fromAddr);
+        } else {
+          assert(bt == T_FLOAT, "must be float");
+          movflt(r_1->as_XMMRegister(), fromAddr);
+        }
+      }
+    }
+  } while (vt != 0);
+  if (mark_done && reg_state[from->value()] != reg_written) {
+    // This is okay because no one else will write to that slot
+    reg_state[from->value()] = reg_writable;
+  }
+  return done;
+}
+
+// Unpack all value type arguments passed as oops
+void MacroAssembler::unpack_value_args(Compile* C) {
+  assert(C->has_scalarized_args(), "value type argument scalarization is disabled");
+  Method* method = C->method()->get_Method();
+  const GrowableArray<SigEntry>* sig_cc = method->adapter()->get_sig_cc();
+  assert(sig_cc != NULL, "must have scalarized signature");
+
+  // Get unscalarized calling convention
+  BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sig_cc->length());
+  int args_passed = 0;
+  if (!method->is_static()) {
+    sig_bt[args_passed++] = T_OBJECT;
+  }
+  for (SignatureStream ss(method->signature()); !ss.at_return_type(); ss.next()) {
+    BasicType bt = ss.type();
+    if (bt == T_VALUETYPE) {
+      bt = T_OBJECT;
+    }
+    sig_bt[args_passed++] = bt;
+    if (type2size[bt] == 2) {
+      sig_bt[args_passed++] = T_VOID;
+    }
+  }
+  VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, args_passed);
+  int args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, args_passed, false);
+
+  // Get scalarized calling convention
+  int args_passed_cc = SigEntry::fill_sig_bt(sig_cc, sig_bt);
+  VMRegPair* regs_cc = NEW_RESOURCE_ARRAY(VMRegPair, sig_cc->length());
+  int args_on_stack_cc = SharedRuntime::java_calling_convention(sig_bt, regs_cc, args_passed_cc, false);
+
+  // Check if we need to extend the stack for unpacking
+  int sp_inc = (args_on_stack_cc - args_on_stack) * VMRegImpl::stack_slot_size;
+  if (sp_inc > 0) {
+    // Save the return address, adjust the stack (make sure it is properly
+    // 16-byte aligned) and copy the return address to the new top of the stack.
+    pop(r13);
+    sp_inc = align_up(sp_inc, StackAlignmentInBytes);
+    subptr(rsp, sp_inc);
+    push(r13);
+  } else {
+    // The scalarized calling convention needs less stack space than the unscalarized one.
+    // No need to extend the stack, the caller will take care of these adjustments.
+    sp_inc = 0;
+  }
+
+  // Initialize register/stack slot states (make all writable)
+  int max_stack = MAX2(args_on_stack + sp_inc/VMRegImpl::stack_slot_size, args_on_stack_cc);
+  int max_reg = VMRegImpl::stack2reg(max_stack)->value();
+  RegState* reg_state = NEW_RESOURCE_ARRAY(RegState, max_reg);
+  for (int i = 0; i < max_reg; ++i) {
+    reg_state[i] = reg_writable;
+  }
+  // Set all source registers/stack slots to readonly to prevent accidental overwriting
+  for (int i = 0; i < args_passed; ++i) {
+    VMReg reg = regs[i].first();
+    if (!reg->is_valid()) continue;
+    if (reg->is_stack()) {
+      // Update source stack location by adding stack increment
+      reg = VMRegImpl::stack2reg(reg->reg2stack() + sp_inc/VMRegImpl::stack_slot_size);
+      regs[i] = reg;
+    }
+    assert(reg->value() >= 0 && reg->value() < max_reg, "reg value out of bounds");
+    reg_state[reg->value()] = reg_readonly;
+  }
+
+  // Emit code for unpacking value type arguments
+  // We try multiple times and eventually start spilling to resolve (circular) dependencies
+  bool done = false;
+  for (int i = 0; i < 2*args_passed_cc && !done; ++i) {
+    done = true;
+    bool spill = (i > args_passed_cc); // Start spilling?
+    // Iterate over all arguments (in reverse)
+    for (int from_index = args_passed-1, to_index = args_passed_cc-1, sig_index = sig_cc->length()-1; sig_index >= 0; sig_index--) {
+      if (SigEntry::is_reserved_entry(sig_cc, sig_index)) {
+        to_index--; // Skipp reserved entry
+      } else {
+        assert(from_index >= 0, "index out of bounds");
+        VMReg reg = regs[from_index--].first();
+        if (spill && reg->is_valid() && reg_state[reg->value()] == reg_readonly) {
+          // Spill argument to be able to write the source and resolve circular dependencies
+          VMReg spill_reg = reg->is_XMMRegister() ? xmm8->as_VMReg() : r14->as_VMReg();
+          bool res = move_helper(reg, spill_reg, T_DOUBLE, reg_state);
+          assert(res, "Spilling should not fail");
+          // Set spill_reg as new source and update state
+          reg = spill_reg;
+          regs[from_index+1].set1(reg);
+          reg_state[reg->value()] = reg_readonly;
+          spill = false; // Do not spill again in this round
+        }
+        if (SigEntry::skip_value_delimiters(sig_cc, sig_index)) {
+          BasicType bt = sig_cc->at(sig_index)._bt;
+          assert(to_index >= 0, "index out of bounds");
+          done &= move_helper(reg, regs_cc[to_index].first(), bt, reg_state);
+          to_index--;
+        } else {
+          done &= unpack_value_helper(sig_cc, sig_index, reg, regs_cc, to_index, reg_state);
+        }
+      }
+    }
+  }
+  guarantee(done, "Could not resolve circular dependency when unpacking value type arguments");
+
+  // Emit code for verified entry and save increment for stack repair on return
+  verified_entry(C, sp_inc);
+}
+
+// Restores the stack on return
+void MacroAssembler::restore_stack(Compile* C) {
+  int framesize = C->frame_size_in_bytes();
+  assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned");
+  // Remove word for return addr already pushed and RBP
+  framesize -= 2*wordSize;
+
+  if (C->needs_stack_repair()) {
+    // Restore rbp and repair rsp by adding the stack increment
+    movq(rbp, Address(rsp, framesize));
+    addq(rsp, Address(rsp, C->sp_inc_offset()));
+  } else {
+    if (framesize > 0) {
+      addq(rsp, framesize);
+    }
+    pop(rbp);
+  }
+}
+
 void MacroAssembler::clear_mem(Register base, Register cnt, Register val, XMMRegister xtmp, bool is_large, bool word_copy_only) {
   // cnt - number of qwords (8-byte words).
   // base - start address, qword aligned.
--- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -28,6 +28,7 @@
 #include "asm/assembler.hpp"
 #include "utilities/macros.hpp"
 #include "runtime/rtmLocking.hpp"
+#include "runtime/signature.hpp"
 
 // MacroAssembler extends Assembler by frequently used macros.
 //
@@ -1596,7 +1597,19 @@
   void movl2ptr(Register dst, Register src) { LP64_ONLY(movslq(dst, src)) NOT_LP64(if (dst != src) movl(dst, src)); }
 
   // C2 compiled method's prolog code.
-  void verified_entry(int framesize, int stack_bang_size, bool fp_mode_24b, bool is_stub);
+  void verified_entry(Compile* C, int sp_inc = 0);
+
+  enum RegState {
+    reg_readonly,
+    reg_writable,
+    reg_written
+  };
+
+  // Unpack all value type arguments passed as oops
+  void unpack_value_args(Compile* C);
+  bool move_helper(VMReg from, VMReg to, BasicType bt, RegState reg_state[]);
+  bool unpack_value_helper(const GrowableArray<SigEntry>* sig, int& sig_index, VMReg from, VMRegPair* regs_to, int& to_index, RegState reg_state[]);
+  void restore_stack(Compile* C);
 
   // clear memory of size 'cnt' qwords, starting at 'base';
   // if 'is_large' is set, do not try to produce short loop
--- a/src/hotspot/cpu/x86/methodHandles_x86.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/methodHandles_x86.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -147,7 +147,11 @@
     __ BIND(run_compiled_code);
   }
 
-  const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_offset() :
+  // The following jump might pass a value type argument that was erased to Object as oop to a
+  // callee that expects value type arguments to be passed as fields. We need to call the compiled
+  // value entry (_code->value_entry_point() or _adapter->c2i_value_entry()) which will take care
+  // of translating between the calling conventions.
+  const ByteSize entry_offset = for_compiler_entry ? Method::from_compiled_value_offset() :
                                                      Method::from_interpreted_offset();
   __ jmp(Address(method, entry_offset));
 
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -492,8 +492,6 @@
     case T_OBJECT:
     case T_ARRAY:
     case T_ADDRESS:
-    case T_VALUETYPE:
-    case T_VALUETYPEPTR:
       if (int_args < Argument::n_int_register_parameters_j) {
         regs[i].set2(INT_ArgReg[int_args++]->as_VMReg());
       } else {
@@ -576,7 +574,6 @@
     case T_ARRAY:
     case T_ADDRESS:
     case T_METADATA:
-    case T_VALUETYPEPTR:
       if (int_args < Argument::n_int_register_parameters_j+1) {
         regs[i].set2(INT_ArgReg[int_args]->as_VMReg());
         int_args++;
@@ -656,12 +653,14 @@
 // the value type. This utility function computes the number of
 // arguments for the call if value types are passed by reference (the
 // calling convention the interpreter expects).
-static int compute_total_args_passed_int(const GrowableArray<SigEntry>& sig_extended) {
+static int compute_total_args_passed_int(const GrowableArray<SigEntry>* sig_extended) {
   int total_args_passed = 0;
   if (ValueTypePassFieldsAsArgs) {
-    for (int i = 0; i < sig_extended.length(); i++) {
-      BasicType bt = sig_extended.at(i)._bt;
-      if (bt == T_VALUETYPE) {
+    for (int i = 0; i < sig_extended->length(); i++) {
+      BasicType bt = sig_extended->at(i)._bt;
+      if (SigEntry::is_reserved_entry(sig_extended, i)) {
+        // Ignore reserved entry
+      } else if (bt == T_VALUETYPE) {
         // In sig_extended, a value type argument starts with:
         // T_VALUETYPE, followed by the types of the fields of the
         // value type and T_VOID to mark the end of the value
@@ -675,8 +674,8 @@
         int vt = 1;
         do {
           i++;
-          BasicType bt = sig_extended.at(i)._bt;
-          BasicType prev_bt = sig_extended.at(i-1)._bt;
+          BasicType bt = sig_extended->at(i)._bt;
+          BasicType prev_bt = sig_extended->at(i-1)._bt;
           if (bt == T_VALUETYPE) {
             vt++;
           } else if (bt == T_VOID &&
@@ -690,7 +689,7 @@
       }
     }
   } else {
-    total_args_passed = sig_extended.length();
+    total_args_passed = sig_extended->length();
   }
   return total_args_passed;
 }
@@ -742,7 +741,14 @@
       val = r_1->as_Register();
     }
     if (is_oop) {
-      __ store_heap_oop(to, val);
+      // We don't need barriers because the destination is a newly allocated object.
+      // Also, we cannot use store_heap_oop(to, val) because it uses r8 as tmp.
+      if (UseCompressedOops) {
+        __ encode_heap_oop(val);
+        __ movl(to, val);
+      } else {
+        __ movptr(to, val);
+      }
     } else {
       __ store_sized_value(to, val, size_in_bytes);
     }
@@ -756,7 +762,7 @@
 }
 
 static void gen_c2i_adapter(MacroAssembler *masm,
-                            const GrowableArray<SigEntry>& sig_extended,
+                            const GrowableArray<SigEntry>* sig_extended,
                             const VMRegPair *regs,
                             Label& skip_fixup,
                             address start,
@@ -775,8 +781,8 @@
   bool has_value_argument = false;
   if (ValueTypePassFieldsAsArgs) {
     // 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);
+    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
@@ -853,17 +859,20 @@
   // 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");
-    assert(next_arg_int < total_args_passed, "more arguments for the interpreter than expected?");
-    BasicType bt = sig_extended.at(next_arg_comp)._bt;
+       next_arg_comp < sig_extended->length(); next_arg_comp++) {
+    assert(ignored <= next_arg_comp, "shouldn't skip over more slots than there are arguments");
+    assert(next_arg_int <= total_args_passed, "more arguments for the interpreter than expected?");
+    BasicType bt = sig_extended->at(next_arg_comp)._bt;
     int st_off = (total_args_passed - next_arg_int) * Interpreter::stackElementSize;
     if (!ValueTypePassFieldsAsArgs || bt != T_VALUETYPE) {
+      if (SigEntry::is_reserved_entry(sig_extended, next_arg_comp)) {
+        continue; // Ignore reserved entry
+      }
       int next_off = st_off - Interpreter::stackElementSize;
       const int offset = (bt == T_LONG || bt == T_DOUBLE) ? next_off : st_off;
       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,
+      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, false);
       next_arg_int++;
 #ifdef ASSERT
@@ -888,8 +897,8 @@
       // sig_extended contains a field offset in the buffer.
       do {
         next_arg_comp++;
-        BasicType bt = sig_extended.at(next_arg_comp)._bt;
-        BasicType prev_bt = sig_extended.at(next_arg_comp-1)._bt;
+        BasicType bt = sig_extended->at(next_arg_comp)._bt;
+        BasicType prev_bt = sig_extended->at(next_arg_comp-1)._bt;
         if (bt == T_VALUETYPE) {
           vt++;
           ignored++;
@@ -898,13 +907,15 @@
                    prev_bt != T_DOUBLE) {
           vt--;
           ignored++;
+        } else if (SigEntry::is_reserved_entry(sig_extended, next_arg_comp)) {
+          // Ignore reserved entry
         } else {
-          int off = sig_extended.at(next_arg_comp)._offset;
+          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_VALUETYPEPTR || bt == T_ARRAY);
+          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,
+          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, is_oop);
         }
       } while (vt != 0);
@@ -1001,7 +1012,7 @@
 
 void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm,
                                     int comp_args_on_stack,
-                                    const GrowableArray<SigEntry>& sig_extended,
+                                    const GrowableArray<SigEntry>* sig,
                                     const VMRegPair *regs) {
 
   // Note: r13 contains the senderSP on entry. We must preserve it since
@@ -1079,7 +1090,6 @@
     __ subptr(rsp, comp_words_on_stack * wordSize);
   }
 
-
   // Ensure compiled code always sees stack at proper alignment
   __ andptr(rsp, -16);
 
@@ -1093,7 +1103,13 @@
 
   // Will jump to the compiled code just as if compiled code was doing it.
   // Pre-load the register-jump target early, to schedule it better.
-  __ movptr(r11, Address(rbx, in_bytes(Method::from_compiled_offset())));
+  if (StressValueTypePassFieldsAsArgs) {
+    // For stress testing, don't unpack value types in the i2c adapter but
+    // call the value type entry point and let it take care of unpacking.
+    __ movptr(r11, Address(rbx, in_bytes(Method::from_compiled_value_offset())));
+  } else {
+    __ movptr(r11, Address(rbx, in_bytes(Method::from_compiled_offset())));
+  }
 
 #if INCLUDE_JVMCI
   if (EnableJVMCI || UseAOT) {
@@ -1107,7 +1123,7 @@
   }
 #endif // INCLUDE_JVMCI
 
-  int total_args_passed = compute_total_args_passed_int(sig_extended);
+  int total_args_passed = compute_total_args_passed_int(sig);
   // Now generate the shuffle code.  Pick up all register args and move the
   // rest through the floating point stack top.
 
@@ -1118,19 +1134,22 @@
   // to mark the end of the value type. ignored counts the number of
   // T_VALUETYPE/T_VOID. next_arg_int is the next argument from the
   // interpreter point of view (value types are passed by reference).
-  for (int next_arg_comp = 0, ignored = 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");
-    assert(next_arg_int < total_args_passed, "more arguments from the interpreter than expected?");
-    BasicType bt = sig_extended.at(next_arg_comp)._bt;
+  for (int next_arg_comp = 0, ignored = 0, next_arg_int = 0; next_arg_comp < sig->length(); next_arg_comp++) {
+    assert(ignored <= next_arg_comp, "shouldn't skip over more slots than there are arguments");
+    assert(next_arg_int <= total_args_passed, "more arguments from the interpreter than expected?");
+    BasicType bt = sig->at(next_arg_comp)._bt;
     int ld_off = (total_args_passed - next_arg_int)*Interpreter::stackElementSize;
     if (!ValueTypePassFieldsAsArgs || bt != T_VALUETYPE) {
       // Load in argument order going down.
       // Point to interpreter value (vs. tag)
+      if (SigEntry::is_reserved_entry(sig, next_arg_comp)) {
+        continue; // Ignore reserved entry
+      }
       int next_off = ld_off - Interpreter::stackElementSize;
       int offset = (bt == T_LONG || bt == T_DOUBLE) ? next_off : ld_off;
       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,
+      gen_i2c_adapter_helper(masm, bt, next_arg_comp > 0 ? sig->at(next_arg_comp-1)._bt : T_ILLEGAL,
                              size_in_bytes, reg_pair, Address(saved_sp, offset), false);
       next_arg_int++;
     } else {
@@ -1147,8 +1166,8 @@
       // field offset in the buffer.
       do {
         next_arg_comp++;
-        BasicType bt = sig_extended.at(next_arg_comp)._bt;
-        BasicType prev_bt = sig_extended.at(next_arg_comp-1)._bt;
+        BasicType bt = sig->at(next_arg_comp)._bt;
+        BasicType prev_bt = sig->at(next_arg_comp-1)._bt;
         if (bt == T_VALUETYPE) {
           vt++;
           ignored++;
@@ -1157,11 +1176,13 @@
                    prev_bt != T_DOUBLE) {
           vt--;
           ignored++;
+        } else if (SigEntry::is_reserved_entry(sig, next_arg_comp)) {
+          // Ignore reserved entry
         } else {
-          int off = sig_extended.at(next_arg_comp)._offset;
+          int off = sig->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_VALUETYPEPTR || bt == T_ARRAY);
+          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);
@@ -1190,13 +1211,22 @@
 // ---------------------------------------------------------------
 AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm,
                                                             int comp_args_on_stack,
-                                                            const GrowableArray<SigEntry>& sig_extended,
-                                                            const VMRegPair *regs,
+                                                            int comp_args_on_stack_cc,
+                                                            const GrowableArray<SigEntry>* sig,
+                                                            const VMRegPair* regs,
+                                                            const GrowableArray<SigEntry>* sig_cc,
+                                                            const VMRegPair* regs_cc,
                                                             AdapterFingerPrint* fingerprint,
                                                             AdapterBlob*& new_adapter) {
   address i2c_entry = __ pc();
 
-  gen_i2c_adapter(masm, comp_args_on_stack, sig_extended, regs);
+  if (StressValueTypePassFieldsAsArgs) {
+    // For stress testing, don't unpack value types in the i2c adapter but
+    // call the value type entry point and let it take care of unpacking.
+    gen_i2c_adapter(masm, comp_args_on_stack, sig, regs);
+  } else {
+    gen_i2c_adapter(masm, comp_args_on_stack_cc, sig_cc, regs_cc);
+  }
 
   // -------------------------------------------------------------------------
   // Generate a C2I adapter.  On entry we know rbx holds the Method* during calls
@@ -1232,54 +1262,28 @@
   }
 
   address c2i_entry = __ pc();
+  address c2i_value_entry = c2i_entry;
 
   OopMapSet* oop_maps = NULL;
   int frame_complete = CodeOffsets::frame_never_safe;
   int frame_size_in_words = 0;
-  gen_c2i_adapter(masm, sig_extended, regs, skip_fixup, i2c_entry, oop_maps, frame_complete, frame_size_in_words);
+  gen_c2i_adapter(masm, sig_cc, regs_cc, skip_fixup, i2c_entry, oop_maps, frame_complete, frame_size_in_words);
+
+  if (regs != regs_cc) {
+    // Non-scalarized c2i adapter
+    c2i_value_entry = __ pc();
+    Label unused;
+    gen_c2i_adapter(masm, sig, regs, unused, i2c_entry, oop_maps, frame_complete, frame_size_in_words);
+  }
 
   __ flush();
-  new_adapter = AdapterBlob::create(masm->code(), frame_complete, frame_size_in_words, oop_maps);
-
-  // If the method has value types arguments, save the extended signature as symbol in
-  // the AdapterHandlerEntry to be used for scalarization of value type arguments.
-  Symbol* extended_signature = NULL;
-  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) {
-      has_value_argument = true;
-    } else if (bt == T_VALUETYPEPTR) {
-      has_value_argument = true;
-      // non-flattened value type field
-      sig_str[idx++] = type2char(T_VALUETYPE);
-      sig_str[idx++] = ';';
-    } else if (bt == T_VOID) {
-      // Ignore
-    } else {
-      if (bt == T_ARRAY) {
-        bt = T_OBJECT; // We don't know the element type, treat as Object
-      }
-      sig_str[idx++] = type2char(bt);
-      if (bt == T_OBJECT) {
-        sig_str[idx++] = ';';
-      }
-    }
-  }
-  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);
+
+  // The c2i adapter might safepoint and trigger a GC. The caller must make sure that
+  // the GC knows about the location of oop argument locations passed to the c2i adapter.
+  bool caller_must_gc_arguments = (regs != regs_cc);
+  new_adapter = AdapterBlob::create(masm->code(), frame_complete, frame_size_in_words, oop_maps, caller_must_gc_arguments);
+
+  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_value_entry, c2i_unverified_entry);
 }
 
 int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
@@ -4348,8 +4352,7 @@
   buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs,
                                          sizeof(buffer_locs)/sizeof(relocInfo));
 
-  MacroAssembler _masm(&buffer);
-  MacroAssembler* masm = &_masm;
+  MacroAssembler* masm = new MacroAssembler(&buffer);
 
   const Array<SigEntry>* sig_vk = vk->extended_sig();
   const Array<VMRegPair>* regs = vk->return_regs();
@@ -4370,6 +4373,7 @@
       continue;
     }
     int off = sig_vk->at(i)._offset;
+    assert(off > 0, "offset in object should be positive");
     VMRegPair pair = regs->at(j);
     VMReg r_1 = pair.first();
     VMReg r_2 = pair.second();
@@ -4378,10 +4382,21 @@
       __ movflt(to, r_1->as_XMMRegister());
     } else if (bt == T_DOUBLE) {
       __ movdbl(to, r_1->as_XMMRegister());
-    } else if (bt == T_OBJECT || bt == T_VALUETYPEPTR || bt == T_ARRAY) {
-      __ store_heap_oop(to, r_1->as_Register());
+    } else if (bt == T_OBJECT || bt == T_ARRAY) {
+      Register val = r_1->as_Register();
+      assert_different_registers(rax, val);
+      // We don't need barriers because the destination is a newly allocated object.
+      // Also, we cannot use store_heap_oop(to, val) because it uses r8 as tmp.
+      if (UseCompressedOops) {
+        __ encode_heap_oop(val);
+        __ movl(to, val);
+      } else {
+        __ movptr(to, val);
+      }
+
     } else {
       assert(is_java_primitive(bt), "unexpected basic type");
+      assert_different_registers(rax, r_1->as_Register());
       size_t size_in_bytes = type2aelembytes(bt);
       __ store_sized_value(to, r_1->as_Register(), size_in_bytes);
     }
@@ -4407,6 +4422,7 @@
       continue;
     }
     int off = sig_vk->at(i)._offset;
+    assert(off > 0, "offset in object should be positive");
     VMRegPair pair = regs->at(j);
     VMReg r_1 = pair.first();
     VMReg r_2 = pair.second();
@@ -4415,10 +4431,12 @@
       __ movflt(r_1->as_XMMRegister(), from);
     } else if (bt == T_DOUBLE) {
       __ movdbl(r_1->as_XMMRegister(), from);
-    } else if (bt == T_OBJECT || bt == T_VALUETYPEPTR || bt == T_ARRAY) {
+    } else if (bt == T_OBJECT || bt == T_ARRAY) {
+      assert_different_registers(rax, r_1->as_Register());
       __ load_heap_oop(r_1->as_Register(), from);
     } else {
       assert(is_java_primitive(bt), "unexpected basic type");
+      assert_different_registers(rax, r_1->as_Register());
       size_t size_in_bytes = type2aelembytes(bt);
       __ load_sized_value(r_1->as_Register(), from, size_in_bytes, bt != T_CHAR && bt != T_BOOLEAN);
     }
--- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -206,11 +206,11 @@
   // and NULL it as marker that esp is now tos until next java call
   __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int32_t)NULL_WORD);
 
-  if (/*state == qtos*/ false && ValueTypeReturnedAsFields) {
+  if (state == atos && ValueTypeReturnedAsFields) {
 #ifndef _LP64
     __ super_call_VM_leaf(StubRoutines::store_value_type_fields_to_buf());
 #else
-    // A value type is being returned. If fields are in registers we
+    // A value type might be returned. If fields are in registers we
     // need to allocate a value type instance and initialize it with
     // the value of the fields.
     Label skip, slow_case;
@@ -230,14 +230,8 @@
       __ cmpptr(r14, Address(r15_thread, in_bytes(JavaThread::tlab_end_offset())));
       __ jcc(Assembler::above, slow_case);
       __ movptr(Address(r15_thread, in_bytes(JavaThread::tlab_top_offset())), r14);
+      __ movptr(Address(r13, oopDesc::mark_offset_in_bytes()), (intptr_t)markOopDesc::always_locked_prototype());
 
-      if (UseBiasedLocking) {
-        __ movptr(rax, Address(rbx, Klass::prototype_header_offset()));
-        __ movptr(Address(r13, oopDesc::mark_offset_in_bytes ()), rax);
-      } else {
-        __ movptr(Address(r13, oopDesc::mark_offset_in_bytes ()),
-                  (intptr_t)markOopDesc::prototype());
-      }
       __ xorl(rax, rax); // use zero reg to clear memory (shorter code)
       __ store_klass_gap(r13, rax);  // zero klass gap for compressed oops
       __ mov(rax, rbx);
@@ -245,7 +239,8 @@
 
       // We have our new buffered value, initialize its fields with a
       // value class specific handler
-      __ movptr(rbx, Address(rax, ValueKlass::pack_handler_offset()));
+      __ movptr(rbx, Address(rax, InstanceKlass::adr_valueklass_fixed_block_offset()));
+      __ movptr(rbx, Address(rbx, ValueKlass::pack_handler_offset()));
       __ mov(rax, r13);
       __ call(rbx);
       __ jmp(skip);
--- a/src/hotspot/cpu/x86/templateTable_x86.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/templateTable_x86.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -2774,7 +2774,7 @@
     __ narrow(rax);
   }
 
-  __ remove_activation(state, rbcp, true, true, true, /*state == qtos*/ false && ValueTypeReturnedAsFields);
+  __ remove_activation(state, rbcp, true, true, true);
 
   __ jmp(rbcp);
 }
--- a/src/hotspot/cpu/x86/x86_32.ad	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/x86_32.ad	Tue Dec 18 16:05:31 2018 +0100
@@ -616,10 +616,7 @@
   Compile* C = ra_->C;
   MacroAssembler _masm(&cbuf);
 
-  int framesize = C->frame_size_in_bytes();
-  int bangsize = C->bang_size_in_bytes();
-
-  __ verified_entry(framesize, C->need_stack_bang(bangsize)?bangsize:0, C->in_24_bit_fp_mode(), C->stub_function() != NULL);
+  __ verified_entry(C);
 
   C->set_frame_complete(cbuf.insts_size());
 
--- a/src/hotspot/cpu/x86/x86_64.ad	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/cpu/x86/x86_64.ad	Tue Dec 18 16:05:31 2018 +0100
@@ -907,10 +907,8 @@
   Compile* C = ra_->C;
   MacroAssembler _masm(&cbuf);
 
-  int framesize = C->frame_size_in_bytes();
-  int bangsize = C->bang_size_in_bytes();
-
-  __ verified_entry(framesize, C->need_stack_bang(bangsize)?bangsize:0, false, C->stub_function() != NULL);
+  __ verified_entry(C);
+  __ bind(*_verified_entry);
 
   C->set_frame_complete(cbuf.insts_size());
 
@@ -984,29 +982,8 @@
     __ vzeroupper();
   }
 
-  int framesize = C->frame_size_in_bytes();
-  assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned");
-  // Remove word for return adr already pushed
-  // and RBP
-  framesize -= 2*wordSize;
-
-  // Note that VerifyStackAtCalls' Majik cookie does not change the frame size popped here
-
-  if (framesize) {
-    emit_opcode(cbuf, Assembler::REX_W);
-    if (framesize < 0x80) {
-      emit_opcode(cbuf, 0x83); // addq rsp, #framesize
-      emit_rm(cbuf, 0x3, 0x00, RSP_enc);
-      emit_d8(cbuf, framesize);
-    } else {
-      emit_opcode(cbuf, 0x81); // addq rsp, #framesize
-      emit_rm(cbuf, 0x3, 0x00, RSP_enc);
-      emit_d32(cbuf, framesize);
-    }
-  }
-
-  // popq rbp
-  emit_opcode(cbuf, 0x58 | RBP_enc);
+  __ restore_stack(C);
+
 
   if (StackReservedPages > 0 && C->has_reserved_stack_access()) {
     __ reserved_stack_check();
@@ -1579,6 +1556,28 @@
 
 //=============================================================================
 #ifndef PRODUCT
+void MachVVEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const
+{
+  st->print_cr("MachVVEPNode");
+}
+#endif
+
+void MachVVEPNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const
+{
+  // Unpack all value type args passed as oop and then jump to
+  // the verified entry point (skipping the unverified entry).
+  MacroAssembler masm(&cbuf);
+  masm.unpack_value_args(ra_->C);
+  masm.jmp(*_verified_entry);
+}
+
+uint MachVVEPNode::size(PhaseRegAlloc* ra_) const
+{
+  return MachNode::size(ra_); // too many variables; just compute it the hard way
+}
+
+//=============================================================================
+#ifndef PRODUCT
 void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const
 {
   if (UseCompressedClassPointers) {
--- a/src/hotspot/share/aot/aotCompiledMethod.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/aot/aotCompiledMethod.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -194,6 +194,7 @@
 
   virtual int comp_level() const { return CompLevel_aot; }
   virtual address verified_entry_point() const { return _code + _meta->verified_entry_offset(); }
+  virtual address verified_value_entry_point() const { return NULL; }
   virtual void log_identity(xmlStream* stream) const;
   virtual void log_state_change() const;
   virtual bool make_entrant() NOT_TIERED({ ShouldNotReachHere(); return false; });
--- a/src/hotspot/share/asm/codeBuffer.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/asm/codeBuffer.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -42,6 +42,7 @@
 public:
   enum Entries { Entry,
                  Verified_Entry,
+                 Verified_Value_Entry,
                  Frame_Complete, // Offset in the code where the frame setup is (for forte stackwalks) is complete
                  OSR_Entry,
                  Exceptions,     // Offset where exception handler lives
@@ -62,6 +63,7 @@
   CodeOffsets() {
     _values[Entry         ] = 0;
     _values[Verified_Entry] = 0;
+    _values[Verified_Value_Entry] = -1;
     _values[Frame_Complete] = frame_never_safe;
     _values[OSR_Entry     ] = 0;
     _values[Exceptions    ] = -1;
--- a/src/hotspot/share/c1/c1_LIRAssembler.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/c1/c1_LIRAssembler.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -621,6 +621,7 @@
         check_icache();
       }
       offsets()->set_value(CodeOffsets::Verified_Entry, _masm->offset());
+      offsets()->set_value(CodeOffsets::Verified_Value_Entry, _masm->offset());
       _masm->verified_entry();
       build_frame();
       offsets()->set_value(CodeOffsets::Frame_Complete, _masm->offset());
--- a/src/hotspot/share/ci/ciEnv.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/ci/ciEnv.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -554,7 +554,7 @@
     klass_name = cpool->symbol_at(index);
   } else {
     // Check if it's resolved if it's not a symbol constant pool entry.
-    klass =  ConstantPool::klass_at_if_loaded(cpool, index);
+    klass = ConstantPool::klass_at_if_loaded(cpool, index);
     // Try to look it up by name.
     if (klass == NULL) {
       klass_name = cpool->klass_name_at(index);
--- a/src/hotspot/share/classfile/classFileParser.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/classfile/classFileParser.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -1500,8 +1500,7 @@
   BAD_ALLOCATION_TYPE, // T_NARROWOOP   = 17,
   BAD_ALLOCATION_TYPE, // T_METADATA    = 18,
   BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 19,
-  BAD_ALLOCATION_TYPE, // T_VALUETYPEPTR= 20,
-  BAD_ALLOCATION_TYPE, // T_CONFLICT    = 21,
+  BAD_ALLOCATION_TYPE, // T_CONFLICT    = 20,
   BAD_ALLOCATION_TYPE, // 0
   BAD_ALLOCATION_TYPE, // 1
   BAD_ALLOCATION_TYPE, // 2
@@ -1522,8 +1521,7 @@
   BAD_ALLOCATION_TYPE, // T_NARROWOOP   = 17,
   BAD_ALLOCATION_TYPE, // T_METADATA    = 18,
   BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 19,
-  BAD_ALLOCATION_TYPE, // T_VALUETYPEPTR= 20,
-  BAD_ALLOCATION_TYPE, // T_CONFLICT    = 21,
+  BAD_ALLOCATION_TYPE, // T_CONFLICT    = 20
 };
 
 static FieldAllocationType basic_type_to_atype(bool is_static, BasicType type, bool is_flattenable) {
@@ -6021,11 +6019,6 @@
                                              CHECK);
   }
 
-  if (is_value_type()) {
-    ValueKlass* vk = ValueKlass::cast(ik);
-    vk->initialize_calling_convention();
-  }
-
   // Add read edges to the unnamed modules of the bootstrap and app class loaders.
   if (changed_by_loadhook && !module_handle.is_null() && module_entry->is_named() &&
       !module_entry->has_default_read_edges()) {
@@ -6037,7 +6030,7 @@
 
   int nfields = ik->java_fields_count();
   if (ik->is_value()) nfields++;
-  for(int i = 0; i < nfields; i++) {
+  for (int i = 0; i < nfields; i++) {
     if (ik->field_access_flags(i) & JVM_ACC_FLATTENABLE) {
       Symbol* klass_name = ik->field_signature(i)->fundamental_name(CHECK);
       // Value classes must have been pre-loaded
@@ -6054,6 +6047,10 @@
     }
   }
 
+  if (is_value_type()) {
+    ValueKlass::cast(ik)->initialize_calling_convention(CHECK);
+  }
+
   // Update the loader_data graph.
   record_defined_class_dependencies(ik, CHECK);
 
--- a/src/hotspot/share/code/codeBlob.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/code/codeBlob.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -272,27 +272,27 @@
   MemoryService::track_code_cache_memory_usage();
 }
 
-BufferBlob::BufferBlob(const char* name, int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps)
-  : RuntimeBlob(name, cb, sizeof(BufferBlob), size, frame_complete, frame_size, oop_maps)
+BufferBlob::BufferBlob(const char* name, int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments)
+  : RuntimeBlob(name, cb, sizeof(BufferBlob), size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments)
 {}
 
 
 //----------------------------------------------------------------------------------------------------
 // Implementation of AdapterBlob
 
-AdapterBlob::AdapterBlob(int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps) :
-  BufferBlob("I2C/C2I adapters", size, cb, frame_complete, frame_size, oop_maps) {
+AdapterBlob::AdapterBlob(int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments) :
+  BufferBlob("I2C/C2I adapters", size, cb, frame_complete, frame_size, oop_maps, caller_must_gc_arguments) {
   CodeCache::commit(this);
 }
 
-AdapterBlob* AdapterBlob::create(CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps) {
+AdapterBlob* AdapterBlob::create(CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments) {
   ThreadInVMfromUnknown __tiv;  // get to VM state in case we block on CodeCache_lock
 
   AdapterBlob* blob = NULL;
   unsigned int size = CodeBlob::allocation_size(cb, sizeof(AdapterBlob));
   {
     MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
-    blob = new (size) AdapterBlob(size, cb, frame_complete, frame_size, oop_maps);
+    blob = new (size) AdapterBlob(size, cb, frame_complete, frame_size, oop_maps, caller_must_gc_arguments);
   }
   // Track memory usage statistic after releasing CodeCache_lock
   MemoryService::track_code_cache_memory_usage();
--- a/src/hotspot/share/code/codeBlob.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/code/codeBlob.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -394,7 +394,7 @@
   // Creation support
   BufferBlob(const char* name, int size);
   BufferBlob(const char* name, int size, CodeBuffer* cb);
-  BufferBlob(const char* name, int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps);
+  BufferBlob(const char* name, int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments = false);
 
   // This ordinary operator delete is needed even though not used, so the
   // below two-argument operator delete will be treated as a placement
@@ -427,14 +427,15 @@
 
 class AdapterBlob: public BufferBlob {
 private:
-  AdapterBlob(int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps);
+  AdapterBlob(int size, CodeBuffer* cb, int frame_complete, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments = false);
 
 public:
   // Creation
   static AdapterBlob* create(CodeBuffer* cb,
                              int frame_complete,
                              int frame_size,
-                             OopMapSet* oop_maps);
+                             OopMapSet* oop_maps,
+                             bool caller_must_gc_arguments = false);
 
   // Typing
   virtual bool is_adapter_blob() const { return true; }
--- a/src/hotspot/share/code/compiledMethod.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/code/compiledMethod.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -357,27 +357,12 @@
       has_receiver = !(callee->access_flags().is_static());
       has_appendix = false;
       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 && callee != NULL) {
-      // Get the extended signature from the callee's adapter through the attached method
-      Symbol* sig_ext = callee->adapter()->get_sig_extended();
-#ifdef ASSERT
-      // Check if receiver or one of the arguments is a value type
-      bool has_value_receiver = has_receiver && callee->method_holder()->is_value();
-      bool has_value_argument = has_value_receiver;
-      for (SignatureStream ss(signature); !has_value_argument && !ss.at_return_type(); ss.next()) {
-        if (ss.type() == T_VALUETYPE) {
-          has_value_argument = true;
-          break;
-        }
-      }
-      assert(has_value_argument == (sig_ext != NULL), "Signature is inconsistent");
-#endif
-      if (sig_ext != NULL) {
-        signature = sig_ext;
+      // If value types are passed as fields, use the extended signature
+      // which contains the types of all (oop) fields of the value type.
+      if (callee->has_scalarized_args()) {
+        const GrowableArray<SigEntry>* sig = callee->adapter()->get_sig_cc();
+        signature = SigEntry::create_symbol(sig);
         has_receiver = false; // The extended signature contains the receiver type
       }
     }
--- a/src/hotspot/share/code/compiledMethod.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/code/compiledMethod.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -212,6 +212,7 @@
   virtual int   compile_id() const = 0;
 
   virtual address verified_entry_point() const = 0;
+  virtual address verified_value_entry_point() const = 0;
   virtual void log_identity(xmlStream* log) const = 0;
   virtual void log_state_change() const = 0;
   virtual bool make_not_used() = 0;
--- a/src/hotspot/share/code/nmethod.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/code/nmethod.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -598,6 +598,7 @@
     _comp_level              = CompLevel_none;
     _entry_point             = code_begin()          + offsets->value(CodeOffsets::Entry);
     _verified_entry_point    = code_begin()          + offsets->value(CodeOffsets::Verified_Entry);
+    _verified_value_entry_point = _verified_entry_point;
     _osr_entry_point         = NULL;
     _exception_cache         = NULL;
     _pc_desc_container.reset_to(NULL);
@@ -758,6 +759,7 @@
     _nmethod_end_offset      = _nul_chk_table_offset + align_up(nul_chk_table->size_in_bytes(), oopSize);
     _entry_point             = code_begin()          + offsets->value(CodeOffsets::Entry);
     _verified_entry_point    = code_begin()          + offsets->value(CodeOffsets::Verified_Entry);
+    _verified_value_entry_point = code_begin()       + offsets->value(CodeOffsets::Verified_Value_Entry);
     _osr_entry_point         = code_begin()          + offsets->value(CodeOffsets::OSR_Entry);
     _exception_cache         = NULL;
 
@@ -2515,7 +2517,7 @@
 }
 
 void nmethod::print_nmethod_labels(outputStream* stream, address block_begin) const {
-  address low = entry_point();
+  address low = verified_value_entry_point() != NULL ? verified_value_entry_point() : entry_point();
   if (block_begin == low) {
     // Print method arguments before the method entry
     methodHandle m = method();
@@ -2531,18 +2533,15 @@
       VMRegPair* regs   = NEW_RESOURCE_ARRAY(VMRegPair, 256);
       Symbol* sig = m->signature();
       bool has_value_arg = false;
-      if (ValueTypePassFieldsAsArgs && m->adapter()->get_sig_extended() != NULL) {
+      if (m->has_scalarized_args()) {
         // Use extended signature if value type arguments are passed as fields
-        sig = m->adapter()->get_sig_extended();
+        sig = SigEntry::create_symbol(m->adapter()->get_sig_cc());
         has_value_arg = true;
       } else if (!m->is_static()) {
         sig_bt[sizeargs++] = T_OBJECT; // 'this'
       }
       for (SignatureStream ss(sig); !ss.at_return_type(); ss.next()) {
         BasicType t = ss.type();
-        if (!ValueTypePassFieldsAsArgs && t == T_VALUETYPE) {
-          t = T_VALUETYPEPTR; // Pass value types by reference
-        }
         sig_bt[sizeargs++] = t;
         if (type2size[t] == 2) {
           sig_bt[sizeargs++] = T_VOID;
@@ -2557,13 +2556,11 @@
       int sig_index = 0;
       int arg_index = ((m->is_static() || has_value_arg) ? 0 : -1);
       bool did_old_sp = false;
+      SigEntry res_entry = m->get_res_entry();
       for (SignatureStream ss(sig); !ss.at_return_type(); ) {
         bool at_this = (arg_index == -1);
         bool at_old_sp = false;
         BasicType t = (at_this ? T_OBJECT : ss.type());
-        if (!ValueTypePassFieldsAsArgs && t == T_VALUETYPE) {
-          t = T_VALUETYPEPTR; // Pass value types by reference
-        }
         assert(t == sig_bt[sig_index], "sigs in sync");
         if (at_this) {
           stream->print("  # this: ");
@@ -2602,6 +2599,9 @@
           if (!did_name)
             stream->print("%s", type2name(t));
         }
+        if (sig_index == res_entry._offset) {
+          stream->print(" [RESERVED] ");
+        }
         if (at_old_sp) {
           stream->print("  (%s of caller)", spname);
           did_old_sp = true;
@@ -2623,6 +2623,7 @@
 
   if (block_begin == entry_point())             stream->print_cr("[Entry Point]");
   if (block_begin == verified_entry_point())    stream->print_cr("[Verified Entry Point]");
+  if (block_begin == verified_value_entry_point()) stream->print_cr("[Verified Value Entry Point]");
   if (JVMCI_ONLY(_exception_offset >= 0 &&) block_begin == exception_begin())         stream->print_cr("[Exception Handler]");
   if (block_begin == stub_begin())              stream->print_cr("[Stub Code]");
   if (JVMCI_ONLY(_deopt_handler_begin != NULL &&) block_begin == deopt_handler_begin())     stream->print_cr("[Deopt Handler Code]");
--- a/src/hotspot/share/code/nmethod.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/code/nmethod.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -90,6 +90,7 @@
   // offsets for entry points
   address _entry_point;                      // entry point with class check
   address _verified_entry_point;             // entry point without class check
+  address _verified_value_entry_point;       // value type entry point without class check
   address _osr_entry_point;                  // entry point for on stack replacement
 
   // Offsets for different nmethod parts
@@ -317,6 +318,7 @@
   // entry points
   address entry_point() const                     { return _entry_point;             } // normal entry point
   address verified_entry_point() const            { return _verified_entry_point;    } // if klass is correct
+  address verified_value_entry_point() const      { return _verified_value_entry_point; } // pass value type args as oops
 
   // flag accessing and manipulation
   bool  is_not_installed() const                  { return _state == not_installed; }
--- a/src/hotspot/share/compiler/compileBroker.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/compiler/compileBroker.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -1188,18 +1188,6 @@
   assert(WhiteBoxAPI || TieredCompilation || comp_level == CompLevel_highest_tier, "only CompLevel_highest_tier must be used in non-tiered");
   // return quickly if possible
 
-  // Lambda forms with an Object in their signature can be passed a
-  // value type. If compiled as root of a compilation, C2 has no way
-  // to know a value type is passed.
-  if (ValueTypePassFieldsAsArgs && method->is_compiled_lambda_form()) {
-    ResourceMark rm;
-    for (SignatureStream ss(method->signature()); !ss.at_return_type(); ss.next()) {
-      if (ss.type() == T_VALUETYPE) {
-        return NULL;
-      }
-    }
-  }
-
   // lock, make sure that the compilation
   // isn't prohibited in a straightforward way.
   AbstractCompiler* comp = CompileBroker::compiler(comp_level);
--- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -1320,6 +1320,7 @@
         break;
       case VERIFIED_ENTRY:
         _offsets.set_value(CodeOffsets::Verified_Entry, pc_offset);
+        _offsets.set_value(CodeOffsets::Verified_Value_Entry, pc_offset);
         break;
       case OSR_ENTRY:
         _offsets.set_value(CodeOffsets::OSR_Entry, pc_offset);
--- a/src/hotspot/share/oops/method.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/oops/method.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -471,7 +471,6 @@
 // safepoint as it is called with references live on the stack at
 // locations the GC is unaware of.
 ValueKlass* Method::returned_value_type(Thread* thread) const {
-  assert(is_returning_vt(), "method return type should be value type");
   SignatureStream ss(signature());
   while (!ss.at_return_type()) {
     ss.next();
@@ -488,8 +487,16 @@
 }
 #endif
 
-bool Method::has_value_args() const {
-  return adapter()->get_sig_extended() != NULL;
+bool Method::has_scalarized_args() const {
+  return adapter() != NULL ? (adapter()->get_sig_cc() != NULL) : false;
+}
+
+bool Method::needs_stack_repair() const {
+  return adapter() != NULL ? (adapter()->get_res_entry()._offset != -1) : false;
+}
+
+SigEntry Method::get_res_entry() const {
+  return adapter() != NULL ? adapter()->get_res_entry() : SigEntry();
 }
 
 bool Method::is_empty_method() const {
@@ -941,7 +948,7 @@
     _from_compiled_value_entry = NULL;
   } else {
     _from_compiled_entry    = adapter()->get_c2i_entry();
-    _from_compiled_value_entry = adapter()->get_c2i_entry();
+    _from_compiled_value_entry = adapter()->get_c2i_value_entry();
   }
   OrderAccess::storestore();
   _from_interpreted_entry = _i2i_entry;
@@ -1104,7 +1111,7 @@
   // Adapters for compiled code are made eagerly here.  They are fairly
   // small (generally < 100 bytes) and quick to make (and cached and shared)
   // so making them eagerly shouldn't be too expensive.
-  AdapterHandlerEntry* adapter = AdapterHandlerLibrary::get_adapter(mh, CHECK_0);
+  AdapterHandlerEntry* adapter = AdapterHandlerLibrary::get_adapter(mh);
   if (adapter == NULL ) {
     if (!is_init_completed()) {
       // Don't throw exceptions during VM initialization because java.lang.* classes
@@ -1123,7 +1130,7 @@
   } else {
     mh->set_adapter_entry(adapter);
     mh->_from_compiled_entry = adapter->get_c2i_entry();
-    mh->_from_compiled_value_entry = adapter->get_c2i_entry();
+    mh->_from_compiled_value_entry = adapter->get_c2i_value_entry();
   }
   return adapter->get_c2i_entry();
 }
@@ -1192,7 +1199,7 @@
 
   OrderAccess::storestore();
   mh->_from_compiled_entry = code->verified_entry_point();
-  mh->_from_compiled_value_entry = code->verified_entry_point();
+  mh->_from_compiled_value_entry = code->verified_value_entry_point();
   OrderAccess::storestore();
   // Instantly compiled code can execute.
   if (!mh->is_method_handle_intrinsic())
--- a/src/hotspot/share/oops/method.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/oops/method.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -104,7 +104,7 @@
   // Entry point for calling from compiled code, to compiled code if it exists
   // or else the interpreter.
   volatile address _from_compiled_entry;        // Cache of: _code ? _code->entry_point() : _adapter->c2i_entry()
-  volatile address _from_compiled_value_entry;  // Cache of: _code ? _code->value_entry_point() : _adapter->c2i_entry()
+  volatile address _from_compiled_value_entry;  // Cache of: _code ? _code->value_entry_point() : _adapter->c2i_value_entry()
   // The entry point for calling both from and to compiled code is
   // "_code->entry_point()".  Because of tiered compilation and de-opt, this
   // field can come and go.  It can transition from NULL to not-null at any
@@ -576,11 +576,14 @@
   Symbol* klass_name() const;                    // returns the name of the method holder
   BasicType result_type() const;                 // type of the method result
   bool may_return_oop() const                    { BasicType r = result_type(); return (r == T_OBJECT || r == T_ARRAY ||  r == T_VALUETYPE); }
-  bool is_returning_vt() const                   { BasicType r = result_type(); return r == T_VALUETYPE; }
 #ifdef ASSERT
   ValueKlass* returned_value_type(Thread* thread) const;
 #endif
-  bool has_value_args() const;
+
+  // Support for scalarized value type calling convention
+  bool has_scalarized_args() const;
+  bool needs_stack_repair() const;
+  SigEntry get_res_entry() const;
 
   // Checked exceptions thrown by this method (resolved to mirrors)
   objArrayHandle resolved_checked_exceptions(TRAPS) { return resolved_checked_exceptions_impl(this, THREAD); }
--- a/src/hotspot/share/oops/valueKlass.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/oops/valueKlass.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -267,10 +267,10 @@
 // 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.
+// compiled code need to agree upon the 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
+// and ends with an extra T_VOID. T_VALUETYPE/T_VOID pairs 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
@@ -280,74 +280,58 @@
 // 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));
+int ValueKlass::collect_fields(GrowableArray<SigEntry>* sig, int base_off) const {
+  int count = 0;
+  SigEntry::add_entry(sig, 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) {
-      if (fd.is_flattened()) {
-        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(T_VALUETYPEPTR, offset));
+    int offset = base_off + fs.offset() - (base_off > 0 ? first_field_offset() : 0);
+    if (fs.is_flattened()) {
+      // Resolve klass of flattened value type field and recursively collect fields
+      Klass* vk = get_value_field_klass(fs.index());
+      count += ValueKlass::cast(vk)->collect_fields(sig, offset);
+    } else {
+      BasicType bt = FieldType::basic_type(fs.signature());
+      if (bt == T_VALUETYPE) {
+        bt = T_OBJECT;
       }
-    } else {
-      sig_extended.push(SigEntry(bt, offset));
-      if (bt == T_LONG || bt == T_DOUBLE) {
-        sig_extended.push(SigEntry(T_VOID, offset));
-      }
+      SigEntry::add_entry(sig, bt, offset);
+      count += type2size[bt];
     }
   }
   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
+  SigEntry::add_entry(sig, T_VOID, offset);
   if (base_off == 0) {
-    sig_extended.sort(SigEntry::compare);
+    sig->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;
+  assert(sig->at(0)._bt == T_VALUETYPE && sig->at(sig->length()-1)._bt == T_VOID, "broken structure");
+  return count;
 }
 
-void ValueKlass::initialize_calling_convention() {
+void ValueKlass::initialize_calling_convention(TRAPS) {
   // Because the pack and unpack handler addresses need to be loadable from generated code,
   // they are stored at a fixed offset in the klass metadata. Since value type klasses do
   // not have a vtable, the vtable offset is used to store these addresses.
-  //guarantee(vtable_length() == 0, "vtables are not supported in value klasses");
   if (ValueTypeReturnedAsFields || ValueTypePassFieldsAsArgs) {
-    Thread* THREAD = Thread::current();
-    assert(!HAS_PENDING_EXCEPTION, "should have no exception");
     ResourceMark rm;
-    const GrowableArray<SigEntry>& sig_vk = collect_fields();
-    int nb_fields = SigEntry::count_fields(sig_vk)+1;
-    Array<SigEntry>* extended_sig = MetadataFactory::new_array<SigEntry>(class_loader_data(), sig_vk.length(), CHECK_AND_CLEAR);
+    GrowableArray<SigEntry> sig_vk;
+    int nb_fields = collect_fields(&sig_vk);
+    Array<SigEntry>* extended_sig = MetadataFactory::new_array<SigEntry>(class_loader_data(), sig_vk.length(), CHECK);
     *((Array<SigEntry>**)adr_extended_sig()) = extended_sig;
     for (int i = 0; i < sig_vk.length(); i++) {
       extended_sig->at_put(i, sig_vk.at(i));
     }
 
     if (ValueTypeReturnedAsFields) {
+      nb_fields++;
       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);
+      SigEntry::fill_sig_bt(&sig_vk, sig_bt+1);
       VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, nb_fields);
       int total = SharedRuntime::java_return_convention(sig_bt, regs, nb_fields);
 
       if (total > 0) {
-        Array<VMRegPair>* return_regs = MetadataFactory::new_array<VMRegPair>(class_loader_data(), nb_fields, CHECK_AND_CLEAR);
+        Array<VMRegPair>* return_regs = MetadataFactory::new_array<VMRegPair>(class_loader_data(), nb_fields, CHECK);
         *((Array<VMRegPair>**)adr_return_regs()) = return_regs;
         for (int i = 0; i < nb_fields; i++) {
           return_regs->at_put(i, regs[i]);
@@ -401,8 +385,7 @@
 
   for (int i = 0; i < sig_vk->length(); i++) {
     BasicType bt = sig_vk->at(i)._bt;
-    if (bt == T_OBJECT || bt == T_VALUETYPEPTR || bt == T_ARRAY) {
-      int off = sig_vk->at(i)._offset;
+    if (bt == T_OBJECT || bt == T_ARRAY) {
       VMRegPair pair = regs->at(j);
       address loc = reg_map.location(pair.first());
       oop v = *(oop*)loc;
@@ -434,7 +417,6 @@
   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->at(j);
       address loc = reg_map.location(pair.first());
       *(oop*)loc = handles.at(k++)();
@@ -455,7 +437,6 @@
 // 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 RegisterMap& reg_map, const GrowableArray<Handle>& handles, TRAPS) {
-
   oop new_vt = allocate_instance(CHECK_NULL);
   const Array<SigEntry>* sig_vk = extended_sig();
   const Array<VMRegPair>* regs = return_regs();
@@ -475,58 +456,50 @@
       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 v = *(intptr_t*)loc;
-      *(jboolean*)((address)new_vt + off) = v;
+      new_vt->bool_field_put(off, *(jboolean*)loc);
       break;
     }
     case T_CHAR: {
-      jchar v = *(intptr_t*)loc;
-      *(jchar*)((address)new_vt + off) = v;
+      new_vt->char_field_put(off, *(jchar*)loc);
       break;
     }
     case T_BYTE: {
-      jbyte v = *(intptr_t*)loc;
-      *(jbyte*)((address)new_vt + off) = v;
+      new_vt->byte_field_put(off, *(jbyte*)loc);
       break;
     }
     case T_SHORT: {
-      jshort v = *(intptr_t*)loc;
-      *(jshort*)((address)new_vt + off) = v;
+      new_vt->short_field_put(off, *(jshort*)loc);
       break;
     }
     case T_INT: {
-      jint v = *(intptr_t*)loc;
-      *(jint*)((address)new_vt + off) = v;
+      new_vt->int_field_put(off, *(jint*)loc);
       break;
     }
     case T_LONG: {
 #ifdef _LP64
-      jlong v = *(intptr_t*)loc;
-      *(jlong*)((address)new_vt + off) = v;
+      new_vt->double_field_put(off,  *(jdouble*)loc);
 #else
       Unimplemented();
 #endif
       break;
     }
     case T_OBJECT:
-    case T_VALUETYPEPTR:
     case T_ARRAY: {
       Handle handle = handles.at(k++);
-      HeapAccess<>::oop_store_at(new_vt, off, handle());
+      new_vt->obj_field_put(off, handle());
       break;
     }
     case T_FLOAT: {
-      jfloat v = *(jfloat*)loc;
-      *(jfloat*)((address)new_vt + off) = v;
+      new_vt->float_field_put(off,  *(jfloat*)loc);
       break;
     }
     case T_DOUBLE: {
-      jdouble v = *(jdouble*)loc;
-      *(jdouble*)((address)new_vt + off) = v;
+      new_vt->double_field_put(off, *(jdouble*)loc);
       break;
     }
     default:
--- a/src/hotspot/share/oops/valueKlass.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/oops/valueKlass.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -111,9 +111,7 @@
     return ((address)_adr_valueklass_fixed_block) + in_bytes(default_value_offset_offset());
   }
 
-  // static Klass* array_klass_impl(InstanceKlass* this_k, bool or_null, int n, TRAPS);
-
-  GrowableArray<SigEntry> collect_fields(int base_off = 0) const;
+  int collect_fields(GrowableArray<SigEntry>* sig, int base_off = 0) const;
 
   void cleanup_blobs();
 
@@ -131,7 +129,476 @@
   oop value_mirror() const {
     return java_lang_Class::value_mirror(java_mirror());
   }
-  
+  /*
+   * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+   *
+   * This code is free software; you can redistribute it and/or modify it
+   * under the terms of the GNU General Public License version 2 only, as
+   * published by the Free Software Foundation.
+   *
+   * This code is distributed in the hope that it will be useful, but WITHOUT
+   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   * version 2 for more details (a copy is included in the LICENSE file that
+   * accompanied this code).
+   *
+   * You should have received a copy of the GNU General Public License version
+   * 2 along with this work; if not, write to the Free Software Foundation,
+   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+   *
+   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+   * or visit www.oracle.com if you need additional information or have any
+   * questions.
+   */
+
+  package compiler.valhalla.valuetypes;
+
+  import jdk.test.lib.Asserts;
+
+  import java.lang.reflect.Method;
+
+  /*
+   * @test
+   * @summary Test value type calling convention optimizations
+   * @library /testlibrary /test/lib /compiler/whitebox /
+   * @requires os.simpleArch == "x64"
+   * @compile -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator -XDallowFlattenabilityModifiers TestCallingConvention.java
+   * @run driver ClassFileInstaller sun.hotspot.WhiteBox jdk.test.lib.Platform
+   * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+   *                               -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:+EnableValhalla
+   *                               compiler.valhalla.valuetypes.ValueTypeTest
+   *                               compiler.valhalla.valuetypes.TestCallingConvention
+   */
+  public class TestCallingConvention extends ValueTypeTest {
+      // Extra VM parameters for some test scenarios. See ValueTypeTest.getVMParameters()
+      @Override
+      public String[] getExtraVMParameters(int scenario) {
+          switch (scenario) {
+          case 3: return new String[] {"-XX:-ValueArrayFlatten"};
+          }
+          return null;
+      }
+
+      public static void main(String[] args) throws Throwable {
+          TestCallingConvention test = new TestCallingConvention();
+          test.run(args, MyValue1.class, MyValue2.class, MyValue2Inline.class, MyValue4.class);
+      }
+
+      // Test interpreter to compiled code with various signatures
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test1(MyValue2 v) {
+          return v.hash();
+      }
+
+      @DontCompile
+      public void test1_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test1(v);
+          Asserts.assertEQ(result, v.hashInterpreted());
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test2(int i1, MyValue2 v, int i2) {
+          return v.hash() + i1 - i2;
+      }
+
+      @DontCompile
+      public void test2_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test2(rI, v, 2*rI);
+          Asserts.assertEQ(result, v.hashInterpreted() - rI);
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test3(long l1, MyValue2 v, long l2) {
+          return v.hash() + l1 - l2;
+      }
+
+      @DontCompile
+      public void test3_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test3(rL, v, 2*rL);
+          Asserts.assertEQ(result, v.hashInterpreted() - rL);
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test4(int i, MyValue2 v, long l) {
+          return v.hash() + i + l;
+      }
+
+      @DontCompile
+      public void test4_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test4(rI, v, rL);
+          Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test5(long l, MyValue2 v, int i) {
+          return v.hash() + i + l;
+      }
+
+      @DontCompile
+      public void test5_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test5(rL, v, rI);
+          Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test6(long l, MyValue1 v1, int i, MyValue2 v2) {
+          return v1.hash() + i + l + v2.hash();
+      }
+
+      @DontCompile
+      public void test6_verifier(boolean warmup) {
+          MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
+          MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
+          long result = test6(rL, v1, rI, v2);
+          Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
+      }
+
+      // Test compiled code to interpreter with various signatures
+      @DontCompile
+      public long test7_interp(MyValue2 v) {
+          return v.hash();
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test7(MyValue2 v) {
+          return test7_interp(v);
+      }
+
+      @DontCompile
+      public void test7_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test7(v);
+          Asserts.assertEQ(result, v.hashInterpreted());
+      }
+
+      @DontCompile
+      public long test8_interp(int i1, MyValue2 v, int i2) {
+          return v.hash() + i1 - i2;
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test8(int i1, MyValue2 v, int i2) {
+          return test8_interp(i1, v, i2);
+      }
+
+      @DontCompile
+      public void test8_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test8(rI, v, 2*rI);
+          Asserts.assertEQ(result, v.hashInterpreted() - rI);
+      }
+
+      @DontCompile
+      public long test9_interp(long l1, MyValue2 v, long l2) {
+          return v.hash() + l1 - l2;
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test9(long l1, MyValue2 v, long l2) {
+          return test9_interp(l1, v, l2);
+      }
+
+      @DontCompile
+      public void test9_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test9(rL, v, 2*rL);
+          Asserts.assertEQ(result, v.hashInterpreted() - rL);
+      }
+
+      @DontCompile
+      public long test10_interp(int i, MyValue2 v, long l) {
+          return v.hash() + i + l;
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test10(int i, MyValue2 v, long l) {
+          return test10_interp(i, v, l);
+      }
+
+      @DontCompile
+      public void test10_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test10(rI, v, rL);
+          Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+      }
+
+      @DontCompile
+      public long test11_interp(long l, MyValue2 v, int i) {
+          return v.hash() + i + l;
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test11(long l, MyValue2 v, int i) {
+          return test11_interp(l, v, i);
+      }
+
+      @DontCompile
+      public void test11_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          long result = test11(rL, v, rI);
+          Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+      }
+
+      @DontCompile
+      public long test12_interp(long l, MyValue1 v1, int i, MyValue2 v2) {
+          return v1.hash() + i + l + v2.hash();
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test12(long l, MyValue1 v1, int i, MyValue2 v2) {
+          return test12_interp(l, v1, i, v2);
+      }
+
+      @DontCompile
+      public void test12_verifier(boolean warmup) {
+          MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
+          MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
+          long result = test12(rL, v1, rI, v2);
+          Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
+      }
+
+      // Test that debug info at a call is correct
+      @DontCompile
+      public long test13_interp(MyValue2 v, MyValue1[] va, boolean deopt) {
+          if (deopt) {
+              // uncommon trap
+              WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test13"));
+          }
+          return v.hash() + va[0].hash() + va[1].hash();
+      }
+
+      @Test(failOn = ALLOC + STORE + TRAP)
+      public long test13(MyValue2 v, MyValue1[] va, boolean flag, long l) {
+          return test13_interp(v, va, flag) + l;
+      }
+
+      @DontCompile
+      public void test13_verifier(boolean warmup) {
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          MyValue1[] va = new MyValue1[2];
+          va[0] = MyValue1.createWithFieldsDontInline(rI, rL);
+          va[1] = MyValue1.createWithFieldsDontInline(rI, rL);
+          long result = test13(v, va, !warmup, rL);
+          Asserts.assertEQ(result, v.hashInterpreted() + va[0].hash() + va[1].hash() + rL);
+      }
+
+      // Test deoptimization at call return with return value in registers
+      @DontCompile
+      public MyValue2 test14_interp(boolean deopt) {
+          if (deopt) {
+              // uncommon trap
+              WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test14"));
+          }
+          return MyValue2.createWithFieldsInline(rI, true);
+      }
+
+      @Test()
+      public MyValue2 test14(boolean flag) {
+          return test14_interp(flag);
+      }
+
+      @DontCompile
+      public void test14_verifier(boolean warmup) {
+          MyValue2 result = test14(!warmup);
+          MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+          Asserts.assertEQ(result.hash(), v.hash());
+      }
+
+      // Return value types in registers from interpreter -> compiled
+      final MyValue3 test15_vt = MyValue3.create();
+      @DontCompile
+      public MyValue3 test15_interp() {
+          return test15_vt;
+      }
+
+      MyValue3 test15_vt2;
+      @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
+      @Test(valid = ValueTypeReturnedAsFieldsOff)
+      public void test15() {
+          test15_vt2 = test15_interp();
+      }
+
+      @DontCompile
+      public void test15_verifier(boolean warmup) {
+          test15();
+          test15_vt.verify(test15_vt2);
+      }
+
+      // Return value types in registers from compiled -> interpreter
+      final MyValue3 test16_vt = MyValue3.create();
+      @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + STORE + TRAP)
+      @Test(valid = ValueTypeReturnedAsFieldsOff)
+      public MyValue3 test16() {
+          return test16_vt;
+      }
+
+      @DontCompile
+      public void test16_verifier(boolean warmup) {
+          MyValue3 vt = test16();
+          test16_vt.verify(vt);
+      }
+
+      // Return value types in registers from compiled -> compiled
+      final MyValue3 test17_vt = MyValue3.create();
+      @DontInline
+      public MyValue3 test17_comp() {
+          return test17_vt;
+      }
+
+      MyValue3 test17_vt2;
+      @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
+      @Test(valid = ValueTypeReturnedAsFieldsOff)
+      public void test17() {
+          test17_vt2 = test17_comp();
+      }
+
+      @DontCompile
+      public void test17_verifier(boolean warmup) throws Exception {
+          Method helper_m = getClass().getDeclaredMethod("test17_comp");
+          if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
+              WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
+              Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test17_comp not compiled");
+          }
+          test17();
+          test17_vt.verify(test17_vt2);
+      }
+
+      // Same tests as above but with a value type that cannot be returned in registers
+
+      // Return value types in registers from interpreter -> compiled
+      final MyValue4 test18_vt = MyValue4.create();
+      @DontCompile
+      public MyValue4 test18_interp() {
+          return test18_vt;
+      }
+
+      MyValue4 test18_vt2;
+      @Test
+      public void test18() {
+          test18_vt2 = test18_interp();
+      }
+
+      @DontCompile
+      public void test18_verifier(boolean warmup) {
+          test18();
+          test18_vt.verify(test18_vt2);
+      }
+
+      // Return value types in registers from compiled -> interpreter
+      final MyValue4 test19_vt = MyValue4.create();
+      @Test
+      public MyValue4 test19() {
+          return test19_vt;
+      }
+
+      @DontCompile
+      public void test19_verifier(boolean warmup) {
+          MyValue4 vt = test19();
+          test19_vt.verify(vt);
+      }
+
+      // Return value types in registers from compiled -> compiled
+      final MyValue4 test20_vt = MyValue4.create();
+      @DontInline
+      public MyValue4 test20_comp() {
+          return test20_vt;
+      }
+
+      MyValue4 test20_vt2;
+      @Test
+      public void test20() {
+          test20_vt2 = test20_comp();
+      }
+
+      @DontCompile
+      public void test20_verifier(boolean warmup) throws Exception {
+          Method helper_m = getClass().getDeclaredMethod("test20_comp");
+          if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
+              WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
+              Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test20_comp not compiled");
+          }
+          test20();
+          test20_vt.verify(test20_vt2);
+      }
+
+      // Test no result from inlined method for incremental inlining
+      final MyValue3 test21_vt = MyValue3.create();
+      public MyValue3 test21_inlined() {
+          throw new RuntimeException();
+      }
+
+      @Test
+      public MyValue3 test21() {
+          try {
+              return test21_inlined();
+          } catch (RuntimeException ex) {
+              return test21_vt;
+          }
+      }
+
+      @DontCompile
+      public void test21_verifier(boolean warmup) {
+          MyValue3 vt = test21();
+          test21_vt.verify(vt);
+      }
+
+      // Test returning a non-flattened value type as fields
+      MyValue3.box test22_vt = MyValue3.create();
+
+      @Test
+      public MyValue3 test22() {
+          return test22_vt;
+      }
+
+      @DontCompile
+      public void test22_verifier(boolean warmup) {
+          MyValue3 vt = test22();
+          test22_vt.verify(vt);
+      }
+
+      // Test calling a method that has circular register/stack dependencies when unpacking value type arguments
+      value class TestValue23 {
+          final double f1;
+          TestValue23(double val) {
+              f1 = val;
+          }
+      }
+
+      static double test23Callee(int i1, int i2, int i3, int i4, int i5, int i6,
+                                 TestValue23 v1, TestValue23 v2, TestValue23 v3, TestValue23 v4, TestValue23 v5, TestValue23 v6, TestValue23 v7, TestValue23 v8,
+                                 double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
+          return i1 + i2 + i3 + i4 + i5 + i6 + v1.f1 + v2.f1 + v3.f1 + v4.f1 + v5.f1 + v6.f1 + v7.f1 + v8.f1 + d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8;
+      }
+
+      @Test
+      public double test23(int i1, int i2, int i3, int i4, int i5, int i6,
+                           TestValue23 v1, TestValue23 v2, TestValue23 v3, TestValue23 v4, TestValue23 v5, TestValue23 v6, TestValue23 v7, TestValue23 v8,
+                           double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
+          return test23Callee(i1, i2, i3, i4, i5, i6,
+                              v1, v2, v3, v4, v5, v6, v7, v8,
+                              d1, d2, d3, d4, d5, d6, d7, d8);
+      }
+
+      @DontCompile
+      public void test23_verifier(boolean warmup) {
+          TestValue23 vt = new TestValue23(rI);
+          double res1 = test23(rI, rI, rI, rI, rI, rI,
+                              vt, vt, vt, vt, vt, vt, vt, vt,
+                              rI, rI, rI, rI, rI, rI, rI, rI);
+          double res2 = test23Callee(rI, rI, rI, rI, rI, rI,
+                                     vt, vt, vt, vt, vt, vt, vt, vt,
+                                     rI, rI, rI, rI, rI, rI, rI, rI);
+          double res3 = 6*rI + 8*rI + 8*rI;
+          Asserts.assertEQ(res1, res2);
+          Asserts.assertEQ(res2, res3);
+      }
+  }
   // Casting from Klass*
   static ValueKlass* cast(Klass* k) {
     assert(k->is_value(), "cast to ValueKlass");
@@ -195,7 +662,7 @@
   inline void oop_iterate_specialized_bounded(const address oop_addr, OopClosureType* closure, void* lo, void* hi);
 
   // calling convention support
-  void initialize_calling_convention();
+  void initialize_calling_convention(TRAPS);
   Array<SigEntry>* extended_sig() const {
     return *((Array<SigEntry>**)adr_extended_sig());
   }
@@ -211,13 +678,11 @@
   // pack and unpack handlers. Need to be loadable from generated code
   // so at a fixed offset from the base of the klass pointer.
   static ByteSize pack_handler_offset() {
-    fatal("Should be re-implemented using the ValueKlassStaticBlock indirection");
-    return in_ByteSize(InstanceKlass::header_size() * wordSize);
+    return byte_offset_of(ValueKlassFixedBlock, _pack_handler);
   }
 
   static ByteSize unpack_handler_offset() {
-    fatal("Should be re-implemented using the ValueKlassStaticBlock indirection");
-    return in_ByteSize((InstanceKlass::header_size()+1) * wordSize);
+    return byte_offset_of(ValueKlassFixedBlock, _unpack_handler);
   }
 
   static ByteSize default_value_offset_offset() {
--- a/src/hotspot/share/opto/callGenerator.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/callGenerator.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -123,14 +123,14 @@
  public:
   DirectCallGenerator(ciMethod* method, bool separate_io_proj)
     : CallGenerator(method),
+      _call_node(NULL),
       _separate_io_proj(separate_io_proj)
   {
-    // TODO fix this with the calling convention changes
-    if (false /*method->signature()->return_type()->is__Value()*/) {
-      // If that call has not been optimized by the time optimizations
-      // are over, we'll need to add a call to create a value type
-      // instance from the klass returned by the call. Separating
-      // memory and I/O projections for exceptions is required to
+    if (ValueTypeReturnedAsFields && method->is_method_handle_intrinsic()) {
+      // If that call has not been optimized by the time optimizations are over,
+      // we'll need to add a call to create a value type instance from the klass
+      // returned by the call (see PhaseMacroExpand::expand_mh_intrinsic_return).
+      // Separating memory and I/O projections for exceptions is required to
       // perform that graph transformation.
       _separate_io_proj = true;
     }
@@ -436,24 +436,27 @@
   uint j = TypeFunc::Parms;
   for (uint i1 = 0; i1 < nargs; i1++) {
     const Type* t = domain_sig->field_at(TypeFunc::Parms + i1);
-    if (!ValueTypePassFieldsAsArgs) {
+    if (method()->get_Method()->has_scalarized_args()) {
+      GraphKit arg_kit(jvms, &gvn);
+      // TODO for now, don't scalarize value type receivers because of interface calls
+      if (t->is_valuetypeptr() && (method()->is_static() || i1 != 0)) {
+        arg_kit.set_control(map->control());
+        ValueTypeNode* vt = ValueTypeNode::make_from_multi(&arg_kit, call, t->value_klass(), j, true);
+        map->set_control(arg_kit.control());
+        map->set_argument(jvms, i1, vt);
+      } else {
+        int index = j;
+        SigEntry res_entry = method()->get_Method()->get_res_entry();
+        if (res_entry._offset != -1 && (index - TypeFunc::Parms) >= res_entry._offset) {
+          // Skip reserved entry
+          index += type2size[res_entry._bt];
+        }
+        map->set_argument(jvms, i1, call->in(index));
+        j++;
+      }
+    } else {
       Node* arg = call->in(TypeFunc::Parms + i1);
       map->set_argument(jvms, i1, arg);
-    } else {
-      assert(false, "FIXME");
-      // TODO move this into Parse::Parse because we might need to deopt
-      /*
-      GraphKit arg_kit(jvms, &gvn);
-      if (t->is_valuetypeptr()) {
-        ciValueKlass* vk = t->value_klass();
-        ValueTypeNode* vt = ValueTypeNode::make_from_multi(&arg_kit, call, vk, j, true);
-        arg_kit.set_argument(i1, vt);
-        j += vk->value_arg_slots();
-      } else {
-        arg_kit.set_argument(i1, call->in(j));
-        j++;
-      }
-      */
     }
   }
 
@@ -502,20 +505,18 @@
   bool returned_as_fields = call->tf()->returns_value_type_as_fields();
   if (result->is_ValueType()) {
     ValueTypeNode* vt = result->as_ValueType();
-    if (!returned_as_fields) {
-      result = ValueTypePtrNode::make_from_value_type(&kit, vt);
-    } else {
-      assert(false, "FIXME");
+    if (returned_as_fields) {
       // Return of multiple values (the fields of a value type)
       vt->replace_call_results(&kit, call, C);
-      if (gvn.type(vt->get_oop()) == TypePtr::NULL_PTR) {
+      if (vt->is_allocated(&gvn) && !StressValueTypeReturnedAsFields) {
+        result = vt->get_oop();
+      } else {
         result = vt->tagged_klass(gvn);
-      } else {
-        result = vt->get_oop();
       }
+    } else {
+      result = ValueTypePtrNode::make_from_value_type(&kit, vt);
     }
   } else if (gvn.type(result)->is_valuetypeptr() && returned_as_fields) {
-    assert(false, "FIXME");
     const Type* vt_t = call->_tf->range_sig()->field_at(TypeFunc::Parms);
     Node* cast = new CheckCastPPNode(NULL, result, vt_t);
     gvn.record_for_igvn(cast);
--- a/src/hotspot/share/opto/callnode.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/callnode.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -727,9 +727,9 @@
         method != NULL &&
         method->is_method_handle_intrinsic() &&
         r->cnt() > TypeFunc::Parms &&
-        r->field_at(TypeFunc::Parms)->is_valuetypeptr() &&
-        // TODO fix this with the calling convention changes
-        false /*r->field_at(TypeFunc::Parms)->is_valuetypeptr()->is__Value()*/) {
+        r->field_at(TypeFunc::Parms)->isa_oopptr() &&
+        r->field_at(TypeFunc::Parms)->is_oopptr()->can_be_value_type()) {
+      // Make sure this call is processed by PhaseMacroExpand::expand_mh_intrinsic_return
       init_flags(Flag_is_macro);
       C->add_macro_node(this);
     }
--- a/src/hotspot/share/opto/chaitin.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/chaitin.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -2229,6 +2229,9 @@
           _matcher._parm_regs[j].second() == reg ) {
         tty->print("parm %d: ",j);
         domain->field_at(j + TypeFunc::Parms)->dump();
+        if (j == C->get_res_entry()._offset) {
+          tty->print(" [RESERVED] ");
+        }
         tty->cr();
         break;
       }
--- a/src/hotspot/share/opto/compile.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/compile.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -547,6 +547,12 @@
     ResourceMark rm;
     _scratch_const_size = const_size;
     int size = C2Compiler::initial_code_buffer_size(const_size);
+#ifdef ASSERT
+    if (C->has_scalarized_args()) {
+      // Oop verification for loading object fields from scalarized value types in the new entry point requires lots of space
+      size += 5120;
+    }
+#endif
     blob = BufferBlob::create("Compile::scratch_buffer", size);
     // Record the buffer blob for next time.
     set_scratch_buffer_blob(blob);
@@ -611,6 +617,9 @@
     masm.bind(fakeL);
     n->as_MachBranch()->save_label(&saveL, &save_bnum);
     n->as_MachBranch()->label_set(&fakeL, 0);
+  } else if (n->is_MachProlog()) {
+    saveL = ((MachPrologNode*)n)->_verified_entry;
+    ((MachPrologNode*)n)->_verified_entry = &fakeL;
   }
   n->emit(buf, this->regalloc());
 
@@ -620,6 +629,8 @@
   // Restore label.
   if (is_branch) {
     n->as_MachBranch()->label_set(saveL, save_bnum);
+  } else if (n->is_MachProlog()) {
+    ((MachPrologNode*)n)->_verified_entry = saveL;
   }
 
   // End scratch_emit_size section.
@@ -653,6 +664,8 @@
                   _max_node_limit(MaxNodeLimit),
                   _orig_pc_slot(0),
                   _orig_pc_slot_offset_in_bytes(0),
+                  _sp_inc_slot(0),
+                  _sp_inc_slot_offset_in_bytes(0),
                   _inlining_progress(false),
                   _inlining_incrementally(false),
                   _has_reserved_stack_access(target->has_reserved_stack_access()),
@@ -912,8 +925,15 @@
   // Now that we know the size of all the monitors we can add a fixed slot
   // for the original deopt pc.
 
-  _orig_pc_slot =  fixed_slots();
+  _orig_pc_slot = fixed_slots();
   int next_slot = _orig_pc_slot + (sizeof(address) / VMRegImpl::stack_slot_size);
+
+  if (method()->get_Method()->needs_stack_repair()) {
+    // One extra slot for the special stack increment value
+    _sp_inc_slot = next_slot;
+    next_slot += 2;
+  }
+
   set_fixed_slots(next_slot);
 
   // Compute when to use implicit null checks. Used by matching trap based
@@ -939,6 +959,13 @@
       _code_offsets.set_value(CodeOffsets::OSR_Entry, _first_block_size);
     } else {
       _code_offsets.set_value(CodeOffsets::Verified_Entry, _first_block_size);
+      if (_code_offsets.value(CodeOffsets::Verified_Value_Entry) == -1) {
+        _code_offsets.set_value(CodeOffsets::Verified_Value_Entry, _first_block_size);
+      }
+      if (_code_offsets.value(CodeOffsets::Entry) == -1) {
+        // We emitted a value type entry point, adjust normal entry
+        _code_offsets.set_value(CodeOffsets::Entry, _first_block_size);
+      }
       _code_offsets.set_value(CodeOffsets::OSR_Entry, 0);
     }
 
@@ -984,6 +1011,8 @@
     _max_node_limit(MaxNodeLimit),
     _orig_pc_slot(0),
     _orig_pc_slot_offset_in_bytes(0),
+    _sp_inc_slot(0),
+    _sp_inc_slot_offset_in_bytes(0),
     _inlining_progress(false),
     _inlining_incrementally(false),
     _has_reserved_stack_access(false),
--- a/src/hotspot/share/opto/compile.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/compile.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -381,6 +381,10 @@
   int                   _orig_pc_slot;
   int                   _orig_pc_slot_offset_in_bytes;
 
+  // For value type calling convention
+  int                   _sp_inc_slot;
+  int                   _sp_inc_slot_offset_in_bytes;
+
   int                   _major_progress;        // Count of something big happening
   bool                  _inlining_progress;     // progress doing incremental inlining?
   bool                  _inlining_incrementally;// Are we doing incremental inlining (post parse)
@@ -715,6 +719,12 @@
   uint              max_node_limit() const       { return (uint)_max_node_limit; }
   void          set_max_node_limit(uint n)       { _max_node_limit = n; }
 
+  // Support for scalarized value type calling convention
+  bool              has_scalarized_args() const  { return _method != NULL && _method->get_Method()->has_scalarized_args(); }
+  bool              needs_stack_repair()  const  { return _method != NULL && _method->get_Method()->needs_stack_repair(); }
+  SigEntry          get_res_entry()       const  { return _method->get_Method()->get_res_entry(); }
+  int               sp_inc_offset()       const  { return _sp_inc_slot_offset_in_bytes; }
+
   // check the CompilerOracle for special behaviours for this compile
   bool          method_has_option(const char * option) {
     return method() != NULL && method()->has_option(option);
--- a/src/hotspot/share/opto/connode.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/connode.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -50,7 +50,6 @@
   case T_FLOAT:       return new ConFNode( t->is_float_constant() );
   case T_DOUBLE:      return new ConDNode( t->is_double_constant() );
   case T_VOID:        return new ConNode ( Type::TOP );
-  case T_VALUETYPEPTR:
   case T_OBJECT:      return new ConPNode( t->is_ptr() );
   case T_ARRAY:       return new ConPNode( t->is_aryptr() );
   case T_ADDRESS:     return new ConPNode( t->is_ptr() );
--- a/src/hotspot/share/opto/escape.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/escape.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -2125,9 +2125,7 @@
       }
     }
   }
-  // TODO enable when using T_VALUETYPEPTR
-  //assert(bt != T_VALUETYPE, "should not have valuetype here");
-  return (bt == T_OBJECT || bt == T_VALUETYPE || bt == T_VALUETYPEPTR || bt == T_NARROWOOP || bt == T_ARRAY);
+  return (bt == T_OBJECT || bt == T_VALUETYPE || bt == T_NARROWOOP || bt == T_ARRAY);
 }
 
 // Returns unique pointed java object or NULL.
--- a/src/hotspot/share/opto/graphKit.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/graphKit.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -1544,7 +1544,7 @@
   }
   ld = _gvn.transform(ld);
 
-  if (((bt == T_OBJECT || bt == T_VALUETYPE || bt == T_VALUETYPEPTR) && C->do_escape_analysis()) || C->eliminate_boxing()) {
+  if (((bt == T_OBJECT || bt == T_VALUETYPE) && C->do_escape_analysis()) || C->eliminate_boxing()) {
     // Improve graph before escape analysis and boxing elimination.
     record_for_igvn(ld);
   }
@@ -1791,7 +1791,8 @@
     if (arg->is_ValueType()) {
       assert(t->is_oopptr()->can_be_value_type(), "wrong argument type");
       ValueTypeNode* vt = arg->as_ValueType();
-      if (ValueTypePassFieldsAsArgs) {
+      // TODO for now, don't scalarize value type receivers because of interface calls
+      if (call->method()->get_Method()->has_scalarized_args() && t->is_valuetypeptr() && (call->method()->is_static() || i != TypeFunc::Parms)) {
         // We don't pass value type arguments by reference but instead
         // pass each field of the value type
         idx += vt->pass_fields(call, idx, *this);
@@ -1805,8 +1806,16 @@
         arg = vt->allocate(this)->get_oop();
       }
     }
-    call->init_req(idx, arg);
-    idx++;
+    call->init_req(idx++, arg);
+
+    SigEntry res_entry = call->method()->get_Method()->get_res_entry();
+    if ((int)(idx - TypeFunc::Parms) == res_entry._offset) {
+      // Skip reserved entry
+      call->init_req(idx++, top());
+      if (res_entry._bt == T_DOUBLE || res_entry._bt == T_LONG) {
+        call->init_req(idx++, top());
+      }
+    }
   }
 }
 
@@ -1862,18 +1871,15 @@
   if (call->method() == NULL ||
       call->method()->return_type()->basic_type() == T_VOID) {
     ret = top();
+  } else if (call->tf()->returns_value_type_as_fields()) {
+    // Return of multiple values (value type fields): we create a
+    // ValueType node, each field is a projection from the call.
+    const TypeTuple* range_sig = call->tf()->range_sig();
+    const Type* t = range_sig->field_at(TypeFunc::Parms);
+    uint base_input = TypeFunc::Parms + 1;
+    ret = ValueTypeNode::make_from_multi(this, call, t->value_klass(), base_input, false);
   } else {
-    if (!call->tf()->returns_value_type_as_fields()) {
-      ret = _gvn.transform(new ProjNode(call, TypeFunc::Parms));
-    } else {
-      // Return of multiple values (value type fields): we create a
-      // ValueType node, each field is a projection from the call.
-      const TypeTuple* range_sig = call->tf()->range_sig();
-      const Type* t = range_sig->field_at(TypeFunc::Parms);
-      assert(t->is_valuetypeptr(), "only value types for multiple return values");
-      ciValueKlass* vk = t->value_klass();
-      ret = ValueTypeNode::make_from_multi(this, call, vk, TypeFunc::Parms+1, false);
-    }
+    ret = _gvn.transform(new ProjNode(call, TypeFunc::Parms));
   }
 
   return ret;
--- a/src/hotspot/share/opto/machnode.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/machnode.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -463,6 +463,22 @@
   int  constant_offset_unchecked() const;
 };
 
+//------------------------------MachVVEPNode-----------------------------------
+// Machine Verified Value Type Entry Point Node
+class MachVVEPNode : public MachIdealNode {
+public:
+  MachVVEPNode(Label* verified_entry) : _verified_entry(verified_entry) {}
+  virtual void emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const;
+  virtual uint size(PhaseRegAlloc* ra_) const;
+
+#ifndef PRODUCT
+  virtual const char* Name() const { return "Verified ValueType Entry-Point"; }
+  virtual void format(PhaseRegAlloc*, outputStream* st) const;
+#endif
+private:
+  Label* _verified_entry;
+};
+
 //------------------------------MachUEPNode-----------------------------------
 // Machine Unvalidated Entry Point Node
 class MachUEPNode : public MachIdealNode {
@@ -481,11 +497,14 @@
 // Machine function Prolog Node
 class MachPrologNode : public MachIdealNode {
 public:
-  MachPrologNode( ) {}
+  MachPrologNode(Label* verified_entry) : _verified_entry(verified_entry) {
+    init_class_id(Class_MachProlog);
+  }
   virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const;
   virtual uint size(PhaseRegAlloc *ra_) const;
   virtual int reloc() const;
 
+  Label* _verified_entry;
 #ifndef PRODUCT
   virtual const char *Name() const { return "Prolog"; }
   virtual void format( PhaseRegAlloc *, outputStream *st ) const;
--- a/src/hotspot/share/opto/macro.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/macro.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -2497,7 +2497,7 @@
   _igvn.replace_node(_memproj_fallthrough, mem_phi);
 }
 
-// A value type is returned from the call but we don't know its
+// A value type might be returned from the call but we don't know its
 // type. Either we get a buffered value (and nothing needs to be done)
 // or one of the values being returned is the klass of the value type
 // and we need to allocate a value type instance of that type and
@@ -2505,18 +2505,16 @@
 // first try a fast path allocation and initialize the value with the
 // value klass's pack handler or we fall back to a runtime call.
 void PhaseMacroExpand::expand_mh_intrinsic_return(CallStaticJavaNode* call) {
-  Node* ret = call->proj_out(TypeFunc::Parms);
+  assert(call->method()->is_method_handle_intrinsic(), "must be a method handle intrinsic call");
+  Node* ret = call->proj_out_or_null(TypeFunc::Parms);
   if (ret == NULL) {
     return;
   }
-  // TODO fix this with the calling convention changes
-  //assert(ret->bottom_type()->is_valuetypeptr()->is__Value(), "unexpected return type from MH intrinsic");
   const TypeFunc* tf = call->_tf;
   const TypeTuple* domain = OptoRuntime::store_value_type_fields_Type()->domain_cc();
   const TypeFunc* new_tf = TypeFunc::make(tf->domain_sig(), tf->domain_cc(), tf->range_sig(), domain);
   call->_tf = new_tf;
-  // Make sure the change of type is applied before projections are
-  // processed by igvn
+  // Make sure the change of type is applied before projections are processed by igvn
   _igvn.set_type(call, call->Value(&_igvn));
   _igvn.set_type(ret, ret->Value(&_igvn));
 
@@ -2541,7 +2539,7 @@
   Node* allocation_ctl = transform_later(new IfTrueNode(allocation_iff));
   Node* no_allocation_ctl = transform_later(new IfFalseNode(allocation_iff));
 
-  Node* no_allocation_res = transform_later(new CheckCastPPNode(no_allocation_ctl, res, TypeInstPtr::NOTNULL));
+  Node* no_allocation_res = transform_later(new CheckCastPPNode(no_allocation_ctl, res, TypeInstPtr::BOTTOM));
 
   Node* mask2 = MakeConX(-2);
   Node* masked2 = transform_later(new AndXNode(cast, mask2));
@@ -2622,7 +2620,8 @@
   if (UseCompressedClassPointers) {
     rawmem = make_store(slowpath_false, rawmem, old_top, oopDesc::klass_gap_offset_in_bytes(), intcon(0), T_INT);
   }
-  Node* pack_handler = make_load(slowpath_false, rawmem, klass_node, in_bytes(ValueKlass::pack_handler_offset()), TypeRawPtr::BOTTOM, T_ADDRESS);
+  Node* fixed_block  = make_load(slowpath_false, rawmem, klass_node, in_bytes(InstanceKlass::adr_valueklass_fixed_block_offset()), TypeRawPtr::BOTTOM, T_ADDRESS);
+  Node* pack_handler = make_load(slowpath_false, rawmem, fixed_block, in_bytes(ValueKlass::pack_handler_offset()), TypeRawPtr::BOTTOM, T_ADDRESS);
 
   CallLeafNoFPNode* handler_call = new CallLeafNoFPNode(OptoRuntime::pack_value_type_Type(),
                                                         NULL,
@@ -2665,7 +2664,7 @@
   Node* r = new RegionNode(4);
   Node* mem_phi = new PhiNode(r, Type::MEMORY, TypePtr::BOTTOM);
   Node* io_phi = new PhiNode(r, Type::ABIO);
-  Node* res_phi = new PhiNode(r, ret->bottom_type());
+  Node* res_phi = new PhiNode(r, TypeInstPtr::BOTTOM);
 
   r->init_req(1, no_allocation_ctl);
   mem_phi->init_req(1, mem);
--- a/src/hotspot/share/opto/matcher.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/matcher.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -492,9 +492,20 @@
   // At first, start with the empty mask
   C->FIRST_STACK_mask().Clear();
 
+  // Check if method has a reserved entry in the argument stack area that
+  // should not be used for spilling because it holds the return address.
+  OptoRegPair res_entry;
+  if (C->needs_stack_repair()) {
+    int res_idx = C->get_res_entry()._offset;
+    res_entry = _parm_regs[res_idx];
+  }
+
   // Add in the incoming argument area
   OptoReg::Name init_in = OptoReg::add(_old_SP, C->out_preserve_stack_slots());
   for (i = init_in; i < _in_arg_limit; i = OptoReg::add(i,1)) {
+    if (i == res_entry.first() || i == res_entry.second()) {
+      continue; // Skip reserved slot to avoid spilling
+    }
     C->FIRST_STACK_mask().Insert(i);
   }
   // Add in all bits past the outgoing argument area
--- a/src/hotspot/share/opto/memnode.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/memnode.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -817,7 +817,6 @@
   case T_DOUBLE:  load = new LoadDNode (ctl, mem, adr, adr_type, rt,            mo, control_dependency); break;
   case T_ADDRESS: load = new LoadPNode (ctl, mem, adr, adr_type, rt->is_ptr(),  mo, control_dependency); break;
   case T_VALUETYPE:
-  case T_VALUETYPEPTR:
   case T_OBJECT:
 #ifdef _LP64
     if (adr->bottom_type()->is_ptr_to_narrowoop()) {
--- a/src/hotspot/share/opto/node.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/node.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -102,6 +102,7 @@
 class MachNode;
 class MachNullCheckNode;
 class MachProjNode;
+class MachPrologNode;
 class MachReturnNode;
 class MachSafePointNode;
 class MachSpillCopyNode;
@@ -664,6 +665,7 @@
         DEFINE_CLASS_ID(MachJump,       MachConstant, 0)
       DEFINE_CLASS_ID(MachMerge,        Mach, 6)
       DEFINE_CLASS_ID(MachMemBar,       Mach, 7)
+      DEFINE_CLASS_ID(MachProlog,       Mach, 8)
 
     DEFINE_CLASS_ID(Type,  Node, 2)
       DEFINE_CLASS_ID(Phi,   Type, 0)
@@ -856,6 +858,7 @@
   DEFINE_CLASS_QUERY(MachJump)
   DEFINE_CLASS_QUERY(MachNullCheck)
   DEFINE_CLASS_QUERY(MachProj)
+  DEFINE_CLASS_QUERY(MachProlog)
   DEFINE_CLASS_QUERY(MachReturn)
   DEFINE_CLASS_QUERY(MachSafePoint)
   DEFINE_CLASS_QUERY(MachSpillCopy)
--- a/src/hotspot/share/opto/output.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/output.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -71,12 +71,14 @@
   const StartNode *start = entry->head()->as_Start();
 
   // Replace StartNode with prolog
-  MachPrologNode *prolog = new MachPrologNode();
+  Label verified_entry;
+  MachPrologNode* prolog = new MachPrologNode(&verified_entry);
   entry->map_node(prolog, 0);
   _cfg->map_node_to_block(prolog, entry);
   _cfg->unmap_node_from_block(start); // start is no longer in any block
 
   // Virtual methods need an unverified entry point
+  bool has_value_entry = false;
   if (is_osr_compilation()) {
     if (PoisonOSREntry) {
       // TODO: Should use a ShouldNotReachHereNode...
@@ -87,6 +89,11 @@
       // Insert unvalidated entry point
       _cfg->insert(broot, 0, new MachUEPNode());
     }
+    if (_method && _method->get_Method()->has_scalarized_args()) {
+      // Insert value type entry point
+      _cfg->insert(broot, 0, new MachVVEPNode(&verified_entry));
+      has_value_entry = true;
+    }
   }
 
   // Break before main entry point
@@ -122,6 +129,18 @@
     return;
   }
 
+  if (has_value_entry) {
+    // We added an entry point for unscalarized value types
+    // Compute offset of "normal" entry point
+    _code_offsets.set_value(CodeOffsets::Verified_Value_Entry, 0);
+    uint entry_offset = -1; // will be patched later
+    if (!_method->flags().is_static()) {
+      MachVVEPNode* vvep = (MachVVEPNode*)broot->get_node(0);
+      entry_offset = vvep->size(_regalloc);
+    }
+    _code_offsets.set_value(CodeOffsets::Entry, entry_offset);
+  }
+
   ScheduleAndBundle();
 
 #ifndef PRODUCT
@@ -973,6 +992,10 @@
   if (fixed_slots() != 0) {
     _orig_pc_slot_offset_in_bytes = _regalloc->reg2offset(OptoReg::stack2reg(_orig_pc_slot));
   }
+  if (C->needs_stack_repair()) {
+    // Compute the byte offset of the stack increment value
+    _sp_inc_slot_offset_in_bytes = _regalloc->reg2offset(OptoReg::stack2reg(_sp_inc_slot));
+  }
 
   // Compute prolog code size
   _method_size = 0;
--- a/src/hotspot/share/opto/parse1.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/parse1.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -609,15 +609,10 @@
   for (uint i = 0; i < (uint)arg_size_sig; i++) {
     Node* parm = map()->in(i);
     const Type* t = _gvn.type(parm);
-    if (!ValueTypePassFieldsAsArgs) {
-      if (t->is_valuetypeptr() && t->value_klass()->is_scalarizable() && !t->maybe_null()) {
-        // Create ValueTypeNode from the oop and replace the parameter
-        Node* vt = ValueTypeNode::make_from_oop(this, parm, t->value_klass());
-        map()->replace_edge(parm, vt);
-      }
-    } else {
-      assert(false, "FIXME");
-      // TODO move the code from build_start_state and do_late_inline here
+    if (t->is_valuetypeptr() && t->value_klass()->is_scalarizable() && !t->maybe_null()) {
+      // Create ValueTypeNode from the oop and replace the parameter
+      Node* vt = ValueTypeNode::make_from_oop(this, parm, t->value_klass());
+      map()->replace_edge(parm, vt);
     }
   }
 
@@ -842,12 +837,14 @@
 // Construct a state which contains only the incoming arguments from an
 // unknown caller.  The method & bci will be NULL & InvocationEntryBci.
 JVMState* Compile::build_start_state(StartNode* start, const TypeFunc* tf) {
-  int        arg_size_sig = tf->domain_sig()->cnt();
-  int        max_size = MAX2(arg_size_sig, (int)tf->range_cc()->cnt());
+  int        arg_size = tf->domain_sig()->cnt();
+  int        max_size = MAX2(arg_size, (int)tf->range_cc()->cnt());
   JVMState*  jvms     = new (this) JVMState(max_size - TypeFunc::Parms);
   SafePointNode* map  = new SafePointNode(max_size, NULL);
+  map->set_jvms(jvms);
+  jvms->set_map(map);
   record_for_igvn(map);
-  assert(arg_size_sig == TypeFunc::Parms + (is_osr_compilation() ? 1 : method()->arg_size()), "correct arg_size");
+  assert(arg_size == TypeFunc::Parms + (is_osr_compilation() ? 1 : method()->arg_size()), "correct arg_size");
   Node_Notes* old_nn = default_node_notes();
   if (old_nn != NULL && has_method()) {
     Node_Notes* entry_nn = old_nn->clone(this);
@@ -859,56 +856,41 @@
   }
   PhaseGVN& gvn = *initial_gvn();
   uint j = 0;
-  for (uint i = 0; i < (uint)arg_size_sig; i++) {
-    assert(j >= i, "less actual arguments than in the signature?");
-    if (ValueTypePassFieldsAsArgs) {
-      assert(false, "FIXME");
-      // TODO move this into Parse::Parse because we might need to deopt
-      /*
-      if (i < TypeFunc::Parms) {
-        assert(i == j, "no change before the actual arguments");
-        Node* parm = gvn.transform(new ParmNode(start, i));
-        map->init_req(i, parm);
-        // Record all these guys for later GVN.
-        record_for_igvn(parm);
-        j++;
-      } else {
-        // Value type arguments are not passed by reference: we get an
-        // argument per field of the value type. Build ValueTypeNodes
-        // from the value type arguments.
-        const Type* t = tf->domain_sig()->field_at(i);
-        if (t->is_valuetypeptr()) {
-          ciValueKlass* vk = t->value_klass();
-          GraphKit kit(jvms, &gvn);
-          kit.set_control(map->control());
-          ValueTypeNode* vt = ValueTypeNode::make_from_multi(&kit, start, vk, j, true);
-          map->set_control(kit.control());
-          map->init_req(i, vt);
-          j += vk->value_arg_slots();
-        } else {
-          Node* parm = gvn.transform(new ParmNode(start, j));
-          map->init_req(i, parm);
-          // Record all these guys for later GVN.
-          record_for_igvn(parm);
-          j++;
-        }
+  for (uint i = 0; i < (uint)arg_size; i++) {
+    const Type* t = tf->domain_sig()->field_at(i);
+    Node* parm = NULL;
+    // TODO for now, don't scalarize value type receivers because of interface calls
+    if (has_scalarized_args() && t->is_valuetypeptr() && (method()->is_static() || i != TypeFunc::Parms)) {
+      // Value type arguments are not passed by reference: we get an argument per
+      // field of the value type. Build ValueTypeNodes from the value type arguments.
+      GraphKit kit(jvms, &gvn);
+      kit.set_control(map->control());
+      Node* old_mem = map->memory();
+      // Use immutable memory for value type loads and restore it below
+      // TODO make sure value types are always loaded from immutable memory
+      kit.set_all_memory(C->immutable_memory());
+      parm = ValueTypeNode::make_from_multi(&kit, start, t->value_klass(), j, true);
+      map->set_control(kit.control());
+      map->set_memory(old_mem);
+    } else {
+      int index = j;
+      SigEntry res_slot = get_res_entry();
+      if (res_slot._offset != -1 && (index - TypeFunc::Parms) >= res_slot._offset) {
+        // Skip reserved entry
+        index += type2size[res_slot._bt];
       }
-      */
-    } else {
-      Node* parm = gvn.transform(new ParmNode(start, i));
-      map->init_req(i, parm);
-      // Record all these guys for later GVN.
-      record_for_igvn(parm);
+      parm = gvn.transform(new ParmNode(start, index));
       j++;
     }
+    map->init_req(i, parm);
+    // Record all these guys for later GVN.
+    record_for_igvn(parm);
   }
   for (; j < map->req(); j++) {
     map->init_req(j, top());
   }
   assert(jvms->argoff() == TypeFunc::Parms, "parser gets arguments here");
   set_default_node_notes(old_nn);
-  map->set_jvms(jvms);
-  jvms->set_map(map);
   return jvms;
 }
 
@@ -943,10 +925,14 @@
     if (tf()->returns_value_type_as_fields()) {
       // Multiple return values (value type fields): add as many edges
       // to the Return node as returned values.
-      assert(res->is_ValueType(), "what else supports multi value return");
+      assert(res->is_ValueType(), "what else supports multi value return?");
       ValueTypeNode* vt = res->as_ValueType();
       ret->add_req_batch(NULL, tf()->range_cc()->cnt() - TypeFunc::Parms);
-      vt->pass_klass(ret, TypeFunc::Parms, kit);
+      if (vt->is_allocated(&kit.gvn()) && !StressValueTypeReturnedAsFields) {
+        ret->init_req(TypeFunc::Parms, vt->get_oop());
+      } else {
+        ret->init_req(TypeFunc::Parms, vt->tagged_klass(kit.gvn()));
+      }
       vt->pass_fields(ret, TypeFunc::Parms+1, kit, /* assert_allocated */ true);
     } else {
       ret->add_req(res);
@@ -2324,12 +2310,6 @@
 //------------------------------return_current---------------------------------
 // Append current _map to _exit_return
 void Parse::return_current(Node* value) {
-  if (tf()->returns_value_type_as_fields()) {
-    assert(false, "Fix this with the calling convention changes");
-    // Value type is returned as fields, make sure non-flattened value type fields are allocated
-    // value = value->as_ValueType()->allocate_fields(this);
-  }
-
   if (RegisterFinalizersAtInit &&
       method()->intrinsic_id() == vmIntrinsics::_Object_init) {
     call_register_finalizer();
@@ -2350,9 +2330,19 @@
   if (value != NULL) {
     Node* phi = _exits.argument(0);
     const TypeOopPtr* tr = phi->bottom_type()->isa_oopptr();
+    if (tf()->returns_value_type_as_fields() && !_caller->has_method() && !value->is_ValueType()) {
+      // TODO there should be a checkcast in between, right?
+      value = ValueTypeNode::make_from_oop(this, value, phi->bottom_type()->is_valuetype()->value_klass());
+    }
     if (value->is_ValueType() && !_caller->has_method()) {
-      // Value type is returned as oop from root method, make sure it's allocated
-      value = value->as_ValueType()->allocate(this)->get_oop();
+      // Value type is returned as oop from root method
+      if (tf()->returns_value_type_as_fields()) {
+        // Make sure non-flattened value type fields are allocated
+        value = value->as_ValueType()->allocate_fields(this);
+      } else {
+        // Make sure value type is allocated
+        value = value->as_ValueType()->allocate(this)->get_oop();
+      }
     } else if (tr && tr->isa_instptr() && tr->klass()->is_loaded() && tr->klass()->is_interface()) {
       // If returning oops to an interface-return, there is a silent free
       // cast from oop to interface allowed by the Verifier. Make it explicit here.
--- a/src/hotspot/share/opto/type.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/type.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -138,7 +138,6 @@
   { Bad,             T_ADDRESS,    "rawptr:",       false, Op_RegP,              relocInfo::none          },  // RawPtr
   { Bad,             T_OBJECT,     "oop:",          true,  Op_RegP,              relocInfo::oop_type      },  // OopPtr
   { Bad,             T_OBJECT,     "inst:",         true,  Op_RegP,              relocInfo::oop_type      },  // InstPtr
-  { Bad,             T_VALUETYPEPTR,"valueptr:",     true,  Op_RegP,              relocInfo::oop_type      },  // ValueTypePtr
   { Bad,             T_OBJECT,     "ary:",          true,  Op_RegP,              relocInfo::oop_type      },  // AryPtr
   { Bad,             T_METADATA,   "metadata:",     false, Op_RegP,              relocInfo::metadata_type },  // MetadataPtr
   { Bad,             T_METADATA,   "klass:",        false, Op_RegP,              relocInfo::metadata_type },  // KlassPtr
@@ -642,7 +641,6 @@
   TypeAryPtr::_array_body_type[T_OBJECT]  = TypeAryPtr::OOPS;
   TypeAryPtr::_array_body_type[T_VALUETYPE] = TypeAryPtr::OOPS;
   TypeAryPtr::_array_body_type[T_ARRAY]   = TypeAryPtr::OOPS; // arrays are stored in oop arrays
-  TypeAryPtr::_array_body_type[T_VALUETYPEPTR] = NULL;
   TypeAryPtr::_array_body_type[T_BYTE]    = TypeAryPtr::BYTES;
   TypeAryPtr::_array_body_type[T_BOOLEAN] = TypeAryPtr::BYTES;  // boolean[] is a byte array
   TypeAryPtr::_array_body_type[T_SHORT]   = TypeAryPtr::SHORTS;
@@ -691,7 +689,6 @@
   _const_basic_type[T_FLOAT]       = Type::FLOAT;
   _const_basic_type[T_DOUBLE]      = Type::DOUBLE;
   _const_basic_type[T_OBJECT]      = TypeInstPtr::BOTTOM;
-  _const_basic_type[T_VALUETYPEPTR]= TypeInstPtr::BOTTOM;
   _const_basic_type[T_ARRAY]       = TypeInstPtr::BOTTOM; // there is no separate bottom for arrays
   _const_basic_type[T_VALUETYPE]   = TypeInstPtr::BOTTOM;
   _const_basic_type[T_VOID]        = TypePtr::NULL_PTR;   // reflection represents void this way
@@ -709,7 +706,6 @@
   _zero_type[T_FLOAT]       = TypeF::ZERO;
   _zero_type[T_DOUBLE]      = TypeD::ZERO;
   _zero_type[T_OBJECT]      = TypePtr::NULL_PTR;
-  _zero_type[T_VALUETYPEPTR]= TypePtr::NULL_PTR;
   _zero_type[T_ARRAY]       = TypePtr::NULL_PTR; // null array is null oop
   _zero_type[T_VALUETYPE]   = TypePtr::NULL_PTR;
   _zero_type[T_ADDRESS]     = TypePtr::NULL_PTR; // raw pointers use the same null
@@ -1040,7 +1036,6 @@
   Bad,          // RawPtr - handled in v-call
   Bad,          // OopPtr - handled in v-call
   Bad,          // InstPtr - handled in v-call
-  Bad,          // ValueTypePtr - handled in v-call
   Bad,          // AryPtr - handled in v-call
 
   Bad,          //  MetadataPtr - handled in v-call
@@ -1927,8 +1922,15 @@
 const TypeTuple *TypeTuple::INT_CC_PAIR;
 const TypeTuple *TypeTuple::LONG_CC_PAIR;
 
-static void collect_value_fields(ciValueKlass* vk, const Type** field_array, uint& pos) {
+static void collect_value_fields(ciValueKlass* vk, const Type** field_array, uint& pos, SigEntry* res_entry = NULL) {
   for (int j = 0; j < vk->nof_nonstatic_fields(); j++) {
+    if (res_entry != NULL && (int)pos == (res_entry->_offset + TypeFunc::Parms)) {
+      // Add reserved entry
+      field_array[pos++] = Type::get_const_basic_type(res_entry->_bt);
+      if (res_entry->_bt == T_LONG || res_entry->_bt == T_DOUBLE) {
+        field_array[pos++] = Type::HALF;
+      }
+    }
     ciField* field = vk->nonstatic_field_at(j);
     BasicType bt = field->type()->basic_type();
     const Type* ft = Type::get_const_type(field->type());
@@ -1998,36 +2000,40 @@
 }
 
 // Make a TypeTuple from the domain of a method signature
-const TypeTuple *TypeTuple::make_domain(ciInstanceKlass* recv, ciSignature* sig, bool vt_fields_as_args) {
+const TypeTuple *TypeTuple::make_domain(ciMethod* method, bool vt_fields_as_args) {
+  ciInstanceKlass* recv = method->is_static() ? NULL : method->holder();
+  ciSignature* sig = method->signature();
   uint arg_cnt = sig->size();
 
   int vt_extra = 0;
+  SigEntry res_entry = method->get_Method()->get_res_entry();
   if (vt_fields_as_args) {
     for (int i = 0; i < sig->count(); i++) {
       ciType* type = sig->type_at(i);
-      if (type->basic_type() == T_VALUETYPE) {
-        assert(type->is_valuetype(), "inconsistent type");
-        ciValueKlass* vk = (ciValueKlass*)type;
-        vt_extra += vk->value_arg_slots()-1;
+      if (type->is_valuetype()) {
+        vt_extra += type->as_value_klass()->value_arg_slots()-1;
       }
     }
-    assert(((int)arg_cnt) + vt_extra >= 0, "negative number of actual arguments?");
+    if (res_entry._offset != -1) {
+      // Account for the reserved stack slot
+      vt_extra += type2size[res_entry._bt];
+    }
   }
 
   uint pos = TypeFunc::Parms;
   const Type **field_array;
   if (recv != NULL) {
     arg_cnt++;
-    bool vt_fields_for_recv = vt_fields_as_args && recv->is_valuetype();
+    // TODO for now, don't scalarize value type receivers because of interface calls
+    //bool vt_fields_for_recv = vt_fields_as_args && recv->is_valuetype();
+    bool vt_fields_for_recv = false;
     if (vt_fields_for_recv) {
-      ciValueKlass* vk = (ciValueKlass*)recv;
-      vt_extra += vk->value_arg_slots()-1;
+      vt_extra += recv->as_value_klass()->value_arg_slots()-1;
     }
     field_array = fields(arg_cnt + vt_extra);
     // Use get_const_type here because it respects UseUniqueSubclasses:
     if (vt_fields_for_recv) {
-      ciValueKlass* vk = (ciValueKlass*)recv;
-      collect_value_fields(vk, field_array, pos);
+      collect_value_fields(recv->as_value_klass(), field_array, pos, &res_entry);
     } else {
       field_array[pos++] = get_const_type(recv)->join_speculative(TypePtr::NOTNULL);
     }
@@ -2061,10 +2067,8 @@
       field_array[pos++] = TypeInt::INT;
       break;
     case T_VALUETYPE: {
-      assert(type->is_valuetype(), "inconsistent type");
       if (vt_fields_as_args) {
-        ciValueKlass* vk = (ciValueKlass*)type;
-        collect_value_fields(vk, field_array, pos);
+        collect_value_fields(type->as_value_klass(), field_array, pos, &res_entry);
       } else {
         field_array[pos++] = get_const_type(type)->join_speculative(sig->is_never_null_at(i) ? TypePtr::NOTNULL : TypePtr::BOTTOM);
       }
@@ -2074,6 +2078,14 @@
       ShouldNotReachHere();
     }
     i++;
+
+    if (vt_fields_as_args && (int)pos == (res_entry._offset + TypeFunc::Parms)) {
+      // Add reserved entry
+      field_array[pos++] = Type::get_const_basic_type(res_entry._bt);
+      if (res_entry._bt == T_LONG || res_entry._bt == T_DOUBLE) {
+        field_array[pos++] = Type::HALF;
+      }
+    }
   }
   assert(pos == TypeFunc::Parms + arg_cnt + vt_extra, "wrong number of arguments");
 
@@ -3210,7 +3222,6 @@
         ciField* field = vk->get_field_by_offset(foffset, false);
         assert(field != NULL, "missing field");
         BasicType bt = field->layout_type();
-        assert(bt != T_VALUETYPEPTR, "unexpected type");
         _is_ptr_to_narrowoop = (bt == T_OBJECT || bt == T_ARRAY || T_VALUETYPE);
       }
     } else if (klass()->is_instance_klass()) {
@@ -3253,7 +3264,6 @@
             _is_ptr_to_narrowoop = UseCompressedOops && (basic_elem_type == T_OBJECT ||
                                                          basic_elem_type == T_VALUETYPE ||
                                                          basic_elem_type == T_ARRAY);
-            assert(basic_elem_type != T_VALUETYPEPTR, "unexpected type");
           } else if (klass()->equals(ciEnv::current()->Object_klass())) {
             // Compile::find_alias_type() cast exactness on all types to verify
             // that it does not affect alias type.
@@ -5605,11 +5615,11 @@
   // calling convention (with a value type argument as a list of its
   // fields).
   if (method->is_static()) {
-    domain_sig = TypeTuple::make_domain(NULL, method->signature(), false);
-    domain_cc = TypeTuple::make_domain(NULL, method->signature(), ValueTypePassFieldsAsArgs);
+    domain_sig = TypeTuple::make_domain(method, false);
+    domain_cc = TypeTuple::make_domain(method, method->get_Method()->has_scalarized_args());
   } else {
-    domain_sig = TypeTuple::make_domain(method->holder(), method->signature(), false);
-    domain_cc = TypeTuple::make_domain(method->holder(), method->signature(), ValueTypePassFieldsAsArgs);
+    domain_sig = TypeTuple::make_domain(method, false);
+    domain_cc = TypeTuple::make_domain(method, method->get_Method()->has_scalarized_args());
   }
   const TypeTuple *range_sig = TypeTuple::make_range(method->signature(), false);
   const TypeTuple *range_cc = TypeTuple::make_range(method->signature(), ValueTypeReturnedAsFields);
--- a/src/hotspot/share/opto/type.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/type.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -101,7 +101,6 @@
     RawPtr,                     // Raw (non-oop) pointers
     OopPtr,                     // Any and all Java heap entities
     InstPtr,                    // Instance pointers (non-array objects)
-    ValueTypePtr,               // Oop to heap allocated value type
     AryPtr,                     // Array pointers
     // (Ptr order matters:  See is_ptr, isa_ptr, is_oopptr, isa_oopptr.)
 
@@ -692,7 +691,7 @@
   static const TypeTuple *make( uint cnt, const Type **fields );
   static const TypeTuple *make_range(ciSignature* sig, bool ret_vt_fields = false);
   static const TypeTuple *make_range(ciType* return_type, bool never_null = false, bool ret_vt_fields = false);
-  static const TypeTuple *make_domain(ciInstanceKlass* recv, ciSignature *sig, bool vt_fields_as_args = false);
+  static const TypeTuple *make_domain(ciMethod* method, bool vt_fields_as_args = false);
 
   // Subroutine call type with space allocated for argument types
   // Memory for Control, I_O, Memory, FramePtr, and ReturnAdr is allocated implicitly
--- a/src/hotspot/share/opto/valuetypenode.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/valuetypenode.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -254,8 +254,9 @@
   }
 }
 
-void ValueTypeBaseNode::initialize(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, int base_offset, int base_input, bool in) {
+void ValueTypeBaseNode::initialize(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, int base_offset, uint& base_input, bool in) {
   assert(base_offset >= 0, "offset in value type must be positive");
+  assert(base_input >= TypeFunc::Parms, "invalid base input");
   PhaseGVN& gvn = kit->gvn();
   for (uint i = 0; i < field_count(); i++) {
     ciType* ft = field_type(i);
@@ -263,7 +264,8 @@
     if (field_is_flattened(i)) {
       // Flattened value type field
       ValueTypeNode* vt = ValueTypeNode::make_uninitialized(gvn, ft->as_value_klass());
-      vt->initialize(kit, multi, vk, offset - value_klass()->first_field_offset(), base_input, in);
+      uint base = base_input;
+      vt->initialize(kit, multi, vk, offset - value_klass()->first_field_offset(), base, in);
       set_field_value(i, gvn.transform(vt));
     } else {
       int j = 0; int extra = 0;
@@ -280,26 +282,40 @@
       }
       assert(j != vk->nof_nonstatic_fields(), "must find");
       Node* parm = NULL;
+      int index = base_input + j + extra;
+
+      ciMethod* method = multi->is_Start()? kit->C->method() : multi->as_CallStaticJava()->method();
+      SigEntry res_entry = method->get_Method()->get_res_entry();
+      if (res_entry._offset != -1 && (index - TypeFunc::Parms) >= res_entry._offset) {
+        // Skip reserved entry
+        index += type2size[res_entry._bt];
+      }
       if (multi->is_Start()) {
         assert(in, "return from start?");
-        parm = gvn.transform(new ParmNode(multi->as_Start(), base_input + j + extra));
+        parm = gvn.transform(new ParmNode(multi->as_Start(), index));
       } else {
         if (in) {
-          parm = multi->as_Call()->in(base_input + j + extra);
+          parm = multi->as_Call()->in(index);
         } else {
-          parm = gvn.transform(new ProjNode(multi->as_Call(), base_input + j + extra));
+          parm = gvn.transform(new ProjNode(multi->as_Call(), index));
         }
       }
-      if (ft->is_valuetype()) {
-        // Non-flattened value type field
-        assert(!gvn.type(parm)->maybe_null(), "should never be null");
-        parm = ValueTypeNode::make_from_oop(kit, parm, ft->as_value_klass());
+
+      if (field_is_flattenable(i)) {
+        // Non-flattened but flattenable value type
+        if (ft->as_value_klass()->is_scalarizable()) {
+          parm = ValueTypeNode::make_from_oop(kit, parm, ft->as_value_klass());
+        } else {
+          parm = kit->null2default(parm, ft->as_value_klass());
+        }
       }
+
       set_field_value(i, parm);
       // Record all these guys for later GVN.
       gvn.record_for_igvn(parm);
     }
   }
+  base_input += vk->value_arg_slots();
 }
 
 const TypePtr* ValueTypeBaseNode::field_adr_type(Node* base, int offset, ciInstanceKlass* holder, PhaseGVN& gvn) const {
@@ -602,7 +618,7 @@
   return kit->gvn().transform(vt)->as_ValueType();
 }
 
-ValueTypeNode* ValueTypeNode::make_from_multi(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, int base_input, bool in) {
+ValueTypeNode* ValueTypeNode::make_from_multi(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, uint& base_input, bool in) {
   ValueTypeNode* vt = ValueTypeNode::make_uninitialized(kit->gvn(), vk);
   vt->initialize(kit, multi, vk, 0, base_input, in);
   return kit->gvn().transform(vt)->as_ValueType();
@@ -686,11 +702,8 @@
   return gvn.makecon(TypeRawPtr::make((address)bits));
 }
 
-void ValueTypeNode::pass_klass(Node* n, uint pos, const GraphKit& kit) {
-  n->init_req(pos, tagged_klass(kit.gvn()));
-}
-
 uint ValueTypeNode::pass_fields(Node* n, int base_input, GraphKit& kit, bool assert_allocated, ciValueKlass* base_vk, int base_offset) {
+  assert(base_input >= TypeFunc::Parms, "invalid base input");
   ciValueKlass* vk = value_klass();
   if (base_vk == NULL) {
     base_vk = vk;
@@ -721,13 +734,29 @@
         assert(!assert_allocated || vt->is_allocated(&kit.gvn()), "value type field should be allocated");
         arg = vt->allocate(&kit)->get_oop();
       }
-      n->init_req(base_input + j + extra, arg);
+
+      int index = base_input + j + extra;
+      n->init_req(index++, arg);
       edges++;
       BasicType bt = field_type(i)->basic_type();
       if (bt == T_LONG || bt == T_DOUBLE) {
-        n->init_req(base_input + j + extra + 1, kit.top());
+        n->init_req(index++, kit.top());
         edges++;
       }
+      if (n->isa_CallJava()) {
+        Method* m = n->as_CallJava()->method()->get_Method();
+        SigEntry res_entry = m->get_res_entry();
+        if ((index - TypeFunc::Parms) == res_entry._offset) {
+          // Skip reserved entry
+          int size = type2size[res_entry._bt];
+          n->init_req(index++, kit.top());
+          if (size == 2) {
+            n->init_req(index++, kit.top());
+          }
+          base_input += size;
+          edges += size;
+        }
+      }
     }
   }
   return edges;
--- a/src/hotspot/share/opto/valuetypenode.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/opto/valuetypenode.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -51,7 +51,7 @@
   int make_scalar_in_safepoint(Unique_Node_List& worklist, SafePointNode* sfpt, Node* root, PhaseGVN* gvn);
 
   // Initialize the value type fields with the inputs or outputs of a MultiNode
-  void initialize(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, int base_offset = 0, int base_input = TypeFunc::Parms+1, bool in = false);
+  void initialize(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, int base_offset, uint& base_input, bool in);
 
   const TypePtr* field_adr_type(Node* base, int offset, ciInstanceKlass* holder, PhaseGVN& gvn) const;
 
@@ -125,7 +125,7 @@
   // Create and initialize by loading the field values from a flattened field or array
   static ValueTypeNode* make_from_flattened(GraphKit* kit, ciValueKlass* vk, Node* obj, Node* ptr, ciInstanceKlass* holder = NULL, int holder_offset = 0);
   // Create and initialize with the inputs or outputs of a MultiNode (method entry or call)
-  static ValueTypeNode* make_from_multi(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, int base_input, bool in);
+  static ValueTypeNode* make_from_multi(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, uint& base_input, bool in);
 
   // Returns the constant oop of the default value type allocation
   static Node* default_oop(PhaseGVN& gvn, ciValueKlass* vk);
@@ -134,7 +134,6 @@
   Node* allocate_fields(GraphKit* kit);
 
   Node* tagged_klass(PhaseGVN& gvn);
-  void pass_klass(Node* n, uint pos, const GraphKit& kit);
   uint pass_fields(Node* call, int base_input, GraphKit& kit, bool assert_allocated = false, ciValueKlass* base_vk = NULL, int base_offset = 0);
 
   // Allocation optimizations
--- a/src/hotspot/share/runtime/arguments.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/runtime/arguments.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -2063,16 +2063,12 @@
     }
   }
 
-  // FIXME
-  //if (LP64_ONLY(false &&) !FLAG_IS_DEFAULT(ValueTypePassFieldsAsArgs)) {
-  if (!FLAG_IS_DEFAULT(ValueTypePassFieldsAsArgs)) {
+  if (LP64_ONLY(false &&) !FLAG_IS_DEFAULT(ValueTypePassFieldsAsArgs)) {
     FLAG_SET_CMDLINE(bool, ValueTypePassFieldsAsArgs, false);
     warning("ValueTypePassFieldsAsArgs is not supported on this platform");
   }
 
-  // FIXME
-  //if (LP64_ONLY(false &&) !FLAG_IS_DEFAULT(ValueTypeReturnedAsFields)) {
-  if (!FLAG_IS_DEFAULT(ValueTypeReturnedAsFields)) {
+  if (LP64_ONLY(false &&) !FLAG_IS_DEFAULT(ValueTypeReturnedAsFields)) {
     FLAG_SET_CMDLINE(bool, ValueTypeReturnedAsFields, false);
     warning("ValueTypeReturnedAsFields is not supported on this platform");
   }
@@ -2089,6 +2085,14 @@
         warning("C1 doesn't work with C2 yet. Forcing TieredStopAtLevel=1");
         FLAG_SET_CMDLINE(intx, TieredStopAtLevel, 1);
       }
+      if (ValueTypePassFieldsAsArgs) {
+        warning("C1 doesn't work with ValueTypePassFieldsAsArgs yet. Forcing ValueTypePassFieldsAsArgs=false");
+        FLAG_SET_CMDLINE(bool, ValueTypePassFieldsAsArgs, false);
+      }
+      if (ValueTypeReturnedAsFields) {
+        warning("C1 doesn't work with ValueTypeReturnedAsFields yet. Forcing ValueTypeReturnedAsFields=false");
+        FLAG_SET_CMDLINE(bool, ValueTypeReturnedAsFields, false);
+      }
     }
   } else {
     FLAG_SET_CMDLINE(bool, ValueArrayFlatten, false);
--- a/src/hotspot/share/runtime/deoptimization.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/runtime/deoptimization.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -503,7 +503,7 @@
     caller_adjustment = last_frame_adjust(callee_parameters, callee_locals);
   }
 
-  // If the sender is deoptimized the we must retrieve the address of the handler
+  // If the sender is deoptimized we must retrieve the address of the handler
   // since the frame will "magically" show the original pc before the deopt
   // and we'd undo the deopt.
 
@@ -1008,17 +1008,15 @@
     if (!fs.access_flags().is_static() && (!skip_internal || !fs.access_flags().is_internal())) {
       ReassignedField field;
       field._offset = fs.offset();
-      field._type = fs.is_flattened() ? T_VALUETYPE : FieldType::basic_type(fs.signature());
+      field._type = FieldType::basic_type(fs.signature());
       if (field._type == T_VALUETYPE) {
-        if (fs.is_flattened()) {
-          // Resolve klass of flattened value type field
-          Klass* vk = klass->get_value_field_klass(fs.index());
-          assert(vk->is_value(), "must be a ValueKlass");
-          field._klass = InstanceKlass::cast(vk);
-        } else {
-          // Non-flattened value type field
-          field._type = T_VALUETYPEPTR;
-        }
+        field._type = T_OBJECT;
+      }
+      if (fs.is_flattened()) {
+        // Resolve klass of flattened value type field
+        Klass* vk = klass->get_value_field_klass(fs.index());
+        field._klass = ValueKlass::cast(vk);
+        field._type = T_VALUETYPE;
       }
       fields->append(field);
     }
@@ -1032,7 +1030,6 @@
     BasicType type = fields->at(i)._type;
     switch (type) {
       case T_OBJECT:
-      case T_VALUETYPEPTR:
       case T_ARRAY:
         assert(value->type() == T_OBJECT, "Agreement.");
         obj->obj_field_put(offset, value->get_obj()());
--- a/src/hotspot/share/runtime/globals.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/runtime/globals.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -2631,13 +2631,16 @@
           "Enable C1 compiler for Valhalla")                                \
                                                                             \
   product_pd(bool, ValueTypePassFieldsAsArgs,                               \
-             "Pass each value type field as an argument at calls")          \
+          "Pass each value type field as an argument at calls")             \
                                                                             \
   product_pd(bool, ValueTypeReturnedAsFields,                               \
-            "return fields instead of a value type reference")              \
+          "Return fields instead of a value type reference")                \
+                                                                            \
+  develop(bool, StressValueTypePassFieldsAsArgs, false,                     \
+          "Stress passing each value type field as an argument at calls")   \
                                                                             \
   develop(bool, StressValueTypeReturnedAsFields, false,                     \
-          "stress return of fields instead of a value type reference")      \
+          "Stress return of fields instead of a value type reference")      \
                                                                             \
   develop(bool, ScalarizeValueTypes, true,                                  \
           "Scalarize value types in compiled code")                         \
--- a/src/hotspot/share/runtime/safepoint.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/runtime/safepoint.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -1169,16 +1169,23 @@
 
     GrowableArray<Handle> return_values;
     ValueKlass* vk = NULL;
-    if (method->is_returning_vt() && ValueTypeReturnedAsFields) {
-      // Check if value type is returned as fields
-      vk = ValueKlass::returned_value_klass(map);
-      if (vk != NULL) {
-        // We're at a safepoint at the return of a method that returns
-        // multiple values. We must make sure we preserve the oop values
-        // across the safepoint.
-        assert(vk == method->returned_value_type(thread()), "bad value klass");
-        vk->save_oop_fields(map, return_values);
-        return_oop = false;
+
+    if (return_oop && ValueTypeReturnedAsFields) {
+      SignatureStream ss(method->signature());
+      while (!ss.at_return_type()) {
+        ss.next();
+      }
+      if (ss.type() == T_VALUETYPE) {
+        // Check if value type is returned as fields
+        vk = ValueKlass::returned_value_klass(map);
+        if (vk != NULL) {
+          // We're at a safepoint at the return of a method that returns
+          // multiple values. We must make sure we preserve the oop values
+          // across the safepoint.
+          assert(vk == method->returned_value_type(thread()), "bad value klass");
+          vk->save_oop_fields(map, return_values);
+          return_oop = false;
+        }
       }
     }
 
--- a/src/hotspot/share/runtime/sharedRuntime.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -1151,7 +1151,8 @@
         THROW_(vmSymbols::java_lang_NoSuchMethodException(), nullHandle);
       }
     }
-    if (ValueTypePassFieldsAsArgs && callee->method_holder()->is_value()) {
+    // TODO for now, don't scalarize value type receivers because of interface calls
+    if (ValueTypePassFieldsAsArgs && callee->method_holder()->is_value() && false) {
       // 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");
@@ -2296,9 +2297,6 @@
         return ValueTypePassFieldsAsArgs ? in : adapter_encoding(T_OBJECT, false);
       }
 
-      case T_VALUETYPEPTR:
-        return T_VALUETYPE; // TODO hack because we don't have enough bits to represent T_VALUETYPEPTR.
-
       case T_OBJECT:
       case T_ARRAY:
         // In other words, we assume that any register good enough for
@@ -2323,7 +2321,7 @@
   }
 
  public:
-  AdapterFingerPrint(int total_args_passed, BasicType* sig_bt) {
+  AdapterFingerPrint(int total_args_passed, const GrowableArray<SigEntry>* sig) {
     // The fingerprint is based on the BasicType signature encoded
     // into an array of ints with eight entries per int.
     int* ptr;
@@ -2350,7 +2348,7 @@
       for (int byte = 0; byte < _basic_types_per_int; byte++) {
         int bt = 0;
         if (sig_index < total_args_passed) {
-          BasicType sbt = sig_bt[sig_index++];
+          BasicType sbt = sig->at(sig_index++)._bt;
           if (ValueTypePassFieldsAsArgs && sbt == T_VALUETYPE) {
             // Found start of value type in signature
             vt_count++;
@@ -2454,9 +2452,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, Symbol* sig_extended) {
+  AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_value_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, sig_extended);
+    entry->init(fingerprint, i2c_entry, c2i_entry, c2i_value_entry, c2i_unverified_entry);
     if (DumpSharedSpaces) {
       ((CDSAdapterHandlerEntry*)entry)->init();
     }
@@ -2475,9 +2473,9 @@
   }
 
   // Find a entry with the same fingerprint if it exists
-  AdapterHandlerEntry* lookup(int total_args_passed, BasicType* sig_bt) {
+  AdapterHandlerEntry* lookup(int total_args_passed, const GrowableArray<SigEntry>* sig) {
     NOT_PRODUCT(_lookups++);
-    AdapterFingerPrint fp(total_args_passed, sig_bt);
+    AdapterFingerPrint fp(total_args_passed, sig);
     unsigned int hash = fp.compute_hash();
     int index = hash_to_index(hash);
     for (AdapterHandlerEntry* e = bucket(index); e != NULL; e = e->next()) {
@@ -2599,19 +2597,19 @@
   address wrong_method_abstract = SharedRuntime::get_handle_wrong_method_abstract_stub();
   _abstract_method_handler = AdapterHandlerLibrary::new_entry(new AdapterFingerPrint(0, NULL),
                                                               StubRoutines::throw_AbstractMethodError_entry(),
-                                                              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_unverified_entry,
-                                                      Symbol* sig_extended) {
-  return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, sig_extended);
+                                                      address c2i_value_entry,
+                                                      address c2i_unverified_entry) {
+  return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_value_entry, c2i_unverified_entry);
 }
 
-AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& method, TRAPS) {
-  AdapterHandlerEntry* entry = get_adapter0(method, CHECK_NULL);
+AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& method) {
+  AdapterHandlerEntry* entry = get_adapter0(method);
   if (method->is_shared()) {
     // See comments around Method::link_method()
     MutexLocker mu(AdapterHandlerLibrary_lock);
@@ -2634,7 +2632,7 @@
   return entry;
 }
 
-AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter0(const methodHandle& method, TRAPS) {
+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
@@ -2655,68 +2653,101 @@
       return _abstract_method_handler;
     }
 
-    // Fill in the signature array, for the calling-convention call.
-    GrowableArray<SigEntry> sig_extended;
-    {
+    bool has_value_arg = false;
+    GrowableArray<SigEntry> sig(method->size_of_parameters());
+    if (!method->is_static()) {
+      // TODO for now, don't scalarize value type receivers because of interface calls
+      //has_value_arg |= method->method_holder()->is_value();
+      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);
+    }
+
+    // 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);
+
+    // Now compute the scalarized calling convention if there are value types in the signature
+    GrowableArray<SigEntry> sig_cc = sig;
+    VMRegPair* regs_cc = regs;
+    SigEntry reserved_entry;
+    int args_on_stack_cc = args_on_stack;
+
+    if (ValueTypePassFieldsAsArgs && has_value_arg) {
       MutexUnlocker mul(AdapterHandlerLibrary_lock);
-      Thread* THREAD = Thread::current();
-      Klass* holder = method->method_holder();
-      GrowableArray<BasicType> sig_bt_tmp;
-
-      int i = 0;
-      if (!method->is_static()) {  // Pass in receiver first
-        if (holder->is_value()) {
-          ValueKlass* vk = ValueKlass::cast(holder);
-          if (!ValueTypePassFieldsAsArgs) {
-            // If we don't pass value types as arguments or if the holder of
-            // the method is __Value, we must pass a reference.
-            sig_extended.push(SigEntry(T_VALUETYPEPTR));
-          } else {
-            const Array<SigEntry>* sig_vk = vk->extended_sig();
-            sig_extended.appendAll(sig_vk);
-          }
+      InstanceKlass* holder = method->method_holder();
+
+      sig_cc = GrowableArray<SigEntry>(method->size_of_parameters());
+      if (!method->is_static()) {
+        // TODO for now, don't scalarize value type receivers because of interface calls
+        if (false && holder->is_value()) {
+          sig_cc.appendAll(ValueKlass::cast(holder)->extended_sig());
         } else {
-          sig_extended.push(SigEntry(T_OBJECT));
+          SigEntry::add_entry(&sig_cc, T_OBJECT);
         }
       }
+      Thread* THREAD = Thread::current();
       for (SignatureStream ss(method->signature()); !ss.at_return_type(); ss.next()) {
-        Symbol* sym = ss.as_symbol_or_null();
-        if (sym != NULL && sym->is_Q_signature()) {
-          if (!ValueTypePassFieldsAsArgs) {
-            sig_extended.push(SigEntry(T_VALUETYPEPTR));
-          } else {
-            // Method handle intrinsics with a __Value argument may be created during
-            // compilation. Only do a full system dictionary lookup if the argument name
-            // is not __Value, to avoid lookups from the compiler thread.
-            Klass* k = ss.as_klass(Handle(THREAD, holder->class_loader()),
-                                   Handle(THREAD, holder->protection_domain()),
-                                   SignatureStream::ReturnNull, CHECK_NULL);
-            const Array<SigEntry>* sig_vk = ValueKlass::cast(k)->extended_sig();
-            sig_extended.appendAll(sig_vk);
-          }
+        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 {
-          sig_extended.push(SigEntry(ss.type()));
-          if (ss.type() == T_LONG || ss.type() == T_DOUBLE) {
-            sig_extended.push(SigEntry(T_VOID));
+          SigEntry::add_entry(&sig_cc, ss.type());
+        }
+      }
+      regs_cc = NEW_RESOURCE_ARRAY(VMRegPair, sig_cc.length() + 2);
+      args_on_stack_cc = SharedRuntime::java_calling_convention(&sig_cc, regs_cc);
+
+      // This stack slot is occupied by the return address with the unscalarized calling
+      // convention. Don't use it for argument with the scalarized calling convention.
+      int ret_addr_slot = args_on_stack_cc - args_on_stack;
+      if (ret_addr_slot > 0) {
+        // Make sure stack of the scalarized calling convention with
+        // the reserved entry (2 slots) is 16-byte (4 slots) aligned.
+        int alignment = StackAlignmentInBytes/VMRegImpl::stack_slot_size;
+        ret_addr_slot = align_up(ret_addr_slot + 2, alignment) - 2;
+        // Find index in signature that belongs to return address slot
+        reserved_entry._offset = 0;
+        int sig_idx = 0;
+        for (; sig_idx < sig_cc.length(); ++sig_idx) {
+          if (SigEntry::skip_value_delimiters(&sig_cc, sig_idx)) {
+            VMReg first = regs_cc[reserved_entry._offset].first();
+            if (first->is_stack()) {
+              // Select a type for the reserved entry that will end up on the stack
+              reserved_entry._bt = sig_cc.at(sig_idx)._bt;
+              if ((int)first->reg2stack() == ret_addr_slot) {
+                break;
+              }
+            }
+            reserved_entry._offset++;
           }
         }
+        // Insert reserved entry and re-compute calling convention
+        SigEntry::insert_reserved_entry(&sig_cc, sig_idx, reserved_entry._bt);
+        args_on_stack_cc = SharedRuntime::java_calling_convention(&sig_cc, regs_cc);
+      }
+      // 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;
+        regs_cc = regs;
+        args_on_stack_cc = args_on_stack;
       }
     }
 
-    int total_args_passed_cc = ValueTypePassFieldsAsArgs ? SigEntry::count_fields(sig_extended) : sig_extended.length();
-    BasicType* sig_bt_cc = NEW_RESOURCE_ARRAY(BasicType, total_args_passed_cc);
-    SigEntry::fill_sig_bt(sig_extended, sig_bt_cc, total_args_passed_cc, ValueTypePassFieldsAsArgs);
-
-    int total_args_passed_fp = sig_extended.length();
-    BasicType* sig_bt_fp = NEW_RESOURCE_ARRAY(BasicType, total_args_passed_fp);
-    for (int i = 0; i < sig_extended.length(); i++) {
-      sig_bt_fp[i] = sig_extended.at(i)._bt;
-    }
-
-    VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed_cc);
-
     // Lookup method signature's fingerprint
-    entry = _adapters->lookup(total_args_passed_fp, sig_bt_fp);
+    entry = _adapters->lookup(sig_cc.length(), &sig_cc);
 
 #ifdef ASSERT
     AdapterHandlerEntry* shared_entry = NULL;
@@ -2731,11 +2762,8 @@
       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_cc, regs, total_args_passed_cc, false);
-
     // Make a C heap allocated version of the fingerprint to store in the adapter
-    fingerprint = new AdapterFingerPrint(total_args_passed_fp, sig_bt_fp);
+    fingerprint = new AdapterFingerPrint(sig_cc.length(), &sig_cc);
 
     // 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
@@ -2753,11 +2781,23 @@
 
       MacroAssembler _masm(&buffer);
       entry = SharedRuntime::generate_i2c2i_adapters(&_masm,
-                                                     comp_args_on_stack,
-                                                     sig_extended,
+                                                     args_on_stack,
+                                                     args_on_stack_cc,
+                                                     &sig,
                                                      regs,
+                                                     &sig_cc,
+                                                     regs_cc,
                                                      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>(method->size_of_parameters(), true);
+        heap_sig->appendAll(&sig_cc);
+        entry->set_sig_cc(heap_sig);
+        entry->set_res_entry(reserved_entry);
+      }
+
 #ifdef ASSERT
       if (VerifyAdapterSharing) {
         if (shared_entry != NULL) {
@@ -2826,6 +2866,7 @@
   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_unverified_entry || _c2i_unverified_entry == NULL, "");
   return base;
 }
@@ -2838,6 +2879,8 @@
     _i2c_entry += delta;
   if (_c2i_entry != NULL)
     _c2i_entry += delta;
+  if (_c2i_value_entry != NULL)
+    _c2i_value_entry += delta;
   if (_c2i_unverified_entry != NULL)
     _c2i_unverified_entry += delta;
   assert(base_address() == new_base, "");
@@ -2846,6 +2889,9 @@
 
 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
@@ -2918,19 +2964,6 @@
       SignatureStream ss(method->signature());
       for (; !ss.at_return_type(); ss.next()) {
         BasicType bt = ss.type();
-        if (bt == T_VALUETYPE) {
-#ifdef ASSERT
-          Thread* THREAD = Thread::current();
-          // Avoid class loading from compiler thread
-          if (THREAD->can_call_java()) {
-            Handle class_loader(THREAD, method->method_holder()->class_loader());
-            Handle protection_domain(THREAD, method->method_holder()->protection_domain());
-            Klass* k = ss.as_klass(class_loader, protection_domain, SignatureStream::ReturnNull, THREAD);
-            assert(k != NULL && !HAS_PENDING_EXCEPTION, "can't resolve klass");
-          }
-#endif
-          bt = T_VALUETYPEPTR;
-        }
         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
@@ -3196,9 +3229,9 @@
 }
 
 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 " c2iMH: " INTPTR_FORMAT " c2iUV: " 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_unverified_entry()));
 
 }
 
@@ -3305,12 +3338,15 @@
   methodHandle callee(callee_method);
 
   int nb_slots = 0;
-  bool has_value_receiver = !callee->is_static() && callee->method_holder()->is_value();
+  InstanceKlass* holder = callee->method_holder();
+  // TODO for now, don't scalarize value type receivers because of interface calls
+  //bool has_value_receiver = !callee->is_static() && holder->is_value();
+  bool has_value_receiver = false;
   if (has_value_receiver) {
     nb_slots++;
   }
-  Handle class_loader(THREAD, callee->method_holder()->class_loader());
-  Handle protection_domain(THREAD, callee->method_holder()->protection_domain());
+  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++;
@@ -3320,7 +3356,7 @@
   objArrayHandle array(THREAD, array_oop);
   int i = 0;
   if (has_value_receiver) {
-    ValueKlass* vk = ValueKlass::cast(callee->method_holder());
+    ValueKlass* vk = ValueKlass::cast(holder);
     oop res = vk->allocate_instance(CHECK);
     array->obj_at_put(i, res);
     i++;
@@ -3377,7 +3413,7 @@
 
   ValueKlass* vk = ValueKlass::cast(res->klass());
 
-  const Array<SigEntry>* sig_vk = vk->extended_sig() ;
+  const Array<SigEntry>* sig_vk = vk->extended_sig();
   const Array<VMRegPair>* regs = vk->return_regs();
 
   if (regs == NULL) {
@@ -3399,44 +3435,43 @@
       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:
-      *(intptr_t*)loc = *(jboolean*)((address)res + off);
+      *(jboolean*)loc = res->bool_field(off);
       break;
     case T_CHAR:
-      *(intptr_t*)loc = *(jchar*)((address)res + off);
+      *(jchar*)loc = res->char_field(off);
       break;
     case T_BYTE:
-      *(intptr_t*)loc = *(jbyte*)((address)res + off);
+      *(jbyte*)loc = res->byte_field(off);
       break;
     case T_SHORT:
-      *(intptr_t*)loc = *(jshort*)((address)res + off);
+      *(jshort*)loc = res->short_field(off);
       break;
     case T_INT: {
-      jint v = *(jint*)((address)res + off);
-      *(intptr_t*)loc = v;
+      *(jint*)loc = res->int_field(off);
       break;
     }
     case T_LONG:
 #ifdef _LP64
-      *(intptr_t*)loc = *(jlong*)((address)res + off);
+      *(intptr_t*)loc = res->long_field(off);
 #else
       Unimplemented();
 #endif
       break;
     case T_OBJECT:
     case T_ARRAY: {
-      oop v = HeapAccess<>::oop_load_at(res, off);
-      *(oop*)loc = v;
+      *(oop*)loc = res->obj_field(off);
       break;
     }
     case T_FLOAT:
-      *(jfloat*)loc = *(jfloat*)((address)res + off);
+      *(jfloat*)loc = res->float_field(off);
       break;
     case T_DOUBLE:
-      *(jdouble*)loc = *(jdouble*)((address)res + off);
+      *(jdouble*)loc = res->double_field(off);
       break;
     default:
       ShouldNotReachHere();
@@ -3483,35 +3518,19 @@
   assert(verif_vk == vk, "broken calling convention");
   assert(Metaspace::contains((void*)res), "should be klass");
 
-  // Allocate handles for every oop fields so they are safe in case of
+  // 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
-
-  Handle new_vt;
   JRT_BLOCK;
   {
     Thread* THREAD = thread;
     oop vt = vk->realloc_result(reg_map, handles, CHECK);
-    new_vt = Handle(thread, vt);
-
-#ifdef ASSERT
-    javaVFrame* vf = javaVFrame::cast(vframe::new_vframe(&callerFrame, &reg_map, thread));
-    Method* m = vf->method();
-    int bci = vf->bci();
-    Bytecode_invoke inv(m, bci);
-
-    methodHandle callee = inv.static_target(thread);
-    assert(!thread->has_pending_exception(), "call resolution should work");
-    ValueKlass* verif_vk2 = callee->returned_value_type(thread);
-    assert(verif_vk == verif_vk2, "Bad value klass");
-#endif
+    thread->set_vm_result(vt);
   }
   JRT_BLOCK_END;
-
-  thread->set_vm_result(new_vt());
 }
 JRT_END
 
--- a/src/hotspot/share/runtime/sharedRuntime.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/runtime/sharedRuntime.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -372,6 +372,11 @@
   // will be just above it. (
   // return value is the maximum number of VMReg stack slots the convention will use.
   static int java_calling_convention(const BasicType* sig_bt, VMRegPair* regs, int total_args_passed, int is_outgoing);
+  static int java_calling_convention(const GrowableArray<SigEntry>* sig, VMRegPair* regs) {
+    BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sig->length());
+    int total_args_passed = SigEntry::fill_sig_bt(sig, sig_bt);
+    return java_calling_convention(sig_bt, regs, total_args_passed, false);
+  }
   static int java_return_convention(const BasicType* sig_bt, VMRegPair* regs, int total_args_passed);
   static const uint java_return_convention_max_int;
   static const uint java_return_convention_max_float;
@@ -424,14 +429,17 @@
 
   static AdapterHandlerEntry* generate_i2c2i_adapters(MacroAssembler *masm,
                                                       int comp_args_on_stack,
-                                                      const GrowableArray<SigEntry>& sig_extended,
-                                                      const VMRegPair *regs,
+                                                      int comp_args_on_stack_cc,
+                                                      const GrowableArray<SigEntry>* sig,
+                                                      const VMRegPair* regs,
+                                                      const GrowableArray<SigEntry>* sig_cc,
+                                                      const VMRegPair* regs_cc,
                                                       AdapterFingerPrint* fingerprint,
                                                       AdapterBlob*& new_adapter);
 
   static void gen_i2c_adapter(MacroAssembler *_masm,
                               int comp_args_on_stack,
-                              const GrowableArray<SigEntry>& sig_extended,
+                              const GrowableArray<SigEntry>* sig,
                               const VMRegPair *regs);
 
   // OSR support
@@ -640,8 +648,12 @@
   AdapterFingerPrint* _fingerprint;
   address _i2c_entry;
   address _c2i_entry;
+  address _c2i_value_entry;
   address _c2i_unverified_entry;
-  Symbol* _sig_extended;
+
+  // Support for scalarized value type calling convention
+  const GrowableArray<SigEntry>* _sig_cc;
+  SigEntry _res_sig_entry;
 
 #ifdef ASSERT
   // Captures code and signature used to generate this adapter when
@@ -650,12 +662,14 @@
   int            _saved_code_length;
 #endif
 
-  void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry, Symbol* sig_extended) {
+  void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_value_entry, address c2i_unverified_entry) {
     _fingerprint = fingerprint;
     _i2c_entry = i2c_entry;
     _c2i_entry = c2i_entry;
+    _c2i_value_entry = c2i_value_entry;
     _c2i_unverified_entry = c2i_unverified_entry;
-    _sig_extended = sig_extended;
+    _sig_cc = NULL;
+    _res_sig_entry = SigEntry();
 #ifdef ASSERT
     _saved_code = NULL;
     _saved_code_length = 0;
@@ -670,11 +684,17 @@
  public:
   address get_i2c_entry()            const { return _i2c_entry; }
   address get_c2i_entry()            const { return _c2i_entry; }
+  address get_c2i_value_entry()      const { return _c2i_value_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);
 
+  // Support for scalarized value type calling convention
+  void set_sig_cc(const GrowableArray<SigEntry>* sig)  { _sig_cc = sig; }
+  const GrowableArray<SigEntry>* get_sig_cc()    const { return _sig_cc; }
+  void     set_res_entry(SigEntry res_sig_entry)       { _res_sig_entry = res_sig_entry; }
+  SigEntry get_res_entry()                       const { return _res_sig_entry; }
+
   AdapterFingerPrint* fingerprint() const { return _fingerprint; }
 
   AdapterHandlerEntry* next() {
@@ -712,15 +732,14 @@
   static AdapterHandlerEntry* _abstract_method_handler;
   static BufferBlob* buffer_blob();
   static void initialize();
-  static AdapterHandlerEntry* get_adapter0(const methodHandle& method, TRAPS);
+  static AdapterHandlerEntry* get_adapter0(const methodHandle& method);
 
  public:
 
   static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint,
-                                        address i2c_entry, address c2i_entry, address c2i_unverified_entry,
-                                        Symbol* sig_extended = NULL);
+                                        address i2c_entry, address c2i_entry, address c2i_value_entry, address c2i_unverified_entry);
   static void create_native_wrapper(const methodHandle& method);
-  static AdapterHandlerEntry* get_adapter(const methodHandle& method, TRAPS);
+  static AdapterHandlerEntry* get_adapter(const methodHandle& method);
 
   static void print_handler(const CodeBlob* b) { print_handler_on(tty, b); }
   static void print_handler_on(outputStream* st, const CodeBlob* b);
--- a/src/hotspot/share/runtime/signature.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/runtime/signature.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -523,29 +523,70 @@
   }
 }
 
-int SigEntry::count_fields(const GrowableArray<SigEntry>& sig_extended) {
-  int values = 0;
-  for (int i = 0; i < sig_extended.length(); i++) {
-    if (sig_extended.at(i)._bt == T_VALUETYPE) {
-      values++;
+// Adds an argument to the signature
+void SigEntry::add_entry(GrowableArray<SigEntry>* sig, BasicType bt, int offset) {
+  sig->append(SigEntry(bt, offset));
+  if (bt == T_LONG || bt == T_DOUBLE) {
+    sig->append(SigEntry(T_VOID, offset)); // Longs and doubles take two stack slots
+  }
+}
+
+// Inserts a reserved argument at position 'i'
+void SigEntry::insert_reserved_entry(GrowableArray<SigEntry>* sig, int i, BasicType bt) {
+  if (bt == T_OBJECT || bt == T_ARRAY || bt == T_VALUETYPE) {
+    // Treat this as INT to not confuse the GC
+    bt = T_INT;
+  } else if (bt == T_LONG || bt == T_DOUBLE) {
+    // Longs and doubles take two stack slots
+    sig->insert_before(i, SigEntry(T_VOID, SigEntry::ReservedOffset));
+  }
+  sig->insert_before(i, SigEntry(bt, SigEntry::ReservedOffset));
+}
+
+// Returns true if the argument at index 'i' is a reserved argument
+bool SigEntry::is_reserved_entry(const GrowableArray<SigEntry>* sig, int i) {
+  return sig->at(i)._offset == SigEntry::ReservedOffset;
+}
+
+// Returns true if the argument at index 'i' is not a value type delimiter
+bool SigEntry::skip_value_delimiters(const GrowableArray<SigEntry>* sig, int i) {
+  return (sig->at(i)._bt != T_VALUETYPE &&
+          (sig->at(i)._bt != T_VOID || sig->at(i-1)._bt == T_LONG || sig->at(i-1)._bt == T_DOUBLE));
+}
+
+// Fill basic type array from signature array
+int SigEntry::fill_sig_bt(const GrowableArray<SigEntry>* sig, BasicType* sig_bt) {
+  int count = 0;
+  for (int i = 0; i < sig->length(); i++) {
+    if (skip_value_delimiters(sig, i)) {
+      sig_bt[count++] = sig->at(i)._bt;
     }
   }
-  return sig_extended.length() - 2 * values;
+  return count;
 }
 
-void SigEntry::fill_sig_bt(const GrowableArray<SigEntry>& sig_extended, BasicType* sig_bt_cc, int total_args_passed_cc, bool skip_vt) {
-  int j = 0;
-  for (int i = 0; i < sig_extended.length(); i++) {
-    if (!skip_vt) {
-      BasicType bt = sig_extended.at(i)._bt;
-      // assert(bt != T_VALUETYPE, "value types should be passed as fields or reference");
-      sig_bt_cc[j++] = bt;
-    } else if (sig_extended.at(i)._bt != T_VALUETYPE &&
-               (sig_extended.at(i)._bt != T_VOID ||
-                sig_extended.at(i-1)._bt == T_LONG ||
-                sig_extended.at(i-1)._bt == T_DOUBLE)) {
-      sig_bt_cc[j++] = sig_extended.at(i)._bt;
+// Create a temporary symbol from the signature array
+TempNewSymbol SigEntry::create_symbol(const GrowableArray<SigEntry>* sig) {
+  ResourceMark rm;
+  int length = sig->length();
+  char* sig_str = NEW_RESOURCE_ARRAY(char, 2*length + 3);
+  int idx = 0;
+  sig_str[idx++] = '(';
+  for (int i = 0; i < length; i++) {
+    BasicType bt = sig->at(i)._bt;
+    if (bt == T_VALUETYPE || bt == T_VOID) {
+      // Ignore
+    } else {
+      if (bt == T_ARRAY) {
+        bt = T_OBJECT; // We don't know the element type, treat as Object
+      }
+      sig_str[idx++] = type2char(bt);
+      if (bt == T_OBJECT) {
+        sig_str[idx++] = ';';
+      }
     }
   }
-  assert(j == total_args_passed_cc, "bad number of arguments");
+  sig_str[idx++] = ')';
+  sig_str[idx++] = '\0';
+  return SymbolTable::new_symbol(sig_str, Thread::current());
 }
--- a/src/hotspot/share/runtime/signature.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/runtime/signature.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -25,6 +25,7 @@
 #ifndef SHARE_VM_RUNTIME_SIGNATURE_HPP
 #define SHARE_VM_RUNTIME_SIGNATURE_HPP
 
+#include "classfile/symbolTable.hpp"
 #include "memory/allocation.hpp"
 #include "oops/method.hpp"
 
@@ -441,7 +442,9 @@
  public:
   BasicType _bt;
   int _offset;
-    
+
+  enum { ReservedOffset = -2 }; // Special offset to mark the reserved entry
+
   SigEntry()
     : _bt(T_ILLEGAL), _offset(-1) {
   }
@@ -450,7 +453,7 @@
 
   SigEntry(BasicType bt)
     : _bt(bt), _offset(-1) {}
-  
+
   static int compare(SigEntry* e1, SigEntry* e2) {
     if (e1->_offset != e2->_offset) {
       return e1->_offset - e2->_offset;
@@ -473,8 +476,12 @@
     ShouldNotReachHere();
     return 0;
   }
-  static int count_fields(const GrowableArray<SigEntry>& sig_extended);
-  static void fill_sig_bt(const GrowableArray<SigEntry>& sig_extended, BasicType* sig_bt_cc, int total_args_passed_cc, bool skip_vt);
+  static void add_entry(GrowableArray<SigEntry>* sig, BasicType bt, int offset = -1);
+  static void insert_reserved_entry(GrowableArray<SigEntry>* sig, int i, BasicType bt);
+  static bool is_reserved_entry(const GrowableArray<SigEntry>* sig, int i);
+  static bool skip_value_delimiters(const GrowableArray<SigEntry>* sig, int i);
+  static int fill_sig_bt(const GrowableArray<SigEntry>* sig, BasicType* sig_bt);
+  static TempNewSymbol create_symbol(const GrowableArray<SigEntry>* sig);
 };
 
 #endif // SHARE_VM_RUNTIME_SIGNATURE_HPP
--- a/src/hotspot/share/utilities/globalDefinitions.cpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/utilities/globalDefinitions.cpp	Tue Dec 18 16:05:31 2018 +0100
@@ -115,7 +115,6 @@
       case T_NARROWKLASS: // compressed klass pointer
       case T_CONFLICT:    // might as well support a bottom type
       case T_VOID:        // padding or other unaddressed word
-      case T_VALUETYPEPTR:
         // layout type must map to itself
         assert(vt == ft, "");
         break;
@@ -180,7 +179,7 @@
 
 
 // Map BasicType to signature character
-char type2char_tab[T_CONFLICT+1]={ 0, 0, 0, 0, 'Z', 'C', 'F', 'D', 'B', 'S', 'I', 'J', 'L', '[', 'Q', 'V', 0, 0, 0, 0, 0, 0};
+char type2char_tab[T_CONFLICT+1]={ 0, 0, 0, 0, 'Z', 'C', 'F', 'D', 'B', 'S', 'I', 'J', 'L', '[', 'Q', 'V', 0, 0, 0, 0, 0};
 
 // Map BasicType to Java type name
 const char* type2name_tab[T_CONFLICT+1] = {
@@ -201,7 +200,6 @@
   "*narrowoop*",
   "*metadata*",
   "*narrowklass*",
-  "valuetypeptr",
   "*conflict*"
 };
 
@@ -216,7 +214,7 @@
 }
 
 // Map BasicType to size in words
-int type2size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, 1, 1, 1, -1};
+int type2size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, 1, 1, -1};
 
 BasicType type2field[T_CONFLICT+1] = {
   (BasicType)0,            // 0,
@@ -239,8 +237,7 @@
   T_NARROWOOP,             // T_NARROWOOP= 17,
   T_METADATA,              // T_METADATA = 18,
   T_NARROWKLASS,           // T_NARROWKLASS = 19,
-  T_VALUETYPEPTR,          // T_VALUETYPEPTR = 20,
-  T_CONFLICT               // T_CONFLICT = 21,
+  T_CONFLICT               // T_CONFLICT = 20
 };
 
 
@@ -265,8 +262,7 @@
   T_NARROWOOP, // T_NARROWOOP  = 17,
   T_METADATA,  // T_METADATA   = 18,
   T_NARROWKLASS, // T_NARROWKLASS  = 19,
-  T_VALUETYPEPTR,// T_VALUETYPEPTR = 20,
-  T_CONFLICT // T_CONFLICT = 21,
+  T_CONFLICT // T_CONFLICT = 20
 };
 
 
@@ -291,8 +287,7 @@
   T_NARROWOOP_aelem_bytes,   // T_NARROWOOP= 17,
   T_OBJECT_aelem_bytes,      // T_METADATA = 18,
   T_NARROWKLASS_aelem_bytes, // T_NARROWKLASS= 19,
-  T_VALUETYPEPTR_aelem_bytes,// T_VALUETYPEPTR = 20,
-  0                          // T_CONFLICT = 21,
+  0                          // T_CONFLICT = 20
 };
 
 #ifdef ASSERT
--- a/src/hotspot/share/utilities/globalDefinitions.hpp	Mon Dec 17 15:19:06 2018 -0800
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp	Tue Dec 18 16:05:31 2018 +0100
@@ -585,8 +585,7 @@
   T_NARROWOOP   = 17,
   T_METADATA    = 18,
   T_NARROWKLASS = 19,
-  T_VALUETYPEPTR= 20, // the compiler needs a way to identify buffered values
-  T_CONFLICT    = 21, // for stack value type with conflicting contents
+  T_CONFLICT    = 20, // for stack value type with conflicting contents
   T_ILLEGAL     = 99
 };
 
@@ -684,12 +683,7 @@
 #endif
   T_NARROWOOP_aelem_bytes   = 4,
   T_NARROWKLASS_aelem_bytes = 4,
-  T_VOID_aelem_bytes        = 0,
-#ifdef _LP64
-  T_VALUETYPEPTR_aelem_bytes= 8
-#else
-  T_VALUETYPEPTR_aelem_bytes= 4
-#endif
+  T_VOID_aelem_bytes        = 0
 };
 
 extern int _type2aelembytes[T_CONFLICT+1]; // maps a BasicType to nof bytes used by its array element
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java	Mon Dec 17 15:19:06 2018 -0800
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java	Tue Dec 18 16:05:31 2018 +0100
@@ -89,7 +89,7 @@
     }
 
     // Return incoming value type without accessing fields
-    @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount = {1, 11}, failOn = LOAD + TRAP)
+    @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount = {1, 14}, failOn = LOAD + TRAP)
     @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = ALLOC + LOAD + STORE + TRAP)
     public MyValue1 test3(MyValue1 v) {
         return v;
@@ -138,7 +138,9 @@
 
     // Create a value type in compiled code and pass it to
     // the interpreter via a call.
-    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + TRAP + ALLOC)
+// TODO enable once receiver is scalarized
+//    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + TRAP + ALLOC)
+    @Test(valid = ValueTypePassFieldsAsArgsOn)
     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
     public long test6() {
         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
@@ -185,7 +187,9 @@
     }
 
     // Merge value types created from two branches
-    @Test(valid = ValueTypePassFieldsAsArgsOn, match = {LOAD}, matchCount = {10}, failOn = TRAP + ALLOC + STORE)
+// TODO enable once receiver is scalarized
+//    @Test(valid = ValueTypePassFieldsAsArgsOn, match = {LOAD}, matchCount = {10}, failOn = TRAP + ALLOC + STORE)
+    @Test(valid = ValueTypePassFieldsAsArgsOn)
     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 5}, failOn = LOAD + TRAP)
     public MyValue1 test9(boolean b) {
         MyValue1 v;
@@ -311,7 +315,9 @@
 
     // Create a value type in a non-inlined method and then call a
     // non-inlined method on that value type.
-    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (ALLOC + STORE + TRAP), match = {LOAD}, matchCount = {10})
+// TODO enable once receiver is scalarized
+//    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (ALLOC + STORE + TRAP), match = {LOAD}, matchCount = {10})
+    @Test(valid = ValueTypePassFieldsAsArgsOn)
     @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (ALLOC + LOAD + STORE + TRAP))
     public long test14() {
         MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
@@ -326,7 +332,9 @@
 
     // Create a value type in an inlined method and then call a
     // non-inlined method on that value type.
-    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (LOAD + TRAP + ALLOC))
+// TODO enable once receiver is scalarized
+//    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (LOAD + TRAP + ALLOC))
+    @Test(valid = ValueTypePassFieldsAsArgsOn)
     @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1})
     public long test15() {
         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
@@ -370,7 +378,9 @@
     // Create a value type in compiled code and pass it to the
     // interpreter via a call. The value is live at the first call so
     // debug info should include a reference to all its fields.
-    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP)
+// TODO enable once receiver is scalarized
+//    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP)
+    @Test(valid = ValueTypePassFieldsAsArgsOn)
     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
     public long test18() {
         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
@@ -409,7 +419,9 @@
     // interpreter via a call. The value type is live at the uncommon
     // trap: verify that deoptimization causes the value type to be
     // correctly allocated.
-    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE)
+// TODO enable once receiver is scalarized
+//    @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE)
+    @Test(valid = ValueTypePassFieldsAsArgsOn)
     @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD)
     public long test20(boolean deopt) {
         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java	Mon Dec 17 15:19:06 2018 -0800
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java	Tue Dec 18 16:05:31 2018 +0100
@@ -49,371 +49,422 @@
         return null;
     }
 
-   public static void main(String[] args) throws Throwable {
-       TestCallingConvention test = new TestCallingConvention();
-       test.run(args, MyValue1.class, MyValue2.class, MyValue2Inline.class, MyValue4.class);
-   }
+    public static void main(String[] args) throws Throwable {
+        TestCallingConvention test = new TestCallingConvention();
+        test.run(args, MyValue1.class, MyValue2.class, MyValue2Inline.class, MyValue4.class);
+    }
 
-   // Test interpreter to compiled code with various signatures
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test1(MyValue2 v) {
-       return v.hash();
-   }
+    // Test interpreter to compiled code with various signatures
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test1(MyValue2 v) {
+        return v.hash();
+    }
 
-   @DontCompile
-   public void test1_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test1(v);
-       Asserts.assertEQ(result, v.hashInterpreted());
-   }
+    @DontCompile
+    public void test1_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test1(v);
+        Asserts.assertEQ(result, v.hashInterpreted());
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test2(int i1, MyValue2 v, int i2) {
-       return v.hash() + i1 - i2;
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test2(int i1, MyValue2 v, int i2) {
+        return v.hash() + i1 - i2;
+    }
 
-   @DontCompile
-   public void test2_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test2(rI, v, 2*rI);
-       Asserts.assertEQ(result, v.hashInterpreted() - rI);
-   }
+    @DontCompile
+    public void test2_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test2(rI, v, 2*rI);
+        Asserts.assertEQ(result, v.hashInterpreted() - rI);
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test3(long l1, MyValue2 v, long l2) {
-       return v.hash() + l1 - l2;
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test3(long l1, MyValue2 v, long l2) {
+        return v.hash() + l1 - l2;
+    }
 
-   @DontCompile
-   public void test3_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test3(rL, v, 2*rL);
-       Asserts.assertEQ(result, v.hashInterpreted() - rL);
-   }
+    @DontCompile
+    public void test3_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test3(rL, v, 2*rL);
+        Asserts.assertEQ(result, v.hashInterpreted() - rL);
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test4(int i, MyValue2 v, long l) {
-       return v.hash() + i + l;
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test4(int i, MyValue2 v, long l) {
+        return v.hash() + i + l;
+    }
 
-   @DontCompile
-   public void test4_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test4(rI, v, rL);
-       Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
-   }
+    @DontCompile
+    public void test4_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test4(rI, v, rL);
+        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test5(long l, MyValue2 v, int i) {
-       return v.hash() + i + l;
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test5(long l, MyValue2 v, int i) {
+        return v.hash() + i + l;
+    }
 
-   @DontCompile
-   public void test5_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test5(rL, v, rI);
-       Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
-   }
+    @DontCompile
+    public void test5_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test5(rL, v, rI);
+        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test6(long l, MyValue1 v1, int i, MyValue2 v2) {
-       return v1.hash() + i + l + v2.hash();
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test6(long l, MyValue1 v1, int i, MyValue2 v2) {
+        return v1.hash() + i + l + v2.hash();
+    }
 
-   @DontCompile
-   public void test6_verifier(boolean warmup) {
-       MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
-       MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
-       long result = test6(rL, v1, rI, v2);
-       Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
-   }
+    @DontCompile
+    public void test6_verifier(boolean warmup) {
+        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
+        long result = test6(rL, v1, rI, v2);
+        Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
+    }
 
-   // Test compiled code to interpreter with various signatures
-   @DontCompile
-   public long test7_interp(MyValue2 v) {
-       return v.hash();
-   }
+    // Test compiled code to interpreter with various signatures
+    @DontCompile
+    public long test7_interp(MyValue2 v) {
+        return v.hash();
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test7(MyValue2 v) {
-       return test7_interp(v);
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test7(MyValue2 v) {
+        return test7_interp(v);
+    }
 
-   @DontCompile
-   public void test7_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test7(v);
-       Asserts.assertEQ(result, v.hashInterpreted());
-   }
+    @DontCompile
+    public void test7_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test7(v);
+        Asserts.assertEQ(result, v.hashInterpreted());
+    }
 
-   @DontCompile
-   public long test8_interp(int i1, MyValue2 v, int i2) {
-       return v.hash() + i1 - i2;
-   }
+    @DontCompile
+    public long test8_interp(int i1, MyValue2 v, int i2) {
+        return v.hash() + i1 - i2;
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test8(int i1, MyValue2 v, int i2) {
-       return test8_interp(i1, v, i2);
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test8(int i1, MyValue2 v, int i2) {
+        return test8_interp(i1, v, i2);
+    }
 
-   @DontCompile
-   public void test8_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test8(rI, v, 2*rI);
-       Asserts.assertEQ(result, v.hashInterpreted() - rI);
-   }
+    @DontCompile
+    public void test8_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test8(rI, v, 2*rI);
+        Asserts.assertEQ(result, v.hashInterpreted() - rI);
+    }
 
-   @DontCompile
-   public long test9_interp(long l1, MyValue2 v, long l2) {
-       return v.hash() + l1 - l2;
-   }
+    @DontCompile
+    public long test9_interp(long l1, MyValue2 v, long l2) {
+        return v.hash() + l1 - l2;
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test9(long l1, MyValue2 v, long l2) {
-       return test9_interp(l1, v, l2);
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test9(long l1, MyValue2 v, long l2) {
+        return test9_interp(l1, v, l2);
+    }
 
-   @DontCompile
-   public void test9_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test9(rL, v, 2*rL);
-       Asserts.assertEQ(result, v.hashInterpreted() - rL);
-   }
+    @DontCompile
+    public void test9_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test9(rL, v, 2*rL);
+        Asserts.assertEQ(result, v.hashInterpreted() - rL);
+    }
 
-   @DontCompile
-   public long test10_interp(int i, MyValue2 v, long l) {
-       return v.hash() + i + l;
-   }
+    @DontCompile
+    public long test10_interp(int i, MyValue2 v, long l) {
+        return v.hash() + i + l;
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test10(int i, MyValue2 v, long l) {
-       return test10_interp(i, v, l);
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test10(int i, MyValue2 v, long l) {
+        return test10_interp(i, v, l);
+    }
 
-   @DontCompile
-   public void test10_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test10(rI, v, rL);
-       Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
-   }
+    @DontCompile
+    public void test10_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test10(rI, v, rL);
+        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+    }
 
-   @DontCompile
-   public long test11_interp(long l, MyValue2 v, int i) {
-       return v.hash() + i + l;
-   }
+    @DontCompile
+    public long test11_interp(long l, MyValue2 v, int i) {
+        return v.hash() + i + l;
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test11(long l, MyValue2 v, int i) {
-       return test11_interp(l, v, i);
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test11(long l, MyValue2 v, int i) {
+        return test11_interp(l, v, i);
+    }
 
-   @DontCompile
-   public void test11_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       long result = test11(rL, v, rI);
-       Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
-   }
+    @DontCompile
+    public void test11_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        long result = test11(rL, v, rI);
+        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+    }
 
-   @DontCompile
-   public long test12_interp(long l, MyValue1 v1, int i, MyValue2 v2) {
-       return v1.hash() + i + l + v2.hash();
-   }
+    @DontCompile
+    public long test12_interp(long l, MyValue1 v1, int i, MyValue2 v2) {
+        return v1.hash() + i + l + v2.hash();
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test12(long l, MyValue1 v1, int i, MyValue2 v2) {
-       return test12_interp(l, v1, i, v2);
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test12(long l, MyValue1 v1, int i, MyValue2 v2) {
+        return test12_interp(l, v1, i, v2);
+    }
 
-   @DontCompile
-   public void test12_verifier(boolean warmup) {
-       MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
-       MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
-       long result = test12(rL, v1, rI, v2);
-       Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
-   }
+    @DontCompile
+    public void test12_verifier(boolean warmup) {
+        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
+        MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
+        long result = test12(rL, v1, rI, v2);
+        Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
+    }
 
-   // Test that debug info at a call is correct
-   @DontCompile
-   public long test13_interp(MyValue2 v, MyValue1[] va, boolean deopt) {
-       if (deopt) {
-           // uncommon trap
-           WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test13"));
-       }
-       return v.hash() + va[0].hash() + va[1].hash();
-   }
+    // Test that debug info at a call is correct
+    @DontCompile
+    public long test13_interp(MyValue2 v, MyValue1[] va, boolean deopt) {
+        if (deopt) {
+            // uncommon trap
+            WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test13"));
+        }
+        return v.hash() + va[0].hash() + va[1].hash();
+    }
 
-   @Test(failOn = ALLOC + STORE + TRAP)
-   public long test13(MyValue2 v, MyValue1[] va, boolean flag, long l) {
-       return test13_interp(v, va, flag) + l;
-   }
+    @Test(failOn = ALLOC + STORE + TRAP)
+    public long test13(MyValue2 v, MyValue1[] va, boolean flag, long l) {
+        return test13_interp(v, va, flag) + l;
+    }
 
-   @DontCompile
-   public void test13_verifier(boolean warmup) {
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       MyValue1[] va = new MyValue1[2];
-       va[0] = MyValue1.createWithFieldsDontInline(rI, rL);
-       va[1] = MyValue1.createWithFieldsDontInline(rI, rL);
-       long result = test13(v, va, !warmup, rL);
-       Asserts.assertEQ(result, v.hashInterpreted() + va[0].hash() + va[1].hash() + rL);
-   }
+    @DontCompile
+    public void test13_verifier(boolean warmup) {
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        MyValue1[] va = new MyValue1[2];
+        va[0] = MyValue1.createWithFieldsDontInline(rI, rL);
+        va[1] = MyValue1.createWithFieldsDontInline(rI, rL);
+        long result = test13(v, va, !warmup, rL);
+        Asserts.assertEQ(result, v.hashInterpreted() + va[0].hash() + va[1].hash() + rL);
+    }
 
-   // Test deoptimization at call return with return value in registers
-   @DontCompile
-   public MyValue2 test14_interp(boolean deopt) {
-       if (deopt) {
-           // uncommon trap
-           WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test14"));
-       }
-       return MyValue2.createWithFieldsInline(rI, true);
-   }
+    // Test deoptimization at call return with return value in registers
+    @DontCompile
+    public MyValue2 test14_interp(boolean deopt) {
+        if (deopt) {
+            // uncommon trap
+            WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test14"));
+        }
+        return MyValue2.createWithFieldsInline(rI, true);
+    }
 
-   @Test()
-   public MyValue2 test14(boolean flag) {
-       return test14_interp(flag);
-   }
+    @Test()
+    public MyValue2 test14(boolean flag) {
+        return test14_interp(flag);
+    }
 
-   @DontCompile
-   public void test14_verifier(boolean warmup) {
-       MyValue2 result = test14(!warmup);
-       MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
-       Asserts.assertEQ(result.hash(), v.hash());
-   }
+    @DontCompile
+    public void test14_verifier(boolean warmup) {
+        MyValue2 result = test14(!warmup);
+        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
+        Asserts.assertEQ(result.hash(), v.hash());
+    }
 
-   // Return value types in registers from interpreter -> compiled
-   final MyValue3 test15_vt = MyValue3.create();
-   @DontCompile
-   public MyValue3 test15_interp() {
-       return test15_vt;
-   }
+    // Return value types in registers from interpreter -> compiled
+    final MyValue3 test15_vt = MyValue3.create();
+    @DontCompile
+    public MyValue3 test15_interp() {
+        return test15_vt;
+    }
 
-   MyValue3 test15_vt2;
-   @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
-   @Test(valid = ValueTypeReturnedAsFieldsOff)
-   public void test15() {
-       test15_vt2 = test15_interp();
-   }
+    MyValue3 test15_vt2;
+    @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
+    @Test(valid = ValueTypeReturnedAsFieldsOff)
+    public void test15() {
+        test15_vt2 = test15_interp();
+    }
 
-   @DontCompile
-   public void test15_verifier(boolean warmup) {
-       test15();
-       test15_vt.verify(test15_vt2);
-   }
+    @DontCompile
+    public void test15_verifier(boolean warmup) {
+        test15();
+        test15_vt.verify(test15_vt2);
+    }
 
-   // Return value types in registers from compiled -> interpreter
-   final MyValue3 test16_vt = MyValue3.create();
-   @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + STORE + TRAP)
-   @Test(valid = ValueTypeReturnedAsFieldsOff)
-   public MyValue3 test16() {
-       return test16_vt;
-   }
+    // Return value types in registers from compiled -> interpreter
+    final MyValue3 test16_vt = MyValue3.create();
+    @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + STORE + TRAP)
+    @Test(valid = ValueTypeReturnedAsFieldsOff)
+    public MyValue3 test16() {
+        return test16_vt;
+    }
 
-   @DontCompile
-   public void test16_verifier(boolean warmup) {
-       MyValue3 vt = test16();
-       test16_vt.verify(vt);
-   }
+    @DontCompile
+    public void test16_verifier(boolean warmup) {
+        MyValue3 vt = test16();
+        test16_vt.verify(vt);
+    }
 
-   // Return value types in registers from compiled -> compiled
-   final MyValue3 test17_vt = MyValue3.create();
-   @DontInline
-   public MyValue3 test17_comp() {
-       return test17_vt;
-   }
+    // Return value types in registers from compiled -> compiled
+    final MyValue3 test17_vt = MyValue3.create();
+    @DontInline
+    public MyValue3 test17_comp() {
+        return test17_vt;
+    }
 
-   MyValue3 test17_vt2;
-   @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
-   @Test(valid = ValueTypeReturnedAsFieldsOff)
-   public void test17() {
-       test17_vt2 = test17_comp();
-   }
+    MyValue3 test17_vt2;
+    @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
+    @Test(valid = ValueTypeReturnedAsFieldsOff)
+    public void test17() {
+        test17_vt2 = test17_comp();
+    }
 
-   @DontCompile
-   public void test17_verifier(boolean warmup) throws Exception {
-       Method helper_m = getClass().getDeclaredMethod("test17_comp");
-       if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
-           WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
-           Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test17_comp not compiled");
-       }
-       test17();
-       test17_vt.verify(test17_vt2);
-   }
+    @DontCompile
+    public void test17_verifier(boolean warmup) throws Exception {
+        Method helper_m = getClass().getDeclaredMethod("test17_comp");
+        if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
+            WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
+            Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test17_comp not compiled");
+        }
+        test17();
+        test17_vt.verify(test17_vt2);
+    }
 
-   // Same tests as above but with a value type that cannot be returned in registers
+    // Same tests as above but with a value type that cannot be returned in registers
 
-   // Return value types in registers from interpreter -> compiled
-   final MyValue4 test18_vt = MyValue4.create();
-   @DontCompile
-   public MyValue4 test18_interp() {
-       return test18_vt;
-   }
+    // Return value types in registers from interpreter -> compiled
+    final MyValue4 test18_vt = MyValue4.create();
+    @DontCompile
+    public MyValue4 test18_interp() {
+        return test18_vt;
+    }
 
-   MyValue4 test18_vt2;
-   @Test
-   public void test18() {
-       test18_vt2 = test18_interp();
-   }
+    MyValue4 test18_vt2;
+    @Test
+    public void test18() {
+        test18_vt2 = test18_interp();
+    }
 
-   @DontCompile
-   public void test18_verifier(boolean warmup) {
-       test18();
-       test18_vt.verify(test18_vt2);
-   }
+    @DontCompile
+    public void test18_verifier(boolean warmup) {
+        test18();
+        test18_vt.verify(test18_vt2);
+    }
 
-   // Return value types in registers from compiled -> interpreter
-   final MyValue4 test19_vt = MyValue4.create();
-   @Test
-   public MyValue4 test19() {
-       return test19_vt;
-   }
+    // Return value types in registers from compiled -> interpreter
+    final MyValue4 test19_vt = MyValue4.create();
+    @Test
+    public MyValue4 test19() {
+        return test19_vt;
+    }
 
-   @DontCompile
-   public void test19_verifier(boolean warmup) {
-       MyValue4 vt = test19();
-       test19_vt.verify(vt);
-   }
+    @DontCompile
+    public void test19_verifier(boolean warmup) {
+        MyValue4 vt = test19();
+        test19_vt.verify(vt);
+    }
 
-   // Return value types in registers from compiled -> compiled
-   final MyValue4 test20_vt = MyValue4.create();
-   @DontInline
-   public MyValue4 test20_comp() {
-       return test20_vt;
-   }
+    // Return value types in registers from compiled -> compiled
+    final MyValue4 test20_vt = MyValue4.create();
+    @DontInline
+    public MyValue4 test20_comp() {
+        return test20_vt;
+    }
 
-   MyValue4 test20_vt2;
-   @Test
-   public void test20() {
-       test20_vt2 = test20_comp();
-   }
+    MyValue4 test20_vt2;
+    @Test
+    public void test20() {
+        test20_vt2 = test20_comp();
+    }
 
-   @DontCompile
-   public void test20_verifier(boolean warmup) throws Exception {
-       Method helper_m = getClass().getDeclaredMethod("test20_comp");
-       if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
-           WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
-           Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test20_comp not compiled");
-       }
-       test20();
-       test20_vt.verify(test20_vt2);
-   }
+    @DontCompile
+    public void test20_verifier(boolean warmup) throws Exception {
+        Method helper_m = getClass().getDeclaredMethod("test20_comp");
+        if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
+            WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
+            Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test20_comp not compiled");
+        }
+        test20();
+        test20_vt.verify(test20_vt2);
+    }
 
-   // Test no result from inlined method for incremental inlining
-   final MyValue3 test21_vt = MyValue3.create();
-   public MyValue3 test21_inlined() {
-       throw new RuntimeException();
-   }
+    // Test no result from inlined method for incremental inlining
+    final MyValue3 test21_vt = MyValue3.create();
+    public MyValue3 test21_inlined() {
+        throw new RuntimeException();
+    }
 
-   @Test
-   public MyValue3 test21() {
-       try {
-           return test21_inlined();
-       } catch (RuntimeException ex) {
-           return test21_vt;
-       }
-   }
+    @Test
+    public MyValue3 test21() {
+        try {
+            return test21_inlined();
+        } catch (RuntimeException ex) {
+            return test21_vt;
+        }
+    }
 
-   @DontCompile
-   public void test21_verifier(boolean warmup) {
-       MyValue3 vt = test21();
-       test21_vt.verify(vt);
-   }
+    @DontCompile
+    public void test21_verifier(boolean warmup) {
+        MyValue3 vt = test21();
+        test21_vt.verify(vt);
+    }
+
+    // Test returning a non-flattened value type as fields
+    MyValue3.box test22_vt = MyValue3.create();
+
+    @Test
+    public MyValue3 test22() {
+        return test22_vt;
+    }
+
+    @DontCompile
+    public void test22_verifier(boolean warmup) {
+        MyValue3 vt = test22();
+        test22_vt.verify(vt);
+    }
+
+    // Test calling a method that has circular register/stack dependencies when unpacking value type arguments
+    value class TestValue23 {
+        final double f1;
+        TestValue23(double val) {
+            f1 = val;
+        }
+    }
+
+    static double test23Callee(int i1, int i2, int i3, int i4, int i5, int i6,
+                               TestValue23 v1, TestValue23 v2, TestValue23 v3, TestValue23 v4, TestValue23 v5, TestValue23 v6, TestValue23 v7, TestValue23 v8,
+                               double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
+        return i1 + i2 + i3 + i4 + i5 + i6 + v1.f1 + v2.f1 + v3.f1 + v4.f1 + v5.f1 + v6.f1 + v7.f1 + v8.f1 + d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8;
+    }
+
+    @Test
+    public double test23(int i1, int i2, int i3, int i4, int i5, int i6,
+                         TestValue23 v1, TestValue23 v2, TestValue23 v3, TestValue23 v4, TestValue23 v5, TestValue23 v6, TestValue23 v7, TestValue23 v8,
+                         double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
+        return test23Callee(i1, i2, i3, i4, i5, i6,
+                            v1, v2, v3, v4, v5, v6, v7, v8,
+                            d1, d2, d3, d4, d5, d6, d7, d8);
+    }
+
+    @DontCompile
+    public void test23_verifier(boolean warmup) {
+        TestValue23 vt = new TestValue23(rI);
+        double res1 = test23(rI, rI, rI, rI, rI, rI,
+                            vt, vt, vt, vt, vt, vt, vt, vt,
+                            rI, rI, rI, rI, rI, rI, rI, rI);
+        double res2 = test23Callee(rI, rI, rI, rI, rI, rI,
+                                   vt, vt, vt, vt, vt, vt, vt, vt,
+                                   rI, rI, rI, rI, rI, rI, rI, rI);
+        double res3 = 6*rI + 8*rI + 8*rI;
+        Asserts.assertEQ(res1, res2);
+        Asserts.assertEQ(res2, res3);
+    }
 }
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMethodHandles.java	Mon Dec 17 15:19:06 2018 -0800
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMethodHandles.java	Tue Dec 18 16:05:31 2018 +0100
@@ -43,6 +43,7 @@
  * @run driver ClassFileInstaller sun.hotspot.WhiteBox jdk.test.lib.Platform
  * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
  *                               -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:+EnableValhalla
+ *                               -DVerifyIR=false
  *                               compiler.valhalla.valuetypes.ValueTypeTest
  *                               compiler.valhalla.valuetypes.TestMethodHandles
  */
@@ -51,7 +52,10 @@
     @Override
     public String[] getExtraVMParameters(int scenario) {
         switch (scenario) {
+        // Prevent inlining through MethodHandle linkTo adapters to stress the calling convention
+        case 2: return new String[] {"-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName"};
         case 3: return new String[] {"-XX:-ValueArrayFlatten"};
+        case 4: return new String[] {"-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName"};
         }
         return null;
     }
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnresolvedValueClass.java	Mon Dec 17 15:19:06 2018 -0800
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnresolvedValueClass.java	Tue Dec 18 16:05:31 2018 +0100
@@ -25,7 +25,6 @@
  * @test
  * @bug 8187679
  * @summary The VM should exit gracefully when unable to resolve a value type argument
- * @requires vm.compMode != "Xint"
  * @library /test/lib
  * @compile -XDemitQtypes -XDenableValueTypes -XDallowFlattenabilityModifiers TestUnresolvedValueClass.java
  * @run main/othervm -XX:+EnableValhalla TestUnresolvedValueClass
@@ -58,10 +57,7 @@
             // Adapter creation for TestUnresolvedValueClass::test1 should fail with a
             // ClassNotFoundException because the class for argument 'vt' was not found.
             String output = oa.getOutput();
-            if (!output.contains("ValueTypePassFieldsAsArgs is not supported on this platform")) {
-                oa.shouldContain("java.lang.ClassNotFoundException: SimpleValueType");
-                oa.shouldHaveExitValue(1);
-            }
+            oa.shouldContain("java.lang.NoClassDefFoundError: SimpleValueType");
         }
     }
 }
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/ValueTypeTest.java	Mon Dec 17 15:19:06 2018 -0800
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/ValueTypeTest.java	Tue Dec 18 16:05:31 2018 +0100
@@ -115,7 +115,7 @@
         "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly");
     private static final List<String> verifyFlags = Arrays.asList(
         "-XX:+VerifyOops", "-XX:+VerifyStack", "-XX:+VerifyLastFrame", "-XX:+VerifyBeforeGC", "-XX:+VerifyAfterGC",
-        "-XX:+VerifyDuringGC", "-XX:+VerifyAdapterSharing", "-XX:+StressValueTypeReturnedAsFields");
+        "-XX:+VerifyDuringGC", "-XX:+VerifyAdapterSharing");
 
     protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
     protected static final int ValueTypePassFieldsAsArgsOn = 0x1;
@@ -145,7 +145,7 @@
     protected static final String ALLOCA = "(.*precise klass \\[Lcompiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_array_Java" + END;
     protected static final String LOAD   = START + "Load(B|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/valuetypes/MyValue.*" + END;
     protected static final String LOADK  = START + "LoadK" + MID + END;
-    protected static final String STORE  = START + "Store(B|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/valuetypes/MyValue.*" + END;
+    protected static final String STORE  = START + "Store(B|C|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/valuetypes/MyValue.*" + END;
     protected static final String LOOP   = START + "Loop" + MID + "" + END;
     protected static final String TRAP   = START + "CallStaticJava" + MID + "uncommon_trap.*(unstable_if|predicate)" + END;
     protected static final String RETURN = START + "Return" + MID + "returns" + END;
@@ -219,7 +219,9 @@
                 "-XX:-ValueArrayFlatten",
                 "-XX:ValueFieldMaxFlatSize=0",
                 "-XX:+ValueTypePassFieldsAsArgs",
-                "-XX:+ValueTypeReturnedAsFields"};
+                "-XX:+ValueTypeReturnedAsFields",
+                "-XX:+StressValueTypePassFieldsAsArgs",
+                "-XX:+StressValueTypeReturnedAsFields"};
         case 3: return new String[] {
                 "-DVerifyIR=false",
                 "-XX:+AlwaysIncrementalInline",
@@ -235,7 +237,8 @@
                 "-XX:+ValueArrayFlatten",
                 "-XX:ValueFieldMaxFlatSize=0",
                 "-XX:+ValueTypePassFieldsAsArgs",
-                "-XX:-ValueTypeReturnedAsFields"};
+                "-XX:-ValueTypeReturnedAsFields",
+                "-XX:+StressValueTypePassFieldsAsArgs"};
         }
 
         return null;