changeset 15089:448338c9ce96

Truffle: Made inlining context-insensitive again to reduce complexity.
author Christian Humer <christian.humer@gmail.com>
date Mon, 14 Apr 2014 18:25:23 +0200
parents d3add9b82b71
children 07e7aae05983
files CHANGELOG.md graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotOptimizedCallTarget.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultInliningPolicy.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallUtils.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCallPath.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningHandler.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningPolicy.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningResult.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleTreeDumpHandler.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/NodeUtil.java
diffstat 17 files changed, 295 insertions(+), 708 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG.md	Mon Apr 14 18:20:09 2014 +0200
+++ b/CHANGELOG.md	Mon Apr 14 18:25:23 2014 +0200
@@ -12,6 +12,7 @@
 
 ### Truffle
 * Support for collecting stack traces and for accessing the current frame in slow paths
+* CallNode#inline was renamed to CallNode#forceInlining().
 * ...
 
 ## Version 0.2
--- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotOptimizedCallTarget.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotOptimizedCallTarget.java	Mon Apr 14 18:25:23 2014 +0200
@@ -97,15 +97,13 @@
         if (m != null) {
             CompilerAsserts.neverPartOfCompilation();
             installedCode = null;
-            inliningResult = null;
             compilationProfile.reportInvalidated();
             logOptimizedInvalidated(this, oldNode, newNode, reason);
         }
         cancelInstalledTask(oldNode, newNode, reason);
     }
 
