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/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 }
    430