Skip to content

Commit e625817

Browse files
committed
[GR-71280] Simplification for LoadIndexed with a conditional as index.
PullRequest: graal/22597
2 parents c523e33 + 7a95dd3 commit e625817

File tree

2 files changed

+182
-3
lines changed

2 files changed

+182
-3
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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.core.test;
26+
27+
import org.junit.Test;
28+
29+
import jdk.graal.compiler.api.directives.GraalDirectives;
30+
import jdk.graal.compiler.nodes.ConstantNode;
31+
import jdk.graal.compiler.nodes.PhiNode;
32+
import jdk.graal.compiler.nodes.ReturnNode;
33+
import jdk.graal.compiler.nodes.StructuredGraph;
34+
import jdk.graal.compiler.nodes.java.LoadIndexedNode;
35+
36+
public class LoadIndexedSimplificationTest extends GraalCompilerTest {
37+
38+
static final int[] ARRAY = new int[]{1, 2, 3, 4};
39+
40+
public static int testConditionalIndexWithConstantTrueValueSnippet(int x) {
41+
int index = x < 1 ? 3 : x;
42+
var array = GraalDirectives.assumeStableDimension(ARRAY, 1);
43+
return array[index];
44+
}
45+
46+
public static int testConditionalIndexWithConstantFalseValueSnippet(int x) {
47+
int index = x < 1 ? x : 3;
48+
var array = GraalDirectives.assumeStableDimension(ARRAY, 1);
49+
return array[index];
50+
}
51+
52+
public static int testConditionalIndexWithAllConstantsSnippet(int x) {
53+
int index = x < 1 ? 3 : 1;
54+
var array = GraalDirectives.assumeStableDimension(ARRAY, 1);
55+
return array[index];
56+
}
57+
58+
@Test
59+
public void testConditionalIndexWithOneConstant() {
60+
// true value is constant
61+
var graph = canonicalizedGraph("testConditionalIndexWithConstantTrueValueSnippet");
62+
assert checkResultBranchFolded(0, graph);
63+
// false value is constant
64+
graph = canonicalizedGraph("testConditionalIndexWithConstantFalseValueSnippet");
65+
assert checkResultBranchFolded(1, graph);
66+
}
67+
68+
private boolean checkResultBranchFolded(int idx, StructuredGraph graph) {
69+
/*
70+
* Check that the graph's return value is a phi with the value at idx is constant folded and
71+
* the other value being a LoadIndexedNode
72+
*/
73+
if (graph.getNodes().filter(ReturnNode.class).count() != 1) {
74+
return false;
75+
}
76+
ReturnNode ret = graph.getNodes().filter(ReturnNode.class).first();
77+
if (ret.result() instanceof PhiNode phi && phi.valueCount() == 2) {
78+
int nonConstantBranch = idx == 0 ? 1 : 0;
79+
return phi.valueAt(idx) instanceof ConstantNode && phi.valueAt(nonConstantBranch) instanceof LoadIndexedNode;
80+
}
81+
return false;
82+
}
83+
84+
@Test
85+
public void testConditionalIndexWithAllConstants() {
86+
var graph = canonicalizedGraph("testConditionalIndexWithAllConstantsSnippet");
87+
assert graph.getNodes().filter(LoadIndexedNode.class).count() == 0 : "LoadIndexed should be folded away.";
88+
}
89+
90+
private StructuredGraph canonicalizedGraph(String snippet) {
91+
var graph = parseEager(getResolvedJavaMethod(snippet), StructuredGraph.AllowAssumptions.NO);
92+
createCanonicalizerPhase().apply(graph, getDefaultHighTierContext());
93+
return graph;
94+
}
95+
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/java/LoadIndexedNode.java

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,24 @@
3535
import jdk.graal.compiler.graph.Node;
3636
import jdk.graal.compiler.graph.NodeClass;
3737
import jdk.graal.compiler.nodeinfo.NodeInfo;
38-
import jdk.graal.compiler.nodes.virtual.VirtualArrayNode;
39-
import jdk.graal.compiler.nodes.virtual.VirtualObjectNode;
38+
import jdk.graal.compiler.nodes.BeginNode;
4039
import jdk.graal.compiler.nodes.ConstantNode;
4140
import jdk.graal.compiler.nodes.DeoptimizeNode;
41+
import jdk.graal.compiler.nodes.EndNode;
4242
import jdk.graal.compiler.nodes.FixedGuardNode;
43+
import jdk.graal.compiler.nodes.FixedNode;
4344
import jdk.graal.compiler.nodes.FixedWithNextNode;
45+
import jdk.graal.compiler.nodes.FrameState;
46+
import jdk.graal.compiler.nodes.IfNode;
4447
import jdk.graal.compiler.nodes.LogicNode;
48+
import jdk.graal.compiler.nodes.MergeNode;
4549
import jdk.graal.compiler.nodes.NodeView;
50+
import jdk.graal.compiler.nodes.ProfileData;
51+
import jdk.graal.compiler.nodes.StructuredGraph;
4652
import jdk.graal.compiler.nodes.ValueNode;
53+
import jdk.graal.compiler.nodes.ValuePhiNode;
4754
import jdk.graal.compiler.nodes.calc.CompareNode;
55+
import jdk.graal.compiler.nodes.calc.ConditionalNode;
4856
import jdk.graal.compiler.nodes.extended.GuardingNode;
4957
import jdk.graal.compiler.nodes.memory.MemoryAccess;
5058
import jdk.graal.compiler.nodes.spi.Canonicalizable;
@@ -54,7 +62,9 @@
5462
import jdk.graal.compiler.nodes.spi.Virtualizable;
5563
import jdk.graal.compiler.nodes.spi.VirtualizerTool;
5664
import jdk.graal.compiler.nodes.type.StampTool;
57-
65+
import jdk.graal.compiler.nodes.util.GraphUtil;
66+
import jdk.graal.compiler.nodes.virtual.VirtualArrayNode;
67+
import jdk.graal.compiler.nodes.virtual.VirtualObjectNode;
5868
import jdk.vm.ci.meta.Assumptions;
5969
import jdk.vm.ci.meta.ConstantReflectionProvider;
6070
import jdk.vm.ci.meta.DeoptimizationAction;
@@ -181,6 +191,80 @@ public void simplify(SimplifierTool tool) {
181191
graph().replaceFixedWithFixed(this, graph().add(fixedGuard));
182192
}
183193
}
194+
195+
// Try to simplify loads from constant arrays with the index being a conditional.
196+
if (array() instanceof ConstantNode && index() instanceof ConditionalNode conditional) {
197+
StructuredGraph graph = graph();
198+
var trueVal = tryConstantFold(array(), conditional.trueValue(), tool.getMetaAccess(), tool.getConstantReflection());
199+
var falseVal = tryConstantFold(array(), conditional.falseValue(), tool.getMetaAccess(), tool.getConstantReflection());
200+
201+
if (trueVal != null && falseVal != null) {
202+
/*-
203+
* Both conditional inputs are constant.
204+
*
205+
* cond C1 C2
206+
* | | |
207+
* Const(arr) Conditional
208+
* | /
209+
* LoadIndexed
210+
*
211+
* Replace with:
212+
*
213+
* cond arr[C1] arr[C2]
214+
* | | /
215+
* Conditional
216+
*/
217+
graph.replaceFixedWithFloating(this, graph.addOrUniqueWithInputs(ConditionalNode.create(conditional.condition(), trueVal, falseVal, NodeView.from(tool))));
218+
} else if (trueVal != null || falseVal != null) {
219+
/*-
220+
* Just one conditional input is constant.
221+
*
222+
* cond C1 var
223+
* | | |
224+
* Const(arr) Conditional
225+
* | /
226+
* LoadIndexed
227+
*
228+
* Replace with control flow:
229+
* cond
230+
* \
231+
* if arr var
232+
* / \ | /
233+
* | LoadIndexed-----------
234+
* \ / |
235+
* Merge -- Phi (arr[C1], . )
236+
*/
237+
boolean trueValConstant = trueVal != null;
238+
var lastState = GraphUtil.findLastFrameState(this);
239+
IfNode ifNode = graph.add(new IfNode(conditional.condition(), graph.add(new BeginNode()), graph.add(new BeginNode()), ProfileData.BranchProbabilityData.unknown()));
240+
this.replaceAtPredecessor(ifNode);
241+
242+
var trueEnd = graph.add(new EndNode());
243+
var falseEnd = graph.add(new EndNode());
244+
graph.addAfterFixed(ifNode.trueSuccessor(), trueEnd);
245+
graph.addAfterFixed(ifNode.falseSuccessor(), falseEnd);
246+
var merge = graph.add(new MergeNode());
247+
merge.addForwardEnd(trueEnd);
248+
merge.addForwardEnd(falseEnd);
249+
250+
FrameState state = lastState == null ? null : lastState.duplicateWithVirtualState();
251+
merge.setStateAfter(state);
252+
253+
var nonConstantIndex = trueValConstant ? conditional.falseValue() : conditional.trueValue();
254+
var newLoad = graph.add(LoadIndexedNode.create(graph.getAssumptions(), array(), nonConstantIndex,
255+
getBoundsCheck(), elementKind(), tool.getMetaAccess(), tool.getConstantReflection()));
256+
assert newLoad instanceof LoadIndexedNode : "Unexpectedly folding LoadField to: " + newLoad;
257+
if (trueValConstant) {
258+
graph.addAfterFixed(ifNode.falseSuccessor(), (FixedNode) newLoad);
259+
falseVal = newLoad;
260+
} else {
261+
graph.addAfterFixed(ifNode.trueSuccessor(), (FixedNode) newLoad);
262+
trueVal = newLoad;
263+
}
264+
this.replaceAtUsages(graph.addOrUniqueWithInputs((new ValuePhiNode(this.stamp, merge, trueVal, falseVal))));
265+
graph.replaceFixed(this, merge);
266+
}
267+
}
184268
}
185269

186270
public static ValueNode tryConstantFold(ValueNode array, ValueNode index, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) {

0 commit comments

Comments
 (0)