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