8280126: C2: detect and remove dead irreducible loops

Reviewed-by: kvn, chagedorn, thartmann
This commit is contained in:
Emanuel Peter 2023-02-08 07:45:09 +00:00
parent 2a579ab839
commit ac7119f0d5
12 changed files with 2100 additions and 71 deletions

@ -1624,7 +1624,8 @@ void ciTypeFlow::Block::df_init() {
_pre_order = -1; assert(!has_pre_order(), "");
_post_order = -1; assert(!has_post_order(), "");
_loop = nullptr;
_irreducible_entry = false;
_irreducible_loop_head = false;
_irreducible_loop_secondary_entry = false;
_rpo_next = nullptr;
}
@ -1842,6 +1843,40 @@ void ciTypeFlow::Block::set_backedge_copy(bool z) {
_backedge_copy = z;
}
// Analogous to PhaseIdealLoop::is_in_irreducible_loop
bool ciTypeFlow::Block::is_in_irreducible_loop() const {
if (!outer()->has_irreducible_entry()) {
return false; // No irreducible loop in method.
}
Loop* lp = loop(); // Innermost loop containing block.
if (lp == nullptr) {
assert(!is_post_visited(), "must have enclosing loop once post-visited");
return false; // Not yet processed, so we do not know, yet.
}
// Walk all the way up the loop-tree, search for an irreducible loop.
do {
if (lp->is_irreducible()) {
return true; // We are in irreducible loop.
}
if (lp->head()->pre_order() == 0) {
return false; // Found root loop, terminate.
}
lp = lp->parent();
} while (lp != nullptr);
// We have "lp->parent() == nullptr", which happens only for infinite loops,
// where no parent is attached to the loop. We did not find any irreducible
// loop from this block out to lp. Thus lp only has one entry, and no exit
// (it is infinite and reducible). We can always rewrite an infinite loop
// that is nested inside other loops:
// while(condition) { infinite_loop; }
// with an equivalent program where the infinite loop is an outermost loop
// that is not nested in any loop:
// while(condition) { break; } infinite_loop;
// Thus, we can understand lp as an outermost loop, and can terminate and
// conclude: this block is in no irreducible loop.
return false;
}
// ------------------------------------------------------------------
// ciTypeFlow::Block::is_clonable_exit
//
@ -1886,7 +1921,9 @@ void ciTypeFlow::Block::print_value_on(outputStream* st) const {
if (has_rpo()) st->print("rpo#%-2d ", rpo());
st->print("[%d - %d)", start(), limit());
if (is_loop_head()) st->print(" lphd");
if (is_irreducible_entry()) st->print(" irred");
if (is_in_irreducible_loop()) st->print(" in_irred");
if (is_irreducible_loop_head()) st->print(" irred_head");
if (is_irreducible_loop_secondary_entry()) st->print(" irred_entry");
if (_jsrs->size() > 0) { st->print("/"); _jsrs->print_on(st); }
if (is_backedge_copy()) st->print("/backedge_copy");
}

@ -535,8 +535,11 @@ public:
// Has this block been cloned for a loop backedge?
bool _backedge_copy;
// This block is entry to irreducible loop.
bool _irreducible_entry;
// This block is a loop head of an irreducible loop.
bool _irreducible_loop_head;
// This block is a secondary entry to an irreducible loop (entry but not head).
bool _irreducible_loop_secondary_entry;
// This block has monitor entry point.
bool _has_monitorenter;
@ -687,8 +690,11 @@ public:
Loop* loop() const { return _loop; }
void set_loop(Loop* lp) { _loop = lp; }
bool is_loop_head() const { return _loop && _loop->head() == this; }
void set_irreducible_entry(bool c) { _irreducible_entry = c; }
bool is_irreducible_entry() const { return _irreducible_entry; }
bool is_in_irreducible_loop() const;
void set_irreducible_loop_head() { _irreducible_loop_head = true; }
bool is_irreducible_loop_head() const { return _irreducible_loop_head; }
void set_irreducible_loop_secondary_entry() { _irreducible_loop_secondary_entry = true; }
bool is_irreducible_loop_secondary_entry() const { return _irreducible_loop_secondary_entry; }
void set_has_monitorenter() { _has_monitorenter = true; }
bool has_monitorenter() const { return _has_monitorenter; }
bool is_visited() const { return has_pre_order(); }
@ -755,7 +761,8 @@ public:
// Mark non-single entry to loop
void set_irreducible(Block* entry) {
_irreducible = true;
entry->set_irreducible_entry(true);
head()->set_irreducible_loop_head();
entry->set_irreducible_loop_secondary_entry();
}
bool is_irreducible() const { return _irreducible; }
@ -879,7 +886,7 @@ public:
JsrRecord* make_jsr_record(int entry_address, int return_address);
void set_loop_tree_root(Loop* ltr) { _loop_tree_root = ltr; }
Loop* loop_tree_root() { return _loop_tree_root; }
Loop* loop_tree_root() const { return _loop_tree_root; }
private:
// Get the initial state for start_bci:

