Skip to content

Commit 921a2d3

Browse files
flohuemerelkorchi
authored andcommitted
partial evluation: create proxies after PE not during.
(cherry picked from commit d93f5c0)
1 parent d55d561 commit 921a2d3

File tree

4 files changed

+577
-180
lines changed

4 files changed

+577
-180
lines changed

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/MergeExplodeProxyTest.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 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
@@ -24,12 +24,7 @@
2424
*/
2525
package jdk.graal.compiler.truffle.test;
2626

27-
import jdk.graal.compiler.api.directives.GraalDirectives;
28-
import jdk.graal.compiler.nodes.ProxyNode;
29-
import jdk.graal.compiler.nodes.StructuredGraph;
30-
import jdk.graal.compiler.phases.util.GraphOrder;
3127
import org.junit.Assert;
32-
import org.junit.Ignore;
3328
import org.junit.Test;
3429

3530
import com.oracle.truffle.api.CallTarget;
@@ -43,8 +38,13 @@
4338
import com.oracle.truffle.api.nodes.DirectCallNode;
4439
import com.oracle.truffle.api.nodes.ExplodeLoop;
4540
import com.oracle.truffle.api.nodes.ExplodeLoop.LoopExplosionKind;
46-
import com.oracle.truffle.runtime.OptimizedCallTarget;
4741
import com.oracle.truffle.api.nodes.RootNode;
42+
import com.oracle.truffle.runtime.OptimizedCallTarget;
43+
44+
import jdk.graal.compiler.api.directives.GraalDirectives;
45+
import jdk.graal.compiler.nodes.ProxyNode;
46+
import jdk.graal.compiler.nodes.StructuredGraph;
47+
import jdk.graal.compiler.phases.util.GraphOrder;
4848

