changeset 17400:e3dd05527c2f

Truffle: enabled context sensitive inlining by default. removed old inlining structures.
author Christian Humer <christian.humer@gmail.com>
date Thu, 09 Oct 2014 17:25:24 +0200
parents 5787218bad91
children c0f71f81708a
files graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/ContextSensitiveInlining.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleSplittingStrategy.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalFrameInstance.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetLog.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallUtils.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedDirectCallNode.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedIndirectCallNode.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInlining.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningDecision.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningHandler.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultDirectCallNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/DirectCallNode.java
diffstat 19 files changed, 612 insertions(+), 815 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Thu Oct 09 17:25:24 2014 +0200
@@ -95,7 +95,7 @@
     }
 
     private static void installOptimizedCallTargetCallDirect() {
-        if (TruffleCompilerOptions.TruffleContextSensitiveInlining.getValue()) {
+        if (TruffleCompilerOptions.TruffleFunctionInlining.getValue()) {
             ((HotSpotResolvedJavaMethod) getGraalProviders().getMetaAccess().lookupJavaMethod(OptimizedCallTarget.getCallDirectMethod())).setNotInlineable();
         }
     }
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Thu Oct 09 17:25:24 2014 +0200
@@ -93,11 +93,10 @@
         compilable.call(arguments);
         compilable.call(arguments);
         compilable.call(arguments);
-        compilable.performInlining();
 
         try (Scope s = Debug.scope("TruffleCompilation", new TruffleDebugJavaMethod(compilable))) {
 
-            StructuredGraph resultGraph = truffleCompiler.getPartialEvaluator().createGraph(compilable, assumptions, null);
+            StructuredGraph resultGraph = truffleCompiler.getPartialEvaluator().createGraph(compilable, assumptions);
             CanonicalizerPhase canonicalizer = new CanonicalizerPhase(canonicalizeReads);
             PhaseContext context = new PhaseContext(getProviders(), assumptions);
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/ContextSensitiveInlining.java	Thu Oct 09 17:25:18 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +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 java.util.stream.*;
-
-import com.oracle.graal.truffle.ContextSensitiveInlining.InliningDecision;
-
-public class ContextSensitiveInlining implements Iterable<InliningDecision> {
-
-    private final List<InliningDecision> callSites;
-
-    private ContextSensitiveInlining(List<InliningDecision> callSites) {
-        this.callSites = callSites;
-    }
-
-    public ContextSensitiveInlining(OptimizedCallTarget sourceTarget, TruffleInliningPolicy policy) {
-        this(createDecisions(sourceTarget, policy));
-    }
-
-    private static List<InliningDecision> createDecisions(OptimizedCallTarget sourceTarget, TruffleInliningPolicy policy) {
-        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(sourceTarget, false);
-        List<InliningDecision> exploredCallSites = exploreCallSites(new ArrayList<>(Arrays.asList(sourceTarget)), nodeCount, policy);
-        return decideInlining(exploredCallSites, policy, nodeCount);
-    }
-
-    private static List<InliningDecision> exploreCallSites(List<OptimizedCallTarget> stack, int callStackNodeCount, TruffleInliningPolicy policy) {
-        List<InliningDecision> exploredCallSites = new ArrayList<>();
-        OptimizedCallTarget parentTarget = stack.get(stack.size() - 1);
-        for (OptimizedDirectCallNode callNode : parentTarget.getCallNodes()) {
-            OptimizedCallTarget currentTarget = callNode.getCurrentCallTarget();
-            stack.add(currentTarget); // push
-            exploredCallSites.add(exploreCallSite(stack, callStackNodeCount, policy, callNode));
-            stack.remove(stack.size() - 1); // pop
-        }
-        return exploredCallSites;
-    }
-
-    private static InliningDecision exploreCallSite(List<OptimizedCallTarget> callStack, int callStackNodeCount, TruffleInliningPolicy policy, OptimizedDirectCallNode callNode) {
-        OptimizedCallTarget parentTarget = callStack.get(callStack.size() - 2);
-        OptimizedCallTarget currentTarget = callStack.get(callStack.size() - 1);
-
-        boolean recursive = isRecursiveStack(callStack);
-        boolean maxDepth = callStack.size() >= 15;
-
-        List<InliningDecision> childCallSites = Collections.emptyList();
-        double frequency = TruffleInliningHandler.calculateFrequency(parentTarget, callNode);
-        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(callNode.getCurrentCallTarget(), false);
-
-        int deepNodeCount = nodeCount;
-        if (!recursive && !maxDepth) {
-            /*
-             * We make a preliminary optimistic inlining decision with best possible characteristics
-             * to avoid the exploration of unnecessary pathes in the inlining tree.
-             */
-            if (policy.isAllowed(new TruffleInliningProfile(callNode, nodeCount, nodeCount, frequency, recursive, null), callStackNodeCount)) {
-                List<InliningDecision> exploredCallSites = exploreCallSites(callStack, callStackNodeCount + nodeCount, policy);
-                childCallSites = decideInlining(exploredCallSites, policy, nodeCount);
-                for (InliningDecision childCallSite : childCallSites) {
-                    if (childCallSite.isInline()) {
-                        deepNodeCount += childCallSite.getProfile().getDeepNodeCount();
-                    } else {
-                        /* we don't need those anymore. */
-                        childCallSite.getCallSites().clear();
-                    }
-                }
-            }
-        }
-
-        TruffleInliningProfile profile = new TruffleInliningProfile(callNode, nodeCount, deepNodeCount, frequency, recursive, null);
-        profile.setScore(policy.calculateScore(profile));
-        return new InliningDecision(currentTarget, profile, childCallSites);
-    }
-
-    private static boolean isRecursiveStack(List<OptimizedCallTarget> stack) {
-        OptimizedCallTarget top = stack.get(stack.size() - 1);
-        for (int i = 0; i < stack.size() - 1; i++) {
-            if (stack.get(i) == top) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private static List<InliningDecision> decideInlining(List<InliningDecision> callSites, TruffleInliningPolicy policy, int nodeCount) {
-        int deepNodeCount = nodeCount;
-        int index = 0;
-        for (InliningDecision callSite : callSites.stream().sorted().collect(Collectors.toList())) {
-            TruffleInliningProfile profile = callSite.getProfile();
-            profile.setQueryIndex(index++);
-            if (policy.isAllowed(profile, deepNodeCount)) {
-                callSite.setInline(true);
-                deepNodeCount += profile.getDeepNodeCount();
-            }
-        }
-        return callSites;
-    }
-
-    public boolean isInlined(List<OptimizedDirectCallNode> callNodeTrace) {
-        if (callNodeTrace.isEmpty()) {
-            return false;
-        }
-
-        InliningDecision prev = null;
-        for (int i = 0; i < callNodeTrace.size(); i++) {
-            if (prev == null) {
-                prev = findByCall(callNodeTrace.get(i));
-            } else {
-                prev = prev.findByCall(callNodeTrace.get(i));
-            }
-            if (prev == null || !prev.isInline()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public int countCalls() {
-        return callSites.stream().mapToInt(callSite -> callSite.isInline() ? callSite.countCalls() + 1 : 1).sum();
-    }
-
-    public int countInlinedCalls() {
-        return callSites.stream().filter(InliningDecision::isInline).mapToInt(callSite -> callSite.countInlinedCalls() + 1).sum();
-    }
-
-    public List<InliningDecision> getCallSites() {
-        return callSites;
-    }
-
-    public Iterator<InliningDecision> iterator() {
-        return callSites.iterator();
-    }
-
-    public InliningDecision findByCall(OptimizedDirectCallNode callNode) {
-        return getCallSites().stream().filter(c -> c.getProfile().getCallNode() == callNode).findFirst().orElse(null);
-    }
-
-    public static final class InliningDecision extends ContextSensitiveInlining implements Comparable<InliningDecision> {
-
-        private final OptimizedCallTarget target;
-        private final TruffleInliningProfile profile;
-        private boolean inline;
-
-        public InliningDecision(OptimizedCallTarget target, TruffleInliningProfile profile, List<InliningDecision> children) {
-            super(children);
-            this.target = target;
-            this.profile = profile;
-        }
-
-        public OptimizedCallTarget getTarget() {
-            return target;
-        }
-
-        public void setInline(boolean inline) {
-            this.inline = inline;
-        }
-
-        public boolean isInline() {
-            return inline;
-        }
-
-        public TruffleInliningProfile getProfile() {
-            return profile;
-        }
-
-        public int compareTo(InliningDecision o) {
-            return Double.compare(o.getProfile().getScore(), getProfile().getScore());
-        }
-
-        public boolean isSameAs(InliningDecision other) {
-            if (getTarget() != other.getTarget()) {
-                return false;
-            } else if (isInline() != other.isInline()) {
-                return false;
-            } else if (!isInline()) {
-                assert !other.isInline();
-                return true;
-            } else {
-                Iterator<InliningDecision> i1 = iterator();
-                Iterator<InliningDecision> i2 = other.iterator();
-                while (i1.hasNext() && i2.hasNext()) {
-                    if (!i1.next().isSameAs(i2.next())) {
-                        return false;
-                    }
-                }
-                return !i1.hasNext() && !i2.hasNext();
-            }
-        }
-
-        @Override
-        public String toString() {
-            return String.format("InliningDecision(callNode=%s, inline=%b)", profile.getCallNode(), inline);
-        }
-
-    }
-
-}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleSplittingStrategy.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultTruffleSplittingStrategy.java	Thu Oct 09 17:25:24 2014 +0200
@@ -34,7 +34,7 @@
     }
 
     public void beforeCall(Object[] arguments) {
-        if (call.getCallCount() == 2 && !call.isInlined()) {
+        if (call.getCallCount() == 2) {
             if (shouldSplit()) {
                 forceSplitting();
             }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalFrameInstance.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalFrameInstance.java	Thu Oct 09 17:25:24 2014 +0200
@@ -87,14 +87,14 @@
 
     /**
      * This class represents a frame that is taken from the
-     * {@link OptimizedDirectCallNode#callProxy(MaterializedFrameNotify, CallTarget, VirtualFrame, Object[], boolean, boolean)}
+     * {@link OptimizedDirectCallNode#callProxy(MaterializedFrameNotify, CallTarget, VirtualFrame, Object[], boolean)}
      * method.
      */
     public static final class CallNodeFrame extends GraalFrameInstance {
         public static final Method METHOD;
         static {
             try {
-                METHOD = OptimizedDirectCallNode.class.getDeclaredMethod("callProxy", MaterializedFrameNotify.class, CallTarget.class, VirtualFrame.class, Object[].class, boolean.class, boolean.class);
+                METHOD = OptimizedDirectCallNode.class.getDeclaredMethod("callProxy", MaterializedFrameNotify.class, CallTarget.class, VirtualFrame.class, Object[].class, boolean.class);
             } catch (NoSuchMethodException | SecurityException e) {
                 throw new GraalInternalError(e);
             }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Thu Oct 09 17:25:24 2014 +0200
@@ -29,15 +29,16 @@
 import java.lang.reflect.*;
 import java.util.*;
 import java.util.concurrent.atomic.*;
+import java.util.stream.*;
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.debug.*;
-import com.oracle.graal.truffle.ContextSensitiveInlining.InliningDecision;
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.nodes.Node;
 
 /**
  * Call target that is optimized by Graal upon surpassing a specific invocation threshold.
@@ -66,7 +67,7 @@
     private TruffleStamp argumentStamp = DefaultTruffleStamp.getInstance();
 
     /* Experimental field for context sensitive inlining. */
-    private ContextSensitiveInlining inliningDecision;
+    private TruffleInlining inlining;
 
     public final RootNode getRootNode() {
         return rootNode;
@@ -256,49 +257,18 @@
         if (isValid()) {
             CompilerAsserts.neverPartOfCompilation();
             invalidate();
-            invalidateInlining();
             compilationProfile.reportInvalidated();
             logOptimizedInvalidated(this, oldNode, newNode, reason);
         }
         cancelInstalledTask(oldNode, newNode, reason);
-        // invalidateInlining();
     }
 
-    public void invalidateInlining() {
-        if (inliningPerformed) {
-            inliningPerformed = false;
-            getRootNode().accept(new NodeVisitor() {
-                public boolean visit(Node node) {
-                    if (node instanceof OptimizedDirectCallNode) {
-                        OptimizedDirectCallNode callNode = (OptimizedDirectCallNode) node;
-                        if (callNode.isInlined()) {
-                            callNode.resetInlining();
-                        }
-                    }
-                    return true;
-                }
-            });
-        }
+    public TruffleInlining getInlining() {
+        return inlining;
     }
 
-    public ContextSensitiveInlining getInliningDecision() {
-        return inliningDecision;
-    }
-
-    public void setInliningDecision(ContextSensitiveInlining inliningDecision) {
-        this.inliningDecision = inliningDecision;
-    }
-
-    public boolean isInlined(List<OptimizedDirectCallNode> callNodeTrace) {
-        if (TruffleCompilerOptions.TruffleContextSensitiveInlining.getValue()) {
-            if (inliningDecision == null) {
-                return false;
-            } else {
-                return inliningDecision.isInlined(callNodeTrace);
-            }
-        } else {
-            return callNodeTrace.get(callNodeTrace.size() - 1).isInlined();
-        }
+    public void setInlining(TruffleInlining inliningDecision) {
+        this.inlining = inliningDecision;
     }
 
     private void cancelInstalledTask(Node oldNode, Node newNode, CharSequence reason) {
@@ -326,7 +296,6 @@
 
     public void compile() {
         if (!runtime.isCompiling(this)) {
-            performInlining();
             logOptimizingQueued(this);
             runtime.compile(this, TruffleBackgroundCompilation.getValue());
         }
@@ -335,8 +304,8 @@
     public void compilationFinished(Throwable t) {
         if (t == null) {
             // Compilation was successful.
-            if (inliningDecision != null) {
-                dequeueInlinedCallSites(inliningDecision);
+            if (inlining != null) {
+                dequeueInlinedCallSites(inlining);
             }
         } else {
             if (!(t instanceof BailoutException) || ((BailoutException) t).isPermanent()) {
@@ -346,7 +315,7 @@
             if (TruffleCompilationExceptionsAreThrown.getValue()) {
                 throw new OptimizationFailedException(t, rootNode);
             }
-            logOptimizingFailed(this, t.getMessage());
+            logOptimizingFailed(this, t.toString());
             if (t instanceof BailoutException) {
                 // Bailout => move on.
             } else if (TruffleCompilationExceptionsAreFatal.getValue()) {
@@ -356,10 +325,10 @@
         }
     }
 
-    private void dequeueInlinedCallSites(ContextSensitiveInlining parentDecision) {
-        for (InliningDecision decision : parentDecision) {
+    private void dequeueInlinedCallSites(TruffleInlining parentDecision) {
+        for (TruffleInliningDecision decision : parentDecision) {
             if (decision.isInline()) {
-                OptimizedCallTarget target = decision.getProfile().getCallNode().getCurrentCallTarget();
+                OptimizedCallTarget target = decision.getTarget();
                 if (runtime.cancelInstalledTask(target)) {
                     logOptimizingUnqueued(target, null, null, "Inlining caller compiled.");
                 }
@@ -413,33 +382,6 @@
         return compilationProfile;
     }
 
-    public final void performInlining() {
-        if (!TruffleFunctionInlining.getValue() || TruffleContextSensitiveInlining.getValue()) {
-            return;
-        }
-        if (inliningPerformed) {
-            return;
-        }
-        TruffleInliningHandler handler = new TruffleInliningHandler(new DefaultInliningPolicy());
-        TruffleInliningDecision result = handler.decideInlining(this, 0);
-        performInlining(result);
-        logInliningDecision(result);
-    }
-
-    private static void performInlining(TruffleInliningDecision result) {
-        if (result.getCallTarget().inliningPerformed) {
-            return;
-        }
-        result.getCallTarget().inliningPerformed = true;
-        for (TruffleInliningProfile profile : result) {
-            profile.getCallNode().inline();
-            TruffleInliningDecision recursiveResult = profile.getRecursiveResult();
-            if (recursiveResult != null) {
-                performInlining(recursiveResult);
-            }
-        }
-    }
-
     @ExplodeLoop
     private Object[] castArguments(Object[] originalArguments) {
         Object[] castArguments = new Object[profiledArgumentTypes.length];
@@ -497,7 +439,6 @@
         addASTSizeProperty(this, properties);
         properties.putAll(getCompilationProfile().getDebugProperties());
         return properties;
-
     }
 
     public static Method getCallDirectMethod() {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetLog.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetLog.java	Thu Oct 09 17:25:24 2014 +0200
@@ -28,9 +28,8 @@
 import java.util.*;
 
 import com.oracle.graal.debug.*;
-import com.oracle.graal.truffle.ContextSensitiveInlining.InliningDecision;
+import com.oracle.graal.truffle.TruffleInlining.CallTreeNodeVisitor;
 import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.nodes.NodeUtil.NodeCountFilter;
 
 public final class OptimizedCallTargetLog {
 
@@ -55,8 +54,8 @@
     }
 
     public static void logInliningDecision(OptimizedCallTarget target) {
-        ContextSensitiveInlining inlining = target.getInliningDecision();
-        if (!TraceTruffleInlining.getValue() || inlining == null) {
+        TruffleInlining inlining = target.getInlining();
+        if (inlining == null) {
             return;
         }
 
@@ -65,8 +64,8 @@
         logInliningDone(target);
     }
 
-    private static void logInliningDecisionRecursive(ContextSensitiveInlining result, int depth) {
-        for (InliningDecision decision : result) {
+    private static void logInliningDecisionRecursive(TruffleInlining result, int depth) {
+        for (TruffleInliningDecision decision : result) {
             TruffleInliningProfile profile = decision.getProfile();
             boolean inlined = decision.isInline();
             String msg = inlined ? "inline success" : "inline failed";
@@ -77,36 +76,12 @@
         }
     }
 
-    public static void logInliningDecision(TruffleInliningDecision result) {
-        if (!TraceTruffleInlining.getValue()) {
-            return;
-        }
-
-        logInliningStart(result.getCallTarget());
-        logInliningDecisionRecursive(result, 0);
-        logInliningDone(result.getCallTarget());
-    }
+    public static void logTruffleCalls(OptimizedCallTarget compilable) {
+        CallTreeNodeVisitor visitor = new CallTreeNodeVisitor() {
 
-    private static void logInliningDecisionRecursive(TruffleInliningDecision result, int depth) {
-        List<OptimizedDirectCallNode> callNodes = searchCallNodes(result.getCallTarget());
-        for (OptimizedDirectCallNode callNode : callNodes) {
-            TruffleInliningProfile profile = result.getProfiles().get(callNode);
-            boolean inlined = result.isInlined(callNode);
-            String msg = inlined ? "inline success" : "inline failed";
-            logInlinedImpl(msg, callNode, profile, depth);
-            if (profile.getRecursiveResult() != null && inlined) {
-                logInliningDecisionRecursive(profile.getRecursiveResult(), depth + 1);
-            }
-        }
-    }
-
-    public static void logTruffleCalls(OptimizedCallTarget compilable) {
-        compilable.getRootNode().accept(new NodeVisitor() {
-
-            int depth = 1;
-
-            public boolean visit(Node node) {
+            public boolean visit(List<TruffleInlining> decisionStack, Node node) {
                 if (node instanceof OptimizedDirectCallNode) {
+                    int depth = decisionStack == null ? 0 : decisionStack.size();
                     OptimizedDirectCallNode callNode = ((OptimizedDirectCallNode) node);
                     String dispatched = !callNode.isInlined() ? " <dispatched>" : "";
                     Map<String, Object> properties = new LinkedHashMap<>();
@@ -114,31 +89,21 @@
                     properties.putAll(callNode.getCurrentCallTarget().getDebugProperties());
                     properties.put("Stamp", callNode.getCurrentCallTarget().getArgumentStamp());
                     log((depth * 2), "call", callNode.getCurrentCallTarget().toString() + dispatched, properties);
-
-                    if (callNode.isInlined()) {
-                        depth++;
-                        callNode.getCurrentRootNode().accept(this);
-                        depth--;
-                    }
                 } else if (node instanceof OptimizedIndirectCallNode) {
+                    int depth = decisionStack == null ? 0 : decisionStack.size();
                     log((depth * 2), "call", "<indirect>", new LinkedHashMap<String, Object>());
                 }
                 return true;
             }
-        });
-    }
+
+        };
 
-    private static List<OptimizedDirectCallNode> searchCallNodes(final OptimizedCallTarget target) {
-        final List<OptimizedDirectCallNode> callNodes = new ArrayList<>();
-        target.getRootNode().accept(new NodeVisitor() {
-            public boolean visit(Node node) {
-                if (node instanceof OptimizedDirectCallNode) {
-                    callNodes.add((OptimizedDirectCallNode) node);
-                }
-                return true;
-            }
-        });
-        return callNodes;
+        TruffleInlining inlining = compilable.getInlining();
+        if (inlining == null) {
+            compilable.getRootNode().accept(visitor);
+        } else {
+            inlining.accept(compilable, visitor);
+        }
     }
 
     private static void logInlinedImpl(String status, OptimizedDirectCallNode callNode, TruffleInliningProfile profile, int depth) {
@@ -247,26 +212,13 @@
     }
 
     public static void addASTSizeProperty(OptimizedCallTarget target, Map<String, Object> properties) {
-        if (TruffleContextSensitiveInlining.getValue() && target.getInliningDecision() != null) {
-            int deepCount = target.getInliningDecision().getCallSites().stream().filter(callSite -> callSite.isInline()).mapToInt(callSite -> callSite.getProfile().getDeepNodeCount()).sum();
-            long nodeCount = OptimizedCallUtils.countNonTrivialNodes(target, false);
-            properties.put("ASTSize", String.format("%5d/%5d", nodeCount, nodeCount + deepCount));
-        } else {
-            int polymorphicCount = NodeUtil.countNodes(target.getRootNode(), new NodeCountFilter() {
-                public boolean isCounted(Node node) {
-                    return node.getCost() == NodeCost.POLYMORPHIC;
-                }
-            }, true);
-
-            int megamorphicCount = NodeUtil.countNodes(target.getRootNode(), new NodeCountFilter() {
-                public boolean isCounted(Node node) {
-                    return node.getCost() == NodeCost.MEGAMORPHIC;
-                }
-            }, true);
-
-            String value = String.format("%4d (%d/%d)", OptimizedCallUtils.countNonTrivialNodes(target, true), polymorphicCount, megamorphicCount);
-            properties.put("ASTSize", value);
+        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(target, false);
+        int deepNodeCount = nodeCount;
+        TruffleInlining inlining = target.getInlining();
+        if (inlining != null) {
+            deepNodeCount += inlining.getInlinedNodeCount();
         }
+        properties.put("ASTSize", String.format("%5d/%5d", nodeCount, deepNodeCount));
 
     }
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallUtils.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallUtils.java	Thu Oct 09 17:25:24 2014 +0200
@@ -23,99 +23,66 @@
 package com.oracle.graal.truffle;
 
 import java.io.*;
+import java.util.*;
 
+import com.oracle.graal.truffle.TruffleInlining.CallTreeNodeVisitor;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.nodes.NodeUtil.NodeClass;
-import com.oracle.truffle.api.nodes.NodeUtil.NodeCountFilter;
 import com.oracle.truffle.api.nodes.NodeUtil.NodeField;
 
 public class OptimizedCallUtils {
 
-    public static int countCalls(OptimizedCallTarget target) {
-        ContextSensitiveInlining inlining = target.getInliningDecision();
-        if (inlining != null) {
-            return inlining.countCalls();
-        } else {
-            return NodeUtil.countNodes(target.getRootNode(), new NodeCountFilter() {
-                public boolean isCounted(Node node) {
-                    return node instanceof DirectCallNode;
-                }
-            }, true);
-        }
+    public static int countNonTrivialNodes(final OptimizedCallTarget target, final boolean inlined) {
+        return (int) target.nodeStream(inlined).filter(e -> e != null && !e.getCost().isTrivial()).count();
     }
 
-    public static int countCallsInlined(OptimizedCallTarget target) {
-        ContextSensitiveInlining inlining = target.getInliningDecision();
-        if (inlining != null) {
-            return inlining.countInlinedCalls();
-        } else {
-            return NodeUtil.countNodes(target.getRootNode(), new NodeCountFilter() {
-                public boolean isCounted(Node node) {
-                    return (node instanceof OptimizedDirectCallNode) && ((OptimizedDirectCallNode) node).isInlined();
-                }
-            }, true);
-        }
-    }
-
-    public static int countNonTrivialNodes(final OptimizedCallTarget target, final boolean inlined) {
-        return NodeUtil.countNodes(target.getRootNode(), 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);
-    }
-
-    public static void printCompactTree(OutputStream out, Node node) {
-        printCompactTree(new PrintWriter(out), null, node, 1);
+    public static void printCompactTree(OutputStream out, OptimizedCallTarget target) {
+        printCompactTree(new PrintWriter(out), target);
     }
 
-    private static void printCompactTree(PrintWriter p, Node parent, Node node, int level) {
-        if (node == null) {
-            return;
-        }
-        for (int i = 0; i < level; i++) {
-            p.print("  ");
-        }
-        if (parent == null) {
-            p.println(node.getClass().getSimpleName());
-        } else {
-            String fieldName = "unknownField";
-            NodeField[] fields = NodeClass.get(parent.getClass()).getFields();
-            for (NodeField field : fields) {
-                Object value = field.loadValue(parent);
-                if (value == node) {
-                    fieldName = field.getName();
-                    break;
-                } else if (value instanceof Node[]) {
-                    int index = 0;
-                    for (Node arrayNode : (Node[]) value) {
-                        if (arrayNode == node) {
-                            fieldName = field.getName() + "[" + index + "]";
+    public static void printCompactTree(PrintWriter p, OptimizedCallTarget target) {
+        target.accept(new CallTreeNodeVisitor() {
+
+            public boolean visit(List<TruffleInlining> decisionStack, Node node) {
+                if (node == null) {
+                    return false;
+                }
+                int level = CallTreeNodeVisitor.getNodeDepth(decisionStack, node);
+                for (int i = 0; i < level; i++) {
+                    p.print("  ");
+                }
+                Node parent = node.getParent();
+
+                if (parent == null) {
+                    p.println(node.getClass().getSimpleName());
+                } else {
+                    String fieldName = "unknownField";
+                    NodeField[] fields = NodeClass.get(parent.getClass()).getFields();
+                    for (NodeField field : fields) {
+                        Object value = field.loadValue(parent);
+                        if (value == node) {
+                            fieldName = field.getName();
                             break;
+                        } else if (value instanceof Node[]) {
+                            int index = 0;
+                            for (Node arrayNode : (Node[]) value) {
+                                if (arrayNode == node) {
+                                    fieldName = field.getName() + "[" + index + "]";
+                                    break;
+                                }
+                                index++;
+                            }
                         }
-                        index++;
                     }
+                    p.print(fieldName);
+                    p.print(" = ");
+                    p.println(node.getClass().getSimpleName());
                 }
+                p.flush();
+                return false;
             }
-            p.print(fieldName);
-            p.print(" = ");
-            p.println(node.getClass().getSimpleName());
-        }
 
-        for (Node child : node.getChildren()) {
-            printCompactTree(p, node, child, level + 1);
-        }
-        if (node instanceof OptimizedDirectCallNode) {
-            OptimizedDirectCallNode callNode = (OptimizedDirectCallNode) node;
-            if (callNode.isInlined()) {
-                printCompactTree(p, node, callNode.getCurrentRootNode(), level + 1);
-            }
-        }
-        p.flush();
+        }, true);
     }
 
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedDirectCallNode.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedDirectCallNode.java	Thu Oct 09 17:25:24 2014 +0200
@@ -37,7 +37,6 @@
     private int callCount;
     private boolean inliningForced;
 
-    @CompilationFinal private boolean inlined;
     @CompilationFinal private OptimizedCallTarget splitCallTarget;
     @CompilationFinal private FrameAccess outsideFrameAccess = FrameAccess.NONE;
 
@@ -57,14 +56,7 @@
         if (CompilerDirectives.inInterpreter()) {
             onInterpreterCall(arguments);
         }
-        boolean isInlined;
-        if (TruffleCompilerOptions.TruffleContextSensitiveInlining.getValue()) {
-            /* Inlining done during partial evalulation. */
-            isInlined = false;
-        } else {
-            isInlined = this.inlined;
-        }
-        Object result = callProxy(this, getCurrentCallTarget(), frame, arguments, isInlined, true);
+        Object result = callProxy(this, getCurrentCallTarget(), frame, arguments, true);
 
         if (CompilerDirectives.inInterpreter()) {
             afterInterpreterCall(result);
@@ -74,17 +66,14 @@
 
     private void afterInterpreterCall(Object result) {
         splittingStrategy.afterCall(result);
-        // propagateInliningInvalidations();
     }
 
-    public static Object callProxy(MaterializedFrameNotify notify, CallTarget callTarget, VirtualFrame frame, Object[] arguments, boolean inlined, boolean direct) {
+    public static Object callProxy(MaterializedFrameNotify notify, CallTarget callTarget, VirtualFrame frame, Object[] arguments, boolean direct) {
         try {
             if (notify.getOutsideFrameAccess() != FrameAccess.NONE) {
                 CompilerDirectives.materialize(frame);
             }
-            if (inlined) {
-                return ((OptimizedCallTarget) callTarget).callInlined(arguments);
-            } else if (direct) {
+            if (direct) {
                 return ((OptimizedCallTarget) callTarget).callDirect(arguments);
             } else {
                 return callTarget.call(arguments);
@@ -95,13 +84,6 @@
         }
     }
 
-    public void resetInlining() {
-        CompilerAsserts.neverPartOfCompilation();
-        if (inlined && !inliningForced) {
-            inlined = false;
-        }
-    }
-
     @Override
     public boolean isInlinable() {
         return true;
@@ -157,7 +139,6 @@
             getCurrentCallTarget().incrementKnownCallSites();
         }
         splittingStrategy.beforeCall(arguments);
-        // propagateInliningInvalidations();
     }
 
     /** Used by the splitting strategy to install new targets. */
@@ -184,23 +165,6 @@
         }
     }
 
-    @SuppressWarnings("unused")
-    private void propagateInliningInvalidations() {
-        if (isInlined() && !getCurrentCallTarget().inliningPerformed) {
-            replace(this, "Propagate invalid inlining from " + getCurrentCallTarget().toString());
-        }
-    }
-
-    /* Called by the runtime system if this CallNode is really going to be inlined. */
-    void inline() {
-        inlined = true;
-    }
-
-    @Override
-    public boolean isInlined() {
-        return inlined;
-    }
-
     @Override
     public boolean split() {
         splittingStrategy.forceSplitting();
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedIndirectCallNode.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedIndirectCallNode.java	Thu Oct 09 17:25:24 2014 +0200
@@ -38,7 +38,7 @@
 
     @Override
     public Object call(VirtualFrame frame, CallTarget target, Object[] arguments) {
-        return OptimizedDirectCallNode.callProxy(this, target, frame, arguments, false, false);
+        return OptimizedDirectCallNode.callProxy(this, target, frame, arguments, false);
     }
 
     @Override
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Thu Oct 09 17:25:24 2014 +0200
@@ -51,7 +51,6 @@
 import com.oracle.graal.phases.common.inlining.info.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.phases.util.*;
-import com.oracle.graal.truffle.ContextSensitiveInlining.InliningDecision;
 import com.oracle.graal.truffle.nodes.asserts.*;
 import com.oracle.graal.truffle.nodes.frame.*;
 import com.oracle.graal.truffle.nodes.frame.NewFrameNode.VirtualOnlyInstanceNode;
@@ -83,7 +82,7 @@
         this.callSiteProxyMethod = providers.getMetaAccess().lookupJavaMethod(GraalFrameInstance.CallNodeFrame.METHOD);
     }
 
-    public StructuredGraph createGraph(final OptimizedCallTarget callTarget, final Assumptions assumptions, ContextSensitiveInlining inlining) {
+    public StructuredGraph createGraph(final OptimizedCallTarget callTarget, final Assumptions assumptions) {
         if (TraceTruffleCompilationHistogram.getValue() || TraceTruffleCompilationDetails.getValue()) {
             constantReceivers = new HashSet<>();
         }
@@ -106,7 +105,15 @@
 
             expandTree(graph, assumptions);
 
-            expandDirectCalls(graph, assumptions, inlining != null ? new TruffleInliningCache() : null, inlining);
+            TruffleInliningCache inliningCache = null;
+            if (TruffleFunctionInlining.getValue()) {
+                callTarget.setInlining(new TruffleInlining(callTarget, new DefaultInliningPolicy()));
+                if (TruffleFunctionInliningCache.getValue()) {
+                    inliningCache = new TruffleInliningCache();
+                }
+            }
+
+            expandDirectCalls(graph, assumptions, callTarget.getInlining(), inliningCache);
 
             if (Thread.currentThread().isInterrupted()) {
                 return null;
@@ -171,34 +178,6 @@
         return graph;
     }
 
-    private void expandDirectCalls(StructuredGraph graph, Assumptions assumptions, TruffleInliningCache inliningCache, ContextSensitiveInlining inlining) {
-        if (inlining == null) {
-            return;
-        }
-        PhaseContext phaseContext = new PhaseContext(providers, assumptions);
-        TruffleExpansionLogger expansionLogger = new TruffleExpansionLogger(providers, graph);
-
-        for (MethodCallTargetNode methodCallTargetNode : graph.getNodes(MethodCallTargetNode.class).snapshot()) {
-            StructuredGraph inlineGraph = parseDirectCallGraph(phaseContext, assumptions, inliningCache, inlining, methodCallTargetNode);
-
-            if (inlineGraph != null) {
-                expandTreeInline(graph, phaseContext, expansionLogger, methodCallTargetNode, inlineGraph);
-            }
-        }
-        // non inlined direct calls need to be expanded until TruffleCallBoundary.
-        expandTree(graph, assumptions);
-        assert noDirectCallsLeft(graph);
-    }
-
-    private boolean noDirectCallsLeft(StructuredGraph graph) {
-        for (MethodCallTargetNode methodCallTargetNode : graph.getNodes(MethodCallTargetNode.class).snapshot()) {
-            if (methodCallTargetNode.targetMethod().equals(callDirectMethod)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     private void injectConstantCallTarget(final StructuredGraph graph, final OptimizedCallTarget constantCallTarget, PhaseContext baseContext) {
         ParameterNode thisNode = graph.getParameter(0);
 
@@ -283,114 +262,16 @@
         return changed;
     }
 
-    private StructuredGraph parseDirectCallGraph(PhaseContext phaseContext, Assumptions assumptions, TruffleInliningCache inliningCache, ContextSensitiveInlining inlining,
-                    MethodCallTargetNode methodCallTargetNode) {
-        OptimizedDirectCallNode callNode = resolveConstantCallNode(methodCallTargetNode);
-        if (callNode == null) {
-            return null;
-        }
-
-        InliningDecision decision = inlining.findByCall(callNode);
-        if (decision == null) {
-            if (TruffleCompilerOptions.PrintTrufflePerformanceWarnings.getValue()) {
-                Map<String, Object> properties = new LinkedHashMap<>();
-                properties.put("callNode", callNode);
-                logPerformanceWarning("A direct call within the Truffle AST is not reachable anymore. Call node was not inlined.", properties);
-            }
-            return null;
-        }
-
-        assert decision.getProfile().getCallNode() == callNode;
-
-        OptimizedCallTarget currentTarget = decision.getProfile().getCallNode().getCurrentCallTarget();
-        if (decision.getTarget() != currentTarget) {
-            if (TruffleCompilerOptions.PrintTrufflePerformanceWarnings.getValue()) {
-                Map<String, Object> properties = new LinkedHashMap<>();
-                properties.put("originalTarget", decision.getTarget());
-                properties.put("callNode", callNode);
-                logPerformanceWarning(String.format("CallTarget changed during compilation. Call node was not inlined."), properties);
-            }
-            return null;
-        }
-
-        StructuredGraph graph;
-        if (decision.isInline()) {
-            if (inliningCache == null) {
-                graph = createInlineGraph(phaseContext, assumptions, null, decision);
-            } else {
-                graph = inliningCache.getCachedGraph(phaseContext, assumptions, decision);
-            }
-            decision.getProfile().setGraalDeepNodeCount(graph.getNodeCount());
-        } else {
-            graph = parseGraph(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, phaseContext);
-        }
-
-        return graph;
-    }
-
-    private StructuredGraph createInlineGraph(PhaseContext phaseContext, Assumptions assumptions, TruffleInliningCache cache, InliningDecision decision) {
-        try (Scope s = Debug.scope("GuestLanguageInlinedGraph", new DebugDumpScope(decision.getTarget().toString()))) {
-            OptimizedCallTarget target = decision.getTarget();
-            StructuredGraph inlineGraph = truffleCache.createInlineGraph(target.toString());
-            injectConstantCallTarget(inlineGraph, decision.getTarget(), phaseContext);
-            expandTree(inlineGraph, assumptions);
-            expandDirectCalls(inlineGraph, assumptions, cache, decision);
-            return inlineGraph;
-        } catch (Throwable e) {
-            throw Debug.handle(e);
-        }
-    }
-
-    private OptimizedDirectCallNode resolveConstantCallNode(MethodCallTargetNode methodCallTargetNode) {
-        if (!methodCallTargetNode.targetMethod().equals(callDirectMethod)) {
-            return null;
-        }
-
-        Invoke invoke = methodCallTargetNode.invoke();
-        if (invoke == null) {
-            return null;
-        }
-
-        FrameState directCallState = invoke.stateAfter();
-        while (directCallState != null && directCallState.method() != callSiteProxyMethod) {
-            directCallState = directCallState.outerFrameState();
-        }
-
-        if (directCallState == null) {
-            // not a direct call. May be indirect call.
-            return null;
-        }
-
-        if (directCallState.values().isEmpty()) {
-            throw new AssertionError(String.format("Frame state of method '%s' is invalid.", callDirectMethod.toString()));
-        }
-
-        ValueNode node = directCallState.values().get(0);
-        if (!node.isConstant()) {
-            throw new AssertionError(String.format("Method argument for method '%s' is not constant.", callDirectMethod.toString()));
-        }
-
-        Constant constantCallNode = node.asConstant();
-        Object value = snippetReflection.asObject(constantCallNode);
-
-        if (!(value instanceof OptimizedDirectCallNode)) {
-            // might be an indirect call.
-            return null;
-        }
-
-        return (OptimizedDirectCallNode) value;
-    }
-
     private void expandTreeInline(StructuredGraph graph, PhaseContext phaseContext, TruffleExpansionLogger expansionLogger, MethodCallTargetNode methodCallTargetNode, StructuredGraph inlineGraph) {
         try (Indent indent = Debug.logAndIndent("expand graph %s", methodCallTargetNode.targetMethod())) {
             int nodeCountBefore = graph.getNodeCount();
-            if (TraceTruffleExpansion.getValue()) {
+            if (TraceTruffleExpansion.getValue() && expansionLogger != null) {
                 expansionLogger.preExpand(methodCallTargetNode, inlineGraph);
             }
             List<Node> canonicalizedNodes = methodCallTargetNode.invoke().asNode().usages().snapshot();
 
             Map<Node, Node> inlined = InliningUtil.inline(methodCallTargetNode.invoke(), inlineGraph, false, canonicalizedNodes);
-            if (TraceTruffleExpansion.getValue()) {
+            if (TraceTruffleExpansion.getValue() && expansionLogger != null) {
                 expansionLogger.postExpand(inlined);
             }
             if (Debug.isDumpEnabled()) {
@@ -454,6 +335,132 @@
         }
     }
 
+    private void expandDirectCalls(StructuredGraph graph, Assumptions assumptions, TruffleInlining inlining, TruffleInliningCache inliningCache) {
+        PhaseContext phaseContext = new PhaseContext(providers, assumptions);
+
+        for (MethodCallTargetNode methodCallTargetNode : graph.getNodes(MethodCallTargetNode.class).snapshot()) {
+            StructuredGraph inlineGraph = parseDirectCallGraph(phaseContext, assumptions, inlining, inliningCache, methodCallTargetNode);
+
+            if (inlineGraph != null) {
+                expandTreeInline(graph, phaseContext, null, methodCallTargetNode, inlineGraph);
+            }
+        }
+        // non inlined direct calls need to be expanded until TruffleCallBoundary.
+        expandTree(graph, assumptions);
+        assert noDirectCallsLeft(graph);
+    }
+
+    private boolean noDirectCallsLeft(StructuredGraph graph) {
+        for (MethodCallTargetNode methodCallTargetNode : graph.getNodes(MethodCallTargetNode.class).snapshot()) {
+            if (methodCallTargetNode.targetMethod().equals(callDirectMethod)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private StructuredGraph parseDirectCallGraph(PhaseContext phaseContext, Assumptions assumptions, TruffleInlining inlining, TruffleInliningCache inliningCache,
+                    MethodCallTargetNode methodCallTargetNode) {
+        OptimizedDirectCallNode callNode = resolveConstantCallNode(methodCallTargetNode);
+        if (callNode == null) {
+            return null;
+        }
+
+        TruffleInliningDecision decision = inlining.findByCall(callNode);
+        boolean inline;
+        if (decision == null) {
+            if (TruffleCompilerOptions.PrintTrufflePerformanceWarnings.getValue()) {
+                Map<String, Object> properties = new LinkedHashMap<>();
+                properties.put("callNode", callNode);
+                logPerformanceWarning("A direct call within the Truffle AST is not reachable anymore. Call node could not be inlined.", properties);
+            }
+            inline = false;
+        } else {
+            inline = decision.isInline();
+        }
+
+        assert decision.getProfile().getCallNode() == callNode;
+
+        OptimizedCallTarget currentTarget = decision.getProfile().getCallNode().getCurrentCallTarget();
+        if (decision.getTarget() != currentTarget) {
+            if (TruffleCompilerOptions.PrintTrufflePerformanceWarnings.getValue()) {
+                Map<String, Object> properties = new LinkedHashMap<>();
+                properties.put("originalTarget", decision.getTarget());
+                properties.put("callNode", callNode);
+                logPerformanceWarning(String.format("CallTarget changed during compilation. Call node could not be inlined."), properties);
+            }
+            inline = false;
+        }
+
+        StructuredGraph graph;
+        if (inline) {
+            if (inliningCache == null) {
+                graph = createInlineGraph(phaseContext, assumptions, null, decision);
+            } else {
+                graph = inliningCache.getCachedGraph(phaseContext, assumptions, decision);
+            }
+            decision.getProfile().setGraalDeepNodeCount(graph.getNodeCount());
+        } else {
+            // we continue expansion of callDirect until we reach the callBoundary.
+            graph = parseGraph(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, phaseContext);
+        }
+
+        return graph;
+    }
+
+    private OptimizedDirectCallNode resolveConstantCallNode(MethodCallTargetNode methodCallTargetNode) {
+        if (!methodCallTargetNode.targetMethod().equals(callDirectMethod)) {
+            return null;
+        }
+
+        Invoke invoke = methodCallTargetNode.invoke();
+        if (invoke == null) {
+            return null;
+        }
+
+        FrameState directCallState = invoke.stateAfter();
+        while (directCallState != null && directCallState.method() != callSiteProxyMethod) {
+            directCallState = directCallState.outerFrameState();
+        }
+
+        if (directCallState == null) {
+            // not a direct call. May be indirect call.
+            return null;
+        }
+
+        if (directCallState.values().isEmpty()) {
+            throw new AssertionError(String.format("Frame state of method '%s' is invalid.", callDirectMethod.toString()));
+        }
+
+        ValueNode node = directCallState.values().get(0);
+        if (!node.isConstant()) {
+            throw new AssertionError(String.format("Method argument for method '%s' is not constant.", callDirectMethod.toString()));
+        }
+
+        Constant constantCallNode = node.asConstant();
+        Object value = snippetReflection.asObject(constantCallNode);
+
+        if (!(value instanceof OptimizedDirectCallNode)) {
+            // might be an indirect call.
+            return null;
+        }
+
+        return (OptimizedDirectCallNode) value;
+    }
+
+    private StructuredGraph createInlineGraph(PhaseContext phaseContext, Assumptions assumptions, TruffleInliningCache cache, TruffleInliningDecision decision) {
+        try (Scope s = Debug.scope("GuestLanguageInlinedGraph", new DebugDumpScope(decision.getTarget().toString()))) {
+            OptimizedCallTarget target = decision.getTarget();
+            StructuredGraph inlineGraph = truffleCache.createInlineGraph(target.toString());
+            injectConstantCallTarget(inlineGraph, decision.getTarget(), phaseContext);
+            expandTree(inlineGraph, assumptions);
+            expandDirectCalls(inlineGraph, assumptions, decision, cache);
+            return inlineGraph;
+        } catch (Throwable e) {
+            throw Debug.handle(e);
+        }
+    }
+
     private static List<LoopEx> innerLoopsFirst(Collection<LoopEx> loops) {
         ArrayList<LoopEx> sortedLoops = new ArrayList<>(loops);
         Collections.sort(sortedLoops, new Comparator<LoopEx>() {
@@ -474,7 +481,7 @@
             this.cache = new HashMap<>();
         }
 
-        public StructuredGraph getCachedGraph(PhaseContext phaseContext, Assumptions assumptions, InliningDecision decision) {
+        public StructuredGraph getCachedGraph(PhaseContext phaseContext, Assumptions assumptions, TruffleInliningDecision decision) {
             CacheKey cacheKey = new CacheKey(decision);
             StructuredGraph inlineGraph = cache.get(cacheKey);
             if (inlineGraph == null) {
@@ -486,9 +493,9 @@
 
         private final class CacheKey {
 
-            public final InliningDecision decision;
+            public final TruffleInliningDecision decision;
 
-            public CacheKey(InliningDecision decision) {
+            public CacheKey(TruffleInliningDecision decision) {
                 this.decision = decision;
                 /*
                  * If decision.isInline() is not true CacheKey#hashCode does not match
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Thu Oct 09 17:25:24 2014 +0200
@@ -108,22 +108,16 @@
     public void compileMethodImpl(final OptimizedCallTarget compilable) {
         final StructuredGraph graph;
 
-        if (TraceTruffleCompilation.getValue() || TraceTruffleCompilationAST.getValue()) {
+        if (TraceTruffleCompilation.getValue()) {
             OptimizedCallTargetLog.logOptimizingStart(compilable);
-            if (TraceTruffleCompilationAST.getValue()) {
-                OptimizedCallUtils.printCompactTree(OptimizedCallTarget.OUT, compilable.getRootNode());
-            }
-        }
-        if (TraceTruffleCompilationCallTree.getValue()) {
-            OptimizedCallTargetLog.log(0, "opt call tree", compilable.toString(), compilable.getDebugProperties());
-            OptimizedCallTargetLog.logTruffleCalls(compilable);
+
         }
 
         long timeCompilationStarted = System.nanoTime();
         Assumptions assumptions = new Assumptions(true);
-        ContextSensitiveInlining inlining = TruffleCompilerOptions.TruffleContextSensitiveInlining.getValue() ? new ContextSensitiveInlining(compilable, new DefaultInliningPolicy()) : null;
+
         try (TimerCloseable a = PartialEvaluationTime.start(); Closeable c = PartialEvaluationMemUse.start()) {
-            graph = partialEvaluator.createGraph(compilable, assumptions, inlining);
+            graph = partialEvaluator.createGraph(compilable, assumptions);
         }
 
         if (Thread.currentThread().isInterrupted()) {
@@ -136,21 +130,37 @@
         long timeCompilationFinished = System.nanoTime();
         int nodeCountLowered = graph.getNodeCount();
 
-        compilable.setInliningDecision(inlining);
-
-        if (TraceTruffleInlining.getValue() && inlining != null) {
-            OptimizedCallTargetLog.logInliningDecision(compilable);
-        }
         if (TraceTruffleCompilation.getValue()) {
             printTruffleCompilation(compilable, timeCompilationStarted, timePartialEvaluationFinished, nodeCountPartialEval, compilationResult, timeCompilationFinished, nodeCountLowered);
         }
+        if (TraceTruffleCompilationAST.getValue()) {
+            OptimizedCallUtils.printCompactTree(OptimizedCallTarget.OUT, compilable);
+        }
+        if (TraceTruffleCompilationCallTree.getValue()) {
+            OptimizedCallTargetLog.log(0, "opt call tree", compilable.toString(), compilable.getDebugProperties());
+            OptimizedCallTargetLog.logTruffleCallTree(compilable);
+        }
+        if (TraceTruffleInlining.getValue()) {
+            OptimizedCallTargetLog.logInliningDecision(compilable);
+        }
 
     }
 
     private static void printTruffleCompilation(final OptimizedCallTarget compilable, long timeCompilationStarted, long timePartialEvaluationFinished, int nodeCountPartialEval,
                     CompilationResult compilationResult, long timeCompilationFinished, int nodeCountLowered) {
-        int calls = OptimizedCallUtils.countCalls(compilable);
-        int inlinedCalls = OptimizedCallUtils.countCallsInlined(compilable);
+
+        TruffleInlining inlining = compilable.getInlining();
+
+        int calls;
+        int inlinedCalls;
+        if (inlining == null) {
+            calls = (int) compilable.nodeStream(false).filter(node -> (node instanceof OptimizedDirectCallNode)).count();
+            inlinedCalls = 0;
+        } else {
+            calls = inlining.countCalls();
+            inlinedCalls = inlining.countInlinedCalls();
+        }
+
         int dispatchedCalls = calls - inlinedCalls;
         Map<String, Object> properties = new LinkedHashMap<>();
         OptimizedCallTargetLog.addASTSizeProperty(compilable, properties);
@@ -158,7 +168,7 @@
                         (timeCompilationFinished - timeCompilationStarted) / 1e6, //
                         (timePartialEvaluationFinished - timeCompilationStarted) / 1e6, //
                         (timeCompilationFinished - timePartialEvaluationFinished) / 1e6));
-        properties.put("CallNodes", String.format("I %5d/D %5d", inlinedCalls, dispatchedCalls));
+        properties.put("DirectCallNodes", String.format("I %4d/D %4d", inlinedCalls, dispatchedCalls));
         properties.put("GraalNodes", String.format("%5d/%5d", nodeCountPartialEval, nodeCountLowered));
         properties.put("CodeSize", compilationResult.getTargetCodeSize());
         properties.put("Source", formatSourceSection(compilable.getRootNode().getSourceSection()));
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Thu Oct 09 17:25:24 2014 +0200
@@ -53,20 +53,12 @@
     public static final OptionValue<Integer> TruffleReplaceReprofileCount = new OptionValue<>(10);
     @Option(help = "Enable automatic inlining of call targets")
     public static final OptionValue<Boolean> TruffleFunctionInlining = new OptionValue<>(true);
-    @Option(help = "Experimental: Enable context senstive inlining decisions.")
-    public static final StableOptionValue<Boolean> TruffleContextSensitiveInlining = new StableOptionValue<>(false);
-    @Option(help = "Experimental: Enable an expansion cache per CallTarget. Only functionable with TruffleContextSensitiveInlining enabled.")
-    public static final OptionValue<Boolean> TruffleCallTargetExpansionCache = new OptionValue<>(true);
+    @Option(help = "Enable an expansion cache per CallTarget. Only functionable with TruffleContextSensitiveInlining enabled.")
+    public static final OptionValue<Boolean> TruffleFunctionInliningCache = new OptionValue<>(true);
     @Option(help = "Maximum number of Graal IR nodes during partial evaluation")
     public static final OptionValue<Integer> TruffleGraphMaxNodes = new OptionValue<>(200000);
     @Option(help = "Stop inlining if caller's cumulative tree size would exceed this limit")
     public static final OptionValue<Integer> TruffleInliningMaxCallerSize = new OptionValue<>(2250);
-    @Option(help = "Skip inlining candidate if its tree size exceeds this limit")
-    public static final OptionValue<Integer> TruffleInliningMaxCalleeSize = new OptionValue<>(500);
-    @Option(help = "Call frequency relative to call target")
-    public static final OptionValue<Double> TruffleInliningMinFrequency = new OptionValue<>(0.3);
-    @Option(help = "Allow inlining of less hot candidates if tree size is small")
-    public static final OptionValue<Integer> TruffleInliningTrivialSize = new OptionValue<>(10);
 
     @Option(help = "Defines the number of graal nodes that triggers a performance warning.")
     public static final OptionValue<Integer> TrufflePerformanceWarningGraalNodeCount = new OptionValue<>(1000);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInlining.java	Thu Oct 09 17:25:24 2014 +0200
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2014, 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 java.util.stream.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.nodes.Node;
+
+public class TruffleInlining implements Iterable<TruffleInliningDecision> {
+
+    private final List<TruffleInliningDecision> callSites;
+
+    TruffleInlining(List<TruffleInliningDecision> callSites) {
+        this.callSites = callSites;
+    }
+
+    public TruffleInlining(OptimizedCallTarget sourceTarget, TruffleInliningPolicy policy) {
+        this(createDecisions(sourceTarget, policy));
+    }
+
+    private static List<TruffleInliningDecision> createDecisions(OptimizedCallTarget sourceTarget, TruffleInliningPolicy policy) {
+        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(sourceTarget, false);
+        List<TruffleInliningDecision> exploredCallSites = exploreCallSites(new ArrayList<>(Arrays.asList(sourceTarget)), nodeCount, policy);
+        return decideInlining(exploredCallSites, policy, nodeCount);
+    }
+
+    private static List<TruffleInliningDecision> exploreCallSites(List<OptimizedCallTarget> stack, int callStackNodeCount, TruffleInliningPolicy policy) {
+        List<TruffleInliningDecision> exploredCallSites = new ArrayList<>();
+        OptimizedCallTarget parentTarget = stack.get(stack.size() - 1);
+        for (OptimizedDirectCallNode callNode : parentTarget.getCallNodes()) {
+            OptimizedCallTarget currentTarget = callNode.getCurrentCallTarget();
+            stack.add(currentTarget); // push
+            exploredCallSites.add(exploreCallSite(stack, callStackNodeCount, policy, callNode));
+            stack.remove(stack.size() - 1); // pop
+        }
+        return exploredCallSites;
+    }
+
+    private static TruffleInliningDecision exploreCallSite(List<OptimizedCallTarget> callStack, int callStackNodeCount, TruffleInliningPolicy policy, OptimizedDirectCallNode callNode) {
+        OptimizedCallTarget parentTarget = callStack.get(callStack.size() - 2);
+        OptimizedCallTarget currentTarget = callStack.get(callStack.size() - 1);
+
+        List<TruffleInliningDecision> childCallSites = Collections.emptyList();
+        double frequency = calculateFrequency(parentTarget, callNode);
+        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(callNode.getCurrentCallTarget(), false);
+
+        boolean recursive = isRecursiveStack(callStack);
+        int deepNodeCount = nodeCount;
+        if (!recursive && callStack.size() < 15) {
+            /*
+             * We make a preliminary optimistic inlining decision with best possible characteristics
+             * to avoid the exploration of unnecessary pathes in the inlining tree.
+             */
+            if (policy.isAllowed(new TruffleInliningProfile(callNode, nodeCount, nodeCount, frequency, recursive), callStackNodeCount)) {
+                List<TruffleInliningDecision> exploredCallSites = exploreCallSites(callStack, callStackNodeCount + nodeCount, policy);
+                childCallSites = decideInlining(exploredCallSites, policy, nodeCount);
+                for (TruffleInliningDecision childCallSite : childCallSites) {
+                    if (childCallSite.isInline()) {
+                        deepNodeCount += childCallSite.getProfile().getDeepNodeCount();
+                    } else {
+                        /* we don't need those anymore. */
+                        childCallSite.getCallSites().clear();
+                    }
+                }
+            }
+        }
+
+        TruffleInliningProfile profile = new TruffleInliningProfile(callNode, nodeCount, deepNodeCount, frequency, recursive);
+        profile.setScore(policy.calculateScore(profile));
+        return new TruffleInliningDecision(currentTarget, profile, childCallSites);
+    }
+
+    private static double calculateFrequency(OptimizedCallTarget target, OptimizedDirectCallNode ocn) {
+        return (double) Math.max(1, ocn.getCallCount()) / (double) Math.max(1, target.getCompilationProfile().getCallCount());
+    }
+
+    private static boolean isRecursiveStack(List<OptimizedCallTarget> stack) {
+        OptimizedCallTarget top = stack.get(stack.size() - 1);
+        for (int i = 0; i < stack.size() - 1; i++) {
+            if (stack.get(i) == top) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static List<TruffleInliningDecision> decideInlining(List<TruffleInliningDecision> callSites, TruffleInliningPolicy policy, int nodeCount) {
+        int deepNodeCount = nodeCount;
+        int index = 0;
+        for (TruffleInliningDecision callSite : callSites.stream().sorted().collect(Collectors.toList())) {
+            TruffleInliningProfile profile = callSite.getProfile();
+            profile.setQueryIndex(index++);
+            if (policy.isAllowed(profile, deepNodeCount)) {
+                callSite.setInline(true);
+                deepNodeCount += profile.getDeepNodeCount();
+            }
+        }
+        return callSites;
+    }
+
+    public int getInlinedNodeCount() {
+        return getCallSites().stream().filter(callSite -> callSite.isInline()).mapToInt(callSite -> callSite.getProfile().getDeepNodeCount()).sum();
+    }
+
+    public int countCalls() {
+        return getCallSites().stream().mapToInt(callSite -> callSite.isInline() ? callSite.countCalls() + 1 : 1).sum();
+    }
+
+    public int countInlinedCalls() {
+        return getCallSites().stream().filter(TruffleInliningDecision::isInline).mapToInt(callSite -> callSite.countInlinedCalls() + 1).sum();
+    }
+
+    public final List<TruffleInliningDecision> getCallSites() {
+        return callSites;
+    }
+
+    public Iterator<TruffleInliningDecision> iterator() {
+        return callSites.iterator();
+    }
+
+    public TruffleInliningDecision findByCall(OptimizedDirectCallNode callNode) {
+        return getCallSites().stream().filter(c -> c.getProfile().getCallNode() == callNode).findFirst().orElse(null);
+    }
+
+    /**
+     * Visits all nodes of the {@link CallTarget} and all of its inlined calls.
+     */
+    public void accept(OptimizedCallTarget target, NodeVisitor visitor) {
+        target.getRootNode().accept(new CallTreeNodeVisitorImpl(target, visitor));
+    }
+
+    /**
+     * Creates an iterator for all nodes of the {@link CallTarget} and all of its inlined calls.
+     */
+    public Iterator<Node> makeNodeIterator(OptimizedCallTarget target) {
+        return new CallTreeNodeIterator(target);
+    }
+
+    /**
+     * This visitor extends the {@link NodeVisitor} interface to be usable for traversing the full
+     * call tree.
+     */
+    public interface CallTreeNodeVisitor extends NodeVisitor {
+
+        public boolean visit(List<TruffleInlining> decisionStack, Node node);
+
+        public default boolean visit(Node node) {
+            return visit(null, node);
+        }
+
+        static int getNodeDepth(List<TruffleInlining> decisionStack, Node node) {
+            int depth = calculateNodeDepth(node);
+            for (int i = decisionStack.size() - 1; i > 0; i--) {
+                TruffleInliningDecision decision = (TruffleInliningDecision) decisionStack.get(i);
+                depth += calculateNodeDepth(decision.getProfile().getCallNode());
+            }
+            return depth;
+        }
+
+        static int calculateNodeDepth(Node node) {
+            int depth = 0;
+            Node traverseNode = node;
+            while (traverseNode != null) {
+                depth++;
+                traverseNode = traverseNode.getParent();
+            }
+            return depth;
+        }
+
+    }
+
+    /**
+     * This visitor wraps an existing {@link NodeVisitor} or {@link CallTreeNodeVisitor} and
+     * traverses the full Truffle tree including inlined call sites.
+     */
+    private final static class CallTreeNodeVisitorImpl implements NodeVisitor {
+
+        protected final List<TruffleInlining> stack = new ArrayList<>();
+        private final NodeVisitor visitor;
+        private boolean continueTraverse = true;
+
+        public CallTreeNodeVisitorImpl(OptimizedCallTarget target, NodeVisitor visitor) {
+            stack.add(target.getInlining());
+            this.visitor = visitor;
+        }
+
+        public final boolean visit(Node node) {
+            if (node instanceof OptimizedDirectCallNode) {
+                OptimizedDirectCallNode callNode = (OptimizedDirectCallNode) node;
+                TruffleInlining inlining = stack.get(stack.size() - 1);
+                if (inlining != null) {
+                    TruffleInliningDecision childInlining = inlining.findByCall(callNode);
+                    if (childInlining != null) {
+                        stack.add(childInlining);
+                        continueTraverse = visitNode(node);
+                        if (continueTraverse) {
+                            childInlining.getTarget().getRootNode().accept(this);
+                        }
+                        stack.remove(stack.size() - 1);
+                    }
+                }
+                return continueTraverse;
+            } else {
+                continueTraverse = visitNode(node);
+                return continueTraverse;
+            }
+        }
+
+        private boolean visitNode(Node node) {
+            if (visitor instanceof CallTreeNodeVisitor) {
+                return ((CallTreeNodeVisitor) visitor).visit(stack, node);
+            } else {
+                return visitor.visit(node);
+            }
+        }
+    }
+
+    private static final class CallTreeNodeIterator implements Iterator<Node> {
+
+        private List<TruffleInlining> inliningDecisionStack = new ArrayList<>();
+        private List<Iterator<Node>> iteratorStack = new ArrayList<>();
+
+        public CallTreeNodeIterator(OptimizedCallTarget target) {
+            inliningDecisionStack.add(target.getInlining());
+            iteratorStack.add(NodeUtil.makeRecursiveIterator(target.getRootNode()));
+        }
+
+        public boolean hasNext() {
+            return peekIterator() != null;
+        }
+
+        public Node next() {
+            Iterator<Node> iterator = peekIterator();
+            if (iterator == null) {
+                throw new NoSuchElementException();
+            }
+
+            Node node = iterator.next();
+            if (node instanceof OptimizedDirectCallNode) {
+                visitInlinedCall(node);
+            }
+            return node;
+        }
+
+        private void visitInlinedCall(Node node) {
+            TruffleInlining currentDecision = inliningDecisionStack.get(inliningDecisionStack.size() - 1);
+            if (currentDecision == null) {
+                return;
+            }
+            TruffleInliningDecision decision = currentDecision.findByCall((OptimizedDirectCallNode) node);
+            if (decision.isInline()) {
+                inliningDecisionStack.add(decision);
+                iteratorStack.add(NodeUtil.makeRecursiveIterator(decision.getTarget().getRootNode()));
+            }
+        }
+
+        private Iterator<Node> peekIterator() {
+            int tos = iteratorStack.size() - 1;
+            while (tos >= 0) {
+                Iterator<Node> iterable = iteratorStack.get(tos);
+                if (iterable.hasNext()) {
+                    return iterable;
+                } else {
+                    iteratorStack.remove(--tos);
+                    inliningDecisionStack.remove(--tos);
+                }
+            }
+            return null;
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningDecision.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningDecision.java	Thu Oct 09 17:25:24 2014 +0200
@@ -24,49 +24,61 @@
 
 import java.util.*;
 
-public final class TruffleInliningDecision implements Iterable<TruffleInliningProfile> {
+public final class TruffleInliningDecision extends TruffleInlining implements Comparable<TruffleInliningDecision> {
 
-    private final OptimizedCallTarget callTarget;
-    private final Map<OptimizedDirectCallNode, TruffleInliningProfile> profiles;
-    private final Set<TruffleInliningProfile> inlined;
-    private final int nodeCount;
+    private final OptimizedCallTarget target;
+    private final TruffleInliningProfile profile;
+    private boolean inline;
 
-    public TruffleInliningDecision(OptimizedCallTarget callTarget, List<TruffleInliningProfile> profiles, Set<TruffleInliningProfile> inlined, int nodeCount) {
-        this.callTarget = callTarget;
-        this.profiles = new HashMap<>();
-        for (TruffleInliningProfile profile : profiles) {
-            this.profiles.put(profile.getCallNode(), profile);
-        }
-        this.nodeCount = nodeCount;
-        this.inlined = inlined;
+    public TruffleInliningDecision(OptimizedCallTarget target, TruffleInliningProfile profile, List<TruffleInliningDecision> children) {
+        super(children);
+        this.target = target;
+        this.profile = profile;
+    }
+
+    public OptimizedCallTarget getTarget() {
+        return target;
+    }
+
+    void setInline(boolean inline) {
+        this.inline = inline;
+    }
+
+    public boolean isInline() {
+        return inline;
     }
 
-    public Map<OptimizedDirectCallNode, TruffleInliningProfile> getProfiles() {
-        return profiles;
+    public TruffleInliningProfile getProfile() {
+        return profile;
     }
 
-    public int getNodeCount() {
-        return nodeCount;
+    public int compareTo(TruffleInliningDecision o) {
+        return Double.compare(o.getProfile().getScore(), getProfile().getScore());
     }
 
-    public OptimizedCallTarget getCallTarget() {
-        return callTarget;
-    }
-
-    public boolean isInlined(OptimizedDirectCallNode path) {
-        return inlined.contains(profiles.get(path));
-    }
-
-    public int size() {
-        return inlined.size();
-    }
-
-    public Iterator<TruffleInliningProfile> iterator() {
-        return Collections.unmodifiableSet(inlined).iterator();
+    public boolean isSameAs(TruffleInliningDecision other) {
+        if (getTarget() != other.getTarget()) {
+            return false;
+        } else if (isInline() != other.isInline()) {
+            return false;
+        } else if (!isInline()) {
+            assert !other.isInline();
+            return true;
+        } else {
+            Iterator<TruffleInliningDecision> i1 = iterator();
+            Iterator<TruffleInliningDecision> i2 = other.iterator();
+            while (i1.hasNext() && i2.hasNext()) {
+                if (!i1.next().isSameAs(i2.next())) {
+                    return false;
+                }
+            }
+            return !i1.hasNext() && !i2.hasNext();
+        }
     }
 
     @Override
     public String toString() {
-        return inlined.toString();
+        return String.format("TruffleInliningDecision(callNode=%s, inline=%b)", profile.getCallNode(), inline);
     }
+
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningHandler.java	Thu Oct 09 17:25:18 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +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.nodes.*;
-
-public final class TruffleInliningHandler {
-
-    private static final int MAXIMUM_RECURSIVE_DEPTH = 15;
-    private static final ProfileScoreComparator INLINING_SCORE = new ProfileScoreComparator();
-    private final TruffleInliningPolicy policy;
-    private final Map<OptimizedCallTarget, TruffleInliningDecision> resultCache;
-
-    public TruffleInliningHandler(TruffleInliningPolicy policy) {
-        this.policy = policy;
-        this.resultCache = new HashMap<>();
-    }
-
-    public TruffleInliningDecision decideInlining(OptimizedCallTarget target, int depth) {
-        if (resultCache.containsKey(target)) {
-            return resultCache.get(target);
-        }
-        resultCache.put(target, null); // important for recursion detection
-        TruffleInliningDecision result = createInliningDecision(target, depth);
-        resultCache.put(target, result);
-        return result;
-    }
-
-    private TruffleInliningDecision createInliningDecision(OptimizedCallTarget target, int depth) {
-        List<TruffleInliningProfile> profiles = createProfiles(target, depth);
-        Set<TruffleInliningProfile> inlined = new HashSet<>();
-        Collections.sort(profiles, INLINING_SCORE);
-        int deepNodeCount = OptimizedCallUtils.countNonTrivialNodes(target, true);
-        int index = 0;
-
-        for (TruffleInliningProfile profile : profiles) {
-            profile.setQueryIndex(index++);
-            if (policy.isAllowed(profile, deepNodeCount)) {
-                inlined.add(profile);
-                deepNodeCount += profile.getDeepNodeCount();
-            }
-        }
-
-        return new TruffleInliningDecision(target, profiles, inlined, deepNodeCount);
-    }
-
-    private List<TruffleInliningProfile> createProfiles(final OptimizedCallTarget target, final int depth) {
-        final List<TruffleInliningProfile> profiles = new ArrayList<>();
-        target.getRootNode().accept(new NodeVisitor() {
-            public boolean visit(Node node) {
-                if (node instanceof OptimizedDirectCallNode) {
-                    profiles.add(createProfile(target, (OptimizedDirectCallNode) node, depth));
-                }
-                return true;
-            }
-        });
-        return profiles;
-    }
-
-    public TruffleInliningProfile createProfile(OptimizedCallTarget parentTarget, OptimizedDirectCallNode ocn, int depth) {
-        OptimizedCallTarget target = ocn.getCurrentCallTarget();
-
-        int nodeCount = OptimizedCallUtils.countNonTrivialNodes(target, false);
-        double frequency = calculateFrequency(parentTarget, ocn);
-
-        int deepNodeCount;
-        TruffleInliningDecision 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, nodeCount, deepNodeCount, frequency, recursiveCall, recursiveResult);
-        profile.setScore(policy.calculateScore(profile));
-        return profile;
-    }
-
-    public TruffleInliningPolicy getPolicy() {
-        return policy;
-    }
-
-    public static double calculateFrequency(OptimizedCallTarget target, OptimizedDirectCallNode ocn) {
-        return (double) Math.max(1, ocn.getCallCount()) / (double) Math.max(1, target.getCompilationProfile().getCallCount());
-    }
-
-    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/TruffleInliningProfile.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java	Thu Oct 09 17:25:24 2014 +0200
@@ -31,20 +31,18 @@
     private final int deepNodeCount;
     private final double frequency;
     private final boolean recursiveCall;
-    private final TruffleInliningDecision recursiveResult;
 
     private int graalDeepNodeCount = -1;
     private String failedReason;
     private int queryIndex = -1;
     private double score;
 
-    public TruffleInliningProfile(OptimizedDirectCallNode callNode, int nodeCount, int deepNodeCount, double frequency, boolean recursiveCall, TruffleInliningDecision recursiveResult) {
+    public TruffleInliningProfile(OptimizedDirectCallNode callNode, int nodeCount, int deepNodeCount, double frequency, boolean recursiveCall) {
         this.callNode = callNode;
         this.nodeCount = nodeCount;
         this.deepNodeCount = deepNodeCount;
         this.frequency = frequency;
         this.recursiveCall = recursiveCall;
-        this.recursiveResult = recursiveResult;
     }
 
     public boolean isRecursiveCall() {
@@ -63,10 +61,6 @@
         return nodeCount;
     }
 
-    public TruffleInliningDecision getRecursiveResult() {
-        return recursiveResult;
-    }
-
     public void setScore(double score) {
         this.score = score;
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultDirectCallNode.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultDirectCallNode.java	Thu Oct 09 17:25:24 2014 +0200
@@ -89,11 +89,6 @@
     }
 
     @Override
-    public boolean isInlined() {
-        return false;
-    }
-
-    @Override
     public boolean isSplittable() {
         return false;
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/DirectCallNode.java	Thu Oct 09 17:25:18 2014 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/DirectCallNode.java	Thu Oct 09 17:25:24 2014 +0200
@@ -101,8 +101,14 @@
      * {@link DirectCallNode} is really going to be inlined. This depends on whether or not the
      * runtime system supports inlining. The runtime system may also decide to not inline calls
      * which were forced to inline.
+     *
+     * @deprecated we do not expose this information any longer. returns always false.
      */
-    public abstract boolean isInlined();
+    @SuppressWarnings("static-method")
+    @Deprecated
+    public final boolean isInlined() {
+        return false;
+    }
 
     /**
      * Returns <code>true</code> if this {@link DirectCallNode} can be split. A