--- old/src/hotspot/share/opto/cfgnode.hpp 2019-07-17 14:45:52.273878393 +0200 +++ new/src/hotspot/share/opto/cfgnode.hpp 2019-07-17 14:45:51.977853308 +0200 @@ -400,6 +400,8 @@ // Returns NULL is it couldn't improve the type. static const TypeInt* filtered_int_type(PhaseGVN* phase, Node* val, Node* if_proj); + bool is_flattened_array_check(PhaseTransform* phase, Node*& array); + #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; virtual void related(GrowableArray *in_rel, GrowableArray *out_rel, bool compact) const; --- old/src/hotspot/share/opto/graphKit.cpp 2019-07-17 14:45:52.889930597 +0200 +++ new/src/hotspot/share/opto/graphKit.cpp 2019-07-17 14:45:52.593905512 +0200 @@ -3411,6 +3411,42 @@ set_control( _gvn.transform(region) ); record_for_igvn(region); + bool not_null_free = !toop->can_be_value_type(); + bool not_flattenable = !ValueArrayFlatten || not_null_free || (toop->is_valuetypeptr() && !toop->value_klass()->flatten_array()); + if (EnableValhalla && not_flattenable) { + // Check if obj has been loaded from an array + obj = obj->isa_DecodeN() ? obj->in(1) : obj; + Node* array = NULL; + if (obj->isa_Load()) { + Node* address = obj->in(MemNode::Address); + if (address->isa_AddP()) { + array = address->as_AddP()->in(AddPNode::Base); + } + } else if (obj->is_Phi()) { + Node* region = obj->in(0); + if (region->req() == 3 && region->in(2) != NULL) { + IfNode* iff = region->in(2)->in(0)->isa_If(); + if (iff != NULL) { + iff->is_flattened_array_check(&_gvn, array); + } + } + } + if (array != NULL) { + const TypeAryPtr* ary_t = _gvn.type(array)->isa_aryptr(); + if (ary_t != NULL) { + if (!ary_t->is_not_null_free() && not_null_free) { + // Casting array element to a non-inline-type, mark array as not null-free. + Node* cast = _gvn.transform(new CheckCastPPNode(control(), array, ary_t->cast_to_not_null_free())); + replace_in_map(array, cast); + } else if (!ary_t->is_not_flat()) { + // Casting array element to a non-flattenable type, mark array as not flat. + Node* cast = _gvn.transform(new CheckCastPPNode(control(), array, ary_t->cast_to_not_flat())); + replace_in_map(array, cast); + } + } + } + } + if (!is_value) { res = record_profiled_receiver_for_speculation(res); if (toop->is_valuetypeptr() && toop->value_klass()->is_scalarizable() && !gvn().type(res)->maybe_null()) { @@ -3476,10 +3512,10 @@ } // 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) { +Node* GraphKit::gen_value_array_null_guard(Node* ary, Node* val, int nargs, bool safe_for_replace) { const Type* val_t = _gvn.type(val); if (val->is_ValueType() || !TypePtr::NULL_PTR->higher_equal(val_t)) { - return; // Never null + return ary; // Never null } RegionNode* region = new RegionNode(3); Node* null_ctl = top(); @@ -3499,6 +3535,17 @@ region->init_req(2, control()); set_control(_gvn.transform(region)); record_for_igvn(region); + const TypeAryPtr* ary_t = _gvn.type(ary)->is_aryptr(); + if (val_t == TypePtr::NULL_PTR && !ary_t->is_not_null_free()) { + // Since we were just successfully storing null, the array can't be null free. + ary_t = ary_t->cast_to_not_null_free(); + Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, ary_t)); + if (safe_for_replace) { + replace_in_map(ary, cast); + } + ary = cast; + } + return ary; } Node* GraphKit::load_lh_array_tag(Node* kls) { --- old/src/hotspot/share/opto/graphKit.hpp 2019-07-17 14:45:53.513983479 +0200 +++ new/src/hotspot/share/opto/graphKit.hpp 2019-07-17 14:45:53.233959750 +0200 @@ -861,7 +861,7 @@ 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* gen_value_array_null_guard(Node* ary, Node* val, int nargs, bool safe_for_replace = false); Node* load_lh_array_tag(Node* kls); Node* gen_lh_array_test(Node* kls, unsigned int lh_value); --- old/src/hotspot/share/opto/ifnode.cpp 2019-07-17 14:45:54.046028565 +0200 +++ new/src/hotspot/share/opto/ifnode.cpp 2019-07-17 14:45:53.762004497 +0200 @@ -1212,6 +1212,53 @@ return false; } +// Returns true if this IfNode belongs to a flattened array check +// and returns the corresponding array in the 'array' parameter. +bool IfNode::is_flattened_array_check(PhaseTransform* phase, Node*& array) { + Node* bol = in(1); + if (!bol->is_Bool() || bol->as_Bool()->_test._test != BoolTest::ne) { + return false; + } + Node* cmp = bol->in(1); + if (cmp->Opcode() != Op_CmpI) { + return false; + } + Node* cmp_in1 = cmp->in(1); + Node* cmp_in2 = cmp->in(2); + if ((unsigned int)cmp_in2->find_int_con(0) != Klass::_lh_array_tag_vt_value) { + return false; + } + if (cmp_in1->Opcode() != Op_RShiftI) { + return false; + } + Node* shift_in1 = cmp_in1->in(1); + Node* shift_in2 = cmp_in1->in(2); + if ((unsigned int)shift_in2->find_int_con(0) != Klass::_lh_array_tag_shift) { + return false; + } + if (shift_in1->Opcode() != Op_LoadI) { + return false; + } + intptr_t offset; + Node* ptr = shift_in1->in(MemNode::Address); + Node* addr = AddPNode::Ideal_base_and_offset(ptr, phase, offset); + if (addr == NULL || offset != in_bytes(Klass::layout_helper_offset())) { + return false; + } + if (!phase->type(addr)->isa_klassptr()) { + return false; + } + Node* klass_load = ptr->as_AddP()->in(AddPNode::Base)->uncast(); + if (klass_load->is_DecodeNKlass()) { + klass_load = klass_load->in(1); + } + if (klass_load->is_Load()) { + Node* address = klass_load->in(MemNode::Address); + array = address->as_AddP()->in(AddPNode::Base); + } + return true; +} + // Check that the If that is in between the 2 integer comparisons has // no side effect bool IfNode::is_side_effect_free_test(ProjNode* proj, PhaseIterGVN* igvn) { --- old/src/hotspot/share/opto/loopUnswitch.cpp 2019-07-17 14:45:54.606076023 +0200 +++ new/src/hotspot/share/opto/loopUnswitch.cpp 2019-07-17 14:45:54.322051956 +0200 @@ -53,45 +53,6 @@ // // Note: the "else" clause may be empty -static bool is_flattened_array_check(Node* iff, PhaseTransform* phase) { - if (iff->Opcode() != Op_If) { - return false; - } - Node* bol = iff->in(1); - if (!bol->is_Bool() || bol->as_Bool()->_test._test != BoolTest::ne) { - return false; - } - Node* cmp = bol->in(1); - if (cmp->Opcode() != Op_CmpI) { - return false; - } - Node* cmp_in1 = cmp->in(1); - Node* cmp_in2 = cmp->in(2); - if ((unsigned int)cmp_in2->find_int_con(0) != Klass::_lh_array_tag_vt_value) { - return false; - } - if (cmp_in1->Opcode() != Op_RShiftI) { - return false; - } - Node* shift_in1 = cmp_in1->in(1); - Node* shift_in2 = cmp_in1->in(2); - if ((unsigned int)shift_in2->find_int_con(0) != Klass::_lh_array_tag_shift) { - return false; - } - if (shift_in1->Opcode() != Op_LoadI) { - return false; - } - intptr_t offset; - Node* addr = AddPNode::Ideal_base_and_offset(shift_in1->in(MemNode::Address), phase, offset); - if (addr == NULL || offset != in_bytes(Klass::layout_helper_offset())) { - return false; - } - if (!phase->type(addr)->isa_klassptr()) { - return false; - } - - return true; -} //------------------------------policy_unswitching----------------------------- // Return TRUE or FALSE if the loop should be unswitched @@ -158,11 +119,12 @@ n = n_dom; } - if (unswitch_iff == NULL || is_flattened_array_check(unswitch_iff, &_igvn)) { + Node* array; + if (unswitch_iff == NULL || unswitch_iff->is_flattened_array_check(&_igvn, array)) { // collect all flattened array checks for (uint i = 0; i < loop->_body.size(); i++) { Node* n = loop->_body.at(i); - if (is_flattened_array_check(n, &_igvn) && + if (n->is_If() && n->as_If()->is_flattened_array_check(&_igvn, array) && loop->is_invariant(n->in(1)) && !loop->is_loop_exit(n)) { flattened_checks.push(n); --- old/src/hotspot/share/opto/parse2.cpp 2019-07-17 14:45:55.166123482 +0200 +++ new/src/hotspot/share/opto/parse2.cpp 2019-07-17 14:45:54.866098059 +0200 @@ -73,9 +73,9 @@ } 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 (!ary_t->is_not_flat() && !ary_t->klass_is_exact()) { + } else if (!ary_t->is_not_flat()) { // Cannot statically determine if array is flattened, emit runtime check - assert(ValueArrayFlatten && elemptr != NULL && elemptr->can_be_value_type() && + assert(ValueArrayFlatten && elemptr->can_be_value_type() && !ary_t->klass_is_exact() && !ary_t->is_not_null_free() && (!elemptr->is_valuetypeptr() || elemptr->value_klass()->flatten_array()), "array can't be flattened"); Node* ctl = control(); IdealKit ideal(this); @@ -96,9 +96,8 @@ sync_kit(ideal); if (elemptr->is_valuetypeptr()) { // Element type is known, cast and load from flattened representation - assert(elemptr->maybe_null(), "must be nullable"); ciValueKlass* vk = elemptr->value_klass(); - assert(vk->flatten_array(), "must be flattenable"); + assert(vk->flatten_array() && elemptr->maybe_null(), "must be a flattenable and nullable array"); ciArrayKlass* array_klass = ciArrayKlass::make(vk, /* never_null */ true); const TypeAryPtr* arytype = TypeOopPtr::make_from_klass(array_klass)->isa_aryptr(); Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, arytype)); @@ -213,129 +212,146 @@ Node* ary = pop(); // The array itself 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(cast_val); - if (elemtype->isa_valuetype() != NULL) { - C->set_flattened_accesses(); + const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(bt); + + if (elemtype == TypeInt::BOOL) { + bt = T_BOOLEAN; + } else if (bt == T_OBJECT) { + elemtype = elemtype->make_oopptr(); + const Type* tval = _gvn.type(cast_val); + // We may have lost type information for 'val' here due to the casts + // emitted by the array_store_check code (see JDK-6312651) + // TODO Remove this code once JDK-6312651 is in. + const Type* tval_init = _gvn.type(val); + bool can_be_value_type = tval->isa_valuetype() || (tval != TypePtr::NULL_PTR && tval_init->is_oopptr()->can_be_value_type() && tval->is_oopptr()->can_be_value_type()); + bool not_flattenable = !can_be_value_type || ((tval_init->is_valuetypeptr() || tval_init->isa_valuetype()) && !tval_init->value_klass()->flatten_array()); + + if (!ary_t->is_not_null_free() && !can_be_value_type && (!tval->maybe_null() || !tval_init->maybe_null())) { + // Storing a non-inline-type, mark array as not null-free. + // This is only legal for non-null stores because the array_store_check passes for null. + ary_t = ary_t->cast_to_not_null_free(); + Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, ary_t)); + replace_in_map(ary, cast); + ary = cast; + } else if (!ary_t->is_not_flat() && not_flattenable) { + // Storing a non-flattenable value, mark array as not flat. + ary_t = ary_t->cast_to_not_flat(); + if (tval != TypePtr::NULL_PTR) { + // For NULL, this transformation is only valid after the null guard below + Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, ary_t)); + replace_in_map(ary, cast); + ary = cast; + } + } + + if (ary_t->elem()->isa_valuetype() != NULL) { // Store to flattened value type array + C->set_flattened_accesses(); if (!cast_val->is_ValueType()) { inc_sp(3); cast_val = null_check(cast_val); if (stopped()) return; dec_sp(3); - cast_val = ValueTypeNode::make_from_oop(this, cast_val, elemtype->value_klass()); + cast_val = ValueTypeNode::make_from_oop(this, cast_val, ary_t->elem()->value_klass()); } cast_val->as_ValueType()->store_flattened(this, ary, adr); return; - } else if (elemptr->is_valuetypeptr() && !elemptr->maybe_null()) { + } else if (elemtype->is_valuetypeptr() && !elemtype->maybe_null()) { // Store to non-flattened but flattenable value type array (elements can never be null) - if (!cast_val->is_ValueType() && val_t->maybe_null()) { + if (!cast_val->is_ValueType() && tval->maybe_null()) { inc_sp(3); cast_val = null_check(cast_val); if (stopped()) return; dec_sp(3); } - } 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 (cast_val->is_ValueType()) { - vk = val_t->value_klass(); - } else if (elemptr->is_valuetypeptr()) { - vk = elemptr->value_klass(); + } else if (!ary_t->is_not_flat()) { + // Array might be flattened, emit runtime checks + assert(ValueArrayFlatten && !not_flattenable && elemtype->is_oopptr()->can_be_value_type() && + !ary_t->klass_is_exact() && !ary_t->is_not_null_free(), "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)); + { + // non-flattened + sync_kit(ideal); + gen_value_array_null_guard(ary, cast_val, 3); + access_store_at(ary, adr, adr_type, cast_val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY, false, false); + ideal.sync_kit(this); } - 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)); - { - // non-flattened + ideal.else_(); + { + // flattened + if (!cast_val->is_ValueType() && tval->maybe_null()) { + // Add null check sync_kit(ideal); - 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, cast_val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY, false, false); + Node* null_ctl = top(); + cast_val = null_check_oop(cast_val, &null_ctl); + if (null_ctl != top()) { + PreserveJVMState pjvms(this); + inc_sp(3); + set_control(null_ctl); + uncommon_trap(Deoptimization::Reason_null_check, Deoptimization::Action_none); + dec_sp(3); + } ideal.sync_kit(this); } - ideal.else_(); - { - // flattened - if (!cast_val->is_ValueType() && val_t->maybe_null()) { - // Add null check - sync_kit(ideal); - Node* null_ctl = top(); - cast_val = null_check_oop(cast_val, &null_ctl); - if (null_ctl != top()) { - PreserveJVMState pjvms(this); - inc_sp(3); - set_control(null_ctl); - uncommon_trap(Deoptimization::Reason_null_check, Deoptimization::Action_none); - dec_sp(3); - } - ideal.sync_kit(this); + // Try to determine the value klass + ciValueKlass* vk = NULL; + if (tval->isa_valuetype() || tval->is_valuetypeptr()) { + vk = tval->value_klass(); + } else if (tval_init->isa_valuetype() || tval_init->is_valuetypeptr()) { + vk = tval_init->value_klass(); + } else if (elemtype->is_valuetypeptr()) { + vk = elemtype->value_klass(); + } + if (vk != NULL && !stopped()) { + // Element type is known, cast and store to flattened representation + sync_kit(ideal); + assert(vk->flatten_array() && elemtype->maybe_null(), "must be a flattenable and nullable array"); + ciArrayKlass* array_klass = ciArrayKlass::make(vk, /* never_null */ true); + 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 (!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); } - if (vk != NULL && !stopped()) { - // Element type is known, cast and store to flattened representation - sync_kit(ideal); - assert(vk->flatten_array(), "must be flattenable"); - assert(elemptr->maybe_null(), "must be nullable"); - ciArrayKlass* array_klass = ciArrayKlass::make(vk, /* never_null */ true); - 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 (!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); - } - 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 - sync_kit(ideal); - - // This membar keeps this access to an unknown flattened - // array correctly ordered with other unknown and known - // flattened array accesses. - insert_mem_bar_volatile(Op_MemBarCPUOrder, C->get_alias_index(TypeAryPtr::VALUES)); - ideal.sync_kit(this); - - ideal.make_leaf_call(OptoRuntime::store_unknown_value_Type(), - CAST_FROM_FN_PTR(address, OptoRuntime::store_unknown_value), - "store_unknown_value", - cast_val, ary, idx); - - sync_kit(ideal); - // Same as MemBarCPUOrder above: keep this unknown - // flattened array access correctly ordered with other - // flattened array access - insert_mem_bar_volatile(Op_MemBarCPUOrder, C->get_alias_index(TypeAryPtr::VALUES)); - ideal.sync_kit(this); + 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 + sync_kit(ideal); - } + // This membar keeps this access to an unknown flattened + // array correctly ordered with other unknown and known + // flattened array accesses. + insert_mem_bar_volatile(Op_MemBarCPUOrder, C->get_alias_index(TypeAryPtr::VALUES)); + ideal.sync_kit(this); + + ideal.make_leaf_call(OptoRuntime::store_unknown_value_Type(), + CAST_FROM_FN_PTR(address, OptoRuntime::store_unknown_value), + "store_unknown_value", + cast_val, ary, idx); + + sync_kit(ideal); + // Same as MemBarCPUOrder above: keep this unknown + // flattened array access correctly ordered with other + // flattened array access + insert_mem_bar_volatile(Op_MemBarCPUOrder, C->get_alias_index(TypeAryPtr::VALUES)); + ideal.sync_kit(this); } - ideal.end_if(); - sync_kit(ideal); - return; - } else if (!ary_t->is_not_null_free()) { - // Array is never flattened - gen_value_array_null_guard(ary, cast_val, 3); } + ideal.end_if(); + sync_kit(ideal); + return; + } else if (!ary_t->is_not_null_free()) { + // Array is not flattened but may be null free + assert(elemtype->is_oopptr()->can_be_value_type() && !ary_t->klass_is_exact(), "array can't be null free"); + ary = gen_value_array_null_guard(ary, cast_val, 3, true); } } - if (elemtype == TypeInt::BOOL) { - bt = T_BOOLEAN; - } else if (bt == T_OBJECT) { - elemtype = ary_t->elem()->make_oopptr(); - } - - const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(bt); - access_store_at(ary, adr, adr_type, val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY); } --- old/src/hotspot/share/opto/type.cpp 2019-07-17 14:45:55.730171279 +0200 +++ new/src/hotspot/share/opto/type.cpp 2019-07-17 14:45:55.442146872 +0200 @@ -3480,8 +3480,8 @@ } // [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); - 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(); + bool not_null_free = !etype->can_be_value_type() || xk; + bool not_flat = !ValueArrayFlatten || not_null_free || (etype->is_valuetypeptr() && !etype->value_klass()->flatten_array()); 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 @@ -4415,7 +4415,13 @@ if( klass_is_exact == _klass_is_exact ) return this; if (!UseExactTypes) return this; if (_ary->ary_must_be_exact()) return this; // cannot clear xk - return make(ptr(), const_oop(), _ary, klass(), klass_is_exact, _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache); + + const TypeAry* new_ary = _ary; + if (klass() != NULL && klass()->is_obj_array_klass() && klass_is_exact) { + // An object array can't be flat or null-free if the klass is exact + new_ary = TypeAry::make(elem(), size(), is_stable(), /* not_flat= */ true, /* not_null_free= */ true); + } + return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact, _offset, _field_offset, _instance_id, _speculative, _inline_depth, _is_autobox_cache); } //-----------------------------cast_to_instance_id---------------------------- --- old/src/hotspot/share/opto/valuetypenode.cpp 2019-07-17 14:45:56.302219755 +0200 +++ new/src/hotspot/share/opto/valuetypenode.cpp 2019-07-17 14:45:56.022196025 +0200 @@ -535,8 +535,8 @@ // Oop can never be null Node* init_ctl = kit->control(); vt->load(kit, oop, oop, vk, /* holder_offset */ 0); - assert(init_ctl != kit->control() || oop->is_Con() || oop->is_CheckCastPP() || oop->Opcode() == Op_ValueTypePtr || - vt->is_loaded(&gvn) == oop, "value type should be loaded"); + assert(init_ctl != kit->control() || !gvn.type(oop)->is_valuetypeptr() || oop->is_Con() || oop->Opcode() == Op_ValueTypePtr || + AllocateNode::Ideal_allocation(oop, &gvn) != NULL || vt->is_loaded(&gvn) == oop, "value type should be loaded"); } assert(vt->is_allocated(&gvn), "value type should be allocated"); --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java 2019-07-17 14:45:56.822263824 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java 2019-07-17 14:45:56.546240433 +0200 @@ -60,8 +60,10 @@ @Override public String[] getExtraVMParameters(int scenario) { switch (scenario) { - case 3: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:ValueArrayElemMaxFlatSize=-1"}; - case 4: return new String[] {"-XX:-MonomorphicArrayCheck"}; + case 2: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"}; + case 3: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:ValueArrayElemMaxFlatSize=-1", "-XX:-UncommonNullCast"}; + case 4: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"}; + case 5: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:-UncommonNullCast"}; } return null; } @@ -82,8 +84,8 @@ } // Test value type array creation and initialization - @Test(valid = ValueTypeArrayFlattenOff, failOn = LOAD) @Test(valid = ValueTypeArrayFlattenOn) + @Test(valid = ValueTypeArrayFlattenOff, failOn = LOAD) public MyValue1[] test1(int len) { MyValue1[] va = new MyValue1[len]; for (int i = 0; i < len; ++i) { @@ -102,7 +104,9 @@ } // Test creation of a value type array and element access - @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) + // TODO 8227588 + @Test(valid = ValueTypeArrayFlattenOn, failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) + @Test(valid = ValueTypeArrayFlattenOff) public long test2() { MyValue1[] va = new MyValue1[1]; va[0] = MyValue1.createWithFieldsInline(rI, rL); @@ -203,7 +207,7 @@ } // Test creation of value type array with single element - @Test(failOn = ALLOCA + LOOP + LOAD + TRAP) + @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) public MyValue1 test6() { MyValue1[] va = new MyValue1[1]; return va[0]; @@ -678,7 +682,9 @@ } // non escaping allocations - @Test(failOn = ALLOCA + LOOP + LOAD + TRAP) + // TODO 8227588: shouldn't this have the same IR matching rules as test6? + @Test(valid = ValueTypeArrayFlattenOn, failOn = ALLOCA + LOOP + LOAD + TRAP) + @Test(valid = ValueTypeArrayFlattenOff) public MyValue2 test29(MyValue2[] src) { MyValue2[] dst = new MyValue2[10]; System.arraycopy(src, 0, dst, 0, 10); @@ -717,7 +723,9 @@ } // non escaping allocation with memory phi - @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + TRAP) + // TODO 8227588 + @Test(valid = ValueTypeArrayFlattenOn, failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) + @Test(valid = ValueTypeArrayFlattenOff) public long test31(boolean b, boolean deopt) { MyValue2[] src = new MyValue2[1]; if (b) { @@ -1826,4 +1834,295 @@ MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); Asserts.assertEQ(test78(v, 1), v.hash()); } + + // Verify that casting an array element to a non-flattenable type marks the array as not-flat + @Test(valid = ValueTypeArrayFlattenOn, match = { ALLOC_G, LOAD_UNKNOWN_VALUE }, matchCount = { 1, 1 }) + @Test(valid = ValueTypeArrayFlattenOff, failOn = ALLOC_G + ALLOCA_G + LOAD_UNKNOWN_VALUE) + public Object test79(Object[] array, int i) { + Integer i1 = (Integer)array[0]; + Object o = array[1]; + return array[i]; + } + + @DontCompile + public void test79_verifier(boolean warmup) { + Integer i = new Integer(rI); + Integer[] array = new Integer[2]; + array[1] = i; + Object result = test79(array, 1); + Asserts.assertEquals(result, i); + } + + inline static class NotFlattenable { + private final Object o1 = null; + private final Object o2 = null; + private final Object o3 = null; + private final Object o4 = null; + private final Object o5 = null; + private final Object o6 = null; + } + + // Same as test80 but with not-flattenable inline type + @Test(valid = ValueTypeArrayFlattenOn, match = { ALLOC_G, LOAD_UNKNOWN_VALUE }, matchCount = { 1, 1 }) + @Test(valid = ValueTypeArrayFlattenOff, failOn = ALLOC_G + ALLOCA_G + LOAD_UNKNOWN_VALUE) + public Object test80(Object[] array, int i) { + NotFlattenable vt = (NotFlattenable)array[0]; + Object o = array[1]; + return array[i]; + } + + @DontCompile + public void test80_verifier(boolean warmup) { + NotFlattenable vt = new NotFlattenable(); + NotFlattenable[] array = new NotFlattenable[2]; + array[1] = vt; + Object result = test80(array, 1); + Asserts.assertEquals(result, vt); + } + + // Verify that writing an object of a non-inline, non-null type to an array marks the array as not-null-free and not-flat + @Test(failOn = ALLOC_G + ALLOCA_G + LOAD_UNKNOWN_VALUE + STORE_UNKNOWN_VALUE + VALUE_ARRAY_NULL_GUARD) + public Object test81(Object[] array, Integer v, Object o, int i) { + if (v == null) { + return null; + } + array[0] = v; + array[1] = array[0]; + array[2] = o; + return array[i]; + } + + @DontCompile + public void test81_verifier(boolean warmup) { + Integer i = new Integer(rI); + Integer[] array1 = new Integer[3]; + Object[] array2 = new Object[3]; + Object result = test81(array1, i, i, 0); + Asserts.assertEquals(array1[0], i); + Asserts.assertEquals(array1[1], i); + Asserts.assertEquals(array1[2], i); + Asserts.assertEquals(result, i); + result = test81(array2, i, i, 1); + Asserts.assertEquals(array2[0], i); + Asserts.assertEquals(array2[1], i); + Asserts.assertEquals(array2[2], i); + Asserts.assertEquals(result, i); + } + + // Verify that writing an object of a non-flattenable inline type to an array marks the array as not-flat + @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOCA_G + LOAD_UNKNOWN_VALUE + STORE_UNKNOWN_VALUE) + @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = ALLOC_G + ALLOCA_G + LOAD_UNKNOWN_VALUE + STORE_UNKNOWN_VALUE) + public Object test82(Object[] array, NotFlattenable vt, Object o, int i) { + array[0] = vt; + array[1] = array[0]; + array[2] = o; + return array[i]; + } + + @DontCompile + public void test82_verifier(boolean warmup) { + NotFlattenable vt = new NotFlattenable(); + NotFlattenable[] array1 = new NotFlattenable[3]; + Object[] array2 = new Object[3]; + Object result = test82(array1, vt, vt, 0); + Asserts.assertEquals(array1[0], vt); + Asserts.assertEquals(array1[1], vt); + Asserts.assertEquals(array1[2], vt); + Asserts.assertEquals(result, vt); + result = test82(array2, vt, vt, 1); + Asserts.assertEquals(array2[0], vt); + Asserts.assertEquals(array2[1], vt); + Asserts.assertEquals(array2[2], vt); + Asserts.assertEquals(result, vt); + } + + // Verify that casting an array element to a non-inline type type marks the array as not-null-free and not-flat + @Test(valid = ValueTypeArrayFlattenOn, match = { ALLOC_G, LOAD_UNKNOWN_VALUE }, matchCount = { 1, 1 }, failOn = ALLOCA_G + STORE_UNKNOWN_VALUE + VALUE_ARRAY_NULL_GUARD) + @Test(valid = ValueTypeArrayFlattenOff, failOn = ALLOC_G + ALLOCA_G + LOAD_UNKNOWN_VALUE + STORE_UNKNOWN_VALUE + VALUE_ARRAY_NULL_GUARD) + public void test83(Object[] array, Object o) { + Integer i = (Integer)array[0]; + array[1] = o; + } + + @DontCompile + public void test83_verifier(boolean warmup) { + Integer i = new Integer(rI); + Integer[] array1 = new Integer[2]; + Object[] array2 = new Object[2]; + test83(array1, i); + Asserts.assertEquals(array1[1], i); + test83(array2, null); + Asserts.assertEquals(array2[1], null); + } + + // Verify that writing constant null into an array marks the array as not-null-free and not-flat + @Test(failOn = ALLOC_G + ALLOCA_G + LOAD_UNKNOWN_VALUE + STORE_UNKNOWN_VALUE, match = { VALUE_ARRAY_NULL_GUARD }, matchCount = { 1 }) + public Object test84(Object[] array, int i) { + array[0] = null; + array[1] = null; + return array[i]; + } + + @DontCompile + public void test84_verifier(boolean warmup) { + NotFlattenable?[] array1 = new NotFlattenable?[2]; + Object[] array2 = new Object[2]; + Object result = test84(array1, 0); + Asserts.assertEquals(array1[0], null); + Asserts.assertEquals(result, null); + result = test84(array2, 1); + Asserts.assertEquals(array2[0], null); + Asserts.assertEquals(result, null); + if (!warmup) { + NotFlattenable[] array3 = new NotFlattenable[2]; + try { + test84(array3, 1); + throw new RuntimeException("Should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + } + + // Same as test84 but with branches + @Test(failOn = ALLOC_G + ALLOCA_G + LOAD_UNKNOWN_VALUE + STORE_UNKNOWN_VALUE, match = { VALUE_ARRAY_NULL_GUARD }, matchCount = { 2 }) + public void test85(Object[] array, Object o, boolean b) { + if (b) { + array[0] = null; + } else { + array[1] = null; + } + array[1] = o; + } + + @DontCompile + public void test85_verifier(boolean warmup) { + Integer i = new Integer(rI); + Integer[] array1 = new Integer[2]; + Object[] array2 = new Object[2]; + test85(array1, i, true); + Asserts.assertEquals(array1[1], i); + test85(array1, null, false); + Asserts.assertEquals(array1[1], null); + test85(array2, i, true); + Asserts.assertEquals(array2[1], i); + test85(array2, null, false); + Asserts.assertEquals(array2[1], null); + if (!warmup) { + NotFlattenable[] array3 = new NotFlattenable[2]; + try { + test85(array3, null, true); + throw new RuntimeException("Should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + } + + // Same as test85 but with not-flattenable inline type array + @Test(failOn = ALLOC_G + ALLOCA_G + LOAD_UNKNOWN_VALUE + STORE_UNKNOWN_VALUE, match = { VALUE_ARRAY_NULL_GUARD }, matchCount = { 2 }) + public void test86(NotFlattenable?[] array, NotFlattenable? o, boolean b) { + if (b) { + array[0] = null; + } else { + array[1] = null; + } + array[1] = o; + } + + @DontCompile + public void test86_verifier(boolean warmup) { + NotFlattenable vt = new NotFlattenable(); + NotFlattenable?[] array1 = new NotFlattenable?[2]; + test86(array1, vt, true); + Asserts.assertEquals(array1[1], vt); + test86(array1, null, false); + Asserts.assertEquals(array1[1], null); + if (!warmup) { + NotFlattenable[] array2 = new NotFlattenable[2]; + try { + test86(array2, null, true); + throw new RuntimeException("Should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + } + + // Same as test85 but with inline type array + @Test(failOn = ALLOC_G + ALLOCA_G + LOAD_UNKNOWN_VALUE + STORE_UNKNOWN_VALUE, match = { VALUE_ARRAY_NULL_GUARD }, matchCount = { 2 }) + public void test87(MyValue1?[] array, MyValue1? o, boolean b) { + if (b) { + array[0] = null; + } else { + array[1] = null; + } + array[1] = o; + } + + @DontCompile + public void test87_verifier(boolean warmup) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + MyValue1?[] array1 = new MyValue1?[2]; + test87(array1, vt, true); + Asserts.assertEquals(array1[1], vt); + test87(array1, null, false); + Asserts.assertEquals(array1[1], null); + if (!warmup) { + MyValue1[] array2 = new MyValue1[2]; + try { + test87(array2, null, true); + throw new RuntimeException("Should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + } + + // Additional correcntess tests to make sure we have the required null checks + @Test() + public void test88(Object[] array, Integer v) { + array[0] = v; + } + + @DontCompile + public void test88_verifier(boolean warmup) { + Integer[] array1 = new Integer[1]; + Object[] array2 = new Object[1]; + test88(array1, null); + Asserts.assertEquals(array1[0], null); + test88(array2, null); + Asserts.assertEquals(array2[0], null); + if (!warmup) { + MyValue1[] array3 = new MyValue1[1]; + try { + test88(array3, null); + throw new RuntimeException("Should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + } + + @Test() + public void test89(MyValue1?[] array, Integer v) { + Object o = v; + array[0] = (MyValue1?)o; + } + + @DontCompile + public void test89_verifier(boolean warmup) { + MyValue1?[] array1 = new MyValue1?[1]; + test89(array1, null); + Asserts.assertEquals(array1[0], null); + if (!warmup) { + MyValue1[] array2 = new MyValue1[1]; + try { + test89(array2, null); + throw new RuntimeException("Should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + } } --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java 2019-07-17 14:45:57.574327555 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java 2019-07-17 14:45:57.174293656 +0200 @@ -42,6 +42,7 @@ @Override public String[] getExtraVMParameters(int scenario) { switch (scenario) { + case 2: return new String[] {"-DVerifyIR=false"}; case 3: return new String[] {"-XX:ValueArrayElemMaxFlatSize=0"}; } return null; --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java 2019-07-17 14:45:58.134375013 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java 2019-07-17 14:45:57.838349927 +0200 @@ -49,7 +49,7 @@ public String[] getExtraVMParameters(int scenario) { switch (scenario) { case 1: return new String[] {"-XX:-UseOptoBiasInlining"}; - case 2: return new String[] {"-XX:-UseBiasedLocking"}; + case 2: return new String[] {"-DVerifyIR=false", "-XX:-UseBiasedLocking"}; case 3: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:-UseBiasedLocking", "-XX:ValueArrayElemMaxFlatSize=-1"}; case 4: return new String[] {"-XX:-MonomorphicArrayCheck"}; } --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMethodHandles.java 2019-07-17 14:45:58.742426539 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMethodHandles.java 2019-07-17 14:45:58.402397726 +0200 @@ -46,8 +46,7 @@ public String[] getExtraVMParameters(int scenario) { switch (scenario) { // Prevent inlining through MethodHandle linkTo adapters to stress the calling convention - case 2: return new String[] {"-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName"}; - case 3: return new String[] {"-XX:ValueArrayElemMaxFlatSize=0"}; + case 2: return new String[] {"-DVerifyIR=false", "-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName"}; case 4: return new String[] {"-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName"}; } return null; --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableArrays.java 2019-07-17 14:45:59.310474675 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableArrays.java 2019-07-17 14:45:59.022450268 +0200 @@ -85,8 +85,8 @@ private static final MyValue1 testValue1 = MyValue1.createWithFieldsInline(rI, rL); // Test nullable value type array creation and initialization - @Test(valid = ValueTypeArrayFlattenOff, failOn = LOAD) @Test(valid = ValueTypeArrayFlattenOn) + @Test(valid = ValueTypeArrayFlattenOff, failOn = LOAD) public MyValue1?[] test1(int len) { MyValue1?[] va = new MyValue1?[len]; if (len > 0) { @@ -111,9 +111,9 @@ } // Test creation of a value type array and element access - @Test() -// TODO fix -// @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) + @Test + // TODO 8227588 + // @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) public long test2() { MyValue1?[] va = new MyValue1?[1]; va[0] = MyValue1.createWithFieldsInline(rI, rL); @@ -222,7 +222,7 @@ } // Test creation of value type array with single element - @Test(failOn = ALLOCA + LOOP + LOAD + TRAP) + @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) public MyValue1? test6() { MyValue1?[] va = new MyValue1?[1]; return va[0]; @@ -864,8 +864,7 @@ } // non escaping allocations -// TODO fix -// @Test(failOn = ALLOCA + LOOP + LOAD + TRAP) + @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) public MyValue2? test28() { MyValue2?[] src = new MyValue2?[10]; src[0] = null; @@ -881,6 +880,7 @@ } // non escaping allocations + // TODO 8227588: shouldn't this have the same IR matching rules as test6? @Test(failOn = ALLOCA + LOOP + TRAP) public MyValue2? test29(MyValue2?[] src) { MyValue2?[] dst = new MyValue2?[10]; @@ -921,8 +921,8 @@ // non escaping allocation with memory phi @Test() -// TODO fix -// @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + TRAP) + // TODO 8227588 + // @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) public long test31(boolean b, boolean deopt) { MyValue2?[] src = new MyValue2?[1]; if (b) { @@ -2112,7 +2112,7 @@ } // Test widening conversions from [Q to [L - @Test(failOn = ALLOC + ALLOCA + STORE) + @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) public static MyValue1?[] test79(MyValue1[] va) { return va; } @@ -2134,7 +2134,7 @@ } // Same as test79 but with explicit cast and Object return - @Test(failOn = ALLOC + ALLOCA + STORE) + @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) public static Object[] test80(MyValue1[] va) { return (MyValue1?[])va; } @@ -2248,7 +2248,8 @@ Asserts.assertEquals(res, testValue1.hash()); } - @Test(failOn = ALLOC + ALLOCA + STORE) + @Test(valid = ValueTypeArrayFlattenOn, failOn = ALLOC + ALLOCA + LOOP + STORE + TRAP) + @Test(valid = ValueTypeArrayFlattenOff) public static MyValue1?[] test84(MyValue1 vt1, MyValue1? vt2) { MyValue1?[] result = new MyValue1[2]; result[0] = vt1; @@ -2334,7 +2335,7 @@ } // Test narrowing conversion from [L to [Q - @Test(failOn = ALLOC + ALLOCA + STORE) + @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) public static MyValue1[] test88(MyValue1?[] va) { return (MyValue1[])va; } @@ -2356,7 +2357,7 @@ } // Same as test88 but with explicit cast and Object argument - @Test(failOn = ALLOC + ALLOCA + STORE) + @Test(failOn = ALLOC + ALLOCA + LOOP + LOAD + STORE + TRAP) public static MyValue1[] test89(Object[] va) { return (MyValue1[])va; } --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/ValueTypeTest.java 2019-07-17 14:45:59.878522812 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/ValueTypeTest.java 2019-07-17 14:45:59.574497050 +0200 @@ -183,6 +183,10 @@ protected static final String START = "(\\d+\\t(.*"; protected static final String MID = ".*)+\\t===.*"; protected static final String END = ")|"; + // Generic allocation + protected static final String ALLOC_G = "(.*call,static wrapper for: _new_instance_Java" + END; + protected static final String ALLOCA_G = "(.*call,static wrapper for: _new_array_Java" + END; + // Value type allocation protected static final String ALLOC = "(.*precise klass compiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_instance_Java" + END; protected static final String ALLOCA = "(.*precise klass \\[Lcompiler/valhalla/valuetypes/MyValue.*\\R(.*(nop|spill).*\\R)*.*_new_array_Java" + END; protected static final String LOAD = START + "Load(B|S|I|L|F|D|P|N)" + MID + "@compiler/valhalla/valuetypes/MyValue.*" + END; @@ -199,6 +203,7 @@ 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; + protected static final String VALUE_ARRAY_NULL_GUARD = "(.*call,static wrapper for: uncommon_trap.*reason='null_check' action='none'.*" + END; public static String[] concat(String prefix[], String... extra) { ArrayList list = new ArrayList(); @@ -232,24 +237,23 @@ switch (scenario) { case 0: return new String[] { "-XX:+AlwaysIncrementalInline", - "-XX:ValueArrayElemMaxFlatOops=-1", + "-XX:ValueArrayElemMaxFlatOops=5", "-XX:ValueArrayElemMaxFlatSize=-1", "-XX:ValueFieldMaxFlatSize=-1", "-XX:+ValueTypePassFieldsAsArgs", "-XX:+ValueTypeReturnedAsFields"}; case 1: return new String[] { "-XX:-UseCompressedOops", - "-XX:ValueArrayElemMaxFlatOops=-1", + "-XX:ValueArrayElemMaxFlatOops=5", "-XX:ValueArrayElemMaxFlatSize=-1", "-XX:ValueFieldMaxFlatSize=-1", "-XX:-ValueTypePassFieldsAsArgs", "-XX:-ValueTypeReturnedAsFields"}; case 2: return new String[] { - "-DVerifyIR=false", "-XX:-UseCompressedOops", "-XX:ValueArrayElemMaxFlatOops=0", "-XX:ValueArrayElemMaxFlatSize=0", - "-XX:ValueFieldMaxFlatSize=0", + "-XX:ValueFieldMaxFlatSize=-1", "-XX:+ValueTypePassFieldsAsArgs", "-XX:+ValueTypeReturnedAsFields", "-XX:+StressValueTypeReturnedAsFields"}; @@ -271,7 +275,7 @@ "-XX:-ReduceInitialCardMarks"}; case 5: return new String[] { "-XX:+AlwaysIncrementalInline", - "-XX:ValueArrayElemMaxFlatOops=-1", + "-XX:ValueArrayElemMaxFlatOops=5", "-XX:ValueArrayElemMaxFlatSize=-1", "-XX:ValueFieldMaxFlatSize=-1", "-XX:-ValueTypePassFieldsAsArgs",