4949
/**
5050
* Collection of tests that penetrate the partial evaluation logic to produce {@linkplain ProxyNode}
@@ -747,7 +747,6 @@ public Object execute(VirtualFrame frame) {
747747

748748
}
749749

750-
@Ignore("GR-21520: Merge explode partial evaluation cannot proxy nodes that are not alive in the framestate of inner loop begins")
751750
@Test
752751
public void testNoneLiveLoopExitProxy() {
753752
byte[] bytecodes = new byte[]{

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

Lines changed: 33 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
import jdk.graal.compiler.nodeinfo.InputType;
6666
import jdk.graal.compiler.nodeinfo.NodeInfo;
6767
import jdk.graal.compiler.nodes.GraphDecoder.MethodScope;
68-
import jdk.graal.compiler.nodes.GraphDecoder.ProxyPlaceholder;
6968
import jdk.graal.compiler.nodes.calc.FloatingNode;
7069
import jdk.graal.compiler.nodes.extended.IntegerSwitchNode;
7170
import jdk.graal.compiler.nodes.extended.SwitchNode;
@@ -420,7 +419,7 @@ protected LoopExplosionState(FrameState state, MergeNode merge) {
420419
if (value == null) {
421420
h = h * 31 + 1234;
422421
} else {
423-
h = h * 31 + ProxyPlaceholder.unwrap(value).hashCode();
422+
h = h * 31 + value.hashCode();
424423
}
425424
}
426425
this.hashCode = h;
@@ -436,11 +435,18 @@ public boolean equals(Object obj) {
436435
FrameState thisState = state;
437436
assert thisState.outerFrameState() == otherState.outerFrameState() : Assertions.errorMessage(thisState, thisState.outerFrameState(), otherState, otherState.outerFrameState());
438437

439-
Iterator<ValueNode> thisIter = thisState.values().iterator();
440-
Iterator<ValueNode> otherIter = otherState.values().iterator();
441-
while (thisIter.hasNext() && otherIter.hasNext()) {
442-
ValueNode thisValue = ProxyPlaceholder.unwrap(thisIter.next());
443-
ValueNode otherValue = ProxyPlaceholder.unwrap(otherIter.next());
438+
final NodeInputList<ValueNode> thisValues = thisState.values();
439+
final NodeInputList<ValueNode> otherValues = otherState.values();
440+
441+
final int size = thisValues.size();
442+
if (size != otherValues.size()) {
443+
return false;
444+
}
445+
446+
for (int i = 0; i < size; i++) {
447+
final ValueNode thisValue = thisValues.get(i);
448+
final ValueNode otherValue = otherValues.get(i);
449+
444450
if (thisValue != otherValue) {
445451
return false;
446452
}
@@ -502,49 +508,6 @@ public InvokeData(Invoke invoke, ResolvedJavaType contextType, int invokeOrderId
502508
}
503509
}
504510

505-
/**
506-
* A node that is created during
507-
* {@link jdk.graal.compiler.nodes.graphbuilderconf.LoopExplosionPlugin.LoopExplosionKind#MERGE_EXPLODE
508-
* loop explosion} that can later be replaced by a ProxyNode if {@link LoopDetector loop
509-
* detection} finds out that the value is defined in the loop, but used outside the loop.
510-
*/
511-
@NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
512-
protected static final class ProxyPlaceholder extends FloatingNode implements Canonicalizable {
513-
public static final NodeClass<ProxyPlaceholder> TYPE = NodeClass.create(ProxyPlaceholder.class);
514-
515-
@Input ValueNode value;
516-
@Input(InputType.Association) Node proxyPoint;
517-
518-
public ProxyPlaceholder(ValueNode value, MergeNode proxyPoint) {
519-
super(TYPE, value.stamp(NodeView.DEFAULT));
520-
this.value = value;
521-
this.proxyPoint = proxyPoint;
522-
}
523-
524-
void setValue(ValueNode value) {
525-
updateUsages(this.value, value);
526-
this.value = value;
527-
}
528-
529-
@Override
530-
public Node canonical(CanonicalizerTool tool) {
531-
if (tool.allUsagesAvailable()) {
532-
/* The node is always unnecessary after graph decoding. */
533-
return value;
534-
} else {
535-
return this;
536-
}
537-
}
538-
539-
public static ValueNode unwrap(ValueNode value) {
540-
ValueNode result = value;
541-
while (result instanceof ProxyPlaceholder) {
542-
result = ((ProxyPlaceholder) result).value;
543-
}
544-
return result;
545-
}
546-
}
547-
548511
private static final TimerKey MakeSuccessorStubsTimer = DebugContext.timer("PartialEvaluation-MakeSuccessorStubs").doc("Time spent in making successor stubs for the PE.");
549512
private static final TimerKey ReadPropertiesTimer = DebugContext.timer("PartialEvaluation-ReadProperties").doc("Time spent in reading node properties in the PE.");
550513

@@ -1042,6 +1005,11 @@ protected void handleLoopExplosionBegin(MethodScope methodScope, LoopScope loopS
10421005
}
10431006
}
10441007

1008+
/*
1009+
* Merge explosion: When doing merge-explode PE loops are detected after partial evaluation
1010+
* in a dedicated steps. Therefore, we create merge nodes instead of loop begins and loop
1011+
* exits and later replace them with the detected loop begin and loop exit nodes.
1012+
*/
10451013
MergeNode merge = graph.add(new MergeNode());
10461014
methodScope.loopExplosionMerges.add(merge);
10471015

