changeset 10611:1546866ebb8d

First draft of Truffle graph caching.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Sat, 06 Jul 2013 00:29:59 +0200
parents 1db97e3de11c
children 549a7568ce14
files graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/VerifyFrameDoesNotEscapePhase.java
diffstat 7 files changed, 199 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java	Fri Jul 05 15:18:37 2013 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java	Sat Jul 06 00:29:59 2013 +0200
@@ -372,4 +372,21 @@
             record(assumptions.list[i]);
         }
     }
+
+    public void print(PrintStream out) {
+        List<Assumption> nonNullList = new ArrayList<>();
+        if (list != null) {
+            for (int i = 0; i < list.length; ++i) {
+                Assumption a = list[i];
+                if (a != null) {
+                    nonNullList.add(a);
+                }
+            }
+        }
+
+        out.printf("%d assumptions:\n", nonNullList.size());
+        for (Assumption a : nonNullList) {
+            out.println(a.toString());
+        }
+    }
 }
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Fri Jul 05 15:18:37 2013 +0200
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Sat Jul 06 00:29:59 2013 +0200
@@ -101,6 +101,23 @@
         }
     }
 
+    /**
+     * Creates a new debug scope that is unrelated to the current scope and runs a given task in the
+     * new scope.
+     * 
+     * @param name new scope name
+     * @param context the context objects of the new scope
+     * @param config the debug configuration to use for the new scope
+     * @param callable the task to run in the new scope
+     */
+    public static <T> T sandbox(String name, Object[] context, DebugConfig config, Callable<T> callable) {
+        if (ENABLED) {
+            return DebugScope.getInstance().scope(name, null, callable, true, config, context);
+        } else {
+            return DebugScope.call(callable);
+        }
+    }
+
     public static void scope(String name, Runnable runnable) {
         scope(name, new Object[0], runnable);
     }
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Fri Jul 05 15:18:37 2013 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Sat Jul 06 00:29:59 2013 +0200
@@ -57,7 +57,8 @@
     public PartialEvaluationTest() {
         // Make sure Truffle runtime is initialized.
         Assert.assertTrue(Truffle.getRuntime() instanceof GraalTruffleRuntime);
-        this.partialEvaluator = new PartialEvaluator(runtime, ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements());
+        this.partialEvaluator = new PartialEvaluator(runtime, ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements(), new TruffleCache(runtime, GraphBuilderConfiguration.getDefault(),
+                        TruffleCompilerImpl.Optimizations, ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements()));
 
         DebugEnvironment.initialize(System.out);
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri Jul 05 15:18:37 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Sat Jul 06 00:29:59 2013 +0200
@@ -70,14 +70,16 @@
     private final Replacements replacements;
     private Set<Constant> constantReceivers;
     private final HotSpotGraphCache cache;
+    private final TruffleCache truffleCache;
 
-    public PartialEvaluator(MetaAccessProvider metaAccessProvider, Replacements replacements) {
+    public PartialEvaluator(MetaAccessProvider metaAccessProvider, Replacements replacements, TruffleCache truffleCache) {
         this.metaAccessProvider = metaAccessProvider;
         this.nodeClass = metaAccessProvider.lookupJavaType(com.oracle.truffle.api.nodes.Node.class);
         this.customCanonicalizer = new PartialEvaluatorCanonicalizer(metaAccessProvider, nodeClass);
         this.skippedExceptionTypes = TruffleCompilerImpl.getSkippedExceptionTypes(metaAccessProvider);
         this.replacements = replacements;
         this.cache = HotSpotGraalRuntime.graalRuntime().getCache();
+        this.truffleCache = truffleCache;
 
         try {
             executeHelperMethod = metaAccessProvider.lookupJavaMethod(OptimizedCallTarget.class.getDeclaredMethod("executeHelper", PackedFrame.class, Arguments.class));
@@ -215,7 +217,7 @@
                         StructuredGraph inlineGraph = replacements.getMethodSubstitution(methodCallTargetNode.targetMethod());
                         NewFrameNode otherNewFrame = null;
                         if (inlineGraph == null) {
-                            inlineGraph = parseGraph(config, methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, !AOTCompilation.getValue());
+                            inlineGraph = parseGraph(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, !AOTCompilation.getValue());
                             otherNewFrame = inlineGraph.getNodes(NewFrameNode.class).first();
                         }
 
@@ -225,7 +227,6 @@
                             int nodeCountAfter = graph.getNodeCount();
                             Debug.dump(graph, "After inlining %s %+d (%d)", methodCallTargetNode.targetMethod().toString(), nodeCountAfter - nodeCountBefore, nodeCountAfter);
                         }
-
                         changed = true;
 
                         if (otherNewFrame != null) {
@@ -240,21 +241,17 @@
         } while (changed && newFrameNode.isAlive() && newFrameNode.usages().isNotEmpty());
     }
 
-    private StructuredGraph parseGraph(final GraphBuilderConfiguration config, final ResolvedJavaMethod targetMethod, final NodeInputList<ValueNode> arguments, final Assumptions assumptions,
-                    final boolean canonicalizeReads) {
+    private StructuredGraph parseGraph(final ResolvedJavaMethod targetMethod, final NodeInputList<ValueNode> arguments, final Assumptions assumptions, final boolean canonicalizeReads) {
 
-        final StructuredGraph graph = new StructuredGraph(targetMethod);
+        final StructuredGraph graph = truffleCache.lookup(targetMethod, arguments).copy();
         Debug.scope("parseGraph", targetMethod, new Runnable() {
 
             @Override
             public void run() {
-                new GraphBuilderPhase(metaAccessProvider, config, TruffleCompilerImpl.Optimizations).apply(graph);
                 // Pass on constant arguments.
                 for (LocalNode local : graph.getNodes(LocalNode.class)) {
                     ValueNode arg = arguments.get(local.index());
-                    if (arg instanceof NewFrameNode) {
-                        local.setStamp(arg.stamp());
-                    } else if (arg.isConstant()) {
+                    if (arg.isConstant()) {
                         Constant constant = arg.asConstant();
                         local.replaceAndDelete(ConstantNode.forConstant(constant, metaAccessProvider, graph));
                     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java	Sat Jul 06 00:29:59 2013 +0200
@@ -0,0 +1,144 @@
+/*
+ * 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.util.*;
+import java.util.concurrent.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.internal.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.java.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.truffle.phases.*;
+
+/**
+ * Implementation of a cache for Truffle graphs for improving partial evaluation time.
+ */
+public final class TruffleCache {
+
+    private final MetaAccessProvider metaAccessProvider;
+    private final GraphBuilderConfiguration config;
+    private final OptimisticOptimizations optimisticOptimizations;
+    private final Replacements replacements;
+
+    private final HashMap<ResolvedJavaMethod, StructuredGraph> cache = new HashMap<>();
+
+    public TruffleCache(MetaAccessProvider metaAccessProvider, GraphBuilderConfiguration config, OptimisticOptimizations optimisticOptimizations, Replacements replacements) {
+        this.metaAccessProvider = metaAccessProvider;
+        this.config = config;
+        this.optimisticOptimizations = optimisticOptimizations;
+        this.replacements = replacements;
+    }
+
+    public StructuredGraph lookup(final ResolvedJavaMethod method, final NodeInputList<ValueNode> arguments) {
+
+        if (cache.containsKey(method)) {
+            StructuredGraph graph = cache.get(method);
+            if (checkArgumentStamps(graph, arguments)) {
+                return graph;
+            }
+        }
+
+        StructuredGraph resultGraph = Debug.sandbox("TruffleCache", new Object[]{metaAccessProvider, method}, DebugScope.getConfig(), new Callable<StructuredGraph>() {
+
+            public StructuredGraph call() {
+                StructuredGraph newGraph = parseGraph(method);
+
+                // Get stamps from actual arguments.
+                List<Stamp> stamps = new ArrayList<>();
+                for (ValueNode arg : arguments) {
+                    stamps.add(arg.stamp());
+                }
+
+                if (cache.containsKey(method)) {
+                    // Make sure stamps are generalized based on previous stamps.
+                    StructuredGraph graph = cache.get(method);
+                    for (LocalNode localNode : graph.getNodes(LocalNode.class)) {
+                        int index = localNode.index();
+                        Stamp stamp = stamps.get(index);
+                        stamps.set(index, stamp.meet(localNode.stamp()));
+                    }
+                }
+
+                // Set stamps into graph before optimizing.
+                for (LocalNode localNode : newGraph.getNodes(LocalNode.class)) {
+                    int index = localNode.index();
+                    Stamp stamp = stamps.get(index);
+                    localNode.setStamp(stamp);
+                }
+
+                optimizeGraph(newGraph);
+                cache.put(method, newGraph);
+                if (TruffleCompilerOptions.TraceTruffleCompilationDetails.getValue()) {
+                    TTY.println(String.format("[truffle] added to graph cache method %s with %d nodes.", method, newGraph.getNodeCount()));
+                }
+                return newGraph;
+            }
+        });
+        return resultGraph;
+    }
+
+    private void optimizeGraph(StructuredGraph newGraph) {
+        // Canonicalize / constant propagate.
+        Assumptions assumptions = new Assumptions(false);
+        CanonicalizerPhase.Instance canonicalizerPhase = new CanonicalizerPhase.Instance(metaAccessProvider, assumptions, !AOTCompilation.getValue(), null, null);
+        canonicalizerPhase.apply(newGraph);
+
+        // Intrinsify methods.
+        new ReplaceIntrinsicsPhase(replacements).apply(newGraph);
+
+        // Convert deopt to guards.
+        new ConvertDeoptimizeToGuardPhase().apply(newGraph);
+    }
+
+    private StructuredGraph parseGraph(ResolvedJavaMethod method) {
+        StructuredGraph graph = new StructuredGraph(method);
+        new GraphBuilderPhase(metaAccessProvider, config, optimisticOptimizations).apply(graph);
+        return graph;
+    }
+
+    private static boolean checkArgumentStamps(StructuredGraph graph, NodeInputList<ValueNode> arguments) {
+        assert graph.getNodes(LocalNode.class).count() == arguments.count();
+        for (LocalNode localNode : graph.getNodes(LocalNode.class)) {
+            Stamp newStamp = localNode.stamp().meet(arguments.get(localNode.index()).stamp());
+            if (!newStamp.equals(localNode.stamp())) {
+                if (TruffleCompilerOptions.TraceTruffleCompilationDetails.getValue()) {
+                    TTY.println(String.format("[truffle] graph cache entry too specific for method %s argument %s previous stamp %s new stamp %s.", graph.method(), localNode, localNode.stamp(),
+                                    newStamp));
+                }
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Fri Jul 05 15:18:37 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Sat Jul 06 00:29:59 2013 +0200
@@ -62,6 +62,7 @@
     private final Backend backend;
     private final ResolvedJavaType[] skippedExceptionTypes;
     private final HotSpotGraalRuntime graalRuntime;
+    private final TruffleCache truffleCache;
 
     private static final Class[] SKIPPED_EXCEPTION_CLASSES = new Class[]{SlowPathException.class, UnexpectedResultException.class, ArithmeticException.class};
 
@@ -75,9 +76,13 @@
         this.backend = Graal.getRequiredCapability(Backend.class);
         this.replacements = ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements();
         this.graalRuntime = HotSpotGraalRuntime.graalRuntime();
+        this.skippedExceptionTypes = getSkippedExceptionTypes(metaAccessProvider);
 
-        this.partialEvaluator = new PartialEvaluator(metaAccessProvider, replacements);
-        this.skippedExceptionTypes = getSkippedExceptionTypes(metaAccessProvider);
+        final GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault();
+        config.setSkippedExceptionTypes(skippedExceptionTypes);
+        this.truffleCache = new TruffleCache(this.runtime, config, TruffleCompilerImpl.Optimizations, this.replacements);
+
+        this.partialEvaluator = new PartialEvaluator(metaAccessProvider, replacements, truffleCache);
 
         if (DebugEnabled.getValue()) {
             DebugEnvironment.initialize(System.out);
@@ -142,7 +147,8 @@
             @Override
             public CompilationResult call() {
                 CallingConvention cc = getCallingConvention(runtime, Type.JavaCallee, graph.method(), false);
-                return GraalCompiler.compileGraph(graph, cc, graph.method(), runtime, replacements, backend, runtime.getTarget(), null, plan, OptimisticOptimizations.ALL, new SpeculationLog(), suites, new CompilationResult());
+                return GraalCompiler.compileGraph(graph, cc, graph.method(), runtime, replacements, backend, runtime.getTarget(), null, plan, OptimisticOptimizations.ALL, new SpeculationLog(),
+                                suites, new CompilationResult());
             }
         });
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/VerifyFrameDoesNotEscapePhase.java	Fri Jul 05 15:18:37 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/VerifyFrameDoesNotEscapePhase.java	Sat Jul 06 00:29:59 2013 +0200
@@ -41,7 +41,9 @@
         if (frame != null) {
             for (MethodCallTargetNode callTarget : frame.usages().filter(MethodCallTargetNode.class)) {
                 if (callTarget.invoke() != null) {
-                    Throwable exception = new VerificationError("Frame escapes at: %s#%s", callTarget, callTarget.targetMethod());
+                    String properties = callTarget.getDebugProperties().toString();
+                    String arguments = callTarget.arguments().toString();
+                    Throwable exception = new VerificationError("Frame escapes at: %s#%s\nproperties:%s\narguments: %s", callTarget, callTarget.targetMethod(), properties, arguments);
                     throw GraphUtil.approxSourceException(callTarget, exception);
                 }
             }