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