8319256: Print more diagnostic information when an unexpected user is found in a Phi

Reviewed-by: kvn, thartmann
This commit is contained in:
Cesar Soares Lucas 2023-11-10 07:10:13 +00:00 committed by Tobias Hartmann
parent a95062b39a
commit 9cce9fe067
3 changed files with 96 additions and 42 deletions
src/hotspot/share/opto

@ -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") \

@ -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, GrowableArray<No
// though the load doesn't have an unique instance type.
bool ignore_missing_instance_id = true;
#ifdef ASSERT
if (VerifyReduceAllocationMerges && !can_reduce_phi(ophi)) {
TraceReduceAllocationMerges = true;
ophi->dump(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, GrowableArray<No
_igvn->replace_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<Node *> &alloc_worklist,
GrowableArray<ArrayCopyNode*> &arraycopy_worklist,
GrowableArray<MergeMemNode*> &mergemem_worklist,
Unique_Node_List &reducible_merges) {
DEBUG_ONLY(Unique_Node_List reduced_merges;)
GrowableArray<Node *> memnode_worklist;
GrowableArray<PhiNode *> orig_phis;
PhaseIterGVN *igvn = _igvn;
@ -3783,6 +3814,11 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &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<Node *> &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());
}
}
}
}

@ -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;
}