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