001/* 002 * Copyright (c) 2011, 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 jdk.internal.jvmci.code.*; 026import jdk.internal.jvmci.meta.*; 027import jdk.internal.jvmci.meta.Assumptions.*; 028 029import com.oracle.graal.compiler.common.type.*; 030import com.oracle.graal.graph.*; 031import com.oracle.graal.graph.spi.*; 032import com.oracle.graal.nodeinfo.*; 033import com.oracle.graal.nodes.*; 034import com.oracle.graal.nodes.spi.*; 035import com.oracle.graal.nodes.type.*; 036 037@NodeInfo 038public class MethodCallTargetNode extends CallTargetNode implements IterableNodeType, Simplifiable { 039 public static final NodeClass<MethodCallTargetNode> TYPE = NodeClass.create(MethodCallTargetNode.class); 040 protected final JavaType returnType; 041 protected JavaTypeProfile profile; 042 043 public MethodCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, JavaTypeProfile profile) { 044 this(TYPE, invokeKind, targetMethod, arguments, returnType, profile); 045 } 046 047 protected MethodCallTargetNode(NodeClass<? extends MethodCallTargetNode> c, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, 048 JavaTypeProfile profile) { 049 super(c, arguments, targetMethod, invokeKind); 050 this.returnType = returnType; 051 this.profile = profile; 052 } 053 054 /** 055 * Gets the instruction that produces the receiver object for this invocation, if any. 056 * 057 * @return the instruction that produces the receiver object for this invocation if any, 058 * {@code null} if this invocation does not take a receiver object 059 */ 060 public ValueNode receiver() { 061 return isStatic() ? null : arguments().get(0); 062 } 063 064 /** 065 * Checks whether this is an invocation of a static method. 066 * 067 * @return {@code true} if the invocation is a static invocation 068 */ 069 public boolean isStatic() { 070 return invokeKind() == InvokeKind.Static; 071 } 072 073 public Kind returnKind() { 074 return targetMethod().getSignature().getReturnKind(); 075 } 076 077 public Invoke invoke() { 078 return (Invoke) this.usages().first(); 079 } 080 081 @Override 082 public boolean verify() { 083 assert getUsageCount() <= 1 : "call target may only be used by a single invoke"; 084 for (Node n : usages()) { 085 assertTrue(n instanceof Invoke, "call target can only be used from an invoke (%s)", n); 086 } 087 if (invokeKind().isDirect()) { 088 assertTrue(targetMethod().isConcrete(), "special calls or static calls are only allowed for concrete methods (%s)", targetMethod()); 089 } 090 if (invokeKind() == InvokeKind.Static) { 091 assertTrue(targetMethod().isStatic(), "static calls are only allowed for static methods (%s)", targetMethod()); 092 } else { 093 assertFalse(targetMethod().isStatic(), "static calls are only allowed for non-static methods (%s)", targetMethod()); 094 } 095 return super.verify(); 096 } 097 098 @Override 099 public String toString(Verbosity verbosity) { 100 if (verbosity == Verbosity.Long) { 101 return super.toString(Verbosity.Short) + "(" + targetMethod() + ")"; 102 } else { 103 return super.toString(verbosity); 104 } 105 } 106 107 public static ResolvedJavaMethod findSpecialCallTarget(InvokeKind invokeKind, ValueNode receiver, ResolvedJavaMethod targetMethod, ResolvedJavaType contextType) { 108 if (invokeKind.isDirect()) { 109 return null; 110 } 111 112 // check for trivial cases (e.g. final methods, nonvirtual methods) 113 if (targetMethod.canBeStaticallyBound()) { 114 return targetMethod; 115 } 116 117 ResolvedJavaType type = StampTool.typeOrNull(receiver); 118 if (type == null && invokeKind == InvokeKind.Virtual) { 119 // For virtual calls, we are guaranteed to receive a correct receiver type. 120 type = targetMethod.getDeclaringClass(); 121 } 122 123 if (type != null) { 124 /* 125 * either the holder class is exact, or the receiver object has an exact type, or it's 126 * an array type 127 */ 128 ResolvedJavaMethod resolvedMethod = type.resolveConcreteMethod(targetMethod, contextType); 129 if (resolvedMethod != null && (resolvedMethod.canBeStaticallyBound() || StampTool.isExactType(receiver) || type.isArray())) { 130 return resolvedMethod; 131 } 132 Assumptions assumptions = receiver.graph().getAssumptions(); 133 if (assumptions != null) { 134 AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype(); 135 if (leafConcreteSubtype != null) { 136 ResolvedJavaMethod methodFromUniqueType = leafConcreteSubtype.getResult().resolveConcreteMethod(targetMethod, contextType); 137 if (methodFromUniqueType != null) { 138 assumptions.record(leafConcreteSubtype); 139 return methodFromUniqueType; 140 } 141 } 142 143 AssumptionResult<ResolvedJavaMethod> uniqueConcreteMethod = type.findUniqueConcreteMethod(targetMethod); 144 if (uniqueConcreteMethod != null) { 145 assumptions.record(uniqueConcreteMethod); 146 return uniqueConcreteMethod.getResult(); 147 } 148 } 149 } 150 151 return null; 152 } 153 154 @Override 155 public void simplify(SimplifierTool tool) { 156 // attempt to devirtualize the call 157 if (invoke().getContextMethod() == null) { 158 // avoid invokes that have placeholder bcis: they do not have a valid contextType 159 assert (invoke().stateAfter() != null && BytecodeFrame.isPlaceholderBci(invoke().stateAfter().bci)) || BytecodeFrame.isPlaceholderBci(invoke().stateDuring().bci); 160 return; 161 } 162 ResolvedJavaType contextType = (invoke().stateAfter() == null && invoke().stateDuring() == null) ? null : invoke().getContextType(); 163 ResolvedJavaMethod specialCallTarget = findSpecialCallTarget(invokeKind, receiver(), targetMethod, contextType); 164 if (specialCallTarget != null) { 165 this.setTargetMethod(specialCallTarget); 166 setInvokeKind(InvokeKind.Special); 167 return; 168 } 169 170 if (invokeKind().isIndirect() && invokeKind().isInterface()) { 171 172 // check if the type of the receiver can narrow the result 173 ValueNode receiver = receiver(); 174 175 // try to turn a interface call into a virtual call 176 ResolvedJavaType declaredReceiverType = targetMethod().getDeclaringClass(); 177 /* 178 * We need to check the invoke kind to avoid recursive simplification for virtual 179 * interface methods calls. 180 */ 181 if (declaredReceiverType.isInterface()) { 182 tryCheckCastSingleImplementor(graph().getAssumptions(), receiver, declaredReceiverType); 183 } 184 185 if (receiver instanceof UncheckedInterfaceProvider) { 186 UncheckedInterfaceProvider uncheckedInterfaceProvider = (UncheckedInterfaceProvider) receiver; 187 Stamp uncheckedStamp = uncheckedInterfaceProvider.uncheckedStamp(); 188 if (uncheckedStamp != null) { 189 ResolvedJavaType uncheckedReceiverType = StampTool.typeOrNull(uncheckedStamp); 190 if (uncheckedReceiverType.isInterface()) { 191 tryCheckCastSingleImplementor(graph().getAssumptions(), receiver, uncheckedReceiverType); 192 } 193 } 194 } 195 } 196 } 197 198 private void tryCheckCastSingleImplementor(Assumptions assumptions, ValueNode receiver, ResolvedJavaType declaredReceiverType) { 199 if (assumptions == null) { 200 /* 201 * Even though we are not registering an assumption (see comment below), the 202 * optimization is only valid when speculative optimizations are enabled. 203 */ 204 return; 205 } 206 207 ResolvedJavaType singleImplementor = declaredReceiverType.getSingleImplementor(); 208 if (singleImplementor != null && !singleImplementor.equals(declaredReceiverType)) { 209 ResolvedJavaMethod singleImplementorMethod = singleImplementor.resolveMethod(targetMethod(), invoke().getContextType()); 210 if (singleImplementorMethod != null) { 211 /** 212 * We have an invoke on an interface with a single implementor. We can replace this 213 * with an invoke virtual. 214 * 215 * To do so we need to ensure two properties: 1) the receiver must implement the 216 * interface (declaredReceiverType). The verifier does not prove this so we need a 217 * dynamic check. 2) we need to ensure that there is still only one implementor of 218 * this interface, i.e. that we are calling the right method. We could do this with 219 * an assumption but as we need an instanceof check anyway we can verify both 220 * properties by checking of the receiver is an instance of the single implementor. 221 */ 222 LogicNode condition = graph().unique(InstanceOfNode.create(singleImplementor, receiver, getProfile())); 223 FixedGuardNode guard = graph().add(new FixedGuardNode(condition, DeoptimizationReason.OptimizedTypeCheckViolated, DeoptimizationAction.InvalidateRecompile, false)); 224 graph().addBeforeFixed(invoke().asNode(), guard); 225 PiNode piNode = graph().unique(new PiNode(receiver, StampFactory.declaredNonNull(singleImplementor), guard)); 226 arguments().set(0, piNode); 227 setInvokeKind(InvokeKind.Virtual); 228 setTargetMethod(singleImplementorMethod); 229 } 230 } 231 } 232 233 public JavaTypeProfile getProfile() { 234 return profile; 235 } 236 237 @Override 238 public Stamp returnStamp() { 239 Kind returnKind = targetMethod().getSignature().getReturnKind(); 240 if (returnKind == Kind.Object && returnType instanceof ResolvedJavaType) { 241 return StampFactory.declared((ResolvedJavaType) returnType); 242 } else { 243 return StampFactory.forKind(returnKind); 244 } 245 } 246 247 public JavaType returnType() { 248 return returnType; 249 } 250 251 @Override 252 public String targetName() { 253 if (targetMethod() == null) { 254 return "??Invalid!"; 255 } 256 return targetMethod().format("%h.%n"); 257 } 258 259 public static MethodCallTargetNode find(StructuredGraph graph, ResolvedJavaMethod method) { 260 for (MethodCallTargetNode target : graph.getNodes(MethodCallTargetNode.TYPE)) { 261 if (target.targetMethod().equals(method)) { 262 return target; 263 } 264 } 265 return null; 266 } 267}