changeset 7510:225002aba5a5

added new macro node facility, removed ArrayCopyIntrinsificationPhase
author Lukas Stadler <lukas.stadler@jku.at>
date Tue, 22 Jan 2013 11:29:40 +0100
parents 442668d41bc2
children b5316e551965 4c269aec7d62
files graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/ArrayCopyIntrinsificationTest.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotSnippetInstaller.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopyNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopySnippets.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/IntrinsifyArrayCopyPhase.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/SystemSubstitutions.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractStateSplit.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java graal/com.oracle.graal.phases/src/com/oracle/graal/phases/OptimisticOptimizations.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/MacroNode.java
diffstat 14 files changed, 439 insertions(+), 216 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/ArrayCopyIntrinsificationTest.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/ArrayCopyIntrinsificationTest.java	Tue Jan 22 11:29:40 2013 +0100
@@ -33,10 +33,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.test.*;
 import com.oracle.graal.graph.*;
-import com.oracle.graal.hotspot.snippets.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.phases.*;
-import com.oracle.graal.phases.PhasePlan.PhasePosition;
 
 
 /**
@@ -45,11 +42,6 @@
 public class ArrayCopyIntrinsificationTest extends GraalCompilerTest {
 
     @Override
-    protected void editPhasePlan(ResolvedJavaMethod method, StructuredGraph graph, PhasePlan phasePlan) {
-        phasePlan.addPhase(PhasePosition.HIGH_LEVEL, new IntrinsifyArrayCopyPhase(runtime, new Assumptions(false)));
-    }
-
-    @Override
     protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph graph) {
         int nodeCount = graph.getNodeCount();
         InstalledCode result = super.getCode(method, graph);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotSnippetInstaller.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotSnippetInstaller.java	Tue Jan 22 11:29:40 2013 +0100
@@ -42,7 +42,7 @@
     }
 
     @Override
-    protected void installSubstitution(Method originalMethod, Method substituteMethod) {
+    protected void installMethodSubstitution(Method originalMethod, Method substituteMethod) {
         if (substituteMethod.getDeclaringClass() == IntegerSubstitutions.class || substituteMethod.getDeclaringClass() == LongSubstitutions.class) {
             if (substituteMethod.getName().equals("bitCount")) {
                 if (!config.usePopCountInstruction) {
@@ -58,6 +58,6 @@
             assert config.cipherBlockChainingEncryptAESCryptStub != 0L;
             assert config.cipherBlockChainingDecryptAESCryptStub != 0L;
         }
-        super.installSubstitution(originalMethod, substituteMethod);
+        super.installMethodSubstitution(originalMethod, substituteMethod);
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Tue Jan 22 11:29:40 2013 +0100
@@ -39,7 +39,6 @@
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.phases.*;
-import com.oracle.graal.hotspot.snippets.*;
 import com.oracle.graal.java.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.phases.*;
@@ -53,7 +52,6 @@
 public class VMToCompilerImpl implements VMToCompiler {
 
     private final HotSpotGraalRuntime graalRuntime;
-    private IntrinsifyArrayCopyPhase intrinsifyArrayCopy;
 
     public final HotSpotResolvedPrimitiveType typeBoolean;
     public final HotSpotResolvedPrimitiveType typeChar;
@@ -141,7 +139,6 @@
                 public void run() {
                     // Snippets cannot have speculative optimizations since they have to be valid for the entire run of the VM.
                     Assumptions assumptions = new Assumptions(false);
-                    VMToCompilerImpl.this.intrinsifyArrayCopy = new IntrinsifyArrayCopyPhase(runtime, assumptions);
                     SnippetInstaller installer = new HotSpotSnippetInstaller(runtime, assumptions, runtime.getGraalRuntime().getTarget());
                     GraalIntrinsics.installIntrinsics(installer);
                     runtime.installSnippets(installer, assumptions);
@@ -572,9 +569,6 @@
         if (onStackReplacement) {
             phasePlan.addPhase(PhasePosition.AFTER_PARSING, new OnStackReplacementPhase());
         }
-        if (GraalOptions.Intrinsify && GraalOptions.IntrinsifyArrayCopy) {
-            phasePlan.addPhase(PhasePosition.HIGH_LEVEL, intrinsifyArrayCopy);
-        }
         return phasePlan;
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopyNode.java	Tue Jan 22 11:29:40 2013 +0100
@@ -0,0 +1,130 @@
+/*
+ * 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.snippets;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Node.IterableNodeType;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.snippets.nodes.*;
+
+public class ArrayCopyNode extends MacroNode implements Virtualizable, IterableNodeType, Lowerable {
+
+    public ArrayCopyNode(InvokeNode invoke) {
+        super(invoke);
+    }
+
+    public ValueNode src() {
+        return arguments.get(0);
+    }
+
+    public ValueNode srcPos() {
+        return arguments.get(1);
+    }
+
+    public ValueNode dest() {
+        return arguments.get(2);
+    }
+
+    public ValueNode destPos() {
+        return arguments.get(3);
+    }
+
+    public ValueNode length() {
+        return arguments.get(4);
+    }
+
+    @Override
+    public void lower(LoweringTool tool) {
+        ResolvedJavaMethod snippetMethod = null;
+        ResolvedJavaType srcType = src().objectStamp().type();
+        ResolvedJavaType destType = dest().objectStamp().type();
+        if (srcType != null && srcType.isArray() && destType != null && destType.isArray()) {
+            Kind componentKind = srcType.getComponentType().getKind();
+            if (srcType.getComponentType() == destType.getComponentType()) {
+                snippetMethod = tool.getRuntime().lookupJavaMethod(ArrayCopySnippets.getSnippetForKind(componentKind));
+            } else if (componentKind == Kind.Object && destType.getComponentType().isAssignableFrom(srcType.getComponentType())) {
+                snippetMethod = tool.getRuntime().lookupJavaMethod(ArrayCopySnippets.getSnippetForKind(Kind.Object));
+            }
+        }
+        if (snippetMethod == null) {
+            snippetMethod = tool.getRuntime().lookupJavaMethod(ArrayCopySnippets.increaseGenericCallCounterMethod);
+            // we will call the generic method. the generic snippet will only increase the counter, not call the actual
+            // method. therefore we create a second invoke here.
+            ((StructuredGraph) graph()).addAfterFixed(this, createInvoke());
+        } else {
+            Debug.log("%s > Intrinsify (%s)", Debug.currentScope(), snippetMethod.getSignature().getParameterType(0, snippetMethod.getDeclaringClass()).getComponentType());
+        }
+
+        if (snippetMethod != null) {
+            StructuredGraph snippetGraph = (StructuredGraph) snippetMethod.getCompilerStorage().get(Graph.class);
+            assert snippetGraph != null : "ArrayCopySnippets should be installed";
+            InvokeNode invoke = replaceWithInvoke();
+            InliningUtil.inline(invoke, snippetGraph, false);
+        } else {
+            super.lower(tool);
+        }
+    }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        if (srcPos().isConstant() && destPos().isConstant() && length().isConstant()) {
+            int srcPos = srcPos().asConstant().asInt();
+            int destPos = destPos().asConstant().asInt();
+            int length = length().asConstant().asInt();
+            State srcState = tool.getObjectState(src());
+            State destState = tool.getObjectState(dest());
+
+            if (srcState != null && srcState.getState() == EscapeState.Virtual && destState != null && destState.getState() == EscapeState.Virtual) {
+                VirtualObjectNode srcVirtual = srcState.getVirtualObject();
+                VirtualObjectNode destVirtual = destState.getVirtualObject();
+                if (length < 0) {
+                    return;
+                }
+                if (srcPos < 0 || srcPos + length > srcVirtual.entryCount()) {
+                    return;
+                }
+                if (destPos < 0 || destPos + length > destVirtual.entryCount()) {
+                    return;
+                }
+                ResolvedJavaType destComponentType = destVirtual.type().getComponentType();
+                if (destComponentType.getKind() == Kind.Object) {
+                    for (int i = 0; i < length; i++) {
+                        if (!destComponentType.isAssignableFrom(srcState.getEntry(srcPos + i).objectStamp().javaType(tool.getMetaAccessProvider()))) {
+                            return;
+                        }
+                    }
+                }
+                for (int i = 0; i < length; i++) {
+                    tool.setVirtualEntry(destState, destPos + i, srcState.getEntry(srcPos + i));
+                }
+                tool.delete();
+                Debug.log("virtualized arraycopyf(%s, %d, %s, %d, %d)", src(), srcPos, dest(), destPos, length);
+            }
+        }
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopySnippets.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/ArrayCopySnippets.java	Tue Jan 22 11:29:40 2013 +0100
@@ -25,8 +25,12 @@
 import static com.oracle.graal.api.meta.DeoptimizationReason.*;
 import static com.oracle.graal.hotspot.snippets.HotSpotSnippetUtils.*;
 
+import java.lang.reflect.*;
+import java.util.*;
+
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.nodes.*;
@@ -34,6 +38,7 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.phases.*;
 import com.oracle.graal.snippets.*;
 import com.oracle.graal.snippets.Snippet.ConstantParameter;
 import com.oracle.graal.snippets.Snippet.Fold;
@@ -42,6 +47,35 @@
 
 @SuppressWarnings("unused")
 public class ArrayCopySnippets implements SnippetsInterface{
+
+    private static final EnumMap<Kind, Method> arraycopyMethods = new EnumMap<>(Kind.class);
+    public static final Method increaseGenericCallCounterMethod;
+
+    private static void addArraycopySnippetMethod(Kind kind, Class<?> arrayClass) throws NoSuchMethodException {
+        arraycopyMethods.put(kind, ArrayCopySnippets.class.getDeclaredMethod("arraycopy", arrayClass, int.class, arrayClass, int.class, int.class));
+    }
+
+    static {
+        try {
+            addArraycopySnippetMethod(Kind.Byte, byte[].class);
+            addArraycopySnippetMethod(Kind.Boolean, boolean[].class);
+            addArraycopySnippetMethod(Kind.Char, char[].class);
+            addArraycopySnippetMethod(Kind.Short, short[].class);
+            addArraycopySnippetMethod(Kind.Int, int[].class);
+            addArraycopySnippetMethod(Kind.Long, long[].class);
+            addArraycopySnippetMethod(Kind.Float, float[].class);
+            addArraycopySnippetMethod(Kind.Double, double[].class);
+            addArraycopySnippetMethod(Kind.Object, Object[].class);
+            increaseGenericCallCounterMethod = ArrayCopySnippets.class.getDeclaredMethod("increaseGenericCallCounter", Object.class, int.class, Object.class, int.class, int.class);
+        } catch (SecurityException | NoSuchMethodException e) {
+            throw new GraalInternalError(e);
+        }
+    }
+
+    public static Method getSnippetForKind(Kind kind) {
+        return arraycopyMethods.get(kind);
+    }
+
     private static final Kind VECTOR_KIND = Kind.Long;
     private static final long VECTOR_SIZE = arrayIndexScale(Kind.Long);
 
@@ -75,40 +109,55 @@
 
     public static void checkInputs(Object src, int srcPos, Object dest, int destPos, int length) {
         if (src == null || dest == null) {
+            checkNPECounter.inc();
             throw new NullPointerException();
         }
         if (srcPos < 0 || destPos < 0 || length < 0 || srcPos + length > ArrayLengthNode.arrayLength(src) || destPos + length > ArrayLengthNode.arrayLength(dest)) {
+            checkAIOOBECounter.inc();
             throw new ArrayIndexOutOfBoundsException();
         }
+        checkSuccessCounter.inc();
     }
 
     @Snippet
     public static void arraycopy(byte[] src, int srcPos, byte[] dest, int destPos, int length) {
+        byteCounter.inc();
+        vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Byte);
+    }
+
+    @Snippet
+    public static void arraycopy(boolean[] src, int srcPos, boolean[] dest, int destPos, int length) {
+        booleanCounter.inc();
         vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Byte);
     }
 
     @Snippet
     public static void arraycopy(char[] src, int srcPos, char[] dest, int destPos, int length) {
+        charCounter.inc();
         vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Char);
     }
 
     @Snippet
     public static void arraycopy(short[] src, int srcPos, short[] dest, int destPos, int length) {
+        shortCounter.inc();
         vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Short);
     }
 
     @Snippet
     public static void arraycopy(int[] src, int srcPos, int[] dest, int destPos, int length) {
+        intCounter.inc();
         vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Int);
     }
 
     @Snippet
     public static void arraycopy(float[] src, int srcPos, float[] dest, int destPos, int length) {
+        floatCounter.inc();
         vectorizedCopy(src, srcPos, dest, destPos, length, Kind.Float);
     }
 
     @Snippet
     public static void arraycopy(long[] src, int srcPos, long[] dest, int destPos, int length) {
+        longCounter.inc();
         checkInputs(src, srcPos, dest, destPos, length);
         Kind baseKind = Kind.Long;
         int header = arrayBaseOffset(baseKind);
@@ -130,6 +179,7 @@
 
     @Snippet
     public static void arraycopy(double[] src, int srcPos, double[] dest, int destPos, int length) {
+        doubleCounter.inc();
         checkInputs(src, srcPos, dest, destPos, length);
         Kind baseKind = Kind.Double;
         int header = arrayBaseOffset(baseKind);
@@ -152,6 +202,7 @@
     // Does NOT perform store checks
     @Snippet
     public static void arraycopy(Object[] src, int srcPos, Object[] dest, int destPos, int length) {
+        objectCounter.inc();
         checkInputs(src, srcPos, dest, destPos, length);
         final int scale = arrayIndexScale(Kind.Object);
         int header = arrayBaseOffset(Kind.Object);
@@ -180,4 +231,35 @@
             }
         }
     }
+
+    @Snippet
+    public static void increaseGenericCallCounter(Object src, int srcPos, Object dest, int destPos, int length) {
+        if (GraalOptions.SnippetCounters) {
+            if (src.getClass().getComponentType().isPrimitive()) {
+                genericPrimitiveCallCounter.inc();
+            } else {
+                genericObjectCallCounter.inc();
+            }
+        }
+    }
+
+    private static final SnippetCounter.Group checkCounters = GraalOptions.SnippetCounters ? new SnippetCounter.Group("System.arraycopy checkInputs") : null;
+    private static final SnippetCounter checkSuccessCounter = new SnippetCounter(checkCounters, "checkSuccess", "checkSuccess");
+    private static final SnippetCounter checkNPECounter = new SnippetCounter(checkCounters, "checkNPE", "checkNPE");
+    private static final SnippetCounter checkAIOOBECounter = new SnippetCounter(checkCounters, "checkAIOOBE", "checkAIOOBE");
+
+    private static final SnippetCounter.Group counters = GraalOptions.SnippetCounters ? new SnippetCounter.Group("System.arraycopy") : null;
+    private static final SnippetCounter byteCounter = new SnippetCounter(counters, "byte[]", "arraycopy for byte[] arrays");
+    private static final SnippetCounter charCounter = new SnippetCounter(counters, "char[]", "arraycopy for char[] arrays");
+    private static final SnippetCounter shortCounter = new SnippetCounter(counters, "short[]", "arraycopy for short[] arrays");
+    private static final SnippetCounter intCounter = new SnippetCounter(counters, "int[]", "arraycopy for int[] arrays");
+    private static final SnippetCounter booleanCounter = new SnippetCounter(counters, "boolean[]", "arraycopy for boolean[] arrays");
+    private static final SnippetCounter longCounter = new SnippetCounter(counters, "long[]", "arraycopy for long[] arrays");
+    private static final SnippetCounter objectCounter = new SnippetCounter(counters, "Object[]", "arraycopy for Object[] arrays");
+    private static final SnippetCounter floatCounter = new SnippetCounter(counters, "float[]", "arraycopy for float[] arrays");
+    private static final SnippetCounter doubleCounter = new SnippetCounter(counters, "double[]", "arraycopy for double[] arrays");
+    private static final SnippetCounter genericPrimitiveCallCounter = new SnippetCounter(counters, "genericPrimitive", "call to the generic, native arraycopy method");
+    private static final SnippetCounter genericObjectCallCounter = new SnippetCounter(counters, "genericObject", "call to the generic, native arraycopy method");
+
+
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/IntrinsifyArrayCopyPhase.java	Mon Jan 21 12:23:55 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/*
- * Copyright (c) 2011, 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.snippets;
-
-import java.lang.reflect.*;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.debug.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.phases.*;
-import com.oracle.graal.phases.common.*;
-
-public class IntrinsifyArrayCopyPhase extends Phase {
-    private final GraalCodeCacheProvider runtime;
-    private final Assumptions assumptions;
-    private ResolvedJavaMethod arrayCopy;
-    private ResolvedJavaMethod byteArrayCopy;
-    private ResolvedJavaMethod shortArrayCopy;
-    private ResolvedJavaMethod charArrayCopy;
-    private ResolvedJavaMethod intArrayCopy;
-    private ResolvedJavaMethod longArrayCopy;
-    private ResolvedJavaMethod floatArrayCopy;
-    private ResolvedJavaMethod doubleArrayCopy;
-    private ResolvedJavaMethod objectArrayCopy;
-
-    public IntrinsifyArrayCopyPhase(GraalCodeCacheProvider runtime, Assumptions assumptions) {
-        this.runtime = runtime;
-        this.assumptions = assumptions;
-        try {
-            byteArrayCopy = getArrayCopySnippet(runtime, byte.class);
-            charArrayCopy = getArrayCopySnippet(runtime, char.class);
-            shortArrayCopy = getArrayCopySnippet(runtime, short.class);
-            intArrayCopy = getArrayCopySnippet(runtime, int.class);
-            longArrayCopy = getArrayCopySnippet(runtime, long.class);
-            floatArrayCopy = getArrayCopySnippet(runtime, float.class);
-            doubleArrayCopy = getArrayCopySnippet(runtime, double.class);
-            objectArrayCopy = getArrayCopySnippet(runtime, Object.class);
-            arrayCopy = runtime.lookupJavaMethod(System.class.getDeclaredMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class));
-        } catch (SecurityException e) {
-            e.printStackTrace();
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-        }
-    }
-
-    private static ResolvedJavaMethod getArrayCopySnippet(CodeCacheProvider runtime, Class<?> componentClass) throws NoSuchMethodException {
-        Class<?> arrayClass = Array.newInstance(componentClass, 0).getClass();
-        return runtime.lookupJavaMethod(ArrayCopySnippets.class.getDeclaredMethod("arraycopy", arrayClass, int.class, arrayClass, int.class, int.class));
-    }
-
-    @Override
-    protected void run(StructuredGraph graph) {
-        boolean hits = false;
-        for (MethodCallTargetNode methodCallTarget : graph.getNodes(MethodCallTargetNode.class)) {
-            ResolvedJavaMethod targetMethod = methodCallTarget.targetMethod();
-            ResolvedJavaMethod snippetMethod = null;
-            if (targetMethod == arrayCopy) {
-                ValueNode src = methodCallTarget.arguments().get(0);
-                ValueNode dest = methodCallTarget.arguments().get(2);
-                assert src != null && dest != null;
-                ResolvedJavaType srcType = src.objectStamp().type();
-                ResolvedJavaType destType = dest.objectStamp().type();
-                if (srcType != null
-                                && srcType.isArray()
-                                && destType != null
-                                && destType.isArray()) {
-                    Kind componentKind = srcType.getComponentType().getKind();
-                    if (srcType.getComponentType() == destType.getComponentType()) {
-                        if (componentKind == Kind.Int) {
-                            snippetMethod = intArrayCopy;
-                        } else if (componentKind == Kind.Char) {
-                            snippetMethod = charArrayCopy;
-                        } else if (componentKind == Kind.Long) {
-                            snippetMethod = longArrayCopy;
-                        } else if (componentKind == Kind.Byte) {
-                            snippetMethod = byteArrayCopy;
-                        } else if (componentKind == Kind.Short) {
-                            snippetMethod = shortArrayCopy;
-                        } else if (componentKind == Kind.Float) {
-                            snippetMethod = floatArrayCopy;
-                        } else if (componentKind == Kind.Double) {
-                            snippetMethod = doubleArrayCopy;
-                        } else if (componentKind == Kind.Object) {
-                            snippetMethod = objectArrayCopy;
-                        }
-                    } else if (componentKind == Kind.Object
-                                    && destType.getComponentType().isAssignableFrom(srcType.getComponentType())) {
-                        snippetMethod = objectArrayCopy;
-                    }
-                }
-            }
-
-            if (snippetMethod != null) {
-                StructuredGraph snippetGraph = (StructuredGraph) snippetMethod.getCompilerStorage().get(Graph.class);
-                assert snippetGraph != null : "ArrayCopySnippets should be installed";
-                hits = true;
-                Debug.log("%s > Intrinsify (%s)", Debug.currentScope(), snippetMethod.getSignature().getParameterType(0, snippetMethod.getDeclaringClass()).getComponentType());
-                InliningUtil.inline(methodCallTarget.invoke(), snippetGraph, false);
-            } else {
-                Debug.log("%s > not intrinsifying arraycopy", Debug.currentScope());
-            }
-        }
-        if (GraalOptions.OptCanonicalizer && hits) {
-            new CanonicalizerPhase(null, runtime, assumptions).apply(graph);
-        }
-    }
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/SystemSubstitutions.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/SystemSubstitutions.java	Tue Jan 22 11:29:40 2013 +0100
@@ -31,6 +31,7 @@
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.snippets.*;
+import com.oracle.graal.snippets.ClassSubstitution.MacroSubstitution;
 import com.oracle.graal.snippets.ClassSubstitution.MethodSubstitution;
 import com.oracle.graal.word.*;
 
@@ -43,6 +44,9 @@
     public static final Descriptor JAVA_TIME_MILLIS = new Descriptor("javaTimeMillis", false, long.class);
     public static final Descriptor JAVA_TIME_NANOS = new Descriptor("javaTimeNanos", false, long.class);
 
+    @MacroSubstitution(macro = ArrayCopyNode.class, isStatic = true)
+    public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
+
     @MethodSubstitution
     public static long currentTimeMillis() {
         return callLong(JAVA_TIME_MILLIS);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractStateSplit.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractStateSplit.java	Tue Jan 22 11:29:40 2013 +0100
@@ -48,4 +48,9 @@
     public AbstractStateSplit(Stamp stamp) {
         super(stamp);
     }
+
+    public AbstractStateSplit(Stamp stamp, FrameState stateAfter) {
+        super(stamp);
+        this.stateAfter = stateAfter;
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Tue Jan 22 11:29:40 2013 +0100
@@ -131,6 +131,10 @@
         return this;
     }
 
+    public JavaType returnType() {
+        return returnType;
+    }
+
     @Override
     public Stamp returnStamp() {
         Kind returnKind = targetMethod.getSignature().getReturnKind();
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Tue Jan 22 11:29:40 2013 +0100
@@ -200,16 +200,36 @@
             return computeInliningLevel(invoke);
         }
 
-        protected static StructuredGraph getGraph(final ResolvedJavaMethod concrete, final InliningCallback callback) {
+        protected static void inline(Invoke invoke, ResolvedJavaMethod concrete, InliningCallback callback, Assumptions assumptions, boolean receiverNullCheck) {
+            Class< ? extends FixedWithNextNode> macroNodeClass = getMacroNodeClass(concrete);
+            if (macroNodeClass != null) {
+                StructuredGraph graph = (StructuredGraph) invoke.graph();
+                assert invoke instanceof InvokeNode;
+                FixedWithNextNode macroNode;
+                try {
+                    macroNode = macroNodeClass.getConstructor(InvokeNode.class).newInstance(invoke);
+                } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) {
+                    throw new GraalInternalError(e).addContext(invoke.node()).addContext("macroSubstitution", macroNodeClass);
+                }
+                CallTargetNode callTarget = invoke.callTarget();
+                graph.replaceFixedWithFixed((InvokeNode) invoke, graph.add(macroNode));
+                GraphUtil.killWithUnusedFloatingInputs(callTarget);
+            } else {
+                StructuredGraph calleeGraph = getIntrinsicGraph(concrete);
+                if (calleeGraph == null) {
+                    calleeGraph = getGraph(concrete, callback);
+                }
+                assumptions.recordMethodContents(concrete);
+                InliningUtil.inline(invoke, calleeGraph, receiverNullCheck);
+            }
+        }
+
+        private static StructuredGraph getGraph(final ResolvedJavaMethod concrete, final InliningCallback callback) {
             return Debug.scope("GetInliningGraph", concrete, new Callable<StructuredGraph>() {
                 @Override
                 public StructuredGraph call() throws Exception {
-                    StructuredGraph result = getIntrinsicGraph(concrete);
-                    if (result == null) {
-                        assert !Modifier.isNative(concrete.getModifiers());
-                        result = callback.buildGraph(concrete);
-                    }
-                    return result;
+                    assert !Modifier.isNative(concrete.getModifiers());
+                    return callback.buildGraph(concrete);
                 }
             });
         }
@@ -229,9 +249,7 @@
 
         @Override
         public void inline(StructuredGraph compilerGraph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) {
-            StructuredGraph graph = getGraph(concrete, callback);
-            assumptions.recordMethodContents(concrete);
-            InliningUtil.inline(invoke, graph, true);
+            inline(invoke, concrete, callback, assumptions, true);
         }
 
         @Override
@@ -283,9 +301,7 @@
             graph.addBeforeFixed(invoke.node(), guard);
             graph.addBeforeFixed(invoke.node(), anchor);
 
-            StructuredGraph calleeGraph = getGraph(concrete, callback);
-            assumptions.recordMethodContents(concrete);
-            InliningUtil.inline(invoke, calleeGraph, false);
+            inline(invoke, concrete, callback, assumptions, false);
         }
 
         @Override
@@ -405,14 +421,7 @@
                 GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge());
             }
 
-            // get all graphs and record assumptions
             assert invoke.node().isAlive();
-            StructuredGraph[] calleeGraphs = new StructuredGraph[numberOfMethods];
-            for (int i = 0; i < numberOfMethods; i++) {
-                ResolvedJavaMethod concrete = concretes.get(i);
-                calleeGraphs[i] = getGraph(concrete, callback);
-                assumptions.recordMethodContents(concrete);
-            }
 
             // replace the invoke with a switch on the type of the actual receiver
             Kind hubKind = invoke.methodCallTarget().targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind();
@@ -439,8 +448,8 @@
                 PiNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact);
                 invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver);
 
-                StructuredGraph calleeGraph = calleeGraphs[i];
-                InliningUtil.inline(invokeForInlining, calleeGraph, false);
+                inline(invokeForInlining, concretes.get(i), callback, assumptions, false);
+
                 replacements.add(anchoredReceiver);
             }
             if (shouldFallbackToInvoke()) {
@@ -512,9 +521,7 @@
             calleeEntryNode.setNext(invoke.node());
 
             ResolvedJavaMethod concrete = concretes.get(0);
-            StructuredGraph calleeGraph = getGraph(concrete, callback);
-            assumptions.recordMethodContents(concrete);
-            InliningUtil.inline(invoke, calleeGraph, false);
+            inline(invoke, concrete, callback, assumptions, false);
         }
 
         private FixedNode createDispatchOnType(StructuredGraph graph, LoadHubNode hub, BeginNode[] successors) {
@@ -1043,8 +1050,7 @@
     }
 
     public static boolean canIntrinsify(ResolvedJavaMethod target) {
-        StructuredGraph intrinsicGraph = getIntrinsicGraph(target);
-        return intrinsicGraph != null;
+        return getIntrinsicGraph(target) != null || getMacroNodeClass(target) != null;
     }
 
     public static StructuredGraph getIntrinsicGraph(ResolvedJavaMethod target) {
@@ -1058,4 +1064,9 @@
             return target.getCompiledCodeSize();
         }
     }
+
+    public static Class< ? extends FixedWithNextNode> getMacroNodeClass(ResolvedJavaMethod target) {
+        Object result = target.getCompilerStorage().get(Node.class);
+        return result == null ? null : ((Class< ? >) result).asSubclass(FixedWithNextNode.class);
+    }
 }
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/OptimisticOptimizations.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/OptimisticOptimizations.java	Tue Jan 22 11:29:40 2013 +0100
@@ -27,18 +27,14 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.debug.*;
 
-
+public final class OptimisticOptimizations {
 
-public final class OptimisticOptimizations {
     public static final OptimisticOptimizations ALL = new OptimisticOptimizations(EnumSet.allOf(Optimization.class));
     public static final OptimisticOptimizations NONE = new OptimisticOptimizations(EnumSet.noneOf(Optimization.class));
     private static final DebugMetric disabledOptimisticOptsMetric = Debug.metric("DisabledOptimisticOpts");
 
     private static enum Optimization {
-        RemoveNeverExecutedCode,
-        UseTypeCheckedInlining,
-        UseTypeCheckHints,
-        UseExceptionProbability
+        RemoveNeverExecutedCode, UseTypeCheckedInlining, UseTypeCheckHints, UseExceptionProbability
     }
 
     private final Set<Optimization> enabledOpts;
@@ -56,7 +52,8 @@
         if (checkDeoptimizations(method.getProfilingInfo(), deoptReason)) {
             enabledOpts.add(optimization);
         } else {
-            // TODO (chaeubl): change to Debug.log when we are sure that optimistic optimizations are not disabled unnecessarily
+            // TODO (chaeubl): change to Debug.log when we are sure that optimistic optimizations are not disabled
+            // unnecessarily
             TTY.println("WARN: deactivated optimistic optimization %s for %s", optimization.name(), MetaUtil.format("%H.%n(%p)", method));
             disabledOptimisticOptsMetric.increment();
         }
@@ -91,7 +88,7 @@
     }
 
     public boolean lessOptimisticThan(OptimisticOptimizations other) {
-        for (Optimization opt: Optimization.values()) {
+        for (Optimization opt : Optimization.values()) {
             if (!enabledOpts.contains(opt) && other.enabledOpts.contains(opt)) {
                 return true;
             }
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/ClassSubstitution.java	Tue Jan 22 11:29:40 2013 +0100
@@ -25,10 +25,12 @@
 import java.lang.annotation.*;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.snippets.nodes.*;
 
 /**
- * Denotes a class that substitutes methods of another specified class.
- * The substitute methods are exactly those annotated by {@link MethodSubstitution}.
+ * Denotes a class that substitutes methods of another specified class. The substitute methods are exactly those
+ * annotated by {@link MethodSubstitution}.
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
@@ -37,34 +39,35 @@
     /**
      * Specifies the original class.
      * <p>
-     * If the default value is specified for this element, then a non-default
-     * value must be given for the {@link #className()} element.
-      */
-    Class<?> value() default ClassSubstitution.class;
+     * If the default value is specified for this element, then a non-default value must be given for the
+     * {@link #className()} element.
+     */
+    Class< ? > value() default ClassSubstitution.class;
 
     /**
      * Specifies the original class.
      * <p>
-     * This method is provided for cases where the original class
-     * is not accessible (according to Java language access control rules).
+     * This method is provided for cases where the original class is not accessible (according to Java language access
+     * control rules).
      * <p>
-     * If the default value is specified for this element, then a non-default
-     * value must be given for the {@link #value()} element.
+     * If the default value is specified for this element, then a non-default value must be given for the
+     * {@link #value()} element.
      */
     String className() default "";
 
     /**
-     * Denotes a substitute method. A substitute method can call the original/substituted
-     * method by making a recursive call to itself.
+     * Denotes a substitute method. A substitute method can call the original/substituted method by making a recursive
+     * call to itself.
      */
     @Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.METHOD)
     public @interface MethodSubstitution {
+
         /**
          * Gets the name of the original method.
          * <p>
-         * If the default value is specified for this element, then the
-         * name of the original method is same as the substitute method.
+         * If the default value is specified for this element, then the name of the original method is same as the
+         * substitute method.
          */
         String value() default "";
 
@@ -76,9 +79,49 @@
         /**
          * Gets the {@linkplain Signature#getMethodDescriptor() signature} of the original method.
          * <p>
-         * If the default value is specified for this element, then the
-         * signature of the original method is the same as the substitute method.
+         * If the default value is specified for this element, then the signature of the original method is the same as
+         * the substitute method.
          */
         String signature() default "";
     }
