Skip to content
This repository was archived by the owner on Sep 19, 2023. It is now read-only.
/ jdk19u Public archive

Commit 4708c7b

Browse files
committedNov 23, 2022
8287217: C2: PhaseCCP: remove not visited nodes, prevent type inconsistency
Reviewed-by: kvn Backport-of: 379f3094db0b8afe90ed6b7a341164222744085f
1 parent 71b38e2 commit 4708c7b

File tree

5 files changed

+100
-21
lines changed

5 files changed

+100
-21
lines changed
 

‎src/hotspot/share/opto/compile.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ void Compile::remove_useless_node(Node* dead) {
402402
}
403403

404404
// Disconnect all useless nodes by disconnecting those at the boundary.
405-
void Compile::remove_useless_nodes(Unique_Node_List &useful) {
405+
void Compile::disconnect_useless_nodes(Unique_Node_List &useful, Unique_Node_List* worklist) {
406406
uint next = 0;
407407
while (next < useful.size()) {
408408
Node *n = useful.at(next++);
@@ -425,7 +425,7 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) {
425425
}
426426
}
427427
if (n->outcnt() == 1 && n->has_special_unique_user()) {
428-
record_for_igvn(n->unique_out());
428+
worklist->push(n->unique_out());
429429
}
430430
}
431431