@ -435,6 +435,19 @@ bool RegionNode::are_all_nodes_in_infinite_subgraph(Unique_Node_List& worklist)
}
#endif //ASSERT
void RegionNode::set_loop_status(RegionNode::LoopStatus status) {
assert(status != RegionNode::LoopStatus::NeverIrreducibleEntry, "do not set this");
assert(loop_status() == RegionNode::LoopStatus::NeverIrreducibleEntry, "why set our status again?");
_loop_status = status;
}
#ifdef ASSERT
void RegionNode::verify_can_be_irreducible_entry() const {
assert(loop_status() == RegionNode::LoopStatus::MaybeIrreducibleEntry, "must be marked irreducible");
assert(!is_Loop(), "LoopNode cannot be irreducible loop entry");
}
#endif //ASSERT
bool RegionNode::try_clean_mem_phi(PhaseGVN *phase) {
// Incremental inlining + PhaseStringOpts sometimes produce:
//
@ -522,6 +535,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
int cnt = 0; // Count of values merging
DEBUG_ONLY( int cnt_orig = req(); ) // Save original inputs count
int del_it = 0; // The last input path we delete
bool found_top = false; // irreducible loops need to check reachability if we find TOP
// For all inputs...
for( uint i=1; i<req(); ++i ){// For all paths in
Node *n = in(i); // Get the input
@ -545,6 +559,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
if( phase->type(n) == Type::TOP ) {
set_req_X(i, NULL, phase); // Ignore TOP inputs
modified = true;
found_top = true;
i--;
continue;
}
@ -580,7 +595,20 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
}
}
if (can_reshape && cnt == 1) {
if (can_reshape && found_top && loop_status() == RegionNode::LoopStatus::MaybeIrreducibleEntry) {
// Is it a dead irreducible loop?
// If an irreducible loop loses one of the multiple entries
// that went into the loop head, or any secondary entries,
// we need to verify if the irreducible loop is still reachable,
// as the special logic in is_unreachable_region only works
// for reducible loops.
if (is_unreachable_from_root(phase)) {
// The irreducible loop is dead - must remove it
PhaseIterGVN* igvn = phase->is_IterGVN();
remove_unreachable_subgraph(igvn);
return nullptr;
}
} else if (can_reshape && cnt == 1) {
// Is it dead loop?
// If it is LoopNopde it had 2 (+1 itself) inputs and
// one of them was cut. The loop is dead if it was EntryContol.
@ -590,49 +618,9 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
if ((this->is_Loop() && (del_it == LoopNode::EntryControl ||
(del_it == 0 && is_unreachable_region(phase)))) ||
(!this->is_Loop() && has_phis && is_unreachable_region(phase))) {
// This region and therefore all nodes on the input control path(s) are unreachable
// from root. To avoid incomplete removal of unreachable subgraphs, walk up the CFG
// and aggressively replace all nodes by top.
PhaseIterGVN* igvn = phase->is_IterGVN();
Node* top = phase->C->top();
ResourceMark rm;
Node_List nstack;
VectorSet visited;
nstack.push(this);
visited.set(_idx);
while (nstack.size() != 0) {
Node* n = nstack.pop();
for (uint i = 0; i < n->req(); ++i) {
Node* m = n->in(i);
assert(m != (Node*)phase->C->root(), "Should be unreachable from root");
if (m != NULL && m->is_CFG() && !visited.test_set(m->_idx)) {
nstack.push(m);
}
}
if (n->is_Region()) {
// Eagerly replace phis with top to avoid regionless phis.
n->set_req(0, NULL);
bool progress = true;
uint max = n->outcnt();
DUIterator j;
while (progress) {
progress = false;
for (j = n->outs(); n->has_out(j); j++) {
Node* u = n->out(j);
if (u->is_Phi()) {
igvn->replace_node(u, top);
if (max != n->outcnt()) {
progress = true;
j = n->refresh_out_pos(j);
max = n->outcnt();
}
}
}
}
}
igvn->replace_node(n, top);
}
return NULL;
remove_unreachable_subgraph(igvn);
return nullptr;
}
}
@ -804,6 +792,59 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
return modified ? this : NULL;
}
//--------------------------remove_unreachable_subgraph----------------------
// This region and therefore all nodes on the input control path(s) are unreachable
// from root. To avoid incomplete removal of unreachable subgraphs, walk up the CFG
// and aggressively replace all nodes by top.
// If a control node "def" with a single control output "use" has its single output
// "use" replaced with top, then "use" removes itself. This has the consequence that
// when we visit "use", it already has all inputs removed. They are lost and we cannot
// traverse them. This is why we fist find all unreachable nodes, and then remove
// them in a second step.
void RegionNode::remove_unreachable_subgraph(PhaseIterGVN* igvn) {
Node* top = igvn->C->top();
ResourceMark rm;
Unique_Node_List unreachable; // visit each only once
unreachable.push(this);
// Recursively find all control inputs.
for (uint i = 0; i < unreachable.size(); i++) {
Node* n = unreachable.at(i);
for (uint i = 0; i < n->req(); ++i) {
Node* m = n->in(i);
assert(m == nullptr || !m->is_Root(), "Should be unreachable from root");
if (m != nullptr && m->is_CFG()) {
unreachable.push(m);
}
}
}
// Remove all unreachable nodes.
for (uint i = 0; i < unreachable.size(); i++) {
Node* n = unreachable.at(i);
if (n->is_Region()) {
// Eagerly replace phis with top to avoid regionless phis.
n->set_req(0, nullptr);
bool progress = true;
uint max = n->outcnt();
DUIterator j;
while (progress) {
progress = false;
for (j = n->outs(); n->has_out(j); j++) {
Node* u = n->out(j);
if (u->is_Phi()) {
igvn->replace_node(u, top);
if (max != n->outcnt()) {
progress = true;
j = n->refresh_out_pos(j);
max = n->outcnt();
}
}
}
}
}
igvn->replace_node(n, top);
}
}
//------------------------------optimize_trichotomy--------------------------
// Optimize nested comparisons of the following kind:
//
@ -952,6 +993,22 @@ const RegMask &RegionNode::out_RegMask() const {
return RegMask::Empty;
}
#ifndef PRODUCT
void RegionNode::dump_spec(outputStream* st) const {
Node::dump_spec(st);
switch (loop_status()) {
case RegionNode::LoopStatus::MaybeIrreducibleEntry:
st->print("#irreducible ");
break;
case RegionNode::LoopStatus::Reducible:
st->print("#reducible ");
break;
case RegionNode::LoopStatus::NeverIrreducibleEntry:
break; // nothing
}
}
#endif
// Find the one non-null required input. RegionNode only
Node *Node::nonnull_req() const {
assert( is_Region(), "" );
@ -1353,6 +1410,9 @@ Node* PhiNode::is_cmove_id(PhaseTransform* phase, int true_path) {
//------------------------------Identity---------------------------------------
// Check for Region being Identity.
Node* PhiNode::Identity(PhaseGVN* phase) {
if (must_wait_for_region_in_irreducible_loop(phase)) {
return this;
}
// Check for no merging going on
// (There used to be special-case code here when this->region->is_Loop.
// It would check for a tributary phi on the backedge that the main phi
@ -1700,6 +1760,18 @@ static void split_once(PhaseIterGVN *igvn, Node *phi, Node *val, Node *n, Node *
//------------------------------split_flow_path--------------------------------
// Check for merging identical values and split flow paths
static Node* split_flow_path(PhaseGVN *phase, PhiNode *phi) {
// This optimization tries to find two or more inputs of phi with the same constant value
// It then splits them into a separate Phi, and according Region. If this is a loop-entry,
// and the loop entry has multiple fall-in edges, and some of those fall-in edges have that
// constant, and others not, we may split the fall-in edges into separate Phi's, and create
// an irreducible loop. For reducible loops, this never seems to happen, as the multiple
// fall-in edges are already merged before the loop head during parsing. But with irreducible
// loops present the order or merging during parsing can sometimes prevent this.
if (phase->C->has_irreducible_loop()) {
// Avoid this optimization if any irreducible loops are present. Else we may create
// an irreducible loop that we do not detect.
return nullptr;
}
BasicType bt = phi->type()->basic_type();
if( bt == T_ILLEGAL || type2size[bt] <= 0 )
return NULL; // Bail out on funny non-value stuff
@ -1894,6 +1966,31 @@ bool PhiNode::wait_for_region_igvn(PhaseGVN* phase) {
return delay;
}
// If the Phi's Region is in an irreducible loop, and the Region
// has had an input removed, but not yet transformed, it could be
// that the Region (and this Phi) are not reachable from Root.
// If we allow the Phi to collapse before the Region, this may lead
// to dead-loop data. Wait for the Region to check for reachability,
// and potentially remove the dead code.
bool PhiNode::must_wait_for_region_in_irreducible_loop(PhaseGVN* phase) const {
RegionNode* region = in(0)->as_Region();
if (region->loop_status() == RegionNode::LoopStatus::MaybeIrreducibleEntry) {
Node* top = phase->C->top();
for (uint j = 1; j < req(); j++) {
Node* rc = region->in(j); // for each control input
if (rc == nullptr || phase->type(rc) == Type::TOP) {
// Region is missing a control input
Node* n = in(j);
if (n != nullptr && n != top) {
// Phi still has its input, so region just lost its input
return true;
}
}
}
}
return false;
}
//------------------------------Ideal------------------------------------------
// Return a node which is more "ideal" than the current node. Must preserve
// the CFG, but we can still strip out dead paths.
@ -1913,6 +2010,10 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
if (new_phi && can_reshape)
return NULL;
if (must_wait_for_region_in_irreducible_loop(phase)) {
return nullptr;
}
// The are 2 situations when only one valid phi's input is left
// (in addition to Region input).
// One: region is not loop - replace phi with this input.
@ -2030,7 +2131,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
debug_only(Node* ident = Identity(phase));
// The unique input must eventually be detected by the Identity call.
#ifdef ASSERT
if (ident != uin && !ident->is_top()) {
if (ident != uin && !ident->is_top() && !must_wait_for_region_in_irreducible_loop(phase)) {
// print this output before failing assert
r->dump(3);
this->dump(3);
@ -2038,7 +2139,8 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
uin->dump();
}
#endif
assert(ident == uin || ident->is_top(), "Identity must clean this up");
// Identity may not return the expected uin, if it has to wait for the region, in irreducible case
assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up");
return NULL;
}
@ -2492,7 +2594,11 @@ Node* PhiNode::merge_through_phi(Node* root_phi, PhaseIterGVN* igvn) {
stack.pop();
}
}
assert(cached_vbox != NULL, "sanity");
if (cached_vbox == nullptr) {
// We have a Phi dead-loop (no data-input). Phi nodes are considered safe,
// so just avoid this optimization.
return nullptr;
}
const TypeInstPtr* btype = cached_vbox->box_type();
const TypeVect* vtype = cached_vbox->vec_type();
Node* new_vbox_phi = clone_through_phi(root_phi, btype, VectorBoxNode::Box, igvn);
@ -2733,6 +2839,10 @@ void CatchProjNode::dump_spec(outputStream *st) const {
Node* CreateExNode::Identity(PhaseGVN* phase) {
if( phase->type(in(1)) == Type::TOP ) return in(1);
if( phase->type(in(0)) == Type::TOP ) return in(0);
if (phase->type(in(0)->in(0)) == Type::TOP) {
assert(in(0)->is_CatchProj(), "control is CatchProj");
return phase->C->top(); // dead code
}
// We only come from CatchProj, unless the CatchProj goes away.
// If the CatchProj is optimized away, then we just carry the
// exception oop through.

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -65,8 +65,21 @@ class PhaseIdealLoop;
// correspond 1-to-1 with RegionNode inputs. The zero input of a PhiNode is
// the RegionNode, and the zero input of the RegionNode is itself.
class RegionNode : public Node {
public:
enum LoopStatus {
// No guarantee: the region may be an irreducible loop entry, thus we have to
// be careful when removing entry control to it.
MaybeIrreducibleEntry,
// Limited guarantee: this region may be (nested) inside an irreducible loop,
// but it will never be an irreducible loop entry.
NeverIrreducibleEntry,
// Strong guarantee: this region is not (nested) inside an irreducible loop.
Reducible,
};
private:
bool _is_unreachable_region;
LoopStatus _loop_status;
bool is_possible_unsafe_loop(const PhaseGVN* phase) const;
bool is_unreachable_from_root(const PhaseGVN* phase) const;
@ -76,7 +89,11 @@ public:
Control // Control arcs are [1..len)
};
RegionNode(uint required) : Node(required), _is_unreachable_region(false) {
RegionNode(uint required)
: Node(required),
_is_unreachable_region(false),
_loop_status(LoopStatus::NeverIrreducibleEntry)
{
init_class_id(Class_Region);
init_req(0, this);
}
@ -95,6 +112,10 @@ public:
bool is_in_infinite_subgraph();
static bool are_all_nodes_in_infinite_subgraph(Unique_Node_List& worklist);
#endif //ASSERT
LoopStatus loop_status() const { return _loop_status; };
void set_loop_status(LoopStatus status);
DEBUG_ONLY(void verify_can_be_irreducible_entry() const;)
virtual int Opcode() const;
virtual uint size_of() const { return sizeof(*this); }
virtual bool pinned() const { return (const Node*)in(0) == this; }
@ -105,9 +126,11 @@ public:
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node* Identity(PhaseGVN* phase);
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
void remove_unreachable_subgraph(PhaseIterGVN* igvn);
virtual const RegMask &out_RegMask() const;
bool try_clean_mem_phi(PhaseGVN* phase);
bool optimize_trichotomy(PhaseIterGVN* igvn);
NOT_PRODUCT(virtual void dump_spec(outputStream* st) const;)
};
//------------------------------JProjNode--------------------------------------
@ -151,6 +174,8 @@ class PhiNode : public TypeNode {
static Node* clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIterGVN* igvn);
static Node* merge_through_phi(Node* root_phi, PhaseIterGVN* igvn);
bool must_wait_for_region_in_irreducible_loop(PhaseGVN* phase) const;
public:
// Node layout (parallels RegionNode):
enum { Region, // Control input is the Phi's region.

@ -63,6 +63,7 @@ bool Node::is_cloop_ind_var() const {
// Dump special per-node info
#ifndef PRODUCT
void LoopNode::dump_spec(outputStream *st) const {
RegionNode::dump_spec(st);
if (is_inner_loop()) st->print( "inner " );
if (is_partial_peel_loop()) st->print( "partial_peel " );
if (partial_peel_has_failed()) st->print( "partial_peel_failed " );
@ -3137,8 +3138,10 @@ void IdealLoopTree::split_fall_in( PhaseIdealLoop *phase, int fall_in_cnt ) {
uint i;
// Make a new RegionNode to be the landing pad.
Node *landing_pad = new RegionNode( fall_in_cnt+1 );
RegionNode* landing_pad = new RegionNode(fall_in_cnt + 1);
phase->set_loop(landing_pad,_parent);
// If _head was irreducible loop entry, landing_pad may now be too
landing_pad->set_loop_status(_head->as_Region()->loop_status());
// Gather all the fall-in control paths into the landing pad
uint icnt = fall_in_cnt;
uint oreq = _head->req();
@ -4626,10 +4629,6 @@ void PhaseIdealLoop::build_and_optimize() {
}
}
}
// disable assert until issue with split_flow_path is resolved (6742111)
// assert(!_has_irreducible_loops || C->parsed_irreducible_loop() || C->is_osr_compilation(),
// "shouldn't introduce irreducible loops");
}
#ifndef PRODUCT
@ -5055,6 +5054,7 @@ void PhaseIdealLoop::build_loop_tree() {
(void)bltstack.pop(); // Remove post-visited node from stack
}
}
DEBUG_ONLY(verify_regions_in_irreducible_loops();)
}
//------------------------------build_loop_tree_impl---------------------------
@ -5149,21 +5149,34 @@ int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) {
set_loop(C->root(), _ltree_root);
}
}
// Weeny check for irreducible. This child was already visited (this
// IS the post-work phase). Is this child's loop header post-visited
// as well? If so, then I found another entry into the loop.
if (!_verify_only) {
if (is_postvisited(l->_head)) {
// We are currently visiting l, but its head has already been post-visited.
// l is irreducible: we just found a second entry m.
_has_irreducible_loops = true;
RegionNode* secondary_entry = m->as_Region();
DEBUG_ONLY(secondary_entry->verify_can_be_irreducible_entry();)
// Walk up the loop-tree, mark all loops that are already post-visited as irreducible
// Since m is a secondary entry to them all.
while( is_postvisited(l->_head) ) {
// found irreducible
l->_irreducible = 1; // = true
RegionNode* head = l->_head->as_Region();
DEBUG_ONLY(head->verify_can_be_irreducible_entry();)
l = l->_parent;
_has_irreducible_loops = true;
// Check for bad CFG here to prevent crash, and bailout of compile
if (l == NULL) {
#ifndef PRODUCT
if (TraceLoopOpts) {
tty->print_cr("bailout: unhandled CFG: infinite irreducible loop");
m->dump();
}
#endif
C->record_method_not_compilable("unhandled CFG detected during loop optimization");
return pre_order;
}
}
}
if (!_verify_only) {
C->set_has_irreducible_loop(_has_irreducible_loops);
}
@ -5231,6 +5244,74 @@ int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) {
return pre_order;
}
#ifdef ASSERT
//--------------------------verify_regions_in_irreducible_loops----------------
// Iterate down from Root through CFG, verify for every region:
// if it is in an irreducible loop it must be marked as such
void PhaseIdealLoop::verify_regions_in_irreducible_loops() {
ResourceMark rm;
if (!_has_irreducible_loops) {
// last build_loop_tree has not found any irreducible loops
// hence no region has to be marked is_in_irreduible_loop
return;
}
RootNode* root = C->root();
Unique_Node_List worklist; // visit all nodes once
worklist.push(root);
bool failure = false;
for (uint i = 0; i < worklist.size(); i++) {
Node* n = worklist.at(i);
if (n->is_Region()) {
RegionNode* region = n->as_Region();
if (is_in_irreducible_loop(region) &&
region->loop_status() == RegionNode::LoopStatus::Reducible) {
failure = true;
tty->print("irreducible! ");
region->dump();
}
}
for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) {
Node* use = n->fast_out(j);
if (use->is_CFG()) {
worklist.push(use); // push if was not pushed before
}
}
}
assert(!failure, "region in irreducible loop was marked as reducible");
}
//---------------------------is_in_irreducible_loop-------------------------
// Analogous to ciTypeFlow::Block::is_in_irreducible_loop
bool PhaseIdealLoop::is_in_irreducible_loop(RegionNode* region) {
if (!_has_irreducible_loops) {
return false; // no irreducible loop in graph
}
IdealLoopTree* l = get_loop(region); // l: innermost loop that contains region
do {
if (l->_irreducible) {
return true; // found it
}
if (l == _ltree_root) {
return false; // reached root, terimnate
}
l = l->_parent;
} while (l != nullptr);
assert(region->is_in_infinite_subgraph(), "must be in infinite subgraph");
// We have "l->_parent == nullptr", which happens only for infinite loops,
// where no parent is attached to the loop. We did not find any irreducible
// loop from this block out to lp. Thus lp only has one entry, and no exit
// (it is infinite and reducible). We can always rewrite an infinite loop
// that is nested inside other loops:
// while(condition) { infinite_loop; }
// with an equivalent program where the infinite loop is an outermost loop
// that is not nested in any loop:
// while(condition) { break; } infinite_loop;
// Thus, we can understand lp as an outermost loop, and can terminate and
// conclude: this block is in no irreducible loop.
return false;
}
#endif
//------------------------------build_loop_early-------------------------------
// Put Data nodes into some loop nest, by setting the _nodes[]->loop mapping.

@ -1072,6 +1072,12 @@ private:
// loop tree, not the root.
IdealLoopTree *sort( IdealLoopTree *loop, IdealLoopTree *innermost );
#ifdef ASSERT
// verify that regions in irreducible loops are marked is_in_irreducible_loop
void verify_regions_in_irreducible_loops();
bool is_in_irreducible_loop(RegionNode* region);
#endif
// Place Data nodes in some loop nest
void build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack );
void build_loop_late ( VectorSet &visited, Node_List &worklist, Node_Stack &nstack );

@ -187,6 +187,8 @@ Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oo
break;
}
result = proj_in->in(TypeFunc::Memory);
} else if (proj_in->is_top()) {
break; // dead code
} else {
assert(false, "unexpected projection");
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -222,6 +222,29 @@ class Parse : public GraphKit {
int start_sp() const { return flow()->stack_size(); }
bool is_loop_head() const { return flow()->is_loop_head(); }
bool is_in_irreducible_loop() const {
return flow()->is_in_irreducible_loop();
}
bool is_irreducible_loop_entry() const {
return flow()->is_irreducible_loop_head() || flow()->is_irreducible_loop_secondary_entry();
}
void copy_irreducible_status_to(RegionNode* region, const JVMState* jvms) {
assert(!is_irreducible_loop_entry() || is_in_irreducible_loop(), "entry is part of irreducible loop");
if (is_in_irreducible_loop()) {
// The block is in an irreducible loop of this method, so it is possible that this
// region becomes an irreducible loop entry. (no guarantee)
region->set_loop_status(RegionNode::LoopStatus::MaybeIrreducibleEntry);
} else if (jvms->caller() != nullptr) {
// The block is not in an irreducible loop of this method, hence it cannot ever
// be the entry of an irreducible loop. But it may be inside an irreducible loop
// of a caller of this inlined method. (limited guarantee)
assert(region->loop_status() == RegionNode::LoopStatus::NeverIrreducibleEntry, "status not changed");
} else {
// The block is not in an irreducible loop of this method, and there is no outer
// method. This region will never be in an irreducible loop (strong guarantee)
region->set_loop_status(RegionNode::LoopStatus::Reducible);
}
}
bool is_SEL_head() const { return flow()->is_single_entry_loop_head(); }
bool is_SEL_backedge(Block* pred) const{ return is_SEL_head() && pred->rpo() >= rpo(); }
bool is_invariant_local(uint i) const {

@ -678,6 +678,7 @@ void Parse::do_all_blocks() {
record_for_igvn(r);
r->init_req(edges, control());
set_control(r);
block->copy_irreducible_status_to(r, jvms());
// Add new phis.
ensure_phis_everywhere();
}
@ -1499,7 +1500,12 @@ void Parse::do_one_block() {
for (int i = 0; i < nt; i++) {
tty->print((( i < ns) ? " %d" : " %d(e)"), b->successor_at(i)->rpo());
}
if (b->is_loop_head()) tty->print(" lphd");
if (b->is_loop_head()) {
tty->print(" lphd");
}
if (b->is_irreducible_loop_entry()) {
tty->print(" irreducible");
}
tty->cr();
}
@ -1688,6 +1694,7 @@ void Parse::merge_common(Parse::Block* target, int pnum) {
// for (int j = 1; j < edges+1; j++) { r->init_req(j, NULL); }
r->init_req(pnum, control());
set_control(r);
target->copy_irreducible_status_to(r, jvms());
set_parse_bci(current_bci); // Restore bci
}
@ -1725,7 +1732,7 @@ void Parse::merge_common(Parse::Block* target, int pnum) {
r->init_req(pnum, newin->control());
if (pnum == 1) { // Last merge for this Region?
if (!block()->flow()->is_irreducible_entry()) {
if (!block()->flow()->is_irreducible_loop_secondary_entry()) {
Node* result = _gvn.transform_no_reclaim(r);
if (r != result && TraceOptoParse) {
tty->print_cr("Block #%d replace %d with %d", block()->rpo(), r->_idx, result->_idx);

@ -939,7 +939,7 @@ void PhaseGVN::dead_loop_check( Node *n ) {
}
}
}
if (!no_dead_loop) n->dump(3);
if (!no_dead_loop) n->dump_bfs(100,0,"#");
assert(no_dead_loop, "dead loop detected");
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,235 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/*
* @test
* @bug 8280126
* @compile TestDeadIrreducibleLoops.jasm
* @summary Irreducible loops have many entries, only when the last entry loses
* control from the outside does the loop die, and have to disappear.
* @run main/othervm
* -XX:CompileCommand=compileonly,TestDeadIrreducibleLoopsMain::test*
* -XX:CompileCommand=compileonly,TestDeadIrreducibleLoops::test*
* -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
* TestDeadIrreducibleLoopsMain
*/
/*
* @test
* @bug 8280126
* @compile TestDeadIrreducibleLoops.jasm
* @summary Irreducible loops have many entries, only when the last entry loses
* control from the outside does the loop die, and have to disappear.
* @run main/othervm
* -XX:CompileCommand=compileonly,TestDeadIrreducibleLoopsMain::test*
* -XX:CompileCommand=compileonly,TestDeadIrreducibleLoops::test*
* -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
* -XX:PerMethodTrapLimit=0
* TestDeadIrreducibleLoopsMain
*/
/*
* @test
* @bug 8280126
* @compile TestDeadIrreducibleLoops.jasm
* @summary Irreducible loops have many entries, only when the last entry loses
* control from the outside does the loop die, and have to disappear.
* @run main/othervm
* -XX:CompileCommand=compileonly,TestDeadIrreducibleLoopsMain::test*
* -XX:CompileCommand=compileonly,TestDeadIrreducibleLoops::test*
* -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
* -Xcomp -XX:-TieredCompilation
* TestDeadIrreducibleLoopsMain
*/
/*
* @test
* @bug 8280126
* @compile TestDeadIrreducibleLoops.jasm
* @summary Irreducible loops have many entries, only when the last entry loses
* control from the outside does the loop die, and have to disappear.
* @run main/othervm
* -XX:CompileCommand=compileonly,TestDeadIrreducibleLoopsMain::test*
* -XX:CompileCommand=compileonly,TestDeadIrreducibleLoops::test*
* -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
* -Xcomp -XX:-TieredCompilation
* -XX:PerMethodTrapLimit=0
* TestDeadIrreducibleLoopsMain
*/
// Note: if this test fails intermittently, then use -XX:RepeatCompilation=1000
// The tests are run in no particular order. If an earlier test fails, a later one
// may fail too and be easier to debug.
public class TestDeadIrreducibleLoopsMain {
static public void main(String[] args) {
TestDeadIrreducibleLoops t = new TestDeadIrreducibleLoops();
test_000(false, false);
t.test_001(0, 0, 0, 0);
t.test_002(-1);
t.test_003(255);
t.test_004("I am an object\n");
t.test_005(0, 0);
t.test_006(0);
t.test_007(0, 0, 0);
t.test_008(0, 0, 0);
t.test_009(0, 0, 0, 0, 0, 0, 0);
t.test_010(0, 0, 0, 0, 0);
t.test_011(0, 0, 0, 0, 0);
t.test_012a(0, 0, 0, 0, 0, 0, 0, 0);
t.test_012b(0, 0, 0, 0, 0);
t.test_013(0, 0, 0, 0, 0, 0, 0, 0, 0);
t.test_014a(0, 0, 0);
t.test_014b(0, 0, 0);
int x = t.test_015a(123);
int y = t.test_015b(123);
assert x == y: "pow(3,x)";
t.test_016(0, 0, 0);
t.test_017(0, 0, 0);
t.test_018(0);
t.test_019(0, 0, 0, 0);
t.test_020(0, 0, 0, 0, 0);
t.test_021(0, 0, 0, 0, 0, 0);
t.test_022a(0, 0, 0, 0);
t.test_022b(0, 0);
t.test_023(0);
t.test_024();
test_025a(false);
test_025b(false, false);
}
public static float test_000(boolean flag1, boolean flag2) {
float ret = 1.0f;
int x = 0;
LOOP1:
for (int i = 1; i < 1000000; i *= 2) { // about 20 iterations
if (i % 5 != 0) { // SKIP1
LOOP2:
for (int j = 1; j < 1000000; j *= 2) { // about 20 iterations
if (j % 5 != 0) { // SKIP2
if (x == 0) { // eventually always false -> continue statements float out of loop
ret *= 1.0001;
if (j > 100) {
LOOP3:
for (float m = 1.0f; m < 30000.0f; m *= 1.0001f) {
// OSR starts here - should do more than 100k iterations
ret *= 0.99999f;
}
x = 1;
}
int y = 77;
for (int e = 0; e < 77; e++) {
y -= x; // empty_loop, once we know that x == 1
}
if (y == 0) {
// always true after OSR -> cut off ENTRY1 and ENTRY2
return ret;
}
ret += 0.01;
if (ret > 20000) {
ret = 7.0f;
continue LOOP1; // ENTRY1
}
// back to LOOP2 -> ENTRY2
} // end if (x == 0)
} // end SKIP2
} // end LOOP2
} // end SKIP1
} // end LOOP1
return ret;
}
static float test_025a(boolean flag) {
// Based on test_000, but much simplified.
// Irreducible loop with OSR. Inlining in irreducible loop.
float ret = 3.0f;
LOOP1:
for (long i = 1; i < 1000_000_000_000L; i *= 2) {
ret = test_025_inline(ret); // inline region
LOOP2:
for (long j = 1; j < 1000_000_000_000L; j *= 2) {
for (int e = 0; e < 77; e++) {}
if (flag) {
continue LOOP1; // ENTRY1
}
// back to LOOP2 -> ENTRY2
} // end LOOP2
} // end LOOP1
return ret;
}
static float test_025b(boolean flag1, boolean flag2) {
// Based on test_000.
// Irreducible loop with OSR. Inlining in irreducible loop.
float ret = 1.0f;
int x = 0;
LOOP1:
for (long i = 1; i < 1000_000_000_000L; i *= 2) {
ret = test_025_inline(ret);
if (i % 5 != 0) { // SKIP1
LOOP2:
for (long j = 1; j < 1000_000_000_000L; j *= 2) {
if (j % 5 != 0) { // SKIP2
if (x == 0) { // eventually always false -> continue statements float out of loop
ret *= 1.0001;
if (i > 1000_000_000L) {
LOOP3:
for (float m = 1.0f; m < 30000.0f; m *= 1.0001f) {
// OSR starts here - should do more than 100k iterations
ret *= 0.99999f;
}
x = 1;
}
int y = 77;
for (int e = 0; e < 77; e++) {
y -= x; // empty_loop, once we know that x == 1
}
if (y == 0) {
// always true after OSR -> cut off ENTRY1 and ENTRY2
return ret;
}
ret += 0.01;
if (ret > 20000) {
ret = 7.0f;
continue LOOP1; // ENTRY1
}
// back to LOOP2 -> ENTRY2
} // end if (x == 0)
} // end SKIP2
} // end LOOP2
} // end SKIP1
} // end LOOP1
return ret;
}
static float test_025_inline(float x) {
if (x >= 1.0f) {
x *= 0.5f;
} else {
x *= 2.0f;
}
// Region to merge the if
return x;
}
}