• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 package com.android.tools.r8.ir.optimize;
5 
6 import com.android.tools.r8.graph.DexClass;
7 import com.android.tools.r8.graph.DexEncodedMethod;
8 import com.android.tools.r8.graph.DexType;
9 import com.android.tools.r8.ir.code.InvokeDirect;
10 import com.android.tools.r8.ir.code.InvokeInterface;
11 import com.android.tools.r8.ir.code.InvokeMethod;
12 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
13 import com.android.tools.r8.ir.code.InvokePolymorphic;
14 import com.android.tools.r8.ir.code.InvokeStatic;
15 import com.android.tools.r8.ir.code.InvokeSuper;
16 import com.android.tools.r8.ir.code.InvokeVirtual;
17 import com.android.tools.r8.ir.code.Value;
18 import com.android.tools.r8.ir.conversion.CallGraph;
19 import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
20 import com.android.tools.r8.ir.optimize.Inliner.Reason;
21 import com.android.tools.r8.logging.Log;
22 
23 /**
24  * The InliningOracle contains information needed for when inlining
25  * other methods into @method.
26  */
27 public class InliningOracle {
28 
29   final Inliner inliner;
30   final DexEncodedMethod method;
31   final Value receiver;
32   final CallGraph callGraph;
33   final private InliningInfo info;
34 
InliningOracle( Inliner inliner, DexEncodedMethod method, Value receiver, CallGraph callGraph)35   public InliningOracle(
36       Inliner inliner,
37       DexEncodedMethod method,
38       Value receiver,
39       CallGraph callGraph) {
40     this.inliner = inliner;
41     this.method = method;
42     this.receiver = receiver;
43     this.callGraph = callGraph;
44     info = Log.ENABLED ? new InliningInfo(method) : null;
45   }
46 
finish()47   public void finish() {
48     if (Log.ENABLED) {
49       System.out.println(info.toString());
50     }
51   }
52 
validateCandidate(InvokeMethod invoke)53   DexEncodedMethod validateCandidate(InvokeMethod invoke) {
54     DexEncodedMethod candidate = invoke.computeSingleTarget(inliner.appInfo);
55     if ((candidate == null)
56         || (candidate.getCode() == null)
57         || inliner.appInfo.definitionFor(candidate.method.getHolder()).isLibraryClass()) {
58       if (info != null) {
59         info.exclude(invoke, "No inlinee");
60       }
61       return null;
62     }
63     if (method == candidate) {
64       // Cannot handle recursive inlining at this point.
65       // Bridge methods should never have recursive calls.
66       assert !candidate.getOptimizationInfo().forceInline();
67       return null;
68     }
69 
70     if (candidate.accessFlags.isSynchronized()) {
71       // Don't inline if target is synchronized.
72       if (info != null) {
73         info.exclude(invoke, "Inlinee candidate is synchronized");
74       }
75       return null;
76     }
77 
78     if (callGraph.isBreaker(method, candidate)) {
79       // Cycle breaker so abort to preserve compilation order.
80       return null;
81     }
82 
83     if (!inliner.hasInliningAccess(method, candidate)) {
84       if (info != null) {
85         info.exclude(invoke, "Inlinee candidate does not have right access flags");
86       }
87       return null;
88     }
89     return candidate;
90   }
91 
computeInliningReason(DexEncodedMethod target)92   private Reason computeInliningReason(DexEncodedMethod target) {
93     if (target.getOptimizationInfo().forceInline()) {
94       return Reason.FORCE;
95     }
96     if (callGraph.hasSingleCallSite(target)) {
97       return Reason.SINGLE_CALLER;
98     }
99     if (isDoubleInliningTarget(target)) {
100       return Reason.DUAL_CALLER;
101     }
102     return Reason.SIMPLE;
103   }
104 
computeForInvokeWithReceiver(InvokeMethodWithReceiver invoke)105   public InlineAction computeForInvokeWithReceiver(InvokeMethodWithReceiver invoke) {
106     boolean receiverIsNeverNull = invoke.receiverIsNeverNull();
107     if (!receiverIsNeverNull) {
108       if (info != null) {
109         info.exclude(invoke, "receiver for candidate can be null");
110       }
111       return null;
112     }
113     DexEncodedMethod target = invoke.computeSingleTarget(inliner.appInfo);
114     if (target == null) {
115       // Abort inlining attempt if we cannot find single target.
116       if (info != null) {
117         info.exclude(invoke, "could not find single target");
118       }
119       return null;
120     }
121 
122     if (target == method) {
123       // Bridge methods should never have recursive calls.
124       assert !target.getOptimizationInfo().forceInline();
125       return null;
126     }
127 
128     if (target.getCode() == null) {
129       return null;
130     }
131 
132     DexClass holder = inliner.appInfo.definitionFor(target.method.getHolder());
133     if (holder.isInterface()) {
134       // Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception at runtime.
135       if (info != null) {
136         info.exclude(invoke, "Do not inline target if method holder is an interface class");
137       }
138       return null;
139     }
140 
141     if (holder.isLibraryClass()) {
142       // Library functions should not be inlined.
143       return null;
144     }
145 
146     // Don't inline if target is synchronized.
147     if (target.accessFlags.isSynchronized()) {
148       if (info != null) {
149         info.exclude(invoke, "target is synchronized");
150       }
151       return null;
152     }
153 
154     Reason reason = computeInliningReason(target);
155     // Determine if this should be inlined no matter how big it is.
156     if (!target.isInliningCandidate(method, reason != Reason.SIMPLE)) {
157       // Abort inlining attempt if the single target is not an inlining candidate.
158       if (info != null) {
159         info.exclude(invoke, "target is not identified for inlining");
160       }
161       return null;
162     }
163 
164     if (callGraph.isBreaker(method, target)) {
165       // Cycle breaker so abort to preserve compilation order.
166       return null;
167     }
168 
169     // Abort inlining attempt if method -> target access is not right.
170     if (!inliner.hasInliningAccess(method, target)) {
171       if (info != null) {
172         info.exclude(invoke, "target does not have right access");
173       }
174       return null;
175     }
176 
177     // Attempt to inline a candidate that is only called twice.
178     if ((reason == Reason.DUAL_CALLER) && (inliner.doubleInlining(method, target) == null)) {
179       if (info != null) {
180         info.exclude(invoke, "target is not ready for double inlining");
181       }
182       return null;
183     }
184 
185     if (info != null) {
186       info.include(invoke.getType(), target);
187     }
188     return new InlineAction(target, invoke, reason);
189   }
190 
computeForInvokeVirtual(InvokeVirtual invoke)191   public InlineAction computeForInvokeVirtual(InvokeVirtual invoke) {
192     return computeForInvokeWithReceiver(invoke);
193   }
194 
computeForInvokeInterface(InvokeInterface invoke)195   public InlineAction computeForInvokeInterface(InvokeInterface invoke) {
196     return computeForInvokeWithReceiver(invoke);
197   }
198 
computeForInvokeDirect(InvokeDirect invoke)199   public InlineAction computeForInvokeDirect(InvokeDirect invoke) {
200     return computeForInvokeWithReceiver(invoke);
201   }
202 
canInlineStaticInvoke(DexEncodedMethod method, DexEncodedMethod target)203   private boolean canInlineStaticInvoke(DexEncodedMethod method, DexEncodedMethod target) {
204     // Only proceed with inlining a static invoke if:
205     // - the holder for the target equals the holder for the method, or
206     // - there is no class initializer.
207     DexType targetHolder = target.method.getHolder();
208     if (method.method.getHolder() == targetHolder) {
209       return true;
210     }
211     DexClass clazz = inliner.appInfo.definitionFor(targetHolder);
212     return (clazz != null) && (!clazz.hasNonTrivialClassInitializer());
213   }
214 
isDoubleInliningTarget(DexEncodedMethod candidate)215   private synchronized boolean isDoubleInliningTarget(DexEncodedMethod candidate) {
216     // 10 is found from measuring.
217     return callGraph.hasDoubleCallSite(candidate)
218         && candidate.getCode().isDexCode()
219         && (candidate.getCode().asDexCode().instructions.length <= 10);
220   }
221 
computeForInvokeStatic(InvokeStatic invoke)222   public InlineAction computeForInvokeStatic(InvokeStatic invoke) {
223     DexEncodedMethod candidate = validateCandidate(invoke);
224     if (candidate == null) {
225       return null;
226     }
227     Reason reason = computeInliningReason(candidate);
228     // Determine if this should be inlined no matter how big it is.
229     if (!candidate.isInliningCandidate(method, reason != Reason.SIMPLE)) {
230       // Abort inlining attempt if the single target is not an inlining candidate.
231       if (info != null) {
232         info.exclude(invoke, "target is not identified for inlining");
233       }
234       return null;
235     }
236 
237     // Abort inlining attempt if we can not guarantee class for static target has been initialized.
238     if (!canInlineStaticInvoke(method, candidate)) {
239       if (info != null) {
240         info.exclude(invoke, "target is static but we cannot guarantee class has been initialized");
241       }
242       return null;
243     }
244 
245     // Attempt to inline a candidate that is only called twice.
246     if ((reason == Reason.DUAL_CALLER) && (inliner.doubleInlining(method, candidate) == null)) {
247       if (info != null) {
248         info.exclude(invoke, "target is not ready for double inlining");
249       }
250       return null;
251     }
252 
253     if (info != null) {
254       info.include(invoke.getType(), candidate);
255     }
256     return new InlineAction(candidate, invoke, reason);
257   }
258 
computeForInvokeSuper(InvokeSuper invoke)259   public InlineAction computeForInvokeSuper(InvokeSuper invoke) {
260     DexEncodedMethod candidate = validateCandidate(invoke);
261     if (candidate == null) {
262       if (info != null) {
263         info.exclude(invoke, "not a valid inlining target");
264       }
265       return null;
266     }
267     if (info != null) {
268       info.include(invoke.getType(), candidate);
269     }
270     return new InlineAction(candidate, invoke, Reason.SIMPLE);
271   }
272 
computeForInvokePolymorpic(InvokePolymorphic invoke)273   public InlineAction computeForInvokePolymorpic(InvokePolymorphic invoke) {
274     // TODO: No inlining of invoke polymorphic for now.
275     if (info != null) {
276       info.exclude(invoke, "inlining through invoke signature polymorpic is not supported");
277     }
278     return null;
279   }
280 }
281