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 }