changeset 9691:bd5c6b3dedc5

implement inlining support for JSR 292
author twisti
date Tue, 14 May 2013 11:27:09 -0700
parents a7f10828c4ff
children b2ba1c6f9bf8
files graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AbstractMethodHandleNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleInvokeBasicNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToInterfaceNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToSpecialNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToStaticNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToVirtualNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/SelfReplacingMethodCallTargetNode.java
diffstat 8 files changed, 450 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Tue May 14 18:40:15 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Tue May 14 11:27:09 2013 -0700
@@ -358,6 +358,9 @@
 
     @Override
     public boolean canBeInlined() {
+        if (dontInline) {
+            return false;
+        }
         return graalRuntime().getCompilerToVM().isMethodCompilable(metaspaceMethod);
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/AbstractMethodHandleNode.java	Tue May 14 11:27:09 2013 -0700
@@ -0,0 +1,283 @@
+/*
+ * 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.reflect.Modifier;
+import java.util.Arrays;
+
+import com.oracle.graal.api.meta.Constant;
+import com.oracle.graal.api.meta.JavaType;
+import com.oracle.graal.api.meta.ResolvedJavaField;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.api.meta.ResolvedJavaType;
+import com.oracle.graal.graph.GraalInternalError;
+import com.oracle.graal.graph.NodeInputList;
+import com.oracle.graal.hotspot.meta.HotSpotResolvedJavaMethod;
+import com.oracle.graal.hotspot.meta.HotSpotResolvedObjectType;
+import com.oracle.graal.hotspot.meta.HotSpotSignature;
+import com.oracle.graal.nodes.CallTargetNode;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.InvokeNode;
+import com.oracle.graal.nodes.PiNode;
+import com.oracle.graal.nodes.ValueNode;
+import com.oracle.graal.nodes.java.MethodCallTargetNode;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.java.SelfReplacingMethodCallTargetNode;
+import com.oracle.graal.nodes.spi.Canonicalizable;
+import com.oracle.graal.nodes.type.StampFactory;
+import com.oracle.graal.replacements.nodes.MacroNode;
+
+/**
+ * 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 NodeInputList<ValueNode> 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<?>) clazz.asObject();
+        HotSpotResolvedObjectType holderClass = (HotSpotResolvedObjectType) HotSpotResolvedObjectType.fromClass(c);
+        HotSpotResolvedJavaMethod targetMethod = holderClass.createMethod(vmtarget.asLong());
+
+        // In lamda 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 = Modifier.isStatic(targetMethod.getModifiers());
+        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 = getReceiver().objectStamp().type();
+            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 = argument.objectStamp().type();
+                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 = Modifier.isStatic(targetMethod.getModifiers()) ? 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);
+        InvokeNode invoke = graph().add(new InvokeNode(callTarget, getBci()));
+        invoke.setStateAfter(stateAfter());
+        return invoke;
+    }
+
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleInvokeBasicNode.java	Tue May 14 18:40:15 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleInvokeBasicNode.java	Tue May 14 11:27:09 2013 -0700
@@ -22,17 +22,29 @@
  */
 package com.oracle.graal.hotspot.replacements;
 
-import java.lang.invoke.*;
+import java.lang.invoke.MethodHandle;
 
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.replacements.nodes.*;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.InvokeNode;
+import com.oracle.graal.nodes.ValueNode;
+import com.oracle.graal.nodes.spi.CanonicalizerTool;
 
 /**
  * Macro node for {@link MethodHandle}{@code .invokeBasic(Object...)}.
  */
-public class MethodHandleInvokeBasicNode extends MacroNode {
+public class MethodHandleInvokeBasicNode extends AbstractMethodHandleNode {
 
     public MethodHandleInvokeBasicNode(Invoke invoke) {
         super(invoke);
     }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool) {
+        InvokeNode invoke = getInvokeBasicTarget();
+        if (invoke != null) {
+            return invoke;
+        }
+        return this;
+    }
+
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToInterfaceNode.java	Tue May 14 18:40:15 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToInterfaceNode.java	Tue May 14 11:27:09 2013 -0700
@@ -22,17 +22,29 @@
  */
 package com.oracle.graal.hotspot.replacements;
 
-import java.lang.invoke.*;
+import java.lang.invoke.MethodHandle;
 
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.replacements.nodes.*;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.InvokeNode;
+import com.oracle.graal.nodes.ValueNode;
+import com.oracle.graal.nodes.spi.CanonicalizerTool;
 
 /**
  * Macro node for {@link MethodHandle}{@code .linkToInterface(Object...)}.
  */
-public class MethodHandleLinkToInterfaceNode extends MacroNode {
+public class MethodHandleLinkToInterfaceNode extends AbstractMethodHandleNode {
 
     public MethodHandleLinkToInterfaceNode(Invoke invoke) {
         super(invoke);
     }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool) {
+        InvokeNode invoke = getLinkToTarget();
+        if (invoke != null) {
+            return invoke;
+        }
+        return this;
+    }
+
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToSpecialNode.java	Tue May 14 18:40:15 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToSpecialNode.java	Tue May 14 11:27:09 2013 -0700
@@ -22,17 +22,29 @@
  */
 package com.oracle.graal.hotspot.replacements;
 
-import java.lang.invoke.*;
+import java.lang.invoke.MethodHandle;
 
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.replacements.nodes.*;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.InvokeNode;
+import com.oracle.graal.nodes.ValueNode;
+import com.oracle.graal.nodes.spi.CanonicalizerTool;
 
 /**
  * Macro node for {@link MethodHandle}{@code .linkToSpecial(Object...)}.
  */
-public class MethodHandleLinkToSpecialNode extends MacroNode {
+public class MethodHandleLinkToSpecialNode extends AbstractMethodHandleNode {
 
     public MethodHandleLinkToSpecialNode(Invoke invoke) {
         super(invoke);
     }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool) {
+        InvokeNode invoke = getLinkToTarget();
+        if (invoke != null) {
+            return invoke;
+        }
+        return this;
+    }
+
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToStaticNode.java	Tue May 14 18:40:15 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToStaticNode.java	Tue May 14 11:27:09 2013 -0700
@@ -22,17 +22,29 @@
  */
 package com.oracle.graal.hotspot.replacements;
 
-import java.lang.invoke.*;
+import java.lang.invoke.MethodHandle;
 
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.replacements.nodes.*;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.InvokeNode;
+import com.oracle.graal.nodes.ValueNode;
+import com.oracle.graal.nodes.spi.CanonicalizerTool;
 
 /**
  * Macro node for {@link MethodHandle}{@code .linkToStatic(Object...)}.
  */
-public class MethodHandleLinkToStaticNode extends MacroNode {
+public class MethodHandleLinkToStaticNode extends AbstractMethodHandleNode {
 
     public MethodHandleLinkToStaticNode(Invoke invoke) {
         super(invoke);
     }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool) {
+        InvokeNode invoke = getLinkToTarget();
+        if (invoke != null) {
+            return invoke;
+        }
+        return this;
+    }
+
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToVirtualNode.java	Tue May 14 18:40:15 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MethodHandleLinkToVirtualNode.java	Tue May 14 11:27:09 2013 -0700
@@ -22,17 +22,29 @@
  */
 package com.oracle.graal.hotspot.replacements;
 
-import java.lang.invoke.*;
+import java.lang.invoke.MethodHandle;
 
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.replacements.nodes.*;
+import com.oracle.graal.nodes.Invoke;
+import com.oracle.graal.nodes.InvokeNode;
+import com.oracle.graal.nodes.ValueNode;
+import com.oracle.graal.nodes.spi.CanonicalizerTool;
 
 /**
  * Macro node for {@link MethodHandle}{@code .linkToVirtual(Object...)}.
  */
-public class MethodHandleLinkToVirtualNode extends MacroNode {
+public class MethodHandleLinkToVirtualNode extends AbstractMethodHandleNode {
 
     public MethodHandleLinkToVirtualNode(Invoke invoke) {
         super(invoke);
     }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool) {
+        InvokeNode invoke = getLinkToTarget();
+        if (invoke != null) {
+            return invoke;
+        }
+        return this;
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/SelfReplacingMethodCallTargetNode.java	Tue May 14 11:27:09 2013 -0700
@@ -0,0 +1,84 @@
+/*
+ * 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.nodes.java;
+
+import java.lang.reflect.Modifier;
+
+import com.oracle.graal.api.meta.JavaType;
+import com.oracle.graal.api.meta.ResolvedJavaMethod;
+import com.oracle.graal.graph.GraalInternalError;
+import com.oracle.graal.graph.NodeInputList;
+import com.oracle.graal.nodes.ValueNode;
+import com.oracle.graal.nodes.spi.LIRGeneratorTool;
+import com.oracle.graal.nodes.spi.Lowerable;
+import com.oracle.graal.nodes.spi.LoweringTool;
+
+/**
+ * A SelfReplacingMethodCallTargetNode replaces itself in the graph when being lowered with a
+ * {@link MethodCallTargetNode} that calls the stored replacement target method.
+ * 
+ * This node is used for method handle call nodes which have a constant call target but are not
+ * inlined.
+ */
+public class SelfReplacingMethodCallTargetNode extends MethodCallTargetNode implements Lowerable {
+
+    // Replacement method data
+    private final ResolvedJavaMethod replacementTargetMethod;
+    private final JavaType replacementReturnType;
+    @Input private final NodeInputList<ValueNode> replacementArguments;
+
+    public SelfReplacingMethodCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] arguments, JavaType returnType, ResolvedJavaMethod replacementTargetMethod,
+                    ValueNode[] replacementArguments, JavaType replacementReturnType) {
+        super(invokeKind, targetMethod, arguments, returnType);
+        this.replacementTargetMethod = replacementTargetMethod;
+        this.replacementReturnType = replacementReturnType;
+        this.replacementArguments = new NodeInputList<>(this, replacementArguments);
+    }
+
+    public ResolvedJavaMethod replacementTargetMethod() {
+        return replacementTargetMethod;
+    }
+
+    public JavaType replacementReturnType() {
+        return replacementReturnType;
+    }
+
+    public NodeInputList<ValueNode> replacementArguments() {
+        return replacementArguments;
+    }
+
+    @Override
+    public void lower(LoweringTool tool, LoweringType loweringType) {
+        InvokeKind invokeKind = Modifier.isStatic(replacementTargetMethod.getModifiers()) ? InvokeKind.Static : InvokeKind.Special;
+        MethodCallTargetNode replacement = graph().add(
+                        new MethodCallTargetNode(invokeKind, replacementTargetMethod, replacementArguments.toArray(new ValueNode[replacementArguments.size()]), replacementReturnType));
+
+        // Replace myself...
+        this.replaceAndDelete(replacement);
+    }
+
+    @Override
+    public void generate(LIRGeneratorTool gen) {
+        throw GraalInternalError.shouldNotReachHere("should have replaced itself");
+    }
+}