changeset 13803:e076c87ab175

Truffle: refactored inlining interfaces to a more compact CallNode.
author Christian Humer <christian.humer@gmail.com>
date Fri, 24 Jan 2014 15:55:41 +0100
parents a03cb658e68e
children 3840d61e0e68
files graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/InlinableCallSite.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/InlinedCallSite.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyASTPrinter.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLNodeFactory.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/CallNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionCallNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionRootNode.java
diffstat 12 files changed, 480 insertions(+), 357 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Fri Jan 24 12:26:05 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Fri Jan 24 15:55:41 2014 +0100
@@ -261,7 +261,7 @@
 
             int notInlinedCallSiteCount = TruffleInliningImpl.getInlinableCallSites(callTarget).size();
             int nodeCount = NodeUtil.countNodes(callTarget.rootNode);
-            int inlinedCallSiteCount = NodeUtil.countNodes(callTarget.rootNode, InlinedCallSite.class);
+            int inlinedCallSiteCount = countInlinedNodes(callTarget.rootNode);
             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,
@@ -276,6 +276,17 @@
         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++;
+            }
+        }
+        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	Fri Jan 24 12:26:05 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Fri Jan 24 15:55:41 2014 +0100
@@ -186,13 +186,15 @@
     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());
                 }
-                OUT.println(inlinedCallSite.getCallTarget());
             }
             return true;
         }
