# HG changeset patch # User Christian Wimmer # Date 1399053891 25200 # Node ID 09d721bcffe21c4983383ccdc2bee9a24e63c656 # Parent 1a7ebcf3ae22bb040f35e0c28636843629a6b64b Introduce API for lookup of VM-internals of method handles diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.api.replacements/src/com/oracle/graal/api/replacements/MethodHandleAccessProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.api.replacements/src/com/oracle/graal/api/replacements/MethodHandleAccessProvider.java Fri May 02 11:04:51 2014 -0700 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014, 2014, 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.api.replacements; + +import java.lang.invoke.*; + +import com.oracle.graal.api.meta.*; + +/** + * Interface to access the internals of the {@link MethodHandle} implementation of the VM. An + * implementation of this interface is usually required to access non-public classes, methods, and + * fields of {@link MethodHandle}, i.e., data that is not standardized by the Java specification. + */ +public interface MethodHandleAccessProvider { + + /** + * Identification for methods defined on the class {@link MethodHandle} that are processed by + * the {@link MethodHandleAccessProvider}. + */ + public enum IntrinsicMethod { + /** The method {@code MethodHandle.invokeBasic}. */ + INVOKE_BASIC, + /** The method {@code MethodHandle.linkToStatic}. */ + LINK_TO_STATIC, + /** The method {@code MethodHandle.linkToSpecial}. */ + LINK_TO_SPECIAL, + /** The method {@code MethodHandle.linkToVirtual}. */ + LINK_TO_VIRTUAL, + /** The method {@code MethodHandle.linkToInterface}. */ + LINK_TO_INTERFACE + } + + /** + * Returns the method handle method intrinsic identifier for the provided method, or + * {@code null} if the method is not an intrinsic processed by this interface. + */ + IntrinsicMethod lookupMethodHandleIntrinsic(ResolvedJavaMethod method); + + /** + * Resolves the invocation target for an invocation of {@link IntrinsicMethod#INVOKE_BASIC + * MethodHandle.invokeBasic} with the given constant receiver {@link MethodHandle}. Returns + * {@code null} if the invocation target is not available at this time. + *

