Skip to content

Commit 52d7dcc

Browse files
committed
[GR-62822] Loop peeling: peeling with floating guard fixes.
PullRequest: graal/20265
2 parents f27bf50 + 327115f commit 52d7dcc

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/loop/LoopFragmentInside.java

+81
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import jdk.graal.compiler.nodes.FixedWithNextNode;
5454
import jdk.graal.compiler.nodes.FrameState;
5555
import jdk.graal.compiler.nodes.GraphState;
56+
import jdk.graal.compiler.nodes.GuardNode;
5657
import jdk.graal.compiler.nodes.GuardPhiNode;
5758
import jdk.graal.compiler.nodes.GuardProxyNode;
5859
import jdk.graal.compiler.nodes.IfNode;
@@ -149,8 +150,84 @@ public Loop loop() {
149150
return super.loop();
150151
}
151152

153+
/**
154+
* Collects all floating guards with outside anchors.
155+
*
156+
* Floating guards and loop peeling: when peeling loops while we have floating guards we have to
157+
* take special care about the position of a guard after peeling. A guard has 2 things that
158+
* define its position: the anchor and the condition. Both are relevant to execute the guard at
159+
* the correct point in time. The correct point is as early as possible without being to eager.
160+
* Peeling can now "free" guards of their conditional scheduling position. What does that mean?
161+
*
162+
* Consider the following loop
163+
*
164+
* <pre>
165+
* int phi = 0;
166+
* int sum = 0;
167+
* while (true) {
168+
* if (phi > 0) {
169+
* // Guard of the second iteration should not float before the
170+
* // first
171+
* // break
172+
* GraalDirectives.deoptimizeAndInvalidate();
173+
* }
174+
* phi = (int) Math.tan(phi + 1);
175+
* sum += phi;
176+
* if (flag) {
177+
* break;
178+
* }
179+
* }
180+
* </pre>
181+
*
182+
* The position of the guard is determined by the scheduling of the inputs: that is the phi. The
183+
* anchor is already outside of the loop. If we peel this loop now 2 times and duplicate the
184+
* guard, the fact that the phi input is gone lets the guard float up above the original part of
185+
* the loop's previous iteration. That means we have 2 duplicates of this guard one that evals
186+
* the condition {@code 0>0} which evaluates to {@code false}. The other one, of iteration 1
187+
* evals then the condition {@code 1>0} which is true and thus unconditionally will yield a
188+
* deopt. That guard however can float to the beginning of the method and cause too eager
189+
* deopts. The fact that peeling replaces phi nodes with their inputs at the respective
190+
* iteration "frees" the guards of their scheduling positions. Thus, we manually inject the
191+
* anchor to be the end of the last iteration that was peeled. That naturally is the correct
192+
* position. Later optimization can specualtively move guards further.
193+
*/
194+
private static NodeBitMap collectExistingGuardsWithOutsideAnchors(StructuredGraph graph, Loop loop) {
195+
NodeBitMap guardsWithOutsideAnchors = null;
196+
if (graph.getGuardsStage().allowsFloatingGuards()) {
197+
for (Node inside : loop.inside().nodes()) {
198+
if (inside instanceof GuardNode g) {
199+
// the anchor is already outside the loop
200+
if (!loop.whole().contains(g.getAnchor().asNode())) {
201+
if (guardsWithOutsideAnchors == null) {
202+
guardsWithOutsideAnchors = graph.createNodeBitMap();
203+
}
204+
guardsWithOutsideAnchors.mark(g);
205+
}
206+
}
207+
}
208+
}
209+
return guardsWithOutsideAnchors;
210+
}
211+
212+
private static void reconnectAnchors(Loop loop, LoopFragment fragment, StructuredGraph graph, NodeBitMap guardsWithOutsideAnchors) {
213+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "Before correcting floating guard anchors for loop %s", loop.loopBegin());
214+
AnchoringNode newAnchor = AbstractBeginNode.prevBegin(fragment.getDuplicatedNode(loop.loopBegin()));
215+
for (Node originalGuard : guardsWithOutsideAnchors) {
216+
assert originalGuard instanceof GuardNode : Assertions.errorMessage(originalGuard, guardsWithOutsideAnchors);
217+
GuardNode g = (GuardNode) originalGuard;
218+
GuardNode duplicate = (GuardNode) fragment.getDuplicatedNode(g.asNode());
219+
if (duplicate != null) {
220+
duplicate.setAnchor(newAnchor);
221+
}
222+
}
223+
graph.getDebug().dump(DebugContext.VERY_DETAILED_LEVEL, graph, "After correcting floating guard anchors for loop %s", loop.loopBegin());
224+
}
225+
152226
@Override
153227
public void insertBefore(Loop loop) {
228+
StructuredGraph graph = loop.loopBegin().graph();
229+
NodeBitMap guardsWithOutsideAnchors = collectExistingGuardsWithOutsideAnchors(graph, loop);
230+
154231
assert this.isDuplicate();
155232
assert this.original().loop() == loop : "Original loop " + this.original().loop() + " != " + loop;
156233

@@ -165,6 +242,10 @@ public void insertBefore(Loop loop) {
165242
AbstractBeginNode entry = getDuplicatedNode(loop.loopBegin());
166243
loop.entryPoint().replaceAtPredecessor(entry);
167244
end.setNext(loop.entryPoint());
245+
246+
if (graph.getGuardsStage().allowsFloatingGuards() && guardsWithOutsideAnchors != null) {
247+
reconnectAnchors(loop, this, graph, guardsWithOutsideAnchors);
248+
}
168249
}
169250

170251
/**

0 commit comments

Comments
 (0)