# HG changeset patch # User Christian Wimmer # Date 1431645991 25200 # Node ID 331da25c2dda1fdd17c6f6226bc90ec2c132e213 # Parent f9883cab45800f10e5e69071cb94d0691ab4d649 Make support for method handles VM independent diff -r f9883cab4580 -r 331da25c2dda graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java Thu May 14 16:20:12 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotGraphBuilderPlugins.java Thu May 14 16:26:31 2015 -0700 @@ -66,7 +66,7 @@ */ public static Plugins create(HotSpotVMConfig config, HotSpotWordTypes wordTypes, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, SnippetReflectionProvider snippetReflection, ForeignCallsProvider foreignCalls, StampProvider stampProvider, ReplacementsImpl replacements) { - InvocationPlugins invocationPlugins = new HotSpotInvocationPlugins(config, metaAccess, constantReflection.getMethodHandleAccess()); + InvocationPlugins invocationPlugins = new HotSpotInvocationPlugins(config, metaAccess); Plugins plugins = new Plugins(invocationPlugins); NodeIntrinsificationPhase nodeIntrinsification = new NodeIntrinsificationPhase(metaAccess, constantReflection, snippetReflection, foreignCalls, stampProvider); @@ -77,7 +77,8 @@ plugins.setLoadIndexedPlugin(new HotSpotLoadIndexedPlugin(wordTypes)); plugins.setTypeCheckPlugin(wordOperationPlugin); plugins.setInlineInvokePlugin(new DefaultInlineInvokePlugin(replacements)); - plugins.setGenericInvocationPlugin(new DefaultGenericInvocationPlugin(metaAccess, nodeIntrinsification, wordOperationPlugin)); + plugins.setGenericInvocationPlugin(new MethodHandleInvocationPlugin(constantReflection.getMethodHandleAccess(), new DefaultGenericInvocationPlugin(metaAccess, nodeIntrinsification, + wordOperationPlugin))); registerObjectPlugins(invocationPlugins); registerClassPlugins(plugins); diff -r f9883cab4580 -r 331da25c2dda graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java Thu May 14 16:20:12 2015 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotInvocationPlugins.java Thu May 14 16:26:31 2015 -0700 @@ -22,22 +22,14 @@ */ package com.oracle.graal.hotspot.meta; -import static com.oracle.graal.hotspot.meta.HotSpotMethodHandleAccessProvider.*; - -import java.lang.invoke.*; -import java.util.*; - import com.oracle.graal.api.meta.*; -import com.oracle.graal.api.meta.MethodHandleAccessProvider.IntrinsicMethod; import com.oracle.graal.compiler.common.*; import com.oracle.graal.graph.*; import com.oracle.graal.graph.iterators.*; import com.oracle.graal.graphbuilderconf.*; -import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver; import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.phases.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.CallTargetNode.InvokeKind; import com.oracle.graal.nodes.type.*; import com.oracle.graal.replacements.StandardGraphBuilderPlugins.BoxPlugin; import com.oracle.graal.replacements.nodes.*; @@ -47,12 +39,10 @@ */ final class HotSpotInvocationPlugins extends InvocationPlugins { final HotSpotVMConfig config; - final MethodHandleAccessProvider methodHandleAccess; - public HotSpotInvocationPlugins(HotSpotVMConfig config, MetaAccessProvider metaAccess, MethodHandleAccessProvider methodHandleAccess) { + public HotSpotInvocationPlugins(HotSpotVMConfig config, MetaAccessProvider metaAccess) { super(metaAccess); this.config = config; - this.methodHandleAccess = methodHandleAccess; } @Override @@ -73,68 +63,6 @@ super.register(plugin, declaringClass, name, argumentTypes); } - private ResolvedJavaType methodHandleClass; - private final Map methodHandlePlugins = new EnumMap<>(IntrinsicMethod.class); - - @Override - public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) { - if (methodHandleClass == null) { - methodHandleClass = plugins.getMetaAccess().lookupJavaType(MethodHandle.class); - } - if (method.getDeclaringClass().equals(methodHandleClass)) { - HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method; - int intrinsicId = hsMethod.intrinsicId(); - if (intrinsicId != 0) { - /* - * The methods of MethodHandle that need substitution are signature-polymorphic, - * i.e., the VM replicates them for every signature that they are actually used for. - */ - IntrinsicMethod intrinsicMethod = getMethodHandleIntrinsic(intrinsicId); - if (intrinsicMethod != null) { - InvocationPlugin plugin = methodHandlePlugins.get(intrinsicMethod); - if (plugin == null) { - plugin = new InvocationPlugin() { - public boolean applyPolymorphic(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode... argsIncludingReceiver) { - InvokeKind invokeKind = b.getInvokeKind(); - if (invokeKind != InvokeKind.Static) { - receiver.get(); - } - JavaType invokeReturnType = b.getInvokeReturnType(); - InvokeNode invoke = MethodHandleNode.tryResolveTargetInvoke(b.getAssumptions(), b.getConstantReflection().getMethodHandleAccess(), intrinsicMethod, targetMethod, - b.bci(), invokeReturnType, argsIncludingReceiver); - if (invoke == null) { - MethodHandleNode methodHandleNode = new MethodHandleNode(intrinsicMethod, invokeKind, targetMethod, b.bci(), invokeReturnType, argsIncludingReceiver); - if (invokeReturnType.getKind() == Kind.Void) { - b.add(methodHandleNode); - } else { - b.addPush(methodHandleNode); - } - } else { - CallTargetNode callTarget = invoke.callTarget(); - NodeInputList argumentsList = callTarget.arguments(); - ValueNode[] args = argumentsList.toArray(new ValueNode[argumentsList.size()]); - for (ValueNode arg : args) { - b.recursiveAppend(arg); - } - b.handleReplacedInvoke(invoke.getInvokeKind(), callTarget.targetMethod(), args); - } - return true; - } - - public boolean isSignaturePolymorphic() { - return true; - } - }; - methodHandlePlugins.put(intrinsicMethod, plugin); - } - return plugin; - } - } - - } - return super.lookupInvocation(method); - } - @Override public void checkNewNodes(GraphBuilderContext b, InvocationPlugin plugin, NodeIterable newNodes) { for (Node node : newNodes) { diff -r f9883cab4580 -r 331da25c2dda graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ResolvedMethodHandleCallTargetNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/ResolvedMethodHandleCallTargetNode.java Thu May 14 16:20:12 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.hotspot.nodes; - -import static sun.misc.Version.*; - -import java.lang.invoke.*; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.hotspot.replacements.*; -import com.oracle.graal.nodeinfo.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.spi.*; - -/** - * A call target that replaces itself in the graph when being lowered by restoring the original - * {@link MethodHandle} invocation target. Prior to - * https://bugs.openjdk.java.net/browse/JDK-8072008, this is required for when a - * {@link MethodHandle} call is resolved to a constant target but the target was not inlined. In - * that case, the original invocation must be restored with all of its original arguments. Why? - * HotSpot linkage for {@link MethodHandle} intrinsics (see - * {@code MethodHandles::generate_method_handle_dispatch}) expects certain implicit arguments to be - * on the stack such as the MemberName suffix argument for a call to one of the MethodHandle.linkTo* - * methods. An {@linkplain MethodHandleNode#tryResolveTargetInvoke resolved} {@link MethodHandle} - * invocation drops these arguments which means the interpreter won't find them. - */ -@NodeInfo -public final class ResolvedMethodHandleCallTargetNode extends MethodCallTargetNode implements Lowerable { - - public static final NodeClass TYPE = NodeClass.create(ResolvedMethodHandleCallTargetNode.class); - - /** - * Creates a call target for an invocation on a direct target derived by resolving a constant - * {@link MethodHandle}. - */ - public static MethodCallTargetNode create(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod, - ValueNode[] originalArguments, JavaType originalReturnType) { - if (jdkMajorVersion() >= 1 && jdkMinorVersion() >= 8 && jdkMicroVersion() >= 0 && jdkUpdateVersion() >= 60) { - // https://bugs.openjdk.java.net/browse/JDK-8072008 is targeted for 8u60 - return new MethodCallTargetNode(invokeKind, targetMethod, arguments, returnType); - } - return new ResolvedMethodHandleCallTargetNode(invokeKind, targetMethod, arguments, returnType, originalTargetMethod, originalArguments, originalReturnType); - } - - protected final ResolvedJavaMethod originalTargetMethod; - protected final JavaType originalReturnType; - @Input NodeInputList originalArguments; - - protected ResolvedMethodHandleCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod, - ValueNode[] originalArguments, JavaType originalReturnType) { - super(TYPE, invokeKind, targetMethod, arguments, returnType); - this.originalTargetMethod = originalTargetMethod; - this.originalReturnType = originalReturnType; - this.originalArguments = new NodeInputList<>(this, originalArguments); - } - - @Override - public void lower(LoweringTool tool) { - InvokeKind replacementInvokeKind = originalTargetMethod.isStatic() ? InvokeKind.Static : InvokeKind.Special; - MethodCallTargetNode replacement = graph().add( - new MethodCallTargetNode(replacementInvokeKind, originalTargetMethod, originalArguments.toArray(new ValueNode[originalArguments.size()]), originalReturnType)); - - // Replace myself... - this.replaceAndDelete(replacement); - } - - @Override - public void generate(NodeLIRBuilderTool gen) { - throw GraalInternalError.shouldNotReachHere("should have replaced itself"); - } -} diff -r f9883cab4580 -r 331da25c2dda graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java Thu May 14 16:20:12 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.graal.hotspot.replacements; - -import java.lang.invoke.*; -import java.util.*; - -import com.oracle.graal.api.meta.*; -import com.oracle.graal.api.meta.Assumptions.AssumptionResult; -import com.oracle.graal.api.meta.MethodHandleAccessProvider.IntrinsicMethod; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.compiler.common.type.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.hotspot.nodes.*; -import com.oracle.graal.nodeinfo.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.CallTargetNode.InvokeKind; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.replacements.nodes.*; - -/** - * Node for invocation methods defined on the class {@link MethodHandle}. - */ -@NodeInfo -public final class MethodHandleNode extends MacroStateSplitNode implements Simplifiable { - public static final NodeClass TYPE = NodeClass.create(MethodHandleNode.class); - - protected final IntrinsicMethod intrinsicMethod; - - public MethodHandleNode(IntrinsicMethod intrinsicMethod, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, JavaType returnType, ValueNode... arguments) { - super(TYPE, invokeKind, targetMethod, bci, returnType, arguments); - this.intrinsicMethod = intrinsicMethod; - } - - /** - * Attempts to transform application of an intrinsifiable {@link MethodHandle} method into an - * invocation on another method with possibly transformed arguments. - * - * @param assumptions object for recording any speculations made during the transformation - * @param methodHandleAccess objects for accessing the implementation internals of a - * {@link MethodHandle} - * @param intrinsicMethod denotes the intrinsifiable {@link MethodHandle} method being processed - * @param bci the BCI of the original {@link MethodHandle} call - * @param returnType return type of the original {@link MethodHandle} call - * @param arguments arguments to the original {@link MethodHandle} call - * @return a more direct invocation derived from the {@link MethodHandle} call or null - */ - public static InvokeNode tryResolveTargetInvoke(Assumptions assumptions, MethodHandleAccessProvider methodHandleAccess, IntrinsicMethod intrinsicMethod, ResolvedJavaMethod original, int bci, - JavaType returnType, ValueNode... arguments) { - switch (intrinsicMethod) { - case INVOKE_BASIC: - return getInvokeBasicTarget(assumptions, intrinsicMethod, methodHandleAccess, original, bci, returnType, arguments); - case LINK_TO_STATIC: - case LINK_TO_SPECIAL: - case LINK_TO_VIRTUAL: - case LINK_TO_INTERFACE: - return getLinkToTarget(assumptions, intrinsicMethod, methodHandleAccess, original, bci, returnType, arguments); - default: - throw GraalInternalError.shouldNotReachHere(); - } - } - - @Override - public void simplify(SimplifierTool tool) { - MethodHandleAccessProvider methodHandleAccess = tool.getConstantReflection().getMethodHandleAccess(); - ValueNode[] argumentsArray = arguments.toArray(new ValueNode[arguments.size()]); - InvokeNode invoke = tryResolveTargetInvoke(graph().getAssumptions(), methodHandleAccess, intrinsicMethod, targetMethod, bci, returnType, argumentsArray); - if (invoke != null) { - assert invoke.graph() == null; - invoke = graph().addOrUniqueWithInputs(invoke); - invoke.setStateAfter(stateAfter()); - FixedNode currentNext = next(); - replaceAtUsages(invoke); - GraphUtil.removeFixedWithUnusedInputs(this); - graph().addBeforeFixed(currentNext, invoke); - } - } - - /** - * Get the receiver of a MethodHandle.invokeBasic call. - * - * @return the receiver argument node - */ - private static ValueNode getReceiver(ValueNode[] arguments) { - return arguments[0]; - } - - /** - * Get the MemberName argument of a MethodHandle.linkTo* call. - * - * @return the MemberName argument node (which is the last argument) - */ - private static ValueNode getMemberName(ValueNode[] arguments) { - return arguments[arguments.length - 1]; - } - - /** - * Used for the MethodHandle.invokeBasic method (the {@link IntrinsicMethod#INVOKE_BASIC } - * method) to get the target {@link InvokeNode} if the method handle receiver is constant. - * - * @return invoke node for the {@link java.lang.invoke.MethodHandle} target - */ - private static InvokeNode getInvokeBasicTarget(Assumptions assumptions, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess, ResolvedJavaMethod original, int bci, - JavaType returnType, ValueNode[] arguments) { - ValueNode methodHandleNode = getReceiver(arguments); - if (methodHandleNode.isConstant()) { - return getTargetInvokeNode(assumptions, intrinsicMethod, bci, returnType, arguments, methodHandleAccess.resolveInvokeBasicTarget(methodHandleNode.asJavaConstant(), true), original); - } - return null; - } - - /** - * Used for the MethodHandle.linkTo* methods (the {@link IntrinsicMethod#LINK_TO_STATIC}, - * {@link IntrinsicMethod#LINK_TO_SPECIAL}, {@link IntrinsicMethod#LINK_TO_VIRTUAL}, and - * {@link IntrinsicMethod#LINK_TO_INTERFACE} methods) to get the target {@link InvokeNode} if - * the member name argument is constant. - * - * @return invoke node for the member name target - */ - private static InvokeNode getLinkToTarget(Assumptions assumptions, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess, ResolvedJavaMethod original, int bci, - JavaType returnType, ValueNode[] arguments) { - ValueNode memberNameNode = getMemberName(arguments); - if (memberNameNode.isConstant()) { - return getTargetInvokeNode(assumptions, intrinsicMethod, bci, returnType, arguments, methodHandleAccess.resolveLinkToTarget(memberNameNode.asJavaConstant()), original); - } - return null; - } - - /** - * Helper function to get the {@link InvokeNode} for the targetMethod of a - * java.lang.invoke.MemberName. - * - * @param target the target, already loaded from the member name node - * @return invoke node for the member name target - */ - private static InvokeNode getTargetInvokeNode(Assumptions assumptions, IntrinsicMethod intrinsicMethod, int bci, JavaType returnType, ValueNode[] arguments, ResolvedJavaMethod target, - ResolvedJavaMethod original) { - if (target == null) { - return null; - } - - // In lambda forms we erase signature types to avoid resolving issues - // involving class loaders. When we optimize a method handle invoke - // to a direct call we must cast the receiver and arguments to its - // actual types. - Signature signature = target.getSignature(); - final boolean isStatic = target.isStatic(); - final int receiverSkip = isStatic ? 0 : 1; - - // Cast receiver to its type. - if (!isStatic) { - JavaType receiverType = target.getDeclaringClass(); - maybeCastArgument(arguments, 0, receiverType); - } - - // Cast reference arguments to its type. - for (int index = 0; index < signature.getParameterCount(false); index++) { - JavaType parameterType = signature.getParameterType(index, target.getDeclaringClass()); - maybeCastArgument(arguments, receiverSkip + index, parameterType); - } - - if (target.canBeStaticallyBound()) { - return createTargetInvokeNode(intrinsicMethod, target, original, bci, returnType, arguments); - } - - // Try to get the most accurate receiver type - if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) { - ValueNode receiver = getReceiver(arguments); - ResolvedJavaType receiverType = StampTool.typeOrNull(receiver.stamp()); - if (receiverType != null) { - AssumptionResult concreteMethod = receiverType.findUniqueConcreteMethod(target); - if (concreteMethod != null) { - assumptions.record(concreteMethod); - return createTargetInvokeNode(intrinsicMethod, concreteMethod.getResult(), original, bci, returnType, arguments); - } - } - } else { - AssumptionResult concreteMethod = target.getDeclaringClass().findUniqueConcreteMethod(target); - if (concreteMethod != null) { - assumptions.record(concreteMethod); - return createTargetInvokeNode(intrinsicMethod, concreteMethod.getResult(), original, bci, returnType, arguments); - } - } - - return null; - } - - /** - * Inserts a node to cast the argument at index to the given type if the given type is more - * concrete than the argument type. - * - * @param index of the argument to be cast - * @param type the type the argument should be cast to - */ - private static void maybeCastArgument(ValueNode[] arguments, int index, JavaType type) { - if (type instanceof ResolvedJavaType) { - ResolvedJavaType targetType = (ResolvedJavaType) type; - if (!targetType.isPrimitive()) { - ValueNode argument = arguments[index]; - ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp()); - if (argumentType == null || (argumentType.isAssignableFrom(targetType) && !argumentType.equals(targetType))) { - PiNode piNode = new PiNode(argument, StampFactory.declared(targetType)); - arguments[index] = piNode; - } - } - } - } - - /** - * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed - * to the InvokeNode is in fact a {@link ResolvedMethodHandleCallTargetNode}. - * - * @return invoke node for the member name target - */ - private static InvokeNode createTargetInvokeNode(IntrinsicMethod intrinsicMethod, ResolvedJavaMethod target, ResolvedJavaMethod original, int bci, JavaType returnType, ValueNode[] arguments) { - InvokeKind targetInvokeKind = target.isStatic() ? InvokeKind.Static : InvokeKind.Special; - JavaType targetReturnType = target.getSignature().getReturnType(null); - - // MethodHandleLinkTo* nodes have a trailing MemberName argument which - // needs to be popped. - ValueNode[] targetArguments; - switch (intrinsicMethod) { - case INVOKE_BASIC: - targetArguments = arguments; - break; - case LINK_TO_STATIC: - case LINK_TO_SPECIAL: - case LINK_TO_VIRTUAL: - case LINK_TO_INTERFACE: - targetArguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1); - break; - default: - throw GraalInternalError.shouldNotReachHere(); - } - - MethodCallTargetNode callTarget = ResolvedMethodHandleCallTargetNode.create(targetInvokeKind, target, targetArguments, targetReturnType, original, arguments, returnType); - - // The call target can have a different return type than the invoker, - // e.g. the target returns an Object but the invoker void. In this case - // we need to use the stamp of the invoker. Note: always using the - // invoker's stamp would be wrong because it's a less concrete type - // (usually java.lang.Object). - if (returnType.getKind() == Kind.Void) { - return new InvokeNode(callTarget, bci, StampFactory.forVoid()); - } else { - return new InvokeNode(callTarget, bci); - } - } -} diff -r f9883cab4580 -r 331da25c2dda graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/MethodHandleInvocationPlugin.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/MethodHandleInvocationPlugin.java Thu May 14 16:26:31 2015 -0700 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.replacements; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.meta.MethodHandleAccessProvider.IntrinsicMethod; +import com.oracle.graal.graph.*; +import com.oracle.graal.graphbuilderconf.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.CallTargetNode.InvokeKind; +import com.oracle.graal.replacements.nodes.*; + +public class MethodHandleInvocationPlugin implements GenericInvocationPlugin { + private final MethodHandleAccessProvider methodHandleAccess; + private final GenericInvocationPlugin delegate; + + public MethodHandleInvocationPlugin(MethodHandleAccessProvider methodHandleAccess, GenericInvocationPlugin delegate) { + this.methodHandleAccess = methodHandleAccess; + this.delegate = delegate; + } + + @Override + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { + IntrinsicMethod intrinsicMethod = methodHandleAccess.lookupMethodHandleIntrinsic(method); + if (intrinsicMethod != null) { + InvokeKind invokeKind = b.getInvokeKind(); + if (invokeKind != InvokeKind.Static) { + args[0] = b.nullCheckedValue(args[0]); + } + JavaType invokeReturnType = b.getInvokeReturnType(); + InvokeNode invoke = MethodHandleNode.tryResolveTargetInvoke(b.getAssumptions(), b.getConstantReflection().getMethodHandleAccess(), intrinsicMethod, method, b.bci(), invokeReturnType, args); + if (invoke == null) { + MethodHandleNode methodHandleNode = new MethodHandleNode(intrinsicMethod, invokeKind, method, b.bci(), invokeReturnType, args); + if (invokeReturnType.getKind() == Kind.Void) { + b.add(methodHandleNode); + } else { + b.addPush(methodHandleNode); + } + } else { + CallTargetNode callTarget = invoke.callTarget(); + NodeInputList argumentsList = callTarget.arguments(); + ValueNode[] newArgs = argumentsList.toArray(new ValueNode[argumentsList.size()]); + for (ValueNode arg : newArgs) { + b.recursiveAppend(arg); + } + b.handleReplacedInvoke(invoke.getInvokeKind(), callTarget.targetMethod(), newArgs); + } + return true; + } + return delegate.apply(b, method, args); + } +} diff -r f9883cab4580 -r 331da25c2dda graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MethodHandleNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MethodHandleNode.java Thu May 14 16:26:31 2015 -0700 @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.replacements.nodes; + +import java.lang.invoke.*; +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.meta.Assumptions.AssumptionResult; +import com.oracle.graal.api.meta.MethodHandleAccessProvider.IntrinsicMethod; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.spi.*; +import com.oracle.graal.nodeinfo.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.CallTargetNode.InvokeKind; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; + +/** + * Node for invocation methods defined on the class {@link MethodHandle}. + */ +@NodeInfo +public final class MethodHandleNode extends MacroStateSplitNode implements Simplifiable { + public static final NodeClass TYPE = NodeClass.create(MethodHandleNode.class); + + protected final IntrinsicMethod intrinsicMethod; + + public MethodHandleNode(IntrinsicMethod intrinsicMethod, InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, JavaType returnType, ValueNode... arguments) { + super(TYPE, invokeKind, targetMethod, bci, returnType, arguments); + this.intrinsicMethod = intrinsicMethod; + } + + /** + * Attempts to transform application of an intrinsifiable {@link MethodHandle} method into an + * invocation on another method with possibly transformed arguments. + * + * @param assumptions object for recording any speculations made during the transformation + * @param methodHandleAccess objects for accessing the implementation internals of a + * {@link MethodHandle} + * @param intrinsicMethod denotes the intrinsifiable {@link MethodHandle} method being processed + * @param bci the BCI of the original {@link MethodHandle} call + * @param returnType return type of the original {@link MethodHandle} call + * @param arguments arguments to the original {@link MethodHandle} call + * @return a more direct invocation derived from the {@link MethodHandle} call or null + */ + public static InvokeNode tryResolveTargetInvoke(Assumptions assumptions, MethodHandleAccessProvider methodHandleAccess, IntrinsicMethod intrinsicMethod, ResolvedJavaMethod original, int bci, + JavaType returnType, ValueNode... arguments) { + switch (intrinsicMethod) { + case INVOKE_BASIC: + return getInvokeBasicTarget(assumptions, intrinsicMethod, methodHandleAccess, original, bci, returnType, arguments); + case LINK_TO_STATIC: + case LINK_TO_SPECIAL: + case LINK_TO_VIRTUAL: + case LINK_TO_INTERFACE: + return getLinkToTarget(assumptions, intrinsicMethod, methodHandleAccess, original, bci, returnType, arguments); + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + + @Override + public void simplify(SimplifierTool tool) { + MethodHandleAccessProvider methodHandleAccess = tool.getConstantReflection().getMethodHandleAccess(); + ValueNode[] argumentsArray = arguments.toArray(new ValueNode[arguments.size()]); + InvokeNode invoke = tryResolveTargetInvoke(graph().getAssumptions(), methodHandleAccess, intrinsicMethod, targetMethod, bci, returnType, argumentsArray); + if (invoke != null) { + assert invoke.graph() == null; + invoke = graph().addOrUniqueWithInputs(invoke); + invoke.setStateAfter(stateAfter()); + FixedNode currentNext = next(); + replaceAtUsages(invoke); + GraphUtil.removeFixedWithUnusedInputs(this); + graph().addBeforeFixed(currentNext, invoke); + } + } + + /** + * Get the receiver of a MethodHandle.invokeBasic call. + * + * @return the receiver argument node + */ + private static ValueNode getReceiver(ValueNode[] arguments) { + return arguments[0]; + } + + /** + * Get the MemberName argument of a MethodHandle.linkTo* call. + * + * @return the MemberName argument node (which is the last argument) + */ + private static ValueNode getMemberName(ValueNode[] arguments) { + return arguments[arguments.length - 1]; + } + + /** + * Used for the MethodHandle.invokeBasic method (the {@link IntrinsicMethod#INVOKE_BASIC } + * method) to get the target {@link InvokeNode} if the method handle receiver is constant. + * + * @return invoke node for the {@link java.lang.invoke.MethodHandle} target + */ + private static InvokeNode getInvokeBasicTarget(Assumptions assumptions, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess, ResolvedJavaMethod original, int bci, + JavaType returnType, ValueNode[] arguments) { + ValueNode methodHandleNode = getReceiver(arguments); + if (methodHandleNode.isConstant()) { + return getTargetInvokeNode(assumptions, intrinsicMethod, bci, returnType, arguments, methodHandleAccess.resolveInvokeBasicTarget(methodHandleNode.asJavaConstant(), true), original); + } + return null; + } + + /** + * Used for the MethodHandle.linkTo* methods (the {@link IntrinsicMethod#LINK_TO_STATIC}, + * {@link IntrinsicMethod#LINK_TO_SPECIAL}, {@link IntrinsicMethod#LINK_TO_VIRTUAL}, and + * {@link IntrinsicMethod#LINK_TO_INTERFACE} methods) to get the target {@link InvokeNode} if + * the member name argument is constant. + * + * @return invoke node for the member name target + */ + private static InvokeNode getLinkToTarget(Assumptions assumptions, IntrinsicMethod intrinsicMethod, MethodHandleAccessProvider methodHandleAccess, ResolvedJavaMethod original, int bci, + JavaType returnType, ValueNode[] arguments) { + ValueNode memberNameNode = getMemberName(arguments); + if (memberNameNode.isConstant()) { + return getTargetInvokeNode(assumptions, intrinsicMethod, bci, returnType, arguments, methodHandleAccess.resolveLinkToTarget(memberNameNode.asJavaConstant()), original); + } + return null; + } + + /** + * Helper function to get the {@link InvokeNode} for the targetMethod of a + * java.lang.invoke.MemberName. + * + * @param target the target, already loaded from the member name node + * @return invoke node for the member name target + */ + private static InvokeNode getTargetInvokeNode(Assumptions assumptions, IntrinsicMethod intrinsicMethod, int bci, JavaType returnType, ValueNode[] arguments, ResolvedJavaMethod target, + ResolvedJavaMethod original) { + if (target == null) { + return null; + } + + // In lambda forms we erase signature types to avoid resolving issues + // involving class loaders. When we optimize a method handle invoke + // to a direct call we must cast the receiver and arguments to its + // actual types. + Signature signature = target.getSignature(); + final boolean isStatic = target.isStatic(); + final int receiverSkip = isStatic ? 0 : 1; + + // Cast receiver to its type. + if (!isStatic) { + JavaType receiverType = target.getDeclaringClass(); + maybeCastArgument(arguments, 0, receiverType); + } + + // Cast reference arguments to its type. + for (int index = 0; index < signature.getParameterCount(false); index++) { + JavaType parameterType = signature.getParameterType(index, target.getDeclaringClass()); + maybeCastArgument(arguments, receiverSkip + index, parameterType); + } + + if (target.canBeStaticallyBound()) { + return createTargetInvokeNode(intrinsicMethod, target, original, bci, returnType, arguments); + } + + // Try to get the most accurate receiver type + if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) { + ValueNode receiver = getReceiver(arguments); + ResolvedJavaType receiverType = StampTool.typeOrNull(receiver.stamp()); + if (receiverType != null) { + AssumptionResult concreteMethod = receiverType.findUniqueConcreteMethod(target); + if (concreteMethod != null) { + assumptions.record(concreteMethod); + return createTargetInvokeNode(intrinsicMethod, concreteMethod.getResult(), original, bci, returnType, arguments); + } + } + } else { + AssumptionResult concreteMethod = target.getDeclaringClass().findUniqueConcreteMethod(target); + if (concreteMethod != null) { + assumptions.record(concreteMethod); + return createTargetInvokeNode(intrinsicMethod, concreteMethod.getResult(), original, bci, returnType, arguments); + } + } + + return null; + } + + /** + * Inserts a node to cast the argument at index to the given type if the given type is more + * concrete than the argument type. + * + * @param index of the argument to be cast + * @param type the type the argument should be cast to + */ + private static void maybeCastArgument(ValueNode[] arguments, int index, JavaType type) { + if (type instanceof ResolvedJavaType) { + ResolvedJavaType targetType = (ResolvedJavaType) type; + if (!targetType.isPrimitive()) { + ValueNode argument = arguments[index]; + ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp()); + if (argumentType == null || (argumentType.isAssignableFrom(targetType) && !argumentType.equals(targetType))) { + PiNode piNode = new PiNode(argument, StampFactory.declared(targetType)); + arguments[index] = piNode; + } + } + } + } + + /** + * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed + * to the InvokeNode is in fact a {@link ResolvedMethodHandleCallTargetNode}. + * + * @return invoke node for the member name target + */ + private static InvokeNode createTargetInvokeNode(IntrinsicMethod intrinsicMethod, ResolvedJavaMethod target, ResolvedJavaMethod original, int bci, JavaType returnType, ValueNode[] arguments) { + InvokeKind targetInvokeKind = target.isStatic() ? InvokeKind.Static : InvokeKind.Special; + JavaType targetReturnType = target.getSignature().getReturnType(null); + + // MethodHandleLinkTo* nodes have a trailing MemberName argument which + // needs to be popped. + ValueNode[] targetArguments; + switch (intrinsicMethod) { + case INVOKE_BASIC: + targetArguments = arguments; + break; + case LINK_TO_STATIC: + case LINK_TO_SPECIAL: + case LINK_TO_VIRTUAL: + case LINK_TO_INTERFACE: + targetArguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + + MethodCallTargetNode callTarget = ResolvedMethodHandleCallTargetNode.create(targetInvokeKind, target, targetArguments, targetReturnType, original, arguments, returnType); + + // The call target can have a different return type than the invoker, + // e.g. the target returns an Object but the invoker void. In this case + // we need to use the stamp of the invoker. Note: always using the + // invoker's stamp would be wrong because it's a less concrete type + // (usually java.lang.Object). + if (returnType.getKind() == Kind.Void) { + return new InvokeNode(callTarget, bci, StampFactory.forVoid()); + } else { + return new InvokeNode(callTarget, bci); + } + } +} diff -r f9883cab4580 -r 331da25c2dda graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ResolvedMethodHandleCallTargetNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ResolvedMethodHandleCallTargetNode.java Thu May 14 16:26:31 2015 -0700 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.replacements.nodes; + +import static sun.misc.Version.*; + +import java.lang.invoke.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodeinfo.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.spi.*; + +/** + * A call target that replaces itself in the graph when being lowered by restoring the original + * {@link MethodHandle} invocation target. Prior to + * https://bugs.openjdk.java.net/browse/JDK-8072008, this is required for when a + * {@link MethodHandle} call is resolved to a constant target but the target was not inlined. In + * that case, the original invocation must be restored with all of its original arguments. Why? + * HotSpot linkage for {@link MethodHandle} intrinsics (see + * {@code MethodHandles::generate_method_handle_dispatch}) expects certain implicit arguments to be + * on the stack such as the MemberName suffix argument for a call to one of the MethodHandle.linkTo* + * methods. An {@linkplain MethodHandleNode#tryResolveTargetInvoke resolved} {@link MethodHandle} + * invocation drops these arguments which means the interpreter won't find them. + */ +@NodeInfo +public final class ResolvedMethodHandleCallTargetNode extends MethodCallTargetNode implements Lowerable { + + public static final NodeClass TYPE = NodeClass.create(ResolvedMethodHandleCallTargetNode.class); + + /** + * Creates a call target for an invocation on a direct target derived by resolving a constant + * {@link MethodHandle}. + */ + public static MethodCallTargetNode create(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod, + ValueNode[] originalArguments, JavaType originalReturnType) { + if (jdkMajorVersion() >= 1 && jdkMinorVersion() >= 8 && jdkMicroVersion() >= 0 && jdkUpdateVersion() >= 60) { + // https://bugs.openjdk.java.net/browse/JDK-8072008 is targeted for 8u60 + return new MethodCallTargetNode(invokeKind, targetMethod, arguments, returnType); + } + return new ResolvedMethodHandleCallTargetNode(invokeKind, targetMethod, arguments, returnType, originalTargetMethod, originalArguments, originalReturnType); + } + + protected final ResolvedJavaMethod originalTargetMethod; + protected final JavaType originalReturnType; + @Input NodeInputList originalArguments; + + protected ResolvedMethodHandleCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod originalTargetMethod, + ValueNode[] originalArguments, JavaType originalReturnType) { + super(TYPE, invokeKind, targetMethod, arguments, returnType); + this.originalTargetMethod = originalTargetMethod; + this.originalReturnType = originalReturnType; + this.originalArguments = new NodeInputList<>(this, originalArguments); + } + + @Override + public void lower(LoweringTool tool) { + InvokeKind replacementInvokeKind = originalTargetMethod.isStatic() ? InvokeKind.Static : InvokeKind.Special; + MethodCallTargetNode replacement = graph().add( + new MethodCallTargetNode(replacementInvokeKind, originalTargetMethod, originalArguments.toArray(new ValueNode[originalArguments.size()]), originalReturnType)); + + // Replace myself... + this.replaceAndDelete(replacement); + } + + @Override + public void generate(NodeLIRBuilderTool gen) { + throw GraalInternalError.shouldNotReachHere("should have replaced itself"); + } +}