001/* 002 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.replacements.nodes; 024 025import java.lang.invoke.*; 026import java.util.*; 027 028import jdk.internal.jvmci.common.*; 029import jdk.internal.jvmci.meta.*; 030import jdk.internal.jvmci.meta.Assumptions.*; 031import jdk.internal.jvmci.meta.MethodHandleAccessProvider.*; 032 033import com.oracle.graal.compiler.common.type.*; 034import com.oracle.graal.graph.*; 035import com.oracle.graal.graph.spi.*; 036import com.oracle.graal.nodeinfo.*; 037import com.oracle.graal.nodes.*; 038import com.oracle.graal.nodes.CallTargetNode.InvokeKind; 039import com.oracle.graal.nodes.java.*; 040import com.oracle.graal.nodes.type.*; 041import com.oracle.graal.nodes.util.*; 042 043/** 044 * Node for invocation methods defined on the class {@link MethodHandle}. 045 */ 046@NodeInfo 047public final class MethodHandleNode extends MacroStateSplitNode implements Simplifiable { 048 public static final NodeClass<MethodHandleNode> TYPE = NodeClass.create(MethodHandleNode.class); 049 050 protected final IntrinsicMethod intrinsicMethod; 051 052 public MethodHandleNode(IntrinsicMethod intrinsicMethod, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, JavaType returnType, ValueNode... arguments) { 053 super(TYPE, invokeKind, targetMethod, bci, returnType, arguments); 054 this.intrinsicMethod = intrinsicMethod; 055 } 056 057 /** 058 * Attempts to transform application of an intrinsifiable {@link MethodHandle} method into an 059 * invocation on another method with possibly transformed arguments. 060 * 061 * @param assumptions object for recording any speculations made during the transformation 062 * @param methodHandleAccess objects for accessing the implementation internals of a 063 * {@link MethodHandle} 064 * @param intrinsicMethod denotes the intrinsifiable {@link MethodHandle} method being processed 065 * @param bci the BCI of the original {@link MethodHandle} call 066 * @param returnType return type of the original {@link MethodHandle} call 067 * @param arguments arguments to the original {@link MethodHandle} call 068 * @return a more direct invocation derived from the {@link MethodHandle} call or null 069 */ 070 public static InvokeNode tryResolveTargetInvoke(Assumptions assumptions, MethodHandleAccessProvider methodHandleAccess, IntrinsicMethod intrinsicMethod, ResolvedJavaMethod original, int bci, 071 JavaType returnType, ValueNode... arguments) { 072 switch (intrinsicMethod) { 073 case INVOKE_BASIC: 074 return getInvokeBasicTarget(assumptions, intrinsicMethod, methodHandleAccess, original, bci, returnType, arguments); 075 case LINK_TO_STATIC: 076 case LINK_TO_SPECIAL: 077 case LINK_TO_VIRTUAL: 078 case LINK_TO_INTERFACE: 079 return getLinkToTarget(assumptions, intrinsicMethod, methodHandleAccess, original, bci, returnType, arguments); 080 default: 081 throw JVMCIError.shouldNotReachHere(); 082 } 083 } 084 085 @Override 086 public void simplify(SimplifierTool tool) { 087 MethodHandleAccessProvider methodHandleAccess = tool.getConstantReflection().getMethodHandleAccess(); 088 ValueNode[] argumentsArray = arguments.toArray(new ValueNode[arguments.size()]); 089 InvokeNode invoke = tryResolveTargetInvoke(graph().getAssumptions(), methodHandleAccess, intrinsicMethod, targetMethod, bci, returnType, argumentsArray); 090 if (invoke != null) { 091 assert invoke.graph() == null; 092 invoke = graph().addOrUniqueWithInputs(invoke); 093 invoke.setStateAfter(stateAfter()); 094 FixedNode currentNext = next(); 095 replaceAtUsages(invoke); 096 GraphUtil.removeFixedWithUnusedInputs(this); 097 graph().addBeforeFixed(currentNext, invoke); 098 } 099 } 100 101 /** 102 * Get the receiver of a MethodHandle.invokeBasic call. 103 * 104 * @return the receiver argument node 105 */ 106 private static ValueNode getReceiver(ValueNode[] arguments) { 107 return arguments[0]; 108 } 109 110 /** 111 * Get the MemberName argument of a MethodHandle.linkTo* call. 112 * 113 * @return the MemberName argument node (which is the last argument) 114 */ 115 private static ValueNode getMemberName(ValueNode[] arguments) { 116 return arguments[arguments.length - 1]; 117 } 118 119 /** 120 * Used for the MethodHandle.invokeBasic method (the {@link IntrinsicMethod#INVOKE_BASIC } 121 * method) to get the target {@link InvokeNode} if the method handle receiver is constant. 122 * 123 * @return invoke node for the {@link java.lang.invoke.MethodHandle} target 124 */ 125 private static InvokeNode getInvokeBasicTarget(Assumptions assumptions, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess, ResolvedJavaMethod original, int bci, 126 JavaType returnType, ValueNode[] arguments) { 127 ValueNode methodHandleNode = getReceiver(arguments); 128 if (methodHandleNode.isConstant()) { 129 return getTargetInvokeNode(assumptions, intrinsicMethod, bci, returnType, arguments, methodHandleAccess.resolveInvokeBasicTarget(methodHandleNode.asJavaConstant(), true), original); 130 } 131 return null; 132 } 133 134 /** 135 * Used for the MethodHandle.linkTo* methods (the {@link IntrinsicMethod#LINK_TO_STATIC}, 136 * {@link IntrinsicMethod#LINK_TO_SPECIAL}, {@link IntrinsicMethod#LINK_TO_VIRTUAL}, and 137 * {@link IntrinsicMethod#LINK_TO_INTERFACE} methods) to get the target {@link InvokeNode} if 138 * the member name argument is constant. 139 * 140 * @return invoke node for the member name target 141 */ 142 private static InvokeNode getLinkToTarget(Assumptions assumptions, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess, ResolvedJavaMethod original, int bci, 143 JavaType returnType, ValueNode[] arguments) { 144 ValueNode memberNameNode = getMemberName(arguments); 145 if (memberNameNode.isConstant()) { 146 return getTargetInvokeNode(assumptions, intrinsicMethod, bci, returnType, arguments, methodHandleAccess.resolveLinkToTarget(memberNameNode.asJavaConstant()), original); 147 } 148 return null; 149 } 150 151 /** 152 * Helper function to get the {@link InvokeNode} for the targetMethod of a 153 * java.lang.invoke.MemberName. 154 * 155 * @param target the target, already loaded from the member name node 156 * @return invoke node for the member name target 157 */ 158 private static InvokeNode getTargetInvokeNode(Assumptions assumptions, IntrinsicMethod intrinsicMethod, int bci, JavaType returnType, ValueNode[] arguments, ResolvedJavaMethod target, 159 ResolvedJavaMethod original) { 160 if (target == null) { 161 return null; 162 } 163 164 // In lambda forms we erase signature types to avoid resolving issues 165 // involving class loaders. When we optimize a method handle invoke 166 // to a direct call we must cast the receiver and arguments to its 167 // actual types. 168 Signature signature = target.getSignature(); 169 final boolean isStatic = target.isStatic(); 170 final int receiverSkip = isStatic ? 0 : 1; 171 172 // Cast receiver to its type. 173 if (!isStatic) { 174 JavaType receiverType = target.getDeclaringClass(); 175 maybeCastArgument(arguments, 0, receiverType); 176 } 177 178 // Cast reference arguments to its type. 179 for (int index = 0; index < signature.getParameterCount(false); index++) { 180 JavaType parameterType = signature.getParameterType(index, target.getDeclaringClass()); 181 maybeCastArgument(arguments, receiverSkip + index, parameterType); 182 } 183 184 if (target.canBeStaticallyBound()) { 185 return createTargetInvokeNode(intrinsicMethod, target, original, bci, returnType, arguments); 186 } 187 188 // Try to get the most accurate receiver type 189 if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) { 190 ValueNode receiver = getReceiver(arguments); 191 ResolvedJavaType receiverType = StampTool.typeOrNull(receiver.stamp()); 192 if (receiverType != null) { 193 AssumptionResult<ResolvedJavaMethod> concreteMethod = receiverType.findUniqueConcreteMethod(target); 194 if (concreteMethod != null) { 195 assumptions.record(concreteMethod); 196 return createTargetInvokeNode(intrinsicMethod, concreteMethod.getResult(), original, bci, returnType, arguments); 197 } 198 } 199 } else { 200 AssumptionResult<ResolvedJavaMethod> concreteMethod = target.getDeclaringClass().findUniqueConcreteMethod(target); 201 if (concreteMethod != null) { 202 assumptions.record(concreteMethod); 203 return createTargetInvokeNode(intrinsicMethod, concreteMethod.getResult(), original, bci, returnType, arguments); 204 } 205 } 206 207 return null; 208 } 209 210 /** 211 * Inserts a node to cast the argument at index to the given type if the given type is more 212 * concrete than the argument type. 213 * 214 * @param index of the argument to be cast 215 * @param type the type the argument should be cast to 216 */ 217 private static void maybeCastArgument(ValueNode[] arguments, int index, JavaType type) { 218 if (type instanceof ResolvedJavaType) { 219 ResolvedJavaType targetType = (ResolvedJavaType) type; 220 if (!targetType.isPrimitive()) { 221 ValueNode argument = arguments[index]; 222 ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp()); 223 if (argumentType == null || (argumentType.isAssignableFrom(targetType) && !argumentType.equals(targetType))) { 224 PiNode piNode = new PiNode(argument, StampFactory.declared(targetType)); 225 arguments[index] = piNode; 226 } 227 } 228 } 229 } 230 231 /** 232 * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed 233 * to the InvokeNode is in fact a {@link ResolvedMethodHandleCallTargetNode}. 234 * 235 * @return invoke node for the member name target 236 */ 237 private static InvokeNode createTargetInvokeNode(IntrinsicMethod intrinsicMethod, ResolvedJavaMethod target, ResolvedJavaMethod original, int bci, JavaType returnType, ValueNode[] arguments) { 238 InvokeKind targetInvokeKind = target.isStatic() ? InvokeKind.Static : InvokeKind.Special; 239 JavaType targetReturnType = target.getSignature().getReturnType(null); 240 241 // MethodHandleLinkTo* nodes have a trailing MemberName argument which 242 // needs to be popped. 243 ValueNode[] targetArguments; 244 switch (intrinsicMethod) { 245 case INVOKE_BASIC: 246 targetArguments = arguments; 247 break; 248 case LINK_TO_STATIC: 249 case LINK_TO_SPECIAL: 250 case LINK_TO_VIRTUAL: 251 case LINK_TO_INTERFACE: 252 targetArguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1); 253 break; 254 default: 255 throw JVMCIError.shouldNotReachHere(); 256 } 257 258 MethodCallTargetNode callTarget = ResolvedMethodHandleCallTargetNode.create(targetInvokeKind, target, targetArguments, targetReturnType, original, arguments, returnType); 259 260 // The call target can have a different return type than the invoker, 261 // e.g. the target returns an Object but the invoker void. In this case 262 // we need to use the stamp of the invoker. Note: always using the 263 // invoker's stamp would be wrong because it's a less concrete type 264 // (usually java.lang.Object). 265 if (returnType.getKind() == Kind.Void) { 266 return new InvokeNode(callTarget, bci, StampFactory.forVoid()); 267 } else { 268 return new InvokeNode(callTarget, bci); 269 } 270 } 271}