@@ -1053,65 +1021,6 @@ protected void handleLoopExplosionBegin(MethodScope methodScope, LoopScope loopS
10531021
}
10541022
methodScope.loopExplosionHead = merge;
10551023
}
1056-
1057-
/*
1058-
* Proxy generation and merge explosion: When doing merge-explode PE loops are detected
1059-
* after partial evaluation in a dedicated steps. Therefore, we create merge nodes
1060-
* instead of loop begins and loop exits and later replace them with the detected loop
1061-
* begin and loop exit nodes.
1062-
*
1063-
* However, in order to create the correct loop proxies, all values alive at a merge
1064-
* created for a loop begin/loop exit replacement merge, we create so called proxy
1065-
* placeholder nodes. These nodes are attached to a merge and proxy the corresponding
1066-
* node. Later, when we replace a merge with a loop exit, we visit all live nodes at
1067-
* that loop exit and replace the proxy placeholders with real value proxy nodes.
1068-
*
1069-
* Since we cannot (this would explode immediately) proxy all nodes for every merge
1070-
* during explosion, we only create nodes at the loop explosion begins for nodes that
1071-
* are alive at the framestate of the respective loop begin. This is fine as long as all
1072-
* values proxied out of a loop are alive at the loop header. However, this is not true
1073-
* for all values (imagine do-while loops). Thus, we may create, in very specific
1074-
* patterns, unschedulable graphs since we miss the creation of proxy nodes.
1075-
*
1076-
* There is currently no straight forward solution to this problem, thus we shift the
1077-
* work to the implementor side where such patterns can typically be easily fixed by
1078-
* creating loop phis and assigning them correctly.
1079-
*/
1080-
FrameState.ValueFunction valueFunction = new FrameState.ValueFunction() {
1081-
1082-
@Override
1083-
public ValueNode apply(int index, ValueNode frameStateValue) {
1084-
if (frameStateValue == null || frameStateValue.isConstant() || !graph.isNew(methodScope.methodStartMark, frameStateValue)) {
1085-
return frameStateValue;
1086-
1087-
} else {
1088-
ProxyPlaceholder newFrameStateValue = graph.unique(new ProxyPlaceholder(frameStateValue, merge));
1089-
1090-
/*
1091-
* We do not have the orderID of the value anymore, so we need to search
1092-
* through the complete list of nodes to find a match.
1093-
*/
1094-
for (int i = 0; i < loopScope.createdNodes.length; i++) {
1095-
if (loopScope.createdNodes[i] == frameStateValue) {
1096-
loopScope.createdNodes[i] = newFrameStateValue;
1097-
}
1098-
}
1099-
1100-
if (loopScope.initialCreatedNodes != null) {
1101-
for (int i = 0; i < loopScope.initialCreatedNodes.length; i++) {
1102-
if (loopScope.initialCreatedNodes[i] == frameStateValue) {
1103-
loopScope.initialCreatedNodes[i] = newFrameStateValue;
1104-
}
1105-
}
1106-
}
1107-
return newFrameStateValue;
1108-
}
1109-
}
1110-
};
1111-
FrameState newFrameState = graph.add(frameState.duplicate(valueFunction));
1112-
1113-
frameState.replaceAtUsagesAndDelete(newFrameState);
1114-
frameState = newFrameState;
11151024
}
11161025

