43972
|
1 |
/*
|
|
2 |
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
|
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 |
*
|
|
5 |
* This code is free software; you can redistribute it and/or modify it
|
|
6 |
* under the terms of the GNU General Public License version 2 only, as
|
|
7 |
* published by the Free Software Foundation.
|
|
8 |
*
|
|
9 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
10 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
11 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
12 |
* version 2 for more details (a copy is included in the LICENSE file that
|
|
13 |
* accompanied this code).
|
|
14 |
*
|
|
15 |
* You should have received a copy of the GNU General Public License version
|
|
16 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
17 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
18 |
*
|
|
19 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
20 |
* or visit www.oracle.com if you need additional information or have any
|
|
21 |
* questions.
|
|
22 |
*/
|
|
23 |
package org.graalvm.compiler.phases.common.inlining.walker;
|
|
24 |
|
|
25 |
import static org.graalvm.compiler.core.common.GraalOptions.Intrinsify;
|
|
26 |
import static org.graalvm.compiler.core.common.GraalOptions.MaximumRecursiveInlining;
|
|
27 |
import static org.graalvm.compiler.core.common.GraalOptions.MegamorphicInliningMinMethodProbability;
|
|
28 |
|
|
29 |
import java.util.ArrayDeque;
|
|
30 |
import java.util.ArrayList;
|
|
31 |
import java.util.BitSet;
|
|
32 |
import java.util.Collection;
|
|
33 |
import java.util.Iterator;
|
46344
|
34 |
import java.util.LinkedList;
|
43972
|
35 |
import java.util.List;
|
|
36 |
|
48861
|
37 |
import org.graalvm.collections.EconomicSet;
|
|
38 |
import org.graalvm.collections.Equivalence;
|
43972
|
39 |
import org.graalvm.compiler.core.common.type.ObjectStamp;
|
46640
|
40 |
import org.graalvm.compiler.debug.CounterKey;
|
|
41 |
import org.graalvm.compiler.debug.DebugContext;
|
43972
|
42 |
import org.graalvm.compiler.debug.GraalError;
|
|
43 |
import org.graalvm.compiler.graph.Graph;
|
|
44 |
import org.graalvm.compiler.graph.Node;
|
|
45 |
import org.graalvm.compiler.nodes.CallTargetNode;
|
|
46 |
import org.graalvm.compiler.nodes.Invoke;
|
48190
|
47 |
import org.graalvm.compiler.nodes.NodeView;
|
43972
|
48 |
import org.graalvm.compiler.nodes.ParameterNode;
|
|
49 |
import org.graalvm.compiler.nodes.StructuredGraph;
|
|
50 |
import org.graalvm.compiler.nodes.ValueNode;
|
|
51 |
import org.graalvm.compiler.nodes.java.AbstractNewObjectNode;
|
|
52 |
import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
|
|
53 |
import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
|
|
54 |
import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
|
46344
|
55 |
import org.graalvm.compiler.options.OptionValues;
|
43972
|
56 |
import org.graalvm.compiler.phases.OptimisticOptimizations;
|
|
57 |
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
|
|
58 |
import org.graalvm.compiler.phases.common.inlining.InliningUtil;
|
|
59 |
import org.graalvm.compiler.phases.common.inlining.info.AssumptionInlineInfo;
|
|
60 |
import org.graalvm.compiler.phases.common.inlining.info.ExactInlineInfo;
|
|
61 |
import org.graalvm.compiler.phases.common.inlining.info.InlineInfo;
|
|
62 |
import org.graalvm.compiler.phases.common.inlining.info.MultiTypeGuardInlineInfo;
|
|
63 |
import org.graalvm.compiler.phases.common.inlining.info.TypeGuardInlineInfo;
|
|
64 |
import org.graalvm.compiler.phases.common.inlining.info.elem.Inlineable;
|
|
65 |
import org.graalvm.compiler.phases.common.inlining.info.elem.InlineableGraph;
|
|
66 |
import org.graalvm.compiler.phases.common.inlining.policy.InliningPolicy;
|
|
67 |
import org.graalvm.compiler.phases.tiers.HighTierContext;
|
|
68 |
import org.graalvm.compiler.phases.util.Providers;
|
|
69 |
|
|
70 |
import jdk.vm.ci.code.BailoutException;
|
|
71 |
import jdk.vm.ci.meta.Assumptions.AssumptionResult;
|
|
72 |
import jdk.vm.ci.meta.JavaTypeProfile;
|
|
73 |
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
|
74 |
import jdk.vm.ci.meta.ResolvedJavaType;
|
|
75 |
|
|
76 |
/**
|
|
77 |
* <p>
|
|
78 |
* The space of inlining decisions is explored depth-first with the help of a stack realized by
|
|
79 |
* {@link InliningData}. At any point in time, the topmost element of that stack consists of:
|
|
80 |
* <ul>
|
|
81 |
* <li>the callsite under consideration is tracked as a {@link MethodInvocation}.</li>
|
|
82 |
* <li>one or more {@link CallsiteHolder}s, all of them associated to the callsite above. Why more
|
|
83 |
* than one? Depending on the type-profile for the receiver more than one concrete method may be
|
|
84 |
* feasible target.</li>
|
|
85 |
* </ul>
|
|
86 |
* </p>
|
|
87 |
*
|
|
88 |
* <p>
|
|
89 |
* The bottom element in the stack consists of:
|
|
90 |
* <ul>
|
|
91 |
* <li>a single {@link MethodInvocation} (the
|
|
92 |
* {@link org.graalvm.compiler.phases.common.inlining.walker.MethodInvocation#isRoot root} one, ie
|
|
93 |
* the unknown caller of the root graph)</li>
|
|
94 |
* <li>a single {@link CallsiteHolder} (the root one, for the method on which inlining was called)
|
|
95 |
* </li>
|
|
96 |
* </ul>
|
|
97 |
* </p>
|
|
98 |
*
|
|
99 |
* @see #moveForward()
|
|
100 |
*/
|
|
101 |
public class InliningData {
|
|
102 |
|
|
103 |
// Counters
|
46640
|
104 |
private static final CounterKey counterInliningPerformed = DebugContext.counter("InliningPerformed");
|
|
105 |
private static final CounterKey counterInliningRuns = DebugContext.counter("InliningRuns");
|
|
106 |
private static final CounterKey counterInliningConsidered = DebugContext.counter("InliningConsidered");
|
43972
|
107 |
|
|
108 |
/**
|
|
109 |
* Call hierarchy from outer most call (i.e., compilation unit) to inner most callee.
|
|
110 |
*/
|
|
111 |
private final ArrayDeque<CallsiteHolder> graphQueue = new ArrayDeque<>();
|
|
112 |
private final ArrayDeque<MethodInvocation> invocationQueue = new ArrayDeque<>();
|
|
113 |
|
|
114 |
private final HighTierContext context;
|
|
115 |
private final int maxMethodPerInlining;
|
|
116 |
private final CanonicalizerPhase canonicalizer;
|
|
117 |
private final InliningPolicy inliningPolicy;
|
|
118 |
private final StructuredGraph rootGraph;
|
46640
|
119 |
private final DebugContext debug;
|
43972
|
120 |
|
|
121 |
private int maxGraphs;
|
|
122 |
|
46344
|
123 |
public InliningData(StructuredGraph rootGraph, HighTierContext context, int maxMethodPerInlining, CanonicalizerPhase canonicalizer, InliningPolicy inliningPolicy, LinkedList<Invoke> rootInvokes) {
|
43972
|
124 |
assert rootGraph != null;
|
|
125 |
this.context = context;
|
|
126 |
this.maxMethodPerInlining = maxMethodPerInlining;
|
|
127 |
this.canonicalizer = canonicalizer;
|
|
128 |
this.inliningPolicy = inliningPolicy;
|
|
129 |
this.maxGraphs = 1;
|
|
130 |
this.rootGraph = rootGraph;
|
46640
|
131 |
this.debug = rootGraph.getDebug();
|
43972
|
132 |
|
|
133 |
invocationQueue.push(new MethodInvocation(null, 1.0, 1.0, null));
|
46344
|
134 |
graphQueue.push(new CallsiteHolderExplorable(rootGraph, 1.0, 1.0, null, rootInvokes));
|
43972
|
135 |
}
|
|
136 |
|
|
137 |
public static boolean isFreshInstantiation(ValueNode arg) {
|
|
138 |
return (arg instanceof AbstractNewObjectNode) || (arg instanceof AllocatedObjectNode) || (arg instanceof VirtualObjectNode);
|
|
139 |
}
|
|
140 |
|
|
141 |
private String checkTargetConditionsHelper(ResolvedJavaMethod method, int invokeBci) {
|
46344
|
142 |
OptionValues options = rootGraph.getOptions();
|
43972
|
143 |
if (method == null) {
|
|
144 |
return "the method is not resolved";
|
46344
|
145 |
} else if (method.isNative() && (!Intrinsify.getValue(options) || !InliningUtil.canIntrinsify(context.getReplacements(), method, invokeBci))) {
|
43972
|
146 |
return "it is a non-intrinsic native method";
|
|
147 |
} else if (method.isAbstract()) {
|
|
148 |
return "it is an abstract method";
|
|
149 |
} else if (!method.getDeclaringClass().isInitialized()) {
|
|
150 |
return "the method's class is not initialized";
|
|
151 |
} else if (!method.canBeInlined()) {
|
|
152 |
return "it is marked non-inlinable";
|
46344
|
153 |
} else if (countRecursiveInlining(method) > MaximumRecursiveInlining.getValue(options)) {
|
43972
|
154 |
return "it exceeds the maximum recursive inlining depth";
|
|
155 |
} else {
|
46344
|
156 |
if (new OptimisticOptimizations(rootGraph.getProfilingInfo(method), options).lessOptimisticThan(context.getOptimisticOptimizations())) {
|
|
157 |
return "the callee uses less optimistic optimizations than caller";
|
|
158 |
} else {
|
|
159 |
return null;
|
|
160 |
}
|
43972
|
161 |
}
|
|
162 |
}
|
|
163 |
|
|
164 |
private boolean checkTargetConditions(Invoke invoke, ResolvedJavaMethod method) {
|
|
165 |
final String failureMessage = checkTargetConditionsHelper(method, invoke.bci());
|
|
166 |
if (failureMessage == null) {
|
|
167 |
return true;
|
|
168 |
} else {
|
|
169 |
InliningUtil.logNotInlined(invoke, inliningDepth(), method, failureMessage);
|
|
170 |
return false;
|
|
171 |
}
|
|
172 |
}
|
|
173 |
|
|
174 |
/**
|
|
175 |
* Determines if inlining is possible at the given invoke node.
|
|
176 |
*
|
|
177 |
* @param invoke the invoke that should be inlined
|
|
178 |
* @return an instance of InlineInfo, or null if no inlining is possible at the given invoke
|
|
179 |
*/
|
|
180 |
private InlineInfo getInlineInfo(Invoke invoke) {
|
|
181 |
final String failureMessage = InliningUtil.checkInvokeConditions(invoke);
|
|
182 |
if (failureMessage != null) {
|
|
183 |
InliningUtil.logNotInlinedMethod(invoke, failureMessage);
|
|
184 |
return null;
|
|
185 |
}
|
|
186 |
MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
|
|
187 |
ResolvedJavaMethod targetMethod = callTarget.targetMethod();
|
|
188 |
|
|
189 |
if (callTarget.invokeKind() == CallTargetNode.InvokeKind.Special || targetMethod.canBeStaticallyBound()) {
|
|
190 |
return getExactInlineInfo(invoke, targetMethod);
|
|
191 |
}
|
|
192 |
|
|
193 |
assert callTarget.invokeKind().isIndirect();
|
|
194 |
|
|
195 |
ResolvedJavaType holder = targetMethod.getDeclaringClass();
|
48190
|
196 |
if (!(callTarget.receiver().stamp(NodeView.DEFAULT) instanceof ObjectStamp)) {
|
43972
|
197 |
return null;
|
|
198 |
}
|
48190
|
199 |
ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp(NodeView.DEFAULT);
|
43972
|
200 |
if (receiverStamp.alwaysNull()) {
|
|
201 |
// Don't inline if receiver is known to be null
|
|
202 |
return null;
|
|
203 |
}
|
|
204 |
ResolvedJavaType contextType = invoke.getContextType();
|
|
205 |
if (receiverStamp.type() != null) {
|
|
206 |
// the invoke target might be more specific than the holder (happens after inlining:
|
|
207 |
// parameters lose their declared type...)
|
|
208 |
ResolvedJavaType receiverType = receiverStamp.type();
|
|
209 |
if (receiverType != null && holder.isAssignableFrom(receiverType)) {
|
|
210 |
holder = receiverType;
|
|
211 |
if (receiverStamp.isExactType()) {
|
|
212 |
assert targetMethod.getDeclaringClass().isAssignableFrom(holder) : holder + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod;
|
|
213 |
ResolvedJavaMethod resolvedMethod = holder.resolveConcreteMethod(targetMethod, contextType);
|
|
214 |
if (resolvedMethod != null) {
|
|
215 |
return getExactInlineInfo(invoke, resolvedMethod);
|
|
216 |
}
|
|
217 |
}
|
|
218 |
}
|
|
219 |
}
|
|
220 |
|
|
221 |
if (holder.isArray()) {
|
|
222 |
// arrays can be treated as Objects
|
|
223 |
ResolvedJavaMethod resolvedMethod = holder.resolveConcreteMethod(targetMethod, contextType);
|
|
224 |
if (resolvedMethod != null) {
|
|
225 |
return getExactInlineInfo(invoke, resolvedMethod);
|
|
226 |
}
|
|
227 |
}
|
|
228 |
|
|
229 |
AssumptionResult<ResolvedJavaType> leafConcreteSubtype = holder.findLeafConcreteSubtype();
|
|
230 |
if (leafConcreteSubtype != null) {
|
|
231 |
ResolvedJavaMethod resolvedMethod = leafConcreteSubtype.getResult().resolveConcreteMethod(targetMethod, contextType);
|
|
232 |
if (resolvedMethod != null) {
|
|
233 |
if (leafConcreteSubtype.canRecordTo(callTarget.graph().getAssumptions())) {
|
|
234 |
return getAssumptionInlineInfo(invoke, resolvedMethod, leafConcreteSubtype);
|
|
235 |
} else {
|
|
236 |
return getTypeCheckedAssumptionInfo(invoke, resolvedMethod, leafConcreteSubtype.getResult());
|
|
237 |
}
|
|
238 |
}
|
|
239 |
}
|
|
240 |
|
|
241 |
AssumptionResult<ResolvedJavaMethod> concrete = holder.findUniqueConcreteMethod(targetMethod);
|
|
242 |
if (concrete != null && concrete.canRecordTo(callTarget.graph().getAssumptions())) {
|
|
243 |
return getAssumptionInlineInfo(invoke, concrete.getResult(), concrete);
|
|
244 |
}
|
|
245 |
|
|
246 |
// type check based inlining
|
|
247 |
return getTypeCheckedInlineInfo(invoke, targetMethod);
|
|
248 |
}
|
|
249 |
|
|
250 |
private InlineInfo getTypeCheckedAssumptionInfo(Invoke invoke, ResolvedJavaMethod method, ResolvedJavaType type) {
|
|
251 |
if (!checkTargetConditions(invoke, method)) {
|
|
252 |
return null;
|
|
253 |
}
|
|
254 |
return new TypeGuardInlineInfo(invoke, method, type);
|
|
255 |
}
|
|
256 |
|
|
257 |
private InlineInfo getTypeCheckedInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) {
|
|
258 |
JavaTypeProfile typeProfile = ((MethodCallTargetNode) invoke.callTarget()).getProfile();
|
|
259 |
if (typeProfile == null) {
|
|
260 |
InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "no type profile exists");
|
|
261 |
return null;
|
|
262 |
}
|
|
263 |
|
|
264 |
JavaTypeProfile.ProfiledType[] ptypes = typeProfile.getTypes();
|
|
265 |
if (ptypes == null || ptypes.length <= 0) {
|
|
266 |
InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "no types in profile");
|
|
267 |
return null;
|
|
268 |
}
|
|
269 |
ResolvedJavaType contextType = invoke.getContextType();
|
|
270 |
double notRecordedTypeProbability = typeProfile.getNotRecordedProbability();
|
|
271 |
final OptimisticOptimizations optimisticOpts = context.getOptimisticOptimizations();
|
46344
|
272 |
OptionValues options = invoke.asNode().getOptions();
|
43972
|
273 |
if (ptypes.length == 1 && notRecordedTypeProbability == 0) {
|
46344
|
274 |
if (!optimisticOpts.inlineMonomorphicCalls(options)) {
|
43972
|
275 |
InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "inlining monomorphic calls is disabled");
|
|
276 |
return null;
|
|
277 |
}
|
|
278 |
|
|
279 |
ResolvedJavaType type = ptypes[0].getType();
|
|
280 |
assert type.isArray() || type.isConcrete();
|
|
281 |
ResolvedJavaMethod concrete = type.resolveConcreteMethod(targetMethod, contextType);
|
|
282 |
if (!checkTargetConditions(invoke, concrete)) {
|
|
283 |
return null;
|
|
284 |
}
|
|
285 |
return new TypeGuardInlineInfo(invoke, concrete, type);
|
|
286 |
} else {
|
|
287 |
invoke.setPolymorphic(true);
|
|
288 |
|
46344
|
289 |
if (!optimisticOpts.inlinePolymorphicCalls(options) && notRecordedTypeProbability == 0) {
|
43972
|
290 |
InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length);
|
|
291 |
return null;
|
|
292 |
}
|
46344
|
293 |
if (!optimisticOpts.inlineMegamorphicCalls(options) && notRecordedTypeProbability > 0) {
|
43972
|
294 |
// due to filtering impossible types, notRecordedTypeProbability can be > 0 although
|
|
295 |
// the number of types is lower than what can be recorded in a type profile
|
|
296 |
InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length,
|
|
297 |
notRecordedTypeProbability * 100);
|
|
298 |
return null;
|
|
299 |
}
|
|
300 |
|
|
301 |
// Find unique methods and their probabilities.
|
|
302 |
ArrayList<ResolvedJavaMethod> concreteMethods = new ArrayList<>();
|
|
303 |
ArrayList<Double> concreteMethodsProbabilities = new ArrayList<>();
|
|
304 |
for (int i = 0; i < ptypes.length; i++) {
|
|
305 |
ResolvedJavaMethod concrete = ptypes[i].getType().resolveConcreteMethod(targetMethod, contextType);
|
|
306 |
if (concrete == null) {
|
|
307 |
InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "could not resolve method");
|
|
308 |
return null;
|
|
309 |
}
|
|
310 |
int index = concreteMethods.indexOf(concrete);
|
|
311 |
double curProbability = ptypes[i].getProbability();
|
|
312 |
if (index < 0) {
|
|
313 |
index = concreteMethods.size();
|
|
314 |
concreteMethods.add(concrete);
|
|
315 |
concreteMethodsProbabilities.add(curProbability);
|
|
316 |
} else {
|
|
317 |
concreteMethodsProbabilities.set(index, concreteMethodsProbabilities.get(index) + curProbability);
|
|
318 |
}
|
|
319 |
}
|
|
320 |
|
|
321 |
// Clear methods that fall below the threshold.
|
|
322 |
if (notRecordedTypeProbability > 0) {
|
|
323 |
ArrayList<ResolvedJavaMethod> newConcreteMethods = new ArrayList<>();
|
|
324 |
ArrayList<Double> newConcreteMethodsProbabilities = new ArrayList<>();
|
|
325 |
for (int i = 0; i < concreteMethods.size(); ++i) {
|
46344
|
326 |
if (concreteMethodsProbabilities.get(i) >= MegamorphicInliningMinMethodProbability.getValue(options)) {
|
43972
|
327 |
newConcreteMethods.add(concreteMethods.get(i));
|
|
328 |
newConcreteMethodsProbabilities.add(concreteMethodsProbabilities.get(i));
|
|
329 |
}
|
|
330 |
}
|
|
331 |
|
|
332 |
if (newConcreteMethods.isEmpty()) {
|
|
333 |
// No method left that is worth inlining.
|
|
334 |
InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)",
|
|
335 |
concreteMethods.size());
|
|
336 |
return null;
|
|
337 |
}
|
|
338 |
|
|
339 |
concreteMethods = newConcreteMethods;
|
|
340 |
concreteMethodsProbabilities = newConcreteMethodsProbabilities;
|
|
341 |
}
|
|
342 |
|
|
343 |
if (concreteMethods.size() > maxMethodPerInlining) {
|
|
344 |
InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxMethodPerInlining);
|
|
345 |
return null;
|
|
346 |
}
|
|
347 |
|
|
348 |
// Clean out types whose methods are no longer available.
|
|
349 |
ArrayList<JavaTypeProfile.ProfiledType> usedTypes = new ArrayList<>();
|
|
350 |
ArrayList<Integer> typesToConcretes = new ArrayList<>();
|
|
351 |
for (JavaTypeProfile.ProfiledType type : ptypes) {
|
|
352 |
ResolvedJavaMethod concrete = type.getType().resolveConcreteMethod(targetMethod, contextType);
|
|
353 |
int index = concreteMethods.indexOf(concrete);
|
|
354 |
if (index == -1) {
|
|
355 |
notRecordedTypeProbability += type.getProbability();
|
|
356 |
} else {
|
|
357 |
assert type.getType().isArray() || !type.getType().isAbstract() : type + " " + concrete;
|
|
358 |
usedTypes.add(type);
|
|
359 |
typesToConcretes.add(index);
|
|
360 |
}
|
|
361 |
}
|
|
362 |
|
|
363 |
if (usedTypes.isEmpty()) {
|
|
364 |
// No type left that is worth checking for.
|
|
365 |
InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length);
|
|
366 |
return null;
|
|
367 |
}
|
|
368 |
|
|
369 |
for (ResolvedJavaMethod concrete : concreteMethods) {
|
|
370 |
if (!checkTargetConditions(invoke, concrete)) {
|
|
371 |
InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined");
|
|
372 |
return null;
|
|
373 |
}
|
|
374 |
}
|
|
375 |
return new MultiTypeGuardInlineInfo(invoke, concreteMethods, usedTypes, typesToConcretes, notRecordedTypeProbability);
|
|
376 |
}
|
|
377 |
}
|
|
378 |
|
|
379 |
private InlineInfo getAssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, AssumptionResult<?> takenAssumption) {
|
|
380 |
assert concrete.isConcrete();
|
|
381 |
if (checkTargetConditions(invoke, concrete)) {
|
|
382 |
return new AssumptionInlineInfo(invoke, concrete, takenAssumption);
|
|
383 |
}
|
|
384 |
return null;
|
|
385 |
}
|
|
386 |
|
|
387 |
private InlineInfo getExactInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) {
|
|
388 |
assert targetMethod.isConcrete();
|
|
389 |
if (checkTargetConditions(invoke, targetMethod)) {
|
|
390 |
return new ExactInlineInfo(invoke, targetMethod);
|
|
391 |
}
|
|
392 |
return null;
|
|
393 |
}
|
|
394 |
|
|
395 |
@SuppressWarnings("try")
|
|
396 |
private void doInline(CallsiteHolderExplorable callerCallsiteHolder, MethodInvocation calleeInvocation) {
|
|
397 |
StructuredGraph callerGraph = callerCallsiteHolder.graph();
|
|
398 |
InlineInfo calleeInfo = calleeInvocation.callee();
|
|
399 |
try {
|
46640
|
400 |
try (DebugContext.Scope scope = debug.scope("doInline", callerGraph)) {
|
46344
|
401 |
EconomicSet<Node> canonicalizedNodes = EconomicSet.create(Equivalence.IDENTITY);
|
|
402 |
canonicalizedNodes.addAll(calleeInfo.invoke().asNode().usages());
|
|
403 |
EconomicSet<Node> parameterUsages = calleeInfo.inline(new Providers(context));
|
43972
|
404 |
canonicalizedNodes.addAll(parameterUsages);
|
46640
|
405 |
counterInliningRuns.increment(debug);
|
|
406 |
debug.dump(DebugContext.DETAILED_LEVEL, callerGraph, "after %s", calleeInfo);
|
43972
|
407 |
|
|
408 |
Graph.Mark markBeforeCanonicalization = callerGraph.getMark();
|
|
409 |
|
|
410 |
canonicalizer.applyIncremental(callerGraph, context, canonicalizedNodes);
|
|
411 |
|
|
412 |
// process invokes that are possibly created during canonicalization
|
|
413 |
for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) {
|
|
414 |
if (newNode instanceof Invoke) {
|
|
415 |
callerCallsiteHolder.pushInvoke((Invoke) newNode);
|
|
416 |
}
|
|
417 |
}
|
|
418 |
|
|
419 |
callerCallsiteHolder.computeProbabilities();
|
|
420 |
|
46640
|
421 |
counterInliningPerformed.increment(debug);
|
43972
|
422 |
}
|
|
423 |
} catch (BailoutException bailout) {
|
|
424 |
throw bailout;
|
|
425 |
} catch (AssertionError | RuntimeException e) {
|
|
426 |
throw new GraalError(e).addContext(calleeInfo.toString());
|
|
427 |
} catch (GraalError e) {
|
|
428 |
throw e.addContext(calleeInfo.toString());
|
|
429 |
} catch (Throwable e) {
|
46640
|
430 |
throw debug.handle(e);
|
43972
|
431 |
}
|
|
432 |
}
|
|
433 |
|
|
434 |
/**
|
|
435 |
*
|
|
436 |
* This method attempts:
|
|
437 |
* <ol>
|
|
438 |
* <li>to inline at the callsite given by <code>calleeInvocation</code>, where that callsite
|
|
439 |
* belongs to the {@link CallsiteHolderExplorable} at the top of the {@link #graphQueue}
|
|
440 |
* maintained in this class.</li>
|
|
441 |
* <li>otherwise, to devirtualize the callsite in question.</li>
|
|
442 |
* </ol>
|
|
443 |
*
|
|
444 |
* @return true iff inlining was actually performed
|
|
445 |
*/
|
|
446 |
private boolean tryToInline(MethodInvocation calleeInvocation, int inliningDepth) {
|
|
447 |
CallsiteHolderExplorable callerCallsiteHolder = (CallsiteHolderExplorable) currentGraph();
|
|
448 |
InlineInfo calleeInfo = calleeInvocation.callee();
|
|
449 |
assert callerCallsiteHolder.containsInvoke(calleeInfo.invoke());
|
46640
|
450 |
counterInliningConsidered.increment(debug);
|
43972
|
451 |
|
|
452 |
if (inliningPolicy.isWorthInlining(context.getReplacements(), calleeInvocation, inliningDepth, true)) {
|
|
453 |
doInline(callerCallsiteHolder, calleeInvocation);
|
|
454 |
return true;
|
|
455 |
}
|
|
456 |
|
46344
|
457 |
if (context.getOptimisticOptimizations().devirtualizeInvokes(calleeInfo.graph().getOptions())) {
|
43972
|
458 |
calleeInfo.tryToDevirtualizeInvoke(new Providers(context));
|
|
459 |
}
|
|
460 |
|
|
461 |
return false;
|
|
462 |
}
|
|
463 |
|
|
464 |
/**
|
|
465 |
* This method picks one of the callsites belonging to the current
|
|
466 |
* {@link CallsiteHolderExplorable}. Provided the callsite qualifies to be analyzed for
|
|
467 |
* inlining, this method prepares a new stack top in {@link InliningData} for such callsite,
|
|
468 |
* which comprises:
|
|
469 |
* <ul>
|
|
470 |
* <li>preparing a summary of feasible targets, ie preparing an {@link InlineInfo}</li>
|
|
471 |
* <li>based on it, preparing the stack top proper which consists of:</li>
|
|
472 |
* <ul>
|
|
473 |
* <li>one {@link MethodInvocation}</li>
|
|
474 |
* <li>a {@link CallsiteHolder} for each feasible target</li>
|
|
475 |
* </ul>
|
|
476 |
* </ul>
|
|
477 |
*
|
|
478 |
* <p>
|
|
479 |
* The thus prepared "stack top" is needed by {@link #moveForward()} to explore the space of
|
|
480 |
* inlining decisions (each decision one of: backtracking, delving, inlining).
|
|
481 |
* </p>
|
|
482 |
*
|
|
483 |
* <p>
|
|
484 |
* The {@link InlineInfo} used to get things rolling is kept around in the
|
|
485 |
* {@link MethodInvocation}, it will be needed in case of inlining, see
|
|
486 |
* {@link InlineInfo#inline(Providers)}
|
|
487 |
* </p>
|
|
488 |
*/
|
|
489 |
private void processNextInvoke() {
|
|
490 |
CallsiteHolderExplorable callsiteHolder = (CallsiteHolderExplorable) currentGraph();
|
|
491 |
Invoke invoke = callsiteHolder.popInvoke();
|
|
492 |
InlineInfo info = getInlineInfo(invoke);
|
|
493 |
|
|
494 |
if (info != null) {
|
46344
|
495 |
info.populateInlinableElements(context, currentGraph().graph(), canonicalizer, rootGraph.getOptions());
|
43972
|
496 |
double invokeProbability = callsiteHolder.invokeProbability(invoke);
|
|
497 |
double invokeRelevance = callsiteHolder.invokeRelevance(invoke);
|
|
498 |
MethodInvocation methodInvocation = new MethodInvocation(info, invokeProbability, invokeRelevance, freshlyInstantiatedArguments(invoke, callsiteHolder.getFixedParams()));
|
|
499 |
pushInvocationAndGraphs(methodInvocation);
|
|
500 |
}
|
|
501 |
}
|
|
502 |
|
|
503 |
/**
|
|
504 |
* Gets the freshly instantiated arguments.
|
|
505 |
* <p>
|
|
506 |
* A freshly instantiated argument is either:
|
|
507 |
* <uL>
|
|
508 |
* <li>an {@link InliningData#isFreshInstantiation(org.graalvm.compiler.nodes.ValueNode)}</li>
|
|
509 |
* <li>a fixed-param, ie a {@link ParameterNode} receiving a freshly instantiated argument</li>
|
|
510 |
* </uL>
|
|
511 |
* </p>
|
|
512 |
*
|
|
513 |
* @return the positions of freshly instantiated arguments in the argument list of the
|
|
514 |
* <code>invoke</code>, or null if no such positions exist.
|
|
515 |
*/
|
46344
|
516 |
public static BitSet freshlyInstantiatedArguments(Invoke invoke, EconomicSet<ParameterNode> fixedParams) {
|
43972
|
517 |
assert fixedParams != null;
|
|
518 |
assert paramsAndInvokeAreInSameGraph(invoke, fixedParams);
|
|
519 |
BitSet result = null;
|
|
520 |
int argIdx = 0;
|
|
521 |
for (ValueNode arg : invoke.callTarget().arguments()) {
|
|
522 |
assert arg != null;
|
46344
|
523 |
if (isFreshInstantiation(arg) || (arg instanceof ParameterNode && fixedParams.contains((ParameterNode) arg))) {
|
43972
|
524 |
if (result == null) {
|
|
525 |
result = new BitSet();
|
|
526 |
}
|
|
527 |
result.set(argIdx);
|
|
528 |
}
|
|
529 |
argIdx++;
|
|
530 |
}
|
|
531 |
return result;
|
|
532 |
}
|
|
533 |
|
46344
|
534 |
private static boolean paramsAndInvokeAreInSameGraph(Invoke invoke, EconomicSet<ParameterNode> fixedParams) {
|
43972
|
535 |
if (fixedParams.isEmpty()) {
|
|
536 |
return true;
|
|
537 |
}
|
|
538 |
for (ParameterNode p : fixedParams) {
|
|
539 |
if (p.graph() != invoke.asNode().graph()) {
|
|
540 |
return false;
|
|
541 |
}
|
|
542 |
}
|
|
543 |
return true;
|
|
544 |
}
|
|
545 |
|
|
546 |
public int graphCount() {
|
|
547 |
return graphQueue.size();
|
|
548 |
}
|
|
549 |
|
|
550 |
public boolean hasUnprocessedGraphs() {
|
|
551 |
return !graphQueue.isEmpty();
|
|
552 |
}
|
|
553 |
|
|
554 |
private CallsiteHolder currentGraph() {
|
|
555 |
return graphQueue.peek();
|
|
556 |
}
|
|
557 |
|
|
558 |
private void popGraph() {
|
|
559 |
graphQueue.pop();
|
|
560 |
assert graphQueue.size() <= maxGraphs;
|
|
561 |
}
|
|
562 |
|
|
563 |
private void popGraphs(int count) {
|
|
564 |
assert count >= 0;
|
|
565 |
for (int i = 0; i < count; i++) {
|
|
566 |
graphQueue.pop();
|
|
567 |
}
|
|
568 |
}
|
|
569 |
|
|
570 |
private static final Object[] NO_CONTEXT = {};
|
|
571 |
|
|
572 |
/**
|
|
573 |
* Gets the call hierarchy of this inlining from outer most call to inner most callee.
|
|
574 |
*/
|
|
575 |
private Object[] inliningContext() {
|
46640
|
576 |
if (!debug.isDumpEnabled(DebugContext.INFO_LEVEL)) {
|
43972
|
577 |
return NO_CONTEXT;
|
|
578 |
}
|
|
579 |
Object[] result = new Object[graphQueue.size()];
|
|
580 |
int i = 0;
|
|
581 |
for (CallsiteHolder g : graphQueue) {
|
|
582 |
result[i++] = g.method();
|
|
583 |
}
|
|
584 |
return result;
|
|
585 |
}
|
|
586 |
|
|
587 |
private MethodInvocation currentInvocation() {
|
|
588 |
return invocationQueue.peekFirst();
|
|
589 |
}
|
|
590 |
|
|
591 |
private void pushInvocationAndGraphs(MethodInvocation methodInvocation) {
|
|
592 |
invocationQueue.addFirst(methodInvocation);
|
|
593 |
InlineInfo info = methodInvocation.callee();
|
|
594 |
maxGraphs += info.numberOfMethods();
|
|
595 |
assert graphQueue.size() <= maxGraphs;
|
|
596 |
for (int i = 0; i < info.numberOfMethods(); i++) {
|
|
597 |
CallsiteHolder ch = methodInvocation.buildCallsiteHolderForElement(i);
|
|
598 |
assert !contains(ch.graph());
|
|
599 |
graphQueue.push(ch);
|
|
600 |
assert graphQueue.size() <= maxGraphs;
|
|
601 |
}
|
|
602 |
}
|
|
603 |
|
|
604 |
private void popInvocation() {
|
|
605 |
maxGraphs -= invocationQueue.peekFirst().callee().numberOfMethods();
|
|
606 |
assert graphQueue.size() <= maxGraphs;
|
|
607 |
invocationQueue.removeFirst();
|
|
608 |
}
|
|
609 |
|
|
610 |
public int countRecursiveInlining(ResolvedJavaMethod method) {
|
|
611 |
int count = 0;
|
|
612 |
for (CallsiteHolder callsiteHolder : graphQueue) {
|
|
613 |
if (method.equals(callsiteHolder.method())) {
|
|
614 |
count++;
|
|
615 |
}
|
|
616 |
}
|
|
617 |
return count;
|
|
618 |
}
|
|
619 |
|
|
620 |
public int inliningDepth() {
|
|
621 |
assert invocationQueue.size() > 0;
|
|
622 |
return invocationQueue.size() - 1;
|
|
623 |
}
|
|
624 |
|
|
625 |
@Override
|
|
626 |
public String toString() {
|
|
627 |
StringBuilder result = new StringBuilder("Invocations: ");
|
|
628 |
|
|
629 |
for (MethodInvocation invocation : invocationQueue) {
|
|
630 |
if (invocation.callee() != null) {
|
|
631 |
result.append(invocation.callee().numberOfMethods());
|
|
632 |
result.append("x ");
|
|
633 |
result.append(invocation.callee().invoke());
|
|
634 |
result.append("; ");
|
|
635 |
}
|
|
636 |
}
|
|
637 |
|
|
638 |
result.append("\nGraphs: ");
|
|
639 |
for (CallsiteHolder graph : graphQueue) {
|
|
640 |
result.append(graph.graph());
|
|
641 |
result.append("; ");
|
|
642 |
}
|
|
643 |
|
|
644 |
return result.toString();
|
|
645 |
}
|
|
646 |
|
|
647 |
/**
|
|
648 |
* Gets a stack trace representing the current inlining stack represented by this object.
|
|
649 |
*/
|
|
650 |
public Collection<StackTraceElement> getInvocationStackTrace() {
|
|
651 |
List<StackTraceElement> result = new ArrayList<>();
|
|
652 |
for (CallsiteHolder graph : graphQueue) {
|
|
653 |
result.add(graph.method().asStackTraceElement(0));
|
|
654 |
}
|
|
655 |
|
|
656 |
return result;
|
|
657 |
}
|
|
658 |
|
|
659 |
private boolean contains(StructuredGraph graph) {
|
|
660 |
assert graph != null;
|
|
661 |
for (CallsiteHolder info : graphQueue) {
|
|
662 |
if (info.graph() == graph) {
|
|
663 |
return true;
|
|
664 |
}
|
|
665 |
}
|
|
666 |
return false;
|
|
667 |
}
|
|
668 |
|
|
669 |
/**
|
|
670 |
* <p>
|
|
671 |
* The stack realized by {@link InliningData} grows and shrinks as choices are made among the
|
|
672 |
* alternatives below:
|
|
673 |
* <ol>
|
|
674 |
* <li>not worth inlining: pop stack top, which comprises:
|
|
675 |
* <ul>
|
|
676 |
* <li>pop any remaining graphs not yet delved into</li>
|
|
677 |
* <li>pop the current invocation</li>
|
|
678 |
* </ul>
|
|
679 |
* </li>
|
|
680 |
* <li>{@link #processNextInvoke() delve} into one of the callsites hosted in the current graph,
|
|
681 |
* such callsite is explored next by {@link #moveForward()}</li>
|
|
682 |
* <li>{@link #tryToInline(MethodInvocation, int) try to inline}: move past the current graph
|
|
683 |
* (remove it from the topmost element).
|
|
684 |
* <ul>
|
|
685 |
* <li>If that was the last one then {@link #tryToInline(MethodInvocation, int) try to inline}
|
|
686 |
* the callsite under consideration (ie, the "current invocation").</li>
|
|
687 |
* <li>Whether inlining occurs or not, that callsite is removed from the top of
|
|
688 |
* {@link InliningData} .</li>
|
|
689 |
* </ul>
|
|
690 |
* </li>
|
|
691 |
* </ol>
|
|
692 |
* </p>
|
|
693 |
*
|
|
694 |
* <p>
|
|
695 |
* Some facts about the alternatives above:
|
|
696 |
* <ul>
|
|
697 |
* <li>the first step amounts to backtracking, the 2nd one to depth-search, and the 3rd one also
|
|
698 |
* involves backtracking (however possibly after inlining).</li>
|
|
699 |
* <li>the choice of abandon-and-backtrack or delve-into depends on
|
|
700 |
* {@link InliningPolicy#isWorthInlining} and {@link InliningPolicy#continueInlining}.</li>
|
|
701 |
* <li>the 3rd choice is picked whenever none of the previous choices are made</li>
|
|
702 |
* </ul>
|
|
703 |
* </p>
|
|
704 |
*
|
|
705 |
* @return true iff inlining was actually performed
|
|
706 |
*/
|
|
707 |
@SuppressWarnings("try")
|
|
708 |
public boolean moveForward() {
|
|
709 |
|
|
710 |
final MethodInvocation currentInvocation = currentInvocation();
|
|
711 |
|
|
712 |
final boolean backtrack = (!currentInvocation.isRoot() && !inliningPolicy.isWorthInlining(context.getReplacements(), currentInvocation, inliningDepth(), false));
|
|
713 |
if (backtrack) {
|
|
714 |
int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs();
|
|
715 |
assert remainingGraphs > 0;
|
|
716 |
popGraphs(remainingGraphs);
|
|
717 |
popInvocation();
|
|
718 |
return false;
|
|
719 |
}
|
|
720 |
|
|
721 |
final boolean delve = currentGraph().hasRemainingInvokes() && inliningPolicy.continueInlining(currentGraph().graph());
|
|
722 |
if (delve) {
|
|
723 |
processNextInvoke();
|
|
724 |
return false;
|
|
725 |
}
|
|
726 |
|
|
727 |
popGraph();
|
|
728 |
if (currentInvocation.isRoot()) {
|
|
729 |
return false;
|
|
730 |
}
|
|
731 |
|
|
732 |
// try to inline
|
|
733 |
assert currentInvocation.callee().invoke().asNode().isAlive();
|
|
734 |
currentInvocation.incrementProcessedGraphs();
|
|
735 |
if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) {
|
|
736 |
/*
|
|
737 |
* "all of currentInvocation's graphs processed" amounts to
|
|
738 |
* "all concrete methods that come into question already had the callees they contain analyzed for inlining"
|
|
739 |
*/
|
|
740 |
popInvocation();
|
46640
|
741 |
try (DebugContext.Scope s = debug.scope("Inlining", inliningContext())) {
|
43972
|
742 |
if (tryToInline(currentInvocation, inliningDepth() + 1)) {
|
|
743 |
// Report real progress only if we inline into the root graph
|
|
744 |
return currentGraph().graph() == rootGraph;
|
|
745 |
}
|
|
746 |
return false;
|
|
747 |
} catch (Throwable e) {
|
46640
|
748 |
throw debug.handle(e);
|
43972
|
749 |
}
|
|
750 |
}
|
|
751 |
|
|
752 |
return false;
|
|
753 |
}
|
|
754 |
|
|
755 |
/**
|
|
756 |
* Checks an invariant that {@link #moveForward()} must maintain: "the top invocation records
|
|
757 |
* how many concrete target methods (for it) remain on the {@link #graphQueue}; those targets
|
|
758 |
* 'belong' to the current invocation in question.
|
|
759 |
*/
|
|
760 |
private boolean topGraphsForTopInvocation() {
|
|
761 |
if (invocationQueue.isEmpty()) {
|
|
762 |
assert graphQueue.isEmpty();
|
|
763 |
return true;
|
|
764 |
}
|
|
765 |
if (currentInvocation().isRoot()) {
|
|
766 |
if (!graphQueue.isEmpty()) {
|
|
767 |
assert graphQueue.size() == 1;
|
|
768 |
}
|
|
769 |
return true;
|
|
770 |
}
|
|
771 |
final int remainingGraphs = currentInvocation().totalGraphs() - currentInvocation().processedGraphs();
|
|
772 |
final Iterator<CallsiteHolder> iter = graphQueue.iterator();
|
|
773 |
for (int i = (remainingGraphs - 1); i >= 0; i--) {
|
|
774 |
if (!iter.hasNext()) {
|
|
775 |
assert false;
|
|
776 |
return false;
|
|
777 |
}
|
|
778 |
CallsiteHolder queuedTargetCH = iter.next();
|
|
779 |
Inlineable targetIE = currentInvocation().callee().inlineableElementAt(i);
|
|
780 |
InlineableGraph targetIG = (InlineableGraph) targetIE;
|
|
781 |
assert queuedTargetCH.method().equals(targetIG.getGraph().method());
|
|
782 |
}
|
|
783 |
return true;
|
|
784 |
}
|
|
785 |
|
|
786 |
/**
|
|
787 |
* This method checks invariants for this class. Named after shorthand for "internal
|
|
788 |
* representation is ok".
|
|
789 |
*/
|
|
790 |
public boolean repOK() {
|
|
791 |
assert topGraphsForTopInvocation();
|
|
792 |
return true;
|
|
793 |
}
|
|
794 |
}
|