• 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