Skip to content

Commit 2280d5b

Browse files
committed
8347018: C2: Insertion of Assertion Predicates ignores the effects of PhaseIdealLoop::clone_up_backedge_goo()
Reviewed-by: kvn Backport-of: 8a83dc213ac630ec79d62637133fe7aa102a27a3
1 parent 63a800d commit 2280d5b

File tree

5 files changed

+168
-26
lines changed

5 files changed

+168
-26
lines changed

src/hotspot/share/opto/loopTransform.cpp

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -1428,11 +1428,13 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n
14281428
_igvn.hash_delete(outer_main_head);
14291429
outer_main_head->set_req(LoopNode::EntryControl, min_taken);
14301430
set_idom(outer_main_head, min_taken, dd_main_head);
1431+
assert(post_head->in(1)->is_IfProj(), "must be zero-trip guard If node projection of the post loop");
14311432

14321433
VectorSet visited;
14331434
Node_Stack clones(main_head->back_control()->outcnt());
14341435
// Step B3: Make the fall-in values to the main-loop come from the
14351436
// fall-out values of the pre-loop.
1437+
const uint last_node_index_in_pre_loop_body = Compile::current()->unique() - 1;
14361438
for (DUIterator i2 = main_head->outs(); main_head->has_out(i2); i2++) {
14371439
Node* main_phi = main_head->out(i2);
14381440
if (main_phi->is_Phi() && main_phi->in(0) == main_head && main_phi->outcnt() > 0) {
@@ -1445,21 +1447,13 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n
14451447
main_phi->set_req(LoopNode::EntryControl, fallpre);
14461448
}
14471449
}
1450+
DEBUG_ONLY(const uint last_node_index_from_backedge_goo = Compile::current()->unique() - 1);
14481451

1449-
// Nodes inside the loop may be control dependent on a predicate
1450-
// that was moved before the preloop. If the back branch of the main
1451-
// or post loops becomes dead, those nodes won't be dependent on the
1452-
// test that guards that loop nest anymore which could lead to an
1453-
// incorrect array access because it executes independently of the
1454-
// test that was guarding the loop nest. We add a special CastII on
1455-
// the if branch that enters the loop, between the input induction
1456-
// variable value and the induction variable Phi to preserve correct
1457-
// dependencies.
1458-
1459-
assert(post_head->in(1)->is_IfProj(), "must be zero-trip guard If node projection of the post loop");
14601452
DEBUG_ONLY(ensure_zero_trip_guard_proj(outer_main_head->in(LoopNode::EntryControl), true);)
14611453
if (UseLoopPredicate) {
1462-
initialize_assertion_predicates_for_main_loop(pre_head, main_head, first_node_index_in_pre_loop_body, old_new);
1454+
initialize_assertion_predicates_for_main_loop(pre_head, main_head, first_node_index_in_pre_loop_body,
1455+
last_node_index_in_pre_loop_body,
1456+
DEBUG_ONLY(last_node_index_from_backedge_goo COMMA) old_new);
14631457
}
14641458