+
+    /**
+     * Denotes a macro substitute method. This replaces a method invocation with an instance of the specified node
+     * class.
+     *
+     * A macro substitution can be combined with a normal substitution, so that the macro node can be replaced with the
+     * actual substitution code during lowering.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.METHOD)
+    public @interface MacroSubstitution {
+
+        /**
+         * Gets the name of the substituted method.
+         * <p>
+         * If the default value is specified for this element, then the name of the substituted method is same as the
+         * substitute method.
+         */
+        String value() default "";
+
+        /**
+         * Determines if the substituted method is static.
+         */
+        boolean isStatic() default true;
+
+        /**
+         * Gets the {@linkplain Signature#getMethodDescriptor() signature} of the substituted method.
+         * <p>
+         * If the default value is specified for this element, then the signature of the substituted method is the same
+         * as the substitute method.
+         */
+        String signature() default "";
+
+        /**
+         * The node class with which the method invocation should be replaced. It needs to be a subclass of
+         * {@link FixedWithNextNode}, and it is expected to provide a public constructor that takes an InvokeNode as a
+         * parameter. For most cases this class should subclass {@link MacroNode} and use its constructor.
+         */
+        Class< ? extends FixedWithNextNode> macro();
+    }
 }
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java	Mon Jan 21 12:23:55 2013 +0100
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetInstaller.java	Tue Jan 22 11:29:40 2013 +0100
@@ -41,6 +41,7 @@
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.snippets.ClassSubstitution.MacroSubstitution;
 import com.oracle.graal.snippets.ClassSubstitution.MethodSubstitution;
 import com.oracle.graal.snippets.Snippet.DefaultSnippetInliningPolicy;
 import com.oracle.graal.snippets.Snippet.SnippetInliningPolicy;
