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 }