OSDN Git Service

am be3942f2: (-s ours) am 3245c2e4: am 5017f3f2: Merge "Support wrapping app processe...
[android-x86/dalvik.git] / vm / compiler / InlineTransformation.cpp
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "Dalvik.h"
18 #include "Dataflow.h"
19 #include "libdex/DexOpcodes.h"
20
21 /* Convert the reg id from the callee to the original id passed by the caller */
22 static inline u4 convertRegId(const DecodedInstruction *invoke,
23                               const Method *calleeMethod,
24                               int calleeRegId, bool isRange)
25 {
26     /* The order in the original arg passing list */
27     int rank = calleeRegId -
28                (calleeMethod->registersSize - calleeMethod->insSize);
29     assert(rank >= 0);
30     if (!isRange) {
31         return invoke->arg[rank];
32     } else {
33         return invoke->vC + rank;
34     }
35 }
36
37 static bool inlineGetter(CompilationUnit *cUnit,
38                          const Method *calleeMethod,
39                          MIR *invokeMIR,
40                          BasicBlock *invokeBB,
41                          bool isPredicted,
42                          bool isRange)
43 {
44     BasicBlock *moveResultBB = invokeBB->fallThrough;
45     MIR *moveResultMIR = moveResultBB->firstMIRInsn;
46     MIR *newGetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
47     DecodedInstruction getterInsn;
48
49     dexDecodeInstruction(calleeMethod->insns, &getterInsn);
50
51     if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn))
52         return false;
53
54     /*
55      * Some getters (especially invoked through interface) are not followed
56      * by a move result.
57      */
58     if ((moveResultMIR == NULL) ||
59         (moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT &&
60          moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_OBJECT &&
61          moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_WIDE)) {
62         return false;
63     }
64
65     int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opcode];
66
67     /* Expecting vA to be the destination register */
68     if (dfFlags & (DF_UA | DF_UA_WIDE)) {
69         LOGE("opcode %d has DF_UA set (not expected)", getterInsn.opcode);
70         dvmAbort();
71     }
72
73     if (dfFlags & DF_UB) {
74         getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
75                                      getterInsn.vB, isRange);
76     }
77
78     if (dfFlags & DF_UC) {
79         getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
80                                      getterInsn.vC, isRange);
81     }
82
83     getterInsn.vA = moveResultMIR->dalvikInsn.vA;
84
85     /* Now setup the Dalvik instruction with converted src/dst registers */
86     newGetterMIR->dalvikInsn = getterInsn;
87
88     newGetterMIR->width = dexGetWidthFromOpcode(getterInsn.opcode);
89
90     newGetterMIR->OptimizationFlags |= MIR_CALLEE;
91
92     /*
93      * If the getter instruction is about to raise any exception, punt to the
94      * interpreter and re-execute the invoke.
95      */
96     newGetterMIR->offset = invokeMIR->offset;
97
98     newGetterMIR->meta.calleeMethod = calleeMethod;
99
100     dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR);
101
102     if (isPredicted) {
103         MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
104         *invokeMIRSlow = *invokeMIR;
105         invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
106
107         /* Use vC to denote the first argument (ie this) */
108         if (!isRange) {
109             invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
110         }
111
112         moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED;
113
114         dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow);
115         invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
116 #if defined(WITH_JIT_TUNING)
117         gDvmJit.invokePolyGetterInlined++;
118 #endif
119     } else {
120         invokeMIR->OptimizationFlags |= MIR_INLINED;
121         moveResultMIR->OptimizationFlags |= MIR_INLINED;
122 #if defined(WITH_JIT_TUNING)
123         gDvmJit.invokeMonoGetterInlined++;
124 #endif
125     }
126
127     return true;
128 }
129
130 static bool inlineSetter(CompilationUnit *cUnit,
131                          const Method *calleeMethod,
132                          MIR *invokeMIR,
133                          BasicBlock *invokeBB,
134                          bool isPredicted,
135                          bool isRange)
136 {
137     MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
138     DecodedInstruction setterInsn;
139
140     dexDecodeInstruction(calleeMethod->insns, &setterInsn);
141
142     if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn))
143         return false;
144
145     int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opcode];
146
147     if (dfFlags & (DF_UA | DF_UA_WIDE)) {
148         setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
149                                      setterInsn.vA, isRange);
150
151     }
152
153     if (dfFlags & DF_UB) {
154         setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
155                                      setterInsn.vB, isRange);
156
157     }
158
159     if (dfFlags & DF_UC) {
160         setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
161                                      setterInsn.vC, isRange);
162     }
163
164     /* Now setup the Dalvik instruction with converted src/dst registers */
165     newSetterMIR->dalvikInsn = setterInsn;
166
167     newSetterMIR->width = dexGetWidthFromOpcode(setterInsn.opcode);
168
169     newSetterMIR->OptimizationFlags |= MIR_CALLEE;
170
171     /*
172      * If the setter instruction is about to raise any exception, punt to the
173      * interpreter and re-execute the invoke.
174      */
175     newSetterMIR->offset = invokeMIR->offset;
176
177     newSetterMIR->meta.calleeMethod = calleeMethod;
178
179     dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR);
180
181     if (isPredicted) {
182         MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
183         *invokeMIRSlow = *invokeMIR;
184         invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
185
186         /* Use vC to denote the first argument (ie this) */
187         if (!isRange) {
188             invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
189         }
190
191         dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow);
192         invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
193 #if defined(WITH_JIT_TUNING)
194         gDvmJit.invokePolySetterInlined++;
195 #endif
196     } else {
197         /*
198          * The invoke becomes no-op so it needs an explicit branch to jump to
199          * the chaining cell.
200          */
201         invokeBB->needFallThroughBranch = true;
202         invokeMIR->OptimizationFlags |= MIR_INLINED;
203 #if defined(WITH_JIT_TUNING)
204         gDvmJit.invokeMonoSetterInlined++;
205 #endif
206     }
207
208     return true;
209 }
210
211 static bool tryInlineSingletonCallsite(CompilationUnit *cUnit,
212                                        const Method *calleeMethod,
213                                        MIR *invokeMIR,
214                                        BasicBlock *invokeBB,
215                                        bool isRange)
216 {
217     /* Not a Java method */
218     if (dvmIsNativeMethod(calleeMethod)) return false;
219
220     CompilerMethodStats *methodStats =
221         dvmCompilerAnalyzeMethodBody(calleeMethod, true);
222
223     /* Empty callee - do nothing */
224     if (methodStats->attributes & METHOD_IS_EMPTY) {
225         /* The original invoke instruction is effectively turned into NOP */
226         invokeMIR->OptimizationFlags |= MIR_INLINED;
227         /*
228          * Need to insert an explicit branch to catch the falling knife (into
229          * the PC reconstruction or chaining cell).
230          */
231         invokeBB->needFallThroughBranch = true;
232         return true;
233     }
234
235     if (methodStats->attributes & METHOD_IS_GETTER) {
236         return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
237                             isRange);
238     } else if (methodStats->attributes & METHOD_IS_SETTER) {
239         return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
240                             isRange);
241     }
242     return false;
243 }
244
245 static bool inlineEmptyVirtualCallee(CompilationUnit *cUnit,
246                                      const Method *calleeMethod,
247                                      MIR *invokeMIR,
248                                      BasicBlock *invokeBB)
249 {
250     MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
251     *invokeMIRSlow = *invokeMIR;
252     invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
253
254     dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow);
255     invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
256     return true;
257 }
258
259 static bool tryInlineVirtualCallsite(CompilationUnit *cUnit,
260                                      const Method *calleeMethod,
261                                      MIR *invokeMIR,
262                                      BasicBlock *invokeBB,
263                                      bool isRange)
264 {
265     /* Not a Java method */
266     if (dvmIsNativeMethod(calleeMethod)) return false;
267
268     CompilerMethodStats *methodStats =
269         dvmCompilerAnalyzeMethodBody(calleeMethod, true);
270
271     /* Empty callee - do nothing by checking the clazz pointer */
272     if (methodStats->attributes & METHOD_IS_EMPTY) {
273         return inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR,
274                                         invokeBB);
275     }
276
277     if (methodStats->attributes & METHOD_IS_GETTER) {
278         return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
279                             isRange);
280     } else if (methodStats->attributes & METHOD_IS_SETTER) {
281         return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
282                             isRange);
283     }
284     return false;
285 }
286
287
288 void dvmCompilerInlineMIR(CompilationUnit *cUnit, JitTranslationInfo *info)
289 {
290     bool isRange = false;
291     GrowableListIterator iterator;
292
293     dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
294     /*
295      * Analyze the basic block containing an invoke to see if it can be inlined
296      */
297     while (true) {
298         BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
299         if (bb == NULL) break;
300         if (bb->blockType != kDalvikByteCode)
301             continue;
302         MIR *lastMIRInsn = bb->lastMIRInsn;
303         Opcode opcode = lastMIRInsn->dalvikInsn.opcode;
304         int flags = (int)dexGetFlagsFromOpcode(opcode);
305
306         /* No invoke - continue */
307         if ((flags & kInstrInvoke) == 0)
308             continue;
309
310         /* Disable inlining when doing method tracing */
311         if (gDvmJit.methodTraceSupport)
312             continue;
313
314         /*
315          * If the invoke itself is selected for single stepping, don't bother
316          * to inline it.
317          */
318         if (SINGLE_STEP_OP(opcode))
319             continue;
320
321         const Method *calleeMethod;
322
323         switch (opcode) {
324             case OP_INVOKE_SUPER:
325             case OP_INVOKE_DIRECT:
326             case OP_INVOKE_STATIC:
327             case OP_INVOKE_SUPER_QUICK:
328                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
329                 break;
330             case OP_INVOKE_SUPER_RANGE:
331             case OP_INVOKE_DIRECT_RANGE:
332             case OP_INVOKE_STATIC_RANGE:
333             case OP_INVOKE_SUPER_QUICK_RANGE:
334                 isRange = true;
335                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
336                 break;
337             default:
338                 calleeMethod = NULL;
339                 break;
340         }
341
342         if (calleeMethod) {
343             bool inlined = tryInlineSingletonCallsite(cUnit, calleeMethod,
344                                                       lastMIRInsn, bb, isRange);
345             if (!inlined &&
346                 !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
347                 !dvmIsNativeMethod(calleeMethod)) {
348                 CompilerMethodStats *methodStats =
349                     dvmCompilerAnalyzeMethodBody(calleeMethod, true);
350                 if ((methodStats->attributes & METHOD_IS_LEAF) &&
351                     !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
352                     /* Callee has been previously compiled */
353                     if (dvmJitGetMethodAddr(calleeMethod->insns)) {
354                         lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
355                     } else {
356                         /* Compile the callee first */
357                         dvmCompileMethod(calleeMethod, info);
358                         if (dvmJitGetMethodAddr(calleeMethod->insns)) {
359                             lastMIRInsn->OptimizationFlags |=
360                                 MIR_INVOKE_METHOD_JIT;
361                         } else {
362                             methodStats->attributes |= METHOD_CANNOT_COMPILE;
363                         }
364                     }
365                 }
366             }
367             return;
368         }
369
370         switch (opcode) {
371             case OP_INVOKE_VIRTUAL:
372             case OP_INVOKE_VIRTUAL_QUICK:
373             case OP_INVOKE_INTERFACE:
374                 isRange = false;
375                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
376                 break;
377             case OP_INVOKE_VIRTUAL_RANGE:
378             case OP_INVOKE_VIRTUAL_QUICK_RANGE:
379             case OP_INVOKE_INTERFACE_RANGE:
380                 isRange = true;
381                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
382                 break;
383             default:
384                 break;
385         }
386
387         if (calleeMethod) {
388             bool inlined = tryInlineVirtualCallsite(cUnit, calleeMethod,
389                                                     lastMIRInsn, bb, isRange);
390             if (!inlined &&
391                 !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
392                 !dvmIsNativeMethod(calleeMethod)) {
393                 CompilerMethodStats *methodStats =
394                     dvmCompilerAnalyzeMethodBody(calleeMethod, true);
395                 if ((methodStats->attributes & METHOD_IS_LEAF) &&
396                     !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
397                     /* Callee has been previously compiled */
398                     if (dvmJitGetMethodAddr(calleeMethod->insns)) {
399                         lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
400                     } else {
401                         /* Compile the callee first */
402                         dvmCompileMethod(calleeMethod, info);
403                         if (dvmJitGetMethodAddr(calleeMethod->insns)) {
404                             lastMIRInsn->OptimizationFlags |=
405                                 MIR_INVOKE_METHOD_JIT;
406                         } else {
407                             methodStats->attributes |= METHOD_CANNOT_COMPILE;
408                         }
409                     }
410                 }
411             }
412             return;
413         }
414     }
415 }