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