OSDN Git Service

am e421d118: am 007c01f8: Merge "Fix a minor leak in dvmCreateInlineSubsTable"
[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     /*
50      * Not all getter instructions have vC but vC will be read by
51      * dvmCompilerGetDalvikDisassembly unconditionally.
52      * Initialize it here to get Valgrind happy.
53      */
54     getterInsn.vC = 0;
55
56     dexDecodeInstruction(calleeMethod->insns, &getterInsn);
57
58     if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn))
59         return false;
60
61     /*
62      * Some getters (especially invoked through interface) are not followed
63      * by a move result.
64      */
65     if ((moveResultMIR == NULL) ||
66         (moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT &&
67          moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_OBJECT &&
68          moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_WIDE)) {
69         return false;
70     }
71
72     int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opcode];
73
74     /* Expecting vA to be the destination register */
75     if (dfFlags & (DF_UA | DF_UA_WIDE)) {
76         ALOGE("opcode %d has DF_UA set (not expected)", getterInsn.opcode);
77         dvmAbort();
78     }
79
80     if (dfFlags & DF_UB) {
81         getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
82                                      getterInsn.vB, isRange);
83     }
84
85     if (dfFlags & DF_UC) {
86         getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
87                                      getterInsn.vC, isRange);
88     }
89
90     getterInsn.vA = moveResultMIR->dalvikInsn.vA;
91
92     /* Now setup the Dalvik instruction with converted src/dst registers */
93     newGetterMIR->dalvikInsn = getterInsn;
94
95     newGetterMIR->width = dexGetWidthFromOpcode(getterInsn.opcode);
96
97     newGetterMIR->OptimizationFlags |= MIR_CALLEE;
98
99     /*
100      * If the getter instruction is about to raise any exception, punt to the
101      * interpreter and re-execute the invoke.
102      */
103     newGetterMIR->offset = invokeMIR->offset;
104
105     newGetterMIR->meta.calleeMethod = calleeMethod;
106
107     dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR);
108
109     if (isPredicted) {
110         MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
111         *invokeMIRSlow = *invokeMIR;
112         invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
113
114         /* Use vC to denote the first argument (ie this) */
115         if (!isRange) {
116             invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
117         }
118
119         moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED;
120
121         dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow);
122         invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
123 #if defined(WITH_JIT_TUNING)
124         gDvmJit.invokePolyGetterInlined++;
125 #endif
126     } else {
127         invokeMIR->OptimizationFlags |= MIR_INLINED;
128         moveResultMIR->OptimizationFlags |= MIR_INLINED;
129 #if defined(WITH_JIT_TUNING)
130         gDvmJit.invokeMonoGetterInlined++;
131 #endif
132     }
133
134     return true;
135 }
136
137 static bool inlineSetter(CompilationUnit *cUnit,
138                          const Method *calleeMethod,
139                          MIR *invokeMIR,
140                          BasicBlock *invokeBB,
141                          bool isPredicted,
142                          bool isRange)
143 {
144     MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
145     DecodedInstruction setterInsn;
146
147     /*
148      * Not all setter instructions have vC but vC will be read by
149      * dvmCompilerGetDalvikDisassembly unconditionally.
150      * Initialize it here to get Valgrind happy.
151      */
152     setterInsn.vC = 0;
153
154     dexDecodeInstruction(calleeMethod->insns, &setterInsn);
155
156     if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn))
157         return false;
158
159     int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opcode];
160
161     if (dfFlags & (DF_UA | DF_UA_WIDE)) {
162         setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
163                                      setterInsn.vA, isRange);
164
165     }
166
167     if (dfFlags & DF_UB) {
168         setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
169                                      setterInsn.vB, isRange);
170
171     }
172
173     if (dfFlags & DF_UC) {
174         setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
175                                      setterInsn.vC, isRange);
176     }
177
178     /* Now setup the Dalvik instruction with converted src/dst registers */
179     newSetterMIR->dalvikInsn = setterInsn;
180
181     newSetterMIR->width = dexGetWidthFromOpcode(setterInsn.opcode);
182
183     newSetterMIR->OptimizationFlags |= MIR_CALLEE;
184
185     /*
186      * If the setter instruction is about to raise any exception, punt to the
187      * interpreter and re-execute the invoke.
188      */
189     newSetterMIR->offset = invokeMIR->offset;
190
191     newSetterMIR->meta.calleeMethod = calleeMethod;
192
193     dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR);
194
195     if (isPredicted) {
196         MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
197         *invokeMIRSlow = *invokeMIR;
198         invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
199
200         /* Use vC to denote the first argument (ie this) */
201         if (!isRange) {
202             invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
203         }
204
205         dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow);
206         invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
207 #if defined(WITH_JIT_TUNING)
208         gDvmJit.invokePolySetterInlined++;
209 #endif
210     } else {
211         /*
212          * The invoke becomes no-op so it needs an explicit branch to jump to
213          * the chaining cell.
214          */
215         invokeBB->needFallThroughBranch = true;
216         invokeMIR->OptimizationFlags |= MIR_INLINED;
217 #if defined(WITH_JIT_TUNING)
218         gDvmJit.invokeMonoSetterInlined++;
219 #endif
220     }
221
222     return true;
223 }
224
225 static bool tryInlineSingletonCallsite(CompilationUnit *cUnit,
226                                        const Method *calleeMethod,
227                                        MIR *invokeMIR,
228                                        BasicBlock *invokeBB,
229                                        bool isRange)
230 {
231     /* Not a Java method */
232     if (dvmIsNativeMethod(calleeMethod)) return false;
233
234     CompilerMethodStats *methodStats =
235         dvmCompilerAnalyzeMethodBody(calleeMethod, true);
236
237     /* Empty callee - do nothing */
238     if (methodStats->attributes & METHOD_IS_EMPTY) {
239         /* The original invoke instruction is effectively turned into NOP */
240         invokeMIR->OptimizationFlags |= MIR_INLINED;
241         /*
242          * Need to insert an explicit branch to catch the falling knife (into
243          * the PC reconstruction or chaining cell).
244          */
245         invokeBB->needFallThroughBranch = true;
246         return true;
247     }
248
249     if (methodStats->attributes & METHOD_IS_GETTER) {
250         return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
251                             isRange);
252     } else if (methodStats->attributes & METHOD_IS_SETTER) {
253         return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false,
254                             isRange);
255     }
256     return false;
257 }
258
259 static bool inlineEmptyVirtualCallee(CompilationUnit *cUnit,
260                                      const Method *calleeMethod,
261                                      MIR *invokeMIR,
262                                      BasicBlock *invokeBB)
263 {
264     MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
265     *invokeMIRSlow = *invokeMIR;
266     invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction;
267
268     dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow);
269     invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
270     return true;
271 }
272
273 static bool tryInlineVirtualCallsite(CompilationUnit *cUnit,
274                                      const Method *calleeMethod,
275                                      MIR *invokeMIR,
276                                      BasicBlock *invokeBB,
277                                      bool isRange)
278 {
279     /* Not a Java method */
280     if (dvmIsNativeMethod(calleeMethod)) return false;
281
282     CompilerMethodStats *methodStats =
283         dvmCompilerAnalyzeMethodBody(calleeMethod, true);
284
285     /* Empty callee - do nothing by checking the clazz pointer */
286     if (methodStats->attributes & METHOD_IS_EMPTY) {
287         return inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR,
288                                         invokeBB);
289     }
290
291     if (methodStats->attributes & METHOD_IS_GETTER) {
292         return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
293                             isRange);
294     } else if (methodStats->attributes & METHOD_IS_SETTER) {
295         return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true,
296                             isRange);
297     }
298     return false;
299 }
300
301
302 void dvmCompilerInlineMIR(CompilationUnit *cUnit, JitTranslationInfo *info)
303 {
304     bool isRange = false;
305     GrowableListIterator iterator;
306
307     dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
308     /*
309      * Analyze the basic block containing an invoke to see if it can be inlined
310      */
311     while (true) {
312         BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
313         if (bb == NULL) break;
314         if (bb->blockType != kDalvikByteCode)
315             continue;
316         MIR *lastMIRInsn = bb->lastMIRInsn;
317         Opcode opcode = lastMIRInsn->dalvikInsn.opcode;
318         int flags = (int)dexGetFlagsFromOpcode(opcode);
319
320         /* No invoke - continue */
321         if ((flags & kInstrInvoke) == 0)
322             continue;
323
324         /* Disable inlining when doing method tracing */
325         if (gDvmJit.methodTraceSupport)
326             continue;
327
328         /*
329          * If the invoke itself is selected for single stepping, don't bother
330          * to inline it.
331          */
332         if (SINGLE_STEP_OP(opcode))
333             continue;
334
335         const Method *calleeMethod;
336
337         switch (opcode) {
338             case OP_INVOKE_SUPER:
339             case OP_INVOKE_DIRECT:
340             case OP_INVOKE_STATIC:
341             case OP_INVOKE_SUPER_QUICK:
342                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
343                 break;
344             case OP_INVOKE_SUPER_RANGE:
345             case OP_INVOKE_DIRECT_RANGE:
346             case OP_INVOKE_STATIC_RANGE:
347             case OP_INVOKE_SUPER_QUICK_RANGE:
348                 isRange = true;
349                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
350                 break;
351             default:
352                 calleeMethod = NULL;
353                 break;
354         }
355
356         if (calleeMethod) {
357             bool inlined = tryInlineSingletonCallsite(cUnit, calleeMethod,
358                                                       lastMIRInsn, bb, isRange);
359             if (!inlined &&
360                 !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
361                 !dvmIsNativeMethod(calleeMethod)) {
362                 CompilerMethodStats *methodStats =
363                     dvmCompilerAnalyzeMethodBody(calleeMethod, true);
364                 if ((methodStats->attributes & METHOD_IS_LEAF) &&
365                     !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
366                     /* Callee has been previously compiled */
367                     if (dvmJitGetMethodAddr(calleeMethod->insns)) {
368                         lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
369                     } else {
370                         /* Compile the callee first */
371                         dvmCompileMethod(calleeMethod, info);
372                         if (dvmJitGetMethodAddr(calleeMethod->insns)) {
373                             lastMIRInsn->OptimizationFlags |=
374                                 MIR_INVOKE_METHOD_JIT;
375                         } else {
376                             methodStats->attributes |= METHOD_CANNOT_COMPILE;
377                         }
378                     }
379                 }
380             }
381             return;
382         }
383
384         switch (opcode) {
385             case OP_INVOKE_VIRTUAL:
386             case OP_INVOKE_VIRTUAL_QUICK:
387             case OP_INVOKE_INTERFACE:
388                 isRange = false;
389                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
390                 break;
391             case OP_INVOKE_VIRTUAL_RANGE:
392             case OP_INVOKE_VIRTUAL_QUICK_RANGE:
393             case OP_INVOKE_INTERFACE_RANGE:
394                 isRange = true;
395                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
396                 break;
397             default:
398                 break;
399         }
400
401         if (calleeMethod) {
402             bool inlined = tryInlineVirtualCallsite(cUnit, calleeMethod,
403                                                     lastMIRInsn, bb, isRange);
404             if (!inlined &&
405                 !(gDvmJit.disableOpt & (1 << kMethodJit)) &&
406                 !dvmIsNativeMethod(calleeMethod)) {
407                 CompilerMethodStats *methodStats =
408                     dvmCompilerAnalyzeMethodBody(calleeMethod, true);
409                 if ((methodStats->attributes & METHOD_IS_LEAF) &&
410                     !(methodStats->attributes & METHOD_CANNOT_COMPILE)) {
411                     /* Callee has been previously compiled */
412                     if (dvmJitGetMethodAddr(calleeMethod->insns)) {
413                         lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT;
414                     } else {
415                         /* Compile the callee first */
416                         dvmCompileMethod(calleeMethod, info);
417                         if (dvmJitGetMethodAddr(calleeMethod->insns)) {
418                             lastMIRInsn->OptimizationFlags |=
419                                 MIR_INVOKE_METHOD_JIT;
420                         } else {
421                             methodStats->attributes |= METHOD_CANNOT_COMPILE;
422                         }
423                     }
424                 }
425             }
426             return;
427         }
428     }
429 }