Home | History | Annotate | Download | only in x86
      1 /*
      2  * Copyright (C) 2012 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 
     18 /*! \file LowerInvoke.cpp
     19     \brief This file lowers the following bytecodes: INVOKE_XXX
     20 */
     21 #include "libdex/DexOpcodes.h"
     22 #include "libdex/DexFile.h"
     23 #include "mterp/Mterp.h"
     24 #include "Lower.h"
     25 #include "NcgAot.h"
     26 #include "enc_wrapper.h"
     27 
     28 char* streamMisPred = NULL;
     29 
     30 /* according to callee, decide the ArgsDoneType*/
     31 ArgsDoneType convertCalleeToType(const Method* calleeMethod) {
     32     if(calleeMethod == NULL)
     33         return ArgsDone_Full;
     34     if(dvmIsNativeMethod(calleeMethod))
     35         return ArgsDone_Native;
     36     return ArgsDone_Normal;
     37 }
     38 int common_invokeMethodRange(ArgsDoneType);
     39 int common_invokeMethodNoRange(ArgsDoneType);
     40 void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg);
     41 
     42 //inputs to common_invokeMethodRange: %ecx
     43 //          common_errNoSuchMethod: %edx
     44 #define P_GPR_1 PhysicalReg_ESI
     45 #define P_GPR_2 PhysicalReg_EBX
     46 #define P_GPR_3 PhysicalReg_ECX
     47 #define P_SCRATCH_1 PhysicalReg_EDX
     48 #define PP_GPR_1 PhysicalReg_EBX
     49 #define PP_GPR_2 PhysicalReg_ESI
     50 #define PP_GPR_3 PhysicalReg_EAX
     51 #define PP_GPR_4 PhysicalReg_EDX
     52 
     53 #ifdef WITH_JIT_INLINING
     54 /*
     55  * The function here takes care the
     56  * branch over if prediction is correct and the misprediction target for misPredBranchOver.
     57  */
     58 static void genLandingPadForMispredictedCallee(MIR* mir) {
     59     BasicBlock *fallThrough = traceCurrentBB->fallThrough;
     60     /* Bypass the move-result block if there is one */
     61     if (fallThrough->firstMIRInsn) {
     62         assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED);
     63         fallThrough = fallThrough->fallThrough;
     64     }
     65     /* Generate a branch over if the predicted inlining is correct */
     66     jumpToBasicBlock(stream, fallThrough->id);
     67     /* Hook up the target to the verification branch */
     68     int relativeNCG = stream - streamMisPred;
     69     unsigned instSize = encoder_get_inst_size(streamMisPred);
     70     relativeNCG -= instSize; //size of the instruction
     71     updateJumpInst(streamMisPred, OpndSize_8, relativeNCG);
     72 }
     73 #endif
     74 
     75 //! LOWER bytecode INVOKE_VIRTUAL without usage of helper function
     76 
     77 //!
     78 int common_invoke_virtual_nohelper(bool isRange, u2 tmp, u2 vD) {
     79 #ifdef WITH_JIT_INLINING
     80     /*
     81      * If the invoke has non-null misPredBranchOver, we need to generate
     82      * the non-inlined version of the invoke here to handle the
     83      * mispredicted case.
     84      */
     85     if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
     86         genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
     87     }
     88 #endif
     89     scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
     90     export_pc();
     91     constVREndOfBB();
     92     beforeCall("exception"); //dump GG, GL VRs
     93 
     94     get_virtual_reg(vD, OpndSize_32, 5, false);
     95     simpleNullCheck(5, false, vD);
     96 #ifndef PREDICTED_CHAINING
     97     move_mem_to_reg(OpndSize_32, offObject_clazz, 5, false, 6, false); //clazz of "this"
     98     move_mem_to_reg(OpndSize_32, offClassObject_vtable, 6, false, 7, false); //vtable
     99     /* method is already resolved in trace-based JIT */
    100     int methodIndex =
    101                 currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
    102     move_mem_to_reg(OpndSize_32, methodIndex*4, 7, false, PhysicalReg_ECX, true);
    103     if(isRange) {
    104         common_invokeMethodRange(ArgsDone_Full);
    105     }
    106     else {
    107         common_invokeMethodNoRange(ArgsDone_Full);
    108     }
    109 #else
    110     int methodIndex =
    111                 currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
    112     gen_predicted_chain(isRange, tmp, methodIndex*4, false, 5/*tmp5*/);
    113 #endif
    114     ///////////////////////////////////
    115     return 0;
    116 }
    117 //! wrapper to call either common_invoke_virtual_helper or common_invoke_virtual_nohelper
    118 
    119 //!
    120 int common_invoke_virtual(bool isRange, u2 tmp, u2 vD) {
    121     return common_invoke_virtual_nohelper(isRange, tmp, vD);
    122 }
    123 #undef P_GPR_1
    124 #undef P_GPR_2
    125 #undef P_GPR_3
    126 #undef P_SCRATCH_1
    127 #undef PP_GPR_1
    128 #undef PP_GPR_2
    129 #undef PP_GPR_3
    130 #undef PP_GPR_4
    131 
    132 #define P_GPR_1 PhysicalReg_ESI
    133 #define P_GPR_2 PhysicalReg_EBX
    134 #define P_GPR_3 PhysicalReg_EDX
    135 #define PP_GPR_1 PhysicalReg_EBX
    136 #define PP_GPR_2 PhysicalReg_ESI
    137 #define PP_GPR_3 PhysicalReg_EAX
    138 #define PP_GPR_4 PhysicalReg_EDX
    139 //! common section to lower INVOKE_SUPER
    140 
    141 //! It will use helper function if the switch is on
    142 int common_invoke_super(bool isRange, u2 tmp) {
    143     export_pc();
    144     constVREndOfBB();
    145     beforeCall("exception"); //dump GG, GL VRs
    146     ///////////////////////
    147     scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
    148     /* method is already resolved in trace-based JIT */
    149     int mIndex = currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
    150     const Method *calleeMethod =
    151         currentMethod->clazz->super->vtable[mIndex];
    152     move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
    153     if(isRange) {
    154         common_invokeMethodRange(convertCalleeToType(calleeMethod));
    155     }
    156     else {
    157         common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
    158     }
    159     ///////////////////////////////
    160     return 0;
    161 }
    162 #undef PP_GPR_1
    163 #undef PP_GPR_2
    164 #undef PP_GPR_3
    165 #undef PP_GPR_4
    166 
    167 //! helper function to handle no such method error
    168 
    169 //!
    170 int invoke_super_nsm() {
    171     insertLabel(".invoke_super_nsm", false);
    172     //NOTE: it seems that the name in %edx is not used in common_errNoSuchMethod
    173     move_mem_to_reg(OpndSize_32, offMethod_name, PhysicalReg_EAX, true, PhysicalReg_EDX, true); //method name
    174     unconditional_jump("common_errNoSuchMethod", false);
    175     return 0;
    176 }
    177 #undef P_GPR_1
    178 #undef P_GPR_2
    179 #undef P_GPR_3
    180 
    181 #define P_GPR_1 PhysicalReg_EBX
    182 #define P_GPR_2 PhysicalReg_ESI
    183 #define P_GPR_3 PhysicalReg_ECX
    184 #define PP_GPR_1 PhysicalReg_EBX
    185 #define PP_GPR_2 PhysicalReg_ESI
    186 #define PP_GPR_3 PhysicalReg_EAX
    187 #define PP_GPR_4 PhysicalReg_EDX
    188 //! common section to lower INVOKE_DIRECT
    189 
    190 //! It will use helper function if the switch is on
    191 int common_invoke_direct(bool isRange, u2 tmp, u2 vD) {
    192     //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method
    193     export_pc();
    194     constVREndOfBB();
    195     beforeCall("exception"); //dump GG, GL VRs
    196     ////////////////////////////////////
    197     get_virtual_reg(vD, OpndSize_32, 5, false);
    198     simpleNullCheck(5, false, vD);
    199     /* method is already resolved in trace-based JIT */
    200     const Method *calleeMethod =
    201         currentMethod->clazz->pDvmDex->pResMethods[tmp];
    202     move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
    203     //%ecx passed to common_invokeMethod...
    204     if(isRange) {
    205         common_invokeMethodRange(convertCalleeToType(calleeMethod));
    206     }
    207     else {
    208         common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
    209     }
    210     ////////////////////////////
    211     return 0;
    212 }
    213 #undef P_GPR_1
    214 #undef P_GPR_2
    215 #undef P_GPR_3
    216 #undef PP_GPR_1
    217 #undef PP_GPR_2
    218 #undef PP_GPR_3
    219 #undef PP_GPR_4
    220 
    221 #define P_GPR_1  PhysicalReg_EBX
    222 #define P_GPR_3  PhysicalReg_ECX
    223 #define PP_GPR_1 PhysicalReg_EBX
    224 #define PP_GPR_2 PhysicalReg_ESI
    225 #define PP_GPR_3 PhysicalReg_EAX
    226 #define PP_GPR_4 PhysicalReg_EDX
    227 //! common section to lower INVOKE_STATIC
    228 
    229 //! It will use helper function if the switch is on
    230 int common_invoke_static(bool isRange, u2 tmp) {
    231     //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method
    232     export_pc();
    233     constVREndOfBB();
    234     beforeCall("exception"); //dump GG, GL VRs
    235     ////////////////////////////
    236     /* method is already resolved in trace-based JIT */
    237     const Method *calleeMethod =
    238         currentMethod->clazz->pDvmDex->pResMethods[tmp];
    239     move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
    240     //%ecx passed to common_invokeMethod...
    241     if(isRange) {
    242         common_invokeMethodRange(convertCalleeToType(calleeMethod));
    243     }
    244     else {
    245         common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
    246     }
    247     ////////////////////////
    248     return 0;
    249 }
    250 #undef P_GPR_1
    251 #undef PP_GPR_1
    252 #undef PP_GPR_2
    253 #undef PP_GPR_3
    254 #undef PP_GPR_4
    255 
    256 #define P_GPR_1 PhysicalReg_EBX
    257 #define P_GPR_2 PhysicalReg_EAX //scratch
    258 #define P_GPR_3 PhysicalReg_ECX
    259 #define P_SCRATCH_1 PhysicalReg_ESI //clazz of object
    260 #define PP_GPR_1 PhysicalReg_EBX
    261 #define PP_GPR_2 PhysicalReg_ESI
    262 #define PP_GPR_3 PhysicalReg_EAX
    263 #define PP_GPR_4 PhysicalReg_EDX
    264 //! common section to lower INVOKE_INTERFACE
    265 
    266 //! It will use helper function if the switch is on
    267 int common_invoke_interface(bool isRange, u2 tmp, u2 vD) {
    268 #ifdef WITH_JIT_INLINING
    269     /*
    270      * If the invoke has non-null misPredBranchOver, we need to generate
    271      * the non-inlined version of the invoke here to handle the
    272      * mispredicted case.
    273      */
    274     if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
    275         genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
    276     }
    277 #endif
    278     export_pc(); //use %edx
    279     constVREndOfBB();
    280     beforeCall("exception"); //dump GG, GL VRs
    281     ///////////////////////
    282     scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
    283     get_virtual_reg(vD, OpndSize_32, 1, false);
    284     simpleNullCheck(1, false, vD);
    285 
    286 #ifndef PREDICTED_CHAINING
    287     load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    288     move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
    289     /* for trace-based JIT, pDvmDex is a constant at JIT time
    290        4th argument to dvmFindInterfaceMethodInCache at -4(%esp) */
    291     move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
    292     move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 5, false);
    293     /* for trace-based JIT, method is a constant at JIT time
    294        3rd argument to dvmFindInterfaceMethodInCache at 8(%esp) */
    295     move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
    296     move_reg_to_mem(OpndSize_32, 5, false, 0, PhysicalReg_ESP, true);
    297     scratchRegs[0] = PhysicalReg_SCRATCH_3; scratchRegs[1] = PhysicalReg_Null;
    298     call_dvmFindInterfaceMethodInCache();
    299     load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    300     compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
    301 
    302     conditional_jump_global_API(Condition_E, "common_exceptionThrown", false);
    303     move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
    304     if(isRange) {
    305         common_invokeMethodRange(ArgsDone_Full);
    306     }
    307     else {
    308         common_invokeMethodNoRange(ArgsDone_Full);
    309     }
    310 #else
    311     gen_predicted_chain(isRange, tmp, -1, true /*interface*/, 1/*tmp1*/);
    312 #endif
    313     ///////////////////////
    314     return 0;
    315 }
    316 #undef PP_GPR_1
    317 #undef PP_GPR_2
    318 #undef PP_GPR_3
    319 #undef PP_GPR_4
    320 #undef P_GPR_1
    321 #undef P_GPR_2
    322 #undef P_GPR_3
    323 #undef P_SCRATCH_1
    324 //! lower bytecode INVOKE_VIRTUAL by calling common_invoke_virtual
    325 
    326 //!
    327 int op_invoke_virtual() {
    328 #ifdef WITH_JIT_INLINING
    329     /* An invoke with the MIR_INLINED is effectively a no-op */
    330     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    331         return false;
    332 #endif
    333     //B|A|op CCCC G|F|E|D
    334     //D: the first argument, which is the "this" pointer
    335     //B: argument count
    336     //D,E,F,G,A: arguments
    337     u2 vD = FETCH(2) & 0xf;
    338     u2 tmp = FETCH(1); //method index
    339     int retval = common_invoke_virtual(false/*not range*/, tmp, vD);
    340 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    341     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    342 #endif
    343     rPC += 3;
    344     return retval;
    345 }
    346 //! lower bytecode INVOKE_SUPER by calling common_invoke_super
    347 
    348 //!
    349 int op_invoke_super() {
    350 #ifdef WITH_JIT_INLINING
    351     /* An invoke with the MIR_INLINED is effectively a no-op */
    352     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    353         return false;
    354 #endif
    355     //B|A|op CCCC G|F|E|D
    356     //D: the first argument
    357     //B: argument count
    358     //D,E,F,G,A: arguments
    359     u2 tmp = FETCH(1); //method index
    360     int retval = common_invoke_super(false/*not range*/, tmp);
    361 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    362     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    363 #endif
    364     rPC += 3;
    365     return retval;
    366 }
    367 //! lower bytecode INVOKE_DIRECT by calling common_invoke_direct
    368 
    369 //!
    370 int op_invoke_direct() {
    371 #ifdef WITH_JIT_INLINING
    372     /* An invoke with the MIR_INLINED is effectively a no-op */
    373     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    374         return false;
    375 #endif
    376     //B|A|op CCCC G|F|E|D
    377     //D: the first argument, which is the "this" pointer
    378     //B: argument count
    379     //D,E,F,G,A: arguments
    380     u2 vD = FETCH(2) & 0xf;
    381     u2 tmp = FETCH(1); //method index
    382     int retval = common_invoke_direct(false/*not range*/, tmp, vD);
    383 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    384     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    385 #endif
    386     rPC += 3;
    387     return retval;
    388 }
    389 //! lower bytecode INVOKE_STATIC by calling common_invoke_static
    390 
    391 //!
    392 int op_invoke_static() {
    393 #ifdef WITH_JIT_INLINING
    394     /* An invoke with the MIR_INLINED is effectively a no-op */
    395     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    396         return false;
    397 #endif
    398     //B|A|op CCCC G|F|E|D
    399     //D: the first argument
    400     //B: argument count
    401     //D,E,F,G,A: arguments
    402     u2 tmp = FETCH(1); //method index
    403     int retval = common_invoke_static(false/*not range*/, tmp);
    404 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    405     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    406 #endif
    407     rPC += 3;
    408     return retval;
    409 }
    410 //! lower bytecode INVOKE_INTERFACE by calling common_invoke_interface
    411 
    412 //!
    413 int op_invoke_interface() {
    414 #ifdef WITH_JIT_INLINING
    415     /* An invoke with the MIR_INLINED is effectively a no-op */
    416     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    417         return false;
    418 #endif
    419     //B|A|op CCCC G|F|E|D
    420     //D: the first argument, which is the "this" pointer
    421     //B: argument count
    422     //D,E,F,G,A: arguments
    423     u2 vD = FETCH(2) & 0xf;
    424     u2 tmp = FETCH(1); //method index
    425     int retval = common_invoke_interface(false/*not range*/, tmp, vD);
    426 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    427     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    428 #endif
    429     rPC += 3;
    430     return retval;
    431 }
    432 //! lower bytecode INVOKE_VIRTUAL_RANGE by calling common_invoke_virtual
    433 
    434 //!
    435 int op_invoke_virtual_range() {
    436 #ifdef WITH_JIT_INLINING
    437     /* An invoke with the MIR_INLINED is effectively a no-op */
    438     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    439         return false;
    440 #endif
    441     //AA|op BBBB CCCC
    442     //CCCC: the first argument, which is the "this" pointer
    443     //AA: argument count
    444     u2 tmp = FETCH(1); //BBBB, method index
    445     u2 vD = FETCH(2); //the first argument
    446     int retval = common_invoke_virtual(true/*range*/, tmp, vD);
    447 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    448     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    449 #endif
    450     rPC += 3;
    451     return retval;
    452 }
    453 //! lower bytecode INVOKE_SUPER_RANGE by calling common_invoke_super
    454 
    455 //!
    456 int op_invoke_super_range() {
    457 #ifdef WITH_JIT_INLINING
    458     /* An invoke with the MIR_INLINED is effectively a no-op */
    459     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    460         return false;
    461 #endif
    462     u2 tmp = FETCH(1); //BBBB, method index
    463     int retval = common_invoke_super(true/*range*/, tmp);
    464 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    465     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    466 #endif
    467     rPC += 3;
    468     return retval;
    469 }
    470 //! lower bytecode INVOKE_DIRECT_RANGE by calling common_invoke_direct
    471 
    472 //!
    473 int op_invoke_direct_range() {
    474 #ifdef WITH_JIT_INLINING
    475     /* An invoke with the MIR_INLINED is effectively a no-op */
    476     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    477         return false;
    478 #endif
    479     u2 tmp = FETCH(1); //BBBB, method index
    480     u2 vD = FETCH(2); //the first argument
    481     int retval = common_invoke_direct(true/*range*/, tmp, vD);
    482 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    483     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    484 #endif
    485     rPC += 3;
    486     return retval;
    487 }
    488 //! lower bytecode INVOKE_STATIC_RANGE by calling common_invoke_static
    489 
    490 //!
    491 int op_invoke_static_range() {
    492 #ifdef WITH_JIT_INLINING
    493     /* An invoke with the MIR_INLINED is effectively a no-op */
    494     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    495         return false;
    496 #endif
    497     u2 tmp = FETCH(1); //BBBB, method index
    498     int retval = common_invoke_static(true/*range*/, tmp);
    499 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    500     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    501 #endif
    502     rPC += 3;
    503     return retval;
    504 }
    505 //! lower bytecode INVOKE_INTERFACE_RANGE by calling common_invoke_interface
    506 
    507 //!
    508 int op_invoke_interface_range() {
    509 #ifdef WITH_JIT_INLINING
    510     /* An invoke with the MIR_INLINED is effectively a no-op */
    511     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
    512         return false;
    513 #endif
    514     u2 tmp = FETCH(1); //BBBB, method index
    515     u2 vD = FETCH(2); //the first argument
    516     int retval = common_invoke_interface(true/*range*/, tmp, vD);
    517 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
    518     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
    519 #endif
    520     rPC += 3;
    521     return retval;
    522 }
    523 
    524 //used %ecx, %edi, %esp %ebp
    525 #define P_GPR_1 PhysicalReg_EBX
    526 #define P_SCRATCH_1 PhysicalReg_ESI
    527 #define P_SCRATCH_2 PhysicalReg_EAX
    528 #define P_SCRATCH_3 PhysicalReg_EDX
    529 #define P_SCRATCH_4 PhysicalReg_ESI
    530 #define P_SCRATCH_5 PhysicalReg_EAX
    531 //! pass the arguments for invoking method without range
    532 
    533 //!
    534 int common_invokeMethodNoRange_noJmp() {
    535     u2 count = INST_B(inst);
    536     u2 vD = FETCH(2) & 0xf;
    537     u2 vE = (FETCH(2) >> 4) & 0xf;
    538     u2 vF = (FETCH(2) >> 8) & 0xf;
    539     u2 vG = (FETCH(2) >> 12) & 0xf;
    540     u2 vA = INST_A(inst); //5th argument
    541     int offsetFromSaveArea = -4;
    542     if(count == 5) {
    543         get_virtual_reg(vA, OpndSize_32, 22, false);
    544         move_reg_to_mem(OpndSize_32, 22, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
    545         offsetFromSaveArea -= 4;
    546     }
    547     if(count >= 4) {
    548         get_virtual_reg(vG, OpndSize_32, 23, false);
    549         move_reg_to_mem(OpndSize_32, 23, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
    550         offsetFromSaveArea -= 4;
    551     }
    552     if(count >= 3) {
    553         get_virtual_reg(vF, OpndSize_32, 24, false);
    554         move_reg_to_mem(OpndSize_32, 24, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
    555         offsetFromSaveArea -= 4;
    556     }
    557     if(count >= 2) {
    558         get_virtual_reg(vE, OpndSize_32, 25, false);
    559         move_reg_to_mem(OpndSize_32, 25, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
    560         offsetFromSaveArea -= 4;
    561     }
    562     if(count >= 1) {
    563         get_virtual_reg(vD, OpndSize_32, 26, false);
    564         move_reg_to_mem(OpndSize_32, 26, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
    565     }
    566     return 0;
    567 }
    568 
    569 int common_invokeMethod_Jmp(ArgsDoneType form) {
    570     nextVersionOfHardReg(PhysicalReg_EDX, 1);
    571     move_imm_to_reg(OpndSize_32, (int)rPC, PhysicalReg_EDX, true);
    572     //arguments needed in ArgsDone:
    573     //    start of HotChainingCell for next bytecode: -4(%esp)
    574     //    start of InvokeSingletonChainingCell for callee: -8(%esp)
    575     load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    576     insertChainingWorklist(traceCurrentBB->fallThrough->id, stream);
    577     move_chain_to_mem(OpndSize_32, traceCurrentBB->fallThrough->id, 4, PhysicalReg_ESP, true);
    578     // for honeycomb: JNI call doesn't need a chaining cell, so the taken branch is null
    579     if(traceCurrentBB->taken)
    580         insertChainingWorklist(traceCurrentBB->taken->id, stream);
    581     int takenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
    582     move_chain_to_mem(OpndSize_32, takenId, 0, PhysicalReg_ESP, true);
    583     if(form == ArgsDone_Full)
    584         unconditional_jump_global_API(".invokeArgsDone_jit", false);
    585     else if(form == ArgsDone_Native)
    586         unconditional_jump_global_API(".invokeArgsDone_native", false);
    587     else
    588         unconditional_jump_global_API(".invokeArgsDone_normal", false);
    589     return 0;
    590 }
    591 
    592 int common_invokeMethodNoRange(ArgsDoneType form) {
    593     common_invokeMethodNoRange_noJmp();
    594     common_invokeMethod_Jmp(form);
    595     return 0;
    596 }
    597 
    598 #undef P_GPR_1
    599 #undef P_SCRATCH_1
    600 #undef P_SCRATCH_2
    601 #undef P_SCRATCH_3
    602 #undef P_SCRATCH_4
    603 #undef P_SCRATCH_5
    604 
    605 //input: %ecx (method to call)
    606 #define P_GPR_1 PhysicalReg_EBX
    607 #define P_GPR_2 PhysicalReg_ESI
    608 #define P_GPR_3 PhysicalReg_EDX //not used with P_SCRATCH_2
    609 #define P_SCRATCH_1 PhysicalReg_EAX
    610 #define P_SCRATCH_2 PhysicalReg_EDX
    611 #define P_SCRATCH_3 PhysicalReg_EAX
    612 #define P_SCRATCH_4 PhysicalReg_EDX
    613 #define P_SCRATCH_5 PhysicalReg_EAX
    614 #define P_SCRATCH_6 PhysicalReg_EDX
    615 #define P_SCRATCH_7 PhysicalReg_EAX
    616 #define P_SCRATCH_8 PhysicalReg_EDX
    617 #define P_SCRATCH_9 PhysicalReg_EAX
    618 #define P_SCRATCH_10 PhysicalReg_EDX
    619 //! pass the arguments for invoking method with range
    620 
    621 //! loop is unrolled when count <= 10
    622 int common_invokeMethodRange_noJmp() {
    623     u2 count = INST_AA(inst);
    624     u2 vD = FETCH(2); //the first argument
    625     savearea_from_fp(21, false);
    626     //vD to rFP-4*count-20
    627     //vD+1 to rFP-4*count-20+4 = rFP-20-4*(count-1)
    628     if(count >= 1 && count <= 10) {
    629         get_virtual_reg(vD, OpndSize_32, 22, false);
    630         move_reg_to_mem(OpndSize_32, 22, false, -4*count, 21, false);
    631     }
    632     if(count >= 2 && count <= 10) {
    633         get_virtual_reg(vD+1, OpndSize_32, 23, false);
    634         move_reg_to_mem(OpndSize_32, 23, false, -4*(count-1), 21, false);
    635     }
    636     if(count >= 3 && count <= 10) {
    637         get_virtual_reg(vD+2, OpndSize_32, 24, false);
    638         move_reg_to_mem(OpndSize_32, 24, false, -4*(count-2), 21, false);
    639     }
    640     if(count >= 4 && count <= 10) {
    641         get_virtual_reg(vD+3, OpndSize_32, 25, false);
    642         move_reg_to_mem(OpndSize_32, 25, false, -4*(count-3), 21, false);
    643     }
    644     if(count >= 5 && count <= 10) {
    645         get_virtual_reg(vD+4, OpndSize_32, 26, false);
    646         move_reg_to_mem(OpndSize_32, 26, false, -4*(count-4), 21, false);
    647     }
    648     if(count >= 6 && count <= 10) {
    649         get_virtual_reg(vD+5, OpndSize_32, 27, false);
    650         move_reg_to_mem(OpndSize_32, 27, false, -4*(count-5), 21, false);
    651     }
    652     if(count >= 7 && count <= 10) {
    653         get_virtual_reg(vD+6, OpndSize_32, 28, false);
    654         move_reg_to_mem(OpndSize_32, 28, false, -4*(count-6), 21, false);
    655     }
    656     if(count >= 8 && count <= 10) {
    657         get_virtual_reg(vD+7, OpndSize_32, 29, false);
    658         move_reg_to_mem(OpndSize_32, 29, false, -4*(count-7), 21, false);
    659     }
    660     if(count >= 9 && count <= 10) {
    661         get_virtual_reg(vD+8, OpndSize_32, 30, false);
    662         move_reg_to_mem(OpndSize_32, 30, false, -4*(count-8), 21, false);
    663     }
    664     if(count == 10) {
    665         get_virtual_reg(vD+9, OpndSize_32, 31, false);
    666         move_reg_to_mem(OpndSize_32, 31, false, -4*(count-9), 21, false);
    667     }
    668     if(count > 10) {
    669         //dump to memory first, should we set physicalReg to Null?
    670         //this bytecodes uses a set of virtual registers (update getVirtualInfo)
    671         //this is necessary to correctly insert transfer points
    672         int k;
    673         for(k = 0; k < count; k++) {
    674             spillVirtualReg(vD+k, LowOpndRegType_gp, true); //will update refCount
    675         }
    676         load_effective_addr(4*vD, PhysicalReg_FP, true, 12, false);
    677         alu_binary_imm_reg(OpndSize_32, sub_opc, 4*count, 21, false);
    678         move_imm_to_reg(OpndSize_32, count, 13, false);
    679         insertLabel(".invokeMethod_1", true); //if checkDup: will perform work from ShortWorklist
    680         rememberState(1);
    681         move_mem_to_reg(OpndSize_32, 0, 12, false, 14, false);
    682         move_reg_to_mem(OpndSize_32, 14, false, 0, 21, false);
    683         load_effective_addr(4, 12, false, 12, false);
    684         alu_binary_imm_reg(OpndSize_32, sub_opc, 1, 13, false);
    685         load_effective_addr(4, 21, false, 21, false);
    686         transferToState(1);
    687         conditional_jump(Condition_NE, ".invokeMethod_1", true); //backward branch
    688     }
    689     return 0;
    690 }
    691 
    692 int common_invokeMethodRange(ArgsDoneType form) {
    693     common_invokeMethodRange_noJmp();
    694     common_invokeMethod_Jmp(form);
    695     return 0;
    696 }
    697 #undef P_GPR_1
    698 #undef P_GPR_2
    699 #undef P_GPR_3
    700 #undef P_SCRATCH_1
    701 #undef P_SCRATCH_2
    702 #undef P_SCRATCH_3
    703 #undef P_SCRATCH_4
    704 #undef P_SCRATCH_5
    705 #undef P_SCRATCH_6
    706 #undef P_SCRATCH_7
    707 #undef P_SCRATCH_8
    708 #undef P_SCRATCH_9
    709 #undef P_SCRATCH_10
    710 
    711 #define P_GPR_1 PhysicalReg_EBX
    712 #define P_GPR_3 PhysicalReg_ESI
    713 #define P_SCRATCH_1 PhysicalReg_EAX
    714 #define P_SCRATCH_2 PhysicalReg_EDX
    715 #define P_SCRATCH_3 PhysicalReg_EAX
    716 #define P_SCRATCH_4 PhysicalReg_EDX
    717 #define P_SCRATCH_5 PhysicalReg_EAX
    718 #define P_SCRATCH_6 PhysicalReg_EDX
    719 
    720 //! spill a register to native stack
    721 
    722 //! decrease %esp by 4, then store a register at 0(%esp)
    723 int spill_reg(int reg, bool isPhysical) {
    724     load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    725     move_reg_to_mem(OpndSize_32, reg, isPhysical, 0, PhysicalReg_ESP, true);
    726     return 0;
    727 }
    728 //! get a register from native stack
    729 
    730 //! load a register from 0(%esp), then increase %esp by 4
    731 int unspill_reg(int reg, bool isPhysical) {
    732     move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, reg, isPhysical);
    733     load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    734     return 0;
    735 }
    736 
    737 void generate_invokeNative(bool generateForNcg); //forward declaration
    738 void generate_stackOverflow(); //forward declaration
    739 
    740 //! common code to invoke a method after all arguments are handled
    741 
    742 //!
    743 //takes one argument to generate code
    744 //  for invokeNativeSingle (form == ArgsDone_Native)
    745 //   or invokeNonNativeSingle (form == ArgsDone_Normal) when WITH_JIT is true
    746 //   to dynamically determine which one to choose (form == ArgsDone_Full)
    747 /* common_invokeArgsDone is called at NCG time and
    748      at execution time during relocation
    749    generate invokeArgsDone for NCG if isJitFull is false && form == Full */
    750 int common_invokeArgsDone(ArgsDoneType form, bool isJitFull) {
    751     bool generateForNcg = false;
    752     if(form == ArgsDone_Full) {
    753         if(isJitFull)
    754             insertLabel(".invokeArgsDone_jit", false);
    755         else {
    756             insertLabel(".invokeArgsDone", false);
    757             generateForNcg = true;
    758         }
    759     }
    760     else if(form == ArgsDone_Normal)
    761         insertLabel(".invokeArgsDone_normal", false);
    762     else if(form == ArgsDone_Native)
    763         insertLabel(".invokeArgsDone_native", false);
    764     //%ecx: methodToCall
    765     movez_mem_to_reg(OpndSize_16, offMethod_registersSize, PhysicalReg_ECX, true, P_SCRATCH_1, true); //regSize
    766     scratchRegs[0] = PhysicalReg_EBX; scratchRegs[1] = PhysicalReg_ESI;
    767     scratchRegs[2] = PhysicalReg_EDX; scratchRegs[3] = PhysicalReg_Null;
    768     savearea_from_fp(P_GPR_3, true);
    769     alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_1, true);
    770     alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_1, true, P_GPR_3, true);
    771     //update newSaveArea->savedPc, here P_GPR_3 is new FP
    772     move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true);
    773     movez_mem_to_reg(OpndSize_16, offMethod_outsSize, PhysicalReg_ECX, true, P_SCRATCH_2, true); //outsSize
    774     move_reg_to_reg(OpndSize_32, P_GPR_3, true, P_GPR_1, true); //new FP
    775     alu_binary_imm_reg(OpndSize_32, sub_opc, sizeofStackSaveArea, P_GPR_3, true);
    776 
    777     alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_2, true);
    778     alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_2, true, P_GPR_3, true);
    779     get_self_pointer(P_SCRATCH_3, true);
    780     move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offStackSaveArea_prevFrame-sizeofStackSaveArea, P_GPR_1, true); //set stack->prevFrame
    781     compare_mem_reg(OpndSize_32, offsetof(Thread, interpStackEnd), P_SCRATCH_3, true, P_GPR_3, true);
    782     conditional_jump(Condition_L, ".stackOverflow", true);
    783 
    784     if(form == ArgsDone_Full) {
    785         test_imm_mem(OpndSize_32, ACC_NATIVE, offMethod_accessFlags, PhysicalReg_ECX, true);
    786     }
    787     move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offStackSaveArea_method-sizeofStackSaveArea, P_GPR_1, true); //set stack->method
    788 
    789     if(form == ArgsDone_Native || form == ArgsDone_Full) {
    790         /* to correctly handle code cache reset:
    791            update returnAddr and check returnAddr after done with the native method
    792            if returnAddr is set to NULL during code cache reset,
    793            the execution will correctly continue with interpreter */
    794         //get returnAddr from 4(%esp) and update stack
    795         move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true,
    796                         PhysicalReg_EDX, true);
    797         move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true,
    798                         offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_1, true);
    799     }
    800     if(form == ArgsDone_Native) {
    801         generate_invokeNative(generateForNcg);
    802         return 0;
    803     }
    804     if(form == ArgsDone_Full) {
    805         conditional_jump(Condition_NE, ".invokeNative", true);
    806     }
    807     move_mem_to_reg(OpndSize_32, offMethod_clazz, PhysicalReg_ECX, true, P_SCRATCH_4, true); //get method->claz
    808     move_mem_to_reg(OpndSize_32, offClassObject_pDvmDex, P_SCRATCH_4, true, P_SCRATCH_4, true); //get method->clazz->pDvmDex
    809     move_reg_to_reg(OpndSize_32, P_GPR_1, true, PhysicalReg_FP, true); //update rFP
    810     get_self_pointer(P_GPR_1, true);
    811     move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offsetof(Thread, interpSave.method), P_GPR_1, true); //glue->method
    812     move_reg_to_mem(OpndSize_32, P_SCRATCH_4, true, offsetof(Thread, interpSave.methodClassDex), P_GPR_1, true); //set_glue_dvmdex
    813     move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set glue->self->frame
    814     if(!generateForNcg) {
    815         /* returnAddr updated already for Full */
    816         //get returnAddr from 4(%esp) and update stack
    817         if(form == ArgsDone_Normal)
    818             move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true,
    819                             PhysicalReg_EDX, true);
    820         //for JIT: starting bytecode in %ebx to invoke JitToInterp
    821         move_mem_to_reg(OpndSize_32, offMethod_insns, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
    822         if(form == ArgsDone_Normal)
    823             move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true,
    824                             offStackSaveArea_returnAddr-sizeofStackSaveArea, PhysicalReg_FP, true);
    825     }
    826 
    827     insertLabel(".invokeInterp", true);
    828     if(!generateForNcg) {
    829         bool callNoChain = false;
    830 #ifdef PREDICTED_CHAINING
    831         if(form == ArgsDone_Full) callNoChain = true;
    832 #endif
    833         if(callNoChain) {
    834             scratchRegs[0] = PhysicalReg_EAX;
    835             load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    836             call_dvmJitToInterpTraceSelectNoChain(); //input: rPC in %ebx
    837         } else {
    838             //jump to the stub at (%esp)
    839             move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true,
    840                             PhysicalReg_EDX, true);
    841             load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    842             unconditional_jump_reg(PhysicalReg_EDX, true);
    843         }
    844     }
    845 
    846     if(form == ArgsDone_Full) generate_invokeNative(generateForNcg);
    847     generate_stackOverflow();
    848     return 0;
    849 }
    850 
    851 /* when WITH_JIT is true,
    852      JIT'ed code invokes native method, after invoke, execution will continue
    853      with the interpreter or with JIT'ed code if chained
    854 */
    855 void generate_invokeNative(bool generateForNcg) {
    856     insertLabel(".invokeNative", true);
    857     //if(!generateForNcg)
    858     //    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    859     load_effective_addr(-28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    860     move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
    861     move_reg_to_mem(OpndSize_32, P_GPR_1, true, 20, PhysicalReg_ESP, true);
    862     scratchRegs[0] = PhysicalReg_EDX;
    863     get_self_pointer(P_SCRATCH_1, true); //glue->self
    864     move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 8, PhysicalReg_ESP, true);
    865     move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 12, PhysicalReg_ESP, true);
    866     move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 24, PhysicalReg_ESP, true);
    867     move_mem_to_reg(OpndSize_32, offThread_jniLocal_nextEntry, P_SCRATCH_1, true, P_SCRATCH_2, true); //get self->local_next
    868     scratchRegs[1] = PhysicalReg_EAX;
    869     move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_1, true); //update jniLocalRef of stack
    870     move_reg_to_mem(OpndSize_32, P_GPR_1, true, offThread_curFrame, P_SCRATCH_1, true); //set self->curFrame
    871     move_imm_to_mem(OpndSize_32, 0, offThread_inJitCodeCache, P_SCRATCH_1, true); //clear self->inJitCodeCache
    872     load_effective_addr(offsetof(Thread, interpSave.retval), P_SCRATCH_1, true, P_SCRATCH_3, true); //self->retval
    873     move_reg_to_mem(OpndSize_32, P_SCRATCH_3, true, 4, PhysicalReg_ESP, true);
    874     //NOTE: native method checks the interpreted stack for arguments
    875     //      The immediate arguments on native stack: address of return value, new FP, self
    876     call_mem(40, PhysicalReg_ECX, true);//*40(%ecx)
    877     //we can't assume the argument stack is unmodified after the function call
    878     //duplicate newFP & glue->self on stack: newFP (-28 & -8) glue->self (-16 & -4)
    879     move_mem_to_reg(OpndSize_32, 20, PhysicalReg_ESP, true, P_GPR_3, true); //new FP
    880     move_mem_to_reg(OpndSize_32, 24, PhysicalReg_ESP, true, P_GPR_1, true); //glue->self
    881     load_effective_addr(28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    882     move_mem_to_reg(OpndSize_32, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_1, true); //newSaveArea->jniLocal
    883     compare_imm_mem(OpndSize_32, 0, offThread_exception, P_GPR_1, true); //self->exception
    884     if(!generateForNcg)
    885         load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    886     //NOTE: PhysicalReg_FP should be callee-saved register
    887     move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set self->curFrame
    888     move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, offThread_jniLocal_nextEntry, P_GPR_1, true); //set self->jniLocal
    889     conditional_jump(Condition_NE, "common_exceptionThrown", false);
    890     if(!generateForNcg) {
    891         //get returnAddr, if it is not NULL,
    892         //    return to JIT'ed returnAddr after executing the native method
    893         /* to correctly handle code cache reset:
    894            update returnAddr and check returnAddr after done with the native method
    895            if returnAddr is set to NULL during code cache reset,
    896            the execution will correctly continue with interpreter */
    897         move_mem_to_reg(OpndSize_32, offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_2, true);
    898         //set self->inJitCodeCache to returnAddr (P_GPR_1 is in %ebx)
    899         move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offThread_inJitCodeCache, P_GPR_1, true);
    900         move_mem_to_reg(OpndSize_32, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true, PhysicalReg_EBX, true); //savedPc
    901         compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
    902         conditional_jump(Condition_E, ".nativeToInterp", true);
    903         unconditional_jump_reg(P_SCRATCH_2, true);
    904         //if returnAddr is NULL, return to interpreter after executing the native method
    905         insertLabel(".nativeToInterp", true);
    906         //move rPC by 6 (3 bytecode units for INVOKE)
    907         alu_binary_imm_reg(OpndSize_32, add_opc, 6, PhysicalReg_EBX, true);
    908         scratchRegs[0] = PhysicalReg_EAX;
    909         call_dvmJitToInterpTraceSelectNoChain(); //rPC in %ebx
    910     }
    911     return;
    912 }
    913 void generate_stackOverflow() {
    914     insertLabel(".stackOverflow", true);
    915     //load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    916     move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 4, PhysicalReg_ESP, true);
    917     get_self_pointer(P_GPR_1, true); //glue->self
    918     move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
    919     call_dvmHandleStackOverflow();
    920     load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
    921     unconditional_jump("common_exceptionThrown", false);
    922 }
    923 #undef P_GPR_1
    924 #undef P_GPR_2
    925 #undef P_GPR_3
    926 #undef P_SCRATCH_1
    927 #undef P_SCRATCH_2
    928 #undef P_SCRATCH_3
    929 #undef P_SCRATCH_4
    930 #undef P_SCRATCH_5
    931 #undef P_SCRATCH_6
    932 
    933 /////////////////////////////////////////////
    934 #define P_GPR_1 PhysicalReg_EBX
    935 #define P_GPR_2 PhysicalReg_ECX
    936 #define P_SCRATCH_1 PhysicalReg_ESI
    937 #define P_SCRATCH_2 PhysicalReg_EDX
    938 #define P_SCRATCH_3 PhysicalReg_ESI
    939 #define P_SCRATCH_4 PhysicalReg_EDX
    940 //! lower bytecode EXECUTE_INLINE
    941 
    942 //!
    943 int op_execute_inline(bool isRange) {
    944     //tmp, vC, vD, vE, vF
    945     int num;
    946     if(!isRange) num = INST_B(inst);
    947     else num = INST_AA(inst);
    948     u2 tmp = FETCH(1);
    949     u2 vC, vD, vE, vF;
    950     if(!isRange) {
    951         vC = FETCH(2) & 0xf;
    952         vD = (FETCH(2) >> 4) & 0xf;
    953         vE = (FETCH(2) >> 8) & 0xf;
    954         vF = FETCH(2) >> 12;
    955     } else {
    956         vC = FETCH(2);
    957         vD = vC + 1;
    958         vE = vC + 2;
    959         vF = vC + 3;
    960     }
    961     export_pc();
    962     switch (tmp) {
    963         case INLINE_EMPTYINLINEMETHOD:
    964             return 0;  /* Nop */
    965         case INLINE_STRING_LENGTH:
    966             get_virtual_reg(vC, OpndSize_32, 1, false);
    967             compare_imm_reg(OpndSize_32, 0, 1, false);
    968             conditional_jump(Condition_NE, ".do_inlined_string_length", true);
    969             scratchRegs[0] = PhysicalReg_SCRATCH_1;
    970             jumpToExceptionThrown(1/*exception number*/);
    971             insertLabel(".do_inlined_string_length", true);
    972             move_mem_to_reg(OpndSize_32, 0x14, 1, false, 2, false);
    973             get_self_pointer(3, false);
    974             move_reg_to_mem(OpndSize_32, 2, false, offsetof(Thread, interpSave.retval), 3, false);
    975             return 0;
    976         case INLINE_STRING_IS_EMPTY:
    977             get_virtual_reg(vC, OpndSize_32, 1, false);
    978             compare_imm_reg(OpndSize_32, 0, 1, false);
    979             conditional_jump(Condition_NE, ".do_inlined_string_length", true);
    980             scratchRegs[0] = PhysicalReg_SCRATCH_1;
    981             jumpToExceptionThrown(1/*exception number*/);
    982             insertLabel(".do_inlined_string_length", true);
    983             compare_imm_mem(OpndSize_32, 0, 0x14, 1, false);
    984             conditional_jump(Condition_E, ".inlined_string_length_return_true",
    985                              true);
    986             get_self_pointer(2, false);
    987             move_imm_to_mem(OpndSize_32, 0, offsetof(Thread, interpSave.retval), 2, false);
    988             unconditional_jump(".inlined_string_length_done", true);
    989             insertLabel(".inlined_string_length_return_true", true);
    990             get_self_pointer(2, false);
    991             move_imm_to_mem(OpndSize_32, 1, offsetof(Thread, interpSave.retval), 2, false);
    992             insertLabel(".inlined_string_length_done", true);
    993             return 0;
    994         case INLINE_MATH_ABS_INT:
    995             get_virtual_reg(vC, OpndSize_32, 1, false);
    996             move_reg_to_reg(OpndSize_32, 1, false, 2, false);
    997             alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 2, false);
    998             alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 1, false);
    999             alu_binary_reg_reg(OpndSize_32, sub_opc, 2, false, 1, false);
   1000             get_self_pointer(3, false);
   1001             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
   1002             return 0;
   1003         case INLINE_MATH_ABS_LONG:
   1004             get_virtual_reg(vD, OpndSize_32, 1, false);
   1005             move_reg_to_reg(OpndSize_32, 1, false, 2, false);
   1006             alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 1, false);
   1007             move_reg_to_reg(OpndSize_32, 1, false, 3, false);
   1008             move_reg_to_reg(OpndSize_32, 1, false, 4, false);
   1009             get_virtual_reg(vC, OpndSize_32, 5, false);
   1010             alu_binary_reg_reg(OpndSize_32, xor_opc, 5, false, 1, false);
   1011             get_self_pointer(6, false);
   1012             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 6, false);
   1013             alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 3, false);
   1014             move_reg_to_mem(OpndSize_32, 3, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
   1015             alu_binary_reg_mem(OpndSize_32, sub_opc, 4, false, offsetof(Thread, interpSave.retval), 6, false);
   1016             alu_binary_reg_mem(OpndSize_32, sbb_opc, 4, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
   1017             return 0;
   1018         case INLINE_MATH_MAX_INT:
   1019             get_virtual_reg(vC, OpndSize_32, 1, false);
   1020             get_virtual_reg(vD, OpndSize_32, 2, false);
   1021             compare_reg_reg(1, false, 2, false);
   1022             conditional_move_reg_to_reg(OpndSize_32, Condition_GE, 2,
   1023                                         false/*src*/, 1, false/*dst*/);
   1024             get_self_pointer(3, false);
   1025             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
   1026             return 0;
   1027         case INLINE_MATH_ABS_FLOAT:
   1028             get_virtual_reg(vC, OpndSize_32, 1, false);
   1029             alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 1, false);
   1030             get_self_pointer(2, false);
   1031             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
   1032             return 0;
   1033         case INLINE_MATH_ABS_DOUBLE:
   1034             get_virtual_reg(vC, OpndSize_32, 1, false);
   1035             get_virtual_reg(vD, OpndSize_32, 2, false);
   1036             alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 2, false);
   1037             get_self_pointer(3, false);
   1038             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
   1039             move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
   1040             return 0;
   1041         case INLINE_STRING_FASTINDEXOF_II:
   1042 #if defined(USE_GLOBAL_STRING_DEFS)
   1043             break;
   1044 #else
   1045             get_virtual_reg(vC, OpndSize_32, 1, false);
   1046             compare_imm_reg(OpndSize_32, 0, 1, false);
   1047             get_virtual_reg(vD, OpndSize_32, 2, false);
   1048             get_virtual_reg(vE, OpndSize_32, 3, false);
   1049             conditional_jump(Condition_NE, ".do_inlined_string_fastIndexof",
   1050                              true);
   1051             scratchRegs[0] = PhysicalReg_SCRATCH_1;
   1052             jumpToExceptionThrown(1/*exception number*/);
   1053             insertLabel(".do_inlined_string_fastIndexof", true);
   1054             move_mem_to_reg(OpndSize_32, 0x14, 1, false, 4, false);
   1055             move_mem_to_reg(OpndSize_32, 0x8, 1, false, 5, false);
   1056             move_mem_to_reg(OpndSize_32, 0x10, 1, false, 6, false);
   1057             alu_binary_reg_reg(OpndSize_32, xor_opc, 1, false, 1, false);
   1058             compare_imm_reg(OpndSize_32, 0, 3, false);
   1059             conditional_move_reg_to_reg(OpndSize_32, Condition_NS, 3, false, 1,
   1060                                         false);
   1061             compare_reg_reg(4, false, 1, false);
   1062             conditional_jump(Condition_GE,
   1063                              ".do_inlined_string_fastIndexof_exitfalse", true);
   1064             dump_mem_scale_reg(Mnemonic_LEA, OpndSize_32, 5, false, 0xc/*disp*/,
   1065                                6, false, 2, 5, false, LowOpndRegType_gp);
   1066             movez_mem_disp_scale_to_reg(OpndSize_16, 5, false, 0, 1, false, 2,
   1067                                         3, false);
   1068             compare_reg_reg(3, false, 2, false);
   1069             conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
   1070                              true);
   1071             load_effective_addr(0x1, 1, false, 3, false);
   1072             load_effective_addr_scale(5, false, 3, false, 2, 5, false);
   1073             unconditional_jump(".do_inlined_string_fastIndexof_iter", true);
   1074             insertLabel(".do_inlined_string_fastIndexof_ch_cmp", true);
   1075             if(gDvm.executionMode == kExecutionModeNcgO1) {
   1076                 rememberState(1);
   1077             }
   1078             movez_mem_to_reg(OpndSize_16, 0, 5, false, 6, false);
   1079             load_effective_addr(0x2, 5, false, 5, false);
   1080             compare_reg_reg(6, false, 2, false);
   1081             conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
   1082                              true);
   1083             load_effective_addr(0x1, 3, false, 3, false);
   1084             insertLabel(".do_inlined_string_fastIndexof_iter", true);
   1085             compare_reg_reg(4, false, 3, false);
   1086             move_reg_to_reg(OpndSize_32, 3, false, 1, false);
   1087             if(gDvm.executionMode == kExecutionModeNcgO1) {
   1088                 transferToState(1);
   1089             }
   1090             conditional_jump(Condition_NE,
   1091                              ".do_inlined_string_fastIndexof_ch_cmp", true);
   1092             insertLabel(".do_inlined_string_fastIndexof_exitfalse", true);
   1093             move_imm_to_reg(OpndSize_32, 0xffffffff, 1,  false);
   1094             insertLabel(".do_inlined_string_fastIndexof_exit", true);
   1095             get_self_pointer(7, false);
   1096             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 7, false);
   1097             return 0;
   1098         case INLINE_FLOAT_TO_RAW_INT_BITS:
   1099             get_virtual_reg(vC, OpndSize_32, 1, false);
   1100             get_self_pointer(2, false);
   1101             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
   1102             return 0;
   1103         case INLINE_INT_BITS_TO_FLOAT:
   1104             get_virtual_reg(vC, OpndSize_32, 1, false);
   1105             get_self_pointer(2, false);
   1106             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
   1107             return 0;
   1108         case INLINE_DOUBLE_TO_RAW_LONG_BITS:
   1109             get_virtual_reg(vC, OpndSize_32, 1, false);
   1110             get_self_pointer(3, false);
   1111             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
   1112             get_virtual_reg(vD, OpndSize_32, 2, false);
   1113             move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
   1114             return 0;
   1115         case INLINE_LONG_BITS_TO_DOUBLE:
   1116             get_virtual_reg(vC, OpndSize_32, 1, false);
   1117             get_virtual_reg(vD, OpndSize_32, 2, false);
   1118             get_self_pointer(3, false);
   1119             move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
   1120             move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
   1121             return 0;
   1122         default:
   1123                 break;
   1124     }
   1125 #endif
   1126     get_self_pointer(PhysicalReg_SCRATCH_1, false);
   1127     load_effective_addr(offsetof(Thread, interpSave.retval), PhysicalReg_SCRATCH_1, false, 1, false);
   1128     load_effective_addr(-24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1129     move_reg_to_mem(OpndSize_32, 1, false, 16, PhysicalReg_ESP, true);
   1130     if(num >= 1) {
   1131         get_virtual_reg(vC, OpndSize_32, 2, false);
   1132         move_reg_to_mem(OpndSize_32, 2, false, 0, PhysicalReg_ESP, true);
   1133     }
   1134     if(num >= 2) {
   1135         get_virtual_reg(vD, OpndSize_32, 3, false);
   1136         move_reg_to_mem(OpndSize_32, 3, false, 4, PhysicalReg_ESP, true);
   1137     }
   1138     if(num >= 3) {
   1139         get_virtual_reg(vE, OpndSize_32, 4, false);
   1140         move_reg_to_mem(OpndSize_32, 4, false, 8, PhysicalReg_ESP, true);
   1141     }
   1142     if(num >= 4) {
   1143         get_virtual_reg(vF, OpndSize_32, 5, false);
   1144         move_reg_to_mem(OpndSize_32, 5, false, 12, PhysicalReg_ESP, true);
   1145     }
   1146     beforeCall("execute_inline");
   1147     load_imm_global_data_API("gDvmInlineOpsTable", OpndSize_32, 6, false);
   1148     call_mem(16*tmp, 6, false);//
   1149     afterCall("execute_inline");
   1150     compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
   1151 
   1152     load_effective_addr(24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1153     conditional_jump(Condition_NE, ".execute_inline_done", true);
   1154     //jump to dvmJitToExceptionThrown
   1155     scratchRegs[0] = PhysicalReg_SCRATCH_1;
   1156     jumpToExceptionThrown(1/*exception number*/);
   1157     insertLabel(".execute_inline_done", true);
   1158     rPC += 3;
   1159     return 0;
   1160 }
   1161 #undef P_GPR_1
   1162 #undef P_GPR_2
   1163 #undef P_SCRATCH_1
   1164 #undef P_SCRATCH_2
   1165 #undef P_SCRATCH_3
   1166 #undef P_SCRATCH_4
   1167 
   1168 //! lower bytecode INVOKE_OBJECT_INIT_RANGE
   1169 
   1170 //!
   1171 int op_invoke_object_init_range() {
   1172     return -1;
   1173 }
   1174 
   1175 #define P_GPR_1 PhysicalReg_EBX
   1176 #define P_SCRATCH_1 PhysicalReg_ESI
   1177 #define P_SCRATCH_2 PhysicalReg_EDX
   1178 #define PP_GPR_1 PhysicalReg_EBX
   1179 #define PP_GPR_2 PhysicalReg_ESI
   1180 #define PP_GPR_3 PhysicalReg_EAX
   1181 #define PP_GPR_4 PhysicalReg_EDX
   1182 //! common code for INVOKE_VIRTUAL_QUICK
   1183 
   1184 //! It uses helper function if the switch is on
   1185 int common_invoke_virtual_quick(bool hasRange, u2 vD, u2 IMMC) {
   1186 #ifdef WITH_JIT_INLINING
   1187     /* An invoke with the MIR_INLINED is effectively a no-op */
   1188     if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
   1189         return false;
   1190     /*
   1191      * If the invoke has non-null misPredBranchOver, we need to generate
   1192      * the non-inlined version of the invoke here to handle the
   1193      * mispredicted case.
   1194      */
   1195     if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
   1196         genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
   1197     }
   1198 #endif
   1199     export_pc();
   1200     constVREndOfBB();
   1201     beforeCall("exception"); //dump GG, GL VRs
   1202     /////////////////////////////////////////////////
   1203     get_virtual_reg(vD, OpndSize_32, 1, false);
   1204     simpleNullCheck(1, false, vD);
   1205 #ifndef PREDICTED_CHAINING
   1206     move_mem_to_reg(OpndSize_32, 0, 1, false, 2, false);
   1207     move_mem_to_reg(OpndSize_32, offClassObject_vtable, 2, false, 3, false);
   1208     move_mem_to_reg(OpndSize_32, IMMC, 3, false, PhysicalReg_ECX, true);
   1209 
   1210     if(hasRange) {
   1211         common_invokeMethodRange(ArgsDone_Full);
   1212     }
   1213     else {
   1214         common_invokeMethodNoRange(ArgsDone_Full);
   1215     }
   1216 #else
   1217     gen_predicted_chain(hasRange, -1, IMMC, false, 1/*tmp1*/);
   1218 #endif
   1219     ////////////////////////
   1220     return 0;
   1221 }
   1222 #undef P_GPR_1
   1223 #undef P_SCRATCH_1
   1224 #undef P_SCRATCH_2
   1225 #undef PP_GPR_1
   1226 #undef PP_GPR_2
   1227 #undef PP_GPR_3
   1228 #undef PP_GPR_4
   1229 //! lower bytecode INVOKE_VIRTUAL_QUICK by calling common_invoke_virtual_quick
   1230 
   1231 //!
   1232 int op_invoke_virtual_quick() {
   1233     u2 vD = FETCH(2) & 0xf;
   1234     u2 IMMC = 4*FETCH(1);
   1235     int retval = common_invoke_virtual_quick(false, vD, IMMC);
   1236 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
   1237     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
   1238 #endif
   1239     rPC += 3;
   1240     return retval;
   1241 }
   1242 //! lower bytecode INVOKE_VIRTUAL_QUICK_RANGE by calling common_invoke_virtual_quick
   1243 
   1244 //!
   1245 int op_invoke_virtual_quick_range() {
   1246     u2 vD = FETCH(2);
   1247     u2 IMMC = 4*FETCH(1);
   1248     int retval = common_invoke_virtual_quick(true, vD, IMMC);
   1249 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
   1250     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
   1251 #endif
   1252     rPC += 3;
   1253     return retval;
   1254 }
   1255 #define P_GPR_1 PhysicalReg_EBX
   1256 #define P_GPR_2 PhysicalReg_ESI
   1257 #define P_SCRATCH_1 PhysicalReg_EDX
   1258 //! common code to lower INVOKE_SUPER_QUICK
   1259 
   1260 //!
   1261 int common_invoke_super_quick(bool hasRange, u2 vD, u2 IMMC) {
   1262     export_pc();
   1263     constVREndOfBB();
   1264     beforeCall("exception"); //dump GG, GL VRs
   1265     compare_imm_VR(OpndSize_32, 0, vD);
   1266 
   1267     conditional_jump_global_API(Condition_E, "common_errNullObject", false);
   1268     /* for trace-based JIT, callee is already resolved */
   1269     int mIndex = IMMC/4;
   1270     const Method *calleeMethod = currentMethod->clazz->super->vtable[mIndex];
   1271     move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
   1272     if(hasRange) {
   1273         common_invokeMethodRange(convertCalleeToType(calleeMethod));
   1274     }
   1275     else {
   1276         common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
   1277     }
   1278     return 0;
   1279 }
   1280 #undef P_GPR_1
   1281 #undef P_GPR_2
   1282 #undef P_SCRATCH_1
   1283 //! lower bytecode INVOKE_SUPER_QUICK by calling common_invoke_super_quick
   1284 
   1285 //!
   1286 int op_invoke_super_quick() {
   1287     u2 vD = FETCH(2) & 0xf;
   1288     u2 IMMC = 4*FETCH(1);
   1289     int retval = common_invoke_super_quick(false, vD, IMMC);
   1290 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
   1291     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
   1292 #endif
   1293     rPC += 3;
   1294     return retval;
   1295 }
   1296 //! lower bytecode INVOKE_SUPER_QUICK_RANGE by calling common_invoke_super_quick
   1297 
   1298 //!
   1299 int op_invoke_super_quick_range() {
   1300     u2 vD = FETCH(2);
   1301     u2 IMMC = 4*FETCH(1);
   1302     int retval = common_invoke_super_quick(true, vD, IMMC);
   1303 #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
   1304     insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
   1305 #endif
   1306     rPC += 3;
   1307     return retval;
   1308 }
   1309 /////// code to predict the callee method for invoke_virtual & invoke_interface
   1310 #define offChainingCell_clazz 8
   1311 #define offChainingCell_method 12
   1312 #define offChainingCell_counter 16
   1313 #define P_GPR_1 PhysicalReg_EBX
   1314 #define P_GPR_2 PhysicalReg_EAX
   1315 #define P_GPR_3 PhysicalReg_ESI
   1316 #define P_SCRATCH_2 PhysicalReg_EDX
   1317 /* TODO gingerbread: implemented for O1, but not for O0:
   1318    valid input to JitToPatch & use icRechainCount */
   1319 /* update predicted method for invoke interface */
   1320 // 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
   1321 void predicted_chain_interface_O0(u2 tmp) {
   1322     ALOGI("TODO chain_interface_O0");
   1323 
   1324     /* set up arguments to dvmFindInterfaceMethodInCache */
   1325     load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1326     move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
   1327     move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
   1328     move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
   1329     move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
   1330     scratchRegs[0] = PhysicalReg_EDX;
   1331     call_dvmFindInterfaceMethodInCache();
   1332     load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1333 
   1334     /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
   1335        otherwise, jump to .find_interface_done */
   1336     compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
   1337     conditional_jump(Condition_NE, ".find_interface_done", true);
   1338     scratchRegs[0] = PhysicalReg_EAX;
   1339     jumpToExceptionThrown(1/*exception number*/);
   1340 
   1341     /* the interface method is found */
   1342     insertLabel(".find_interface_done", true);
   1343     /* reduce counter in chaining cell by 1 */
   1344     move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_SCRATCH_2, true); //counter
   1345     alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_SCRATCH_2, true);
   1346     move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offChainingCell_counter, P_GPR_1, true);
   1347 
   1348     /* if counter is still greater than zero, skip prediction
   1349        if it is zero, update predicted method */
   1350     compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
   1351     conditional_jump(Condition_G, ".skipPrediction", true);
   1352 
   1353     /* call dvmJitToPatchPredictedChain to update predicted method */
   1354     //%ecx has callee method for virtual, %eax has callee for interface
   1355     /* set up arguments for dvmJitToPatchPredictedChain */
   1356     load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1357     move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
   1358     insertChainingWorklist(traceCurrentBB->taken->id, stream);
   1359     move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
   1360     move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
   1361     scratchRegs[0] = PhysicalReg_EAX;
   1362     call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
   1363     load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1364     insertLabel(".skipPrediction", true);
   1365     move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
   1366 }
   1367 
   1368 // 2 inputs: ChainingCell in temp 41, current class object in temp 40
   1369 void predicted_chain_interface_O1(u2 tmp) {
   1370 
   1371     /* set up arguments to dvmFindInterfaceMethodInCache */
   1372     load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1373     move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
   1374     move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
   1375     move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
   1376     move_reg_to_mem(OpndSize_32, 40, false, 0, PhysicalReg_ESP, true);
   1377     scratchRegs[0] = PhysicalReg_SCRATCH_10;
   1378     call_dvmFindInterfaceMethodInCache();
   1379     load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1380 
   1381     /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
   1382        otherwise, jump to .find_interface_done */
   1383     compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
   1384     conditional_jump(Condition_NE, ".find_interface_done", true);
   1385     rememberState(3);
   1386     scratchRegs[0] = PhysicalReg_SCRATCH_9;
   1387     jumpToExceptionThrown(1/*exception number*/);
   1388 
   1389     goToState(3);
   1390     /* the interface method is found */
   1391     insertLabel(".find_interface_done", true);
   1392 #if 1 //
   1393     /* for gingerbread, counter is stored in glue structure
   1394        if clazz is not initialized, set icRechainCount to 0, otherwise, reduce it by 1 */
   1395     /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
   1396     move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 45, false);
   1397     move_imm_to_reg(OpndSize_32, 0, 43, false);
   1398     get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
   1399     move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
   1400     move_reg_to_reg(OpndSize_32, 33, false, 44, false);
   1401     alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
   1402     /* sub_opc will update control flags, so compare_imm_reg must happen after */
   1403     compare_imm_reg(OpndSize_32, 0, 45, false);
   1404     conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
   1405     conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
   1406     move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
   1407 #else
   1408     /* reduce counter in chaining cell by 1 */
   1409     move_mem_to_reg(OpndSize_32, offChainingCell_counter, 41, false, 33, false); //counter
   1410     alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
   1411     move_reg_to_mem(OpndSize_32, 33, false, offChainingCell_counter, 41, false);
   1412 #endif
   1413 
   1414     /* if counter is still greater than zero, skip prediction
   1415        if it is zero, update predicted method */
   1416     compare_imm_reg(OpndSize_32, 0, 43, false);
   1417     conditional_jump(Condition_G, ".skipPrediction", true);
   1418 
   1419     rememberState(4);
   1420     /* call dvmJitToPatchPredictedChain to update predicted method */
   1421     //%ecx has callee method for virtual, %eax has callee for interface
   1422     /* set up arguments for dvmJitToPatchPredictedChain */
   1423     load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1424     move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
   1425     move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
   1426     insertChainingWorklist(traceCurrentBB->taken->id, stream);
   1427     move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
   1428     move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
   1429     scratchRegs[0] = PhysicalReg_SCRATCH_8;
   1430     call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
   1431     load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1432     transferToState(4);
   1433 
   1434     insertLabel(".skipPrediction", true);
   1435     move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
   1436 }
   1437 
   1438 /* update predicted method for invoke virtual */
   1439 // 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
   1440 void predicted_chain_virtual_O0(u2 IMMC) {
   1441     ALOGI("TODO chain_virtual_O0");
   1442 
   1443     /* reduce counter in chaining cell by 1 */
   1444     move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_GPR_2, true); //counter
   1445     move_mem_to_reg(OpndSize_32, offClassObject_vtable, P_GPR_3, true, P_SCRATCH_2, true);
   1446     alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_GPR_2, true);
   1447     move_mem_to_reg(OpndSize_32, IMMC, P_SCRATCH_2, true, PhysicalReg_ECX, true);
   1448     move_reg_to_mem(OpndSize_32, P_GPR_2, true, offChainingCell_counter, P_GPR_1, true);
   1449 
   1450     /* if counter is still greater than zero, skip prediction
   1451        if it is zero, update predicted method */
   1452     compare_imm_reg(OpndSize_32, 0, P_GPR_2, true);
   1453     conditional_jump(Condition_G, ".skipPrediction", true);
   1454 
   1455     /* call dvmJitToPatchPredictedChain to update predicted method */
   1456     //%ecx has callee method for virtual, %eax has callee for interface
   1457     /* set up arguments for dvmJitToPatchPredictedChain */
   1458     load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1459     move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0,  PhysicalReg_ESP, true);
   1460     insertChainingWorklist(traceCurrentBB->taken->id, stream);
   1461     move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
   1462     move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
   1463     scratchRegs[0] = PhysicalReg_EAX;
   1464     call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
   1465     load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1466 
   1467     //callee method in %ecx for invoke virtual
   1468     move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
   1469     insertLabel(".skipPrediction", true);
   1470 }
   1471 
   1472 // 2 inputs: ChainingCell in temp 41, current class object in temp 40
   1473 // extra input: predicted clazz in temp 32
   1474 void predicted_chain_virtual_O1(u2 IMMC) {
   1475 
   1476     /* reduce counter in chaining cell by 1 */
   1477     /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
   1478     get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
   1479     move_imm_to_reg(OpndSize_32, 0, 43, false);
   1480     move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
   1481     move_mem_to_reg(OpndSize_32, offClassObject_vtable, 40, false, 34, false);
   1482     move_reg_to_reg(OpndSize_32, 33, false, 44, false);
   1483     alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
   1484     compare_imm_reg(OpndSize_32, 0, 32, false); // after sub_opc
   1485     move_mem_to_reg(OpndSize_32, IMMC, 34, false, PhysicalReg_ECX, true);
   1486     conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
   1487     conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
   1488     move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
   1489 
   1490     /* if counter is still greater than zero, skip prediction
   1491        if it is zero, update predicted method */
   1492     compare_imm_reg(OpndSize_32, 0, 43, false);
   1493     conditional_jump(Condition_G, ".skipPrediction", true);
   1494 
   1495     rememberState(2);
   1496     /* call dvmJitToPatchPredictedChain to update predicted method */
   1497     //%ecx has callee method for virtual, %eax has callee for interface
   1498     /* set up arguments for dvmJitToPatchPredictedChain */
   1499     load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1500     move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0, PhysicalReg_ESP, true);
   1501     move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
   1502     if(traceCurrentBB->taken)
   1503         insertChainingWorklist(traceCurrentBB->taken->id, stream);
   1504     int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
   1505     move_chain_to_mem(OpndSize_32, traceTakenId, 8, PhysicalReg_ESP, true); //predictedChainCell
   1506     move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
   1507     scratchRegs[0] = PhysicalReg_SCRATCH_10;
   1508     call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
   1509     load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1510 
   1511     //callee method in %ecx for invoke virtual
   1512     move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
   1513     transferToState(2);
   1514 
   1515     insertLabel(".skipPrediction", true);
   1516 }
   1517 
   1518 static int invokeChain_inst = 0;
   1519 /* object "this" is in %ebx */
   1520 void gen_predicted_chain_O0(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
   1521     ALOGI("TODO predicted_chain_O0");
   1522 
   1523     /* get current class object */
   1524     move_mem_to_reg(OpndSize_32, offObject_clazz, PhysicalReg_EBX, true,
   1525              P_GPR_3, true);
   1526 #ifdef DEBUG_CALL_STACK3
   1527     scratchRegs[0] = PhysicalReg_EAX;
   1528     call_debug_dumpSwitch(); //%ebx, %eax, %edx
   1529     move_imm_to_reg(OpndSize_32, 0xdd11, PhysicalReg_EBX, true);
   1530     call_debug_dumpSwitch();
   1531 #endif
   1532 
   1533     /* get predicted clazz
   1534        get predicted method
   1535     */
   1536     insertChainingWorklist(traceCurrentBB->taken->id, stream);
   1537     move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, P_GPR_1, true); //predictedChainCell
   1538     move_mem_to_reg(OpndSize_32, offChainingCell_clazz, P_GPR_1, true, P_SCRATCH_2, true);//predicted clazz
   1539     move_mem_to_reg(OpndSize_32, offChainingCell_method, P_GPR_1, true, PhysicalReg_ECX, true);//predicted method
   1540 
   1541 #ifdef DEBUG_CALL_STACK3
   1542     load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1543     move_reg_to_mem(OpndSize_32, P_GPR_1, true, 8, PhysicalReg_ESP, true);
   1544     move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, 4, PhysicalReg_ESP, true);
   1545     move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
   1546 
   1547     move_reg_to_reg(OpndSize_32, P_SCRATCH_2, true, PhysicalReg_EBX, true);
   1548     call_debug_dumpSwitch();
   1549     move_imm_to_reg(OpndSize_32, 0xdd22, PhysicalReg_EBX, true);
   1550     scratchRegs[0] = PhysicalReg_EAX;
   1551     call_debug_dumpSwitch(); //%ebx, %eax, %edx
   1552     move_reg_to_reg(OpndSize_32, P_GPR_3, true, PhysicalReg_EBX, true);
   1553     call_debug_dumpSwitch();
   1554     move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
   1555     call_debug_dumpSwitch();
   1556 
   1557     move_mem_to_reg(OpndSize_32, 8, PhysicalReg_ESP, true, P_GPR_1, true);
   1558     move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, P_SCRATCH_2, true);
   1559     move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, P_GPR_3, true);
   1560     load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
   1561 #endif
   1562 
   1563     /* compare current class object against predicted clazz
   1564        if equal, prediction is still valid, jump to .invokeChain */
   1565     //live registers: P_GPR_1, P_GPR_3, P_SCRATCH_2
   1566     compare_reg_reg(P_GPR_3, true, P_SCRATCH_2, true);
   1567     conditional_jump(Condition_E, ".invokeChain", true);
   1568     invokeChain_inst++;
   1569 
   1570     //get callee method and update predicted method if necessary
   1571     if(isInterface) {
   1572         predicted_chain_interface_O0(tmp);
   1573     } else {
   1574         predicted_chain_virtual_O0(IMMC);
   1575     }
   1576 
   1577 #ifdef DEBUG_CALL_STACK3
   1578     move_imm_to_reg(OpndSize_32, 0xeeee, PhysicalReg_EBX, true);
   1579     scratchRegs[0] = PhysicalReg_EAX;
   1580     call_debug_dumpSwitch(); //%ebx, %eax, %edx
   1581     insertChainingWorklist(traceCurrentBB->taken->id, stream);
   1582     move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
   1583     call_debug_dumpSwitch();
   1584 #endif
   1585 
   1586     if(isRange) {
   1587         common_invokeMethodRange(ArgsDone_Full);
   1588     }
   1589     else {
   1590         common_invokeMethodNoRange(ArgsDone_Full);
   1591     }
   1592 
   1593     insertLabel(".invokeChain", true);
   1594 #ifdef DEBUG_CALL_STACK3
   1595     move_imm_to_reg(OpndSize_32, 0xdddd, PhysicalReg_EBX, true);
   1596     scratchRegs[0] = PhysicalReg_EAX;
   1597     call_debug_dumpSwitch(); //%ebx, %eax, %edx
   1598     insertChainingWorklist(traceCurrentBB->taken->id, stream);
   1599     move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
   1600     call_debug_dumpSwitch();
   1601     move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
   1602     call_debug_dumpSwitch();
   1603 #endif
   1604 
   1605     if(isRange) {
   1606         common_invokeMethodRange(ArgsDone_Normal);
   1607     }
   1608     else {
   1609         common_invokeMethodNoRange(ArgsDone_Normal);
   1610     }
   1611 }
   1612 
   1613 /* object "this" is in inputReg: 5 for virtual, 1 for interface, 1 for virtual_quick */
   1614 void gen_predicted_chain_O1(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
   1615 
   1616     /* get current class object */
   1617     move_mem_to_reg(OpndSize_32, offObject_clazz, inputReg, false,
   1618              40, false);
   1619 
   1620     /* get predicted clazz
   1621        get predicted method
   1622     */
   1623     if(traceCurrentBB->taken)
   1624         insertChainingWorklist(traceCurrentBB->taken->id, stream);
   1625     int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
   1626     move_chain_to_reg(OpndSize_32, traceTakenId, 41, false); //predictedChainCell
   1627     move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 32, false);//predicted clazz
   1628     move_mem_to_reg(OpndSize_32, offChainingCell_method, 41, false, PhysicalReg_ECX, true);//predicted method
   1629 
   1630     /* update stack with parameters first, then decide the callee */
   1631     if(isRange) common_invokeMethodRange_noJmp();
   1632     else common_invokeMethodNoRange_noJmp();
   1633 
   1634     /* compare current class object against predicted clazz
   1635        if equal, prediction is still valid, jump to .invokeChain */
   1636     compare_reg_reg(40, false, 32, false);
   1637     conditional_jump(Condition_E, ".invokeChain", true);
   1638     rememberState(1);
   1639     invokeChain_inst++;
   1640 
   1641     //get callee method and update predicted method if necessary
   1642     if(isInterface) {
   1643         predicted_chain_interface_O1(tmp);
   1644     } else {
   1645         predicted_chain_virtual_O1(IMMC);
   1646     }
   1647 
   1648     common_invokeMethod_Jmp(ArgsDone_Full); //will touch %ecx
   1649 
   1650     insertLabel(".invokeChain", true);
   1651     goToState(1);
   1652     common_invokeMethod_Jmp(ArgsDone_Normal);
   1653 }
   1654 
   1655 void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
   1656     return gen_predicted_chain_O1(isRange, tmp, IMMC, isInterface, inputReg);
   1657 }
   1658 #undef P_GPR_1
   1659 #undef P_GPR_2
   1660 #undef P_GPR_3
   1661 #undef P_SCRATCH_2
   1662