• 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/OpCodeNames.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 void 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 = dvmCompilerNew(sizeof(MIR), true);
47     DecodedInstruction getterInsn;
48 
49     dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &getterInsn);
50 
51     if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn))
52         return;
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;
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 = gDvm.instrWidth[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 = dvmCompilerNew(sizeof(MIR), true);
104         *invokeMIRSlow = *invokeMIR;
105         invokeMIR->dalvikInsn.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;
128 }
129 
inlineSetter(CompilationUnit * cUnit,const Method * calleeMethod,MIR * invokeMIR,BasicBlock * invokeBB,bool isPredicted,bool isRange)130 static void inlineSetter(CompilationUnit *cUnit,
131                          const Method *calleeMethod,
132                          MIR *invokeMIR,
133                          BasicBlock *invokeBB,
134                          bool isPredicted,
135                          bool isRange)
136 {
137     MIR *newSetterMIR = dvmCompilerNew(sizeof(MIR), true);
138     DecodedInstruction setterInsn;
139 
140     dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &setterInsn);
141 
142     if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn))
143         return;
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 = gDvm.instrWidth[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 = dvmCompilerNew(sizeof(MIR), true);
183         *invokeMIRSlow = *invokeMIR;
184         invokeMIR->dalvikInsn.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;
209 }
210 
tryInlineSingletonCallsite(CompilationUnit * cUnit,const Method * calleeMethod,MIR * invokeMIR,BasicBlock * invokeBB,bool isRange)211 static void 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;
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;
233     }
234 
235     if (methodStats->attributes & METHOD_IS_GETTER) {
236         inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange);
237         return;
238     } else if (methodStats->attributes & METHOD_IS_SETTER) {
239         inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange);
240         return;
241     }
242 }
243 
inlineEmptyVirtualCallee(CompilationUnit * cUnit,const Method * calleeMethod,MIR * invokeMIR,BasicBlock * invokeBB)244 static void inlineEmptyVirtualCallee(CompilationUnit *cUnit,
245                                      const Method *calleeMethod,
246                                      MIR *invokeMIR,
247                                      BasicBlock *invokeBB)
248 {
249     MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
250     *invokeMIRSlow = *invokeMIR;
251     invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction;
252 
253     dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow);
254     invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
255 }
256 
tryInlineVirtualCallsite(CompilationUnit * cUnit,const Method * calleeMethod,MIR * invokeMIR,BasicBlock * invokeBB,bool isRange)257 static void tryInlineVirtualCallsite(CompilationUnit *cUnit,
258                                      const Method *calleeMethod,
259                                      MIR *invokeMIR,
260                                      BasicBlock *invokeBB,
261                                      bool isRange)
262 {
263     /* Not a Java method */
264     if (dvmIsNativeMethod(calleeMethod)) return;
265 
266     CompilerMethodStats *methodStats =
267         dvmCompilerAnalyzeMethodBody(calleeMethod, true);
268 
269     /* Empty callee - do nothing by checking the clazz pointer */
270     if (methodStats->attributes & METHOD_IS_EMPTY) {
271         inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR, invokeBB);
272         return;
273     }
274 
275     if (methodStats->attributes & METHOD_IS_GETTER) {
276         inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange);
277         return;
278     } else if (methodStats->attributes & METHOD_IS_SETTER) {
279         inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange);
280         return;
281     }
282 }
283 
284 
dvmCompilerInlineMIR(CompilationUnit * cUnit)285 void dvmCompilerInlineMIR(CompilationUnit *cUnit)
286 {
287     int i;
288     bool isRange = false;
289 
290     /*
291      * Analyze the basic block containing an invoke to see if it can be inlined
292      */
293     for (i = 0; i < cUnit->numBlocks; i++) {
294         BasicBlock *bb = cUnit->blockList[i];
295         if (bb->blockType != kDalvikByteCode)
296             continue;
297         MIR *lastMIRInsn = bb->lastMIRInsn;
298         int opCode = lastMIRInsn->dalvikInsn.opCode;
299         int flags = dexGetInstrFlags(gDvm.instrFlags, opCode);
300 
301         /* No invoke - continue */
302         if ((flags & kInstrInvoke) == 0)
303             continue;
304 
305         /* Not a real invoke - continue */
306         if (opCode == OP_INVOKE_DIRECT_EMPTY)
307             continue;
308 
309         /*
310          * If the invoke itself is selected for single stepping, don't bother
311          * to inline it.
312          */
313         if (SINGLE_STEP_OP(opCode))
314             continue;
315 
316         const Method *calleeMethod;
317 
318         switch (opCode) {
319             case OP_INVOKE_SUPER:
320             case OP_INVOKE_DIRECT:
321             case OP_INVOKE_STATIC:
322             case OP_INVOKE_SUPER_QUICK:
323                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
324                 break;
325             case OP_INVOKE_SUPER_RANGE:
326             case OP_INVOKE_DIRECT_RANGE:
327             case OP_INVOKE_STATIC_RANGE:
328             case OP_INVOKE_SUPER_QUICK_RANGE:
329                 isRange = true;
330                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
331                 break;
332             default:
333                 calleeMethod = NULL;
334                 break;
335         }
336 
337         if (calleeMethod) {
338             tryInlineSingletonCallsite(cUnit, calleeMethod, lastMIRInsn, bb,
339                                        isRange);
340             return;
341         }
342 
343         switch (opCode) {
344             case OP_INVOKE_VIRTUAL:
345             case OP_INVOKE_VIRTUAL_QUICK:
346             case OP_INVOKE_INTERFACE:
347                 isRange = false;
348                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
349                 break;
350             case OP_INVOKE_VIRTUAL_RANGE:
351             case OP_INVOKE_VIRTUAL_QUICK_RANGE:
352             case OP_INVOKE_INTERFACE_RANGE:
353                 isRange = true;
354                 calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
355                 break;
356             default:
357                 break;
358         }
359 
360         if (calleeMethod) {
361             tryInlineVirtualCallsite(cUnit, calleeMethod, lastMIRInsn, bb,
362                                      isRange);
363             return;
364         }
365     }
366 }
367