changeset 14866:a0185cd77565

Truffle: refactor ompilation related classes for SubstrateVM
author Erik Eckstein <erik.eckstein@oracle.com>
date Fri, 28 Mar 2014 08:38:49 +0100
parents ae7cbf13e765
children 8c420f405328
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/GraalTruffleRuntime.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetImpl.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluatorCanonicalizer.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCacheImpl.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleReplacements.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/OptimizedCallTargetImplSubstitutions.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/OptimizedCallTargetSubstitutions.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/UnexpectedResultExceptionSubstitutions.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/UnexpectedResultException.java
diffstat 16 files changed, 589 insertions(+), 518 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Fri Mar 28 08:38:49 2014 +0100
@@ -85,7 +85,7 @@
     }
 
     protected StructuredGraph partialEval(RootNode root, Arguments arguments, final Assumptions assumptions, final boolean canonicalizeReads) {
-        final OptimizedCallTarget compilable = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(root);
+        final OptimizedCallTargetImpl compilable = (OptimizedCallTargetImpl) Truffle.getRuntime().createCallTarget(root);
 
         // Executed AST so that all classes are loaded and initialized.
         compilable.call(null, arguments);
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.java	Fri Mar 28 08:38:49 2014 +0100
@@ -132,7 +132,7 @@
         ensureProfiling(reprofile, reprofile);
     }
 