@@ -200,7 +202,7 @@
         private int indent(Node n) {
             if (n instanceof RootNode) {
                 return 0;
-            } else if (n instanceof InlinedCallSite) {
+            } else if (n instanceof CallNode && ((CallNode) n).isInlined()) {
                 return indent(n.getParent()) + 1;
             } else {
                 return indent(n.getParent());
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java	Fri Jan 24 12:26:05 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java	Fri Jan 24 15:55:41 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,9 @@
 
         if (inlined) {
             for (InlinableCallSiteInfo callSite : inlinableCallSites) {
-                callSite.getCallSite().resetCallCount();
+                if (callSite.getCallSite().isInlinable()) {
+                    CallNode.internalResetCallCount(callSite.getCallSite());
+                }
             }
         } else {
             if (TraceTruffleInliningDetails.getValue()) {
@@ -155,15 +157,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);
+            DefaultCallTarget target = (DefaultCallTarget) callSite.getCallTarget();
+            this.nodeCount = target.getRootNode().getInlineNodeCount();
             this.recursiveDepth = calculateRecursiveDepth();
         }
 
@@ -176,8 +180,11 @@
             Node parent = ((Node) callSite).getParent();
             while (!(parent instanceof RootNode)) {
                 assert parent != null;
-                if (parent instanceof InlinedCallSite && ((InlinedCallSite) parent).getCallTarget() == callSite.getCallTarget()) {
-                    depth++;
+                if (parent instanceof CallNode) {
+                    CallNode parentCall = (CallNode) parent;
+                    if (parentCall.isInlined() && parentCall.getCallTarget() == callSite.getCallTarget()) {
+                        depth++;
+                    }
                 }
                 parent = parent.getParent();
             }
@@ -187,7 +194,7 @@
             return depth;
         }
 
-        public InlinableCallSite getCallSite() {
+        public CallNode getCallSite() {
             return callSite;
         }
 
@@ -206,8 +213,11 @@
 
             @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.isInlinable()) {
+                        inlinableCallSites.add(new InlinableCallSiteInfo(callNode));
+                    }
                 }
                 return true;
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Fri Jan 24 15:55:41 2014 +0100
@@ -0,0 +1,230 @@
+/*
+ * 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 constant {@link CallTarget} in the Truffle AST. This node should
+ * be used whenever a {@link CallTarget} is considered constant at a certain location. 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;
+    }
+
+    /**
+     * Returns 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 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 boolean isInlinable() {
+        return false;
+    }
+
+    /**
+     * Returns true if this {@link CallNode} was already inlined.
+     */
+    public boolean isInlined() {
+        return false;
+    }
+
+    /**
+     * 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 optimized version of
+     * {@link RootNode#inline()}.
+     * 
+     * @return true if the inlining operation was successful.
+     * @throws IllegalStateException if {@link #isInlinable()} is false.
+     */
+    public boolean inline() {
+        CompilerDirectives.transferToInterpreter();
+        if (!isInlinable()) {
+            throw new IllegalStateException("Invoked inline on a non inlinable CallNode.");
+        }
+        assert !isInlined();
+        return false;
+    }
+
+    /**
+     * Creates a new {@link CallNode} using a constant {@link CallTarget}.
+     * 
+     * @param target the {@link CallTarget} the {@link CallNode} should 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()) {
+            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()) {
+            ((InlinableCallNode) callNode).resetCallCount();
+            return;
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    private static boolean isInlinable(CallTarget callTarget) {
+        if (callTarget instanceof DefaultCallTarget) {
+            return (((DefaultCallTarget) callTarget).getRootNode()).isInlinable();
+        }
+        return false;
+    }
+
+    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);
+        }
+
+    }
+
+    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() {
+            super.inline();
+            DefaultCallTarget defaultTarget = (DefaultCallTarget) getCallTarget();
+            RootNode originalRootNode = defaultTarget.getRootNode();
+            boolean inlined = false;
+            if (originalRootNode.isInlinable()) {
+                RootNode inlinedRootNode = defaultTarget.getRootNode().inline();
+                replace(new InlinedCallNode(defaultTarget, inlinedRootNode));
+                inlined = true;
+            }
+            return inlined;
+        }
+
+        @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 boolean isInlinable() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlined() {
+            return true;
+        }
+
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/InlinableCallSite.java	Fri Jan 24 12:26:05 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	Fri Jan 24 12:26:05 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/RootNode.java	Fri Jan 24 12:26:05 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Fri Jan 24 15:55:41 2014 +0100
@@ -55,6 +55,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 of 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 proper
+     * implementations of {@link #inline()} and {@link #getInlineNodeCount()} must be provided.
+     * Returns true 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,7 +118,7 @@
         return callTarget;
     }
 
-    public FrameDescriptor getFrameDescriptor() {
+    public final FrameDescriptor getFrameDescriptor() {
         return frameDescriptor;
     }
 
--- a/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyASTPrinter.java	Fri Jan 24 12:26:05 2014 +0100
+++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyASTPrinter.java	Fri Jan 24 15:55:41 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.*;
 
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLNodeFactory.java	Fri Jan 24 12:26:05 2014 +0100
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLNodeFactory.java	Fri Jan 24 15:55:41 2014 +0100
@@ -96,7 +96,7 @@
     }
 
     public TypedNode createCall(TypedNode function, TypedNode[] parameters) {
-        return assignSource(CallNode.create(function, parameters));
+        return assignSource(FunctionCallNode.create(function, parameters));
     }
 
     public TypedNode createBinary(String operation, TypedNode left, TypedNode right) {
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/CallNode.java	Fri Jan 24 12:26:05 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,222 +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.nodes;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.impl.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.sl.runtime.*;
-
-public abstract class CallNode extends TypedNode {
-
-    private static final int INLINE_CACHE_SIZE = 2;
-
-    @Child protected TypedNode functionNode;
-    @Child protected ArgumentsNode argumentsNode;
-
-    public CallNode(TypedNode functionNode, ArgumentsNode argumentsNode) {
-        this.functionNode = adoptChild(functionNode);
-        this.argumentsNode = adoptChild(argumentsNode);
-    }
-
-    @Override
-    public final Object executeGeneric(VirtualFrame frame) {
-        CallTarget function;
-        try {
-            function = functionNode.executeCallTarget(frame);
-        } catch (UnexpectedResultException e) {
-            throw new UnsupportedOperationException("Call to " + e.getMessage() + " not supported.");
-        }
-        Object[] arguments = argumentsNode.executeArray(frame);
-        return executeCall(frame, function, arguments);
-    }
-
-    public abstract Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments);
-
-    public static CallNode create(TypedNode function, TypedNode[] arguments) {
-        return new UninitializedCallNode(function, new ArgumentsNode(arguments), 0);
-    }
-
-    private static final class UninitializedCallNode extends CallNode {
-
-        protected final int depth;
-
-        UninitializedCallNode(TypedNode function, ArgumentsNode args, int depth) {
-            super(function, args);
-            this.depth = depth;
-        }
-
-        UninitializedCallNode(UninitializedCallNode copy) {
-            super(null, null);
-            this.depth = copy.depth + 1;
-        }
-
-        @Override
-        public Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments) {
-            CompilerDirectives.transferToInterpreter();
-            return specialize(function).executeCall(frame, function, arguments);
-        }
-
-        private CallNode specialize(CallTarget function) {
-            CompilerAsserts.neverPartOfCompilation();
-
-            if (depth < INLINE_CACHE_SIZE) {
-                DefaultCallTarget callTarget = (DefaultCallTarget) function;
-                FunctionRootNode root = (FunctionRootNode) callTarget.getRootNode();
-                CallNode next = new UninitializedCallNode(this);
-                InlinableDirectCallNode directCall = new InlinableDirectCallNode(functionNode, argumentsNode, next, callTarget);
-                replace(directCall);
-                if (root.isInlineImmediatly()) {
-                    return directCall.inlineImpl();
-                } else {
-                    return directCall;
-                }
-            } else {
-                CallNode topMost = (CallNode) NodeUtil.getNthParent(this, depth);
-                return topMost.replace(new GenericCallNode(topMost.functionNode, topMost.argumentsNode));
-            }
-        }
-
-    }
-
-    private abstract static class DirectCallNode extends CallNode {
-
-        protected final DefaultCallTarget cachedFunction;
-
-        @Child protected CallNode nextNode;
-
-        public DirectCallNode(TypedNode function, ArgumentsNode arguments, DefaultCallTarget cachedFunction, CallNode next) {
-            super(function, arguments);
-            this.cachedFunction = cachedFunction;
-            this.nextNode = adoptChild(next);
-        }
-
-        @Override
-        public Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments) {
-            if (this.cachedFunction == function) {
-                return executeCurrent(frame, arguments);
-            }
-            return nextNode.executeCall(frame, function, arguments);
-        }
-
-        protected abstract Object executeCurrent(VirtualFrame frame, Object[] arguments);
-
-    }
-
-    private static final class InlinableDirectCallNode extends DirectCallNode implements InlinableCallSite {
-
-        @CompilationFinal private int callCount;
-
-        InlinableDirectCallNode(TypedNode function, ArgumentsNode arguments, CallNode next, DefaultCallTarget cachedFunction) {
-            super(function, arguments, cachedFunction, next);
-        }
-
-        @Override
-        public Object executeCurrent(VirtualFrame frame, Object[] arguments) {
-            if (CompilerDirectives.inInterpreter()) {
-                callCount++;
-            }
-            return cachedFunction.call(frame.pack(), new SLArguments(arguments));
-        }
-
-        InlinedDirectCallNode inlineImpl() {
-            CompilerAsserts.neverPartOfCompilation();
-            RootNode root = cachedFunction.getRootNode();
-            TypedNode inlinedNode = ((FunctionRootNode) root).inline();
-            assert inlinedNode != null;
-            return replace(new InlinedDirectCallNode(this, inlinedNode), "Inlined " + root);
-        }
-
-        @Override
-        public boolean inline(FrameFactory factory) {
-            inlineImpl();
-            /* SL is 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 = cachedFunction.getRootNode();
-            if (root instanceof FunctionRootNode) {
-                return ((FunctionRootNode) root).getUninitializedBody();
-            }
-            return null;
-        }
-
-        @Override
-        public CallTarget getCallTarget() {
-            return cachedFunction;
-        }
-
-    }
-
-    private static class InlinedDirectCallNode extends DirectCallNode implements InlinedCallSite {
-
-        private final FrameDescriptor descriptor;
-        @Child private TypedNode inlinedBody;
-
-        InlinedDirectCallNode(InlinableDirectCallNode prev, TypedNode inlinedBody) {
-            super(prev.functionNode, prev.argumentsNode, prev.cachedFunction, prev.nextNode);
-            this.descriptor = cachedFunction.getRootNode().getFrameDescriptor();
-            this.inlinedBody = adoptChild(inlinedBody);
-        }
-
-        @Override
-        public Object executeCurrent(VirtualFrame frame, Object[] arguments) {
-            SLArguments slArguments = new SLArguments(arguments);
-            VirtualFrame newFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), slArguments, descriptor);
-            return inlinedBody.executeGeneric(newFrame);
-        }
-
-        @Override
-        public CallTarget getCallTarget() {
-            return cachedFunction;
-        }
-
-    }
-
-    private static final class GenericCallNode extends CallNode {
-
-        GenericCallNode(TypedNode functionNode, ArgumentsNode arguments) {
-            super(functionNode, arguments);
-        }
-
-        @Override
-        public Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments) {
-            return function.call(frame.pack(), new SLArguments(arguments));
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionCallNode.java	Fri Jan 24 15:55:41 2014 +0100
@@ -0,0 +1,140 @@
+/*
+ * 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.nodes;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.impl.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.sl.runtime.*;
+
+public abstract class FunctionCallNode extends TypedNode {
+
+    private static final int INLINE_CACHE_SIZE = 2;
+
+    @Child protected TypedNode functionNode;
+    @Child protected ArgumentsNode argumentsNode;
+
+    public FunctionCallNode(TypedNode functionNode, ArgumentsNode argumentsNode) {
+        this.functionNode = adoptChild(functionNode);
+        this.argumentsNode = adoptChild(argumentsNode);
+    }
+
+    @Override
+    public final Object executeGeneric(VirtualFrame frame) {
+        CallTarget function;
+        try {
+            function = functionNode.executeCallTarget(frame);
+        } catch (UnexpectedResultException e) {
+            throw new UnsupportedOperationException("Call to " + e.getMessage() + " not supported.");
+        }
+        Object[] arguments = argumentsNode.executeArray(frame);
+        return executeCall(frame, function, new SLArguments(arguments));
+    }
+
+    public abstract Object executeCall(VirtualFrame frame, CallTarget function, SLArguments arguments);
+
+    public static FunctionCallNode create(TypedNode function, TypedNode[] arguments) {
+        return new UninitializedCallNode(function, new ArgumentsNode(arguments), 0);
+    }
+
+    private static final class UninitializedCallNode extends FunctionCallNode {
+
+        protected final int depth;
+
+        UninitializedCallNode(TypedNode function, ArgumentsNode args, int depth) {
+            super(function, args);
+            this.depth = depth;
+        }
+
+        UninitializedCallNode(UninitializedCallNode copy) {
+            super(null, null);
+            this.depth = copy.depth + 1;
+        }
+
+        @Override
+        public Object executeCall(VirtualFrame frame, CallTarget function, SLArguments arguments) {
+            CompilerDirectives.transferToInterpreter();
+            return specialize(function).executeCall(frame, function, arguments);
+        }
+
+        private FunctionCallNode specialize(CallTarget function) {
+            CompilerAsserts.neverPartOfCompilation();
+            if (depth < INLINE_CACHE_SIZE) {
+                return replace(new CachedCallNode(functionNode, argumentsNode, function, new UninitializedCallNode(this)));
+            } else {
+                FunctionCallNode topMost = (FunctionCallNode) NodeUtil.getNthParent(this, depth);
+                return topMost.replace(new GenericCallNode(topMost.functionNode, topMost.argumentsNode));
+            }
+        }
+
+    }
+
+    private static final class CachedCallNode extends FunctionCallNode {
+
+        protected final CallTarget cachedFunction;
+
+        @Child protected CallNode callNode;
+        @Child protected FunctionCallNode nextNode;
+
+        public CachedCallNode(TypedNode function, ArgumentsNode arguments, CallTarget cachedFunction, FunctionCallNode next) {
+            super(function, arguments);
+            this.cachedFunction = cachedFunction;
+            this.callNode = adoptChild(CallNode.create(cachedFunction));
+            this.nextNode = adoptChild(next);
+
+            // inline usually known functions that should always be inlined
+            if (findSLFunctionRoot(cachedFunction).isInlineImmediatly()) {
+                if (callNode.isInlinable() && !callNode.isInlined()) {
+                    callNode.inline();
+                }
+            }
+        }
+
+        @Override
+        public Object executeCall(VirtualFrame frame, CallTarget function, SLArguments arguments) {
+            if (this.cachedFunction == function) {
+                return callNode.call(frame.pack(), arguments);
+            }
+            return nextNode.executeCall(frame, function, arguments);
+        }
+
+        private static FunctionRootNode findSLFunctionRoot(CallTarget target) {
+            return (FunctionRootNode) ((DefaultCallTarget) target).getRootNode();
+        }
+
+    }
+
+    private static final class GenericCallNode extends FunctionCallNode {
+
+        GenericCallNode(TypedNode functionNode, ArgumentsNode arguments) {
+            super(functionNode, arguments);
+        }
+
+        @Override
+        public Object executeCall(VirtualFrame frame, CallTarget function, SLArguments arguments) {
+            return function.call(frame.pack(), arguments);
+        }
+    }
+
+}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionRootNode.java	Fri Jan 24 12:26:05 2014 +0100
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionRootNode.java	Fri Jan 24 15:55:41 2014 +0100
@@ -71,8 +71,19 @@
         return inlineImmediatly;
     }
 
-    public TypedNode inline() {
-        return NodeUtil.cloneNode(uninitializedBody);
+    @Override
+    public RootNode inline() {
+        return new FunctionRootNode(getFrameDescriptor().shallowCopy(), NodeUtil.cloneNode(uninitializedBody), name, inlineImmediatly);
+    }
+
+    @Override
+    public int getInlineNodeCount() {
+        return NodeUtil.countNodes(uninitializedBody);
+    }
+
+    @Override
+    public boolean isInlinable() {
+        return true;
     }
 
     public Node getUninitializedBody() {