OpenJDK / valhalla / valhalla
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",