diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 3f89fc9f398..5055cd48a96 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -468,7 +468,10 @@ "Try to simplify allocation merges before Scalar Replacement") \ \ notproduct(bool, TraceReduceAllocationMerges, false, \ - "Trace decision for simplifying allocation merges.") \ + "Trace decision for simplifying allocation merges.") \ + \ + develop(bool, VerifyReduceAllocationMerges, true, \ + "Verify reduce allocation merges in escape analysis") \ \ product(bool, DoEscapeAnalysis, true, \ "Perform escape analysis") \ diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index c0da4b4a120..514155c6af8 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -369,6 +369,18 @@ bool ConnectionGraph::compute_escape() { assert(ptn->escape_state() == PointsToNode::NoEscape && ptn->scalar_replaceable(), "sanity"); } } + + if (VerifyReduceAllocationMerges) { + for (uint i = 0; i < reducible_merges.size(); i++ ) { + Node* n = reducible_merges.at(i); + if (!can_reduce_phi(n->as_Phi())) { + TraceReduceAllocationMerges = true; + n->dump(2); + n->dump(-2); + assert(can_reduce_phi(n->as_Phi()), "Sanity: previous reducible Phi is no longer reducible before SUT."); + } + } + } #endif // 5. Separate memory graph for scalar replaceable allcations. @@ -530,14 +542,21 @@ void ConnectionGraph::reduce_phi_on_field_access(PhiNode* ophi, GrowableArraydump(2); + ophi->dump(-2); + assert(can_reduce_phi(ophi), "Sanity: previous reducible Phi is no longer reducible inside reduce_phi_on_field_access."); + } +#endif + // Iterate over Phi outputs looking for an AddP for (int j = ophi->outcnt()-1; j >= 0;) { Node* previous_addp = ophi->raw_out(j); - uint num_edges = 1; if (previous_addp->is_AddP()) { // All AddPs are present in the connection graph FieldNode* fn = ptnode_adr(previous_addp->_idx)->as_Field(); - num_edges = previous_addp->in(AddPNode::Address) == previous_addp->in(AddPNode::Base) ? 2 : 1; // Iterate over AddP looking for a Load for (int k = previous_addp->outcnt()-1; k >= 0;) { @@ -547,55 +566,66 @@ void ConnectionGraph::reduce_phi_on_field_access(PhiNode* ophi, GrowableArrayreplace_node(previous_load, data_phi); assert(data_phi != nullptr, "Output of split_through_phi is null."); assert(data_phi != previous_load, "Output of split_through_phi is same as input."); + assert(data_phi->is_Phi(), "Return of split_through_phi should be a Phi."); // Push the newly created AddP on alloc_worklist and patch // the connection graph. Note that the changes in the CG below // won't affect the ES of objects since the new nodes have the // same status as the old ones. - if (data_phi != nullptr && data_phi->is_Phi()) { - for (uint i = 1; i < data_phi->req(); i++) { - Node* new_load = data_phi->in(i); - if (new_load->is_Load()) { - Node* new_addp = new_load->in(MemNode::Address); - Node* base = get_addp_base(new_addp); + for (uint i = 1; i < data_phi->req(); i++) { + Node* new_load = data_phi->in(i); + if (new_load->is_Load()) { + Node* new_addp = new_load->in(MemNode::Address); + Node* base = get_addp_base(new_addp); - // The base might not be something that we can create an unique - // type for. If that's the case we are done with that input. - PointsToNode* jobj_ptn = unique_java_object(base); - if (jobj_ptn == nullptr || !jobj_ptn->scalar_replaceable()) { - continue; - } + // The base might not be something that we can create an unique + // type for. If that's the case we are done with that input. + PointsToNode* jobj_ptn = unique_java_object(base); + if (jobj_ptn == nullptr || !jobj_ptn->scalar_replaceable()) { + continue; + } - // Push to alloc_worklist since the base has an unique_type - alloc_worklist.append_if_missing(new_addp); + // Push to alloc_worklist since the base has an unique_type + alloc_worklist.append_if_missing(new_addp); - // Now let's add the node to the connection graph - _nodes.at_grow(new_addp->_idx, nullptr); - add_field(new_addp, fn->escape_state(), fn->offset()); - add_base(ptnode_adr(new_addp->_idx)->as_Field(), ptnode_adr(base->_idx)); + // Now let's add the node to the connection graph + _nodes.at_grow(new_addp->_idx, nullptr); + add_field(new_addp, fn->escape_state(), fn->offset()); + add_base(ptnode_adr(new_addp->_idx)->as_Field(), ptnode_adr(base->_idx)); - // If the load doesn't load an object then it won't be - // part of the connection graph - PointsToNode* curr_load_ptn = ptnode_adr(previous_load->_idx); - if (curr_load_ptn != nullptr) { - _nodes.at_grow(new_load->_idx, nullptr); - add_local_var(new_load, curr_load_ptn->escape_state()); - add_edge(ptnode_adr(new_load->_idx), ptnode_adr(new_addp->_idx)->as_Field()); - } + // If the load doesn't load an object then it won't be + // part of the connection graph + PointsToNode* curr_load_ptn = ptnode_adr(previous_load->_idx); + if (curr_load_ptn != nullptr) { + _nodes.at_grow(new_load->_idx, nullptr); + add_local_var(new_load, curr_load_ptn->escape_state()); + add_edge(ptnode_adr(new_load->_idx), ptnode_adr(new_addp->_idx)->as_Field()); } } } } - --k; - k = MIN2(k, (int)previous_addp->outcnt()-1); + k = MIN2(--k, (int)previous_addp->outcnt()-1); } // Remove the old AddP from the processing list because it's dead now alloc_worklist.remove_if_existing(previous_addp); + _igvn->remove_globally_dead_node(previous_addp); } - j -= num_edges; - j = MIN2(j, (int)ophi->outcnt()-1); + j = MIN2(--j, (int)ophi->outcnt()-1); } + +#ifdef ASSERT + if (VerifyReduceAllocationMerges) { + for (uint j = 0; j < ophi->outcnt(); j++) { + Node* use = ophi->raw_out(j); + if (!use->is_SafePoint()) { + ophi->dump(2); + ophi->dump(-2); + assert(false, "Should be a SafePoint."); + } + } + } +#endif } // This method will create a SafePointScalarObjectNode for each combination of @@ -3607,6 +3637,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, GrowableArray &arraycopy_worklist, GrowableArray &mergemem_worklist, Unique_Node_List &reducible_merges) { + DEBUG_ONLY(Unique_Node_List reduced_merges;) GrowableArray memnode_worklist; GrowableArray orig_phis; PhaseIterGVN *igvn = _igvn; @@ -3783,6 +3814,11 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, if (reducible_merges.member(n)) { // Split loads through phi reduce_phi_on_field_access(n->as_Phi(), alloc_worklist); +#ifdef ASSERT + if (VerifyReduceAllocationMerges) { + reduced_merges.push(n); + } +#endif continue; } JavaObjectNode* jobj = unique_java_object(n); @@ -3895,14 +3931,24 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, } #ifdef ASSERT - // At this point reducible Phis shouldn't have AddP users anymore; only SafePoints. - for (uint i = 0; i < reducible_merges.size(); i++) { - Node* phi = reducible_merges.at(i); - for (DUIterator_Fast jmax, j = phi->fast_outs(jmax); j < jmax; j++) { - Node* use = phi->fast_out(j); - if (!use->is_SafePoint()) { - phi->dump(-3); - assert(false, "Unexpected user of reducible Phi -> %s", use->Name()); + if (VerifyReduceAllocationMerges) { + // At this point reducible Phis shouldn't have AddP users anymore; only SafePoints. + for (uint i = 0; i < reducible_merges.size(); i++) { + Node* phi = reducible_merges.at(i); + + if (!reduced_merges.member(phi)) { + phi->dump(2); + phi->dump(-2); + assert(false, "This reducible merge wasn't reduced."); + } + + for (DUIterator_Fast jmax, j = phi->fast_outs(jmax); j < jmax; j++) { + Node* use = phi->fast_out(j); + if (!use->is_SafePoint()) { + phi->dump(2); + phi->dump(-2); + assert(false, "Unexpected user of reducible Phi -> %d:%s:%d", use->_idx, use->Name(), use->outcnt()); + } } } } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 4982c1286ec..960206137f6 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -597,7 +597,7 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode for (DUIterator_Fast kmax, k = use->fast_outs(kmax); k < kmax && can_eliminate; k++) { Node* n = use->fast_out(k); - if (!n->is_Store() && n->Opcode() != Op_CastP2X && !bs->is_gc_pre_barrier_node(n)) { + if (!n->is_Store() && n->Opcode() != Op_CastP2X && !bs->is_gc_pre_barrier_node(n) && !reduce_merge_precheck) { DEBUG_ONLY(disq_node = n;) if (n->is_Load() || n->is_LoadStore()) { NOT_PRODUCT(fail_eliminate = "Field load";) @@ -675,6 +675,11 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode #endif /*ASSERT*/ } } + + if (TraceReduceAllocationMerges && !can_eliminate && reduce_merge_precheck) { + tty->print_cr("\tCan't eliminate allocation because '%s': ", fail_eliminate != nullptr ? fail_eliminate : ""); + DEBUG_ONLY(if (disq_node != nullptr) disq_node->dump();) + } #endif return can_eliminate; }