14651459
// Step B4: Shorten the pre-loop to run only 1 iteration (for now).
@@ -1734,10 +1728,15 @@ void PhaseIdealLoop::initialize_assertion_predicates_for_peeled_loop(CountedLoop
17341728
// Target Loop: Original - main_loop_head
17351729
void PhaseIdealLoop::initialize_assertion_predicates_for_main_loop(CountedLoopNode* pre_loop_head,
17361730
CountedLoopNode* main_loop_head,
1737-
const uint first_node_index_in_cloned_loop_body,
1731+
const uint first_node_index_in_pre_loop_body,
1732+
const uint last_node_index_in_pre_loop_body,
1733+
DEBUG_ONLY(const uint last_node_index_from_backedge_goo COMMA)
17381734
const Node_List& old_new) {
1739-
const NodeInOriginalLoopBody node_in_original_loop_body(first_node_index_in_cloned_loop_body, old_new);
1740-
create_assertion_predicates_at_loop(pre_loop_head, main_loop_head, node_in_original_loop_body, true);
1735+
assert(first_node_index_in_pre_loop_body < last_node_index_in_pre_loop_body, "cloned some nodes");
1736+
const NodeInMainLoopBody node_in_main_loop_body(first_node_index_in_pre_loop_body,
1737+
last_node_index_in_pre_loop_body,
1738+
DEBUG_ONLY(last_node_index_from_backedge_goo COMMA) old_new);
1739+
create_assertion_predicates_at_main_or_post_loop(pre_loop_head, main_loop_head, node_in_main_loop_body, true);
17411740
}
17421741

17431742
// Source Loop: Original - main_loop_head
@@ -1746,7 +1745,7 @@ void PhaseIdealLoop::initialize_assertion_predicates_for_post_loop(CountedLoopNo
17461745
CountedLoopNode* post_loop_head,
17471746
const uint first_node_index_in_cloned_loop_body) {
17481747
const NodeInClonedLoopBody node_in_cloned_loop_body(first_node_index_in_cloned_loop_body);
1749-
create_assertion_predicates_at_loop(main_loop_head, post_loop_head, node_in_cloned_loop_body, false);
1748+
create_assertion_predicates_at_main_or_post_loop(main_loop_head, post_loop_head, node_in_cloned_loop_body, false);
17501749
}
17511750

17521751
void PhaseIdealLoop::create_assertion_predicates_at_loop(CountedLoopNode* source_loop_head,
@@ -1768,6 +1767,47 @@ void PhaseIdealLoop::create_assertion_predicates_at_loop(CountedLoopNode* source
17681767
set_idom(target_outer_loop_head, last_created_predicate_success_proj, dom_depth(target_outer_loop_head));
17691768
}
17701769
}
1770+
1771+
void PhaseIdealLoop::create_assertion_predicates_at_main_or_post_loop(CountedLoopNode* source_loop_head,
1772+
CountedLoopNode* target_loop_head,
1773+
const NodeInLoopBody& _node_in_loop_body,
1774+
bool clone_template) {
1775+
Node* old_target_loop_head_entry = target_loop_head->skip_strip_mined()->in(LoopNode::EntryControl);
1776+
const uint node_index_before_new_assertion_predicate_nodes = C->unique();
1777+
const bool need_to_rewire_old_target_loop_entry_dependencies = old_target_loop_head_entry->outcnt() > 1;
1778+
create_assertion_predicates_at_loop(source_loop_head, target_loop_head, _node_in_loop_body, clone_template);
1779+
if (need_to_rewire_old_target_loop_entry_dependencies) {
1780+
rewire_old_target_loop_entry_dependency_to_new_entry(target_loop_head, old_target_loop_head_entry,
1781+
node_index_before_new_assertion_predicate_nodes);
1782+
}
1783+
}
1784+
1785+
// Rewire any control dependent nodes on the old target loop entry before adding Assertion Predicate related nodes.
1786+
// These have been added by PhaseIdealLoop::clone_up_backedge_goo() and assume to be ending up at the target loop entry
1787+
// which is no longer the case when adding additional Assertion Predicates. Fix this by rewiring these nodes to the new
1788+
// target loop entry which corresponds to the tail of the last Assertion Predicate before the target loop. This is safe
1789+
// to do because these control dependent nodes on the old target loop entry created by clone_up_backedge_goo() were
1790+
// pinned on the loop backedge before. The Assertion Predicates are not control dependent on these nodes in any way.
1791+
void PhaseIdealLoop::rewire_old_target_loop_entry_dependency_to_new_entry(
1792+
LoopNode* target_loop_head, const Node* old_target_loop_entry,
1793+
const uint node_index_before_new_assertion_predicate_nodes) {
1794+
Node* new_main_loop_entry = target_loop_head->skip_strip_mined()->in(LoopNode::EntryControl);
1795+
if (new_main_loop_entry == old_target_loop_entry) {
1796+
// No Assertion Predicates added.
1797+
return;
1798+
}
1799+
1800+
for (DUIterator_Fast imax, i = old_target_loop_entry->fast_outs(imax); i < imax; i++) {
1801+
Node* out = old_target_loop_entry->fast_out(i);
1802+
if (!out->is_CFG() && out->_idx < node_index_before_new_assertion_predicate_nodes) {
1803+
_igvn.replace_input_of(out, 0, new_main_loop_entry);
1804+
set_ctrl(out, new_main_loop_entry);
1805+
--i;
1806+
--imax;
1807+
}
1808+
}
1809+
}
1810+
17711811
//------------------------------do_unroll--------------------------------------
17721812
// Unroll the loop body one step - make each trip do 2 iterations.
17731813
void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adjust_min_trip) {

src/hotspot/share/opto/loopnode.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -950,12 +950,20 @@ class PhaseIdealLoop : public PhaseTransform {
950950
const Node_List& old_new);
951951
void initialize_assertion_predicates_for_main_loop(CountedLoopNode* pre_loop_head,
952952
CountedLoopNode* main_loop_head,
953-
uint first_node_index_in_cloned_loop_body,
953+
uint first_node_index_in_pre_loop_body,
954+
uint last_node_index_in_pre_loop_body,
955+
DEBUG_ONLY(uint last_node_index_from_backedge_goo COMMA)
954956
const Node_List& old_new);
955957
void initialize_assertion_predicates_for_post_loop(CountedLoopNode* main_loop_head, CountedLoopNode* post_loop_head,
956958
uint first_node_index_in_cloned_loop_body);
957959
void create_assertion_predicates_at_loop(CountedLoopNode* source_loop_head, CountedLoopNode* target_loop_head,
958960
const NodeInLoopBody& _node_in_loop_body, bool clone_template);
961+
void create_assertion_predicates_at_main_or_post_loop(CountedLoopNode* source_loop_head,
962+
CountedLoopNode* target_loop_head,
963+
const NodeInLoopBody& _node_in_loop_body, bool clone_template);
964+
void rewire_old_target_loop_entry_dependency_to_new_entry(LoopNode* target_loop_head,
965+
const Node* old_target_loop_entry,
966+
uint node_index_before_new_assertion_predicate_nodes);
959967
void insert_loop_limit_check_predicate(ParsePredicateSuccessProj* loop_limit_check_parse_proj, Node* cmp_limit,
960968
Node* bol);
961969
void log_loop_tree();

src/hotspot/share/opto/predicates.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ void TemplateAssertionPredicate::rewire_loop_data_dependencies(IfTrueNode* targe
135135
PhaseIdealLoop* phase) const {
136136
for (DUIterator i = _success_proj->outs(); _success_proj->has_out(i); i++) {
137137
Node* output = _success_proj->out(i);
138-
if (!output->is_CFG() && data_in_loop_body.check(output)) {
138+
if (!output->is_CFG() && data_in_loop_body.check_node_in_loop_body(output)) {
139139
phase->igvn().replace_input_of(output, 0, target_predicate);
140140
--i; // account for the just deleted output
141141
}

src/hotspot/share/opto/predicates.hpp

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -249,7 +249,7 @@ class PredicateVisitor : StackObj {
249249
// Interface to check whether a node is in a loop body or not.
250250
class NodeInLoopBody : public StackObj {
251251
public:
252-
virtual bool check(Node* node) const = 0;
252+
virtual bool check_node_in_loop_body(Node* node) const = 0;
253253
};
254254

255255
// Class to represent Assertion Predicates (i.e. either Initialized and/or Template Assertion Predicates).
@@ -952,13 +952,52 @@ class NodeInOriginalLoopBody : public NodeInLoopBody {
952952

953953
// Check if 'node' is not a cloned node (i.e. "< _first_node_index_in_cloned_loop_body") and if we've created a
954954
// clone from 'node' (i.e. _old_new entry is non-null). Then we know that 'node' belongs to the original loop body.
955-
bool check(Node* node) const override {
955+
bool check_node_in_loop_body(Node* node) const override {
956956
if (node->_idx < _first_node_index_in_cloned_loop_body) {
957957
Node* cloned_node = _old_new[node->_idx];
958+
// Check that the clone is actually part of the cloned loop body and not from some earlier cloning.
958959
return cloned_node != nullptr && cloned_node->_idx >= _first_node_index_in_cloned_loop_body;
959-
} else {
960-
return false;
961960
}
961+
return false;
962+
}
963+
};
964+
965+
// This class checks whether a node is in the main loop body and not the pre loop body. We cannot use the
966+
// NodeInOriginalLoopBody class because PhaseIdealLoop::clone_up_backedge_goo() could clone additional nodes that
967+
// should be pinned at the main loop body entry. The check in NodeInOriginalLoopBody will ignore these.
968+
class NodeInMainLoopBody : public NodeInLoopBody {
969+
const uint _first_node_index_in_pre_loop_body;
970+
const uint _last_node_index_in_pre_loop_body;
971+
DEBUG_ONLY(const uint _last_node_index_from_backedge_goo;)
972+
const Node_List& _old_new;
973+
974+
public:
975+
NodeInMainLoopBody(const uint first_node_index_in_pre_loop_body, const uint last_node_index_in_pre_loop_body,
976+
DEBUG_ONLY(const uint last_node_index_from_backedge_goo COMMA) const Node_List& old_new)
977+
: _first_node_index_in_pre_loop_body(first_node_index_in_pre_loop_body),
978+
_last_node_index_in_pre_loop_body(last_node_index_in_pre_loop_body),
979+
DEBUG_ONLY(_last_node_index_from_backedge_goo(last_node_index_from_backedge_goo) COMMA)
980+
_old_new(old_new) {}
981+
NONCOPYABLE(NodeInMainLoopBody);
982+
983+
// Check if 'node' is not a cloned node (i.e. "< _first_node_index_in_cloned_loop_body") and if we've created a
984+
// clone from 'node' (i.e. _old_new entry is non-null). Then we know that 'node' belongs to the original loop body.
985+
// Additionally check if a node was cloned after the pre loop was created. This indicates that it was created by
986+
// PhaseIdealLoop::clone_up_backedge_goo(). These nodes should also be pinned at the main loop entry.
987+
bool check_node_in_loop_body(Node* node) const override {
988+
if (node->_idx < _first_node_index_in_pre_loop_body) {
989+
Node* cloned_node = _old_new[node->_idx];
990+
// Check that the clone is actually part of the cloned loop body and not from some earlier cloning.
991+
bool cloned_node_in_pre_loop_body = cloned_node != nullptr && cloned_node->_idx >= _first_node_index_in_pre_loop_body;
992+
assert(!cloned_node_in_pre_loop_body || cloned_node->_idx <= _last_node_index_in_pre_loop_body,
993+
"clone must be part of pre loop body");
994+
return cloned_node_in_pre_loop_body;
995+
}
996+
// Created in PhaseIdealLoop::clone_up_backedge_goo()?
997+
bool node_created_by_backedge_goo = node->_idx > _last_node_index_in_pre_loop_body;
998+
assert(!node_created_by_backedge_goo || node->_idx <= _last_node_index_from_backedge_goo,
999+
"cloned node must have been created in PhaseIdealLoop::clone_up_backedge_goo()");
1000+
return node_created_by_backedge_goo;
9621001
}
9631002
};
9641003

@@ -973,7 +1012,7 @@ class NodeInClonedLoopBody : public NodeInLoopBody {
9731012

9741013
// Check if 'node' is a clone. This can easily be achieved by comparing its node index to the first node index
9751014
// inside the cloned loop body (all of them are clones).
976-
bool check(Node* node) const override {
1015+
bool check_node_in_loop_body(Node* node) const override {
9771016
return node->_idx >= _first_node_index_in_cloned_loop_body;
9781017
}
9791018
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (c) 2025, 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+
/*
26+
* @test
27+
* @bug 8347018
28+
* @summary Test that stores cloned with clone_up_backedge_goo() are not pinned above Assertion Predicates on which a
29+
* load node is pinned at which will later fail in scheduling.
30+
* @run main/othervm -Xbatch -XX:CompileCommand=compileonly,*TestLoadPinnedAboveAssertionPredicatesAndUsingStore::test
31+
* compiler.predicates.assertion.TestLoadPinnedAboveAssertionPredicatesAndUsingStore
32+
*/
33+
34+
package compiler.predicates.assertion;
35+
36+
public class TestLoadPinnedAboveAssertionPredicatesAndUsingStore {
37+
static int iFld;
38+
static int iArr[] = new int[100];
39+
40+
static void test() {
41+
int i = 63;
42+
do {
43+
iArr[1] = 34;
44+
iArr[i] += iFld;
45+
for (int j = i; j < 1; j++) {
46+
}
47+
} while (--i > 0);
48+
}
49+
50+
public static void main(String[] strArr) {
51+
for (int i = 0; i < 10000; i++) {
52+
test();
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)