Mercurial > hg > truffle
view graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java @ 7530:5e3d1a68664e
applied mx eclipseformat to all Java files
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Wed, 23 Jan 2013 16:34:57 +0100 |
parents | 42b6e0905881 |
children | 7d66682cc901 |
line wrap: on
line source
/* * 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; import java.lang.reflect.*; import java.util.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.PhasePlan.*; import com.oracle.graal.phases.common.CanonicalizerPhase.CustomCanonicalizer; import com.oracle.graal.phases.common.InliningUtil.*; public class InliningPhase extends Phase implements InliningCallback { /* * - Detect method which only call another method with some parameters set to constants: void * foo(a) -> void foo(a, b) -> void foo(a, b, c) ... These should not be taken into account when * determining inlining depth. - honor the result of overrideInliningDecision(0, caller, * invoke.bci, method, true); */ private final TargetDescription target; private final PhasePlan plan; private final GraalCodeCacheProvider runtime; private final Assumptions assumptions; private final GraphCache cache; private final InliningPolicy inliningPolicy; private CustomCanonicalizer customCanonicalizer; // 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("Runs"); public InliningPhase(TargetDescription target, GraalCodeCacheProvider runtime, Collection<Invoke> hints, Assumptions assumptions, GraphCache cache, PhasePlan plan, OptimisticOptimizations optimisticOpts) { this(target, runtime, assumptions, cache, plan, createInliningPolicy(assumptions, optimisticOpts, hints)); } public void setCustomCanonicalizer(CustomCanonicalizer customCanonicalizer) { this.customCanonicalizer = customCanonicalizer; } public InliningPhase(TargetDescription target, GraalCodeCacheProvider runtime, Assumptions assumptions, GraphCache cache, PhasePlan plan, InliningPolicy inliningPolicy) { this.target = target; this.runtime = runtime; this.assumptions = assumptions; this.cache = cache; this.plan = plan; this.inliningPolicy = inliningPolicy; } @Override protected void run(final StructuredGraph graph) { inliningPolicy.initialize(graph); while (inliningPolicy.continueInlining(graph)) { final InlineInfo candidate = inliningPolicy.next(); if (candidate != null) { boolean isWorthInlining = inliningPolicy.isWorthInlining(candidate); metricInliningConsidered.increment(); if (isWorthInlining) { int mark = graph.getMark(); try { List<Node> invokeUsages = candidate.invoke().node().usages().snapshot(); candidate.inline(graph, runtime, this, assumptions); Debug.dump(graph, "after %s", candidate); Iterable<Node> newNodes = graph.getNewNodes(mark); if (GraalOptions.OptCanonicalizer) { new CanonicalizerPhase(target, runtime, assumptions, invokeUsages, mark, customCanonicalizer).apply(graph); } metricInliningPerformed.increment(); inliningPolicy.scanInvokes(newNodes); } catch (BailoutException bailout) { // TODO determine if we should really bail out of the whole compilation. throw bailout; } catch (AssertionError e) { throw new GraalInternalError(e).addContext(candidate.toString()); } catch (RuntimeException e) { throw new GraalInternalError(e).addContext(candidate.toString()); } catch (GraalInternalError e) { throw e.addContext(candidate.toString()); } } } } } @Override public StructuredGraph buildGraph(final ResolvedJavaMethod method) { metricInliningRuns.increment(); if (GraalOptions.CacheGraphs && cache != null) { StructuredGraph cachedGraph = cache.get(method); if (cachedGraph != null) { return cachedGraph; } } StructuredGraph newGraph = new StructuredGraph(method); if (plan != null) { plan.runPhases(PhasePosition.AFTER_PARSING, newGraph); } assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING"; if (GraalOptions.ProbabilityAnalysis) { new DeadCodeEliminationPhase().apply(newGraph); new ComputeProbabilityPhase().apply(newGraph); } if (GraalOptions.OptCanonicalizer) { new CanonicalizerPhase(target, runtime, assumptions).apply(newGraph); } if (GraalOptions.CullFrameStates) { new CullFrameStatesPhase().apply(newGraph); } if (GraalOptions.CacheGraphs && cache != null) { cache.put(newGraph); } return newGraph; } private interface InliningDecision { boolean isWorthInlining(InlineInfo info); } private abstract static class AbstractInliningDecision implements InliningDecision { protected static boolean decideSizeBasedInlining(InlineInfo info, double maxSize) { assert !Double.isNaN(info.weight()) && !Double.isNaN(maxSize); boolean success = info.weight() <= maxSize; if (GraalOptions.Debug) { String formatterString = success ? "(size %f <= %f)" : "(too large %f > %f)"; InliningUtil.logInliningDecision(info, success, formatterString, info.weight(), maxSize); } return success; } protected static boolean checkCompiledCodeSize(InlineInfo info) { if (GraalOptions.SmallCompiledCodeSize >= 0 && info.compiledCodeSize() > GraalOptions.SmallCompiledCodeSize) { InliningUtil.logNotInlinedMethod(info, "(CompiledCodeSize %d > %d)", info.compiledCodeSize(), GraalOptions.SmallCompiledCodeSize); return false; } return true; } protected static double getRelevance(Invoke invoke) { if (GraalOptions.UseRelevanceBasedInlining) { return invoke.inliningRelevance(); } else { return invoke.probability(); } } } private static class C1StaticSizeBasedInliningDecision extends AbstractInliningDecision { @Override public boolean isWorthInlining(InlineInfo info) { double maxSize = Math.max(GraalOptions.MaximumTrivialSize, Math.pow(GraalOptions.NestedInliningSizeRatio, info.level()) * GraalOptions.MaximumInlineSize); return decideSizeBasedInlining(info, maxSize); } } private static class MinimumCodeSizeBasedInliningDecision extends AbstractInliningDecision { @Override public boolean isWorthInlining(InlineInfo info) { assert GraalOptions.ProbabilityAnalysis; if (!checkCompiledCodeSize(info)) { return false; } double inlineWeight = Math.min(GraalOptions.RatioCapForInlining, getRelevance(info.invoke())); double maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level()) * GraalOptions.MaximumInlineSize * inlineWeight; maxSize = Math.max(GraalOptions.MaximumTrivialSize, maxSize); return decideSizeBasedInlining(info, maxSize); } } private static class DynamicSizeBasedInliningDecision extends AbstractInliningDecision { @Override public boolean isWorthInlining(InlineInfo info) { assert GraalOptions.ProbabilityAnalysis; if (!checkCompiledCodeSize(info)) { return false; } double relevance = getRelevance(info.invoke()); double inlineBoost = Math.min(GraalOptions.RatioCapForInlining, relevance) + Math.log10(Math.max(1, relevance - GraalOptions.RatioCapForInlining + 1)); double maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level()) * GraalOptions.MaximumInlineSize; maxSize = maxSize + maxSize * inlineBoost; maxSize = Math.min(GraalOptions.MaximumGreedyInlineSize, Math.max(GraalOptions.MaximumTrivialSize, maxSize)); return decideSizeBasedInlining(info, maxSize); } } private static class GreedySizeBasedInliningDecision extends AbstractInliningDecision { @Override public boolean isWorthInlining(InlineInfo info) { assert GraalOptions.ProbabilityAnalysis; if (!checkCompiledCodeSize(info)) { return false; } double maxSize = GraalOptions.MaximumGreedyInlineSize; if (GraalOptions.InliningBonusPerTransferredValue != 0) { Signature signature = info.invoke().methodCallTarget().targetMethod().getSignature(); int transferredValues = signature.getParameterCount(!Modifier.isStatic(info.invoke().methodCallTarget().targetMethod().getModifiers())); if (signature.getReturnKind() != Kind.Void) { transferredValues++; } maxSize += transferredValues * GraalOptions.InliningBonusPerTransferredValue; } double inlineRatio = Math.min(GraalOptions.RatioCapForInlining, getRelevance(info.invoke())); maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level()) * maxSize * inlineRatio; maxSize = Math.max(maxSize, GraalOptions.MaximumTrivialSize); return decideSizeBasedInlining(info, maxSize); } } private static class GreedyMachineCodeInliningDecision extends AbstractInliningDecision { @Override public boolean isWorthInlining(InlineInfo info) { assert GraalOptions.ProbabilityAnalysis; double maxSize = GraalOptions.MaximumGreedyInlineSize; double inlineRatio = Math.min(GraalOptions.RatioCapForInlining, getRelevance(info.invoke())); maxSize = Math.pow(GraalOptions.NestedInliningSizeRatio, info.level()) * maxSize * inlineRatio; maxSize = Math.max(maxSize, GraalOptions.MaximumTrivialSize); return decideSizeBasedInlining(info, maxSize); } } private static class BytecodeSizeBasedWeightComputationPolicy implements WeightComputationPolicy { @Override public double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke) { if (GraalOptions.AlwaysInlineIntrinsics && InliningUtil.canIntrinsify(method)) { return 0; } double codeSize = method.getCodeSize(); if (preferredInvoke) { codeSize = codeSize / GraalOptions.BoostInliningForEscapeAnalysis; } return codeSize; } } private static class ComplexityBasedWeightComputationPolicy implements WeightComputationPolicy { @Override public double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke) { if (GraalOptions.AlwaysInlineIntrinsics && InliningUtil.canIntrinsify(method)) { return 0; } double complexity = method.getCompilationComplexity(); if (preferredInvoke) { complexity = complexity / GraalOptions.BoostInliningForEscapeAnalysis; } return complexity; } } private static class CompiledCodeSizeWeightComputationPolicy implements WeightComputationPolicy { @Override public double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke) { if (GraalOptions.AlwaysInlineIntrinsics && InliningUtil.canIntrinsify(method)) { return 0; } int compiledCodeSize = method.getCompiledCodeSize(); return compiledCodeSize > 0 ? compiledCodeSize : method.getCodeSize() * 10; } } private static class CFInliningPolicy implements InliningPolicy { private final InliningDecision inliningDecision; private final WeightComputationPolicy weightComputationPolicy; private final Collection<Invoke> hints; private final Assumptions assumptions; private final OptimisticOptimizations optimisticOpts; private final Deque<Invoke> sortedInvokes; private NodeBitMap visitedFixedNodes; private FixedNode invokePredecessor; public CFInliningPolicy(InliningDecision inliningPolicy, WeightComputationPolicy weightComputationPolicy, Collection<Invoke> hints, Assumptions assumptions, OptimisticOptimizations optimisticOpts) { this.inliningDecision = inliningPolicy; this.weightComputationPolicy = weightComputationPolicy; this.hints = hints; this.assumptions = assumptions; this.optimisticOpts = optimisticOpts; this.sortedInvokes = new ArrayDeque<>(); } public boolean continueInlining(StructuredGraph graph) { if (graph.getNodeCount() >= GraalOptions.MaximumDesiredSize) { InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize"); metricInliningStoppedByMaxDesiredSize.increment(); return false; } return !sortedInvokes.isEmpty(); } public InlineInfo next() { Invoke invoke = sortedInvokes.pop(); InlineInfo info = InliningUtil.getInlineInfo(invoke, assumptions, this, optimisticOpts); if (info != null) { invokePredecessor = (FixedNode) info.invoke().predecessor(); assert invokePredecessor.isAlive(); } return info; } public boolean isWorthInlining(InlineInfo info) { return inliningDecision.isWorthInlining(info); } public void initialize(StructuredGraph graph) { visitedFixedNodes = graph.createNodeBitMap(true); scanGraphForInvokes(graph.start()); if (hints != null) { sortedInvokes.retainAll(hints); } } public void scanInvokes(Iterable<? extends Node> newNodes) { scanGraphForInvokes(invokePredecessor); } private void scanGraphForInvokes(FixedNode start) { ArrayList<Invoke> invokes = new InliningIterator(start, visitedFixedNodes).apply(); // insert the newly found invokes in their correct control-flow order for (int i = invokes.size() - 1; i >= 0; i--) { sortedInvokes.addFirst(invokes.get(i)); } } public double inliningWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke) { boolean preferredInvoke = hints != null && hints.contains(invoke); return weightComputationPolicy.computeWeight(caller, method, invoke, preferredInvoke); } } private static class PriorityInliningPolicy implements InliningPolicy { private final InliningDecision inliningDecision; private final WeightComputationPolicy weightComputationPolicy; private final Collection<Invoke> hints; private final Assumptions assumptions; private final OptimisticOptimizations optimisticOpts; private final PriorityQueue<InlineInfo> sortedCandidates; public PriorityInliningPolicy(InliningDecision inliningPolicy, WeightComputationPolicy weightComputationPolicy, Collection<Invoke> hints, Assumptions assumptions, OptimisticOptimizations optimisticOpts) { this.inliningDecision = inliningPolicy; this.weightComputationPolicy = weightComputationPolicy; this.hints = hints; this.assumptions = assumptions; this.optimisticOpts = optimisticOpts; sortedCandidates = new PriorityQueue<>(); } public boolean continueInlining(StructuredGraph graph) { if (graph.getNodeCount() >= GraalOptions.MaximumDesiredSize) { InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize"); metricInliningStoppedByMaxDesiredSize.increment(); return false; } return !sortedCandidates.isEmpty(); } public InlineInfo next() { // refresh cached info before using it (it might have been in the queue for a long time) InlineInfo info = sortedCandidates.remove(); return InliningUtil.getInlineInfo(info.invoke(), assumptions, this, optimisticOpts); } @Override public boolean isWorthInlining(InlineInfo info) { return inliningDecision.isWorthInlining(info); } @SuppressWarnings("unchecked") public void initialize(StructuredGraph graph) { if (hints == null) { scanInvokes(graph.getNodes(InvokeNode.class)); scanInvokes(graph.getNodes(InvokeWithExceptionNode.class)); } else { scanInvokes((Iterable<? extends Node>) (Iterable<?>) hints); } } public void scanInvokes(Iterable<? extends Node> nodes) { for (Node node : nodes) { if (node != null) { if (node instanceof Invoke) { Invoke invoke = (Invoke) node; scanInvoke(invoke); } for (Node usage : node.usages().filterInterface(Invoke.class).snapshot()) { scanInvoke((Invoke) usage); } } } } private void scanInvoke(Invoke invoke) { InlineInfo info = InliningUtil.getInlineInfo(invoke, assumptions, this, optimisticOpts); if (info != null) { sortedCandidates.add(info); } } @Override public double inliningWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke) { boolean preferredInvoke = hints != null && hints.contains(invoke); return weightComputationPolicy.computeWeight(caller, method, invoke, preferredInvoke); } } private static class InliningIterator { private final FixedNode start; private final NodeBitMap processedNodes; private final Deque<FixedNode> nodeQueue; private final NodeBitMap queuedNodes; public InliningIterator(FixedNode start, NodeBitMap visitedFixedNodes) { this.start = start; this.processedNodes = visitedFixedNodes; this.nodeQueue = new ArrayDeque<>(); this.queuedNodes = visitedFixedNodes.copy(); assert start.isAlive(); } public ArrayList<Invoke> apply() { ArrayList<Invoke> invokes = new ArrayList<>(); FixedNode current = start; do { assert current.isAlive(); processedNodes.mark(current); if (current instanceof InvokeWithExceptionNode || current instanceof InvokeNode) { invokes.add((Invoke) current); queueSuccessors(current); current = nextQueuedNode(); } else if (current instanceof LoopBeginNode) { current = ((LoopBeginNode) current).next(); assert current != null; } else if (current instanceof LoopEndNode) { current = nextQueuedNode(); } else if (current instanceof MergeNode) { current = ((MergeNode) current).next(); assert current != null; } else if (current instanceof FixedWithNextNode) { queueSuccessors(current); current = nextQueuedNode(); } else if (current instanceof EndNode) { queueMerge((EndNode) current); current = nextQueuedNode(); } else if (current instanceof DeoptimizeNode) { current = nextQueuedNode(); } else if (current instanceof ReturnNode) { current = nextQueuedNode(); } else if (current instanceof UnwindNode) { current = nextQueuedNode(); } else if (current instanceof ControlSplitNode) { queueSuccessors(current); current = nextQueuedNode(); } else { assert false : current; } } while (current != null); return invokes; } private void queueSuccessors(FixedNode x) { for (Node node : x.successors()) { if (node != null && !queuedNodes.isMarked(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(EndNode 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 (!processedNodes.isMarked(merge.forwardEndAt(i))) { return false; } } return true; } } private static InliningPolicy createInliningPolicy(Assumptions assumptions, OptimisticOptimizations optimisticOpts, Collection<Invoke> hints) { switch (GraalOptions.InliningPolicy) { case 0: return new CFInliningPolicy(createInliningDecision(), createWeightComputationPolicy(), hints, assumptions, optimisticOpts); case 1: return new PriorityInliningPolicy(createInliningDecision(), createWeightComputationPolicy(), hints, assumptions, optimisticOpts); default: GraalInternalError.shouldNotReachHere(); return null; } } private static InliningDecision createInliningDecision() { switch (GraalOptions.InliningDecision) { case 1: return new C1StaticSizeBasedInliningDecision(); case 2: return new MinimumCodeSizeBasedInliningDecision(); case 3: return new DynamicSizeBasedInliningDecision(); case 4: return new GreedySizeBasedInliningDecision(); case 5: return new GreedyMachineCodeInliningDecision(); default: GraalInternalError.shouldNotReachHere(); return null; } } private static WeightComputationPolicy createWeightComputationPolicy() { switch (GraalOptions.WeightComputationPolicy) { case 0: throw new GraalInternalError("removed because of invokation counter changes"); case 1: return new BytecodeSizeBasedWeightComputationPolicy(); case 2: return new ComplexityBasedWeightComputationPolicy(); case 3: return new CompiledCodeSizeWeightComputationPolicy(); default: GraalInternalError.shouldNotReachHere(); return null; } } }