changeset 56202:598fb53656f9 lworld

8222221: [lworld] Valhalla causes performance slowdown for reflective invocations Reviewed-by: roland
author thartmann
date Wed, 03 Jul 2019 14:27:42 +0200
parents 789a8a219886
children 3de42b2ccd6c
files src/hotspot/share/ci/ciFlags.hpp src/hotspot/share/ci/ciMethod.hpp src/hotspot/share/ci/ciSignature.cpp src/hotspot/share/opto/compile.cpp src/hotspot/share/opto/graphKit.cpp src/hotspot/share/opto/graphKit.hpp src/hotspot/share/opto/library_call.cpp src/hotspot/share/opto/macro.cpp src/hotspot/share/opto/macroArrayCopy.cpp src/hotspot/share/opto/parse1.cpp src/hotspot/share/opto/parse2.cpp src/hotspot/share/opto/type.cpp src/hotspot/share/opto/type.hpp test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableArrays.java test/hotspot/jtreg/compiler/valhalla/valuetypes/ValueTypeTest.java
diffstat 15 files changed, 483 insertions(+), 138 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/ci/ciFlags.hpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/ci/ciFlags.hpp	Wed Jul 03 14:27:42 2019 +0200
@@ -60,6 +60,7 @@
   bool is_abstract             () const { return (_flags & JVM_ACC_ABSTRACT                  ) != 0; }
   bool is_strict               () const { return (_flags & JVM_ACC_STRICT                    ) != 0; }
   bool is_stable               () const { return (_flags & JVM_ACC_FIELD_STABLE              ) != 0; }
+  bool has_vararg              () const { return (_flags & JVM_ACC_VARARGS                   ) != 0; }
   // In case the current object represents a field, return true if
   // the field is modified outside of instance initializer methods
   // (or class/initializer methods if the field is static) and false
--- a/src/hotspot/share/ci/ciMethod.hpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/ci/ciMethod.hpp	Wed Jul 03 14:27:42 2019 +0200
@@ -337,6 +337,7 @@
   bool is_interface   () const                   { return flags().is_interface(); }
   bool is_abstract    () const                   { return flags().is_abstract(); }
   bool is_strict      () const                   { return flags().is_strict(); }
+  bool has_vararg     () const                   { return flags().has_vararg(); }
 
   // Other flags
   bool is_empty_method() const;
--- a/src/hotspot/share/ci/ciSignature.cpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/ci/ciSignature.cpp	Wed Jul 03 14:27:42 2019 +0200
@@ -62,9 +62,9 @@
       ciSymbol* klass_name = env->get_symbol(name);
       type = env->get_klass_by_name_impl(_accessing_klass, cpool, klass_name, false);
     }
-      if (type->is_valuetype() && ss.type() == T_VALUETYPE) {
-        type = env->make_never_null_wrapper(type);
-      }
+    if (type->is_valuetype() && ss.type() == T_VALUETYPE) {
+      type = env->make_never_null_wrapper(type);
+    }
     _types->append(type);
     if (ss.at_return_type()) {
       // Done processing the return type; do not add it into the count.
--- a/src/hotspot/share/opto/compile.cpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/compile.cpp	Wed Jul 03 14:27:42 2019 +0200
@@ -470,7 +470,6 @@
   compile->set_type_dict(NULL);
   compile->set_clone_map(new Dict(cmpkey, hashkey, _compile->comp_arena()));
   compile->clone_map().set_clone_idx(0);
-  compile->set_type_hwm(NULL);
   compile->set_type_last_size(0);
   compile->set_last_tf(NULL, NULL);
   compile->set_indexSet_arena(NULL);
@@ -1494,6 +1493,15 @@
     // Erase stability property for alias analysis.
     tj = ta = ta->cast_to_stable(false);
   }
