Home | History | Annotate | Download | only in compiler
      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 */
     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 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 
    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 
    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 
    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 
    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 
    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