changeset 12712:882a0aadfed6

Merge.
author Christian Humer <christian.humer@gmail.com>
date Thu, 07 Nov 2013 20:55:13 +0100
parents fb4658e16b5d (diff) 3b2b8a71d10d (current diff)
children 8716b7ceef94 71991b7a0f14
files
diffstat 7 files changed, 528 insertions(+), 344 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationPolicy.java	Thu Nov 07 18:36:33 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationPolicy.java	Thu Nov 07 20:55:13 2013 +0100
@@ -22,96 +22,8 @@
  */
 package com.oracle.graal.truffle;
 
-import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
-
-public class CompilationPolicy {
-
-    private int invokeCounter;
-    private int originalInvokeCounter;
-    private int loopAndInvokeCounter;
-    private long prevTimestamp;
-
-    private final int compilationThreshold;
-    private final String name;
-
-    public CompilationPolicy(final int compilationThreshold, final int initialInvokeCounter, final String name) {
-        this.invokeCounter = initialInvokeCounter;
-        this.loopAndInvokeCounter = compilationThreshold;
-        this.originalInvokeCounter = compilationThreshold;
-        this.prevTimestamp = System.nanoTime();
-
-        this.compilationThreshold = compilationThreshold;
-        this.name = name;
-    }
-
-    public String getName() {
-        return this.name;
-    }
-
-    public int getInvokeCounter() {
-        return invokeCounter;
-    }
-
-    public int getOriginalInvokeCounter() {
-        return originalInvokeCounter;
-    }
-
-    public int getLoopAndInvokeCounter() {
-        return loopAndInvokeCounter;
-    }
-
-    public void compilationInvalidated() {
-        int invalidationReprofileCount = TruffleInvalidationReprofileCount.getValue();
-        invokeCounter = invalidationReprofileCount;
-        if (TruffleFunctionInlining.getValue()) {
-            originalInvokeCounter += invalidationReprofileCount;
-        }
-    }
+public interface CompilationPolicy {
 
-    public void countInterpreterCall() {
-        invokeCounter--;
-        loopAndInvokeCounter--;
-    }
-
-    public void inlined(int minInvokesAfterInlining) {
-        invokeCounter = minInvokesAfterInlining;
-        int inliningReprofileCount = TruffleInliningReprofileCount.getValue();
-        loopAndInvokeCounter = inliningReprofileCount;
-        originalInvokeCounter = inliningReprofileCount;
-    }
-
-    public void reportLoopCount(int count) {
-        loopAndInvokeCounter = Math.max(0, loopAndInvokeCounter - count);
-    }
-
-    public void nodeReplaced() {
-        // delay compilation until tree is deemed stable enough
-        int replaceBackoff = TruffleReplaceReprofileCount.getValue();
-        if (loopAndInvokeCounter < replaceBackoff) {
-            loopAndInvokeCounter = replaceBackoff;
-        }
-    }
+    boolean shouldCompile(CompilationProfile profile);
 
-    public boolean compileOrInline() {
-        if (invokeCounter <= 0 && loopAndInvokeCounter <= 0) {
-            if (TruffleUseTimeForCompilationDecision.getValue()) {
-                long timestamp = System.nanoTime();
-                long timespan = (timestamp - prevTimestamp);
-                if (timespan < (TruffleCompilationDecisionTime.getValue())) {
-                    return true;
-                }
-                this.loopAndInvokeCounter = compilationThreshold;
-                this.originalInvokeCounter = compilationThreshold;
-                this.prevTimestamp = timestamp;
-                if (TruffleCompilationDecisionTimePrintFail.getValue()) {
-                    // Checkstyle: stop
-                    System.out.println(name + ": timespan  " + (timespan / 1000000) + " ms  larger than threshold");
-                    // Checkstyle: resume
-                }
-            } else {
-                return true;
-            }
-        }
-        return false;
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.java	Thu Nov 07 20:55:13 2013 +0100
@@ -0,0 +1,125 @@
+/*
+ * 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.*;
+
+public class CompilationProfile {
+
+    private static final int MIN_INVOKES_AFTER_INLINING = 2;
+
+    private int invokeCounter;
+    private int originalInvokeCounter;
+    private int loopAndInvokeCounter;
+    /**
+     * Number of times an installed code for this tree was invalidated.
+     */
+    private int invalidationCount;
+
+    /**
+     * Number of times a node was replaced in this tree.
+     */
+    private int nodeReplaceCount;
+
+    private long previousTimestamp;
+
+    private final int compilationThreshold;
+    private final String name;
+
+    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.compilationThreshold = compilationThreshold;
+        this.name = name;
+    }
+
+    public long getPreviousTimestamp() {
+        return previousTimestamp;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public int getInvalidationCount() {
+        return invalidationCount;
+    }
+
+    public int getNodeReplaceCount() {
+        return nodeReplaceCount;
+    }
+
+    public int getInvokeCounter() {
+        return invokeCounter;
+    }
+
+    public int getOriginalInvokeCounter() {
+        return originalInvokeCounter;
+    }
+
+    public int getLoopAndInvokeCounter() {
+        return loopAndInvokeCounter;
+    }
+
+    void reportTiminingFailed(long timestamp) {
+        this.loopAndInvokeCounter = compilationThreshold;
+        this.originalInvokeCounter = compilationThreshold;
+        this.previousTimestamp = timestamp;
+    }
+
+    void reportInvalidated() {
+        invalidationCount++;
+        int invalidationReprofileCount = TruffleInvalidationReprofileCount.getValue();
+        invokeCounter = invalidationReprofileCount;
+        originalInvokeCounter += invalidationReprofileCount;
+    }
+
+    void reportInterpreterCall() {
+        invokeCounter--;
+        loopAndInvokeCounter--;
+    }
+
+    void reportInliningPerformed() {
+        invokeCounter = MIN_INVOKES_AFTER_INLINING;
+        int inliningReprofileCount = TruffleInliningReprofileCount.getValue();
+        loopAndInvokeCounter = inliningReprofileCount;
+        originalInvokeCounter = inliningReprofileCount;
+    }
+
+    void reportLoopCount(int count) {
+        loopAndInvokeCounter = Math.max(0, loopAndInvokeCounter - count);
+    }
+
+    void reportNodeReplaced() {
+        nodeReplaceCount++;
+        // delay compilation until tree is deemed stable enough
+        int replaceBackoff = TruffleReplaceReprofileCount.getValue();
+        if (loopAndInvokeCounter < replaceBackoff) {
+            loopAndInvokeCounter = replaceBackoff;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultCompilationPolicy.java	Thu Nov 07 20:55:13 2013 +0100
@@ -0,0 +1,31 @@
+/*
+ * 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 class DefaultCompilationPolicy implements CompilationPolicy {
+
+    public boolean shouldCompile(CompilationProfile profile) {
+        return profile.getInvokeCounter() <= 0 && profile.getLoopAndInvokeCounter() <= 0;
+    }
+
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Thu Nov 07 18:36:33 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Thu Nov 07 20:55:13 2013 +0100
@@ -41,36 +41,35 @@
 public final class OptimizedCallTarget extends DefaultCallTarget implements FrameFactory, LoopCountReceiver, ReplaceObserver {
 
     private static final PrintStream OUT = TTY.out().out();
-    private static final int MIN_INVOKES_AFTER_INLINING = 2;
+
+    private InstalledCode installedCode;
+    private Future<InstalledCode> installedCodeTask;
+    private final TruffleCompiler compiler;
+    private final CompilationProfile compilationProfile;
+    private final CompilationPolicy compilationPolicy;
+    private final TruffleInlining inlining;
+    private boolean compilationEnabled;
+    private int callCount;
 
     protected OptimizedCallTarget(RootNode rootNode, FrameDescriptor descriptor, TruffleCompiler compiler, int invokeCounter, int compilationThreshold) {
         super(rootNode, descriptor);
         this.compiler = compiler;
-        this.compilationPolicy = new CompilationPolicy(compilationThreshold, invokeCounter, rootNode.toString());
+        this.compilationProfile = new CompilationProfile(compilationThreshold, invokeCounter, rootNode.toString());
+        this.inlining = new TruffleInliningImpl();
         this.rootNode.setCallTarget(this);
 
+        if (TruffleUseTimeForCompilationDecision.getValue()) {
+            compilationPolicy = new TimedCompilationPolicy();
+        } else {
+            compilationPolicy = new DefaultCompilationPolicy();
+        }
+        this.compilationEnabled = true;
+
         if (TruffleCallTargetProfiling.getValue()) {
             registerCallTarget(this);
         }
     }
 
-    private InstalledCode installedCode;
-    private Future<InstalledCode> installedCodeTask;
-    private final TruffleCompiler compiler;
-    private final CompilationPolicy compilationPolicy;
-    private boolean disableCompilation;
-    private int callCount;
-
-    /**
-     * Number of times an installed code for this tree was invalidated.
-     */
-    private int invalidationCount;
-
-    /**
-     * Number of times a node was replaced in this tree.
-     */
-    private int nodeReplaceCount;
-
     @Override
     public Object call(PackedFrame caller, Arguments args) {
         return callHelper(caller, args);
@@ -99,6 +98,10 @@
         }
     }
 
+    public CompilationProfile getCompilationProfile() {
+        return compilationProfile;
+    }
+
     private Object compiledCodeInvalidated(PackedFrame caller, Arguments args) {
         invalidate();
         return call(caller, args);
@@ -109,10 +112,10 @@
         if (m != null) {
             CompilerAsserts.neverPartOfCompilation();
             installedCode = null;
-            invalidationCount++;
-            compilationPolicy.compilationInvalidated();
+            compilationProfile.reportInvalidated();
             if (TraceTruffleCompilation.getValue()) {
-                OUT.printf("[truffle] invalidated %-48s |Inv# %d                                     |Replace# %d\n", rootNode, invalidationCount, nodeReplaceCount);
+                OUT.printf("[truffle] invalidated %-48s |Inv# %d                                     |Replace# %d\n", rootNode, compilationProfile.getInvalidationCount(),
+                                compilationProfile.getNodeReplaceCount());
             }
         }
 
@@ -120,50 +123,63 @@
         if (task != null) {
             task.cancel(true);
             this.installedCodeTask = null;
-            compilationPolicy.compilationInvalidated();
+            compilationProfile.reportInvalidated();
         }
     }
 
     private Object interpreterCall(PackedFrame caller, Arguments args) {
         CompilerAsserts.neverPartOfCompilation();
-        compilationPolicy.countInterpreterCall();
-        if (disableCompilation || !compilationPolicy.compileOrInline()) {
-            return executeHelper(caller, args);
-        } else {
-            return compileOrInline(caller, args);
+        compilationProfile.reportInterpreterCall();
+        if (compilationEnabled && shouldCompile()) {
+            if (isCompiling()) {
+                return waitForCompilation(caller, args);
+            }
+            boolean inlined = shouldInline() && inline();
+            if (!inlined) {
+                compile();
+            }
+        }
+        return executeHelper(caller, args);
+    }
+
+    private boolean shouldCompile() {
+        return compilationPolicy.shouldCompile(compilationProfile);
+    }
+
+    private static boolean shouldInline() {
+        return TruffleFunctionInlining.getValue();
+    }
+
+    private boolean isCompiling() {
+        if (installedCodeTask != null) {
+            if (installedCodeTask.isCancelled()) {
+                installedCodeTask = null;
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public void compile() {
+        this.installedCodeTask = compiler.compile(this);
+        if (!TruffleBackgroundCompilation.getValue()) {
+            installedCode = receiveInstalledCode();
         }
     }
 
-    private Object compileOrInline(PackedFrame caller, Arguments args) {
-        if (installedCodeTask != null) {
-            // There is already a compilation running.
-            if (installedCodeTask.isCancelled()) {
-                installedCodeTask = null;
-            } else {
-                if (installedCodeTask.isDone()) {
-                    receiveInstalledCode();
-                }
-                return executeHelper(caller, args);
-            }
+    private Object waitForCompilation(PackedFrame caller, Arguments args) {
+        if (installedCodeTask.isDone()) {
+            installedCode = receiveInstalledCode();
         }
-
-        if (TruffleFunctionInlining.getValue() && inline()) {
-            compilationPolicy.inlined(MIN_INVOKES_AFTER_INLINING);
-            return call(caller, args);
-        } else {
-            compile();
-            return executeHelper(caller, args);
-        }
+        return executeHelper(caller, args);
     }
 
-    private void receiveInstalledCode() {
+    private InstalledCode receiveInstalledCode() {
         try {
-            this.installedCode = installedCodeTask.get();
-            if (TruffleCallTargetProfiling.getValue()) {
-                resetProfiling();
-            }
+            return installedCodeTask.get();
         } catch (InterruptedException | ExecutionException e) {
-            disableCompilation = true;
+            compilationEnabled = false;
             OUT.printf("[truffle] opt failed %-48s  %s\n", rootNode, e.getMessage());
             if (e.getCause() instanceof BailoutException) {
                 // Bailout => move on.
@@ -175,21 +191,21 @@
                     System.exit(-1);
                 }
             }
+            return null;
         }
-        installedCodeTask = null;
     }
 
+    /**
+     * Forces inlining whether or not function inlining is enabled.
+     * 
+     * @return true if an inlining was performed
+     */
     public boolean inline() {
-        CompilerAsserts.neverPartOfCompilation();
-        return new InliningHelper(this).inline();
-    }
-
-    public void compile() {
-        CompilerAsserts.neverPartOfCompilation();
-        this.installedCodeTask = compiler.compile(this);
-        if (!TruffleBackgroundCompilation.getValue()) {
-            receiveInstalledCode();
+        boolean result = inlining.performInlining(this);
+        if (result) {
+            compilationProfile.reportInliningPerformed();
         }
+        return result;
     }
 
     public Object executeHelper(PackedFrame caller, Arguments args) {
@@ -208,198 +224,13 @@
 
     @Override
     public void reportLoopCount(int count) {
-        compilationPolicy.reportLoopCount(count);
+        compilationProfile.reportLoopCount(count);
     }
 
     @Override
     public void nodeReplaced() {
-        nodeReplaceCount++;
+        compilationProfile.reportNodeReplaced();
         invalidate();
-        compilationPolicy.nodeReplaced();
-    }
-
-    private static class InliningHelper {
-
-        private final OptimizedCallTarget target;
-
-        public InliningHelper(OptimizedCallTarget target) {
-            this.target = target;
-        }
-
-        public boolean inline() {
-            final InliningPolicy policy = new InliningPolicy(target);
-            if (!policy.continueInlining()) {
-                if (TraceTruffleInliningDetails.getValue()) {
-                    List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(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());
-                        policy.sortByRelevance(inlinableCallSites);
-                        printCallSiteInfo(policy, inlinableCallSites, "");
-                    }
-                }
-                return false;
-            }
-
-            List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(target);
-            if (inlinableCallSites.isEmpty()) {
-                return false;
-            }
-
-            policy.sortByRelevance(inlinableCallSites);
-
-            boolean inlined = false;
-            for (InlinableCallSiteInfo inlinableCallSite : inlinableCallSites) {
-                if (!policy.isWorthInlining(inlinableCallSite)) {
-                    break;
-                }
-                if (inlinableCallSite.getCallSite().inline(target)) {
-                    if (TraceTruffleInlining.getValue()) {
-                        printCallSiteInfo(policy, inlinableCallSite, "inlined");
-                    }
-                    inlined = true;
-                    break;
-                }
-            }
-
-            if (inlined) {
-                for (InlinableCallSiteInfo callSite : inlinableCallSites) {
-                    callSite.getCallSite().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);
-            OUT.printf("[truffle] %-9s %-50s |Nodes %6s |Calls %6s %7.3f |%s\n", msg, callSite.getCallSite(), nodes, calls, policy.metric(callSite), callSite.getCallSite().getCallTarget());
-        }
-
-        private static final class InliningPolicy {
-
-            private final int callerNodeCount;
-            private final int callerInvocationCount;
-
-            public InliningPolicy(OptimizedCallTarget caller) {
-                this.callerNodeCount = NodeUtil.countNodes(caller.getRootNode());
-                this.callerInvocationCount = caller.compilationPolicy.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;
-            }
-
-            public void sortByRelevance(List<InlinableCallSiteInfo> inlinableCallSites) {
-                Collections.sort(inlinableCallSites, new Comparator<InlinableCallSiteInfo>() {
-
-                    @Override
-                    public int compare(InlinableCallSiteInfo cs1, InlinableCallSiteInfo cs2) {
-                        int result = (isWorthInlining(cs2) ? 1 : 0) - (isWorthInlining(cs1) ? 1 : 0);
-                        if (result == 0) {
-                            return Double.compare(metric(cs2), metric(cs1));
-                        }
-                        return result;
-                    }
-                });
-            }
-        }
-
-        private static final class InlinableCallSiteInfo {
-
-            private final InlinableCallSite callSite;
-            private final int callCount;
-            private final int nodeCount;
-            private final int recursiveDepth;
-
-            public InlinableCallSiteInfo(InlinableCallSite callSite) {
-                this.callSite = callSite;
-                this.callCount = callSite.getCallCount();
-                this.nodeCount = NodeUtil.countNodes(callSite.getInlineTree());
-                this.recursiveDepth = calculateRecursiveDepth();
-            }
-
-            public int getRecursiveDepth() {
-                return recursiveDepth;
-            }
-
-            private int calculateRecursiveDepth() {
-                int depth = 0;
-                Node parent = ((Node) callSite).getParent();
-                while (!(parent instanceof RootNode)) {
-                    assert parent != null;
-                    if (parent instanceof InlinedCallSite && ((InlinedCallSite) parent).getCallTarget() == callSite.getCallTarget()) {
-                        depth++;
-                    }
-                    parent = parent.getParent();
-                }
-                if (((RootNode) parent).getCallTarget() == callSite.getCallTarget()) {
-                    depth++;
-                }
-                return depth;
-            }
-
-            public InlinableCallSite getCallSite() {
-                return callSite;
-            }
-
-            public int getCallCount() {
-                return callCount;
-            }
-
-            public int getInlineNodeCount() {
-                return nodeCount;
-            }
-        }
-
-        private static List<InlinableCallSiteInfo> getInlinableCallSites(final DefaultCallTarget target) {
-            final ArrayList<InlinableCallSiteInfo> inlinableCallSites = new ArrayList<>();
-            target.getRootNode().accept(new NodeVisitor() {
-
-                @Override
-                public boolean visit(Node node) {
-                    if (node instanceof InlinableCallSite) {
-                        inlinableCallSites.add(new InlinableCallSiteInfo((InlinableCallSite) node));
-                    }
-                    return true;
-                }
-            });
-            return inlinableCallSites;
-        }
-    }
-
-    private static void resetProfiling() {
-        for (OptimizedCallTarget callTarget : OptimizedCallTarget.callTargets.keySet()) {
-            callTarget.callCount = 0;
-        }
     }
 
     private static void printProfiling() {
@@ -425,19 +256,19 @@
                 continue;
             }
 
-            int notInlinedCallSiteCount = InliningHelper.getInlinableCallSites(callTarget).size();
+            int notInlinedCallSiteCount = TruffleInliningImpl.getInlinableCallSites(callTarget).size();
             int nodeCount = NodeUtil.countNodes(callTarget.rootNode);
             int inlinedCallSiteCount = NodeUtil.countNodes(callTarget.rootNode, InlinedCallSite.class);
             String comment = callTarget.installedCode == null ? " int" : "";
-            comment += callTarget.disableCompilation ? " fail" : "";
+            comment += callTarget.compilationEnabled ? "" : " fail";
             OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d%s\n", callTarget.getRootNode(), callTarget.callCount, inlinedCallSiteCount, notInlinedCallSiteCount, nodeCount,
-                            callTarget.invalidationCount, comment);
+                            callTarget.getCompilationProfile().getInvalidationCount(), comment);
 
             totalCallCount += callTarget.callCount;
             totalInlinedCallSiteCount += inlinedCallSiteCount;
             totalNotInlinedCallSiteCount += notInlinedCallSiteCount;
             totalNodeCount += nodeCount;
-            totalInvalidationCount += callTarget.invalidationCount;
+            totalInvalidationCount += callTarget.getCompilationProfile().getInvalidationCount();
         }
         OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d\n", "Total", totalCallCount, totalInlinedCallSiteCount, totalNotInlinedCallSiteCount, totalNodeCount, totalInvalidationCount);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TimedCompilationPolicy.java	Thu Nov 07 20:55:13 2013 +0100
@@ -0,0 +1,50 @@
+/*
+ * 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.*;
+
+public class TimedCompilationPolicy extends DefaultCompilationPolicy {
+
+    @Override
+    public boolean shouldCompile(CompilationProfile profile) {
+        if (super.shouldCompile(profile)) {
+            long timestamp = System.nanoTime();
+            long prevTimestamp = profile.getPreviousTimestamp();
+            long timespan = (timestamp - prevTimestamp);
+            if (timespan < (TruffleCompilationDecisionTime.getValue())) {
+                return true;
+            }
+            // TODO shouldCompile should not modify the compilation profile
+            // maybe introduce another method?
+            profile.reportTiminingFailed(timestamp);
+            if (TruffleCompilationDecisionTimePrintFail.getValue()) {
+                // Checkstyle: stop
+                System.out.println(profile.getName() + ": timespan  " + (timespan / 1000000) + " ms  larger than threshold");
+                // Checkstyle: resume
+            }
+        }
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInlining.java	Thu Nov 07 20:55:13 2013 +0100
@@ -0,0 +1,28 @@
+/*
+ * 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 {
+
+    boolean performInlining(OptimizedCallTarget callTarget);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java	Thu Nov 07 20:55:13 2013 +0100
@@ -0,0 +1,207 @@
+/*
+ * 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.impl.*;
+import com.oracle.truffle.api.nodes.*;
+
+class TruffleInliningImpl implements TruffleInlining {
+
+    private static final PrintStream OUT = TTY.out().out();
+
+    @Override
+    public boolean performInlining(OptimizedCallTarget target) {
+        final InliningPolicy policy = new InliningPolicy(target);
+        if (!policy.continueInlining()) {
+            if (TraceTruffleInliningDetails.getValue()) {
+                List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(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());
+                    policy.sortByRelevance(inlinableCallSites);
+                    printCallSiteInfo(policy, inlinableCallSites, "");
+                }
+            }
+            return false;
+        }
+
+        List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(target);
+        if (inlinableCallSites.isEmpty()) {
+            return false;
+        }
+
+        policy.sortByRelevance(inlinableCallSites);
+
+        boolean inlined = false;
+        for (InlinableCallSiteInfo inlinableCallSite : inlinableCallSites) {
+            if (!policy.isWorthInlining(inlinableCallSite)) {
+                break;
+            }
+            if (inlinableCallSite.getCallSite().inline(target)) {
+                if (TraceTruffleInlining.getValue()) {
+                    printCallSiteInfo(policy, inlinableCallSite, "inlined");
+                }
+                inlined = true;
+                break;
+            }
+        }
+
+        if (inlined) {
+            for (InlinableCallSiteInfo callSite : inlinableCallSites) {
+                callSite.getCallSite().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);
+        OUT.printf("[truffle] %-9s %-50s |Nodes %6s |Calls %6s %7.3f |%s\n", msg, callSite.getCallSite(), nodes, calls, policy.metric(callSite), callSite.getCallSite().getCallTarget());
+    }
+
+    private static final class InliningPolicy {
+
+        private final int callerNodeCount;
+        private final int callerInvocationCount;
+
+        public InliningPolicy(OptimizedCallTarget caller) {
+            this.callerNodeCount = NodeUtil.countNodes(caller.getRootNode());
+            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;
+        }
+
+        public void sortByRelevance(List<InlinableCallSiteInfo> inlinableCallSites) {
+            Collections.sort(inlinableCallSites, new Comparator<InlinableCallSiteInfo>() {
+
+                @Override
+                public int compare(InlinableCallSiteInfo cs1, InlinableCallSiteInfo cs2) {
+                    int result = (isWorthInlining(cs2) ? 1 : 0) - (isWorthInlining(cs1) ? 1 : 0);
+                    if (result == 0) {
+                        return Double.compare(metric(cs2), metric(cs1));
+                    }
+                    return result;
+                }
+            });
+        }
+    }
+
+    private static final class InlinableCallSiteInfo {
+
+        private final InlinableCallSite callSite;
+        private final int callCount;
+        private final int nodeCount;
+        private final int recursiveDepth;
+
+        public InlinableCallSiteInfo(InlinableCallSite callSite) {
+            this.callSite = callSite;
+            this.callCount = callSite.getCallCount();
+            this.nodeCount = NodeUtil.countNodes(callSite.getInlineTree());
+            this.recursiveDepth = calculateRecursiveDepth();
+        }
+
+        public int getRecursiveDepth() {
+            return recursiveDepth;
+        }
+
+        private int calculateRecursiveDepth() {
+            int depth = 0;
+            Node parent = ((Node) callSite).getParent();
+            while (!(parent instanceof RootNode)) {
+                assert parent != null;
+                if (parent instanceof InlinedCallSite && ((InlinedCallSite) parent).getCallTarget() == callSite.getCallTarget()) {
+                    depth++;
+                }
+                parent = parent.getParent();
+            }
+            if (((RootNode) parent).getCallTarget() == callSite.getCallTarget()) {
+                depth++;
+            }
+            return depth;
+        }
+
+        public InlinableCallSite getCallSite() {
+            return callSite;
+        }
+
+        public int getCallCount() {
+            return callCount;
+        }
+
+        public int getInlineNodeCount() {
+            return nodeCount;
+        }
+    }
+
+    static List<InlinableCallSiteInfo> getInlinableCallSites(final DefaultCallTarget target) {
+        final ArrayList<InlinableCallSiteInfo> inlinableCallSites = new ArrayList<>();
+        target.getRootNode().accept(new NodeVisitor() {
+
+            @Override
+            public boolean visit(Node node) {
+                if (node instanceof InlinableCallSite) {
+                    inlinableCallSites.add(new InlinableCallSiteInfo((InlinableCallSite) node));
+                }
+                return true;
+            }
+        });
+        return inlinableCallSites;
+    }
+}