1 /* 2 * Copyright (c) 2012, 2018, 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 24 25 package org.graalvm.compiler.loop; 26 27 import java.util.ArrayDeque; 28 import java.util.Collections; 29 import java.util.Deque; 30 import java.util.Iterator; 31 32 import jdk.internal.vm.compiler.collections.EconomicMap; 33 import org.graalvm.compiler.debug.GraalError; 34 import org.graalvm.compiler.graph.Graph; 35 import org.graalvm.compiler.graph.Graph.DuplicationReplacement; 36 import org.graalvm.compiler.graph.Node; 37 import org.graalvm.compiler.graph.NodeBitMap; 38 import org.graalvm.compiler.graph.iterators.NodeIterable; 39 import org.graalvm.compiler.nodes.AbstractBeginNode; 40 import org.graalvm.compiler.nodes.AbstractMergeNode; 41 import org.graalvm.compiler.nodes.EndNode; 42 import org.graalvm.compiler.nodes.FixedNode; 43 import org.graalvm.compiler.nodes.FrameState; 44 import org.graalvm.compiler.nodes.GuardNode; 45 import org.graalvm.compiler.nodes.GuardPhiNode; 46 import org.graalvm.compiler.nodes.GuardProxyNode; 47 import org.graalvm.compiler.nodes.Invoke; 48 import org.graalvm.compiler.nodes.LoopExitNode; 49 import org.graalvm.compiler.nodes.MergeNode; 50 import org.graalvm.compiler.nodes.NodeView; 51 import org.graalvm.compiler.nodes.PhiNode; 52 import org.graalvm.compiler.nodes.ProxyNode; 53 import org.graalvm.compiler.nodes.StructuredGraph; 54 import org.graalvm.compiler.nodes.ValueNode; 55 import org.graalvm.compiler.nodes.ValuePhiNode; 56 import org.graalvm.compiler.nodes.ValueProxyNode; 57 import org.graalvm.compiler.nodes.VirtualState; 58 import org.graalvm.compiler.nodes.cfg.Block; 59 import org.graalvm.compiler.nodes.java.MonitorEnterNode; 60 import org.graalvm.compiler.nodes.spi.NodeWithState; 61 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode; 62 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; 63 64 import jdk.vm.ci.meta.TriState; 65 66 public abstract class LoopFragment { 67 68 private final LoopEx loop; 69 private final LoopFragment original; 70 protected NodeBitMap nodes; 71 protected boolean nodesReady; 72 private EconomicMap<Node, Node> duplicationMap; 73 74 public LoopFragment(LoopEx loop) { 75 this(loop, null); 76 this.nodesReady = true; 77 } 78 79 public LoopFragment(LoopEx loop, LoopFragment original) { 80 this.loop = loop; 81 this.original = original; 82 this.nodesReady = false; 83 } 84 85 /** 86 * Return the original LoopEx for this fragment. For duplicated fragments this returns null. 87 */ 88 protected LoopEx loop() { 89 return loop; 90 } 91 92 public abstract LoopFragment duplicate(); 93 94 public abstract void insertBefore(LoopEx l); 95 96 public void disconnect() { 97 // TODO (gd) possibly abstract 98 } 99 100 public boolean contains(Node n) { 101 return nodes().isMarkedAndGrow(n); 102 } 103 104 @SuppressWarnings("unchecked") 105 public <New extends Node, Old extends New> New getDuplicatedNode(Old n) { 106 assert isDuplicate(); 107 return (New) duplicationMap.get(n); 108 } 109 110 protected <New extends Node, Old extends New> void putDuplicatedNode(Old oldNode, New newNode) { 111 duplicationMap.put(oldNode, newNode); 112 } 113 114 /** 115 * Gets the corresponding value in this fragment. Should be called on duplicate fragments with a 116 * node from the original fragment as argument. 117 * 118 * @param b original value 119 * @return corresponding value in the peel 120 */ 121 protected abstract ValueNode prim(ValueNode b); 122 123 public boolean isDuplicate() { 124 return original != null; 125 } 126 127 public LoopFragment original() { 128 return original; 129 } 130 131 public abstract NodeBitMap nodes(); 132 133 public StructuredGraph graph() { 134 LoopEx l; 135 if (isDuplicate()) { 136 l = original().loop(); 137 } else { 138 l = loop(); 139 } 140 return l.loopBegin().graph(); 141 } 142 143 protected abstract DuplicationReplacement getDuplicationReplacement(); 144 145 protected abstract void beforeDuplication(); 146 147 protected abstract void finishDuplication(); 148 149 protected void patchNodes(final DuplicationReplacement dataFix) { 150 if (isDuplicate() && !nodesReady) { 151 assert !original.isDuplicate(); 152 final DuplicationReplacement cfgFix = original().getDuplicationReplacement(); 153 DuplicationReplacement dr; 154 if (cfgFix == null && dataFix != null) { 155 dr = dataFix; 156 } else if (cfgFix != null && dataFix == null) { 157 dr = cfgFix; 158 } else if (cfgFix != null && dataFix != null) { 159 dr = new DuplicationReplacement() { 160 161 @Override 162 public Node replacement(Node o) { 163 Node r1 = dataFix.replacement(o); 164 if (r1 != o) { 165 assert cfgFix.replacement(o) == o; 166 return r1; 167 } 168 Node r2 = cfgFix.replacement(o); 169 if (r2 != o) { 170 return r2; 171 } 172 return o; 173 } 174 }; 175 } else { 176 dr = null; 177 } 178 beforeDuplication(); 179 NodeIterable<Node> nodesIterable = original().nodes(); 180 duplicationMap = graph().addDuplicates(nodesIterable, graph(), nodesIterable.count(), dr); 181 finishDuplication(); 182 nodes = new NodeBitMap(graph()); 183 nodes.markAll(duplicationMap.getValues()); 184 nodesReady = true; 185 } else { 186 // TODO (gd) apply fix ? 187 } 188 } 189 190 protected static NodeBitMap computeNodes(Graph graph, Iterable<AbstractBeginNode> blocks) { 191 return computeNodes(graph, blocks, Collections.emptyList()); 192 } 193 194 protected static NodeBitMap computeNodes(Graph graph, Iterable<AbstractBeginNode> blocks, Iterable<AbstractBeginNode> earlyExits) { 195 final NodeBitMap nodes = graph.createNodeBitMap(); 196 computeNodes(nodes, graph, blocks, earlyExits); 197 return nodes; 198 } 199 200 protected static void computeNodes(NodeBitMap nodes, Graph graph, Iterable<AbstractBeginNode> blocks, Iterable<AbstractBeginNode> earlyExits) { 201 for (AbstractBeginNode b : blocks) { 202 if (b.isDeleted()) { 203 continue; 204 } 205 206 for (Node n : b.getBlockNodes()) { 207 if (n instanceof Invoke) { 208 nodes.mark(((Invoke) n).callTarget()); 209 } 210 if (n instanceof NodeWithState) { 211 NodeWithState withState = (NodeWithState) n; 212 withState.states().forEach(state -> state.applyToVirtual(node -> nodes.mark(node))); 213 } 214 if (n instanceof AbstractMergeNode) { 215 // if a merge is in the loop, all of its phis are also in the loop 216 for (PhiNode phi : ((AbstractMergeNode) n).phis()) { 217 nodes.mark(phi); 218 } 219 } 220 nodes.mark(n); 221 } 222 } 223 for (AbstractBeginNode earlyExit : earlyExits) { 224 if (earlyExit.isDeleted()) { 225 continue; 226 } 227 228 nodes.mark(earlyExit); 229 230 if (earlyExit instanceof LoopExitNode) { 231 LoopExitNode loopExit = (LoopExitNode) earlyExit; 232 FrameState stateAfter = loopExit.stateAfter(); 233 if (stateAfter != null) { 234 stateAfter.applyToVirtual(node -> nodes.mark(node)); 235 } 236 for (ProxyNode proxy : loopExit.proxies()) { 237 nodes.mark(proxy); 238 } 239 } 240 } 241 242 final NodeBitMap nonLoopNodes = graph.createNodeBitMap(); 243 Deque<WorkListEntry> worklist = new ArrayDeque<>(); 244 for (AbstractBeginNode b : blocks) { 245 if (b.isDeleted()) { 246 continue; 247 } 248 249 for (Node n : b.getBlockNodes()) { 250 if (n instanceof CommitAllocationNode) { 251 for (VirtualObjectNode obj : ((CommitAllocationNode) n).getVirtualObjects()) { 252 markFloating(worklist, obj, nodes, nonLoopNodes); 253 } 254 } 255 if (n instanceof MonitorEnterNode) { 256 markFloating(worklist, ((MonitorEnterNode) n).getMonitorId(), nodes, nonLoopNodes); 257 } 258 if (n instanceof AbstractMergeNode) { 259 /* 260 * Since we already marked all phi nodes as being in the loop to break cycles, 261 * we also have to iterate over their usages here. 262 */ 263 for (PhiNode phi : ((AbstractMergeNode) n).phis()) { 264 for (Node usage : phi.usages()) { 265 markFloating(worklist, usage, nodes, nonLoopNodes); 266 } 267 } 268 } 269 for (Node usage : n.usages()) { 270 markFloating(worklist, usage, nodes, nonLoopNodes); 271 } 272 } 273 } 274 } 275 276 static class WorkListEntry { 277 final Iterator<Node> usages; 278 final Node n; 279 boolean isLoopNode; 280 281 WorkListEntry(Node n, NodeBitMap loopNodes) { 282 this.n = n; 283 this.usages = n.usages().iterator(); 284 this.isLoopNode = loopNodes.isMarked(n); 285 } 286 287 @Override 288 public boolean equals(Object obj) { 289 if (!(obj instanceof WorkListEntry)) { 290 return false; 291 } 292 WorkListEntry other = (WorkListEntry) obj; 293 return this.n == other.n; 294 } 295 296 @Override 297 public int hashCode() { 298 return n.hashCode(); 299 } 300 } 301 302 static TriState isLoopNode(Node n, NodeBitMap loopNodes, NodeBitMap nonLoopNodes) { 303 if (loopNodes.isMarked(n)) { 304 return TriState.TRUE; 305 } 306 if (nonLoopNodes.isMarked(n)) { 307 return TriState.FALSE; 308 } 309 if (n instanceof FixedNode || n instanceof PhiNode) { 310 // phi nodes are treated the same as fixed nodes in this algorithm to break cycles 311 return TriState.FALSE; 312 } 313 return TriState.UNKNOWN; 314 } 315 316 private static void pushWorkList(Deque<WorkListEntry> workList, Node node, NodeBitMap loopNodes) { 317 WorkListEntry entry = new WorkListEntry(node, loopNodes); 318 assert !workList.contains(entry) : "node " + node + " added to worklist twice"; 319 workList.push(entry); 320 } 321 322 private static void markFloating(Deque<WorkListEntry> workList, Node start, NodeBitMap loopNodes, NodeBitMap nonLoopNodes) { 323 if (isLoopNode(start, loopNodes, nonLoopNodes).isKnown()) { 324 return; 325 } 326 pushWorkList(workList, start, loopNodes); 327 while (!workList.isEmpty()) { 328 WorkListEntry currentEntry = workList.peek(); 329 if (currentEntry.usages.hasNext()) { 330 Node current = currentEntry.usages.next(); 331 TriState result = isLoopNode(current, loopNodes, nonLoopNodes); 332 if (result.isKnown()) { 333 if (result.toBoolean()) { 334 currentEntry.isLoopNode = true; 335 } 336 } else { 337 pushWorkList(workList, current, loopNodes); 338 } 339 } else { 340 workList.pop(); 341 boolean isLoopNode = currentEntry.isLoopNode; 342 Node current = currentEntry.n; 343 if (!isLoopNode && current instanceof GuardNode) { 344 /* 345 * (gd) this is only OK if we are not going to make loop transforms based on 346 * this 347 */ 348 assert !((GuardNode) current).graph().hasValueProxies(); 349 isLoopNode = true; 350 } 351 if (isLoopNode) { 352 loopNodes.mark(current); 353 for (WorkListEntry e : workList) { 354 e.isLoopNode = true; 355 } 356 } else { 357 nonLoopNodes.mark(current); 358 } 359 } 360 } 361 } 362 363 public static NodeIterable<AbstractBeginNode> toHirBlocks(final Iterable<Block> blocks) { 364 return new NodeIterable<AbstractBeginNode>() { 365 366 @Override 367 public Iterator<AbstractBeginNode> iterator() { 368 final Iterator<Block> it = blocks.iterator(); 369 return new Iterator<AbstractBeginNode>() { 370 371 @Override 372 public void remove() { 373 throw new UnsupportedOperationException(); 374 } 375 376 @Override 377 public AbstractBeginNode next() { 378 return it.next().getBeginNode(); 379 } 380 381 @Override 382 public boolean hasNext() { 383 return it.hasNext(); 384 } 385 }; 386 } 387 388 }; 389 } 390 391 public static NodeIterable<AbstractBeginNode> toHirExits(final Iterable<Block> blocks) { 392 return new NodeIterable<AbstractBeginNode>() { 393 394 @Override 395 public Iterator<AbstractBeginNode> iterator() { 396 final Iterator<Block> it = blocks.iterator(); 397 return new Iterator<AbstractBeginNode>() { 398 399 @Override 400 public void remove() { 401 throw new UnsupportedOperationException(); 402 } 403 404 /** 405 * Return the true LoopExitNode for this loop or the BeginNode for the block. 406 */ 407 @Override 408 public AbstractBeginNode next() { 409 Block next = it.next(); 410 LoopExitNode exit = next.getLoopExit(); 411 if (exit != null) { 412 return exit; 413 } 414 return next.getBeginNode(); 415 } 416 417 @Override 418 public boolean hasNext() { 419 return it.hasNext(); 420 } 421 }; 422 } 423 424 }; 425 } 426 427 /** 428 * Merges the early exits (i.e. loop exits) that were duplicated as part of this fragment, with 429 * the original fragment's exits. 430 */ 431 protected void mergeEarlyExits() { 432 assert isDuplicate(); 433 StructuredGraph graph = graph(); 434 for (AbstractBeginNode earlyExit : LoopFragment.toHirBlocks(original().loop().loop().getExits())) { 435 LoopExitNode loopEarlyExit = (LoopExitNode) earlyExit; 436 FixedNode next = loopEarlyExit.next(); 437 if (loopEarlyExit.isDeleted() || !this.original().contains(loopEarlyExit)) { 438 continue; 439 } 440 AbstractBeginNode newEarlyExit = getDuplicatedNode(loopEarlyExit); 441 if (newEarlyExit == null) { 442 continue; 443 } 444 MergeNode merge = graph.add(new MergeNode()); 445 EndNode originalEnd = graph.add(new EndNode()); 446 EndNode newEnd = graph.add(new EndNode()); 447 merge.addForwardEnd(originalEnd); 448 merge.addForwardEnd(newEnd); 449 loopEarlyExit.setNext(originalEnd); 450 newEarlyExit.setNext(newEnd); 451 merge.setNext(next); 452 453 FrameState exitState = loopEarlyExit.stateAfter(); 454 if (exitState != null) { 455 FrameState originalExitState = exitState; 456 exitState = exitState.duplicateWithVirtualState(); 457 loopEarlyExit.setStateAfter(exitState); 458 merge.setStateAfter(originalExitState); 459 /* 460 * Using the old exit's state as the merge's state is necessary because some of the 461 * VirtualState nodes contained in the old exit's state may be shared by other 462 * dominated VirtualStates. Those dominated virtual states need to see the 463 * proxy->phi update that are applied below. 464 * 465 * We now update the original fragment's nodes accordingly: 466 */ 467 originalExitState.applyToVirtual(node -> original.nodes.clearAndGrow(node)); 468 exitState.applyToVirtual(node -> original.nodes.markAndGrow(node)); 469 } 470 FrameState finalExitState = exitState; 471 472 for (Node anchored : loopEarlyExit.anchored().snapshot()) { 473 anchored.replaceFirstInput(loopEarlyExit, merge); 474 } 475 476 boolean newEarlyExitIsLoopExit = newEarlyExit instanceof LoopExitNode; 477 for (ProxyNode vpn : loopEarlyExit.proxies().snapshot()) { 478 if (vpn.hasNoUsages()) { 479 continue; 480 } 481 if (vpn.value() == null) { 482 assert vpn instanceof GuardProxyNode; 483 vpn.replaceAtUsages(null); 484 continue; 485 } 486 final ValueNode replaceWith; 487 ValueNode newVpn = prim(newEarlyExitIsLoopExit ? vpn : vpn.value()); 488 if (newVpn != null) { 489 PhiNode phi; 490 if (vpn instanceof ValueProxyNode) { 491 phi = graph.addWithoutUnique(new ValuePhiNode(vpn.stamp(NodeView.DEFAULT), merge)); 492 } else if (vpn instanceof GuardProxyNode) { 493 phi = graph.addWithoutUnique(new GuardPhiNode(merge)); 494 } else { 495 throw GraalError.shouldNotReachHere(); 496 } 497 phi.addInput(vpn); 498 phi.addInput(newVpn); 499 replaceWith = phi; 500 } else { 501 replaceWith = vpn.value(); 502 } 503 vpn.replaceAtMatchingUsages(replaceWith, usage -> { 504 if (merge.isPhiAtMerge(usage)) { 505 return false; 506 } 507 if (usage instanceof VirtualState) { 508 VirtualState stateUsage = (VirtualState) usage; 509 if (finalExitState != null && finalExitState.isPartOfThisState(stateUsage)) { 510 return false; 511 } 512 } 513 return true; 514 }); 515 } 516 } 517 } 518 }