changeset 14073:c5411233cdf8

Truffle: Now keeps track of all not just inlined call-sites called by CallNode. Deprecated some old API in NodeUtil.
author Christian Humer <christian.humer@gmail.com>
date Wed, 05 Mar 2014 23:33:25 +0100
parents 036e61d4cebd
children ed4b5d1ef667
files graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNodeProfile.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallNode.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/GraphPrintVisitor.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 9 files changed, 195 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java	Wed Mar 05 21:37:50 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java	Wed Mar 05 23:33:25 2014 +0100
@@ -65,6 +65,11 @@
     }
 
     @Override
+    public final OptimizedCallTarget getCurrentCallTarget() {
+        return (OptimizedCallTarget) super.getCurrentCallTarget();
+    }
+
+    @Override
     public OptimizedCallTarget getSplitCallTarget() {
         return null;
     }
@@ -74,11 +79,7 @@
             throw new IllegalStateException("CallNode must be adopted before it is split.");
         }
 
-        return replace(new InlinedOptimizedCallNode(getCallTarget(), getSplitCallTarget(), getExecutedCallTarget().getRootNode(), callCount));
-    }
-
-    public final OptimizedCallTarget getExecutedCallTarget() {
-        return getSplitCallTarget() != null ? getSplitCallTarget() : getCallTarget();
+        return replace(new InlinedOptimizedCallNode(getCallTarget(), getSplitCallTarget(), getCurrentCallTarget().getRootNode(), callCount));
     }
 
     public static OptimizedCallNode create(OptimizedCallTarget target) {
@@ -126,7 +127,7 @@
             int nodeCount = NodeUtil.countNodes(getCallTarget().getRootNode(), null, false);
 
             // max one child call and callCount > 2 and kind of small number of nodes
-            if (callCount > 2 && isCallMethod()) {
+            if (callCount > 2 && isMaxSingleCall()) {
                 if (nodeCount <= 100) {
                     return true;
                 }
@@ -138,9 +139,9 @@
             return countPolymorphic() > 1 || countGeneric() > 0;
         }
 
-        private boolean isCallMethod() {
+        private boolean isMaxSingleCall() {
             final AtomicInteger count = new AtomicInteger(0);
-            getExecutedCallTarget().getRootNode().accept(new NodeVisitor() {
+            getCurrentCallTarget().getRootNode().accept(new NodeVisitor() {
 
                 public boolean visit(Node node) {
                     if (node instanceof CallNode) {
@@ -179,8 +180,7 @@
         }
 
         private OptimizedCallNode splitImpl(boolean heuristic) {
-            RootNode splittedRoot = getCallTarget().getRootNode().split();
-            OptimizedCallTarget splitCallTarget = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(splittedRoot);
+            OptimizedCallTarget splitCallTarget = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(getCallTarget().getRootNode().split());
             splitCallTarget.setSplitSource(getCallTarget());
             if (heuristic) {
                 OptimizedCallTarget.logSplit(this, getCallTarget(), splitCallTarget);
@@ -210,7 +210,6 @@
             this.inlinedRoot = inlinedRoot;
             this.splittedTarget = splittedTarget;
             this.callCount = callCount;
-            installParentInlinedCall();
         }
 
         @Override
@@ -236,11 +235,6 @@
         }
 
         @Override
-        public RootNode getInlinedRoot() {
-            return inlinedRoot;
-        }
-
-        @Override
         public OptimizedCallTarget getSplitCallTarget() {
             return splittedTarget;
         }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNodeProfile.java	Wed Mar 05 21:37:50 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNodeProfile.java	Wed Mar 05 23:33:25 2014 +0100
@@ -47,7 +47,7 @@
 
     public OptimizedCallNodeProfile(OptimizedCallTarget target, OptimizedCallNode callNode) {
         this.callNode = callNode;
-        RootNode inlineRoot = callNode.getExecutedCallTarget().getRootNode();
+        RootNode inlineRoot = callNode.getCurrentCallTarget().getRootNode();
         this.callTarget = target;
         this.targetShallowNodeCount = NodeUtil.countNodes(inlineRoot, null, false);
         this.targetDeepNodeCount = NodeUtil.countNodes(inlineRoot, null, true);
@@ -75,7 +75,7 @@
     public boolean isInliningAllowed() {
         this.compilationRoots = findCompilationRoots(getCallNode());
 
-        OptimizedCallTarget inlineTarget = callNode.getExecutedCallTarget();
+        OptimizedCallTarget inlineTarget = callNode.getCurrentCallTarget();
         for (OptimizedCallTarget compilationRoot : compilationRoots) {
             if (compilationRoot == inlineTarget) {
                 // recursive call found
@@ -109,11 +109,9 @@
     private int calculateInlinedTotalNodeCount(OptimizedCallNode node) {
         int currentNodeCount = 0;
         for (OptimizedCallTarget compilationRoot : compilationRoots) {
-            if (compilationRoot.getRootNode().getParentInlinedCalls().isEmpty()) {
-                TotalNodeCountVisitor visitor = new TotalNodeCountVisitor(node, targetDeepNodeCount);
-                compilationRoot.getRootNode().accept(visitor);
-                currentNodeCount = Math.max(currentNodeCount, visitor.count);
-            }
+            TotalNodeCountVisitor visitor = new TotalNodeCountVisitor(node, targetDeepNodeCount);
+            compilationRoot.getRootNode().accept(visitor);
+            currentNodeCount = Math.max(currentNodeCount, visitor.count);
         }
         return currentNodeCount;
     }
@@ -132,12 +130,12 @@
 
         public boolean visit(Node node) {
             count++;
-            if (node instanceof CallNode) {
-                RootNode inlinedRoot = ((CallNode) node).getInlinedRoot();
-                if (inlinedRoot != null) {
-                    inlinedRoot.accept(this);
-                } else if (node == inlinedNode) {
+            if (node instanceof OptimizedCallNode) {
+                OptimizedCallNode callNode = ((OptimizedCallNode) node);
+                if (callNode == inlinedNode) {
                     count += inlinedNodeCount;
+                } else if (callNode.isInlined()) {
+                    callNode.getCurrentRootNode().accept(this);
                 }
             }
             return true;
@@ -149,18 +147,6 @@
         return callNode.getCallCount() / (double) callTarget.getCompilationProfile().getCallCount();
     }
 
-    double calculateAverageFrequency(List<OptimizedCallTarget> roots) {
-        int compilationRootCallCountSum = 0;
-        int compilationRootCount = 0;
-        for (OptimizedCallTarget compilationRoot : roots) {
-            if (compilationRoot.getRootNode().getParentInlinedCalls().isEmpty()) {
-                compilationRootCallCountSum += compilationRoot.getCompilationProfile().getCallCount();
-                compilationRootCount++;
-            }
-        }
-        return (callNode.getCallCount() * compilationRootCount) / (double) compilationRootCallCountSum;
-    }
-
     private static List<OptimizedCallTarget> findCompilationRoots(Node call) {
         RootNode root = call.getRootNode();
         if (root == null) {
@@ -168,8 +154,10 @@
         }
         List<OptimizedCallTarget> roots = new ArrayList<>();
         roots.add((OptimizedCallTarget) root.getCallTarget());
-        for (CallNode callNode : root.getParentInlinedCalls()) {
-            roots.addAll(findCompilationRoots(callNode));
+        for (CallNode callNode : root.getCachedCallNodes()) {
+            if (callNode.isInlined()) {
+                roots.addAll(findCompilationRoots(callNode));
+            }
         }
         return roots;
     }
@@ -183,7 +171,7 @@
 
     public Map<String, Object> getDebugProperties() {
         Map<String, Object> properties = new LinkedHashMap<>();
-        OptimizedCallTarget.addASTSizeProperty(getCallNode().getExecutedCallTarget().getRootNode(), properties);
+        OptimizedCallTarget.addASTSizeProperty(getCallNode().getCurrentRootNode().getRootNode(), properties);
         properties.put("shallowCount", targetShallowNodeCount);
         properties.put("currentCount", calculateInlinedTotalNodeCount(null));
         properties.put("inlinedTotalCount", calculateInlinedTotalNodeCount(getCallNode()));
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Wed Mar 05 21:37:50 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Wed Mar 05 23:33:25 2014 +0100
@@ -72,6 +72,10 @@
         }
     }
 
+    public OptimizedCallTarget getSplitSource() {
+        return splitSource;
+    }
+
     public void setSplitSource(OptimizedCallTarget splitSource) {
         this.splitSource = splitSource;
     }
@@ -193,7 +197,7 @@
             if (callSite.isInliningAllowed()) {
                 OptimizedCallNode callNode = callSite.getCallNode();
                 logInlined(this, callSite);
-                RootNode inlinedRoot = callNode.inlineImpl().getInlinedRoot();
+                RootNode inlinedRoot = callNode.inlineImpl().getCurrentRootNode();
                 assert inlinedRoot != null;
                 queueCallSitesForInlining(this, inlinedRoot, visitedCallNodes, queue);
             } else {
@@ -212,8 +216,10 @@
                     if (call.isInlinable() && !call.isInlined() && !visitedCallSites.contains(call)) {
                         queue.add(call.createInliningProfile(target));
                         visitedCallSites.add(call);
-                    } else if (call.getInlinedRoot() != null) {
-                        call.getInlinedRoot().accept(this);
+                    }
+                    RootNode root = call.getCurrentRootNode();
+                    if (root != null && call.isInlined()) {
+                        root.accept(this);
                     }
                 }
                 return true;
@@ -282,12 +288,29 @@
     @Override
     public void reportLoopCount(int count) {
         compilationProfile.reportLoopCount(count);
+
+        // delegate to inlined call sites
+        for (CallNode callNode : getRootNode().getCachedCallNodes()) {
+            if (callNode.isInlined()) {
+                callNode.getRootNode().reportLoopCount(count);
+            }
+        }
     }
 
     @Override
     public void nodeReplaced(Node oldNode, Node newNode, String reason) {
         compilationProfile.reportNodeReplaced();
         invalidate(oldNode, newNode, reason);
+
+        // delegate to inlined call sites
+        for (CallNode callNode : getRootNode().getCachedCallNodes()) {
+            if (callNode.isInlined()) {
+                CallTarget target = callNode.getRootNode().getCallTarget();
+                if (target instanceof ReplaceObserver) {
+                    ((ReplaceObserver) target).nodeReplaced(oldNode, newNode, reason);
+                }
+            }
+        }
     }
 
     public SpeculationLog getSpeculationLog() {
@@ -304,25 +327,28 @@
 
     private static void logInliningFailed(TruffleInliningProfile callSite) {
         if (TraceTruffleInliningDetails.getValue()) {
-            log(2, "inline failed", callSite.getCallNode().getExecutedCallTarget().toString(), callSite.getDebugProperties());
+            log(2, "inline failed", callSite.getCallNode().getCurrentCallTarget().toString(), callSite.getDebugProperties());
         }
     }
 
-    private static void logInlined(final OptimizedCallTarget target, TruffleInliningProfile callSite) {
+    private static void logInlined(@SuppressWarnings("unused") final OptimizedCallTarget target, TruffleInliningProfile callSite) {
         if (TraceTruffleInliningDetails.getValue() || TraceTruffleInlining.getValue()) {
-            log(2, "inline success", callSite.getCallNode().getExecutedCallTarget().toString(), callSite.getDebugProperties());
+            log(2, "inline success", callSite.getCallNode().getCurrentCallTarget().toString(), callSite.getDebugProperties());
 
             if (TraceTruffleInliningDetails.getValue()) {
-                RootNode root = callSite.getCallNode().getExecutedCallTarget().getRootNode();
+                RootNode root = callSite.getCallNode().getCurrentCallTarget().getRootNode();
                 root.accept(new NodeVisitor() {
                     int depth = 1;
 
                     public boolean visit(Node node) {
                         if (node instanceof OptimizedCallNode) {
                             OptimizedCallNode callNode = ((OptimizedCallNode) node);
-                            log(2 + (depth * 2), "inline success", callNode.getExecutedCallTarget().toString(), callNode.createInliningProfile(target).getDebugProperties());
-                            RootNode inlinedRoot = callNode.getInlinedRoot();
-                            if (inlinedRoot != null) {
+                            RootNode inlinedRoot = callNode.getCurrentRootNode();
+
+                            if (inlinedRoot != null && callNode.isInlined()) {
+                                Map<String, Object> properties = new LinkedHashMap<>();
+                                addASTSizeProperty(callNode.getCurrentRootNode(), properties);
+                                log(2 + (depth * 2), "inline success", callNode.getCurrentCallTarget().toString(), properties);
                                 depth++;
                                 inlinedRoot.accept(this);
                                 depth--;
@@ -487,7 +513,10 @@
         for (CallNode callNode : callers) {
             if (callNode.isInlined()) {
                 count++;
-                count += countInlinedNodes(callNode.getInlinedRoot());
+                RootNode root = callNode.getCurrentRootNode();
+                if (root != null) {
+                    count += countInlinedNodes(root);
+                }
             }
         }
         return count;
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallNode.java	Wed Mar 05 21:37:50 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallNode.java	Wed Mar 05 23:33:25 2014 +0100
@@ -64,11 +64,6 @@
     }
 
     @Override
-    public RootNode getInlinedRoot() {
-        return null;
-    }
-
-    @Override
     public boolean isInlined() {
         return false;
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Wed Mar 05 21:37:50 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Wed Mar 05 23:33:25 2014 +0100
@@ -31,7 +31,7 @@
  * 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. This class is
- * intended to be implemented by truffle runtime implementors and not by guest languague
+ * intended to be implemented by truffle runtime implementors and not by guest language
  * implementors.
  * 
  * @see #create(CallTarget) to create a CallNode instance.
@@ -45,13 +45,6 @@
     }
 
     /**
-     * @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
@@ -60,6 +53,17 @@
      */
     public abstract Object call(PackedFrame caller, Arguments arguments);
 
+    /**
+     * Returns the originally supplied {@link CallTarget} when this call node was created. Please
+     * note that the returned {@link CallTarget} is not necessarily the {@link CallTarget} that is
+     * called. For that use {@link #getCurrentCallTarget()} instead.
+     * 
+     * @return the {@link CallTarget} provided.
+     */
+    public CallTarget getCallTarget() {
+        return callTarget;
+    }
+
     public abstract boolean isInlinable();
 
     /**
@@ -73,9 +77,78 @@
 
     public abstract boolean split();
 
+    public final boolean isSplit() {
+        return getSplitCallTarget() != null;
+    }
+
     public abstract CallTarget getSplitCallTarget();
 
-    public abstract RootNode getInlinedRoot();
+    /**
+     * Returns the used call target when {@link #call(PackedFrame, Arguments)} is invoked. If the
+     * {@link CallNode} was split this method returns the {@link CallTarget} returned by
+     * {@link #getSplitCallTarget()}. If not split this method returns the original supplied
+     * {@link CallTarget}.
+     * 
+     * @return the used {@link CallTarget} when node is called
+     */
+    public CallTarget getCurrentCallTarget() {
+        CallTarget split = getSplitCallTarget();
+        if (split != null) {
+            return split;
+        } else {
+            return getCallTarget();
+        }
+    }
+
+    @Override
+    protected void onReplace(Node newNode, String reason) {
+        super.onReplace(newNode, reason);
+
+        /*
+         * Old call nodes are removed in the old target root node.
+         */
+        CallNode oldCall = this;
+        RootNode oldRoot = getCurrentRootNode();
+        if (oldRoot != null) {
+            oldRoot.removeCachedCallNode(oldCall);
+        }
+
+        /*
+         * New call nodes are registered in the new target root node.
+         */
+        CallNode newCall = (CallNode) newNode;
+        RootNode newRoot = newCall.getCurrentRootNode();
+        if (newRoot != null) {
+            newRoot.addCachedCallNode(newCall);
+        }
+    }
+
+    /**
+     * Returns the {@link RootNode} associated with {@link CallTarget} returned by
+     * {@link #getCurrentCallTarget()}.
+     * 
+     * @see #getCurrentCallTarget()
+     * @return the root node of the used call target
+     */
+    public final RootNode getCurrentRootNode() {
+        CallTarget target = getCurrentCallTarget();
+        if (target instanceof RootCallTarget) {
+            return ((RootCallTarget) target).getRootNode();
+        }
+        return null;
+    }
+
+    /**
+     * @deprecated instead use {@link #getCurrentRootNode()} and check for {@link #isInlined()} for
+     *             true.
+     */
+    @Deprecated
+    public RootNode getInlinedRoot() {
+        if (!isInlined()) {
+            return null;
+        }
+        return getCurrentRootNode();
+    }
 
     /**
      * Creates a new {@link CallNode} using a {@link CallTarget}.
@@ -89,8 +162,4 @@
         return Truffle.getRuntime().createCallNode(target);
     }
 
-    protected final void installParentInlinedCall() {
-        getInlinedRoot().addParentInlinedCall(this);
-    }
-
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java	Wed Mar 05 21:37:50 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java	Wed Mar 05 23:33:25 2014 +0100
@@ -344,8 +344,9 @@
         NodeClass nodeClass = NodeClass.get(node.getClass());
 
         if (node instanceof CallNode) {
-            RootNode inlinedRoot = ((CallNode) node).getInlinedRoot();
-            if (inlinedRoot != null) {
+            CallNode callNode = ((CallNode) node);
+            RootNode inlinedRoot = callNode.getCurrentRootNode();
+            if (inlinedRoot != null && callNode.isInlined()) {
                 nodes.put("inlinedRoot", inlinedRoot);
             }
         }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed Mar 05 21:37:50 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed Mar 05 23:33:25 2014 +0100
@@ -254,8 +254,9 @@
     }
 
     private void reportReplace(Node oldNode, Node newNode, String reason) {
-        Collection<CallTarget> targets = NodeUtil.findOutermostCallTargets(this);
-        for (CallTarget target : targets) {
+        RootNode rootNode = getRootNode();
+        if (rootNode != null) {
+            CallTarget target = rootNode.getCallTarget();
             if (target instanceof ReplaceObserver) {
                 ((ReplaceObserver) target).nodeReplaced(oldNode, newNode, reason);
             }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Mar 05 21:37:50 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Mar 05 23:33:25 2014 +0100
@@ -452,6 +452,10 @@
         return null;
     }
 
+    /**
+     * @deprecated will be removed, exposed Truffle runtime specific functionality.
+     */
+    @Deprecated
     public static List<CallTarget> findOutermostCallTargets(Node node) {
         RootNode root = node.getRootNode();
         if (root == null) {
@@ -459,19 +463,16 @@
         }
         List<CallTarget> roots = new ArrayList<>();
         roots.add(root.getCallTarget());
-        for (CallNode callNode : root.getParentInlinedCalls()) {
-            roots.addAll(findOutermostCallTargets(callNode));
+        for (CallNode callNode : root.getCachedCallNodes()) {
+            if (callNode.isInlined()) {
+                roots.addAll(findOutermostCallTargets(callNode));
+            }
         }
         return roots;
     }
 
     /**
-     * Returns the outermost not inlined {@link RootNode} which is a parent of this node.
-     * 
-     * @see RootNode#getParentInlinedCalls()
-     * @param node to search
-     * @return the outermost {@link RootNode}
-     * @deprecated use {@link #findOutermostCallTargets(Node)}
+     * @deprecated will be removed, exposed Truffle runtime specific functionality.
      */
     @Deprecated
     public static RootNode findOutermostRootNode(Node node) {
@@ -617,7 +618,7 @@
     }
 
     public static int countNodes(Node root, Class<?> clazz, Kind nodeKind, boolean countInlinedCallNodes) {
-        NodeCountVisitor nodeCount = new NodeCountVisitor(root, clazz, nodeKind, countInlinedCallNodes);
+        NodeCountVisitor nodeCount = new NodeCountVisitor(clazz, nodeKind, countInlinedCallNodes);
         root.accept(nodeCount);
         return nodeCount.nodeCount;
     }
@@ -628,14 +629,12 @@
 
     private static final class NodeCountVisitor implements NodeVisitor {
 
-        private Node root;
         private final boolean inspectInlinedCalls;
         int nodeCount;
         private final Kind kind;
         private final Class<?> clazz;
 
-        private NodeCountVisitor(Node root, Class<?> clazz, Kind kind, boolean inspectInlinedCalls) {
-            this.root = root;
+        private NodeCountVisitor(Class<?> clazz, Kind kind, boolean inspectInlinedCalls) {
             this.clazz = clazz;
             this.kind = kind;
             this.inspectInlinedCalls = inspectInlinedCalls;
@@ -643,10 +642,6 @@
 
         @Override
         public boolean visit(Node node) {
-            if (node instanceof RootNode && node != root) {
-                return false;
-            }
-
             if ((clazz == null || clazz.isInstance(node)) && (kind == null || isKind(node))) {
                 nodeCount++;
             }
@@ -654,7 +649,10 @@
             if (inspectInlinedCalls && node instanceof CallNode) {
                 CallNode call = (CallNode) node;
                 if (call.isInlined()) {
-                    call.getInlinedRoot().getChildren().iterator().next().accept(this);
+                    Node target = ((RootCallTarget) call.getCurrentCallTarget()).getRootNode();
+                    if (target != null) {
+                        target.accept(this);
+                    }
                 }
             }
 
@@ -673,8 +671,9 @@
 
             public boolean visit(Node node) {
                 if (node instanceof CallNode) {
-                    RootNode inlinedRoot = ((CallNode) node).getInlinedRoot();
-                    if (inlinedRoot != null) {
+                    CallNode callNode = ((CallNode) node);
+                    RootNode inlinedRoot = callNode.getCurrentRootNode();
+                    if (inlinedRoot != null && callNode.isInlined()) {
                         depth++;
                         printRootNode(stream, depth * 2, inlinedRoot);
                         inlinedRoot.accept(this);
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Wed Mar 05 21:37:50 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Wed Mar 05 23:33:25 2014 +0100
@@ -27,7 +27,6 @@
 import java.util.*;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.*;
 import com.oracle.truffle.api.frame.*;
 
 /**
@@ -41,10 +40,9 @@
     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.
+     * Internal set to keep back-references to the call-sites.
      */
-    @CompilationFinal private List<CallNode> parentInlinedCalls = new ArrayList<>();
+    private final Set<CallNode> cachedCallNodes = Collections.newSetFromMap(new WeakHashMap<CallNode, Boolean>());
 
     protected RootNode() {
         this(null, null);
@@ -103,11 +101,8 @@
      * heuristics can use the loop count to guide compilation and inlining.
      */
     public void reportLoopCount(int count) {
-        List<CallTarget> callTargets = NodeUtil.findOutermostCallTargets(this);
-        for (CallTarget target : callTargets) {
-            if (target instanceof LoopCountReceiver) {
-                ((LoopCountReceiver) target).reportLoopCount(count);
-            }
+        if (getCallTarget() instanceof LoopCountReceiver) {
+            ((LoopCountReceiver) getCallTarget()).reportLoopCount(count);
         }
     }
 
@@ -127,24 +122,37 @@
         return frameDescriptor;
     }
 
-    public void setCallTarget(CallTarget callTarget) {
+    public final void setCallTarget(CallTarget callTarget) {
         this.callTarget = callTarget;
     }
 
     /* Internal API. Do not use. */
-    void addParentInlinedCall(CallNode inlinedParent) {
-        this.parentInlinedCalls.add(inlinedParent);
+    void addCachedCallNode(CallNode callSite) {
+        this.cachedCallNodes.add(callSite);
     }
 
-    public final List<CallNode> getParentInlinedCalls() {
-        return Collections.unmodifiableList(parentInlinedCalls);
+    /* Internal API. Do not use. */
+    void removeCachedCallNode(CallNode callSite) {
+        this.cachedCallNodes.remove(callSite);
     }
 
     /**
-     * @deprecated use {@link #getParentInlinedCalls()} instead.
+     * Returns a {@link Set} of {@link CallNode} nodes which are created to invoke this RootNode.
+     * This method does not make any guarantees to contain all the {@link CallNode} nodes that are
+     * invoking this method. Due to its weak nature the elements returned by this method may change
+     * with each consecutive call.
+     * 
+     * @return a set of {@link CallNode} nodes
+     */
+    public final Set<CallNode> getCachedCallNodes() {
+        return Collections.unmodifiableSet(cachedCallNodes);
+    }
+
+    /**
+     * @deprecated use {@link #getCachedCallNodes()} instead.
      */
     @Deprecated
     public final CallNode getParentInlinedCall() {
-        return parentInlinedCalls.isEmpty() ? null : parentInlinedCalls.get(0);
+        return cachedCallNodes.isEmpty() ? null : cachedCallNodes.iterator().next();
     }
 }