53
53
import jdk .graal .compiler .nodes .FixedWithNextNode ;
54
54
import jdk .graal .compiler .nodes .FrameState ;
55
55
import jdk .graal .compiler .nodes .GraphState ;
56
+ import jdk .graal .compiler .nodes .GuardNode ;
56
57
import jdk .graal .compiler .nodes .GuardPhiNode ;
57
58
import jdk .graal .compiler .nodes .GuardProxyNode ;
58
59
import jdk .graal .compiler .nodes .IfNode ;
@@ -149,8 +150,84 @@ public Loop loop() {
149
150
return super .loop ();
150
151
}
151
152
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
+
152
226
@ Override
153
227
public void insertBefore (Loop loop ) {
228
+ StructuredGraph graph = loop .loopBegin ().graph ();
229
+ NodeBitMap guardsWithOutsideAnchors = collectExistingGuardsWithOutsideAnchors (graph , loop );
230
+
154
231
assert this .isDuplicate ();
155
232
assert this .original ().loop () == loop : "Original loop " + this .original ().loop () + " != " + loop ;
156
233
@@ -165,6 +242,10 @@ public void insertBefore(Loop loop) {
165
242
AbstractBeginNode entry = getDuplicatedNode (loop .loopBegin ());
166
243
loop .entryPoint ().replaceAtPredecessor (entry );
167
244
end .setNext (loop .entryPoint ());
245
+
246
+ if (graph .getGuardsStage ().allowsFloatingGuards () && guardsWithOutsideAnchors != null ) {
247
+ reconnectAnchors (loop , this , graph , guardsWithOutsideAnchors );
248
+ }
168
249
}
169
250
170
251
/**
0 commit comments