< prev index next >

src/share/vm/opto/valuetypenode.cpp

Print this page

        

*** 56,81 **** 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 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, ciInstanceKlass* holder, int holder_offset) { // Create and initialize a ValueTypeNode by loading all field values from // a flattened value type field at 'holder_offset' or from a value type array. ValueTypeNode* vt = make(gvn, vk); // 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, 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 = holder_offset + field_offset(i); ciType* ftype = field_type(i); --- 56,85 ---- 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 TypeValueType* type = gvn.type(oop)->is_valuetypeptr()->value_type(); ValueTypeNode* vt = new ValueTypeNode(type, oop); ! vt->load(gvn, mem, oop, oop, type->value_klass()); ! assert(vt->is_allocated(&gvn), "value type should be allocated"); ! assert(oop->is_Con() || oop->is_CheckCastPP() || vt->is_loaded(&gvn, type) != NULL, "value type should be loaded"); return gvn.transform(vt); } 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 'holder_offset' or from a value type array. ValueTypeNode* vt = make(gvn, vk); // 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(gvn, mem, obj, ptr, holder, holder_offset); ! vt = gvn.transform(vt)->as_ValueType(); ! assert(!vt->is_allocated(&gvn), "value type should not be allocated"); ! return vt; } ! void ValueTypeNode::load(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 = holder_offset + field_offset(i); ciType* ftype = field_type(i);
*** 116,140 **** } set_field_value(i, gvn.transform(value)); } } ! 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. 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, 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 ! value->isa_ValueType()->store(kit, base, ptr, holder, offset); } else { 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 --- 120,182 ---- } set_field_value(i, gvn.transform(value)); } } ! Node* ValueTypeNode::is_loaded(PhaseGVN* phase, const TypeValueType* t, Node* base, int holder_offset) { ! for (uint i = 0; i < field_count(); ++i) { ! int offset = holder_offset + field_offset(i); ! Node* value = field_value(i); ! if (value->isa_DecodeN()) { ! // Skip DecodeN ! value = value->in(1); ! } ! if (value->isa_Load()) { ! AddPNode* load_addr = value->in(MemNode::Address)->as_AddP(); ! if (base == NULL) { ! // Set base and check if pointer type matches ! base = load_addr->base_node(); ! const TypeValueTypePtr* vtptr = phase->type(base)->isa_valuetypeptr(); ! if (vtptr == NULL || !vtptr->value_type()->eq(t)) { ! return NULL; ! } ! } ! // Check if base and offset of field load matches ! Node* off = load_addr->in(AddPNode::Offset); ! int load_offset = LP64_ONLY(off->get_long()) NOT_LP64(off->get_int()); ! if (base != load_addr->base_node() || offset != load_offset) { ! return NULL; ! } ! } else if (value->isa_ValueType()) { ! // Check value type field load recursively ! ValueTypeNode* vt = value->as_ValueType(); ! base = vt->is_loaded(phase, t, base, offset - vt->value_klass()->first_field_offset()); ! if (base == NULL) { ! return NULL; ! } ! } else { ! return NULL; ! } ! } ! return base; ! } ! ! void ValueTypeNode::store_flattened(GraphKit* kit, Node* base, 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. holder_offset -= value_klass()->first_field_offset(); ! store(kit, base, ptr, holder, holder_offset); } ! void ValueTypeNode::store(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 ! value->isa_ValueType()->store_flattened(kit, base, ptr, holder, offset); } else { 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
*** 151,179 **** const TypeOopPtr* ft = TypeOopPtr::make_from_klass(field_type(i)->as_klass()); assert(adr->bottom_type()->is_ptr_to_narrowoop() == UseCompressedOops, "inconsistent"); bool is_array = base_type->isa_aryptr() != NULL; kit->store_oop(kit->control(), base, adr, adr_type, value, ft, bt, is_array, MemNode::unordered); } - } } } ! Node* ValueTypeNode::store_to_memory(GraphKit* kit) { Node* in_oop = get_oop(); Node* null_ctl = kit->top(); // Check if value type is already allocated Node* not_null_oop = kit->null_check_oop(in_oop, &null_ctl); if (null_ctl->is_top()) { // Value type is allocated return not_null_oop; } // Not able to prove that value type is allocated. // Emit runtime check that may be folded later. ! const Type* oop_type = kit->gvn().type(in_oop); ! assert(TypePtr::NULL_PTR->higher_equal(oop_type), "should not be allocated"); ! const TypeValueTypePtr* vtptr_type = TypeValueTypePtr::make(bottom_type()->isa_valuetype(), TypePtr::NotNull); RegionNode* region = new RegionNode(3); PhiNode* oop = new PhiNode(region, vtptr_type); PhiNode* io = new PhiNode(region, Type::ABIO); PhiNode* mem = new PhiNode(region, Type::MEMORY, TypePtr::BOTTOM); --- 193,218 ---- const TypeOopPtr* ft = TypeOopPtr::make_from_klass(field_type(i)->as_klass()); assert(adr->bottom_type()->is_ptr_to_narrowoop() == UseCompressedOops, "inconsistent"); bool is_array = base_type->isa_aryptr() != NULL; kit->store_oop(kit->control(), base, adr, adr_type, value, ft, bt, is_array, MemNode::unordered); } } } } ! Node* ValueTypeNode::allocate(GraphKit* kit) { Node* in_oop = get_oop(); Node* null_ctl = kit->top(); // Check if value type is already allocated Node* not_null_oop = kit->null_check_oop(in_oop, &null_ctl); if (null_ctl->is_top()) { // Value type is allocated return not_null_oop; } // Not able to prove that value type is allocated. // Emit runtime check that may be folded later. ! assert(!is_allocated(&kit->gvn()), "should not be allocated"); const TypeValueTypePtr* vtptr_type = TypeValueTypePtr::make(bottom_type()->isa_valuetype(), TypePtr::NotNull); RegionNode* region = new RegionNode(3); PhiNode* oop = new PhiNode(region, vtptr_type); PhiNode* io = new PhiNode(region, Type::ABIO); PhiNode* mem = new PhiNode(region, Type::MEMORY, TypePtr::BOTTOM);
*** 187,199 **** // Oop is NULL, allocate value type kit->set_control(null_ctl); kit->kill_dead_locals(); ciValueKlass* vk = value_klass(); Node* klass_node = kit->makecon(TypeKlassPtr::make(vk)); ! Node* alloc_oop = kit->new_instance(klass_node); // Write field values to memory ! 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()); mem ->init_req(2, kit->merged_memory()); --- 226,238 ---- // Oop is NULL, allocate value type kit->set_control(null_ctl); kit->kill_dead_locals(); ciValueKlass* vk = value_klass(); Node* klass_node = kit->makecon(TypeKlassPtr::make(vk)); ! Node* alloc_oop = kit->new_instance(klass_node, NULL, NULL, false, this); // Write field values to memory ! store(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()); mem ->init_req(2, kit->merged_memory());
*** 212,221 **** --- 251,265 ---- vt->set_oop(res_oop); kit->replace_in_map(this, kit->gvn().transform(vt)); return res_oop; } + bool ValueTypeNode::is_allocated(PhaseGVN* phase) const { + const Type* oop_type = phase->type(get_oop()); + return oop_type->meet(TypePtr::NULL_PTR) != oop_type; + } + // Clones the values type to handle control flow merges involving multiple value types. // The inputs are replaced by PhiNodes to represent the merged values for the given region. ValueTypeNode* ValueTypeNode::clone_with_phis(PhaseGVN* gvn, Node* region) { assert(!has_phi_inputs(region), "already cloned with phis"); ValueTypeNode* vt = clone()->as_ValueType();
*** 413,426 **** } return edges; } Node* ValueTypeNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (can_reshape) { PhaseIterGVN* igvn = phase->is_IterGVN(); ! const Type* oop_type = igvn->type(get_oop()); ! if (oop_type->meet(TypePtr::NULL_PTR) != oop_type) { // Value type is heap allocated, search for safepoint uses for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { Node* out = fast_out(i); if (out->is_SafePoint()) { // Let SafePointNode::Ideal() take care of re-wiring the --- 457,479 ---- } return edges; } Node* ValueTypeNode::Ideal(PhaseGVN* phase, bool can_reshape) { + if (!is_allocated(phase)) { + // Check if this value type is loaded from memory + Node* base = is_loaded(phase, type()->is_valuetype()); + if (base != NULL) { + // Save the oop + set_oop(base); + assert(is_allocated(phase), "should now be allocated"); + } + } + if (can_reshape) { PhaseIterGVN* igvn = phase->is_IterGVN(); ! if (is_allocated(igvn)) { // Value type is heap allocated, search for safepoint uses for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { Node* out = fast_out(i); if (out->is_SafePoint()) { // Let SafePointNode::Ideal() take care of re-wiring the
*** 428,441 **** igvn->rehash_node_delayed(out); } } } } - return NULL; } // When a call returns multiple values, it has several result // projections, one per field. Replacing the result of the call by a // value type node (after late inlining) requires that for each result // projection, we find the corresponding value type field. void ValueTypeNode::replace_call_results(Node* call, Compile* C) { --- 481,560 ---- igvn->rehash_node_delayed(out); } } } } return NULL; } + // Search for multiple allocations of this value type + // and try to replace them by dominating allocations. + void ValueTypeNode::remove_redundant_allocations(PhaseIterGVN* igvn, PhaseIdealLoop* phase) { + assert(EliminateAllocations, "allocation elimination should be enabled"); + Node_List dead_allocations; + // Search for allocations of this value type + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + Node* out1 = fast_out(i); + if (out1->is_Allocate() && out1->in(AllocateNode::ValueNode) == this) { + AllocateNode* alloc = out1->as_Allocate(); + Node* res_dom = NULL; + if (is_allocated(igvn)) { + // The value type is already allocated but still connected to an AllocateNode. + // This can happen with late inlining when we first allocate a value type argument + // but later decide to inline the call with the callee code also allocating. + res_dom = get_oop(); + } else { + // Search for a dominating allocation of the same value type + for (DUIterator_Fast jmax, j = fast_outs(jmax); j < jmax; j++) { + Node* out2 = fast_out(j); + if (alloc != out2 && out2->is_Allocate() && out2->in(AllocateNode::ValueNode) == this && + phase->is_dominator(out2, alloc)) { + AllocateNode* alloc_dom = out2->as_Allocate(); + assert(alloc->in(AllocateNode::KlassNode) == alloc_dom->in(AllocateNode::KlassNode), "klasses should match"); + res_dom = alloc_dom->result_cast(); + break; + } + } + } + if (res_dom != NULL) { + // Found a dominating allocation + Node* res = alloc->result_cast(); + assert(res != NULL, "value type allocation should not be dead"); + // Move users to dominating allocation + igvn->replace_node(res, res_dom); + // The dominated allocation is now dead, remove the + // value type node connection and adjust the iterator. + dead_allocations.push(alloc); + igvn->replace_input_of(alloc, AllocateNode::ValueNode, NULL); + --i; --imax; + #ifdef ASSERT + if (PrintEliminateAllocations) { + tty->print("++++ Eliminated: %d Allocate ", alloc->_idx); + dump_spec(tty); + tty->cr(); + } + #endif + } + } + } + + // Remove dead value type allocations by replacing the projection nodes + for (uint i = 0; i < dead_allocations.size(); ++i) { + CallProjections projs; + AllocateNode* alloc = dead_allocations.at(i)->as_Allocate(); + alloc->extract_projections(&projs, true); + // Use lazy_replace to avoid corrupting the dominator tree of PhaseIdealLoop + phase->lazy_replace(projs.fallthrough_catchproj, alloc->in(TypeFunc::Control)); + phase->lazy_replace(projs.fallthrough_memproj, alloc->in(TypeFunc::Memory)); + phase->lazy_replace(projs.catchall_memproj, phase->C->top()); + phase->lazy_replace(projs.fallthrough_ioproj, alloc->in(TypeFunc::I_O)); + phase->lazy_replace(projs.catchall_ioproj, phase->C->top()); + phase->lazy_replace(projs.catchall_catchproj, phase->C->top()); + phase->lazy_replace(projs.resproj, phase->C->top()); + } + } + // When a call returns multiple values, it has several result // projections, one per field. Replacing the result of the call by a // value type node (after late inlining) requires that for each result // projection, we find the corresponding value type field. void ValueTypeNode::replace_call_results(Node* call, Compile* C) {
< prev index next >