Skip to content

Commit a9820fd

Browse files
committed
[GR-66434] Partial evaluation: create proxies after PE not during.
PullRequest: graal/21569
2 parents 04d438d + 25f3b5e commit a9820fd

File tree

12 files changed

+1149
-410
lines changed

12 files changed

+1149
-410
lines changed
Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
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+
public void test02() {
147+
DebugContext debug = getDebugContext();
148+
149+
OptionValues opt = new OptionValues(getInitialOptions(), BytecodeParserOptions.ParserCreateProxies, false);
150+
StructuredGraph graph = parseEager("snippet2", StructuredGraph.AllowAssumptions.NO, opt);
151+
152+
try (DebugContext.Scope _ = debug.scope("ReProxyTest")) {
153+
154+
// removing all the proxies
155+
for (LoopExitNode exit : graph.getNodes(LoopExitNode.TYPE)) {
156+
exit.removeProxies();
157+
}
158+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After removing proxies");
159+
160+
// clear all states for now, we only care about proxy generation
161+
graph.clearAllStateAfterForTestingOnly();
162+
163+
// now find the merge
164+
MergeNode withThreePred = (MergeNode) graph.getNodes().filter(x -> x instanceof MergeNode && ((MergeNode) x).phiPredecessorCount() == 3).first();
165+
LoopBeginNode onlyLoopBegin = graph.getNodes(LoopBeginNode.TYPE).first();
166+
167+
for (LoopExitNode oldLex : onlyLoopBegin.loopExits().snapshot()) {
168+
FixedWithNextNode pred = (FixedWithNextNode) oldLex.predecessor();
169+
FixedNode next = oldLex.next();
170+
oldLex.setNext(null);
171+
pred.setNext(null);
172+
pred.setNext(next);
173+
oldLex.safeDelete();
174+
}
175+
176+
for (EndNode end : withThreePred.forwardEnds().snapshot()) {
177+
LoopExitNode lex = graph.add(new LoopExitNode(onlyLoopBegin));
178+
graph.addAfterFixed((FixedWithNextNode) end.predecessor(), lex);
179+
180+
}
181+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After adding strange ");
182+
183+
// the cleanup the graph
184+
CanonicalizerPhase.create().apply(graph, getDefaultHighTierContext());
185+
186+
// then add them again
187+
new InsertProxyPhase().apply(graph);
188+
189+
} catch (Throwable e) {
190+
throw GraalError.shouldNotReachHere(e);
191+
}
192+
}
193+
194+
boolean testMemoryGraphProxies;
195+
196+
@Override
197+
protected Suites createSuites(OptionValues opts) {
198+
Suites s = super.createSuites(opts).copy();
199+
if (testMemoryGraphProxies) {
200+
var pos = s.getMidTier().findPhase(FloatingReadPhase.class, true);
201+
pos.add(new Phase() {
202+
@Override
203+
public Optional<NotApplicable> notApplicableTo(GraphState graphState) {
204+
return ALWAYS_APPLICABLE;
205+
}
206+
207+
@Override
208+
protected void run(StructuredGraph graph) {
209+
for (LoopExitNode exit : graph.getNodes(LoopExitNode.TYPE)) {
210+
exit.removeProxies();
211+
}
212+
new InsertProxyPhase().apply(graph);
213+
}
214+
});
215+
}
216+
return s;
217+
}
218+
219+
class TestMemoryGraphProxies implements AutoCloseable {
220+
TestMemoryGraphProxies() {
221+
testMemoryGraphProxies = true;
222+
}
223+
224+
@Override
225+
public void close() {
226+
testMemoryGraphProxies = false;
227+
}
228+
}
229+
230+
public static int snippet3(int limit) {
231+
int i = 0;
232+
int result = 0;
233+
while (true) {
234+
result = S;
235+
if (i < limit) {
236+
GraalDirectives.sideEffect();
237+
i++;
238+
} else {
239+
240+
if (limit == 23) {
241+
S = i;
242+
}
243+
GraalDirectives.controlFlowAnchor();
244+
245+
if (i == 44) {
246+
continue;
247+
}
248+
GraalDirectives.controlFlowAnchor();
249+
250+
break;
251+
}
252+
}
253+
254+
return result + S/* read S again to get a proxy for the kill */;
255+
}
256+
257+
@Test
258+
@SuppressWarnings("try")
259+
public void testMemoryGraph01() {
260+
try (var testMemProxy = new TestMemoryGraphProxies()) {
261+
OptionValues optionValues = new OptionValues(getInitialOptions(), BytecodeParserOptions.ParserCreateProxies, false, GraalOptions.OptReadElimination, false, GraalOptions.LoopUnswitch,
262+
false, GraalOptions.LoopPeeling, false, GraalOptions.PartialEscapeAnalysis, false);
263+
test(optionValues, "snippet3", 10);
264+
}
265+
}
266+
267+
public static int snippet31(int limit) {
268+
int i = 0;
269+
int result = 0;
270+
while (true) {
271+
result = S;
272+
if (i < limit) {
273+
GraalDirectives.sideEffect();
274+
i++;
275+
} else {
276+
277+
GraalDirectives.controlFlowAnchor();
278+
if (limit == 23) {
279+
S = i;
280+
}
281+
GraalDirectives.controlFlowAnchor();
282+
283+
if (i == 44) {
284+
continue;
285+
} else {
286+
GraalDirectives.controlFlowAnchor();
287+
}
288+
GraalDirectives.controlFlowAnchor();
289+
290+
if (G == 12) {
291+
L = 1;
292+
} else if (G == 123) {
293+
L = 2;
294+
} else {
295+
L = 3;
296+
}
297+
if (K == 125) {
298+
L = 4;
299+
} else {
300+
L = 5;
301+
}
302+
303+
break;
304+
}
305+
}
306+
307+
return result + S/* read S again to get a proxy for the kill */;
308+
}
309+
310+
static int G;
311+
static int L;
312+
static int K;
313+
314+
@Test
315+
@SuppressWarnings("try")
316+
public void testMemoryGraph02() {
317+
DebugContext debug = getDebugContext();
318+
319+
OptionValues opt = new OptionValues(getInitialOptions(), BytecodeParserOptions.ParserCreateProxies, true);
320+
StructuredGraph graph = parseEager("snippet31", StructuredGraph.AllowAssumptions.NO, opt);
321+
322+
try (DebugContext.Scope _ = debug.scope("ReProxyTest")) {
323+
324+
// clear all states for now, we only care about proxy generation
325+
graph.clearAllStateAfterForTestingOnly();
326+
327+
new HighTierLoweringPhase(CanonicalizerPhase.create()).apply(graph, getDefaultHighTierContext());
328+
329+
// run floating reads
330+
new FloatingReadPhase(false, true, CanonicalizerPhase.create()).apply(graph, getDefaultMidTierContext());
331+
332+
// removing all the proxies
333+
for (LoopExitNode exit : graph.getNodes(LoopExitNode.TYPE)) {
334+
exit.removeProxies();
335+
}
336+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After removing proxies");
337+
338+
// clear all states for now, we only care about proxy generation
339+
graph.clearAllStateAfterForTestingOnly();
340+
341+
// now find the merge
342+
MergeNode withThreePred = (MergeNode) graph.getNodes().filter(x -> x instanceof MergeNode && ((MergeNode) x).phiPredecessorCount() == 3).first();
343+
LoopBeginNode onlyLoopBegin = graph.getNodes(LoopBeginNode.TYPE).first();
344+
345+
for (LoopExitNode oldLex : onlyLoopBegin.loopExits().snapshot()) {
346+
if (oldLex.predecessor() instanceof IfNode ifNode) {
347+
FixedNode next = oldLex.next();
348+
oldLex.setNext(null);
349+
350+
if (next instanceof AbstractBeginNode) {
351+
oldLex.replaceAtPredecessor(next);
352+
} else {
353+
BeginNode begin = graph.add(new BeginNode());
354+
begin.setNext(next);
355+
oldLex.replaceAtPredecessor(begin);
356+
}
357+
358+
oldLex.safeDelete();
359+
} else {
360+
FixedWithNextNode pred = (FixedWithNextNode) oldLex.predecessor();
361+
FixedNode next = oldLex.next();
362+
oldLex.setNext(null);
363+
pred.setNext(null);
364+
pred.setNext(next);
365+
oldLex.safeDelete();
366+
}
367+
}
368+
369+
for (EndNode end : withThreePred.forwardEnds().snapshot()) {
370+
LoopExitNode lex = graph.add(new LoopExitNode(onlyLoopBegin));
371+
graph.addAfterFixed((FixedWithNextNode) end.predecessor(), lex);
372+
373+
}
374+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After adding strange ");
375+
376+
// the cleanup the graph
377+
CanonicalizerPhase.create().apply(graph, getDefaultHighTierContext());
378+
379+
// then add them again
380+
new InsertProxyPhase().apply(graph);
381+
382+
} catch (Throwable e) {
383+
throw GraalError.shouldNotReachHere(e);
384+
}
385+
}
386+
}

0 commit comments

Comments
 (0)