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.Formatter;
  31 import java.util.List;
  32 
  33 import org.graalvm.compiler.core.common.GraalOptions;
  34 import org.graalvm.compiler.core.common.SuppressFBWarnings;
  35 import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
  36 import org.graalvm.compiler.core.common.cfg.BlockMap;
  37 import org.graalvm.compiler.debug.Assertions;
  38 import org.graalvm.compiler.graph.Graph.NodeEvent;
  39 import org.graalvm.compiler.graph.Graph.NodeEventListener;
  40 import org.graalvm.compiler.graph.Graph.NodeEventScope;
  41 import org.graalvm.compiler.graph.Node;
  42 import org.graalvm.compiler.graph.NodeBitMap;
  43 import org.graalvm.compiler.graph.NodeMap;
  44 import org.graalvm.compiler.graph.NodeStack;
  45 import org.graalvm.compiler.nodes.AbstractBeginNode;
  46 import org.graalvm.compiler.nodes.AbstractEndNode;
  47 import org.graalvm.compiler.nodes.AbstractMergeNode;
  48 import org.graalvm.compiler.nodes.ControlSinkNode;
  49 import org.graalvm.compiler.nodes.ControlSplitNode;
  50 import org.graalvm.compiler.nodes.DeoptimizeNode;
  51 import org.graalvm.compiler.nodes.FixedNode;
  52 import org.graalvm.compiler.nodes.FixedWithNextNode;
  53 import org.graalvm.compiler.nodes.GuardNode;
  54 import org.graalvm.compiler.nodes.IfNode;
  55 import org.graalvm.compiler.nodes.KillingBeginNode;
  56 import org.graalvm.compiler.nodes.LoopBeginNode;
  57 import org.graalvm.compiler.nodes.LoopExitNode;
  58 import org.graalvm.compiler.nodes.PhiNode;
  59 import org.graalvm.compiler.nodes.ProxyNode;
  60 import org.graalvm.compiler.nodes.StartNode;
  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.calc.ConvertNode;
  67 import org.graalvm.compiler.nodes.calc.IsNullNode;
  68 import org.graalvm.compiler.nodes.cfg.Block;
  69 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  70 import org.graalvm.compiler.nodes.cfg.HIRLoop;
  71 import org.graalvm.compiler.nodes.cfg.LocationSet;
  72 import org.graalvm.compiler.nodes.memory.FloatingReadNode;
  73 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
  74 import org.graalvm.compiler.nodes.spi.ValueProxy;
  75 import org.graalvm.compiler.options.OptionValues;
  76 import org.graalvm.compiler.phases.Phase;
  77 import org.graalvm.word.LocationIdentity;
  78 
  79 public final class SchedulePhase extends Phase {
  80 
  81     public enum SchedulingStrategy {
  82         EARLIEST,
  83         LATEST,
  84         LATEST_OUT_OF_LOOPS,
  85         FINAL_SCHEDULE
  86     }
  87 
  88     private final SchedulingStrategy selectedStrategy;
  89 
  90     private final boolean immutableGraph;
  91 
  92     public SchedulePhase(OptionValues options) {
  93         this(false, options);
  94     }
  95 
  96     public SchedulePhase(boolean immutableGraph, OptionValues options) {
  97         this(OptScheduleOutOfLoops.getValue(options) ? SchedulingStrategy.LATEST_OUT_OF_LOOPS : SchedulingStrategy.LATEST, immutableGraph);
  98     }
  99 
 100     public SchedulePhase(SchedulingStrategy strategy) {
 101         this(strategy, false);
 102     }
 103 
 104     public SchedulePhase(SchedulingStrategy strategy, boolean immutableGraph) {
 105         this.selectedStrategy = strategy;
 106         this.immutableGraph = immutableGraph;
 107     }
 108 
 109     private NodeEventScope verifyImmutableGraph(StructuredGraph graph) {
 110         if (immutableGraph && Assertions.ENABLED) {
 111             return graph.trackNodeEvents(new NodeEventListener() {
 112                 @Override
 113                 public void event(NodeEvent e, Node node) {
 114                     assert false : "graph changed: " + e + " on node " + node;
 115                 }
 116             });
 117         } else {
 118             return null;
 119         }
 120     }
 121 
 122     @Override
 123     @SuppressWarnings("try")
 124     protected void run(StructuredGraph graph) {
 125         try (NodeEventScope scope = verifyImmutableGraph(graph)) {
 126             Instance inst = new Instance();
 127             inst.run(graph, selectedStrategy, immutableGraph);
 128         }
 129     }
 130 
 131     public static void run(StructuredGraph graph, SchedulingStrategy strategy, ControlFlowGraph cfg) {
 132         Instance inst = new Instance(cfg);
 133         inst.run(graph, strategy, false);
 134     }
 135 
 136     public static class Instance {
 137 
 138         private static final double IMPLICIT_NULL_CHECK_OPPORTUNITY_PROBABILITY_FACTOR = 2;
 139         /**
 140          * Map from blocks to the nodes in each block.
 141          */
 142         protected ControlFlowGraph cfg;
 143         protected BlockMap<List<Node>> blockToNodesMap;
 144         protected NodeMap<Block> nodeToBlockMap;
 145 
 146         public Instance() {
 147             this(null);
 148         }
 149 
 150         public Instance(ControlFlowGraph cfg) {
 151             this.cfg = cfg;
 152         }
 153 
 154         @SuppressWarnings("try")
 155         public void run(StructuredGraph graph, SchedulingStrategy selectedStrategy, boolean immutableGraph) {
 156             // assert GraphOrder.assertNonCyclicGraph(graph);
 157 
 158             if (this.cfg == null) {
 159                 this.cfg = ControlFlowGraph.compute(graph, true, true, true, false);
 160             }
 161 
 162             NodeMap<Block> currentNodeMap = graph.createNodeMap();
 163             NodeBitMap visited = graph.createNodeBitMap();
 164             BlockMap<List<Node>> earliestBlockToNodesMap = new BlockMap<>(cfg);
 165             this.nodeToBlockMap = currentNodeMap;
 166             this.blockToNodesMap = earliestBlockToNodesMap;
 167 
 168             scheduleEarliestIterative(earliestBlockToNodesMap, currentNodeMap, visited, graph, immutableGraph);
 169 
 170             if (selectedStrategy != SchedulingStrategy.EARLIEST) {
 171                 // For non-earliest schedules, we need to do a second pass.
 172                 BlockMap<List<Node>> latestBlockToNodesMap = new BlockMap<>(cfg);
 173                 for (Block b : cfg.getBlocks()) {
 174                     latestBlockToNodesMap.put(b, new ArrayList<Node>());
 175                 }
 176 
 177                 BlockMap<ArrayList<FloatingReadNode>> watchListMap = calcLatestBlocks(selectedStrategy, currentNodeMap, earliestBlockToNodesMap, visited, latestBlockToNodesMap, immutableGraph);
 178                 sortNodesLatestWithinBlock(cfg, earliestBlockToNodesMap, latestBlockToNodesMap, currentNodeMap, watchListMap, visited);
 179 
 180                 assert verifySchedule(cfg, latestBlockToNodesMap, currentNodeMap);
 181                 assert (!GraalOptions.DetailedAsserts.getValue(graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), latestBlockToNodesMap);
 182 
 183                 this.blockToNodesMap = latestBlockToNodesMap;
 184 
 185                 cfg.setNodeToBlock(currentNodeMap);
 186             }
 187 
 188             graph.setLastSchedule(new ScheduleResult(this.cfg, this.nodeToBlockMap, this.blockToNodesMap));
 189         }
 190 
 191         @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE", justification = "false positive found by findbugs")
 192         private BlockMap<ArrayList<FloatingReadNode>> calcLatestBlocks(SchedulingStrategy strategy, NodeMap<Block> currentNodeMap, BlockMap<List<Node>> earliestBlockToNodesMap, NodeBitMap visited,
 193                         BlockMap<List<Node>> latestBlockToNodesMap, boolean immutableGraph) {
 194             BlockMap<ArrayList<FloatingReadNode>> watchListMap = new BlockMap<>(cfg);
 195             Block[] reversePostOrder = cfg.reversePostOrder();
 196             for (int j = reversePostOrder.length - 1; j >= 0; --j) {
 197                 Block currentBlock = reversePostOrder[j];
 198                 List<Node> blockToNodes = earliestBlockToNodesMap.get(currentBlock);
 199                 LocationSet killed = null;
 200                 int previousIndex = blockToNodes.size();
 201                 for (int i = blockToNodes.size() - 1; i >= 0; --i) {
 202                     Node currentNode = blockToNodes.get(i);
 203                     assert currentNodeMap.get(currentNode) == currentBlock;
 204                     assert !(currentNode instanceof PhiNode) && !(currentNode instanceof ProxyNode);
 205                     assert visited.isMarked(currentNode);
 206                     if (currentNode instanceof FixedNode) {
 207                         // For these nodes, the earliest is at the same time the latest block.
 208                     } else {
 209                         Block latestBlock = null;
 210 
 211                         LocationIdentity constrainingLocation = null;
 212                         if (currentNode instanceof FloatingReadNode) {
 213                             // We are scheduling a floating read node => check memory
 214                             // anti-dependencies.
 215                             FloatingReadNode floatingReadNode = (FloatingReadNode) currentNode;
 216                             LocationIdentity location = floatingReadNode.getLocationIdentity();
 217                             if (location.isMutable()) {
 218                                 // Location can be killed.
 219                                 constrainingLocation = location;
 220                                 if (currentBlock.canKill(location)) {
 221                                     if (killed == null) {
 222                                         killed = new LocationSet();
 223                                     }
 224                                     fillKillSet(killed, blockToNodes.subList(i + 1, previousIndex));
 225                                     previousIndex = i;
 226                                     if (killed.contains(location)) {
 227                                         // Earliest block kills location => we need to stay within
 228                                         // earliest block.
 229                                         latestBlock = currentBlock;
 230                                     }
 231                                 }
 232                             }
 233                         }
 234 
 235                         if (latestBlock == null) {
 236                             // We are not constraint within earliest block => calculate optimized
 237                             // schedule.
 238                             calcLatestBlock(currentBlock, strategy, currentNode, currentNodeMap, constrainingLocation, watchListMap, latestBlockToNodesMap, visited, immutableGraph);
 239                         } else {
 240                             selectLatestBlock(currentNode, currentBlock, latestBlock, currentNodeMap, watchListMap, constrainingLocation, latestBlockToNodesMap);
 241                         }
 242                     }
 243                 }
 244             }
 245             return watchListMap;
 246         }
 247 
 248         protected static void selectLatestBlock(Node currentNode, Block currentBlock, Block latestBlock, NodeMap<Block> currentNodeMap, BlockMap<ArrayList<FloatingReadNode>> watchListMap,
 249                         LocationIdentity constrainingLocation, BlockMap<List<Node>> latestBlockToNodesMap) {
 250 
 251             assert checkLatestEarliestRelation(currentNode, currentBlock, latestBlock);
 252             if (currentBlock != latestBlock) {
 253 
 254                 currentNodeMap.setAndGrow(currentNode, latestBlock);
 255 
 256                 if (constrainingLocation != null && latestBlock.canKill(constrainingLocation)) {
 257                     if (watchListMap.get(latestBlock) == null) {
 258                         watchListMap.put(latestBlock, new ArrayList<>());
 259                     }
 260                     watchListMap.get(latestBlock).add((FloatingReadNode) currentNode);
 261                 }
 262             }
 263 
 264             latestBlockToNodesMap.get(latestBlock).add(currentNode);
 265         }
 266 
 267         private static boolean checkLatestEarliestRelation(Node currentNode, Block earliestBlock, Block latestBlock) {
 268             assert AbstractControlFlowGraph.dominates(earliestBlock, latestBlock) || (currentNode instanceof VirtualState && latestBlock == earliestBlock.getDominator()) : String.format(
 269                             "%s %s (%s) %s (%s)", currentNode, earliestBlock, earliestBlock.getBeginNode(), latestBlock, latestBlock.getBeginNode());
 270             return true;
 271         }
 272 
 273         private static boolean verifySchedule(ControlFlowGraph cfg, BlockMap<List<Node>> blockToNodesMap, NodeMap<Block> nodeMap) {
 274             for (Block b : cfg.getBlocks()) {
 275                 List<Node> nodes = blockToNodesMap.get(b);
 276                 for (Node n : nodes) {
 277                     assert n.isAlive();
 278                     assert nodeMap.get(n) == b;
 279                     StructuredGraph g = (StructuredGraph) n.graph();
 280                     if (g.hasLoops() && g.getGuardsStage() == GuardsStage.AFTER_FSA && n instanceof DeoptimizeNode) {
 281                         assert b.getLoopDepth() == 0 : n;
 282                     }
 283                 }
 284             }
 285             return true;
 286         }
 287 
 288         public static Block checkKillsBetween(Block earliestBlock, Block latestBlock, LocationIdentity location) {
 289             assert strictlyDominates(earliestBlock, latestBlock);
 290             Block current = latestBlock.getDominator();
 291 
 292             // Collect dominator chain that needs checking.
 293             List<Block> dominatorChain = new ArrayList<>();
 294             dominatorChain.add(latestBlock);
 295             while (current != earliestBlock) {
 296                 // Current is an intermediate dominator between earliestBlock and latestBlock.
 297                 assert strictlyDominates(earliestBlock, current) && strictlyDominates(current, latestBlock);
 298                 if (current.canKill(location)) {
 299                     dominatorChain.clear();
 300                 }
 301                 dominatorChain.add(current);
 302                 current = current.getDominator();
 303             }
 304 
 305             // The first element of dominatorChain now contains the latest possible block.
 306             assert dominatorChain.size() >= 1;
 307             assert dominatorChain.get(dominatorChain.size() - 1).getDominator() == earliestBlock;
 308 
 309             Block lastBlock = earliestBlock;
 310             for (int i = dominatorChain.size() - 1; i >= 0; --i) {
 311                 Block currentBlock = dominatorChain.get(i);
 312                 if (currentBlock.getLoopDepth() > lastBlock.getLoopDepth()) {
 313                     // We are entering a loop boundary. The new loops must not kill the location for
 314                     // the crossing to be safe.
 315                     if (currentBlock.getLoop() != null && ((HIRLoop) currentBlock.getLoop()).canKill(location)) {
 316                         break;
 317                     }
 318                 }
 319 
 320                 if (currentBlock.canKillBetweenThisAndDominator(location)) {
 321                     break;
 322                 }
 323                 lastBlock = currentBlock;
 324             }
 325 
 326             if (lastBlock.getBeginNode() instanceof KillingBeginNode) {
 327                 LocationIdentity locationIdentity = ((KillingBeginNode) lastBlock.getBeginNode()).getLocationIdentity();
 328                 if ((locationIdentity.isAny() || locationIdentity.equals(location)) && lastBlock != earliestBlock) {
 329                     // The begin of this block kills the location, so we *have* to schedule the node
 330                     // in the dominating block.
 331                     lastBlock = lastBlock.getDominator();
 332                 }
 333             }
 334 
 335             return lastBlock;
 336         }
 337 
 338         private static void fillKillSet(LocationSet killed, List<Node> subList) {
 339             if (!killed.isAny()) {
 340                 for (Node n : subList) {
 341                     // Check if this node kills a node in the watch list.
 342                     if (n instanceof MemoryCheckpoint.Single) {
 343                         LocationIdentity identity = ((MemoryCheckpoint.Single) n).getLocationIdentity();
 344                         killed.add(identity);
 345                         if (killed.isAny()) {
 346                             return;
 347                         }
 348                     } else if (n instanceof MemoryCheckpoint.Multi) {
 349                         for (LocationIdentity identity : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
 350                             killed.add(identity);
 351                             if (killed.isAny()) {
 352                                 return;
 353                             }
 354                         }
 355                     }
 356                 }
 357             }
 358         }
 359 
 360         private static void sortNodesLatestWithinBlock(ControlFlowGraph cfg, BlockMap<List<Node>> earliestBlockToNodesMap, BlockMap<List<Node>> latestBlockToNodesMap, NodeMap<Block> currentNodeMap,
 361                         BlockMap<ArrayList<FloatingReadNode>> watchListMap, NodeBitMap visited) {
 362             for (Block b : cfg.getBlocks()) {
 363                 sortNodesLatestWithinBlock(b, earliestBlockToNodesMap, latestBlockToNodesMap, currentNodeMap, watchListMap, visited);
 364             }
 365         }
 366 
 367         private static void sortNodesLatestWithinBlock(Block b, BlockMap<List<Node>> earliestBlockToNodesMap, BlockMap<List<Node>> latestBlockToNodesMap, NodeMap<Block> nodeMap,
 368                         BlockMap<ArrayList<FloatingReadNode>> watchListMap, NodeBitMap unprocessed) {
 369             List<Node> earliestSorting = earliestBlockToNodesMap.get(b);
 370             ArrayList<Node> result = new ArrayList<>(earliestSorting.size());
 371             ArrayList<FloatingReadNode> watchList = null;
 372             if (watchListMap != null) {
 373                 watchList = watchListMap.get(b);
 374                 assert watchList == null || !b.getKillLocations().isEmpty();
 375             }
 376             AbstractBeginNode beginNode = b.getBeginNode();
 377             if (beginNode instanceof LoopExitNode) {
 378                 LoopExitNode loopExitNode = (LoopExitNode) beginNode;
 379                 for (ProxyNode proxy : loopExitNode.proxies()) {
 380                     unprocessed.clear(proxy);
 381                     ValueNode value = proxy.value();
 382                     // if multiple proxies reference the same value, schedule the value of a
 383                     // proxy once
 384                     if (value != null && nodeMap.get(value) == b && unprocessed.isMarked(value)) {
 385                         sortIntoList(value, b, result, nodeMap, unprocessed, null);
 386                     }
 387                 }
 388             }
 389             FixedNode endNode = b.getEndNode();
 390             FixedNode fixedEndNode = null;
 391             if (isFixedEnd(endNode)) {
 392                 // Only if the end node is either a control split or an end node, we need to force
 393                 // it to be the last node in the schedule.
 394                 fixedEndNode = endNode;
 395             }
 396             for (Node n : earliestSorting) {
 397                 if (n != fixedEndNode) {
 398                     if (n instanceof FixedNode) {
 399                         assert nodeMap.get(n) == b;
 400                         checkWatchList(b, nodeMap, unprocessed, result, watchList, n);
 401                         sortIntoList(n, b, result, nodeMap, unprocessed, null);
 402                     } else if (nodeMap.get(n) == b && n instanceof FloatingReadNode) {
 403                         FloatingReadNode floatingReadNode = (FloatingReadNode) n;
 404                         if (isImplicitNullOpportunity(floatingReadNode, b)) {
 405                             // Schedule at the beginning of the block.
 406                             sortIntoList(floatingReadNode, b, result, nodeMap, unprocessed, null);
 407                         } else {
 408                             LocationIdentity location = floatingReadNode.getLocationIdentity();
 409                             if (b.canKill(location)) {
 410                                 // This read can be killed in this block, add to watch list.
 411                                 if (watchList == null) {
 412                                     watchList = new ArrayList<>();
 413                                 }
 414                                 watchList.add(floatingReadNode);
 415                             }
 416                         }
 417                     }
 418                 }
 419             }
 420 
 421             for (Node n : latestBlockToNodesMap.get(b)) {
 422                 assert nodeMap.get(n) == b : n;
 423                 assert !(n instanceof FixedNode);
 424                 if (unprocessed.isMarked(n)) {
 425                     sortIntoList(n, b, result, nodeMap, unprocessed, fixedEndNode);
 426                 }
 427             }
 428 
 429             if (endNode != null && unprocessed.isMarked(endNode)) {
 430                 sortIntoList(endNode, b, result, nodeMap, unprocessed, null);
 431             }
 432 
 433             latestBlockToNodesMap.put(b, result);
 434         }
 435 
 436         private static void checkWatchList(Block b, NodeMap<Block> nodeMap, NodeBitMap unprocessed, ArrayList<Node> result, ArrayList<FloatingReadNode> watchList, Node n) {
 437             if (watchList != null && !watchList.isEmpty()) {
 438                 // Check if this node kills a node in the watch list.
 439                 if (n instanceof MemoryCheckpoint.Single) {
 440                     LocationIdentity identity = ((MemoryCheckpoint.Single) n).getLocationIdentity();
 441                     checkWatchList(watchList, identity, b, result, nodeMap, unprocessed);
 442                 } else if (n instanceof MemoryCheckpoint.Multi) {
 443                     for (LocationIdentity identity : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
 444                         checkWatchList(watchList, identity, b, result, nodeMap, unprocessed);
 445                     }
 446                 }
 447             }
 448         }
 449 
 450         private static void checkWatchList(ArrayList<FloatingReadNode> watchList, LocationIdentity identity, Block b, ArrayList<Node> result, NodeMap<Block> nodeMap, NodeBitMap unprocessed) {
 451             if (identity.isImmutable()) {
 452                 // Nothing to do. This can happen for an initialization write.
 453             } else if (identity.isAny()) {
 454                 for (FloatingReadNode r : watchList) {
 455                     if (unprocessed.isMarked(r)) {
 456                         sortIntoList(r, b, result, nodeMap, unprocessed, null);
 457                     }
 458                 }
 459                 watchList.clear();
 460             } else {
 461                 int index = 0;
 462                 while (index < watchList.size()) {
 463                     FloatingReadNode r = watchList.get(index);
 464                     LocationIdentity locationIdentity = r.getLocationIdentity();
 465                     assert locationIdentity.isMutable();
 466                     if (unprocessed.isMarked(r)) {
 467                         if (identity.overlaps(locationIdentity)) {
 468                             sortIntoList(r, b, result, nodeMap, unprocessed, null);
 469                         } else {
 470                             ++index;
 471                             continue;
 472                         }
 473                     }
 474                     int lastIndex = watchList.size() - 1;
 475                     watchList.set(index, watchList.get(lastIndex));
 476                     watchList.remove(lastIndex);
 477                 }
 478             }
 479         }
 480 
 481         private static void sortIntoList(Node n, Block b, ArrayList<Node> result, NodeMap<Block> nodeMap, NodeBitMap unprocessed, Node excludeNode) {
 482             assert unprocessed.isMarked(n) : n;
 483             assert nodeMap.get(n) == b;
 484 
 485             if (n instanceof PhiNode) {
 486                 return;
 487             }
 488 
 489             unprocessed.clear(n);
 490 
 491             for (Node input : n.inputs()) {
 492                 if (nodeMap.get(input) == b && unprocessed.isMarked(input) && input != excludeNode) {
 493                     sortIntoList(input, b, result, nodeMap, unprocessed, excludeNode);
 494                 }
 495             }
 496 
 497             if (n instanceof ProxyNode) {
 498                 // Skip proxy nodes.
 499             } else {
 500                 result.add(n);
 501             }
 502 
 503         }
 504 
 505         protected void calcLatestBlock(Block earliestBlock, SchedulingStrategy strategy, Node currentNode, NodeMap<Block> currentNodeMap, LocationIdentity constrainingLocation,
 506                         BlockMap<ArrayList<FloatingReadNode>> watchListMap, BlockMap<List<Node>> latestBlockToNodesMap, NodeBitMap visited, boolean immutableGraph) {
 507             Block latestBlock = null;
 508             if (!currentNode.hasUsages()) {
 509                 assert currentNode instanceof GuardNode;
 510                 latestBlock = earliestBlock;
 511             } else {
 512                 assert currentNode.hasUsages();
 513                 for (Node usage : currentNode.usages()) {
 514                     if (immutableGraph && !visited.contains(usage)) {
 515                         /*
 516                          * Normally, dead nodes are deleted by the scheduler before we reach this
 517                          * point. Only when the scheduler is asked to not modify a graph, we can see
 518                          * dead nodes here.
 519                          */
 520                         continue;
 521                     }
 522                     latestBlock = calcBlockForUsage(currentNode, usage, latestBlock, currentNodeMap);
 523                 }
 524 
 525                 assert latestBlock != null : currentNode;
 526 
 527                 if (strategy == SchedulingStrategy.FINAL_SCHEDULE || strategy == SchedulingStrategy.LATEST_OUT_OF_LOOPS) {
 528                     assert latestBlock != null;
 529                     Block currentBlock = latestBlock;
 530                     while (currentBlock.getLoopDepth() > earliestBlock.getLoopDepth() && currentBlock != earliestBlock.getDominator()) {
 531                         Block previousCurrentBlock = currentBlock;
 532                         currentBlock = currentBlock.getDominator();
 533                         if (previousCurrentBlock.isLoopHeader()) {
 534                             if (currentBlock.probability() < latestBlock.probability() || ((StructuredGraph) currentNode.graph()).hasValueProxies()) {
 535                                 // Only assign new latest block if frequency is actually lower or if
 536                                 // loop proxies would be required otherwise.
 537                                 latestBlock = currentBlock;
 538                             }
 539                         }
 540                     }
 541                 }
 542 
 543                 if (latestBlock != earliestBlock && latestBlock != earliestBlock.getDominator() && constrainingLocation != null) {
 544                     latestBlock = checkKillsBetween(earliestBlock, latestBlock, constrainingLocation);
 545                 }
 546             }
 547 
 548             if (latestBlock != earliestBlock && currentNode instanceof FloatingReadNode) {
 549 
 550                 FloatingReadNode floatingReadNode = (FloatingReadNode) currentNode;
 551                 if (isImplicitNullOpportunity(floatingReadNode, earliestBlock) && earliestBlock.probability() < latestBlock.probability() * IMPLICIT_NULL_CHECK_OPPORTUNITY_PROBABILITY_FACTOR) {
 552                     latestBlock = earliestBlock;
 553                 }
 554             }
 555 
 556             selectLatestBlock(currentNode, earliestBlock, latestBlock, currentNodeMap, watchListMap, constrainingLocation, latestBlockToNodesMap);
 557         }
 558 
 559         private static boolean isImplicitNullOpportunity(FloatingReadNode floatingReadNode, Block block) {
 560 
 561             Node pred = block.getBeginNode().predecessor();
 562             if (pred instanceof IfNode) {
 563                 IfNode ifNode = (IfNode) pred;
 564                 if (ifNode.condition() instanceof IsNullNode) {
 565                     IsNullNode isNullNode = (IsNullNode) ifNode.condition();
 566                     if (getUnproxifiedUncompressed(floatingReadNode.getAddress().getBase()) == getUnproxifiedUncompressed(isNullNode.getValue())) {
 567                         return true;
 568                     }
 569                 }
 570             }
 571             return false;
 572         }
 573 
 574         private static Node getUnproxifiedUncompressed(Node node) {
 575             Node result = node;
 576             while (true) {
 577                 if (result instanceof ValueProxy) {
 578                     ValueProxy valueProxy = (ValueProxy) result;
 579                     result = valueProxy.getOriginalNode();
 580                 } else if (result instanceof ConvertNode) {
 581                     ConvertNode convertNode = (ConvertNode) result;
 582                     if (convertNode.mayNullCheckSkipConversion()) {
 583                         result = convertNode.getValue();
 584                     } else {
 585                         break;
 586                     }
 587                 } else {
 588                     break;
 589                 }
 590             }
 591             return result;
 592         }
 593 
 594         private static Block calcBlockForUsage(Node node, Node usage, Block startBlock, NodeMap<Block> currentNodeMap) {
 595             assert !(node instanceof PhiNode);
 596             Block currentBlock = startBlock;
 597             if (usage instanceof PhiNode) {
 598                 // An input to a PhiNode is used at the end of the predecessor block that
 599                 // corresponds to the PhiNode input. One PhiNode can use an input multiple times.
 600                 PhiNode phi = (PhiNode) usage;
 601                 AbstractMergeNode merge = phi.merge();
 602                 Block mergeBlock = currentNodeMap.get(merge);
 603                 for (int i = 0; i < phi.valueCount(); ++i) {
 604                     if (phi.valueAt(i) == node) {
 605                         Block otherBlock = mergeBlock.getPredecessors()[i];
 606                         currentBlock = AbstractControlFlowGraph.commonDominatorTyped(currentBlock, otherBlock);
 607                     }
 608                 }
 609             } else if (usage instanceof AbstractBeginNode) {
 610                 AbstractBeginNode abstractBeginNode = (AbstractBeginNode) usage;
 611                 if (abstractBeginNode instanceof StartNode) {
 612                     currentBlock = AbstractControlFlowGraph.commonDominatorTyped(currentBlock, currentNodeMap.get(abstractBeginNode));
 613                 } else {
 614                     Block otherBlock = currentNodeMap.get(abstractBeginNode).getDominator();
 615                     currentBlock = AbstractControlFlowGraph.commonDominatorTyped(currentBlock, otherBlock);
 616                 }
 617             } else {
 618                 // All other types of usages: Put the input into the same block as the usage.
 619                 Block otherBlock = currentNodeMap.get(usage);
 620                 if (usage instanceof ProxyNode) {
 621                     ProxyNode proxyNode = (ProxyNode) usage;
 622                     otherBlock = currentNodeMap.get(proxyNode.proxyPoint());
 623 
 624                 }
 625                 currentBlock = AbstractControlFlowGraph.commonDominatorTyped(currentBlock, otherBlock);
 626             }
 627             return currentBlock;
 628         }
 629 
 630         /**
 631          * Micro block that is allocated for each fixed node and captures all floating nodes that
 632          * need to be scheduled immediately after the corresponding fixed node.
 633          */
 634         private static class MicroBlock {
 635             private final int id;
 636             private int nodeCount;
 637             private NodeEntry head;
 638             private NodeEntry tail;
 639 
 640             MicroBlock(int id) {
 641                 this.id = id;
 642             }
 643 
 644             /**
 645              * Adds a new floating node into the micro block.
 646              */
 647             public void add(Node node) {
 648                 assert !(node instanceof FixedNode) : node;
 649                 NodeEntry newTail = new NodeEntry(node, null);
 650                 if (tail == null) {
 651                     tail = head = newTail;
 652                 } else {
 653                     tail.next = newTail;
 654                     tail = newTail;
 655                 }
 656                 nodeCount++;
 657             }
 658 
 659             /**
 660              * Number of nodes in this micro block.
 661              */
 662             public int getNodeCount() {
 663                 return nodeCount;
 664             }
 665 
 666             /**
 667              * The id of the micro block, with a block always associated with a lower id than its
 668              * successors.
 669              */
 670             public int getId() {
 671                 return id;
 672             }
 673 
 674             /**
 675              * First node of the linked list of nodes of this micro block.
 676              */
 677             public NodeEntry getFirstNode() {
 678                 return head;
 679             }
 680 
 681             /**
 682              * Takes all nodes in this micro blocks and prepends them to the nodes of the given
 683              * parameter.
 684              *
 685              * @param newBlock the new block for the nodes
 686              */
 687             public void prependChildrenTo(MicroBlock newBlock) {
 688                 if (tail != null) {
 689                     tail.next = newBlock.head;
 690                     newBlock.head = head;
 691                     head = tail = null;
 692                     newBlock.nodeCount += nodeCount;
 693                     nodeCount = 0;
 694                 }
 695             }
 696 
 697             @Override
 698             public String toString() {
 699                 return String.format("MicroBlock[id=%d]", id);
 700             }
 701         }
 702 
 703         /**
 704          * Entry in the linked list of nodes.
 705          */
 706         private static class NodeEntry {
 707             private final Node node;
 708             private NodeEntry next;
 709 
 710             NodeEntry(Node node, NodeEntry next) {
 711                 this.node = node;
 712                 this.next = next;
 713             }
 714 
 715             public NodeEntry getNext() {
 716                 return next;
 717             }
 718 
 719             public Node getNode() {
 720                 return node;
 721             }
 722         }
 723 
 724         private void scheduleEarliestIterative(BlockMap<List<Node>> blockToNodes, NodeMap<Block> nodeToBlock, NodeBitMap visited, StructuredGraph graph, boolean immutableGraph) {
 725 
 726             NodeMap<MicroBlock> entries = graph.createNodeMap();
 727             NodeStack stack = new NodeStack();
 728 
 729             // Initialize with fixed nodes.
 730             MicroBlock startBlock = null;
 731             int nextId = 1;
 732             for (Block b : cfg.reversePostOrder()) {
 733                 FixedNode current = b.getBeginNode();
 734                 while (true) {
 735                     MicroBlock microBlock = new MicroBlock(nextId++);
 736                     entries.put(current, microBlock);
 737                     visited.checkAndMarkInc(current);
 738 
 739                     if (startBlock == null) {
 740                         startBlock = microBlock;
 741                     }
 742 
 743                     // Process inputs of this fixed node.
 744                     for (Node input : current.inputs()) {
 745                         if (entries.get(input) == null) {
 746                             processStack(input, startBlock, entries, visited, stack);
 747                         }
 748                     }
 749 
 750                     if (current == b.getEndNode()) {
 751                         // Break loop when reaching end node.
 752                         break;
 753                     }
 754 
 755                     current = ((FixedWithNextNode) current).next();
 756                 }
 757             }
 758 
 759             // Now process guards.
 760             for (GuardNode guardNode : graph.getNodes(GuardNode.TYPE)) {
 761                 if (entries.get(guardNode) == null) {
 762                     processStack(guardNode, startBlock, entries, visited, stack);
 763                 }
 764             }
 765 
 766             // Now process inputs of fixed nodes.
 767             for (Block b : cfg.reversePostOrder()) {
 768                 FixedNode current = b.getBeginNode();
 769                 while (true) {
 770 
 771                     // Process inputs of this fixed node.
 772                     for (Node input : current.inputs()) {
 773                         if (entries.get(input) == null) {
 774                             processStack(input, startBlock, entries, visited, stack);
 775                         }
 776                     }
 777 
 778                     if (current == b.getEndNode()) {
 779                         // Break loop when reaching end node.
 780                         break;
 781                     }
 782 
 783                     current = ((FixedWithNextNode) current).next();
 784                 }
 785             }
 786 
 787             if (visited.getCounter() < graph.getNodeCount()) {
 788 
 789                 // Visit back input edges of loop phis.
 790                 boolean changed;
 791                 boolean unmarkedPhi;
 792                 do {
 793                     changed = false;
 794                     unmarkedPhi = false;
 795                     for (LoopBeginNode loopBegin : graph.getNodes(LoopBeginNode.TYPE)) {
 796                         for (PhiNode phi : loopBegin.phis()) {
 797                             if (visited.isMarked(phi)) {
 798                                 for (int i = 0; i < loopBegin.getLoopEndCount(); ++i) {
 799                                     Node node = phi.valueAt(i + loopBegin.forwardEndCount());
 800                                     if (node != null && entries.get(node) == null) {
 801                                         changed = true;
 802                                         processStack(node, startBlock, entries, visited, stack);
 803                                     }
 804                                 }
 805                             } else {
 806                                 unmarkedPhi = true;
 807                             }
 808                         }
 809                     }
 810 
 811                     /*
 812                      * the processing of one loop phi could have marked a previously checked loop
 813                      * phi, therefore this needs to be iterative.
 814                      */
 815                 } while (unmarkedPhi && changed);
 816             }
 817 
 818             // Check for dead nodes.
 819             if (!immutableGraph && visited.getCounter() < graph.getNodeCount()) {
 820                 for (Node n : graph.getNodes()) {
 821                     if (!visited.isMarked(n)) {
 822                         n.clearInputs();
 823                         n.markDeleted();
 824                     }
 825                 }
 826             }
 827 
 828             for (Block b : cfg.reversePostOrder()) {
 829                 FixedNode fixedNode = b.getEndNode();
 830                 if (fixedNode instanceof ControlSplitNode) {
 831                     ControlSplitNode controlSplitNode = (ControlSplitNode) fixedNode;
 832                     MicroBlock endBlock = entries.get(fixedNode);
 833                     MicroBlock primarySuccessor = entries.get(controlSplitNode.getPrimarySuccessor());
 834                     endBlock.prependChildrenTo(primarySuccessor);
 835                 }
 836             }
 837 
 838             // Initialize with begin nodes
 839             for (Block b : cfg.reversePostOrder()) {
 840 
 841                 FixedNode current = b.getBeginNode();
 842                 int totalCount = 0;
 843                 while (true) {
 844 
 845                     MicroBlock microBlock = entries.get(current);
 846                     totalCount += microBlock.getNodeCount() + 1;
 847 
 848                     if (current == b.getEndNode()) {
 849                         // Break loop when reaching end node.
 850                         break;
 851                     }
 852 
 853                     current = ((FixedWithNextNode) current).next();
 854                 }
 855 
 856                 // Initialize with begin node, it is always the first node.
 857                 ArrayList<Node> nodes = new ArrayList<>(totalCount);
 858                 blockToNodes.put(b, nodes);
 859 
 860                 current = b.getBeginNode();
 861                 while (true) {
 862 
 863                     MicroBlock microBlock = entries.get(current);
 864                     nodeToBlock.set(current, b);
 865                     nodes.add(current);
 866                     NodeEntry next = microBlock.getFirstNode();
 867                     while (next != null) {
 868                         Node nextNode = next.getNode();
 869                         nodeToBlock.set(nextNode, b);
 870                         nodes.add(nextNode);
 871                         next = next.getNext();
 872                     }
 873 
 874                     if (current == b.getEndNode()) {
 875                         // Break loop when reaching end node.
 876                         break;
 877                     }
 878 
 879                     current = ((FixedWithNextNode) current).next();
 880                 }
 881             }
 882 
 883             assert (!GraalOptions.DetailedAsserts.getValue(cfg.graph.getOptions())) || MemoryScheduleVerification.check(cfg.getStartBlock(), blockToNodes);
 884         }
 885 
 886         private static void processStackPhi(NodeStack stack, PhiNode phiNode, NodeMap<MicroBlock> nodeToBlock, NodeBitMap visited) {
 887             stack.pop();
 888             if (visited.checkAndMarkInc(phiNode)) {
 889                 MicroBlock mergeBlock = nodeToBlock.get(phiNode.merge());
 890                 assert mergeBlock != null : phiNode;
 891                 nodeToBlock.set(phiNode, mergeBlock);
 892                 AbstractMergeNode merge = phiNode.merge();
 893                 for (int i = 0; i < merge.forwardEndCount(); ++i) {
 894                     Node input = phiNode.valueAt(i);
 895                     if (input != null && nodeToBlock.get(input) == null) {
 896                         stack.push(input);
 897                     }
 898                 }
 899             }
 900         }
 901 
 902         private static void processStackProxy(NodeStack stack, ProxyNode proxyNode, NodeMap<MicroBlock> nodeToBlock, NodeBitMap visited) {
 903             stack.pop();
 904             if (visited.checkAndMarkInc(proxyNode)) {
 905                 nodeToBlock.set(proxyNode, nodeToBlock.get(proxyNode.proxyPoint()));
 906                 Node input = proxyNode.value();
 907                 if (input != null && nodeToBlock.get(input) == null) {
 908                     stack.push(input);
 909                 }
 910             }
 911         }
 912 
 913         private static void processStack(Node first, MicroBlock startBlock, NodeMap<MicroBlock> nodeToMicroBlock, NodeBitMap visited, NodeStack stack) {
 914             assert stack.isEmpty();
 915             assert !visited.isMarked(first);
 916             stack.push(first);
 917             Node current = first;
 918             while (true) {
 919                 if (current instanceof PhiNode) {
 920                     processStackPhi(stack, (PhiNode) current, nodeToMicroBlock, visited);
 921                 } else if (current instanceof ProxyNode) {
 922                     processStackProxy(stack, (ProxyNode) current, nodeToMicroBlock, visited);
 923                 } else {
 924                     MicroBlock currentBlock = nodeToMicroBlock.get(current);
 925                     if (currentBlock == null) {
 926                         MicroBlock earliestBlock = processInputs(nodeToMicroBlock, stack, startBlock, current);
 927                         if (earliestBlock == null) {
 928                             // We need to delay until inputs are processed.
 929                         } else {
 930                             // Can immediately process and pop.
 931                             stack.pop();
 932                             visited.checkAndMarkInc(current);
 933                             nodeToMicroBlock.set(current, earliestBlock);
 934                             earliestBlock.add(current);
 935                         }
 936                     } else {
 937                         stack.pop();
 938                     }
 939                 }
 940 
 941                 if (stack.isEmpty()) {
 942                     break;
 943                 }
 944                 current = stack.peek();
 945             }
 946         }
 947 
 948         /**
 949          * Processes the inputs of given block. Pushes unprocessed inputs onto the stack. Returns
 950          * null if there were still unprocessed inputs, otherwise returns the earliest block given
 951          * node can be scheduled in.
 952          */
 953         private static MicroBlock processInputs(NodeMap<MicroBlock> nodeToBlock, NodeStack stack, MicroBlock startBlock, Node current) {
 954             if (current.getNodeClass().isLeafNode()) {
 955                 return startBlock;
 956             }
 957 
 958             MicroBlock earliestBlock = startBlock;
 959             for (Node input : current.inputs()) {
 960                 MicroBlock inputBlock = nodeToBlock.get(input);
 961                 if (inputBlock == null) {
 962                     earliestBlock = null;
 963                     stack.push(input);
 964                 } else if (earliestBlock != null && inputBlock.getId() >= earliestBlock.getId()) {
 965                     earliestBlock = inputBlock;
 966                 }
 967             }
 968             return earliestBlock;
 969         }
 970 
 971         private static boolean isFixedEnd(FixedNode endNode) {
 972             return endNode instanceof ControlSplitNode || endNode instanceof ControlSinkNode || endNode instanceof AbstractEndNode;
 973         }
 974 
 975         public String printScheduleHelper(String desc) {
 976             Formatter buf = new Formatter();
 977             buf.format("=== %s / %s ===%n", getCFG().getStartBlock().getBeginNode().graph(), desc);
 978             for (Block b : getCFG().getBlocks()) {
 979                 buf.format("==== b: %s (loopDepth: %s). ", b, b.getLoopDepth());
 980                 buf.format("dom: %s. ", b.getDominator());
 981                 buf.format("preds: %s. ", Arrays.toString(b.getPredecessors()));
 982                 buf.format("succs: %s ====%n", Arrays.toString(b.getSuccessors()));
 983 
 984                 if (blockToNodesMap.get(b) != null) {
 985                     for (Node n : nodesFor(b)) {
 986                         printNode(n);
 987                     }
 988                 } else {
 989                     for (Node n : b.getNodes()) {
 990                         printNode(n);
 991                     }
 992                 }
 993             }
 994             buf.format("%n");
 995             return buf.toString();
 996         }
 997 
 998         private static void printNode(Node n) {
 999             Formatter buf = new Formatter();
1000             buf.format("%s", n);
1001             if (n instanceof MemoryCheckpoint.Single) {
1002                 buf.format(" // kills %s", ((MemoryCheckpoint.Single) n).getLocationIdentity());
1003             } else if (n instanceof MemoryCheckpoint.Multi) {
1004                 buf.format(" // kills ");
1005                 for (LocationIdentity locid : ((MemoryCheckpoint.Multi) n).getLocationIdentities()) {
1006                     buf.format("%s, ", locid);
1007                 }
1008             } else if (n instanceof FloatingReadNode) {
1009                 FloatingReadNode frn = (FloatingReadNode) n;
1010                 buf.format(" // from %s", frn.getLocationIdentity());
1011                 buf.format(", lastAccess: %s", frn.getLastLocationAccess());
1012                 buf.format(", address: %s", frn.getAddress());
1013             } else if (n instanceof GuardNode) {
1014                 buf.format(", anchor: %s", ((GuardNode) n).getAnchor());
1015             }
1016             n.getDebug().log("%s", buf);
1017         }
1018 
1019         public ControlFlowGraph getCFG() {
1020             return cfg;
1021         }
1022 
1023         /**
1024          * Gets the nodes in a given block.
1025          */
1026         public List<Node> nodesFor(Block block) {
1027             return blockToNodesMap.get(block);
1028         }
1029     }
1030 
1031 }