changeset 13813:460e453d6fec

Merge.
author Christian Humer <christian.humer@gmail.com>
date Wed, 29 Jan 2014 21:56:34 +0100
parents f270f09616da (diff) 3e13ec261278 (current diff)
children d7ed39d0a6d9
files graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java
diffstat 22 files changed, 767 insertions(+), 421 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Wed Jan 29 21:56:34 2014 +0100
@@ -56,7 +56,6 @@
         super(rootNode);
         this.compiler = compiler;
         this.compilationProfile = new CompilationProfile(compilationThreshold, invokeCounter, rootNode.toString());
-        this.getRootNode().setCallTarget(this);
 
         if (TruffleUseTimeForCompilationDecision.getValue()) {
             compilationPolicy = new TimedCompilationPolicy();
@@ -261,8 +260,8 @@
             }
 
             int notInlinedCallSiteCount = TruffleInliningImpl.getInlinableCallSites(callTarget).size();
-            int nodeCount = NodeUtil.countNodes(callTarget.getRootNode());
-            int inlinedCallSiteCount = NodeUtil.countNodes(callTarget.getRootNode(), InlinedCallSite.class);
+            int nodeCount = NodeUtil.countNodes(callTarget.getRootNode(), null, true);
+            int inlinedCallSiteCount = countInlinedNodes(callTarget.getRootNode());
             String comment = callTarget.installedCode == null ? " int" : "";
             comment += callTarget.compilationEnabled ? "" : " fail";
             OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d%s\n", callTarget.getRootNode(), callTarget.callCount, inlinedCallSiteCount, notInlinedCallSiteCount, nodeCount,
@@ -277,6 +276,18 @@
         OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d\n", "Total", totalCallCount, totalInlinedCallSiteCount, totalNotInlinedCallSiteCount, totalNodeCount, totalInvalidationCount);
     }
 
