Skip to content

Commit 0acc1d1

Browse files
committed
8377163: C2: Iteration split must take into consideration sunk stores
Reviewed-by: chagedorn, dfenacci
1 parent 6371da9 commit 0acc1d1

2 files changed

Lines changed: 144 additions & 4 deletions

File tree

src/hotspot/share/opto/loopTransform.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
#include "opto/loopnode.hpp"
3636
#include "opto/movenode.hpp"
3737
#include "opto/mulnode.hpp"
38+
#include "opto/node.hpp"
3839
#include "opto/opaquenode.hpp"
40+
#include "opto/opcodes.hpp"
3941
#include "opto/phase.hpp"
4042
#include "opto/predicates.hpp"
4143
#include "opto/rootnode.hpp"
@@ -1774,13 +1776,39 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new,
17741776
for (DUIterator i = main_head->outs(); main_head->has_out(i); i++) {
17751777
Node* main_phi = main_head->out(i);
17761778
if (main_phi->is_Phi() && main_phi->in(0) == main_head && main_phi->outcnt() > 0) {
1777-
Node* cur_phi = old_new[main_phi->_idx];
1779+
Node* post_phi = old_new[main_phi->_idx];
1780+
Node* loopback_input = main_phi->in(LoopNode::LoopBackControl);
17781781
Node* fallnew = clone_up_backedge_goo(main_head->back_control(),
17791782
post_head->init_control(),
1780-
main_phi->in(LoopNode::LoopBackControl),
1783+
loopback_input,
17811784
visited, clones);
1782-
_igvn.hash_delete(cur_phi);
1783-
cur_phi->set_req(LoopNode::EntryControl, fallnew);
1785+
// Technically, the entry value of post_phi must be the loop back input of the corresponding
1786+
// Phi of the outer loop, not the Phi of the inner loop (i.e. main_phi). However, we have not
1787+
// constructed the Phis for the OuterStripMinedLoop yet, so the input must be inferred from
1788+
// the loop back input of main_phi.
1789+
// - If post_phi is a data Phi, then we can use the loop back input of main_phi.
1790+
// - If post_phi is a memory Phi, since Stores can be sunk below the inner loop, but still
1791+
// inside the outer loop, we have 2 cases:
1792+
// + If the loop back input of main_phi is on the backedge, then the entry input of
1793+
// post_phi is the clone of the node on the entry of post_head, similar to when post_phi
1794+
// is a data Phi.
1795+
// + If the loop back input of main_phi is not on the backedge, we need to find whether
1796+
// there is a sunk Store corresponding to post_phi, if there is any, the latest such
1797+
// store will be the entry input of post_phi. Fortunately, the safepoint at the exit of
1798+
// the outer loop captures all memory states, so we can use it as the entry input of
1799+
// post_phi.
1800+
// Another way to see it is that, the memory phi should capture the latest state at the
1801+
// post-loop entry. If loopback_input is cloned by clone_up_backedge_goo, it is pinned at
1802+
// the post-loop entry, and is surely the latest state. Otherwise, the latest memory state
1803+
// corresponding to post_phi is the memory state at the exit of the outer main-loop, which
1804+
// is captured by the safepoint there.
1805+
if (main_head->is_strip_mined() && fallnew == loopback_input && post_phi->is_memory_phi()) {
1806+
SafePointNode* main_safepoint = main_head->outer_safepoint();
1807+
assert(main_safepoint != nullptr, "outer loop must have a safepoint");
1808+
fallnew = main_safepoint->memory();
1809+
}
1810+
_igvn.hash_delete(post_phi);
1811+
post_phi->set_req(LoopNode::EntryControl, fallnew);
17841812
}
17851813
}
17861814
// Store nodes that were moved to the outer loop by PhaseIdealLoop::try_move_store_after_loop
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright (c) 2026, 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+
package compiler.loopopts;
24+
25+
import java.util.Objects;
26+
import jdk.internal.misc.Unsafe;
27+
28+
/*
29+
* @test
30+
* @bug 8377163
31+
* @summary Iteration splitting a counted loop with sunk stores should connect the memory phi of
32+
* the post loop to the sunk store in the main loop, not the store at the loop back input
33+
* of the corresponding phi of the main loop.
34+
* @modules java.base/jdk.internal.misc
35+
* @run main ${test.main.class}
36+
* @run main/othervm -Xbatch -XX:-TieredCompilation ${test.main.class}
37+
*/
38+
public class TestIterationSplitWithSunkStores {
39+
private static final Unsafe U = Unsafe.getUnsafe();
40+
41+
public static void main(String[] args) {
42+
test1();
43+
44+
int[] array = new int[1000];
45+
MyInteger v = new MyInteger(0);
46+
for (int i = 0; i < 100; i++) {
47+
test2(array, v, v, v, v);
48+
}
49+
}
50+
51+
private static void test1() {
52+
int[] dst = new int[5];
53+
for (long i = 0L; i < 20_000; i++) {
54+
test1(dst, 1);
55+
for (int j = 1; j < 5; j++) {
56+
if (dst[j] != j) {
57+
throw new RuntimeException("Bad copy");
58+
}
59+
}
60+
}
61+
}
62+
63+
private static void test1(int[] dst, int dstPos) {
64+
int[] src = new int[4];
65+
src[0] = new MyInteger(1).v();
66+
src[1] = 2;
67+
src[2] = 3;
68+
src[3] = 4;
69+
System.arraycopy(src, 0, dst, dstPos, 4);
70+
}
71+
72+
private static void test2(int[] array, MyInteger v1, MyInteger v2, MyInteger v3, MyInteger v4) {
73+
Objects.requireNonNull(array);
74+
Objects.requireNonNull(v1);
75+
Objects.requireNonNull(v2);
76+
Objects.requireNonNull(v3);
77+
Objects.requireNonNull(v4);
78+
79+
// Using Unsafe to access the array so that the stores can be sunk without loop
80+
// predication. This is because store sinking is only attempted during the first and the
81+
// last loop opt passes, and we need it to occur before iteration splitting.
82+
for (int i = 0; i < array.length; i++) {
83+
long elemOffset = Unsafe.ARRAY_INT_BASE_OFFSET + (long) i * Unsafe.ARRAY_INT_INDEX_SCALE;
84+
int e = U.getInt(array, elemOffset);
85+
U.putInt(array, elemOffset, e + 1);
86+
87+
// These 4 stores can all be sunk, but depending on the order in which they are
88+
// visited, it is most likely that only some of them are actually sunk
89+
v1.v = e + 1;
90+
v2.v = e + 2;
91+
v3.v = e + 3;
92+
v4.v = e + 4;
93+
}
94+
}
95+
96+
static class MyInteger {
97+
public int v;
98+
99+
public MyInteger(int v) {
100+
for (int i = 0; i < 32; i++) {
101+
if (i < 10) {
102+
this.v = v;
103+
}
104+
}
105+
this.v = v;
106+
}
107+
108+
public int v() {
109+
return v;
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)