Skip to content

Commit 6ea8be5

Browse files
AliLinaboui01marwan-hallaoui
authored andcommitted
[GR-69464] Backport to 25.0: Evaluate Merge Explode PE without ProxyPlaceHolder nodes
PullRequest: graal/22172
2 parents 32edf14 + 26a139b commit 6ea8be5

File tree

11 files changed

+1163
-199
lines changed

11 files changed

+1163
-199
lines changed
Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
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. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.graal.compiler.truffle.test;
26+
27+
import java.util.Optional;
28+
29+
import org.junit.Assert;
30+
import org.junit.Test;
31+
32+
import jdk.graal.compiler.api.directives.GraalDirectives;
33+
import jdk.graal.compiler.core.common.GraalOptions;
34+
import jdk.graal.compiler.core.test.GraalCompilerTest;
35+
import jdk.graal.compiler.debug.DebugContext;
36+
import jdk.graal.compiler.debug.GraalError;
37+
import jdk.graal.compiler.graph.Node;
38+
import jdk.graal.compiler.java.BytecodeParserOptions;
39+
import jdk.graal.compiler.nodes.AbstractBeginNode;
40+
import jdk.graal.compiler.nodes.BeginNode;
41+
import jdk.graal.compiler.nodes.ConstantNode;
42+
import jdk.graal.compiler.nodes.EndNode;
43+
import jdk.graal.compiler.nodes.FixedNode;
44+
import jdk.graal.compiler.nodes.FixedWithNextNode;
45+
import jdk.graal.compiler.nodes.FrameState;
46+
import jdk.graal.compiler.nodes.GraphState;
47+
import jdk.graal.compiler.nodes.IfNode;
48+
import jdk.graal.compiler.nodes.LogicConstantNode;
49+
import jdk.graal.compiler.nodes.LoopBeginNode;
50+
import jdk.graal.compiler.nodes.LoopExitNode;
51+
import jdk.graal.compiler.nodes.MergeNode;
52+
import jdk.graal.compiler.nodes.ProxyNode;
53+
import jdk.graal.compiler.nodes.StructuredGraph;
54+
import jdk.graal.compiler.nodes.calc.IntegerEqualsNode;
55+
import jdk.graal.compiler.nodes.debug.SideEffectNode;
56+
import jdk.graal.compiler.options.OptionValues;
57+
import jdk.graal.compiler.phases.Phase;
58+
import jdk.graal.compiler.phases.common.CanonicalizerPhase;
59+
import jdk.graal.compiler.phases.common.FloatingReadPhase;
60+
import jdk.graal.compiler.phases.common.HighTierLoweringPhase;
61+
import jdk.graal.compiler.phases.common.InsertProxyPhase;
62+
import jdk.graal.compiler.phases.schedule.SchedulePhase;
63+
import jdk.graal.compiler.phases.tiers.Suites;
64+
65+
public class InsertProxyReProxyTest extends GraalCompilerTest {
66+
67+
public static int snippet(int limit, Object o) {
68+
int i = 0;
69+
Object j = null;
70+
if (limit == 0) {
71+
j = o;
72+
} else {
73+
74+
while (true) {
75+
if (i < limit) {
76+
GraalDirectives.sideEffect();
77+
} else {
78+
j = GraalDirectives.guardingNonNull(o).getClass();
79+
// force a path here that we can later remove to force the guard into the loop
80+
if (GraalDirectives.sideEffect(limit) == limit) {
81+
continue;
82+
}
83+
break;
84+
}
85+
}
86+
}
87+
GraalDirectives.blackhole(j);
88+
return 0;
89+
}
90+
91+
@Test
92+
public void testReProxy() {
93+
StructuredGraph graph = parseEager("snippet", StructuredGraph.AllowAssumptions.NO);
94+
for (Node n : graph.getNodes()) {
95+
if (n instanceof IntegerEqualsNode ie && ie.getY() instanceof SideEffectNode se) {
96+
n.replaceAtUsages(LogicConstantNode.contradiction(graph));
97+
se.replaceAtUsages(ConstantNode.forInt(0, graph));
98+
FrameState fs = se.stateAfter();
99+
se.setStateAfter(null);
100+
fs.safeDelete();
101+
graph.removeFixed(se);
102+
}
103+
}
104+
CanonicalizerPhase.create().apply(graph, getDefaultHighTierContext());
105+
// do not re-proxy the proxied pi already.
106+
new InsertProxyPhase().apply(graph);
107+
assert graph.verify(true);
108+
new SchedulePhase(graph.getOptions()).apply(graph, getDefaultHighTierContext());
109+
Assert.assertEquals(1, graph.getNodes().filter(ProxyNode.class).count());
110+
}
111+
112+
static int S;
113+
114+
public static int snippet2(int limit) {
115+
int i = 0;
116+
int result = 0;
117+
while (true) {
118+
result = S;
119+
if (i < limit) {
120+
GraalDirectives.sideEffect();
121+
} else {
122+
123+
if (i == 44) {
124+
continue;
125+
}
126+
127+
if (GraalDirectives.sideEffect(limit) == 12) {
128+
GraalDirectives.sideEffect(1);
129+
} else if (GraalDirectives.sideEffect(limit) == 123) {
130+
GraalDirectives.sideEffect(2);
131+
} else {
132+
GraalDirectives.sideEffect(3);
133+
}
134+
if (GraalDirectives.sideEffect(limit) == 125) {
135+
GraalDirectives.sideEffect(4);
136+
} else {
137+
GraalDirectives.sideEffect(5);
138+
}
139+
break;
140+
}
141+
}
142+
return result;
143+
}
144+
145+
@Test
146+
@SuppressWarnings({"try", "resource"})
147+
public void test02() {
148+
DebugContext debug = getDebugContext();
149+
150+
OptionValues opt = new OptionValues(getInitialOptions(), BytecodeParserOptions.ParserCreateProxies, false);
151+
StructuredGraph graph = parseEager("snippet2", StructuredGraph.AllowAssumptions.NO, opt);
152+
153+
try (DebugContext.Scope ignored = debug.scope("ReProxyTest")) {
154+
155+
// removing all the proxies
156+
for (LoopExitNode exit : graph.getNodes(LoopExitNode.TYPE)) {
157+
exit.removeProxies();
158+
}
159+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After removing proxies");
160+
161+
// clear all states for now, we only care about proxy generation
162+
graph.clearAllStateAfterForTestingOnly();
163+
164+
// now find the merge
165+
MergeNode withThreePred = (MergeNode) graph.getNodes().filter(x -> x instanceof MergeNode && ((MergeNode) x).phiPredecessorCount() == 3).first();
166+
LoopBeginNode onlyLoopBegin = graph.getNodes(LoopBeginNode.TYPE).first();
167+
168+
for (LoopExitNode oldLex : onlyLoopBegin.loopExits().snapshot()) {
169+
FixedWithNextNode pred = (FixedWithNextNode) oldLex.predecessor();
170+
FixedNode next = oldLex.next();
171+
oldLex.setNext(null);
172+
pred.setNext(null);
173+
pred.setNext(next);
174+
oldLex.safeDelete();
175+
}
176+
177+
for (EndNode end : withThreePred.forwardEnds().snapshot()) {
178+
LoopExitNode lex = graph.add(new LoopExitNode(onlyLoopBegin));
179+
graph.addAfterFixed((FixedWithNextNode) end.predecessor(), lex);
180+
181+
}
182+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After adding strange ");
183+
184+
// the cleanup the graph
185+
CanonicalizerPhase.create().apply(graph, getDefaultHighTierContext());
186+
187+
// then add them again
188+
new InsertProxyPhase().apply(graph);
189+
190+
} catch (Throwable e) {
191+
throw GraalError.shouldNotReachHere(e);
192+
}
193+
}
194+
195+
boolean testMemoryGraphProxies;
196+
197+
@Override
198+
protected Suites createSuites(OptionValues opts) {
199+
Suites s = super.createSuites(opts).copy();
200+
if (testMemoryGraphProxies) {
201+
var pos = s.getMidTier().findPhase(FloatingReadPhase.class, true);
202+
pos.add(new Phase() {
203+
@Override
204+
public Optional<NotApplicable> notApplicableTo(GraphState graphState) {
205+
return ALWAYS_APPLICABLE;
206+
}
207+
208+
@Override
209+
protected void run(StructuredGraph graph) {
210+
for (LoopExitNode exit : graph.getNodes(LoopExitNode.TYPE)) {
211+
exit.removeProxies();
212+
}
213+
new InsertProxyPhase().apply(graph);
214+
}
215+
});
216+
}
217+
return s;
218+
}
219+
220+
class TestMemoryGraphProxies implements AutoCloseable {
221+
TestMemoryGraphProxies() {
222+
testMemoryGraphProxies = true;
223+
}
224+
225+
@Override
226+
public void close() {
227+
testMemoryGraphProxies = false;
228+
}
229+
}
230+
231+
public static int snippet3(int limit) {
232+
int i = 0;
233+
int result = 0;
234+
while (true) {
235+
result = S;
236+
if (i < limit) {
237+
GraalDirectives.sideEffect();
238+
i++;
239+
} else {
240+
241+
if (limit == 23) {
242+
S = i;
243+
}
244+
GraalDirectives.controlFlowAnchor();
245+
246+
if (i == 44) {
247+
continue;
248+
}
249+
GraalDirectives.controlFlowAnchor();
250+
251+
break;
252+
}
253+
}
254+
255+
return result + S/* read S again to get a proxy for the kill */;
256+
}
257+
258+
@Test
259+
@SuppressWarnings("try")
260+
public void testMemoryGraph01() {
261+
try (var testMemProxy = new TestMemoryGraphProxies()) {
262+
OptionValues optionValues = new OptionValues(getInitialOptions(), BytecodeParserOptions.ParserCreateProxies, false, GraalOptions.OptReadElimination, false, GraalOptions.LoopUnswitch,
263+
false, GraalOptions.LoopPeeling, false, GraalOptions.PartialEscapeAnalysis, false);
264+
test(optionValues, "snippet3", 10);
265+
}
266+
}
267+
268+
public static int snippet31(int limit) {
269+
int i = 0;
270+
int result = 0;
271+
while (true) {
272+
result = S;
273+
if (i < limit) {
274+
GraalDirectives.sideEffect();
275+
i++;
276+
} else {
277+
278+
GraalDirectives.controlFlowAnchor();
279+
if (limit == 23) {
280+
S = i;
281+
}
282+
GraalDirectives.controlFlowAnchor();
283+
284+
if (i == 44) {
285+
continue;
286+
} else {
287+
GraalDirectives.controlFlowAnchor();
288+
}
289+
GraalDirectives.controlFlowAnchor();
290+
291+
if (G == 12) {
292+
L = 1;
293+
} else if (G == 123) {
294+
L = 2;
295+
} else {
296+
L = 3;
297+
}
298+
if (K == 125) {
299+
L = 4;
300+
} else {
301+
L = 5;
302+
}
303+
304+
break;
305+
}
306+
}
307+
308+
return result + S/* read S again to get a proxy for the kill */;
309+
}
310+
311+
static int G;
312+
static int L;
313+
static int K;
314+
315+
@Test
316+
@SuppressWarnings({"try", "resource", "unused"})
317+
public void testMemoryGraph02() {
318+
DebugContext debug = getDebugContext();
319+
320+
OptionValues opt = new OptionValues(getInitialOptions(), BytecodeParserOptions.ParserCreateProxies, true);
321+
StructuredGraph graph = parseEager("snippet31", StructuredGraph.AllowAssumptions.NO, opt);
322+
323+
try (DebugContext.Scope ignored = debug.scope("ReProxyTest")) {
324+
325+
// clear all states for now, we only care about proxy generation
326+
graph.clearAllStateAfterForTestingOnly();
327+
328+
new HighTierLoweringPhase(CanonicalizerPhase.create()).apply(graph, getDefaultHighTierContext());
329+
330+
// run floating reads
331+
new FloatingReadPhase(false, CanonicalizerPhase.create());
332+
333+
// removing all the proxies
334+
for (LoopExitNode exit : graph.getNodes(LoopExitNode.TYPE)) {
335+
exit.removeProxies();
336+
}
337+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After removing proxies");
338+
339+
// clear all states for now, we only care about proxy generation
340+
graph.clearAllStateAfterForTestingOnly();
341+
342+
// now find the merge
343+
MergeNode withThreePred = (MergeNode) graph.getNodes().filter(x -> x instanceof MergeNode && ((MergeNode) x).phiPredecessorCount() == 3).first();
344+
LoopBeginNode onlyLoopBegin = graph.getNodes(LoopBeginNode.TYPE).first();
345+
346+
for (LoopExitNode oldLex : onlyLoopBegin.loopExits().snapshot()) {
347+
if (oldLex.predecessor() instanceof IfNode) {
348+
FixedNode next = oldLex.next();
349+
oldLex.setNext(null);
350+
351+
if (next instanceof AbstractBeginNode) {
352+
oldLex.replaceAtPredecessor(next);
353+
} else {
354+
BeginNode begin = graph.add(new BeginNode());
355+
begin.setNext(next);
356+
oldLex.replaceAtPredecessor(begin);
357+
}
358+
359+
oldLex.safeDelete();
360+
} else {
361+
FixedWithNextNode pred = (FixedWithNextNode) oldLex.predecessor();
362+
FixedNode next = oldLex.next();
363+
oldLex.setNext(null);
364+
pred.setNext(null);
365+
pred.setNext(next);
366+
oldLex.safeDelete();
367+
}
368+
}
369+
370+
for (EndNode end : withThreePred.forwardEnds().snapshot()) {
371+
LoopExitNode lex = graph.add(new LoopExitNode(onlyLoopBegin));
372+
graph.addAfterFixed((FixedWithNextNode) end.predecessor(), lex);
373+
374+
}
375+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After adding strange ");
376+
377+
// the cleanup the graph
378+
CanonicalizerPhase.create().apply(graph, getDefaultHighTierContext());
379+
380+
// then add them again
381+
new InsertProxyPhase().apply(graph);
382+
383+
} catch (Throwable e) {
384+
throw GraalError.shouldNotReachHere(e);
385+
}
386+
}
387+
}

0 commit comments

Comments
 (0)