@@ -107,21 +108,31 @@
         ClassSubstitution classSubstitution = substitutions.getAnnotation(ClassSubstitution.class);
         for (Method substituteMethod : substitutions.getDeclaredMethods()) {
             MethodSubstitution methodSubstitution = substituteMethod.getAnnotation(MethodSubstitution.class);
-            if (methodSubstitution == null) {
+            MacroSubstitution macroSubstitution = substituteMethod.getAnnotation(MacroSubstitution.class);
+            if (methodSubstitution == null && macroSubstitution == null) {
                 continue;
             }
 
             int modifiers = substituteMethod.getModifiers();
             if (!Modifier.isStatic(modifiers)) {
                 throw new RuntimeException("Substitution methods must be static: " + substituteMethod);
-            } else if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
-                throw new RuntimeException("Substitution method must not be abstract or native: " + substituteMethod);
             }
 
-            String originalName = originalName(substituteMethod, methodSubstitution);
-            Class[] originalParameters = originalParameters(substituteMethod, methodSubstitution);
-            Method originalMethod = originalMethod(classSubstitution, originalName, originalParameters);
-            installSubstitution(originalMethod, substituteMethod);
+            if (methodSubstitution != null) {
+                if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
+                    throw new RuntimeException("Substitution method must not be abstract or native: " + substituteMethod);
+                }
+                String originalName = originalName(substituteMethod, methodSubstitution.value());
+                Class[] originalParameters = originalParameters(substituteMethod, methodSubstitution.signature(), methodSubstitution.isStatic());
+                Method originalMethod = originalMethod(classSubstitution, originalName, originalParameters);
+                installMethodSubstitution(originalMethod, substituteMethod);
+            }
+            if (macroSubstitution != null) {
+                String originalName = originalName(substituteMethod, macroSubstitution.value());
+                Class[] originalParameters = originalParameters(substituteMethod, macroSubstitution.signature(), macroSubstitution.isStatic());
+                Method originalMethod = originalMethod(classSubstitution, originalName, originalParameters);
+                installMacroSubstitution(originalMethod, macroSubstitution.macro());
+            }
         }
     }
 
