Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8265973: [lworld] C2 compilation fails due to infinite loop in PhaseIterGVN::optimize #397

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -411,7 +411,7 @@ void Compile::remove_useless_node(Node* dead) {
}

// Disconnect all useless nodes by disconnecting those at the boundary.
void Compile::remove_useless_nodes(Unique_Node_List &useful) {
void Compile::disconnect_useless_nodes(Unique_Node_List &useful, Unique_Node_List* worklist) {
uint next = 0;
while (next < useful.size()) {
Node *n = useful.at(next++);
@@ -434,14 +434,21 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) {
}
}
if (n->outcnt() == 1 && n->has_special_unique_user()) {
record_for_igvn(n->unique_out());
worklist->push(n->unique_out());
}
}

remove_useless_nodes(_macro_nodes, useful); // remove useless macro and predicate opaq nodes
remove_useless_nodes(_macro_nodes, useful); // remove useless macro nodes
remove_useless_nodes(_predicate_opaqs, useful); // remove useless predicate opaque nodes
remove_useless_nodes(_skeleton_predicate_opaqs, useful);
remove_useless_nodes(_expensive_nodes, useful); // remove useless expensive nodes
remove_useless_nodes(_for_post_loop_igvn, useful); // remove useless node recorded for post loop opts IGVN pass
remove_useless_nodes(_inline_type_nodes, useful); // remove useless inline type nodes
#ifdef ASSERT
if (_modified_nodes != NULL) {
_modified_nodes->remove_useless_nodes(useful.member_set());
}
#endif

BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
bs->eliminate_useless_gc_barriers(useful, this);
@@ -943,7 +943,7 @@ class Compile : public Phase {

void identify_useful_nodes(Unique_Node_List &useful);
void update_dead_node_list(Unique_Node_List &useful);
void remove_useless_nodes (Unique_Node_List &useful);
void disconnect_useless_nodes(Unique_Node_List &useful, Unique_Node_List* worklist);

void remove_useless_node(Node* dead);

@@ -506,6 +506,18 @@ Node* InlineTypeBaseNode::Ideal(PhaseGVN* phase, bool can_reshape) {
if (phase->C->scalarize_in_safepoints() && can_reshape) {
PhaseIterGVN* igvn = phase->is_IterGVN();
make_scalar_in_safepoints(igvn);
if (outcnt() == 0) {
return NULL;
}
}
Node* oop = get_oop();
if (oop->isa_InlineTypePtr()) {
InlineTypePtrNode* vtptr = oop->as_InlineTypePtr();
set_oop(vtptr->get_oop());
for (uint i = Values; i < vtptr->req(); ++i) {
set_req(i, vtptr->in(i));
}
return this;
}
return NULL;
}
@@ -836,14 +848,6 @@ Node* InlineTypeNode::Ideal(PhaseGVN* phase, bool can_reshape) {
set_oop(default_oop(*phase, inline_klass()));
assert(is_allocated(phase), "should now be allocated");
return this;
} else if (oop->isa_InlineTypePtr()) {
// Can happen with late inlining
InlineTypePtrNode* vtptr = oop->as_InlineTypePtr();
set_oop(vtptr->get_oop());
for (uint i = Oop+1; i < vtptr->req(); ++i) {
set_req(i, vtptr->in(i));
}
return this;
}

if (!is_allocated(phase)) {
@@ -872,7 +876,7 @@ Node* InlineTypeNode::Ideal(PhaseGVN* phase, bool can_reshape) {
Node* res = alloc->result_cast();
if (res != NULL && res->is_CheckCastPP()) {
// Replace allocation by oop and unlink AllocateNode
replace_allocation(igvn, res, get_oop());
replace_allocation(igvn, res, oop);
igvn->replace_input_of(alloc, AllocateNode::InlineTypeNode, igvn->C->top());
--i; --imax;
}
@@ -425,7 +425,7 @@ PhaseRemoveUseless::PhaseRemoveUseless(PhaseGVN* gvn, Unique_Node_List* worklist
worklist->remove_useless_nodes(_useful.member_set());

// Disconnect 'useless' nodes that are adjacent to useful nodes
C->remove_useless_nodes(_useful);
C->disconnect_useless_nodes(_useful, worklist);
}

//=============================================================================
@@ -1756,7 +1756,7 @@ uint PhaseCCP::_total_constants = 0;
#endif
//------------------------------PhaseCCP---------------------------------------
// Conditional Constant Propagation, ala Wegman & Zadeck
PhaseCCP::PhaseCCP( PhaseIterGVN *igvn ) : PhaseIterGVN(igvn) {
PhaseCCP::PhaseCCP(PhaseIterGVN* igvn) : PhaseIterGVN(igvn), _trstack(C->live_nodes() >> 1) {
NOT_PRODUCT( clear_constants(); )
assert( _worklist.size() == 0, "" );
// Clear out _nodes from IterGVN. Must be clear to transform call.
@@ -1810,6 +1810,11 @@ void PhaseCCP::analyze() {
} else {
n = worklist.pop();
}
if (n->is_SafePoint()) {
// Make sure safepoints are processed by PhaseCCP::transform even if they are
// not reachable from the bottom. Otherwise, infinite loops would be removed.
_trstack.push(n);
}
const Type *t = n->Value(this);
if (t != type(n)) {
assert(ccp_type_widens(t, type(n)), "ccp type must widen");
@@ -1929,13 +1934,11 @@ Node *PhaseCCP::transform( Node *n ) {
return new_node; // Been there, done that, return old answer
new_node = transform_once(n); // Check for constant
_nodes.map( n->_idx, new_node ); // Flag as having been cloned
_useful.push(new_node); // Keep track of nodes that are reachable from the bottom

// Allocate stack of size _nodes.Size()/2 to avoid frequent realloc
GrowableArray <Node *> trstack(C->live_nodes() >> 1);

trstack.push(new_node); // Process children of cloned node
while ( trstack.is_nonempty() ) {
Node *clone = trstack.pop();
_trstack.push(new_node); // Process children of cloned node
while (_trstack.is_nonempty()) {
Node* clone = _trstack.pop();
uint cnt = clone->req();
for( uint i = 0; i < cnt; i++ ) { // For all inputs do
Node *input = clone->in(i);
@@ -1944,12 +1947,30 @@ Node *PhaseCCP::transform( Node *n ) {
if( new_input == NULL ) {
new_input = transform_once(input); // Check for constant
_nodes.map( input->_idx, new_input );// Flag as having been cloned
trstack.push(new_input);
_useful.push(new_input);
_trstack.push(new_input);
}
assert( new_input == clone->in(i), "insanity check");
}
}
}

// The above transformation might lead to subgraphs becoming unreachable from the
// bottom while still being reachable from the top. As a result, nodes in that
// subgraph are not transformed and their bottom types are not updated, leading to
// an inconsistency between bottom_type() and type(). In rare cases, LoadNodes in
// such a subgraph, kept alive by InlineTypePtrNodes, might be re-enqueued for IGVN
// indefinitely by MemNode::Ideal_common because their address type is inconsistent.
// Therefore, we aggressively remove all useless nodes here even before
// PhaseIdealLoop::build_loop_late gets a chance to remove them anyway.
if (C->cached_top_node()) {
_useful.push(C->cached_top_node());
}
C->update_dead_node_list(_useful);
remove_useless_nodes(_useful.member_set());
_worklist.remove_useless_nodes(_useful.member_set());
C->disconnect_useless_nodes(_useful, &_worklist);

return new_node;
}

@@ -567,6 +567,9 @@ class PhaseIterGVN : public PhaseGVN {
// Phase for performing global Conditional Constant Propagation.
// Should be replaced with combined CCP & GVN someday.
class PhaseCCP : public PhaseIterGVN {
GrowableArray<Node*> _trstack; // Stack for transform operation
Unique_Node_List _useful; // Nodes reachable from the bottom

// Non-recursive. Use analysis to transform single Node.
virtual Node *transform_once( Node *n );

@@ -311,7 +311,7 @@ public void test11_verifier(boolean warmup) {
// Array load out of bounds (upper bound) at compile time
@Test
public int test12() {
int arraySize = Math.abs(rI) % 10;;
int arraySize = Math.abs(rI) % 10;
MyValue1[] va = new MyValue1[arraySize];

for (int i = 0; i < arraySize; i++) {
@@ -333,7 +333,7 @@ public void test12_verifier(boolean warmup) {
// Array load out of bounds (lower bound) at compile time
@Test
public int test13() {
int arraySize = Math.abs(rI) % 10;;
int arraySize = Math.abs(rI) % 10;
MyValue1[] va = new MyValue1[arraySize];

for (int i = 0; i < arraySize; i++) {
@@ -381,7 +381,7 @@ public void test14_verifier(boolean warmup) {
// Array store out of bounds (upper bound) at compile time
@Test
public int test15() {
int arraySize = Math.abs(rI) % 10;;
int arraySize = Math.abs(rI) % 10;
MyValue1[] va = new MyValue1[arraySize];

try {
@@ -402,7 +402,7 @@ public void test15_verifier(boolean warmup) {
// Array store out of bounds (lower bound) at compile time
@Test
public int test16() {
int arraySize = Math.abs(rI) % 10;;
int arraySize = Math.abs(rI) % 10;
MyValue1[] va = new MyValue1[arraySize];

try {
@@ -58,6 +58,14 @@
int c = 8;
}

class MyValue4Wrapper {
public MyValue4.ref val;

public MyValue4Wrapper(MyValue4 val) {
this.val = val;
}
}

primitive class MyValue5 {
int b = 2;
}
@@ -219,6 +227,30 @@ void test14(boolean b, MyValue4 val) {
}
}

void test15() {
MyValue4 val = new MyValue4();
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
MyValue4[] array = new MyValue4[1];
for (int k = 0; k < 10; ++k) {
array[0] = val;
val = array[0];
}
}
}
}

void test16() {
MyValue4 val = MyValue4.default;
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
val = (new MyValue4Wrapper(val)).val;
for (int k = 0; k < 10; ++k) {
}
}
}
}

public static void main(String[] args) {
TestGenerated t = new TestGenerated();
EmptyValue[] array1 = { new EmptyValue() };
@@ -243,6 +275,8 @@ public static void main(String[] args) {
t.test12();
t.test13(array5);
t.test14(false, MyValue4.default);
t.test15();
t.test16();
}
}
}