1 /*
   2  * Copyright (c) 2012, 2012, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.loop.phases;
  24 
  25 import static org.graalvm.compiler.core.common.GraalOptions.MaximumDesiredSize;
  26 import static org.graalvm.compiler.loop.MathUtil.add;
  27 import static org.graalvm.compiler.loop.MathUtil.sub;
  28 
  29 import java.util.ArrayList;
  30 import java.util.Iterator;
  31 import java.util.List;
  32 
  33 import org.graalvm.compiler.core.common.RetryableBailoutException;
  34 import org.graalvm.compiler.core.common.type.Stamp;
  35 import org.graalvm.compiler.debug.DebugContext;
  36 import org.graalvm.compiler.debug.GraalError;
  37 import org.graalvm.compiler.graph.Graph.Mark;
  38 import org.graalvm.compiler.graph.Node;
  39 import org.graalvm.compiler.graph.NodeWorkList;
  40 import org.graalvm.compiler.graph.Position;
  41 import org.graalvm.compiler.loop.BasicInductionVariable;
  42 import org.graalvm.compiler.loop.CountedLoopInfo;
  43 import org.graalvm.compiler.loop.DerivedInductionVariable;
  44 import org.graalvm.compiler.loop.InductionVariable;
  45 import org.graalvm.compiler.loop.InductionVariable.Direction;
  46 import org.graalvm.compiler.loop.LoopEx;
  47 import org.graalvm.compiler.loop.LoopFragmentInside;
  48 import org.graalvm.compiler.loop.LoopFragmentWhole;
  49 import org.graalvm.compiler.nodeinfo.InputType;
  50 import org.graalvm.compiler.nodes.AbstractBeginNode;
  51 import org.graalvm.compiler.nodes.AbstractEndNode;
  52 import org.graalvm.compiler.nodes.AbstractMergeNode;
  53 import org.graalvm.compiler.nodes.BeginNode;
  54 import org.graalvm.compiler.nodes.ConstantNode;
  55 import org.graalvm.compiler.nodes.ControlSplitNode;
  56 import org.graalvm.compiler.nodes.EndNode;
  57 import org.graalvm.compiler.nodes.FixedNode;
  58 import org.graalvm.compiler.nodes.FixedWithNextNode;
  59 import org.graalvm.compiler.nodes.IfNode;
  60 import org.graalvm.compiler.nodes.LogicNode;
  61 import org.graalvm.compiler.nodes.LoopBeginNode;
  62 import org.graalvm.compiler.nodes.LoopEndNode;
  63 import org.graalvm.compiler.nodes.LoopExitNode;
  64 import org.graalvm.compiler.nodes.PhiNode;
  65 import org.graalvm.compiler.nodes.StructuredGraph;
  66 import org.graalvm.compiler.nodes.ValueNode;
  67 import org.graalvm.compiler.nodes.ValuePhiNode;
  68 import org.graalvm.compiler.nodes.calc.AddNode;
  69 import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
  70 import org.graalvm.compiler.nodes.calc.CompareNode;
  71 import org.graalvm.compiler.nodes.calc.ConditionalNode;
  72 import org.graalvm.compiler.nodes.calc.IntegerLessThanNode;
  73 import org.graalvm.compiler.nodes.calc.SubNode;
  74 import org.graalvm.compiler.nodes.extended.SwitchNode;
  75 import org.graalvm.compiler.nodes.util.GraphUtil;
  76 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
  77 import org.graalvm.compiler.phases.tiers.PhaseContext;
  78 
  79 public abstract class LoopTransformations {
  80 
  81     private LoopTransformations() {
  82         // does not need to be instantiated
  83     }
  84 
  85     public static void peel(LoopEx loop) {
  86         loop.inside().duplicate().insertBefore(loop);
  87         loop.loopBegin().setLoopFrequency(Math.max(0.0, loop.loopBegin().loopFrequency() - 1));
  88     }
  89 
  90     public static void fullUnroll(LoopEx loop, PhaseContext context, CanonicalizerPhase canonicalizer) {
  91         // assert loop.isCounted(); //TODO (gd) strengthen : counted with known trip count
  92         LoopBeginNode loopBegin = loop.loopBegin();
  93         StructuredGraph graph = loopBegin.graph();
  94         int initialNodeCount = graph.getNodeCount();
  95         while (!loopBegin.isDeleted()) {
  96             Mark mark = graph.getMark();
  97             peel(loop);
  98             canonicalizer.applyIncremental(graph, context, mark);
  99             loop.invalidateFragments();
 100             if (graph.getNodeCount() > initialNodeCount + MaximumDesiredSize.getValue(graph.getOptions()) * 2) {
 101                 throw new RetryableBailoutException("FullUnroll : Graph seems to grow out of proportion");
 102             }
 103         }
 104     }
 105 
 106     public static void unswitch(LoopEx loop, List<ControlSplitNode> controlSplitNodeSet) {
 107         ControlSplitNode firstNode = controlSplitNodeSet.iterator().next();
 108         LoopFragmentWhole originalLoop = loop.whole();
 109         StructuredGraph graph = firstNode.graph();
 110 
 111         loop.loopBegin().incrementUnswitches();
 112 
 113         // create new control split out of loop
 114         ControlSplitNode newControlSplit = (ControlSplitNode) firstNode.copyWithInputs();
 115         originalLoop.entryPoint().replaceAtPredecessor(newControlSplit);
 116 
 117         /*
 118          * The code below assumes that all of the control split nodes have the same successor
 119          * structure, which should have been enforced by findUnswitchable.
 120          */
 121         Iterator<Position> successors = firstNode.successorPositions().iterator();
 122         assert successors.hasNext();
 123         // original loop is used as first successor
 124         Position firstPosition = successors.next();
 125         AbstractBeginNode originalLoopBegin = BeginNode.begin(originalLoop.entryPoint());
 126         firstPosition.set(newControlSplit, originalLoopBegin);
 127 
 128         while (successors.hasNext()) {
 129             Position position = successors.next();
 130             // create a new loop duplicate and connect it.
 131             LoopFragmentWhole duplicateLoop = originalLoop.duplicate();
 132             AbstractBeginNode newBegin = BeginNode.begin(duplicateLoop.entryPoint());
 133             position.set(newControlSplit, newBegin);
 134 
 135             // For each cloned ControlSplitNode, simplify the proper path
 136             for (ControlSplitNode controlSplitNode : controlSplitNodeSet) {
 137                 ControlSplitNode duplicatedControlSplit = duplicateLoop.getDuplicatedNode(controlSplitNode);
 138                 if (duplicatedControlSplit.isAlive()) {
 139                     AbstractBeginNode survivingSuccessor = (AbstractBeginNode) position.get(duplicatedControlSplit);
 140                     survivingSuccessor.replaceAtUsages(InputType.Guard, newBegin);
 141                     graph.removeSplitPropagate(duplicatedControlSplit, survivingSuccessor);
 142                 }
 143             }
 144         }
 145         // original loop is simplified last to avoid deleting controlSplitNode too early
 146         for (ControlSplitNode controlSplitNode : controlSplitNodeSet) {
 147             if (controlSplitNode.isAlive()) {
 148                 AbstractBeginNode survivingSuccessor = (AbstractBeginNode) firstPosition.get(controlSplitNode);
 149                 survivingSuccessor.replaceAtUsages(InputType.Guard, originalLoopBegin);
 150                 graph.removeSplitPropagate(controlSplitNode, survivingSuccessor);
 151             }
 152         }
 153 
 154         // TODO (gd) probabilities need some amount of fixup.. (probably also in other transforms)
 155     }
 156 
 157     public static boolean partialUnroll(LoopEx loop, StructuredGraph graph) {
 158         assert loop.loopBegin().isMainLoop();
 159         graph.getDebug().log("LoopPartialUnroll %s", loop);
 160         boolean changed = false;
 161         CountedLoopInfo mainCounted = loop.counted();
 162         LoopBeginNode mainLoopBegin = loop.loopBegin();
 163         InductionVariable iv = mainCounted.getCounter();
 164         IfNode mainLimit = mainCounted.getLimitTest();
 165         LogicNode ifTest = mainLimit.condition();
 166         CompareNode compareNode = (CompareNode) ifTest;
 167         ValueNode compareBound = null;
 168         ValueNode curPhi = iv.valueNode();
 169         if (compareNode.getX() == curPhi) {
 170             compareBound = compareNode.getY();
 171         } else if (compareNode.getY() == curPhi) {
 172             compareBound = compareNode.getX();
 173         }
 174         LoopFragmentInside newSegment = loop.inside().duplicate();
 175         newSegment.insertWithinAfter(loop);
 176         graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication inside %s", mainLoopBegin);
 177         ValueNode inductionNode = iv.valueNode();
 178         Node newStrideNode = null;
 179         for (PhiNode mainPhiNode : mainLoopBegin.phis()) {
 180             Node segmentOrigOp = null;
 181             Node replacementOp = null;
 182             changed = false;
 183             // Rework each phi with a loop carried dependence
 184             for (Node phiUsage : mainPhiNode.usages()) {
 185                 if (!loop.isOutsideLoop(phiUsage)) {
 186                     for (int i = 1; i < mainPhiNode.valueCount(); i++) {
 187                         ValueNode v = mainPhiNode.valueAt(i);
 188                         if (mainPhiNode != inductionNode) {
 189                             if (closureOnPhiInputToPhiUse(v, phiUsage, loop, graph)) {
 190                                 segmentOrigOp = v;
 191                                 Node node = newSegment.getDuplicatedNode(v);
 192                                 replacementOp = updateUnrollSegmentValue(mainPhiNode, inductionNode, phiUsage, v, newSegment);
 193 
 194                                 // Update the induction phi with new stride node
 195                                 mainPhiNode.setValueAt(i, (ValueNode) node);
 196                                 // This is for induction variables not referenced in the loop body
 197                                 if (inductionNode == v) {
 198                                     newStrideNode = node;
 199                                 }
 200                                 changed = true;
 201                                 break;
 202                             }
 203                         } else if (v == phiUsage) {
 204                             segmentOrigOp = phiUsage;
 205                             Node node = newSegment.getDuplicatedNode(phiUsage);
 206                             newStrideNode = node;
 207                             replacementOp = updateUnrollSegmentValue(mainPhiNode, inductionNode, phiUsage, phiUsage, newSegment);
 208 
 209                             // Update the induction phi with new stride node
 210                             mainPhiNode.setValueAt(i, (ValueNode) node);
 211                             changed = true;
 212                             break;
 213                         }
 214                     }
 215                 }
 216                 if (changed) {
 217                     break;
 218                 }
 219             }
 220 
 221             if (changed) {
 222                 // Patch the new segments induction uses of replacementOp with the old stride node
 223                 for (Node usage : mainPhiNode.usages()) {
 224                     if (usage != segmentOrigOp) {
 225                         if (!loop.isOutsideLoop(usage)) {
 226                             Node node = newSegment.getDuplicatedNode(usage);
 227                             if (node instanceof CompareNode) {
 228                                 continue;
 229                             }
 230                             node.replaceFirstInput(replacementOp, segmentOrigOp);
 231                         }
 232                     }
 233                 }
 234             }
 235         }
 236 
 237         if (changed && newStrideNode == null) {
 238             throw GraalError.shouldNotReachHere("Can't find stride node");
 239         }
 240         if (newStrideNode != null) {
 241             // If merge the duplicate code into the loop and remove redundant code
 242             placeNewSegmentAndCleanup(mainCounted, mainLoopBegin, newSegment);
 243             int unrollFactor = mainLoopBegin.getUnrollFactor();
 244             // First restore the old pattern of the loop exit condition so we can update it one way
 245             if (unrollFactor > 1) {
 246                 if (compareBound instanceof SubNode) {
 247                     SubNode newLimit = (SubNode) compareBound;
 248                     ValueNode oldcompareBound = newLimit.getX();
 249                     compareNode.replaceFirstInput(newLimit, oldcompareBound);
 250                     newLimit.safeDelete();
 251                     compareBound = oldcompareBound;
 252                 } else if (compareBound instanceof AddNode) {
 253                     AddNode newLimit = (AddNode) compareBound;
 254                     ValueNode oldcompareBound = newLimit.getX();
 255                     compareNode.replaceFirstInput(newLimit, oldcompareBound);
 256                     newLimit.safeDelete();
 257                     compareBound = oldcompareBound;
 258                 }
 259             }
 260             unrollFactor *= 2;
 261             mainLoopBegin.setUnrollFactor(unrollFactor);
 262             // Reset stride to include new segment in loop control.
 263             long oldStride = iv.constantStride() * 2;
 264             // Now update the induction op and the exit condition
 265             if (iv instanceof BasicInductionVariable) {
 266                 BasicInductionVariable biv = (BasicInductionVariable) iv;
 267                 BinaryArithmeticNode<?> newOp = (BinaryArithmeticNode<?>) newStrideNode;
 268                 Stamp strideStamp = newOp.stamp();
 269                 ConstantNode newStrideVal = graph.unique(ConstantNode.forIntegerStamp(strideStamp, oldStride));
 270                 newOp.setY(newStrideVal);
 271                 biv.setOP(newOp);
 272                 // Now use the current unrollFactor to update the exit condition to power of two
 273                 if (unrollFactor > 1) {
 274                     if (iv.direction() == Direction.Up) {
 275                         int modulas = (unrollFactor - 1);
 276                         ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(strideStamp, modulas));
 277                         ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
 278                         compareNode.replaceFirstInput(compareBound, newLimit);
 279                     } else if (iv.direction() == Direction.Down) {
 280                         int modulas = (unrollFactor - 1);
 281                         ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(strideStamp, modulas));
 282                         ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
 283                         compareNode.replaceFirstInput(compareBound, newLimit);
 284                     }
 285                 }
 286                 mainLoopBegin.setLoopFrequency(mainLoopBegin.loopFrequency() / 2);
 287             }
 288             changed = true;
 289         }
 290         if (changed) {
 291             graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "LoopPartialUnroll %s", loop);
 292         }
 293         return changed;
 294     }
 295 
 296     private static Node updateUnrollSegmentValue(PhiNode mainPhiNode, Node inductionNode, Node phiUsage, Node patchNode, LoopFragmentInside newSegment) {
 297         Node node = newSegment.getDuplicatedNode(phiUsage);
 298         assert node != null : phiUsage;
 299         Node replacementOp = null;
 300         int inputCnt = 0;
 301         for (Node input : phiUsage.inputs()) {
 302             inputCnt++;
 303             if (input == mainPhiNode) {
 304                 break;
 305             }
 306         }
 307         int newInputCnt = 0;
 308         for (Node input : node.inputs()) {
 309             newInputCnt++;
 310             if (newInputCnt == inputCnt) {
 311                 replacementOp = input;
 312                 if (mainPhiNode == inductionNode) {
 313                     node.replaceFirstInput(input, mainPhiNode);
 314                 } else {
 315                     node.replaceFirstInput(input, patchNode);
 316                 }
 317                 break;
 318             }
 319         }
 320         return replacementOp;
 321     }
 322 
 323     private static boolean closureOnPhiInputToPhiUse(Node inNode, Node usage, LoopEx loop, StructuredGraph graph) {
 324         NodeWorkList nodes = graph.createNodeWorkList();
 325         nodes.add(inNode);
 326         // Now walk from the inNode to usage if we can find it else we do not have closure
 327         for (Node node : nodes) {
 328             if (node == usage) {
 329                 return true;
 330             }
 331             for (Node input : node.inputs()) {
 332                 if (!loop.isOutsideLoop(input)) {
 333                     if (input != usage) {
 334                         nodes.add(input);
 335                     } else {
 336                         return true;
 337                         // For any reason if we have completed a closure, stop processing more
 338                     }
 339                 }
 340             }
 341         }
 342         return false;
 343     }
 344 
 345     private static void placeNewSegmentAndCleanup(CountedLoopInfo mainCounted, LoopBeginNode mainLoopBegin, LoopFragmentInside newSegment) {
 346         // Discard the segment entry and its flow, after if merging it into the loop
 347         StructuredGraph graph = mainLoopBegin.graph();
 348         IfNode loopTest = mainCounted.getLimitTest();
 349         IfNode newSegmentTest = newSegment.getDuplicatedNode(loopTest);
 350         AbstractBeginNode trueSuccessor = loopTest.trueSuccessor();
 351         AbstractBeginNode falseSuccessor = loopTest.falseSuccessor();
 352         FixedNode firstNode;
 353         boolean codeInTrueSide = false;
 354         if (trueSuccessor == mainCounted.getBody()) {
 355             firstNode = trueSuccessor.next();
 356             codeInTrueSide = true;
 357         } else {
 358             assert (falseSuccessor == mainCounted.getBody());
 359             firstNode = falseSuccessor.next();
 360         }
 361         trueSuccessor = newSegmentTest.trueSuccessor();
 362         falseSuccessor = newSegmentTest.falseSuccessor();
 363         for (Node usage : falseSuccessor.anchored().snapshot()) {
 364             usage.replaceFirstInput(falseSuccessor, loopTest.falseSuccessor());
 365         }
 366         for (Node usage : trueSuccessor.anchored().snapshot()) {
 367             usage.replaceFirstInput(trueSuccessor, loopTest.trueSuccessor());
 368         }
 369         AbstractBeginNode startBlockNode;
 370         if (codeInTrueSide) {
 371             startBlockNode = trueSuccessor;
 372         } else {
 373             graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, mainLoopBegin.graph(), "before");
 374             startBlockNode = falseSuccessor;
 375         }
 376         FixedNode lastNode = getBlockEnd(startBlockNode);
 377         LoopEndNode loopEndNode = getSingleLoopEndFromLoop(mainLoopBegin);
 378         FixedNode lastCodeNode = (FixedNode) loopEndNode.predecessor();
 379         FixedNode newSegmentFirstNode = newSegment.getDuplicatedNode(firstNode);
 380         FixedNode newSegmentLastNode = newSegment.getDuplicatedNode(lastCodeNode);
 381         graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "Before placing segment");
 382         if (firstNode instanceof LoopEndNode) {
 383             GraphUtil.killCFG(newSegment.getDuplicatedNode(mainLoopBegin));
 384         } else {
 385             newSegmentLastNode.clearSuccessors();
 386             startBlockNode.setNext(lastNode);
 387             lastCodeNode.replaceFirstSuccessor(loopEndNode, newSegmentFirstNode);
 388             newSegmentLastNode.replaceFirstSuccessor(lastNode, loopEndNode);
 389             FixedWithNextNode oldLastNode = (FixedWithNextNode) lastCodeNode;
 390             oldLastNode.setNext(newSegmentFirstNode);
 391             FixedWithNextNode newLastNode = (FixedWithNextNode) newSegmentLastNode;
 392             newLastNode.setNext(loopEndNode);
 393             startBlockNode.clearSuccessors();
 394             lastNode.safeDelete();
 395             Node newSegmentTestStart = newSegmentTest.predecessor();
 396             LogicNode newSegmentIfTest = newSegmentTest.condition();
 397             newSegmentTestStart.clearSuccessors();
 398             newSegmentTest.safeDelete();
 399             newSegmentIfTest.safeDelete();
 400             trueSuccessor.safeDelete();
 401             falseSuccessor.safeDelete();
 402             newSegmentTestStart.safeDelete();
 403         }
 404         graph.getDebug().dump(DebugContext.DETAILED_LEVEL, loopEndNode.graph(), "After placing segment");
 405     }
 406 
 407     // This function splits candidate loops into pre, main and post loops,
 408     // dividing the iteration space to facilitate the majority of iterations
 409     // being executed in a main loop, which will have RCE implemented upon it.
 410     // The initial loop form is constrained to single entry/exit, but can have
 411     // flow. The translation looks like:
 412     //
 413     //  @formatter:off
 414     //
 415     //       (Simple Loop entry)                   (Pre Loop Entry)
 416     //                |                                  |
 417     //         (LoopBeginNode)                    (LoopBeginNode)
 418     //                |                                  |
 419     //       (Loop Control Test)<------   ==>  (Loop control Test)<------
 420     //         /               \       \         /               \       \
 421     //    (Loop Exit)      (Loop Body) |    (Loop Exit)      (Loop Body) |
 422     //        |                |       |        |                |       |
 423     // (continue code)     (Loop End)  |  if (M < length)*   (Loop End)  |
 424     //                         \       /       /      \           \      /
 425     //                          ----->        /       |            ----->
 426     //                                       /  if ( ... )*
 427     //                                      /     /       \
 428     //                                     /     /         \
 429     //                                    /     /           \
 430     //                                   |     /     (Main Loop Entry)
 431     //                                   |    |             |
 432     //                                   |    |      (LoopBeginNode)
 433     //                                   |    |             |
 434     //                                   |    |     (Loop Control Test)<------
 435     //                                   |    |      /               \        \
 436     //                                   |    |  (Loop Exit)      (Loop Body) |
 437     //                                    \   \      |                |       |
 438     //                                     \   \     |            (Loop End)  |
 439     //                                      \   \    |                \       /
 440     //                                       \   \   |                 ------>
 441     //                                        \   \  |
 442     //                                      (Main Loop Merge)*
 443     //                                               |
 444     //                                      (Post Loop Entry)
 445     //                                               |
 446     //                                        (LoopBeginNode)
 447     //                                               |
 448     //                                       (Loop Control Test)<-----
 449     //                                        /               \       \
 450     //                                    (Loop Exit)     (Loop Body) |
 451     //                                        |               |       |
 452     //                                 (continue code)    (Loop End)  |
 453     //                                                         \      /
 454     //                                                          ----->
 455     //
 456     // Key: "*" = optional.
 457     // @formatter:on
 458     //
 459     // The value "M" is the maximal value of the loop trip for the original
 460     // loop. The value of "length" is applicable to the number of arrays found
 461     // in the loop but is reduced if some or all of the arrays are known to be
 462     // the same length as "M". The maximum number of tests can be equal to the
 463     // number of arrays in the loop, where multiple instances of an array are
 464     // subsumed into a single test for that arrays length.
 465     //
 466     // If the optional main loop entry tests are absent, the Pre Loop exit
 467     // connects to the Main loops entry and there is no merge hanging off the
 468     // main loops exit to converge flow from said tests. All split use data
 469     // flow is mitigated through phi(s) in the main merge if present and
 470     // passed through the main and post loop phi(s) from the originating pre
 471     // loop with final phi(s) and data flow patched to the "continue code".
 472     // The pre loop is constrained to one iteration for now and will likely
 473     // be updated to produce vector alignment if applicable.
 474 
 475     public static void insertPrePostLoops(LoopEx loop, StructuredGraph graph) {
 476         graph.getDebug().log("LoopTransformations.insertPrePostLoops %s", loop);
 477         LoopFragmentWhole preLoop = loop.whole();
 478         CountedLoopInfo preCounted = preLoop.loop().counted();
 479         IfNode preLimit = preCounted.getLimitTest();
 480         if (preLimit != null) {
 481             LoopBeginNode preLoopBegin = loop.loopBegin();
 482             InductionVariable preIv = preCounted.getCounter();
 483             LoopExitNode preLoopExitNode = getSingleExitFromLoop(preLoopBegin);
 484             FixedNode continuationNode = preLoopExitNode.next();
 485 
 486             // Each duplication is inserted after the original, ergo create the post loop first
 487             LoopFragmentWhole mainLoop = preLoop.duplicate();
 488             LoopFragmentWhole postLoop = preLoop.duplicate();
 489             preLoopBegin.incrementSplits();
 490             preLoopBegin.incrementSplits();
 491             preLoopBegin.setPreLoop();
 492             graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication");
 493             LoopBeginNode mainLoopBegin = mainLoop.getDuplicatedNode(preLoopBegin);
 494             mainLoopBegin.setMainLoop();
 495             LoopBeginNode postLoopBegin = postLoop.getDuplicatedNode(preLoopBegin);
 496             postLoopBegin.setPostLoop();
 497 
 498             EndNode postEndNode = getBlockEndAfterLoopExit(postLoopBegin);
 499             AbstractMergeNode postMergeNode = postEndNode.merge();
 500             LoopExitNode postLoopExitNode = getSingleExitFromLoop(postLoopBegin);
 501 
 502             // Update the main loop phi initialization to carry from the pre loop
 503             for (PhiNode prePhiNode : preLoopBegin.phis()) {
 504                 PhiNode mainPhiNode = mainLoop.getDuplicatedNode(prePhiNode);
 505                 mainPhiNode.setValueAt(0, prePhiNode);
 506             }
 507 
 508             EndNode mainEndNode = getBlockEndAfterLoopExit(mainLoopBegin);
 509             AbstractMergeNode mainMergeNode = mainEndNode.merge();
 510             AbstractEndNode postEntryNode = postLoopBegin.forwardEnd();
 511 
 512             // In the case of no Bounds tests, we just flow right into the main loop
 513             AbstractBeginNode mainLandingNode = BeginNode.begin(postEntryNode);
 514             LoopExitNode mainLoopExitNode = getSingleExitFromLoop(mainLoopBegin);
 515             mainLoopExitNode.setNext(mainLandingNode);
 516             preLoopExitNode.setNext(mainLoopBegin.forwardEnd());
 517 
 518             // Add and update any phi edges as per merge usage as needed and update usages
 519             processPreLoopPhis(loop, mainLoop, postLoop);
 520             continuationNode.predecessor().clearSuccessors();
 521             postLoopExitNode.setNext(continuationNode);
 522             cleanupMerge(postMergeNode, postLoopExitNode);
 523             cleanupMerge(mainMergeNode, mainLandingNode);
 524 
 525             // Change the preLoop to execute one iteration for now
 526             updateMainLoopLimit(preLimit, preIv, mainLoop);
 527             updatePreLoopLimit(preLimit, preIv, preCounted);
 528             preLoopBegin.setLoopFrequency(1);
 529             mainLoopBegin.setLoopFrequency(Math.max(0.0, mainLoopBegin.loopFrequency() - 2));
 530             postLoopBegin.setLoopFrequency(Math.max(0.0, postLoopBegin.loopFrequency() - 1));
 531         }
 532         graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "InsertPrePostLoops %s", loop);
 533     }
 534 
 535     /**
 536      * Cleanup the merge and remove the predecessors too.
 537      */
 538     private static void cleanupMerge(AbstractMergeNode mergeNode, AbstractBeginNode landingNode) {
 539         for (EndNode end : mergeNode.cfgPredecessors().snapshot()) {
 540             mergeNode.removeEnd(end);
 541             end.safeDelete();
 542         }
 543         mergeNode.prepareDelete(landingNode);
 544         mergeNode.safeDelete();
 545     }
 546 
 547     private static void processPreLoopPhis(LoopEx preLoop, LoopFragmentWhole mainLoop, LoopFragmentWhole postLoop) {
 548         // process phis for the post loop
 549         LoopBeginNode preLoopBegin = preLoop.loopBegin();
 550         for (PhiNode prePhiNode : preLoopBegin.phis()) {
 551             PhiNode postPhiNode = postLoop.getDuplicatedNode(prePhiNode);
 552             PhiNode mainPhiNode = mainLoop.getDuplicatedNode(prePhiNode);
 553             postPhiNode.setValueAt(0, mainPhiNode);
 554 
 555             // Build a work list to update the pre loop phis to the post loops phis
 556             for (Node usage : prePhiNode.usages().snapshot()) {
 557                 if (usage == mainPhiNode) {
 558                     continue;
 559                 }
 560                 if (preLoop.isOutsideLoop(usage)) {
 561                     usage.replaceFirstInput(prePhiNode, postPhiNode);
 562                 }
 563             }
 564         }
 565         for (Node node : preLoop.inside().nodes()) {
 566             for (Node externalUsage : node.usages().snapshot()) {
 567                 if (preLoop.isOutsideLoop(externalUsage)) {
 568                     Node postUsage = postLoop.getDuplicatedNode(node);
 569                     assert postUsage != null;
 570                     externalUsage.replaceFirstInput(node, postUsage);
 571                 }
 572             }
 573         }
 574     }
 575 
 576     private static LoopExitNode getSingleExitFromLoop(LoopBeginNode curLoopBegin) {
 577         assert curLoopBegin.loopExits().count() == 1;
 578         return curLoopBegin.loopExits().first();
 579     }
 580 
 581     private static LoopEndNode getSingleLoopEndFromLoop(LoopBeginNode curLoopBegin) {
 582         assert curLoopBegin.loopEnds().count() == 1;
 583         return curLoopBegin.loopEnds().first();
 584     }
 585 
 586     /**
 587      * Find the end of the block following the LoopExit.
 588      */
 589     private static EndNode getBlockEndAfterLoopExit(LoopBeginNode curLoopBegin) {
 590         FixedNode node = getSingleExitFromLoop(curLoopBegin).next();
 591         // Find the last node after the exit blocks starts
 592         return getBlockEnd(node);
 593     }
 594 
 595     private static EndNode getBlockEnd(FixedNode node) {
 596         FixedNode curNode = node;
 597         while (curNode instanceof FixedWithNextNode) {
 598             curNode = ((FixedWithNextNode) curNode).next();
 599         }
 600         return (EndNode) curNode;
 601     }
 602 
 603     private static void updateMainLoopLimit(IfNode preLimit, InductionVariable preIv, LoopFragmentWhole mainLoop) {
 604         // Update the main loops limit test to be different than the post loop
 605         StructuredGraph graph = preLimit.graph();
 606         IfNode mainLimit = mainLoop.getDuplicatedNode(preLimit);
 607         LogicNode ifTest = mainLimit.condition();
 608         CompareNode compareNode = (CompareNode) ifTest;
 609         ValueNode prePhi = preIv.valueNode();
 610         ValueNode mainPhi = mainLoop.getDuplicatedNode(prePhi);
 611         ValueNode preStride = preIv.strideNode();
 612         ValueNode mainStride;
 613         if (preStride instanceof ConstantNode) {
 614             mainStride = preStride;
 615         } else {
 616             mainStride = mainLoop.getDuplicatedNode(preStride);
 617         }
 618         // Fetch the bounds to pose lowering the range by one
 619         ValueNode ub = null;
 620         if (compareNode.getX() == mainPhi) {
 621             ub = compareNode.getY();
 622         } else if (compareNode.getY() == mainPhi) {
 623             ub = compareNode.getX();
 624         } else {
 625             throw GraalError.shouldNotReachHere();
 626         }
 627 
 628         // Preloop always performs at least once iteration, so remove that from the main loop.
 629         ValueNode newLimit = sub(graph, ub, mainStride);
 630 
 631         // Re-wire the condition with the new limit
 632         compareNode.replaceFirstInput(ub, newLimit);
 633     }
 634 
 635     private static void updatePreLoopLimit(IfNode preLimit, InductionVariable preIv, CountedLoopInfo preCounted) {
 636         // Update the pre loops limit test
 637         StructuredGraph graph = preLimit.graph();
 638         LogicNode ifTest = preLimit.condition();
 639         CompareNode compareNode = (CompareNode) ifTest;
 640         ValueNode prePhi = preIv.valueNode();
 641         // Make new limit one iteration
 642         ValueNode initIv = preCounted.getStart();
 643         ValueNode newLimit = add(graph, initIv, preIv.strideNode());
 644 
 645         // Fetch the variable we are not replacing and configure the one we are
 646         ValueNode ub;
 647         if (compareNode.getX() == prePhi) {
 648             ub = compareNode.getY();
 649         } else if (compareNode.getY() == prePhi) {
 650             ub = compareNode.getX();
 651         } else {
 652             throw GraalError.shouldNotReachHere();
 653         }
 654         // Re-wire the condition with the new limit
 655         if (preIv.direction() == Direction.Up) {
 656             compareNode.replaceFirstInput(ub, graph.unique(new ConditionalNode(graph.unique(new IntegerLessThanNode(newLimit, ub)), newLimit, ub)));
 657         } else {
 658             compareNode.replaceFirstInput(ub, graph.unique(new ConditionalNode(graph.unique(new IntegerLessThanNode(ub, newLimit)), newLimit, ub)));
 659         }
 660     }
 661 
 662     public static List<ControlSplitNode> findUnswitchable(LoopEx loop) {
 663         List<ControlSplitNode> controls = null;
 664         ValueNode invariantValue = null;
 665         for (IfNode ifNode : loop.whole().nodes().filter(IfNode.class)) {
 666             if (loop.isOutsideLoop(ifNode.condition())) {
 667                 if (controls == null) {
 668                     invariantValue = ifNode.condition();
 669                     controls = new ArrayList<>();
 670                     controls.add(ifNode);
 671                 } else if (ifNode.condition() == invariantValue) {
 672                     controls.add(ifNode);
 673                 }
 674             }
 675         }
 676         if (controls == null) {
 677             SwitchNode firstSwitch = null;
 678             for (SwitchNode switchNode : loop.whole().nodes().filter(SwitchNode.class)) {
 679                 if (switchNode.successors().count() > 1 && loop.isOutsideLoop(switchNode.value())) {
 680                     if (controls == null) {
 681                         firstSwitch = switchNode;
 682                         invariantValue = switchNode.value();
 683                         controls = new ArrayList<>();
 684                         controls.add(switchNode);
 685                     } else if (switchNode.value() == invariantValue && firstSwitch.structureEquals(switchNode)) {
 686                         // Only collect switches which test the same values in the same order
 687                         controls.add(switchNode);
 688                     }
 689                 }
 690             }
 691         }
 692         return controls;
 693     }
 694 
 695     public static boolean isUnrollableLoop(LoopEx loop) {
 696         if (!loop.isCounted()) {
 697             return false;
 698         }
 699         LoopBeginNode loopBegin = loop.loopBegin();
 700         boolean isCanonical = false;
 701         if (loopBegin.isMainLoop() || loopBegin.isSimpleLoop()) {
 702             // Flow-less loops to partial unroll for now. 3 blocks corresponds to an if that either
 703             // exits or continues the loop. There might be fixed and floating work within the loop
 704             // as well.
 705             if (loop.loop().getBlocks().size() < 3) {
 706                 isCanonical = true;
 707             }
 708         }
 709         if (!isCanonical) {
 710             return false;
 711         }
 712         for (ValuePhiNode phi : loopBegin.valuePhis()) {
 713             if (phi.usages().filter(x -> loopBegin.isPhiAtMerge(x)).isNotEmpty()) {
 714                 // Filter out Phis which reference Phis at the same merge until the duplication
 715                 // logic handles it properly.
 716                 return false;
 717             }
 718             InductionVariable iv = loop.getInductionVariables().get(phi);
 719             if (iv == null) {
 720                 continue;
 721             }
 722             if (iv instanceof DerivedInductionVariable) {
 723                 return false;
 724             } else if (iv instanceof BasicInductionVariable) {
 725                 BasicInductionVariable biv = (BasicInductionVariable) iv;
 726                 if (!biv.isConstantStride()) {
 727                     return false;
 728                 }
 729             } else {
 730                 return false;
 731             }
 732         }
 733         return true;
 734     }
 735 }