--- old/src/share/vm/memory/oopFactory.cpp 2017-02-13 17:38:59.975392639 +0100 +++ new/src/share/vm/memory/oopFactory.cpp 2017-02-13 17:38:59.903392642 +0100 @@ -99,7 +99,7 @@ "Expect an array class here"); if (array_klass->is_valueArray_klass()) { - return (arrayOop) ValueArrayKlass::cast(array_klass)->allocate_array(length, true, THREAD); + return (arrayOop) ValueArrayKlass::cast(array_klass)->allocate(length, true, THREAD); } valueKlassHandle vklass_h(ValueKlass::cast(klass)); --- old/src/share/vm/oops/instanceKlass.cpp 2017-02-13 17:39:00.235392627 +0100 +++ new/src/share/vm/oops/instanceKlass.cpp 2017-02-13 17:39:00.163392630 +0100 @@ -2954,7 +2954,6 @@ _st->cr(); } else { fd->print_on_for(_st, _obj); - _st->cr(); } } --- old/src/share/vm/oops/valueArrayKlass.cpp 2017-02-13 17:39:00.527392613 +0100 +++ new/src/share/vm/oops/valueArrayKlass.cpp 2017-02-13 17:39:00.463392616 +0100 @@ -89,9 +89,7 @@ } // Oops allocation... -valueArrayOop ValueArrayKlass::allocate_array(int length, - bool do_zero, - TRAPS) { +oop ValueArrayKlass::allocate(int length, bool do_zero, TRAPS) { CMH("this is virtually identical in all arrayKlasses, refactor") if (length < 0) { THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); @@ -104,14 +102,11 @@ size_t size = valueArrayOopDesc::object_size(layout_helper(), length); KlassHandle h_k(THREAD, this); - valueArrayOop array; - CollectedHeap* ch = Universe::heap(); if (do_zero) { - array = (valueArrayOop)CollectedHeap::array_allocate(h_k, (int)size, length, CHECK_NULL); + return CollectedHeap::array_allocate(h_k, (int)size, length, CHECK_NULL); } else { - array = (valueArrayOop)CollectedHeap::array_allocate_nozero(h_k, (int)size, length, CHECK_NULL); + return CollectedHeap::array_allocate_nozero(h_k, (int)size, length, CHECK_NULL); } - return array; } @@ -119,7 +114,7 @@ // For valueArrays this is only called for the last dimension assert(rank == 1, "just checking"); int length = *last_size; - return allocate_array(length, true, THREAD); + return allocate(length, true, THREAD); } jint ValueArrayKlass::array_layout_helper(ValueKlass* vk) { @@ -128,11 +123,11 @@ int esize = upper_log2(vk->raw_value_byte_size()); int hsize = arrayOopDesc::base_offset_in_bytes(etype); - int lh = (atag << _lh_array_tag_shift) + int lh = (atag << _lh_array_tag_shift) | (1 << _lh_valuetype_bit) | (hsize << _lh_header_size_shift) | ((int)etype << _lh_element_type_shift) - | (esize); + | ((esize) << _lh_log2_element_size_shift); #ifdef DEBUG assert(lh < (int)_lh_neutral_value, "must look like an array layout"); @@ -260,11 +255,13 @@ void ValueArrayKlass::oop_print_on(oop obj, outputStream* st) { ArrayKlass::oop_print_on(obj, st); valueArrayOop va = valueArrayOop(obj); + ValueKlass* vk = element_klass(); int print_len = MIN2((intx) va->length(), MaxElementPrintSize); for(int index = 0; index < print_len; index++) { - st->print(" - %3d : ", index); - CMH("CMH: helper method for valueOop/valorind printing") - st->print(" CMH: helper method for valueOop/valorind printing"); + st->print_cr(" - %d : ", index); + oop obj = (oop) ((uintptr_t)va->value_at_addr(index, layout_helper()) - vk->first_field_offset()); + FieldPrinter print_field(st, obj); + vk->do_nonstatic_fields(&print_field); st->cr(); } int remaining = va->length() - print_len; --- old/src/share/vm/oops/valueArrayKlass.hpp 2017-02-13 17:39:00.819392599 +0100 +++ new/src/share/vm/oops/valueArrayKlass.hpp 2017-02-13 17:39:00.747392603 +0100 @@ -88,9 +88,7 @@ int oop_size(oop obj) const; // Oop Allocation - valueArrayOop allocate_array(int length, - bool do_zero, - TRAPS); + oop allocate(int length, bool do_zero, TRAPS); oop multi_allocate(int rank, jint* sizes, TRAPS); // Naming --- old/src/share/vm/opto/addnode.cpp 2017-02-13 17:39:01.135392585 +0100 +++ new/src/share/vm/opto/addnode.cpp 2017-02-13 17:39:01.051392589 +0100 @@ -681,11 +681,11 @@ if (p2->is_con()) { // Left input is an add of a constant? p2offset = p2->get_con(); } - if (t1->isa_aryptr()) { + if (p1->isa_aryptr()) { // In the case of a flattened value type array, each field has its // own slice so we need to extract the field being accessed from // the address computation - return t1->is_aryptr()->with_field_offset_and_offset(p2offset); + return p1->is_aryptr()->with_field_offset_and_offset(p2offset); } return p1->add_offset(p2offset); } --- old/src/share/vm/opto/escape.cpp 2017-02-13 17:39:01.427392571 +0100 +++ new/src/share/vm/opto/escape.cpp 2017-02-13 17:39:01.351392575 +0100 @@ -2377,7 +2377,7 @@ assert(offs != Type::OffsetBot, "offset must be a constant"); t = base_t->add_offset(offs)->is_oopptr(); } - int inst_id = base_t->instance_id(); + int inst_id = base_t->instance_id(); assert(!t->is_known_instance() || t->instance_id() == inst_id, "old type must be non-instance or match new type"); @@ -2401,7 +2401,13 @@ !base_t->klass()->is_subtype_of(t->klass())) { return false; // bail out } - const TypeOopPtr *tinst = base_t->add_offset(t->offset())->is_oopptr(); + const TypePtr* tinst = base_t->add_offset(t->offset()); + if (tinst->isa_aryptr()) { + // In the case of a flattened value type array, each field has its + // own slice so we need to keep track of the field being accessed. + tinst = tinst->is_aryptr()->with_field_offset(t->is_aryptr()->field_offset()); + } + // Do NOT remove the next line: ensure a new alias index is allocated // for the instance type. Note: C++ will not remove it since the call // has side effect. @@ -3285,7 +3291,7 @@ // Phase 3: Process MergeMem nodes from mergemem_worklist. // Walk each memory slice moving the first node encountered of each - // instance type to the the input corresponding to its alias index. + // instance type to the input corresponding to its alias index. uint length = _mergemem_worklist.length(); for( uint next = 0; next < length; ++next ) { MergeMemNode* nmm = _mergemem_worklist.at(next); --- old/src/share/vm/opto/graphKit.cpp 2017-02-13 17:39:01.703392558 +0100 +++ new/src/share/vm/opto/graphKit.cpp 2017-02-13 17:39:01.635392561 +0100 @@ -3590,10 +3590,10 @@ int hsize = Klass::layout_helper_header_size(layout_con); int eshift = Klass::layout_helper_log2_element_size(layout_con); BasicType etype = Klass::layout_helper_element_type(layout_con); + bool is_value_array = Klass::layout_helper_is_valueArray(layout_con); if ((round_mask & ~right_n_bits(eshift)) == 0) round_mask = 0; // strength-reduce it if it goes away completely - // TODO re-enabled assert - // assert((hsize & right_n_bits(eshift)) == 0, "hsize is pre-rounded"); + assert(is_value_array || (hsize & right_n_bits(eshift)) == 0, "hsize is pre-rounded"); assert(header_size_min <= hsize, "generic minimum is smallest"); header_size_min = hsize; header_size = intcon(hsize + round_mask); @@ -3719,13 +3719,10 @@ const TypeAryPtr* ary_ptr = ary_type->isa_aryptr(); ciKlass* elem_klass = ary_ptr != NULL ? ary_ptr->klass()->as_array_klass()->element_klass() : NULL; - //if (layout_is_con && Klass::layout_helper_element_type(layout_con) == T_VALUETYPE) { if (elem_klass != NULL && elem_klass->is_valuetype()) { ciValueKlass* vk = elem_klass->as_value_klass(); - if (vk->flatten_array()) { - // TODO - } else { - // TODO explain this and add asserts + if (!vk->flatten_array()) { + // Non-flattened value type arrays need to be initialized with default value type oops initialize_value_type_array(javaoop, length, elem_klass->as_value_klass(), nargs); InitializeNode* init = alloc->initialization(); init->set_complete_with_arraycopy(); @@ -3782,7 +3779,7 @@ set_all_memory(mem); // Initialize array element Node* adr = array_element_address(array, index, T_OBJECT); - const TypeOopPtr* elemtype = TypeValueTypePtr::make(TypePtr::NotNull, vk); // ary_type->is_aryptr()->elem()->make_oopptr(); + const TypeOopPtr* elemtype = TypeValueTypePtr::make(TypePtr::NotNull, vk); Node* store = store_oop_to_array(control(), array, adr, TypeAryPtr::OOPS, oop, elemtype, T_OBJECT, MemNode::release); IfNode* iff = create_and_map_if(control(), Bool(CmpI(index, length), BoolTest::lt), PROB_FAIR, COUNT_UNKNOWN); @@ -3798,6 +3795,14 @@ set_control(res_ctl); set_i_o(res_io); merge_memory(loop_map->merged_memory(), res_ctl, 2); + + // Transform new memory Phis. + for (MergeMemStream mms(merged_memory()); mms.next_non_empty();) { + Node* phi = mms.memory(); + if (phi->is_Phi() && phi->in(0) == res_ctl) { + mms.set_memory(gvn().transform(phi)); + } + } } // The following "Ideal_foo" functions are placed here because they recognize --- old/src/share/vm/opto/macro.cpp 2017-02-13 17:39:02.023392543 +0100 +++ new/src/share/vm/opto/macro.cpp 2017-02-13 17:39:01.951392547 +0100 @@ -44,6 +44,7 @@ #include "opto/runtime.hpp" #include "opto/subnode.hpp" #include "opto/type.hpp" +#include "opto/valuetypenode.hpp" #include "runtime/sharedRuntime.hpp" @@ -391,7 +392,7 @@ return NULL; } mem = mem->in(MemNode::Memory); - } else if (mem->Opcode() == Op_StrInflatedCopy) { + } else if (mem->Opcode() == Op_StrInflatedCopy) { Node* adr = mem->in(3); // Destination array const TypePtr* atype = adr->bottom_type()->is_ptr(); int adr_idx = phase->C->get_alias_index(atype); @@ -562,6 +563,7 @@ // Search the last value stored into the object's field. Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, Node *sfpt_ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc) { assert(adr_t->is_known_instance_field(), "instance required"); + assert(ft != T_VALUETYPE, "should not be used for value type fields"); int instance_id = adr_t->instance_id(); assert((uint)instance_id == alloc->_idx, "wrong allocation"); @@ -573,7 +575,6 @@ Arena *a = Thread::current()->resource_area(); VectorSet visited(a); - bool done = sfpt_mem == alloc_mem; Node *mem = sfpt_mem; while (!done) { @@ -586,7 +587,7 @@ } else if (mem->is_Initialize()) { mem = mem->as_Initialize()->find_captured_store(offset, type2aelembytes(ft), &_igvn); if (mem == NULL) { - done = true; // Something go wrong. + done = true; // Something went wrong. } else if (mem->is_Store()) { const TypePtr* atype = mem->as_Store()->adr_type(); assert(C->get_alias_index(atype) == Compile::AliasIdxRaw, "store is correct memory slice"); @@ -654,10 +655,32 @@ return make_arraycopy_load(mem->as_ArrayCopy(), offset, ctl, ft, ftype, alloc); } } - // Something go wrong. + // Something went wrong. return NULL; } +// Search the last value stored into the value type's fields. +Node* PhaseMacroExpand::value_type_from_mem(Node* mem, Node* ctl, ciValueKlass* vk, const TypeAryPtr* adr_type, int offset, AllocateNode* alloc) { + // Subtract the offset of the first field to account for the missing oop header + offset -= vk->first_field_offset(); + // Create a new ValueTypeNode and retrieve the field values from memory + ValueTypeNode* vt = ValueTypeNode::make(_igvn, vk)->as_ValueType(); + for (int i = 0; i < vk->field_count(); ++i) { + ciType* field_type = vt->field_type(i); + int field_offset = offset + vt->field_offset(i); + // Each value type field has its own memory slice + adr_type = adr_type->with_field_offset(field_offset); + Node* value = NULL; + if (field_type->basic_type() == T_VALUETYPE) { + value = value_type_from_mem(mem, ctl, field_type->as_value_klass(), adr_type, field_offset, alloc); + } else { + value = value_from_mem(mem, ctl, field_type->basic_type(), Type::get_const_type(field_type), adr_type, alloc); + } + vt->set_field_value(i, value); + } + return vt; +} + // Check the possibility of scalar replacement. bool PhaseMacroExpand::can_eliminate_allocation(AllocateNode *alloc, GrowableArray & safepoints) { // Scan the uses of the allocation to check for anything that would @@ -712,7 +735,7 @@ if (n->is_Load() || n->is_LoadStore()) { NOT_PRODUCT(fail_eliminate = "Field load";) } else { - NOT_PRODUCT(fail_eliminate = "Not store field referrence";) + NOT_PRODUCT(fail_eliminate = "Not store field reference";) } can_eliminate = false; } @@ -750,7 +773,7 @@ } else { if (use->Opcode() == Op_Return) { NOT_PRODUCT(fail_eliminate = "Object is return value";) - }else { + } else { NOT_PRODUCT(fail_eliminate = "Object is referenced by node";) } DEBUG_ONLY(disq_node = use;) @@ -807,7 +830,9 @@ if (res != NULL) { klass = res_type->klass(); - if (res_type->isa_instptr() || res_type->isa_valuetypeptr()) { + // Value types are only allocated on demand + assert(!klass->is_valuetype(), "value type allocations should not be scalar replaceable"); + if (res_type->isa_instptr()) { // find the fields of the class which will be needed for safepoint debug information assert(klass->is_instance_klass(), "must be an instance klass."); iklass = klass->as_instance_klass(); @@ -851,6 +876,7 @@ offset = field->offset(); elem_type = field->type(); basic_elem_type = field->layout_type(); + // Value type fields should not have safepoint uses assert(basic_elem_type != T_VALUETYPE, "value type fields are flattened"); } else { offset = array_base + j * (intptr_t)element_size; @@ -879,9 +905,15 @@ field_type = Type::get_const_basic_type(basic_elem_type); } - const TypeOopPtr *field_addr_type = res_type->add_offset(offset)->isa_oopptr(); - - Node *field_val = value_from_mem(mem, ctl, basic_elem_type, field_type, field_addr_type, alloc); + Node* field_val = NULL; + const TypeOopPtr* field_addr_type = res_type->add_offset(offset)->isa_oopptr(); + if (klass->is_value_array_klass()) { + ciValueKlass* vk = elem_type->as_value_klass(); + assert(vk->flatten_array(), "must be flattened"); + field_val = value_type_from_mem(mem, ctl, vk, field_addr_type->isa_aryptr(), 0, alloc); + } else { + field_val = value_from_mem(mem, ctl, basic_elem_type, field_type, field_addr_type, alloc); + } if (field_val == NULL) { // We weren't able to find a value for this field, // give up on eliminating this allocation. --- old/src/share/vm/opto/macro.hpp 2017-02-13 17:39:02.335392529 +0100 +++ new/src/share/vm/opto/macro.hpp 2017-02-13 17:39:02.271392532 +0100 @@ -87,6 +87,7 @@ address slow_call_address); Node *value_from_mem(Node *mem, Node *ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc); Node *value_from_mem_phi(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level); + Node* value_type_from_mem(Node* mem, Node* ctl, ciValueKlass* vk, const TypeAryPtr* adr_type, int offset, AllocateNode* alloc); bool eliminate_boxing_node(CallStaticJavaNode *boxing); bool eliminate_allocate_node(AllocateNode *alloc); --- old/src/share/vm/opto/memnode.cpp 2017-02-13 17:39:02.611392516 +0100 +++ new/src/share/vm/opto/memnode.cpp 2017-02-13 17:39:02.531392520 +0100 @@ -1016,6 +1016,7 @@ // (This is one of the few places where a generic PhaseTransform // can create new nodes. Think of it as lazily manifesting // virtually pre-existing constants.) + assert(memory_type() != T_VALUETYPE, "should not be used for value types"); return phase->zerocon(memory_type()); } @@ -2417,8 +2418,6 @@ while (st->is_Store() && st->outcnt() == 1 && st->Opcode() != Op_StoreCM) { // Looking at a dead closed cycle of memory? assert(st != st->in(MemNode::Memory), "dead loop in StoreNode::Ideal"); - // TODO re-enable assert - /* assert(Opcode() == st->Opcode() || st->Opcode() == Op_StoreVector || Opcode() == Op_StoreVector || @@ -2426,7 +2425,6 @@ (Opcode() == Op_StoreL && st->Opcode() == Op_StoreI) || // expanded ClearArrayNode (is_mismatched_access() || st->as_Store()->is_mismatched_access()), "no mismatched stores, except on raw memory: %s %s", NodeClassNames[Opcode()], NodeClassNames[st->Opcode()]); -*/ if (st->in(MemNode::Address)->eqv_uncast(address) && st->as_Store()->memory_size() <= this->memory_size()) { Node* use = st->raw_out(0); --- old/src/share/vm/opto/parse2.cpp 2017-02-13 17:39:02.907392502 +0100 +++ new/src/share/vm/opto/parse2.cpp 2017-02-13 17:39:02.827392506 +0100 @@ -61,7 +61,7 @@ const TypeAryPtr* arytype = _gvn.type(ary)->is_aryptr(); if (arytype->klass()->is_value_array_klass()) { ciValueArrayKlass* vak = arytype->klass()->as_value_array_klass(); - Node* vt = ValueTypeNode::make(gvn(), vak->element_klass()->as_value_klass(), map()->memory(), ary, adr, vak); + Node* vt = ValueTypeNode::make(gvn(), vak->element_klass()->as_value_klass(), map()->memory(), ary, adr); push(vt); return; } @@ -1749,7 +1749,7 @@ const Type* elemtype = arytype->elem(); if (elemtype->isa_valuetype()) { - c->as_ValueType()->store_values(this, a, d, arytype->klass()->as_value_array_klass()); + c->as_ValueType()->store(this, a, d); break; } --- old/src/share/vm/opto/parse3.cpp 2017-02-13 17:39:03.203392488 +0100 +++ new/src/share/vm/opto/parse3.cpp 2017-02-13 17:39:03.131392492 +0100 @@ -295,7 +295,7 @@ } if (bt == T_VALUETYPE && !field->is_static()) { // Store flattened value type to non-static field - val->as_ValueType()->store_to_field(this, obj, obj, field->holder(), offset); + val->as_ValueType()->store(this, obj, obj, field->holder(), offset); } else { store_oop_to_object(control(), obj, adr, adr_type, val, field_type, bt, mo); } --- old/src/share/vm/opto/type.cpp 2017-02-13 17:39:03.467392476 +0100 +++ new/src/share/vm/opto/type.cpp 2017-02-13 17:39:03.403392479 +0100 @@ -4587,7 +4587,7 @@ } } } - return add_offset(offset);; + return add_offset(offset); } //============================================================================= --- old/src/share/vm/opto/valuetypenode.cpp 2017-02-13 17:39:03.763392462 +0100 +++ new/src/share/vm/opto/valuetypenode.cpp 2017-02-13 17:39:03.691392466 +0100 @@ -55,55 +55,33 @@ Node* ValueTypeNode::make(PhaseGVN& gvn, Node* mem, Node* oop) { // Create and initialize a ValueTypeNode by loading all field // values from a heap-allocated version and also save the oop. - const TypeValueTypePtr* vtptr = gvn.type(oop)->is_valuetypeptr(); - ValueTypeNode* vt = new ValueTypeNode(vtptr->value_type(), oop); - vt->load_values(gvn, mem, oop, oop); + const TypeValueType* type = gvn.type(oop)->is_valuetypeptr()->value_type(); + ValueTypeNode* vt = new ValueTypeNode(type, oop); + vt->load_values(gvn, mem, oop, oop, type->value_klass()); return gvn.transform(vt); } -Node* ValueTypeNode::make(PhaseGVN& gvn, ciValueKlass* vk, Node* mem, Node* obj, Node* ptr, ciKlass* holder, int field_offset) { +Node* ValueTypeNode::make(PhaseGVN& gvn, ciValueKlass* vk, Node* mem, Node* obj, Node* ptr, ciInstanceKlass* holder, int holder_offset) { // Create and initialize a ValueTypeNode by loading all field values from - // a flattened value type field at 'field_offset' or from a value type array. + // a flattened value type field at 'holder_offset' or from a value type array. ValueTypeNode* vt = make(gvn, vk); - int base_offset = 0; - if (holder->is_value_array_klass()) { - assert(field_offset == 0, "field offset not supported for arrays"); - } else { - // The value type is flattened into the object without an oop header. Subtract the - // offset of the first field to account for the missing header when loading the values. - base_offset = field_offset - vk->first_field_offset(); - } - vt->load_values(gvn, mem, obj, ptr, holder, base_offset); + // The value type is flattened into the object without an oop header. Subtract the + // offset of the first field to account for the missing header when loading the values. + holder_offset -= vk->first_field_offset(); + vt->load_values(gvn, mem, obj, ptr, holder, holder_offset); return gvn.transform(vt); } -void ValueTypeNode::load_values(PhaseGVN& gvn, Node* mem, Node* base, Node* ptr, ciKlass* holder, int f_offset) { - ciInstanceKlass* lookup; - if (holder) { - // Flattened - if (holder->is_value_array_klass()) { - lookup = value_klass(); - } else { - lookup = holder->as_instance_klass(); - } - } else { - // Not flattened - assert(f_offset == 0, "must be"); - lookup = value_klass(); - } +void ValueTypeNode::load_values(PhaseGVN& gvn, Node* mem, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset) { // Initialize the value type by loading its field values from // memory and adding the values as input edges to the node. for (uint i = 0; i < field_count(); ++i) { - int offset = f_offset + field_offset(i); - ciField* field = lookup->get_field_by_offset(offset, false); - ciType* f_type = field_type(i); + int offset = holder_offset + field_offset(i); + ciType* ftype = field_type(i); Node* value = NULL; - if (f_type->is_valuetype()) { - if (holder && holder->is_value_array_klass()) { - offset -= value_klass()->first_field_offset(); - } + if (ftype->is_valuetype()) { // Recursively load the flattened value type field - value = ValueTypeNode::make(gvn, f_type->as_value_klass(), mem, base, ptr, lookup, offset); + value = ValueTypeNode::make(gvn, ftype->as_value_klass(), mem, base, ptr, holder, offset); } else { const Type* con_type = NULL; if (base->is_Con()) { @@ -111,6 +89,7 @@ // also treat the fields as constants because the value type is immutable. const TypeOopPtr* oop_ptr = base->bottom_type()->isa_oopptr(); ciObject* constant_oop = oop_ptr->const_oop(); + ciField* field = holder->get_field_by_offset(offset, false); ciConstant constant = constant_oop->as_instance()->field_value(field); con_type = Type::make_from_constant(constant, /*require_const=*/ true); } @@ -119,9 +98,6 @@ value = gvn.makecon(con_type); } else { // Load field value from memory - if (holder && holder->is_value_array_klass()) { - offset -= value_klass()->first_field_offset(); - } const Type* base_type = gvn.type(base); const TypePtr* adr_type = NULL; if (base_type->isa_aryptr()) { @@ -129,60 +105,40 @@ // has its own slice adr_type = base_type->is_aryptr()->with_field_offset(offset)->add_offset(Type::OffsetBot); } else { + ciField* field = holder->get_field_by_offset(offset, false); adr_type = gvn.C->alias_type(field)->adr_type(); } Node* adr = gvn.transform(new AddPNode(base, ptr, gvn.MakeConX(offset))); - value = LoadNode::make(gvn, NULL, mem, adr, adr_type, Type::get_const_type(f_type), f_type->basic_type(), MemNode::unordered); + value = LoadNode::make(gvn, NULL, mem, adr, adr_type, Type::get_const_type(ftype), ftype->basic_type(), MemNode::unordered); } } set_field_value(i, gvn.transform(value)); } } -void ValueTypeNode::store_to_field(GraphKit* kit, Node* obj, Node* ptr, ciInstanceKlass* instance_type, int field_offset) const { +void ValueTypeNode::store(GraphKit* kit, Node* obj, Node* ptr, ciInstanceKlass* holder, int holder_offset) const { // The value type is embedded into the object without an oop header. Subtract the // offset of the first field to account for the missing header when storing the values. - int base_offset = field_offset - value_klass()->first_field_offset(); - store_values(kit, obj, ptr, instance_type, base_offset); + holder_offset -= value_klass()->first_field_offset(); + store_values(kit, obj, ptr, holder, holder_offset); } -void ValueTypeNode::store_values(GraphKit* kit, Node* base, Node* ptr, ciKlass* holder, int holder_offset) const { - ciInstanceKlass* lookup; - if (holder) { - // flattened - if (holder->is_value_array_klass()) { - assert(holder_offset == 0, "must be"); - lookup = value_klass(); - } else { - lookup = holder->as_instance_klass(); - } - } else { - // not flattened - assert(holder_offset == 0, "must be"); - lookup = value_klass(); - } +void ValueTypeNode::store_values(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset) const { // Write field values to memory for (uint i = 0; i < field_count(); ++i) { int offset = holder_offset + field_offset(i); Node* value = field_value(i); if (value->is_ValueType()) { // Recursively store the flattened value type field - if (holder && holder->is_value_array_klass()) { - offset -= value_klass()->first_field_offset(); - } - value->isa_ValueType()->store_to_field(kit, base, ptr, lookup, offset); + value->isa_ValueType()->store(kit, base, ptr, value_klass(), offset); } else { - if (holder && holder->is_value_array_klass()) { - offset -= value_klass()->first_field_offset(); - } const Type* base_type = kit->gvn().type(base); const TypePtr* adr_type = NULL; if (base_type->isa_aryptr()) { - // In the case of a flattened value type array, each field has - // its own slice + // In the case of a flattened value type array, each field has its own slice adr_type = base_type->is_aryptr()->with_field_offset(offset)->add_offset(Type::OffsetBot); } else { - ciField* field = lookup->get_field_by_offset(offset, false); + ciField* field = holder->get_field_by_offset(offset, false); adr_type = kit->C->alias_type(field)->adr_type(); } Node* adr = kit->basic_plus_adr(base, ptr, offset); @@ -223,13 +179,14 @@ // Oop is NULL, allocate value type kit->set_control(null_ctl); kit->kill_dead_locals(); - Node* klass_node = kit->makecon(TypeKlassPtr::make(value_klass())); + ciValueKlass* vk = value_klass(); + Node* klass_node = kit->makecon(TypeKlassPtr::make(vk)); Node* alloc_oop = kit->new_instance(klass_node); AllocateNode* alloc = AllocateNode::Ideal_allocation(alloc_oop, &kit->gvn()); // TODO enable/fix this // alloc->initialization()->set_complete_with_arraycopy(); // Write field values to memory - store_values(kit, alloc_oop, alloc_oop); + store_values(kit, alloc_oop, alloc_oop, vk); region->init_req(2, kit->control()); oop ->init_req(2, alloc_oop); io ->init_req(2, kit->i_o()); --- old/src/share/vm/opto/valuetypenode.hpp 2017-02-13 17:39:04.047392449 +0100 +++ new/src/share/vm/opto/valuetypenode.hpp 2017-02-13 17:39:03.975392452 +0100 @@ -49,7 +49,7 @@ // Get the klass defining the field layout of the value type ciValueKlass* value_klass() const { return type()->is_valuetype()->value_klass(); } // Initialize the value type by loading its field values from memory - void load_values(PhaseGVN& gvn, Node* mem, Node* base, Node* ptr, ciKlass* holder = NULL, int f_offset = 0); + void load_values(PhaseGVN& gvn, Node* mem, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset = 0); enum { Control, // Control input Oop, // Oop of TypeValueTypePtr @@ -66,7 +66,7 @@ // Create a new ValueTypeNode and load its values from an oop static Node* make(PhaseGVN& gvn, Node* mem, Node* oop); // Create a new ValueTypeNode and load its values from a flattened value type field or array - static Node* make(PhaseGVN& gvn, ciValueKlass* vk, Node* mem, Node* obj, Node* ptr, ciKlass* holder, int field_offset = 0); + static Node* make(PhaseGVN& gvn, ciValueKlass* vk, Node* mem, Node* obj, Node* ptr, ciInstanceKlass* holder = NULL, int holder_offset = 0); // Support for control flow merges ValueTypeNode* clone_with_phis(PhaseGVN& gvn, Node* region); @@ -75,10 +75,10 @@ // Store the value type to memory if not yet allocated and returns the oop Node* store_to_memory(GraphKit* kit); - // Store the value type in a field of an object - void store_to_field(GraphKit* kit, Node* obj, Node* ptr, ciInstanceKlass* instance_type, int field_offset) const; + // Store the value type to a flattened value type field or array + void store(GraphKit* kit, Node* obj, Node* ptr, ciInstanceKlass* holder = NULL, int holder_offset = 0) const; // Store the field values to memory - void store_values(GraphKit* kit, Node* base, Node* ptr, ciKlass* holder = NULL, int holder_offset = 0) const; + void store_values(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset = 0) const; // Get oop for heap allocated value type (may be TypePtr::NULL_PTR) Node* get_oop() const { return in(Oop); } --- old/src/share/vm/runtime/deoptimization.cpp 2017-02-13 17:39:04.295392437 +0100 +++ new/src/share/vm/runtime/deoptimization.cpp 2017-02-13 17:39:04.231392440 +0100 @@ -39,6 +39,8 @@ #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/fieldStreams.hpp" +#include "oops/valueArrayKlass.hpp" +#include "oops/valueArrayOop.hpp" #include "oops/valueKlass.hpp" #include "oops/verifyOopClosure.hpp" #include "prims/jvmtiThreadState.hpp" @@ -805,6 +807,10 @@ if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k()); obj = ik->allocate_instance(THREAD); + } else if (k->is_valueArray_klass()) { + ValueArrayKlass* ak = ValueArrayKlass::cast(k()); + // Value type array must be zeroed because not all memory is reassigned + obj = ak->allocate(sv->field_size(), true, THREAD); } else if (k->is_typeArray_klass()) { TypeArrayKlass* ak = TypeArrayKlass::cast(k()); assert(sv->field_size() % type2size[ak->element_type()] == 0, "non-integral array length"); @@ -1059,6 +1065,20 @@ return svIndex; } +// restore fields of an eliminated value type array +void Deoptimization::reassign_value_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, valueArrayOop obj, ValueArrayKlass* vak, TRAPS) { + ValueKlass* vk = vak->element_klass(); + assert(vk->flatten_array(), "should only be used for flattened value type arrays"); + // Adjust offset to omit oop header + int base_offset = arrayOopDesc::base_offset_in_bytes(T_VALUETYPE) - ValueKlass::cast(vk)->first_field_offset(); + // Initialize all elements of the flattened value type array + for (int i = 0; i < sv->field_size(); i++) { + ScopeValue* val = sv->field_at(i); + int offset = base_offset + (i << Klass::layout_helper_log2_element_size(vak->layout_helper())); + reassign_fields_by_klass(vk, fr, reg_map, val->as_ObjectValue(), 0, (oop)obj, false /* skip_internal */, offset, CHECK); + } +} + // restore fields of all eliminated objects and arrays void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects, bool realloc_failures, bool skip_internal, TRAPS) { for (int i = 0; i < objects->length(); i++) { @@ -1076,6 +1096,9 @@ if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k()); reassign_fields_by_klass(ik, fr, reg_map, sv, 0, obj(), skip_internal, 0, CHECK); + } else if (k->is_valueArray_klass()) { + ValueArrayKlass* vak = ValueArrayKlass::cast(k()); + reassign_value_array_elements(fr, reg_map, sv, (valueArrayOop) obj(), vak, CHECK); } else if (k->is_typeArray_klass()) { TypeArrayKlass* ak = TypeArrayKlass::cast(k()); reassign_type_array_elements(fr, reg_map, sv, (typeArrayOop) obj(), ak->element_type()); --- old/src/share/vm/runtime/deoptimization.hpp 2017-02-13 17:39:04.579392424 +0100 +++ new/src/share/vm/runtime/deoptimization.hpp 2017-02-13 17:39:04.511392427 +0100 @@ -153,6 +153,7 @@ static bool realloc_objects(JavaThread* thread, frame* fr, GrowableArray* objects, TRAPS); static void reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type); static void reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj); + static void reassign_value_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, valueArrayOop obj, ValueArrayKlass* vak, TRAPS); static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects, bool realloc_failures, bool skip_internal, TRAPS); static void relock_objects(GrowableArray* monitors, JavaThread* thread, bool realloc_failures); static void pop_frames_failed_reallocs(JavaThread* thread, vframeArray* array); --- old/src/share/vm/runtime/fieldDescriptor.cpp 2017-02-13 17:39:04.839392412 +0100 +++ new/src/share/vm/runtime/fieldDescriptor.cpp 2017-02-13 17:39:04.771392415 +0100 @@ -31,6 +31,7 @@ #include "oops/instanceKlass.hpp" #include "oops/oop.inline.hpp" #include "oops/fieldStreams.hpp" +#include "oops/valueKlass.hpp" #include "runtime/fieldDescriptor.hpp" #include "runtime/handles.inline.hpp" #include "runtime/signature.hpp" @@ -146,8 +147,10 @@ } void fieldDescriptor::print_on_for(outputStream* st, oop obj) { - print_on(st); BasicType ft = field_type(); + if (ft != T_VALUETYPE) { + print_on(st); + } jint as_int = 0; switch (ft) { case T_BYTE: @@ -195,11 +198,21 @@ NOT_LP64(as_int = obj->int_field(offset())); obj->obj_field(offset())->print_value_on(st); break; - case T_VALUETYPE: - st->print(" "); - NOT_LP64(as_int = obj->int_field(offset())); - obj->obj_field(offset())->print_value_on(st); - break; + case T_VALUETYPE: { + // Resolve klass of flattened value type field + Thread* THREAD = Thread::current(); + SignatureStream ss(signature(), false); + Klass* k = ss.as_klass(Handle(field_holder()->class_loader()), Handle(field_holder()->protection_domain()), SignatureStream::ReturnNull, THREAD); + assert(k != NULL && !HAS_PENDING_EXCEPTION, "can resolve klass?"); + ValueKlass* vk = ValueKlass::cast(k); + int field_offset = offset() - vk->first_field_offset(); + obj = (oop)((uintptr_t)obj + field_offset); + // Print flattened fields of the value type field + st->print_cr("Flattened value type '%s':", vk->name()->as_C_string()); + FieldPrinter print_field(st, obj); + vk->do_nonstatic_fields(&print_field); + return; // Do not print underlying representation + } default: ShouldNotReachHere(); break; @@ -211,6 +224,7 @@ } else if (as_int < 0 || as_int > 9) { st->print(" (%x)", as_int); } + st->cr(); } #endif /* PRODUCT */ --- old/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2017-02-13 17:39:05.079392401 +0100 +++ new/test/compiler/valhalla/valuetypes/ValueTypeTestBench.java 2017-02-13 17:39:05.011392404 +0100 @@ -37,6 +37,13 @@ * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench */ +// TODO Enable this +/* + * run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UnlockExperimentalVMOptions -XX:+ValueTypePassFieldsAsArgs -XX:+AlwaysIncrementalInline -XX:+ValueArrayFlatten + * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench + */ + package compiler.valhalla.valuetypes; import compiler.whitebox.CompilerWhiteBoxTest; @@ -475,10 +482,14 @@ } // Test loop with uncommon trap referencing a value type - @Test(match = {TRAP, SCOBJ}, matchCount = {1, 1}, failOn = ALLOC + LOAD + STORE) + @Test(match = {TRAP, SCOBJ}, matchCount = {1, -1 /* at least 1 */}, failOn = LOAD) public long test12(boolean b) { MyValue1 v = MyValue1.createInline(rI, rL); - long result = 42; + MyValue1[] va = new MyValue1[Math.abs(rI) % 10]; + for (int i = 0; i < va.length; ++i) { + va[i] = MyValue1.createInline(rI, rL); + } + long result = rL; for (int i = 0; i < 1000; ++i) { if (b) { result += v.x; @@ -486,6 +497,9 @@ // Uncommon trap referencing v. We delegate allocation to the // interpreter by adding a SafePointScalarObjectNode. result = v.hashInterpreted(); + for (int j = 0; j < va.length; ++j) { + result += va[j].hash(); + } } } return result; @@ -494,14 +508,18 @@ @DontCompile public void test12_verifier(boolean warmup) { long result = test12(warmup); - Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash()); + Asserts.assertEQ(result, warmup ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash()); } // Test loop with uncommon trap referencing a value type - @Test(match = {TRAP, LOAD}, matchCount = {1, 1}, failOn = ALLOC + STORE + SCOBJ) + @Test(match = {TRAP}, matchCount = {1}) public long test13(boolean b) { MyValue1 v = MyValue1.createDontInline(rI, rL); - long result = 42; + MyValue1[] va = new MyValue1[Math.abs(rI) % 10]; + for (int i = 0; i < va.length; ++i) { + va[i] = MyValue1.createDontInline(rI, rL); + } + long result = rL; for (int i = 0; i < 1000; ++i) { if (b) { result += v.x; @@ -509,6 +527,9 @@ // Uncommon trap referencing v. Should not allocate // but just pass the existing oop to the uncommon trap. result = v.hashInterpreted(); + for (int j = 0; j < va.length; ++j) { + result += va[j].hashInterpreted(); + } } } return result; @@ -517,7 +538,7 @@ @DontCompile public void test13_verifier(boolean warmup) { long result = test13(warmup); - Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash()); + Asserts.assertEQ(result, warmup ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash()); } // Create a value type in a non-inlined method and then call a @@ -621,26 +642,26 @@ // trap: verify that deoptimization causes the value type to be // correctly allocated. @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE) - @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD) + @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {2}, failOn = LOAD) public long test20(boolean flag) { MyValue1 v = MyValue1.createInline(rI, rL); - // TODO add value type array testcase - // MyValue1[] va = new MyValue1[42]; + MyValue1[] va = new MyValue1[3]; if (flag) { // uncommon trap WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20")); } - return v.hashInterpreted(); // + va[0].hashInterpreted(); + return v.hashInterpreted() + va[0].hashInterpreted() + + va[1].hashInterpreted() + va[2].hashInterpreted(); } @DontCompile public void test20_verifier(boolean warmup) { MyValue1[] va = new MyValue1[42]; long result = test20(false); - Asserts.assertEQ(result, hash() /* + va[0].hash() */); + Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash()); if (!warmup) { result = test20(true); - Asserts.assertEQ(result, hash() /* + va[0].hash() */); + Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash()); } } @@ -895,27 +916,30 @@ // test that debug info at a call is correct @DontCompile - public long test36_interp(MyValue2 v, boolean flag) { + public long test36_interp(MyValue2 v, MyValue1[] va, boolean flag) { if (flag) { // uncommon trap WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test36")); } - return v.hash(); + return v.hash() + va[0].hash() + va[1].hash(); } @Test(failOn = ALLOC + STORE + TRAP) - public long test36(MyValue2 v, boolean flag, long l) { - return test36_interp(v, flag) + l; + public long test36(MyValue2 v, MyValue1[] va, boolean flag, long l) { + return test36_interp(v, va, flag) + l; } @DontCompile public void test36_verifier(boolean warmup) { MyValue2 v = MyValue2.createInline(rI, true); - long result = test36(v, false, rL); - Asserts.assertEQ(result, v.hashInterpreted() + rL); + MyValue1[] va = new MyValue1[2]; + va[0] = MyValue1.createDontInline(rI, rL); + va[1] = MyValue1.createDontInline(rI, rL); + long result = test36(v, va, false, rL); + Asserts.assertEQ(result, v.hashInterpreted() + va[0].hash() + va[1].hash() + rL); if (!warmup) { - result = test36(v, true, rL); - Asserts.assertEQ(result, v.hashInterpreted() + rL); + result = test36(v, va, true, rL); + Asserts.assertEQ(result, v.hashInterpreted() + va[0].hash() + va[1].hash() + rL); } } @@ -1068,7 +1092,8 @@ } // Test creation of a value type array and element access - @Test(failOn = (LOOP + LOAD + TRAP)) + @Test(valid = ValueTypeArrayFlattenOff, failOn = (LOOP + LOAD + TRAP)) + @Test(valid = ValueTypeArrayFlattenOn, failOn = (ALLOC + ALLOCA + LOOP + LOAD + LOADP + STORE + TRAP)) public long test42() { MyValue1[] va = new MyValue1[1]; va[0] = MyValue1.createInline(rI, rL); @@ -1128,8 +1153,7 @@ } } - // TODO add match rules - @Test() + @Test(failOn = (TRAP)) public MyValue1[] test45(boolean b) { MyValue1[] va; if (b) { @@ -1169,7 +1193,8 @@ } // Test creation of value type array with single element - @Test(failOn = LOOP + TRAP) + @Test(valid = ValueTypeArrayFlattenOff, failOn = (LOAD + LOOP + TRAP)) + @Test(valid = ValueTypeArrayFlattenOn, failOn = (ALLOCA + LOAD + LOOP + TRAP)) public MyValue1 test46() { MyValue1[] va = new MyValue1[1]; return va[0]; @@ -1210,8 +1235,9 @@ Asserts.assertEQ(va.length, 0); } - // Test that value type array loaded from field has correct type static MyValue1[] test49_va; + + // Test that value type array loaded from field has correct type @Test(failOn = (LOOP)) public long test49() { return test49_va[0].hash(); @@ -1226,7 +1252,7 @@ } // test vdefault - @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP) + @Test(failOn = ALLOC + LOAD + LOADP + STORE + LOOP + TRAP) public long test50() { MyValue2 v = MyValue2.createDefaultInline(); return v.hash(); @@ -1253,7 +1279,7 @@ } // test vwithfield - @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP) + @Test(failOn = ALLOC + LOAD + LOADP + STORE + LOOP + TRAP) public long test52() { MyValue2 v = MyValue2.createWithFieldsInline(rI, true); return v.hash(); @@ -1280,7 +1306,7 @@ } // multi-dimensional arrays - @Test() + @Test public MyValue1[][][] test54(int len1, int len2, int len3) { MyValue1[][][] arr = new MyValue1[len1][len2][len3]; for (int i = 0; i < len1; i++) { @@ -1305,7 +1331,7 @@ } } - @Test() + @Test public void test55(MyValue1[][][] arr, long[] res) { int l = 0; for (int i = 0; i < arr.length; i++) { @@ -1383,21 +1409,14 @@ } } - private static void execute_vm(String... extra_args) throws Throwable { - ArrayList all_args = new ArrayList(List.of( - "-noverify", - "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI", - "-XX:-TieredCompilation", "-XX:-BackgroundCompilation", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", - "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", - "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*", - "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*" - )); - all_args.addAll(List.of(extra_args)); + private static void execute_vm(String... args) throws Throwable { + Asserts.assertFalse(tests.isEmpty(), "no tests to execute"); + ArrayList all_args = new ArrayList(List.of(args)); // Run tests in own process and verify output all_args.add(ValueTypeTestBench.class.getName()); all_args.add("run"); - OutputAnalyzer oa = ProcessTools.executeTestJvm(all_args.toArray(new String[0])); + // Spawn process with default JVM options from the test's run command + OutputAnalyzer oa = ProcessTools.executeTestJvmAllArgs(all_args.toArray(new String[all_args.size()])); // If ideal graph printing is enabled/supported, verify output String output = oa.getOutput(); oa.shouldHaveExitValue(0); @@ -1410,24 +1429,12 @@ public static void main(String[] args) throws Throwable { if (args.length == 0) { - String field_as_args; - String array_flatten; - if (ValueTypePassFieldsAsArgs) { - field_as_args = "-XX:+ValueTypePassFieldsAsArgs"; - } else { - field_as_args = "-XX:-ValueTypePassFieldsAsArgs"; - } - if (ValueTypeArrayFlatten) { - array_flatten = "-XX:+ValueArrayFlatten"; - } else { - array_flatten = "-XX:-ValueArrayFlatten"; - } - execute_vm("-XX:+UnlockExperimentalVMOptions", field_as_args, array_flatten); - execute_vm("-XX:+AlwaysIncrementalInline", "-XX:+UnlockExperimentalVMOptions", field_as_args, array_flatten); + execute_vm("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-BackgroundCompilation", + "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", + "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", + "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*", + "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*"); } else { - if (USE_COMPILER && PRINT_IDEAL && !XCOMP) { - System.out.println("PrintIdeal enabled"); - } // Execute tests ValueTypeTestBench bench = new ValueTypeTestBench(); bench.run(); @@ -1491,7 +1498,11 @@ count++; nodes += matcher.group() + "\n"; } - Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes); + if (matchCount[i] < 0) { + Asserts.assertLTE(Math.abs(matchCount[i]), count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes); + } else { + Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes); + } } tests.remove(testName); System.out.println(testName + " passed"); @@ -1524,6 +1535,9 @@ } public void run() throws Exception { + if (USE_COMPILER && PRINT_IDEAL && !XCOMP) { + System.out.println("PrintIdeal enabled"); + } System.out.format("rI = %d, rL = %d\n", rI, rL); setup(this.getClass().getDeclaredMethods()); setup(MyValue1.class.getDeclaredMethods());