# HG changeset patch # User Thomas Wuerthinger # Date 1373063399 -7200 # Node ID 1546866ebb8d1ea186ae703f652b015d9c76b73b # Parent 1db97e3de11c36c08f346e7601a867ad6c0e45c5 First draft of Truffle graph caching. diff -r 1db97e3de11c -r 1546866ebb8d graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java --- 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 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()); + } + } } diff -r 1db97e3de11c -r 1546866ebb8d graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java --- 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 sandbox(String name, Object[] context, DebugConfig config, Callable 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); } diff -r 1db97e3de11c -r 1546866ebb8d graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java --- 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); } diff -r 1db97e3de11c -r 1546866ebb8d graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java --- 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 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 arguments, final Assumptions assumptions, - final boolean canonicalizeReads) { + private StructuredGraph parseGraph(final ResolvedJavaMethod targetMethod, final NodeInputList 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)); } diff -r 1db97e3de11c -r 1546866ebb8d graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java --- /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 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 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() { + + public StructuredGraph call() { + StructuredGraph newGraph = parseGraph(method); + + // Get stamps from actual arguments. + List 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 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; + } +} diff -r 1db97e3de11c -r 1546866ebb8d graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java --- 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()); } }); diff -r 1db97e3de11c -r 1546866ebb8d graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/VerifyFrameDoesNotEscapePhase.java --- 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); } }