1 /*
   2  * Copyright (c) 2011, 2015, 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 org.graalvm.compiler.core.common.type.Stamp;
  26 import org.graalvm.compiler.core.common.type.StampFactory;
  27 import org.graalvm.compiler.core.common.type.StampPair;
  28 import org.graalvm.compiler.core.common.type.TypeReference;
  29 import org.graalvm.compiler.graph.IterableNodeType;
  30 import org.graalvm.compiler.graph.Node;
  31 import org.graalvm.compiler.graph.NodeClass;
  32 import org.graalvm.compiler.graph.spi.Simplifiable;
  33 import org.graalvm.compiler.graph.spi.SimplifierTool;
  34 import org.graalvm.compiler.nodeinfo.NodeInfo;
  35 import org.graalvm.compiler.nodeinfo.Verbosity;
  36 import org.graalvm.compiler.nodes.CallTargetNode;
  37 import org.graalvm.compiler.nodes.FixedGuardNode;
  38 import org.graalvm.compiler.nodes.Invoke;
  39 import org.graalvm.compiler.nodes.LogicNode;
  40 import org.graalvm.compiler.nodes.PiNode;
  41 import org.graalvm.compiler.nodes.StructuredGraph;
  42 import org.graalvm.compiler.nodes.ValueNode;
  43 import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
  44 import org.graalvm.compiler.nodes.spi.UncheckedInterfaceProvider;
  45 import org.graalvm.compiler.nodes.type.StampTool;
  46 
  47 import jdk.vm.ci.code.BytecodeFrame;
  48 import jdk.vm.ci.meta.Assumptions;
  49 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
  50 import jdk.vm.ci.meta.DeoptimizationAction;
  51 import jdk.vm.ci.meta.DeoptimizationReason;
  52 import jdk.vm.ci.meta.JavaKind;
  53 import jdk.vm.ci.meta.JavaTypeProfile;
  54 import jdk.vm.ci.meta.ResolvedJavaMethod;
  55 import jdk.vm.ci.meta.ResolvedJavaType;
  56 
  57 @NodeInfo
  58 public class MethodCallTargetNode extends CallTargetNode implements IterableNodeType, Simplifiable {
  59     public static final NodeClass<MethodCallTargetNode> TYPE = NodeClass.create(MethodCallTargetNode.class);
  60     protected JavaTypeProfile profile;
  61 
  62     public MethodCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, StampPair returnStamp, JavaTypeProfile profile) {
  63         this(TYPE, invokeKind, targetMethod, arguments, returnStamp, profile);
  64     }
  65 
  66     protected MethodCallTargetNode(NodeClass<? extends MethodCallTargetNode> c, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, StampPair returnStamp,
  67                     JavaTypeProfile profile) {
  68         super(c, arguments, targetMethod, invokeKind, returnStamp);
  69         this.profile = profile;
  70     }
  71 
  72     /**
  73      * Gets the instruction that produces the receiver object for this invocation, if any.
  74      *
  75      * @return the instruction that produces the receiver object for this invocation if any,
  76      *         {@code null} if this invocation does not take a receiver object
  77      */
  78     public ValueNode receiver() {
  79         return isStatic() ? null : arguments().get(0);
  80     }
  81 
  82     /**
  83      * Checks whether this is an invocation of a static method.
  84      *
  85      * @return {@code true} if the invocation is a static invocation
  86      */
  87     public boolean isStatic() {
  88         return invokeKind() == InvokeKind.Static;
  89     }
  90 
  91     public JavaKind returnKind() {
  92         return targetMethod().getSignature().getReturnKind();
  93     }
  94 
  95     public Invoke invoke() {
  96         return (Invoke) this.usages().first();
  97     }
  98 
  99     @Override
 100     public boolean verify() {
 101         assert getUsageCount() <= 1 : "call target may only be used by a single invoke";
 102         for (Node n : usages()) {
 103             assertTrue(n instanceof Invoke, "call target can only be used from an invoke (%s)", n);
 104         }
 105         if (invokeKind().isDirect()) {
 106             assertTrue(targetMethod().isConcrete(), "special calls or static calls are only allowed for concrete methods (%s)", targetMethod());
 107         }
 108         if (invokeKind() == InvokeKind.Static) {
 109             assertTrue(targetMethod().isStatic(), "static calls are only allowed for static methods (%s)", targetMethod());
 110         } else {
 111             assertFalse(targetMethod().isStatic(), "static calls are only allowed for non-static methods (%s)", targetMethod());
 112         }
 113         return super.verify();
 114     }
 115 
 116     @Override
 117     public String toString(Verbosity verbosity) {
 118         if (verbosity == Verbosity.Long) {
 119             return super.toString(Verbosity.Short) + "(" + targetMethod() + ")";
 120         } else {
 121             return super.toString(verbosity);
 122         }
 123     }
 124 
 125     public static ResolvedJavaMethod findSpecialCallTarget(InvokeKind invokeKind, ValueNode receiver, ResolvedJavaMethod targetMethod, ResolvedJavaType contextType) {
 126         if (invokeKind.isDirect()) {
 127             return null;
 128         }
 129 
 130         // check for trivial cases (e.g. final methods, nonvirtual methods)
 131         if (targetMethod.canBeStaticallyBound()) {
 132             return targetMethod;
 133         }
 134 
 135         Assumptions assumptions = receiver.graph().getAssumptions();
 136         TypeReference type = StampTool.typeReferenceOrNull(receiver);
 137         if (type == null && invokeKind == InvokeKind.Virtual) {
 138             // For virtual calls, we are guaranteed to receive a correct receiver type.
 139             type = TypeReference.createTrusted(assumptions, targetMethod.getDeclaringClass());
 140         }
 141 
 142         if (type != null) {
 143             /*
 144              * either the holder class is exact, or the receiver object has an exact type, or it's
 145              * an array type
 146              */
 147             ResolvedJavaMethod resolvedMethod = type.getType().resolveConcreteMethod(targetMethod, contextType);
 148             if (resolvedMethod != null && (resolvedMethod.canBeStaticallyBound() || type.isExact() || type.getType().isArray())) {
 149                 return resolvedMethod;
 150             }
 151 
 152             AssumptionResult<ResolvedJavaMethod> uniqueConcreteMethod = type.getType().findUniqueConcreteMethod(targetMethod);
 153             if (uniqueConcreteMethod != null && uniqueConcreteMethod.canRecordTo(assumptions)) {
 154                 uniqueConcreteMethod.recordTo(assumptions);
 155                 return uniqueConcreteMethod.getResult();
 156             }
 157         }
 158 
 159         return null;
 160     }
 161 
 162     @Override
 163     public void simplify(SimplifierTool tool) {
 164         // attempt to devirtualize the call
 165         if (invoke().getContextMethod() == null) {
 166             // avoid invokes that have placeholder bcis: they do not have a valid contextType
 167             assert (invoke().stateAfter() != null && BytecodeFrame.isPlaceholderBci(invoke().stateAfter().bci)) || BytecodeFrame.isPlaceholderBci(invoke().stateDuring().bci);
 168             return;
 169         }
 170         ResolvedJavaType contextType = (invoke().stateAfter() == null && invoke().stateDuring() == null) ? null : invoke().getContextType();
 171         ResolvedJavaMethod specialCallTarget = findSpecialCallTarget(invokeKind, receiver(), targetMethod, contextType);
 172         if (specialCallTarget != null) {
 173             this.setTargetMethod(specialCallTarget);
 174             setInvokeKind(InvokeKind.Special);
 175             return;
 176         }
 177 
 178         Assumptions assumptions = graph().getAssumptions();
 179         /*
 180          * Even though we are not registering an assumption (see comment below), the optimization is
 181          * only valid when speculative optimizations are enabled.
 182          */
 183         if (invokeKind().isIndirect() && invokeKind().isInterface() && assumptions != null) {
 184 
 185             // check if the type of the receiver can narrow the result
 186             ValueNode receiver = receiver();
 187 
 188             // try to turn a interface call into a virtual call
 189             ResolvedJavaType declaredReceiverType = targetMethod().getDeclaringClass();
 190 
 191             /*
 192              * We need to check the invoke kind to avoid recursive simplification for virtual
 193              * interface methods calls.
 194              */
 195             if (declaredReceiverType.isInterface()) {
 196                 ResolvedJavaType singleImplementor = declaredReceiverType.getSingleImplementor();
 197                 if (singleImplementor != null && !singleImplementor.equals(declaredReceiverType)) {
 198                     TypeReference speculatedType = TypeReference.createTrusted(assumptions, singleImplementor);
 199                     if (tryCheckCastSingleImplementor(receiver, speculatedType)) {
 200                         return;
 201                     }
 202                 }
 203             }
 204 
 205             if (receiver instanceof UncheckedInterfaceProvider) {
 206                 UncheckedInterfaceProvider uncheckedInterfaceProvider = (UncheckedInterfaceProvider) receiver;
 207                 Stamp uncheckedStamp = uncheckedInterfaceProvider.uncheckedStamp();
 208                 if (uncheckedStamp != null) {
 209                     TypeReference speculatedType = StampTool.typeReferenceOrNull(uncheckedStamp);
 210                     if (speculatedType != null) {
 211                         tryCheckCastSingleImplementor(receiver, speculatedType);
 212                     }
 213                 }
 214             }
 215         }
 216     }
 217 
 218     private boolean tryCheckCastSingleImplementor(ValueNode receiver, TypeReference speculatedType) {
 219         ResolvedJavaType singleImplementor = speculatedType.getType();
 220         if (singleImplementor != null) {
 221             ResolvedJavaMethod singleImplementorMethod = singleImplementor.resolveConcreteMethod(targetMethod(), invoke().getContextType());
 222             if (singleImplementorMethod != null) {
 223                 /**
 224                  * We have an invoke on an interface with a single implementor. We can replace this
 225                  * with an invoke virtual.
 226                  *
 227                  * To do so we need to ensure two properties: 1) the receiver must implement the
 228                  * interface (declaredReceiverType). The verifier does not prove this so we need a
 229                  * dynamic check. 2) we need to ensure that there is still only one implementor of
 230                  * this interface, i.e. that we are calling the right method. We could do this with
 231                  * an assumption but as we need an instanceof check anyway we can verify both
 232                  * properties by checking of the receiver is an instance of the single implementor.
 233                  */
 234                 ValueAnchorNode anchor = new ValueAnchorNode(null);
 235                 if (anchor != null) {
 236                     graph().add(anchor);
 237                     graph().addBeforeFixed(invoke().asNode(), anchor);
 238                 }
 239                 LogicNode condition = graph().addOrUniqueWithInputs(InstanceOfNode.create(speculatedType, receiver, getProfile(), anchor));
 240                 FixedGuardNode guard = graph().add(new FixedGuardNode(condition, DeoptimizationReason.OptimizedTypeCheckViolated, DeoptimizationAction.InvalidateRecompile, false));
 241                 graph().addBeforeFixed(invoke().asNode(), guard);
 242                 PiNode piNode = graph().unique(new PiNode(receiver, StampFactory.objectNonNull(speculatedType), guard));
 243                 arguments().set(0, piNode);
 244                 if (speculatedType.isExact()) {
 245                     setInvokeKind(InvokeKind.Special);
 246                 } else {
 247                     setInvokeKind(InvokeKind.Virtual);
 248                 }
 249                 setTargetMethod(singleImplementorMethod);
 250                 return true;
 251             }
 252         }
 253         return false;
 254     }
 255 
 256     public JavaTypeProfile getProfile() {
 257         return profile;
 258     }
 259 
 260     @Override
 261     public String targetName() {
 262         if (targetMethod() == null) {
 263             return "??Invalid!";
 264         }
 265         return targetMethod().format("%h.%n");
 266     }
 267 
 268     public static MethodCallTargetNode find(StructuredGraph graph, ResolvedJavaMethod method) {
 269         for (MethodCallTargetNode target : graph.getNodes(MethodCallTargetNode.TYPE)) {
 270             if (target.targetMethod().equals(method)) {
 271                 return target;
 272             }
 273         }
 274         return null;
 275     }
 276 }