@@ -435,6 +435,11 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) {
435435
remove_useless_nodes(_expensive_nodes, useful); // remove useless expensive nodes
436436
remove_useless_nodes(_for_post_loop_igvn, useful); // remove useless node recorded for post loop opts IGVN pass
437437
remove_useless_coarsened_locks(useful); // remove useless coarsened locks nodes
438+
#ifdef ASSERT
439+
if (_modified_nodes != NULL) {
440+
_modified_nodes->remove_useless_nodes(useful.member_set());
441+
}
442+
#endif
438443

439444
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
440445
bs->eliminate_useless_gc_barriers(useful, this);

‎src/hotspot/share/opto/compile.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ class Compile : public Phase {
945945

946946
void identify_useful_nodes(Unique_Node_List &useful);
947947
void update_dead_node_list(Unique_Node_List &useful);
948-
void remove_useless_nodes (Unique_Node_List &useful);
948+
void disconnect_useless_nodes(Unique_Node_List &useful, Unique_Node_List* worklist);
949949

950950
void remove_useless_node(Node* dead);
951951

‎src/hotspot/share/opto/phaseX.cpp

+43-17
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ PhaseRemoveUseless::PhaseRemoveUseless(PhaseGVN* gvn, Unique_Node_List* worklist
423423
worklist->remove_useless_nodes(_useful.member_set());
424424

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

429429
//=============================================================================
@@ -1764,6 +1764,9 @@ void PhaseCCP::analyze() {
17641764
Unique_Node_List worklist;
17651765
worklist.push(C->root());
17661766

1767+
assert(_root_and_safepoints.size() == 0, "must be empty (unused)");
1768+
_root_and_safepoints.push(C->root());
1769+
17671770
// Pull from worklist; compute new value; push changes out.
17681771
// This loop is the meat of CCP.
17691772
while( worklist.size() ) {
@@ -1774,8 +1777,9 @@ void PhaseCCP::analyze() {
17741777
n = worklist.pop();
17751778
}
17761779
if (n->is_SafePoint()) {
1777-
// Keep track of SafePoint nodes for PhaseCCP::transform()
1778-
_safepoints.push(n);
1780+
// Make sure safepoints are processed by PhaseCCP::transform even if they are
1781+
// not reachable from the bottom. Otherwise, infinite loops would be removed.
1782+
_root_and_safepoints.push(n);
17791783
}
17801784
const Type *t = n->Value(this);
17811785
if (t != type(n)) {
@@ -1904,32 +1908,34 @@ Node *PhaseCCP::transform( Node *n ) {
19041908
Node *new_node = _nodes[n->_idx]; // Check for transformed node
19051909
if( new_node != NULL )
19061910
return new_node; // Been there, done that, return old answer
1907-
new_node = transform_once(n); // Check for constant
1908-
_nodes.map( n->_idx, new_node ); // Flag as having been cloned
19091911

1910-
// Allocate stack of size _nodes.Size()/2 to avoid frequent realloc
1911-
GrowableArray <Node *> trstack(C->live_nodes() >> 1);
1912+
assert(n->is_Root(), "traversal must start at root");
1913+
assert(_root_and_safepoints.member(n), "root (n) must be in list");
19121914

1913-
trstack.push(new_node); // Process children of cloned node
1915+
// Allocate stack of size _nodes.Size()/2 to avoid frequent realloc
1916+
GrowableArray <Node *> transform_stack(C->live_nodes() >> 1);
1917+
Unique_Node_List useful; // track all visited nodes, so that we can remove the complement
19141918

1919+
// Initialize the traversal.
19151920
// This CCP pass may prove that no exit test for a loop ever succeeds (i.e. the loop is infinite). In that case,
19161921
// the logic below doesn't follow any path from Root to the loop body: there's at least one such path but it's proven
19171922
// never taken (its type is TOP). As a consequence the node on the exit path that's input to Root (let's call it n) is
19181923
// replaced by the top node and the inputs of that node n are not enqueued for further processing. If CCP only works
19191924
// through the graph from Root, this causes the loop body to never be processed here even when it's not dead (that
19201925
// is reachable from Root following its uses). To prevent that issue, transform() starts walking the graph from Root
19211926
// and all safepoints.
1922-
for (uint i = 0; i < _safepoints.size(); ++i) {
1923-
Node* nn = _safepoints.at(i);
1927+
for (uint i = 0; i < _root_and_safepoints.size(); ++i) {
1928+
Node* nn = _root_and_safepoints.at(i);
19241929
Node* new_node = _nodes[nn->_idx];
19251930
assert(new_node == NULL, "");
1926-
new_node = transform_once(nn);
1927-
_nodes.map(nn->_idx, new_node);
1928-
trstack.push(new_node);
1931+
new_node = transform_once(nn); // Check for constant
1932+
_nodes.map(nn->_idx, new_node); // Flag as having been cloned
1933+
transform_stack.push(new_node); // Process children of cloned node
1934+
useful.push(new_node);
19291935
}
19301936

1931-
while ( trstack.is_nonempty() ) {
1932-
Node *clone = trstack.pop();
1937+
while (transform_stack.is_nonempty()) {
1938+
Node* clone = transform_stack.pop();
19331939
uint cnt = clone->req();
19341940
for( uint i = 0; i < cnt; i++ ) { // For all inputs do
19351941
Node *input = clone->in(i);
@@ -1938,13 +1944,33 @@ Node *PhaseCCP::transform( Node *n ) {
19381944
if( new_input == NULL ) {
19391945
new_input = transform_once(input); // Check for constant
19401946
_nodes.map( input->_idx, new_input );// Flag as having been cloned
1941-
trstack.push(new_input);
1947+
transform_stack.push(new_input); // Process children of cloned node
1948+
useful.push(new_input);
19421949
}
19431950
assert( new_input == clone->in(i), "insanity check");
19441951
}
19451952
}
19461953
}
1947-
return new_node;
1954+
1955+
// The above transformation might lead to subgraphs becoming unreachable from the
1956+
// bottom while still being reachable from the top. As a result, nodes in that
1957+
// subgraph are not transformed and their bottom types are not updated, leading to
1958+
// an inconsistency between bottom_type() and type(). In rare cases, LoadNodes in
1959+
// such a subgraph, might be re-enqueued for IGVN indefinitely by MemNode::Ideal_common
1960+
// because their address type is inconsistent. Therefore, we aggressively remove
1961+
// all useless nodes here even before PhaseIdealLoop::build_loop_late gets a chance
1962+
// to remove them anyway.
1963+
if (C->cached_top_node()) {
1964+
useful.push(C->cached_top_node());
1965+
}
1966+
C->update_dead_node_list(useful);
1967+
remove_useless_nodes(useful.member_set());
1968+
_worklist.remove_useless_nodes(useful.member_set());
1969+
C->disconnect_useless_nodes(useful, &_worklist);
1970+
1971+
Node* new_root = _nodes[n->_idx];
1972+
assert(new_root->is_Root(), "transformed root node must be a root node");
1973+
return new_root;
19481974
}
19491975

19501976

‎src/hotspot/share/opto/phaseX.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ class PhaseIterGVN : public PhaseGVN {
565565
// Phase for performing global Conditional Constant Propagation.
566566
// Should be replaced with combined CCP & GVN someday.
567567
class PhaseCCP : public PhaseIterGVN {
568-
Unique_Node_List _safepoints;
568+
Unique_Node_List _root_and_safepoints;
569569
// Non-recursive. Use analysis to transform single Node.
570570
virtual Node *transform_once( Node *n );
571571

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8287217
27+
* @summary CCP must remove nodes that are not traversed, else their type can be inconsistent
28+
* @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestRemoveUnreachableCCP::test
29+
* TestRemoveUnreachableCCP
30+
*/
31+
32+
public class TestRemoveUnreachableCCP {
33+
34+
static void test() {
35+
Byte x = 1;
36+
for (int i = 0; i < 10000; i++) {
37+
if ((i & 1) == 0) {
38+
x = (byte)x;
39+
}
40+
}
41+
}
42+
43+
public static void main(String[] strArr) {
44+
for (int i = 0; i < 11; i++) {
45+
test();
46+
}
47+
}
48+
}

1 commit comments

Comments
 (1)

openjdk-notifier[bot] commented on Nov 23, 2022

@openjdk-notifier[bot]
This repository has been archived.