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