-    @Override
-    protected void cancelInstalledTask(Node oldNode, Node newNode, CharSequence reason) {
+    private void cancelInstalledTask(Node oldNode, Node newNode, CharSequence reason) {
         Future<InstalledCode> task = this.installedCodeTask;
         if (task != null) {
             task.cancel(true);
@@ -154,7 +152,7 @@
             return null;
         } else {
             performInlining();
-            cancelInlinedCallOptimization();
+// cancelInlinedCallOptimization();
             logOptimizingQueued(this);
             this.installedCodeTask = compiler.compile(this);
             if (!TruffleBackgroundCompilation.getValue()) {
@@ -181,8 +179,6 @@
                 }
             }
             return null;
-        } finally {
-            onCompilationDone();
         }
     }
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultInliningPolicy.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultInliningPolicy.java	Mon Apr 14 18:25:23 2014 +0200
@@ -27,29 +27,25 @@
 public class DefaultInliningPolicy implements TruffleInliningPolicy {
 
     private static final String REASON_RECURSION = "recursion";
-    private static final String REASON_MAXIMUM_NODE_COUNT = "nodeCount * callSites  > " + TruffleInliningMaxCallerSize.getValue();
+    private static final String REASON_MAXIMUM_NODE_COUNT = "deepNodeCount * callSites  > " + TruffleInliningMaxCallerSize.getValue();
     private static final String REASON_MAXIMUM_TOTAL_NODE_COUNT = "totalNodeCount > " + TruffleInliningMaxCallerSize.getValue();
 
     public double calculateScore(TruffleInliningProfile profile) {
         return profile.getFrequency() / profile.getDeepNodeCount();
     }
 
-    public boolean isAllowed(TruffleInliningResult state, TruffleInliningProfile profile, int nodeCountBudget) {
-        TruffleCallPath callPath = profile.getCallPath();
-        OptimizedCallTarget inlineTarget = callPath.getCallTarget();
-        for (TruffleCallPath path : callPath.getParent().toList()) {
-            if (path.getCallTarget() == inlineTarget) {
-                // recursive call found
-                profile.setFailedReason(REASON_RECURSION);
-                return false;
-            }
+    public boolean isAllowed(TruffleInliningProfile profile, int currentBudgetLeft) {
+        if (profile.isRecursiveCall()) {
+            // recursive call found
+            profile.setFailedReason(REASON_RECURSION);
+            return false;
         }
 
         if (profile.isForced()) {
             return true;
         }
 
-        if (nodeCountBudget - profile.getDeepNodeCount() < 0) {
+        if (currentBudgetLeft - profile.getDeepNodeCount() < 0) {
             profile.setFailedReason(REASON_MAXIMUM_TOTAL_NODE_COUNT);
             return false;
         }
@@ -62,9 +58,4 @@
 
         return true;
     }
-
-    public boolean isAllowedDeep(TruffleInliningResult state, TruffleInliningProfile profile, int nodeCountBudget) {
-        return true;
-    }
-
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java	Mon Apr 14 18:25:23 2014 +0200
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.truffle;
 
-import java.util.concurrent.atomic.*;
-
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.frame.*;
@@ -32,15 +30,15 @@
 import com.oracle.truffle.api.nodes.NodeUtil.NodeCountFilter;
 
 /**
- * Call target that is optimized by Graal upon surpassing a specific invocation threshold.
+ * A call node with a constant {@link CallTarget} that can be optimized by Graal.
  */
 public final class OptimizedCallNode extends DefaultCallNode {
 
-    protected int callCount;
+    private int callCount;
     private boolean trySplit = true;
-    private boolean inliningForced;
+
+    @CompilationFinal private boolean inlined;
     @CompilationFinal private OptimizedCallTarget splitCallTarget;
-    private final AtomicInteger inliningCounter = new AtomicInteger(0);
 
     private OptimizedCallNode(OptimizedCallTarget target) {
         super(target);
@@ -70,29 +68,27 @@
         return splitCallTarget;
     }
 
-    public static OptimizedCallNode create(OptimizedCallTarget target) {
-        return new OptimizedCallNode(target);
-    }
-
     @Override
     public Object call(VirtualFrame frame, Object[] arguments) {
         if (CompilerDirectives.inInterpreter()) {
-            interpreterCall();
-            if (inliningCounter.get() > 0 || inliningForced) {
-                return getCurrentCallTarget().callInlined(arguments);
-            }
+            onInterpreterCall();
         }
-        return callProxy(this, getCurrentCallTarget(), frame, arguments);
+        OptimizedCallTarget target = getCurrentCallTarget();
+        if (inlined) {
+            return target.callInlined(arguments);
+        } else {
+            return super.call(frame, arguments);
+        }
     }
 
-    private void interpreterCall() {
+    private void onInterpreterCall() {
         callCount++;
         if (trySplit) {
             if (callCount == 1) {
                 // on first call
-                getCurrentCallTarget().incrementKnownCallSite();
+                getCurrentCallTarget().incrementKnownCallSites();
             }
-            if (callCount > 1) {
+            if (callCount > 1 && !inlined) {
                 trySplit = false;
                 if (shouldSplit()) {
                     splitImpl(true);
@@ -101,22 +97,20 @@
         }
     }
 
-    void notifyInlining() {
-        inliningCounter.incrementAndGet();
-    }
-
-    void notifyInliningDone() {
-        inliningCounter.decrementAndGet();
-    }
-
-    @Override
-    public void inline() {
-        inliningForced = true;
+    /* Called by the runtime system if this CallNode is really going to be inlined. */
+    void inline() {
+        inlined = true;
     }
 
     @Override
     public boolean isInlined() {
-        return inliningForced;
+        return inlined;
+    }
+
+    @Override
+    public boolean split() {
+        splitImpl(false);
+        return true;
     }
 
     private void splitImpl(boolean heuristic) {
@@ -128,8 +122,8 @@
             OptimizedCallTarget.logSplit(this, getCallTarget(), splitTarget);
         }
         if (callCount >= 1) {
-            getCallTarget().decrementKnownCallSite();
-            splitTarget.incrementKnownCallSite();
+            getCallTarget().decrementKnownCallSites();
+            splitTarget.incrementKnownCallSites();
         }
         this.splitCallTarget = splitTarget;
     }
@@ -145,7 +139,7 @@
             return false;
         }
         OptimizedCallTarget splitTarget = getCallTarget();
-        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(null, new TruffleCallPath(splitTarget));
+        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(splitTarget, false);
         if (nodeCount > TruffleCompilerOptions.TruffleSplittingMaxCalleeSize.getValue()) {
             return false;
         }
@@ -173,7 +167,7 @@
     }
 
     private int countPolymorphic() {
-        return NodeUtil.countNodes(getCallTarget().getRootNode(), new NodeCountFilter() {
+        return NodeUtil.countNodes(getCurrentCallTarget().getRootNode(), new NodeCountFilter() {
             public boolean isCounted(Node node) {
                 NodeCost cost = node.getCost();
                 boolean polymorphic = cost == NodeCost.POLYMORPHIC || cost == NodeCost.MEGAMORPHIC;
@@ -182,10 +176,7 @@
         });
     }
 
-    @SuppressWarnings("unused")
-    public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) {
-        if (!isSplit() && isSplittable()) {
-            trySplit = true;
-        }
+    public static OptimizedCallNode create(OptimizedCallTarget target) {
+        return new OptimizedCallNode(target);
     }
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Mon Apr 14 18:25:23 2014 +0200
@@ -30,11 +30,11 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.debug.*;
-import com.oracle.graal.truffle.OptimizedCallUtils.InlinedNodeCountFilter;
 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.api.nodes.NodeUtil.NodeCountFilter;
 
 /**
  * Call target that is optimized by Graal upon surpassing a specific invocation threshold.
@@ -46,11 +46,10 @@
     protected InstalledCode installedCode;
     protected boolean compilationEnabled;
     protected int callCount;
-    protected TruffleInliningResult inliningResult;
+    protected boolean inliningPerformed;
     protected final CompilationProfile compilationProfile;
     protected final CompilationPolicy compilationPolicy;
     private OptimizedCallTarget splitSource;
-
     private final AtomicInteger callSitesKnown = new AtomicInteger(0);
 
     public OptimizedCallTarget(RootNode rootNode, int invokeCounter, int compilationThreshold, boolean compilationEnabled, CompilationPolicy compilationPolicy) {
@@ -67,18 +66,14 @@
         return callSitesKnown.get();
     }
 
-    public final void incrementKnownCallSite() {
+    public final void incrementKnownCallSites() {
         callSitesKnown.incrementAndGet();
     }
 
-    public final void decrementKnownCallSite() {
+    public final void decrementKnownCallSites() {
         callSitesKnown.decrementAndGet();
     }
 
-    public final TruffleInliningResult getInliningResult() {
-        return inliningResult;
-    }
-
     public final OptimizedCallTarget getSplitSource() {
         return splitSource;
     }
@@ -116,48 +111,33 @@
     }
 
     public final void performInlining() {
-        if (!shouldInline()) {
+        if (!TruffleFunctionInlining.getValue()) {
             return;
         }
-
-        if (inliningResult != null) {
+        if (inliningPerformed) {
             return;
         }
 
-        TruffleInliningHandler handler = new TruffleInliningHandler(this, new DefaultInliningPolicy(), new HashMap<OptimizedCallTarget, TruffleInliningResult>());
-        int startNodeCount = OptimizedCallUtils.countNonTrivialNodes(null, new TruffleCallPath(this));
-        this.inliningResult = handler.inline(startNodeCount);
-        logInliningDecision(this, inliningResult, handler);
-    }
-
-    protected boolean shouldCompile() {
-        return compilationPolicy.shouldCompile(compilationProfile);
+        TruffleInliningHandler handler = new TruffleInliningHandler(new DefaultInliningPolicy());
+        TruffleInliningResult result = handler.decideInlining(this, 0);
+        performInlining(result);
+        logInliningDecision(result, 0);
     }
 
-    protected static boolean shouldInline() {
-        return TruffleFunctionInlining.getValue();
-    }
-
-    protected final void cancelInlinedCallOptimization() {
-        if (getInliningResult() != null) {
-            for (TruffleCallPath path : getInliningResult()) {
-                OptimizedCallNode top = path.getCallNode();
-                top.notifyInlining();
-                top.getCurrentCallTarget().cancelInstalledTask(top, top, "Inlined");
+    private void performInlining(TruffleInliningResult result) {
+        if (inliningPerformed) {
+            return;
+        }
+        inliningPerformed = true;
+        for (TruffleInliningProfile profile : result) {
+            profile.getCallNode().inline();
+            TruffleInliningResult recursiveResult = profile.getRecursiveResult();
+            if (recursiveResult != null) {
+                recursiveResult.getCallTarget().performInlining(recursiveResult);
             }
         }
     }
 
-    protected final void onCompilationDone() {
-        if (inliningResult != null) {
-            for (TruffleCallPath path : inliningResult) {
-                path.getCallNode().notifyInliningDone();
-            }
-        }
-    }
-
-    protected abstract void cancelInstalledTask(Node oldNode, Node newNode, CharSequence reason);
-
     protected abstract void invalidate(Node oldNode, Node newNode, CharSequence reason);
 
     public final Object executeHelper(Object[] args) {
@@ -188,39 +168,55 @@
 
     public Map<String, Object> getDebugProperties() {
         Map<String, Object> properties = new LinkedHashMap<>();
-        addASTSizeProperty(getInliningResult(), new TruffleCallPath(this), properties);
+        addASTSizeProperty(this, properties);
         properties.putAll(getCompilationProfile().getDebugProperties());
         return properties;
 
     }
 
-    private static void logInliningDecision(OptimizedCallTarget target, TruffleInliningResult result, TruffleInliningHandler handler) {
+    private static void logInliningDecision(TruffleInliningResult result, int depth) {
         if (!TraceTruffleInlining.getValue()) {
             return;
         }
 
-        List<TruffleInliningProfile> profiles = handler.lookupProfiles(result, new TruffleCallPath(target));
-
-        Collections.sort(profiles); // sorts by hierarchy and source section
+        logInliningStart(result.getCallTarget());
+        logInliningDecisionRecursive(result, depth);
+        logInliningDone(result.getCallTarget());
+    }
 
-        logInliningStart(target);
-        for (TruffleInliningProfile profile : profiles) {
-            TruffleCallPath path = profile.getCallPath();
-            if (path.getRootCallTarget() == target) {
-                String msg = result.isInlined(path) ? "inline success" : "inline failed";
-                logInlinedImpl(msg, result, handler.getProfiles().get(path), path);
+    private static void logInliningDecisionRecursive(TruffleInliningResult result, int depth) {
+        List<OptimizedCallNode> callNodes = searchCallNodes(result.getCallTarget());
+        for (OptimizedCallNode callNode : callNodes) {
+            TruffleInliningProfile profile = result.getProfiles().get(callNode);
+            boolean inlined = result.isInlined(callNode);
+            String msg = inlined ? "inline success" : "inline failed";
+            logInlinedImpl(msg, profile, depth);
+            if (profile.getRecursiveResult() != null && inlined) {
+                logInliningDecisionRecursive(profile.getRecursiveResult(), depth + 1);
             }
         }
-        logInliningDone(target);
     }
 
-    private static void logInlinedImpl(String status, TruffleInliningResult result, TruffleInliningProfile profile, TruffleCallPath path) {
+    private static List<OptimizedCallNode> searchCallNodes(final OptimizedCallTarget target) {
+        final List<OptimizedCallNode> callNodes = new ArrayList<>();
+        target.getRootNode().accept(new NodeVisitor() {
+            public boolean visit(Node node) {
+                if (node instanceof OptimizedCallNode) {
+                    callNodes.add((OptimizedCallNode) node);
+                }
+                return true;
+            }
+        });
+        return callNodes;
+    }
+
+    private static void logInlinedImpl(String status, TruffleInliningProfile profile, int depth) {
         Map<String, Object> properties = new LinkedHashMap<>();
-        addASTSizeProperty(result, path, properties);
+        addASTSizeProperty(profile.getCallNode().getCurrentCallTarget(), properties);
         if (profile != null) {
             properties.putAll(profile.getDebugProperties());
         }
-        log((path.getDepth() * 2), status, path.getCallTarget().toString(), properties);
+        log((depth * 2), status, profile.getCallNode().getCurrentCallTarget().toString(), properties);
     }
 
     private static void logInliningStart(OptimizedCallTarget target) {
@@ -296,7 +292,7 @@
                     }
                     if (node instanceof CallNode) {
                         CallNode callNode = (CallNode) node;
-                        if (callNode.isInlined()) {
+                        if (callNode.isInliningForced()) {
                             callNode.getCurrentRootNode().accept(this);
                         }
                     }
@@ -312,27 +308,27 @@
     static void logSplit(OptimizedCallNode callNode, OptimizedCallTarget target, OptimizedCallTarget newTarget) {
         if (TraceTruffleSplitting.getValue()) {
             Map<String, Object> properties = new LinkedHashMap<>();
-            addASTSizeProperty(target.getInliningResult(), new TruffleCallPath(target), properties);
+            addASTSizeProperty(target, properties);
             properties.put("Split#", ++splitCount);
             properties.put("Source", callNode.getEncapsulatingSourceSection());
             log(0, "split", newTarget.toString(), properties);
         }
     }
 
-    static void addASTSizeProperty(TruffleInliningResult inliningResult, TruffleCallPath countedPath, Map<String, Object> properties) {
-        int polymorphicCount = OptimizedCallUtils.countNodes(inliningResult, countedPath, new InlinedNodeCountFilter() {
-            public boolean isCounted(TruffleCallPath path, Node node) {
+    static void addASTSizeProperty(OptimizedCallTarget target, Map<String, Object> properties) {
+        int polymorphicCount = OptimizedCallUtils.countNodes(target, new NodeCountFilter() {
+            public boolean isCounted(Node node) {
                 return node.getCost() == NodeCost.POLYMORPHIC;
             }
-        });
+        }, true);
 
-        int megamorphicCount = OptimizedCallUtils.countNodes(inliningResult, countedPath, new InlinedNodeCountFilter() {
-            public boolean isCounted(TruffleCallPath path, Node node) {
+        int megamorphicCount = OptimizedCallUtils.countNodes(target, new NodeCountFilter() {
+            public boolean isCounted(Node node) {
                 return node.getCost() == NodeCost.MEGAMORPHIC;
             }
-        });
+        }, true);
 
-        String value = String.format("%4d (%d/%d)", OptimizedCallUtils.countNonTrivialNodes(inliningResult, countedPath), polymorphicCount, megamorphicCount);
+        String value = String.format("%4d (%d/%d)", OptimizedCallUtils.countNonTrivialNodes(target, true), polymorphicCount, megamorphicCount);
         properties.put("ASTSize", value);
     }
 
@@ -390,7 +386,7 @@
                 continue;
             }
 
-            int nodeCount = OptimizedCallUtils.countNonTrivialNodes(callTarget.getInliningResult(), new TruffleCallPath(callTarget));
+            int nodeCount = OptimizedCallUtils.countNonTrivialNodes(callTarget, true);
             String comment = callTarget.installedCode == null ? " int" : "";
             comment += callTarget.compilationEnabled ? "" : " fail";
             OUT.printf("%-50s | %10d | %15d | %10d | %3d%s\n", callTarget.getRootNode(), callTarget.callCount, nodeCount, nodeCount, callTarget.getCompilationProfile().getInvalidationCount(), comment);
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallUtils.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallUtils.java	Mon Apr 14 18:25:23 2014 +0200
@@ -23,98 +23,70 @@
 package com.oracle.graal.truffle;
 
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.nodes.NodeUtil.NodeCountFilter;
 
 class OptimizedCallUtils {
 
-    public abstract static class InlinedCallVisitor implements NodeVisitor {
-
-        private TruffleCallPath currentPath;
-        private final TruffleInliningResult inliningDecision;
-
-        public InlinedCallVisitor(TruffleInliningResult inliningDecision, TruffleCallPath initialPath) {
-            this.inliningDecision = inliningDecision;
-            this.currentPath = initialPath;
-        }
-
-        public final TruffleInliningResult getInliningDecision() {
-            return inliningDecision;
-        }
-
-        public final boolean visit(Node node) {
-            if (node instanceof OptimizedCallNode) {
-                OptimizedCallNode callNode = ((OptimizedCallNode) node);
-                this.currentPath = new TruffleCallPath(this.currentPath, callNode);
-                try {
-                    boolean result = visit(currentPath, node);
-                    TruffleInliningResult decision = inliningDecision;
-                    if (decision != null && decision.isInlined(currentPath)) {
-                        callNode.getCurrentRootNode().accept(this);
-                    }
-                    return result;
-                } finally {
-                    this.currentPath = this.currentPath.getParent();
-                }
-            } else {
-                return visit(currentPath, node);
-            }
-        }
-
-        public abstract boolean visit(TruffleCallPath path, Node node);
-
-    }
-
-    public static int countNodes(TruffleInliningResult decision, TruffleCallPath path, InlinedNodeCountFilter filter) {
-        InlinedNodeCountVisitor nodeCount = new InlinedNodeCountVisitor(decision, path, filter);
-        path.getCallTarget().getRootNode().accept(nodeCount);
+    public static int countNodes(OptimizedCallTarget target, NodeCountFilter filter, boolean inlined) {
+        NodeCountVisitorImpl nodeCount = new NodeCountVisitorImpl(filter, inlined);
+        target.getRootNode().accept(nodeCount);
         return nodeCount.nodeCount;
     }
 
-    public static int countCalls(TruffleInliningResult decision, TruffleCallPath path) {
-        InlinedNodeCountVisitor nodeCount = new InlinedNodeCountVisitor(decision, path, new InlinedNodeCountFilter() {
-            public boolean isCounted(TruffleCallPath p, Node node) {
+    public static int countCalls(OptimizedCallTarget target) {
+        return countNodes(target, new NodeCountFilter() {
+            public boolean isCounted(Node node) {
                 return node instanceof CallNode;
             }
-        });
-        path.getCallTarget().getRootNode().accept(nodeCount);
-        return nodeCount.nodeCount;
+        }, true);
     }
 
-    public interface InlinedNodeCountFilter {
-
-        boolean isCounted(TruffleCallPath path, Node node);
+    public static int countCallsInlined(OptimizedCallTarget target) {
+        return countNodes(target, new NodeCountFilter() {
+            public boolean isCounted(Node node) {
+                return (node instanceof OptimizedCallNode) && ((OptimizedCallNode) node).isInlined();
+            }
+        }, true);
     }
 
-    private static final class InlinedNodeCountVisitor extends InlinedCallVisitor {
+    public static int countNonTrivialNodes(final OptimizedCallTarget target, final boolean inlined) {
+        return countNodes(target, new NodeCountFilter() {
+            public boolean isCounted(Node node) {
+                NodeCost cost = node.getCost();
+                if (cost != null && cost != NodeCost.NONE && cost != NodeCost.UNINITIALIZED) {
+                    return true;
+                }
+                return false;
+            }
+        }, inlined);
+    }
 
-        private final InlinedNodeCountFilter filter;
-        int nodeCount;
+    private static final class NodeCountVisitorImpl implements NodeVisitor {
 
-        private InlinedNodeCountVisitor(TruffleInliningResult decision, TruffleCallPath initialPath, InlinedNodeCountFilter filter) {
-            super(decision, initialPath);
+        private final NodeCountFilter filter;
+        private int nodeCount;
+        private boolean followInlined;
+
+        private NodeCountVisitorImpl(NodeCountFilter filter, boolean followInlined) {
             this.filter = filter;
+            this.followInlined = followInlined;
         }
 
-        @Override
-        public boolean visit(TruffleCallPath path, Node node) {
-            if (filter == null || filter.isCounted(path, node)) {
+        public boolean visit(Node node) {
+            if (filter == null || filter.isCounted(node)) {
                 nodeCount++;
             }
+            if (followInlined) {
+                if (node instanceof OptimizedCallNode) {
+                    OptimizedCallNode ocn = (OptimizedCallNode) node;
+                    if (ocn.isInlined()) {
+                        ocn.getCurrentRootNode().accept(this);
+                    }
+                }
+            }
             return true;
         }
 
     }
 
-    static int countNonTrivialNodes(TruffleInliningResult state, TruffleCallPath path) {
-        return countNodes(state, path, new InlinedNodeCountFilter() {
-
-            public boolean isCounted(TruffleCallPath p, Node node) {
-                NodeCost cost = node.getCost();
-                if (cost != null && cost != NodeCost.NONE && cost != NodeCost.UNINITIALIZED) {
-                    return true;
-                }
-                return false;
-            }
-        });
-    }
-
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Mon Apr 14 18:25:23 2014 +0200
@@ -121,7 +121,7 @@
             Debug.dump(graph, "Before inlining");
 
             // Make sure frame does not escape.
-            expandTree(callTarget, graph, assumptions);
+            expandTree(graph, assumptions);
 
             if (Thread.currentThread().isInterrupted()) {
                 return null;
@@ -175,14 +175,12 @@
         return graph;
     }
 
-    private void expandTree(OptimizedCallTarget target, StructuredGraph graph, Assumptions assumptions) {
+    private void expandTree(StructuredGraph graph, Assumptions assumptions) {
         PhaseContext phaseContext = new PhaseContext(providers, assumptions);
         TruffleExpansionLogger expansionLogger = null;
         if (TraceTruffleExpansion.getValue()) {
             expansionLogger = new TruffleExpansionLogger(providers, graph);
         }
-        boolean inliningEnabled = target.getInliningResult() != null && target.getInliningResult().size() > 0;
-        Map<Node, TruffleCallPath> methodTargetToStack = new HashMap<>();
         boolean changed;
         do {
             changed = false;
@@ -203,19 +201,13 @@
                         }
 
                         StructuredGraph inlineGraph = replacements.getMethodSubstitution(methodCallTargetNode.targetMethod());
-                        if (inliningEnabled && inlineGraph == null) {
-                            inlineGraph = expandInlinableCallNode(target, methodTargetToStack, assumptions, phaseContext, methodCallTargetNode);
-                        }
-
                         if (inlineGraph == null && !Modifier.isNative(methodCallTargetNode.targetMethod().getModifiers())) {
                             inlineGraph = parseGraph(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, phaseContext, false);
                         }
 
                         if (inlineGraph != null) {
                             try (Indent indent = Debug.logAndIndent("inline graph %s", methodCallTargetNode.targetMethod())) {
-                                if (inliningEnabled) {
-                                    preExpandTruffleCallPath(inlineGraph, methodTargetToStack, methodTargetToStack.get(methodCallTargetNode));
-                                }
+
                                 int nodeCountBefore = graph.getNodeCount();
                                 Mark mark = graph.getMark();
                                 if (TraceTruffleExpansion.getValue()) {
@@ -226,9 +218,6 @@
                                 if (TraceTruffleExpansion.getValue()) {
                                     expansionLogger.postExpand(inlined);
                                 }
-                                if (inliningEnabled) {
-                                    postExpandTruffleCallPath(methodTargetToStack, inlined);
-                                }
                                 if (Debug.isDumpEnabled()) {
                                     Debug.log("dump enabled");
                                     int nodeCountAfter = graph.getNodeCount();
@@ -253,76 +242,6 @@
         }
     }
 
-    private static void preExpandTruffleCallPath(StructuredGraph inlineGraph, Map<Node, TruffleCallPath> methodTargetToCallPath, TruffleCallPath truffleCallPath) {
-        for (MethodCallTargetNode methodTargetNode : inlineGraph.getNodes(MethodCallTargetNode.class)) {
-            methodTargetToCallPath.put(methodTargetNode, truffleCallPath);
-        }
-    }
-
-    private static void postExpandTruffleCallPath(Map<Node, TruffleCallPath> methodCallTargetToCallPath, Map<Node, Node> nodeUpdates) {
-        for (Object key : methodCallTargetToCallPath.keySet().toArray()) {
-            if (nodeUpdates.containsKey(key)) {
-                methodCallTargetToCallPath.put(nodeUpdates.get(key), methodCallTargetToCallPath.get(key));
-                methodCallTargetToCallPath.remove(key);
-            }
-        }
-    }
-
-    private StructuredGraph expandInlinableCallNode(OptimizedCallTarget target, Map<Node, TruffleCallPath> methodCallToCallPath, Assumptions assumptions, PhaseContext phaseContext,
-                    MethodCallTargetNode methodCallTargetNode) {
-
-        ValueNode receiverNode = methodCallTargetNode.receiver();
-        if (receiverNode == null || !receiverNode.isConstant() || !receiverNode.asConstant().getKind().isObject()) {
-            return null;
-        }
-
-        ResolvedJavaMethod method = methodCallTargetNode.targetMethod();
-
-        /*
-         * Accessing the constant using the SnippetReflectionProvider is a workaround, we should
-         * think about a better solution. Since object constants are VM-specific, only the hosting
-         * VM knows how to do the conversion.
-         */
-        SnippetReflectionProvider snippetReflection = Graal.getRequiredCapability(SnippetReflectionProvider.class);
-        Object receiverValue = snippetReflection.asObject(receiverNode.asConstant());
-        if (receiverValue instanceof OptimizedCallNode) {
-            if (!method.getName().equals("call") || method.getSignature().getParameterCount(false) != 2) { // FIXME
-                return null;
-            }
-
-            OptimizedCallNode callNode = (OptimizedCallNode) receiverValue;
-            TruffleCallPath callPath = methodCallToCallPath.get(methodCallTargetNode);
-            if (callPath == null) {
-                callPath = new TruffleCallPath(target);
-            }
-            callPath = new TruffleCallPath(callPath, callNode);
-            methodCallToCallPath.put(methodCallTargetNode, callPath);
-            // let the normal expansion do the work
-            return null;
-        } else if (receiverValue instanceof OptimizedCallTarget) {
-            TruffleCallPath path = methodCallToCallPath.get(methodCallTargetNode);
-            // path unknown. direct call to OptimizedCallTarget without OptimizedCallNode?
-            if (path == null) {
-                return null;
-            }
-
-            TruffleInliningResult decision = target.getInliningResult();
-            if (decision == null) { // no inlining decision. inlining disabled?
-                return null;
-            }
-
-            if (!decision.isInlined(path)) {
-                // the OptimizedCallTarget has decided not to inline this call path
-                return null;
-            }
-
-            // inline truffle call
-            return parseGraph(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, phaseContext, true);
-        }
-
-        return null;
-    }
-
     private boolean isFrame(ValueNode receiver) {
         return receiver instanceof NewFrameNode || Objects.equals(ObjectStamp.typeOrNull(receiver.stamp()), frameType);
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCallPath.java	Mon Apr 14 18:20:09 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,219 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.truffle;
-
-import java.util.*;
-
-import com.oracle.truffle.api.*;
-
-public final class TruffleCallPath implements Iterable<TruffleCallPath>, Comparable<TruffleCallPath> {
-
-    private final OptimizedCallNode callNode;
-    private final TruffleCallPath parent;
-    private final OptimizedCallTarget rootCallTarget;
-
-    public TruffleCallPath(OptimizedCallTarget parent) {
-        this.rootCallTarget = Objects.requireNonNull(parent);
-        this.callNode = null;
-        this.parent = null;
-    }
-
-    public TruffleCallPath(TruffleCallPath parent, OptimizedCallNode node) {
-        this.parent = Objects.requireNonNull(parent);
-        assert !parent.containsPath(this) : "cyclic path detected";
-        this.callNode = Objects.requireNonNull(node);
-        this.rootCallTarget = parent.getRootCallTarget();
-    }
-
-    public boolean isRoot() {
-        return parent == null && rootCallTarget != null;
-    }
-
-    private boolean containsPath(TruffleCallPath p) {
-        return p == this || (getParent() != null && this.getParent().containsPath(p));
-    }
-
-    public OptimizedCallTarget getRootCallTarget() {
-        return rootCallTarget;
-    }
-
-    @Override
-    public String toString() {
-        return (parent != null ? (parent.toString() + " -> ") : "") + getCallTarget().toString();
-    }
-
-    public Iterator<TruffleCallPath> iterator() {
-        return toList().iterator();
-    }
-
-    public OptimizedCallTarget getCallTarget() {
-        return parent == null ? rootCallTarget : callNode.getCurrentCallTarget();
-    }
-
-    public List<TruffleCallPath> toList() {
-        List<TruffleCallPath> list = new ArrayList<>();
-        toListImpl(list);
-        return list;
-    }
-
-    private void toListImpl(List<TruffleCallPath> list) {
-        if (parent != null) {
-            parent.toListImpl(list);
-        }
-        list.add(this);
-    }
-
-    public TruffleCallPath getParent() {
-        return parent;
-    }
-
-    public OptimizedCallNode getCallNode() {
-        return callNode;
-    }
-
-    public int getDepth() {
-        return parent == null ? 0 : (parent.getDepth() + 1);
-    }
-
-    public int compareTo(TruffleCallPath o) {
-        return Objects.compare(this, o, new PathComparator());
-    }
-
-    private static class PathComparator implements Comparator<TruffleCallPath> {
-        public int compare(TruffleCallPath c1, TruffleCallPath c2) {
-            if (c1 == c2) {
-                return 0;
-            }
-
-            Iterator<TruffleCallPath> p1 = c1.toList().iterator();
-            Iterator<TruffleCallPath> p2 = c2.toList().iterator();
-
-            int cmp = 0;
-            while (cmp == 0 && (p1.hasNext() || p2.hasNext())) {
-                TruffleCallPath o1;
-                TruffleCallPath o2;
-                if (p1.hasNext()) {
-                    o1 = p1.next();
-                } else {
-                    return -1;
-                }
-                if (p2.hasNext()) {
-                    o2 = p2.next();
-                } else {
-                    return 1;
-                }
-
-                if (o1 == o2) {
-                    continue;
-                }
-
-                SourceSection s1;
-                if (o1.callNode != null) {
-                    s1 = o1.callNode.getEncapsulatingSourceSection();
-                } else {
-                    s1 = o1.getCallTarget().getRootNode().getSourceSection();
-                }
-
-                SourceSection s2;
-                if (o2.callNode != null) {
-                    s2 = o2.callNode.getEncapsulatingSourceSection();
-                } else {
-                    s2 = o2.getCallTarget().getRootNode().getSourceSection();
-                }
-                cmp = compareSourceSection(s2, s1);
-
-                if (cmp == 0) {
-                    cmp = o1.getCallTarget().toString().compareTo(o2.getCallTarget().toString());
-                }
-            }
-            return cmp;
-        }
-    }
-
-    private static int compareSourceSection(SourceSection s1, SourceSection s2) {
-        return Objects.compare(s1, s2, new Comparator<SourceSection>() {
-            public int compare(SourceSection o1, SourceSection o2) {
-                if (o1 == o2) {
-                    return 0;
-                }
-                if (o1 == null || o2 == null) {
-                    return 0;
-                }
-                int cmp = 0;
-                if (o1.getSource() != null && o2.getSource() != null && !Objects.equals(o1.getSource().getName(), o2.getSource().getName())) {
-                    cmp = o2.getSource().getName().compareTo(o1.getSource().getName());
-                }
-                if (cmp == 0) {
-                    cmp = o2.getStartLine() - o1.getStartLine();
-                }
-                if (cmp == 0) {
-                    cmp = o2.getStartColumn() - o1.getStartColumn();
-                }
-                return cmp;
-            }
-        });
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((callNode == null) ? 0 : callNode.hashCode());
-        result = prime * result + ((parent == null) ? 0 : parent.hashCode());
-        result = prime * result + ((rootCallTarget == null) ? 0 : rootCallTarget.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj instanceof TruffleCallPath) {
-            TruffleCallPath other = (TruffleCallPath) obj;
-            if (other.callNode != callNode) {
-                return false;
-            }
-            if (!Objects.equals(other.parent, parent)) {
-                return false;
-            }
-            if (!Objects.equals(other.rootCallTarget, rootCallTarget)) {
-                return false;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    public TruffleCallPath append(TruffleCallPath path) {
-        if (getCallTarget() != path.getRootCallTarget()) {
-            throw new IllegalArgumentException("Pathes are not compatible and can therfore not be appended.");
-        }
-
-        TruffleCallPath append = this;
-        for (TruffleCallPath childPath : path) {
-            if (!childPath.isRoot()) {
-                append = new TruffleCallPath(append, childPath.getCallNode());
-            }
-        }
-        return append;
-    }
-
-}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Mon Apr 14 18:25:23 2014 +0200
@@ -165,11 +165,11 @@
 
         if (TraceTruffleCompilation.getValue()) {
             byte[] code = compiledMethod.getCode();
-            int calls = OptimizedCallUtils.countCalls(compilable.getInliningResult(), new TruffleCallPath(compilable));
-            int inlinedCalls = (compilable.getInliningResult() != null ? compilable.getInliningResult().size() : 0);
+            int calls = OptimizedCallUtils.countCalls(compilable);
+            int inlinedCalls = OptimizedCallUtils.countCallsInlined(compilable);
             int dispatchedCalls = calls - inlinedCalls;
             Map<String, Object> properties = new LinkedHashMap<>();
-            OptimizedCallTarget.addASTSizeProperty(compilable.getInliningResult(), new TruffleCallPath(compilable), properties);
+            OptimizedCallTarget.addASTSizeProperty(compilable, properties);
             properties.put("Time", String.format("%5.0f(%4.0f+%-4.0f)ms", //
                             (timeCompilationFinished - timeCompilationStarted) / 1e6, //
                             (timePartialEvaluationFinished - timeCompilationStarted) / 1e6, //
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningHandler.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningHandler.java	Mon Apr 14 18:25:23 2014 +0200
@@ -24,164 +24,103 @@
 
 import java.util.*;
 
-import com.oracle.graal.truffle.OptimizedCallUtils.InlinedCallVisitor;
 import com.oracle.truffle.api.nodes.*;
 
 public final class TruffleInliningHandler {
 
-    private final ProfileScoreComparator inliningOrder = new ProfileScoreComparator();
-
-    private final OptimizedCallTarget callTarget;
+    private static final int MAXIMUM_RECURSIVE_DEPTH = 15;
+    private static final ProfileScoreComparator INLINING_SCORE = new ProfileScoreComparator();
     private final TruffleInliningPolicy policy;
-    private final Map<TruffleCallPath, TruffleInliningProfile> profiles;
-    private final Map<OptimizedCallTarget, TruffleInliningResult> inliningResultCache;
+    private final Map<OptimizedCallTarget, TruffleInliningResult> resultCache;
 
-    public TruffleInliningHandler(OptimizedCallTarget callTarget, TruffleInliningPolicy policy, Map<OptimizedCallTarget, TruffleInliningResult> inliningResultCache) {
-        this.callTarget = callTarget;
+    public TruffleInliningHandler(TruffleInliningPolicy policy) {
         this.policy = policy;
-        this.profiles = new HashMap<>();
-        this.inliningResultCache = inliningResultCache;
+        this.resultCache = new HashMap<>();
     }
 
-    public TruffleInliningResult inline(int originalTotalNodeCount) {
-        Set<TruffleCallPath> inlinedPathes = new HashSet<>();
-        TruffleInliningResult result = new TruffleInliningResult(callTarget, inlinedPathes);
-        TruffleCallPath startPath = new TruffleCallPath(callTarget);
-
-        PriorityQueue<TruffleInliningProfile> queue = new PriorityQueue<>(10, inliningOrder);
-        queueCallSitesForInlining(result, startPath, queue);
-
-        int budget = TruffleCompilerOptions.TruffleInliningMaxCallerSize.getValue() - originalTotalNodeCount;
-        int index = 0;
-        while (!queue.isEmpty()) {
-            TruffleInliningProfile profile = queue.poll();
-            profile.setQueryIndex(index);
-            budget = tryInline(queue, result, inlinedPathes, profile, budget);
-            profiles.put(profile.getCallPath(), profile);
-            index++;
+    public TruffleInliningResult decideInlining(OptimizedCallTarget target, int depth) {
+        if (resultCache.containsKey(target)) {
+            return resultCache.get(target);
         }
+        resultCache.put(target, null);
+        TruffleInliningResult result = decideInliningImpl(target, depth);
+        resultCache.put(target, result);
         return result;
     }
 
-    private int tryInline(PriorityQueue<TruffleInliningProfile> queue, TruffleInliningResult result, Set<TruffleCallPath> inlinedPathes, TruffleInliningProfile profile, int budget) {
-
-        if (policy.isAllowed(result, profile, budget)) {
-            int remainingBudget;
-            inlinedPathes.add(profile.getCallPath());
-            if (policy.isAllowedDeep(result, profile, budget)) {
-                if (profile.getRecursiveResult() != null) {
-                    TruffleInliningResult inliningResult = profile.getRecursiveResult();
-                    for (TruffleCallPath recursiveInlinedPath : inliningResult) {
-                        inlinedPathes.add(profile.getCallPath().append(recursiveInlinedPath));
-                    }
-                }
-                remainingBudget = budget - profile.getDeepNodeCount();
-            } else {
-                remainingBudget = budget - profile.getNodeCount();
+    private TruffleInliningResult decideInliningImpl(OptimizedCallTarget target, int depth) {
+        List<TruffleInliningProfile> profiles = lookupProfiles(target, depth);
+        Set<TruffleInliningProfile> inlined = new HashSet<>();
+        Collections.sort(profiles, INLINING_SCORE);
+        int budget = TruffleCompilerOptions.TruffleInliningMaxCallerSize.getValue() - OptimizedCallUtils.countNonTrivialNodes(target, true);
+        int index = 0;
+        for (TruffleInliningProfile profile : profiles) {
+            profile.setQueryIndex(index++);
+            if (policy.isAllowed(profile, budget)) {
+                inlined.add(profile);
+                budget -= profile.getDeepNodeCount();
             }
-
-            queueCallSitesForInlining(result, profile.getCallPath(), queue);
-            return remainingBudget;
         }
 
-        return budget;
+        int deepNodeCount = TruffleCompilerOptions.TruffleInliningMaxCallerSize.getValue() - budget;
+        return new TruffleInliningResult(target, profiles, inlined, deepNodeCount);
+    }
+
+    private List<TruffleInliningProfile> lookupProfiles(final OptimizedCallTarget target, int depth) {
+        final List<OptimizedCallNode> callNodes = new ArrayList<>();
+        target.getRootNode().accept(new NodeVisitor() {
+            public boolean visit(Node node) {
+                if (node instanceof OptimizedCallNode) {
+                    callNodes.add((OptimizedCallNode) node);
+                }
+                return true;
+            }
+        });
+        final List<TruffleInliningProfile> profiles = new ArrayList<>();
+        for (OptimizedCallNode callNode : callNodes) {
+            profiles.add(lookupProfile(target, callNode, depth));
+        }
+        return profiles;
+    }
+
+    public TruffleInliningProfile lookupProfile(OptimizedCallTarget parentTarget, OptimizedCallNode ocn, int depth) {
+        OptimizedCallTarget target = ocn.getCurrentCallTarget();
+
+        int callSites = ocn.getCurrentCallTarget().getKnownCallSiteCount();
+        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(target, false);
+        double frequency = calculateFrequency(parentTarget, ocn);
+        boolean forced = ocn.isInliningForced();
+
+        int deepNodeCount;
+        TruffleInliningResult recursiveResult;
+        boolean recursiveCall = false;
+        if (target.inliningPerformed || depth > MAXIMUM_RECURSIVE_DEPTH) {
+            deepNodeCount = OptimizedCallUtils.countNonTrivialNodes(target, true);
+            recursiveResult = null;
+        } else {
+            recursiveResult = decideInlining(ocn.getCurrentCallTarget(), depth + 1);
+            if (recursiveResult == null) {
+                recursiveCall = true;
+                deepNodeCount = Integer.MAX_VALUE;
+            } else {
+                deepNodeCount = recursiveResult.getNodeCount();
+            }
+        }
+
+        TruffleInliningProfile profile = new TruffleInliningProfile(ocn, callSites, nodeCount, deepNodeCount, frequency, forced, recursiveCall, recursiveResult);
+        profile.setScore(policy.calculateScore(profile));
+        return profile;
     }
 
     public TruffleInliningPolicy getPolicy() {
         return policy;
     }
 
-    public Map<TruffleCallPath, TruffleInliningProfile> getProfiles() {
-        return profiles;
-    }
-
-    private void queueCallSitesForInlining(final TruffleInliningResult currentDecision, TruffleCallPath fromPath, final Collection<TruffleInliningProfile> queue) {
-        fromPath.getCallTarget().getRootNode().accept(new InlinedCallVisitor(currentDecision, fromPath) {
-            @Override
-            public boolean visit(TruffleCallPath path, Node node) {
-                if (node instanceof OptimizedCallNode) {
-                    if (!currentDecision.isInlined(path)) {
-                        addToQueue(queue, currentDecision, path);
-                    }
-                }
-                return true;
-            }
-        });
-    }
-
-    private void addToQueue(final Collection<TruffleInliningProfile> queue, final TruffleInliningResult currentDecision, TruffleCallPath path) {
-        queue.add(lookupProfile(currentDecision, path));
-    }
-
-    public List<TruffleInliningProfile> lookupProfiles(final TruffleInliningResult currentDecision, TruffleCallPath fromPath) {
-        final List<TruffleInliningProfile> pathes = new ArrayList<>();
-        fromPath.getCallTarget().getRootNode().accept(new InlinedCallVisitor(currentDecision, fromPath) {
-            @Override
-            public boolean visit(TruffleCallPath path, Node node) {
-                if (node instanceof OptimizedCallNode) {
-                    pathes.add(lookupProfile(currentDecision, path));
-                }
-                return true;
-            }
-        });
-        return pathes;
+    private static double calculateFrequency(OptimizedCallTarget target, OptimizedCallNode ocn) {
+        return (double) Math.max(1, target.getCompilationProfile().getCallCount()) / Math.max(1, ocn.getCallCount());
     }
 
-    public TruffleInliningProfile lookupProfile(TruffleInliningResult state, TruffleCallPath callPath) {
-        TruffleInliningProfile profile = profiles.get(callPath);
-        if (profile != null) {
-            return profile;
-        }
-
-        int callSites = callPath.getCallTarget().getKnownCallSiteCount();
-        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(state, callPath);
-        double frequency = calculatePathFrequency(callPath);
-        boolean forced = callPath.getCallNode().isInlined();
-        TruffleInliningResult recursiveResult = lookupChildResult(callPath, nodeCount);
-        int deepNodeCount = OptimizedCallUtils.countNonTrivialNodes(recursiveResult, new TruffleCallPath(callPath.getCallTarget()));
-
-        profile = new TruffleInliningProfile(callPath, callSites, nodeCount, deepNodeCount, frequency, forced, recursiveResult);
-        profile.setScore(policy.calculateScore(profile));
-        profiles.put(callPath, profile);
-
-        return profile;
-    }
-
-    private TruffleInliningResult lookupChildResult(TruffleCallPath callPath, int nodeCount) {
-        OptimizedCallTarget target = callPath.getCallTarget();
-
-        TruffleInliningResult recursiveResult = target.getInliningResult();
-
-        if (recursiveResult == null) {
-            recursiveResult = inliningResultCache.get(callPath.getCallTarget());
-
-            if (recursiveResult == null) {
-                if (inliningResultCache.containsKey(target)) {
-                    // cancel on recursion
-                    return new TruffleInliningResult(target, new HashSet<TruffleCallPath>());
-                }
-                inliningResultCache.put(target, null);
-                TruffleInliningHandler handler = new TruffleInliningHandler(target, policy, inliningResultCache);
-                recursiveResult = handler.inline(nodeCount);
-                inliningResultCache.put(callPath.getCallTarget(), recursiveResult);
-            }
-        }
-        return recursiveResult;
-    }
-
-    private static double calculatePathFrequency(TruffleCallPath callPath) {
-        int parentCallCount = -1;
-        double f = 1.0d;
-        for (TruffleCallPath path : callPath.toList()) {
-            if (parentCallCount != -1) {
-                f *= path.getCallNode().getCallCount() / (double) parentCallCount;
-            }
-            parentCallCount = path.getCallTarget().getCompilationProfile().getCallCount();
-        }
-        return f;
-    }
-
-    private final class ProfileScoreComparator implements Comparator<TruffleInliningProfile> {
+    private final static class ProfileScoreComparator implements Comparator<TruffleInliningProfile> {
 
         public int compare(TruffleInliningProfile o1, TruffleInliningProfile o2) {
             return Double.compare(o2.getScore(), o1.getScore());
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningPolicy.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningPolicy.java	Mon Apr 14 18:25:23 2014 +0200
@@ -24,9 +24,7 @@
 
 public interface TruffleInliningPolicy {
 
-    boolean isAllowed(TruffleInliningResult state, TruffleInliningProfile profile, int nodeCountBudget);
-
-    boolean isAllowedDeep(TruffleInliningResult state, TruffleInliningProfile profile, int nodeCountBudget);
+    boolean isAllowed(TruffleInliningProfile profile, int currentBudgetLeft);
 
     double calculateScore(TruffleInliningProfile profile);
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java	Mon Apr 14 18:25:23 2014 +0200
@@ -24,34 +24,41 @@
 
 import java.util.*;
 
-public class TruffleInliningProfile implements Comparable<TruffleInliningProfile> {
+public class TruffleInliningProfile {
 
-    private final TruffleCallPath callPath;
-
+    private final OptimizedCallNode callNode;
     private final int nodeCount;
     private final int deepNodeCount;
     private final int callSites;
     private final double frequency;
     private final boolean forced;
+    private final boolean recursiveCall;
     private final TruffleInliningResult recursiveResult;
 
     private String failedReason;
     private int queryIndex = -1;
     private double score;
 
-    public TruffleInliningProfile(TruffleCallPath callPath, int callSites, int nodeCount, int deepNodeCount, double frequency, boolean forced, TruffleInliningResult recursiveResult) {
-        if (callPath.isRoot()) {
-            throw new IllegalArgumentException("Root call path not profilable.");
-        }
+    public TruffleInliningProfile(OptimizedCallNode callNode, int callSites, int nodeCount, int deepNodeCount, double frequency, boolean forced, boolean recursiveCall,
+                    TruffleInliningResult recursiveResult) {
+        this.callNode = callNode;
         this.callSites = callSites;
-        this.callPath = callPath;
         this.nodeCount = nodeCount;
         this.deepNodeCount = deepNodeCount;
         this.frequency = frequency;
+        this.recursiveCall = recursiveCall;
         this.forced = forced;
         this.recursiveResult = recursiveResult;
     }
 
+    public boolean isRecursiveCall() {
+        return recursiveCall;
+    }
+
+    public OptimizedCallNode getCallNode() {
+        return callNode;
+    }
+
     public int getCallSites() {
         return callSites;
     }
@@ -100,21 +107,12 @@
         return deepNodeCount;
     }
 
-    public TruffleCallPath getCallPath() {
-        return callPath;
-    }
-
-    public int compareTo(TruffleInliningProfile o) {
-        return callPath.compareTo(o.callPath);
-    }
-
     public Map<String, Object> getDebugProperties() {
         Map<String, Object> properties = new LinkedHashMap<>();
-        properties.put("callSites", callSites);
-        properties.put("nodeCount", nodeCount);
+        properties.put("nodeCount", String.format("%5d/%5d", deepNodeCount, nodeCount));
         properties.put("frequency", frequency);
-        properties.put("score", score);
-        properties.put(String.format("index=%3d, force=%s", queryIndex, (forced ? "Y" : "N")), "");
+        properties.put("score", String.format("%8.4f", getScore()));
+        properties.put(String.format("index=%3d, force=%s, callSites=%2d", queryIndex, (forced ? "Y" : "N"), callSites), "");
         properties.put("reason", failedReason);
         return properties;
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningResult.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningResult.java	Mon Apr 14 18:25:23 2014 +0200
@@ -24,34 +24,49 @@
 
 import java.util.*;
 
-public final class TruffleInliningResult implements Iterable<TruffleCallPath> {
+public final class TruffleInliningResult implements Iterable<TruffleInliningProfile> {
 
     private final OptimizedCallTarget callTarget;
-    private final Set<TruffleCallPath> inlinedPathes;
+    private final Map<OptimizedCallNode, TruffleInliningProfile> profiles;
+    private final Set<TruffleInliningProfile> inlined;
+    private final int nodeCount;
 
-    public TruffleInliningResult(OptimizedCallTarget callTarget, Set<TruffleCallPath> pathes) {
+    public TruffleInliningResult(OptimizedCallTarget callTarget, List<TruffleInliningProfile> profiles, Set<TruffleInliningProfile> inlined, int nodeCount) {
         this.callTarget = callTarget;
-        this.inlinedPathes = pathes;
+        this.profiles = new HashMap<>();
+        for (TruffleInliningProfile profile : profiles) {
+            this.profiles.put(profile.getCallNode(), profile);
+        }
+        this.nodeCount = nodeCount;
+        this.inlined = inlined;
+    }
+
+    public Map<OptimizedCallNode, TruffleInliningProfile> getProfiles() {
+        return profiles;
+    }
+
+    public int getNodeCount() {
+        return nodeCount;
     }
 
     public OptimizedCallTarget getCallTarget() {
         return callTarget;
     }
 
-    public boolean isInlined(TruffleCallPath path) {
-        return inlinedPathes.contains(path);
+    public boolean isInlined(OptimizedCallNode path) {
+        return inlined.contains(profiles.get(path));
     }
 
     public int size() {
-        return inlinedPathes.size();
+        return inlined.size();
     }
 
-    public Iterator<TruffleCallPath> iterator() {
-        return Collections.unmodifiableSet(inlinedPathes).iterator();
+    public Iterator<TruffleInliningProfile> iterator() {
+        return Collections.unmodifiableSet(inlined).iterator();
     }
 
     @Override
     public String toString() {
-        return inlinedPathes.toString();
+        return inlined.toString();
     }
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleTreeDumpHandler.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleTreeDumpHandler.java	Mon Apr 14 18:25:23 2014 +0200
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.truffle;
 
-import java.util.*;
-
 import com.oracle.graal.debug.*;
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.nodes.*;
@@ -46,7 +44,6 @@
             final OptimizedCallTarget oct = (OptimizedCallTarget) callTarget;
 
             visitor.beginGroup(callTarget.toString());
-            dumpInlinedCalls(visitor, oct);
             dumpFullTree(visitor, message, oct);
             visitor.printToNetwork(false);
         }
@@ -54,12 +51,10 @@
 
     private static void dumpFullTree(final GraphPrintVisitor visitor, final String message, final OptimizedCallTarget oct) {
         visitor.setChildSupplier(new ChildSupplier() {
-            private TruffleCallPath currentPath = new TruffleCallPath(oct);
 
             public Object startNode(Object callNode) {
                 if (callNode instanceof OptimizedCallNode) {
-                    currentPath = new TruffleCallPath(currentPath, (OptimizedCallNode) callNode);
-                    if (oct.getInliningResult() != null && oct.getInliningResult().isInlined(currentPath)) {
+                    if (((OptimizedCallNode) callNode).isInlined()) {
                         return ((OptimizedCallNode) callNode).getCurrentRootNode();
                     }
                 }
@@ -67,9 +62,6 @@
             }
 
             public void endNode(Object callNode) {
-                if (callNode instanceof OptimizedCallNode) {
-                    currentPath = currentPath.getParent();
-                }
             }
         });
 
@@ -77,23 +69,6 @@
         visitor.setChildSupplier(null);
     }
 
-    private static void dumpInlinedCalls(final GraphPrintVisitor visitor, final OptimizedCallTarget oct) {
-        final Set<OptimizedCallTarget> visitedRoots = new HashSet<>();
-        oct.getRootNode().accept(new OptimizedCallUtils.InlinedCallVisitor(oct.getInliningResult(), new TruffleCallPath(oct)) {
-            @Override
-            public boolean visit(TruffleCallPath path, Node node) {
-                if (node instanceof OptimizedCallNode) {
-                    OptimizedCallTarget target = ((OptimizedCallNode) node).getCurrentCallTarget();
-                    if (!visitedRoots.contains(target)) {
-                        visitor.beginGraph("inlined " + target.toString()).visit(target.getRootNode());
-                        visitedRoots.add(target);
-                    }
-                }
-                return true;
-            }
-        });
-    }
-
     public void close() {
         // nothing to do
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallNode.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallNode.java	Mon Apr 14 18:25:23 2014 +0200
@@ -34,6 +34,8 @@
 
     @CompilationFinal private FrameAccess outsideFrameAccess = FrameAccess.NONE;
 
+    private boolean inliningForced;
+
     public DefaultCallNode(CallTarget target) {
         super(target);
     }
@@ -66,7 +68,8 @@
     }
 
     @Override
-    public void inline() {
+    public void forceInlining() {
+        inliningForced = true;
     }
 
     @Override
@@ -80,13 +83,18 @@
     }
 
     @Override
+    public boolean isInlined() {
+        return false;
+    }
+
+    @Override
     public boolean isSplittable() {
         return false;
     }
 
     @Override
-    public boolean isInlined() {
-        return false;
+    public boolean isInliningForced() {
+        return inliningForced;
     }
 
     @Override
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/CallNode.java	Mon Apr 14 18:25:23 2014 +0200
@@ -25,8 +25,6 @@
 package com.oracle.truffle.api.nodes;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.frame.*;
 
 /**
@@ -40,7 +38,7 @@
  * Please note: This class is not intended to be subclassed by guest language implementations.
  *
  * @see TruffleRuntime#createCallNode(CallTarget)
- * @see #inline()
+ * @see #forceInlining()
  * @see #split()
  */
 public abstract class CallNode extends Node {
@@ -79,19 +77,28 @@
     public abstract boolean isInlinable();
 
     /**
-     * Returns <code>true</code> if the {@link CallTarget} in this {@link CallNode} is inlined. A
-     * {@link CallNode} can either be inlined manually by invoking {@link #inline()} or by the
-     * runtime system which may at any point decide to inline.
+     * Returns <code>true</code> if the {@link CallTarget} is forced to be inlined. A
+     * {@link CallNode} can either be inlined manually by invoking {@link #forceInlining()} or by
+     * the runtime system which may at any point decide to inline.
      *
      * @return true if this method was inlined else false.
      */
-    public abstract boolean isInlined();
+    public abstract boolean isInliningForced();
 
     /**
      * Enforces the runtime system to inline the {@link CallTarget} at this call site. If the
      * runtime system does not support inlining or it is already inlined this method has no effect.
      */
-    public abstract void inline();
+    public abstract void forceInlining();
+
+    /**
+     * Returns true if the runtime system has decided to inline this call-site. If the
+     * {@link CallNode} was forced to inline then this does not necessarily mean that the
+     * {@link CallNode} is really going to be inlined. This depends on whether or not the runtime
+     * system supports inlining or not. The runtime system may also decide to ignore calls to
+     * {@link #forceInlining()}.
+     */
+    public abstract boolean isInlined();
 
     /**
      * Returns <code>true</code> if this {@link CallNode} can be split. A {@link CallNode} can only
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Mon Apr 14 18:20:09 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Mon Apr 14 18:25:23 2014 +0200
@@ -638,7 +638,7 @@
 
             if (visitInlinedCallNodes && node instanceof CallNode) {
                 CallNode call = (CallNode) node;
-                if (call.isInlined()) {
+                if (call.isInliningForced()) {
                     Node target = ((RootCallTarget) call.getCurrentCallTarget()).getRootNode();
                     if (target != null) {
                         target.accept(this);