001/* 002 * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.nodes.java; 024 025import static com.oracle.graal.nodes.extended.BranchProbabilityNode.*; 026import static jdk.internal.jvmci.meta.DeoptimizationAction.*; 027import static jdk.internal.jvmci.meta.DeoptimizationReason.*; 028import jdk.internal.jvmci.meta.*; 029import jdk.internal.jvmci.meta.Assumptions.*; 030 031import com.oracle.graal.compiler.common.type.*; 032import com.oracle.graal.graph.*; 033import com.oracle.graal.graph.spi.*; 034import com.oracle.graal.nodeinfo.*; 035import com.oracle.graal.nodes.*; 036import com.oracle.graal.nodes.calc.*; 037import com.oracle.graal.nodes.extended.*; 038import com.oracle.graal.nodes.spi.*; 039import com.oracle.graal.nodes.type.*; 040 041/** 042 * Implements a type check against a compile-time known type. 043 */ 044@NodeInfo 045public class CheckCastNode extends FixedWithNextNode implements Canonicalizable, Simplifiable, Lowerable, Virtualizable, ValueProxy { 046 047 public static final NodeClass<CheckCastNode> TYPE = NodeClass.create(CheckCastNode.class); 048 @Input protected ValueNode object; 049 protected final ResolvedJavaType type; 050 protected final JavaTypeProfile profile; 051 052 /** 053 * Determines the exception thrown by this node if the check fails: {@link ClassCastException} 054 * if false; {@link ArrayStoreException} if true. 055 */ 056 protected final boolean forStoreCheck; 057 058 public CheckCastNode(ResolvedJavaType type, ValueNode object, JavaTypeProfile profile, boolean forStoreCheck) { 059 this(TYPE, type, object, profile, forStoreCheck); 060 } 061 062 protected CheckCastNode(NodeClass<? extends CheckCastNode> c, ResolvedJavaType type, ValueNode object, JavaTypeProfile profile, boolean forStoreCheck) { 063 super(c, StampFactory.declaredTrusted(type).improveWith(object.stamp())); 064 assert object.stamp() instanceof ObjectStamp : object; 065 assert type != null; 066 this.type = type; 067 this.object = object; 068 this.profile = profile; 069 this.forStoreCheck = forStoreCheck; 070 } 071 072 public static ValueNode create(ResolvedJavaType inputType, ValueNode object, JavaTypeProfile profile, boolean forStoreCheck, Assumptions assumptions) { 073 ResolvedJavaType type = inputType; 074 ValueNode synonym = findSynonym(type, object); 075 if (synonym != null) { 076 return synonym; 077 } 078 assert object.stamp() instanceof ObjectStamp : object; 079 if (assumptions != null) { 080 AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype(); 081 if (leafConcreteSubtype != null && !leafConcreteSubtype.getResult().equals(type)) { 082 assumptions.record(leafConcreteSubtype); 083 type = leafConcreteSubtype.getResult(); 084 } 085 } 086 return new CheckCastNode(type, object, profile, forStoreCheck); 087 } 088 089 public boolean isForStoreCheck() { 090 return forStoreCheck; 091 } 092 093 /** 094 * Lowers a {@link CheckCastNode}. That is: 095 * 096 * <pre> 097 * 1: A a = ... 098 * 2: B b = (B) a; 099 * </pre> 100 * 101 * is lowered to: 102 * 103 * <pre> 104 * 1: A a = ... 105 * 2: B b = guardingPi(a == null || a instanceof B, a, stamp(B)) 106 * </pre> 107 * 108 * or if a is known to be non-null: 109 * 110 * <pre> 111 * 1: A a = ... 112 * 2: B b = guardingPi(a instanceof B, a, stamp(B, non-null)) 113 * </pre> 114 * 115 * Note: we use {@link Graph#addWithoutUnique} as opposed to {@link Graph#unique} for the new 116 * {@link InstanceOfNode} to maintain the invariant checked by 117 * {@code LoweringPhase.checkUsagesAreScheduled()}. 118 */ 119 @Override 120 public void lower(LoweringTool tool) { 121 Stamp newStamp = StampFactory.declaredTrusted(type).improveWith(object().stamp()); 122 LogicNode condition; 123 LogicNode innerNode = null; 124 ValueNode theValue = object; 125 if (newStamp.isEmpty()) { 126 // This is a check cast that will always fail 127 condition = LogicConstantNode.contradiction(graph()); 128 newStamp = StampFactory.declaredTrusted(type); 129 } else if (StampTool.isPointerNonNull(object)) { 130 condition = graph().addWithoutUnique(InstanceOfNode.create(type, object, profile)); 131 innerNode = condition; 132 } else { 133 if (profile != null && profile.getNullSeen() == TriState.FALSE) { 134 FixedGuardNode nullCheck = graph().add(new FixedGuardNode(graph().unique(new IsNullNode(object)), UnreachedCode, InvalidateReprofile, JavaConstant.NULL_POINTER, true)); 135 PiNode nullGuarded = graph().unique(new PiNode(object, object().stamp().join(StampFactory.objectNonNull()), nullCheck)); 136 LogicNode typeTest = graph().addWithoutUnique(InstanceOfNode.create(type, nullGuarded, profile)); 137 innerNode = typeTest; 138 graph().addBeforeFixed(this, nullCheck); 139 condition = typeTest; 140 /* 141 * The PiNode is injecting an extra guard into the type so make sure it's used in 142 * the GuardingPi, otherwise we can lose the null guard if the InstanceOf is 143 * optimized away. 144 */ 145 theValue = nullGuarded; 146 newStamp = newStamp.join(StampFactory.objectNonNull()); 147 nullCheck.lower(tool); 148 } else { 149 // TODO (ds) replace with probability of null-seen when available 150 double shortCircuitProbability = NOT_FREQUENT_PROBABILITY; 151 LogicNode typeTest = graph().addOrUnique(InstanceOfNode.create(type, object, profile)); 152 innerNode = typeTest; 153 condition = LogicNode.or(graph().unique(new IsNullNode(object)), typeTest, shortCircuitProbability); 154 } 155 } 156 GuardingNode guard = tool.createGuard(next(), condition, forStoreCheck ? ArrayStoreException : ClassCastException, InvalidateReprofile); 157 ValueAnchorNode valueAnchor = graph().add(new ValueAnchorNode((ValueNode) guard)); 158 PiNode piNode = graph().unique(new PiNode(theValue, newStamp, (ValueNode) guard)); 159 this.replaceAtUsages(piNode); 160 graph().replaceFixedWithFixed(this, valueAnchor); 161 162 if (innerNode instanceof Lowerable) { 163 tool.getLowerer().lower(innerNode, tool); 164 } 165 } 166 167 @Override 168 public boolean inferStamp() { 169 if (object().stamp() instanceof ObjectStamp) { 170 ObjectStamp castStamp = (ObjectStamp) StampFactory.declaredTrusted(type); 171 return updateStamp(castStamp.improveWith(object().stamp())); 172 } 173 return false; 174 } 175 176 @Override 177 public Node canonical(CanonicalizerTool tool) { 178 ValueNode synonym = findSynonym(type, object()); 179 if (synonym != null) { 180 return synonym; 181 } 182 183 Assumptions assumptions = graph().getAssumptions(); 184 if (assumptions != null) { 185 AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype(); 186 if (leafConcreteSubtype != null && !leafConcreteSubtype.getResult().equals(type)) { 187 // Propagate more precise type information to usages of the checkcast. 188 assumptions.record(leafConcreteSubtype); 189 return new CheckCastNode(leafConcreteSubtype.getResult(), object, profile, forStoreCheck); 190 } 191 } 192 193 return this; 194 } 195 196 protected static ValueNode findSynonym(ResolvedJavaType type, ValueNode object) { 197 ResolvedJavaType objectType = StampTool.typeOrNull(object); 198 if (objectType != null && type.isAssignableFrom(objectType)) { 199 // we don't have to check for null types here because they will also pass the 200 // checkcast. 201 return object; 202 } 203 204 if (StampTool.isPointerAlwaysNull(object)) { 205 return object; 206 } 207 return null; 208 } 209 210 @Override 211 public void simplify(SimplifierTool tool) { 212 // if the previous node is also a checkcast, with a less precise and compatible type, 213 // replace both with one checkcast checking the more specific type. 214 if (predecessor() instanceof CheckCastNode) { 215 CheckCastNode ccn = (CheckCastNode) predecessor(); 216 if (ccn != null && ccn.type != null && ccn == object && ccn.forStoreCheck == forStoreCheck && ccn.type.isAssignableFrom(type)) { 217 StructuredGraph graph = ccn.graph(); 218 CheckCastNode newccn = graph.add(new CheckCastNode(type, ccn.object, ccn.profile, ccn.forStoreCheck)); 219 graph.replaceFixedWithFixed(ccn, newccn); 220 replaceAtUsages(newccn); 221 graph.removeFixed(this); 222 } 223 } 224 } 225 226 public ValueNode object() { 227 return object; 228 } 229 230 /** 231 * Gets the type being cast to. 232 */ 233 public ResolvedJavaType type() { 234 return type; 235 } 236 237 public JavaTypeProfile profile() { 238 return profile; 239 } 240 241 @Override 242 public void virtualize(VirtualizerTool tool) { 243 ValueNode alias = tool.getAlias(object); 244 if (tryFold(alias.stamp()) == TriState.TRUE) { 245 tool.replaceWith(alias); 246 } 247 } 248 249 @Override 250 public ValueNode getOriginalNode() { 251 return object; 252 } 253 254 public TriState tryFold(Stamp testStamp) { 255 if (testStamp instanceof ObjectStamp) { 256 ObjectStamp objectStamp = (ObjectStamp) testStamp; 257 ResolvedJavaType objectType = objectStamp.type(); 258 if (objectType != null && type.isAssignableFrom(objectType)) { 259 return TriState.TRUE; 260 } else if (objectStamp.alwaysNull()) { 261 return TriState.TRUE; 262 } 263 } 264 return TriState.UNKNOWN; 265 } 266}