+  if (ta && ta->is_not_flat()) {
+    // Erase not flat property for alias analysis.
+    tj = ta = ta->cast_to_not_flat(false);
+  }
+  if (ta && ta->is_not_null_free()) {
+    // Erase not null free property for alias analysis.
+    tj = ta = ta->cast_to_not_null_free(false);
+  }
+
   if( ta && is_known_inst ) {
     if ( offset != Type::OffsetBot &&
          offset > arrayOopDesc::length_offset_in_bytes() ) {
@@ -3419,39 +3427,36 @@
       assert( !tp || oop_offset_is_sane(tp), "" );
     }
 #endif
-    if (nop == Op_LoadKlass || nop == Op_LoadNKlass) {
+    if (EnableValhalla && (nop == Op_LoadKlass || nop == Op_LoadNKlass)) {
       const TypeKlassPtr* tk = n->bottom_type()->make_ptr()->is_klassptr();
       assert(!tk->klass_is_exact(), "should have been folded");
-      if (tk->klass()->is_obj_array_klass() || tk->klass()->is_java_lang_Object()) {
-        bool maybe_value_array = tk->klass()->is_java_lang_Object();
-        if (!maybe_value_array) {
-          ciArrayKlass* ak = tk->klass()->as_array_klass();
-          ciKlass* elem = ak->element_klass();
-          maybe_value_array = elem->is_java_lang_Object() || elem->is_interface() || elem->is_valuetype();
+      ciKlass* klass = tk->klass();
+      bool maybe_value_array = klass->is_java_lang_Object();
+      if (!maybe_value_array && klass->is_obj_array_klass()) {
+        klass = klass->as_array_klass()->element_klass();
+        maybe_value_array = klass->is_java_lang_Object() || klass->is_interface() || klass->is_valuetype();
+      }
+      if (maybe_value_array) {
+        // Array load klass needs to filter out property bits (but not
+        // GetNullFreePropertyNode which needs to extract the null free bits)
+        uint last = unique();
+        Node* pointer = NULL;
+        if (nop == Op_LoadKlass) {
+          Node* cast = new CastP2XNode(NULL, n);
+          Node* masked = new LShiftXNode(cast, new ConINode(TypeInt::make(oopDesc::storage_props_nof_bits)));
+          masked = new RShiftXNode(masked, new ConINode(TypeInt::make(oopDesc::storage_props_nof_bits)));
+          pointer = new CastX2PNode(masked);
+          pointer = new CheckCastPPNode(NULL, pointer, n->bottom_type());
+        } else {
+          Node* cast = new CastN2INode(n);
+          Node* masked = new AndINode(cast, new ConINode(TypeInt::make(oopDesc::compressed_klass_mask())));
+          pointer = new CastI2NNode(masked, n->bottom_type());
         }
-        if (maybe_value_array) {
-          // Array load klass needs to filter out property bits (but not
-          // GetNullFreePropertyNode which needs to extract the null free
-          // bits)
-          uint last = unique();
-          Node* pointer = NULL;
-          if (nop == Op_LoadKlass) {
-            Node* cast = new CastP2XNode(NULL, n);
-            Node* masked = new LShiftXNode(cast, new ConINode(TypeInt::make(oopDesc::storage_props_nof_bits)));
-            masked = new RShiftXNode(masked, new ConINode(TypeInt::make(oopDesc::storage_props_nof_bits)));
-            pointer = new CastX2PNode(masked);
-            pointer = new CheckCastPPNode(NULL, pointer, n->bottom_type());
-          } else {
-            Node* cast = new CastN2INode(n);
-            Node* masked = new AndINode(cast, new ConINode(TypeInt::make(oopDesc::compressed_klass_mask())));
-            pointer = new CastI2NNode(masked, n->bottom_type());
-          }
-          for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
-            Node* u = n->fast_out(i);
-            if (u->_idx < last && u->Opcode() != Op_GetNullFreeProperty) {
-              int nb = u->replace_edge(n, pointer);
-              --i, imax -= nb;
-            }
+        for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
+          Node* u = n->fast_out(i);
+          if (u->_idx < last && u->Opcode() != Op_GetNullFreeProperty) {
+            int nb = u->replace_edge(n, pointer);
+            --i, imax -= nb;
           }
         }
       }
--- a/src/hotspot/share/opto/graphKit.cpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/graphKit.cpp	Wed Jul 03 14:27:42 2019 +0200
@@ -3453,9 +3453,30 @@
   }
 }
 
+// Check if 'ary' is a null-free value type array
+Node* GraphKit::gen_null_free_array_check(Node* ary) {
+  assert(EnableValhalla, "should only be used if value types are enabled");
+  // Extract null free property from klass pointer
+  Node* k_adr = basic_plus_adr(ary, oopDesc::klass_offset_in_bytes());
+  const TypePtr* k_adr_type = k_adr->bottom_type()->isa_ptr();
+  Node* klass = NULL;
+  if (k_adr_type->is_ptr_to_narrowklass()) {
+    klass = _gvn.transform(new LoadNKlassNode(NULL, immutable_memory(), k_adr, TypeInstPtr::KLASS, TypeKlassPtr::OBJECT->make_narrowklass(), MemNode::unordered));
+  } else {
+    klass = _gvn.transform(new LoadKlassNode(NULL, immutable_memory(), k_adr, TypeInstPtr::KLASS, TypeKlassPtr::OBJECT, MemNode::unordered));
+  }
+  Node* null_free = _gvn.transform(new GetNullFreePropertyNode(klass));
+  Node* cmp = NULL;
+  if (_gvn.type(klass)->isa_klassptr()) {
+    cmp = _gvn.transform(new CmpLNode(null_free, zerocon(T_LONG)));
+  } else {
+    cmp = _gvn.transform(new CmpINode(null_free, zerocon(T_INT)));
+  }
+  return _gvn.transform(new BoolNode(cmp, BoolTest::eq));
+}
+
 // Deoptimize if 'ary' is a null-free value type array and 'val' is null
 void GraphKit::gen_value_array_null_guard(Node* ary, Node* val, int nargs) {
-  assert(EnableValhalla, "should only be used if value types are enabled");
   const Type* val_t = _gvn.type(val);
   if (val->is_ValueType() || !TypePtr::NULL_PTR->higher_equal(val_t)) {
     return; // Never null
@@ -3466,26 +3487,8 @@
   if (null_ctl != top()) {
     PreserveJVMState pjvms(this);
     set_control(null_ctl);
-    // Extract null free property from klass pointer
-    Node* k_adr = basic_plus_adr(ary, oopDesc::klass_offset_in_bytes());
-    const TypePtr *k_adr_type = k_adr->bottom_type()->isa_ptr();
-    Node* klass = NULL;
-    if (k_adr_type->is_ptr_to_narrowklass()) {
-      klass = _gvn.transform(new LoadNKlassNode(NULL, immutable_memory(), k_adr, TypeInstPtr::KLASS, TypeKlassPtr::OBJECT->make_narrowklass(), MemNode::unordered));
-    } else {
-      klass = _gvn.transform(new LoadKlassNode(NULL, immutable_memory(), k_adr, TypeInstPtr::KLASS, TypeKlassPtr::OBJECT, MemNode::unordered));
-    }
-
-    Node* null_free = _gvn.transform(new GetNullFreePropertyNode(klass));
     // Deoptimize if null-free array
-    Node* cmp = NULL;
-    if (_gvn.type(klass)->isa_klassptr()) {
-      cmp = new CmpLNode(null_free, zerocon(T_LONG));
-    } else {
-      cmp = new CmpINode(null_free, zerocon(T_INT));
-    }
-    cmp = _gvn.transform(cmp);
-    Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq));
+    Node* bol = gen_null_free_array_check(ary);
     { BuildCutout unless(this, bol, PROB_MAX);
       inc_sp(nargs);
       uncommon_trap(Deoptimization::Reason_null_check,
@@ -4365,7 +4368,7 @@
                                                      false, NULL, Type::Offset(0));
   const TypePtr* value_field_type = string_type->add_offset(value_offset);
   const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::NotNull,
-                                                  TypeAry::make(TypeInt::BYTE, TypeInt::POS),
+                                                  TypeAry::make(TypeInt::BYTE, TypeInt::POS, false, true, true),
                                                   ciTypeArrayKlass::make(T_BYTE), true, Type::Offset(0));
   Node* p = basic_plus_adr(str, str, value_offset);
   Node* load = access_load_at(str, p, value_field_type, value_type, T_OBJECT,
--- a/src/hotspot/share/opto/graphKit.hpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/graphKit.hpp	Wed Jul 03 14:27:42 2019 +0200
@@ -860,6 +860,7 @@
   Node* is_always_locked(Node* obj);
   Node* is_value_mirror(Node* mirror);
   void gen_value_type_guard(Node* obj, int nargs = 0);
+  Node* gen_null_free_array_check(Node* ary);
   void gen_value_array_null_guard(Node* ary, Node* val, int nargs);
   Node* load_lh_array_tag(Node* kls);
   Node* gen_lh_array_test(Node* kls, unsigned int lh_value);
--- a/src/hotspot/share/opto/library_call.cpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/library_call.cpp	Wed Jul 03 14:27:42 2019 +0200
@@ -212,6 +212,7 @@
     return generate_array_guard_common(kls, region, TypeArray);
   }
   Node* generate_valueArray_guard(Node* kls, RegionNode* region) {
+    assert(ValueArrayFlatten, "can never be flattened");
     return generate_array_guard_common(kls, region, ValueArray);
   }
   Node* generate_array_guard_common(Node* kls, RegionNode* region, ArrayKind kind);
@@ -4035,17 +4036,21 @@
       }
     }
 
-    if (EnableValhalla) {
+    if (ValueArrayFlatten) {
       // Either both or neither new array klass and original array
       // klass must be flattened
-      Node* flattened_klass = generate_valueArray_guard(klass_node, NULL);
-      generate_valueArray_guard(original_kls, bailout);
-      if (flattened_klass != NULL) {
+      Node* is_flat = generate_valueArray_guard(klass_node, NULL);
+      if (!original_t->is_not_flat()) {
+        generate_valueArray_guard(original_kls, bailout);
+      }
+      if (is_flat != NULL) {
         RegionNode* r = new RegionNode(2);
         record_for_igvn(r);
         r->init_req(1, control());
-        set_control(flattened_klass);
-        generate_valueArray_guard(original_kls, r);
+        set_control(is_flat);
+        if (!original_t->is_not_flat()) {
+          generate_valueArray_guard(original_kls, r);
+        }
         bailout->add_req(control());
         set_control(_gvn.transform(r));
       }
@@ -4757,7 +4762,8 @@
       set_control(array_ctl);
 
       BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
-      if (bs->array_copy_requires_gc_barriers(true, T_OBJECT, true, BarrierSetC2::Parsing)) {
+      if (bs->array_copy_requires_gc_barriers(true, T_OBJECT, true, BarrierSetC2::Parsing) &&
+          (!obj_type->isa_aryptr() || !obj_type->is_aryptr()->is_not_flat())) {
         // Flattened value type array may have object field that would require a
         // write barrier. Conservatively, go to slow path.
         generate_valueArray_guard(obj_klass, slow_region);
@@ -5242,15 +5248,11 @@
     src_type = _gvn.type(src);
     top_src  = src_type->isa_aryptr();
 
-    if (top_dest != NULL &&
-        top_dest->elem()->make_oopptr() != NULL &&
-        top_dest->elem()->make_oopptr()->can_be_value_type()) {
+    if (top_dest != NULL && !top_dest->elem()->isa_valuetype() && !top_dest->is_not_flat()) {
       generate_valueArray_guard(dest_klass, slow_region);
     }
 
-    if (top_src != NULL &&
-        top_src->elem()->make_oopptr() != NULL &&
-        top_src->elem()->make_oopptr()->can_be_value_type()) {
+    if (top_src != NULL && !top_src->elem()->isa_valuetype() && !top_src->is_not_flat()) {
       generate_valueArray_guard(src_klass, slow_region);
     }
 
--- a/src/hotspot/share/opto/macro.cpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/macro.cpp	Wed Jul 03 14:27:42 2019 +0200
@@ -1720,13 +1720,13 @@
     if (UseCompressedClassPointers) {
       // Compress the klass pointer before inserting the storage properties value
       metadata = transform_later(new EncodePKlassNode(metadata, metadata->bottom_type()->make_narrowklass()));
-    }
-    metadata = transform_later(new CastP2XNode(NULL, metadata));
-    metadata = transform_later(new OrXNode(metadata, properties));
-    bt = T_LONG;
-    if (UseCompressedClassPointers) {
+      metadata = transform_later(new CastN2INode(metadata));
+      metadata = transform_later(new OrINode(metadata, transform_later(new ConvL2INode(properties))));
       bt = T_INT;
-      metadata = transform_later(new ConvL2INode(metadata));
+    } else {
+      metadata = transform_later(new CastP2XNode(NULL, metadata));
+      metadata = transform_later(new OrXNode(metadata, properties));
+      bt = T_LONG;
     }
   }
   rawmem = make_store(control, rawmem, object, oopDesc::klass_offset_in_bytes(), metadata, bt);
--- a/src/hotspot/share/opto/macroArrayCopy.cpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/macroArrayCopy.cpp	Wed Jul 03 14:27:42 2019 +0200
@@ -189,6 +189,7 @@
 }
 
 Node* PhaseMacroExpand::generate_flattened_array_guard(Node** ctrl, Node* mem, Node* obj_or_klass, RegionNode* region) {
+  assert(ValueArrayFlatten, "can never be flattened");
   return generate_array_guard(ctrl, mem, obj_or_klass, region, Klass::_lh_array_tag_vt_value);
 }
 
@@ -1323,7 +1324,9 @@
     RegionNode* slow_region = new RegionNode(1);
     transform_later(slow_region);
 
-    generate_flattened_array_guard(&ctrl, merge_mem, dest, slow_region);
+    if (ValueArrayFlatten && (top_dest == NULL || !top_dest->is_not_flat())) {
+      generate_flattened_array_guard(&ctrl, merge_mem, dest, slow_region);
+    }
 
     // Call StubRoutines::generic_arraycopy stub.
     Node* mem = generate_arraycopy(ac, NULL, &ctrl, merge_mem, &io,
@@ -1423,15 +1426,11 @@
     // (9) each element of an oop array must be assignable
     // The generate_arraycopy subroutine checks this.
 
-    if (dest_elem == T_OBJECT &&
-        ValueArrayFlatten &&
-        top_dest->elem()->make_oopptr()->can_be_value_type()) {
+    if (dest_elem == T_OBJECT && !top_dest->elem()->isa_valuetype() && !top_dest->is_not_flat()) {
       generate_flattened_array_guard(&ctrl, merge_mem, dest, slow_region);
     }
 
-    if (src_elem == T_OBJECT &&
-        ValueArrayFlatten &&
-        top_src->elem()->make_oopptr()->can_be_value_type()) {
+    if (src_elem == T_OBJECT && !top_src->elem()->isa_valuetype() && !top_src->is_not_flat()) {
       generate_flattened_array_guard(&ctrl, merge_mem, src, slow_region);
     }
   }
--- a/src/hotspot/share/opto/parse1.cpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/parse1.cpp	Wed Jul 03 14:27:42 2019 +0200
@@ -615,6 +615,15 @@
       // 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 if (i == (uint)(arg_size_sig - 1) && !is_osr_parse() && method()->has_vararg() &&
+               t->isa_aryptr() != NULL && !t->is_aryptr()->is_not_null_free()) {
+      // Speculate on varargs Object array being not null-free (and therefore also not flattened)
+      const TypePtr* spec_type = t->speculative();
+      spec_type = (spec_type != NULL && spec_type->isa_aryptr() != NULL) ? spec_type : t->is_aryptr();
+      spec_type = spec_type->remove_speculative()->is_aryptr()->cast_to_not_null_free();
+      spec_type = TypeOopPtr::make(TypePtr::BotPTR, Type::Offset::bottom, TypeOopPtr::InstanceBot, spec_type);
+      Node* cast = _gvn.transform(new CheckCastPPNode(control(), parm, t->join_speculative(spec_type)));
+      replace_in_map(parm, cast);
     }
   }
 
--- a/src/hotspot/share/opto/parse2.cpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/parse2.cpp	Wed Jul 03 14:27:42 2019 +0200
@@ -73,9 +73,10 @@
   } else if (elemptr != NULL && elemptr->is_valuetypeptr() && !elemptr->maybe_null()) {
     // Load from non-flattened but flattenable value type array (elements can never be null)
     bt = T_VALUETYPE;
-  } else if (ValueArrayFlatten && elemptr != NULL && elemptr->can_be_value_type() &&
-             !ary_t->klass_is_exact() && (!elemptr->is_valuetypeptr() || elemptr->value_klass()->flatten_array())) {
+  } else if (!ary_t->is_not_flat() && !ary_t->klass_is_exact()) {
     // Cannot statically determine if array is flattened, emit runtime check
+    assert(ValueArrayFlatten && elemptr != NULL && elemptr->can_be_value_type() &&
+           (!elemptr->is_valuetypeptr() || elemptr->value_klass()->flatten_array()), "array can't be flattened");
     Node* ctl = control();
     IdealKit ideal(this);
     IdealVariable res(ideal);
@@ -107,7 +108,6 @@
         ideal.sync_kit(this);
       } else {
         // Element type is unknown, emit runtime call
-        assert(!ary_t->klass_is_exact(), "should not have exact type here");
         Node* k_adr = basic_plus_adr(kls, in_bytes(ArrayKlass::element_klass_offset()));
         Node* elem_klass = _gvn.transform(LoadKlassNode::make(_gvn, NULL, immutable_memory(), k_adr, TypeInstPtr::KLASS));
         Node* obj_size  = NULL;
@@ -215,7 +215,7 @@
   const TypeAryPtr* ary_t = _gvn.type(ary)->is_aryptr();
   if (bt == T_OBJECT) {
     const TypeOopPtr* elemptr = elemtype->make_oopptr();
-    const Type* val_t = _gvn.type(val);
+    const Type* val_t = _gvn.type(cast_val);
     if (elemtype->isa_valuetype() != NULL) {
       C->set_flattened_accesses();
       // Store to flattened value type array
@@ -236,35 +236,40 @@
         if (stopped()) return;
         dec_sp(3);
       }
-    } else if (elemptr->can_be_value_type() && (!ary_t->klass_is_exact() || elemptr->is_valuetypeptr()) &&
-               (val->is_ValueType() || val_t == TypePtr::NULL_PTR || val_t->is_oopptr()->can_be_value_type())) {
-      // Cannot statically determine if array is flattened, emit runtime check
+    } else if (elemptr->can_be_value_type() && !ary_t->klass_is_exact() &&
+               (cast_val->is_ValueType() || val_t == TypePtr::NULL_PTR || val_t->is_oopptr()->can_be_value_type())) {
+      // Cannot statically determine if array is flattened or null-free, emit runtime checks
       ciValueKlass* vk = NULL;
       // Try to determine the value klass
-      if (val->is_ValueType()) {
+      if (cast_val->is_ValueType()) {
         vk = val_t->value_klass();
       } else if (elemptr->is_valuetypeptr()) {
         vk = elemptr->value_klass();
       }
-      if (ValueArrayFlatten && (vk == NULL || vk->flatten_array())) {
+      if (!ary_t->is_not_flat() && (vk == NULL || vk->flatten_array())) {
+        // Array might be flattened
+        assert(ValueArrayFlatten && !ary_t->is_not_null_free(), "a null-ok array can't be flattened");
         IdealKit ideal(this);
         Node* kls = load_object_klass(ary);
         Node* layout_val = load_lh_array_tag(kls);
-        ideal.if_then(layout_val, BoolTest::ne, intcon(Klass::_lh_array_tag_vt_value)); {
+        ideal.if_then(layout_val, BoolTest::ne, intcon(Klass::_lh_array_tag_vt_value));
+        {
           // non-flattened
           sync_kit(ideal);
-          gen_value_array_null_guard(ary, val, 3);
+          gen_value_array_null_guard(ary, cast_val, 3);
           const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(bt);
           elemtype = ary_t->elem()->make_oopptr();
-          access_store_at(ary, adr, adr_type, val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY, false, false);
+          access_store_at(ary, adr, adr_type, cast_val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY, false, false);
           ideal.sync_kit(this);
-        } ideal.else_(); {
+        }
+        ideal.else_();
+        {
           // flattened
-          if (!val->is_ValueType() && TypePtr::NULL_PTR->higher_equal(val_t)) {
+          if (!cast_val->is_ValueType() && TypePtr::NULL_PTR->higher_equal(val_t)) {
             // Add null check
             sync_kit(ideal);
             Node* null_ctl = top();
-            val = null_check_oop(val, &null_ctl);
+            cast_val = null_check_oop(cast_val, &null_ctl);
             if (null_ctl != top()) {
               PreserveJVMState pjvms(this);
               inc_sp(3);
@@ -283,15 +288,14 @@
             const TypeAryPtr* arytype = TypeOopPtr::make_from_klass(array_klass)->isa_aryptr();
             ary = _gvn.transform(new CheckCastPPNode(control(), ary, arytype));
             adr = array_element_address(ary, idx, T_OBJECT, arytype->size(), control());
-            if (!val->is_ValueType()) {
-              assert(!gvn().type(val)->maybe_null(), "value type array elements should never be null");
-              val = ValueTypeNode::make_from_oop(this, val, vk);
+            if (!cast_val->is_ValueType()) {
+              assert(!gvn().type(cast_val)->maybe_null(), "value type array elements should never be null");
+              cast_val = ValueTypeNode::make_from_oop(this, cast_val, vk);
             }
-            val->as_ValueType()->store_flattened(this, ary, adr);
+            cast_val->as_ValueType()->store_flattened(this, ary, adr);
             ideal.sync_kit(this);
           } else if (!ideal.ctrl()->is_top()) {
             // Element type is unknown, emit runtime call
-            assert(!ary_t->klass_is_exact(), "should not have exact type here");
             sync_kit(ideal);
 
             // This membar keeps this access to an unknown flattened
@@ -303,7 +307,7 @@
             ideal.make_leaf_call(OptoRuntime::store_unknown_value_Type(),
                                  CAST_FROM_FN_PTR(address, OptoRuntime::store_unknown_value),
                                  "store_unknown_value",
-                                 val, ary, idx);
+                                 cast_val, ary, idx);
 
             sync_kit(ideal);
             // Same as MemBarCPUOrder above: keep this unknown
@@ -313,11 +317,13 @@
             ideal.sync_kit(this);
 
           }
-        } ideal.end_if();
+        }
+        ideal.end_if();
         sync_kit(ideal);
         return;
-      } else {
-        gen_value_array_null_guard(ary, val, 3);
+      } else if (!ary_t->is_not_null_free()) {
+        // Array is never flattened
+        gen_value_array_null_guard(ary, cast_val, 3);
       }
     }
   }
@@ -430,6 +436,20 @@
   // Check for always knowing you are throwing a range-check exception
   if (stopped())  return top();
 
+  // Speculate on the array not being null-free
+  if (!arytype->is_not_null_free() && arytype->speculative() != NULL && arytype->speculative()->isa_aryptr() != NULL &&
+      arytype->speculative()->is_aryptr()->is_not_null_free() &&
+      !too_many_traps_or_recompiles(Deoptimization::Reason_speculate_class_check)) {
+    Node* tst = gen_null_free_array_check(ary);
+    {
+      BuildCutout unless(this, tst, PROB_ALWAYS);
+      uncommon_trap(Deoptimization::Reason_speculate_class_check,
+                    Deoptimization::Action_maybe_recompile);
+    }
+    Node* cast = new CheckCastPPNode(control(), ary, arytype->cast_to_not_null_free());
+    replace_in_map(ary, _gvn.transform(cast));
+  }
+
   // Make array address computation control dependent to prevent it
   // from floating above the range check during loop optimizations.
   Node* ptr = array_element_address(ary, idx, type, sizetype, control());
--- a/src/hotspot/share/opto/type.cpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/type.cpp	Wed Jul 03 14:27:42 2019 +0200
@@ -2219,12 +2219,13 @@
 }
 
 //------------------------------make-------------------------------------------
-const TypeAry* TypeAry::make(const Type* elem, const TypeInt* size, bool stable) {
+const TypeAry* TypeAry::make(const Type* elem, const TypeInt* size, bool stable,
+                             bool not_flat, bool not_null_free) {
   if (UseCompressedOops && elem->isa_oopptr()) {
     elem = elem->make_narrowoop();
   }
   size = normalize_array_size(size);
-  return (TypeAry*)(new TypeAry(elem,size,stable))->hashcons();
+  return (TypeAry*)(new TypeAry(elem, size, stable, not_flat, not_null_free))->hashcons();
 }
 
 //------------------------------meet-------------------------------------------
@@ -2246,7 +2247,9 @@
     const TypeAry *a = t->is_ary();
     return TypeAry::make(_elem->meet_speculative(a->_elem),
                          _size->xmeet(a->_size)->is_int(),
-                         _stable && a->_stable);
+                         _stable && a->_stable,
+                         _not_flat && a->_not_flat,
+                         _not_null_free && a->_not_null_free);
   }
   case Top:
     break;
@@ -2259,7 +2262,7 @@
 const Type *TypeAry::xdual() const {
   const TypeInt* size_dual = _size->dual()->is_int();
   size_dual = normalize_array_size(size_dual);
-  return new TypeAry(_elem->dual(), size_dual, !_stable);
+  return new TypeAry(_elem->dual(), size_dual, !_stable, !_not_flat, !_not_null_free);
 }
 
 //------------------------------eq---------------------------------------------
@@ -2268,7 +2271,10 @@
   const TypeAry *a = (const TypeAry*)t;
   return _elem == a->_elem &&
     _stable == a->_stable &&
-    _size == a->_size;
+    _size == a->_size &&
+    _not_flat == a->_not_flat &&
+    _not_null_free == a->_not_null_free;
+
 }
 
 //------------------------------hash-------------------------------------------
@@ -2281,14 +2287,14 @@
  * Return same type without a speculative part in the element
  */
 const Type* TypeAry::remove_speculative() const {
-  return make(_elem->remove_speculative(), _size, _stable);
+  return make(_elem->remove_speculative(), _size, _stable, _not_flat, _not_null_free);
 }
 
 /**
  * Return same type with cleaned up speculative part of element
  */
 const Type* TypeAry::cleanup_speculative() const {
-  return make(_elem->cleanup_speculative(), _size, _stable);
+  return make(_elem->cleanup_speculative(), _size, _stable, _not_flat, _not_null_free);
 }
 
 /**
@@ -2322,6 +2328,10 @@
 #ifndef PRODUCT
 void TypeAry::dump2( Dict &d, uint depth, outputStream *st ) const {
   if (_stable)  st->print("stable:");
+  if (Verbose) {
+    if (_not_flat) st->print("not flat:");
+    if (_not_null_free) st->print("not null free:");
+  }
   _elem->dump2(d, depth, st);
   st->print("[");
   _size->dump2(d, depth, st);
@@ -2365,7 +2375,7 @@
   else
     tinst = _elem->isa_instptr();
   if (tinst) {
-    // [V? has a subtype: [V. So eventhough V is final, [V? is not exact.
+    // [V? has a subtype: [V. So even though V is final, [V? is not exact.
     if (tklass->as_instance_klass()->is_final()) {
       if (tinst->is_valuetypeptr() && (tinst->ptr() == TypePtr::BotPTR || tinst->ptr() == TypePtr::TopPTR)) {
         return false;
@@ -3464,12 +3474,15 @@
     // Element is an object or value array. Recursively call ourself.
     const TypeOopPtr* etype = TypeOopPtr::make_from_klass_common(klass->as_array_klass()->element_klass(), false, try_for_exact);
     bool null_free = klass->is_loaded() && klass->as_array_klass()->storage_properties().is_null_free();
-    if (null_free && etype->is_valuetypeptr()) {
+    if (null_free) {
+      assert(etype->is_valuetypeptr(), "must be a valuetypeptr");
       etype = etype->join_speculative(TypePtr::NOTNULL)->is_oopptr();
     }
-    // [V? has a subtype: [V. So eventhough V is final, [V? is not exact.
+    // [V? has a subtype: [V. So even though V is final, [V? is not exact.
     bool xk = etype->klass_is_exact() && (!etype->is_valuetypeptr() || null_free);
-    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS);
+    bool not_flat = !ValueArrayFlatten || xk || !etype->can_be_value_type() || (etype->is_valuetypeptr() && !etype->value_klass()->flatten_array());
+    bool not_null_free = !etype->can_be_value_type();
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS, false, not_flat, not_null_free);
     // We used to pass NotNull in here, asserting that the sub-arrays
     // are all not-null.  This is not true in generally, as code can
     // slam NULLs down in the subarrays.
@@ -3478,7 +3491,8 @@
   } else if (klass->is_type_array_klass()) {
     // Element is an typeArray
     const Type* etype = get_const_basic_type(klass->as_type_array_klass()->element_type());
-    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS);
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS,
+                                        /* stable= */ false, /* not_flat= */ true, /* not_null_free= */ true);
     // We used to pass NotNull in here, asserting that the array pointer
     // is not-null. That was not true in general.
     const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::BotPTR, arr0, klass, true, Offset(0));
@@ -3513,10 +3527,12 @@
     // Element is an object array. Recursively call ourself.
     const TypeOopPtr* etype = TypeOopPtr::make_from_klass_raw(klass->as_array_klass()->element_klass());
     bool null_free = klass->is_loaded() && klass->as_array_klass()->storage_properties().is_null_free();
-    if (null_free && etype->is_valuetypeptr()) {
+    if (null_free) {
+      assert(etype->is_valuetypeptr(), "must be a valuetypeptr");
       etype = etype->join_speculative(TypePtr::NOTNULL)->is_oopptr();
     }
-    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()));
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()),
+                                        /* stable= */ false, /* not_flat= */ true, /* not_null_free= */ !null_free);
     // We used to pass NotNull in here, asserting that the sub-arrays
     // are all not-null.  This is not true in generally, as code can
     // slam NULLs down in the subarrays.
@@ -3527,9 +3543,9 @@
     }
   } else if (klass->is_type_array_klass()) {
     // Element is an typeArray
-    const Type* etype =
-      (Type*)get_const_basic_type(klass->as_type_array_klass()->element_type());
-    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()));
+    const Type* etype = (Type*)get_const_basic_type(klass->as_type_array_klass()->element_type());
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()),
+                                        /* stable= */ false, /* not_flat= */ true, /* not_null_free= */ true);
     // We used to pass NotNull in here, asserting that the array pointer
     // is not-null. That was not true in general.
     if (make_constant) {
@@ -4476,7 +4492,26 @@
   assert(new_size != NULL, "");
   new_size = narrow_size_type(new_size);
   if (new_size == size())  return this;
-  const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable());
+  const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable(), is_not_flat(), is_not_null_free());
+  return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
+}
+
+//-------------------------------cast_to_not_flat------------------------------
+const TypeAryPtr* TypeAryPtr::cast_to_not_flat(bool not_flat) const {
+  if (not_flat == is_not_flat()) {
+    return this;
+  }
+  const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), not_flat, is_not_null_free());
+  return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
+}
+
+//-------------------------------cast_to_not_null_free-------------------------
+const TypeAryPtr* TypeAryPtr::cast_to_not_null_free(bool not_null_free) const {
+  if (not_null_free == is_not_null_free()) {
+    return this;
+  }
+  // Not null free implies not flat
+  const TypeAry* new_ary = TypeAry::make(elem(), size(), is_stable(), not_null_free ? true : is_not_flat(), not_null_free);
   return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
 }
 
