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