Mercurial > hg > truffle
changeset 15689:54011d1d1ae3
Merge
author | Miguel Garcia <miguel.m.garcia@oracle.com> |
---|---|
date | Thu, 15 May 2014 17:25:49 +0200 |
parents | 8af4f23b3847 (diff) 1b0141150854 (current diff) |
children | d3c33144cab5 |
files | |
diffstat | 16 files changed, 1496 insertions(+), 1222 deletions(-) [+] |
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java Thu May 15 16:45:08 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java Thu May 15 17:25:49 2014 +0200 @@ -26,6 +26,7 @@ import java.lang.ref.*; import java.lang.reflect.*; +import com.oracle.graal.phases.common.inlining.policy.InlineEverythingPolicy; import org.junit.*; import com.oracle.graal.api.code.*; @@ -249,7 +250,7 @@ StructuredGraph graph = parse(snippet); HighTierContext highContext = new HighTierContext(getProviders(), new Assumptions(false), null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL); MidTierContext midContext = new MidTierContext(getProviders(), new Assumptions(false), getCodeCache().getTarget(), OptimisticOptimizations.ALL, graph.method().getProfilingInfo(), null); - new InliningPhase(new InliningPhase.InlineEverythingPolicy(), new CanonicalizerPhase(true)).apply(graph, highContext); + new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase(true)).apply(graph, highContext); new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext); new GuardLoweringPhase().apply(graph, midContext); new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/ComputeInliningRelevance.java Thu May 15 16:45:08 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,324 +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.phases.common.inlining; - -import static com.oracle.graal.graph.util.CollectionsAccess.*; - -import java.util.*; -import java.util.function.*; - -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; - -import edu.umd.cs.findbugs.annotations.*; - -public class ComputeInliningRelevance { - - private static final double EPSILON = 1d / Integer.MAX_VALUE; - private static final double UNINITIALIZED = -1D; - - private static final int EXPECTED_MIN_INVOKE_COUNT = 3; - private static final int EXPECTED_INVOKE_RATIO = 20; - private static final int EXPECTED_LOOP_COUNT = 3; - - private final StructuredGraph graph; - private final ToDoubleFunction<FixedNode> nodeProbabilities; - - /** - * Node relevances are pre-computed for all invokes if the graph contains loops. If there are no - * loops, the computation happens lazily based on {@link #rootScope}. - */ - private Map<FixedNode, Double> nodeRelevances; - /** - * This scope is non-null if (and only if) there are no loops in the graph. In this case, the - * root scope is used to compute invoke relevances on the fly. - */ - private Scope rootScope; - - public ComputeInliningRelevance(StructuredGraph graph, ToDoubleFunction<FixedNode> nodeProbabilities) { - this.graph = graph; - this.nodeProbabilities = nodeProbabilities; - } - - /** - * Initializes or updates the relevance computation. If there are no loops within the graph, - * most computation happens lazily. - */ - public void compute() { - rootScope = null; - if (!graph.hasLoops()) { - // fast path for the frequent case of no loops - rootScope = new Scope(graph.start(), null); - } else { - if (nodeRelevances == null) { - nodeRelevances = newNodeIdentityMap(EXPECTED_MIN_INVOKE_COUNT + graph.getNodeCount() / EXPECTED_INVOKE_RATIO); - } - NodeWorkList workList = graph.createNodeWorkList(); - Map<LoopBeginNode, Scope> loops = newNodeIdentityMap(EXPECTED_LOOP_COUNT); - - loops.put(null, new Scope(graph.start(), null)); - for (LoopBeginNode loopBegin : graph.getNodes(LoopBeginNode.class)) { - createLoopScope(loopBegin, loops); - } - - for (Scope scope : loops.values()) { - scope.process(workList); - } - } - } - - public double getRelevance(Invoke invoke) { - if (rootScope != null) { - return rootScope.computeInvokeRelevance(invoke); - } - assert nodeRelevances != null : "uninitialized relevance"; - return nodeRelevances.get(invoke); - } - - /** - * Determines the parent of the given loop and creates a {@link Scope} object for each one. This - * method will call itself recursively if no {@link Scope} for the parent loop exists. - */ - private Scope createLoopScope(LoopBeginNode loopBegin, Map<LoopBeginNode, Scope> loops) { - Scope scope = loops.get(loopBegin); - if (scope == null) { - final Scope parent; - // look for the parent scope - FixedNode current = loopBegin.forwardEnd(); - while (true) { - if (current.predecessor() == null) { - if (current instanceof LoopBeginNode) { - // if we reach a LoopBeginNode then we're within this loop - parent = createLoopScope((LoopBeginNode) current, loops); - break; - } else if (current instanceof StartNode) { - // we're within the outermost scope - parent = loops.get(null); - break; - } else { - assert current.getClass() == MergeNode.class : current; - // follow any path upwards - it doesn't matter which one - current = ((MergeNode) current).forwardEndAt(0); - } - } else if (current instanceof LoopExitNode) { - // if we reach a loop exit then we follow this loop and have the same parent - parent = createLoopScope(((LoopExitNode) current).loopBegin(), loops).parent; - break; - } else { - current = (FixedNode) current.predecessor(); - } - } - scope = new Scope(loopBegin, parent); - loops.put(loopBegin, scope); - } - return scope; - } - - /** - * A scope holds information for the contents of one loop or of the root of the method. It does - * not include child loops, i.e., the iteration in {@link #process(NodeWorkList)} explicitly - * excludes the nodes of child loops. - */ - private class Scope { - public final FixedNode start; - public final Scope parent; // can be null for the outermost scope - - /** - * The minimum probability along the most probable path in this scope. Computed lazily. - */ - private double fastPathMinProbability = UNINITIALIZED; - /** - * A measure of how important this scope is within its parent scope. Computed lazily. - */ - private double scopeRelevanceWithinParent = UNINITIALIZED; - - public Scope(FixedNode start, Scope parent) { - this.start = start; - this.parent = parent; - } - - @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") - public double getFastPathMinProbability() { - if (fastPathMinProbability == UNINITIALIZED) { - fastPathMinProbability = Math.max(EPSILON, computeFastPathMinProbability(start)); - } - return fastPathMinProbability; - } - - /** - * Computes the ratio between the probabilities of the current scope's entry point and the - * parent scope's fastPathMinProbability. - */ - @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") - public double getScopeRelevanceWithinParent() { - if (scopeRelevanceWithinParent == UNINITIALIZED) { - if (start instanceof LoopBeginNode) { - assert parent != null; - double scopeEntryProbability = nodeProbabilities.applyAsDouble(((LoopBeginNode) start).forwardEnd()); - - scopeRelevanceWithinParent = scopeEntryProbability / parent.getFastPathMinProbability(); - } else { - scopeRelevanceWithinParent = 1D; - } - } - return scopeRelevanceWithinParent; - } - - /** - * Processes all invokes in this scope by starting at the scope's start node and iterating - * all fixed nodes. Child loops are skipped by going from loop entries directly to the loop - * exits. Processing stops at loop exits of the current loop. - */ - public void process(NodeWorkList workList) { - assert !(start instanceof Invoke); - workList.addAll(start.successors()); - - for (Node current : workList) { - assert current.isAlive(); - - if (current instanceof Invoke) { - // process the invoke and queue its successors - nodeRelevances.put((FixedNode) current, computeInvokeRelevance((Invoke) current)); - workList.addAll(current.successors()); - } else if (current instanceof LoopBeginNode) { - // skip child loops by advancing over the loop exits - ((LoopBeginNode) current).loopExits().forEach(exit -> workList.add(exit.next())); - } else if (current instanceof LoopEndNode) { - // nothing to do - } else if (current instanceof LoopExitNode) { - // nothing to do - } else if (current instanceof FixedWithNextNode) { - workList.add(((FixedWithNextNode) current).next()); - } else if (current instanceof EndNode) { - workList.add(((EndNode) current).merge()); - } else if (current instanceof ControlSinkNode) { - // nothing to do - } else if (current instanceof ControlSplitNode) { - workList.addAll(current.successors()); - } else { - assert false : current; - } - } - } - - /** - * The relevance of an invoke is the ratio between the invoke's probability and the current - * scope's fastPathMinProbability, adjusted by scopeRelevanceWithinParent. - */ - public double computeInvokeRelevance(Invoke invoke) { - double invokeProbability = nodeProbabilities.applyAsDouble(invoke.asNode()); - assert !Double.isNaN(invokeProbability); - - double relevance = (invokeProbability / getFastPathMinProbability()) * Math.min(1.0, getScopeRelevanceWithinParent()); - assert !Double.isNaN(relevance) : invoke + ": " + relevance + " / " + invokeProbability + " / " + getFastPathMinProbability() + " / " + getScopeRelevanceWithinParent(); - return relevance; - } - } - - /** - * Computes the minimum probability along the most probable path within the scope. During - * iteration, the method returns immediately once a loop exit is discovered. - */ - private double computeFastPathMinProbability(FixedNode scopeStart) { - ArrayList<FixedNode> pathBeginNodes = new ArrayList<>(); - pathBeginNodes.add(scopeStart); - double minPathProbability = nodeProbabilities.applyAsDouble(scopeStart); - boolean isLoopScope = scopeStart instanceof LoopBeginNode; - - do { - Node current = pathBeginNodes.remove(pathBeginNodes.size() - 1); - do { - if (isLoopScope && current instanceof LoopExitNode && ((LoopBeginNode) scopeStart).loopExits().contains((LoopExitNode) current)) { - return minPathProbability; - } else if (current instanceof LoopBeginNode && current != scopeStart) { - current = getMaxProbabilityLoopExit((LoopBeginNode) current, pathBeginNodes); - minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); - } else if (current instanceof ControlSplitNode) { - current = getMaxProbabilitySux((ControlSplitNode) current, pathBeginNodes); - minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); - } else { - assert current.successors().count() <= 1; - current = current.successors().first(); - } - } while (current != null); - } while (!pathBeginNodes.isEmpty()); - - return minPathProbability; - } - - private double getMinPathProbability(FixedNode current, double minPathProbability) { - return current == null ? minPathProbability : Math.min(minPathProbability, nodeProbabilities.applyAsDouble(current)); - } - - /** - * Returns the most probable successor. If multiple successors share the maximum probability, - * one is returned and the others are enqueued in pathBeginNodes. - */ - private static Node getMaxProbabilitySux(ControlSplitNode controlSplit, ArrayList<FixedNode> pathBeginNodes) { - Node maxSux = null; - double maxProbability = 0.0; - int pathBeginCount = pathBeginNodes.size(); - - for (Node sux : controlSplit.successors()) { - double probability = controlSplit.probability((BeginNode) sux); - if (probability > maxProbability) { - maxProbability = probability; - maxSux = sux; - truncate(pathBeginNodes, pathBeginCount); - } else if (probability == maxProbability) { - pathBeginNodes.add((FixedNode) sux); - } - } - - return maxSux; - } - - /** - * Returns the most probable loop exit. If multiple successors share the maximum probability, - * one is returned and the others are enqueued in pathBeginNodes. - */ - private Node getMaxProbabilityLoopExit(LoopBeginNode loopBegin, ArrayList<FixedNode> pathBeginNodes) { - Node maxSux = null; - double maxProbability = 0.0; - int pathBeginCount = pathBeginNodes.size(); - - for (LoopExitNode sux : loopBegin.loopExits()) { - double probability = nodeProbabilities.applyAsDouble(sux); - if (probability > maxProbability) { - maxProbability = probability; - maxSux = sux; - truncate(pathBeginNodes, pathBeginCount); - } else if (probability == maxProbability) { - pathBeginNodes.add(sux); - } - } - - return maxSux; - } - - private static void truncate(ArrayList<FixedNode> pathBeginNodes, int pathBeginCount) { - for (int i = pathBeginNodes.size() - pathBeginCount; i > 0; i--) { - pathBeginNodes.remove(pathBeginNodes.size() - 1); - } - } -}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/DepthSearchUtil.java Thu May 15 16:45:08 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2011, 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.phases.common.inlining; - -import com.oracle.graal.api.meta.Constant; -import com.oracle.graal.api.meta.ResolvedJavaMethod; -import com.oracle.graal.compiler.common.type.Stamp; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.graph.NodeInputList; -import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.common.CanonicalizerPhase; -import com.oracle.graal.phases.common.DeadCodeEliminationPhase; -import com.oracle.graal.phases.tiers.HighTierContext; - -import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer; - -/** - * The workings of {@link InliningPhase#run(StructuredGraph, HighTierContext)} include delving into - * a callsite to explore inlining opportunities. The utilities used for that are grouped in this - * class. - */ -public class DepthSearchUtil { - - private DepthSearchUtil() { - // no instances - } - - static InliningUtil.Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context, CanonicalizerPhase canonicalizer) { - Class<? extends FixedWithNextNode> macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method); - if (macroNodeClass != null) { - return new InliningUtil.InlineableMacroNode(macroNodeClass); - } else { - return new InliningUtil.InlineableGraph(buildGraph(method, invoke, context, canonicalizer)); - } - } - - private static StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) { - final StructuredGraph newGraph; - final boolean parseBytecodes; - - // TODO (chaeubl): copying the graph is only necessary if it is modified or if it contains - // any invokes - StructuredGraph intrinsicGraph = InliningUtil.getIntrinsicGraph(context.getReplacements(), method); - if (intrinsicGraph != null) { - newGraph = intrinsicGraph.copy(); - parseBytecodes = false; - } else { - StructuredGraph cachedGraph = getCachedGraph(method, context); - if (cachedGraph != null) { - newGraph = cachedGraph.copy(); - parseBytecodes = false; - } else { - newGraph = new StructuredGraph(method); - parseBytecodes = true; - } - } - - try (Debug.Scope s = Debug.scope("InlineGraph", newGraph)) { - if (parseBytecodes) { - parseBytecodes(newGraph, context, canonicalizer); - } - - boolean callerHasMoreInformationAboutArguments = false; - NodeInputList<ValueNode> args = invoke.callTarget().arguments(); - for (ParameterNode param : newGraph.getNodes(ParameterNode.class).snapshot()) { - ValueNode arg = args.get(param.index()); - if (arg.isConstant()) { - Constant constant = arg.asConstant(); - newGraph.replaceFloating(param, ConstantNode.forConstant(constant, context.getMetaAccess(), newGraph)); - callerHasMoreInformationAboutArguments = true; - } else { - Stamp joinedStamp = param.stamp().join(arg.stamp()); - if (joinedStamp != null && !joinedStamp.equals(param.stamp())) { - param.setStamp(joinedStamp); - callerHasMoreInformationAboutArguments = true; - } - } - } - - if (!callerHasMoreInformationAboutArguments) { - // TODO (chaeubl): if args are not more concrete, inlining should be avoided - // in most cases or we could at least use the previous graph size + invoke - // probability to check the inlining - } - - if (OptCanonicalizer.getValue()) { - canonicalizer.apply(newGraph, context); - } - - return newGraph; - } catch (Throwable e) { - throw Debug.handle(e); - } - } - - private static StructuredGraph getCachedGraph(ResolvedJavaMethod method, HighTierContext context) { - if (context.getGraphCache() != null) { - StructuredGraph cachedGraph = context.getGraphCache().get(method); - if (cachedGraph != null) { - return cachedGraph; - } - } - return null; - } - - /** - * This method builds the IR nodes for <code>newGraph</code> and canonicalizes them. Provided - * profiling info is mature, the resulting graph is cached. - */ - private static StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context, CanonicalizerPhase canonicalizer) { - final boolean hasMatureProfilingInfo = newGraph.method().getProfilingInfo().isMature(); - - if (context.getGraphBuilderSuite() != null) { - context.getGraphBuilderSuite().apply(newGraph, context); - } - assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING"; - - new DeadCodeEliminationPhase().apply(newGraph); - - if (OptCanonicalizer.getValue()) { - canonicalizer.apply(newGraph, context); - } - - if (hasMatureProfilingInfo && context.getGraphCache() != null) { - context.getGraphCache().put(newGraph.method(), newGraph.copy()); - } - return newGraph; - } -}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningIterator.java Thu May 15 16:45:08 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2011, 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.phases.common.inlining; - -import com.oracle.graal.graph.Node; -import com.oracle.graal.graph.NodeBitMap; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.MethodCallTargetNode; - -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.LinkedList; - -/** - * Given a graph, visit all fixed nodes in dominator-based order, collecting in the process the - * {@link Invoke} nodes with {@link MethodCallTargetNode}. Such list of callsites is returned by - * {@link #apply()} - */ -class InliningIterator { - - private final StartNode start; - private final Deque<FixedNode> nodeQueue; - private final NodeBitMap queuedNodes; - - public InliningIterator(StructuredGraph graph) { - this.start = graph.start(); - this.nodeQueue = new ArrayDeque<>(); - this.queuedNodes = graph.createNodeBitMap(); - assert start.isAlive(); - } - - public LinkedList<Invoke> apply() { - LinkedList<Invoke> invokes = new LinkedList<>(); - FixedNode current; - forcedQueue(start); - - while ((current = nextQueuedNode()) != null) { - assert current.isAlive(); - - if (current instanceof Invoke && ((Invoke) current).callTarget() instanceof MethodCallTargetNode) { - if (current != start) { - invokes.addLast((Invoke) current); - } - queueSuccessors(current); - } else if (current instanceof LoopBeginNode) { - queueSuccessors(current); - } else if (current instanceof LoopEndNode) { - // nothing to do - } else if (current instanceof MergeNode) { - queueSuccessors(current); - } else if (current instanceof FixedWithNextNode) { - queueSuccessors(current); - } else if (current instanceof EndNode) { - queueMerge((EndNode) current); - } else if (current instanceof ControlSinkNode) { - // nothing to do - } else if (current instanceof ControlSplitNode) { - queueSuccessors(current); - } else { - assert false : current; - } - } - - return invokes; - } - - private void queueSuccessors(FixedNode x) { - for (Node node : x.successors()) { - queue(node); - } - } - - private void queue(Node node) { - if (node != null && !queuedNodes.isMarked(node)) { - forcedQueue(node); - } - } - - private void forcedQueue(Node node) { - queuedNodes.mark(node); - nodeQueue.addFirst((FixedNode) node); - } - - private FixedNode nextQueuedNode() { - if (nodeQueue.isEmpty()) { - return null; - } - - FixedNode result = nodeQueue.removeFirst(); - assert queuedNodes.isMarked(result); - return result; - } - - private void queueMerge(AbstractEndNode end) { - MergeNode merge = end.merge(); - if (!queuedNodes.isMarked(merge) && visitedAllEnds(merge)) { - queuedNodes.mark(merge); - nodeQueue.add(merge); - } - } - - private boolean visitedAllEnds(MergeNode merge) { - for (int i = 0; i < merge.forwardEndCount(); i++) { - if (!queuedNodes.isMarked(merge.forwardEndAt(i))) { - return false; - } - } - return true; - } -}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java Thu May 15 16:45:08 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java Thu May 15 17:25:49 2014 +0200 @@ -22,36 +22,23 @@ */ package com.oracle.graal.phases.common.inlining; -import static com.oracle.graal.compiler.common.GraalOptions.*; -import static com.oracle.graal.phases.common.inlining.InliningPhase.Options.*; - import java.util.*; import java.util.function.*; -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.debug.Debug.Scope; -import com.oracle.graal.graph.Graph.Mark; -import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.spi.*; import com.oracle.graal.options.*; import com.oracle.graal.phases.common.*; -import com.oracle.graal.phases.common.inlining.info.InlineInfo; -import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable; -import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableGraph; -import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableMacroNode; -import com.oracle.graal.phases.common.inlining.InliningUtil.InliningPolicy; +import com.oracle.graal.phases.common.inlining.policy.GreedyInliningPolicy; +import com.oracle.graal.phases.common.inlining.policy.InliningPolicy; +import com.oracle.graal.phases.common.inlining.walker.CallsiteHolder; +import com.oracle.graal.phases.common.inlining.walker.InliningData; +import com.oracle.graal.phases.common.inlining.walker.MethodInvocation; import com.oracle.graal.phases.graph.*; import com.oracle.graal.phases.tiers.*; -import com.oracle.graal.phases.util.*; public class InliningPhase extends AbstractInliningPhase { - static class Options { + public static class Options { // @formatter:off @Option(help = "Unconditionally inline intrinsics") @@ -65,12 +52,6 @@ private int inliningCount; private int maxMethodPerInlining = Integer.MAX_VALUE; - // Metrics - private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); - private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered"); - private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize"); - private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns"); - public InliningPhase(CanonicalizerPhase canonicalizer) { this(new GreedyInliningPolicy(null), canonicalizer); } @@ -95,16 +76,17 @@ /** * <p> * The space of inlining decisions is explored depth-first with the help of a stack realized by - * {@link InliningData}. At any point in time, its topmost element consist of: + * {@link com.oracle.graal.phases.common.inlining.walker.InliningData}. At any point in time, + * its topmost element consist of: * <ul> * <li> - * one or more {@link GraphInfo}s of inlining candidates, all of them corresponding to a single - * callsite (details below). For example, "exact inline" leads to a single candidate.</li> + * one or more {@link CallsiteHolder}s of inlining candidates, all of them corresponding to a + * single callsite (details below). For example, "exact inline" leads to a single candidate.</li> * <li> * the callsite (for the targets above) is tracked as a {@link MethodInvocation}. The difference - * between {@link MethodInvocation#totalGraphs()} and {@link MethodInvocation#processedGraphs()} - * indicates the topmost {@link GraphInfo}s that might be delved-into to explore inlining - * opportunities.</li> + * between {@link com.oracle.graal.phases.common.inlining.walker.MethodInvocation#totalGraphs()} + * and {@link MethodInvocation#processedGraphs()} indicates the topmost {@link CallsiteHolder}s + * that might be delved-into to explore inlining opportunities.</li> * </ul> * </p> * @@ -112,10 +94,11 @@ * The bottom-most element in the stack consists of: * <ul> * <li> - * a single {@link GraphInfo} (the root one, for the method on which inlining was called)</li> + * a single {@link CallsiteHolder} (the root one, for the method on which inlining was called)</li> * <li> - * a single {@link MethodInvocation} (the {@link MethodInvocation#isRoot} one, ie the unknown - * caller of the root graph)</li> + * a single {@link MethodInvocation} (the + * {@link com.oracle.graal.phases.common.inlining.walker.MethodInvocation#isRoot} one, ie the + * unknown caller of the root graph)</li> * </ul> * * </p> @@ -132,7 +115,8 @@ * <li> * try to inline: move past the current inlining candidate (remove it from the topmost element). * If that was the last one then try to inline the callsite that is (still) in the topmost - * element of {@link InliningData}, and then remove such callsite.</li> + * element of {@link com.oracle.graal.phases.common.inlining.walker.InliningData}, and then + * remove such callsite.</li> * </ol> * </p> * @@ -156,35 +140,13 @@ */ @Override protected void run(final StructuredGraph graph, final HighTierContext context) { - final InliningData data = new InliningData(graph, context.getAssumptions(), maxMethodPerInlining, canonicalizer); + final InliningData data = new InliningData(graph, context.getAssumptions(), maxMethodPerInlining, canonicalizer, inliningPolicy); ToDoubleFunction<FixedNode> probabilities = new FixedNodeProbabilityCache(); while (data.hasUnprocessedGraphs()) { - final MethodInvocation currentInvocation = data.currentInvocation(); - if (!currentInvocation.isRoot() && - !inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), currentInvocation.callee(), data.inliningDepth(), currentInvocation.probability(), - currentInvocation.relevance(), false)) { - int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs(); - assert remainingGraphs > 0; - data.popGraphs(remainingGraphs); - data.popInvocation(); - } else if (data.currentGraph().hasRemainingInvokes() && inliningPolicy.continueInlining(data.currentGraph().graph())) { - data.processNextInvoke(context); - } else { - data.popGraph(); - if (!currentInvocation.isRoot()) { - assert currentInvocation.callee().invoke().asNode().isAlive(); - currentInvocation.incrementProcessedGraphs(); - if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) { - data.popInvocation(); - final MethodInvocation parentInvoke = data.currentInvocation(); - try (Scope s = Debug.scope("Inlining", data.inliningContext())) { - tryToInline(probabilities, data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1, context); - } catch (Throwable e) { - throw Debug.handle(e); - } - } - } + boolean wasInlined = data.moveForward(context, probabilities); + if (wasInlined) { + inliningCount++; } } @@ -192,552 +154,4 @@ assert data.graphCount() == 0; } - private void tryToInline(ToDoubleFunction<FixedNode> probabilities, GraphInfo callerGraphInfo, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth, - HighTierContext context) { - InlineInfo callee = calleeInfo.callee(); - Assumptions callerAssumptions = parentInvocation.assumptions(); - - if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) { - doInline(callerGraphInfo, calleeInfo, callerAssumptions, context); - } else if (context.getOptimisticOptimizations().devirtualizeInvokes()) { - callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions); - } - metricInliningConsidered.increment(); - } - - private void doInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, Assumptions callerAssumptions, HighTierContext context) { - StructuredGraph callerGraph = callerGraphInfo.graph(); - Mark markBeforeInlining = callerGraph.getMark(); - InlineInfo callee = calleeInfo.callee(); - try { - try (Scope scope = Debug.scope("doInline", callerGraph)) { - List<Node> invokeUsages = callee.invoke().asNode().usages().snapshot(); - callee.inline(new Providers(context), callerAssumptions); - callerAssumptions.record(calleeInfo.assumptions()); - metricInliningRuns.increment(); - Debug.dump(callerGraph, "after %s", callee); - - if (OptCanonicalizer.getValue()) { - Mark markBeforeCanonicalization = callerGraph.getMark(); - canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining); - - // process invokes that are possibly created during canonicalization - for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) { - if (newNode instanceof Invoke) { - callerGraphInfo.pushInvoke((Invoke) newNode); - } - } - } - - callerGraphInfo.computeProbabilities(); - - inliningCount++; - metricInliningPerformed.increment(); - } - } catch (BailoutException bailout) { - throw bailout; - } catch (AssertionError | RuntimeException e) { - throw new GraalInternalError(e).addContext(callee.toString()); - } catch (GraalInternalError e) { - throw e.addContext(callee.toString()); - } - } - - private abstract static class AbstractInliningPolicy implements InliningPolicy { - - protected final Map<Invoke, Double> hints; - - public AbstractInliningPolicy(Map<Invoke, Double> hints) { - this.hints = hints; - } - - protected double computeMaximumSize(double relevance, int configuredMaximum) { - double inlineRatio = Math.min(RelevanceCapForInlining.getValue(), relevance); - return configuredMaximum * inlineRatio; - } - - protected double getInliningBonus(InlineInfo info) { - if (hints != null && hints.containsKey(info.invoke())) { - return hints.get(info.invoke()); - } - return 1; - } - - protected boolean isIntrinsic(Replacements replacements, InlineInfo info) { - if (AlwaysInlineIntrinsics.getValue()) { - return onlyIntrinsics(replacements, info); - } else { - return onlyForcedIntrinsics(replacements, info); - } - } - - private static boolean onlyIntrinsics(Replacements replacements, InlineInfo info) { - for (int i = 0; i < info.numberOfMethods(); i++) { - if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { - return false; - } - } - return true; - } - - private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) { - for (int i = 0; i < info.numberOfMethods(); i++) { - if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { - return false; - } - if (!replacements.isForcedSubstitution(info.methodAt(i))) { - return false; - } - } - return true; - } - - protected static int previousLowLevelGraphSize(InlineInfo info) { - int size = 0; - for (int i = 0; i < info.numberOfMethods(); i++) { - ResolvedJavaMethod m = info.methodAt(i); - ProfilingInfo profile = m.getProfilingInfo(); - int compiledGraphSize = profile.getCompilerIRSize(StructuredGraph.class); - if (compiledGraphSize > 0) { - size += compiledGraphSize; - } - } - return size; - } - - protected static int determineNodeCount(InlineInfo info) { - int nodes = 0; - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable elem = info.inlineableElementAt(i); - if (elem != null) { - nodes += elem.getNodeCount(); - } - } - return nodes; - } - - protected static double determineInvokeProbability(ToDoubleFunction<FixedNode> probabilities, InlineInfo info) { - double invokeProbability = 0; - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable callee = info.inlineableElementAt(i); - Iterable<Invoke> invokes = callee.getInvokes(); - if (invokes.iterator().hasNext()) { - for (Invoke invoke : invokes) { - invokeProbability += probabilities.applyAsDouble(invoke.asNode()); - } - } - } - return invokeProbability; - } - } - - public static class GreedyInliningPolicy extends AbstractInliningPolicy { - - public GreedyInliningPolicy(Map<Invoke, Double> hints) { - super(hints); - } - - public boolean continueInlining(StructuredGraph currentGraph) { - if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) { - InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize"); - metricInliningStoppedByMaxDesiredSize.increment(); - return false; - } - return true; - } - - @Override - public boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, - boolean fullyProcessed) { - if (InlineEverything.getValue()) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything"); - } - - if (isIntrinsic(replacements, info)) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic"); - } - - if (info.shouldInline()) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining"); - } - - double inliningBonus = getInliningBonus(info); - int nodes = determineNodeCount(info); - int lowLevelGraphSize = previousLowLevelGraphSize(info); - - if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) { - return InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)", - lowLevelGraphSize, relevance, probability, inliningBonus, nodes); - } - - if (nodes < TrivialInliningSize.getValue() * inliningBonus) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes); - } - - /* - * TODO (chaeubl): invoked methods that are on important paths but not yet compiled -> - * will be compiled anyways and it is likely that we are the only caller... might be - * useful to inline those methods but increases bootstrap time (maybe those methods are - * also getting queued in the compilation queue concurrently) - */ - double invokes = determineInvokeProbability(probabilities, info); - if (LimitInlinedInvokes.getValue() > 0 && fullyProcessed && invokes > LimitInlinedInvokes.getValue() * inliningBonus) { - return InliningUtil.logNotInlinedMethod(info, inliningDepth, "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)", invokes, - relevance, probability, inliningBonus, nodes); - } - - double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus)); - if (nodes <= maximumNodes) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability, - inliningBonus, nodes, maximumNodes); - } - - return InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes, - maximumNodes); - } - } - - public static final class InlineEverythingPolicy implements InliningPolicy { - - public boolean continueInlining(StructuredGraph graph) { - if (graph.getNodeCount() >= MaximumDesiredSize.getValue()) { - throw new BailoutException("Inline all calls failed. The resulting graph is too large."); - } - return true; - } - - public boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, - boolean fullyProcessed) { - return true; - } - } - - /** - * Holds the data for building the callee graphs recursively: graphs and invocations (each - * invocation can have multiple graphs). - */ - static class InliningData { - - private static final GraphInfo DummyGraphInfo = new GraphInfo(null, 1.0, 1.0); - - /** - * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee. - */ - private final ArrayDeque<GraphInfo> graphQueue; - private final ArrayDeque<MethodInvocation> invocationQueue; - private final int maxMethodPerInlining; - private final CanonicalizerPhase canonicalizer; - - private int maxGraphs; - - public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions, int maxMethodPerInlining, CanonicalizerPhase canonicalizer) { - this.graphQueue = new ArrayDeque<>(); - this.invocationQueue = new ArrayDeque<>(); - this.maxMethodPerInlining = maxMethodPerInlining; - this.canonicalizer = canonicalizer; - this.maxGraphs = 1; - - invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0)); - pushGraph(rootGraph, 1.0, 1.0); - } - - /** - * Process the next invoke and enqueue all its graphs for processing. - */ - void processNextInvoke(HighTierContext context) { - GraphInfo graphInfo = currentGraph(); - Invoke invoke = graphInfo.popInvoke(); - MethodInvocation callerInvocation = currentInvocation(); - Assumptions parentAssumptions = callerInvocation.assumptions(); - InlineInfo info = InliningUtil.getInlineInfo(this, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations()); - - if (info != null) { - double invokeProbability = graphInfo.invokeProbability(invoke); - double invokeRelevance = graphInfo.invokeRelevance(invoke); - MethodInvocation calleeInvocation = pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance); - - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable elem = DepthSearchUtil.getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions()), canonicalizer); - info.setInlinableElement(i, elem); - if (elem instanceof InlineableGraph) { - pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i)); - } else { - assert elem instanceof InlineableMacroNode; - pushDummyGraph(); - } - } - } - } - - public int graphCount() { - return graphQueue.size(); - } - - private void pushGraph(StructuredGraph graph, double probability, double relevance) { - assert !contains(graph); - graphQueue.push(new GraphInfo(graph, probability, relevance)); - assert graphQueue.size() <= maxGraphs; - } - - private void pushDummyGraph() { - graphQueue.push(DummyGraphInfo); - } - - public boolean hasUnprocessedGraphs() { - return !graphQueue.isEmpty(); - } - - public GraphInfo currentGraph() { - return graphQueue.peek(); - } - - public void popGraph() { - graphQueue.pop(); - assert graphQueue.size() <= maxGraphs; - } - - public void popGraphs(int count) { - assert count >= 0; - for (int i = 0; i < count; i++) { - graphQueue.pop(); - } - } - - private static final Object[] NO_CONTEXT = {}; - - /** - * Gets the call hierarchy of this inlining from outer most call to inner most callee. - */ - public Object[] inliningContext() { - if (!Debug.isDumpEnabled()) { - return NO_CONTEXT; - } - Object[] result = new Object[graphQueue.size()]; - int i = 0; - for (GraphInfo g : graphQueue) { - result[i++] = g.graph.method(); - } - return result; - } - - public MethodInvocation currentInvocation() { - return invocationQueue.peekFirst(); - } - - private MethodInvocation pushInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { - MethodInvocation methodInvocation = new MethodInvocation(info, new Assumptions(assumptions.useOptimisticAssumptions()), probability, relevance); - invocationQueue.addFirst(methodInvocation); - maxGraphs += info.numberOfMethods(); - assert graphQueue.size() <= maxGraphs; - return methodInvocation; - } - - public void popInvocation() { - maxGraphs -= invocationQueue.peekFirst().callee.numberOfMethods(); - assert graphQueue.size() <= maxGraphs; - invocationQueue.removeFirst(); - } - - public int countRecursiveInlining(ResolvedJavaMethod method) { - int count = 0; - for (GraphInfo graphInfo : graphQueue) { - if (method.equals(graphInfo.method())) { - count++; - } - } - return count; - } - - public int inliningDepth() { - assert invocationQueue.size() > 0; - return invocationQueue.size() - 1; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder("Invocations: "); - - for (MethodInvocation invocation : invocationQueue) { - if (invocation.callee() != null) { - result.append(invocation.callee().numberOfMethods()); - result.append("x "); - result.append(invocation.callee().invoke()); - result.append("; "); - } - } - - result.append("\nGraphs: "); - for (GraphInfo graph : graphQueue) { - result.append(graph.graph()); - result.append("; "); - } - - return result.toString(); - } - - private boolean contains(StructuredGraph graph) { - for (GraphInfo info : graphQueue) { - if (info.graph() == graph) { - return true; - } - } - return false; - } - } - - private static class MethodInvocation { - - private final InlineInfo callee; - private final Assumptions assumptions; - private final double probability; - private final double relevance; - - private int processedGraphs; - - public MethodInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { - this.callee = info; - this.assumptions = assumptions; - this.probability = probability; - this.relevance = relevance; - } - - public void incrementProcessedGraphs() { - processedGraphs++; - assert processedGraphs <= callee.numberOfMethods(); - } - - public int processedGraphs() { - assert processedGraphs <= callee.numberOfMethods(); - return processedGraphs; - } - - public int totalGraphs() { - return callee.numberOfMethods(); - } - - public InlineInfo callee() { - return callee; - } - - public Assumptions assumptions() { - return assumptions; - } - - public double probability() { - return probability; - } - - public double relevance() { - return relevance; - } - - public boolean isRoot() { - return callee == null; - } - - @Override - public String toString() { - if (isRoot()) { - return "<root>"; - } - CallTargetNode callTarget = callee.invoke().callTarget(); - if (callTarget instanceof MethodCallTargetNode) { - ResolvedJavaMethod calleeMethod = ((MethodCallTargetNode) callTarget).targetMethod(); - return MetaUtil.format("Invoke#%H.%n(%p)", calleeMethod); - } else { - return "Invoke#" + callTarget.targetName(); - } - } - } - - /** - * Information about a graph that will potentially be inlined. This includes tracking the - * invocations in graph that will subject to inlining themselves. - */ - private static class GraphInfo { - - private final StructuredGraph graph; - private final LinkedList<Invoke> remainingInvokes; - private final double probability; - private final double relevance; - - private final ToDoubleFunction<FixedNode> probabilities; - private final ComputeInliningRelevance computeInliningRelevance; - - public GraphInfo(StructuredGraph graph, double probability, double relevance) { - this.graph = graph; - if (graph == null) { - this.remainingInvokes = new LinkedList<>(); - } else { - LinkedList<Invoke> invokes = new InliningIterator(graph).apply(); - assert invokes.size() == count(graph.getInvokes()); - this.remainingInvokes = invokes; - } - this.probability = probability; - this.relevance = relevance; - - if (graph != null && !remainingInvokes.isEmpty()) { - probabilities = new FixedNodeProbabilityCache(); - computeInliningRelevance = new ComputeInliningRelevance(graph, probabilities); - computeProbabilities(); - } else { - probabilities = null; - computeInliningRelevance = null; - } - } - - private static int count(Iterable<Invoke> invokes) { - int count = 0; - Iterator<Invoke> iterator = invokes.iterator(); - while (iterator.hasNext()) { - iterator.next(); - count++; - } - return count; - } - - /** - * Gets the method associated with the {@linkplain #graph() graph} represented by this - * object. - */ - public ResolvedJavaMethod method() { - return graph.method(); - } - - public boolean hasRemainingInvokes() { - return !remainingInvokes.isEmpty(); - } - - /** - * The graph about which this object contains inlining information. - */ - public StructuredGraph graph() { - return graph; - } - - public Invoke popInvoke() { - return remainingInvokes.removeFirst(); - } - - public void pushInvoke(Invoke invoke) { - remainingInvokes.push(invoke); - } - - public void computeProbabilities() { - computeInliningRelevance.compute(); - } - - public double invokeProbability(Invoke invoke) { - return probability * probabilities.applyAsDouble(invoke.asNode()); - } - - public double invokeRelevance(Invoke invoke) { - return Math.min(CapInheritedRelevance.getValue(), relevance) * computeInliningRelevance.getRelevance(invoke); - } - - @Override - public String toString() { - return (graph != null ? MetaUtil.format("%H.%n(%p)", method()) : "<null method>") + remainingInvokes; - } - } }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Thu May 15 16:45:08 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Thu May 15 17:25:49 2014 +0200 @@ -28,7 +28,6 @@ import static com.oracle.graal.compiler.common.type.StampFactory.*; import java.util.*; -import java.util.function.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.code.Assumptions.Assumption; @@ -50,8 +49,8 @@ import com.oracle.graal.nodes.type.*; import com.oracle.graal.nodes.util.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.common.inlining.InliningPhase.*; import com.oracle.graal.phases.common.inlining.info.*; +import com.oracle.graal.phases.common.inlining.walker.InliningData; public class InliningUtil { @@ -63,13 +62,6 @@ */ public static final DebugMetric InlinedBytecodes = Debug.metric("InlinedBytecodes"); - public interface InliningPolicy { - - boolean continueInlining(StructuredGraph graph); - - boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed); - } - public interface Inlineable { int getNodeCount();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/AbstractInliningPolicy.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011, 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.phases.common.inlining.policy; + +import com.oracle.graal.api.meta.ProfilingInfo; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.spi.Replacements; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +import java.util.Map; +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.RelevanceCapForInlining; +import static com.oracle.graal.phases.common.inlining.InliningPhase.Options.AlwaysInlineIntrinsics; + +public abstract class AbstractInliningPolicy implements InliningPolicy { + + protected final Map<Invoke, Double> hints; + + public AbstractInliningPolicy(Map<Invoke, Double> hints) { + this.hints = hints; + } + + protected double computeMaximumSize(double relevance, int configuredMaximum) { + double inlineRatio = Math.min(RelevanceCapForInlining.getValue(), relevance); + return configuredMaximum * inlineRatio; + } + + protected double getInliningBonus(InlineInfo info) { + if (hints != null && hints.containsKey(info.invoke())) { + return hints.get(info.invoke()); + } + return 1; + } + + protected boolean isIntrinsic(Replacements replacements, InlineInfo info) { + if (AlwaysInlineIntrinsics.getValue()) { + return onlyIntrinsics(replacements, info); + } else { + return onlyForcedIntrinsics(replacements, info); + } + } + + private static boolean onlyIntrinsics(Replacements replacements, InlineInfo info) { + for (int i = 0; i < info.numberOfMethods(); i++) { + if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { + return false; + } + } + return true; + } + + private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) { + for (int i = 0; i < info.numberOfMethods(); i++) { + if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { + return false; + } + if (!replacements.isForcedSubstitution(info.methodAt(i))) { + return false; + } + } + return true; + } + + protected static int previousLowLevelGraphSize(InlineInfo info) { + int size = 0; + for (int i = 0; i < info.numberOfMethods(); i++) { + ResolvedJavaMethod m = info.methodAt(i); + ProfilingInfo profile = m.getProfilingInfo(); + int compiledGraphSize = profile.getCompilerIRSize(StructuredGraph.class); + if (compiledGraphSize > 0) { + size += compiledGraphSize; + } + } + return size; + } + + protected static int determineNodeCount(InlineInfo info) { + int nodes = 0; + for (int i = 0; i < info.numberOfMethods(); i++) { + InliningUtil.Inlineable elem = info.inlineableElementAt(i); + if (elem != null) { + nodes += elem.getNodeCount(); + } + } + return nodes; + } + + protected static double determineInvokeProbability(ToDoubleFunction<FixedNode> probabilities, InlineInfo info) { + double invokeProbability = 0; + for (int i = 0; i < info.numberOfMethods(); i++) { + InliningUtil.Inlineable callee = info.inlineableElementAt(i); + Iterable<Invoke> invokes = callee.getInvokes(); + if (invokes.iterator().hasNext()) { + for (Invoke invoke : invokes) { + invokeProbability += probabilities.applyAsDouble(invoke.asNode()); + } + } + } + return invokeProbability; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/GreedyInliningPolicy.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011, 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.phases.common.inlining.policy; + +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.DebugMetric; +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.spi.Replacements; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +import java.util.Map; +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.*; + +public class GreedyInliningPolicy extends AbstractInliningPolicy { + + private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize"); + + public GreedyInliningPolicy(Map<Invoke, Double> hints) { + super(hints); + } + + public boolean continueInlining(StructuredGraph currentGraph) { + if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) { + InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize"); + metricInliningStoppedByMaxDesiredSize.increment(); + return false; + } + return true; + } + + @Override + public boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, + boolean fullyProcessed) { + if (InlineEverything.getValue()) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything"); + } + + if (isIntrinsic(replacements, info)) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic"); + } + + if (info.shouldInline()) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining"); + } + + double inliningBonus = getInliningBonus(info); + int nodes = determineNodeCount(info); + int lowLevelGraphSize = previousLowLevelGraphSize(info); + + if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) { + return InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)", + lowLevelGraphSize, relevance, probability, inliningBonus, nodes); + } + + if (nodes < TrivialInliningSize.getValue() * inliningBonus) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes); + } + + /* + * TODO (chaeubl): invoked methods that are on important paths but not yet compiled -> will + * be compiled anyways and it is likely that we are the only caller... might be useful to + * inline those methods but increases bootstrap time (maybe those methods are also getting + * queued in the compilation queue concurrently) + */ + double invokes = determineInvokeProbability(probabilities, info); + if (LimitInlinedInvokes.getValue() > 0 && fullyProcessed && invokes > LimitInlinedInvokes.getValue() * inliningBonus) { + return InliningUtil.logNotInlinedMethod(info, inliningDepth, "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)", invokes, relevance, + probability, inliningBonus, nodes); + } + + double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus)); + if (nodes <= maximumNodes) { + return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability, + inliningBonus, nodes, maximumNodes); + } + + return InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes, + maximumNodes); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/InlineEverythingPolicy.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011, 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.phases.common.inlining.policy; + +import com.oracle.graal.api.code.BailoutException; +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.spi.Replacements; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.MaximumDesiredSize; + +public final class InlineEverythingPolicy implements InliningPolicy { + + public boolean continueInlining(StructuredGraph graph) { + if (graph.getNodeCount() >= MaximumDesiredSize.getValue()) { + throw new BailoutException("Inline all calls failed. The resulting graph is too large."); + } + return true; + } + + public boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, + boolean fullyProcessed) { + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/InliningPolicy.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011, 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.phases.common.inlining.policy; + +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.spi.Replacements; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +import java.util.function.ToDoubleFunction; + +public interface InliningPolicy { + + boolean continueInlining(StructuredGraph graph); + + boolean isWorthInlining(ToDoubleFunction<FixedNode> probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/CallsiteHolder.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011, 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.phases.common.inlining.walker; + +import com.oracle.graal.api.meta.MetaUtil; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.phases.graph.FixedNodeProbabilityCache; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.CapInheritedRelevance; + +/** + * Information about a graph that will potentially be inlined. This includes tracking the + * invocations in graph that will subject to inlining themselves. + */ +public class CallsiteHolder { + + private final StructuredGraph graph; + private final LinkedList<Invoke> remainingInvokes; + private final double probability; + private final double relevance; + + private final ToDoubleFunction<FixedNode> probabilities; + private final ComputeInliningRelevance computeInliningRelevance; + + public CallsiteHolder(StructuredGraph graph, double probability, double relevance) { + this.graph = graph; + if (graph == null) { + this.remainingInvokes = new LinkedList<>(); + } else { + LinkedList<Invoke> invokes = new InliningIterator(graph).apply(); + assert invokes.size() == count(graph.getInvokes()); + this.remainingInvokes = invokes; + } + this.probability = probability; + this.relevance = relevance; + + if (graph != null && !remainingInvokes.isEmpty()) { + probabilities = new FixedNodeProbabilityCache(); + computeInliningRelevance = new ComputeInliningRelevance(graph, probabilities); + computeProbabilities(); + } else { + probabilities = null; + computeInliningRelevance = null; + } + } + + private static int count(Iterable<Invoke> invokes) { + int count = 0; + Iterator<Invoke> iterator = invokes.iterator(); + while (iterator.hasNext()) { + iterator.next(); + count++; + } + return count; + } + + /** + * Gets the method associated with the {@linkplain #graph() graph} represented by this object. + */ + public ResolvedJavaMethod method() { + return graph.method(); + } + + public boolean hasRemainingInvokes() { + return !remainingInvokes.isEmpty(); + } + + /** + * The graph about which this object contains inlining information. + */ + public StructuredGraph graph() { + return graph; + } + + public Invoke popInvoke() { + return remainingInvokes.removeFirst(); + } + + public void pushInvoke(Invoke invoke) { + remainingInvokes.push(invoke); + } + + public void computeProbabilities() { + computeInliningRelevance.compute(); + } + + public double invokeProbability(Invoke invoke) { + return probability * probabilities.applyAsDouble(invoke.asNode()); + } + + public double invokeRelevance(Invoke invoke) { + return Math.min(CapInheritedRelevance.getValue(), relevance) * computeInliningRelevance.getRelevance(invoke); + } + + @Override + public String toString() { + return (graph != null ? MetaUtil.format("%H.%n(%p)", method()) : "<null method>") + remainingInvokes; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/ComputeInliningRelevance.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,324 @@ +/* + * 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.phases.common.inlining.walker; + +import static com.oracle.graal.graph.util.CollectionsAccess.*; + +import java.util.*; +import java.util.function.*; + +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; + +import edu.umd.cs.findbugs.annotations.*; + +public class ComputeInliningRelevance { + + private static final double EPSILON = 1d / Integer.MAX_VALUE; + private static final double UNINITIALIZED = -1D; + + private static final int EXPECTED_MIN_INVOKE_COUNT = 3; + private static final int EXPECTED_INVOKE_RATIO = 20; + private static final int EXPECTED_LOOP_COUNT = 3; + + private final StructuredGraph graph; + private final ToDoubleFunction<FixedNode> nodeProbabilities; + + /** + * Node relevances are pre-computed for all invokes if the graph contains loops. If there are no + * loops, the computation happens lazily based on {@link #rootScope}. + */ + private Map<FixedNode, Double> nodeRelevances; + /** + * This scope is non-null if (and only if) there are no loops in the graph. In this case, the + * root scope is used to compute invoke relevances on the fly. + */ + private Scope rootScope; + + public ComputeInliningRelevance(StructuredGraph graph, ToDoubleFunction<FixedNode> nodeProbabilities) { + this.graph = graph; + this.nodeProbabilities = nodeProbabilities; + } + + /** + * Initializes or updates the relevance computation. If there are no loops within the graph, + * most computation happens lazily. + */ + public void compute() { + rootScope = null; + if (!graph.hasLoops()) { + // fast path for the frequent case of no loops + rootScope = new Scope(graph.start(), null); + } else { + if (nodeRelevances == null) { + nodeRelevances = newNodeIdentityMap(EXPECTED_MIN_INVOKE_COUNT + graph.getNodeCount() / EXPECTED_INVOKE_RATIO); + } + NodeWorkList workList = graph.createNodeWorkList(); + Map<LoopBeginNode, Scope> loops = newNodeIdentityMap(EXPECTED_LOOP_COUNT); + + loops.put(null, new Scope(graph.start(), null)); + for (LoopBeginNode loopBegin : graph.getNodes(LoopBeginNode.class)) { + createLoopScope(loopBegin, loops); + } + + for (Scope scope : loops.values()) { + scope.process(workList); + } + } + } + + public double getRelevance(Invoke invoke) { + if (rootScope != null) { + return rootScope.computeInvokeRelevance(invoke); + } + assert nodeRelevances != null : "uninitialized relevance"; + return nodeRelevances.get(invoke); + } + + /** + * Determines the parent of the given loop and creates a {@link Scope} object for each one. This + * method will call itself recursively if no {@link Scope} for the parent loop exists. + */ + private Scope createLoopScope(LoopBeginNode loopBegin, Map<LoopBeginNode, Scope> loops) { + Scope scope = loops.get(loopBegin); + if (scope == null) { + final Scope parent; + // look for the parent scope + FixedNode current = loopBegin.forwardEnd(); + while (true) { + if (current.predecessor() == null) { + if (current instanceof LoopBeginNode) { + // if we reach a LoopBeginNode then we're within this loop + parent = createLoopScope((LoopBeginNode) current, loops); + break; + } else if (current instanceof StartNode) { + // we're within the outermost scope + parent = loops.get(null); + break; + } else { + assert current.getClass() == MergeNode.class : current; + // follow any path upwards - it doesn't matter which one + current = ((MergeNode) current).forwardEndAt(0); + } + } else if (current instanceof LoopExitNode) { + // if we reach a loop exit then we follow this loop and have the same parent + parent = createLoopScope(((LoopExitNode) current).loopBegin(), loops).parent; + break; + } else { + current = (FixedNode) current.predecessor(); + } + } + scope = new Scope(loopBegin, parent); + loops.put(loopBegin, scope); + } + return scope; + } + + /** + * A scope holds information for the contents of one loop or of the root of the method. It does + * not include child loops, i.e., the iteration in {@link #process(NodeWorkList)} explicitly + * excludes the nodes of child loops. + */ + private class Scope { + public final FixedNode start; + public final Scope parent; // can be null for the outermost scope + + /** + * The minimum probability along the most probable path in this scope. Computed lazily. + */ + private double fastPathMinProbability = UNINITIALIZED; + /** + * A measure of how important this scope is within its parent scope. Computed lazily. + */ + private double scopeRelevanceWithinParent = UNINITIALIZED; + + public Scope(FixedNode start, Scope parent) { + this.start = start; + this.parent = parent; + } + + @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") + public double getFastPathMinProbability() { + if (fastPathMinProbability == UNINITIALIZED) { + fastPathMinProbability = Math.max(EPSILON, computeFastPathMinProbability(start)); + } + return fastPathMinProbability; + } + + /** + * Computes the ratio between the probabilities of the current scope's entry point and the + * parent scope's fastPathMinProbability. + */ + @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") + public double getScopeRelevanceWithinParent() { + if (scopeRelevanceWithinParent == UNINITIALIZED) { + if (start instanceof LoopBeginNode) { + assert parent != null; + double scopeEntryProbability = nodeProbabilities.applyAsDouble(((LoopBeginNode) start).forwardEnd()); + + scopeRelevanceWithinParent = scopeEntryProbability / parent.getFastPathMinProbability(); + } else { + scopeRelevanceWithinParent = 1D; + } + } + return scopeRelevanceWithinParent; + } + + /** + * Processes all invokes in this scope by starting at the scope's start node and iterating + * all fixed nodes. Child loops are skipped by going from loop entries directly to the loop + * exits. Processing stops at loop exits of the current loop. + */ + public void process(NodeWorkList workList) { + assert !(start instanceof Invoke); + workList.addAll(start.successors()); + + for (Node current : workList) { + assert current.isAlive(); + + if (current instanceof Invoke) { + // process the invoke and queue its successors + nodeRelevances.put((FixedNode) current, computeInvokeRelevance((Invoke) current)); + workList.addAll(current.successors()); + } else if (current instanceof LoopBeginNode) { + // skip child loops by advancing over the loop exits + ((LoopBeginNode) current).loopExits().forEach(exit -> workList.add(exit.next())); + } else if (current instanceof LoopEndNode) { + // nothing to do + } else if (current instanceof LoopExitNode) { + // nothing to do + } else if (current instanceof FixedWithNextNode) { + workList.add(((FixedWithNextNode) current).next()); + } else if (current instanceof EndNode) { + workList.add(((EndNode) current).merge()); + } else if (current instanceof ControlSinkNode) { + // nothing to do + } else if (current instanceof ControlSplitNode) { + workList.addAll(current.successors()); + } else { + assert false : current; + } + } + } + + /** + * The relevance of an invoke is the ratio between the invoke's probability and the current + * scope's fastPathMinProbability, adjusted by scopeRelevanceWithinParent. + */ + public double computeInvokeRelevance(Invoke invoke) { + double invokeProbability = nodeProbabilities.applyAsDouble(invoke.asNode()); + assert !Double.isNaN(invokeProbability); + + double relevance = (invokeProbability / getFastPathMinProbability()) * Math.min(1.0, getScopeRelevanceWithinParent()); + assert !Double.isNaN(relevance) : invoke + ": " + relevance + " / " + invokeProbability + " / " + getFastPathMinProbability() + " / " + getScopeRelevanceWithinParent(); + return relevance; + } + } + + /** + * Computes the minimum probability along the most probable path within the scope. During + * iteration, the method returns immediately once a loop exit is discovered. + */ + private double computeFastPathMinProbability(FixedNode scopeStart) { + ArrayList<FixedNode> pathBeginNodes = new ArrayList<>(); + pathBeginNodes.add(scopeStart); + double minPathProbability = nodeProbabilities.applyAsDouble(scopeStart); + boolean isLoopScope = scopeStart instanceof LoopBeginNode; + + do { + Node current = pathBeginNodes.remove(pathBeginNodes.size() - 1); + do { + if (isLoopScope && current instanceof LoopExitNode && ((LoopBeginNode) scopeStart).loopExits().contains((LoopExitNode) current)) { + return minPathProbability; + } else if (current instanceof LoopBeginNode && current != scopeStart) { + current = getMaxProbabilityLoopExit((LoopBeginNode) current, pathBeginNodes); + minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); + } else if (current instanceof ControlSplitNode) { + current = getMaxProbabilitySux((ControlSplitNode) current, pathBeginNodes); + minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); + } else { + assert current.successors().count() <= 1; + current = current.successors().first(); + } + } while (current != null); + } while (!pathBeginNodes.isEmpty()); + + return minPathProbability; + } + + private double getMinPathProbability(FixedNode current, double minPathProbability) { + return current == null ? minPathProbability : Math.min(minPathProbability, nodeProbabilities.applyAsDouble(current)); + } + + /** + * Returns the most probable successor. If multiple successors share the maximum probability, + * one is returned and the others are enqueued in pathBeginNodes. + */ + private static Node getMaxProbabilitySux(ControlSplitNode controlSplit, ArrayList<FixedNode> pathBeginNodes) { + Node maxSux = null; + double maxProbability = 0.0; + int pathBeginCount = pathBeginNodes.size(); + + for (Node sux : controlSplit.successors()) { + double probability = controlSplit.probability((BeginNode) sux); + if (probability > maxProbability) { + maxProbability = probability; + maxSux = sux; + truncate(pathBeginNodes, pathBeginCount); + } else if (probability == maxProbability) { + pathBeginNodes.add((FixedNode) sux); + } + } + + return maxSux; + } + + /** + * Returns the most probable loop exit. If multiple successors share the maximum probability, + * one is returned and the others are enqueued in pathBeginNodes. + */ + private Node getMaxProbabilityLoopExit(LoopBeginNode loopBegin, ArrayList<FixedNode> pathBeginNodes) { + Node maxSux = null; + double maxProbability = 0.0; + int pathBeginCount = pathBeginNodes.size(); + + for (LoopExitNode sux : loopBegin.loopExits()) { + double probability = nodeProbabilities.applyAsDouble(sux); + if (probability > maxProbability) { + maxProbability = probability; + maxSux = sux; + truncate(pathBeginNodes, pathBeginCount); + } else if (probability == maxProbability) { + pathBeginNodes.add(sux); + } + } + + return maxSux; + } + + private static void truncate(ArrayList<FixedNode> pathBeginNodes, int pathBeginCount) { + for (int i = pathBeginNodes.size() - pathBeginCount; i > 0; i--) { + pathBeginNodes.remove(pathBeginNodes.size() - 1); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/DepthSearchUtil.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2011, 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.phases.common.inlining.walker; + +import com.oracle.graal.api.meta.Constant; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.compiler.common.type.Stamp; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.graph.NodeInputList; +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.common.CanonicalizerPhase; +import com.oracle.graal.phases.common.DeadCodeEliminationPhase; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.tiers.HighTierContext; + +import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer; + +/** + * The workings of {@link InliningData} include delving into a callsite to explore inlining + * opportunities. The utilities used for that are grouped in this class. + */ +public class DepthSearchUtil { + + private DepthSearchUtil() { + // no instances + } + + public static InliningUtil.Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context, CanonicalizerPhase canonicalizer) { + Class<? extends FixedWithNextNode> macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method); + if (macroNodeClass != null) { + return new InliningUtil.InlineableMacroNode(macroNodeClass); + } else { + return new InliningUtil.InlineableGraph(buildGraph(method, invoke, context, canonicalizer)); + } + } + + private static StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) { + final StructuredGraph newGraph; + final boolean parseBytecodes; + + // TODO (chaeubl): copying the graph is only necessary if it is modified or if it contains + // any invokes + StructuredGraph intrinsicGraph = InliningUtil.getIntrinsicGraph(context.getReplacements(), method); + if (intrinsicGraph != null) { + newGraph = intrinsicGraph.copy(); + parseBytecodes = false; + } else { + StructuredGraph cachedGraph = getCachedGraph(method, context); + if (cachedGraph != null) { + newGraph = cachedGraph.copy(); + parseBytecodes = false; + } else { + newGraph = new StructuredGraph(method); + parseBytecodes = true; + } + } + + try (Debug.Scope s = Debug.scope("InlineGraph", newGraph)) { + if (parseBytecodes) { + parseBytecodes(newGraph, context, canonicalizer); + } + + boolean callerHasMoreInformationAboutArguments = false; + NodeInputList<ValueNode> args = invoke.callTarget().arguments(); + for (ParameterNode param : newGraph.getNodes(ParameterNode.class).snapshot()) { + ValueNode arg = args.get(param.index()); + if (arg.isConstant()) { + Constant constant = arg.asConstant(); + newGraph.replaceFloating(param, ConstantNode.forConstant(constant, context.getMetaAccess(), newGraph)); + callerHasMoreInformationAboutArguments = true; + } else { + Stamp joinedStamp = param.stamp().join(arg.stamp()); + if (joinedStamp != null && !joinedStamp.equals(param.stamp())) { + param.setStamp(joinedStamp); + callerHasMoreInformationAboutArguments = true; + } + } + } + + if (!callerHasMoreInformationAboutArguments) { + // TODO (chaeubl): if args are not more concrete, inlining should be avoided + // in most cases or we could at least use the previous graph size + invoke + // probability to check the inlining + } + + if (OptCanonicalizer.getValue()) { + canonicalizer.apply(newGraph, context); + } + + return newGraph; + } catch (Throwable e) { + throw Debug.handle(e); + } + } + + private static StructuredGraph getCachedGraph(ResolvedJavaMethod method, HighTierContext context) { + if (context.getGraphCache() != null) { + StructuredGraph cachedGraph = context.getGraphCache().get(method); + if (cachedGraph != null) { + return cachedGraph; + } + } + return null; + } + + /** + * This method builds the IR nodes for <code>newGraph</code> and canonicalizes them. Provided + * profiling info is mature, the resulting graph is cached. + */ + private static StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context, CanonicalizerPhase canonicalizer) { + final boolean hasMatureProfilingInfo = newGraph.method().getProfilingInfo().isMature(); + + if (context.getGraphBuilderSuite() != null) { + context.getGraphBuilderSuite().apply(newGraph, context); + } + assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING"; + + new DeadCodeEliminationPhase().apply(newGraph); + + if (OptCanonicalizer.getValue()) { + canonicalizer.apply(newGraph, context); + } + + if (hasMatureProfilingInfo && context.getGraphCache() != null) { + context.getGraphCache().put(newGraph.method(), newGraph.copy()); + } + return newGraph; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/InliningData.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2011, 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.phases.common.inlining.walker; + +import com.oracle.graal.api.code.Assumptions; +import com.oracle.graal.api.code.BailoutException; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.compiler.common.GraalInternalError; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.DebugMetric; +import com.oracle.graal.graph.Graph; +import com.oracle.graal.graph.Node; +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.phases.common.CanonicalizerPhase; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; +import com.oracle.graal.phases.common.inlining.policy.InliningPolicy; +import com.oracle.graal.phases.tiers.HighTierContext; +import com.oracle.graal.phases.util.Providers; + +import java.util.ArrayDeque; +import java.util.List; +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer; + +/** + * Holds the data for building the callee graphs recursively: graphs and invocations (each + * invocation can have multiple graphs). + */ +public class InliningData { + + private static final CallsiteHolder DUMMY_CALLSITE_HOLDER = new CallsiteHolder(null, 1.0, 1.0); + // Metrics + private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); + private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns"); + private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered"); + + /** + * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee. + */ + private final ArrayDeque<CallsiteHolder> graphQueue; + private final ArrayDeque<MethodInvocation> invocationQueue; + private final int maxMethodPerInlining; + private final CanonicalizerPhase canonicalizer; + private final InliningPolicy inliningPolicy; + + private int maxGraphs; + + public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions, int maxMethodPerInlining, CanonicalizerPhase canonicalizer, InliningPolicy inliningPolicy) { + assert rootGraph != null; + this.graphQueue = new ArrayDeque<>(); + this.invocationQueue = new ArrayDeque<>(); + this.maxMethodPerInlining = maxMethodPerInlining; + this.canonicalizer = canonicalizer; + this.inliningPolicy = inliningPolicy; + this.maxGraphs = 1; + + invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0)); + pushGraph(rootGraph, 1.0, 1.0); + } + + private void doInline(CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInfo, Assumptions callerAssumptions, HighTierContext context) { + StructuredGraph callerGraph = callerCallsiteHolder.graph(); + Graph.Mark markBeforeInlining = callerGraph.getMark(); + InlineInfo callee = calleeInfo.callee(); + try { + try (Debug.Scope scope = Debug.scope("doInline", callerGraph)) { + List<Node> invokeUsages = callee.invoke().asNode().usages().snapshot(); + callee.inline(new Providers(context), callerAssumptions); + callerAssumptions.record(calleeInfo.assumptions()); + metricInliningRuns.increment(); + Debug.dump(callerGraph, "after %s", callee); + + if (OptCanonicalizer.getValue()) { + Graph.Mark markBeforeCanonicalization = callerGraph.getMark(); + canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining); + + // process invokes that are possibly created during canonicalization + for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) { + if (newNode instanceof Invoke) { + callerCallsiteHolder.pushInvoke((Invoke) newNode); + } + } + } + + callerCallsiteHolder.computeProbabilities(); + + metricInliningPerformed.increment(); + } + } catch (BailoutException bailout) { + throw bailout; + } catch (AssertionError | RuntimeException e) { + throw new GraalInternalError(e).addContext(callee.toString()); + } catch (GraalInternalError e) { + throw e.addContext(callee.toString()); + } + } + + /** + * @return true iff inlining was actually performed + */ + private boolean tryToInline(ToDoubleFunction<FixedNode> probabilities, CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth, + HighTierContext context) { + InlineInfo callee = calleeInfo.callee(); + Assumptions callerAssumptions = parentInvocation.assumptions(); + metricInliningConsidered.increment(); + + if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) { + doInline(callerCallsiteHolder, calleeInfo, callerAssumptions, context); + return true; + } + + if (context.getOptimisticOptimizations().devirtualizeInvokes()) { + callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions); + } + + return false; + } + + /** + * Process the next invoke and enqueue all its graphs for processing. + */ + void processNextInvoke(HighTierContext context) { + CallsiteHolder callsiteHolder = currentGraph(); + Invoke invoke = callsiteHolder.popInvoke(); + MethodInvocation callerInvocation = currentInvocation(); + Assumptions parentAssumptions = callerInvocation.assumptions(); + InlineInfo info = InliningUtil.getInlineInfo(this, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations()); + + if (info != null) { + double invokeProbability = callsiteHolder.invokeProbability(invoke); + double invokeRelevance = callsiteHolder.invokeRelevance(invoke); + MethodInvocation calleeInvocation = pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance); + + for (int i = 0; i < info.numberOfMethods(); i++) { + InliningUtil.Inlineable elem = DepthSearchUtil.getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions()), canonicalizer); + info.setInlinableElement(i, elem); + if (elem instanceof InliningUtil.InlineableGraph) { + pushGraph(((InliningUtil.InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i)); + } else { + assert elem instanceof InliningUtil.InlineableMacroNode; + pushDummyGraph(); + } + } + } + } + + public int graphCount() { + return graphQueue.size(); + } + + private void pushGraph(StructuredGraph graph, double probability, double relevance) { + assert graph != null; + assert !contains(graph); + graphQueue.push(new CallsiteHolder(graph, probability, relevance)); + assert graphQueue.size() <= maxGraphs; + } + + private void pushDummyGraph() { + graphQueue.push(DUMMY_CALLSITE_HOLDER); + } + + public boolean hasUnprocessedGraphs() { + return !graphQueue.isEmpty(); + } + + public CallsiteHolder currentGraph() { + return graphQueue.peek(); + } + + public void popGraph() { + graphQueue.pop(); + assert graphQueue.size() <= maxGraphs; + } + + public void popGraphs(int count) { + assert count >= 0; + for (int i = 0; i < count; i++) { + graphQueue.pop(); + } + } + + private static final Object[] NO_CONTEXT = {}; + + /** + * Gets the call hierarchy of this inlining from outer most call to inner most callee. + */ + public Object[] inliningContext() { + if (!Debug.isDumpEnabled()) { + return NO_CONTEXT; + } + Object[] result = new Object[graphQueue.size()]; + int i = 0; + for (CallsiteHolder g : graphQueue) { + result[i++] = g.method(); + } + return result; + } + + public MethodInvocation currentInvocation() { + return invocationQueue.peekFirst(); + } + + private MethodInvocation pushInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { + MethodInvocation methodInvocation = new MethodInvocation(info, new Assumptions(assumptions.useOptimisticAssumptions()), probability, relevance); + invocationQueue.addFirst(methodInvocation); + maxGraphs += info.numberOfMethods(); + assert graphQueue.size() <= maxGraphs; + return methodInvocation; + } + + public void popInvocation() { + maxGraphs -= invocationQueue.peekFirst().callee().numberOfMethods(); + assert graphQueue.size() <= maxGraphs; + invocationQueue.removeFirst(); + } + + public int countRecursiveInlining(ResolvedJavaMethod method) { + int count = 0; + for (CallsiteHolder callsiteHolder : graphQueue) { + if (method.equals(callsiteHolder.method())) { + count++; + } + } + return count; + } + + public int inliningDepth() { + assert invocationQueue.size() > 0; + return invocationQueue.size() - 1; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder("Invocations: "); + + for (MethodInvocation invocation : invocationQueue) { + if (invocation.callee() != null) { + result.append(invocation.callee().numberOfMethods()); + result.append("x "); + result.append(invocation.callee().invoke()); + result.append("; "); + } + } + + result.append("\nGraphs: "); + for (CallsiteHolder graph : graphQueue) { + result.append(graph.graph()); + result.append("; "); + } + + return result.toString(); + } + + private boolean contains(StructuredGraph graph) { + for (CallsiteHolder info : graphQueue) { + if (info.graph() == graph) { + return true; + } + } + return false; + } + + /** + * @return true iff inlining was actually performed + */ + public boolean moveForward(HighTierContext context, ToDoubleFunction<FixedNode> probabilities) { + + final MethodInvocation currentInvocation = currentInvocation(); + + final boolean backtrack = (!currentInvocation.isRoot() && !inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), currentInvocation.callee(), inliningDepth(), + currentInvocation.probability(), currentInvocation.relevance(), false)); + if (backtrack) { + int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs(); + assert remainingGraphs > 0; + popGraphs(remainingGraphs); + popInvocation(); + return false; + } + + final boolean delve = currentGraph().hasRemainingInvokes() && inliningPolicy.continueInlining(currentGraph().graph()); + if (delve) { + processNextInvoke(context); + return false; + } + + popGraph(); + if (currentInvocation.isRoot()) { + return false; + } + + // try to inline + assert currentInvocation.callee().invoke().asNode().isAlive(); + currentInvocation.incrementProcessedGraphs(); + if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) { + popInvocation(); + final MethodInvocation parentInvoke = currentInvocation(); + try (Debug.Scope s = Debug.scope("Inlining", inliningContext())) { + return tryToInline(probabilities, currentGraph(), currentInvocation, parentInvoke, inliningDepth() + 1, context); + } catch (Throwable e) { + throw Debug.handle(e); + } + } + + return false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/InliningIterator.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2011, 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.phases.common.inlining.walker; + +import com.oracle.graal.graph.Node; +import com.oracle.graal.graph.NodeBitMap; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.LinkedList; + +/** + * Given a graph, visit all fixed nodes in dominator-based order, collecting in the process the + * {@link Invoke} nodes with {@link MethodCallTargetNode}. Such list of callsites is returned by + * {@link #apply()} + */ +public class InliningIterator { + + private final StartNode start; + private final Deque<FixedNode> nodeQueue; + private final NodeBitMap queuedNodes; + + public InliningIterator(StructuredGraph graph) { + this.start = graph.start(); + this.nodeQueue = new ArrayDeque<>(); + this.queuedNodes = graph.createNodeBitMap(); + assert start.isAlive(); + } + + public LinkedList<Invoke> apply() { + LinkedList<Invoke> invokes = new LinkedList<>(); + FixedNode current; + forcedQueue(start); + + while ((current = nextQueuedNode()) != null) { + assert current.isAlive(); + + if (current instanceof Invoke && ((Invoke) current).callTarget() instanceof MethodCallTargetNode) { + if (current != start) { + invokes.addLast((Invoke) current); + } + queueSuccessors(current); + } else if (current instanceof LoopBeginNode) { + queueSuccessors(current); + } else if (current instanceof LoopEndNode) { + // nothing to do + } else if (current instanceof MergeNode) { + queueSuccessors(current); + } else if (current instanceof FixedWithNextNode) { + queueSuccessors(current); + } else if (current instanceof EndNode) { + queueMerge((EndNode) current); + } else if (current instanceof ControlSinkNode) { + // nothing to do + } else if (current instanceof ControlSplitNode) { + queueSuccessors(current); + } else { + assert false : current; + } + } + + return invokes; + } + + private void queueSuccessors(FixedNode x) { + for (Node node : x.successors()) { + queue(node); + } + } + + private void queue(Node node) { + if (node != null && !queuedNodes.isMarked(node)) { + forcedQueue(node); + } + } + + private void forcedQueue(Node node) { + queuedNodes.mark(node); + nodeQueue.addFirst((FixedNode) node); + } + + private FixedNode nextQueuedNode() { + if (nodeQueue.isEmpty()) { + return null; + } + + FixedNode result = nodeQueue.removeFirst(); + assert queuedNodes.isMarked(result); + return result; + } + + private void queueMerge(AbstractEndNode end) { + MergeNode merge = end.merge(); + if (!queuedNodes.isMarked(merge) && visitedAllEnds(merge)) { + queuedNodes.mark(merge); + nodeQueue.add(merge); + } + } + + private boolean visitedAllEnds(MergeNode merge) { + for (int i = 0; i < merge.forwardEndCount(); i++) { + if (!queuedNodes.isMarked(merge.forwardEndAt(i))) { + return false; + } + } + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/MethodInvocation.java Thu May 15 17:25:49 2014 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2011, 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.phases.common.inlining.walker; + +import com.oracle.graal.api.code.Assumptions; +import com.oracle.graal.api.meta.MetaUtil; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.CallTargetNode; +import com.oracle.graal.nodes.java.MethodCallTargetNode; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +public class MethodInvocation { + + private final InlineInfo callee; + private final Assumptions assumptions; + private final double probability; + private final double relevance; + + private int processedGraphs; + + public MethodInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { + this.callee = info; + this.assumptions = assumptions; + this.probability = probability; + this.relevance = relevance; + } + + public void incrementProcessedGraphs() { + processedGraphs++; + assert processedGraphs <= callee.numberOfMethods(); + } + + public int processedGraphs() { + assert processedGraphs <= callee.numberOfMethods(); + return processedGraphs; + } + + public int totalGraphs() { + return callee.numberOfMethods(); + } + + public InlineInfo callee() { + return callee; + } + + public Assumptions assumptions() { + return assumptions; + } + + public double probability() { + return probability; + } + + public double relevance() { + return relevance; + } + + public boolean isRoot() { + return callee == null; + } + + @Override + public String toString() { + if (isRoot()) { + return "<root>"; + } + CallTargetNode callTarget = callee.invoke().callTarget(); + if (callTarget instanceof MethodCallTargetNode) { + ResolvedJavaMethod calleeMethod = ((MethodCallTargetNode) callTarget).targetMethod(); + return MetaUtil.format("Invoke#%H.%n(%p)", calleeMethod); + } else { + return "Invoke#" + callTarget.targetName(); + } + } +}