@@ -4493,7 +4528,7 @@
     elem = elem_ptr = elem_ptr->is_aryptr()->cast_to_stable(stable, stable_dimension - 1);
   }
 
-  const TypeAry* new_ary = TypeAry::make(elem, size(), stable);
+  const TypeAry* new_ary = TypeAry::make(elem, size(), stable, is_not_flat(), is_not_null_free());
 
   return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache);
 }
@@ -4516,7 +4551,7 @@
   // The pointers in the autobox arrays are always non-null.
   TypePtr::PTR ptr_type = cache ? TypePtr::NotNull : TypePtr::AnyNull;
   etype = etype->cast_to_ptr_type(TypePtr::NotNull)->is_oopptr();
-  const TypeAry* new_ary = TypeAry::make(etype, size(), is_stable());
+  const TypeAry* new_ary = TypeAry::make(etype, size(), is_stable(), is_not_flat(), is_not_null_free());
   return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _field_offset, _instance_id, _speculative, _inline_depth, cache);
 }
 
@@ -4636,7 +4671,7 @@
         // Something like byte[int+] meets char[int+].
         // This must fall to bottom, not (int[-128..65535])[int+].
         instance_id = InstanceBot;
-        tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable);
+        tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable, tary->_not_flat, tary->_not_null_free);
       }
     } else if (klass() != NULL && tap->klass() != NULL &&
                klass()->as_array_klass()->storage_properties().value() != tap->klass()->as_array_klass()->storage_properties().value()) {
@@ -4663,7 +4698,7 @@
            // 'this' is exact and super or unrelated:
            (this->_klass_is_exact && !klass()->is_subtype_of(tap->klass())))) {
       if (above_centerline(ptr)) {
-        tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable);
+        tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable, tary->_not_flat, tary->_not_null_free);
       }
       return make(NotNull, NULL, tary, lazy_klass, false, off, field_off, InstanceBot, speculative, depth);
     }
