# HG changeset patch # User Christian Humer # Date 1396544818 -7200 # Node ID a31d807757ee4a4ca9757135e9ee0e40c2e2feeb # Parent 1422f0bd55e36b461cf8da1da3980558d7a911eb Truffle: made inlining fully context sensitive. diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultInliningPolicy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultInliningPolicy.java Thu Apr 03 19:06:58 2014 +0200 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.truffle; + +import static com.oracle.graal.truffle.TruffleCompilerOptions.*; + +public class DefaultInliningPolicy implements TruffleInliningPolicy { + + private static final String REASON_RECURSION = "recursion"; + private static final String REASON_MAXIMUM_NODE_COUNT = "nodeCount * callSites > " + TruffleInliningMaxCallerSize.getValue(); + private static final String REASON_MAXIMUM_TOTAL_NODE_COUNT = "totalNodeCount > " + TruffleInliningMaxCallerSize.getValue(); + + public double calculateScore(TruffleInliningProfile profile) { + return profile.getFrequency() / profile.getDeepNodeCount(); + } + + public boolean isAllowed(TruffleInliningResult state, TruffleInliningProfile profile, int nodeCountBudget) { + TruffleCallPath callPath = profile.getCallPath(); + OptimizedCallTarget inlineTarget = callPath.getCallTarget(); + for (TruffleCallPath path : callPath.getParent().toList()) { + if (path.getCallTarget() == inlineTarget) { + // recursive call found + profile.setFailedReason(REASON_RECURSION); + return false; + } + } + + if (profile.isForced()) { + return true; + } + + if (nodeCountBudget - profile.getDeepNodeCount() < 0) { + profile.setFailedReason(REASON_MAXIMUM_TOTAL_NODE_COUNT); + return false; + } + + int cappedCallSites = Math.min(Math.max(profile.getCallSites(), 1), 10); + if (profile.getDeepNodeCount() * cappedCallSites > TruffleInliningMaxCallerSize.getValue()) { + profile.setFailedReason(REASON_MAXIMUM_NODE_COUNT); + return false; + } + + return true; + } + + public boolean isAllowedDeep(TruffleInliningResult state, TruffleInliningProfile profile, int nodeCountBudget) { + return true; + } + +} diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java Thu Apr 03 18:33:48 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNode.java Thu Apr 03 19:06:58 2014 +0200 @@ -22,9 +22,10 @@ */ package com.oracle.graal.truffle; -import java.util.*; +import java.util.concurrent.atomic.*; import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.impl.*; import com.oracle.truffle.api.nodes.*; @@ -33,258 +34,169 @@ /** * Call target that is optimized by Graal upon surpassing a specific invocation threshold. */ -public abstract class OptimizedCallNode extends DefaultCallNode { +public final class OptimizedCallNode extends DefaultCallNode { protected int callCount; + private boolean trySplit = true; + private boolean inliningForced; + @CompilationFinal private OptimizedCallTarget splitCallTarget; + private final AtomicInteger inliningCounter = new AtomicInteger(0); private OptimizedCallNode(OptimizedCallTarget target) { super(target); } @Override - public final boolean isSplittable() { + public boolean isSplittable() { return getCallTarget().getRootNode().isSplittable(); } @Override - public final OptimizedCallTarget getCallTarget() { + public OptimizedCallTarget getCallTarget() { return (OptimizedCallTarget) super.getCallTarget(); } - public final int getCallCount() { + public int getCallCount() { return callCount; } - public TruffleInliningProfile createInliningProfile(OptimizedCallTarget target) { - return new OptimizedCallNodeProfile(target, this); - } - - @SuppressWarnings("unused") - public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) { - } - @Override - public final OptimizedCallTarget getCurrentCallTarget() { + public OptimizedCallTarget getCurrentCallTarget() { return (OptimizedCallTarget) super.getCurrentCallTarget(); } @Override public OptimizedCallTarget getSplitCallTarget() { - return null; - } - - protected OptimizedCallNode inlineImpl() { - if (getParent() == null) { - throw new IllegalStateException("CallNode must be adopted before it is split."); - } - - return replace(new InlinedOptimizedCallNode(getCallTarget(), getSplitCallTarget(), callCount)); + return splitCallTarget; } public static OptimizedCallNode create(OptimizedCallTarget target) { - return new DefaultOptimizedCallNode(target); + return new OptimizedCallNode(target); } @Override - public boolean isInlinable() { - return true; + public Object call(PackedFrame caller, Arguments arguments) { + if (CompilerDirectives.inInterpreter()) { + interpreterCall(); + if (inliningCounter.get() > 0 || inliningForced) { + return getCurrentCallTarget().callInlined(caller, arguments); + } + } + return getCurrentCallTarget().call(caller, arguments); } - private static final class DefaultOptimizedCallNode extends OptimizedCallNode { - - private boolean trySplit = true; - - DefaultOptimizedCallNode(OptimizedCallTarget target) { - super(target); - registerCallTarget(this); - } - - @Override - public Object call(PackedFrame caller, Arguments arguments) { - if (CompilerDirectives.inInterpreter()) { - callCount++; - if (trySplit && callCount > 1) { - trySplit = false; - return trySplit(caller, arguments); + private void interpreterCall() { + callCount++; + if (trySplit) { + if (callCount == 1) { + // on first call + getCurrentCallTarget().incrementKnownCallSite(); + } + if (callCount > 1) { + trySplit = false; + if (shouldSplit()) { + splitImpl(true); } } - return callTarget.call(caller, arguments); } + } - private Object trySplit(PackedFrame caller, Arguments arguments) { - if (shouldSplit()) { - return splitImpl(true).call(caller, arguments); - } - return callTarget.call(caller, arguments); - } + void notifyInlining() { + inliningCounter.incrementAndGet(); + } + + void notifyInliningDone() { + inliningCounter.decrementAndGet(); + } - private boolean shouldSplit() { - if (!TruffleCompilerOptions.TruffleSplittingEnabled.getValue()) { - return false; - } - if (!isSplittable()) { - return false; - } - int nodeCount = NodeUtil.countNodes(getCallTarget().getRootNode(), OptimizedCallNodeProfile.COUNT_FILTER, false); - if (nodeCount > TruffleCompilerOptions.TruffleSplittingMaxCalleeSize.getValue()) { - return false; - } + /** + * If the method was inlined the truffle magic redirects calls to + * {@link #call(PackedFrame, Arguments)} to this method. You should not call this method + * directly. + * + * @see PartialEvaluator#expandInlinableCallNode + */ + public Object callInlined(PackedFrame caller, Arguments arguments) { + return getCurrentCallTarget().callInlined(caller, arguments); + } - // disable recursive splitting for now - OptimizedCallTarget splitTarget = getCallTarget(); - List compilationRoots = OptimizedCallNodeProfile.findCompilationRoots(this); - for (OptimizedCallTarget compilationRoot : compilationRoots) { - if (compilationRoot == splitTarget || compilationRoot.getSplitSource() == splitTarget) { - // recursive call found - return false; - } - } + @Override + public void inline() { + inliningForced = true; + } - // max one child call and callCount > 2 and kind of small number of nodes - if (isMaxSingleCall()) { - return true; - } - return countPolymorphic() >= 1; - } + @Override + public boolean isInlined() { + return inliningForced; + } - @Override - public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) { - trySplit = true; - } + private void splitImpl(boolean heuristic) { + CompilerAsserts.neverPartOfCompilation(); - private boolean isMaxSingleCall() { - return NodeUtil.countNodes(getCurrentCallTarget().getRootNode(), new NodeCountFilter() { - public boolean isCounted(Node node) { - return node instanceof CallNode; - } - }) <= 1; + OptimizedCallTarget splitTarget = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(getCallTarget().getRootNode().split()); + splitTarget.setSplitSource(getCallTarget()); + if (heuristic) { + OptimizedCallTarget.logSplit(this, getCallTarget(), splitTarget); } + if (callCount >= 1) { + getCallTarget().decrementKnownCallSite(); + splitTarget.incrementKnownCallSite(); + } + this.splitCallTarget = splitTarget; + } - private int countPolymorphic() { - return NodeUtil.countNodes(getCallTarget().getRootNode(), new NodeCountFilter() { - public boolean isCounted(Node node) { - NodeCost cost = node.getCost(); - boolean polymorphic = cost == NodeCost.POLYMORPHIC || cost == NodeCost.MEGAMORPHIC; - return polymorphic; - } - }); + private boolean shouldSplit() { + if (splitCallTarget != null) { + return false; + } + if (!TruffleCompilerOptions.TruffleSplittingEnabled.getValue()) { + return false; } - - @Override - public boolean isInlined() { + if (!isSplittable()) { + return false; + } + OptimizedCallTarget splitTarget = getCallTarget(); + int nodeCount = OptimizedCallUtils.countNonTrivialNodes(null, new TruffleCallPath(splitTarget)); + if (nodeCount > TruffleCompilerOptions.TruffleSplittingMaxCalleeSize.getValue()) { return false; } - @Override - public boolean split() { - if (!isSplittable()) { - // split is only allowed once and if the root node supports it - return false; - } - if (getParent() == null) { - throw new IllegalStateException("CallNode must be adopted before it is split."); - } - splitImpl(false); - return true; - } - - private OptimizedCallNode splitImpl(boolean heuristic) { - OptimizedCallTarget splitCallTarget = (OptimizedCallTarget) Truffle.getRuntime().createCallTarget(getCallTarget().getRootNode().split()); - splitCallTarget.setSplitSource(getCallTarget()); - if (heuristic) { - OptimizedCallTarget.logSplit(this, getCallTarget(), splitCallTarget); - } - return replace(new SplitOptimizedCallNode(getCallTarget(), splitCallTarget, callCount)); - } - - @Override - public void inline() { - inlineImpl(); - } - - @Override - public OptimizedCallTarget getSplitCallTarget() { - return null; - } - - } - - private static final class InlinedOptimizedCallNode extends OptimizedCallNode { - - private final OptimizedCallTarget splittedTarget; - - public InlinedOptimizedCallNode(OptimizedCallTarget target, OptimizedCallTarget splittedTarget, int callCount) { - super(target); - this.splittedTarget = splittedTarget; - this.callCount = callCount; - } - - @Override - public Object call(PackedFrame caller, Arguments arguments) { - if (CompilerDirectives.inInterpreter()) { - callCount++; - } - return getCurrentCallTarget().callInlined(caller, arguments); - } - - @Override - public void inline() { - } - - @Override - public boolean split() { + // disable recursive splitting for now + OptimizedCallTarget root = (OptimizedCallTarget) getRootNode().getCallTarget(); + if (root == splitTarget || root.getSplitSource() == splitTarget) { + // recursive call found return false; } - @Override - public boolean isInlined() { + // max one child call and callCount > 2 and kind of small number of nodes + if (isMaxSingleCall()) { return true; } + return countPolymorphic() >= 1; + } - @Override - public OptimizedCallTarget getSplitCallTarget() { - return splittedTarget; - } + private boolean isMaxSingleCall() { + return NodeUtil.countNodes(getCurrentCallTarget().getRootNode(), new NodeCountFilter() { + public boolean isCounted(Node node) { + return node instanceof CallNode; + } + }) <= 1; } - private static class SplitOptimizedCallNode extends OptimizedCallNode { - - private final OptimizedCallTarget splittedTarget; - - public SplitOptimizedCallNode(OptimizedCallTarget target, OptimizedCallTarget splittedTarget, int callCount) { - super(target); - this.callCount = callCount; - this.splittedTarget = splittedTarget; - } - - @Override - public Object call(PackedFrame caller, Arguments arguments) { - if (CompilerDirectives.inInterpreter()) { - callCount++; + private int countPolymorphic() { + return NodeUtil.countNodes(getCallTarget().getRootNode(), new NodeCountFilter() { + public boolean isCounted(Node node) { + NodeCost cost = node.getCost(); + boolean polymorphic = cost == NodeCost.POLYMORPHIC || cost == NodeCost.MEGAMORPHIC; + return polymorphic; } - return splittedTarget.call(caller, arguments); - } - - @Override - public boolean isInlined() { - return false; - } - - @Override - public final boolean split() { - return false; - } - - @Override - public void inline() { - inlineImpl(); - } - - @Override - public final OptimizedCallTarget getSplitCallTarget() { - return splittedTarget; - } - + }); } + @SuppressWarnings("unused") + public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) { + if (!isSplit() && isSplittable()) { + trySplit = true; + } + } } diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNodeProfile.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallNodeProfile.java Thu Apr 03 18:33:48 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,256 +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.truffle.TruffleCompilerOptions.*; - -import java.util.*; - -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.nodes.NodeUtil.*; - -public class OptimizedCallNodeProfile implements TruffleInliningProfile { - - static final CountFilter COUNT_FILTER = new CountFilter(); - - private static final String REASON_RECURSION = "recursion"; - private static final String REASON_MAXIMUM_NODE_COUNT = "shallowTargetCount > " + TruffleInliningMaxCalleeSize.getValue(); - private static final String REASON_MAXIMUM_TOTAL_NODE_COUNT = "inlinedTotalCount > " + TruffleInliningMaxCallerSize.getValue(); - - private final OptimizedCallTarget callTarget; - private final OptimizedCallNode callNode; - - private int targetDeepNodeCount; - private List compilationRoots; - private final int targetShallowNodeCount; - private final double averageFrequency; - private final double score; - private final boolean leaf; - private String reason; - - public OptimizedCallNodeProfile(OptimizedCallTarget target, OptimizedCallNode callNode) { - this.callNode = callNode; - RootNode inlineRoot = callNode.getCurrentCallTarget().getRootNode(); - this.callTarget = target; - this.targetShallowNodeCount = NodeUtil.countNodes(inlineRoot, COUNT_FILTER, false); - this.targetDeepNodeCount = NodeUtil.countNodes(inlineRoot, COUNT_FILTER, true); - this.compilationRoots = findCompilationRoots(callNode); - this.averageFrequency = calculateFrequency(); - this.score = calculateScore(); - this.leaf = calculateLeaf(); - - } - - private boolean calculateLeaf() { - return NodeUtil.countNodes(callNode.getCurrentRootNode(), new NodeCountFilter() { - public boolean isCounted(Node node) { - if (node instanceof CallNode) { - CallNode childCall = (CallNode) node; - if (!childCall.isInlined()) { - return true; - } - } - return false; - } - }, true) <= 0; - } - - private double calculateFrequency() { - return calculateAdvancedFrequency(); - } - - public OptimizedCallNode getCallNode() { - return callNode; - } - - public double getScore() { - return score; - } - - public double calculateScore() { - return averageFrequency / Math.max(1, targetDeepNodeCount); - } - - public boolean isInliningAllowed() { - this.compilationRoots = findCompilationRoots(getCallNode()); - - OptimizedCallTarget inlineTarget = callNode.getCurrentCallTarget(); - for (OptimizedCallTarget compilationRoot : compilationRoots) { - if (compilationRoot == inlineTarget) { - // recursive call found - reason = REASON_RECURSION; - return false; - } - } - - if (targetShallowNodeCount > TruffleInliningMaxCalleeSize.getValue()) { - reason = REASON_MAXIMUM_NODE_COUNT; - return false; - } - - this.targetDeepNodeCount = NodeUtil.countNodes(inlineTarget.getRootNode(), COUNT_FILTER, true); - // The maximum total node count cannot be cached since it may change during inlining. - int nextNodeCount = calculateInlinedTotalNodeCount(getCallNode()); - if (nextNodeCount > TruffleInliningMaxCallerSize.getValue()) { - reason = REASON_MAXIMUM_TOTAL_NODE_COUNT; - return false; - } - - return true; - } - - private int calculateInlinedTotalNodeCount(OptimizedCallNode node) { - int currentNodeCount = 0; - for (OptimizedCallTarget compilationRoot : compilationRoots) { - TotalNodeCountVisitor visitor = new TotalNodeCountVisitor(node, targetDeepNodeCount); - compilationRoot.getRootNode().accept(visitor); - currentNodeCount = Math.max(currentNodeCount, visitor.count); - } - return currentNodeCount; - } - - static class CountFilter implements NodeCountFilter { - public boolean isCounted(Node node) { - return countNode(node) >= 1; - } - } - - static int countNode(Node node) { - NodeCost cost = node.getCost(); - if (cost != null && cost != NodeCost.NONE && cost != NodeCost.UNINITIALIZED) { - return 1; - } - return 0; - } - - private static class TotalNodeCountVisitor implements NodeVisitor { - - private final OptimizedCallNode inlinedNode; - private final int inlinedNodeCount; - - private int count; - - public TotalNodeCountVisitor(OptimizedCallNode inlinedNode, int inlinedNodeCount) { - this.inlinedNode = inlinedNode; - this.inlinedNodeCount = inlinedNodeCount; - } - - public boolean visit(Node node) { - count += countNode(node); - if (node instanceof OptimizedCallNode) { - OptimizedCallNode callNode = ((OptimizedCallNode) node); - if (callNode == inlinedNode) { - count += inlinedNodeCount; - } else if (callNode.isInlined()) { - callNode.getCurrentRootNode().accept(this); - } - } - return true; - } - - } - - double calculateAdvancedFrequency() { - // get the call hierarchy from call target to the call node - final ArrayDeque callStack = new ArrayDeque<>(); - callTarget.getRootNode().accept(new NodeVisitor() { - private boolean found = false; - - public boolean visit(Node node) { - if (node == callNode) { - // found our call - callStack.push((OptimizedCallNode) node); - found = true; - return false; - } - - if (node instanceof OptimizedCallNode) { - OptimizedCallNode c = ((OptimizedCallNode) node); - if (c.isInlined()) { - if (!found) { - callStack.push(c); - } - c.getCurrentRootNode().accept(this); - if (!found) { - callStack.pop(); - } - } - } - return !found; - } - }); - - int parentCallCount = callTarget.getCompilationProfile().getCallCount(); - double frequency = 1.0d; - for (OptimizedCallNode c : callStack) { - int childCallCount = c.getCallCount(); - frequency *= childCallCount / (double) parentCallCount; - parentCallCount = c.getCurrentCallTarget().getCompilationProfile().getCallCount(); - } - return frequency; - } - - double calculateSimpleFrequency() { - return callNode.getCallCount() / (double) ((OptimizedCallTarget) callNode.getRootNode().getCallTarget()).getCompilationProfile().getCallCount(); - } - - static List findCompilationRoots(Node call) { - RootNode root = call.getRootNode(); - if (root == null) { - return Collections.emptyList(); - } - List roots = new ArrayList<>(); - roots.add((OptimizedCallTarget) root.getCallTarget()); - for (CallNode callNode : root.getCachedCallNodes()) { - if (callNode.isInlined()) { - roots.addAll(findCompilationRoots(callNode)); - } - } - return roots; - } - - public int compareTo(TruffleInliningProfile o) { - if (o instanceof OptimizedCallNodeProfile) { - int cmp = Boolean.compare(((OptimizedCallNodeProfile) o).leaf, leaf); - if (cmp == 0) { - return Double.compare(((OptimizedCallNodeProfile) o).getScore(), getScore()); - } - return cmp; - } - return 0; - } - - public Map getDebugProperties() { - Map properties = new LinkedHashMap<>(); - OptimizedCallTarget.addASTSizeProperty(getCallNode().getCurrentRootNode().getRootNode(), properties); - properties.put("shallowCount", targetShallowNodeCount); - properties.put("currentCount", calculateInlinedTotalNodeCount(null)); - properties.put("inlinedTotalCount", calculateInlinedTotalNodeCount(getCallNode())); - properties.put("score", score); - properties.put("frequency", averageFrequency); - properties.put("leaf", leaf); - properties.put("reason", reason); - return properties; - } - -} diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Thu Apr 03 18:33:48 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Thu Apr 03 19:06:58 2014 +0200 @@ -26,14 +26,15 @@ import java.io.*; import java.util.*; +import java.util.concurrent.atomic.*; import com.oracle.graal.api.code.*; import com.oracle.graal.debug.*; +import com.oracle.graal.truffle.OptimizedCallUtils.InlinedNodeCountFilter; import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.impl.*; import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.nodes.NodeUtil.NodeCountFilter; /** * Call target that is optimized by Graal upon surpassing a specific invocation threshold. @@ -44,26 +45,41 @@ protected InstalledCode installedCode; protected boolean compilationEnabled; - private boolean inlined; protected int callCount; - + private TruffleInliningResult inliningResult; protected final CompilationProfile compilationProfile; protected final CompilationPolicy compilationPolicy; - private SpeculationLog speculationLog = new SpeculationLog(); + private final SpeculationLog speculationLog = new SpeculationLog(); private OptimizedCallTarget splitSource; + private final AtomicInteger callSitesKnown = new AtomicInteger(0); + public OptimizedCallTarget(RootNode rootNode, int invokeCounter, int compilationThreshold, boolean compilationEnabled, CompilationPolicy compilationPolicy) { super(rootNode); - this.compilationEnabled = compilationEnabled; this.compilationPolicy = compilationPolicy; - this.compilationProfile = new CompilationProfile(compilationThreshold, invokeCounter, rootNode.toString()); if (TruffleCallTargetProfiling.getValue()) { registerCallTarget(this); } } + public int getKnownCallSiteCount() { + return callSitesKnown.get(); + } + + public void incrementKnownCallSite() { + callSitesKnown.incrementAndGet(); + } + + public void decrementKnownCallSite() { + callSitesKnown.decrementAndGet(); + } + + public TruffleInliningResult getInliningResult() { + return inliningResult; + } + public OptimizedCallTarget getSplitSource() { return splitSource; } @@ -100,56 +116,19 @@ return executeHelper(caller, arguments); } - public void performInlining() { - if (!TruffleCompilerOptions.TruffleFunctionInlining.getValue()) { - return; - } - if (inlined) { + public final void performInlining() { + if (!shouldInline()) { return; } - inlined = true; - logInliningStart(this); - PriorityQueue queue = new PriorityQueue<>(); - - // Used to avoid running in cycles or inline nodes in Truffle trees - // which do not suffice the tree property. - Set visitedCallNodes = new HashSet<>(); + if (inliningResult != null) { + return; + } - queueCallSitesForInlining(this, getRootNode(), visitedCallNodes, queue); - TruffleInliningProfile callSite = queue.poll(); - while (callSite != null) { - if (callSite.isInliningAllowed()) { - OptimizedCallNode callNode = callSite.getCallNode(); - RootNode inlinedRoot = callNode.inlineImpl().getCurrentRootNode(); - logInlined(this, callSite); - assert inlinedRoot != null; - queueCallSitesForInlining(this, inlinedRoot, visitedCallNodes, queue); - } else { - logInliningFailed(callSite); - } - callSite = queue.poll(); - } - logInliningDone(this); - } - - private static void queueCallSitesForInlining(final OptimizedCallTarget target, RootNode rootNode, final Set visitedCallSites, final PriorityQueue queue) { - rootNode.accept(new NodeVisitor() { - public boolean visit(Node node) { - if (node instanceof OptimizedCallNode) { - OptimizedCallNode call = ((OptimizedCallNode) node); - if (!call.isInlined() && !visitedCallSites.contains(call)) { - queue.add(call.createInliningProfile(target)); - visitedCallSites.add(call); - } - RootNode root = call.getCurrentRootNode(); - if (root != null && call.isInlined()) { - root.accept(this); - } - } - return true; - } - }); + TruffleInliningHandler handler = new TruffleInliningHandler(this, new DefaultInliningPolicy(), new HashMap()); + int startNodeCount = OptimizedCallUtils.countNonTrivialNodes(null, new TruffleCallPath(this)); + this.inliningResult = handler.inline(startNodeCount); + logInliningDecision(this, inliningResult, handler); } protected boolean shouldCompile() { @@ -160,6 +139,26 @@ return TruffleFunctionInlining.getValue(); } + protected final void cancelInlinedCallOptimization() { + if (getInliningResult() != null) { + for (TruffleCallPath path : getInliningResult()) { + OptimizedCallNode top = path.getCallNode(); + top.notifyInlining(); + top.getCurrentCallTarget().cancelInstalledTask(top, top, "Inlined"); + } + } + } + + protected final void onCompilationDone() { + if (inliningResult != null) { + for (TruffleCallPath path : inliningResult) { + path.getCallNode().notifyInliningDone(); + } + } + } + + protected abstract void cancelInstalledTask(Node oldNode, Node newNode, CharSequence reason); + protected abstract void invalidate(Node oldNode, Node newNode, CharSequence reason); public Object executeHelper(PackedFrame caller, Arguments args) { @@ -174,32 +173,12 @@ @Override public void reportLoopCount(int count) { compilationProfile.reportLoopCount(count); - - // delegate to inlined call sites - for (CallNode callNode : getRootNode().getCachedCallNodes()) { - if (callNode.isInlined()) { - callNode.getRootNode().reportLoopCount(count); - } - } } @Override public void nodeReplaced(Node oldNode, Node newNode, CharSequence reason) { compilationProfile.reportNodeReplaced(); invalidate(oldNode, newNode, reason); - - // delegate to inlined call sites - for (CallNode callNode : getRootNode().getCachedCallNodes()) { - if (callNode.isInlined()) { - CallTarget target = callNode.getRootNode().getCallTarget(); - if (target instanceof ReplaceObserver) { - ((ReplaceObserver) target).nodeReplaced(oldNode, newNode, reason); - } - } - if (callNode instanceof OptimizedCallNode) { - ((OptimizedCallNode) callNode).nodeReplaced(oldNode, newNode, reason); - } - } } public SpeculationLog getSpeculationLog() { @@ -208,66 +187,49 @@ public Map getDebugProperties() { Map properties = new LinkedHashMap<>(); - addASTSizeProperty(getRootNode(), properties); + addASTSizeProperty(getInliningResult(), new TruffleCallPath(this), properties); properties.putAll(getCompilationProfile().getDebugProperties()); return properties; } - private static void logInliningFailed(TruffleInliningProfile callSite) { - if (TraceTruffleInliningDetails.getValue()) { - log(2, "inline failed", callSite.getCallNode().getCurrentCallTarget().toString(), callSite.getDebugProperties()); + private static void logInliningDecision(OptimizedCallTarget target, TruffleInliningResult result, TruffleInliningHandler handler) { + if (!TraceTruffleInlining.getValue()) { + return; } + + List profiles = handler.lookupProfiles(result, new TruffleCallPath(target)); + + Collections.sort(profiles); // sorts by hierarchy and source section + + logInliningStart(target); + for (TruffleInliningProfile profile : profiles) { + TruffleCallPath path = profile.getCallPath(); + if (path.getRootCallTarget() == target) { + String msg = result.isInlined(path) ? "inline success" : "inline failed"; + logInlinedImpl(msg, result, handler.getProfiles().get(path), path); + } + } + logInliningDone(target); } - private static void logInlined(final OptimizedCallTarget target, TruffleInliningProfile callSite) { - if (TraceTruffleInliningDetails.getValue() || TraceTruffleInlining.getValue()) { - log(2, "inline success", callSite.getCallNode().getCurrentCallTarget().toString(), callSite.getDebugProperties()); - - if (TraceTruffleInliningDetails.getValue()) { - RootNode root = callSite.getCallNode().getCurrentCallTarget().getRootNode(); - root.accept(new NodeVisitor() { - int depth = 1; - - public boolean visit(Node node) { - if (node instanceof OptimizedCallNode) { - OptimizedCallNode callNode = ((OptimizedCallNode) node); - RootNode inlinedRoot = callNode.getCurrentRootNode(); - - if (inlinedRoot != null) { - Map properties = new LinkedHashMap<>(); - addASTSizeProperty(callNode.getCurrentRootNode(), properties); - properties.putAll(callNode.createInliningProfile(target).getDebugProperties()); - String message; - if (callNode.isInlined()) { - message = "inline success"; - } else { - message = "inline dispatch"; - } - log(2 + (depth * 2), message, callNode.getCurrentCallTarget().toString(), properties); - - if (callNode.isInlined()) { - depth++; - inlinedRoot.accept(this); - depth--; - } - } - } - return true; - } - }); - } + private static void logInlinedImpl(String status, TruffleInliningResult result, TruffleInliningProfile profile, TruffleCallPath path) { + Map properties = new LinkedHashMap<>(); + addASTSizeProperty(result, path, properties); + if (profile != null) { + properties.putAll(profile.getDebugProperties()); } + log((path.getDepth() * 2), status, path.getCallTarget().toString(), properties); } private static void logInliningStart(OptimizedCallTarget target) { - if (TraceTruffleInliningDetails.getValue()) { + if (TraceTruffleInlining.getValue()) { log(0, "inline start", target.toString(), target.getDebugProperties()); } } private static void logInliningDone(OptimizedCallTarget target) { - if (TraceTruffleInliningDetails.getValue()) { + if (TraceTruffleInlining.getValue()) { log(0, "inline done", target.toString(), target.getDebugProperties()); } } @@ -349,29 +311,27 @@ static void logSplit(OptimizedCallNode callNode, OptimizedCallTarget target, OptimizedCallTarget newTarget) { if (TraceTruffleSplitting.getValue()) { Map properties = new LinkedHashMap<>(); - addASTSizeProperty(target.getRootNode(), properties); + addASTSizeProperty(target.getInliningResult(), new TruffleCallPath(target), properties); properties.put("Split#", ++splitCount); properties.put("Source", callNode.getEncapsulatingSourceSection()); log(0, "split", newTarget.toString(), properties); } } - static void addASTSizeProperty(RootNode target, Map properties) { - int polymorphicCount = NodeUtil.countNodes(target.getRootNode(), new NodeCountFilter() { - public boolean isCounted(Node node) { + static void addASTSizeProperty(TruffleInliningResult inliningResult, TruffleCallPath countedPath, Map properties) { + int polymorphicCount = OptimizedCallUtils.countNodes(inliningResult, countedPath, new InlinedNodeCountFilter() { + public boolean isCounted(TruffleCallPath path, Node node) { return node.getCost() == NodeCost.POLYMORPHIC; } - }, true); + }); - int megamorphicCount = NodeUtil.countNodes(target.getRootNode(), new NodeCountFilter() { - public boolean isCounted(Node node) { + int megamorphicCount = OptimizedCallUtils.countNodes(inliningResult, countedPath, new InlinedNodeCountFilter() { + public boolean isCounted(TruffleCallPath path, Node node) { return node.getCost() == NodeCost.MEGAMORPHIC; } - }, true); + }); - String value = String.format("%4d (%d/%d)", NodeUtil.countNodes(target.getRootNode(), OptimizedCallNodeProfile.COUNT_FILTER, true), // - polymorphicCount, megamorphicCount); // - + String value = String.format("%4d (%d/%d)", OptimizedCallUtils.countNonTrivialNodes(inliningResult, countedPath), polymorphicCount, megamorphicCount); properties.put("ASTSize", value); } @@ -429,15 +389,13 @@ continue; } - int nodeCount = NodeUtil.countNodes(callTarget.getRootNode(), OptimizedCallNodeProfile.COUNT_FILTER, false); - int inlinedCallSiteCount = NodeUtil.countNodes(callTarget.getRootNode(), OptimizedCallNodeProfile.COUNT_FILTER, true); + int nodeCount = OptimizedCallUtils.countNonTrivialNodes(callTarget.getInliningResult(), new TruffleCallPath(callTarget)); String comment = callTarget.installedCode == null ? " int" : ""; comment += callTarget.compilationEnabled ? "" : " fail"; - OUT.printf("%-50s | %10d | %15d | %10d | %3d%s\n", callTarget.getRootNode(), callTarget.callCount, inlinedCallSiteCount, nodeCount, - callTarget.getCompilationProfile().getInvalidationCount(), comment); + OUT.printf("%-50s | %10d | %15d | %10d | %3d%s\n", callTarget.getRootNode(), callTarget.callCount, nodeCount, nodeCount, callTarget.getCompilationProfile().getInvalidationCount(), comment); totalCallCount += callTarget.callCount; - totalInlinedCallSiteCount += inlinedCallSiteCount; + totalInlinedCallSiteCount += nodeCount; totalNodeCount += nodeCount; totalInvalidationCount += callTarget.getCompilationProfile().getInvalidationCount(); } diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetImpl.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetImpl.java Thu Apr 03 18:33:48 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTargetImpl.java Thu Apr 03 19:06:58 2014 +0200 @@ -96,7 +96,8 @@ cancelInstalledTask(oldNode, newNode, reason); } - private void cancelInstalledTask(Node oldNode, Node newNode, CharSequence reason) { + @Override + protected void cancelInstalledTask(Node oldNode, Node newNode, CharSequence reason) { Future task = this.installedCodeTask; if (task != null) { task.cancel(true); @@ -145,6 +146,7 @@ return null; } else { performInlining(); + cancelInlinedCallOptimization(); logOptimizingQueued(this); this.installedCodeTask = compiler.compile(this); if (!TruffleBackgroundCompilation.getValue()) { @@ -171,6 +173,8 @@ } } return null; + } finally { + onCompilationDone(); } } diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallUtils.java Thu Apr 03 19:06:58 2014 +0200 @@ -0,0 +1,120 @@ +/* + * 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 com.oracle.truffle.api.nodes.*; + +class OptimizedCallUtils { + + public abstract static class InlinedCallVisitor implements NodeVisitor { + + private TruffleCallPath currentPath; + private final TruffleInliningResult inliningDecision; + + public InlinedCallVisitor(TruffleInliningResult inliningDecision, TruffleCallPath initialPath) { + this.inliningDecision = inliningDecision; + this.currentPath = initialPath; + } + + public final TruffleInliningResult getInliningDecision() { + return inliningDecision; + } + + public final boolean visit(Node node) { + if (node instanceof OptimizedCallNode) { + OptimizedCallNode callNode = ((OptimizedCallNode) node); + this.currentPath = new TruffleCallPath(this.currentPath, callNode); + try { + boolean result = visit(currentPath, node); + TruffleInliningResult decision = inliningDecision; + if (decision != null && decision.isInlined(currentPath)) { + callNode.getCurrentRootNode().accept(this); + } + return result; + } finally { + this.currentPath = this.currentPath.getParent(); + } + } else { + return visit(currentPath, node); + } + } + + public abstract boolean visit(TruffleCallPath path, Node node); + + } + + public static int countNodes(TruffleInliningResult decision, TruffleCallPath path, InlinedNodeCountFilter filter) { + InlinedNodeCountVisitor nodeCount = new InlinedNodeCountVisitor(decision, path, filter); + path.getCallTarget().getRootNode().accept(nodeCount); + return nodeCount.nodeCount; + } + + public static int countCalls(TruffleInliningResult decision, TruffleCallPath path) { + InlinedNodeCountVisitor nodeCount = new InlinedNodeCountVisitor(decision, path, new InlinedNodeCountFilter() { + public boolean isCounted(TruffleCallPath p, Node node) { + return node instanceof CallNode; + } + }); + path.getCallTarget().getRootNode().accept(nodeCount); + return nodeCount.nodeCount; + } + + public interface InlinedNodeCountFilter { + + boolean isCounted(TruffleCallPath path, Node node); + } + + private static final class InlinedNodeCountVisitor extends InlinedCallVisitor { + + private final InlinedNodeCountFilter filter; + int nodeCount; + + private InlinedNodeCountVisitor(TruffleInliningResult decision, TruffleCallPath initialPath, InlinedNodeCountFilter filter) { + super(decision, initialPath); + this.filter = filter; + } + + @Override + public boolean visit(TruffleCallPath path, Node node) { + if (filter == null || filter.isCounted(path, node)) { + nodeCount++; + } + return true; + } + + } + + static int countNonTrivialNodes(TruffleInliningResult state, TruffleCallPath path) { + return countNodes(state, path, new InlinedNodeCountFilter() { + + public boolean isCounted(TruffleCallPath p, Node node) { + NodeCost cost = node.getCost(); + if (cost != null && cost != NodeCost.NONE && cost != NodeCost.UNINITIALIZED) { + return true; + } + return false; + } + }); + } + +} diff -r 1422f0bd55e3 -r a31d807757ee 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 Thu Apr 03 18:33:48 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Thu Apr 03 19:06:58 2014 +0200 @@ -56,6 +56,7 @@ 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.*; /** @@ -68,6 +69,7 @@ private Set constantReceivers; private final TruffleCache truffleCache; private final ResolvedJavaType frameType; + private final ResolvedJavaMethod callInlinedMethod; public PartialEvaluator(Providers providers, TruffleCache truffleCache) { this.providers = providers; @@ -75,6 +77,11 @@ this.canonicalizer = new CanonicalizerPhase(!ImmutableCode.getValue(), customCanonicalizer); this.truffleCache = truffleCache; this.frameType = providers.getMetaAccess().lookupJavaType(FrameWithoutBoxing.class); + try { + callInlinedMethod = providers.getMetaAccess().lookupJavaMethod(OptimizedCallNode.class.getDeclaredMethod("callInlined", PackedFrame.class, Arguments.class)); + } catch (NoSuchMethodException ex) { + throw new RuntimeException(ex); + } } public StructuredGraph createGraph(final OptimizedCallTarget callTarget, final Assumptions assumptions) { @@ -112,10 +119,10 @@ Debug.dump(graph, "Before inlining"); // Make sure frame does not escape. - expandTree(graph, assumptions); + expandTree(callTarget, graph, assumptions); - if (Thread.interrupted()) { - return graph; + if (Thread.currentThread().isInterrupted()) { + return null; } new VerifyFrameDoesNotEscapePhase().apply(graph, false); @@ -166,12 +173,13 @@ return graph; } - private void expandTree(StructuredGraph graph, Assumptions assumptions) { + private void expandTree(OptimizedCallTarget target, StructuredGraph graph, Assumptions assumptions) { PhaseContext phaseContext = new PhaseContext(providers, assumptions); TruffleExpansionLogger expansionLogger = null; if (TraceTruffleExpansion.getValue()) { expansionLogger = new TruffleExpansionLogger(graph); } + Map methodTargetToStack = new HashMap<>(); boolean changed; do { changed = false; @@ -197,13 +205,18 @@ } StructuredGraph inlineGraph = replacements.getMethodSubstitution(methodCallTargetNode.targetMethod()); + + if (inlineGraph == null) { + inlineGraph = expandInlinableCallNode(target, methodTargetToStack, assumptions, phaseContext, methodCallTargetNode); + } + if (inlineGraph == null && !Modifier.isNative(methodCallTargetNode.targetMethod().getModifiers())) { inlineGraph = parseGraph(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, phaseContext); } if (inlineGraph != null) { try (Indent indent = Debug.logAndIndent("inline graph %s", methodCallTargetNode.targetMethod())) { - + preExpandTruffleCallPath(inlineGraph, methodTargetToStack, methodTargetToStack.get(methodCallTargetNode)); int nodeCountBefore = graph.getNodeCount(); Mark mark = graph.getMark(); if (TraceTruffleExpansion.getValue()) { @@ -215,6 +228,7 @@ if (TraceTruffleExpansion.getValue()) { expansionLogger.postExpand(inlined); } + postExpandTruffleCallPath(methodTargetToStack, inlined); // } if (Debug.isDumpEnabled()) { Debug.log("dump enabled"); @@ -228,10 +242,6 @@ } } - if (Thread.interrupted()) { - return; - } - if (graph.getNodeCount() > TruffleCompilerOptions.TruffleGraphMaxNodes.getValue()) { throw new BailoutException("Truffle compilation is exceeding maximum node count: " + graph.getNodeCount()); } @@ -243,6 +253,59 @@ } } + private static void preExpandTruffleCallPath(StructuredGraph inlineGraph, Map methodTargetToCallPath, TruffleCallPath truffleCallPath) { + for (MethodCallTargetNode methodTargetNode : inlineGraph.getNodes(MethodCallTargetNode.class)) { + methodTargetToCallPath.put(methodTargetNode, truffleCallPath); + } + } + + private static void postExpandTruffleCallPath(Map methodCallTargetToCallPath, Map nodeUpdates) { + for (Object key : methodCallTargetToCallPath.keySet().toArray()) { + if (nodeUpdates.containsKey(key)) { + methodCallTargetToCallPath.put(nodeUpdates.get(key), methodCallTargetToCallPath.get(key)); + methodCallTargetToCallPath.remove(key); + } + } + } + + private StructuredGraph expandInlinableCallNode(OptimizedCallTarget target, Map methodCallToCallPath, Assumptions assumptions, PhaseContext phaseContext, + MethodCallTargetNode methodCallTargetNode) { + ValueNode receiverNode = methodCallTargetNode.receiver(); + if (receiverNode == null || !receiverNode.isConstant() || !receiverNode.asConstant().getKind().isObject()) { + return null; + } + Object receiverValue = receiverNode.asConstant().asObject(); + if (!(receiverValue instanceof OptimizedCallNode)) { + return null; + } + + ResolvedJavaMethod method = methodCallTargetNode.targetMethod(); + if (!method.getName().equals("call") || method.getSignature().getParameterCount(false) != 2) { + return null; + } + assert method.getSignature().equals(callInlinedMethod.getSignature()) : "Signature of original and inlined method must match."; + + OptimizedCallNode callNode = (OptimizedCallNode) receiverValue; + TruffleCallPath callPath = methodCallToCallPath.get(methodCallTargetNode); + if (callPath == null) { + callPath = new TruffleCallPath(target); + } + callPath = new TruffleCallPath(callPath, callNode); + + TruffleInliningResult decision = target.getInliningResult(); + if (decision == null || !decision.isInlined(callPath)) { + // the call target decided not to inline this call + return null; + } + + // inline truffle call -> redirect OptimizedCallNode#ca to OptimizedCallNode#inlinedCall + StructuredGraph graph = parseGraph(callInlinedMethod, methodCallTargetNode.arguments(), assumptions, phaseContext); + + methodCallToCallPath.put(methodCallTargetNode, callPath); + + return graph; + } + private boolean isFrame(ValueNode receiver) { return receiver instanceof NewFrameNode || Objects.equals(ObjectStamp.typeOrNull(receiver.stamp()), frameType); } diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCallPath.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCallPath.java Thu Apr 03 19:06:58 2014 +0200 @@ -0,0 +1,219 @@ +/* + * 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 java.util.*; + +import com.oracle.truffle.api.*; + +public final class TruffleCallPath implements Iterable, Comparable { + + private final OptimizedCallNode callNode; + private final TruffleCallPath parent; + private final OptimizedCallTarget rootCallTarget; + + public TruffleCallPath(OptimizedCallTarget parent) { + this.rootCallTarget = Objects.requireNonNull(parent); + this.callNode = null; + this.parent = null; + } + + public TruffleCallPath(TruffleCallPath parent, OptimizedCallNode node) { + this.parent = Objects.requireNonNull(parent); + assert !parent.containsPath(this) : "cyclic path detected"; + this.callNode = Objects.requireNonNull(node); + this.rootCallTarget = parent.getRootCallTarget(); + } + + public boolean isRoot() { + return parent == null && rootCallTarget != null; + } + + private boolean containsPath(TruffleCallPath p) { + return p == this || (getParent() != null && this.getParent().containsPath(p)); + } + + public OptimizedCallTarget getRootCallTarget() { + return rootCallTarget; + } + + @Override + public String toString() { + return (parent != null ? (parent.toString() + " -> ") : "") + getCallTarget().toString(); + } + + public Iterator iterator() { + return toList().iterator(); + } + + public OptimizedCallTarget getCallTarget() { + return parent == null ? rootCallTarget : callNode.getCurrentCallTarget(); + } + + public List toList() { + List list = new ArrayList<>(); + toListImpl(list); + return list; + } + + private void toListImpl(List list) { + if (parent != null) { + parent.toListImpl(list); + } + list.add(this); + } + + public TruffleCallPath getParent() { + return parent; + } + + public OptimizedCallNode getCallNode() { + return callNode; + } + + public int getDepth() { + return parent == null ? 0 : (parent.getDepth() + 1); + } + + public int compareTo(TruffleCallPath o) { + return Objects.compare(this, o, new PathComparator()); + } + + private static class PathComparator implements Comparator { + public int compare(TruffleCallPath c1, TruffleCallPath c2) { + if (c1 == c2) { + return 0; + } + + Iterator p1 = c1.toList().iterator(); + Iterator p2 = c2.toList().iterator(); + + int cmp = 0; + while (cmp == 0 && (p1.hasNext() || p2.hasNext())) { + TruffleCallPath o1; + TruffleCallPath o2; + if (p1.hasNext()) { + o1 = p1.next(); + } else { + return -1; + } + if (p2.hasNext()) { + o2 = p2.next(); + } else { + return 1; + } + + if (o1 == o2) { + continue; + } + + SourceSection s1; + if (o1.callNode != null) { + s1 = o1.callNode.getEncapsulatingSourceSection(); + } else { + s1 = o1.getCallTarget().getRootNode().getSourceSection(); + } + + SourceSection s2; + if (o2.callNode != null) { + s2 = o2.callNode.getEncapsulatingSourceSection(); + } else { + s2 = o2.getCallTarget().getRootNode().getSourceSection(); + } + cmp = compareSourceSection(s2, s1); + + if (cmp == 0) { + cmp = o1.getCallTarget().toString().compareTo(o2.getCallTarget().toString()); + } + } + return cmp; + } + } + + private static int compareSourceSection(SourceSection s1, SourceSection s2) { + return Objects.compare(s1, s2, new Comparator() { + public int compare(SourceSection o1, SourceSection o2) { + if (o1 == o2) { + return 0; + } + if (o1 == null || o2 == null) { + return 0; + } + int cmp = 0; + if (o1.getSource() != null && o2.getSource() != null && !Objects.equals(o1.getSource().getName(), o2.getSource().getName())) { + cmp = o2.getSource().getName().compareTo(o1.getSource().getName()); + } + if (cmp == 0) { + cmp = o2.getStartLine() - o1.getStartLine(); + } + if (cmp == 0) { + cmp = o2.getStartColumn() - o1.getStartColumn(); + } + return cmp; + } + }); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((callNode == null) ? 0 : callNode.hashCode()); + result = prime * result + ((parent == null) ? 0 : parent.hashCode()); + result = prime * result + ((rootCallTarget == null) ? 0 : rootCallTarget.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TruffleCallPath) { + TruffleCallPath other = (TruffleCallPath) obj; + if (other.callNode != callNode) { + return false; + } + if (!Objects.equals(other.parent, parent)) { + return false; + } + if (!Objects.equals(other.rootCallTarget, rootCallTarget)) { + return false; + } + return true; + } + return false; + } + + public TruffleCallPath append(TruffleCallPath path) { + if (getCallTarget() != path.getRootCallTarget()) { + throw new IllegalArgumentException("Pathes are not compatible and can therfore not be appended."); + } + + TruffleCallPath append = this; + for (TruffleCallPath childPath : path) { + if (!childPath.isRoot()) { + append = new TruffleCallPath(append, childPath.getCallNode()); + } + } + return append; + } + +} diff -r 1422f0bd55e3 -r a31d807757ee 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 Thu Apr 03 18:33:48 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java Thu Apr 03 19:06:58 2014 +0200 @@ -26,7 +26,6 @@ import static com.oracle.graal.compiler.GraalCompiler.*; import static com.oracle.graal.truffle.TruffleCompilerOptions.*; -import java.io.*; import java.util.*; import java.util.concurrent.*; @@ -58,8 +57,6 @@ */ public class TruffleCompilerImpl implements TruffleCompiler { - private static final PrintStream OUT = TTY.out().out(); - private final Providers providers; private final Suites suites; private final PartialEvaluator partialEvaluator; @@ -142,18 +139,16 @@ OptimizedCallTargetImpl.logOptimizingStart(compilable); } - if (TraceTruffleInliningTree.getValue()) { - NodeUtil.printInliningTree(OUT, compilable.getRootNode()); - } - long timeCompilationStarted = System.nanoTime(); Assumptions assumptions = new Assumptions(true); try (TimerCloseable a = PartialEvaluationTime.start()) { graph = partialEvaluator.createGraph(compilable, assumptions); } - if (Thread.interrupted()) { + + if (Thread.currentThread().isInterrupted()) { return null; } + long timePartialEvaluationFinished = System.nanoTime(); int nodeCountPartialEval = graph.getNodeCount(); InstalledCode compiledMethod = compileMethodHelper(graph, assumptions, compilable.toString(), compilable.getSpeculationLog()); @@ -163,19 +158,24 @@ if (compiledMethod == null) { throw new BailoutException("Could not install method, code cache is full!"); } + if (!compiledMethod.isValid()) { return null; } if (TraceTruffleCompilation.getValue()) { byte[] code = compiledMethod.getCode(); + int calls = OptimizedCallUtils.countCalls(compilable.getInliningResult(), new TruffleCallPath(compilable)); + int inlinedCalls = (compilable.getInliningResult() != null ? compilable.getInliningResult().size() : 0); + int dispatchedCalls = calls - inlinedCalls; Map properties = new LinkedHashMap<>(); - OptimizedCallTarget.addASTSizeProperty(compilable.getRootNode(), properties); + OptimizedCallTarget.addASTSizeProperty(compilable.getInliningResult(), new TruffleCallPath(compilable), properties); properties.put("Time", String.format("%5.0f(%4.0f+%-4.0f)ms", // (timeCompilationFinished - timeCompilationStarted) / 1e6, // (timePartialEvaluationFinished - timeCompilationStarted) / 1e6, // (timeCompilationFinished - timePartialEvaluationFinished) / 1e6)); - properties.put("Nodes", String.format("%5d/%5d", nodeCountPartialEval, nodeCountLowered)); + properties.put("CallNodes", String.format("I %5d/D %5d", inlinedCalls, dispatchedCalls)); + properties.put("GraalNodes", String.format("%5d/%5d", nodeCountPartialEval, nodeCountLowered)); properties.put("CodeSize", code != null ? code.length : 0); properties.put("Source", formatSourceSection(compilable.getRootNode().getSourceSection())); @@ -222,7 +222,7 @@ result.setAssumptions(newAssumptions); - InstalledCode installedCode = null; + InstalledCode installedCode; try (Scope s = Debug.scope("CodeInstall", providers.getCodeCache()); TimerCloseable a = CodeInstallationTime.start()) { installedCode = providers.getCodeCache().addMethod(graph.method(), result, speculationLog); } catch (Throwable e) { diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java Thu Apr 03 18:33:48 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java Thu Apr 03 19:06:58 2014 +0200 @@ -58,7 +58,7 @@ @Option(help = "Stop inlining if caller's cumulative tree size would exceed this limit") public static final OptionValue TruffleInliningMaxCallerSize = new OptionValue<>(2250); @Option(help = "Skip inlining candidate if its tree size exceeds this limit") - public static final OptionValue TruffleInliningMaxCalleeSize = new OptionValue<>(350); + public static final OptionValue TruffleInliningMaxCalleeSize = new OptionValue<>(500); @Option(help = "Call frequency relative to call target") public static final OptionValue TruffleInliningMinFrequency = new OptionValue<>(0.3); @Option(help = "Allow inlining of less hot candidates if tree size is small") @@ -100,10 +100,6 @@ @Option(help = "") public static final OptionValue TraceTruffleInlining = new OptionValue<>(false); @Option(help = "") - public static final OptionValue TraceTruffleInliningTree = new OptionValue<>(false); - @Option(help = "") - public static final OptionValue TraceTruffleInliningDetails = new OptionValue<>(false); - @Option(help = "") public static final OptionValue TraceTruffleSplitting = new OptionValue<>(false); @Option(help = "") public static final OptionValue TruffleCallTargetProfiling = new StableOptionValue<>(false); diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningHandler.java Thu Apr 03 19:06:58 2014 +0200 @@ -0,0 +1,191 @@ +/* + * 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 java.util.*; + +import com.oracle.graal.truffle.OptimizedCallUtils.InlinedCallVisitor; +import com.oracle.truffle.api.nodes.*; + +public final class TruffleInliningHandler { + + private final ProfileScoreComparator inliningOrder = new ProfileScoreComparator(); + + private final OptimizedCallTarget callTarget; + private final TruffleInliningPolicy policy; + private final Map profiles; + private final Map inliningResultCache; + + public TruffleInliningHandler(OptimizedCallTarget callTarget, TruffleInliningPolicy policy, Map inliningResultCache) { + this.callTarget = callTarget; + this.policy = policy; + this.profiles = new HashMap<>(); + this.inliningResultCache = inliningResultCache; + } + + public TruffleInliningResult inline(int originalTotalNodeCount) { + Set inlinedPathes = new HashSet<>(); + TruffleInliningResult result = new TruffleInliningResult(callTarget, inlinedPathes); + TruffleCallPath startPath = new TruffleCallPath(callTarget); + + PriorityQueue queue = new PriorityQueue<>(10, inliningOrder); + queueCallSitesForInlining(result, startPath, queue); + + int budget = TruffleCompilerOptions.TruffleInliningMaxCallerSize.getValue() - originalTotalNodeCount; + int index = 0; + while (!queue.isEmpty()) { + TruffleInliningProfile profile = queue.poll(); + profile.setQueryIndex(index); + budget = tryInline(queue, result, inlinedPathes, profile, budget); + profiles.put(profile.getCallPath(), profile); + index++; + } + return result; + } + + private int tryInline(PriorityQueue queue, TruffleInliningResult result, Set inlinedPathes, TruffleInliningProfile profile, int budget) { + + if (policy.isAllowed(result, profile, budget)) { + int remainingBudget; + inlinedPathes.add(profile.getCallPath()); + if (policy.isAllowedDeep(result, profile, budget)) { + if (profile.getRecursiveResult() != null) { + TruffleInliningResult inliningResult = profile.getRecursiveResult(); + for (TruffleCallPath recursiveInlinedPath : inliningResult) { + inlinedPathes.add(profile.getCallPath().append(recursiveInlinedPath)); + } + } + remainingBudget = budget - profile.getDeepNodeCount(); + } else { + remainingBudget = budget - profile.getNodeCount(); + } + + queueCallSitesForInlining(result, profile.getCallPath(), queue); + return remainingBudget; + } + + return budget; + } + + public TruffleInliningPolicy getPolicy() { + return policy; + } + + public Map getProfiles() { + return profiles; + } + + private void queueCallSitesForInlining(final TruffleInliningResult currentDecision, TruffleCallPath fromPath, final Collection queue) { + fromPath.getCallTarget().getRootNode().accept(new InlinedCallVisitor(currentDecision, fromPath) { + @Override + public boolean visit(TruffleCallPath path, Node node) { + if (node instanceof OptimizedCallNode) { + if (!currentDecision.isInlined(path)) { + addToQueue(queue, currentDecision, path); + } + } + return true; + } + }); + } + + private void addToQueue(final Collection queue, final TruffleInliningResult currentDecision, TruffleCallPath path) { + queue.add(lookupProfile(currentDecision, path)); + } + + public List lookupProfiles(final TruffleInliningResult currentDecision, TruffleCallPath fromPath) { + final List pathes = new ArrayList<>(); + fromPath.getCallTarget().getRootNode().accept(new InlinedCallVisitor(currentDecision, fromPath) { + @Override + public boolean visit(TruffleCallPath path, Node node) { + if (node instanceof OptimizedCallNode) { + pathes.add(lookupProfile(currentDecision, path)); + } + return true; + } + }); + return pathes; + } + + public TruffleInliningProfile lookupProfile(TruffleInliningResult state, TruffleCallPath callPath) { + TruffleInliningProfile profile = profiles.get(callPath); + if (profile != null) { + return profile; + } + + int callSites = callPath.getCallTarget().getKnownCallSiteCount(); + int nodeCount = OptimizedCallUtils.countNonTrivialNodes(state, callPath); + double frequency = calculatePathFrequency(callPath); + boolean forced = callPath.getCallNode().isInlined(); + TruffleInliningResult recursiveResult = lookupChildResult(callPath, nodeCount); + int deepNodeCount = OptimizedCallUtils.countNonTrivialNodes(recursiveResult, new TruffleCallPath(callPath.getCallTarget())); + + profile = new TruffleInliningProfile(callPath, callSites, nodeCount, deepNodeCount, frequency, forced, recursiveResult); + profile.setScore(policy.calculateScore(profile)); + profiles.put(callPath, profile); + + return profile; + } + + private TruffleInliningResult lookupChildResult(TruffleCallPath callPath, int nodeCount) { + OptimizedCallTarget target = callPath.getCallTarget(); + + TruffleInliningResult recursiveResult = target.getInliningResult(); + + if (recursiveResult == null) { + recursiveResult = inliningResultCache.get(callPath.getCallTarget()); + + if (recursiveResult == null) { + if (inliningResultCache.containsKey(target)) { + // cancel on recursion + return new TruffleInliningResult(target, new HashSet()); + } + inliningResultCache.put(target, null); + TruffleInliningHandler handler = new TruffleInliningHandler(target, policy, inliningResultCache); + recursiveResult = handler.inline(nodeCount); + inliningResultCache.put(callPath.getCallTarget(), recursiveResult); + } + } + return recursiveResult; + } + + private static double calculatePathFrequency(TruffleCallPath callPath) { + int parentCallCount = -1; + double f = 1.0d; + for (TruffleCallPath path : callPath.toList()) { + if (parentCallCount != -1) { + f *= path.getCallNode().getCallCount() / (double) parentCallCount; + } + parentCallCount = path.getCallTarget().getCompilationProfile().getCallCount(); + } + return f; + } + + private final class ProfileScoreComparator implements Comparator { + + public int compare(TruffleInliningProfile o1, TruffleInliningProfile o2) { + return Double.compare(o2.getScore(), o1.getScore()); + } + + } +} diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningPolicy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningPolicy.java Thu Apr 03 19:06:58 2014 +0200 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.truffle; + +public interface TruffleInliningPolicy { + + boolean isAllowed(TruffleInliningResult state, TruffleInliningProfile profile, int nodeCountBudget); + + boolean isAllowedDeep(TruffleInliningResult state, TruffleInliningProfile profile, int nodeCountBudget); + + double calculateScore(TruffleInliningProfile profile); + +} diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java Thu Apr 03 18:33:48 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningProfile.java Thu Apr 03 19:06:58 2014 +0200 @@ -24,14 +24,98 @@ import java.util.*; -public interface TruffleInliningProfile extends Comparable { +public class TruffleInliningProfile implements Comparable { + + private final TruffleCallPath callPath; + + private final int nodeCount; + private final int deepNodeCount; + private final int callSites; + private final double frequency; + private final boolean forced; + private final TruffleInliningResult recursiveResult; + + private String failedReason; + private int queryIndex = -1; + private double score; - OptimizedCallNode getCallNode(); + public TruffleInliningProfile(TruffleCallPath callPath, int callSites, int nodeCount, int deepNodeCount, double frequency, boolean forced, TruffleInliningResult recursiveResult) { + if (callPath.isRoot()) { + throw new IllegalArgumentException("Root call path not profilable."); + } + this.callSites = callSites; + this.callPath = callPath; + this.nodeCount = nodeCount; + this.deepNodeCount = deepNodeCount; + this.frequency = frequency; + this.forced = forced; + this.recursiveResult = recursiveResult; + } - boolean isInliningAllowed(); + public int getCallSites() { + return callSites; + } + + public int getNodeCount() { + return nodeCount; + } + + public TruffleInliningResult getRecursiveResult() { + return recursiveResult; + } + + public void setScore(double score) { + this.score = score; + } + + public double getScore() { + return score; + } - int compareTo(TruffleInliningProfile o); + public String getFailedReason() { + return failedReason; + } + + public void setQueryIndex(int queryIndex) { + this.queryIndex = queryIndex; + } + + public int getQueryIndex() { + return queryIndex; + } + + public void setFailedReason(String reason) { + this.failedReason = reason; + } + + public boolean isForced() { + return forced; + } + + public double getFrequency() { + return frequency; + } - Map getDebugProperties(); + public int getDeepNodeCount() { + return deepNodeCount; + } + + public TruffleCallPath getCallPath() { + return callPath; + } + + public int compareTo(TruffleInliningProfile o) { + return callPath.compareTo(o.callPath); + } + public Map getDebugProperties() { + Map properties = new LinkedHashMap<>(); + properties.put("callSites", callSites); + properties.put("nodeCount", nodeCount); + properties.put("frequency", frequency); + properties.put("score", score); + properties.put(String.format("index=%3d, force=%s", queryIndex, (forced ? "Y" : "N")), ""); + properties.put("reason", failedReason); + return properties; + } } diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningResult.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningResult.java Thu Apr 03 19:06:58 2014 +0200 @@ -0,0 +1,57 @@ +/* + * 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 java.util.*; + +public final class TruffleInliningResult implements Iterable { + + private final OptimizedCallTarget callTarget; + private final Set inlinedPathes; + + public TruffleInliningResult(OptimizedCallTarget callTarget, Set pathes) { + this.callTarget = callTarget; + this.inlinedPathes = pathes; + } + + public OptimizedCallTarget getCallTarget() { + return callTarget; + } + + public boolean isInlined(TruffleCallPath path) { + return inlinedPathes.contains(path); + } + + public int size() { + return inlinedPathes.size(); + } + + public Iterator iterator() { + return Collections.unmodifiableSet(inlinedPathes).iterator(); + } + + @Override + public String toString() { + return inlinedPathes.toString(); + } +} diff -r 1422f0bd55e3 -r a31d807757ee graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleTreeDumpHandler.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleTreeDumpHandler.java Thu Apr 03 18:33:48 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleTreeDumpHandler.java Thu Apr 03 19:06:58 2014 +0200 @@ -22,9 +22,12 @@ */ package com.oracle.graal.truffle; +import java.util.*; + import com.oracle.graal.debug.*; import com.oracle.truffle.api.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.GraphPrintVisitor.ChildSupplier; public class TruffleTreeDumpHandler implements DebugDumpHandler { @@ -32,10 +35,63 @@ public void dump(Object object, final String message) { if (object instanceof RootCallTarget) { RootCallTarget callTarget = (RootCallTarget) object; - if (callTarget.getRootNode() != null) { - new GraphPrintVisitor().beginGroup(callTarget.toString()).beginGraph(message).visit(callTarget.getRootNode()).printToNetwork(false); + dumpRootCallTarget(message, callTarget); + } + } + + private static void dumpRootCallTarget(final String message, RootCallTarget callTarget) { + if (callTarget.getRootNode() != null) { + final GraphPrintVisitor visitor = new GraphPrintVisitor(); + + final OptimizedCallTarget oct = (OptimizedCallTarget) callTarget; + + visitor.beginGroup(callTarget.toString()); + dumpInlinedCalls(visitor, oct); + dumpFullTree(visitor, message, oct); + visitor.printToNetwork(false); + } + } + + private static void dumpFullTree(final GraphPrintVisitor visitor, final String message, final OptimizedCallTarget oct) { + visitor.setChildSupplier(new ChildSupplier() { + private TruffleCallPath currentPath = new TruffleCallPath(oct); + + public Object startNode(Object callNode) { + if (callNode instanceof OptimizedCallNode) { + currentPath = new TruffleCallPath(currentPath, (OptimizedCallNode) callNode); + if (oct.getInliningResult() != null && oct.getInliningResult().isInlined(currentPath)) { + return ((OptimizedCallNode) callNode).getCurrentRootNode(); + } + } + return null; } - } + + public void endNode(Object callNode) { + if (callNode instanceof OptimizedCallNode) { + currentPath = currentPath.getParent(); + } + } + }); + + visitor.beginGraph(message).visit(oct.getRootNode()); + visitor.setChildSupplier(null); + } + + private static void dumpInlinedCalls(final GraphPrintVisitor visitor, final OptimizedCallTarget oct) { + final Set visitedRoots = new HashSet<>(); + oct.getRootNode().accept(new OptimizedCallUtils.InlinedCallVisitor(oct.getInliningResult(), new TruffleCallPath(oct)) { + @Override + public boolean visit(TruffleCallPath path, Node node) { + if (node instanceof OptimizedCallNode) { + OptimizedCallTarget target = ((OptimizedCallNode) node).getCurrentCallTarget(); + if (!visitedRoots.contains(target)) { + visitor.beginGraph("inlined " + target.toString()).visit(target.getRootNode()); + visitedRoots.add(target); + } + } + return true; + } + }); } public void close() {