+ * The first invocations of a method handle can use an interpreter to lookup the actual invoked + * method; frequently executed method handles can use Java bytecode generation to avoid the + * interpreter overhead. If the parameter forceBytecodeGeneration is set to true, the VM should + * try to generate bytecodes before this method returns. + */ + ResolvedJavaMethod resolveInvokeBasicTarget(Constant methodHandle, boolean forceBytecodeGeneration); + + /** + * Resolves the invocation target for an invocation of a {@code MethodHandle.linkTo*} method + * with the given constant member name. The member name is the last parameter of the + * {@code linkTo*} method. Returns {@code null} if the invocation target is not available at + * this time. + */ + ResolvedJavaMethod resolveLinkToTarget(Constant memberName); +} diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Fri May 02 17:14:09 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Fri May 02 11:04:51 2014 -0700 @@ -113,7 +113,9 @@ Replacements replacements = createReplacements(runtime, assumptions, p, snippetReflection); HotSpotDisassemblerProvider disassembler = createDisassembler(runtime); HotSpotSuitesProvider suites = createSuites(runtime); - HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection); + HotSpotMethodHandleAccessProvider methodHandleAccess = new HotSpotMethodHandleAccessProvider(); + HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection, + methodHandleAccess); return createBackend(runtime, providers); } @@ -189,15 +191,15 @@ } else { /* * System V Application Binary Interface, AMD64 Architecture Processor Supplement - * + * * Draft Version 0.96 - * + * * http://www.uclibc.org/docs/psABI-x86_64.pdf - * + * * 3.2.1 - * + * * ... - * + * * This subsection discusses usage of each register. Registers %rbp, %rbx and %r12 * through %r15 "belong" to the calling function and the called function is required to * preserve their values. In other words, a called function must preserve these diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackendFactory.java Fri May 02 17:14:09 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackendFactory.java Fri May 02 11:04:51 2014 -0700 @@ -57,7 +57,8 @@ Replacements replacements = new HSAILHotSpotReplacementsImpl(p, host.getSnippetReflection(), assumptions, codeCache.getTarget(), host.getReplacements()); HotSpotDisassemblerProvider disassembler = host.getDisassembler(); SuitesProvider suites = new HotSpotSuitesProvider(runtime); - HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection()); + HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection(), + host.getMethodHandleAccess()); // pass registers info down to ReplacementsUtil (maybe a better way to do this?) HSAILHotSpotReplacementsUtil.initialize(providers.getRegisters()); diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackendFactory.java Fri May 02 17:14:09 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackendFactory.java Fri May 02 11:04:51 2014 -0700 @@ -47,7 +47,8 @@ HotSpotDisassemblerProvider disassembler = host.getDisassembler(); SuitesProvider suites = new DefaultSuitesProvider(); HotSpotRegistersProvider registers = new HotSpotRegisters(PTX.tid, Register.None, Register.None); - HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection()); + HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, host.getSnippetReflection(), + host.getMethodHandleAccess()); return new PTXHotSpotBackend(runtime, providers); } diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java Fri May 02 17:14:09 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackendFactory.java Fri May 02 11:04:51 2014 -0700 @@ -64,7 +64,9 @@ HotSpotReplacementsImpl replacements = new HotSpotReplacementsImpl(p, snippetReflection, runtime.getConfig(), assumptions, target); HotSpotDisassemblerProvider disassembler = new HotSpotDisassemblerProvider(runtime); HotSpotSuitesProvider suites = new HotSpotSuitesProvider(runtime); - HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection); + HotSpotMethodHandleAccessProvider methodHandleAccess = new HotSpotMethodHandleAccessProvider(); + HotSpotProviders providers = new HotSpotProviders(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements, disassembler, suites, registers, snippetReflection, + methodHandleAccess); return new SPARCHotSpotBackend(runtime, providers); } diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Fri May 02 17:14:09 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Fri May 02 11:04:51 2014 -0700 @@ -368,6 +368,8 @@ return (T) this; } else if (clazz == SnippetReflectionProvider.class) { return (T) getHostProviders().getSnippetReflection(); + } else if (clazz == MethodHandleAccessProvider.class) { + return (T) getHostProviders().getMethodHandleAccess(); } return null; } diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java Fri May 02 17:14:09 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotReplacementsImpl.java Fri May 02 11:04:51 2014 -0700 @@ -86,16 +86,14 @@ HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method; int intrinsicId = hsMethod.intrinsicId(); if (intrinsicId != 0) { - if (intrinsicId == config.vmIntrinsicInvokeBasic) { - return MethodHandleInvokeBasicNode.class; - } else if (intrinsicId == config.vmIntrinsicLinkToInterface) { - return MethodHandleLinkToInterfaceNode.class; - } else if (intrinsicId == config.vmIntrinsicLinkToSpecial) { - return MethodHandleLinkToSpecialNode.class; - } else if (intrinsicId == config.vmIntrinsicLinkToStatic) { - return MethodHandleLinkToStaticNode.class; - } else if (intrinsicId == config.vmIntrinsicLinkToVirtual) { - return MethodHandleLinkToVirtualNode.class; + /* + * 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. + * Therefore, we cannot use the usual annotation-driven mechanism to define the + * substitution. + */ + if (MethodHandleNode.lookupMethodHandleIntrinsic(method) != null) { + return MethodHandleNode.class; } } return super.getMacroSubstitution(method); diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodHandleAccessProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodHandleAccessProvider.java Fri May 02 11:04:51 2014 -0700 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014, 2014, 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.meta; + +import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.replacements.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.hotspot.*; + +public class HotSpotMethodHandleAccessProvider implements MethodHandleAccessProvider { + + /** + * Lazy initialization to break class initialization cycle. Field and method lookup is only + * possible after the {@link HotSpotGraalRuntime} is fully initialized. + */ + static class LazyInitialization { + static final ResolvedJavaField methodHandleFormField; + static final ResolvedJavaField lambdaFormVmentryField; + static final ResolvedJavaMethod lambdaFormCompileToBytecodeMethod; + static final ResolvedJavaField memberNameVmtargetField; + + /** + * Search for an instance field with the given name in a class. + * + * @param className name of the class to search in + * @param fieldName name of the field to be searched + * @return resolved java field + * @throws ClassNotFoundException + */ + private static ResolvedJavaField findFieldInClass(String className, String fieldName) throws ClassNotFoundException { + Class clazz = Class.forName(className); + ResolvedJavaType type = HotSpotResolvedObjectType.fromClass(clazz); + ResolvedJavaField[] fields = type.getInstanceFields(false); + for (ResolvedJavaField field : fields) { + if (field.getName().equals(fieldName)) { + return field; + } + } + return null; + } + + private static ResolvedJavaMethod findMethodInClass(String className, String methodName) throws ClassNotFoundException { + Class clazz = Class.forName(className); + ResolvedJavaType type = HotSpotResolvedObjectType.fromClass(clazz); + ResolvedJavaMethod result = null; + for (ResolvedJavaMethod method : type.getDeclaredMethods()) { + if (method.getName().equals(methodName)) { + assert result == null : "more than one method found: " + className + "." + methodName; + result = method; + } + } + assert result != null : "method not found: " + className + "." + methodName; + return result; + } + + static { + try { + methodHandleFormField = findFieldInClass("java.lang.invoke.MethodHandle", "form"); + lambdaFormVmentryField = findFieldInClass("java.lang.invoke.LambdaForm", "vmentry"); + lambdaFormCompileToBytecodeMethod = findMethodInClass("java.lang.invoke.LambdaForm", "compileToBytecode"); + memberNameVmtargetField = findFieldInClass("java.lang.invoke.MemberName", "vmtarget"); + } catch (Throwable ex) { + throw GraalInternalError.shouldNotReachHere(); + } + } + } + + @Override + public IntrinsicMethod lookupMethodHandleIntrinsic(ResolvedJavaMethod method) { + int intrinsicId = ((HotSpotResolvedJavaMethod) method).intrinsicId(); + if (intrinsicId != 0) { + HotSpotVMConfig config = runtime().getConfig(); + if (intrinsicId == config.vmIntrinsicInvokeBasic) { + return IntrinsicMethod.INVOKE_BASIC; + } else if (intrinsicId == config.vmIntrinsicLinkToInterface) { + return IntrinsicMethod.LINK_TO_INTERFACE; + } else if (intrinsicId == config.vmIntrinsicLinkToSpecial) { + return IntrinsicMethod.LINK_TO_SPECIAL; + } else if (intrinsicId == config.vmIntrinsicLinkToStatic) { + return IntrinsicMethod.LINK_TO_STATIC; + } else if (intrinsicId == config.vmIntrinsicLinkToVirtual) { + return IntrinsicMethod.LINK_TO_VIRTUAL; + } + } + return null; + } + + @Override + public ResolvedJavaMethod resolveInvokeBasicTarget(Constant methodHandle, boolean forceBytecodeGeneration) { + if (methodHandle.isNull()) { + return null; + } + + /* Load non-public field: LambdaForm MethodHandle.form */ + Constant lambdaForm = LazyInitialization.methodHandleFormField.readValue(methodHandle); + if (lambdaForm.isNull()) { + return null; + } + + Constant memberName; + if (forceBytecodeGeneration) { + /* Invoke non-public method: MemberName LambdaForm.compileToBytecode() */ + memberName = LazyInitialization.lambdaFormCompileToBytecodeMethod.invoke(lambdaForm, new Constant[0]); + } else { + /* Load non-public field: MemberName LambdaForm.vmentry */ + memberName = LazyInitialization.lambdaFormVmentryField.readValue(lambdaForm); + } + return getTargetMethod(memberName); + } + + @Override + public ResolvedJavaMethod resolveLinkToTarget(Constant memberName) { + return getTargetMethod(memberName); + } + + /** + * Returns the {@link ResolvedJavaMethod} for the vmtarget of a java.lang.invoke.MemberName. + */ + private static ResolvedJavaMethod getTargetMethod(Constant memberName) { + if (memberName.isNull()) { + return null; + } + + /* Load injected field: JVM_Method* MemberName.vmtarget */ + Constant vmtarget = LazyInitialization.memberNameVmtargetField.readValue(memberName); + /* Create a method from the vmtarget method pointer. */ + return HotSpotResolvedJavaMethod.fromMetaspace(vmtarget.asLong()); + } +} diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java Fri May 02 17:14:09 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotProviders.java Fri May 02 11:04:51 2014 -0700 @@ -38,15 +38,17 @@ private final SuitesProvider suites; private final HotSpotRegistersProvider registers; private final SnippetReflectionProvider snippetReflection; + private final HotSpotMethodHandleAccessProvider methodHandleAccess; public HotSpotProviders(HotSpotMetaAccessProvider metaAccess, HotSpotCodeCacheProvider codeCache, ConstantReflectionProvider constantReflection, HotSpotForeignCallsProvider foreignCalls, LoweringProvider lowerer, Replacements replacements, HotSpotDisassemblerProvider disassembler, SuitesProvider suites, HotSpotRegistersProvider registers, - SnippetReflectionProvider snippetReflection) { + SnippetReflectionProvider snippetReflection, HotSpotMethodHandleAccessProvider methodHandleAccess) { super(metaAccess, codeCache, constantReflection, foreignCalls, lowerer, replacements); this.disassembler = disassembler; this.suites = suites; this.registers = registers; this.snippetReflection = snippetReflection; + this.methodHandleAccess = methodHandleAccess; } @Override @@ -79,4 +81,8 @@ public SnippetReflectionProvider getSnippetReflection() { return snippetReflection; } + + public HotSpotMethodHandleAccessProvider getMethodHandleAccess() { + return methodHandleAccess; + } } diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AbstractMethodHandleNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AbstractMethodHandleNode.java Fri May 02 17:14:09 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,283 +0,0 @@ -/* - * Copyright (c) 2013, 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.util.*; - -import com.oracle.graal.api.meta.*; -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.meta.*; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.replacements.nodes.*; - -/** - * Common base class for method handle invoke nodes. - */ -public abstract class AbstractMethodHandleNode extends MacroNode implements Canonicalizable { - - private static final ResolvedJavaField methodHandleFormField; - private static final ResolvedJavaField lambdaFormVmentryField; - private static final ResolvedJavaField memberNameClazzField; - private static final ResolvedJavaField memberNameVmtargetField; - - // Replacement method data - private ResolvedJavaMethod replacementTargetMethod; - private JavaType replacementReturnType; - @Input private final NodeInputList replacementArguments; - - /** - * Search for an instance field with the given name in a class. - * - * @param className name of the class to search in - * @param fieldName name of the field to be searched - * @return resolved java field - * @throws ClassNotFoundException - */ - private static ResolvedJavaField findFieldInClass(String className, String fieldName) throws ClassNotFoundException { - Class clazz = Class.forName(className); - ResolvedJavaType type = HotSpotResolvedObjectType.fromClass(clazz); - ResolvedJavaField[] fields = type.getInstanceFields(false); - for (ResolvedJavaField field : fields) { - if (field.getName().equals(fieldName)) { - return field; - } - } - return null; - } - - static { - try { - methodHandleFormField = findFieldInClass("java.lang.invoke.MethodHandle", "form"); - lambdaFormVmentryField = findFieldInClass("java.lang.invoke.LambdaForm", "vmentry"); - memberNameClazzField = findFieldInClass("java.lang.invoke.MemberName", "clazz"); - memberNameVmtargetField = findFieldInClass("java.lang.invoke.MemberName", "vmtarget"); - } catch (ClassNotFoundException | SecurityException ex) { - throw GraalInternalError.shouldNotReachHere(); - } - } - - public AbstractMethodHandleNode(Invoke invoke) { - super(invoke); - - // See if we need to save some replacement method data. - CallTargetNode callTarget = invoke.callTarget(); - if (callTarget instanceof SelfReplacingMethodCallTargetNode) { - SelfReplacingMethodCallTargetNode selfReplacingMethodCallTargetNode = (SelfReplacingMethodCallTargetNode) callTarget; - replacementTargetMethod = selfReplacingMethodCallTargetNode.replacementTargetMethod(); - replacementReturnType = selfReplacingMethodCallTargetNode.replacementReturnType(); - replacementArguments = selfReplacingMethodCallTargetNode.replacementArguments(); - } else { - // NodeInputList can't be null. - replacementArguments = new NodeInputList<>(this); - } - } - - /** - * Get the receiver of a MethodHandle.invokeBasic call. - * - * @return the receiver argument node - */ - private ValueNode getReceiver() { - return arguments.first(); - } - - /** - * Get the MemberName argument of a MethodHandle.linkTo* call. - * - * @return the MemberName argument node (which is the last argument) - */ - private ValueNode getMemberName() { - return arguments.last(); - } - - /** - * Used from {@link MethodHandleInvokeBasicNode} to get the target {@link InvokeNode} if the - * method handle receiver is constant. - * - * @return invoke node for the {@link java.lang.invoke.MethodHandle} target - */ - protected InvokeNode getInvokeBasicTarget() { - ValueNode methodHandleNode = getReceiver(); - if (methodHandleNode.isConstant() && !methodHandleNode.isNullConstant()) { - // Get the data we need from MethodHandle.LambdaForm.MemberName - Constant methodHandle = methodHandleNode.asConstant(); - Constant lambdaForm = methodHandleFormField.readValue(methodHandle); - Constant memberName = lambdaFormVmentryField.readValue(lambdaForm); - return getTargetInvokeNode(memberName); - } - return null; - } - - /** - * Used from {@link MethodHandleLinkToStaticNode}, {@link MethodHandleLinkToSpecialNode}, - * {@link MethodHandleLinkToVirtualNode}, and {@link MethodHandleLinkToInterfaceNode} to get the - * target {@link InvokeNode} if the member name argument is constant. - * - * @return invoke node for the member name target - */ - protected InvokeNode getLinkToTarget() { - ValueNode memberNameNode = getMemberName(); - if (memberNameNode.isConstant() && !memberNameNode.isNullConstant()) { - Constant memberName = memberNameNode.asConstant(); - return getTargetInvokeNode(memberName); - } - return null; - } - - /** - * Helper function to get the {@link InvokeNode} for the vmtarget of a - * java.lang.invoke.MemberName. - * - * @param memberName constant member name node - * @return invoke node for the member name target - */ - private InvokeNode getTargetInvokeNode(Constant memberName) { - // Get the data we need from MemberName - Constant clazz = memberNameClazzField.readValue(memberName); - Constant vmtarget = memberNameVmtargetField.readValue(memberName); - - // Create a method from the vmtarget pointer - Class c = (Class) HotSpotObjectConstant.asObject(clazz); - HotSpotResolvedObjectType holderClass = (HotSpotResolvedObjectType) HotSpotResolvedObjectType.fromClass(c); - HotSpotResolvedJavaMethod targetMethod = HotSpotResolvedJavaMethod.fromMetaspace(vmtarget.asLong()); - - // 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. - HotSpotSignature signature = targetMethod.getSignature(); - final boolean isStatic = targetMethod.isStatic(); - final int receiverSkip = isStatic ? 0 : 1; - - // Cast receiver to its type. - if (!isStatic) { - JavaType receiverType = holderClass; - maybeCastArgument(0, receiverType); - } - - // Cast reference arguments to its type. - for (int index = 0; index < signature.getParameterCount(false); index++) { - JavaType parameterType = signature.getParameterType(index, holderClass); - maybeCastArgument(receiverSkip + index, parameterType); - } - - // Try to get the most accurate receiver type - if (this instanceof MethodHandleLinkToVirtualNode || this instanceof MethodHandleLinkToInterfaceNode) { - ResolvedJavaType receiverType = StampTool.typeOrNull(getReceiver().stamp()); - if (receiverType != null) { - ResolvedJavaMethod concreteMethod = receiverType.findUniqueConcreteMethod(targetMethod); - if (concreteMethod != null) { - return createTargetInvokeNode(concreteMethod); - } - } - } - - if (targetMethod.canBeStaticallyBound()) { - return createTargetInvokeNode(targetMethod); - } - - ResolvedJavaMethod concreteMethod = targetMethod.uniqueConcreteMethod(); - if (concreteMethod != null) { - return createTargetInvokeNode(concreteMethod); - } - - 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 void maybeCastArgument(int index, JavaType type) { - if (type instanceof ResolvedJavaType) { - ResolvedJavaType targetType = (ResolvedJavaType) type; - if (!targetType.isPrimitive()) { - ValueNode argument = arguments.get(index); - ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp()); - if (argumentType == null || (argumentType.isAssignableFrom(targetType) && !argumentType.equals(targetType))) { - PiNode piNode = graph().unique(new PiNode(argument, StampFactory.declared(targetType))); - arguments.set(index, piNode); - } - } - } - } - - /** - * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed - * to the InvokeNode is in fact a {@link SelfReplacingMethodCallTargetNode}. - * - * @param targetMethod the method the be called - * @return invoke node for the member name target - */ - private InvokeNode createTargetInvokeNode(ResolvedJavaMethod targetMethod) { - InvokeKind invokeKind = targetMethod.isStatic() ? InvokeKind.Static : InvokeKind.Special; - JavaType returnType = targetMethod.getSignature().getReturnType(null); - - // MethodHandleLinkTo* nodes have a trailing MemberName argument which - // needs to be popped. - ValueNode[] originalArguments = arguments.toArray(new ValueNode[arguments.size()]); - ValueNode[] targetArguments; - if (this instanceof MethodHandleInvokeBasicNode) { - targetArguments = originalArguments; - } else { - assert this instanceof MethodHandleLinkToStaticNode || this instanceof MethodHandleLinkToSpecialNode || this instanceof MethodHandleLinkToVirtualNode || - this instanceof MethodHandleLinkToInterfaceNode : this; - targetArguments = Arrays.copyOfRange(originalArguments, 0, arguments.size() - 1); - } - - // If there is already replacement information, use that instead. - MethodCallTargetNode callTarget; - if (replacementTargetMethod == null) { - callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, getTargetMethod(), originalArguments, getReturnType()); - } else { - ValueNode[] args = replacementArguments.toArray(new ValueNode[replacementArguments.size()]); - callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, replacementTargetMethod, args, replacementReturnType); - } - graph().add(callTarget); - - // 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). - InvokeNode invoke; - if (stamp() == StampFactory.forVoid()) { - invoke = new InvokeNode(callTarget, getBci(), stamp()); - } else { - invoke = new InvokeNode(callTarget, getBci()); - } - graph().add(invoke); - invoke.setStateAfter(stateAfter()); - return invoke; - } - -} diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleInvokeBasicNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleInvokeBasicNode.java Fri May 02 17:14:09 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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 com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .invokeBasic(Object...)}. - */ -public class MethodHandleInvokeBasicNode extends AbstractMethodHandleNode { - - public MethodHandleInvokeBasicNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getInvokeBasicTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToInterfaceNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToInterfaceNode.java Fri May 02 17:14:09 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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 com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .linkToInterface(Object...)}. - */ -public class MethodHandleLinkToInterfaceNode extends AbstractMethodHandleNode { - - public MethodHandleLinkToInterfaceNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getLinkToTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToSpecialNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToSpecialNode.java Fri May 02 17:14:09 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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 com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .linkToSpecial(Object...)}. - */ -public class MethodHandleLinkToSpecialNode extends AbstractMethodHandleNode { - - public MethodHandleLinkToSpecialNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getLinkToTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToStaticNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToStaticNode.java Fri May 02 17:14:09 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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 com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .linkToStatic(Object...)}. - */ -public class MethodHandleLinkToStaticNode extends AbstractMethodHandleNode { - - public MethodHandleLinkToStaticNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getLinkToTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToVirtualNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToVirtualNode.java Fri May 02 17:14:09 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2013, 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 com.oracle.graal.graph.*; -import com.oracle.graal.graph.spi.*; -import com.oracle.graal.nodes.*; - -/** - * Macro node for {@link MethodHandle}{@code .linkToVirtual(Object...)}. - */ -public class MethodHandleLinkToVirtualNode extends AbstractMethodHandleNode { - - public MethodHandleLinkToVirtualNode(Invoke invoke) { - super(invoke); - } - - @Override - public Node canonical(CanonicalizerTool tool) { - InvokeNode invoke = getLinkToTarget(); - if (invoke != null) { - return invoke; - } - return this; - } - -} diff -r 1a7ebcf3ae22 -r 09d721bcffe2 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleNode.java Fri May 02 11:04:51 2014 -0700 @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2013, 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 static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; + +import java.lang.invoke.*; +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.replacements.*; +import com.oracle.graal.api.replacements.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.nodes.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.replacements.nodes.*; + +/** + * Node for invocation methods defined on the class {@link MethodHandle}. + */ +public class MethodHandleNode extends MacroNode implements Canonicalizable { + + /** The method that this node is representing. */ + private final IntrinsicMethod intrinsicMethod; + + // Replacement method data + private ResolvedJavaMethod replacementTargetMethod; + private JavaType replacementReturnType; + @Input private final NodeInputList replacementArguments; + + public MethodHandleNode(Invoke invoke) { + super(invoke); + + MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); + intrinsicMethod = lookupMethodHandleIntrinsic(callTarget.targetMethod()); + assert intrinsicMethod != null; + + // See if we need to save some replacement method data. + if (callTarget instanceof SelfReplacingMethodCallTargetNode) { + SelfReplacingMethodCallTargetNode selfReplacingMethodCallTargetNode = (SelfReplacingMethodCallTargetNode) callTarget; + replacementTargetMethod = selfReplacingMethodCallTargetNode.replacementTargetMethod(); + replacementReturnType = selfReplacingMethodCallTargetNode.replacementReturnType(); + replacementArguments = selfReplacingMethodCallTargetNode.replacementArguments(); + } else { + // NodeInputList can't be null. + replacementArguments = new NodeInputList<>(this); + } + } + + /** + * Returns the method handle method intrinsic identifier for the provided method, or + * {@code null} if the method is not a method that can be handled by this class. + */ + public static IntrinsicMethod lookupMethodHandleIntrinsic(ResolvedJavaMethod method) { + return methodHandleAccess().lookupMethodHandleIntrinsic(method); + } + + @Override + public Node canonical(CanonicalizerTool tool) { + InvokeNode invoke; + switch (intrinsicMethod) { + case INVOKE_BASIC: + invoke = getInvokeBasicTarget(); + break; + case LINK_TO_STATIC: + case LINK_TO_SPECIAL: + case LINK_TO_VIRTUAL: + case LINK_TO_INTERFACE: + invoke = getLinkToTarget(); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + if (invoke != null) { + return invoke; + } + return this; + } + + /** + * Get the receiver of a MethodHandle.invokeBasic call. + * + * @return the receiver argument node + */ + private ValueNode getReceiver() { + return arguments.first(); + } + + /** + * Get the MemberName argument of a MethodHandle.linkTo* call. + * + * @return the MemberName argument node (which is the last argument) + */ + private ValueNode getMemberName() { + return arguments.last(); + } + + /** + * Returns the {@link MethodHandleAccessProvider} that provides introspection of internal + * {@link MethodHandle} data. + */ + private static MethodHandleAccessProvider methodHandleAccess() { + return runtime().getHostProviders().getMethodHandleAccess(); + } + + /** + * 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 + */ + protected InvokeNode getInvokeBasicTarget() { + ValueNode methodHandleNode = getReceiver(); + if (methodHandleNode.isConstant()) { + return getTargetInvokeNode(methodHandleAccess().resolveInvokeBasicTarget(methodHandleNode.asConstant(), false)); + } + 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 + */ + protected InvokeNode getLinkToTarget() { + ValueNode memberNameNode = getMemberName(); + if (memberNameNode.isConstant()) { + return getTargetInvokeNode(methodHandleAccess().resolveLinkToTarget(memberNameNode.asConstant())); + } + return null; + } + + /** + * Helper function to get the {@link InvokeNode} for the targetMethod of a + * java.lang.invoke.MemberName. + * + * @param targetMethod the target, already loaded from the member name node + * @return invoke node for the member name target + */ + private InvokeNode getTargetInvokeNode(ResolvedJavaMethod targetMethod) { + if (targetMethod == 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 = targetMethod.getSignature(); + final boolean isStatic = targetMethod.isStatic(); + final int receiverSkip = isStatic ? 0 : 1; + + // Cast receiver to its type. + if (!isStatic) { + JavaType receiverType = targetMethod.getDeclaringClass(); + maybeCastArgument(0, receiverType); + } + + // Cast reference arguments to its type. + for (int index = 0; index < signature.getParameterCount(false); index++) { + JavaType parameterType = signature.getParameterType(index, targetMethod.getDeclaringClass()); + maybeCastArgument(receiverSkip + index, parameterType); + } + + // Try to get the most accurate receiver type + if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) { + ResolvedJavaType receiverType = StampTool.typeOrNull(getReceiver().stamp()); + if (receiverType != null) { + ResolvedJavaMethod concreteMethod = receiverType.findUniqueConcreteMethod(targetMethod); + if (concreteMethod != null) { + return createTargetInvokeNode(concreteMethod); + } + } + } + + if (targetMethod.canBeStaticallyBound()) { + return createTargetInvokeNode(targetMethod); + } + + ResolvedJavaMethod concreteMethod = targetMethod.getDeclaringClass().findUniqueConcreteMethod(targetMethod); + if (concreteMethod != null) { + return createTargetInvokeNode(concreteMethod); + } + + 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 void maybeCastArgument(int index, JavaType type) { + if (type instanceof ResolvedJavaType) { + ResolvedJavaType targetType = (ResolvedJavaType) type; + if (!targetType.isPrimitive()) { + ValueNode argument = arguments.get(index); + ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp()); + if (argumentType == null || (argumentType.isAssignableFrom(targetType) && !argumentType.equals(targetType))) { + PiNode piNode = graph().unique(new PiNode(argument, StampFactory.declared(targetType))); + arguments.set(index, piNode); + } + } + } + } + + /** + * Creates an {@link InvokeNode} for the given target method. The {@link CallTargetNode} passed + * to the InvokeNode is in fact a {@link SelfReplacingMethodCallTargetNode}. + * + * @param targetMethod the method the be called + * @return invoke node for the member name target + */ + private InvokeNode createTargetInvokeNode(ResolvedJavaMethod targetMethod) { + InvokeKind invokeKind = targetMethod.isStatic() ? InvokeKind.Static : InvokeKind.Special; + JavaType returnType = targetMethod.getSignature().getReturnType(null); + + // MethodHandleLinkTo* nodes have a trailing MemberName argument which + // needs to be popped. + ValueNode[] originalArguments = arguments.toArray(new ValueNode[arguments.size()]); + ValueNode[] targetArguments; + switch (intrinsicMethod) { + case INVOKE_BASIC: + targetArguments = originalArguments; + break; + case LINK_TO_STATIC: + case LINK_TO_SPECIAL: + case LINK_TO_VIRTUAL: + case LINK_TO_INTERFACE: + targetArguments = Arrays.copyOfRange(originalArguments, 0, arguments.size() - 1); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + + // If there is already replacement information, use that instead. + MethodCallTargetNode callTarget; + if (replacementTargetMethod == null) { + callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, getTargetMethod(), originalArguments, getReturnType()); + } else { + ValueNode[] args = replacementArguments.toArray(new ValueNode[replacementArguments.size()]); + callTarget = new SelfReplacingMethodCallTargetNode(invokeKind, targetMethod, targetArguments, returnType, replacementTargetMethod, args, replacementReturnType); + } + graph().add(callTarget); + + // 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). + InvokeNode invoke; + if (stamp() == StampFactory.forVoid()) { + invoke = new InvokeNode(callTarget, getBci(), stamp()); + } else { + invoke = new InvokeNode(callTarget, getBci()); + } + graph().add(invoke); + invoke.setStateAfter(stateAfter()); + return invoke; + } + +}