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