changeset 13996:c7ac129e17e9

Truffle: further tweaks to the inlinig/split heuristic. Improved detailed log output for compilation and inlining. Added separate option to print the node histogram TraceTruffleCompilationHistogram.
author Christian Humer <christian.humer@gmail.com>
date Fri, 21 Feb 2014 02:29:16 +0100
parents e455fc531ec2
children 5f2c0ad0501a
files graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.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/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
diffstat 7 files changed, 263 insertions(+), 173 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.java	Fri Feb 21 02:25:12 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.java	Fri Feb 21 02:29:16 2014 +0100
@@ -24,6 +24,8 @@
 
 import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
 
+import java.util.*;
+
 public class CompilationProfile {
 
     /**
@@ -57,6 +59,17 @@
         this.name = name;
     }
 
+    public Map<String, Object> getDebugProperties() {
+        Map<String, Object> properties = new LinkedHashMap<>();
+        String callsThreshold = String.format("%7d/%5d", getCallCount(), getCompilationCallThreshold());
+        String loopsThreshold = String.format("%7d/%5d", getCallAndLoopCount(), getCompilationCallAndLoopThreshold());
+        String invalidationReplace = String.format("%5d/%5d", invalidationCount, nodeReplaceCount);
+        properties.put("C/T", callsThreshold);
+        properties.put("L/T", loopsThreshold);
+        properties.put("Inval#/Replace#", invalidationReplace);
+        return properties;
+    }
+
     public void reset() {
         callCount = 0;
         callAndLoopCount = 0;
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java	Fri Feb 21 02:25:12 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java	Fri Feb 21 02:29:16 2014 +0100
@@ -22,10 +22,13 @@
  */
 package com.oracle.graal.truffle;
 
+import java.util.concurrent.atomic.*;
+
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.impl.*;
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.nodes.NodeInfo.Kind;
 
 /**
  * Call target that is optimized by Graal upon surpassing a specific invocation threshold.
@@ -57,8 +60,8 @@
         return callCount;
     }
 
-    public TruffleInliningProfile createInliningProfile() {
-        return new OptimizedCallNodeProfile(this);
+    public TruffleInliningProfile createInliningProfile(OptimizedCallTarget target) {
+        return new OptimizedCallNodeProfile(target, this);
     }
 
     @Override
@@ -66,10 +69,11 @@
         return null;
     }
 
-    final OptimizedCallNode inlineImpl() {
+    protected 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));
     }
 
@@ -103,15 +107,57 @@
         private Object trySplit(PackedFrame caller, Arguments arguments) {
             int effectiveCallCount = callCount;
             // we try splitting for the first two invocations
-            if (effectiveCallCount == 1 || effectiveCallCount == 2) {
+            if (effectiveCallCount <= 3) {
                 if (isSplittable() && shouldSplit()) {
-                    return splitImpl().call(caller, arguments);
+                    return splitImpl(true).call(caller, arguments);
+                }
+                if (effectiveCallCount == 3) {
+                    splitTried = true;
+                }
+            }
+            return callTarget.call(caller, arguments);
+        }
+
+        private boolean shouldSplit() {
+            if (!TruffleCompilerOptions.TruffleSplittingEnabled.getValue()) {
+                return false;
+            }
+
+            int nodeCount = NodeUtil.countNodes(getCallTarget().getRootNode(), null, false);
+
+            // max one child call and callCount > 2 and kind of small number of nodes
+            if (callCount > 2 && isCallMethod()) {
+                if (nodeCount <= 100) {
+                    return true;
                 }
             }
-            if (effectiveCallCount >= 2) {
-                splitTried = true;
+
+            if (nodeCount > TruffleCompilerOptions.TruffleSplittingMaxCalleeSize.getValue()) {
+                return false;
             }
-            return callTarget.call(caller, arguments);
+            return countPolymorphic() > 1 || countGeneric() > 0;
+        }
+
+        private boolean isCallMethod() {
+            final AtomicInteger count = new AtomicInteger(0);
+            getExecutedCallTarget().getRootNode().accept(new NodeVisitor() {
+
+                public boolean visit(Node node) {
+                    if (node instanceof CallNode) {
+                        return count.incrementAndGet() > 1;
+                    }
+                    return true;
+                }
+            });
+            return count.get() <= 1;
+        }
+
+        private int countPolymorphic() {
+            return NodeUtil.countNodes(getCallTarget().getRootNode(), null, Kind.POLYMORPHIC, true);
+        }
+
+        private int countGeneric() {
+            return NodeUtil.countNodes(getCallTarget().getRootNode(), null, Kind.GENERIC, true);
         }
 
         @Override
@@ -128,30 +174,18 @@
             if (getParent() == null) {
                 throw new IllegalStateException("CallNode must be adopted before it is split.");
             }
-            splitImpl();
+            splitImpl(false);
             return true;
         }
 
-        private OptimizedCallNode splitImpl() {
+        private OptimizedCallNode splitImpl(boolean heuristic) {
             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;
+            splitCallTarget.setSplitSource(getCallTarget());
+            if (heuristic) {
+                OptimizedCallTarget.logSplit(this, getCallTarget(), splitCallTarget);
             }
-            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;
+            return replace(new SplitOptimizedCallNode(getCallTarget(), splitCallTarget, callCount));
         }
 
         @Override
@@ -252,40 +286,4 @@
 
     }
 
-    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;
-            }
-        }
-
-    }
-
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNodeProfile.java	Fri Feb 21 02:25:12 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNodeProfile.java	Fri Feb 21 02:29:16 2014 +0100
@@ -22,38 +22,44 @@
  */
 package com.oracle.graal.truffle;
 
+import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
+
 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 static final String REASON_MAXIMUM_TOTAL_NODE_COUNT = "inlinedTotalCount > " + TruffleInliningMaxCallerSize.getValue();
 
+    private final OptimizedCallTarget callTarget;
     private final OptimizedCallNode callNode;
 
-    private final int targetDeepNodeCount;
+    private int targetDeepNodeCount;
+    private List<OptimizedCallTarget> compilationRoots;
     private final int targetShallowNodeCount;
-    private final List<OptimizedCallTarget> compilationRoots;
     private final double averageFrequency;
     private final double score;
     private String reason;
 
-    public OptimizedCallNodeProfile(OptimizedCallNode callNode) {
+    public OptimizedCallNodeProfile(OptimizedCallTarget target, OptimizedCallNode callNode) {
         this.callNode = callNode;
         RootNode inlineRoot = callNode.getExecutedCallTarget().getRootNode();
+        this.callTarget = target;
         this.targetShallowNodeCount = NodeUtil.countNodes(inlineRoot, null, false);
         this.targetDeepNodeCount = NodeUtil.countNodes(inlineRoot, null, true);
         this.compilationRoots = findCompilationRoots(callNode);
-        this.averageFrequency = calculateAverageFrequency(compilationRoots);
+        this.averageFrequency = calculateFrequency(compilationRoots);
         this.score = calculateScore();
     }
 
+    private double calculateFrequency(@SuppressWarnings("unused") List<OptimizedCallTarget> compilationRoots2) {
+        return calculateSimpleFrequency();
+    }
+
     public OptimizedCallNode getCallNode() {
         return callNode;
     }
@@ -67,6 +73,8 @@
     }
 
     public boolean isInliningAllowed() {
+        this.compilationRoots = findCompilationRoots(getCallNode());
+
         OptimizedCallTarget inlineTarget = callNode.getExecutedCallTarget();
         for (OptimizedCallTarget compilationRoot : compilationRoots) {
             if (compilationRoot == inlineTarget) {
@@ -87,9 +95,10 @@
             return false;
         }
 
+        this.targetDeepNodeCount = NodeUtil.countNodes(inlineTarget.getRootNode(), null, true);
         // The maximum total node count cannot be cached since it may change during inlining.
-        int currentNodeCount = calculateCurrentNodeCount(compilationRoots);
-        if (targetDeepNodeCount + currentNodeCount > TruffleInliningMaxCallerSize.getValue()) {
+        int nextNodeCount = calculateInlinedTotalNodeCount(getCallNode());
+        if (nextNodeCount > TruffleInliningMaxCallerSize.getValue()) {
             reason = REASON_MAXIMUM_TOTAL_NODE_COUNT;
             return false;
         }
@@ -97,33 +106,50 @@
         return true;
     }
 
-    private static int calculateCurrentNodeCount(List<OptimizedCallTarget> compilationRoots) {
+    private int calculateInlinedTotalNodeCount(OptimizedCallNode node) {
         int currentNodeCount = 0;
         for (OptimizedCallTarget compilationRoot : compilationRoots) {
             if (compilationRoot.getRootNode().getParentInlinedCalls().isEmpty()) {
-                currentNodeCount = Math.max(currentNodeCount, NodeUtil.countNodes(compilationRoot.getRootNode(), null, true));
+                TotalNodeCountVisitor visitor = new TotalNodeCountVisitor(node, targetDeepNodeCount);
+                compilationRoot.getRootNode().accept(visitor);
+                currentNodeCount = Math.max(currentNodeCount, visitor.count);
             }
         }
         return currentNodeCount;
     }
 
-    @SuppressWarnings("unused")
-    private double calculateSimpleFrequency() {
-        RootNode root = callNode.getRootNode();
-        OptimizedCallTarget target = ((OptimizedCallTarget) root.getCallTarget());
+    private static class TotalNodeCountVisitor implements NodeVisitor {
+
+        private final OptimizedCallNode inlinedNode;
+        private final int inlinedNodeCount;
+
+        private int count;
+
+        public TotalNodeCountVisitor(OptimizedCallNode inlinedNode, int inlinedNodeCount) {
+            this.inlinedNode = inlinedNode;
+            this.inlinedNodeCount = inlinedNodeCount;
+        }
 
-        int totalCallCount = target.getCompilationProfile().getCallCount();
-        List<CallNode> parentInlined = root.getParentInlinedCalls();
-        for (CallNode node : parentInlined) {
-            int callCount = ((OptimizedCallNode) node).getCallCount();
-            if (callCount >= 0) {
-                totalCallCount += callCount;
+        public boolean visit(Node node) {
+            count++;
+            if (node instanceof CallNode) {
+                RootNode inlinedRoot = ((CallNode) node).getInlinedRoot();
+                if (inlinedRoot != null) {
+                    inlinedRoot.accept(this);
+                } else if (node == inlinedNode) {
+                    count += inlinedNodeCount;
+                }
             }
+            return true;
         }
-        return callNode.getCallCount() / (double) totalCallCount;
+
     }
 
-    private double calculateAverageFrequency(List<OptimizedCallTarget> roots) {
+    double calculateSimpleFrequency() {
+        return callNode.getCallCount() / (double) callTarget.getCompilationProfile().getCallCount();
+    }
+
+    double calculateAverageFrequency(List<OptimizedCallTarget> roots) {
         int compilationRootCallCountSum = 0;
         int compilationRootCount = 0;
         for (OptimizedCallTarget compilationRoot : roots) {
@@ -157,9 +183,10 @@
 
     public Map<String, Object> getDebugProperties() {
         Map<String, Object> properties = new LinkedHashMap<>();
+        OptimizedCallTarget.addASTSizeProperty(getCallNode().getExecutedCallTarget().getRootNode(), properties);
         properties.put("shallowCount", targetShallowNodeCount);
-        properties.put("deepCount", targetDeepNodeCount);
-        properties.put("currentCount", calculateCurrentNodeCount(compilationRoots));
+        properties.put("currentCount", calculateInlinedTotalNodeCount(null));
+        properties.put("inlinedTotalCount", calculateInlinedTotalNodeCount(getCallNode()));
         properties.put("score", score);
         properties.put("frequency", averageFrequency);
         properties.put("callCount", callNode.getCallCount());
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Fri Feb 21 02:25:12 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Fri Feb 21 02:29:16 2014 +0100
@@ -34,6 +34,7 @@
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.impl.*;
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.nodes.NodeInfo.Kind;
 
 /**
  * Call target that is optimized by Graal upon surpassing a specific invocation threshold.
@@ -45,12 +46,14 @@
     private InstalledCode installedCode;
     private Future<InstalledCode> installedCodeTask;
     private boolean compilationEnabled;
+    private boolean inlined;
     private int callCount;
 
     private final TruffleCompiler compiler;
     private final CompilationProfile compilationProfile;
     private final CompilationPolicy compilationPolicy;
     private final SpeculationLog speculationLog = new SpeculationLog();
+    private OptimizedCallTarget splitSource;
 
     OptimizedCallTarget(RootNode rootNode, TruffleCompiler compiler, int invokeCounter, int compilationThreshold, boolean compilationEnabled) {
         super(rootNode);
@@ -69,12 +72,19 @@
         }
     }
 
+    public void setSplitSource(OptimizedCallTarget splitSource) {
+        this.splitSource = splitSource;
+    }
+
     @Override
     public String toString() {
         String superString = super.toString();
         if (installedCode != null) {
             superString += " <compiled>";
         }
+        if (splitSource != null) {
+            superString += " <split>";
+        }
         return superString;
     }
 
@@ -118,29 +128,27 @@
     }
 
     private Object compiledCodeInvalidated(PackedFrame caller, Arguments args) {
-        invalidate("Compiled code invalidated");
+        invalidate(null, null, "Compiled code invalidated");
         return call(caller, args);
     }
 
-    private void invalidate(String reason) {
+    private void invalidate(Node oldNode, Node newNode, String reason) {
         InstalledCode m = this.installedCode;
         if (m != null) {
             CompilerAsserts.neverPartOfCompilation();
             installedCode = null;
             compilationProfile.reportInvalidated();
-            if (TraceTruffleCompilation.getValue()) {
-                logOptimizedInvalidated(this, reason);
-            }
+            logOptimizedInvalidated(this, oldNode, newNode, reason);
         }
-        cancelInstalledTask(reason);
+        cancelInstalledTask(oldNode, newNode, reason);
     }
 
-    private void cancelInstalledTask(String reason) {
+    private void cancelInstalledTask(Node oldNode, Node newNode, String reason) {
         Future<InstalledCode> task = this.installedCodeTask;
         if (task != null) {
             task.cancel(true);
             this.installedCodeTask = null;
-            logOptimizingCancelled(this, reason);
+            logOptimizingUnqueued(this, oldNode, newNode, reason);
             compilationProfile.reportInvalidated();
         }
     }
@@ -167,35 +175,42 @@
         if (!TruffleCompilerOptions.TruffleFunctionInlining.getValue()) {
             return;
         }
+        if (inlined) {
+            return;
+        }
+        inlined = true;
+
+        logInliningStart(this);
         PriorityQueue<TruffleInliningProfile> queue = new PriorityQueue<>();
 
         // Used to avoid running in cycles or inline nodes in Truffle trees
         // which do not suffice the tree property twice.
         Set<CallNode> visitedCallNodes = new HashSet<>();
 
-        queueCallSitesForInlining(getRootNode(), visitedCallNodes, queue);
+        queueCallSitesForInlining(this, getRootNode(), visitedCallNodes, queue);
         TruffleInliningProfile callSite = queue.poll();
         while (callSite != null) {
             if (callSite.isInliningAllowed()) {
                 OptimizedCallNode callNode = callSite.getCallNode();
-                logInlined(callSite);
+                logInlined(this, callSite);
                 RootNode inlinedRoot = callNode.inlineImpl().getInlinedRoot();
                 assert inlinedRoot != null;
-                queueCallSitesForInlining(inlinedRoot, visitedCallNodes, queue);
+                queueCallSitesForInlining(this, inlinedRoot, visitedCallNodes, queue);
             } else {
                 logInliningFailed(callSite);
             }
             callSite = queue.poll();
         }
+        logInliningDone(this);
     }
 
-    private static void queueCallSitesForInlining(RootNode rootNode, final Set<CallNode> visitedCallSites, final PriorityQueue<TruffleInliningProfile> queue) {
+    private static void queueCallSitesForInlining(final OptimizedCallTarget target, RootNode rootNode, final Set<CallNode> visitedCallSites, 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() && !visitedCallSites.contains(call)) {
-                        queue.add(call.createInliningProfile());
+                        queue.add(call.createInliningProfile(target));
                         visitedCallSites.add(call);
                     } else if (call.getInlinedRoot() != null) {
                         call.getInlinedRoot().accept(this);
@@ -225,8 +240,8 @@
             }
             return null;
         } else {
-            logOptimizing(this);
             performInlining();
+            logOptimizingQueued(this);
             this.installedCodeTask = compiler.compile(this);
             if (!TruffleBackgroundCompilation.getValue()) {
                 return receiveInstalledCode();
@@ -240,7 +255,7 @@
             return installedCodeTask.get();
         } catch (InterruptedException | ExecutionException e) {
             compilationEnabled = false;
-            OUT.printf("[truffle] opt failed %-48s  %s\n", getRootNode(), e.getMessage());
+            logOptimizingFailed(this, e.getMessage());
             if (e.getCause() instanceof BailoutException) {
                 // Bailout => move on.
             } else {
@@ -272,64 +287,133 @@
     @Override
     public void nodeReplaced(Node oldNode, Node newNode, String reason) {
         compilationProfile.reportNodeReplaced();
-        invalidate(reason);
+        invalidate(oldNode, newNode, reason);
     }
 
     public SpeculationLog getSpeculationLog() {
         return speculationLog;
     }
 
+    public Map<String, Object> getDebugProperties() {
+        Map<String, Object> properties = new LinkedHashMap<>();
+        addASTSizeProperty(getRootNode(), properties);
+        properties.putAll(getCompilationProfile().getDebugProperties());
+        return properties;
+
+    }
+
     private static void logInliningFailed(TruffleInliningProfile callSite) {
-        if (TraceTruffleInliningDetails.getValue() || TraceTruffleCompilationDetails.getValue()) {
-            log(0, "inline failed", callSite.getCallNode().getExecutedCallTarget().toString(), callSite.getDebugProperties());
+        if (TraceTruffleInliningDetails.getValue()) {
+            log(2, "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 logInlined(final OptimizedCallTarget target, TruffleInliningProfile callSite) {
+        if (TraceTruffleInliningDetails.getValue() || TraceTruffleInlining.getValue()) {
+            log(2, "inline success", callSite.getCallNode().getExecutedCallTarget().toString(), callSite.getDebugProperties());
+
+            if (TraceTruffleInliningDetails.getValue()) {
+                RootNode root = callSite.getCallNode().getExecutedCallTarget().getRootNode();
+                root.accept(new NodeVisitor() {
+                    int depth = 1;
+
+                    public boolean visit(Node node) {
+                        if (node instanceof OptimizedCallNode) {
+                            OptimizedCallNode callNode = ((OptimizedCallNode) node);
+                            log(2 + (depth * 2), "inline success", callNode.getExecutedCallTarget().toString(), callNode.createInliningProfile(target).getDebugProperties());
+                            RootNode inlinedRoot = callNode.getInlinedRoot();
+                            if (inlinedRoot != null) {
+                                depth++;
+                                inlinedRoot.accept(this);
+                                depth--;
+                            }
+                        }
+                        return true;
+                    }
+                });
+            }
         }
     }
 
-    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 logInliningStart(OptimizedCallTarget target) {
+        if (TraceTruffleInliningDetails.getValue()) {
+            log(0, "inline start", target.toString(), target.getDebugProperties());
+        }
+    }
+
+    private static void logInliningDone(OptimizedCallTarget target) {
+        if (TraceTruffleInliningDetails.getValue()) {
+            log(0, "inline done", target.toString(), target.getDebugProperties());
+        }
+    }
+
+    private static void logOptimizingQueued(OptimizedCallTarget target) {
+        if (TraceTruffleCompilationDetails.getValue()) {
+            log(0, "opt queued", target.toString(), target.getDebugProperties());
         }
     }
 
-    private static void logOptimizingCancelled(OptimizedCallTarget target, String reason) {
-        if (TraceTruffleInliningDetails.getValue() || TraceTruffleCompilationDetails.getValue()) {
+    private static void logOptimizingUnqueued(OptimizedCallTarget target, Node oldNode, Node newNode, String reason) {
+        if (TraceTruffleCompilationDetails.getValue()) {
             Map<String, Object> properties = new LinkedHashMap<>();
-            properties.put("Invalidation#", target.compilationProfile.getInvalidationCount());
-            properties.put("Replace#", target.compilationProfile.getNodeReplaceCount());
+            addReplaceProperties(properties, oldNode, newNode);
             properties.put("Reason", reason);
-            log(0, "optimizing stop", target.toString(), properties);
+            log(0, "opt unqueued", target.toString(), properties);
+        }
+    }
+
+    private static void addReplaceProperties(Map<String, Object> properties, Node oldNode, Node newNode) {
+        if (oldNode != null && newNode != null) {
+            properties.put("OldClass", oldNode.getClass().getSimpleName());
+            properties.put("NewClass", newNode.getClass().getSimpleName());
+            properties.put("Node", newNode);
+        }
+    }
+
+    static void logOptimizingStart(OptimizedCallTarget target) {
+        if (TraceTruffleCompilationDetails.getValue()) {
+            log(0, "opt start", target.toString(), target.getDebugProperties());
         }
     }
 
-    static void logOptimized(OptimizedCallTarget target, Map<String, Object> properties) {
-        if (TraceTruffleCompilationDetails.getValue() || TraceTruffleCompilation.getValue()) {
-            log(0, "optimizing done", target.toString(), properties);
+    private static void logOptimizedInvalidated(OptimizedCallTarget target, Node oldNode, Node newNode, String reason) {
+        if (TraceTruffleCompilation.getValue()) {
+            Map<String, Object> properties = new LinkedHashMap<>();
+            addReplaceProperties(properties, oldNode, newNode);
+            properties.put("Reason", reason);
+            log(0, "opt invalidated", target.toString(), properties);
         }
     }
 
-    private static void logInlined(TruffleInliningProfile callSite) {
-        if (TraceTruffleInliningDetails.getValue() || TraceTruffleInlining.getValue()) {
-            log(0, "inline success", callSite.getCallNode().getExecutedCallTarget().toString(), callSite.getDebugProperties());
+    private static void logOptimizingFailed(OptimizedCallTarget callSite, String reason) {
+        Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("Reason", reason);
+        log(0, "opt fail", callSite.toString(), properties);
+    }
+
+    static void logOptimizingDone(OptimizedCallTarget target, Map<String, Object> properties) {
+        if (TraceTruffleCompilationDetails.getValue() || TraceTruffleCompilation.getValue()) {
+            log(0, "opt done", target.toString(), properties);
         }
     }
 
-    static void logSplit(@SuppressWarnings("unused") OptimizedCallTarget target, OptimizedCallTarget newTarget) {
+    static void logSplit(OptimizedCallNode callNode, OptimizedCallTarget target, OptimizedCallTarget newTarget) {
         if (TraceTruffleInliningDetails.getValue() || TraceTruffleInlining.getValue()) {
-            log(0, "split", newTarget.toString(), null);
+            Map<String, Object> properties = new LinkedHashMap<>();
+            addASTSizeProperty(target.getRootNode(), properties);
+            properties.put("Source", callNode.getEncapsulatingSourceSection());
+            log(0, "split", newTarget.toString(), properties);
         }
     }
 
-    static void log(int indent, String msg, String details, Map<String, Object> properties) {
+    static void addASTSizeProperty(RootNode target, Map<String, Object> properties) {
+        String value = String.format("%4d (%d/%d)", NodeUtil.countNodes(target.getRootNode(), null, true), //
+                        NodeUtil.countNodes(target.getRootNode(), null, Kind.POLYMORPHIC, true), NodeUtil.countNodes(target.getRootNode(), null, Kind.GENERIC, true)); //
+
+        properties.put("ASTSize", value);
+    }
+
+    static synchronized 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(" ");
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri Feb 21 02:25:12 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri Feb 21 02:29:16 2014 +0100
@@ -94,7 +94,7 @@
             throw Debug.handle(e);
         }
 
-        if (TraceTruffleCompilationDetails.getValue()) {
+        if (TraceTruffleCompilationHistogram.getValue()) {
             constantReceivers = new HashSet<>();
         }
 
@@ -130,7 +130,7 @@
 
             new VerifyFrameDoesNotEscapePhase().apply(graph, false);
 
-            if (TraceTruffleCompilationDetails.getValue() && constantReceivers != null) {
+            if (TraceTruffleCompilationHistogram.getValue() && constantReceivers != null) {
                 DebugHistogram histogram = Debug.createHistogram("Expanded Truffle Nodes");
                 for (Constant c : constantReceivers) {
                     histogram.add(c.asObject().getClass().getSimpleName());
@@ -184,7 +184,7 @@
             for (MethodCallTargetNode methodCallTargetNode : graph.getNodes(MethodCallTargetNode.class)) {
                 InvokeKind kind = methodCallTargetNode.invokeKind();
                 if (kind == InvokeKind.Static || (kind == InvokeKind.Special && (methodCallTargetNode.receiver().isConstant() || methodCallTargetNode.receiver() instanceof NewFrameNode))) {
-                    if (TraceTruffleCompilationDetails.getValue() && kind == InvokeKind.Special) {
+                    if (TraceTruffleCompilationHistogram.getValue() && kind == InvokeKind.Special) {
                         ConstantNode constantNode = (ConstantNode) methodCallTargetNode.arguments().first();
                         constantReceivers.add(constantNode.asConstant());
                     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Fri Feb 21 02:25:12 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Fri Feb 21 02:29:16 2014 +0100
@@ -141,8 +141,12 @@
             graphCache.removeStaleGraphs();
         }
 
+        if (TraceTruffleCompilation.getValue()) {
+            OptimizedCallTarget.logOptimizingStart(compilable);
+        }
+
         if (TraceTruffleInliningTree.getValue()) {
-            printInlineTree(compilable.getRootNode());
+            NodeUtil.printInliningTree(OUT, compilable.getRootNode());
         }
 
         long timeCompilationStarted = System.nanoTime();
@@ -170,8 +174,7 @@
             int nodeCountTruffle = NodeUtil.countNodes(compilable.getRootNode(), null, true);
             byte[] code = compiledMethod.getCode();
             Map<String, Object> properties = new LinkedHashMap<>();
-            properties.put("", String.format("%8x", compilable.hashCode()));
-            properties.put("Nodes", nodeCountTruffle);
+            properties.put("ASTSize", nodeCountTruffle);
             properties.put("Time", String.format("%5.0f(%4.0f+%-4.0f)ms", //
                             (timeCompilationFinished - timeCompilationStarted) / 1e6, //
                             (timePartialEvaluationFinished - timeCompilationStarted) / 1e6, //
@@ -180,7 +183,7 @@
             properties.put("CodeSize", code != null ? code.length : 0);
             properties.put("Source", formatSourceSection(compilable.getRootNode().getSourceSection()));
 
-            OptimizedCallTarget.logOptimized(compilable, properties);
+            OptimizedCallTarget.logOptimizingDone(compilable, properties);
         }
         return compiledMethod;
     }
@@ -189,43 +192,6 @@
         return sourceSection != null ? sourceSection.toString() : "n/a";
     }
 
-    private void printInlineTree(RootNode rootNode) {
-        OUT.println();
-        OUT.println("Inlining tree for: " + rootNode);
-        rootNode.accept(new InlineTreeVisitor());
-    }
-
-    private class InlineTreeVisitor implements NodeVisitor {
-
-        public boolean visit(Node node) {
-            if (node instanceof CallNode) {
-                CallNode callNode = (CallNode) node;
-                if (callNode.isInlined()) {
-                    int indent = this.indent(node);
-                    for (int i = 0; i < indent; ++i) {
-                        OUT.print("   ");
-                    }
-                    OUT.println(callNode.getCallTarget());
-                    callNode.getInlinedRoot().accept(this);
-                }
-            }
-            return true;
-        }
-
-        private int indent(Node n) {
-            if (n instanceof RootNode) {
-                List<CallNode> inlinedParents = ((RootNode) n).getParentInlinedCalls();
-                CallNode inlinedParent = inlinedParents.isEmpty() ? null : inlinedParents.get(0);
-                if (inlinedParent != null) {
-                    return indent(inlinedParent) + 1;
-                }
-                return 0;
-            } else {
-                return indent(n.getParent());
-            }
-        }
-    }
-
     public InstalledCode compileMethodHelper(StructuredGraph graph, Assumptions assumptions, String name, SpeculationLog speculationLog) {
         try (Scope s = Debug.scope("TruffleFinal")) {
             Debug.dump(graph, "After TruffleTier");
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Fri Feb 21 02:25:12 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Fri Feb 21 02:29:16 2014 +0100
@@ -86,6 +86,8 @@
     @Option(help = "")
     public static final OptionValue<Boolean> TraceTruffleCompilationDetails = new OptionValue<>(false);
     @Option(help = "")
+    public static final OptionValue<Boolean> TraceTruffleCompilationHistogram = new OptionValue<>(false);
+    @Option(help = "")
     public static final OptionValue<Boolean> TraceTruffleExpansion = new OptionValue<>(false);
     @Option(help = "")
     public static final OptionValue<Boolean> TraceTruffleExpansionSource = new OptionValue<>(false);