1 /*
   2  * Copyright (c) 2009, 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.java;
  26 
  27 import static org.graalvm.compiler.nodeinfo.InputType.Anchor;
  28 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_8;
  29 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8;
  30 
  31 import java.util.Objects;
  32 
  33 import org.graalvm.compiler.core.common.type.ObjectStamp;
  34 import org.graalvm.compiler.core.common.type.Stamp;
  35 import org.graalvm.compiler.core.common.type.StampFactory;
  36 import org.graalvm.compiler.core.common.type.TypeReference;
  37 import org.graalvm.compiler.graph.NodeClass;
  38 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  39 import org.graalvm.compiler.nodeinfo.NodeInfo;
  40 import org.graalvm.compiler.nodes.LogicConstantNode;
  41 import org.graalvm.compiler.nodes.LogicNegationNode;
  42 import org.graalvm.compiler.nodes.LogicNode;
  43 import org.graalvm.compiler.nodes.NodeView;
  44 import org.graalvm.compiler.nodes.UnaryOpLogicNode;
  45 import org.graalvm.compiler.nodes.ValueNode;
  46 import org.graalvm.compiler.nodes.calc.IsNullNode;
  47 import org.graalvm.compiler.nodes.extended.AnchoringNode;
  48 import org.graalvm.compiler.nodes.spi.Lowerable;
  49 import org.graalvm.compiler.nodes.spi.LoweringTool;
  50 import org.graalvm.compiler.nodes.spi.Virtualizable;
  51 import org.graalvm.compiler.nodes.spi.VirtualizerTool;
  52 import org.graalvm.compiler.nodes.type.StampTool;
  53 
  54 import jdk.vm.ci.meta.JavaTypeProfile;
  55 import jdk.vm.ci.meta.TriState;
  56 
  57 /**
  58  * The {@code InstanceOfNode} represents an instanceof test.
  59  */
  60 @NodeInfo(cycles = CYCLES_8, size = SIZE_8)
  61 public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtualizable {
  62     public static final NodeClass<InstanceOfNode> TYPE = NodeClass.create(InstanceOfNode.class);
  63 
  64     private final ObjectStamp checkedStamp;
  65 
  66     private JavaTypeProfile profile;
  67     @OptionalInput(Anchor) protected AnchoringNode anchor;
  68 
  69     private InstanceOfNode(ObjectStamp checkedStamp, ValueNode object, JavaTypeProfile profile, AnchoringNode anchor) {
  70         this(TYPE, checkedStamp, object, profile, anchor);
  71     }
  72 
  73     protected InstanceOfNode(NodeClass<? extends InstanceOfNode> c, ObjectStamp checkedStamp, ValueNode object, JavaTypeProfile profile, AnchoringNode anchor) {
  74         super(c, object);
  75         this.checkedStamp = checkedStamp;
  76         this.profile = profile;
  77         this.anchor = anchor;
  78         assert (profile == null) || (anchor != null) : "profiles must be anchored";
  79         assert checkedStamp != null;
  80         assert type() != null;
  81     }
  82 
  83     public static LogicNode createAllowNull(TypeReference type, ValueNode object, JavaTypeProfile profile, AnchoringNode anchor) {
  84         if (StampTool.isPointerNonNull(object)) {
  85             return create(type, object, profile, anchor);
  86         }
  87         return createHelper(StampFactory.object(type), object, profile, anchor);
  88     }
  89 
  90     public static LogicNode create(TypeReference type, ValueNode object) {
  91         return create(type, object, null, null);
  92     }
  93 
  94     public static LogicNode create(TypeReference type, ValueNode object, JavaTypeProfile profile, AnchoringNode anchor) {
  95         return createHelper(StampFactory.objectNonNull(type), object, profile, anchor);
  96     }
  97 
  98     public static LogicNode createHelper(ObjectStamp checkedStamp, ValueNode object, JavaTypeProfile profile, AnchoringNode anchor) {
  99         LogicNode synonym = findSynonym(checkedStamp, object, NodeView.DEFAULT);
 100         if (synonym != null) {
 101             return synonym;
 102         } else {
 103             return new InstanceOfNode(checkedStamp, object, profile, anchor);
 104         }
 105     }
 106 
 107     @Override
 108     public void lower(LoweringTool tool) {
 109         tool.getLowerer().lower(this, tool);
 110     }
 111 
 112     @Override
 113     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
 114         NodeView view = NodeView.from(tool);
 115         LogicNode synonym = findSynonym(checkedStamp, forValue, view);
 116         if (synonym != null) {
 117             return synonym;
 118         } else {
 119             return this;
 120         }
 121     }
 122 
 123     public static LogicNode findSynonym(ObjectStamp checkedStamp, ValueNode object, NodeView view) {
 124         ObjectStamp inputStamp = (ObjectStamp) object.stamp(view);
 125         ObjectStamp joinedStamp = (ObjectStamp) checkedStamp.join(inputStamp);
 126 
 127         if (joinedStamp.isEmpty()) {
 128             // The check can never succeed, the intersection of the two stamps is empty.
 129             return LogicConstantNode.contradiction();
 130         } else {
 131             ObjectStamp meetStamp = (ObjectStamp) checkedStamp.meet(inputStamp);
 132             if (checkedStamp.equals(meetStamp)) {
 133                 // The check will always succeed, the union of the two stamps is equal to the
 134                 // checked stamp.
 135                 return LogicConstantNode.tautology();
 136             } else if (checkedStamp.alwaysNull()) {
 137                 return IsNullNode.create(object);
 138             } else if (Objects.equals(checkedStamp.type(), meetStamp.type()) && checkedStamp.isExactType() == meetStamp.isExactType() && checkedStamp.alwaysNull() == meetStamp.alwaysNull()) {
 139                 assert checkedStamp.nonNull() != inputStamp.nonNull();
 140                 // The only difference makes the null-ness of the value => simplify the check.
 141                 if (checkedStamp.nonNull()) {
 142                     return LogicNegationNode.create(IsNullNode.create(object));
 143                 } else {
 144                     return IsNullNode.create(object);
 145                 }
 146             }
 147             assert checkedStamp.type() != null;
 148         }
 149         return null;
 150     }
 151 
 152     /**
 153      * Gets the type being tested.
 154      */
 155     public TypeReference type() {
 156         return StampTool.typeReferenceOrNull(checkedStamp);
 157     }
 158 
 159     public JavaTypeProfile profile() {
 160         return profile;
 161     }
 162 
 163     @Override
 164     public void virtualize(VirtualizerTool tool) {
 165         ValueNode alias = tool.getAlias(getValue());
 166         TriState fold = tryFold(alias.stamp(NodeView.DEFAULT));
 167         if (fold != TriState.UNKNOWN) {
 168             tool.replaceWithValue(LogicConstantNode.forBoolean(fold.isTrue(), graph()));
 169         }
 170     }
 171 
 172     @Override
 173     public Stamp getSucceedingStampForValue(boolean negated) {
 174         if (negated) {
 175             return null;
 176         } else {
 177             return checkedStamp;
 178         }
 179     }
 180 
 181     @Override
 182     public TriState tryFold(Stamp valueStamp) {
 183         if (valueStamp instanceof ObjectStamp) {
 184             ObjectStamp inputStamp = (ObjectStamp) valueStamp;
 185             ObjectStamp joinedStamp = (ObjectStamp) checkedStamp.join(inputStamp);
 186 
 187             if (joinedStamp.isEmpty()) {
 188                 // The check can never succeed, the intersection of the two stamps is empty.
 189                 return TriState.FALSE;
 190             } else {
 191                 ObjectStamp meetStamp = (ObjectStamp) checkedStamp.meet(inputStamp);
 192                 if (checkedStamp.equals(meetStamp)) {
 193                     // The check will always succeed, the union of the two stamps is equal to the
 194                     // checked stamp.
 195                     return TriState.TRUE;
 196                 }
 197             }
 198         }
 199         return TriState.UNKNOWN;
 200     }
 201 
 202     public boolean allowsNull() {
 203         return !checkedStamp.nonNull();
 204     }
 205 
 206     public void setProfile(JavaTypeProfile typeProfile, AnchoringNode anchor) {
 207         this.profile = typeProfile;
 208         updateUsagesInterface(this.anchor, anchor);
 209         this.anchor = anchor;
 210         assert (profile == null) || (anchor != null) : "profiles must be anchored";
 211     }
 212 
 213     public AnchoringNode getAnchor() {
 214         return anchor;
 215     }
 216 
 217     public ObjectStamp getCheckedStamp() {
 218         return checkedStamp;
 219     }
 220 
 221     @Override
 222     public TriState implies(boolean thisNegated, LogicNode other) {
 223         if (other instanceof InstanceOfNode) {
 224             InstanceOfNode instanceOfNode = (InstanceOfNode) other;
 225             if (instanceOfNode.getValue() == getValue()) {
 226                 if (thisNegated) {
 227                     // !X => Y
 228                     if (this.getCheckedStamp().meet(instanceOfNode.getCheckedStamp()).equals(this.getCheckedStamp())) {
 229                         return TriState.get(false);
 230                     }
 231                 } else {
 232                     // X => Y
 233                     if (instanceOfNode.getCheckedStamp().meet(this.getCheckedStamp()).equals(instanceOfNode.getCheckedStamp())) {
 234                         return TriState.get(true);
 235                     }
 236                 }
 237             }
 238         }
 239         return super.implies(thisNegated, other);
 240     }
 241 }