Skip to content

Commit f8ba02f

Browse files
committed
8361702: C2: assert(is_dominator(compute_early_ctrl(limit, limit_ctrl), pre_end)) failed: node pinned on loop exit test?
Reviewed-by: epeter, chagedorn, mhaessig
1 parent 60930a3 commit f8ba02f

File tree

4 files changed

+177
-18
lines changed

4 files changed

+177
-18
lines changed

src/hotspot/share/opto/loopnode.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,6 +1859,8 @@ class PhaseIdealLoop : public PhaseTransform {
18591859

18601860
bool ctrl_of_all_uses_out_of_loop(const Node* n, Node* n_ctrl, IdealLoopTree* n_loop);
18611861

1862+
bool would_sink_below_pre_loop_exit(IdealLoopTree* n_loop, Node* ctrl);
1863+
18621864
Node* compute_early_ctrl(Node* n, Node* n_ctrl);
18631865

18641866
void try_sink_out_of_loop(Node* n);

src/hotspot/share/opto/loopopts.cpp

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,18 +1710,20 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) {
17101710
// Rewire control of n to right outside of the loop, regardless if its input(s) are later sunk or not.
17111711
Node* maybe_pinned_n = n;
17121712
Node* outside_ctrl = place_outside_loop(n_ctrl, loop_ctrl);
1713-
if (n->depends_only_on_test()) {
1714-
Node* pinned_clone = n->pin_array_access_node();
1715-
if (pinned_clone != nullptr) {
1716-
// Pin array access nodes: if this is an array load, it's going to be dependent on a condition that's not a
1717-
// range check for that access. If that condition is replaced by an identical dominating one, then an
1718-
// unpinned load would risk floating above its range check.
1719-
register_new_node(pinned_clone, n_ctrl);
1720-
maybe_pinned_n = pinned_clone;
1721-
_igvn.replace_node(n, pinned_clone);
1713+
if (!would_sink_below_pre_loop_exit(loop_ctrl, outside_ctrl)) {
1714+
if (n->depends_only_on_test()) {
1715+
Node* pinned_clone = n->pin_array_access_node();
1716+
if (pinned_clone != nullptr) {
1717+
// Pin array access nodes: if this is an array load, it's going to be dependent on a condition that's not a
1718+
// range check for that access. If that condition is replaced by an identical dominating one, then an
1719+
// unpinned load would risk floating above its range check.
1720+
register_new_node(pinned_clone, n_ctrl);
1721+
maybe_pinned_n = pinned_clone;
1722+
_igvn.replace_node(n, pinned_clone);
1723+
}
17221724
}
1725+
_igvn.replace_input_of(maybe_pinned_n, 0, outside_ctrl);
17231726
}
1724-
_igvn.replace_input_of(maybe_pinned_n, 0, outside_ctrl);
17251727
}
17261728
}
17271729
if (n_loop != _ltree_root && n->outcnt() > 1) {
@@ -1929,6 +1931,21 @@ bool PhaseIdealLoop::ctrl_of_all_uses_out_of_loop(const Node* n, Node* n_ctrl, I
19291931
return true;
19301932
}
19311933

1934+
// Sinking a node from a pre loop to its main loop pins the node between the pre and main loops. If that node is input
1935+
// to a check that's eliminated by range check elimination, it becomes input to an expression that feeds into the exit
1936+
// test of the pre loop above the point in the graph where it's pinned. This results in a broken graph. One way to avoid
1937+
// it would be to not eliminate the check in the main loop. Instead, we prevent sinking of the node here so better code
1938+
// is generated for the main loop.
1939+
bool PhaseIdealLoop::would_sink_below_pre_loop_exit(IdealLoopTree* n_loop, Node* ctrl) {
1940+
if (n_loop->_head->is_CountedLoop() && n_loop->_head->as_CountedLoop()->is_pre_loop()) {
1941+
CountedLoopNode* pre_loop = n_loop->_head->as_CountedLoop();
1942+
if (is_dominator(pre_loop->loopexit(), ctrl)) {
1943+
return true;
1944+
}
1945+
}
1946+
return false;
1947+
}
1948+
19321949
bool PhaseIdealLoop::ctrl_of_use_out_of_loop(const Node* n, Node* n_ctrl, IdealLoopTree* n_loop, Node* ctrl) {
19331950
if (n->is_Load()) {
19341951
ctrl = get_late_ctrl_with_anti_dep(n->as_Load(), n_ctrl, ctrl);
@@ -1940,14 +1957,8 @@ bool PhaseIdealLoop::ctrl_of_use_out_of_loop(const Node* n, Node* n_ctrl, IdealL
19401957
if (n_loop->is_member(u_loop)) {
19411958
return false; // Found use in inner loop
19421959
}
1943-
// Sinking a node from a pre loop to its main loop pins the node between the pre and main loops. If that node is input
1944-
// to a check that's eliminated by range check elimination, it becomes input to an expression that feeds into the exit
1945-
// test of the pre loop above the point in the graph where it's pinned.
1946-
if (n_loop->_head->is_CountedLoop() && n_loop->_head->as_CountedLoop()->is_pre_loop()) {
1947-
CountedLoopNode* pre_loop = n_loop->_head->as_CountedLoop();
1948-
if (is_dominator(pre_loop->loopexit(), ctrl)) {
1949-
return false;
1950-
}
1960+
if (would_sink_below_pre_loop_exit(n_loop, ctrl)) {
1961+
return false;
19511962
}
19521963
return true;
19531964
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2025, Red Hat, Inc. 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 8361702
27+
* @summary C2: assert(is_dominator(compute_early_ctrl(limit, limit_ctrl), pre_end)) failed: node pinned on loop exit test?
28+
*
29+
* @run main/othervm -XX:CompileCommand=compileonly,*TestSunkRangeFromPreLoopRCE2*::* -Xbatch TestSunkRangeFromPreLoopRCE2
30+
* @run main TestSunkRangeFromPreLoopRCE2
31+
*/
32+
33+
public class TestSunkRangeFromPreLoopRCE2 {
34+
static int iFld;
35+
static long lArr[] = new long[400];
36+
37+
public static void main(String[] strArr) {
38+
for (int i = 0; i < 1000; i++) {
39+
test();
40+
}
41+
}
42+
43+
static void test() {
44+
int iArr[] = new int[400];
45+
for (int i = 8; i < 128; i++) {
46+
for (int j = 209; j > 9; j--) {
47+
switch ((j % 5) + 58) {
48+
case 58:
49+
iArr[i] = 194;
50+
break;
51+
case 59:
52+
iFld = 3;
53+
case 62:
54+
default:
55+
iArr[1] = i;
56+
}
57+
for (int k = 2; k > 1; --k) {
58+
lArr[k] -= iFld;
59+
}
60+
}
61+
}
62+
}
63+
}
64+
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2025, Red Hat, Inc. 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 8361702
27+
* @summary C2: assert(is_dominator(compute_early_ctrl(limit, limit_ctrl), pre_end)) failed: node pinned on loop exit test?
28+
* @requires vm.flavor == "server"
29+
* @run main/othervm -XX:-BackgroundCompilation -XX:LoopUnrollLimit=100 -XX:-UseLoopPredicate -XX:-UseProfiledLoopPredicate TestSunkRangeFromPreLoopRCE3
30+
*/
31+
32+
/**
33+
* @test
34+
* @bug 8361702
35+
* @summary C2: assert(is_dominator(compute_early_ctrl(limit, limit_ctrl), pre_end)) failed: node pinned on loop exit test?
36+
* @run main TestSunkRangeFromPreLoopRCE3
37+
*/
38+
39+
import java.util.Arrays;
40+
41+
public class TestSunkRangeFromPreLoopRCE3 {
42+
43+
static final int nbIterations = 100;
44+
45+
public static void main(String[] args) {
46+
for (int i = 0; i < 20_000; i++) {
47+
test1(0);
48+
test1(0);
49+
}
50+
}
51+
52+
private static float test1(int k) {
53+
float v = 0;
54+
int j = 0;
55+
int[] lengths = new int[2];
56+
test1Helper(lengths);
57+
int constantFoldedTo4AfterCCP = 2;
58+
for (; constantFoldedTo4AfterCCP < 4; constantFoldedTo4AfterCCP *= 2);
59+
60+
int constantFoldedTo0AfterCCPAnd1RoundLoopOpts;
61+
for (constantFoldedTo0AfterCCPAnd1RoundLoopOpts = 0; constantFoldedTo0AfterCCPAnd1RoundLoopOpts < 40; constantFoldedTo0AfterCCPAnd1RoundLoopOpts += constantFoldedTo4AfterCCP) {
62+
}
63+
constantFoldedTo0AfterCCPAnd1RoundLoopOpts -= 40;
64+
for (int i = 0; i < nbIterations; i++) {
65+
int arrayLength2 = Integer.max(Integer.min(lengths[j * k], 1000), 0);
66+
float[] array = new float[arrayLength2];
67+
v += array[(constantFoldedTo0AfterCCPAnd1RoundLoopOpts + 1) * i];
68+
69+
int arrayLength = Integer.max(Integer.min(lengths[k], 1000), 0);
70+
71+
v += arrayLength & constantFoldedTo0AfterCCPAnd1RoundLoopOpts;
72+
73+
j = 1;
74+
}
75+
return v;
76+
}
77+
78+
private static void test1Helper(int[] lengths) {
79+
lengths[0] = nbIterations+1;
80+
lengths[1] = nbIterations+1;
81+
}
82+
}

0 commit comments

Comments
 (0)