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