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.FixedGuardNode;
  42 import org.graalvm.compiler.nodes.FixedNode;
  43 import org.graalvm.compiler.nodes.FixedWithNextNode;
  44 import org.graalvm.compiler.nodes.GuardNode;
  45 import org.graalvm.compiler.nodes.InvokeNode;
  46 import org.graalvm.compiler.nodes.LogicNode;
  47 import org.graalvm.compiler.nodes.PiNode;
  48 import org.graalvm.compiler.nodes.StructuredGraph;
  49 import org.graalvm.compiler.nodes.ValueNode;
  50 import org.graalvm.compiler.nodes.extended.AnchoringNode;
  51 import org.graalvm.compiler.nodes.extended.GuardingNode;
  52 import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
  53 import org.graalvm.compiler.nodes.java.InstanceOfNode;
  54 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
  55 import org.graalvm.compiler.nodes.type.StampTool;
  56 import org.graalvm.compiler.nodes.util.GraphUtil;
  57 
  58 import jdk.vm.ci.meta.Assumptions;
  59 import jdk.vm.ci.meta.Assumptions.AssumptionResult;
  60 import jdk.vm.ci.meta.DeoptimizationAction;
  61 import jdk.vm.ci.meta.DeoptimizationReason;
  62 import jdk.vm.ci.meta.JavaConstant;
  63 import jdk.vm.ci.meta.JavaKind;
  64 import jdk.vm.ci.meta.JavaType;
  65 import jdk.vm.ci.meta.MethodHandleAccessProvider;
  66 import jdk.vm.ci.meta.MethodHandleAccessProvider.IntrinsicMethod;
  67 import jdk.vm.ci.meta.ResolvedJavaMethod;
  68 import jdk.vm.ci.meta.ResolvedJavaType;
  69 import jdk.vm.ci.meta.Signature;
  70 
  71 /**
  72  * Node for invocation methods defined on the class {@link MethodHandle}.
  73  */
  74 @NodeInfo(cycles = CYCLES_UNKNOWN, size = SIZE_UNKNOWN)
  75 public final class MethodHandleNode extends MacroStateSplitNode implements Simplifiable {
  76     public static final NodeClass<MethodHandleNode> TYPE = NodeClass.create(MethodHandleNode.class);
  77 
  78     protected final IntrinsicMethod intrinsicMethod;
  79 
  80     public MethodHandleNode(IntrinsicMethod intrinsicMethod, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, StampPair returnStamp, ValueNode... arguments) {
  81         super(TYPE, invokeKind, targetMethod, bci, returnStamp, arguments);
  82         this.intrinsicMethod = intrinsicMethod;
  83     }
  84 
  85     /**
  86      * Attempts to transform application of an intrinsifiable {@link MethodHandle} method into an
  87      * invocation on another method with possibly transformed arguments.
  88      *
  89      * @param methodHandleAccess objects for accessing the implementation internals of a
  90      *            {@link MethodHandle}
  91      * @param intrinsicMethod denotes the intrinsifiable {@link MethodHandle} method being processed
  92      * @param bci the BCI of the original {@link MethodHandle} call
  93      * @param returnStamp return stamp of the original {@link MethodHandle} call
  94      * @param arguments arguments to the original {@link MethodHandle} call
  95      * @return a more direct invocation derived from the {@link MethodHandle} call or null
  96      */
  97     public static InvokeNode tryResolveTargetInvoke(GraphAdder adder, MethodHandleAccessProvider methodHandleAccess, IntrinsicMethod intrinsicMethod,
  98                     ResolvedJavaMethod original, int bci,
  99                     StampPair returnStamp, ValueNode... arguments) {
 100         switch (intrinsicMethod) {
 101             case INVOKE_BASIC:
 102                 return getInvokeBasicTarget(adder, intrinsicMethod, methodHandleAccess, original, bci, returnStamp, arguments);
 103             case LINK_TO_STATIC:
 104             case LINK_TO_SPECIAL:
 105             case LINK_TO_VIRTUAL:
 106             case LINK_TO_INTERFACE:
 107                 return getLinkToTarget(adder, intrinsicMethod, methodHandleAccess, original, bci, returnStamp, arguments);
 108             default:
 109                 throw GraalError.shouldNotReachHere();
 110         }
 111     }
 112 
 113     /**
 114      * A simple utility class for adding nodes to the graph when building a MethodHandle invoke.
 115      */
 116     public abstract static class GraphAdder {
 117         private final StructuredGraph graph;
 118 
 119         public GraphAdder(StructuredGraph graph) {
 120             this.graph = graph;
 121         }
 122 
 123         /**
 124          * Call {@link StructuredGraph#addOrUnique(org.graalvm.compiler.graph.Node)} on {@code node}
 125          * and link any {@link FixedWithNextNode}s into the current control flow.
 126          *
 127          * @param node
 128          * @return the newly added node
 129          */
 130         public abstract <T extends ValueNode> T add(T node);
 131 
 132         /**
 133          * @return an {@link AnchoringNode} if floating guards should be created, otherwise
 134          *         {@link FixedGuardNode}s will be used.
 135          */
 136         public AnchoringNode getGuardAnchor() {
 137             return null;
 138         }
 139 
 140         public Assumptions getAssumptions() {
 141             return graph.getAssumptions();
 142         }
 143     }
 144 
 145     @Override
 146     public void simplify(SimplifierTool tool) {
 147         MethodHandleAccessProvider methodHandleAccess = tool.getConstantReflection().getMethodHandleAccess();
 148         ValueNode[] argumentsArray = arguments.toArray(new ValueNode[arguments.size()]);
 149 
 150         final FixedNode before = this;
 151         GraphAdder adder = new GraphAdder(graph()) {
 152             @Override
 153             public <T extends ValueNode> T add(T node) {
 154                 T added = graph().addOrUnique(node);
 155                 if (added instanceof FixedWithNextNode) {
 156                     graph().addBeforeFixed(before, (FixedWithNextNode) added);
 157                 }
 158                 return added;
 159             }
 160         };
 161         InvokeNode invoke = tryResolveTargetInvoke(adder, methodHandleAccess, intrinsicMethod, targetMethod, bci, returnStamp, argumentsArray);
 162         if (invoke != null) {
 163             assert invoke.graph() == null;
 164             invoke = graph().addOrUniqueWithInputs(invoke);
 165             invoke.setStateAfter(stateAfter());
 166             FixedNode currentNext = next();
 167             replaceAtUsages(invoke);
 168             GraphUtil.removeFixedWithUnusedInputs(this);
 169             graph().addBeforeFixed(currentNext, invoke);
 170         }
 171     }
 172 
 173     /**
 174      * Get the receiver of a MethodHandle.invokeBasic call.
 175      *
 176      * @return the receiver argument node
 177      */
 178     private static ValueNode getReceiver(ValueNode[] arguments) {
 179         return arguments[0];
 180     }
 181 
 182     /**
 183      * Get the MemberName argument of a MethodHandle.linkTo* call.
 184      *
 185      * @return the MemberName argument node (which is the last argument)
 186      */
 187     private static ValueNode getMemberName(ValueNode[] arguments) {
 188         return arguments[arguments.length - 1];
 189     }
 190 
 191     /**
 192      * Used for the MethodHandle.invokeBasic method (the {@link IntrinsicMethod#INVOKE_BASIC }
 193      * method) to get the target {@link InvokeNode} if the method handle receiver is constant.
 194      *
 195      * @param adder
 196      *
 197      * @return invoke node for the {@link java.lang.invoke.MethodHandle} target
 198      */
 199     private static InvokeNode getInvokeBasicTarget(GraphAdder adder, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess,
 200                     ResolvedJavaMethod original,
 201                     int bci,
 202                     StampPair returnStamp, ValueNode[] arguments) {
 203         ValueNode methodHandleNode = getReceiver(arguments);
 204         if (methodHandleNode.isConstant()) {
 205             return getTargetInvokeNode(adder, intrinsicMethod, bci, returnStamp, arguments, methodHandleAccess.resolveInvokeBasicTarget(methodHandleNode.asJavaConstant(), true), original);
 206         }
 207         return null;
 208     }
 209 
 210     /**
 211      * Used for the MethodHandle.linkTo* methods (the {@link IntrinsicMethod#LINK_TO_STATIC},
 212      * {@link IntrinsicMethod#LINK_TO_SPECIAL}, {@link IntrinsicMethod#LINK_TO_VIRTUAL}, and
 213      * {@link IntrinsicMethod#LINK_TO_INTERFACE} methods) to get the target {@link InvokeNode} if
 214      * the member name argument is constant.
 215      *
 216      * @param adder
 217      *
 218      * @return invoke node for the member name target
 219      */
 220     private static InvokeNode getLinkToTarget(GraphAdder adder, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess,
 221                     ResolvedJavaMethod original,
 222                     int bci,
 223                     StampPair returnStamp, ValueNode[] arguments) {
 224         ValueNode memberNameNode = getMemberName(arguments);
 225         if (memberNameNode.isConstant()) {
 226             return getTargetInvokeNode(adder, intrinsicMethod, bci, returnStamp, arguments, methodHandleAccess.resolveLinkToTarget(memberNameNode.asJavaConstant()), original);
 227         }
 228         return null;
 229     }
 230 
 231     /**
 232      * Helper function to get the {@link InvokeNode} for the targetMethod of a
 233      * java.lang.invoke.MemberName.
 234      *
 235      * @param adder
 236      * @param target the target, already loaded from the member name node
 237      *
 238      * @return invoke node for the member name target
 239      */
 240     private static InvokeNode getTargetInvokeNode(GraphAdder adder, IntrinsicMethod intrinsicMethod, int bci, StampPair returnStamp, ValueNode[] originalArguments, ResolvedJavaMethod target,
 241                     ResolvedJavaMethod original) {
 242         if (target == null) {
 243             return null;
 244         }
 245 
 246         // In lambda forms we erase signature types to avoid resolving issues
 247         // involving class loaders. When we optimize a method handle invoke
 248         // to a direct call we must cast the receiver and arguments to its
 249         // actual types.
 250         Signature signature = target.getSignature();
 251         final boolean isStatic = target.isStatic();
 252         final int receiverSkip = isStatic ? 0 : 1;
 253 
 254         Assumptions assumptions = adder.getAssumptions();
 255         ResolvedJavaMethod realTarget = null;
 256         if (target.canBeStaticallyBound()) {
 257             realTarget = target;
 258         } else {
 259             ResolvedJavaType targetType = target.getDeclaringClass();
 260             // Try to bind based on the declaredType
 261             AssumptionResult<ResolvedJavaMethod> concreteMethod = targetType.findUniqueConcreteMethod(target);
 262             if (concreteMethod == null) {
 263                 // Try to get the most accurate receiver type
 264                 if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) {
 265                     ValueNode receiver = getReceiver(originalArguments);
 266                     TypeReference receiverType = StampTool.typeReferenceOrNull(receiver.stamp());
 267                     if (receiverType != null) {
 268                         concreteMethod = receiverType.getType().findUniqueConcreteMethod(target);
 269                     }
 270                 }
 271 
 272             }
 273             if (concreteMethod != null && concreteMethod.canRecordTo(assumptions)) {
 274                 concreteMethod.recordTo(assumptions);
 275                 realTarget = concreteMethod.getResult();
 276             }
 277         }
 278 
 279         if (realTarget != null) {
 280             // Don't mutate the passed in arguments
 281             ValueNode[] arguments = originalArguments.clone();
 282 
 283             // Cast receiver to its type.
 284             if (!isStatic) {
 285                 JavaType receiverType = target.getDeclaringClass();
 286                 maybeCastArgument(adder, arguments, 0, receiverType);
 287             }
 288 
 289             // Cast reference arguments to its type.
 290             for (int index = 0; index < signature.getParameterCount(false); index++) {
 291                 JavaType parameterType = signature.getParameterType(index, target.getDeclaringClass());
 292                 maybeCastArgument(adder, arguments, receiverSkip + index, parameterType);
 293             }
 294             InvokeNode invoke = createTargetInvokeNode(assumptions, intrinsicMethod, realTarget, original, bci, returnStamp, arguments);
 295             assert invoke != null : "graph has been modified so this must result an invoke";
 296             return invoke;
 297         }
 298         return null;
 299     }
 300 
 301     /**
 302      * Inserts a node to cast the argument at index to the given type if the given type is more
 303      * concrete than the argument type.
 304      *
 305      * @param adder
 306      * @param index of the argument to be cast
 307      * @param type the type the argument should be cast to
 308      */
 309     private static void maybeCastArgument(GraphAdder adder, ValueNode[] arguments, int index, JavaType type) {
 310         ValueNode argument = arguments[index];
 311         if (type instanceof ResolvedJavaType && !((ResolvedJavaType) type).isJavaLangObject()) {
 312             Assumptions assumptions = adder.getAssumptions();
 313             TypeReference targetType = TypeReference.create(assumptions, (ResolvedJavaType) type);
 314             /*
 315              * When an argument is a Word type, we can have a mismatch of primitive/object types
 316              * here. Not inserting a PiNode is a safe fallback, and Word types need no additional
 317              * type information anyway.
 318              */
 319             if (targetType != null && !targetType.getType().isPrimitive() && !argument.getStackKind().isPrimitive()) {
 320                 ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp());
 321                 if (argumentType == null || (argumentType.isAssignableFrom(targetType.getType()) && !argumentType.equals(targetType.getType()))) {
 322                     LogicNode inst = InstanceOfNode.createAllowNull(targetType, argument, null, null);
 323                     assert !inst.isAlive();
 324                     if (!inst.isTautology()) {
 325                         inst = adder.add(inst);
 326                         AnchoringNode guardAnchor = adder.getGuardAnchor();
 327                         DeoptimizationReason reason = DeoptimizationReason.ClassCastException;
 328                         DeoptimizationAction action = DeoptimizationAction.InvalidateRecompile;
 329                         JavaConstant speculation = JavaConstant.NULL_POINTER;
 330                         GuardingNode guard;
 331                         if (guardAnchor == null) {
 332                             FixedGuardNode fixedGuard = adder.add(new FixedGuardNode(inst, reason, action, speculation, false));
 333                             guard = fixedGuard;
 334                         } else {
 335                             GuardNode newGuard = adder.add(new GuardNode(inst, guardAnchor, reason, action, false, speculation));
 336                             adder.add(new ValueAnchorNode(newGuard));
 337                             guard = newGuard;
 338                         }
 339                         ValueNode valueNode = adder.add(PiNode.create(argument, StampFactory.object(targetType), guard.asNode()));
 340                         arguments[index] = valueNode;
 341                     }
 342                 }
 343             }
 344         }
 345     }
 346 
 347     /**
 348      * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed
 349      * to the InvokeNode is in fact a {@link ResolvedMethodHandleCallTargetNode}.
 350      *
 351      * @return invoke node for the member name target
 352      */
 353     private static InvokeNode createTargetInvokeNode(Assumptions assumptions, IntrinsicMethod intrinsicMethod, ResolvedJavaMethod target, ResolvedJavaMethod original, int bci, StampPair returnStamp,
 354                     ValueNode[] arguments) {
 355         InvokeKind targetInvokeKind = target.isStatic() ? InvokeKind.Static : InvokeKind.Special;
 356         JavaType targetReturnType = target.getSignature().getReturnType(null);
 357 
 358         // MethodHandleLinkTo* nodes have a trailing MemberName argument which
 359         // needs to be popped.
 360         ValueNode[] targetArguments;
 361         switch (intrinsicMethod) {
 362             case INVOKE_BASIC:
 363                 targetArguments = arguments;
 364                 break;
 365             case LINK_TO_STATIC:
 366             case LINK_TO_SPECIAL:
 367             case LINK_TO_VIRTUAL:
 368             case LINK_TO_INTERFACE:
 369                 targetArguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1);
 370                 break;
 371             default:
 372                 throw GraalError.shouldNotReachHere();
 373         }
 374         StampPair targetReturnStamp = StampFactory.forDeclaredType(assumptions, targetReturnType, false);
 375 
 376         MethodCallTargetNode callTarget = ResolvedMethodHandleCallTargetNode.create(targetInvokeKind, target, targetArguments, targetReturnStamp, original, arguments, returnStamp);
 377 
 378         // The call target can have a different return type than the invoker,
 379         // e.g. the target returns an Object but the invoker void. In this case
 380         // we need to use the stamp of the invoker. Note: always using the
 381         // invoker's stamp would be wrong because it's a less concrete type
 382         // (usually java.lang.Object).
 383         if (returnStamp.getTrustedStamp().getStackKind() == JavaKind.Void) {
 384             return new InvokeNode(callTarget, bci, StampFactory.forVoid());
 385         } else {
 386             return new InvokeNode(callTarget, bci);
 387         }
 388     }
 389 }