/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /*! \file LowerInvoke.cpp \brief This file lowers the following bytecodes: INVOKE_XXX */ #include "libdex/DexOpcodes.h" #include "libdex/DexFile.h" #include "mterp/Mterp.h" #include "Lower.h" #include "NcgAot.h" #include "enc_wrapper.h" char* streamMisPred = NULL; /* according to callee, decide the ArgsDoneType*/ ArgsDoneType convertCalleeToType(const Method* calleeMethod) { if(calleeMethod == NULL) return ArgsDone_Full; if(dvmIsNativeMethod(calleeMethod)) return ArgsDone_Native; return ArgsDone_Normal; } int common_invokeMethodRange(ArgsDoneType); int common_invokeMethodNoRange(ArgsDoneType); void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg); //inputs to common_invokeMethodRange: %ecx // common_errNoSuchMethod: %edx #define P_GPR_1 PhysicalReg_ESI #define P_GPR_2 PhysicalReg_EBX #define P_GPR_3 PhysicalReg_ECX #define P_SCRATCH_1 PhysicalReg_EDX #define PP_GPR_1 PhysicalReg_EBX #define PP_GPR_2 PhysicalReg_ESI #define PP_GPR_3 PhysicalReg_EAX #define PP_GPR_4 PhysicalReg_EDX #ifdef WITH_JIT_INLINING /* * The function here takes care the * branch over if prediction is correct and the misprediction target for misPredBranchOver. */ static void genLandingPadForMispredictedCallee(MIR* mir) { BasicBlock *fallThrough = traceCurrentBB->fallThrough; /* Bypass the move-result block if there is one */ if (fallThrough->firstMIRInsn) { assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED); fallThrough = fallThrough->fallThrough; } /* Generate a branch over if the predicted inlining is correct */ jumpToBasicBlock(stream, fallThrough->id); /* Hook up the target to the verification branch */ int relativeNCG = stream - streamMisPred; unsigned instSize = encoder_get_inst_size(streamMisPred); relativeNCG -= instSize; //size of the instruction updateJumpInst(streamMisPred, OpndSize_8, relativeNCG); } #endif //! LOWER bytecode INVOKE_VIRTUAL without usage of helper function //! int common_invoke_virtual_nohelper(bool isRange, u2 tmp, u2 vD) { #ifdef WITH_JIT_INLINING /* * If the invoke has non-null misPredBranchOver, we need to generate * the non-inlined version of the invoke here to handle the * mispredicted case. */ if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) { genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList); } #endif scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null; export_pc(); constVREndOfBB(); beforeCall("exception"); //dump GG, GL VRs get_virtual_reg(vD, OpndSize_32, 5, false); simpleNullCheck(5, false, vD); #ifndef PREDICTED_CHAINING move_mem_to_reg(OpndSize_32, offObject_clazz, 5, false, 6, false); //clazz of "this" move_mem_to_reg(OpndSize_32, offClassObject_vtable, 6, false, 7, false); //vtable /* method is already resolved in trace-based JIT */ int methodIndex = currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex; move_mem_to_reg(OpndSize_32, methodIndex*4, 7, false, PhysicalReg_ECX, true); if(isRange) { common_invokeMethodRange(ArgsDone_Full); } else { common_invokeMethodNoRange(ArgsDone_Full); } #else int methodIndex = currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex; gen_predicted_chain(isRange, tmp, methodIndex*4, false, 5/*tmp5*/); #endif /////////////////////////////////// return 0; } //! wrapper to call either common_invoke_virtual_helper or common_invoke_virtual_nohelper //! int common_invoke_virtual(bool isRange, u2 tmp, u2 vD) { return common_invoke_virtual_nohelper(isRange, tmp, vD); } #undef P_GPR_1 #undef P_GPR_2 #undef P_GPR_3 #undef P_SCRATCH_1 #undef PP_GPR_1 #undef PP_GPR_2 #undef PP_GPR_3 #undef PP_GPR_4 #define P_GPR_1 PhysicalReg_ESI #define P_GPR_2 PhysicalReg_EBX #define P_GPR_3 PhysicalReg_EDX #define PP_GPR_1 PhysicalReg_EBX #define PP_GPR_2 PhysicalReg_ESI #define PP_GPR_3 PhysicalReg_EAX #define PP_GPR_4 PhysicalReg_EDX //! common section to lower INVOKE_SUPER //! It will use helper function if the switch is on int common_invoke_super(bool isRange, u2 tmp) { export_pc(); constVREndOfBB(); beforeCall("exception"); //dump GG, GL VRs /////////////////////// scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null; /* method is already resolved in trace-based JIT */ int mIndex = currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex; const Method *calleeMethod = currentMethod->clazz->super->vtable[mIndex]; move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true); if(isRange) { common_invokeMethodRange(convertCalleeToType(calleeMethod)); } else { common_invokeMethodNoRange(convertCalleeToType(calleeMethod)); } /////////////////////////////// return 0; } #undef PP_GPR_1 #undef PP_GPR_2 #undef PP_GPR_3 #undef PP_GPR_4 //! helper function to handle no such method error //! int invoke_super_nsm() { insertLabel(".invoke_super_nsm", false); //NOTE: it seems that the name in %edx is not used in common_errNoSuchMethod move_mem_to_reg(OpndSize_32, offMethod_name, PhysicalReg_EAX, true, PhysicalReg_EDX, true); //method name unconditional_jump("common_errNoSuchMethod", false); return 0; } #undef P_GPR_1 #undef P_GPR_2 #undef P_GPR_3 #define P_GPR_1 PhysicalReg_EBX #define P_GPR_2 PhysicalReg_ESI #define P_GPR_3 PhysicalReg_ECX #define PP_GPR_1 PhysicalReg_EBX #define PP_GPR_2 PhysicalReg_ESI #define PP_GPR_3 PhysicalReg_EAX #define PP_GPR_4 PhysicalReg_EDX //! common section to lower INVOKE_DIRECT //! It will use helper function if the switch is on int common_invoke_direct(bool isRange, u2 tmp, u2 vD) { //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method export_pc(); constVREndOfBB(); beforeCall("exception"); //dump GG, GL VRs //////////////////////////////////// get_virtual_reg(vD, OpndSize_32, 5, false); simpleNullCheck(5, false, vD); /* method is already resolved in trace-based JIT */ const Method *calleeMethod = currentMethod->clazz->pDvmDex->pResMethods[tmp]; move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true); //%ecx passed to common_invokeMethod... if(isRange) { common_invokeMethodRange(convertCalleeToType(calleeMethod)); } else { common_invokeMethodNoRange(convertCalleeToType(calleeMethod)); } //////////////////////////// return 0; } #undef P_GPR_1 #undef P_GPR_2 #undef P_GPR_3 #undef PP_GPR_1 #undef PP_GPR_2 #undef PP_GPR_3 #undef PP_GPR_4 #define P_GPR_1 PhysicalReg_EBX #define P_GPR_3 PhysicalReg_ECX #define PP_GPR_1 PhysicalReg_EBX #define PP_GPR_2 PhysicalReg_ESI #define PP_GPR_3 PhysicalReg_EAX #define PP_GPR_4 PhysicalReg_EDX //! common section to lower INVOKE_STATIC //! It will use helper function if the switch is on int common_invoke_static(bool isRange, u2 tmp) { //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method export_pc(); constVREndOfBB(); beforeCall("exception"); //dump GG, GL VRs //////////////////////////// /* method is already resolved in trace-based JIT */ const Method *calleeMethod = currentMethod->clazz->pDvmDex->pResMethods[tmp]; move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true); //%ecx passed to common_invokeMethod... if(isRange) { common_invokeMethodRange(convertCalleeToType(calleeMethod)); } else { common_invokeMethodNoRange(convertCalleeToType(calleeMethod)); } //////////////////////// return 0; } #undef P_GPR_1 #undef PP_GPR_1 #undef PP_GPR_2 #undef PP_GPR_3 #undef PP_GPR_4 #define P_GPR_1 PhysicalReg_EBX #define P_GPR_2 PhysicalReg_EAX //scratch #define P_GPR_3 PhysicalReg_ECX #define P_SCRATCH_1 PhysicalReg_ESI //clazz of object #define PP_GPR_1 PhysicalReg_EBX #define PP_GPR_2 PhysicalReg_ESI #define PP_GPR_3 PhysicalReg_EAX #define PP_GPR_4 PhysicalReg_EDX //! common section to lower INVOKE_INTERFACE //! It will use helper function if the switch is on int common_invoke_interface(bool isRange, u2 tmp, u2 vD) { #ifdef WITH_JIT_INLINING /* * If the invoke has non-null misPredBranchOver, we need to generate * the non-inlined version of the invoke here to handle the * mispredicted case. */ if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) { genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList); } #endif export_pc(); //use %edx constVREndOfBB(); beforeCall("exception"); //dump GG, GL VRs /////////////////////// scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null; get_virtual_reg(vD, OpndSize_32, 1, false); simpleNullCheck(1, false, vD); #ifndef PREDICTED_CHAINING load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true); /* for trace-based JIT, pDvmDex is a constant at JIT time 4th argument to dvmFindInterfaceMethodInCache at -4(%esp) */ move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true); move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 5, false); /* for trace-based JIT, method is a constant at JIT time 3rd argument to dvmFindInterfaceMethodInCache at 8(%esp) */ move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, 5, false, 0, PhysicalReg_ESP, true); scratchRegs[0] = PhysicalReg_SCRATCH_3; scratchRegs[1] = PhysicalReg_Null; call_dvmFindInterfaceMethodInCache(); load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); conditional_jump_global_API(Condition_E, "common_exceptionThrown", false); move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true); if(isRange) { common_invokeMethodRange(ArgsDone_Full); } else { common_invokeMethodNoRange(ArgsDone_Full); } #else gen_predicted_chain(isRange, tmp, -1, true /*interface*/, 1/*tmp1*/); #endif /////////////////////// return 0; } #undef PP_GPR_1 #undef PP_GPR_2 #undef PP_GPR_3 #undef PP_GPR_4 #undef P_GPR_1 #undef P_GPR_2 #undef P_GPR_3 #undef P_SCRATCH_1 //! lower bytecode INVOKE_VIRTUAL by calling common_invoke_virtual //! int op_invoke_virtual() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif //B|A|op CCCC G|F|E|D //D: the first argument, which is the "this" pointer //B: argument count //D,E,F,G,A: arguments u2 vD = FETCH(2) & 0xf; u2 tmp = FETCH(1); //method index int retval = common_invoke_virtual(false/*not range*/, tmp, vD); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_SUPER by calling common_invoke_super //! int op_invoke_super() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif //B|A|op CCCC G|F|E|D //D: the first argument //B: argument count //D,E,F,G,A: arguments u2 tmp = FETCH(1); //method index int retval = common_invoke_super(false/*not range*/, tmp); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_DIRECT by calling common_invoke_direct //! int op_invoke_direct() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif //B|A|op CCCC G|F|E|D //D: the first argument, which is the "this" pointer //B: argument count //D,E,F,G,A: arguments u2 vD = FETCH(2) & 0xf; u2 tmp = FETCH(1); //method index int retval = common_invoke_direct(false/*not range*/, tmp, vD); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_STATIC by calling common_invoke_static //! int op_invoke_static() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif //B|A|op CCCC G|F|E|D //D: the first argument //B: argument count //D,E,F,G,A: arguments u2 tmp = FETCH(1); //method index int retval = common_invoke_static(false/*not range*/, tmp); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_INTERFACE by calling common_invoke_interface //! int op_invoke_interface() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif //B|A|op CCCC G|F|E|D //D: the first argument, which is the "this" pointer //B: argument count //D,E,F,G,A: arguments u2 vD = FETCH(2) & 0xf; u2 tmp = FETCH(1); //method index int retval = common_invoke_interface(false/*not range*/, tmp, vD); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_VIRTUAL_RANGE by calling common_invoke_virtual //! int op_invoke_virtual_range() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif //AA|op BBBB CCCC //CCCC: the first argument, which is the "this" pointer //AA: argument count u2 tmp = FETCH(1); //BBBB, method index u2 vD = FETCH(2); //the first argument int retval = common_invoke_virtual(true/*range*/, tmp, vD); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_SUPER_RANGE by calling common_invoke_super //! int op_invoke_super_range() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif u2 tmp = FETCH(1); //BBBB, method index int retval = common_invoke_super(true/*range*/, tmp); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_DIRECT_RANGE by calling common_invoke_direct //! int op_invoke_direct_range() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif u2 tmp = FETCH(1); //BBBB, method index u2 vD = FETCH(2); //the first argument int retval = common_invoke_direct(true/*range*/, tmp, vD); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_STATIC_RANGE by calling common_invoke_static //! int op_invoke_static_range() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif u2 tmp = FETCH(1); //BBBB, method index int retval = common_invoke_static(true/*range*/, tmp); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_INTERFACE_RANGE by calling common_invoke_interface //! int op_invoke_interface_range() { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; #endif u2 tmp = FETCH(1); //BBBB, method index u2 vD = FETCH(2); //the first argument int retval = common_invoke_interface(true/*range*/, tmp, vD); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //used %ecx, %edi, %esp %ebp #define P_GPR_1 PhysicalReg_EBX #define P_SCRATCH_1 PhysicalReg_ESI #define P_SCRATCH_2 PhysicalReg_EAX #define P_SCRATCH_3 PhysicalReg_EDX #define P_SCRATCH_4 PhysicalReg_ESI #define P_SCRATCH_5 PhysicalReg_EAX //! pass the arguments for invoking method without range //! int common_invokeMethodNoRange_noJmp() { u2 count = INST_B(inst); u2 vD = FETCH(2) & 0xf; u2 vE = (FETCH(2) >> 4) & 0xf; u2 vF = (FETCH(2) >> 8) & 0xf; u2 vG = (FETCH(2) >> 12) & 0xf; u2 vA = INST_A(inst); //5th argument int offsetFromSaveArea = -4; if(count == 5) { get_virtual_reg(vA, OpndSize_32, 22, false); move_reg_to_mem(OpndSize_32, 22, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true); offsetFromSaveArea -= 4; } if(count >= 4) { get_virtual_reg(vG, OpndSize_32, 23, false); move_reg_to_mem(OpndSize_32, 23, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true); offsetFromSaveArea -= 4; } if(count >= 3) { get_virtual_reg(vF, OpndSize_32, 24, false); move_reg_to_mem(OpndSize_32, 24, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true); offsetFromSaveArea -= 4; } if(count >= 2) { get_virtual_reg(vE, OpndSize_32, 25, false); move_reg_to_mem(OpndSize_32, 25, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true); offsetFromSaveArea -= 4; } if(count >= 1) { get_virtual_reg(vD, OpndSize_32, 26, false); move_reg_to_mem(OpndSize_32, 26, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true); } return 0; } int common_invokeMethod_Jmp(ArgsDoneType form) { nextVersionOfHardReg(PhysicalReg_EDX, 1); move_imm_to_reg(OpndSize_32, (int)rPC, PhysicalReg_EDX, true); //arguments needed in ArgsDone: // start of HotChainingCell for next bytecode: -4(%esp) // start of InvokeSingletonChainingCell for callee: -8(%esp) load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true); insertChainingWorklist(traceCurrentBB->fallThrough->id, stream); move_chain_to_mem(OpndSize_32, traceCurrentBB->fallThrough->id, 4, PhysicalReg_ESP, true); // for honeycomb: JNI call doesn't need a chaining cell, so the taken branch is null if(traceCurrentBB->taken) insertChainingWorklist(traceCurrentBB->taken->id, stream); int takenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0; move_chain_to_mem(OpndSize_32, takenId, 0, PhysicalReg_ESP, true); if(form == ArgsDone_Full) unconditional_jump_global_API(".invokeArgsDone_jit", false); else if(form == ArgsDone_Native) unconditional_jump_global_API(".invokeArgsDone_native", false); else unconditional_jump_global_API(".invokeArgsDone_normal", false); return 0; } int common_invokeMethodNoRange(ArgsDoneType form) { common_invokeMethodNoRange_noJmp(); common_invokeMethod_Jmp(form); return 0; } #undef P_GPR_1 #undef P_SCRATCH_1 #undef P_SCRATCH_2 #undef P_SCRATCH_3 #undef P_SCRATCH_4 #undef P_SCRATCH_5 //input: %ecx (method to call) #define P_GPR_1 PhysicalReg_EBX #define P_GPR_2 PhysicalReg_ESI #define P_GPR_3 PhysicalReg_EDX //not used with P_SCRATCH_2 #define P_SCRATCH_1 PhysicalReg_EAX #define P_SCRATCH_2 PhysicalReg_EDX #define P_SCRATCH_3 PhysicalReg_EAX #define P_SCRATCH_4 PhysicalReg_EDX #define P_SCRATCH_5 PhysicalReg_EAX #define P_SCRATCH_6 PhysicalReg_EDX #define P_SCRATCH_7 PhysicalReg_EAX #define P_SCRATCH_8 PhysicalReg_EDX #define P_SCRATCH_9 PhysicalReg_EAX #define P_SCRATCH_10 PhysicalReg_EDX //! pass the arguments for invoking method with range //! loop is unrolled when count <= 10 int common_invokeMethodRange_noJmp() { u2 count = INST_AA(inst); u2 vD = FETCH(2); //the first argument savearea_from_fp(21, false); //vD to rFP-4*count-20 //vD+1 to rFP-4*count-20+4 = rFP-20-4*(count-1) if(count >= 1 && count <= 10) { get_virtual_reg(vD, OpndSize_32, 22, false); move_reg_to_mem(OpndSize_32, 22, false, -4*count, 21, false); } if(count >= 2 && count <= 10) { get_virtual_reg(vD+1, OpndSize_32, 23, false); move_reg_to_mem(OpndSize_32, 23, false, -4*(count-1), 21, false); } if(count >= 3 && count <= 10) { get_virtual_reg(vD+2, OpndSize_32, 24, false); move_reg_to_mem(OpndSize_32, 24, false, -4*(count-2), 21, false); } if(count >= 4 && count <= 10) { get_virtual_reg(vD+3, OpndSize_32, 25, false); move_reg_to_mem(OpndSize_32, 25, false, -4*(count-3), 21, false); } if(count >= 5 && count <= 10) { get_virtual_reg(vD+4, OpndSize_32, 26, false); move_reg_to_mem(OpndSize_32, 26, false, -4*(count-4), 21, false); } if(count >= 6 && count <= 10) { get_virtual_reg(vD+5, OpndSize_32, 27, false); move_reg_to_mem(OpndSize_32, 27, false, -4*(count-5), 21, false); } if(count >= 7 && count <= 10) { get_virtual_reg(vD+6, OpndSize_32, 28, false); move_reg_to_mem(OpndSize_32, 28, false, -4*(count-6), 21, false); } if(count >= 8 && count <= 10) { get_virtual_reg(vD+7, OpndSize_32, 29, false); move_reg_to_mem(OpndSize_32, 29, false, -4*(count-7), 21, false); } if(count >= 9 && count <= 10) { get_virtual_reg(vD+8, OpndSize_32, 30, false); move_reg_to_mem(OpndSize_32, 30, false, -4*(count-8), 21, false); } if(count == 10) { get_virtual_reg(vD+9, OpndSize_32, 31, false); move_reg_to_mem(OpndSize_32, 31, false, -4*(count-9), 21, false); } if(count > 10) { //dump to memory first, should we set physicalReg to Null? //this bytecodes uses a set of virtual registers (update getVirtualInfo) //this is necessary to correctly insert transfer points int k; for(k = 0; k < count; k++) { spillVirtualReg(vD+k, LowOpndRegType_gp, true); //will update refCount } load_effective_addr(4*vD, PhysicalReg_FP, true, 12, false); alu_binary_imm_reg(OpndSize_32, sub_opc, 4*count, 21, false); move_imm_to_reg(OpndSize_32, count, 13, false); insertLabel(".invokeMethod_1", true); //if checkDup: will perform work from ShortWorklist rememberState(1); move_mem_to_reg(OpndSize_32, 0, 12, false, 14, false); move_reg_to_mem(OpndSize_32, 14, false, 0, 21, false); load_effective_addr(4, 12, false, 12, false); alu_binary_imm_reg(OpndSize_32, sub_opc, 1, 13, false); load_effective_addr(4, 21, false, 21, false); transferToState(1); conditional_jump(Condition_NE, ".invokeMethod_1", true); //backward branch } return 0; } int common_invokeMethodRange(ArgsDoneType form) { common_invokeMethodRange_noJmp(); common_invokeMethod_Jmp(form); return 0; } #undef P_GPR_1 #undef P_GPR_2 #undef P_GPR_3 #undef P_SCRATCH_1 #undef P_SCRATCH_2 #undef P_SCRATCH_3 #undef P_SCRATCH_4 #undef P_SCRATCH_5 #undef P_SCRATCH_6 #undef P_SCRATCH_7 #undef P_SCRATCH_8 #undef P_SCRATCH_9 #undef P_SCRATCH_10 #define P_GPR_1 PhysicalReg_EBX #define P_GPR_3 PhysicalReg_ESI #define P_SCRATCH_1 PhysicalReg_EAX #define P_SCRATCH_2 PhysicalReg_EDX #define P_SCRATCH_3 PhysicalReg_EAX #define P_SCRATCH_4 PhysicalReg_EDX #define P_SCRATCH_5 PhysicalReg_EAX #define P_SCRATCH_6 PhysicalReg_EDX //! spill a register to native stack //! decrease %esp by 4, then store a register at 0(%esp) int spill_reg(int reg, bool isPhysical) { load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, reg, isPhysical, 0, PhysicalReg_ESP, true); return 0; } //! get a register from native stack //! load a register from 0(%esp), then increase %esp by 4 int unspill_reg(int reg, bool isPhysical) { move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, reg, isPhysical); load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true); return 0; } void generate_invokeNative(bool generateForNcg); //forward declaration void generate_stackOverflow(); //forward declaration //! common code to invoke a method after all arguments are handled //! //takes one argument to generate code // for invokeNativeSingle (form == ArgsDone_Native) // or invokeNonNativeSingle (form == ArgsDone_Normal) when WITH_JIT is true // to dynamically determine which one to choose (form == ArgsDone_Full) /* common_invokeArgsDone is called at NCG time and at execution time during relocation generate invokeArgsDone for NCG if isJitFull is false && form == Full */ int common_invokeArgsDone(ArgsDoneType form, bool isJitFull) { bool generateForNcg = false; if(form == ArgsDone_Full) { if(isJitFull) insertLabel(".invokeArgsDone_jit", false); else { insertLabel(".invokeArgsDone", false); generateForNcg = true; } } else if(form == ArgsDone_Normal) insertLabel(".invokeArgsDone_normal", false); else if(form == ArgsDone_Native) insertLabel(".invokeArgsDone_native", false); //%ecx: methodToCall movez_mem_to_reg(OpndSize_16, offMethod_registersSize, PhysicalReg_ECX, true, P_SCRATCH_1, true); //regSize scratchRegs[0] = PhysicalReg_EBX; scratchRegs[1] = PhysicalReg_ESI; scratchRegs[2] = PhysicalReg_EDX; scratchRegs[3] = PhysicalReg_Null; savearea_from_fp(P_GPR_3, true); alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_1, true); alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_1, true, P_GPR_3, true); //update newSaveArea->savedPc, here P_GPR_3 is new FP move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true); movez_mem_to_reg(OpndSize_16, offMethod_outsSize, PhysicalReg_ECX, true, P_SCRATCH_2, true); //outsSize move_reg_to_reg(OpndSize_32, P_GPR_3, true, P_GPR_1, true); //new FP alu_binary_imm_reg(OpndSize_32, sub_opc, sizeofStackSaveArea, P_GPR_3, true); alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_2, true); alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_2, true, P_GPR_3, true); get_self_pointer(P_SCRATCH_3, true); move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offStackSaveArea_prevFrame-sizeofStackSaveArea, P_GPR_1, true); //set stack->prevFrame compare_mem_reg(OpndSize_32, offsetof(Thread, interpStackEnd), P_SCRATCH_3, true, P_GPR_3, true); conditional_jump(Condition_L, ".stackOverflow", true); if(form == ArgsDone_Full) { test_imm_mem(OpndSize_32, ACC_NATIVE, offMethod_accessFlags, PhysicalReg_ECX, true); } move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offStackSaveArea_method-sizeofStackSaveArea, P_GPR_1, true); //set stack->method if(form == ArgsDone_Native || form == ArgsDone_Full) { /* to correctly handle code cache reset: update returnAddr and check returnAddr after done with the native method if returnAddr is set to NULL during code cache reset, the execution will correctly continue with interpreter */ //get returnAddr from 4(%esp) and update stack move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, PhysicalReg_EDX, true); move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_1, true); } if(form == ArgsDone_Native) { generate_invokeNative(generateForNcg); return 0; } if(form == ArgsDone_Full) { conditional_jump(Condition_NE, ".invokeNative", true); } move_mem_to_reg(OpndSize_32, offMethod_clazz, PhysicalReg_ECX, true, P_SCRATCH_4, true); //get method->claz move_mem_to_reg(OpndSize_32, offClassObject_pDvmDex, P_SCRATCH_4, true, P_SCRATCH_4, true); //get method->clazz->pDvmDex move_reg_to_reg(OpndSize_32, P_GPR_1, true, PhysicalReg_FP, true); //update rFP get_self_pointer(P_GPR_1, true); move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offsetof(Thread, interpSave.method), P_GPR_1, true); //glue->method move_reg_to_mem(OpndSize_32, P_SCRATCH_4, true, offsetof(Thread, interpSave.methodClassDex), P_GPR_1, true); //set_glue_dvmdex move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set glue->self->frame if(!generateForNcg) { /* returnAddr updated already for Full */ //get returnAddr from 4(%esp) and update stack if(form == ArgsDone_Normal) move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, PhysicalReg_EDX, true); //for JIT: starting bytecode in %ebx to invoke JitToInterp move_mem_to_reg(OpndSize_32, offMethod_insns, PhysicalReg_ECX, true, PhysicalReg_EBX, true); if(form == ArgsDone_Normal) move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offStackSaveArea_returnAddr-sizeofStackSaveArea, PhysicalReg_FP, true); } insertLabel(".invokeInterp", true); if(!generateForNcg) { bool callNoChain = false; #ifdef PREDICTED_CHAINING if(form == ArgsDone_Full) callNoChain = true; #endif if(callNoChain) { scratchRegs[0] = PhysicalReg_EAX; load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true); #if defined(WITH_JIT_TUNING) /* Predicted chaining failed. Fall back to interpreter and indicate * inline cache miss. */ move_imm_to_reg(OpndSize_32, kInlineCacheMiss, PhysicalReg_EDX, true); #endif call_dvmJitToInterpTraceSelectNoChain(); //input: rPC in %ebx } else { //jump to the stub at (%esp) move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EDX, true); load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true); unconditional_jump_reg(PhysicalReg_EDX, true); } } if(form == ArgsDone_Full) generate_invokeNative(generateForNcg); generate_stackOverflow(); return 0; } /* when WITH_JIT is true, JIT'ed code invokes native method, after invoke, execution will continue with the interpreter or with JIT'ed code if chained */ void generate_invokeNative(bool generateForNcg) { insertLabel(".invokeNative", true); //if(!generateForNcg) // load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true); load_effective_addr(-28, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, P_GPR_1, true, 20, PhysicalReg_ESP, true); scratchRegs[0] = PhysicalReg_EDX; get_self_pointer(P_SCRATCH_1, true); //glue->self move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 8, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 12, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 24, PhysicalReg_ESP, true); move_mem_to_reg(OpndSize_32, offThread_jniLocal_nextEntry, P_SCRATCH_1, true, P_SCRATCH_2, true); //get self->local_next scratchRegs[1] = PhysicalReg_EAX; move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_1, true); //update jniLocalRef of stack move_reg_to_mem(OpndSize_32, P_GPR_1, true, offThread_curFrame, P_SCRATCH_1, true); //set self->curFrame move_imm_to_mem(OpndSize_32, 0, offThread_inJitCodeCache, P_SCRATCH_1, true); //clear self->inJitCodeCache load_effective_addr(offsetof(Thread, interpSave.retval), P_SCRATCH_1, true, P_SCRATCH_3, true); //self->retval move_reg_to_mem(OpndSize_32, P_SCRATCH_3, true, 4, PhysicalReg_ESP, true); //NOTE: native method checks the interpreted stack for arguments // The immediate arguments on native stack: address of return value, new FP, self call_mem(40, PhysicalReg_ECX, true);//*40(%ecx) //we can't assume the argument stack is unmodified after the function call //duplicate newFP & glue->self on stack: newFP (-28 & -8) glue->self (-16 & -4) move_mem_to_reg(OpndSize_32, 20, PhysicalReg_ESP, true, P_GPR_3, true); //new FP move_mem_to_reg(OpndSize_32, 24, PhysicalReg_ESP, true, P_GPR_1, true); //glue->self load_effective_addr(28, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_mem_to_reg(OpndSize_32, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_1, true); //newSaveArea->jniLocal compare_imm_mem(OpndSize_32, 0, offThread_exception, P_GPR_1, true); //self->exception if(!generateForNcg) load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true); //NOTE: PhysicalReg_FP should be callee-saved register move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set self->curFrame move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, offThread_jniLocal_nextEntry, P_GPR_1, true); //set self->jniLocal conditional_jump(Condition_NE, "common_exceptionThrown", false); if(!generateForNcg) { //get returnAddr, if it is not NULL, // return to JIT'ed returnAddr after executing the native method /* to correctly handle code cache reset: update returnAddr and check returnAddr after done with the native method if returnAddr is set to NULL during code cache reset, the execution will correctly continue with interpreter */ move_mem_to_reg(OpndSize_32, offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_2, true); //set self->inJitCodeCache to returnAddr (P_GPR_1 is in %ebx) move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offThread_inJitCodeCache, P_GPR_1, true); move_mem_to_reg(OpndSize_32, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true, PhysicalReg_EBX, true); //savedPc compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true); conditional_jump(Condition_E, ".nativeToInterp", true); unconditional_jump_reg(P_SCRATCH_2, true); //if returnAddr is NULL, return to interpreter after executing the native method insertLabel(".nativeToInterp", true); //move rPC by 6 (3 bytecode units for INVOKE) alu_binary_imm_reg(OpndSize_32, add_opc, 6, PhysicalReg_EBX, true); scratchRegs[0] = PhysicalReg_EAX; #if defined(WITH_JIT_TUNING) /* Return address not in code cache. Indicate that continuing with interpreter */ move_imm_to_reg(OpndSize_32, kCallsiteInterpreted, PhysicalReg_EDX, true); #endif call_dvmJitToInterpTraceSelectNoChain(); //rPC in %ebx } return; } void generate_stackOverflow() { insertLabel(".stackOverflow", true); //load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 4, PhysicalReg_ESP, true); get_self_pointer(P_GPR_1, true); //glue->self move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true); call_dvmHandleStackOverflow(); load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true); unconditional_jump("common_exceptionThrown", false); } #undef P_GPR_1 #undef P_GPR_2 #undef P_GPR_3 #undef P_SCRATCH_1 #undef P_SCRATCH_2 #undef P_SCRATCH_3 #undef P_SCRATCH_4 #undef P_SCRATCH_5 #undef P_SCRATCH_6 ///////////////////////////////////////////// #define P_GPR_1 PhysicalReg_EBX #define P_GPR_2 PhysicalReg_ECX #define P_SCRATCH_1 PhysicalReg_ESI #define P_SCRATCH_2 PhysicalReg_EDX #define P_SCRATCH_3 PhysicalReg_ESI #define P_SCRATCH_4 PhysicalReg_EDX //! lower bytecode EXECUTE_INLINE //! int op_execute_inline(bool isRange) { //tmp, vC, vD, vE, vF int num; if(!isRange) num = INST_B(inst); else num = INST_AA(inst); u2 tmp = FETCH(1); u2 vC, vD, vE, vF; if(!isRange) { vC = FETCH(2) & 0xf; vD = (FETCH(2) >> 4) & 0xf; vE = (FETCH(2) >> 8) & 0xf; vF = FETCH(2) >> 12; } else { vC = FETCH(2); vD = vC + 1; vE = vC + 2; vF = vC + 3; } export_pc(); switch (tmp) { case INLINE_EMPTYINLINEMETHOD: return 0; /* Nop */ case INLINE_STRING_LENGTH: get_virtual_reg(vC, OpndSize_32, 1, false); compare_imm_reg(OpndSize_32, 0, 1, false); conditional_jump(Condition_NE, ".do_inlined_string_length", true); scratchRegs[0] = PhysicalReg_SCRATCH_1; jumpToExceptionThrown(1/*exception number*/); insertLabel(".do_inlined_string_length", true); move_mem_to_reg(OpndSize_32, 0x14, 1, false, 2, false); get_self_pointer(3, false); move_reg_to_mem(OpndSize_32, 2, false, offsetof(Thread, interpSave.retval), 3, false); return 0; case INLINE_STRING_IS_EMPTY: get_virtual_reg(vC, OpndSize_32, 1, false); compare_imm_reg(OpndSize_32, 0, 1, false); conditional_jump(Condition_NE, ".do_inlined_string_length", true); scratchRegs[0] = PhysicalReg_SCRATCH_1; jumpToExceptionThrown(1/*exception number*/); insertLabel(".do_inlined_string_length", true); compare_imm_mem(OpndSize_32, 0, 0x14, 1, false); conditional_jump(Condition_E, ".inlined_string_length_return_true", true); get_self_pointer(2, false); move_imm_to_mem(OpndSize_32, 0, offsetof(Thread, interpSave.retval), 2, false); unconditional_jump(".inlined_string_length_done", true); insertLabel(".inlined_string_length_return_true", true); get_self_pointer(2, false); move_imm_to_mem(OpndSize_32, 1, offsetof(Thread, interpSave.retval), 2, false); insertLabel(".inlined_string_length_done", true); return 0; case INLINE_MATH_ABS_INT: get_virtual_reg(vC, OpndSize_32, 1, false); move_reg_to_reg(OpndSize_32, 1, false, 2, false); alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 2, false); alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 1, false); alu_binary_reg_reg(OpndSize_32, sub_opc, 2, false, 1, false); get_self_pointer(3, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false); return 0; case INLINE_MATH_ABS_LONG: get_virtual_reg(vD, OpndSize_32, 1, false); move_reg_to_reg(OpndSize_32, 1, false, 2, false); alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 1, false); move_reg_to_reg(OpndSize_32, 1, false, 3, false); move_reg_to_reg(OpndSize_32, 1, false, 4, false); get_virtual_reg(vC, OpndSize_32, 5, false); alu_binary_reg_reg(OpndSize_32, xor_opc, 5, false, 1, false); get_self_pointer(6, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 6, false); alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 3, false); move_reg_to_mem(OpndSize_32, 3, false, 4 + offsetof(Thread, interpSave.retval), 6, false); alu_binary_reg_mem(OpndSize_32, sub_opc, 4, false, offsetof(Thread, interpSave.retval), 6, false); alu_binary_reg_mem(OpndSize_32, sbb_opc, 4, false, 4 + offsetof(Thread, interpSave.retval), 6, false); return 0; case INLINE_MATH_MAX_INT: get_virtual_reg(vC, OpndSize_32, 1, false); get_virtual_reg(vD, OpndSize_32, 2, false); compare_reg_reg(1, false, 2, false); conditional_move_reg_to_reg(OpndSize_32, Condition_GE, 2, false/*src*/, 1, false/*dst*/); get_self_pointer(3, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false); return 0; case INLINE_MATH_ABS_FLOAT: get_virtual_reg(vC, OpndSize_32, 1, false); alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 1, false); get_self_pointer(2, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false); return 0; case INLINE_MATH_ABS_DOUBLE: get_virtual_reg(vC, OpndSize_32, 1, false); get_virtual_reg(vD, OpndSize_32, 2, false); alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 2, false); get_self_pointer(3, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false); move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false); return 0; case INLINE_STRING_FASTINDEXOF_II: #if defined(USE_GLOBAL_STRING_DEFS) break; #else get_virtual_reg(vC, OpndSize_32, 1, false); compare_imm_reg(OpndSize_32, 0, 1, false); get_virtual_reg(vD, OpndSize_32, 2, false); get_virtual_reg(vE, OpndSize_32, 3, false); conditional_jump(Condition_NE, ".do_inlined_string_fastIndexof", true); scratchRegs[0] = PhysicalReg_SCRATCH_1; jumpToExceptionThrown(1/*exception number*/); insertLabel(".do_inlined_string_fastIndexof", true); move_mem_to_reg(OpndSize_32, 0x14, 1, false, 4, false); move_mem_to_reg(OpndSize_32, 0x8, 1, false, 5, false); move_mem_to_reg(OpndSize_32, 0x10, 1, false, 6, false); alu_binary_reg_reg(OpndSize_32, xor_opc, 1, false, 1, false); compare_imm_reg(OpndSize_32, 0, 3, false); conditional_move_reg_to_reg(OpndSize_32, Condition_NS, 3, false, 1, false); compare_reg_reg(4, false, 1, false); conditional_jump(Condition_GE, ".do_inlined_string_fastIndexof_exitfalse", true); dump_mem_scale_reg(Mnemonic_LEA, OpndSize_32, 5, false, 0xc/*disp*/, 6, false, 2, 5, false, LowOpndRegType_gp); movez_mem_disp_scale_to_reg(OpndSize_16, 5, false, 0, 1, false, 2, 3, false); compare_reg_reg(3, false, 2, false); conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit", true); load_effective_addr(0x1, 1, false, 3, false); load_effective_addr_scale(5, false, 3, false, 2, 5, false); unconditional_jump(".do_inlined_string_fastIndexof_iter", true); insertLabel(".do_inlined_string_fastIndexof_ch_cmp", true); if(gDvm.executionMode == kExecutionModeNcgO1) { rememberState(1); } movez_mem_to_reg(OpndSize_16, 0, 5, false, 6, false); load_effective_addr(0x2, 5, false, 5, false); compare_reg_reg(6, false, 2, false); conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit", true); load_effective_addr(0x1, 3, false, 3, false); insertLabel(".do_inlined_string_fastIndexof_iter", true); compare_reg_reg(4, false, 3, false); move_reg_to_reg(OpndSize_32, 3, false, 1, false); if(gDvm.executionMode == kExecutionModeNcgO1) { transferToState(1); } conditional_jump(Condition_NE, ".do_inlined_string_fastIndexof_ch_cmp", true); insertLabel(".do_inlined_string_fastIndexof_exitfalse", true); move_imm_to_reg(OpndSize_32, 0xffffffff, 1, false); insertLabel(".do_inlined_string_fastIndexof_exit", true); get_self_pointer(7, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 7, false); return 0; case INLINE_FLOAT_TO_RAW_INT_BITS: get_virtual_reg(vC, OpndSize_32, 1, false); get_self_pointer(2, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false); return 0; case INLINE_INT_BITS_TO_FLOAT: get_virtual_reg(vC, OpndSize_32, 1, false); get_self_pointer(2, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false); return 0; case INLINE_DOUBLE_TO_RAW_LONG_BITS: get_virtual_reg(vC, OpndSize_32, 1, false); get_self_pointer(3, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false); get_virtual_reg(vD, OpndSize_32, 2, false); move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false); return 0; case INLINE_LONG_BITS_TO_DOUBLE: get_virtual_reg(vC, OpndSize_32, 1, false); get_virtual_reg(vD, OpndSize_32, 2, false); get_self_pointer(3, false); move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false); move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false); return 0; default: break; } #endif get_self_pointer(PhysicalReg_SCRATCH_1, false); load_effective_addr(offsetof(Thread, interpSave.retval), PhysicalReg_SCRATCH_1, false, 1, false); load_effective_addr(-24, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, 1, false, 16, PhysicalReg_ESP, true); if(num >= 1) { get_virtual_reg(vC, OpndSize_32, 2, false); move_reg_to_mem(OpndSize_32, 2, false, 0, PhysicalReg_ESP, true); } if(num >= 2) { get_virtual_reg(vD, OpndSize_32, 3, false); move_reg_to_mem(OpndSize_32, 3, false, 4, PhysicalReg_ESP, true); } if(num >= 3) { get_virtual_reg(vE, OpndSize_32, 4, false); move_reg_to_mem(OpndSize_32, 4, false, 8, PhysicalReg_ESP, true); } if(num >= 4) { get_virtual_reg(vF, OpndSize_32, 5, false); move_reg_to_mem(OpndSize_32, 5, false, 12, PhysicalReg_ESP, true); } beforeCall("execute_inline"); load_imm_global_data_API("gDvmInlineOpsTable", OpndSize_32, 6, false); call_mem(16*tmp, 6, false);// afterCall("execute_inline"); compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); load_effective_addr(24, PhysicalReg_ESP, true, PhysicalReg_ESP, true); conditional_jump(Condition_NE, ".execute_inline_done", true); //jump to dvmJitToExceptionThrown scratchRegs[0] = PhysicalReg_SCRATCH_1; jumpToExceptionThrown(1/*exception number*/); insertLabel(".execute_inline_done", true); rPC += 3; return 0; } #undef P_GPR_1 #undef P_GPR_2 #undef P_SCRATCH_1 #undef P_SCRATCH_2 #undef P_SCRATCH_3 #undef P_SCRATCH_4 //! lower bytecode INVOKE_OBJECT_INIT_RANGE //! int op_invoke_object_init_range() { return -1; } #define P_GPR_1 PhysicalReg_EBX #define P_SCRATCH_1 PhysicalReg_ESI #define P_SCRATCH_2 PhysicalReg_EDX #define PP_GPR_1 PhysicalReg_EBX #define PP_GPR_2 PhysicalReg_ESI #define PP_GPR_3 PhysicalReg_EAX #define PP_GPR_4 PhysicalReg_EDX //! common code for INVOKE_VIRTUAL_QUICK //! It uses helper function if the switch is on int common_invoke_virtual_quick(bool hasRange, u2 vD, u2 IMMC) { #ifdef WITH_JIT_INLINING /* An invoke with the MIR_INLINED is effectively a no-op */ if (traceCurrentMIR->OptimizationFlags & MIR_INLINED) return false; /* * If the invoke has non-null misPredBranchOver, we need to generate * the non-inlined version of the invoke here to handle the * mispredicted case. */ if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) { genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList); } #endif export_pc(); constVREndOfBB(); beforeCall("exception"); //dump GG, GL VRs ///////////////////////////////////////////////// get_virtual_reg(vD, OpndSize_32, 1, false); simpleNullCheck(1, false, vD); #ifndef PREDICTED_CHAINING move_mem_to_reg(OpndSize_32, 0, 1, false, 2, false); move_mem_to_reg(OpndSize_32, offClassObject_vtable, 2, false, 3, false); move_mem_to_reg(OpndSize_32, IMMC, 3, false, PhysicalReg_ECX, true); if(hasRange) { common_invokeMethodRange(ArgsDone_Full); } else { common_invokeMethodNoRange(ArgsDone_Full); } #else gen_predicted_chain(hasRange, -1, IMMC, false, 1/*tmp1*/); #endif //////////////////////// return 0; } #undef P_GPR_1 #undef P_SCRATCH_1 #undef P_SCRATCH_2 #undef PP_GPR_1 #undef PP_GPR_2 #undef PP_GPR_3 #undef PP_GPR_4 //! lower bytecode INVOKE_VIRTUAL_QUICK by calling common_invoke_virtual_quick //! int op_invoke_virtual_quick() { u2 vD = FETCH(2) & 0xf; u2 IMMC = 4*FETCH(1); int retval = common_invoke_virtual_quick(false, vD, IMMC); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_VIRTUAL_QUICK_RANGE by calling common_invoke_virtual_quick //! int op_invoke_virtual_quick_range() { u2 vD = FETCH(2); u2 IMMC = 4*FETCH(1); int retval = common_invoke_virtual_quick(true, vD, IMMC); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } #define P_GPR_1 PhysicalReg_EBX #define P_GPR_2 PhysicalReg_ESI #define P_SCRATCH_1 PhysicalReg_EDX //! common code to lower INVOKE_SUPER_QUICK //! int common_invoke_super_quick(bool hasRange, u2 vD, u2 IMMC) { export_pc(); constVREndOfBB(); beforeCall("exception"); //dump GG, GL VRs compare_imm_VR(OpndSize_32, 0, vD); conditional_jump_global_API(Condition_E, "common_errNullObject", false); /* for trace-based JIT, callee is already resolved */ int mIndex = IMMC/4; const Method *calleeMethod = currentMethod->clazz->super->vtable[mIndex]; move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true); if(hasRange) { common_invokeMethodRange(convertCalleeToType(calleeMethod)); } else { common_invokeMethodNoRange(convertCalleeToType(calleeMethod)); } return 0; } #undef P_GPR_1 #undef P_GPR_2 #undef P_SCRATCH_1 //! lower bytecode INVOKE_SUPER_QUICK by calling common_invoke_super_quick //! int op_invoke_super_quick() { u2 vD = FETCH(2) & 0xf; u2 IMMC = 4*FETCH(1); int retval = common_invoke_super_quick(false, vD, IMMC); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } //! lower bytecode INVOKE_SUPER_QUICK_RANGE by calling common_invoke_super_quick //! int op_invoke_super_quick_range() { u2 vD = FETCH(2); u2 IMMC = 4*FETCH(1); int retval = common_invoke_super_quick(true, vD, IMMC); #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on #endif rPC += 3; return retval; } /////// code to predict the callee method for invoke_virtual & invoke_interface #define offChainingCell_clazz 8 #define offChainingCell_method 12 #define offChainingCell_counter 16 #define P_GPR_1 PhysicalReg_EBX #define P_GPR_2 PhysicalReg_EAX #define P_GPR_3 PhysicalReg_ESI #define P_SCRATCH_2 PhysicalReg_EDX /* TODO gingerbread: implemented for O1, but not for O0: valid input to JitToPatch & use icRechainCount */ /* update predicted method for invoke interface */ // 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3 void predicted_chain_interface_O0(u2 tmp) { ALOGI("TODO chain_interface_O0"); /* set up arguments to dvmFindInterfaceMethodInCache */ load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true); move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true); move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true); scratchRegs[0] = PhysicalReg_EDX; call_dvmFindInterfaceMethodInCache(); load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); /* if dvmFindInterfaceMethodInCache returns NULL, throw exception otherwise, jump to .find_interface_done */ compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); conditional_jump(Condition_NE, ".find_interface_done", true); scratchRegs[0] = PhysicalReg_EAX; jumpToExceptionThrown(1/*exception number*/); /* the interface method is found */ insertLabel(".find_interface_done", true); /* reduce counter in chaining cell by 1 */ move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_SCRATCH_2, true); //counter alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_SCRATCH_2, true); move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offChainingCell_counter, P_GPR_1, true); /* if counter is still greater than zero, skip prediction if it is zero, update predicted method */ compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true); conditional_jump(Condition_G, ".skipPrediction", true); /* call dvmJitToPatchPredictedChain to update predicted method */ //%ecx has callee method for virtual, %eax has callee for interface /* set up arguments for dvmJitToPatchPredictedChain */ load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true); insertChainingWorklist(traceCurrentBB->taken->id, stream); move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true); scratchRegs[0] = PhysicalReg_EAX; call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); insertLabel(".skipPrediction", true); move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true); } // 2 inputs: ChainingCell in temp 41, current class object in temp 40 void predicted_chain_interface_O1(u2 tmp) { /* set up arguments to dvmFindInterfaceMethodInCache */ load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true); move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true); move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, 40, false, 0, PhysicalReg_ESP, true); scratchRegs[0] = PhysicalReg_SCRATCH_10; call_dvmFindInterfaceMethodInCache(); load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); /* if dvmFindInterfaceMethodInCache returns NULL, throw exception otherwise, jump to .find_interface_done */ compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); conditional_jump(Condition_NE, ".find_interface_done", true); rememberState(3); scratchRegs[0] = PhysicalReg_SCRATCH_9; jumpToExceptionThrown(1/*exception number*/); goToState(3); /* the interface method is found */ insertLabel(".find_interface_done", true); #if 1 // /* for gingerbread, counter is stored in glue structure if clazz is not initialized, set icRechainCount to 0, otherwise, reduce it by 1 */ /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */ move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 45, false); move_imm_to_reg(OpndSize_32, 0, 43, false); get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical); move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter move_reg_to_reg(OpndSize_32, 33, false, 44, false); alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false); /* sub_opc will update control flags, so compare_imm_reg must happen after */ compare_imm_reg(OpndSize_32, 0, 45, false); conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/); conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/); move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical); #else /* reduce counter in chaining cell by 1 */ move_mem_to_reg(OpndSize_32, offChainingCell_counter, 41, false, 33, false); //counter alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false); move_reg_to_mem(OpndSize_32, 33, false, offChainingCell_counter, 41, false); #endif /* if counter is still greater than zero, skip prediction if it is zero, update predicted method */ compare_imm_reg(OpndSize_32, 0, 43, false); conditional_jump(Condition_G, ".skipPrediction", true); rememberState(4); /* call dvmJitToPatchPredictedChain to update predicted method */ //%ecx has callee method for virtual, %eax has callee for interface /* set up arguments for dvmJitToPatchPredictedChain */ load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true); insertChainingWorklist(traceCurrentBB->taken->id, stream); move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true); scratchRegs[0] = PhysicalReg_SCRATCH_8; call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); transferToState(4); insertLabel(".skipPrediction", true); move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true); } /* update predicted method for invoke virtual */ // 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3 void predicted_chain_virtual_O0(u2 IMMC) { ALOGI("TODO chain_virtual_O0"); /* reduce counter in chaining cell by 1 */ move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_GPR_2, true); //counter move_mem_to_reg(OpndSize_32, offClassObject_vtable, P_GPR_3, true, P_SCRATCH_2, true); alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_GPR_2, true); move_mem_to_reg(OpndSize_32, IMMC, P_SCRATCH_2, true, PhysicalReg_ECX, true); move_reg_to_mem(OpndSize_32, P_GPR_2, true, offChainingCell_counter, P_GPR_1, true); /* if counter is still greater than zero, skip prediction if it is zero, update predicted method */ compare_imm_reg(OpndSize_32, 0, P_GPR_2, true); conditional_jump(Condition_G, ".skipPrediction", true); /* call dvmJitToPatchPredictedChain to update predicted method */ //%ecx has callee method for virtual, %eax has callee for interface /* set up arguments for dvmJitToPatchPredictedChain */ load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0, PhysicalReg_ESP, true); insertChainingWorklist(traceCurrentBB->taken->id, stream); move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true); scratchRegs[0] = PhysicalReg_EAX; call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); //callee method in %ecx for invoke virtual move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true); insertLabel(".skipPrediction", true); } // 2 inputs: ChainingCell in temp 41, current class object in temp 40 // extra input: predicted clazz in temp 32 void predicted_chain_virtual_O1(u2 IMMC) { /* reduce counter in chaining cell by 1 */ /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */ get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical); move_imm_to_reg(OpndSize_32, 0, 43, false); move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter move_mem_to_reg(OpndSize_32, offClassObject_vtable, 40, false, 34, false); move_reg_to_reg(OpndSize_32, 33, false, 44, false); alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false); compare_imm_reg(OpndSize_32, 0, 32, false); // after sub_opc move_mem_to_reg(OpndSize_32, IMMC, 34, false, PhysicalReg_ECX, true); conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/); conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/); move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical); /* if counter is still greater than zero, skip prediction if it is zero, update predicted method */ compare_imm_reg(OpndSize_32, 0, 43, false); conditional_jump(Condition_G, ".skipPrediction", true); rememberState(2); /* call dvmJitToPatchPredictedChain to update predicted method */ //%ecx has callee method for virtual, %eax has callee for interface /* set up arguments for dvmJitToPatchPredictedChain */ load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true); if(traceCurrentBB->taken) insertChainingWorklist(traceCurrentBB->taken->id, stream); int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0; move_chain_to_mem(OpndSize_32, traceTakenId, 8, PhysicalReg_ESP, true); //predictedChainCell move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true); scratchRegs[0] = PhysicalReg_SCRATCH_10; call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); //callee method in %ecx for invoke virtual move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true); transferToState(2); insertLabel(".skipPrediction", true); } static int invokeChain_inst = 0; /* object "this" is in %ebx */ void gen_predicted_chain_O0(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) { ALOGI("TODO predicted_chain_O0"); /* get current class object */ move_mem_to_reg(OpndSize_32, offObject_clazz, PhysicalReg_EBX, true, P_GPR_3, true); #ifdef DEBUG_CALL_STACK3 scratchRegs[0] = PhysicalReg_EAX; call_debug_dumpSwitch(); //%ebx, %eax, %edx move_imm_to_reg(OpndSize_32, 0xdd11, PhysicalReg_EBX, true); call_debug_dumpSwitch(); #endif /* get predicted clazz get predicted method */ insertChainingWorklist(traceCurrentBB->taken->id, stream); move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, P_GPR_1, true); //predictedChainCell move_mem_to_reg(OpndSize_32, offChainingCell_clazz, P_GPR_1, true, P_SCRATCH_2, true);//predicted clazz move_mem_to_reg(OpndSize_32, offChainingCell_method, P_GPR_1, true, PhysicalReg_ECX, true);//predicted method #ifdef DEBUG_CALL_STACK3 load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, P_GPR_1, true, 8, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, 4, PhysicalReg_ESP, true); move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true); move_reg_to_reg(OpndSize_32, P_SCRATCH_2, true, PhysicalReg_EBX, true); call_debug_dumpSwitch(); move_imm_to_reg(OpndSize_32, 0xdd22, PhysicalReg_EBX, true); scratchRegs[0] = PhysicalReg_EAX; call_debug_dumpSwitch(); //%ebx, %eax, %edx move_reg_to_reg(OpndSize_32, P_GPR_3, true, PhysicalReg_EBX, true); call_debug_dumpSwitch(); move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true); call_debug_dumpSwitch(); move_mem_to_reg(OpndSize_32, 8, PhysicalReg_ESP, true, P_GPR_1, true); move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, P_SCRATCH_2, true); move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, P_GPR_3, true); load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true); #endif /* compare current class object against predicted clazz if equal, prediction is still valid, jump to .invokeChain */ //live registers: P_GPR_1, P_GPR_3, P_SCRATCH_2 compare_reg_reg(P_GPR_3, true, P_SCRATCH_2, true); conditional_jump(Condition_E, ".invokeChain", true); invokeChain_inst++; //get callee method and update predicted method if necessary if(isInterface) { predicted_chain_interface_O0(tmp); } else { predicted_chain_virtual_O0(IMMC); } #ifdef DEBUG_CALL_STACK3 move_imm_to_reg(OpndSize_32, 0xeeee, PhysicalReg_EBX, true); scratchRegs[0] = PhysicalReg_EAX; call_debug_dumpSwitch(); //%ebx, %eax, %edx insertChainingWorklist(traceCurrentBB->taken->id, stream); move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true); call_debug_dumpSwitch(); #endif if(isRange) { common_invokeMethodRange(ArgsDone_Full); } else { common_invokeMethodNoRange(ArgsDone_Full); } insertLabel(".invokeChain", true); #ifdef DEBUG_CALL_STACK3 move_imm_to_reg(OpndSize_32, 0xdddd, PhysicalReg_EBX, true); scratchRegs[0] = PhysicalReg_EAX; call_debug_dumpSwitch(); //%ebx, %eax, %edx insertChainingWorklist(traceCurrentBB->taken->id, stream); move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true); call_debug_dumpSwitch(); move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true); call_debug_dumpSwitch(); #endif if(isRange) { common_invokeMethodRange(ArgsDone_Normal); } else { common_invokeMethodNoRange(ArgsDone_Normal); } } /* object "this" is in inputReg: 5 for virtual, 1 for interface, 1 for virtual_quick */ void gen_predicted_chain_O1(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) { /* get current class object */ move_mem_to_reg(OpndSize_32, offObject_clazz, inputReg, false, 40, false); /* get predicted clazz get predicted method */ if(traceCurrentBB->taken) insertChainingWorklist(traceCurrentBB->taken->id, stream); int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0; move_chain_to_reg(OpndSize_32, traceTakenId, 41, false); //predictedChainCell move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 32, false);//predicted clazz move_mem_to_reg(OpndSize_32, offChainingCell_method, 41, false, PhysicalReg_ECX, true);//predicted method /* update stack with parameters first, then decide the callee */ if(isRange) common_invokeMethodRange_noJmp(); else common_invokeMethodNoRange_noJmp(); /* compare current class object against predicted clazz if equal, prediction is still valid, jump to .invokeChain */ compare_reg_reg(40, false, 32, false); conditional_jump(Condition_E, ".invokeChain", true); rememberState(1); invokeChain_inst++; //get callee method and update predicted method if necessary if(isInterface) { predicted_chain_interface_O1(tmp); } else { predicted_chain_virtual_O1(IMMC); } common_invokeMethod_Jmp(ArgsDone_Full); //will touch %ecx insertLabel(".invokeChain", true); goToState(1); common_invokeMethod_Jmp(ArgsDone_Normal); } void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) { return gen_predicted_chain_O1(isRange, tmp, IMMC, isInterface, inputReg); } #undef P_GPR_1 #undef P_GPR_2 #undef P_GPR_3 #undef P_SCRATCH_2