• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
convertRegId(const DecodedInstruction * invoke,const Method * calleeMethod,int calleeRegId,bool isRange)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 
inlineGetter(CompilationUnit * cUnit,const Method * calleeMethod,MIR * invokeMIR,BasicBlock * invokeBB,bool isPredicted,bool isRange)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 
inlineSetter(CompilationUnit * cUnit,const Method * calleeMethod,MIR * invokeMIR,BasicBlock * invokeBB,bool isPredicted,bool isRange)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 
tryInlineSingletonCallsite(CompilationUnit * cUnit,const Method * calleeMethod,MIR * invokeMIR,BasicBlock * invokeBB,bool isRange)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 
inlineEmptyVirtualCallee(CompilationUnit * cUnit,const Method * calleeMethod,MIR * invokeMIR,BasicBlock * invokeBB)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 
tryInlineVirtualCallsite(CompilationUnit * cUnit,const Method * calleeMethod,MIR * invokeMIR,BasicBlock * invokeBB,bool isRange)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 
dvmCompilerInlineMIR(CompilationUnit * cUnit,JitTranslationInfo * info)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 }
430