1 /*
   2  * Copyright (c) 2011, 2016, 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.phases.schedule;
  24 
  25 import static org.graalvm.compiler.core.common.GraalOptions.OptScheduleOutOfLoops;
  26 import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.strictlyDominates;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Arrays;
  30 import java.util.BitSet;
  31 import java.util.Formatter;
  32 import java.util.List;
  33 
  34 import org.graalvm.compiler.core.common.LocationIdentity;
  35 import org.graalvm.compiler.core.common.SuppressFBWarnings;
  36 import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
  37 import org.graalvm.compiler.core.common.cfg.BlockMap;
  38 import org.graalvm.compiler.debug.Debug;
  39 import org.graalvm.compiler.graph.Graph.NodeEvent;
  40 import org.graalvm.compiler.graph.Graph.NodeEventListener;
  41 import org.graalvm.compiler.graph.Graph.NodeEventScope;
  42 import org.graalvm.compiler.graph.Node;
  43 import org.graalvm.compiler.graph.NodeBitMap;
  44 import org.graalvm.compiler.graph.NodeMap;
  45 import org.graalvm.compiler.graph.NodeStack;
  46 import org.graalvm.compiler.nodes.AbstractBeginNode;
  47 import org.graalvm.compiler.nodes.AbstractEndNode;
  48 import org.graalvm.compiler.nodes.AbstractMergeNode;
  49 import org.graalvm.compiler.nodes.ControlSinkNode;
  50 import org.graalvm.compiler.nodes.ControlSplitNode;
  51 import org.graalvm.compiler.nodes.DeoptimizeNode;
  52 import org.graalvm.compiler.nodes.FixedNode;
  53 import org.graalvm.compiler.nodes.FrameState;
  54 import org.graalvm.compiler.nodes.GuardNode;
  55 import org.graalvm.compiler.nodes.LoopBeginNode;
  56 import org.graalvm.compiler.nodes.LoopExitNode;
  57 import org.graalvm.compiler.nodes.PhiNode;
  58 import org.graalvm.compiler.nodes.ProxyNode;
  59 import org.graalvm.compiler.nodes.StartNode;
  60 import org.graalvm.compiler.nodes.StateSplit;
  61 import org.graalvm.compiler.nodes.StructuredGraph;
  62 import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
  63 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  64 import org.graalvm.compiler.nodes.ValueNode;
  65 import org.graalvm.compiler.nodes.VirtualState;
  66 import org.graalvm.compiler.nodes.cfg.Block;
  67 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  68 import org.graalvm.compiler.nodes.cfg.HIRLoop;
  69 import org.graalvm.compiler.nodes.cfg.LocationSet;
  70 import org.graalvm.compiler.nodes.memory.FloatingReadNode;
  71 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
  72 import org.graalvm.compiler.nodes.memory.MemoryNode;
  73 import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
  74 import org.graalvm.compiler.phases.Phase;
  75 
  76 public final class SchedulePhase extends Phase {
  77 
  78     public enum SchedulingStrategy {
  79         EARLIEST,
  80         LATEST,
  81         LATEST_OUT_OF_LOOPS,
  82         FINAL_SCHEDULE
  83     }
  84 
  85     private final SchedulingStrategy selectedStrategy;
  86 
  87     private final boolean immutableGraph;
  88 
  89     public SchedulePhase() {
  90         this(false);
  91     }
  92 
  93     public SchedulePhase(boolean immutableGraph) {
  94         this(OptScheduleOutOfLoops.getValue() ? SchedulingStrategy.LATEST_OUT_OF_LOOPS : SchedulingStrategy.LATEST, immutableGraph);
  95     }
  96 
  97     public SchedulePhase(SchedulingStrategy strategy) {
  98         this(strategy, false);
  99     }
 100 
 101     public SchedulePhase(SchedulingStrategy strategy, boolean immutableGraph) {
 102         this.selectedStrategy = strategy;
 103         this.immutableGraph = immutableGraph;
 104     }
 105 
 106     private NodeEventScope verifyImmutableGraph(StructuredGraph graph) {
 107         boolean assertionsEnabled = false;
 108         assert (assertionsEnabled = true) == true;
 109         if (immutableGraph && assertionsEnabled) {
 110             return graph.trackNodeEvents(new NodeEventListener() {
 111                 @Override
 112                 public void event(NodeEvent e, Node node) {
 113                     assert false : "graph changed: " + e + " on node " + node;
 114                 }
 115             });
 116         } else {
 117             return null;
 118         }
 119     }
 120 
 121     @Override
 122     @SuppressWarnings("try")
 123     protected void run(StructuredGraph graph) {
 124         try (NodeEventScope scope = verifyImmutableGraph(graph)) {
 125             Instance inst = new Instance();
 126             inst.run(graph, selectedStrategy, immutableGraph);
 127         }
 128     }
 129 
 130     public static class Instance {
 131 
 132         /**
 133          * Map from blocks to the nodes in each block.
 134          */
 135         protected ControlFlowGraph cfg;
 136         protected BlockMap<List<Node>> blockToNodesMap;
 137         protected NodeMap<Block> nodeToBlockMap;
 138 
 139         @SuppressWarnings("try")
 140         public void run(StructuredGraph graph, SchedulingStrategy selectedStrategy, boolean immutableGraph) {
 141             // assert GraphOrder.assertNonCyclicGraph(graph);
 142             cfg = ControlFlowGraph.compute(graph, true, true, true, false);
 143 
 144             NodeMap<Block> currentNodeMap = graph.createNodeMap();
 145             NodeBitMap visited = graph.createNodeBitMap();
 146             BlockMap<List<Node>> earliestBlockToNodesMap = new BlockMap<>(cfg);
 147             this.nodeToBlockMap = currentNodeMap;
 148             this.blockToNodesMap = earliestBlockToNodesMap;
 149 
 150             scheduleEarliestIterative(earliestBlockToNodesMap, currentNodeMap, visited, graph, immutableGraph);
 151 
 152             if (selectedStrategy != SchedulingStrategy.EARLIEST) {
 153                 // For non-earliest schedules, we need to do a second pass.
 154                 BlockMap<List<Node>> latestBlockToNodesMap = new BlockMap<>(cfg);
 155                 for (Block b : cfg.getBlocks()) {
 156                     latestBlockToNodesMap.put(b, new ArrayList<Node>());
 157                 }
 158 
 159                 BlockMap<ArrayList<FloatingReadNode>> watchListMap = calcLatestBlocks(selectedStrategy, currentNodeMap, earliestBlockToNodesMap, visited, latestBlockToNodesMap, immutableGraph);
 160                 sortNodesLatestWithinBlock(cfg, earliestBlockToNodesMap, latestBlockToNodesMap, currentNodeMap, watchListMap, visited);
 161 
 162                 assert verifySchedule(cfg, latestBlockToNodesMap, currentNodeMap);
 163                 assert MemoryScheduleVerification.check(cfg.getStartBlock(), latestBlockToNodesMap);
 164 
 165                 this.blockToNodesMap = latestBlockToNodesMap;
 166 
 167                 cfg.setNodeToBlock(currentNodeMap);
 168             }
 169 
 170             graph.setLastSchedule(new ScheduleResult(this.cfg, this.nodeToBlockMap, this.blockToNodesMap));
 171         }
 172 
 173         @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "false positive found by findbugs")
 174         private BlockMap<ArrayList<FloatingReadNode>> calcLatestBlocks(SchedulingStrategy strategy, NodeMap<Block> currentNodeMap, BlockMap<List<Node>> earliestBlockToNodesMap, NodeBitMap visited,
 175                         BlockMap<List<Node>> latestBlockToNodesMap, boolean immutableGraph) {
 176             BlockMap<ArrayList<FloatingReadNode>> watchListMap = new BlockMap<>(cfg);
 177             Block[] reversePostOrder = cfg.reversePostOrder();
 178             for (int j = reversePostOrder.length - 1; j >= 0; --j) {
 179                 Block currentBlock = reversePostOrder[j];
 180                 List<Node> blockToNodes = earliestBlockToNodesMap.get(currentBlock);
 181                 LocationSet killed = null;
 182                 int previousIndex = blockToNodes.size();
 183                 for (int i = blockToNodes.size() - 1; i >= 0; --i) {
 184                     Node currentNode = blockToNodes.get(i);
 185                     assert currentNodeMap.get(currentNode) == currentBlock;
 186                     assert !(currentNode instanceof PhiNode) && !(currentNode instanceof ProxyNode);
 187                     assert visited.isMarked(currentNode);
 188                     if (currentNode instanceof FixedNode) {
 189                         // For these nodes, the earliest is at the same time the latest block.
 190                     } else {
 191                         Block latestBlock = null;
 192 
 193                         LocationIdentity constrainingLocation = null;
 194                         if (currentNode instanceof FloatingReadNode) {
 195                             // We are scheduling a floating read node => check memory
 196                             // anti-dependencies.
 197                             FloatingReadNode floatingReadNode = (FloatingReadNode) currentNode;
 198                             LocationIdentity location = floatingReadNode.getLocationIdentity();
 199                             if (location.isMutable()) {
 200                                 // Location can be killed.
 201                                 constrainingLocation = location;
 202                                 if (currentBlock.canKill(location)) {
 203                                     if (killed == null) {
 204                                         killed = new LocationSet();
 205                                     }
 206                                     fillKillSet(killed, blockToNodes.subList(i + 1, previousIndex));
 207                                     previousIndex = i;
 208                                     if (killed.contains(location)) {
 209                                         // Earliest block kills location => we need to stay within
 210                                         // earliest block.
 211                                         latestBlock = currentBlock;
 212                                     }
 213                                 }
 214                             }
 215                         }
 216 
 217                         if (latestBlock == null) {
 218                             // We are not constraint within earliest block => calculate optimized
 219                             // schedule.
 220                             calcLatestBlock(currentBlock, strategy, currentNode, currentNodeMap, constrainingLocation, watchListMap, latestBlockToNodesMap, visited, immutableGraph);
 221                         } else {
 222                             selectLatestBlock(currentNode, currentBlock, latestBlock, currentNodeMap, watchListMap, constrainingLocation, latestBlockToNodesMap);
 223                         }
 224                     }
 225                 }
 226             }
 227             return watchListMap;
 228         }
 229 
 230         protected static void selectLatestBlock(Node currentNode, Block currentBlock, Block latestBlock, NodeMap<Block> currentNodeMap, BlockMap<ArrayList<FloatingReadNode>> watchListMap,
 231                         LocationIdentity constrainingLocation, BlockMap<List<Node>> latestBlockToNodesMap) {
 232 
 233             assert checkLatestEarliestRelation(currentNode, currentBlock, latestBlock);
 234             if (currentBlock != latestBlock) {
 235                 currentNodeMap.setAndGrow(currentNode, latestBlock);
 236 
 237                 if (constrainingLocation != null && latestBlock.canKill(constrainingLocation)) {
 238                     if (watchListMap.get(latestBlock) == null) {
 239                         watchListMap.put(latestBlock, new ArrayList<>());
 240                     }
 241                     watchListMap.get(latestBlock).add((FloatingReadNode) currentNode);
 242                 }
 243             }
 244 
 245             latestBlockToNodesMap.get(latestBlock).add(currentNode);
 246         }
 247 
 248         private static boolean checkLatestEarliestRelation(Node currentNode, Block earliestBlock, Block latestBlock) {
 249             assert AbstractControlFlowGraph.dominates(earliestBlock, latestBlock) || (currentNode instanceof VirtualState && latestBlock == earliestBlock.getDominator()) : String.format(
 250                             "%s %s (%s) %s (%s)", currentNode, earliestBlock, earliestBlock.getBeginNode(), latestBlock, latestBlock.getBeginNode());
 251             return true;
 252         }
 253 
 254         private static boolean verifySchedule(ControlFlowGraph cfg, BlockMap<List<Node>> blockToNodesMap, NodeMap<Block> nodeMap) {
 255             for (Block b : cfg.getBlocks()) {
 256                 List<Node> nodes = blockToNodesMap.get(b);
 257                 for (Node n : nodes) {
 258                     assert n.isAlive();
 259                     assert nodeMap.get(n) == b;
 260                     StructuredGraph g = (StructuredGraph) n.graph();
 261                     if (g.hasLoops() && g.getGuardsStage() == GuardsStage.AFTER_FSA && n instanceof DeoptimizeNode) {
 262                         assert b.getLoopDepth() == 0 : n;
 263                     }
 264                 }
 265             }
 266             return true;
 267         }
 268 
 269         public static Block checkKillsBetween(Block earliestBlock, Block latestBlock, LocationIdentity location) {
 270             assert strictlyDominates(earliestBlock, latestBlock);
 271             Block current = latestBlock.getDominator();
 272 
 273             // Collect dominator chain that needs checking.
 274             List<Block> dominatorChain = new ArrayList<>();
 275             dominatorChain.add(latestBlock);
 276             while (current != earliestBlock) {
 277                 // Current is an intermediate dominator between earliestBlock and latestBlock.
 278                 assert strictlyDominates(earliestBlock, current) && strictlyDominates(current, latestBlock);
 279                 if (current.canKill(location)) {
 280                     dominatorChain.clear();
 281                 }
 282                 dominatorChain.add(current);
 283                 current = current.getDominator();
 284             }
 285 
 286             // The first element of dominatorChain now contains the latest possible block.
 287             assert dominatorChain.size() >= 1;
 288             assert dominatorChain.get(dominatorChain.size() - 1).getDominator() == earliestBlock;
 289 
 290             Block lastBlock = earliestBlock;
 291             for (int i = dominatorChain.size() - 1; i >= 0; --i) {
 292                 Block currentBlock = dominatorChain.get(i);
 293                 if (currentBlock.getLoopDepth() > lastBlock.getLoopDepth()) {
 294                     // We are entering a loop boundary. The new loops must not kill the location for
 295                     // the crossing to be safe.
 296                     if (currentBlock.getLoop() != null && ((HIRLoop) currentBlock.getLoop()).canKill(location)) {
 297                         break;
 298                     }
 299                 }
 300 
 301                 if (currentBlock.canKillBetweenThisAndDominator(location)) {
 302                     break;
 303                 }
 304                 lastBlock = currentBlock;
 305             }
 306 
 307             return lastBlock;
 308         }
 309 
 310         private static void fillKillSet(LocationSet killed, List<Node> subList) {
 311             if (!killed.isAny()) {
 312                 for (Node n : subList) {
 313                     // Check if this node kills a node in the watch list.
 314                     if (n instanceof MemoryCheckpoint.Single) {
 315                         LocationIdentity identity = ((MemoryCheckpoint.Single) n).getLocationIdentity();
 316                         killed.add(identity);
 317                         if (killed.isAny()) {
 318                             return;
 319                         }
 320                     } else if (n instanceof MemoryCheckpoint.Multi) {
 321                         for (LocationIdentity identity : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
 322                             killed.add(identity);
 323                             if (killed.isAny()) {
 324                                 return;
 325                             }
 326                         }
 327                     }
 328                 }
 329             }
 330         }
 331 
 332         private static void sortNodesLatestWithinBlock(ControlFlowGraph cfg, BlockMap<List<Node>> earliestBlockToNodesMap, BlockMap<List<Node>> latestBlockToNodesMap, NodeMap<Block> currentNodeMap,
 333                         BlockMap<ArrayList<FloatingReadNode>> watchListMap, NodeBitMap visited) {
 334             for (Block b : cfg.getBlocks()) {
 335                 sortNodesLatestWithinBlock(b, earliestBlockToNodesMap, latestBlockToNodesMap, currentNodeMap, watchListMap, visited);
 336             }
 337         }
 338 
 339         private static void sortNodesLatestWithinBlock(Block b, BlockMap<List<Node>> earliestBlockToNodesMap, BlockMap<List<Node>> latestBlockToNodesMap, NodeMap<Block> nodeMap,
 340                         BlockMap<ArrayList<FloatingReadNode>> watchListMap, NodeBitMap unprocessed) {
 341             List<Node> earliestSorting = earliestBlockToNodesMap.get(b);
 342             ArrayList<Node> result = new ArrayList<>(earliestSorting.size());
 343             ArrayList<FloatingReadNode> watchList = null;
 344             if (watchListMap != null) {
 345                 watchList = watchListMap.get(b);
 346                 assert watchList == null || !b.getKillLocations().isEmpty();
 347             }
 348             AbstractBeginNode beginNode = b.getBeginNode();
 349             if (beginNode instanceof LoopExitNode) {
 350                 LoopExitNode loopExitNode = (LoopExitNode) beginNode;
 351                 for (ProxyNode proxy : loopExitNode.proxies()) {
 352                     unprocessed.clear(proxy);
 353                     ValueNode value = proxy.value();
 354                     // if multiple proxies reference the same value, schedule the value of a
 355                     // proxy once
 356                     if (value != null && nodeMap.get(value) == b && unprocessed.isMarked(value)) {
 357                         sortIntoList(value, b, result, nodeMap, unprocessed, null);
 358                     }
 359                 }
 360             }
 361             FixedNode endNode = b.getEndNode();
 362             FixedNode fixedEndNode = null;
 363             if (isFixedEnd(endNode)) {
 364                 // Only if the end node is either a control split or an end node, we need to force
 365                 // it to be the last node in the schedule.
 366                 fixedEndNode = endNode;
 367             }
 368             for (Node n : earliestSorting) {
 369                 if (n != fixedEndNode) {
 370                     if (n instanceof FixedNode) {
 371                         assert nodeMap.get(n) == b;
 372                         checkWatchList(b, nodeMap, unprocessed, result, watchList, n);
 373                         sortIntoList(n, b, result, nodeMap, unprocessed, null);
 374                     } else if (nodeMap.get(n) == b && n instanceof FloatingReadNode) {
 375                         FloatingReadNode floatingReadNode = (FloatingReadNode) n;
 376                         LocationIdentity location = floatingReadNode.getLocationIdentity();
 377                         if (b.canKill(location)) {
 378                             // This read can be killed in this block, add to watch list.
 379                             if (watchList == null) {
 380                                 watchList = new ArrayList<>();
 381                             }
 382                             watchList.add(floatingReadNode);
 383                         }
 384                     }
 385                 }
 386             }
 387 
 388             for (Node n : latestBlockToNodesMap.get(b)) {
 389                 assert nodeMap.get(n) == b : n;
 390                 assert !(n instanceof FixedNode);
 391                 if (unprocessed.isMarked(n)) {
 392                     sortIntoList(n, b, result, nodeMap, unprocessed, fixedEndNode);
 393                 }
 394             }
 395 
 396             if (endNode != null && unprocessed.isMarked(endNode)) {
 397                 sortIntoList(endNode, b, result, nodeMap, unprocessed, null);
 398             }
 399 
 400             latestBlockToNodesMap.put(b, result);
 401         }
 402 
 403         private static void checkWatchList(Block b, NodeMap<Block> nodeMap, NodeBitMap unprocessed, ArrayList<Node> result, ArrayList<FloatingReadNode> watchList, Node n) {
 404             if (watchList != null && !watchList.isEmpty()) {
 405                 // Check if this node kills a node in the watch list.
 406                 if (n instanceof MemoryCheckpoint.Single) {
 407                     LocationIdentity identity = ((MemoryCheckpoint.Single) n).getLocationIdentity();
 408                     checkWatchList(watchList, identity, b, result, nodeMap, unprocessed);
 409                 } else if (n instanceof MemoryCheckpoint.Multi) {
 410                     for (LocationIdentity identity : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
 411                         checkWatchList(watchList, identity, b, result, nodeMap, unprocessed);
 412                     }
 413                 }
 414             }
 415         }
 416 
 417         private static void checkWatchList(ArrayList<FloatingReadNode> watchList, LocationIdentity identity, Block b, ArrayList<Node> result, NodeMap<Block> nodeMap, NodeBitMap unprocessed) {
 418             assert identity.isMutable();
 419             if (identity.isAny()) {
 420                 for (FloatingReadNode r : watchList) {
 421                     if (unprocessed.isMarked(r)) {
 422                         sortIntoList(r, b, result, nodeMap, unprocessed, null);
 423                     }
 424                 }
 425                 watchList.clear();
 426             } else {
 427                 int index = 0;
 428                 while (index < watchList.size()) {
 429                     FloatingReadNode r = watchList.get(index);
 430                     LocationIdentity locationIdentity = r.getLocationIdentity();
 431                     assert locationIdentity.isMutable();
 432                     if (unprocessed.isMarked(r)) {
 433                         if (identity.overlaps(locationIdentity)) {
 434                             sortIntoList(r, b, result, nodeMap, unprocessed, null);
 435                         } else {
 436                             ++index;
 437                             continue;
 438                         }
 439                     }
 440                     int lastIndex = watchList.size() - 1;
 441                     watchList.set(index, watchList.get(lastIndex));
 442                     watchList.remove(lastIndex);
 443                 }
 444             }
 445         }
 446 
 447         private static void sortIntoList(Node n, Block b, ArrayList<Node> result, NodeMap<Block> nodeMap, NodeBitMap unprocessed, Node excludeNode) {
 448             assert unprocessed.isMarked(n) : n;
 449             assert nodeMap.get(n) == b;
 450 
 451             if (n instanceof PhiNode) {
 452                 return;
 453             }
 454 
 455             unprocessed.clear(n);
 456 
 457             for (Node input : n.inputs()) {
 458                 if (nodeMap.get(input) == b && unprocessed.isMarked(input) && input != excludeNode) {
 459                     sortIntoList(input, b, result, nodeMap, unprocessed, excludeNode);
 460                 }
 461             }
 462 
 463             if (n instanceof ProxyNode) {
 464                 // Skip proxy nodes.
 465             } else {
 466                 result.add(n);
 467             }
 468 
 469         }
 470 
 471         protected void calcLatestBlock(Block earliestBlock, SchedulingStrategy strategy, Node currentNode, NodeMap<Block> currentNodeMap, LocationIdentity constrainingLocation,
 472                         BlockMap<ArrayList<FloatingReadNode>> watchListMap, BlockMap<List<Node>> latestBlockToNodesMap, NodeBitMap visited, boolean immutableGraph) {
 473             Block latestBlock = null;
 474             assert currentNode.hasUsages();
 475             for (Node usage : currentNode.usages()) {
 476                 if (immutableGraph && !visited.contains(usage)) {
 477                     /*
 478                      * Normally, dead nodes are deleted by the scheduler before we reach this point.
 479                      * Only when the scheduler is asked to not modify a graph, we can see dead nodes
 480                      * here.
 481                      */
 482                     continue;
 483                 }
 484                 latestBlock = calcBlockForUsage(currentNode, usage, latestBlock, currentNodeMap);
 485             }
 486 
 487             if (strategy == SchedulingStrategy.FINAL_SCHEDULE || strategy == SchedulingStrategy.LATEST_OUT_OF_LOOPS) {
 488                 assert latestBlock != null;
 489                 while (latestBlock.getLoopDepth() > earliestBlock.getLoopDepth() && latestBlock != earliestBlock.getDominator()) {
 490                     latestBlock = latestBlock.getDominator();
 491                 }
 492             }
 493 
 494             if (latestBlock != earliestBlock && latestBlock != earliestBlock.getDominator() && constrainingLocation != null) {
 495                 latestBlock = checkKillsBetween(earliestBlock, latestBlock, constrainingLocation);
 496             }
 497 
 498             selectLatestBlock(currentNode, earliestBlock, latestBlock, currentNodeMap, watchListMap, constrainingLocation, latestBlockToNodesMap);
 499         }
 500 
 501         private static Block calcBlockForUsage(Node node, Node usage, Block startBlock, NodeMap<Block> currentNodeMap) {
 502             assert !(node instanceof PhiNode);
 503             Block currentBlock = startBlock;
 504             if (usage instanceof PhiNode) {
 505                 // An input to a PhiNode is used at the end of the predecessor block that
 506                 // corresponds to the PhiNode input. One PhiNode can use an input multiple times.
 507                 PhiNode phi = (PhiNode) usage;
 508                 AbstractMergeNode merge = phi.merge();
 509                 Block mergeBlock = currentNodeMap.get(merge);
 510                 for (int i = 0; i < phi.valueCount(); ++i) {
 511                     if (phi.valueAt(i) == node) {
 512                         Block otherBlock = mergeBlock.getPredecessors()[i];
 513                         currentBlock = AbstractControlFlowGraph.commonDominatorTyped(currentBlock, otherBlock);
 514                     }
 515                 }
 516             } else if (usage instanceof AbstractBeginNode) {
 517                 AbstractBeginNode abstractBeginNode = (AbstractBeginNode) usage;
 518                 if (abstractBeginNode instanceof StartNode) {
 519                     currentBlock = AbstractControlFlowGraph.commonDominatorTyped(currentBlock, currentNodeMap.get(abstractBeginNode));
 520                 } else {
 521                     Block otherBlock = currentNodeMap.get(abstractBeginNode).getDominator();
 522                     currentBlock = AbstractControlFlowGraph.commonDominatorTyped(currentBlock, otherBlock);
 523                 }
 524             } else {
 525                 // All other types of usages: Put the input into the same block as the usage.
 526                 Block otherBlock = currentNodeMap.get(usage);
 527                 currentBlock = AbstractControlFlowGraph.commonDominatorTyped(currentBlock, otherBlock);
 528             }
 529             return currentBlock;
 530         }
 531 
 532         private void scheduleEarliestIterative(BlockMap<List<Node>> blockToNodes, NodeMap<Block> nodeToBlock, NodeBitMap visited, StructuredGraph graph, boolean immutableGraph) {
 533 
 534             BitSet floatingReads = new BitSet(cfg.getBlocks().length);
 535 
 536             // Add begin nodes as the first entry and set the block for phi nodes.
 537             for (Block b : cfg.getBlocks()) {
 538                 AbstractBeginNode beginNode = b.getBeginNode();
 539                 ArrayList<Node> nodes = new ArrayList<>();
 540                 nodeToBlock.set(beginNode, b);
 541                 nodes.add(beginNode);
 542                 blockToNodes.put(b, nodes);
 543 
 544                 if (beginNode instanceof AbstractMergeNode) {
 545                     AbstractMergeNode mergeNode = (AbstractMergeNode) beginNode;
 546                     for (PhiNode phi : mergeNode.phis()) {
 547                         nodeToBlock.set(phi, b);
 548                     }
 549                 } else if (beginNode instanceof LoopExitNode) {
 550                     LoopExitNode loopExitNode = (LoopExitNode) beginNode;
 551                     for (ProxyNode proxy : loopExitNode.proxies()) {
 552                         nodeToBlock.set(proxy, b);
 553                     }
 554                 }
 555             }
 556 
 557             NodeStack stack = new NodeStack();
 558 
 559             // Start analysis with control flow ends.
 560             Block[] reversePostOrder = cfg.reversePostOrder();
 561             for (int j = reversePostOrder.length - 1; j >= 0; --j) {
 562                 Block b = reversePostOrder[j];
 563                 FixedNode endNode = b.getEndNode();
 564                 if (isFixedEnd(endNode)) {
 565                     stack.push(endNode);
 566                     nodeToBlock.set(endNode, b);
 567                 }
 568             }
 569 
 570             processStack(cfg, blockToNodes, nodeToBlock, visited, floatingReads, stack);
 571 
 572             // Visit back input edges of loop phis.
 573             boolean changed;
 574             boolean unmarkedPhi;
 575             do {
 576                 changed = false;
 577                 unmarkedPhi = false;
 578                 for (LoopBeginNode loopBegin : graph.getNodes(LoopBeginNode.TYPE)) {
 579                     for (PhiNode phi : loopBegin.phis()) {
 580                         if (visited.isMarked(phi)) {
 581                             for (int i = 0; i < loopBegin.getLoopEndCount(); ++i) {
 582                                 Node node = phi.valueAt(i + loopBegin.forwardEndCount());
 583                                 if (node != null && !visited.isMarked(node)) {
 584                                     changed = true;
 585                                     stack.push(node);
 586                                     processStack(cfg, blockToNodes, nodeToBlock, visited, floatingReads, stack);
 587                                 }
 588                             }
 589                         } else {
 590                             unmarkedPhi = true;
 591                         }
 592                     }
 593                 }
 594 
 595                 /*
 596                  * the processing of one loop phi could have marked a previously checked loop phi,
 597                  * therefore this needs to be iterative.
 598                  */
 599             } while (unmarkedPhi && changed);
 600 
 601             // Check for dead nodes.
 602             if (!immutableGraph && visited.getCounter() < graph.getNodeCount()) {
 603                 for (Node n : graph.getNodes()) {
 604                     if (!visited.isMarked(n)) {
 605                         n.clearInputs();
 606                         n.markDeleted();
 607                     }
 608                 }
 609             }
 610 
 611             // Add end nodes as the last nodes in each block.
 612             for (Block b : cfg.getBlocks()) {
 613                 FixedNode endNode = b.getEndNode();
 614                 if (isFixedEnd(endNode)) {
 615                     if (endNode != b.getBeginNode()) {
 616                         addNode(blockToNodes, b, endNode);
 617                     }
 618                 }
 619             }
 620 
 621             if (!floatingReads.isEmpty()) {
 622                 for (Block b : cfg.getBlocks()) {
 623                     if (floatingReads.get(b.getId())) {
 624                         resortEarliestWithinBlock(b, blockToNodes, nodeToBlock, visited);
 625                     }
 626                 }
 627             }
 628 
 629             assert MemoryScheduleVerification.check(cfg.getStartBlock(), blockToNodes);
 630         }
 631 
 632         private static boolean isFixedEnd(FixedNode endNode) {
 633             return endNode instanceof ControlSplitNode || endNode instanceof ControlSinkNode || endNode instanceof AbstractEndNode;
 634         }
 635 
 636         private static void resortEarliestWithinBlock(Block b, BlockMap<List<Node>> blockToNodes, NodeMap<Block> nodeToBlock, NodeBitMap unprocessed) {
 637             ArrayList<FloatingReadNode> watchList = new ArrayList<>();
 638             List<Node> oldList = blockToNodes.get(b);
 639             AbstractBeginNode beginNode = b.getBeginNode();
 640             for (Node n : oldList) {
 641                 if (n instanceof FloatingReadNode) {
 642                     FloatingReadNode floatingReadNode = (FloatingReadNode) n;
 643                     LocationIdentity locationIdentity = floatingReadNode.getLocationIdentity();
 644                     MemoryNode lastLocationAccess = floatingReadNode.getLastLocationAccess();
 645                     if (locationIdentity.isMutable() && lastLocationAccess != null) {
 646                         ValueNode lastAccessLocation = lastLocationAccess.asNode();
 647                         if (nodeToBlock.get(lastAccessLocation) == b && lastAccessLocation != beginNode && !(lastAccessLocation instanceof MemoryPhiNode)) {
 648                             // This node's last access location is within this block. Add to watch
 649                             // list when processing the last access location.
 650                         } else {
 651                             watchList.add(floatingReadNode);
 652                         }
 653                     }
 654                 }
 655             }
 656 
 657             ArrayList<Node> newList = new ArrayList<>(oldList.size());
 658             assert oldList.get(0) == beginNode;
 659             unprocessed.clear(beginNode);
 660             newList.add(beginNode);
 661             for (int i = 1; i < oldList.size(); ++i) {
 662                 Node n = oldList.get(i);
 663                 if (unprocessed.isMarked(n)) {
 664                     if (n instanceof MemoryNode) {
 665                         if (n instanceof MemoryCheckpoint) {
 666                             assert n instanceof FixedNode;
 667                             if (watchList.size() > 0) {
 668                                 // Check whether we need to commit reads from the watch list.
 669                                 checkWatchList(b, nodeToBlock, unprocessed, newList, watchList, n);
 670                             }
 671                         }
 672                         // Add potential dependent reads to the watch list.
 673                         for (Node usage : n.usages()) {
 674                             if (usage instanceof FloatingReadNode) {
 675                                 FloatingReadNode floatingReadNode = (FloatingReadNode) usage;
 676                                 if (nodeToBlock.get(floatingReadNode) == b && floatingReadNode.getLastLocationAccess() == n && !(n instanceof MemoryPhiNode)) {
 677                                     watchList.add(floatingReadNode);
 678                                 }
 679                             }
 680                         }
 681                     }
 682                     assert unprocessed.isMarked(n);
 683                     unprocessed.clear(n);
 684                     newList.add(n);
 685                 } else {
 686                     // This node was pulled up.
 687                     assert !(n instanceof FixedNode) : n;
 688                 }
 689             }
 690 
 691             for (Node n : newList) {
 692                 unprocessed.mark(n);
 693             }
 694 
 695             assert newList.size() == oldList.size();
 696             blockToNodes.put(b, newList);
 697         }
 698 
 699         private static void addNode(BlockMap<List<Node>> blockToNodes, Block b, Node endNode) {
 700             assert !blockToNodes.get(b).contains(endNode) : endNode;
 701             blockToNodes.get(b).add(endNode);
 702         }
 703 
 704         private static void processStack(ControlFlowGraph cfg, BlockMap<List<Node>> blockToNodes, NodeMap<Block> nodeToBlock, NodeBitMap visited, BitSet floatingReads, NodeStack stack) {
 705             Block startBlock = cfg.getStartBlock();
 706             while (!stack.isEmpty()) {
 707                 Node current = stack.peek();
 708                 if (visited.checkAndMarkInc(current)) {
 709 
 710                     // Push inputs and predecessor.
 711                     Node predecessor = current.predecessor();
 712                     if (predecessor != null) {
 713                         stack.push(predecessor);
 714                     }
 715 
 716                     if (current instanceof PhiNode) {
 717                         processStackPhi(stack, (PhiNode) current);
 718                     } else if (current instanceof ProxyNode) {
 719                         processStackProxy(stack, (ProxyNode) current);
 720                     } else if (current instanceof FrameState) {
 721                         processStackFrameState(stack, current);
 722                     } else {
 723                         current.pushInputs(stack);
 724                     }
 725                 } else {
 726                     stack.pop();
 727                     if (nodeToBlock.get(current) == null) {
 728                         Block curBlock = cfg.blockFor(current);
 729                         if (curBlock == null) {
 730                             assert current.predecessor() == null && !(current instanceof FixedNode) : "The assignment of blocks to fixed nodes is already done when constructing the cfg.";
 731                             Block earliest = startBlock;
 732                             for (Node input : current.inputs()) {
 733                                 Block inputEarliest = nodeToBlock.get(input);
 734                                 if (inputEarliest == null) {
 735                                     assert current instanceof FrameState && input instanceof StateSplit && ((StateSplit) input).stateAfter() == current : current;
 736                                 } else {
 737                                     assert inputEarliest != null;
 738                                     if (inputEarliest.getEndNode() == input) {
 739                                         // This is the last node of the block.
 740                                         if (current instanceof FrameState && input instanceof StateSplit && ((StateSplit) input).stateAfter() == current) {
 741                                             // Keep regular inputEarliest.
 742                                         } else if (input instanceof ControlSplitNode) {
 743                                             inputEarliest = nodeToBlock.get(((ControlSplitNode) input).getPrimarySuccessor());
 744                                         } else {
 745                                             assert inputEarliest.getSuccessorCount() == 1;
 746                                             assert !(input instanceof AbstractEndNode);
 747                                             // Keep regular inputEarliest
 748                                         }
 749                                     }
 750                                     if (earliest.getDominatorDepth() < inputEarliest.getDominatorDepth()) {
 751                                         earliest = inputEarliest;
 752                                     }
 753                                 }
 754                             }
 755                             curBlock = earliest;
 756                         }
 757                         assert curBlock != null;
 758                         addNode(blockToNodes, curBlock, current);
 759                         nodeToBlock.set(current, curBlock);
 760                         if (current instanceof FloatingReadNode) {
 761                             FloatingReadNode floatingReadNode = (FloatingReadNode) current;
 762                             if (curBlock.canKill(floatingReadNode.getLocationIdentity())) {
 763                                 floatingReads.set(curBlock.getId());
 764                             }
 765                         }
 766                     }
 767                 }
 768             }
 769         }
 770 
 771         private static void processStackFrameState(NodeStack stack, Node current) {
 772             for (Node input : current.inputs()) {
 773                 if (input instanceof StateSplit && ((StateSplit) input).stateAfter() == current) {
 774                     // Ignore the cycle.
 775                 } else {
 776                     stack.push(input);
 777                 }
 778             }
 779         }
 780 
 781         private static void processStackProxy(NodeStack stack, ProxyNode proxyNode) {
 782             LoopExitNode proxyPoint = proxyNode.proxyPoint();
 783             for (Node input : proxyNode.inputs()) {
 784                 if (input != proxyPoint) {
 785                     stack.push(input);
 786                 }
 787             }
 788         }
 789 
 790         private static void processStackPhi(NodeStack stack, PhiNode phiNode) {
 791             AbstractMergeNode merge = phiNode.merge();
 792             for (int i = 0; i < merge.forwardEndCount(); ++i) {
 793                 Node input = phiNode.valueAt(i);
 794                 if (input != null) {
 795                     stack.push(input);
 796                 }
 797             }
 798         }
 799 
 800         public String printScheduleHelper(String desc) {
 801             Formatter buf = new Formatter();
 802             buf.format("=== %s / %s ===%n", getCFG().getStartBlock().getBeginNode().graph(), desc);
 803             for (Block b : getCFG().getBlocks()) {
 804                 buf.format("==== b: %s (loopDepth: %s). ", b, b.getLoopDepth());
 805                 buf.format("dom: %s. ", b.getDominator());
 806                 buf.format("preds: %s. ", Arrays.toString(b.getPredecessors()));
 807                 buf.format("succs: %s ====%n", Arrays.toString(b.getSuccessors()));
 808 
 809                 if (blockToNodesMap.get(b) != null) {
 810                     for (Node n : nodesFor(b)) {
 811                         printNode(n);
 812                     }
 813                 } else {
 814                     for (Node n : b.getNodes()) {
 815                         printNode(n);
 816                     }
 817                 }
 818             }
 819             buf.format("%n");
 820             return buf.toString();
 821         }
 822 
 823         private static void printNode(Node n) {
 824             Formatter buf = new Formatter();
 825             buf.format("%s", n);
 826             if (n instanceof MemoryCheckpoint.Single) {
 827                 buf.format(" // kills %s", ((MemoryCheckpoint.Single) n).getLocationIdentity());
 828             } else if (n instanceof MemoryCheckpoint.Multi) {
 829                 buf.format(" // kills ");
 830                 for (LocationIdentity locid : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
 831                     buf.format("%s, ", locid);
 832                 }
 833             } else if (n instanceof FloatingReadNode) {
 834                 FloatingReadNode frn = (FloatingReadNode) n;
 835                 buf.format(" // from %s", frn.getLocationIdentity());
 836                 buf.format(", lastAccess: %s", frn.getLastLocationAccess());
 837                 buf.format(", address: %s", frn.getAddress());
 838             } else if (n instanceof GuardNode) {
 839                 buf.format(", anchor: %s", ((GuardNode) n).getAnchor());
 840             }
 841             Debug.log("%s", buf);
 842         }
 843 
 844         public ControlFlowGraph getCFG() {
 845             return cfg;
 846         }
 847 
 848         /**
 849          * Gets the nodes in a given block.
 850          */
 851         public List<Node> nodesFor(Block block) {
 852             return blockToNodesMap.get(block);
 853         }
 854     }
 855 
 856 }