11171026
loopBegin.replaceAtUsagesAndDelete(merge);
@@ -1192,7 +1101,7 @@ protected void handleFixedNode(MethodScope methodScope, LoopScope loopScope, int
11921101
* Hook for subclasses for early canonicalization of IfNodes and IntegerSwitchNodes.
11931102
*
11941103
* "Early" means that this is called before successor stubs creation. Therefore, all successors
1195-
* are null a this point, and calling any method using them without prior manual initialization
1104+
* are null at this point, and calling any method using them without prior manual initialization
11961105
* will fail.
11971106
*
11981107
* @param methodScope The current method.
@@ -1303,9 +1212,6 @@ protected void handleLoopExplosionProxyNodes(MethodScope methodScope, LoopScope
13031212
ValueNode phiInput = proxy.value();
13041213

13051214
if (loopExitPlaceholder != null) {
1306-
if (!phiInput.isConstant()) {
1307-
phiInput = graph.unique(new ProxyPlaceholder(phiInput, loopExitPlaceholder));
1308-
}
13091215
registerNode(loopScope, proxyOrderId, phiInput, true, false);
13101216
}
13111217

@@ -1776,6 +1682,7 @@ protected void registerNode(LoopScope loopScope, int nodeOrderId, Node node, boo
17761682
assert node == null || node.isAlive();
17771683
assert allowNull || node != null;
17781684
assert allowOverwrite || lookupNode(loopScope, nodeOrderId) == null;
1685+
17791686
loopScope.createdNodes[nodeOrderId] = node;
17801687
if (loopScope.methodScope.inliningLogDecoder != null) {
17811688
loopScope.methodScope.inliningLogDecoder.registerNode(loopScope.methodScope.inliningLog, node, nodeOrderId);
@@ -1804,11 +1711,6 @@ protected Object readObject(MethodScope methodScope) {
18041711
* @param methodScope The current method.
18051712
*/
18061713
protected void cleanupGraph(MethodScope methodScope) {
1807-
for (MergeNode merge : graph.getNodes(MergeNode.TYPE)) {
1808-
for (ProxyPlaceholder placeholder : merge.usages().filter(ProxyPlaceholder.class).snapshot()) {
1809-
placeholder.replaceAndDelete(placeholder.value);
1810-
}
1811-
}
18121714
assert verifyEdges();
18131715
}
18141716

@@ -2271,70 +2173,24 @@ private void insertLoopNodes(Loop loop) {
22712173

22722174
/**
22732175
* During graph decoding, we create a FrameState for every exploded loop iteration. This is
2274-
* mostly the state that we want, we only need to tweak it a little bit: we need to insert the
2275-
* appropriate ProxyNodes for all values that are created inside the loop and that flow out of
2276-
* the loop.
2176+
* mostly the state that we want, we only need to tweak it a little bit to account for phis.
22772177
*/
22782178
private void assignLoopExitState(LoopExitNode loopExit, AbstractMergeNode loopExplosionMerge, AbstractEndNode loopExplosionEnd) {
22792179
FrameState oldState = loopExplosionMerge.stateAfter();
22802180

2281-
/* Collect all nodes that are in the FrameState at the LoopBegin. */
2282-
EconomicSet<Node> loopBeginValues = EconomicSet.create(Equivalence.IDENTITY);
2283-
for (FrameState state = loopExit.loopBegin().stateAfter(); state != null; state = state.outerFrameState()) {
2284-
for (ValueNode value : state.values()) {
2285-
if (value != null && !value.isConstant() && !loopExit.loopBegin().isPhiAtMerge(value)) {
2286-
loopBeginValues.add(ProxyPlaceholder.unwrap(value));
2287-
}
2288-
}
2289-
}
2290-
2291-
/* The framestate may contain a value multiple times */
2292-
EconomicMap<ValueNode, ValueNode> old2NewValues = EconomicMap.create();
22932181
FrameState.ValueFunction valueFunction = new FrameState.ValueFunction() {
2294-
22952182
@Override
2296-
public ValueNode apply(int index, ValueNode v) {
2297-
if (v != null && old2NewValues.containsKey(v)) {
2298-
return old2NewValues.get(v);
2299-
}
2300-
2301-
ValueNode value = v;
2302-
ValueNode realValue = ProxyPlaceholder.unwrap(value);
2303-
2183+
public ValueNode apply(int index, ValueNode value) {
23042184
/*
23052185
* The LoopExit is inserted before the existing merge, i.e., separately for every
23062186
* branch that leads to the merge. So for phi functions of the merge, we need to
23072187
* take the input that corresponds to our branch.
23082188
*/
2309-
if (realValue instanceof PhiNode && loopExplosionMerge.isPhiAtMerge(realValue)) {
2310-
value = ((PhiNode) realValue).valueAt(loopExplosionEnd);
2311-
realValue = ProxyPlaceholder.unwrap(value);
2312-
}
2313-
2314-
if (realValue == null || realValue.isConstant() || loopBeginValues.contains(realValue) || !graph.isNew(methodScope.methodStartMark, realValue)) {
2315-
/*
2316-
* value v, input to the old state, can already be a proxy placeholder node to
2317-
* another, dominating loop exit, we must not take the unwrapped value in this
2318-
* case but the properly proxied one
2319-
*/
2320-
if (v != null) {
2321-
old2NewValues.put(v, v);
2322-
}
2323-
return v;
2324-
} else {
2325-
/*
2326-
* The node is not in the FrameState of the LoopBegin, i.e., it is a value
2327-
* computed inside the loop.
2328-
*/
2329-
GraalError.guarantee(value instanceof ProxyPlaceholder && ((ProxyPlaceholder) value).proxyPoint == loopExplosionMerge,
2330-
"Value flowing out of loop, but we are not prepared to insert a ProxyNode");
2331-
2332-
ProxyPlaceholder proxyPlaceholder = (ProxyPlaceholder) value;
2333-
ValueProxyNode proxy = ProxyNode.forValue(proxyPlaceholder.value, loopExit);
2334-
proxyPlaceholder.setValue(proxy);
2335-
old2NewValues.put(v, proxy);
2336-
return proxy;
2189+
if (loopExplosionMerge.isPhiAtMerge(value)) {
2190+
assert value instanceof PhiNode : "isPhiAtMerge should guarantee that value is a PhiNode";
2191+
return ((PhiNode) value).valueAt(loopExplosionEnd);
23372192
}
2193+
return value;
23382194
}
23392195
};
23402196

@@ -2344,6 +2200,12 @@ public ValueNode apply(int index, ValueNode v) {
23442200
loopExit.setStateAfter(graph.add(newState));
23452201
}
23462202

2203+
FrameState newState = oldState.duplicate(valueFunction);
2204+
2205+
assert loopExit.stateAfter() == null;
2206+
loopExit.setStateAfter(graph.add(newState));
2207+
}
2208+
23472209
/**
23482210
* Graal does not support irreducible loops (loops with more than one entry point). There are
23492211
* two ways to make them reducible: 1) duplicate nodes (peel a loop iteration starting at the
@@ -2389,7 +2251,7 @@ private void handleIrreducibleLoop(Loop loop) {
23892251
ValueNode curLoopValue = loopValues.get(i);
23902252
ValueNode curExplosionHeadValue = explosionHeadValues.get(i);
23912253

2392-
if (ProxyPlaceholder.unwrap(curLoopValue) != ProxyPlaceholder.unwrap(curExplosionHeadValue)) {
2254+
if (curLoopValue != curExplosionHeadValue) {
23932255
if (loopVariableIndex != -1) {
23942256
throw bailout("must have only one variable that is changed in loop. " + loopValue + " != " + explosionHeadValue + " and " + curLoopValue + " != " + curExplosionHeadValue);
23952257
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/PostPartialEvaluationSuite.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 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
@@ -25,6 +25,7 @@
2525
package jdk.graal.compiler.truffle;
2626

2727
import jdk.graal.compiler.loop.phases.ConvertDeoptimizeToGuardPhase;
28+
import jdk.graal.compiler.truffle.phases.InsertProxyPhase;
2829
import jdk.graal.compiler.nodes.StructuredGraph;
2930
import jdk.graal.compiler.nodes.java.MethodCallTargetNode;
3031
import jdk.graal.compiler.options.OptionValues;
@@ -42,6 +43,7 @@ public class PostPartialEvaluationSuite extends PhaseSuite<TruffleTierContext> {
4243
@SuppressWarnings("this-escape")
4344
public PostPartialEvaluationSuite(OptionValues optionValues, boolean iterativePartialEscape) {
4445
CanonicalizerPhase canonicalizerPhase = CanonicalizerPhase.create();
46+
appendPhase(new InsertProxyPhase(optionValues));
4547
appendPhase(new ConvertDeoptimizeToGuardPhase(canonicalizerPhase));
4648
appendPhase(new InlineReplacementsPhase());
4749
appendPhase(canonicalizerPhase);

0 commit comments

Comments
 (0)