Skip to content

Commit be6e440

Browse files
committed
8349139: C2: Div looses dependency on condition that guarantees divisor not zero in counted loop
Reviewed-by: chagedorn, epeter, qamai
1 parent 290d24d commit be6e440

File tree

7 files changed

+181
-32
lines changed

7 files changed

+181
-32
lines changed

src/hotspot/share/opto/loopTransform.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,6 +1299,22 @@ Node *PhaseIdealLoop::clone_up_backedge_goo(Node *back_ctrl, Node *preheader_ctr
12991299
return n;
13001300
}
13011301

1302+
// When a counted loop is created, the loop phi type may be narrowed down. As a consequence, the control input of some
1303+
// nodes may be cleared: in particular in the case of a division by the loop iv, the Div node would lose its control
1304+
// dependency if the loop phi is never zero. After pre/main/post loops are created (and possibly unrolling), the
1305+
// loop phi type is only correct if the loop is indeed reachable: there's an implicit dependency between the loop phi
1306+
// type and the zero trip guard for the main or post loop and as a consequence a dependency between the Div node and the
1307+
// zero trip guard. This makes the dependency explicit by adding a CastII for the loop entry input of the loop phi. If
1308+
// the backedge of the main or post loop is removed, a Div node won't be able to float above the zero trip guard of the
1309+
// loop and can't execute even if the loop is not reached.
1310+
void PhaseIdealLoop::cast_incr_before_loop(Node* incr, Node* ctrl, CountedLoopNode* loop) {
1311+
Node* castii = new CastIINode(ctrl, incr, TypeInt::INT, ConstraintCastNode::UnconditionalDependency);
1312+
register_new_node(castii, ctrl);
1313+
Node* phi = loop->phi();
1314+
assert(phi->in(LoopNode::EntryControl) == incr, "replacing wrong input?");
1315+
_igvn.replace_input_of(phi, LoopNode::EntryControl, castii);
1316+
}
1317+
13021318
#ifdef ASSERT
13031319
void PhaseIdealLoop::ensure_zero_trip_guard_proj(Node* node, bool is_main_loop) {
13041320
assert(node->is_IfProj(), "must be the zero trip guard If node");
@@ -1453,6 +1469,8 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n
14531469
initialize_assertion_predicates_for_main_loop(pre_head, main_head, first_node_index_in_pre_loop_body,
14541470
last_node_index_in_pre_loop_body,
14551471
DEBUG_ONLY(last_node_index_from_backedge_goo COMMA) old_new);
1472+
// CastII for the main loop:
1473+
cast_incr_before_loop(pre_incr, min_taken, main_head);
14561474

14571475
// Step B4: Shorten the pre-loop to run only 1 iteration (for now).
14581476
// RCE and alignment may change this later.
@@ -1593,7 +1611,7 @@ void PhaseIdealLoop::insert_vector_post_loop(IdealLoopTree *loop, Node_List &old
15931611
// Insert post loops. Add a post loop to the given loop passed.
15941612
Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new,
15951613
CountedLoopNode* main_head, CountedLoopEndNode* main_end,
1596-
Node*& incr, Node* limit, CountedLoopNode*& post_head) {
1614+
Node* incr, Node* limit, CountedLoopNode*& post_head) {
15971615
IfNode* outer_main_end = main_end;
15981616
IdealLoopTree* outer_loop = loop;
15991617
if (main_head->is_strip_mined()) {
@@ -1682,6 +1700,7 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new,
16821700

16831701
DEBUG_ONLY(ensure_zero_trip_guard_proj(post_head->in(LoopNode::EntryControl), false);)
16841702
initialize_assertion_predicates_for_post_loop(main_head, post_head, first_node_index_in_cloned_loop_body);
1703+
cast_incr_before_loop(zer_opaq->in(1), zer_taken, post_head);
16851704
return new_main_exit;
16861705
}
16871706

@@ -1777,8 +1796,8 @@ void PhaseIdealLoop::create_assertion_predicates_at_main_or_post_loop(CountedLoo
17771796
// to do because these control dependent nodes on the old target loop entry created by clone_up_backedge_goo() were
17781797
// pinned on the loop backedge before. The Assertion Predicates are not control dependent on these nodes in any way.
17791798
void PhaseIdealLoop::rewire_old_target_loop_entry_dependency_to_new_entry(
1780-
LoopNode* target_loop_head, const Node* old_target_loop_entry,
1781-
const uint node_index_before_new_assertion_predicate_nodes) {
1799+
CountedLoopNode* target_loop_head, const Node* old_target_loop_entry,
1800+
const uint node_index_before_new_assertion_predicate_nodes) {
17821801
Node* new_main_loop_entry = target_loop_head->skip_strip_mined()->in(LoopNode::EntryControl);
17831802
if (new_main_loop_entry == old_target_loop_entry) {
17841803
// No Assertion Predicates added.
@@ -1788,6 +1807,7 @@ void PhaseIdealLoop::rewire_old_target_loop_entry_dependency_to_new_entry(
17881807
for (DUIterator_Fast imax, i = old_target_loop_entry->fast_outs(imax); i < imax; i++) {
17891808
Node* out = old_target_loop_entry->fast_out(i);
17901809
if (!out->is_CFG() && out->_idx < node_index_before_new_assertion_predicate_nodes) {
1810+
assert(out != target_loop_head->init_trip(), "CastII on loop entry?");
17911811
_igvn.replace_input_of(out, 0, new_main_loop_entry);
17921812
set_ctrl(out, new_main_loop_entry);
17931813
--i;
@@ -2677,7 +2697,8 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) {
26772697
if (b_test._test == BoolTest::lt) { // Range checks always use lt
26782698
// The underflow and overflow limits: 0 <= scale*I+offset < limit
26792699
add_constraint(stride_con, lscale_con, offset, zero, limit, next_limit_ctrl, &pre_limit, &main_limit);
2680-
Node* init = cl->init_trip();
2700+
Node* init = cl->uncasted_init_trip(true);
2701+
26812702
Node* opaque_init = new OpaqueLoopInitNode(C, init);
26822703
register_new_node(opaque_init, loop_entry);
26832704

src/hotspot/share/opto/loopnode.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6058,6 +6058,24 @@ CountedLoopEndNode* CountedLoopNode::find_pre_loop_end() {
60586058
return pre_end;
60596059
}
60606060

6061+
Node* CountedLoopNode::uncasted_init_trip(bool uncast) {
6062+
Node* init = init_trip();
6063+
if (uncast && init->is_CastII()) {
6064+
// skip over the cast added by PhaseIdealLoop::cast_incr_before_loop() when pre/post/main loops are created because
6065+
// it can get in the way of type propagation. For instance, the index tested by an Assertion Predicate, if the cast
6066+
// is not skipped over, could be (1):
6067+
// (AddI (CastII (AddI pre_loop_iv -2) int) 1)
6068+
// while without the cast, it is (2):
6069+
// (AddI (AddI pre_loop_iv -2) 1)
6070+
// which is be transformed to (3):
6071+
// (AddI pre_loop_iv -1)
6072+
// The compiler may be able to constant fold the Assertion Predicate condition for (3) but not (1)
6073+
assert(init->as_CastII()->carry_dependency() && skip_assertion_predicates_with_halt() == init->in(0), "casted iv phi from pre loop expected");
6074+
init = init->in(1);
6075+
}
6076+
return init;
6077+
}
6078+
60616079
//------------------------------get_late_ctrl----------------------------------
60626080
// Compute latest legal control.
60636081
Node *PhaseIdealLoop::get_late_ctrl( Node *n, Node *early ) {

src/hotspot/share/opto/loopnode.hpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@ class CountedLoopNode : public BaseCountedLoopNode {
365365
Node* is_canonical_loop_entry();
366366
CountedLoopEndNode* find_pre_loop_end();
367367

368+
Node* uncasted_init_trip(bool uncasted);
369+
368370
#ifndef PRODUCT
369371
virtual void dump_spec(outputStream *st) const;
370372
#endif
@@ -973,6 +975,8 @@ class PhaseIdealLoop : public PhaseTransform {
973975
return ctrl;
974976
}
975977

978+
void cast_incr_before_loop(Node* incr, Node* ctrl, CountedLoopNode* loop);
979+
976980
#ifdef ASSERT
977981
static void ensure_zero_trip_guard_proj(Node* node, bool is_main_loop);
978982
#endif
@@ -998,7 +1002,7 @@ class PhaseIdealLoop : public PhaseTransform {
9981002
CountedLoopNode* target_loop_head,
9991003
const NodeInLoopBody& _node_in_loop_body,
10001004
bool kill_old_template);
1001-
void rewire_old_target_loop_entry_dependency_to_new_entry(LoopNode* target_loop_head,
1005+
void rewire_old_target_loop_entry_dependency_to_new_entry(CountedLoopNode* target_loop_head,
10021006
const Node* old_target_loop_entry,
10031007
uint node_index_before_new_assertion_predicate_nodes);
10041008
void insert_loop_limit_check_predicate(ParsePredicateSuccessProj* loop_limit_check_parse_proj, Node* cmp_limit,
@@ -1352,7 +1356,7 @@ class PhaseIdealLoop : public PhaseTransform {
13521356
// Add post loop after the given loop.
13531357
Node *insert_post_loop(IdealLoopTree* loop, Node_List& old_new,
13541358
CountedLoopNode* main_head, CountedLoopEndNode* main_end,
1355-
Node*& incr, Node* limit, CountedLoopNode*& post_head);
1359+
Node* incr, Node* limit, CountedLoopNode*& post_head);
13561360

13571361
// Add a vector post loop between a vector main loop and the current post loop
13581362
void insert_vector_post_loop(IdealLoopTree *loop, Node_List &old_new);
@@ -1580,8 +1584,6 @@ class PhaseIdealLoop : public PhaseTransform {
15801584
// Attempt to use a conditional move instead of a phi/branch
15811585
Node *conditional_move( Node *n );
15821586

1583-
bool split_thru_phi_could_prevent_vectorization(Node* n, Node* n_blk);
1584-
15851587
// Check for aggressive application of 'split-if' optimization,
15861588
// using basic block level info.
15871589
void split_if_with_blocks ( VectorSet &visited, Node_Stack &nstack);

src/hotspot/share/opto/loopopts.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,25 +1089,6 @@ void PhaseIdealLoop::try_move_store_after_loop(Node* n) {
10891089
}
10901090
}
10911091

1092-
// Split some nodes that take a counted loop phi as input at a counted
1093-
// loop can cause vectorization of some expressions to fail
1094-
bool PhaseIdealLoop::split_thru_phi_could_prevent_vectorization(Node* n, Node* n_blk) {
1095-
if (!n_blk->is_CountedLoop()) {
1096-
return false;
1097-
}
1098-
1099-
int opcode = n->Opcode();
1100-
1101-
if (opcode != Op_AndI &&
1102-
opcode != Op_MulI &&
1103-
opcode != Op_RotateRight &&
1104-
opcode != Op_RShiftI) {
1105-
return false;
1106-
}
1107-
1108-
return n->in(1) == n_blk->as_BaseCountedLoop()->phi();
1109-
}
1110-
11111092
//------------------------------split_if_with_blocks_pre-----------------------
11121093
// Do the real work in a non-recursive function. Data nodes want to be
11131094
// cloned in the pre-order so they can feed each other nicely.
@@ -1194,10 +1175,6 @@ Node *PhaseIdealLoop::split_if_with_blocks_pre( Node *n ) {
11941175
return n;
11951176
}
11961177

1197-
if (split_thru_phi_could_prevent_vectorization(n, n_blk)) {
1198-
return n;
1199-
}
1200-
12011178
// Check for having no control input; not pinned. Allow
12021179
// dominating control.
12031180
if (n->in(0)) {

src/hotspot/share/opto/predicates.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@ void AssertionPredicateIfCreator::create_halt_node(IfFalseNode* fail_proj, Ideal
758758
}
759759

760760
OpaqueLoopInitNode* TemplateAssertionPredicateCreator::create_opaque_init(Node* new_control) const {
761-
OpaqueLoopInitNode* opaque_init = new OpaqueLoopInitNode(_phase->C, _loop_head->init_trip());
761+
OpaqueLoopInitNode* opaque_init = new OpaqueLoopInitNode(_phase->C, _loop_head->uncasted_init_trip(_loop_head->is_main_loop()));
762762
_phase->register_new_node(opaque_init, new_control);
763763
return opaque_init;
764764
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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 8349139
27+
* @summary C2: Div looses dependency on condition that guarantees divisor not null in counted loop
28+
* @library /test/lib /
29+
* @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileOnly=TestDivDependentOnMainLoopGuard::*
30+
* -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:StressSeed=35878193 TestDivDependentOnMainLoopGuard
31+
* @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileOnly=TestDivDependentOnMainLoopGuard::*
32+
* -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM TestDivDependentOnMainLoopGuard
33+
* @run main/othervm -Xcomp -XX:CompileOnly=TestDivDependentOnMainLoopGuard::* TestDivDependentOnMainLoopGuard
34+
*/
35+
36+
import jdk.test.lib.Utils;
37+
import java.util.Random;
38+
39+
public class TestDivDependentOnMainLoopGuard {
40+
41+
public static final int N = 400;
42+
private static final Random RANDOM = Utils.getRandomInstance();
43+
public static final int stop = RANDOM.nextInt(0, 68);
44+
45+
public int iArrFld[]=new int[N];
46+
47+
public void mainTest(String[] strArr1, int otherPhi) {
48+
49+
int i=57657, i1=577, i2=6, i3=157, i4=12, i23=61271;
50+
boolean bArr[]=new boolean[N];
51+
52+
for (i = 9; 379 > i; i++) {
53+
i2 = 1;
54+
do {
55+
i1 <<= i3;
56+
} while (++i2 < 68);
57+
for (i23 = 68; i23 > stop; otherPhi=i23-1, i23--) {
58+
bArr[i23 + 1] = true;
59+
try {
60+
i1 = (-42360 / i23);
61+
iArrFld[i + 1] = otherPhi;
62+
} catch (ArithmeticException a_e) {}
63+
}
64+
}
65+
}
66+
67+
public static void main(String[] strArr) {
68+
TestDivDependentOnMainLoopGuard _instance = new TestDivDependentOnMainLoopGuard();
69+
for (int i = 0; i < 10; i++ ) {
70+
_instance.mainTest(strArr, 0);
71+
}
72+
}
73+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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 8349139
27+
* @summary C2: Div looses dependency on condition that guarantees divisor not null in counted loop
28+
* @run main/othervm -XX:-BackgroundCompilation TestMainLoopNoBackedgeFloatingDiv
29+
*/
30+
31+
public class TestMainLoopNoBackedgeFloatingDiv {
32+
private static int field;
33+
34+
public static void main(String[] args) {
35+
for (int i = 0; i < 20_000; i++) {
36+
test1(1000, 0, false);
37+
test1Helper(1000, 0, false, false);
38+
}
39+
test1(1, 0, false);
40+
}
41+
42+
private static int test1(int stop, int res, boolean alwaysTrueInMain) {
43+
stop = Integer.max(stop, 1);
44+
res = test1Helper(stop, res, alwaysTrueInMain, true);
45+
return res;
46+
}
47+
48+
private static int test1Helper(int stop, int res, boolean alwaysTrueInMain, boolean flag) {
49+
for (int i = stop; i >= 1; i--) {
50+
res = res / i;
51+
if (alwaysTrueInMain) {
52+
break;
53+
}
54+
alwaysTrueInMain = flag;
55+
}
56+
return res;
57+
}
58+
}

0 commit comments

Comments
 (0)