@@ -4867,6 +4902,18 @@
   return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, _field_offset, _instance_id, NULL, _inline_depth, _is_autobox_cache);
 }
 
+const Type* TypeAryPtr::cleanup_speculative() const {
+  if (speculative() == NULL) {
+    return this;
+  }
+  // Keep speculative part if it contains information about flat-/nullability
+  const TypeAryPtr* spec_aryptr = speculative()->isa_aryptr();
+  if (spec_aryptr != NULL && (spec_aryptr->is_not_flat() || spec_aryptr->is_not_null_free())) {
+    return this;
+  }
+  return TypeOopPtr::cleanup_speculative();
+}
+
 const TypePtr *TypeAryPtr::with_inline_depth(int depth) const {
   if (!UseInlineDepthForSpeculativeTypes) {
     return this;
--- a/src/hotspot/share/opto/type.hpp	Wed Jul 03 10:40:36 2019 +0200
+++ b/src/hotspot/share/opto/type.hpp	Wed Jul 03 14:27:42 2019 +0200
@@ -213,7 +213,6 @@
     Compile* compile = Compile::current();
     compile->set_type_last_size(x);
     void *temp = compile->type_arena()->Amalloc_D(x);
-    compile->set_type_hwm(temp);
     return temp;
   }
   inline void operator delete( void* ptr ) {
@@ -723,8 +722,8 @@
 //------------------------------TypeAry----------------------------------------
 // Class of Array Types
 class TypeAry : public Type {
-  TypeAry(const Type* elem, const TypeInt* size, bool stable) : Type(Array),
-      _elem(elem), _size(size), _stable(stable) {}
+  TypeAry(const Type* elem, const TypeInt* size, bool stable, bool not_flat, bool not_null_free) : Type(Array),
+      _elem(elem), _size(size), _stable(stable), _not_flat(not_flat), _not_null_free(not_null_free) {}
 public:
   virtual bool eq( const Type *t ) const;
   virtual int  hash() const;             // Type specific hashing
@@ -735,10 +734,16 @@
   const Type *_elem;            // Element type of array
   const TypeInt *_size;         // Elements in array
   const bool _stable;           // Are elements @Stable?
+
+  // Value type array properties
+  const bool _not_flat;         // Array is never flattened
+  const bool _not_null_free;    // Array is never null-free
+
   friend class TypeAryPtr;
 
 public:
-  static const TypeAry* make(const Type* elem, const TypeInt* size, bool stable = false);
+  static const TypeAry* make(const Type* elem, const TypeInt* size, bool stable = false,
+                             bool not_flat = false, bool not_null_free = false);
 
   virtual const Type *xmeet( const Type *t ) const;
   virtual const Type *xdual() const;    // Compute dual right now.
@@ -1095,7 +1100,7 @@
   bool is_known_instance_field() const { return is_known_instance() && _offset.get() >= 0; }
 
   virtual bool can_be_value_type() const { return EnableValhalla && can_be_value_type_raw(); }
-  virtual bool can_be_value_type_raw() const { return _klass == NULL || _klass->is_valuetype() || ((_klass->is_java_lang_Object() || _klass->is_interface()) && !klass_is_exact()); }
+  virtual bool can_be_value_type_raw() const { return _klass == NULL || !_klass->is_loaded() || _klass->is_valuetype() || ((_klass->is_java_lang_Object() || _klass->is_interface()) && !klass_is_exact()); }
 
   virtual intptr_t get_con() const;
 
@@ -1265,6 +1270,10 @@
   const TypeInt* size() const { return _ary->_size; }
   bool      is_stable() const { return _ary->_stable; }
 
+  // Value type array properties
+  bool is_not_flat()      const { return _ary->_not_flat; }
+  bool is_not_null_free() const { return _ary->_not_null_free; }
+
   bool is_autobox_cache() const { return _is_autobox_cache; }
 
   static const TypeAryPtr* make(PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, Offset offset,
@@ -1297,6 +1306,7 @@
 
   // Speculative type helper methods.
   virtual const Type* remove_speculative() const;
+  virtual const Type* cleanup_speculative() const;
   virtual const TypePtr* with_inline_depth(int depth) const;
   virtual const TypePtr* with_instance_id(int instance_id) const;
 
@@ -1304,6 +1314,9 @@
   virtual const Type *xmeet_helper(const Type *t) const;
   virtual const Type *xdual() const;    // Compute dual right now.
 
+  const TypeAryPtr* cast_to_not_flat(bool not_flat = true) const;
+  const TypeAryPtr* cast_to_not_null_free(bool not_null_free = true) const;
+
   const TypeAryPtr* cast_to_stable(bool stable, int stable_dimension = 1) const;
   int stable_dimension() const;
 
@@ -1594,12 +1607,11 @@
 
   // Accessors:
   const TypeTuple* domain_sig() const { return _domain_sig; }
-  const TypeTuple* domain_cc() const { return _domain_cc; }
+  const TypeTuple* domain_cc()  const { return _domain_cc; }
   const TypeTuple* range_sig()  const { return _range_sig; }
-  const TypeTuple* range_cc()  const { return _range_cc; }
+  const TypeTuple* range_cc()   const { return _range_cc; }
 
   static const TypeFunc *make(ciMethod* method);
-  static const TypeFunc *make(ciSignature signature, const Type* extra);
   static const TypeFunc *make(const TypeTuple* domain_sig, const TypeTuple* domain_cc,
                               const TypeTuple* range_sig, const TypeTuple* range_cc);
   static const TypeFunc *make(const TypeTuple* domain, const TypeTuple* range);
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableArrays.java	Wed Jul 03 10:40:36 2019 +0200
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableArrays.java	Wed Jul 03 14:27:42 2019 +0200
@@ -59,8 +59,10 @@
     @Override
     public String[] getExtraVMParameters(int scenario) {
         switch (scenario) {
-        case 3: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:+ValueArrayFlatten"};
-        case 4: return new String[] {"-XX:-MonomorphicArrayCheck"};
+        case 2: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"};
+        case 3: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"};
+        case 4: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"};
+        case 5: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"};
         }
         return null;
     }
@@ -2548,4 +2550,244 @@
             }
         }
     }
+
+    // Test loads from vararg arrays
+    @Test(failOn = LOAD_UNKNOWN_VALUE)
+    public static Object test97(Object... args) {
+        return args[0];
+    }
+
+    @DontCompile
+    public static void test97_verifier(boolean warmup) {
+        Object obj = new Object();
+        Object result = test97(obj);
+        Asserts.assertEquals(result, obj);
+        Integer[] myInt = new Integer[1];
+        myInt[0] = rI;
+        result = test97((Object[])myInt);
+        Asserts.assertEquals(result, rI);
+    }
+
+    @Test()
+    public static Object test98(Object... args) {
+        return args[0];
+    }
+
+    @DontCompile
+    public static void test98_verifier(boolean warmup) {
+        Object obj = new Object();
+        Object result = test98(obj);
+        Asserts.assertEquals(result, obj);
+        Integer[] myInt = new Integer[1];
+        myInt[0] = rI;
+        result = test98((Object[])myInt);
+        Asserts.assertEquals(result, rI);
+        if (!warmup) {
+            MyValue1[] va = new MyValue1[1];
+            MyValue1?[] vab = new MyValue1?[1];
+            result = test98((Object[])va);
+            Asserts.assertEquals(((MyValue1)result).hash(), MyValue1.default.hash());
+            result = test98((Object[])vab);
+            Asserts.assertEquals(result, null);
+        }
+    }
+
+    @Test()
+    public static Object test99(Object... args) {
+        return args[0];
+    }
+
+    @DontCompile
+    public static void test99_verifier(boolean warmup) {
+        Object obj = new Object();
+        Object result = test99(obj);
+        Asserts.assertEquals(result, obj);
+        Integer[] myInt = new Integer[1];
+        myInt[0] = rI;
+        result = test99((Object[])myInt);
+        Asserts.assertEquals(result, rI);
+        if (!warmup) {
+            try {
+                test99((Object[])null);
+                throw new RuntimeException("No NPE thrown");
+            } catch (NullPointerException npe) {
+                // Expected
+            }
+        }
+    }
+
+    @Test()
+    public static Object test100(Object... args) {
+        return args[0];
+    }
+
+    @DontCompile
+    public static void test100_verifier(boolean warmup) {
+        Object obj = new Object();
+        Object result = test100(obj);
+        Asserts.assertEquals(result, obj);
+        Integer[] myInt = new Integer[1];
+        myInt[0] = rI;
+        result = test100((Object[])myInt);
+        Asserts.assertEquals(result, rI);
+        if (!warmup) {
+            try {
+                test100();
+                throw new RuntimeException("No AIOOBE thrown");
+            } catch (ArrayIndexOutOfBoundsException aioobe) {
+                // Expected
+            }
+        }
+    }
+
+    // Test stores to varag arrays
+    @Test(failOn = STORE_UNKNOWN_VALUE)
+    public static void test101(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @DontCompile
+    public static void test101_verifier(boolean warmup) {
+        Object obj = new Object();
+        test101(obj, obj);
+        Integer[] myInt = new Integer[1];
+        test101(rI, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], rI);
+        test101(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+    }
+
+    @Test()
+    public static void test102(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @DontCompile
+    public static void test102_verifier(boolean warmup) {
+        Object obj = new Object();
+        test102(obj, obj);
+        Integer[] myInt = new Integer[1];
+        test102(rI, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], rI);
+        test102(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+        if (!warmup) {
+            MyValue1[] va = new MyValue1[1];
+            MyValue1?[] vab = new MyValue1?[1];
+            test102(testValue1, (Object[])va);
+            Asserts.assertEquals(va[0].hash(), testValue1.hash());
+            test102(testValue1, (Object[])vab);
+            Asserts.assertEquals(vab[0].hash(), testValue1.hash());
+            test102(null, (Object[])vab);
+            Asserts.assertEquals(vab[0], null);
+        }
+    }
+
+    @Test()
+    public static void test103(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @DontCompile
+    public static void test103_verifier(boolean warmup) {
+        Object obj = new Object();
+        test103(obj, obj);
+        Integer[] myInt = new Integer[1];
+        test103(rI, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], rI);
+        test103(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+        if (!warmup) {
+            MyValue1[] va = new MyValue1[1];
+            try {
+                test103(null, (Object[])va);
+                throw new RuntimeException("No NPE thrown");
+            } catch (NullPointerException npe) {
+                // Expected
+            }
+        }
+    }
+
+    @Test()
+    public static void test104(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @DontCompile
+    public static void test104_verifier(boolean warmup) {
+        Object obj = new Object();
+        test104(obj, obj);
+        Integer[] myInt = new Integer[1];
+        test104(rI, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], rI);
+        test104(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+        if (!warmup) {
+            try {
+                test104(testValue1);
+                throw new RuntimeException("No AIOOBE thrown");
+            } catch (ArrayIndexOutOfBoundsException aioobe) {
+                // Expected
+            }
+        }
+    }
+
+    @Test()
+    public static void test105(Object val, Object... args) {
+        args[0] = val;
+    }
+
+    @DontCompile
+    public static void test105_verifier(boolean warmup) {
+        Object obj = new Object();
+        test105(obj, obj);
+        Integer[] myInt = new Integer[1];
+        test105(rI, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], rI);
+        test105(null, (Object[])myInt);
+        Asserts.assertEquals(myInt[0], null);
+        if (!warmup) {
+            try {
+                test105(testValue1, (Object[])null);
+                throw new RuntimeException("No NPE thrown");
+            } catch (NullPointerException npe) {
+                // Expected
+            }
+        }
+    }
+
+    @Test()
+    public static Object[] test106(Object[] dst, Object... args) {
+        // Access array to speculate on non-flatness
+        if (args[0] == null) {
+            args[0] = testValue1;
+        }
+        System.arraycopy(args, 0, dst, 0, args.length);
+        System.arraycopy(dst, 0, args, 0, dst.length);
+        Object[] clone = args.clone();
+        if (clone[0] == null) {
+            throw new RuntimeException("Unexpected null");
+        }
+        return Arrays.copyOf(args, args.length, Object[].class);
+    }
+
+    @DontCompile
+    public static void test106_verifier(boolean warmup) {
+        Object[] dst = new Object[1];
+        Object obj = new Object();
+        Object[] result = test106(dst, obj);
+        Asserts.assertEquals(result[0], obj);
+        Integer[] myInt = new Integer[1];
+        myInt[0] = rI;
+        result = test106(myInt, (Object[])myInt);
+        Asserts.assertEquals(result[0], rI);
+        if (!warmup) {
+            MyValue1[] va = new MyValue1[1];
+            MyValue1?[] vab = new MyValue1?[1];
+            result = test106(va, (Object[])va);
+            Asserts.assertEquals(((MyValue1)result[0]).hash(), MyValue1.default.hash());
+            result = test106(vab, (Object[])vab);
+            Asserts.assertEquals(((MyValue1)result[0]).hash(), testValue1.hash());
+        }
+    }
 }
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/ValueTypeTest.java	Wed Jul 03 10:40:36 2019 +0200
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/ValueTypeTest.java	Wed Jul 03 14:27:42 2019 +0200
@@ -196,6 +196,8 @@
     protected static final String CALL = START + "CallStaticJava" + MID + END;
     protected static final String STOREVALUETYPEFIELDS = START + "CallStaticJava" + MID + "store_value_type_fields" + END;
     protected static final String SCOBJ = "(.*# ScObj.*" + END;
+    protected static final String LOAD_UNKNOWN_VALUE = "(.*call_leaf,runtime  load_unknown_value.*" + END;
+    protected static final String STORE_UNKNOWN_VALUE = "(.*call_leaf,runtime  store_unknown_value.*" + END;
 
     public static String[] concat(String prefix[], String... extra) {
         ArrayList<String> list = new ArrayList<String>();
@@ -261,7 +263,8 @@
                 "-XX:ValueArrayElemMaxFlatSize=-1",
                 "-XX:ValueFieldMaxFlatSize=0",
                 "-XX:+ValueTypePassFieldsAsArgs",
-                "-XX:-ValueTypeReturnedAsFields"};
+                "-XX:-ValueTypeReturnedAsFields",
+                "-XX:-ReduceInitialCardMarks"};
         case 5: return new String[] {
                 "-XX:+AlwaysIncrementalInline",
                 "-XX:ValueArrayElemMaxFlatOops=-1",