8280126: C2: detect and remove dead irreducible loops
Reviewed-by: kvn, chagedorn, thartmann
This commit is contained in:
parent
2a579ab839
commit
ac7119f0d5
src/hotspot/share
ci
opto
test/hotspot/jtreg/compiler/loopopts
@ -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");
|
||||
}
|
||||
}
|
||||
|
1496
test/hotspot/jtreg/compiler/loopopts/TestDeadIrreducibleLoops.jasm
Normal file
1496
test/hotspot/jtreg/compiler/loopopts/TestDeadIrreducibleLoops.jasm
Normal file
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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user