1 /*
   2  * Copyright (c) 2011, 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.virtual.phases.ea;
  24 
  25 import java.util.ArrayList;
  26 import java.util.HashSet;
  27 import java.util.List;
  28 import java.util.Map;
  29 import java.util.Set;
  30 
  31 import org.graalvm.compiler.core.common.CollectionsFactory;
  32 import org.graalvm.compiler.core.common.LocationIdentity;
  33 import org.graalvm.compiler.core.common.cfg.BlockMap;
  34 import org.graalvm.compiler.core.common.cfg.Loop;
  35 import org.graalvm.compiler.core.common.type.Stamp;
  36 import org.graalvm.compiler.debug.Debug;
  37 import org.graalvm.compiler.debug.GraalError;
  38 import org.graalvm.compiler.debug.Indent;
  39 import org.graalvm.compiler.graph.Node;
  40 import org.graalvm.compiler.graph.NodeBitMap;
  41 import org.graalvm.compiler.graph.NodeMap;
  42 import org.graalvm.compiler.graph.iterators.NodeIterable;
  43 import org.graalvm.compiler.nodes.AbstractMergeNode;
  44 import org.graalvm.compiler.nodes.FixedWithNextNode;
  45 import org.graalvm.compiler.nodes.IfNode;
  46 import org.graalvm.compiler.nodes.LogicConstantNode;
  47 import org.graalvm.compiler.nodes.LogicNode;
  48 import org.graalvm.compiler.nodes.LoopBeginNode;
  49 import org.graalvm.compiler.nodes.LoopExitNode;
  50 import org.graalvm.compiler.nodes.PhiNode;
  51 import org.graalvm.compiler.nodes.ProxyNode;
  52 import org.graalvm.compiler.nodes.StructuredGraph;
  53 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  54 import org.graalvm.compiler.nodes.ValueNode;
  55 import org.graalvm.compiler.nodes.ValuePhiNode;
  56 import org.graalvm.compiler.nodes.cfg.Block;
  57 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  58 import org.graalvm.compiler.nodes.extended.BoxNode;
  59 import org.graalvm.compiler.nodes.util.GraphUtil;
  60 import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
  61 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
  62 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
  63 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator;
  64 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
  65 import org.graalvm.compiler.phases.graph.ReentrantBlockIterator.LoopInfo;
  66 
  67 public abstract class EffectsClosure<BlockT extends EffectsBlockState<BlockT>> extends EffectsPhase.Closure<BlockT> {
  68 
  69     protected final ControlFlowGraph cfg;
  70     protected final ScheduleResult schedule;
  71 
  72     protected final NodeMap<ValueNode> aliases;
  73     protected final BlockMap<GraphEffectList> blockEffects;
  74     private final Map<Loop<Block>, GraphEffectList> loopMergeEffects = CollectionsFactory.newIdentityMap();
  75     // Intended to be used by read-eliminating phases based on the effects phase.
  76     protected final Map<Loop<Block>, LoopKillCache> loopLocationKillCache = CollectionsFactory.newIdentityMap();
  77     private final Map<LoopBeginNode, BlockT> loopEntryStates = Node.newIdentityMap();
  78     private final NodeBitMap hasScalarReplacedInputs;
  79 
  80     protected boolean changed;
  81 
  82     public EffectsClosure(ScheduleResult schedule, ControlFlowGraph cfg) {
  83         this.schedule = schedule;
  84         this.cfg = cfg;
  85         this.aliases = cfg.graph.createNodeMap();
  86         this.hasScalarReplacedInputs = cfg.graph.createNodeBitMap();
  87         this.blockEffects = new BlockMap<>(cfg);
  88         for (Block block : cfg.getBlocks()) {
  89             blockEffects.put(block, new GraphEffectList());
  90         }
  91     }
  92 
  93     @Override
  94     public boolean hasChanged() {
  95         return changed;
  96     }
  97 
  98     @Override
  99     public void applyEffects() {
 100         final StructuredGraph graph = cfg.graph;
 101         final ArrayList<Node> obsoleteNodes = new ArrayList<>(0);
 102         final ArrayList<GraphEffectList> effectList = new ArrayList<>();
 103         BlockIteratorClosure<Void> closure = new BlockIteratorClosure<Void>() {
 104 
 105             @Override
 106             protected Void getInitialState() {
 107                 return null;
 108             }
 109 
 110             private void apply(GraphEffectList effects) {
 111                 if (effects != null && !effects.isEmpty()) {
 112                     effectList.add(effects);
 113                 }
 114             }
 115 
 116             @Override
 117             protected Void processBlock(Block block, Void currentState) {
 118                 apply(blockEffects.get(block));
 119                 return currentState;
 120             }
 121 
 122             @Override
 123             protected Void merge(Block merge, List<Void> states) {
 124                 return null;
 125             }
 126 
 127             @Override
 128             protected Void cloneState(Void oldState) {
 129                 return oldState;
 130             }
 131 
 132             @Override
 133             protected List<Void> processLoop(Loop<Block> loop, Void initialState) {
 134                 LoopInfo<Void> info = ReentrantBlockIterator.processLoop(this, loop, initialState);
 135                 apply(loopMergeEffects.get(loop));
 136                 return info.exitStates;
 137             }
 138         };
 139         ReentrantBlockIterator.apply(closure, cfg.getStartBlock());
 140         for (GraphEffectList effects : effectList) {
 141             Debug.log(" ==== effects");
 142             effects.apply(graph, obsoleteNodes, false);
 143         }
 144         for (GraphEffectList effects : effectList) {
 145             Debug.log(" ==== cfg kill effects");
 146             effects.apply(graph, obsoleteNodes, true);
 147         }
 148         Debug.dump(Debug.VERBOSE_LOG_LEVEL, graph, "After applying effects");
 149         assert VirtualUtil.assertNonReachable(graph, obsoleteNodes);
 150         for (Node node : obsoleteNodes) {
 151             if (node.isAlive()) {
 152                 node.replaceAtUsages(null);
 153                 GraphUtil.killWithUnusedFloatingInputs(node);
 154             }
 155         }
 156     }
 157 
 158     @Override
 159     protected BlockT processBlock(Block block, BlockT state) {
 160         if (!state.isDead()) {
 161             GraphEffectList effects = blockEffects.get(block);
 162 
 163             if (block.getBeginNode().predecessor() instanceof IfNode) {
 164                 IfNode ifNode = (IfNode) block.getBeginNode().predecessor();
 165                 LogicNode condition = ifNode.condition();
 166                 Node alias = getScalarAlias(condition);
 167                 if (alias instanceof LogicConstantNode) {
 168                     LogicConstantNode constant = (LogicConstantNode) alias;
 169                     boolean deadBranch = constant.getValue() != (block.getBeginNode() == ifNode.trueSuccessor());
 170 
 171                     if (deadBranch) {
 172                         state.markAsDead();
 173                         effects.killIfBranch(ifNode, constant.getValue());
 174                         return state;
 175                     }
 176                 }
 177             }
 178 
 179             VirtualUtil.trace("\nBlock: %s, preds: %s, succ: %s (", block, block.getPredecessors(), block.getSuccessors());
 180 
 181             FixedWithNextNode lastFixedNode = block.getBeginNode().predecessor() instanceof FixedWithNextNode ? (FixedWithNextNode) block.getBeginNode().predecessor() : null;
 182             Iterable<? extends Node> nodes = schedule != null ? schedule.getBlockToNodesMap().get(block) : block.getNodes();
 183             for (Node node : nodes) {
 184                 aliases.set(node, null);
 185                 if (node instanceof LoopExitNode) {
 186                     LoopExitNode loopExit = (LoopExitNode) node;
 187                     for (ProxyNode proxy : loopExit.proxies()) {
 188                         aliases.set(proxy, null);
 189                         changed |= processNode(proxy, state, effects, lastFixedNode) && isSignificantNode(node);
 190                     }
 191                     processLoopExit(loopExit, loopEntryStates.get(loopExit.loopBegin()), state, blockEffects.get(block));
 192                 }
 193                 changed |= processNode(node, state, effects, lastFixedNode) && isSignificantNode(node);
 194                 if (node instanceof FixedWithNextNode) {
 195                     lastFixedNode = (FixedWithNextNode) node;
 196                 }
 197                 if (state.isDead()) {
 198                     break;
 199                 }
 200             }
 201             VirtualUtil.trace(")\n    end state: %s\n", state);
 202         }
 203         return state;
 204     }
 205 
 206     private static boolean isSignificantNode(Node node) {
 207         return !(node instanceof CommitAllocationNode || node instanceof AllocatedObjectNode || node instanceof BoxNode);
 208     }
 209 
 210     /**
 211      * Collects the effects of virtualizing the given node.
 212      *
 213      * @return {@code true} if the effects include removing the node, {@code false} otherwise.
 214      */
 215     protected abstract boolean processNode(Node node, BlockT state, GraphEffectList effects, FixedWithNextNode lastFixedNode);
 216 
 217     @Override
 218     protected BlockT merge(Block merge, List<BlockT> states) {
 219         assert blockEffects.get(merge).isEmpty();
 220         MergeProcessor processor = createMergeProcessor(merge);
 221         doMergeWithoutDead(processor, states);
 222         processor.commitEnds(states);
 223         blockEffects.get(merge).addAll(processor.mergeEffects);
 224         blockEffects.get(merge).addAll(processor.afterMergeEffects);
 225         return processor.newState;
 226     }
 227 
 228     @Override
 229     @SuppressWarnings("try")
 230     protected final List<BlockT> processLoop(Loop<Block> loop, BlockT initialState) {
 231         if (initialState.isDead()) {
 232             ArrayList<BlockT> states = new ArrayList<>();
 233             for (int i = 0; i < loop.getExits().size(); i++) {
 234                 states.add(initialState);
 235             }
 236             return states;
 237         }
 238         /*
 239          * Special case nested loops: To avoid an exponential runtime for nested loops we try to
 240          * only process them as little times as possible.
 241          *
 242          * In the first iteration of an outer most loop we go into the inner most loop(s). We run
 243          * the first iteration of the inner most loop and then, if necessary, a second iteration.
 244          *
 245          * We return from the recursion and finish the first iteration of the outermost loop. If we
 246          * have to do a second iteration in the outer most loop we go again into the inner most
 247          * loop(s) but this time we already know all states that are killed by the loop so inside
 248          * the loop we will only have those changes that propagate from the first iteration of the
 249          * outer most loop into the current loop. We strip the initial loop state for the inner most
 250          * loops and do the first iteration with the (possible) changes from outer loops. If there
 251          * are no changes we only have to do 1 iteration and are done.
 252          *
 253          */
 254         BlockT initialStateRemovedKilledLocations = stripKilledLoopLocations(loop, cloneState(initialState));
 255         BlockT loopEntryState = initialStateRemovedKilledLocations;
 256         BlockT lastMergedState = cloneState(initialStateRemovedKilledLocations);
 257         processInitialLoopState(loop, lastMergedState);
 258         MergeProcessor mergeProcessor = createMergeProcessor(loop.getHeader());
 259         for (int iteration = 0; iteration < 10; iteration++) {
 260             try (Indent i = Debug.logAndIndent("================== Process Loop Effects Closure: block:%s begin node:%s", loop.getHeader(), loop.getHeader().getBeginNode())) {
 261                 LoopInfo<BlockT> info = ReentrantBlockIterator.processLoop(this, loop, cloneState(lastMergedState));
 262 
 263                 List<BlockT> states = new ArrayList<>();
 264                 states.add(initialStateRemovedKilledLocations);
 265                 states.addAll(info.endStates);
 266                 doMergeWithoutDead(mergeProcessor, states);
 267 
 268                 Debug.log("MergeProcessor New State: %s", mergeProcessor.newState);
 269                 Debug.log("===== vs.");
 270                 Debug.log("Last Merged State: %s", lastMergedState);
 271 
 272                 if (mergeProcessor.newState.equivalentTo(lastMergedState)) {
 273                     mergeProcessor.commitEnds(states);
 274 
 275                     blockEffects.get(loop.getHeader()).insertAll(mergeProcessor.mergeEffects, 0);
 276                     loopMergeEffects.put(loop, mergeProcessor.afterMergeEffects);
 277 
 278                     assert info.exitStates.size() == loop.getExits().size();
 279                     loopEntryStates.put((LoopBeginNode) loop.getHeader().getBeginNode(), loopEntryState);
 280                     assert assertExitStatesNonEmpty(loop, info);
 281 
 282                     processKilledLoopLocations(loop, initialStateRemovedKilledLocations, mergeProcessor.newState);
 283                     return info.exitStates;
 284                 } else {
 285                     lastMergedState = mergeProcessor.newState;
 286                     for (Block block : loop.getBlocks()) {
 287                         blockEffects.get(block).clear();
 288                     }
 289                 }
 290             }
 291         }
 292         throw new GraalError("too many iterations at %s", loop);
 293     }
 294 
 295     @SuppressWarnings("unused")
 296     protected BlockT stripKilledLoopLocations(Loop<Block> loop, BlockT initialState) {
 297         return initialState;
 298     }
 299 
 300     @SuppressWarnings("unused")
 301     protected void processKilledLoopLocations(Loop<Block> loop, BlockT initialState, BlockT mergedStates) {
 302         // nothing to do
 303     }
 304 
 305     @SuppressWarnings("unused")
 306     protected void processInitialLoopState(Loop<Block> loop, BlockT initialState) {
 307         // nothing to do
 308     }
 309 
 310     private void doMergeWithoutDead(MergeProcessor mergeProcessor, List<BlockT> states) {
 311         int alive = 0;
 312         for (BlockT state : states) {
 313             if (!state.isDead()) {
 314                 alive++;
 315             }
 316         }
 317         if (alive == 0) {
 318             mergeProcessor.setNewState(states.get(0));
 319         } else if (alive == states.size()) {
 320             int[] stateIndexes = new int[states.size()];
 321             for (int i = 0; i < stateIndexes.length; i++) {
 322                 stateIndexes[i] = i;
 323             }
 324             mergeProcessor.setStateIndexes(stateIndexes);
 325             mergeProcessor.merge(states);
 326         } else {
 327             ArrayList<BlockT> aliveStates = new ArrayList<>(alive);
 328             int[] stateIndexes = new int[alive];
 329             for (int i = 0; i < states.size(); i++) {
 330                 if (!states.get(i).isDead()) {
 331                     stateIndexes[aliveStates.size()] = i;
 332                     aliveStates.add(states.get(i));
 333                 }
 334             }
 335             mergeProcessor.setStateIndexes(stateIndexes);
 336             mergeProcessor.merge(aliveStates);
 337         }
 338     }
 339 
 340     private boolean assertExitStatesNonEmpty(Loop<Block> loop, LoopInfo<BlockT> info) {
 341         for (int i = 0; i < loop.getExits().size(); i++) {
 342             assert info.exitStates.get(i) != null : "no loop exit state at " + loop.getExits().get(i) + " / " + loop.getHeader();
 343         }
 344         return true;
 345     }
 346 
 347     protected abstract void processLoopExit(LoopExitNode exitNode, BlockT initialState, BlockT exitState, GraphEffectList effects);
 348 
 349     protected abstract MergeProcessor createMergeProcessor(Block merge);
 350 
 351     protected class MergeProcessor {
 352 
 353         private final Block mergeBlock;
 354         private final AbstractMergeNode merge;
 355 
 356         protected final GraphEffectList mergeEffects;
 357         protected final GraphEffectList afterMergeEffects;
 358 
 359         private int[] stateIndexes;
 360         protected BlockT newState;
 361 
 362         public MergeProcessor(Block mergeBlock) {
 363             this.mergeBlock = mergeBlock;
 364             this.merge = (AbstractMergeNode) mergeBlock.getBeginNode();
 365             this.mergeEffects = new GraphEffectList();
 366             this.afterMergeEffects = new GraphEffectList();
 367         }
 368 
 369         /**
 370          * @param states the states that should be merged.
 371          */
 372         protected void merge(List<BlockT> states) {
 373             setNewState(getInitialState());
 374         }
 375 
 376         private void setNewState(BlockT state) {
 377             newState = state;
 378             mergeEffects.clear();
 379             afterMergeEffects.clear();
 380         }
 381 
 382         private void setStateIndexes(int[] stateIndexes) {
 383             this.stateIndexes = stateIndexes;
 384         }
 385 
 386         @SuppressWarnings("unused")
 387         protected void commitEnds(List<BlockT> states) {
 388         }
 389 
 390         protected final Block getPredecessor(int index) {
 391             return mergeBlock.getPredecessors()[stateIndexes[index]];
 392         }
 393 
 394         protected final NodeIterable<PhiNode> getPhis() {
 395             return merge.phis();
 396         }
 397 
 398         protected final ValueNode getPhiValueAt(PhiNode phi, int index) {
 399             return phi.valueAt(stateIndexes[index]);
 400         }
 401 
 402         protected final ValuePhiNode createValuePhi(Stamp stamp) {
 403             return new ValuePhiNode(stamp, merge, new ValueNode[mergeBlock.getPredecessorCount()]);
 404         }
 405 
 406         protected final void setPhiInput(PhiNode phi, int index, ValueNode value) {
 407             afterMergeEffects.initializePhiInput(phi, stateIndexes[index], value);
 408         }
 409 
 410         protected final int getStateIndex(int i) {
 411             return stateIndexes[i];
 412         }
 413 
 414         protected final StructuredGraph graph() {
 415             return merge.graph();
 416         }
 417 
 418         @Override
 419         public String toString() {
 420             return "MergeProcessor@" + merge;
 421         }
 422     }
 423 
 424     public void addScalarAlias(ValueNode node, ValueNode alias) {
 425         assert !(alias instanceof VirtualObjectNode);
 426         aliases.set(node, alias);
 427         for (Node usage : node.usages()) {
 428             if (!hasScalarReplacedInputs.isNew(usage)) {
 429                 hasScalarReplacedInputs.mark(usage);
 430             }
 431         }
 432     }
 433 
 434     protected final boolean hasScalarReplacedInputs(Node node) {
 435         return hasScalarReplacedInputs.isMarked(node);
 436     }
 437 
 438     public ValueNode getScalarAlias(ValueNode node) {
 439         assert !(node instanceof VirtualObjectNode);
 440         if (node == null || !node.isAlive() || aliases.isNew(node)) {
 441             return node;
 442         }
 443         ValueNode result = aliases.get(node);
 444         return (result == null || result instanceof VirtualObjectNode) ? node : result;
 445     }
 446 
 447     protected static class LoopKillCache {
 448         private int visits;
 449         private LocationIdentity firstLocation;
 450         private Set<LocationIdentity> killedLocations;
 451         private boolean killsAll;
 452 
 453         protected LoopKillCache(int visits) {
 454             this.visits = visits;
 455         }
 456 
 457         protected void visited() {
 458             visits++;
 459         }
 460 
 461         protected int visits() {
 462             return visits;
 463         }
 464 
 465         protected void setKillsAll() {
 466             killsAll = true;
 467             firstLocation = null;
 468             killedLocations = null;
 469         }
 470 
 471         protected boolean containsLocation(LocationIdentity locationIdentity) {
 472             if (killsAll) {
 473                 return true;
 474             }
 475             if (firstLocation == null) {
 476                 return false;
 477             }
 478             if (!firstLocation.equals(locationIdentity)) {
 479                 return killedLocations != null ? killedLocations.contains(locationIdentity) : false;
 480             }
 481             return true;
 482         }
 483 
 484         protected void rememberLoopKilledLocation(LocationIdentity locationIdentity) {
 485             if (killsAll) {
 486                 return;
 487             }
 488             if (firstLocation == null || firstLocation.equals(locationIdentity)) {
 489                 firstLocation = locationIdentity;
 490             } else {
 491                 if (killedLocations == null) {
 492                     killedLocations = new HashSet<>();
 493                 }
 494                 killedLocations.add(locationIdentity);
 495             }
 496         }
 497 
 498         protected boolean loopKillsLocations() {
 499             if (killsAll) {
 500                 return true;
 501             }
 502             return firstLocation != null;
 503         }
 504     }
 505 
 506 }