1 /*
   2  * Copyright (c) 2015, 2016, 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.nodes;
  24 
  25 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
  26 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
  27 
  28 import java.util.List;
  29 
  30 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
  31 import org.graalvm.compiler.core.common.type.Stamp;
  32 import org.graalvm.compiler.graph.Node;
  33 import org.graalvm.compiler.graph.NodeClass;
  34 import org.graalvm.compiler.graph.spi.Canonicalizable;
  35 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  36 import org.graalvm.compiler.nodeinfo.NodeInfo;
  37 import org.graalvm.compiler.nodes.calc.FloatingNode;
  38 import org.graalvm.compiler.nodes.extended.GuardingNode;
  39 import org.graalvm.compiler.nodes.extended.IntegerSwitchNode;
  40 import org.graalvm.compiler.nodes.spi.StampProvider;
  41 import org.graalvm.compiler.nodes.util.GraphUtil;
  42 
  43 import jdk.vm.ci.code.Architecture;
  44 import jdk.vm.ci.meta.Assumptions;
  45 import jdk.vm.ci.meta.ConstantReflectionProvider;
  46 import jdk.vm.ci.meta.MetaAccessProvider;
  47 
  48 /**
  49  * Graph decoder that simplifies nodes during decoding. The standard
  50  * {@link Canonicalizable#canonical node canonicalization} interface is used to canonicalize nodes
  51  * during decoding. Additionally, {@link IfNode branches} and {@link IntegerSwitchNode switches}
  52  * with constant conditions are simplified.
  53  */
  54 public class SimplifyingGraphDecoder extends GraphDecoder {
  55 
  56     protected final MetaAccessProvider metaAccess;
  57     protected final ConstantReflectionProvider constantReflection;
  58     protected final ConstantFieldProvider constantFieldProvider;
  59     protected final StampProvider stampProvider;
  60     protected final boolean canonicalizeReads;
  61 
  62     protected class PECanonicalizerTool implements CanonicalizerTool {
  63 
  64         private final Assumptions assumptions;
  65 
  66         public PECanonicalizerTool(Assumptions assumptions) {
  67             this.assumptions = assumptions;
  68         }
  69 
  70         @Override
  71         public MetaAccessProvider getMetaAccess() {
  72             return metaAccess;
  73         }
  74 
  75         @Override
  76         public ConstantReflectionProvider getConstantReflection() {
  77             return constantReflection;
  78         }
  79 
  80         @Override
  81         public ConstantFieldProvider getConstantFieldProvider() {
  82             return constantFieldProvider;
  83         }
  84 
  85         @Override
  86         public boolean canonicalizeReads() {
  87             return canonicalizeReads;
  88         }
  89 
  90         @Override
  91         public boolean allUsagesAvailable() {
  92             return false;
  93         }
  94 
  95         @Override
  96         public Assumptions getAssumptions() {
  97             return assumptions;
  98         }
  99 
 100         @Override
 101         public boolean supportSubwordCompare(int bits) {
 102             // to be safe, just report false here
 103             // there will be more opportunities for this optimization later
 104             return false;
 105         }
 106     }
 107 
 108     @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
 109     static class CanonicalizeToNullNode extends FloatingNode implements Canonicalizable, GuardingNode {
 110         public static final NodeClass<CanonicalizeToNullNode> TYPE = NodeClass.create(CanonicalizeToNullNode.class);
 111 
 112         protected CanonicalizeToNullNode(Stamp stamp) {
 113             super(TYPE, stamp);
 114         }
 115 
 116         @Override
 117         public Node canonical(CanonicalizerTool tool) {
 118             return null;
 119         }
 120     }
 121 
 122     public SimplifyingGraphDecoder(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider, StampProvider stampProvider,
 123                     boolean canonicalizeReads, Architecture architecture) {
 124         super(architecture);
 125         this.metaAccess = metaAccess;
 126         this.constantReflection = constantReflection;
 127         this.constantFieldProvider = constantFieldProvider;
 128         this.stampProvider = stampProvider;
 129         this.canonicalizeReads = canonicalizeReads;
 130     }
 131 
 132     @Override
 133     protected void cleanupGraph(MethodScope methodScope) {
 134         GraphUtil.normalizeLoops(methodScope.graph);
 135         super.cleanupGraph(methodScope);
 136 
 137         for (Node node : methodScope.graph.getNewNodes(methodScope.methodStartMark)) {
 138             if (node instanceof MergeNode) {
 139                 MergeNode mergeNode = (MergeNode) node;
 140                 if (mergeNode.forwardEndCount() == 1) {
 141                     methodScope.graph.reduceTrivialMerge(mergeNode);
 142                 }
 143             }
 144         }
 145 
 146         for (Node node : methodScope.graph.getNewNodes(methodScope.methodStartMark)) {
 147             if (node instanceof BeginNode || node instanceof KillingBeginNode) {
 148                 if (!(node.predecessor() instanceof ControlSplitNode) && node.hasNoUsages()) {
 149                     GraphUtil.unlinkFixedNode((AbstractBeginNode) node);
 150                     node.safeDelete();
 151                 }
 152             }
 153         }
 154 
 155         for (Node node : methodScope.graph.getNewNodes(methodScope.methodStartMark)) {
 156             if (!(node instanceof FixedNode) && node.hasNoUsages()) {
 157                 GraphUtil.killCFG(node);
 158             }
 159         }
 160     }
 161 
 162     @Override
 163     protected boolean allowLazyPhis() {
 164         /*
 165          * We do not need to exactly reproduce the encoded graph, so we want to avoid unnecessary
 166          * phi functions.
 167          */
 168         return true;
 169     }
 170 
 171     @Override
 172     protected void handleMergeNode(MergeNode merge) {
 173         /*
 174          * All inputs of non-loop phi nodes are known by now. We can infer the stamp for the phi, so
 175          * that parsing continues with more precise type information.
 176          */
 177         for (ValuePhiNode phi : merge.valuePhis()) {
 178             phi.inferStamp();
 179         }
 180     }
 181 
 182     @Override
 183     protected void handleFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) {
 184         if (node instanceof IfNode) {
 185             IfNode ifNode = (IfNode) node;
 186             if (ifNode.condition() instanceof LogicNegationNode) {
 187                 ifNode.eliminateNegation();
 188             }
 189             if (ifNode.condition() instanceof LogicConstantNode) {
 190                 boolean condition = ((LogicConstantNode) ifNode.condition()).getValue();
 191                 AbstractBeginNode survivingSuccessor = ifNode.getSuccessor(condition);
 192                 AbstractBeginNode deadSuccessor = ifNode.getSuccessor(!condition);
 193 
 194                 methodScope.graph.removeSplit(ifNode, survivingSuccessor);
 195                 assert deadSuccessor.next() == null : "must not be parsed yet";
 196                 deadSuccessor.safeDelete();
 197             }
 198 
 199         } else if (node instanceof IntegerSwitchNode && ((IntegerSwitchNode) node).value().isConstant()) {
 200             IntegerSwitchNode switchNode = (IntegerSwitchNode) node;
 201             int value = switchNode.value().asJavaConstant().asInt();
 202             AbstractBeginNode survivingSuccessor = switchNode.successorAtKey(value);
 203             List<Node> allSuccessors = switchNode.successors().snapshot();
 204 
 205             methodScope.graph.removeSplit(switchNode, survivingSuccessor);
 206             for (Node successor : allSuccessors) {
 207                 if (successor != survivingSuccessor) {
 208                     assert ((AbstractBeginNode) successor).next() == null : "must not be parsed yet";
 209                     successor.safeDelete();
 210                 }
 211             }
 212 
 213         } else if (node instanceof FixedGuardNode) {
 214             FixedGuardNode guard = (FixedGuardNode) node;
 215             if (guard.getCondition() instanceof LogicConstantNode) {
 216                 LogicConstantNode condition = (LogicConstantNode) guard.getCondition();
 217                 Node canonical;
 218                 if (condition.getValue() == guard.isNegated()) {
 219                     DeoptimizeNode deopt = new DeoptimizeNode(guard.getAction(), guard.getReason(), guard.getSpeculation());
 220                     if (guard.stateBefore() != null) {
 221                         deopt.setStateBefore(guard.stateBefore());
 222                     }
 223                     canonical = deopt;
 224                 } else {
 225                     /*
 226                      * The guard is unnecessary, but we cannot remove the node completely yet
 227                      * because there might be nodes that use it as a guard input. Therefore, we
 228                      * replace it with a more lightweight node (which is floating and has no
 229                      * inputs).
 230                      */
 231                     canonical = new CanonicalizeToNullNode(node.stamp);
 232                 }
 233                 handleCanonicalization(methodScope, loopScope, nodeOrderId, node, canonical);
 234             }
 235 
 236         } else if (node instanceof Canonicalizable) {
 237             Node canonical = ((Canonicalizable) node).canonical(new PECanonicalizerTool(methodScope.graph.getAssumptions()));
 238             if (canonical != node) {
 239                 handleCanonicalization(methodScope, loopScope, nodeOrderId, node, canonical);
 240             }
 241         }
 242     }
 243 
 244     private void handleCanonicalization(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node, Node c) {
 245         Node canonical = c;
 246 
 247         if (canonical == null) {
 248             /*
 249              * This is a possible return value of canonicalization. However, we might need to add
 250              * additional usages later on for which we need a node. Therefore, we just do nothing
 251              * and leave the node in place.
 252              */
 253             return;
 254         }
 255 
 256         if (!canonical.isAlive()) {
 257             assert !canonical.isDeleted();
 258             canonical = methodScope.graph.addOrUniqueWithInputs(canonical);
 259             if (canonical instanceof FixedWithNextNode) {
 260                 methodScope.graph.addBeforeFixed(node, (FixedWithNextNode) canonical);
 261             } else if (canonical instanceof ControlSinkNode) {
 262                 FixedWithNextNode predecessor = (FixedWithNextNode) node.predecessor();
 263                 predecessor.setNext((ControlSinkNode) canonical);
 264                 List<Node> successorSnapshot = node.successors().snapshot();
 265                 node.safeDelete();
 266                 for (Node successor : successorSnapshot) {
 267                     successor.safeDelete();
 268                 }
 269 
 270             } else {
 271                 assert !(canonical instanceof FixedNode);
 272             }
 273         }
 274         if (!node.isDeleted()) {
 275             GraphUtil.unlinkFixedNode((FixedWithNextNode) node);
 276             node.replaceAtUsagesAndDelete(canonical);
 277         }
 278         assert lookupNode(loopScope, nodeOrderId) == node;
 279         registerNode(loopScope, nodeOrderId, canonical, true, false);
 280     }
 281 
 282     @Override
 283     protected Node handleFloatingNodeBeforeAdd(MethodScope methodScope, LoopScope loopScope, Node node) {
 284         if (node instanceof ValueNode) {
 285             ((ValueNode) node).inferStamp();
 286         }
 287         if (node instanceof Canonicalizable) {
 288             Node canonical = ((Canonicalizable) node).canonical(new PECanonicalizerTool(methodScope.graph.getAssumptions()));
 289             if (canonical == null) {
 290                 /*
 291                  * This is a possible return value of canonicalization. However, we might need to
 292                  * add additional usages later on for which we need a node. Therefore, we just do
 293                  * nothing and leave the node in place.
 294                  */
 295             } else if (canonical != node) {
 296                 if (!canonical.isAlive()) {
 297                     assert !canonical.isDeleted();
 298                     canonical = methodScope.graph.addOrUniqueWithInputs(canonical);
 299                 }
 300                 assert node.hasNoUsages();
 301                 // methodScope.graph.replaceFloating((FloatingNode) node, canonical);
 302                 return canonical;
 303             }
 304         }
 305         return node;
 306     }
 307 
 308     @Override
 309     protected Node addFloatingNode(MethodScope methodScope, Node node) {
 310         /*
 311          * In contrast to the base class implementation, we do not need to exactly reproduce the
 312          * encoded graph. Since we do canonicalization, we also want nodes to be unique.
 313          */
 314         return methodScope.graph.addOrUnique(node);
 315     }
 316 }