1 /*
   2  * Copyright (c) 2013, 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.replacements.nodes;
  24 
  25 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_UNKNOWN;
  26 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_UNKNOWN;
  27 
  28 import java.lang.invoke.MethodHandle;
  29 import java.util.Arrays;
  30 
  31 import org.graalvm.compiler.core.common.type.StampFactory;
  32 import org.graalvm.compiler.core.common.type.StampPair;
  33 import org.graalvm.compiler.core.common.type.TypeReference;
  34 import org.graalvm.compiler.debug.GraalError;
  35 import org.graalvm.compiler.graph.NodeClass;
  36 import org.graalvm.compiler.graph.spi.Simplifiable;
  37 import org.graalvm.compiler.graph.spi.SimplifierTool;
  38 import org.graalvm.compiler.nodeinfo.NodeInfo;
  39 import org.graalvm.compiler.nodes.CallTargetNode;
  40 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
  41 import org.graalvm.compiler.nodes.FixedNode;
  42 import org.graalvm.compiler.nodes.InvokeNode;
  43 import org.graalvm.compiler.nodes.PiNode;
  44 import org.graalvm.compiler.nodes.ValueNode;
  45 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
  46 import org.graalvm.compiler.nodes.type.StampTool;
  47 import org.graalvm.compiler.nodes.util.GraphUtil;
  48 
  49 import jdk.vm.ci.meta.Assumptions;
  50 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
  51 import jdk.vm.ci.meta.JavaKind;
  52 import jdk.vm.ci.meta.JavaType;
  53 import jdk.vm.ci.meta.MethodHandleAccessProvider;
  54 import jdk.vm.ci.meta.MethodHandleAccessProvider.IntrinsicMethod;
  55 import jdk.vm.ci.meta.ResolvedJavaMethod;
  56 import jdk.vm.ci.meta.ResolvedJavaType;
  57 import jdk.vm.ci.meta.Signature;
  58 
  59 /**
  60  * Node for invocation methods defined on the class {@link MethodHandle}.
  61  */
  62 @NodeInfo(cycles = CYCLES_UNKNOWN, size = SIZE_UNKNOWN)
  63 public final class MethodHandleNode extends MacroStateSplitNode implements Simplifiable {
  64     public static final NodeClass<MethodHandleNode> TYPE = NodeClass.create(MethodHandleNode.class);
  65 
  66     protected final IntrinsicMethod intrinsicMethod;
  67 
  68     public MethodHandleNode(IntrinsicMethod intrinsicMethod, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, StampPair returnStamp, ValueNode... arguments) {
  69         super(TYPE, invokeKind, targetMethod, bci, returnStamp, arguments);
  70         this.intrinsicMethod = intrinsicMethod;
  71     }
  72 
  73     /**
  74      * Attempts to transform application of an intrinsifiable {@link MethodHandle} method into an
  75      * invocation on another method with possibly transformed arguments.
  76      *
  77      * @param assumptions object for recording any speculations made during the transformation
  78      * @param methodHandleAccess objects for accessing the implementation internals of a
  79      *            {@link MethodHandle}
  80      * @param intrinsicMethod denotes the intrinsifiable {@link MethodHandle} method being processed
  81      * @param bci the BCI of the original {@link MethodHandle} call
  82      * @param returnStamp return stamp of the original {@link MethodHandle} call
  83      * @param arguments arguments to the original {@link MethodHandle} call
  84      * @return a more direct invocation derived from the {@link MethodHandle} call or null
  85      */
  86     public static InvokeNode tryResolveTargetInvoke(Assumptions assumptions, MethodHandleAccessProvider methodHandleAccess, IntrinsicMethod intrinsicMethod, ResolvedJavaMethod original, int bci,
  87                     StampPair returnStamp, ValueNode... arguments) {
  88         switch (intrinsicMethod) {
  89             case INVOKE_BASIC:
  90                 return getInvokeBasicTarget(assumptions, intrinsicMethod, methodHandleAccess, original, bci, returnStamp, arguments);
  91             case LINK_TO_STATIC:
  92             case LINK_TO_SPECIAL:
  93             case LINK_TO_VIRTUAL:
  94             case LINK_TO_INTERFACE:
  95                 return getLinkToTarget(assumptions, intrinsicMethod, methodHandleAccess, original, bci, returnStamp, arguments);
  96             default:
  97                 throw GraalError.shouldNotReachHere();
  98         }
  99     }
 100 
 101     @Override
 102     public void simplify(SimplifierTool tool) {
 103         MethodHandleAccessProvider methodHandleAccess = tool.getConstantReflection().getMethodHandleAccess();
 104         ValueNode[] argumentsArray = arguments.toArray(new ValueNode[arguments.size()]);
 105         InvokeNode invoke = tryResolveTargetInvoke(graph().getAssumptions(), methodHandleAccess, intrinsicMethod, targetMethod, bci, returnStamp, argumentsArray);
 106         if (invoke != null) {
 107             assert invoke.graph() == null;
 108             invoke = graph().addOrUniqueWithInputs(invoke);
 109             invoke.setStateAfter(stateAfter());
 110             FixedNode currentNext = next();
 111             replaceAtUsages(invoke);
 112             GraphUtil.removeFixedWithUnusedInputs(this);
 113             graph().addBeforeFixed(currentNext, invoke);
 114         }
 115     }
 116 
 117     /**
 118      * Get the receiver of a MethodHandle.invokeBasic call.
 119      *
 120      * @return the receiver argument node
 121      */
 122     private static ValueNode getReceiver(ValueNode[] arguments) {
 123         return arguments[0];
 124     }
 125 
 126     /**
 127      * Get the MemberName argument of a MethodHandle.linkTo* call.
 128      *
 129      * @return the MemberName argument node (which is the last argument)
 130      */
 131     private static ValueNode getMemberName(ValueNode[] arguments) {
 132         return arguments[arguments.length - 1];
 133     }
 134 
 135     /**
 136      * Used for the MethodHandle.invokeBasic method (the {@link IntrinsicMethod#INVOKE_BASIC }
 137      * method) to get the target {@link InvokeNode} if the method handle receiver is constant.
 138      *
 139      * @return invoke node for the {@link java.lang.invoke.MethodHandle} target
 140      */
 141     private static InvokeNode getInvokeBasicTarget(Assumptions assumptions, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess, ResolvedJavaMethod original, int bci,
 142                     StampPair returnStamp, ValueNode[] arguments) {
 143         ValueNode methodHandleNode = getReceiver(arguments);
 144         if (methodHandleNode.isConstant()) {
 145             return getTargetInvokeNode(assumptions, intrinsicMethod, bci, returnStamp, arguments, methodHandleAccess.resolveInvokeBasicTarget(methodHandleNode.asJavaConstant(), true), original);
 146         }
 147         return null;
 148     }
 149 
 150     /**
 151      * Used for the MethodHandle.linkTo* methods (the {@link IntrinsicMethod#LINK_TO_STATIC},
 152      * {@link IntrinsicMethod#LINK_TO_SPECIAL}, {@link IntrinsicMethod#LINK_TO_VIRTUAL}, and
 153      * {@link IntrinsicMethod#LINK_TO_INTERFACE} methods) to get the target {@link InvokeNode} if
 154      * the member name argument is constant.
 155      *
 156      * @return invoke node for the member name target
 157      */
 158     private static InvokeNode getLinkToTarget(Assumptions assumptions, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess, ResolvedJavaMethod original, int bci,
 159                     StampPair returnStamp, ValueNode[] arguments) {
 160         ValueNode memberNameNode = getMemberName(arguments);
 161         if (memberNameNode.isConstant()) {
 162             return getTargetInvokeNode(assumptions, intrinsicMethod, bci, returnStamp, arguments, methodHandleAccess.resolveLinkToTarget(memberNameNode.asJavaConstant()), original);
 163         }
 164         return null;
 165     }
 166 
 167     /**
 168      * Helper function to get the {@link InvokeNode} for the targetMethod of a
 169      * java.lang.invoke.MemberName.
 170      *
 171      * @param target the target, already loaded from the member name node
 172      * @return invoke node for the member name target
 173      */
 174     private static InvokeNode getTargetInvokeNode(Assumptions assumptions, IntrinsicMethod intrinsicMethod, int bci, StampPair returnStamp, ValueNode[] originalArguments, ResolvedJavaMethod target,
 175                     ResolvedJavaMethod original) {
 176         if (target == null) {
 177             return null;
 178         }
 179 
 180         // In lambda forms we erase signature types to avoid resolving issues
 181         // involving class loaders. When we optimize a method handle invoke
 182         // to a direct call we must cast the receiver and arguments to its
 183         // actual types.
 184         Signature signature = target.getSignature();
 185         final boolean isStatic = target.isStatic();
 186         final int receiverSkip = isStatic ? 0 : 1;
 187 
 188         // Don't mutate the passed in arguments
 189         ValueNode[] arguments = originalArguments.clone();
 190 
 191         // Cast receiver to its type.
 192         if (!isStatic) {
 193             JavaType receiverType = target.getDeclaringClass();
 194             maybeCastArgument(assumptions, arguments, 0, receiverType);
 195         }
 196 
 197         // Cast reference arguments to its type.
 198         for (int index = 0; index < signature.getParameterCount(false); index++) {
 199             JavaType parameterType = signature.getParameterType(index, target.getDeclaringClass());
 200             maybeCastArgument(assumptions, arguments, receiverSkip + index, parameterType);
 201         }
 202 
 203         if (target.canBeStaticallyBound()) {
 204             return createTargetInvokeNode(assumptions, intrinsicMethod, target, original, bci, returnStamp, arguments);
 205         }
 206 
 207         // Try to get the most accurate receiver type
 208         if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) {
 209             ValueNode receiver = getReceiver(arguments);
 210             TypeReference receiverType = StampTool.typeReferenceOrNull(receiver.stamp());
 211             if (receiverType != null) {
 212                 AssumptionResult<ResolvedJavaMethod> concreteMethod = receiverType.getType().findUniqueConcreteMethod(target);
 213                 if (concreteMethod != null && concreteMethod.canRecordTo(assumptions)) {
 214                     concreteMethod.recordTo(assumptions);
 215                     return createTargetInvokeNode(assumptions, intrinsicMethod, concreteMethod.getResult(), original, bci, returnStamp, arguments);
 216                 }
 217             }
 218         } else {
 219             AssumptionResult<ResolvedJavaMethod> concreteMethod = target.getDeclaringClass().findUniqueConcreteMethod(target);
 220             if (concreteMethod != null && concreteMethod.canRecordTo(assumptions)) {
 221                 concreteMethod.recordTo(assumptions);
 222                 return createTargetInvokeNode(assumptions, intrinsicMethod, concreteMethod.getResult(), original, bci, returnStamp, arguments);
 223             }
 224         }
 225 
 226         return null;
 227     }
 228 
 229     /**
 230      * Inserts a node to cast the argument at index to the given type if the given type is more
 231      * concrete than the argument type.
 232      *
 233      * @param index of the argument to be cast
 234      * @param type the type the argument should be cast to
 235      */
 236     private static void maybeCastArgument(Assumptions assumptions, ValueNode[] arguments, int index, JavaType type) {
 237         if (type instanceof ResolvedJavaType) {
 238             TypeReference targetType = TypeReference.create(assumptions, (ResolvedJavaType) type);
 239             ValueNode argument = arguments[index];
 240             /*
 241              * When an argument is a Word type, we can have a mismatch of primitive/object types
 242              * here. Not inserting a PiNode is a safe fallback, and Word types need no additional
 243              * type information anyway.
 244              */
 245             if (targetType != null && !targetType.getType().isPrimitive() && !argument.getStackKind().isPrimitive()) {
 246                 ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp());
 247                 if (argumentType == null || (argumentType.isAssignableFrom(targetType.getType()) && !argumentType.equals(targetType.getType()))) {
 248                     PiNode piNode = new PiNode(argument, StampFactory.object(targetType));
 249                     arguments[index] = piNode;
 250                 }
 251             }
 252         }
 253     }
 254 
 255     /**
 256      * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed
 257      * to the InvokeNode is in fact a {@link ResolvedMethodHandleCallTargetNode}.
 258      *
 259      * @return invoke node for the member name target
 260      */
 261     private static InvokeNode createTargetInvokeNode(Assumptions assumptions, IntrinsicMethod intrinsicMethod, ResolvedJavaMethod target, ResolvedJavaMethod original, int bci, StampPair returnStamp,
 262                     ValueNode[] arguments) {
 263         InvokeKind targetInvokeKind = target.isStatic() ? InvokeKind.Static : InvokeKind.Special;
 264         JavaType targetReturnType = target.getSignature().getReturnType(null);
 265 
 266         // MethodHandleLinkTo* nodes have a trailing MemberName argument which
 267         // needs to be popped.
 268         ValueNode[] targetArguments;
 269         switch (intrinsicMethod) {
 270             case INVOKE_BASIC:
 271                 targetArguments = arguments;
 272                 break;
 273             case LINK_TO_STATIC:
 274             case LINK_TO_SPECIAL:
 275             case LINK_TO_VIRTUAL:
 276             case LINK_TO_INTERFACE:
 277                 targetArguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1);
 278                 break;
 279             default:
 280                 throw GraalError.shouldNotReachHere();
 281         }
 282         StampPair targetReturnStamp = StampFactory.forDeclaredType(assumptions, targetReturnType, false);
 283 
 284         MethodCallTargetNode callTarget = ResolvedMethodHandleCallTargetNode.create(targetInvokeKind, target, targetArguments, targetReturnStamp, original, arguments, returnStamp);
 285 
 286         // The call target can have a different return type than the invoker,
 287         // e.g. the target returns an Object but the invoker void. In this case
 288         // we need to use the stamp of the invoker. Note: always using the
 289         // invoker's stamp would be wrong because it's a less concrete type
 290         // (usually java.lang.Object).
 291         if (returnStamp.getTrustedStamp().getStackKind() == JavaKind.Void) {
 292             return new InvokeNode(callTarget, bci, StampFactory.forVoid());
 293         } else {
 294             return new InvokeNode(callTarget, bci);
 295         }
 296     }
 297 }