+    private static int countInlinedNodes(Node rootNode) {
+        List<CallNode> callers = NodeUtil.findAllNodeInstances(rootNode, CallNode.class);
+        int count = 0;
+        for (CallNode callNode : callers) {
+            if (callNode.isInlined()) {
+                count++;
+                count += countInlinedNodes(callNode.getInlinedRoot());
+            }
+        }
+        return count;
+    }
+
     private static void registerCallTarget(OptimizedCallTarget callTarget) {
         callTargets.put(callTarget, 0);
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Wed Jan 29 21:56:34 2014 +0100
@@ -168,7 +168,7 @@
         }
 
         if (TraceTruffleCompilation.getValue()) {
-            int nodeCountTruffle = NodeUtil.countNodes(compilable.getRootNode());
+            int nodeCountTruffle = NodeUtil.countNodes(compilable.getRootNode(), null, true);
             byte[] code = compiledMethod.getCode();
             OUT.printf("[truffle] optimized %-50s %x |Nodes %7d |Time %5.0f(%4.0f+%-4.0f)ms |Nodes %5d/%5d |CodeSize %d\n", compilable.getRootNode(), compilable.hashCode(), nodeCountTruffle,
                             (timeCompilationFinished - timeCompilationStarted) / 1e6, (timePartialEvaluationFinished - timeCompilationStarted) / 1e6,
@@ -186,22 +186,27 @@
     private class InlineTreeVisitor implements NodeVisitor {
 
         public boolean visit(Node node) {
-            if (node instanceof InlinedCallSite) {
-                InlinedCallSite inlinedCallSite = (InlinedCallSite) node;
-                int indent = this.indent(node);
-                for (int i = 0; i < indent; ++i) {
-                    OUT.print("   ");
+            if (node instanceof CallNode) {
+                CallNode callNode = (CallNode) node;
+                if (callNode.isInlined()) {
+                    int indent = this.indent(node);
+                    for (int i = 0; i < indent; ++i) {
+                        OUT.print("   ");
+                    }
+                    OUT.println(callNode.getCallTarget());
+                    callNode.getInlinedRoot().accept(this);
                 }
-                OUT.println(inlinedCallSite.getCallTarget());
             }
             return true;
         }
 
         private int indent(Node n) {
             if (n instanceof RootNode) {
+                CallNode inlinedParent = ((RootNode) n).getParentInlinedCall();
+                if (inlinedParent != null) {
+                    return indent(inlinedParent) + 1;
+                }
                 return 0;
-            } else if (n instanceof InlinedCallSite) {
-                return indent(n.getParent()) + 1;
             } else {
                 return indent(n.getParent());
             }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java	Wed Jan 29 21:56:34 2014 +0100
@@ -73,7 +73,7 @@
             if (!policy.isWorthInlining(inlinableCallSite)) {
                 break;
             }
-            if (inlinableCallSite.getCallSite().inline(target)) {
+            if (inlinableCallSite.getCallSite().inline()) {
                 if (TraceTruffleInlining.getValue()) {
                     printCallSiteInfo(policy, inlinableCallSite, "inlined");
                 }
@@ -84,7 +84,7 @@
 
         if (inlined) {
             for (InlinableCallSiteInfo callSite : inlinableCallSites) {
-                callSite.getCallSite().resetCallCount();
+                CallNode.internalResetCallCount(callSite.getCallSite());
             }
         } else {
             if (TraceTruffleInliningDetails.getValue()) {
@@ -114,7 +114,7 @@
         private final int callerInvocationCount;
 
         public InliningPolicy(OptimizedCallTarget caller) {
-            this.callerNodeCount = NodeUtil.countNodes(caller.getRootNode());
+            this.callerNodeCount = NodeUtil.countNodes(caller.getRootNode(), null, true);
             this.callerInvocationCount = caller.getCompilationProfile().getOriginalInvokeCounter();
         }
 
@@ -155,15 +155,17 @@
 
     private static final class InlinableCallSiteInfo {
 
-        private final InlinableCallSite callSite;
+        private final CallNode callSite;
         private final int callCount;
         private final int nodeCount;
         private final int recursiveDepth;
 
-        public InlinableCallSiteInfo(InlinableCallSite callSite) {
+        public InlinableCallSiteInfo(CallNode callSite) {
+            assert callSite.isInlinable();
             this.callSite = callSite;
-            this.callCount = callSite.getCallCount();
-            this.nodeCount = NodeUtil.countNodes(callSite.getInlineTree());
+            this.callCount = CallNode.internalGetCallCount(callSite);
+            RootCallTarget target = (RootCallTarget) callSite.getCallTarget();
+            this.nodeCount = target.getRootNode().getInlineNodeCount();
             this.recursiveDepth = calculateRecursiveDepth();
         }
 
@@ -173,21 +175,23 @@
 
         private int calculateRecursiveDepth() {
             int depth = 0;
-            Node parent = ((Node) callSite).getParent();
-            while (!(parent instanceof RootNode)) {
-                assert parent != null;
-                if (parent instanceof InlinedCallSite && ((InlinedCallSite) parent).getCallTarget() == callSite.getCallTarget()) {
-                    depth++;
+
+            Node parent = callSite.getParent();
+            while (parent != null) {
+                if (parent instanceof RootNode) {
+                    RootNode root = ((RootNode) parent);
+                    if (root.getCallTarget() == callSite.getCallTarget()) {
+                        depth++;
+                    }
+                    parent = root.getParentInlinedCall();
+                } else {
+                    parent = parent.getParent();
                 }
-                parent = parent.getParent();
-            }
-            if (((RootNode) parent).getCallTarget() == callSite.getCallTarget()) {
-                depth++;
             }
             return depth;
         }
 
-        public InlinableCallSite getCallSite() {
+        public CallNode getCallSite() {
             return callSite;
         }
 
@@ -206,8 +210,15 @@
 
             @Override
             public boolean visit(Node node) {
-                if (node instanceof InlinableCallSite) {
-                    inlinableCallSites.add(new InlinableCallSiteInfo((InlinableCallSite) node));
+                if (node instanceof CallNode) {
+                    CallNode callNode = (CallNode) node;
+                    if (!callNode.isInlined()) {
+                        if (callNode.isInlinable()) {
+                            inlinableCallSites.add(new InlinableCallSiteInfo(callNode));
+                        }
+                    } else {
+                        callNode.getInlinedRoot().accept(this);
+                    }
                 }
                 return true;
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/UnsupportedSpecializationTest.java	Wed Jan 29 21:56:34 2014 +0100
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2012, 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.truffle.api.dsl.test;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.UnsupportedSpecializationTestFactory.Undefined1Factory;
+
+public class UnsupportedSpecializationTest {
+
+    @Test
+    public void testUndefined1() {
+        TestRootNode<Undefined1> root = TestHelper.createRoot(Undefined1Factory.getInstance());
+        try {
+            TestHelper.executeWith(root, "");
+            Assert.fail();
+        } catch (UnsupportedSpecializationException e) {
+            Assert.assertNotNull(e.getSuppliedValues());
+            Assert.assertEquals(1, e.getSuppliedValues().length);
+            Assert.assertEquals("", e.getSuppliedValues()[0]);
+            Assert.assertEquals(root.getNode(), e.getNode());
+        }
+    }
+
+    @NodeChild("a")
+    abstract static class Undefined1 extends ValueNode {
+
+        @Specialization
+        public int doInteger(@SuppressWarnings("unused") int a) {
+            throw new AssertionError();
+        }
+    }
+
+    // TODO more tests required
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/UnsupportedSpecializationException.java	Wed Jan 29 21:56:34 2014 +0100
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012, 2012, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.dsl;
+
+import java.util.*;
+
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Thrown by the generated code of Truffle-DSL if no compatible Specialization could be found for
+ * the provided values.
+ */
+public final class UnsupportedSpecializationException extends RuntimeException {
+
+    private static final long serialVersionUID = -2122892028296836269L;
+
+    private final Node node;
+    private final Object[] suppliedValues;
+
+    public UnsupportedSpecializationException(Node node, Object... suppliedValues) {
+        super("Unexpected values provided for " + node + ": " + Arrays.toString(suppliedValues));
+        this.node = node;
+        this.suppliedValues = suppliedValues;
+    }
+
+    /**
+     * Returns the {@link Node} that caused the this {@link UnsupportedSpecializationException}.
+     */
+    public Node getNode() {
+        return node;
+    }
+
+    /**
+     * Returns the dynamic values that were supplied to the node.
+     */
+    public Object[] getSuppliedValues() {
+        return suppliedValues;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Wed Jan 29 21:56:34 2014 +0100
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2012, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.nodes;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.impl.*;
+
+/**
+ * This node represents a call to a static {@link CallTarget}. This node should be used whenever a
+ * {@link CallTarget} is considered constant at a certain location in the tree. This enables the
+ * Truffle runtime to perform inlining or other optimizations for this call-site.
+ * 
+ * @see #create(CallTarget) to create a CallNode instance.
+ */
+public abstract class CallNode extends Node {
+
+    protected final CallTarget callTarget;
+
+    private CallNode(CallTarget callTarget) {
+        this.callTarget = callTarget;
+    }
+
+    /**
+     * @return the constant {@link CallTarget} that is associated with this {@link CallNode}.
+     */
+    public CallTarget getCallTarget() {
+        return callTarget;
+    }
+
+    /**
+     * Calls this constant target passing a caller frame and arguments.
+     * 
+     * @param caller the caller frame
+     * @param arguments the arguments that should be passed to the callee
+     * @return the return result of the call
+     */
+    public abstract Object call(PackedFrame caller, Arguments arguments);
+
+    /**
+     * Returns <code>true</code> if the {@link CallTarget} contained in this {@link CallNode} can be
+     * inlined. A {@link CallTarget} is considered inlinable if it was created using
+     * {@link TruffleRuntime#createCallTarget(RootNode)} and if the enclosed {@link RootNode}
+     * returns <code>true</code> for {@link RootNode#isInlinable()}.
+     */
+    public abstract boolean isInlinable();
+
+    /**
+     * @return true if this {@link CallNode} was already inlined.
+     */
+    public abstract boolean isInlined();
+
+    /**
+     * Enforces an inlining optimization on this {@link CallNode} instance. If not performed
+     * manually the Truffle runtime may perform inlining using an heuristic to optimize the
+     * performance of the execution. It is recommended to implement an version of
+     * {@link RootNode#inline()} that adapts the inlining for possible guest language specific
+     * behavior. If the this {@link CallNode} is not inlinable or is already inlined
+     * <code>false</code> is returned.
+     * 
+     * @return <code>true</code> if the inlining operation was successful.
+     */
+    public abstract boolean inline();
+
+    /**
+     * Returns the inlined root node if the call node was inlined. If the {@link CallNode} was not
+     * inlined <code>null</code> is returned.
+     * 
+     * @return the inlined root node returned by {@link RootNode#inline()}
+     */
+    public RootNode getInlinedRoot() {
+        return null;
+    }
+
+    /**
+     * Creates a new {@link CallNode} using a {@link CallTarget}.
+     * 
+     * @param target the {@link CallTarget} to call
+     * @return a call node that calls the provided target
+     */
+    public static CallNode create(CallTarget target) {
+        if (isInlinable(target)) {
+            return new InlinableCallNode(target);
+        } else {
+            return new DefaultCallNode(target);
+        }
+    }
+
+    /**
+     * Warning: this is internal API and may change without notice.
+     */
+    public static int internalGetCallCount(CallNode callNode) {
+        if (callNode.isInlinable() && !callNode.isInlined()) {
+            return ((InlinableCallNode) callNode).getCallCount();
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Warning: this is internal API and may change without notice.
+     */
+    public static void internalResetCallCount(CallNode callNode) {
+        if (callNode.isInlinable() && !callNode.isInlined()) {
+            ((InlinableCallNode) callNode).resetCallCount();
+            return;
+        }
+    }
+
+    private static boolean isInlinable(CallTarget callTarget) {
+        if (callTarget instanceof DefaultCallTarget) {
+            return (((DefaultCallTarget) callTarget).getRootNode()).isInlinable();
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return getParent() != null ? getParent().toString() : super.toString();
+    }
+
+    static final class DefaultCallNode extends CallNode {
+
+        public DefaultCallNode(CallTarget target) {
+            super(target);
+        }
+
+        @Override
+        public Object call(PackedFrame caller, Arguments arguments) {
+            return callTarget.call(caller, arguments);
+        }
+
+        @Override
+        public boolean inline() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlinable() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlined() {
+            return false;
+        }
+
+    }
+
+    static final class InlinableCallNode extends CallNode {
+
+        private int callCount;
+
+        public InlinableCallNode(CallTarget target) {
+            super(target);
+        }
+
+        @Override
+        public Object call(PackedFrame parentFrame, Arguments arguments) {
+            if (CompilerDirectives.inInterpreter()) {
+                callCount++;
+            }
+            return callTarget.call(parentFrame, arguments);
+        }
+
+        @Override
+        public boolean inline() {
+            DefaultCallTarget defaultTarget = (DefaultCallTarget) getCallTarget();
+            RootNode originalRootNode = defaultTarget.getRootNode();
+            if (originalRootNode.isInlinable()) {
+                RootNode inlinedRootNode = defaultTarget.getRootNode().inline();
+                inlinedRootNode.setCallTarget(callTarget);
+                inlinedRootNode.setParentInlinedCall(this);
+                replace(new InlinedCallNode(defaultTarget, inlinedRootNode));
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean isInlined() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlinable() {
+            return true;
+        }
+
+        /* Truffle internal API. */
+        int getCallCount() {
+            return callCount;
+        }
+
+        /* Truffle internal API. */
+        void resetCallCount() {
+            callCount = 0;
+        }
+
+    }
+
+    static final class InlinedCallNode extends CallNode {
+
+        private final RootNode inlinedRoot;
+
+        public InlinedCallNode(DefaultCallTarget callTarget, RootNode inlinedRoot) {
+            super(callTarget);
+            this.inlinedRoot = inlinedRoot;
+        }
+
+        @Override
+        public Object call(PackedFrame caller, Arguments arguments) {
+            return inlinedRoot.execute(Truffle.getRuntime().createVirtualFrame(caller, arguments, inlinedRoot.getFrameDescriptor()));
+        }
+
+        @Override
+        public InlinedCallNode copy() {
+            return new InlinedCallNode((DefaultCallTarget) getCallTarget(), NodeUtil.cloneNode(inlinedRoot));
+        }
+
+        @Override
+        public RootNode getInlinedRoot() {
+            return inlinedRoot;
+        }
+
+        @Override
+        public boolean inline() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlinable() {
+            return true;
+        }
+
+        @Override
+        public boolean isInlined() {
+            return true;
+        }
+
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/InlinableCallSite.java	Wed Jan 29 18:30:42 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2012, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.nodes;
-
-import com.oracle.truffle.api.*;
-
-/**
- * Denotes a call node that can inline the tree of its associated call target.
- * 
- * @see InlinedCallSite
- */
-public interface InlinableCallSite {
-
-    /**
-     * Returns the number of calls since the last reset of the call count.
-     * 
-     * @return the current call count.
-     */
-    int getCallCount();
-
-    /**
-     * Resets the call count to 0.
-     */
-    void resetCallCount();
-
-    /**
-     * Returns the tree that would be inlined by a call to {@link #inline(FrameFactory)}.
-     * 
-     * @return the node tree to be inlined.
-     */
-    Node getInlineTree();
-
-    /**
-     * Returns the call target associated with this call site.
-     * 
-     * @return the inlinable {@link CallTarget}.
-     */
-    CallTarget getCallTarget();
-
-    /**
-     * Instructs the call node to inline the associated call target.
-     * 
-     * @param factory Frame factory for creating new virtual frames for inlined calls.
-     * @return {@code true} if call target was inlined; {@code false} otherwise.
-     */
-    boolean inline(FrameFactory factory);
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/InlinedCallSite.java	Wed Jan 29 18:30:42 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2012, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.truffle.api.nodes;
-
-import com.oracle.truffle.api.*;
-
-/**
- * Denotes a call node with an inlined call target. Allows for recursive call detection.
- * 
- * @see InlinableCallSite
- */
-public interface InlinedCallSite {
-
-    /**
-     * Returns the call target that has been inlined at this call site.
-     * 
-     * @return the inlined call target.
-     */
-    CallTarget getCallTarget();
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed Jan 29 21:56:34 2014 +0100
@@ -246,7 +246,7 @@
     }
 
     private void reportReplace() {
-        RootNode rootNode = getRootNode();
+        RootNode rootNode = NodeUtil.findOutermostRootNode(this);
         if (rootNode != null) {
             if (rootNode.getCallTarget() instanceof ReplaceObserver) {
                 ((ReplaceObserver) rootNode.getCallTarget()).nodeReplaced();
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Jan 29 21:56:34 2014 +0100
@@ -451,6 +451,31 @@
         return null;
     }
 
+    /**
+     * Returns the outermost not inlined {@link RootNode} which is a parent of this node.
+     * 
+     * @see RootNode#getParentInlinedCall()
+     * @param node to search
+     * @return the outermost {@link RootNode}
+     */
+    public static RootNode findOutermostRootNode(Node node) {
+        Node parent = node;
+        while (parent != null) {
+            if (parent instanceof RootNode) {
+                RootNode root = (RootNode) parent;
+                Node next = root.getParentInlinedCall();
+                if (next != null) {
+                    parent = next;
+                } else {
+                    return root;
+                }
+            } else {
+                parent = parent.getParent();
+            }
+        }
+        return null;
+    }
+
     public static <T> T findParent(Node start, Class<T> clazz) {
         Node parent = start.getParent();
         if (parent == null) {
@@ -571,24 +596,26 @@
     }
 
     public static int countNodes(Node root) {
-        return countNodes(root, null);
+        return countNodes(root, null, false);
     }
 
-    public static int countNodes(Node root, Class<?> clazz) {
-        NodeCountVisitor nodeCount = new NodeCountVisitor(root, clazz);
+    public static int countNodes(Node root, Class<?> clazz, boolean countInlinedCallNodes) {
+        NodeCountVisitor nodeCount = new NodeCountVisitor(root, clazz, countInlinedCallNodes);
         root.accept(nodeCount);
         return nodeCount.nodeCount;
     }
 
     private static final class NodeCountVisitor implements NodeVisitor {
 
+        private Node root;
+        private boolean inspectInlinedCalls;
         int nodeCount;
-        private final Node root;
         private final Class<?> clazz;
 
-        private NodeCountVisitor(Node root, Class<?> clazz) {
+        private NodeCountVisitor(Node root, Class<?> clazz, boolean inspectInlinedCalls) {
             this.root = root;
             this.clazz = clazz;
+            this.inspectInlinedCalls = inspectInlinedCalls;
         }
 
         @Override
@@ -596,9 +623,18 @@
             if (node instanceof RootNode && node != root) {
                 return false;
             }
+
             if (clazz == null || clazz.isInstance(node)) {
                 nodeCount++;
             }
+
+            if (inspectInlinedCalls && node instanceof CallNode) {
+                CallNode call = (CallNode) node;
+                if (call.isInlined()) {
+                    call.getInlinedRoot().getChildren().iterator().next().accept(this);
+                }
+            }
+
             return true;
         }
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Wed Jan 29 21:56:34 2014 +0100
@@ -25,6 +25,7 @@
 package com.oracle.truffle.api.nodes;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.*;
 import com.oracle.truffle.api.frame.*;
 
 /**
@@ -37,6 +38,12 @@
     private CallTarget callTarget;
     private final FrameDescriptor frameDescriptor;
 
+    /*
+     * Internal field to keep reference to the inlined call node. The inlined parent should not be
+     * the same as the Node parent to keep the same tree hierarchy if inlined vs not inlined.
+     */
+    @CompilationFinal private CallNode parentInlinedCall;
+
     protected RootNode() {
         this(null, null);
     }
@@ -55,6 +62,58 @@
     }
 
     /**
+     * Creates a copy of the current {@link RootNode} for use as inlined AST. The default
+     * implementation copies this {@link RootNode} and all its children recursively. It is
+     * recommended to override this method to provide an implementation that copies an uninitialized
+     * version of this AST. An uninitialized version of an AST was usually never executed which
+     * means that it has not yet collected any profiling feedback. Please note that changes in the
+     * behavior of this method might also require changes in {@link #getInlineNodeCount()}.
+     * 
+     * @see RootNode#getInlineNodeCount()
+     * @see RootNode#isInlinable()
+     * 
+     * @return the copied RootNode for inlining
+     * @throws UnsupportedOperationException if {@link #isInlinable()} returns false
+     */
+    public RootNode inline() {
+        if (!isInlinable()) {
+            throw new UnsupportedOperationException("Inlining is not enabled.");
+        }
+        return NodeUtil.cloneNode(this);
+    }
+
+    /**
+     * Returns the number of nodes that would be returned if {@link #inline()} would get invoked.
+     * This node count may be used for the calculation in a smart inlining heuristic.
+     * 
+     * @see RootNode#inline()
+     * @see RootNode#isInlinable()
+     * 
+     * @return the number of nodes that will get inlined
+     * @throws UnsupportedOperationException if {@link #isInlinable()} returns false
+     */
+    public int getInlineNodeCount() {
+        if (!isInlinable()) {
+            throw new UnsupportedOperationException("Inlining is not enabled.");
+        }
+        return NodeUtil.countNodes(this);
+    }
+
+    /**
+     * Returns true if this RootNode can be inlined. If this method returns true implementations of
+     * {@link #inline()} and {@link #getInlineNodeCount()} must be provided. Returns
+     * <code>true</code> by default.
+     * 
+     * @see RootNode#inline()
+     * @see RootNode#getInlineNodeCount()
+     * 
+     * @return true if this RootNode can be inlined
+     */
+    public boolean isInlinable() {
+        return true;
+    }
+
+    /**
      * Executes this function using the specified frame and returns the result value.
      * 
      * @param frame the frame of the currently executing guest language method
@@ -66,11 +125,27 @@
         return callTarget;
     }
 
-    public FrameDescriptor getFrameDescriptor() {
+    public final FrameDescriptor getFrameDescriptor() {
         return frameDescriptor;
     }
 
     public void setCallTarget(CallTarget callTarget) {
         this.callTarget = callTarget;
     }
+
+    /* Internal API. Do not use. */
+    void setParentInlinedCall(CallNode inlinedParent) {
+        this.parentInlinedCall = inlinedParent;
+    }
+
+    /**
+     * Returns the {@link CallNode} that uses this {@link RootNode} for an inlined call. Returns
+     * <code>null</code> if this {@link RootNode} is not inlined into a caller. This method can be
+     * used to also traverse parent {@link CallTarget} that have been inlined into this call.
+     * 
+     * @return the responsible {@link CallNode} for inlining.
+     */
+    public final CallNode getParentInlinedCall() {
+        return parentInlinedCall;
+    }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Wed Jan 29 21:56:34 2014 +0100
@@ -331,11 +331,10 @@
     }
 
     protected void emitEncounteredSynthetic(CodeTreeBuilder builder, TemplateMethod current) {
-        builder.startThrow().startNew(getContext().getType(UnsupportedOperationException.class));
-        builder.startCall("createInfo0");
-        builder.doubleQuote("Unsupported values");
+        builder.startThrow().startNew(getContext().getType(UnsupportedSpecializationException.class));
+        builder.string("this");
         addInternalValueParameterNames(builder, current, current, null, false, null);
-        builder.end().end().end();
+        builder.end().end();
     }
 
     private static List<ExecutableElement> findUserConstructors(TypeMirror nodeType) {
--- a/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyASTPrinter.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyASTPrinter.java	Wed Jan 29 21:56:34 2014 +0100
@@ -19,7 +19,7 @@
 import com.oracle.truffle.api.nodes.NodeUtil.NodeFieldKind;
 import com.oracle.truffle.api.nodes.instrument.*;
 import com.oracle.truffle.ruby.nodes.*;
-import com.oracle.truffle.ruby.nodes.call.*;
+import com.oracle.truffle.ruby.nodes.call.CallNode;
 import com.oracle.truffle.ruby.nodes.literal.*;
 import com.oracle.truffle.ruby.nodes.methods.*;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLSimpleTestSuite.java	Wed Jan 29 21:56:34 2014 +0100
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 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.truffle.sl.test;
+
+import org.junit.runner.*;
+
+@RunWith(SLTestRunner.class)
+@SLTestSuite({"graal/com.oracle.truffle.sl.test/tests", "tests"})
+public class SLSimpleTestSuite {
+
+    public static void main(String[] args) throws Exception {
+        SLTestRunner.runInMain(SLSimpleTestSuite.class, args);
+    }
+
+}
--- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java	Wed Jan 29 21:56:34 2014 +0100
@@ -23,103 +23,143 @@
 package com.oracle.truffle.sl.test;
 
 import java.io.*;
+import java.nio.charset.*;
 import java.nio.file.*;
 import java.nio.file.attribute.*;
 import java.util.*;
 
 import org.junit.*;
+import org.junit.internal.*;
+import org.junit.runner.*;
+import org.junit.runner.manipulation.*;
+import org.junit.runner.notification.*;
+import org.junit.runners.*;
+import org.junit.runners.model.*;
 
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.sl.*;
 import com.oracle.truffle.sl.runtime.*;
+import com.oracle.truffle.sl.test.SLTestRunner.TestCase;
 
-public class SLTestRunner {
+public final class SLTestRunner extends ParentRunner<TestCase> {
 
     private static final int REPEATS = 10;
-    private static final String TEST_DIR = "graal/com.oracle.truffle.sl.test/tests";
+
     private static final String INPUT_SUFFIX = ".sl";
     private static final String OUTPUT_SUFFIX = ".output";
 
-    static class TestCase {
-        protected final String name;
-        protected final Source input;
-        protected final String expectedOutput;
-        protected String actualOutput;
+    private static final String LF = System.getProperty("line.separator");
 
-        protected TestCase(String name, Source input, String expectedOutput) {
-            this.name = name;
+    public static final class TestCase {
+        private final Source input;
+        private final String expectedOutput;
+        private final Description name;
+
+        public TestCase(Class<?> testClass, String name, Source input, String expectedOutput) {
             this.input = input;
             this.expectedOutput = expectedOutput;
+            this.name = Description.createTestDescription(testClass, name);
+        }
+    }
+
+    private final SourceManager sourceManager = new SourceManager();
+    private final List<TestCase> testCases;
+
+    public SLTestRunner(Class<?> runningClass) throws InitializationError {
+        super(runningClass);
+        try {
+            testCases = createTests(runningClass);
+        } catch (IOException e) {
+            throw new InitializationError(e);
         }
     }
 
-    protected boolean useConsole = false;
+    @Override
+    protected Description describeChild(TestCase child) {
+        return child.name;
+    }
 
-    protected final SourceManager sourceManager = new SourceManager();
-    protected final List<TestCase> testCases = new ArrayList<>();
+    @Override
+    protected List<TestCase> getChildren() {
+        return testCases;
+    }
+
+    @Override
+    public void filter(Filter filter) throws NoTestsRemainException {
+        super.filter(filter);
+    }
 
-    protected boolean runTests(String namePattern) throws IOException {
-        Path testsRoot = FileSystems.getDefault().getPath(TEST_DIR);
+    protected List<TestCase> createTests(final Class<?> c) throws IOException, InitializationError {
+        SLTestSuite suite = c.getAnnotation(SLTestSuite.class);
+        if (suite == null) {
+            throw new InitializationError(String.format("@%s annotation required on class '%s' to run with '%s'.", SLTestSuite.class.getSimpleName(), c.getName(), SLTestRunner.class.getSimpleName()));
+        }
+
+        String[] pathes = suite.value();
 
-        Files.walkFileTree(testsRoot, new SimpleFileVisitor<Path>() {
+        Path root = null;
+        for (String path : pathes) {
+            root = FileSystems.getDefault().getPath(path);
+            if (Files.exists(root)) {
+                break;
+            }
+        }
+        if (root == null && pathes.length > 0) {
+            throw new FileNotFoundException(pathes[0]);
+        }
+
+        final Path rootPath = root;
+
+        final List<TestCase> foundCases = new ArrayList<>();
+        Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
             @Override
             public FileVisitResult visitFile(Path inputFile, BasicFileAttributes attrs) throws IOException {
                 String name = inputFile.getFileName().toString();
                 if (name.endsWith(INPUT_SUFFIX)) {
-                    name = name.substring(0, name.length() - INPUT_SUFFIX.length());
-                    Path outputFile = inputFile.resolveSibling(name + OUTPUT_SUFFIX);
+                    String baseName = name.substring(0, name.length() - INPUT_SUFFIX.length());
+
+                    Path outputFile = inputFile.resolveSibling(baseName + OUTPUT_SUFFIX);
                     if (!Files.exists(outputFile)) {
                         throw new Error("Output file does not exist: " + outputFile);
                     }
 
-                    testCases.add(new TestCase(name, sourceManager.get(inputFile.toString()), new String(Files.readAllBytes(outputFile))));
+                    // fix line feeds for non unix os
+                    StringBuilder outFile = new StringBuilder();
+                    for (String line : Files.readAllLines(outputFile, Charset.defaultCharset())) {
+                        outFile.append(line);
+                        outFile.append(LF);
+                    }
+                    foundCases.add(new TestCase(c, baseName, sourceManager.get(inputFile.toString()), outFile.toString()));
                 }
                 return FileVisitResult.CONTINUE;
             }
         });
-
-        if (testCases.size() == 0) {
-            System.out.format("No test cases match filter %s", namePattern);
-            return false;
-        }
-
-        boolean success = true;
-        for (TestCase testCase : testCases) {
-            if (namePattern.length() == 0 || testCase.name.toLowerCase().contains(namePattern.toLowerCase())) {
-                success = success & executeTest(testCase);
-            }
-        }
-        return success;
+        return foundCases;
     }
 
-    protected boolean executeTest(TestCase testCase) {
-        System.out.format("Running %s\n", testCase.name);
+    @Override
+    protected void runChild(TestCase testCase, RunNotifier notifier) {
+        notifier.fireTestStarted(testCase.name);
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
-        PrintStream printer = new PrintStream(useConsole ? new SplitOutputStream(out, System.err) : out);
+        PrintStream printer = new PrintStream(out);
         PrintStream origErr = System.err;
         try {
             System.setErr(printer);
             SLContext context = new SLContext(sourceManager, printer);
             SLMain.run(context, testCase.input, null, REPEATS);
+
+            String actualOutput = new String(out.toByteArray());
+
+            Assert.assertEquals(repeat(testCase.expectedOutput, REPEATS), actualOutput);
+        } catch (AssertionError e) {
+            notifier.fireTestFailure(new Failure(testCase.name, e));
         } catch (Throwable ex) {
-            ex.printStackTrace(printer);
+            notifier.fireTestFailure(new Failure(testCase.name, ex));
         } finally {
             System.setErr(origErr);
-        }
-        testCase.actualOutput = new String(out.toByteArray());
-
-        if (testCase.actualOutput.equals(repeat(testCase.expectedOutput, REPEATS))) {
-            System.out.format("OK %s\n", testCase.name);
-            return true;
-        } else {
-            if (!useConsole) {
-                System.out.format("== Expected ==\n%s\n", testCase.expectedOutput);
-                System.out.format("== Actual ==\n%s\n", testCase.actualOutput);
-            }
-            System.out.format("FAILED %s\n", testCase.name);
-            return false;
+            notifier.fireTestFinished(testCase.name);
         }
     }
 
@@ -131,19 +171,35 @@
         return result.toString();
     }
 
-    public static void main(String[] args) throws IOException {
-        String namePattern = "";
+    public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException {
+        JUnitCore core = new JUnitCore();
+        core.addListener(new TextListener(System.out));
+        SLTestRunner suite = new SLTestRunner(testClass);
         if (args.length > 0) {
-            namePattern = args[0];
+            suite.filter(new NameFilter(args[0]));
         }
-        boolean success = new SLTestRunner().runTests(namePattern);
-        if (!success) {
+        Result r = core.run(suite);
+        if (!r.wasSuccessful()) {
             System.exit(1);
         }
     }
 
-    @Test
-    public void test() throws IOException {
-        Assert.assertTrue(runTests(""));
+    private static final class NameFilter extends Filter {
+        private final String pattern;
+
+        private NameFilter(String pattern) {
+            this.pattern = pattern.toLowerCase();
+        }
+
+        @Override
+        public boolean shouldRun(Description description) {
+            return description.getMethodName().toLowerCase().contains(pattern);
+        }
+
+        @Override
+        public String describe() {
+            return "Filter contains " + pattern;
+        }
     }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestSuite.java	Wed Jan 29 21:56:34 2014 +0100
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012, 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.truffle.sl.test;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface SLTestSuite {
+
+    /**
+     * Defines the base path of the test suite. Multiple base pathes can be specified. However only
+     * the first base that exists is used to lookup the test cases.
+     */
+    String[] value();
+
+}
--- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SplitOutputStream.java	Wed Jan 29 18:30:42 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2012, 2012, 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.truffle.sl.test;
-
-import java.io.*;
-
-public class SplitOutputStream extends OutputStream {
-
-    private final OutputStream[] outputs;
-
-    public SplitOutputStream(OutputStream... outputs) {
-        this.outputs = outputs;
-    }
-
-    @Override
-    public void write(int b) throws IOException {
-        for (OutputStream out : outputs) {
-            out.write(b);
-        }
-    }
-
-    @Override
-    public void write(byte[] b) throws IOException {
-        for (OutputStream out : outputs) {
-            out.write(b);
-        }
-    }
-
-    @Override
-    public void write(byte[] b, int off, int len) throws IOException {
-        for (OutputStream out : outputs) {
-            out.write(b, off, len);
-        }
-    }
-
-    @Override
-    public void flush() throws IOException {
-        for (OutputStream out : outputs) {
-            out.flush();
-        }
-    }
-
-    @Override
-    public void close() throws IOException {
-        for (OutputStream out : outputs) {
-            out.close();
-        }
-    }
-}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Wed Jan 29 21:56:34 2014 +0100
@@ -73,8 +73,19 @@
         return inlineImmediatly;
     }
 
-    public SLExpressionNode inline() {
-        return NodeUtil.cloneNode(uninitializedBody);
+    @Override
+    public RootNode inline() {
+        return new SLRootNode(getFrameDescriptor().shallowCopy(), NodeUtil.cloneNode(uninitializedBody), name, inlineImmediatly);
+    }
+
+    @Override
+    public int getInlineNodeCount() {
+        return NodeUtil.countNodes(uninitializedBody);
+    }
+
+    @Override
+    public boolean isInlinable() {
+        return true;
     }
 
     public Node getUninitializedBody() {
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLDirectDispatchNode.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLDirectDispatchNode.java	Wed Jan 29 21:56:34 2014 +0100
@@ -27,27 +27,29 @@
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.sl.runtime.*;
 
-abstract class SLDirectDispatchNode extends SLAbstractDispatchNode {
+final class SLDirectDispatchNode extends SLAbstractDispatchNode {
 
     protected final SLFunction cachedFunction;
     protected final RootCallTarget cachedCallTarget;
     protected final Assumption cachedCallTargetStable;
 
+    @Child protected CallNode callNode;
     @Child protected SLAbstractDispatchNode nextNode;
 
     protected SLDirectDispatchNode(SLAbstractDispatchNode next, SLFunction cachedFunction) {
         this.cachedFunction = cachedFunction;
         this.cachedCallTarget = cachedFunction.getCallTarget();
         this.cachedCallTargetStable = cachedFunction.getCallTargetStable();
+        this.callNode = adoptChild(CallNode.create(cachedCallTarget));
         this.nextNode = adoptChild(next);
     }
 
     @Override
-    protected final Object executeCall(VirtualFrame frame, SLFunction function, SLArguments arguments) {
+    protected Object executeCall(VirtualFrame frame, SLFunction function, SLArguments arguments) {
         if (this.cachedFunction == function) {
             try {
                 cachedCallTargetStable.check();
-                return executeCurrent(frame, arguments);
+                return callNode.call(frame.pack(), arguments);
             } catch (InvalidAssumptionException ex) {
                 /*
                  * Remove ourselfs from the polymorphic inline cache, so that we fail the check only
@@ -61,6 +63,4 @@
         }
         return nextNode.executeCall(frame, function, arguments);
     }
-
-    protected abstract Object executeCurrent(VirtualFrame frame, SLArguments arguments);
 }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInlinableDirectDispatchNode.java	Wed Jan 29 18:30:42 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2013, 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.truffle.sl.nodes.call;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.sl.nodes.*;
-import com.oracle.truffle.sl.runtime.*;
-
-final class SLInlinableDirectDispatchNode extends SLDirectDispatchNode implements InlinableCallSite {
-
-    @CompilationFinal private int callCount;
-
-    protected SLInlinableDirectDispatchNode(SLAbstractDispatchNode next, SLFunction cachedFunction) {
-        super(next, cachedFunction);
-    }
-
-    @Override
-    protected Object executeCurrent(VirtualFrame frame, SLArguments arguments) {
-        if (CompilerDirectives.inInterpreter()) {
-            callCount++;
-        }
-        return cachedCallTarget.call(frame.pack(), arguments);
-    }
-
-    @Override
-    public boolean inline(FrameFactory factory) {
-        CompilerAsserts.neverPartOfCompilation();
-        RootNode root = cachedCallTarget.getRootNode();
-        SLExpressionNode inlinedNode = ((SLRootNode) root).inline();
-        assert inlinedNode != null;
-        replace(new SLInlinedDirectDispatchNode(this, inlinedNode), "Inlined " + root);
-        /* We are always able to inline if required. */
-        return true;
-    }
-
-    @Override
-    public int getCallCount() {
-        return callCount;
-    }
-
-    @Override
-    public void resetCallCount() {
-        callCount = 0;
-    }
-
-    @Override
-    public Node getInlineTree() {
-        RootNode root = cachedCallTarget.getRootNode();
-        if (root instanceof SLRootNode) {
-            return ((SLRootNode) root).getUninitializedBody();
-        }
-        return null;
-    }
-
-    @Override
-    public CallTarget getCallTarget() {
-        return cachedCallTarget;
-    }
-}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLInlinedDirectDispatchNode.java	Wed Jan 29 18:30:42 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2013, 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.truffle.sl.nodes.call;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.sl.nodes.*;
-import com.oracle.truffle.sl.runtime.*;
-
-final class SLInlinedDirectDispatchNode extends SLDirectDispatchNode implements InlinedCallSite {
-
-    private final FrameDescriptor descriptor;
-    @Child private SLExpressionNode inlinedBody;
-
-    protected SLInlinedDirectDispatchNode(SLInlinableDirectDispatchNode prev, SLExpressionNode inlinedBody) {
-        super(prev.nextNode, prev.cachedFunction);
-        this.descriptor = cachedCallTarget.getRootNode().getFrameDescriptor();
-        this.inlinedBody = adoptChild(inlinedBody);
-    }
-
-    @Override
-    protected Object executeCurrent(VirtualFrame frame, SLArguments arguments) {
-        VirtualFrame newFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), arguments, descriptor);
-        return inlinedBody.executeGeneric(newFrame);
-    }
-
-    @Override
-    public CallTarget getCallTarget() {
-        return cachedCallTarget;
-    }
-}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLUninitializedCallNode.java	Wed Jan 29 18:30:42 2014 +0100
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLUninitializedCallNode.java	Wed Jan 29 21:56:34 2014 +0100
@@ -43,7 +43,7 @@
         SLAbstractDispatchNode specialized;
         if (depth < INLINE_CACHE_SIZE) {
             SLAbstractDispatchNode next = new SLUninitializedCallNode();
-            SLAbstractDispatchNode direct = new SLInlinableDirectDispatchNode(next, function);
+            SLAbstractDispatchNode direct = new SLDirectDispatchNode(next, function);
             specialized = replace(direct);
         } else {
             SLAbstractDispatchNode generic = new SLGenericDispatchNode();