changeset 13984:1c9dbfc5b510

Truffle: New more reliable inlining strategy for the Truffle runtime.
author Christian Humer <christian.humer@gmail.com>
date Thu, 20 Feb 2014 01:43:11 +0100
parents f46cab39a9a2
children 5243fe9a3fbc
files graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultCompilationPolicy.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNodeProfile.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java graal/com.oracle.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/TruffleInliningImpl.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java
diffstat 12 files changed, 750 insertions(+), 412 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Thu Feb 20 01:21:49 2014 +0100
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Thu Feb 20 01:43:11 2014 +0100
@@ -88,11 +88,10 @@
         final OptimizedCallTarget compilable = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(root);
 
         // Executed AST so that all classes are loaded and initialized.
-        do {
-            compilable.call(null, arguments);
-            compilable.call(null, arguments);
-            compilable.call(null, arguments);
-        } while (compilable.inline());
+        compilable.call(null, arguments);
+        compilable.call(null, arguments);
+        compilable.call(null, arguments);
+        compilable.performInlining();
 
         try (Scope s = Debug.scope("TruffleCompilation", new TruffleDebugJavaMethod(compilable))) {
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.java	Thu Feb 20 01:21:49 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.java	Thu Feb 20 01:43:11 2014 +0100
@@ -26,9 +26,6 @@
 
 public class CompilationProfile {
 
-    private int invokeCounter;
-    private int originalInvokeCounter;
-    private int loopAndInvokeCounter;
     /**
      * Number of times an installed code for this tree was invalidated.
      */
@@ -41,17 +38,30 @@
 
     private long previousTimestamp;
 
-    private final int compilationThreshold;
     private final String name;
 
+    private int callCount;
+    private int callAndLoopCount;
+    private int compilationCallThreshold;
+    private int compilationCallAndLoopThreshold;
+
+    private final int originalInvokeCounter;
+    private final int originalCompilationThreshold;
+
     public CompilationProfile(final int compilationThreshold, final int initialInvokeCounter, final String name) {
-        this.invokeCounter = initialInvokeCounter;
-        this.loopAndInvokeCounter = compilationThreshold;
-        this.originalInvokeCounter = compilationThreshold;
         this.previousTimestamp = System.nanoTime();
+        this.compilationCallThreshold = initialInvokeCounter;
+        this.compilationCallAndLoopThreshold = compilationThreshold;
+        this.originalInvokeCounter = initialInvokeCounter;
+        this.originalCompilationThreshold = compilationThreshold;
+        this.name = name;
+    }
 
-        this.compilationThreshold = compilationThreshold;
-        this.name = name;
+    public void reset() {
+        callCount = 0;
+        callAndLoopCount = 0;
+        compilationCallAndLoopThreshold = originalCompilationThreshold;
+        compilationCallThreshold = originalInvokeCounter;
     }
 
     public long getPreviousTimestamp() {
@@ -70,54 +80,64 @@
         return nodeReplaceCount;
     }
 
-    public int getInvokeCounter() {
-        return invokeCounter;
+    public int getCallAndLoopCount() {
+        return callAndLoopCount;
+    }
+
+    public int getCallCount() {
+        return callCount;
+    }
+
+    public int getCompilationCallAndLoopThreshold() {
+        return compilationCallAndLoopThreshold;
     }
 
-    public int getOriginalInvokeCounter() {
-        return originalInvokeCounter;
+    public int getCompilationCallThreshold() {
+        return compilationCallThreshold;
     }
 
-    public int getLoopAndInvokeCounter() {
-        return loopAndInvokeCounter;
+    void ensureProfiling(int calls, int callsAndLoop) {
+        int increaseCallAndLoopThreshold = callsAndLoop - (this.compilationCallAndLoopThreshold - this.callAndLoopCount);
+        if (increaseCallAndLoopThreshold > 0) {
+            this.compilationCallAndLoopThreshold += increaseCallAndLoopThreshold;
+        }
+
+        int increaseCallsThreshold = calls - (this.compilationCallThreshold - this.callCount);
+        if (increaseCallsThreshold > 0) {
+            this.compilationCallThreshold += increaseCallsThreshold;
+        }
     }
 
     void reportTiminingFailed(long timestamp) {
-        this.loopAndInvokeCounter = compilationThreshold;
-        this.originalInvokeCounter = compilationThreshold;
+        ensureProfiling(0, originalCompilationThreshold);
         this.previousTimestamp = timestamp;
     }
 
     void reportInvalidated() {
         invalidationCount++;
-        int invalidationReprofileCount = TruffleInvalidationReprofileCount.getValue();
-        invokeCounter = invalidationReprofileCount;
-        originalInvokeCounter += invalidationReprofileCount;
+        int reprofile = TruffleInvalidationReprofileCount.getValue();
+        ensureProfiling(reprofile, reprofile);
     }
 
     void reportInterpreterCall() {
-        invokeCounter--;
-        loopAndInvokeCounter--;
+        callCount++;
+        callAndLoopCount++;
     }
 
-    void reportInliningPerformed(TruffleInlining inlining) {
-        invokeCounter = inlining.getInvocationReprofileCount();
-        int inliningReprofileCount = inlining.getReprofileCount();
-        loopAndInvokeCounter = inliningReprofileCount;
-        originalInvokeCounter = inliningReprofileCount;
+    void reportInterpreterCalls(int calls) {
+        this.callCount += calls;
+        this.callAndLoopCount += calls;
     }
 
     void reportLoopCount(int count) {
-        loopAndInvokeCounter = Math.max(0, loopAndInvokeCounter - count);
+        callAndLoopCount += count;
     }
 
     void reportNodeReplaced() {
         nodeReplaceCount++;
         // delay compilation until tree is deemed stable enough
         int replaceBackoff = TruffleReplaceReprofileCount.getValue();
-        if (loopAndInvokeCounter < replaceBackoff) {
-            loopAndInvokeCounter = replaceBackoff;
-        }
+        ensureProfiling(1, replaceBackoff);
     }
 
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultCompilationPolicy.java	Thu Feb 20 01:21:49 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultCompilationPolicy.java	Thu Feb 20 01:43:11 2014 +0100
@@ -25,7 +25,7 @@
 public class DefaultCompilationPolicy implements CompilationPolicy {
 
     public boolean shouldCompile(CompilationProfile profile) {
-        return profile.getInvokeCounter() <= 0 && profile.getLoopAndInvokeCounter() <= 0;
+        return profile.getCallCount() >= profile.getCompilationCallThreshold() && profile.getCallAndLoopCount() >= profile.getCompilationCallAndLoopThreshold();
     }
 
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Thu Feb 20 01:21:49 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Thu Feb 20 01:43:11 2014 +0100
@@ -48,6 +48,7 @@
 import com.oracle.graal.runtime.*;
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.impl.*;
 import com.oracle.truffle.api.nodes.*;
 
 /**
@@ -79,6 +80,14 @@
         return new OptimizedCallTarget(rootNode, truffleCompiler, TruffleMinInvokeThreshold.getValue(), TruffleCompilationThreshold.getValue(), acceptForCompilation(rootNode));
     }
 
+    public CallNode createCallNode(CallTarget target) {
+        if (target instanceof OptimizedCallTarget) {
+            return OptimizedCallNode.create((OptimizedCallTarget) target);
+        } else {
+            return new DefaultCallNode(target);
+        }
+    }
+
     @Override
     public VirtualFrame createVirtualFrame(PackedFrame caller, Arguments arguments, FrameDescriptor frameDescriptor) {
         return OptimizedCallTarget.createFrame(frameDescriptor, caller, arguments);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java	Thu Feb 20 01:43:11 2014 +0100
@@ -0,0 +1,291 @@
+/*
+ * 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 com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.impl.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Call target that is optimized by Graal upon surpassing a specific invocation threshold.
+ */
+abstract class OptimizedCallNode extends DefaultCallNode {
+
+    protected int callCount;
+
+    private OptimizedCallNode(OptimizedCallTarget target) {
+        super(target);
+    }
+
+    @Override
+    public final boolean isInlinable() {
+        return true;
+    }
+
+    @Override
+    public final boolean isSplittable() {
+        return getCallTarget().getRootNode().isSplittable();
+    }
+
+    @Override
+    public final OptimizedCallTarget getCallTarget() {
+        return (OptimizedCallTarget) super.getCallTarget();
+    }
+
+    public final int getCallCount() {
+        return callCount;
+    }
+
+    public TruffleInliningProfile createInliningProfile() {
+        return new OptimizedCallNodeProfile(this);
+    }
+
+    @Override
+    public OptimizedCallTarget getSplitCallTarget() {
+        return null;
+    }
+
+    final OptimizedCallNode inlineImpl() {
+        if (getParent() == null) {
+            throw new IllegalStateException("CallNode must be adopted before it is split.");
+        }
+        return replace(new InlinedOptimizedCallNode(getCallTarget(), getSplitCallTarget(), getExecutedCallTarget().getRootNode(), callCount));
+    }
+
+    public final OptimizedCallTarget getExecutedCallTarget() {
+        return getSplitCallTarget() != null ? getSplitCallTarget() : getCallTarget();
+    }
+
+    public static OptimizedCallNode create(OptimizedCallTarget target) {
+        return new DefaultOptimizedCallNode(target);
+    }
+
+    private static final class DefaultOptimizedCallNode extends OptimizedCallNode {
+
+        private boolean splitTried;
+
+        DefaultOptimizedCallNode(OptimizedCallTarget target) {
+            super(target);
+        }
+
+        @Override
+        public Object call(PackedFrame caller, Arguments arguments) {
+            if (CompilerDirectives.inInterpreter()) {
+                callCount++;
+                if (!splitTried) {
+                    return trySplit(caller, arguments);
+                }
+            }
+            return callTarget.call(caller, arguments);
+        }
+
+        private Object trySplit(PackedFrame caller, Arguments arguments) {
+            int effectiveCallCount = callCount;
+            // we try splitting for the first two invocations
+            if (effectiveCallCount == 1 || effectiveCallCount == 2) {
+                if (isSplittable() && shouldSplit()) {
+                    return splitImpl().call(caller, arguments);
+                }
+            }
+            if (effectiveCallCount >= 2) {
+                splitTried = true;
+            }
+            return callTarget.call(caller, arguments);
+        }
+
+        @Override
+        public boolean isInlined() {
+            return false;
+        }
+
+        @Override
+        public boolean split() {
+            if (!isSplittable()) {
+                // split is only allowed once and if the root node supports it
+                return false;
+            }
+            if (getParent() == null) {
+                throw new IllegalStateException("CallNode must be adopted before it is split.");
+            }
+            splitImpl();
+            return true;
+        }
+
+        private OptimizedCallNode splitImpl() {
+            RootNode splittedRoot = getCallTarget().getRootNode().split();
+            OptimizedCallTarget splitCallTarget = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(splittedRoot);
+            OptimizedCallTarget.logSplit(getCallTarget(), splitCallTarget);
+            return replace(new SplitOptimizedCallNode(getCallTarget(), splitCallTarget, callCount));
+        }
+
+        private boolean shouldSplit() {
+            if (!TruffleCompilerOptions.TruffleSplittingEnabled.getValue()) {
+                return false;
+            }
+            RootNode targetRoot = getCallTarget().getRootNode();
+            int nodeCount = NodeUtil.countNodes(targetRoot, null, true);
+            if (nodeCount >= TruffleCompilerOptions.TruffleSplittingMaxCalleeSize.getValue()) {
+                return false;
+            }
+            SplitScoreVisitor visitor = new SplitScoreVisitor();
+            targetRoot.accept(visitor);
+            int genericNess = visitor.getSplitScore();
+            return genericNess > 0;
+        }
+
+        @Override
+        public void inline() {
+            inlineImpl();
+        }
+
+        @Override
+        public OptimizedCallTarget getSplitCallTarget() {
+            return null;
+        }
+
+    }
+
+    private static final class InlinedOptimizedCallNode extends OptimizedCallNode {
+
+        private final RootNode inlinedRoot;
+        private final OptimizedCallTarget splittedTarget;
+
+        public InlinedOptimizedCallNode(OptimizedCallTarget target, OptimizedCallTarget splittedTarget, RootNode inlinedRoot, int callCount) {
+            super(target);
+            this.inlinedRoot = inlinedRoot;
+            this.splittedTarget = splittedTarget;
+            this.callCount = callCount;
+            installParentInlinedCall();
+        }
+
+        @Override
+        public Object call(PackedFrame caller, Arguments arguments) {
+            if (CompilerDirectives.inInterpreter()) {
+                callCount++;
+            }
+            return inlinedRoot.execute(Truffle.getRuntime().createVirtualFrame(caller, arguments, inlinedRoot.getFrameDescriptor()));
+        }
+
+        @Override
+        public void inline() {
+        }
+
+        @Override
+        public boolean split() {
+            return false;
+        }
+
+        @Override
+        public boolean isInlined() {
+            return true;
+        }
+
+        @Override
+        public RootNode getInlinedRoot() {
+            return inlinedRoot;
+        }
+
+        @Override
+        public OptimizedCallTarget getSplitCallTarget() {
+            return splittedTarget;
+        }
+    }
+
+    private static class SplitOptimizedCallNode extends OptimizedCallNode {
+
+        private final OptimizedCallTarget splittedTarget;
+
+        public SplitOptimizedCallNode(OptimizedCallTarget target, OptimizedCallTarget splittedTarget, int callCount) {
+            super(target);
+            this.callCount = callCount;
+            this.splittedTarget = splittedTarget;
+        }
+
+        @Override
+        public Object call(PackedFrame caller, Arguments arguments) {
+            if (CompilerDirectives.inInterpreter()) {
+                callCount++;
+            }
+            return splittedTarget.call(caller, arguments);
+        }
+
+        @Override
+        public boolean isInlined() {
+            return false;
+        }
+
+        @Override
+        public final boolean split() {
+            return false;
+        }
+
+        @Override
+        public void inline() {
+            inlineImpl();
+        }
+
+        @Override
+        public final OptimizedCallTarget getSplitCallTarget() {
+            return splittedTarget;
+        }
+
+    }
+
+    private static final class SplitScoreVisitor implements NodeVisitor {
+
+        private int splitScore = 0;
+
+        public boolean visit(Node node) {
+            if (node instanceof OptimizedCallNode) {
+                OptimizedCallNode call = (OptimizedCallNode) node;
+                if (call.getInlinedRoot() != null) {
+                    call.getInlinedRoot().accept(this);
+                }
+            }
+            splitScore += splitScore(node);
+            return true;
+        }
+
+        public int getSplitScore() {
+            return splitScore;
+        }
+
+        private static int splitScore(Node node) {
+            NodeInfo info = node.getClass().getAnnotation(NodeInfo.class);
+            if (info == null) {
+                return 0;
+            }
+            switch (info.kind()) {
+                case GENERIC:
+                    return 3;
+                case POLYMORPHIC:
+                    return 1;
+                default:
+                    return 0;
+            }
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNodeProfile.java	Thu Feb 20 01:43:11 2014 +0100
@@ -0,0 +1,170 @@
+/*
+ * 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.*;
+
+import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
+
+public class OptimizedCallNodeProfile implements TruffleInliningProfile {
+
+    private static final String REASON_RECURSION = "recursion";
+    private static final String REASON_FREQUENCY_CUTOFF = "frequency < " + TruffleInliningMinFrequency.getValue();
+    private static final String REASON_MAXIMUM_NODE_COUNT = "shallowTargetCount  > " + TruffleInliningMaxCalleeSize.getValue();
+    private static final String REASON_MAXIMUM_TOTAL_NODE_COUNT = "deepTargetCount + currentNodeCount > " + TruffleInliningMaxCallerSize.getValue();
+
+    private final OptimizedCallNode callNode;
+
+    private final int targetDeepNodeCount;
+    private final int targetShallowNodeCount;
+    private final List<OptimizedCallTarget> compilationRoots;
+    private final double averageFrequency;
+    private final double score;
+    private String reason;
+
+    public OptimizedCallNodeProfile(OptimizedCallNode callNode) {
+        this.callNode = callNode;
+        RootNode inlineRoot = callNode.getExecutedCallTarget().getRootNode();
+        this.targetShallowNodeCount = NodeUtil.countNodes(inlineRoot, null, false);
+        this.targetDeepNodeCount = NodeUtil.countNodes(inlineRoot, null, true);
+        this.compilationRoots = findCompilationRoots(callNode);
+        this.averageFrequency = calculateAverageFrequency(compilationRoots);
+        this.score = calculateScore();
+    }
+
+    public OptimizedCallNode getCallNode() {
+        return callNode;
+    }
+
+    public double getScore() {
+        return score;
+    }
+
+    public double calculateScore() {
+        return averageFrequency / targetDeepNodeCount;
+    }
+
+    public boolean isInliningAllowed() {
+        OptimizedCallTarget inlineTarget = callNode.getExecutedCallTarget();
+        for (OptimizedCallTarget compilationRoot : compilationRoots) {
+            if (compilationRoot == inlineTarget) {
+                // recursive call found
+                reason = REASON_RECURSION;
+                return false;
+            }
+        }
+
+        // frequency cut-off
+        if (averageFrequency < TruffleInliningMinFrequency.getValue() && targetDeepNodeCount > TruffleInliningTrivialSize.getValue()) {
+            reason = REASON_FREQUENCY_CUTOFF;
+            return false;
+        }
+
+        if (targetShallowNodeCount > TruffleInliningMaxCalleeSize.getValue()) {
+            reason = REASON_MAXIMUM_NODE_COUNT;
+            return false;
+        }
+
+        // The maximum total node count cannot be cached since it may change during inlining.
+        int currentNodeCount = calculateCurrentNodeCount(compilationRoots);
+        if (targetDeepNodeCount + currentNodeCount > TruffleInliningMaxCallerSize.getValue()) {
+            reason = REASON_MAXIMUM_TOTAL_NODE_COUNT;
+            return false;
+        }
+
+        return true;
+    }
+
+    private static int calculateCurrentNodeCount(List<OptimizedCallTarget> compilationRoots) {
+        int currentNodeCount = 0;
+        for (OptimizedCallTarget compilationRoot : compilationRoots) {
+            if (compilationRoot.getRootNode().getParentInlinedCalls().isEmpty()) {
+                currentNodeCount = Math.max(currentNodeCount, NodeUtil.countNodes(compilationRoot.getRootNode(), null, true));
+            }
+        }
+        return currentNodeCount;
+    }
+
+    @SuppressWarnings("unused")
+    private double calculateSimpleFrequency() {
+        RootNode root = callNode.getRootNode();
+        OptimizedCallTarget target = ((OptimizedCallTarget) root.getCallTarget());
+
+        int totalCallCount = target.getCompilationProfile().getCallCount();
+        List<CallNode> parentInlined = root.getParentInlinedCalls();
+        for (CallNode node : parentInlined) {
+            int callCount = ((OptimizedCallNode) node).getCallCount();
+            if (callCount >= 0) {
+                totalCallCount += callCount;
+            }
+        }
+        return callNode.getCallCount() / (double) totalCallCount;
+    }
+
+    private double calculateAverageFrequency(List<OptimizedCallTarget> roots) {
+        int compilationRootCallCountSum = 0;
+        int compilationRootCount = 0;
+        for (OptimizedCallTarget compilationRoot : roots) {
+            if (compilationRoot.getRootNode().getParentInlinedCalls().isEmpty()) {
+                compilationRootCallCountSum += compilationRoot.getCompilationProfile().getCallCount();
+                compilationRootCount++;
+            }
+        }
+        return (callNode.getCallCount() * compilationRootCount) / (double) compilationRootCallCountSum;
+    }
+
+    private static List<OptimizedCallTarget> findCompilationRoots(Node call) {
+        RootNode root = call.getRootNode();
+        if (root == null) {
+            return Collections.emptyList();
+        }
+        List<OptimizedCallTarget> roots = new ArrayList<>();
+        roots.add((OptimizedCallTarget) root.getCallTarget());
+        for (CallNode callNode : root.getParentInlinedCalls()) {
+            roots.addAll(findCompilationRoots(callNode));
+        }
+        return roots;
+    }
+
+    public int compareTo(TruffleInliningProfile o) {
+        if (o instanceof OptimizedCallNodeProfile) {
+            return Double.compare(((OptimizedCallNodeProfile) o).getScore(), getScore());
+        }
+        return 0;
+    }
+
+    public Map<String, Object> getDebugProperties() {
+        Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("shallowCount", targetShallowNodeCount);
+        properties.put("deepCount", targetDeepNodeCount);
+        properties.put("currentCount", calculateCurrentNodeCount(compilationRoots));
+        properties.put("score", score);
+        properties.put("frequency", averageFrequency);
+        properties.put("callCount", callNode.getCallCount());
+        properties.put("reason", reason);
+        return properties;
+    }
+
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Thu Feb 20 01:21:49 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Thu Feb 20 01:43:11 2014 +0100
@@ -44,13 +44,13 @@
 
     private InstalledCode installedCode;
     private Future<InstalledCode> installedCodeTask;
+    private boolean compilationEnabled;
+    private int callCount;
+
     private final TruffleCompiler compiler;
     private final CompilationProfile compilationProfile;
     private final CompilationPolicy compilationPolicy;
-    private final TruffleInlining inlining;
-    private boolean compilationEnabled;
-    private int callCount;
-    private SpeculationLog speculationLog = new SpeculationLog();
+    private final SpeculationLog speculationLog = new SpeculationLog();
 
     OptimizedCallTarget(RootNode rootNode, TruffleCompiler compiler, int invokeCounter, int compilationThreshold, boolean compilationEnabled) {
         super(rootNode);
@@ -67,8 +67,19 @@
         if (TruffleCallTargetProfiling.getValue()) {
             registerCallTarget(this);
         }
-        this.inlining = new TruffleInliningImpl();
+    }
 
+    @Override
+    public String toString() {
+        String superString = super.toString();
+        if (installedCode != null) {
+            superString += " <compiled>";
+        }
+        return superString;
+    }
+
+    public boolean isOptimized() {
+        return installedCode != null || installedCodeTask != null;
     }
 
     @CompilerDirectives.SlowPath
@@ -107,26 +118,29 @@
     }
 
     private Object compiledCodeInvalidated(PackedFrame caller, Arguments args) {
-        invalidate();
+        invalidate("Compiled code invalidated");
         return call(caller, args);
     }
 
-    private void invalidate() {
+    private void invalidate(String reason) {
         InstalledCode m = this.installedCode;
         if (m != null) {
             CompilerAsserts.neverPartOfCompilation();
             installedCode = null;
             compilationProfile.reportInvalidated();
             if (TraceTruffleCompilation.getValue()) {
-                OUT.printf("[truffle] invalidated %-48s %08x |InvalidationCount %2d |ReplaceCount %3d\n", getRootNode(), getRootNode().hashCode(), compilationProfile.getInvalidationCount(),
-                                compilationProfile.getNodeReplaceCount());
+                logOptimizedInvalidated(this, reason);
             }
         }
+        cancelInstalledTask(reason);
+    }
 
+    private void cancelInstalledTask(String reason) {
         Future<InstalledCode> task = this.installedCodeTask;
         if (task != null) {
             task.cancel(true);
             this.installedCodeTask = null;
+            logOptimizingCancelled(this, reason);
             compilationProfile.reportInvalidated();
         }
     }
@@ -134,29 +148,63 @@
     private Object interpreterCall(PackedFrame caller, Arguments args) {
         CompilerAsserts.neverPartOfCompilation();
         compilationProfile.reportInterpreterCall();
-        if (compilationEnabled && shouldCompile()) {
-            if (isCompiling()) {
-                return waitForCompilation(caller, args);
-            }
-            boolean inlined = shouldInline() && inline();
-            if (!inlined) {
-                compile();
+
+        if (compilationEnabled && compilationPolicy.shouldCompile(compilationProfile)) {
+            InstalledCode code = compile();
+            if (code != null && code.isValid()) {
+                this.installedCode = code;
+                try {
+                    return code.execute(this, caller, args);
+                } catch (InvalidInstalledCodeException ex) {
+                    return compiledCodeInvalidated(caller, args);
+                }
             }
         }
         return executeHelper(caller, args);
     }
 
-    private boolean shouldCompile() {
-        return compilationPolicy.shouldCompile(compilationProfile);
+    public void performInlining() {
+        if (!TruffleCompilerOptions.TruffleFunctionInlining.getValue()) {
+            return;
+        }
+        PriorityQueue<TruffleInliningProfile> queue = new PriorityQueue<>();
+        queueCallSitesForInlining(getRootNode(), queue);
+
+        TruffleInliningProfile callSite = queue.poll();
+        while (callSite != null) {
+            if (callSite.isInliningAllowed()) {
+                OptimizedCallNode callNode = callSite.getCallNode();
+                logInlined(callSite);
+                RootNode inlinedRoot = callNode.inlineImpl().getInlinedRoot();
+                assert inlinedRoot != null;
+                queueCallSitesForInlining(inlinedRoot, queue);
+            } else {
+                logInliningFailed(callSite);
+            }
+            callSite = queue.poll();
+        }
     }
 
-    private static boolean shouldInline() {
-        return TruffleFunctionInlining.getValue();
+    private static void queueCallSitesForInlining(RootNode rootNode, final PriorityQueue<TruffleInliningProfile> queue) {
+        rootNode.accept(new NodeVisitor() {
+            public boolean visit(Node node) {
+                if (node instanceof OptimizedCallNode) {
+                    OptimizedCallNode call = ((OptimizedCallNode) node);
+                    if (call.isInlinable() && !call.isInlined()) {
+                        queue.add(call.createInliningProfile());
+                    } else if (call.getInlinedRoot() != null) {
+                        call.getInlinedRoot().accept(this);
+                    }
+                }
+                return true;
+            }
+        });
     }
 
     private boolean isCompiling() {
-        if (installedCodeTask != null) {
-            if (installedCodeTask.isCancelled()) {
+        Future<InstalledCode> codeTask = this.installedCodeTask;
+        if (codeTask != null) {
+            if (codeTask.isCancelled()) {
                 installedCodeTask = null;
                 return false;
             }
@@ -165,18 +213,21 @@
         return false;
     }
 
-    public void compile() {
-        this.installedCodeTask = compiler.compile(this);
-        if (!TruffleBackgroundCompilation.getValue()) {
-            installedCode = receiveInstalledCode();
+    public InstalledCode compile() {
+        if (isCompiling()) {
+            if (installedCodeTask.isDone()) {
+                return receiveInstalledCode();
+            }
+            return null;
+        } else {
+            logOptimizing(this);
+            performInlining();
+            this.installedCodeTask = compiler.compile(this);
+            if (!TruffleBackgroundCompilation.getValue()) {
+                return receiveInstalledCode();
+            }
         }
-    }
-
-    private Object waitForCompilation(PackedFrame caller, Arguments args) {
-        if (installedCodeTask.isDone()) {
-            installedCode = receiveInstalledCode();
-        }
-        return executeHelper(caller, args);
+        return null;
     }
 
     private InstalledCode receiveInstalledCode() {
@@ -199,19 +250,6 @@
         }
     }
 
-    /**
-     * Forces inlining whether or not function inlining is enabled.
-     * 
-     * @return true if an inlining was performed
-     */
-    public boolean inline() {
-        boolean result = inlining.performInlining(this);
-        if (result) {
-            compilationProfile.reportInliningPerformed(inlining);
-        }
-        return result;
-    }
-
     public Object executeHelper(PackedFrame caller, Arguments args) {
         VirtualFrame frame = createFrame(getRootNode().getFrameDescriptor(), caller, args);
         return getRootNode().execute(frame);
@@ -227,15 +265,96 @@
     }
 
     @Override
-    public void nodeReplaced() {
+    public void nodeReplaced(Node oldNode, Node newNode, String reason) {
         compilationProfile.reportNodeReplaced();
-        invalidate();
+        invalidate(reason);
     }
 
     public SpeculationLog getSpeculationLog() {
         return speculationLog;
     }
 
+    private static void logInliningFailed(TruffleInliningProfile callSite) {
+        if (TraceTruffleInliningDetails.getValue() || TraceTruffleCompilationDetails.getValue()) {
+            log(0, "inline failed", callSite.getCallNode().getExecutedCallTarget().toString(), callSite.getDebugProperties());
+        }
+    }
+
+    private static void logOptimizing(OptimizedCallTarget target) {
+        if (TraceTruffleInliningDetails.getValue() || TraceTruffleCompilationDetails.getValue()) {
+            log(0, "optimizing", target.toString(), null);
+        }
+    }
+
+    private static void logOptimizedInvalidated(OptimizedCallTarget target, String reason) {
+        if (TraceTruffleInliningDetails.getValue() || TraceTruffleCompilationDetails.getValue()) {
+            Map<String, Object> properties = new LinkedHashMap<>();
+            properties.put("Invalidation#", target.compilationProfile.getInvalidationCount());
+            properties.put("Replace#", target.compilationProfile.getNodeReplaceCount());
+            properties.put("Reason", reason);
+            log(0, "invalidated", target.toString(), properties);
+        }
+    }
+
+    private static void logOptimizingCancelled(OptimizedCallTarget target, String reason) {
+        if (TraceTruffleInliningDetails.getValue() || TraceTruffleCompilationDetails.getValue()) {
+            Map<String, Object> properties = new LinkedHashMap<>();
+            properties.put("Invalidation#", target.compilationProfile.getInvalidationCount());
+            properties.put("Replace#", target.compilationProfile.getNodeReplaceCount());
+            properties.put("Reason", reason);
+            log(0, "optimizing stop", target.toString(), properties);
+        }
+    }
+
+    static void logOptimized(OptimizedCallTarget target, Map<String, Object> properties) {
+        if (TraceTruffleCompilationDetails.getValue() || TraceTruffleCompilation.getValue()) {
+            log(0, "optimizing done", target.toString(), properties);
+        }
+    }
+
+    private static void logInlined(TruffleInliningProfile callSite) {
+        if (TraceTruffleInliningDetails.getValue() || TraceTruffleInlining.getValue()) {
+            log(0, "inline success", callSite.getCallNode().getExecutedCallTarget().toString(), callSite.getDebugProperties());
+        }
+    }
+
+    static void logSplit(@SuppressWarnings("unused") OptimizedCallTarget target, OptimizedCallTarget newTarget) {
+        if (TraceTruffleInliningDetails.getValue() || TraceTruffleInlining.getValue()) {
+            log(0, "split", newTarget.toString(), null);
+        }
+    }
+
+    static void log(int indent, String msg, String details, Map<String, Object> properties) {
+        OUT.printf("[truffle] %-16s ", msg);
+        for (int i = 0; i < indent; i++) {
+            OUT.print(" ");
+        }
+        OUT.printf("%-" + (60 - indent) + "s", details);
+        if (properties != null) {
+            for (String property : properties.keySet()) {
+                Object value = properties.get(property);
+                if (value == null) {
+                    continue;
+                }
+                OUT.print("|");
+                OUT.print(property);
+
+                StringBuilder propertyBuilder = new StringBuilder();
+                if (value instanceof Integer) {
+                    propertyBuilder.append(String.format("%6d", value));
+                } else if (value instanceof Double) {
+                    propertyBuilder.append(String.format("%8.2f", value));
+                } else {
+                    propertyBuilder.append(value);
+                }
+
+                int length = Math.max(1, 20 - property.length());
+                OUT.printf(" %" + length + "s ", propertyBuilder.toString());
+            }
+        }
+        OUT.println();
+    }
+
     private static void printProfiling() {
         List<OptimizedCallTarget> sortedCallTargets = new ArrayList<>(OptimizedCallTarget.callTargets.keySet());
         Collections.sort(sortedCallTargets, new Comparator<OptimizedCallTarget>() {
@@ -248,7 +367,6 @@
 
         int totalCallCount = 0;
         int totalInlinedCallSiteCount = 0;
-        int totalNotInlinedCallSiteCount = 0;
         int totalNodeCount = 0;
         int totalInvalidationCount = 0;
 
@@ -259,21 +377,19 @@
                 continue;
             }
 
-            int notInlinedCallSiteCount = TruffleInliningImpl.getInlinableCallSites(callTarget, callTarget).size();
             int nodeCount = NodeUtil.countNodes(callTarget.getRootNode(), null, true);
             int inlinedCallSiteCount = countInlinedNodes(callTarget.getRootNode());
             String comment = callTarget.installedCode == null ? " int" : "";
             comment += callTarget.compilationEnabled ? "" : " fail";
-            OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d%s\n", callTarget.getRootNode(), callTarget.callCount, inlinedCallSiteCount, notInlinedCallSiteCount, nodeCount,
+            OUT.printf("%-50s | %10d | %15d | %10d | %3d%s\n", callTarget.getRootNode(), callTarget.callCount, inlinedCallSiteCount, nodeCount,
                             callTarget.getCompilationProfile().getInvalidationCount(), comment);
 
             totalCallCount += callTarget.callCount;
             totalInlinedCallSiteCount += inlinedCallSiteCount;
-            totalNotInlinedCallSiteCount += notInlinedCallSiteCount;
             totalNodeCount += nodeCount;
             totalInvalidationCount += callTarget.getCompilationProfile().getInvalidationCount();
         }
-        OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d\n", "Total", totalCallCount, totalInlinedCallSiteCount, totalNotInlinedCallSiteCount, totalNodeCount, totalInvalidationCount);
+        OUT.printf("%-50s | %10d | %15d | %10d | %3d\n", "Total", totalCallCount, totalInlinedCallSiteCount, totalNodeCount, totalInvalidationCount);
     }
 
     private static int countInlinedNodes(Node rootNode) {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Thu Feb 20 01:21:49 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Thu Feb 20 01:43:11 2014 +0100
@@ -169,10 +169,18 @@
         if (TraceTruffleCompilation.getValue()) {
             int nodeCountTruffle = NodeUtil.countNodes(compilable.getRootNode(), null, true);
             byte[] code = compiledMethod.getCode();
-            OUT.printf("[truffle] optimized %-50s %08x |Tree %8d |Time %5.0f(%4.0f+%-4.0f)ms |Graph %5d/%5d |CodeSize %6d @ %s\n", compilable.getRootNode(), compilable.hashCode(), nodeCountTruffle,
-                            (timeCompilationFinished - timeCompilationStarted) / 1e6, (timePartialEvaluationFinished - timeCompilationStarted) / 1e6,
-                            (timeCompilationFinished - timePartialEvaluationFinished) / 1e6, nodeCountPartialEval, nodeCountLowered, code != null ? code.length : 0,
-                            formatSourceSection(compilable.getRootNode().getSourceSection()));
+            Map<String, Object> properties = new LinkedHashMap<>();
+            properties.put("", String.format("%8x", compilable.hashCode()));
+            properties.put("Nodes", nodeCountTruffle);
+            properties.put("Time", String.format("%5.0f(%4.0f+%-4.0f)ms", //
+                            (timeCompilationFinished - timeCompilationStarted) / 1e6, //
+                            (timePartialEvaluationFinished - timeCompilationStarted) / 1e6, //
+                            (timeCompilationFinished - timePartialEvaluationFinished) / 1e6));
+            properties.put("Nodes", String.format("%5d/%5d", nodeCountPartialEval, nodeCountLowered));
+            properties.put("CodeSize", code != null ? code.length : 0);
+            properties.put("Source", formatSourceSection(compilable.getRootNode().getSourceSection()));
+
+            OptimizedCallTarget.logOptimized(compilable, properties);
         }
         return compiledMethod;
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Thu Feb 20 01:21:49 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Thu Feb 20 01:43:11 2014 +0100
@@ -56,7 +56,7 @@
     @Option(help = "")
     public static final OptionValue<Boolean> TruffleFunctionInlining = new OptionValue<>(true);
     @Option(help = "")
-    public static final OptionValue<Integer> TruffleGraphMaxNodes = new OptionValue<>(30000);
+    public static final OptionValue<Integer> TruffleGraphMaxNodes = new OptionValue<>(45000);
     @Option(help = "")
     public static final OptionValue<Integer> TruffleInliningMaxRecursiveDepth = new OptionValue<>(2);
     @Option(help = "")
@@ -68,6 +68,10 @@
     @Option(help = "")
     public static final OptionValue<Double> TruffleInliningMinFrequency = new OptionValue<>(0.3);
     @Option(help = "")
+    public static final OptionValue<Boolean> TruffleSplittingEnabled = new OptionValue<>(true);
+    @Option(help = "")
+    public static final OptionValue<Integer> TruffleSplittingMaxCalleeSize = new OptionValue<>(50);
+    @Option(help = "")
     public static final OptionValue<Boolean> TruffleUseTimeForCompilationDecision = new OptionValue<>(false);
     @Option(help = "")
     public static final OptionValue<Integer> TruffleCompilationDecisionTime = new OptionValue<>(100);
@@ -92,7 +96,7 @@
     @Option(help = "")
     public static final OptionValue<Boolean> TruffleCompilationExceptionsAreFatal = new OptionValue<>(true);
     @Option(help = "")
-    public static final OptionValue<Boolean> TraceTruffleInlining = new OptionValue<>(true);
+    public static final OptionValue<Boolean> TraceTruffleInlining = new OptionValue<>(false);
     @Option(help = "")
     public static final OptionValue<Boolean> TraceTruffleInliningTree = new OptionValue<>(false);
     @Option(help = "")
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInlining.java	Thu Feb 20 01:21:49 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +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;
-
-public interface TruffleInlining {
-
-    /** Returns true if reprofiling is required else false. */
-    boolean performInlining(OptimizedCallTarget callTarget);
-
-    /**
-     * Returns the minimum number of invocations required until the next inlining can occur. Only
-     * used if {@link #performInlining(OptimizedCallTarget)} returned true.
-     */
-    int getInvocationReprofileCount();
-
-    /**
-     * Returns the number of invocations or loop invocations required until the next inlining can
-     * occur. Only used if {@link #performInlining(OptimizedCallTarget)} returned true.
-     */
-    int getReprofileCount();
-}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java	Thu Feb 20 01:21:49 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,275 +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 static com.oracle.graal.truffle.TruffleCompilerOptions.*;
-
-import java.io.*;
-import java.util.*;
-
-import com.oracle.graal.debug.*;
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.nodes.CallNode.*;
-
-class TruffleInliningImpl implements TruffleInlining {
-
-    private static final int MIN_INVOKES_AFTER_INLINING = 2;
-
-    private static final PrintStream OUT = TTY.out().out();
-
-    public int getReprofileCount() {
-        return TruffleCompilerOptions.TruffleInliningReprofileCount.getValue();
-    }
-
-    public int getInvocationReprofileCount() {
-        return MIN_INVOKES_AFTER_INLINING;
-    }
-
-    private static void refresh(InliningPolicy policy, List<InlinableCallSiteInfo> infos) {
-        for (InlinableCallSiteInfo info : infos) {
-            info.refresh(policy);
-        }
-    }
-
-    @Override
-    public boolean performInlining(OptimizedCallTarget target) {
-        final InliningPolicy policy = new InliningPolicy(target);
-        if (!policy.continueInlining()) {
-            if (TraceTruffleInliningDetails.getValue()) {
-                List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(policy, target);
-                if (!inlinableCallSites.isEmpty()) {
-                    OUT.printf("[truffle] inlining hit caller size limit (%3d >= %3d).%3d remaining call sites in %s:\n", policy.callerNodeCount, TruffleInliningMaxCallerSize.getValue(),
-                                    inlinableCallSites.size(), target.getRootNode());
-                    InliningPolicy.sortByRelevance(inlinableCallSites);
-                    printCallSiteInfo(policy, inlinableCallSites, "");
-                }
-            }
-            return false;
-        }
-
-        List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(policy, target);
-        if (inlinableCallSites.isEmpty()) {
-            return false;
-        }
-
-        InliningPolicy.sortByRelevance(inlinableCallSites);
-
-        boolean inlined = false;
-        for (InlinableCallSiteInfo inlinableCallSite : inlinableCallSites) {
-            if (!inlinableCallSite.isWorth()) {
-                break;
-            }
-            if (inlinableCallSite.getCallNode().inline()) {
-                if (TraceTruffleInlining.getValue()) {
-                    printCallSiteInfo(policy, inlinableCallSite, "inlined");
-                }
-                inlined = true;
-                break;
-            }
-        }
-
-        if (inlined) {
-            for (InlinableCallSiteInfo callSite : inlinableCallSites) {
-                CompilerCallView callView = callSite.getCallNode().getCompilerCallView();
-                if (callView != null) {
-                    callView.resetCallCount();
-                }
-            }
-        } else {
-            if (TraceTruffleInliningDetails.getValue()) {
-                OUT.printf("[truffle] inlining stopped.%3d remaining call sites in %s:\n", inlinableCallSites.size(), target.getRootNode());
-                printCallSiteInfo(policy, inlinableCallSites, "");
-            }
-        }
-
-        return inlined;
-    }
-
-    private static void printCallSiteInfo(InliningPolicy policy, List<InlinableCallSiteInfo> inlinableCallSites, String msg) {
-        for (InlinableCallSiteInfo candidate : inlinableCallSites) {
-            printCallSiteInfo(policy, candidate, msg);
-        }
-    }
-
-    private static void printCallSiteInfo(InliningPolicy policy, InlinableCallSiteInfo callSite, String msg) {
-        String calls = String.format("%4s/%4s", callSite.getCallCount(), policy.callerInvocationCount);
-        String nodes = String.format("%3s/%3s", callSite.getInlineNodeCount(), policy.callerNodeCount);
-        CallTarget inlined = callSite.getCallNode().getCallTarget();
-        OUT.printf("[truffle] %-9s %-50s %08x |Tree %8s |Calls %6s %7.3f @ %s\n", msg, inlined, inlined.hashCode(), nodes, calls, policy.metric(callSite), callSite.getCallNode());
-    }
-
-    private static final class InliningPolicy {
-
-        private final int callerNodeCount;
-        private final int callerInvocationCount;
-
-        public InliningPolicy(OptimizedCallTarget caller) {
-            this.callerNodeCount = NodeUtil.countNodes(caller.getRootNode(), null, true);
-            this.callerInvocationCount = caller.getCompilationProfile().getOriginalInvokeCounter();
-        }
-
-        public boolean continueInlining() {
-            return callerNodeCount < TruffleInliningMaxCallerSize.getValue();
-        }
-
-        public boolean isWorthInlining(InlinableCallSiteInfo callSite) {
-            return callSite.getInlineNodeCount() <= TruffleInliningMaxCalleeSize.getValue() && callSite.getInlineNodeCount() + callerNodeCount <= TruffleInliningMaxCallerSize.getValue() &&
-                            callSite.getCallCount() > 0 && callSite.getRecursiveDepth() < TruffleInliningMaxRecursiveDepth.getValue() &&
-                            (frequency(callSite) >= TruffleInliningMinFrequency.getValue() || callSite.getInlineNodeCount() <= TruffleInliningTrivialSize.getValue());
-        }
-
-        public double metric(InlinableCallSiteInfo callSite) {
-            double cost = callSite.getInlineNodeCount();
-            double metric = frequency(callSite) / cost;
-            return metric;
-        }
-
-        private double frequency(InlinableCallSiteInfo callSite) {
-            return (double) callSite.getCallCount() / (double) callerInvocationCount;
-        }
-
-        private static void sortByRelevance(List<InlinableCallSiteInfo> inlinableCallSites) {
-            Collections.sort(inlinableCallSites, new Comparator<InlinableCallSiteInfo>() {
-
-                @Override
-                public int compare(InlinableCallSiteInfo cs1, InlinableCallSiteInfo cs2) {
-                    boolean cs1Worth = cs1.isWorth();
-                    boolean cs2Worth = cs2.isWorth();
-                    if (cs1Worth && cs2Worth) {
-                        return Double.compare(cs2.getScore(), cs1.getScore());
-                    } else if (cs1Worth ^ cs2Worth) {
-                        return cs1Worth ? -1 : 1;
-                    }
-                    return 0;
-                }
-            });
-        }
-    }
-
-    private static final class InlinableCallSiteInfo {
-
-        private final CallNode callNode;
-        private final int nodeCount;
-        private final int recursiveDepth;
-
-        private int callCount;
-        private boolean worth;
-        private double score;
-
-        @SuppressWarnings("unused")
-        public InlinableCallSiteInfo(InliningPolicy policy, CallNode callNode) {
-            assert callNode.isInlinable();
-            this.callNode = callNode;
-            RootCallTarget target = (RootCallTarget) callNode.getCallTarget();
-
-            this.nodeCount = target.getRootNode().getInlineNodeCount();
-            this.recursiveDepth = calculateRecursiveDepth();
-        }
-
-        public int getRecursiveDepth() {
-            return recursiveDepth;
-        }
-
-        public void refresh(InliningPolicy policy) {
-            this.callCount = callNode.getCompilerCallView().getCallCount();
-            this.worth = policy.isWorthInlining(this);
-            if (worth) {
-                this.score = policy.metric(this);
-            }
-            // TODO shall we refresh the node count as well?
-        }
-
-        public boolean isWorth() {
-            return worth;
-        }
-
-        public double getScore() {
-            return score;
-        }
-
-        private int calculateRecursiveDepth() {
-            int depth = 0;
-
-            Node parent = callNode.getParent();
-            while (parent != null) {
-                if (parent instanceof RootNode) {
-                    RootNode root = ((RootNode) parent);
-                    if (root.getCallTarget() == callNode.getCallTarget()) {
-                        depth++;
-                    }
-                    parent = root.getParentInlinedCall();
-                } else {
-                    parent = parent.getParent();
-                }
-            }
-            return depth;
-        }
-
-        public CallNode getCallNode() {
-            return callNode;
-        }
-
-        public int getCallCount() {
-            return callCount;
-        }
-
-        public int getInlineNodeCount() {
-            return nodeCount;
-        }
-    }
-
-    private static List<InlinableCallSiteInfo> getInlinableCallSites(final InliningPolicy policy, final RootCallTarget target) {
-        final ArrayList<InlinableCallSiteInfo> inlinableCallSites = new ArrayList<>();
-        target.getRootNode().accept(new NodeVisitor() {
-
-            @Override
-            public boolean visit(Node node) {
-                if (node instanceof CallNode) {
-                    CallNode callNode = (CallNode) node;
-
-                    if (!callNode.isInlined()) {
-                        if (callNode.isInlinable()) {
-                            CompilerCallView view = callNode.getCompilerCallView();
-                            InlinableCallSiteInfo info = (InlinableCallSiteInfo) view.load();
-                            if (info == null) {
-                                info = new InlinableCallSiteInfo(policy, callNode);
-                                view.store(info);
-                            }
-                            inlinableCallSites.add(info);
-                        }
-                    } else {
-                        callNode.getInlinedRoot().accept(this);
-                    }
-                }
-                return true;
-            }
-        });
-        refresh(policy, inlinableCallSites);
-        return inlinableCallSites;
-    }
-
-    static List<InlinableCallSiteInfo> getInlinableCallSites(final OptimizedCallTarget target, final RootCallTarget root) {
-        return getInlinableCallSites(new InliningPolicy(target), root);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java	Thu Feb 20 01:43:11 2014 +0100
@@ -0,0 +1,37 @@
+/*
+ * 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.*;
+
+public interface TruffleInliningProfile extends Comparable<TruffleInliningProfile> {
+
+    OptimizedCallNode getCallNode();
+
+    boolean isInliningAllowed();
+
+    int compareTo(TruffleInliningProfile o);
+
+    Map<String, Object> getDebugProperties();
+
+}