-    void reportInterpreterCall() {
+    public void reportInterpreterCall() {
         callCount++;
         callAndLoopCount++;
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Fri Mar 28 08:38:49 2014 +0100
@@ -77,7 +77,7 @@
         if (truffleCompiler == null) {
             truffleCompiler = new TruffleCompilerImpl();
         }
-        return new OptimizedCallTarget(rootNode, truffleCompiler, TruffleMinInvokeThreshold.getValue(), TruffleCompilationThreshold.getValue(), acceptForCompilation(rootNode));
+        return new OptimizedCallTargetImpl(rootNode, truffleCompiler, TruffleMinInvokeThreshold.getValue(), TruffleCompilationThreshold.getValue(), acceptForCompilation(rootNode));
     }
 
     public CallNode createCallNode(CallTarget target) {
@@ -90,7 +90,7 @@
 
     @Override
     public VirtualFrame createVirtualFrame(PackedFrame caller, Arguments arguments, FrameDescriptor frameDescriptor) {
-        return OptimizedCallTarget.createFrame(frameDescriptor, caller, arguments);
+        return OptimizedCallTargetImpl.createFrame(frameDescriptor, caller, arguments);
     }
 
     @Override
@@ -173,7 +173,7 @@
     private static Method getCallMethod() {
         Method method;
         try {
-            method = OptimizedCallTarget.class.getDeclaredMethod("call", new Class[]{PackedFrame.class, Arguments.class});
+            method = OptimizedCallTargetImpl.class.getDeclaredMethod("call", new Class[]{PackedFrame.class, Arguments.class});
         } catch (NoSuchMethodException | SecurityException e) {
             throw GraalInternalError.shouldNotReachHere();
         }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java	Fri Mar 28 08:38:49 2014 +0100
@@ -33,7 +33,7 @@
 /**
  * Call target that is optimized by Graal upon surpassing a specific invocation threshold.
  */
-abstract class OptimizedCallNode extends DefaultCallNode {
+public abstract class OptimizedCallNode extends DefaultCallNode {
 
     protected int callCount;
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Fri Mar 28 08:38:49 2014 +0100
@@ -26,7 +26,6 @@
 
 import java.io.*;
 import java.util.*;
-import java.util.concurrent.*;
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.debug.*;
@@ -39,34 +38,27 @@
 /**
  * Call target that is optimized by Graal upon surpassing a specific invocation threshold.
  */
-public final class OptimizedCallTarget extends DefaultCallTarget implements LoopCountReceiver, ReplaceObserver {
+public abstract class OptimizedCallTarget extends DefaultCallTarget implements LoopCountReceiver, ReplaceObserver {
 
-    private static final PrintStream OUT = TTY.out().out();
+    protected static final PrintStream OUT = TTY.out().out();
 
-    private InstalledCode installedCode;
-    private Future<InstalledCode> installedCodeTask;
-    private boolean compilationEnabled;
+    protected InstalledCode installedCode;
+    protected boolean compilationEnabled;
     private boolean inlined;
-    private int callCount;
+    protected int callCount;
 
-    private final TruffleCompiler compiler;
-    private final CompilationProfile compilationProfile;
-    private final CompilationPolicy compilationPolicy;
-    private final SpeculationLog speculationLog = new SpeculationLog();
+    protected final CompilationProfile compilationProfile;
+    protected final CompilationPolicy compilationPolicy;
+    private SpeculationLog speculationLog = new SpeculationLog();
     private OptimizedCallTarget splitSource;
 
-    OptimizedCallTarget(RootNode rootNode, TruffleCompiler compiler, int invokeCounter, int compilationThreshold, boolean compilationEnabled) {
+    public OptimizedCallTarget(RootNode rootNode, int invokeCounter, int compilationThreshold, boolean compilationEnabled, CompilationPolicy compilationPolicy) {
         super(rootNode);
-        this.compiler = compiler;
-        this.compilationProfile = new CompilationProfile(compilationThreshold, invokeCounter, rootNode.toString());
 
-        if (TruffleUseTimeForCompilationDecision.getValue()) {
-            compilationPolicy = new TimedCompilationPolicy();
-        } else {
-            compilationPolicy = new DefaultCompilationPolicy();
-        }
         this.compilationEnabled = compilationEnabled;
+        this.compilationPolicy = compilationPolicy;
 
+        this.compilationProfile = new CompilationProfile(compilationThreshold, invokeCounter, rootNode.toString());
         if (TruffleCallTargetProfiling.getValue()) {
             registerCallTarget(this);
         }
@@ -92,15 +84,12 @@
         return superString;
     }
 
-    public boolean isOptimized() {
-        return installedCode != null || installedCodeTask != null;
+    public CompilationProfile getCompilationProfile() {
+        return compilationProfile;
     }
 
-    @CompilerDirectives.SlowPath
     @Override
-    public Object call(PackedFrame caller, Arguments args) {
-        return callHelper(caller, args);
-    }
+    public abstract Object call(PackedFrame caller, Arguments args);
 
     public Object callInlined(PackedFrame caller, Arguments arguments) {
         if (CompilerDirectives.inInterpreter()) {
@@ -109,79 +98,6 @@
         return executeHelper(caller, arguments);
     }
 
-    private Object callHelper(PackedFrame caller, Arguments args) {
-        if (installedCode != null && installedCode.isValid()) {
-            reinstallCallMethodShortcut();
-        }
-        if (TruffleCallTargetProfiling.getValue()) {
-            callCount++;
-        }
-        if (CompilerDirectives.injectBranchProbability(CompilerDirectives.FASTPATH_PROBABILITY, installedCode != null)) {
-            try {
-                return installedCode.execute(this, caller, args);
-            } catch (InvalidInstalledCodeException ex) {
-                return compiledCodeInvalidated(caller, args);
-            }
-        } else {
-            return interpreterCall(caller, args);
-        }
-    }
-
-    private static void reinstallCallMethodShortcut() {
-        if (TraceTruffleCompilation.getValue()) {
-            OUT.println("[truffle] reinstall OptimizedCallTarget.call code with frame prolog shortcut.");
-        }
-        GraalTruffleRuntime.installOptimizedCallTargetCallMethod();
-    }
-
-    public CompilationProfile getCompilationProfile() {
-        return compilationProfile;
-    }
-
-    private Object compiledCodeInvalidated(PackedFrame caller, Arguments args) {
-        invalidate(null, null, "Compiled code invalidated");
-        return call(caller, args);
-    }
-
-    private void invalidate(Node oldNode, Node newNode, CharSequence reason) {
-        InstalledCode m = this.installedCode;
-        if (m != null) {
-            CompilerAsserts.neverPartOfCompilation();
-            installedCode = null;
-            compilationProfile.reportInvalidated();
-            logOptimizedInvalidated(this, oldNode, newNode, reason);
-        }
-        cancelInstalledTask(oldNode, newNode, reason);
-    }
-
-    private void cancelInstalledTask(Node oldNode, Node newNode, CharSequence reason) {
-        Future<InstalledCode> task = this.installedCodeTask;
-        if (task != null) {
-            task.cancel(true);
-            this.installedCodeTask = null;
-            logOptimizingUnqueued(this, oldNode, newNode, reason);
-            compilationProfile.reportInvalidated();
-        }
-    }
-
-    private Object interpreterCall(PackedFrame caller, Arguments args) {
-        CompilerAsserts.neverPartOfCompilation();
-        compilationProfile.reportInterpreterCall();
-
-        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);
-    }
-
     public void performInlining() {
         if (!TruffleCompilerOptions.TruffleFunctionInlining.getValue()) {
             return;
@@ -234,61 +150,22 @@
         });
     }
 
-    private boolean isCompiling() {
-        Future<InstalledCode> codeTask = this.installedCodeTask;
-        if (codeTask != null) {
-            if (codeTask.isCancelled()) {
-                installedCodeTask = null;
-                return false;
-            }
-            return true;
-        }
-        return false;
+    protected boolean shouldCompile() {
+        return compilationPolicy.shouldCompile(compilationProfile);
     }
 
-    public InstalledCode compile() {
-        if (isCompiling()) {
-            if (installedCodeTask.isDone()) {
-                return receiveInstalledCode();
-            }
-            return null;
-        } else {
-            performInlining();
-            logOptimizingQueued(this);
-            this.installedCodeTask = compiler.compile(this);
-            if (!TruffleBackgroundCompilation.getValue()) {
-                return receiveInstalledCode();
-            }
-        }
-        return null;
+    protected static boolean shouldInline() {
+        return TruffleFunctionInlining.getValue();
     }
 
-    private InstalledCode receiveInstalledCode() {
-        try {
-            return installedCodeTask.get();
-        } catch (InterruptedException | ExecutionException e) {
-            compilationEnabled = false;
-            logOptimizingFailed(this, e.getMessage());
-            if (e.getCause() instanceof BailoutException) {
-                // Bailout => move on.
-            } else {
-                if (TraceTruffleCompilationExceptions.getValue()) {
-                    e.printStackTrace(OUT);
-                }
-                if (TruffleCompilationExceptionsAreFatal.getValue()) {
-                    System.exit(-1);
-                }
-            }
-            return null;
-        }
-    }
+    protected abstract void invalidate(Node oldNode, Node newNode, CharSequence reason);
 
     public Object executeHelper(PackedFrame caller, Arguments args) {
         VirtualFrame frame = createFrame(getRootNode().getFrameDescriptor(), caller, args);
         return getRootNode().execute(frame);
     }
 
-    protected static FrameWithoutBoxing createFrame(FrameDescriptor descriptor, PackedFrame caller, Arguments args) {
+    public static FrameWithoutBoxing createFrame(FrameDescriptor descriptor, PackedFrame caller, Arguments args) {
         return new FrameWithoutBoxing(descriptor, caller, args);
     }
 
@@ -393,13 +270,13 @@
         }
     }
 
-    private static void logOptimizingQueued(OptimizedCallTarget target) {
+    protected static void logOptimizingQueued(OptimizedCallTarget target) {
         if (TraceTruffleCompilationDetails.getValue()) {
             log(0, "opt queued", target.toString(), target.getDebugProperties());
         }
     }
 
-    private static void logOptimizingUnqueued(OptimizedCallTarget target, Node oldNode, Node newNode, CharSequence reason) {
+    protected static void logOptimizingUnqueued(OptimizedCallTarget target, Node oldNode, Node newNode, CharSequence reason) {
         if (TraceTruffleCompilationDetails.getValue()) {
             Map<String, Object> properties = new LinkedHashMap<>();
             addReplaceProperties(properties, oldNode, newNode);
@@ -422,7 +299,7 @@
         }
     }
 
-    private static void logOptimizedInvalidated(OptimizedCallTarget target, Node oldNode, Node newNode, CharSequence reason) {
+    protected static void logOptimizedInvalidated(OptimizedCallTarget target, Node oldNode, Node newNode, CharSequence reason) {
         if (TraceTruffleCompilation.getValue()) {
             Map<String, Object> properties = new LinkedHashMap<>();
             addReplaceProperties(properties, oldNode, newNode);
@@ -431,7 +308,7 @@
         }
     }
 
-    private static void logOptimizingFailed(OptimizedCallTarget callSite, CharSequence reason) {
+    protected static void logOptimizingFailed(OptimizedCallTarget callSite, CharSequence reason) {
         Map<String, Object> properties = new LinkedHashMap<>();
         properties.put("Reason", reason);
         log(0, "opt fail", callSite.toString(), properties);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetImpl.java	Fri Mar 28 08:38:49 2014 +0100
@@ -0,0 +1,176 @@
+/*
+ * 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.util.concurrent.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Call target for running truffle on a standard VM (and not in SubstrateVM).
+ */
+public final class OptimizedCallTargetImpl extends OptimizedCallTarget {
+
+    protected final TruffleCompiler compiler;
+    private Future<InstalledCode> installedCodeTask;
+
+    OptimizedCallTargetImpl(RootNode rootNode, TruffleCompiler compiler, int invokeCounter, int compilationThreshold, boolean compilationEnabled) {
+        super(rootNode, invokeCounter, compilationThreshold, compilationEnabled, TruffleUseTimeForCompilationDecision.getValue() ? new TimedCompilationPolicy() : new DefaultCompilationPolicy());
+        this.compiler = compiler;
+    }
+
+    public boolean isOptimized() {
+        return installedCode != null || installedCodeTask != null;
+    }
+
+    @CompilerDirectives.SlowPath
+    @Override
+    public Object call(PackedFrame caller, Arguments args) {
+        return callHelper(caller, args);
+    }
+
+    private Object callHelper(PackedFrame caller, Arguments args) {
+        if (installedCode != null && installedCode.isValid()) {
+            reinstallCallMethodShortcut();
+        }
+        if (TruffleCallTargetProfiling.getValue()) {
+            callCount++;
+        }
+        if (CompilerDirectives.injectBranchProbability(CompilerDirectives.FASTPATH_PROBABILITY, installedCode != null)) {
+            try {
+                return installedCode.execute(this, caller, args);
+            } catch (InvalidInstalledCodeException ex) {
+                return compiledCodeInvalidated(caller, args);
+            }
+        } else {
+            return interpreterCall(caller, args);
+        }
+    }
+
+    private static void reinstallCallMethodShortcut() {
+        if (TraceTruffleCompilation.getValue()) {
+            OUT.println("[truffle] reinstall OptimizedCallTarget.call code with frame prolog shortcut.");
+        }
+        GraalTruffleRuntime.installOptimizedCallTargetCallMethod();
+    }
+
+    private Object compiledCodeInvalidated(PackedFrame caller, Arguments args) {
+        invalidate(null, null, "Compiled code invalidated");
+        return call(caller, args);
+    }
+
+    @Override
+    protected void invalidate(Node oldNode, Node newNode, CharSequence reason) {
+        InstalledCode m = this.installedCode;
+        if (m != null) {
+            CompilerAsserts.neverPartOfCompilation();
+            installedCode = null;
+            compilationProfile.reportInvalidated();
+            logOptimizedInvalidated(this, oldNode, newNode, reason);
+        }
+        cancelInstalledTask(oldNode, newNode, reason);
+    }
+
+    private void cancelInstalledTask(Node oldNode, Node newNode, CharSequence reason) {
+        Future<InstalledCode> task = this.installedCodeTask;
+        if (task != null) {
+            task.cancel(true);
+            this.installedCodeTask = null;
+            logOptimizingUnqueued(this, oldNode, newNode, reason);
+            compilationProfile.reportInvalidated();
+        }
+    }
+
+    private Object interpreterCall(PackedFrame caller, Arguments args) {
+        CompilerAsserts.neverPartOfCompilation();
+        compilationProfile.reportInterpreterCall();
+
+        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 isCompiling() {
+        Future<InstalledCode> codeTask = this.installedCodeTask;
+        if (codeTask != null) {
+            if (codeTask.isCancelled()) {
+                installedCodeTask = null;
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public InstalledCode compile() {
+        if (isCompiling()) {
+            if (installedCodeTask.isDone()) {
+                return receiveInstalledCode();
+            }
+            return null;
+        } else {
+            performInlining();
+            logOptimizingQueued(this);
+            this.installedCodeTask = compiler.compile(this);
+            if (!TruffleBackgroundCompilation.getValue()) {
+                return receiveInstalledCode();
+            }
+            return null;
+        }
+    }
+
+    private InstalledCode receiveInstalledCode() {
+        try {
+            return installedCodeTask.get();
+        } catch (InterruptedException | ExecutionException e) {
+            compilationEnabled = false;
+            logOptimizingFailed(this, e.getMessage());
+            if (e.getCause() instanceof BailoutException) {
+                // Bailout => move on.
+            } else {
+                if (TraceTruffleCompilationExceptions.getValue()) {
+                    e.printStackTrace(OUT);
+                }
+                if (TruffleCompilationExceptionsAreFatal.getValue()) {
+                    System.exit(-1);
+                }
+            }
+            return null;
+        }
+    }
+
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri Mar 28 08:38:49 2014 +0100
@@ -37,7 +37,6 @@
 import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.Node;
 import com.oracle.graal.graph.spi.*;
-import com.oracle.graal.java.*;
 import com.oracle.graal.loop.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
@@ -57,7 +56,6 @@
 import com.oracle.graal.truffle.phases.*;
 import com.oracle.graal.virtual.phases.ea.*;
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 
 /**
@@ -66,25 +64,17 @@
 public class PartialEvaluator {
 
     private final Providers providers;
-    private final ResolvedJavaMethod executeHelperMethod;
     private final CanonicalizerPhase canonicalizer;
-    private final GraphBuilderConfiguration config;
     private Set<Constant> constantReceivers;
     private final TruffleCache truffleCache;
     private final ResolvedJavaType frameType;
 
-    public PartialEvaluator(Providers providers, TruffleCache truffleCache, GraphBuilderConfiguration config) {
+    public PartialEvaluator(Providers providers, TruffleCache truffleCache) {
         this.providers = providers;
         CustomCanonicalizer customCanonicalizer = new PartialEvaluatorCanonicalizer(providers.getMetaAccess(), providers.getConstantReflection());
         this.canonicalizer = new CanonicalizerPhase(!ImmutableCode.getValue(), customCanonicalizer);
-        this.config = config;
         this.truffleCache = truffleCache;
         this.frameType = providers.getMetaAccess().lookupJavaType(FrameWithoutBoxing.class);
-        try {
-            executeHelperMethod = providers.getMetaAccess().lookupJavaMethod(OptimizedCallTarget.class.getDeclaredMethod("executeHelper", PackedFrame.class, Arguments.class));
-        } catch (NoSuchMethodException ex) {
-            throw new RuntimeException(ex);
-        }
     }
 
     public StructuredGraph createGraph(final OptimizedCallTarget callTarget, final Assumptions assumptions) {
@@ -98,10 +88,10 @@
             constantReceivers = new HashSet<>();
         }
 
-        final StructuredGraph graph = new StructuredGraph(executeHelperMethod);
+        final StructuredGraph graph = truffleCache.createRootGraph();
+        assert graph != null : "no graph for root method";
 
-        try (Scope s = Debug.scope("CreateGraph", graph)) {
-            new GraphBuilderPhase.Instance(providers.getMetaAccess(), config, TruffleCompilerImpl.Optimizations).apply(graph);
+        try (Scope s = Debug.scope("CreateGraph", graph); Indent indent = Debug.logAndIndent("createGraph %s", graph.method())) {
 
             // Replace thisNode with constant.
             ParameterNode thisNode = graph.getParameter(0);
@@ -187,42 +177,54 @@
             changed = false;
             for (MethodCallTargetNode methodCallTargetNode : graph.getNodes(MethodCallTargetNode.class)) {
                 InvokeKind kind = methodCallTargetNode.invokeKind();
-                if (kind == InvokeKind.Static || (kind == InvokeKind.Special && (methodCallTargetNode.receiver().isConstant() || isFrame(methodCallTargetNode.receiver())))) {
-                    if (TraceTruffleCompilationHistogram.getValue() && kind == InvokeKind.Special) {
-                        ConstantNode constantNode = (ConstantNode) methodCallTargetNode.arguments().first();
-                        constantReceivers.add(constantNode.asConstant());
-                    }
-                    Replacements replacements = providers.getReplacements();
-                    Class<? extends FixedWithNextNode> macroSubstitution = replacements.getMacroSubstitution(methodCallTargetNode.targetMethod());
-                    if (macroSubstitution != null) {
-                        InliningUtil.inlineMacroNode(methodCallTargetNode.invoke(), methodCallTargetNode.targetMethod(), macroSubstitution);
-                        changed = true;
-                        continue;
-                    }
+                try (Indent id1 = Debug.logAndIndent("try inlining %s, kind = %s", methodCallTargetNode.targetMethod(), kind)) {
+                    if (kind == InvokeKind.Static || (kind == InvokeKind.Special && (methodCallTargetNode.receiver().isConstant() || isFrame(methodCallTargetNode.receiver())))) {
+                        if (TraceTruffleCompilationHistogram.getValue() && kind == InvokeKind.Special) {
+                            ConstantNode constantNode = (ConstantNode) methodCallTargetNode.arguments().first();
+                            constantReceivers.add(constantNode.asConstant());
+                        }
+                        Replacements replacements = providers.getReplacements();
+                        Class<? extends FixedWithNextNode> macroSubstitution = replacements.getMacroSubstitution(methodCallTargetNode.targetMethod());
+                        if (macroSubstitution != null) {
+                            InliningUtil.inlineMacroNode(methodCallTargetNode.invoke(), methodCallTargetNode.targetMethod(), macroSubstitution);
+                            changed = true;
+                            continue;
+                        }
+
+                        if (TraceTruffleCompilationDetails.getValue() && kind == InvokeKind.Special) {
+                            ConstantNode constantNode = (ConstantNode) methodCallTargetNode.arguments().first();
+                            constantReceivers.add(constantNode.asConstant());
+                        }
+
+                        StructuredGraph inlineGraph = replacements.getMethodSubstitution(methodCallTargetNode.targetMethod());
+                        if (inlineGraph == null && !Modifier.isNative(methodCallTargetNode.targetMethod().getModifiers())) {
+                            inlineGraph = parseGraph(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, phaseContext);
+                        }
 
-                    StructuredGraph inlineGraph = replacements.getMethodSubstitution(methodCallTargetNode.targetMethod());
-                    if (inlineGraph == null && !Modifier.isNative(methodCallTargetNode.targetMethod().getModifiers()) &&
-                                    methodCallTargetNode.targetMethod().getAnnotation(CompilerDirectives.SlowPath.class) == null) {
-                        inlineGraph = parseGraph(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, phaseContext);
-                    }
+                        if (inlineGraph != null) {
+                            try (Indent indent = Debug.logAndIndent("inline graph %s", methodCallTargetNode.targetMethod())) {
 
-                    if (inlineGraph != null) {
-                        int nodeCountBefore = graph.getNodeCount();
-                        Mark mark = graph.getMark();
-                        if (TraceTruffleExpansion.getValue()) {
-                            expansionLogger.preExpand(methodCallTargetNode, inlineGraph);
+                                int nodeCountBefore = graph.getNodeCount();
+                                Mark mark = graph.getMark();
+                                if (TraceTruffleExpansion.getValue()) {
+                                    expansionLogger.preExpand(methodCallTargetNode, inlineGraph);
+                                }
+                                List<Node> invokeUsages = methodCallTargetNode.invoke().asNode().usages().snapshot();
+                                // try (Indent in2 = Debug.logAndIndent(false, "do inlining")) {
+                                Map<Node, Node> inlined = InliningUtil.inline(methodCallTargetNode.invoke(), inlineGraph, false);
+                                if (TraceTruffleExpansion.getValue()) {
+                                    expansionLogger.postExpand(inlined);
+                                }
+                                // }
+                                if (Debug.isDumpEnabled()) {
+                                    Debug.log("dump enabled");
+                                    int nodeCountAfter = graph.getNodeCount();
+                                    Debug.dump(graph, "After inlining %s %+d (%d)", methodCallTargetNode.targetMethod().toString(), nodeCountAfter - nodeCountBefore, nodeCountAfter);
+                                }
+                                canonicalizer.applyIncremental(graph, phaseContext, invokeUsages, mark);
+                                changed = true;
+                            }
                         }
-                        List<Node> invokeUsages = methodCallTargetNode.invoke().asNode().usages().snapshot();
-                        Map<Node, Node> inlined = InliningUtil.inline(methodCallTargetNode.invoke(), inlineGraph, false);
-                        if (TraceTruffleExpansion.getValue()) {
-                            expansionLogger.postExpand(inlined);
-                        }
-                        if (Debug.isDumpEnabled()) {
-                            int nodeCountAfter = graph.getNodeCount();
-                            Debug.dump(graph, "After inlining %s %+d (%d)", methodCallTargetNode.targetMethod().toString(), nodeCountAfter - nodeCountBefore, nodeCountAfter);
-                        }
-                        canonicalizer.applyIncremental(graph, phaseContext, invokeUsages, mark);
-                        changed = true;
                     }
                 }
 
@@ -249,7 +251,7 @@
 
         StructuredGraph graph = truffleCache.lookup(targetMethod, arguments, assumptions, canonicalizer);
 
-        if (targetMethod.getAnnotation(ExplodeLoop.class) != null) {
+        if (graph != null && targetMethod.getAnnotation(ExplodeLoop.class) != null) {
             assert graph.hasLoops() : graph + " does not contain a loop";
             final StructuredGraph graphCopy = graph.copy();
             final List<Node> modifiedNodes = new ArrayList<>();
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluatorCanonicalizer.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluatorCanonicalizer.java	Fri Mar 28 08:38:49 2014 +0100
@@ -44,6 +44,8 @@
         this.constantReflection = constantReflection;
     }
 
+    private static final Unsafe unsafe = Unsafe.getUnsafe();
+
     @Override
     public Node canonicalize(Node node) {
         if (node instanceof LoadFieldNode) {
@@ -62,8 +64,8 @@
                 Object array = loadIndexedNode.array().asConstant().asObject();
                 long index = loadIndexedNode.index().asConstant().asLong();
                 if (index >= 0 && index < Array.getLength(array)) {
-                    int arrayBaseOffset = Unsafe.getUnsafe().arrayBaseOffset(array.getClass());
-                    int arrayIndexScale = Unsafe.getUnsafe().arrayIndexScale(array.getClass());
+                    int arrayBaseOffset = unsafe.arrayBaseOffset(array.getClass());
+                    int arrayIndexScale = unsafe.arrayIndexScale(array.getClass());
                     Constant constant = constantReflection.readUnsafeConstant(loadIndexedNode.elementKind(), array, arrayBaseOffset + index * arrayIndexScale,
                                     loadIndexedNode.elementKind() == Kind.Object);
                     return ConstantNode.forConstant(constant, metaAccess, loadIndexedNode.graph());
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java	Fri Mar 28 08:26:21 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,253 +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.phases.GraalOptions.*;
-
-import java.lang.reflect.*;
-import java.util.*;
-import java.util.Map.Entry;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.debug.*;
-import com.oracle.graal.debug.Debug.Scope;
-import com.oracle.graal.graph.Graph.Mark;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.Node;
-import com.oracle.graal.graph.spi.*;
-import com.oracle.graal.java.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
-import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.nodes.util.*;
-import com.oracle.graal.phases.*;
-import com.oracle.graal.phases.common.*;
-import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.phases.util.*;
-import com.oracle.graal.truffle.phases.*;
-import com.oracle.graal.virtual.phases.ea.*;
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.nodes.*;
-
-/**
- * Implementation of a cache for Truffle graphs for improving partial evaluation time.
- */
-public final class TruffleCache {
-
-    private final Providers providers;
-    private final GraphBuilderConfiguration config;
-    private final OptimisticOptimizations optimisticOptimizations;
-
-    private final HashMap<List<Object>, StructuredGraph> cache = new HashMap<>();
-    private final HashMap<List<Object>, Long> lastUsed = new HashMap<>();
-    private final StructuredGraph markerGraph = new StructuredGraph();
-    private final ResolvedJavaType stringBuilderClass;
-    private long counter;
-
-    public TruffleCache(Providers providers, GraphBuilderConfiguration config, OptimisticOptimizations optimisticOptimizations) {
-        this.providers = providers;
-        this.config = config;
-        this.optimisticOptimizations = optimisticOptimizations;
-        this.stringBuilderClass = providers.getMetaAccess().lookupJavaType(StringBuilder.class);
-    }
-
-    @SuppressWarnings("unused")
-    public StructuredGraph lookup(final ResolvedJavaMethod method, final NodeInputList<ValueNode> arguments, final Assumptions assumptions, final CanonicalizerPhase finalCanonicalizer) {
-
-        List<Object> key = new ArrayList<>(arguments.size() + 1);
-        key.add(method);
-        for (ValueNode v : arguments) {
-            if (v.getKind() == Kind.Object) {
-                key.add(v.stamp());
-            }
-        }
-        StructuredGraph resultGraph = cache.get(key);
-        if (resultGraph != null) {
-            lastUsed.put(key, counter++);
-            return resultGraph;
-        }
-
-        if (resultGraph == markerGraph) {
-            // Avoid recursive inline.
-            return null;
-        }
-
-        if (lastUsed.values().size() >= TruffleCompilerOptions.TruffleMaxCompilationCacheSize.getValue()) {
-            List<Long> lastUsedList = new ArrayList<>();
-            for (long l : lastUsed.values()) {
-                lastUsedList.add(l);
-            }
-            Collections.sort(lastUsedList);
-            long mid = lastUsedList.get(lastUsedList.size() / 2);
-
-            List<List<Object>> toRemoveList = new ArrayList<>();
-            for (Entry<List<Object>, Long> entry : lastUsed.entrySet()) {
-                if (entry.getValue() < mid) {
-                    toRemoveList.add(entry.getKey());
-                }
-            }
-
-            for (List<Object> entry : toRemoveList) {
-                cache.remove(entry);
-                lastUsed.remove(entry);
-            }
-        }
-
-        lastUsed.put(key, counter++);
-        cache.put(key, markerGraph);
-        try (Scope s = Debug.scope("TruffleCache", providers.getMetaAccess(), method)) {
-
-            final StructuredGraph graph = new StructuredGraph(method);
-            final PhaseContext phaseContext = new PhaseContext(providers, new Assumptions(false));
-            Mark mark = graph.getMark();
-            new GraphBuilderPhase.Instance(phaseContext.getMetaAccess(), config, optimisticOptimizations).apply(graph);
-
-            for (ParameterNode param : graph.getNodes(ParameterNode.class)) {
-                if (param.getKind() == Kind.Object) {
-                    ValueNode actualArgument = arguments.get(param.index());
-                    param.setStamp(param.stamp().join(actualArgument.stamp()));
-                }
-            }
-
-            // Intrinsify methods.
-            new ReplaceIntrinsicsPhase(providers.getReplacements()).apply(graph);
-
-            // Convert deopt to guards.
-            new ConvertDeoptimizeToGuardPhase().apply(graph);
-
-            CanonicalizerPhase canonicalizerPhase = new CanonicalizerPhase(!ImmutableCode.getValue());
-            PartialEscapePhase partialEscapePhase = new PartialEscapePhase(false, canonicalizerPhase);
-
-            while (true) {
-
-                partialEscapePhase.apply(graph, phaseContext);
-
-                // Conditional elimination.
-                ConditionalEliminationPhase conditionalEliminationPhase = new ConditionalEliminationPhase(phaseContext.getMetaAccess());
-                conditionalEliminationPhase.apply(graph);
-
-                // Canonicalize / constant propagate.
-                canonicalizerPhase.apply(graph, phaseContext);
-
-                boolean inliningProgress = false;
-                for (MethodCallTargetNode methodCallTarget : graph.getNodes(MethodCallTargetNode.class)) {
-                    if (!graph.getMark().equals(mark)) {
-                        // Make sure macro substitutions such as
-                        // CompilerDirectives.transferToInterpreter get processed first.
-                        for (Node newNode : graph.getNewNodes(mark)) {
-                            if (newNode instanceof MethodCallTargetNode) {
-                                MethodCallTargetNode methodCallTargetNode = (MethodCallTargetNode) newNode;
-                                Class<? extends FixedWithNextNode> macroSubstitution = providers.getReplacements().getMacroSubstitution(methodCallTargetNode.targetMethod());
-                                if (macroSubstitution != null) {
-                                    InliningUtil.inlineMacroNode(methodCallTargetNode.invoke(), methodCallTargetNode.targetMethod(), macroSubstitution);
-                                } else {
-                                    tryCutOffRuntimeExceptions(methodCallTargetNode);
-                                }
-                            }
-                        }
-                        mark = graph.getMark();
-                    }
-                    if (methodCallTarget.isAlive() && methodCallTarget.invoke() != null && shouldInline(methodCallTarget)) {
-                        inliningProgress = true;
-                        List<Node> canonicalizerUsages = new ArrayList<Node>();
-                        for (Node n : methodCallTarget.invoke().asNode().usages()) {
-                            if (n instanceof Canonicalizable) {
-                                canonicalizerUsages.add(n);
-                            }
-                        }
-                        List<ValueNode> argumentSnapshot = methodCallTarget.arguments().snapshot();
-                        Mark beforeInvokeMark = graph.getMark();
-                        expandInvoke(methodCallTarget);
-                        for (Node arg : argumentSnapshot) {
-                            if (arg != null && arg.recordsUsages()) {
-                                for (Node argUsage : arg.usages()) {
-                                    if (graph.isNew(beforeInvokeMark, argUsage) && argUsage instanceof Canonicalizable) {
-                                        canonicalizerUsages.add(argUsage);
-                                    }
-                                }
-                            }
-                        }
-                        canonicalizerPhase.applyIncremental(graph, phaseContext, canonicalizerUsages);
-                    }
-                }
-
-                // Convert deopt to guards.
-                new ConvertDeoptimizeToGuardPhase().apply(graph);
-
-                new EarlyReadEliminationPhase(canonicalizerPhase).apply(graph, phaseContext);
-
-                if (!inliningProgress) {
-                    break;
-                }
-            }
-
-            cache.put(key, graph);
-            if (TruffleCompilerOptions.TraceTruffleCacheDetails.getValue()) {
-                TTY.println(String.format("[truffle] added to graph cache method %s with %d nodes.", method, graph.getNodeCount()));
-            }
-            return graph;
-        } catch (Throwable e) {
-            throw Debug.handle(e);
-        }
-
-    }
-
-    private void expandInvoke(MethodCallTargetNode methodCallTargetNode) {
-        StructuredGraph inlineGraph = providers.getReplacements().getMethodSubstitution(methodCallTargetNode.targetMethod());
-        if (inlineGraph == null) {
-            inlineGraph = TruffleCache.this.lookup(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), null, null);
-        }
-        if (inlineGraph == this.markerGraph) {
-            // Can happen for recursive calls.
-            throw GraphUtil.approxSourceException(methodCallTargetNode, new IllegalStateException("Found illegal recursive call to " + methodCallTargetNode.targetMethod() +
-                            ", must annotate such calls with @CompilerDirectives.SlowPath!"));
-        }
-        Invoke invoke = methodCallTargetNode.invoke();
-        InliningUtil.inline(invoke, inlineGraph, true);
-    }
-
-    private boolean tryCutOffRuntimeExceptions(MethodCallTargetNode methodCallTargetNode) {
-        if (methodCallTargetNode.targetMethod().isConstructor()) {
-            ResolvedJavaType runtimeException = providers.getMetaAccess().lookupJavaType(RuntimeException.class);
-            ResolvedJavaType controlFlowException = providers.getMetaAccess().lookupJavaType(ControlFlowException.class);
-            ResolvedJavaType exceptionType = Objects.requireNonNull(ObjectStamp.typeOrNull(methodCallTargetNode.receiver().stamp()));
-            if (runtimeException.isAssignableFrom(methodCallTargetNode.targetMethod().getDeclaringClass()) && !controlFlowException.isAssignableFrom(exceptionType)) {
-                DeoptimizeNode deoptNode = methodCallTargetNode.graph().add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.UnreachedCode));
-                FixedNode invokeNode = methodCallTargetNode.invoke().asNode();
-                invokeNode.replaceAtPredecessor(deoptNode);
-                GraphUtil.killCFG(invokeNode);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean shouldInline(final MethodCallTargetNode methodCallTargetNode) {
-        return (methodCallTargetNode.invokeKind() == InvokeKind.Special || methodCallTargetNode.invokeKind() == InvokeKind.Static) &&
-                        !Modifier.isNative(methodCallTargetNode.targetMethod().getModifiers()) && methodCallTargetNode.targetMethod().getAnnotation(ExplodeLoop.class) == null &&
-                        methodCallTargetNode.targetMethod().getAnnotation(CompilerDirectives.SlowPath.class) == null &&
-                        !methodCallTargetNode.targetMethod().getDeclaringClass().equals(stringBuilderClass);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCacheImpl.java	Fri Mar 28 08:38:49 2014 +0100
@@ -0,0 +1,272 @@
+/*
+ * 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.phases.GraalOptions.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.Map.Entry;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.Debug.Scope;
+import com.oracle.graal.graph.Graph.Mark;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Node;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.java.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.tiers.*;
+import com.oracle.graal.phases.util.*;
+import com.oracle.graal.truffle.phases.*;
+import com.oracle.graal.virtual.phases.ea.*;
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Implementation of a cache for Truffle graphs for improving partial evaluation time.
+ */
+public final class TruffleCacheImpl implements TruffleCache {
+
+    private final Providers providers;
+    private final GraphBuilderConfiguration config;
+    private final GraphBuilderConfiguration configForRootGraph;
+    private final OptimisticOptimizations optimisticOptimizations;
+
+    private final HashMap<List<Object>, StructuredGraph> cache = new HashMap<>();
+    private final HashMap<List<Object>, Long> lastUsed = new HashMap<>();
+    private final StructuredGraph markerGraph = new StructuredGraph();
+    private final ResolvedJavaType stringBuilderClass;
+    private final ResolvedJavaMethod executeHelperMethod;
+    private long counter;
+
+    public TruffleCacheImpl(Providers providers, GraphBuilderConfiguration config, GraphBuilderConfiguration configForRootGraph, OptimisticOptimizations optimisticOptimizations) {
+        this.providers = providers;
+        this.config = config;
+        this.configForRootGraph = configForRootGraph;
+        this.optimisticOptimizations = optimisticOptimizations;
+        this.stringBuilderClass = providers.getMetaAccess().lookupJavaType(StringBuilder.class);
+        try {
+            executeHelperMethod = providers.getMetaAccess().lookupJavaMethod(OptimizedCallTarget.class.getDeclaredMethod("executeHelper", PackedFrame.class, Arguments.class));
+        } catch (NoSuchMethodException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public StructuredGraph createRootGraph() {
+        StructuredGraph graph = new StructuredGraph(executeHelperMethod);
+        new GraphBuilderPhase.Instance(providers.getMetaAccess(), configForRootGraph, TruffleCompilerImpl.Optimizations).apply(graph);
+        return graph;
+    }
+
+    @SuppressWarnings("unused")
+    public StructuredGraph lookup(final ResolvedJavaMethod method, final NodeInputList<ValueNode> arguments, final Assumptions assumptions, final CanonicalizerPhase finalCanonicalizer) {
+
+        if (method.getAnnotation(CompilerDirectives.SlowPath.class) != null) {
+            return null;
+        }
+
+        List<Object> key = new ArrayList<>(arguments.size() + 1);
+        key.add(method);
+        for (ValueNode v : arguments) {
+            if (v.getKind() == Kind.Object) {
+                key.add(v.stamp());
+            }
+        }
+        StructuredGraph resultGraph = cache.get(key);
+        if (resultGraph != null) {
+            lastUsed.put(key, counter++);
+            return resultGraph;
+        }
+
+        if (resultGraph == markerGraph) {
+            // Avoid recursive inline.
+            return null;
+        }
+
+        if (lastUsed.values().size() >= TruffleCompilerOptions.TruffleMaxCompilationCacheSize.getValue()) {
+            List<Long> lastUsedList = new ArrayList<>();
+            for (long l : lastUsed.values()) {
+                lastUsedList.add(l);
+            }
+            Collections.sort(lastUsedList);
+            long mid = lastUsedList.get(lastUsedList.size() / 2);
+
+            List<List<Object>> toRemoveList = new ArrayList<>();
+            for (Entry<List<Object>, Long> entry : lastUsed.entrySet()) {
+                if (entry.getValue() < mid) {
+                    toRemoveList.add(entry.getKey());
+                }
+            }
+
+            for (List<Object> entry : toRemoveList) {
+                cache.remove(entry);
+                lastUsed.remove(entry);
+            }
+        }
+
+        lastUsed.put(key, counter++);
+        cache.put(key, markerGraph);
+        try (Scope s = Debug.scope("TruffleCache", providers.getMetaAccess(), method)) {
+
+            final StructuredGraph graph = new StructuredGraph(method);
+            final PhaseContext phaseContext = new PhaseContext(providers, new Assumptions(false));
+            Mark mark = graph.getMark();
+            new GraphBuilderPhase.Instance(phaseContext.getMetaAccess(), config, optimisticOptimizations).apply(graph);
+
+            for (ParameterNode param : graph.getNodes(ParameterNode.class)) {
+                if (param.getKind() == Kind.Object) {
+                    ValueNode actualArgument = arguments.get(param.index());
+                    param.setStamp(param.stamp().join(actualArgument.stamp()));
+                }
+            }
+
+            // Intrinsify methods.
+            new ReplaceIntrinsicsPhase(providers.getReplacements()).apply(graph);
+
+            // Convert deopt to guards.
+            new ConvertDeoptimizeToGuardPhase().apply(graph);
+
+            CanonicalizerPhase canonicalizerPhase = new CanonicalizerPhase(!ImmutableCode.getValue());
+            PartialEscapePhase partialEscapePhase = new PartialEscapePhase(false, canonicalizerPhase);
+
+            while (true) {
+
+                partialEscapePhase.apply(graph, phaseContext);
+
+                // Conditional elimination.
+                ConditionalEliminationPhase conditionalEliminationPhase = new ConditionalEliminationPhase(phaseContext.getMetaAccess());
+                conditionalEliminationPhase.apply(graph);
+
+                // Canonicalize / constant propagate.
+                canonicalizerPhase.apply(graph, phaseContext);
+
+                boolean inliningProgress = false;
+                for (MethodCallTargetNode methodCallTarget : graph.getNodes(MethodCallTargetNode.class)) {
+                    if (!graph.getMark().equals(mark)) {
+                        // Make sure macro substitutions such as
+                        // CompilerDirectives.transferToInterpreter get processed first.
+                        for (Node newNode : graph.getNewNodes(mark)) {
+                            if (newNode instanceof MethodCallTargetNode) {
+                                MethodCallTargetNode methodCallTargetNode = (MethodCallTargetNode) newNode;
+                                Class<? extends FixedWithNextNode> macroSubstitution = providers.getReplacements().getMacroSubstitution(methodCallTargetNode.targetMethod());
+                                if (macroSubstitution != null) {
+                                    InliningUtil.inlineMacroNode(methodCallTargetNode.invoke(), methodCallTargetNode.targetMethod(), macroSubstitution);
+                                } else {
+                                    tryCutOffRuntimeExceptions(methodCallTargetNode);
+                                }
+                            }
+                        }
+                        mark = graph.getMark();
+                    }
+                    if (methodCallTarget.isAlive() && methodCallTarget.invoke() != null && shouldInline(methodCallTarget)) {
+                        inliningProgress = true;
+                        List<Node> canonicalizerUsages = new ArrayList<Node>();
+                        for (Node n : methodCallTarget.invoke().asNode().usages()) {
+                            if (n instanceof Canonicalizable) {
+                                canonicalizerUsages.add(n);
+                            }
+                        }
+                        List<ValueNode> argumentSnapshot = methodCallTarget.arguments().snapshot();
+                        Mark beforeInvokeMark = graph.getMark();
+                        expandInvoke(methodCallTarget);
+                        for (Node arg : argumentSnapshot) {
+                            if (arg != null && arg.recordsUsages()) {
+                                for (Node argUsage : arg.usages()) {
+                                    if (graph.isNew(beforeInvokeMark, argUsage) && argUsage instanceof Canonicalizable) {
+                                        canonicalizerUsages.add(argUsage);
+                                    }
+                                }
+                            }
+                        }
+                        canonicalizerPhase.applyIncremental(graph, phaseContext, canonicalizerUsages);
+                    }
+                }
+
+                // Convert deopt to guards.
+                new ConvertDeoptimizeToGuardPhase().apply(graph);
+
+                new EarlyReadEliminationPhase(canonicalizerPhase).apply(graph, phaseContext);
+
+                if (!inliningProgress) {
+                    break;
+                }
+            }
+
+            cache.put(key, graph);
+            if (TruffleCompilerOptions.TraceTruffleCacheDetails.getValue()) {
+                TTY.println(String.format("[truffle] added to graph cache method %s with %d nodes.", method, graph.getNodeCount()));
+            }
+            return graph;
+        } catch (Throwable e) {
+            throw Debug.handle(e);
+        }
+
+    }
+
+    private void expandInvoke(MethodCallTargetNode methodCallTargetNode) {
+        StructuredGraph inlineGraph = providers.getReplacements().getMethodSubstitution(methodCallTargetNode.targetMethod());
+        if (inlineGraph == null) {
+            inlineGraph = TruffleCacheImpl.this.lookup(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), null, null);
+        }
+        if (inlineGraph == this.markerGraph) {
+            // Can happen for recursive calls.
+            throw GraphUtil.approxSourceException(methodCallTargetNode, new IllegalStateException("Found illegal recursive call to " + methodCallTargetNode.targetMethod() +
+                            ", must annotate such calls with @CompilerDirectives.SlowPath!"));
+        }
+        Invoke invoke = methodCallTargetNode.invoke();
+        InliningUtil.inline(invoke, inlineGraph, true);
+    }
+
+    private boolean tryCutOffRuntimeExceptions(MethodCallTargetNode methodCallTargetNode) {
+        if (methodCallTargetNode.targetMethod().isConstructor()) {
+            ResolvedJavaType runtimeException = providers.getMetaAccess().lookupJavaType(RuntimeException.class);
+            ResolvedJavaType controlFlowException = providers.getMetaAccess().lookupJavaType(ControlFlowException.class);
+            ResolvedJavaType exceptionType = Objects.requireNonNull(ObjectStamp.typeOrNull(methodCallTargetNode.receiver().stamp()));
+            if (runtimeException.isAssignableFrom(methodCallTargetNode.targetMethod().getDeclaringClass()) && !controlFlowException.isAssignableFrom(exceptionType)) {
+                DeoptimizeNode deoptNode = methodCallTargetNode.graph().add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.UnreachedCode));
+                FixedNode invokeNode = methodCallTargetNode.invoke().asNode();
+                invokeNode.replaceAtPredecessor(deoptNode);
+                GraphUtil.killCFG(invokeNode);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean shouldInline(final MethodCallTargetNode methodCallTargetNode) {
+        return (methodCallTargetNode.invokeKind() == InvokeKind.Special || methodCallTargetNode.invokeKind() == InvokeKind.Static) &&
+                        !Modifier.isNative(methodCallTargetNode.targetMethod().getModifiers()) && methodCallTargetNode.targetMethod().getAnnotation(ExplodeLoop.class) == null &&
+                        methodCallTargetNode.targetMethod().getAnnotation(CompilerDirectives.SlowPath.class) == null &&
+                        !methodCallTargetNode.targetMethod().getDeclaringClass().equals(stringBuilderClass);
+    }
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Fri Mar 28 08:38:49 2014 +0100
@@ -98,18 +98,19 @@
         ResolvedJavaType[] skippedExceptionTypes = getSkippedExceptionTypes(providers.getMetaAccess());
         GraphBuilderConfiguration eagerConfig = GraphBuilderConfiguration.getEagerDefault();
         eagerConfig.setSkippedExceptionTypes(skippedExceptionTypes);
-        this.truffleCache = new TruffleCache(providers, eagerConfig, TruffleCompilerImpl.Optimizations);
-
         this.config = GraphBuilderConfiguration.getDefault();
         this.config.setSkippedExceptionTypes(skippedExceptionTypes);
-        this.partialEvaluator = new PartialEvaluator(providers, truffleCache, config);
+
+        this.truffleCache = new TruffleCacheImpl(providers, eagerConfig, config, TruffleCompilerImpl.Optimizations);
+
+        this.partialEvaluator = new PartialEvaluator(providers, truffleCache);
 
         if (Debug.isEnabled()) {
             DebugEnvironment.initialize(System.out);
         }
     }
 
-    private static ResolvedJavaType[] getSkippedExceptionTypes(MetaAccessProvider metaAccess) {
+    public static ResolvedJavaType[] getSkippedExceptionTypes(MetaAccessProvider metaAccess) {
         ResolvedJavaType[] skippedExceptionTypes = new ResolvedJavaType[SKIPPED_EXCEPTION_CLASSES.length];
         for (int i = 0; i < SKIPPED_EXCEPTION_CLASSES.length; i++) {
             skippedExceptionTypes[i] = metaAccess.lookupJavaType(SKIPPED_EXCEPTION_CLASSES[i]);
@@ -122,7 +123,7 @@
             @Override
             public InstalledCode call() throws Exception {
                 try (Scope s = Debug.scope("Truffle", new TruffleDebugJavaMethod(compilable))) {
-                    return compileMethodImpl(compilable);
+                    return compileMethodImpl((OptimizedCallTargetImpl) compilable);
                 } catch (Throwable e) {
                     throw Debug.handle(e);
                 }
@@ -134,11 +135,11 @@
     public static final DebugTimer CompilationTime = Debug.timer("CompilationTime");
     public static final DebugTimer CodeInstallationTime = Debug.timer("CodeInstallation");
 
-    private InstalledCode compileMethodImpl(final OptimizedCallTarget compilable) {
+    private InstalledCode compileMethodImpl(final OptimizedCallTargetImpl compilable) {
         final StructuredGraph graph;
 
         if (TraceTruffleCompilation.getValue()) {
-            OptimizedCallTarget.logOptimizingStart(compilable);
+            OptimizedCallTargetImpl.logOptimizingStart(compilable);
         }
 
         if (TraceTruffleInliningTree.getValue()) {
@@ -178,7 +179,7 @@
             properties.put("CodeSize", code != null ? code.length : 0);
             properties.put("Source", formatSourceSection(compilable.getRootNode().getSourceSection()));
 
-            OptimizedCallTarget.logOptimizingDone(compilable, properties);
+            OptimizedCallTargetImpl.logOptimizingDone(compilable, properties);
         }
         return compiledMethod;
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleReplacements.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleReplacements.java	Fri Mar 28 08:38:49 2014 +0100
@@ -47,17 +47,17 @@
         this.graalReplacements = providers.getReplacements();
     }
 
-    static Replacements makeInstance() {
+    public static Replacements makeInstance() {
         Providers graalProviders = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getProviders();
         Replacements truffleReplacements = new TruffleReplacements(graalProviders);
 
         truffleReplacements.registerSubstitutions(CompilerAssertsSubstitutions.class);
         truffleReplacements.registerSubstitutions(CompilerDirectivesSubstitutions.class);
         truffleReplacements.registerSubstitutions(ExactMathSubstitutions.class);
-        truffleReplacements.registerSubstitutions(UnexpectedResultExceptionSubstitutions.class);
         truffleReplacements.registerSubstitutions(FrameWithoutBoxingSubstitutions.class);
         truffleReplacements.registerSubstitutions(OptimizedAssumptionSubstitutions.class);
         truffleReplacements.registerSubstitutions(OptimizedCallTargetSubstitutions.class);
+        truffleReplacements.registerSubstitutions(OptimizedCallTargetImplSubstitutions.class);
 
         return truffleReplacements;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/OptimizedCallTargetImplSubstitutions.java	Fri Mar 28 08:38:49 2014 +0100
@@ -0,0 +1,43 @@
+/*
+ * 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.substitutions;
+
+import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.truffle.*;
+import com.oracle.graal.truffle.nodes.asserts.*;
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+
+@ClassSubstitution(OptimizedCallTargetImpl.class)
+public class OptimizedCallTargetImplSubstitutions {
+
+    @MacroSubstitution(macro = NeverInlineMacroNode.class, isStatic = false)
+    public static native Object callHelper(OptimizedCallTarget target, PackedFrame caller, Arguments args);
+
+    @MacroSubstitution(macro = NeverInlineMacroNode.class, isStatic = false)
+    public static native Object interpreterCall(OptimizedCallTarget target, PackedFrame caller, Arguments args);
+
+    @MacroSubstitution(macro = NeverInlineMacroNode.class, isStatic = false)
+    public static native Object compiledCodeInvalidated(OptimizedCallTarget target, PackedFrame caller, Arguments args);
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/OptimizedCallTargetSubstitutions.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/OptimizedCallTargetSubstitutions.java	Fri Mar 28 08:38:49 2014 +0100
@@ -23,9 +23,7 @@
 package com.oracle.graal.truffle.substitutions;
 
 import com.oracle.graal.api.replacements.*;
-import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.truffle.*;
-import com.oracle.graal.truffle.nodes.asserts.*;
 import com.oracle.graal.truffle.nodes.frame.*;
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
@@ -33,15 +31,6 @@
 @ClassSubstitution(OptimizedCallTarget.class)
 public class OptimizedCallTargetSubstitutions {
 
-    @MacroSubstitution(macro = NeverInlineMacroNode.class, isStatic = false)
-    public static native Object callHelper(OptimizedCallTarget target, PackedFrame caller, Arguments args);
-
-    @MacroSubstitution(macro = NeverInlineMacroNode.class, isStatic = false)
-    public static native Object interpreterCall(OptimizedCallTarget target, PackedFrame caller, Arguments args);
-
-    @MacroSubstitution(macro = NeverInlineMacroNode.class, isStatic = false)
-    public static native Object compiledCodeInvalidated(OptimizedCallTarget target, PackedFrame caller, Arguments args);
-
     @MethodSubstitution
     private static FrameWithoutBoxing createFrame(FrameDescriptor descriptor, PackedFrame caller, Arguments args) {
         return NewFrameNode.allocate(FrameWithoutBoxing.class, descriptor, caller, args);
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/UnexpectedResultExceptionSubstitutions.java	Fri Mar 28 08:26:21 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.substitutions;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.api.replacements.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.truffle.api.nodes.*;
-
-/**
- * Deoptimize on creation of a new UnexpectedResultException instance.
- */
-@ClassSubstitution(UnexpectedResultException.class)
-public class UnexpectedResultExceptionSubstitutions {
-
-    @SuppressWarnings("unused")
-    @MethodSubstitution(value = "<init>")
-    public static void init(Object result) {
-        DeoptimizeNode.deopt(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.UnreachedCode);
-    }
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/UnexpectedResultException.java	Fri Mar 28 08:26:21 2014 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/UnexpectedResultException.java	Fri Mar 28 08:38:49 2014 +0100
@@ -24,6 +24,8 @@
  */
 package com.oracle.truffle.api.nodes;
 
+import com.oracle.truffle.api.*;
+
 /**
  * An exception that should be thrown if the return value cannot be represented as a value of the
  * return type. The Truffle optimizer has special knowledge of this exception class and will never
@@ -41,6 +43,7 @@
      * @param result the alternative result
      */
     public UnexpectedResultException(Object result) {
+        CompilerDirectives.transferToInterpreter();
         this.result = result;
     }