changeset 13983:f46cab39a9a2

Truffle: Updated inlining API. Pushed inlining implementation to the Truffle runtime.
author Christian Humer <christian.humer@gmail.com>
date Thu, 20 Feb 2014 01:21:49 +0100
parents 39076a984c33
children 1c9dbfc5b510
files graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.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/Node.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java
diffstat 8 files changed, 168 insertions(+), 260 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java	Wed Feb 19 00:39:44 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ReplaceObserver.java	Thu Feb 20 01:21:49 2014 +0100
@@ -24,10 +24,12 @@
  */
 package com.oracle.truffle.api;
 
+import com.oracle.truffle.api.nodes.*;
+
 /**
  * An observer that is notified whenever a child node is replaced.
  */
 public interface ReplaceObserver {
 
-    void nodeReplaced();
+    void nodeReplaced(Node oldNode, Node newNode, String reason);
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java	Wed Feb 19 00:39:44 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java	Thu Feb 20 01:21:49 2014 +0100
@@ -50,6 +50,8 @@
      */
     RootCallTarget createCallTarget(RootNode rootNode);
 
+    CallNode createCallNode(CallTarget target);
+
     /**
      * Creates a new assumption object that can be checked and invalidated.
      * 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallNode.java	Thu Feb 20 01:21:49 2014 +0100
@@ -0,0 +1,80 @@
+/*
+ * 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.impl;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+public class DefaultCallNode extends CallNode {
+
+    public DefaultCallNode(CallTarget target) {
+        super(target);
+    }
+
+    @Override
+    public Object call(PackedFrame caller, Arguments arguments) {
+        return getCallTarget().call(caller, arguments);
+    }
+
+    @Override
+    public void inline() {
+    }
+
+    @Override
+    public CallTarget getSplitCallTarget() {
+        return null;
+    }
+
+    @Override
+    public boolean split() {
+        return false;
+    }
+
+    @Override
+    public boolean isSplittable() {
+        return false;
+    }
+
+    @Override
+    public boolean isInlinable() {
+        return false;
+    }
+
+    @Override
+    public RootNode getInlinedRoot() {
+        return null;
+    }
+
+    @Override
+    public boolean isInlined() {
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return getParent() != null ? getParent().toString() : super.toString();
+    }
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.java	Wed Feb 19 00:39:44 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.java	Thu Feb 20 01:21:49 2014 +0100
@@ -53,6 +53,10 @@
         return new DefaultCallTarget(rootNode);
     }
 
+    public CallNode createCallNode(CallTarget target) {
+        return new DefaultCallNode(target);
+    }
+
     @Override
     public VirtualFrame createVirtualFrame(PackedFrame caller, Arguments arguments, FrameDescriptor frameDescriptor) {
         return new DefaultVirtualFrame(frameDescriptor, caller, arguments);
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Wed Feb 19 00:39:44 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Thu Feb 20 01:21:49 2014 +0100
@@ -26,12 +26,13 @@
 
 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.
+ * Truffle runtime to perform inlining or other optimizations for this call-site. This class is
+ * intended to be implemented by truffle runtime implementors and not by guest languague
+ * implementors.
  * 
  * @see #create(CallTarget) to create a CallNode instance.
  */
@@ -39,7 +40,7 @@
 
     protected final CallTarget callTarget;
 
-    private CallNode(CallTarget callTarget) {
+    protected CallNode(CallTarget callTarget) {
         this.callTarget = callTarget;
     }
 
@@ -59,12 +60,6 @@
      */
     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();
 
     /**
@@ -72,209 +67,30 @@
      */
     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();
+    public abstract void inline();
+
+    public abstract boolean isSplittable();
 
-    /**
-     * 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;
-    }
+    public abstract boolean split();
+
+    public abstract CallTarget getSplitCallTarget();
+
+    public abstract RootNode getInlinedRoot();
 
     /**
      * 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((RootCallTarget) target);
-        } else {
-            return new DefaultCallNode(target);
-        }
-    }
-
-    /**
-     * Warning: this is internal API and may change without notice.
-     */
-    public interface CompilerCallView {
-
-        int getCallCount();
-
-        void resetCallCount();
-
-        void store(Object value);
-
-        Object load();
-    }
-
-    /**
-     * Warning: this is internal API and may change without notice.
+     * @deprecated use {@link TruffleRuntime#createCallNode(CallTarget)} instead
      */
-    public CompilerCallView getCompilerCallView() {
-        return null;
-    }
-
-    private static boolean isInlinable(CallTarget callTarget) {
-        if (callTarget instanceof RootCallTarget) {
-            return (((RootCallTarget) 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;
-        }
-
+    @Deprecated
+    public static CallNode create(CallTarget target) {
+        return Truffle.getRuntime().createCallNode(target);
     }
 
-    static final class InlinableCallNode extends CallNode implements CompilerCallView {
-
-        private int callCount;
-
-        public InlinableCallNode(RootCallTarget 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;
-        }
-
-        @Override
-        public CompilerCallView getCompilerCallView() {
-            return this;
-        }
-
-        /* Truffle internal API. */
-        public int getCallCount() {
-            return callCount;
-        }
-
-        /* Truffle internal API. */
-        public void resetCallCount() {
-            callCount = 0;
-        }
-
-        private Object storedCompilerInfo;
-
-        public void store(Object value) {
-            this.storedCompilerInfo = value;
-        }
-
-        public Object load() {
-            return storedCompilerInfo;
-        }
-
-    }
-
-    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;
-        }
-
+    protected final void installParentInlinedCall() {
+        getInlinedRoot().addParentInlinedCall(this);
     }
 
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed Feb 19 00:39:44 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Thu Feb 20 01:21:49 2014 +0100
@@ -185,7 +185,7 @@
         if (!NodeUtil.replaceChild(this.parent, this, newNode)) {
             fixupTree();
         }
-        reportReplace();
+        reportReplace(this, newNode, reason);
         return newNode;
     }
 
@@ -245,11 +245,11 @@
         return false;
     }
 
-    private void reportReplace() {
-        RootNode rootNode = NodeUtil.findOutermostRootNode(this);
-        if (rootNode != null) {
-            if (rootNode.getCallTarget() instanceof ReplaceObserver) {
-                ((ReplaceObserver) rootNode.getCallTarget()).nodeReplaced();
+    private void reportReplace(Node oldNode, Node newNode, String reason) {
+        Collection<CallTarget> targets = NodeUtil.findOutermostCallTargets(this);
+        for (CallTarget target : targets) {
+            if (target instanceof ReplaceObserver) {
+                ((ReplaceObserver) target).nodeReplaced(oldNode, newNode, reason);
             }
         }
     }
@@ -395,7 +395,7 @@
      * 
      * @return the {@link RootNode} or {@code null} if there is none.
      */
-    protected final RootNode getRootNode() {
+    public final RootNode getRootNode() {
         Node rootNode = this;
         while (rootNode.getParent() != null) {
             assert !(rootNode instanceof RootNode) : "root node must not have a parent";
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Feb 19 00:39:44 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Thu Feb 20 01:21:49 2014 +0100
@@ -451,13 +451,28 @@
         return null;
     }
 
+    public static List<CallTarget> findOutermostCallTargets(Node node) {
+        RootNode root = node.getRootNode();
+        if (root == null) {
+            return Collections.emptyList();
+        }
+        List<CallTarget> roots = new ArrayList<>();
+        roots.add(root.getCallTarget());
+        for (CallNode callNode : root.getParentInlinedCalls()) {
+            roots.addAll(findOutermostCallTargets(callNode));
+        }
+        return roots;
+    }
+
     /**
      * Returns the outermost not inlined {@link RootNode} which is a parent of this node.
      * 
-     * @see RootNode#getParentInlinedCall()
+     * @see RootNode#getParentInlinedCalls()
      * @param node to search
      * @return the outermost {@link RootNode}
+     * @deprecated use {@link #findOutermostCallTargets(Node)}
      */
+    @Deprecated
     public static RootNode findOutermostRootNode(Node node) {
         Node parent = node;
         while (parent != null) {
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Wed Feb 19 00:39:44 2014 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Thu Feb 20 01:21:49 2014 +0100
@@ -24,6 +24,8 @@
  */
 package com.oracle.truffle.api.nodes;
 
+import java.util.*;
+
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.*;
 import com.oracle.truffle.api.frame.*;
@@ -42,7 +44,7 @@
      * 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;
+    @CompilationFinal private List<CallNode> parentInlinedCalls = new ArrayList<>();
 
     protected RootNode() {
         this(null, null);
@@ -62,55 +64,38 @@
     }
 
     /**
-     * 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
+     * @deprecated Not required anymore. Do not use.
      */
+    @Deprecated
     public RootNode inline() {
         if (!isInlinable()) {
             throw new UnsupportedOperationException("Inlining is not enabled.");
         }
-        return NodeUtil.cloneNode(this);
+        return split();
+    }
+
+    /**
+     * @deprecated Not required anymore. Do not use.
+     */
+    @Deprecated
+    public int getInlineNodeCount() {
+        return 0;
     }
 
     /**
-     * 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
+     * @deprecated Not required anymore. Do not use.
      */
-    public int getInlineNodeCount() {
-        if (!isInlinable()) {
-            throw new UnsupportedOperationException("Inlining is not enabled.");
-        }
-        return NodeUtil.countNodes(this);
+    @Deprecated
+    public boolean isInlinable() {
+        return true;
     }
 
-    /**
-     * 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;
+    public RootNode split() {
+        return NodeUtil.cloneNode(this);
+    }
+
+    public boolean isSplittable() {
+        return false;
     }
 
     /**
@@ -118,8 +103,11 @@
      * heuristics can use the loop count to guide compilation and inlining.
      */
     public void reportLoopCount(int count) {
-        if (getCallTarget() instanceof LoopCountReceiver) {
-            ((LoopCountReceiver) getCallTarget()).reportLoopCount(count);
+        List<CallTarget> callTargets = NodeUtil.findOutermostCallTargets(this);
+        for (CallTarget target : callTargets) {
+            if (target instanceof LoopCountReceiver) {
+                ((LoopCountReceiver) target).reportLoopCount(count);
+            }
         }
     }
 
@@ -144,18 +132,19 @@
     }
 
     /* Internal API. Do not use. */
-    void setParentInlinedCall(CallNode inlinedParent) {
-        this.parentInlinedCall = inlinedParent;
+    void addParentInlinedCall(CallNode inlinedParent) {
+        this.parentInlinedCalls.add(inlinedParent);
+    }
+
+    public final List<CallNode> getParentInlinedCalls() {
+        return Collections.unmodifiableList(parentInlinedCalls);
     }
 
     /**
-     * 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.
+     * @deprecated use {@link #getParentInlinedCalls()} instead.
      */
+    @Deprecated
     public final CallNode getParentInlinedCall() {
-        return parentInlinedCall;
+        return parentInlinedCalls.isEmpty() ? null : parentInlinedCalls.get(0);
     }
 }