@@ -136,7 +147,7 @@
      * @param originalMethod a method being substituted
      * @param substituteMethod the substitute method
      */
-    protected void installSubstitution(Method originalMethod, Method substituteMethod) {
+    protected void installMethodSubstitution(Method originalMethod, Method substituteMethod) {
         substitute = runtime.lookupJavaMethod(substituteMethod);
         original = runtime.lookupJavaMethod(originalMethod);
         try {
@@ -151,8 +162,20 @@
         }
     }
 
+    /**
+     * Installs a macro substitution.
+     *
+     * @param originalMethod a method being substituted
+     * @param macro the substitute macro node class
+     */
+    protected void installMacroSubstitution(Method originalMethod, Class< ? extends FixedWithNextNode> macro) {
+        ResolvedJavaMethod originalJavaMethod = runtime.lookupJavaMethod(originalMethod);
+        Object oldValue = originalJavaMethod.getCompilerStorage().put(Node.class, macro);
+        assert oldValue == null;
+    }
+
     private SnippetInliningPolicy inliningPolicy(ResolvedJavaMethod method) {
-        Class<? extends SnippetInliningPolicy> policyClass = SnippetInliningPolicy.class;
+        Class< ? extends SnippetInliningPolicy> policyClass = SnippetInliningPolicy.class;
         Snippet snippet = method.getAnnotation(Snippet.class);
         if (snippet != null) {
             policyClass = snippet.inlining();
@@ -263,12 +286,12 @@
         return graph;
     }
 
-    private static String originalName(Method substituteMethod, MethodSubstitution methodSubstitution) {
-        String name = substituteMethod.getName();
-        if (!methodSubstitution.value().isEmpty()) {
-            name = methodSubstitution.value();
+    private static String originalName(Method substituteMethod, String methodSubstitution) {
+        if (methodSubstitution.isEmpty()) {
+            return substituteMethod.getName();
+        } else {
+            return methodSubstitution;
         }
-        return name;
     }
 
     private static Class resolveType(String className) {
@@ -294,16 +317,16 @@
         return dimensions == 0 ? baseClass : Array.newInstance(baseClass, new int[dimensions]).getClass();
     }
 
-    private Class[] originalParameters(Method substituteMethod, MethodSubstitution methodSubstitution) {
+    private Class[] originalParameters(Method substituteMethod, String methodSubstitution, boolean isStatic) {
         Class[] parameters;
-        if (methodSubstitution.signature().isEmpty()) {
+        if (methodSubstitution.isEmpty()) {
             parameters = substituteMethod.getParameterTypes();
-            if (!methodSubstitution.isStatic()) {
+            if (!isStatic) {
                 assert parameters.length > 0 : "must be a static method with the 'this' object as its first parameter";
                 parameters = Arrays.copyOfRange(parameters, 1, parameters.length);
             }
         } else {
-            Signature signature = runtime.parseMethodDescriptor(methodSubstitution.signature());
+            Signature signature = runtime.parseMethodDescriptor(methodSubstitution);
             parameters = new Class[signature.getParameterCount(false)];
             for (int i = 0; i < parameters.length; i++) {
                 parameters[i] = resolveType(signature.getParameterType(i, null));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/MacroNode.java	Tue Jan 22 11:29:40 2013 +0100
@@ -0,0 +1,69 @@
+/*
+ * 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.snippets.nodes;
+
+import java.lang.reflect.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.spi.*;
+
+public class MacroNode extends AbstractStateSplit implements Lowerable {
+
+    @Input protected NodeInputList<ValueNode> arguments;
+
+    private final int bci;
+    private final ResolvedJavaMethod targetMethod;
+    private final JavaType returnType;
+
+    protected MacroNode(InvokeNode invoke) {
+        super(invoke.stamp(), invoke.stateAfter());
+        this.arguments = new NodeInputList<>(this, invoke.methodCallTarget().arguments());
+        this.bci = invoke.bci();
+        this.targetMethod = invoke.methodCallTarget().targetMethod();
+        this.returnType = invoke.methodCallTarget().returnType();
+    }
+
+    @Override
+    public void lower(LoweringTool tool) {
+        replaceWithInvoke();
+    }
+
+    public InvokeNode replaceWithInvoke() {
+        InvokeNode invoke = createInvoke();
+
+        ((StructuredGraph) graph()).replaceFixedWithFixed(this, invoke);
+        return invoke;
+    }
+
+    protected InvokeNode createInvoke() {
+        InvokeKind invokeKind = Modifier.isStatic(targetMethod.getModifiers()) ? InvokeKind.Static : InvokeKind.Special;
+        MethodCallTargetNode callTarget = graph().add(new MethodCallTargetNode(invokeKind, targetMethod, arguments.toArray(new ValueNode[arguments.size()]), returnType));
+        InvokeNode invoke = graph().add(new InvokeNode(callTarget, bci, StructuredGraph.INVALID_GRAPH_ID));
+        invoke.setStateAfter(stateAfter());
+        return invoke;
+    }
+}