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 }