| /* |
| * =========================================================================== |
| * Common subroutines and data |
| * =========================================================================== |
| */ |
| |
| .text |
| .align 2 |
| |
| #if defined(WITH_JIT) |
| #if defined(WITH_SELF_VERIFICATION) |
| |
| /* |
| * "longjmp" to a translation after single-stepping. Before returning |
| * to translation, must save state for self-verification. |
| */ |
| .global dvmJitResumeTranslation # (Thread* self, u4* dFP) |
| dvmJitResumeTranslation: |
| move rSELF, a0 # restore self |
| move rPC, a1 # restore Dalvik pc |
| move rFP, a2 # restore Dalvik fp |
| lw rBIX, offThread_jitResumeNPC(rSELF) |
| sw zero, offThread_jitResumeNPC(rSELF) # reset resume address |
| lw sp, offThread_jitResumeNSP(rSELF) # cut back native stack |
| b jitSVShadowRunStart # resume as if cache hit |
| # expects resume addr in rBIX |
| |
| .global dvmJitToInterpPunt |
| dvmJitToInterpPunt: |
| li a2, kSVSPunt # a2 <- interpreter entry point |
| sw zero, offThread_inJitCodeCache(rSELF) # Back to the interp land |
| b jitSVShadowRunEnd # doesn't return |
| |
| .global dvmJitToInterpSingleStep |
| dvmJitToInterpSingleStep: |
| move rPC, a0 # set up dalvik pc |
| EXPORT_PC() |
| sw ra, offThread_jitResumeNPC(rSELF) |
| sw a1, offThread_jitResumeDPC(rSELF) |
| li a2, kSVSSingleStep # a2 <- interpreter entry point |
| b jitSVShadowRunEnd # doesn't return |
| |
| .global dvmJitToInterpNoChainNoProfile |
| dvmJitToInterpNoChainNoProfile: |
| move a0, rPC # pass our target PC |
| li a2, kSVSNoProfile # a2 <- interpreter entry point |
| sw zero, offThread_inJitCodeCache(rSELF) # Back to the interp land |
| b jitSVShadowRunEnd # doesn't return |
| |
| .global dvmJitToInterpTraceSelectNoChain |
| dvmJitToInterpTraceSelectNoChain: |
| move a0, rPC # pass our target PC |
| li a2, kSVSTraceSelect # a2 <- interpreter entry point |
| sw zero, offThread_inJitCodeCache(rSELF) # Back to the interp land |
| b jitSVShadowRunEnd # doesn't return |
| |
| .global dvmJitToInterpTraceSelect |
| dvmJitToInterpTraceSelect: |
| lw a0, 0(ra) # pass our target PC |
| li a2, kSVSTraceSelect # a2 <- interpreter entry point |
| sw zero, offThread_inJitCodeCache(rSELF) # Back to the interp land |
| b jitSVShadowRunEnd # doesn't return |
| |
| .global dvmJitToInterpBackwardBranch |
| dvmJitToInterpBackwardBranch: |
| lw a0, 0(ra) # pass our target PC |
| li a2, kSVSBackwardBranch # a2 <- interpreter entry point |
| sw zero, offThread_inJitCodeCache(rSELF) # Back to the interp land |
| b jitSVShadowRunEnd # doesn't return |
| |
| .global dvmJitToInterpNormal |
| dvmJitToInterpNormal: |
| lw a0, 0(ra) # pass our target PC |
| li a2, kSVSNormal # a2 <- interpreter entry point |
| sw zero, offThread_inJitCodeCache(rSELF) # Back to the interp land |
| b jitSVShadowRunEnd # doesn't return |
| |
| .global dvmJitToInterpNoChain |
| dvmJitToInterpNoChain: |
| move a0, rPC # pass our target PC |
| li a2, kSVSNoChain # a2 <- interpreter entry point |
| sw zero, offThread_inJitCodeCache(rSELF) # Back to the interp land |
| b jitSVShadowRunEnd # doesn't return |
| #else /* WITH_SELF_VERIFICATION */ |
| |
| |
| /* |
| * "longjmp" to a translation after single-stepping. |
| */ |
| .global dvmJitResumeTranslation # (Thread* self, u4* dFP) |
| dvmJitResumeTranslation: |
| move rSELF, a0 # restore self |
| move rPC, a1 # restore Dalvik pc |
| move rFP, a2 # restore Dalvik fp |
| lw a0, offThread_jitResumeNPC(rSELF) |
| sw zero, offThread_jitResumeNPC(rSELF) # reset resume address |
| lw sp, offThread_jitResumeNSP(rSELF) # cut back native stack |
| jr a0 # resume translation |
| |
| |
| /* |
| * Return from the translation cache to the interpreter when the compiler is |
| * having issues translating/executing a Dalvik instruction. We have to skip |
| * the code cache lookup otherwise it is possible to indefinitely bouce |
| * between the interpreter and the code cache if the instruction that fails |
| * to be compiled happens to be at a trace start. |
| */ |
| .global dvmJitToInterpPunt |
| dvmJitToInterpPunt: |
| lw gp, STACK_OFFSET_GP(sp) |
| move rPC, a0 |
| #if defined(WITH_JIT_TUNING) |
| move a0, ra |
| JAL(dvmBumpPunt) |
| #endif |
| EXPORT_PC() |
| sw zero, offThread_inJitCodeCache(rSELF) # Back to the interp land |
| lw rIBASE, offThread_curHandlerTable(rSELF) |
| FETCH_INST() |
| GET_INST_OPCODE(t0) |
| GOTO_OPCODE(t0) |
| |
| /* |
| * Return to the interpreter to handle a single instruction. |
| * On entry: |
| * rPC <= Dalvik PC of instrucion to interpret |
| * a1 <= Dalvik PC of resume instruction |
| * ra <= resume point in translation |
| */ |
| |
| .global dvmJitToInterpSingleStep |
| dvmJitToInterpSingleStep: |
| lw gp, STACK_OFFSET_GP(sp) |
| move rPC, a0 # set up dalvik pc |
| EXPORT_PC() |
| sw ra, offThread_jitResumeNPC(rSELF) |
| sw sp, offThread_jitResumeNSP(rSELF) |
| sw a1, offThread_jitResumeDPC(rSELF) |
| li a1, 1 |
| sw a1, offThread_singleStepCount(rSELF) # just step once |
| move a0, rSELF |
| li a1, kSubModeCountedStep |
| JAL(dvmEnableSubMode) # (self, subMode) |
| lw rIBASE, offThread_curHandlerTable(rSELF) |
| FETCH_INST() |
| GET_INST_OPCODE(t0) |
| GOTO_OPCODE(t0) |
| /* |
| * Return from the translation cache and immediately request |
| * a translation for the exit target. Commonly used for callees. |
| */ |
| .global dvmJitToInterpTraceSelectNoChain |
| dvmJitToInterpTraceSelectNoChain: |
| lw gp, STACK_OFFSET_GP(sp) |
| #if defined(WITH_JIT_TUNING) |
| JAL(dvmBumpNoChain) |
| #endif |
| move a0, rPC |
| move a1, rSELF |
| JAL(dvmJitGetTraceAddrThread) # (pc, self) |
| move a0, v0 |
| sw a0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag |
| move a1, rPC # arg1 of translation may need this |
| move ra, zero # in case target is HANDLER_INTERPRET |
| beqz a0, 2f # 0 means translation does not exist |
| jr a0 |
| |
| /* |
| * Return from the translation cache and immediately request |
| * a translation for the exit target. Commonly used following |
| * invokes. |
| */ |
| .global dvmJitToInterpTraceSelect |
| dvmJitToInterpTraceSelect: |
| lw gp, STACK_OFFSET_GP(sp) |
| lw rPC, (ra) # get our target PC |
| subu rINST, ra, 8 # save start of chain branch |
| move a0, rPC |
| move a1, rSELF |
| JAL(dvmJitGetTraceAddrThread) # @ (pc, self) |
| sw v0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag |
| beqz v0, 2f |
| move a0, v0 |
| move a1, rINST |
| JAL(dvmJitChain) # v0 <- dvmJitChain(codeAddr, chainAddr) |
| move a1, rPC # arg1 of translation may need this |
| move ra, zero # in case target is HANDLER_INTERPRET |
| move a0, v0 |
| beqz a0, toInterpreter # didn't chain - resume with interpreter |
| |
| jr a0 # continue native execution |
| |
| /* No translation, so request one if profiling isn't disabled */ |
| 2: |
| lw rIBASE, offThread_curHandlerTable(rSELF) |
| lw a0, offThread_pJitProfTable(rSELF) |
| FETCH_INST() |
| li t0, kJitTSelectRequestHot |
| movn a2, t0, a0 # ask for trace selection |
| bnez a0, common_selectTrace |
| GET_INST_OPCODE(t0) |
| GOTO_OPCODE(t0) |
| |
| /* |
| * Return from the translation cache to the interpreter. |
| * The return was done with a BLX from thumb mode, and |
| * the following 32-bit word contains the target rPC value. |
| * Note that lr (r14) will have its low-order bit set to denote |
| * its thumb-mode origin. |
| * |
| * We'll need to stash our lr origin away, recover the new |
| * target and then check to see if there is a translation available |
| * for our new target. If so, we do a translation chain and |
| * go back to native execution. Otherwise, it's back to the |
| * interpreter (after treating this entry as a potential |
| * trace start). |
| */ |
| .global dvmJitToInterpNormal |
| dvmJitToInterpNormal: |
| lw gp, STACK_OFFSET_GP(sp) |
| lw rPC, (ra) # get our target PC |
| subu rINST, ra, 8 # save start of chain branch |
| #if defined(WITH_JIT_TUNING) |
| JAL(dvmBumpNormal) |
| #endif |
| move a0, rPC |
| move a1, rSELF |
| JAL(dvmJitGetTraceAddrThread) # @ (pc, self) |
| move a0, v0 |
| sw a0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag |
| beqz a0, toInterpreter # go if not, otherwise do chain |
| move a1, rINST |
| JAL(dvmJitChain) # v0 <- dvmJitChain(codeAddr, chainAddr) |
| move a1, rPC # arg1 of translation may need this |
| move ra, zero # in case target is HANDLER_INTERPRET |
| move a0, v0 |
| beqz a0, toInterpreter # didn't chain - resume with interpreter |
| |
| jr a0 # continue native execution |
| |
| /* |
| * Return from the translation cache to the interpreter to do method invocation. |
| * Check if translation exists for the callee, but don't chain to it. |
| */ |
| .global dvmJitToInterpNoChainNoProfile |
| dvmJitToInterpNoChainNoProfile: |
| #if defined(WITH_JIT_TUNING) |
| JAL(dvmBumpNoChain) |
| #endif |
| move a0, rPC |
| move a1, rSELF |
| JAL(dvmJitGetTraceAddrThread) # (pc, self) |
| move a0, v0 |
| sw a0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag |
| move a1, rPC # arg1 of translation may need this |
| move ra, zero # in case target is HANDLER_INTERPRET |
| beqz a0, footer235 |
| |
| jr a0 # continue native execution if so |
| footer235: |
| EXPORT_PC() |
| lw rIBASE, offThread_curHandlerTable(rSELF) |
| FETCH_INST() |
| GET_INST_OPCODE(t0) # extract opcode from rINST |
| GOTO_OPCODE(t0) # jump to next instruction |
| |
| /* |
| * Return from the translation cache to the interpreter to do method invocation. |
| * Check if translation exists for the callee, but don't chain to it. |
| */ |
| |
| .global dvmJitToInterpNoChain |
| dvmJitToInterpNoChain: |
| lw gp, STACK_OFFSET_GP(sp) |
| #if defined(WITH_JIT_TUNING) |
| JAL(dvmBumpNoChain) |
| #endif |
| move a0, rPC |
| move a1, rSELF |
| JAL(dvmJitGetTraceAddrThread) # (pc, self) |
| move a0, v0 |
| sw a0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag |
| move a1, rPC # arg1 of translation may need this |
| move ra, zero # in case target is HANDLER_INTERPRET |
| beqz a0, 1f |
| jr a0 # continue native execution if so |
| 1: |
| #endif /* WITH_SELF_VERIFICATION */ |
| |
| /* |
| * No translation, restore interpreter regs and start interpreting. |
| * rSELF & rFP were preserved in the translated code, and rPC has |
| * already been restored by the time we get here. We'll need to set |
| * up rIBASE & rINST, and load the address of the JitTable into r0. |
| */ |
| |
| toInterpreter: |
| EXPORT_PC() |
| lw rIBASE, offThread_curHandlerTable(rSELF) |
| FETCH_INST() |
| lw a0, offThread_pJitProfTable(rSELF) |
| lw rIBASE, offThread_curHandlerTable(rSELF) |
| # NOTE: intended fallthrough |
| |
| /* |
| * Similar to common_updateProfile, but tests for null pJitProfTable |
| * r0 holds pJifProfTAble, rINST is loaded, rPC is current and |
| * rIBASE has been recently refreshed. |
| */ |
| |
| common_testUpdateProfile: |
| |
| beqz a0, 4f |
| |
| /* |
| * Common code to update potential trace start counter, and initiate |
| * a trace-build if appropriate. |
| * On entry here: |
| * r0 <= pJitProfTable (verified non-NULL) |
| * rPC <= Dalvik PC |
| * rINST <= next instruction |
| */ |
| common_updateProfile: |
| srl a3, rPC, 12 # cheap, but fast hash function |
| xor a3, a3, rPC |
| andi a3, a3, JIT_PROF_SIZE-1 # eliminate excess bits |
| addu t1, a0, a3 |
| lbu a1, (t1) # get counter |
| GET_INST_OPCODE(t0) |
| subu a1, a1, 1 # decrement counter |
| sb a1, (t1) # and store it |
| beqz a1, 1f |
| GOTO_OPCODE(t0) # if not threshold, fallthrough otherwise |
| 1: |
| /* Looks good, reset the counter */ |
| lw a1, offThread_jitThreshold(rSELF) |
| sb a1, (t1) |
| EXPORT_PC() |
| move a0, rPC |
| move a1, rSELF |
| JAL(dvmJitGetTraceAddrThread) # (pc, self) |
| move a0, v0 |
| sw v0, offThread_inJitCodeCache(rSELF) # set the inJitCodeCache flag |
| move a1, rPC # arg1 of translation may need this |
| move ra, zero # in case target is HANDLER_INTERPRET |
| |
| #if !defined(WITH_SELF_VERIFICATION) |
| li t0, kJitTSelectRequest # ask for trace selection |
| movz a2, t0, a0 |
| beqz a0, common_selectTrace |
| jr a0 # jump to the translation |
| #else |
| |
| bne a0, zero, skip_ask_for_trace_selection |
| li a2, kJitTSelectRequest # ask for trace selection |
| j common_selectTrace |
| |
| skip_ask_for_trace_selection: |
| /* |
| * At this point, we have a target translation. However, if |
| * that translation is actually the interpret-only pseudo-translation |
| * we want to treat it the same as no translation. |
| */ |
| move rBIX, a0 # save target |
| jal dvmCompilerGetInterpretTemplate |
| # special case? |
| bne v0, rBIX, jitSVShadowRunStart # set up self verification shadow space |
| # Need to clear the inJitCodeCache flag |
| sw zero, offThread_inJitCodeCache(rSELF) # back to the interp land |
| GET_INST_OPCODE(t0) |
| GOTO_OPCODE(t0) |
| /* no return */ |
| #endif |
| |
| /* |
| * On entry: |
| * r2 is jit state. |
| */ |
| |
| common_selectTrace: |
| lhu a0, offThread_subMode(rSELF) |
| andi a0, (kSubModeJitTraceBuild | kSubModeJitSV) |
| bnez a0, 3f # already doing JIT work, continue |
| sw a2, offThread_jitState(rSELF) |
| move a0, rSELF |
| |
| /* |
| * Call out to validate trace-building request. If successful, |
| * rIBASE will be swapped to to send us into single-stepping trace |
| * building mode, so we need to refresh before we continue. |
| */ |
| |
| EXPORT_PC() |
| SAVE_PC_TO_SELF() |
| SAVE_FP_TO_SELF() |
| JAL(dvmJitCheckTraceRequest) |
| 3: |
| FETCH_INST() |
| lw rIBASE, offThread_curHandlerTable(rSELF) |
| 4: |
| GET_INST_OPCODE(t0) # extract opcode from rINST |
| GOTO_OPCODE(t0) |
| /* no return */ |
| #endif |
| |
| #if defined(WITH_SELF_VERIFICATION) |
| |
| /* |
| * Save PC and registers to shadow memory for self verification mode |
| * before jumping to native translation. |
| * On entry: |
| * rPC, rFP, rSELF: the values that they should contain |
| * r10: the address of the target translation. |
| */ |
| jitSVShadowRunStart: |
| move a0, rPC # r0 <- program counter |
| move a1, rFP # r1 <- frame pointer |
| move a2, rSELF # r2 <- InterpState pointer |
| move a3, rBIX # r3 <- target translation |
| jal dvmSelfVerificationSaveState # save registers to shadow space |
| lw rFP, offShadowSpace_shadowFP(v0) # rFP <- fp in shadow space |
| jr rBIX # jump to the translation |
| |
| /* |
| * Restore PC, registers, and interpState to original values |
| * before jumping back to the interpreter. |
| */ |
| jitSVShadowRunEnd: |
| move a1, rFP # pass ending fp |
| move a3, rSELF # pass self ptr for convenience |
| jal dvmSelfVerificationRestoreState # restore pc and fp values |
| LOAD_PC_FP_FROM_SELF() # restore pc, fp |
| lw a1, offShadowSpace_svState(a0) # get self verification state |
| beq a1, zero, 1f # check for punt condition |
| |
| # Setup SV single-stepping |
| move a0, rSELF |
| li a1, kSubModeJitSV |
| JAL(dvmEnableSubMode) # (self, subMode) |
| li a2, kJitSelfVerification # ask for self verification |
| sw a2, offThread_jitState(rSELF) |
| # Intentional fallthrough |
| |
| 1: |
| # exit to interpreter without check |
| EXPORT_PC() |
| lw rIBASE, offThread_curHandlerTable(rSELF) |
| FETCH_INST() |
| GET_INST_OPCODE(t0) |
| GOTO_OPCODE(t0) |
| #endif |
| |
| /* |
| * The equivalent of "goto bail", this calls through the "bail handler". |
| * It will end this interpreter activation, and return to the caller |
| * of dvmMterpStdRun. |
| * |
| * State registers will be saved to the "thread" area before bailing |
| * debugging purposes |
| */ |
| .ent common_gotoBail |
| common_gotoBail: |
| SAVE_PC_FP_TO_SELF() # export state to "thread" |
| move a0, rSELF # a0 <- self ptr |
| b dvmMterpStdBail # call(self, changeInterp) |
| .end common_gotoBail |
| |
| /* |
| * The JIT's invoke method needs to remember the callsite class and |
| * target pair. Save them here so that they are available to |
| * dvmCheckJit following the interpretation of this invoke. |
| */ |
| #if defined(WITH_JIT) |
| save_callsiteinfo: |
| beqz rOBJ, 1f |
| lw rOBJ, offObject_clazz(rOBJ) |
| 1: |
| sw a0, offThread_methodToCall(rSELF) |
| sw rOBJ, offThread_callsiteClass(rSELF) |
| jr ra |
| #endif |
| |
| /* |
| * Common code for method invocation with range. |
| * |
| * On entry: |
| * a0 is "Method* methodToCall", the method we're trying to call |
| */ |
| common_invokeMethodRange: |
| .LinvokeNewRange: |
| #if defined(WITH_JIT) |
| lhu a1, offThread_subMode(rSELF) |
| andi a1, kSubModeJitTraceBuild |
| beqz a1, 1f |
| JAL(save_callsiteinfo) |
| #endif |
| # prepare to copy args to "outs" area of current frame |
| 1: |
| GET_OPA(a2) |
| SAVEAREA_FROM_FP(rBIX, rFP) # rBIX <- stack save area |
| beqz a2, .LinvokeArgsDone |
| FETCH(a1, 2) # a1 <- CCCC |
| .LinvokeRangeArgs: |
| # a0=methodToCall, a1=CCCC, a2=count, rBIX=outs |
| # (very few methods have > 10 args; could unroll for common cases) |
| EAS2(a3, rFP, a1) |
| sll t0, a2, 2 |
| subu rBIX, rBIX, t0 |
| |
| 1: |
| lw a1, 0(a3) |
| addu a3, a3, 4 |
| subu a2, a2, 1 |
| sw a1, 0(rBIX) |
| addu rBIX, 4 |
| bnez a2, 1b |
| b .LinvokeArgsDone |
| |
| /* |
| * Common code for method invocation without range. |
| * |
| * On entry: |
| * a0 is "Method* methodToCall", "rOBJ is this" |
| */ |
| common_invokeMethodNoRange: |
| .LinvokeNewNoRange: |
| #if defined(WITH_JIT) |
| lhu a1, offThread_subMode(rSELF) |
| andi a1, kSubModeJitTraceBuild |
| beqz a1, 1f |
| JAL(save_callsiteinfo) |
| #endif |
| |
| # prepare to copy args to "outs" area of current frame |
| 1: |
| GET_OPB(a2) |
| SAVEAREA_FROM_FP(rBIX, rFP) |
| beqz a2, .LinvokeArgsDone |
| FETCH(a1, 2) |
| |
| # a0=methodToCall, a1=GFED, a2=count, |
| .LinvokeNonRange: |
| beq a2, 0, 0f |
| beq a2, 1, 1f |
| beq a2, 2, 2f |
| beq a2, 3, 3f |
| beq a2, 4, 4f |
| beq a2, 5, 5f |
| |
| 5: |
| and t0, rINST, 0x0f00 |
| ESRN(t2, rFP, t0, 6) |
| lw a3, (t2) |
| subu rBIX, 4 |
| sw a3, 0(rBIX) |
| |
| 4: |
| and t0, a1, 0xf000 |
| ESRN(t2, rFP, t0, 10) |
| lw a3, (t2) |
| subu rBIX, 4 |
| sw a3, 0(rBIX) |
| |
| 3: |
| and t0, a1, 0x0f00 |
| ESRN(t2, rFP, t0, 6) |
| lw a3, (t2) |
| subu rBIX, 4 |
| sw a3, 0(rBIX) |
| |
| 2: |
| and t0, a1, 0x00f0 |
| ESRN(t2, rFP, t0, 2) |
| lw a3, (t2) |
| subu rBIX, 4 |
| sw a3, 0(rBIX) |
| |
| 1: |
| and t0, a1, 0x000f |
| EASN(t2, rFP, t0, 2) |
| lw a3, (t2) |
| subu rBIX, 4 |
| sw a3, 0(rBIX) |
| |
| 0: |
| #fall through .LinvokeArgsDone |
| |
| |
| .LinvokeArgsDone: # a0=methodToCall |
| lhu rOBJ, offMethod_registersSize(a0) |
| lhu a3, offMethod_outsSize(a0) |
| lw a2, offMethod_insns(a0) |
| lw rINST, offMethod_clazz(a0) |
| # find space for the new stack frame, check for overflow |
| SAVEAREA_FROM_FP(a1, rFP) # a1 <- stack save area |
| sll t0, rOBJ, 2 # a1 <- newFp (old savearea - regsSize) |
| subu a1, a1, t0 |
| SAVEAREA_FROM_FP(rBIX, a1) |
| lw rOBJ, offThread_interpStackEnd(rSELF) # t3 <- interpStackEnd |
| sll t2, a3, 2 |
| subu t0, rBIX, t2 |
| lhu ra, offThread_subMode(rSELF) |
| lw a3, offMethod_accessFlags(a0) # a3 <- methodToCall->accessFlags |
| bltu t0, rOBJ, .LstackOverflow # yes, this frame will overflow stack |
| |
| |
| # set up newSaveArea |
| #ifdef EASY_GDB |
| SAVEAREA_FROM_FP(t0, rFP) |
| sw t0, offStackSaveArea_prevSave(rBIX) |
| #endif |
| sw rFP, (offStackSaveArea_prevFrame)(rBIX) |
| sw rPC, (offStackSaveArea_savedPc)(rBIX) |
| #if defined(WITH_JIT) |
| sw zero, (offStackSaveArea_returnAddr)(rBIX) |
| #endif |
| sw a0, (offStackSaveArea_method)(rBIX) |
| # Profiling? |
| bnez ra, 2f |
| 1: |
| and t2, a3, ACC_NATIVE |
| bnez t2, .LinvokeNative |
| lhu rOBJ, (a2) # rOBJ -< load Inst from New PC |
| lw a3, offClassObject_pDvmDex(rINST) |
| move rPC, a2 # Publish new rPC |
| # Update state values for the new method |
| # a0=methodToCall, a1=newFp, a3=newMethodClass, rOBJ=newINST |
| sw a0, offThread_method(rSELF) |
| sw a3, offThread_methodClassDex(rSELF) |
| li a2, 1 |
| sw a2, offThread_debugIsMethodEntry(rSELF) |
| |
| #if defined(WITH_JIT) |
| lw a0, offThread_pJitProfTable(rSELF) |
| move rFP, a1 # fp = newFp |
| GET_PREFETCHED_OPCODE(t0, rOBJ) # extract prefetched opcode from rOBJ |
| move rINST, rOBJ # publish new rINST |
| sw a1, offThread_curFrame(rSELF) |
| bnez a0, common_updateProfile |
| GOTO_OPCODE(t0) |
| #else |
| move rFP, a1 |
| GET_PREFETCHED_OPCODE(t0, rOBJ) |
| move rINST, rOBJ |
| sw a1, offThread_curFrame(rSELF) |
| GOTO_OPCODE(t0) |
| #endif |
| |
| 2: |
| # Profiling - record method entry. a0: methodToCall |
| STACK_STORE(a0, 0) |
| STACK_STORE(a1, 4) |
| STACK_STORE(a2, 8) |
| STACK_STORE(a3, 12) |
| sw rPC, offThread_pc(rSELF) # update interpSave.pc |
| move a1, a0 |
| move a0, rSELF |
| JAL(dvmReportInvoke) |
| STACK_LOAD(a3, 12) # restore a0-a3 |
| STACK_LOAD(a2, 8) |
| STACK_LOAD(a1, 4) |
| STACK_LOAD(a0, 0) |
| b 1b |
| .LinvokeNative: |
| # Prep for the native call |
| # a0=methodToCall, a1=newFp, rBIX=newSaveArea |
| lhu ra, offThread_subMode(rSELF) |
| lw t3, offThread_jniLocal_topCookie(rSELF) |
| sw a1, offThread_curFrame(rSELF) |
| sw t3, offStackSaveArea_localRefCookie(rBIX) # newFp->localRefCookie=top |
| move a2, a0 |
| move a0, a1 |
| addu a1, rSELF, offThread_retval |
| move a3, rSELF |
| #ifdef ASSIST_DEBUGGER |
| /* insert fake function header to help gdb find the stack frame */ |
| b .Lskip |
| .ent dalvik_mterp |
| dalvik_mterp: |
| STACK_STORE_FULL() |
| .Lskip: |
| #endif |
| bnez ra, 11f # Any special SubModes active? |
| lw t9, offMethod_nativeFunc(a2) |
| jalr t9 |
| lw gp, STACK_OFFSET_GP(sp) |
| 7: |
| # native return; rBIX=newSaveArea |
| # equivalent to dvmPopJniLocals |
| lw a0, offStackSaveArea_localRefCookie(rBIX) |
| lw a1, offThread_exception(rSELF) |
| sw rFP, offThread_curFrame(rSELF) |
| sw a0, offThread_jniLocal_topCookie(rSELF) # new top <- old top |
| bnez a1, common_exceptionThrown |
| |
| FETCH_ADVANCE_INST(3) |
| GET_INST_OPCODE(t0) |
| GOTO_OPCODE(t0) |
| 11: |
| # a0=newFp, a1=&retval, a2=methodToCall, a3=self, ra=subModes |
| SCRATCH_STORE(a0, 0) |
| SCRATCH_STORE(a1, 4) |
| SCRATCH_STORE(a2, 8) |
| SCRATCH_STORE(a3, 12) |
| move a0, a2 # a0 <- methodToCall |
| move a1, rSELF |
| move a2, rFP |
| JAL(dvmReportPreNativeInvoke) # (methodToCall, self, fp) |
| SCRATCH_LOAD(a3, 12) # restore a0-a3 |
| SCRATCH_LOAD(a2, 8) |
| SCRATCH_LOAD(a1, 4) |
| SCRATCH_LOAD(a0, 0) |
| |
| # Call the native method |
| lw t9, offMethod_nativeFunc(a2) # t9<-methodToCall->nativeFunc |
| jalr t9 |
| lw gp, STACK_OFFSET_GP(sp) |
| |
| # Restore the pre-call arguments |
| SCRATCH_LOAD(a3, 12) # restore a0-a3 |
| SCRATCH_LOAD(a2, 8) |
| SCRATCH_LOAD(a1, 4) |
| SCRATCH_LOAD(a0, 0) |
| |
| # Finish up any post-invoke subMode requirements |
| move a0, a2 |
| move a1, rSELF |
| move a2, rFP |
| JAL(dvmReportPostNativeInvoke) # (methodToCall, self, fp) |
| b 7b |
| |
| |
| .LstackOverflow: # a0=methodToCall |
| move a1, a0 # a1 <- methodToCall |
| move a0, rSELF # a0 <- self |
| JAL(dvmHandleStackOverflow) # dvmHandleStackOverflow(self, methodToCall) |
| b common_exceptionThrown |
| #ifdef ASSIST_DEBUGGER |
| .end dalvik_mterp |
| #endif |
| |
| /* |
| * Common code for method invocation, calling through "glue code". |
| * |
| * TODO: now that we have range and non-range invoke handlers, this |
| * needs to be split into two. Maybe just create entry points |
| * that set r9 and jump here? |
| * |
| * On entry: |
| * r0 is "Method* methodToCall", the method we're trying to call |
| * r9 is "bool methodCallRange", indicating if this is a /range variant |
| */ |
| |
| /* |
| * Common code for handling a return instruction. |
| * |
| * This does not return. |
| */ |
| common_returnFromMethod: |
| .LreturnNew: |
| lhu t0, offThread_subMode(rSELF) |
| SAVEAREA_FROM_FP(a0, rFP) |
| lw rOBJ, offStackSaveArea_savedPc(a0) # rOBJ = saveArea->savedPc |
| bnez t0, 19f |
| 14: |
| lw rFP, offStackSaveArea_prevFrame(a0) # fp = saveArea->prevFrame |
| lw a2, (offStackSaveArea_method - sizeofStackSaveArea)(rFP) |
| # a2<- method we're returning to |
| # is this a break frame? |
| beqz a2, common_gotoBail # break frame, bail out completely |
| |
| lw rBIX, offMethod_clazz(a2) # rBIX<- method->clazz |
| lw rIBASE, offThread_curHandlerTable(rSELF) # refresh rIBASE |
| PREFETCH_ADVANCE_INST(rINST, rOBJ, 3) # advance rOBJ, update new rINST |
| sw a2, offThread_method(rSELF) # self->method = newSave->method |
| lw a1, offClassObject_pDvmDex(rBIX) # r1<- method->clazz->pDvmDex |
| sw rFP, offThread_curFrame(rSELF) # curFrame = fp |
| #if defined(WITH_JIT) |
| lw rBIX, offStackSaveArea_returnAddr(a0) |
| move rPC, rOBJ # publish new rPC |
| sw a1, offThread_methodClassDex(rSELF) |
| sw rBIX, offThread_inJitCodeCache(rSELF) # may return to JIT'ed land |
| beqz rBIX, 15f # caller is compiled code |
| move t9, rBIX |
| jalr t9 |
| lw gp, STACK_OFFSET_GP(sp) |
| 15: |
| GET_INST_OPCODE(t0) # extract opcode from rINST |
| GOTO_OPCODE(t0) # jump to next instruction |
| #else |
| GET_INST_OPCODE(t0) # extract opcode from rINST |
| move rPC, rOBJ # publish new rPC |
| sw a1, offThread_methodClassDex(rSELF) |
| GOTO_OPCODE(t0) |
| #endif |
| |
| 19: |
| # Handle special actions |
| # On entry, a0: StackSaveArea |
| lw a1, offStackSaveArea_prevFrame(a0) # a1<- prevFP |
| sw rPC, offThread_pc(rSELF) # update interpSave.pc |
| sw a1, offThread_curFrame(rSELF) # update interpSave.curFrame |
| move a0, rSELF |
| JAL(dvmReportReturn) |
| SAVEAREA_FROM_FP(a0, rFP) # restore StackSaveArea |
| b 14b |
| |
| .if 0 |
| /* |
| * Return handling, calls through "glue code". |
| */ |
| .LreturnOld: |
| SAVE_PC_FP_TO_SELF() # export state |
| move a0, rSELF # arg to function |
| JAL(dvmMterp_returnFromMethod) |
| b common_resumeAfterGlueCall |
| .endif |
| |
| /* |
| * Somebody has thrown an exception. Handle it. |
| * |
| * If the exception processing code returns to us (instead of falling |
| * out of the interpreter), continue with whatever the next instruction |
| * now happens to be. |
| * |
| * This does not return. |
| */ |
| .global dvmMterpCommonExceptionThrown |
| dvmMterpCommonExceptionThrown: |
| common_exceptionThrown: |
| .LexceptionNew: |
| |
| EXPORT_PC() |
| move a0, rSELF |
| JAL(dvmCheckSuspendPending) |
| lw rOBJ, offThread_exception(rSELF) |
| move a1, rSELF |
| move a0, rOBJ |
| JAL(dvmAddTrackedAlloc) |
| lhu a2, offThread_subMode(rSELF) |
| sw zero, offThread_exception(rSELF) |
| |
| # Special subMode? |
| bnez a2, 7f # any special subMode handling needed? |
| 8: |
| /* set up args and a local for "&fp" */ |
| sw rFP, 20(sp) # store rFP => tmp |
| addu t0, sp, 20 # compute &tmp |
| sw t0, STACK_OFFSET_ARG04(sp) # save it in arg4 as per ABI |
| li a3, 0 # a3 <- false |
| lw a1, offThread_method(rSELF) |
| move a0, rSELF |
| lw a1, offMethod_insns(a1) |
| move a2, rOBJ |
| subu a1, rPC, a1 |
| sra a1, a1, 1 |
| |
| /* call, r0 gets catchRelPc (a code-unit offset) */ |
| JAL(dvmFindCatchBlock) # call(self, relPc, exc, scan?, &fp) |
| lw rFP, 20(sp) # retrieve the updated rFP |
| |
| /* update frame pointer and check result from dvmFindCatchBlock */ |
| move a0, v0 |
| bltz v0, .LnotCaughtLocally |
| |
| /* fix earlier stack overflow if necessary; Preserve a0 */ |
| lbu a1, offThread_stackOverflowed(rSELF) |
| beqz a1, 1f |
| move rBIX, a0 |
| move a0, rSELF |
| move a1, rOBJ |
| JAL(dvmCleanupStackOverflow) |
| move a0, rBIX |
| |
| 1: |
| |
| /* adjust locals to match self->interpSave.curFrame and updated PC */ |
| SAVEAREA_FROM_FP(a1, rFP) # a1<- new save area |
| lw a1, offStackSaveArea_method(a1) |
| sw a1, offThread_method(rSELF) |
| lw a2, offMethod_clazz(a1) |
| lw a3, offMethod_insns(a1) |
| lw a2, offClassObject_pDvmDex(a2) |
| EAS1(rPC, a3, a0) |
| sw a2, offThread_methodClassDex(rSELF) |
| |
| /* release the tracked alloc on the exception */ |
| move a0, rOBJ |
| move a1, rSELF |
| JAL(dvmReleaseTrackedAlloc) |
| |
| /* restore the exception if the handler wants it */ |
| lw rIBASE, offThread_curHandlerTable(rSELF) |
| FETCH_INST() |
| GET_INST_OPCODE(t0) |
| bne t0, OP_MOVE_EXCEPTION, 2f |
| sw rOBJ, offThread_exception(rSELF) |
| 2: |
| GOTO_OPCODE(t0) |
| |
| # Manage debugger bookkeeping |
| 7: |
| sw rPC, offThread_pc(rSELF) |
| sw rFP, offThread_curFrame(rSELF) |
| move a0, rSELF |
| move a1, rOBJ |
| JAL(dvmReportExceptionThrow) |
| b 8b |
| |
| .LnotCaughtLocally: # rOBJ = exception |
| /* fix stack overflow if necessary */ |
| lbu a1, offThread_stackOverflowed(rSELF) |
| beqz a1, 3f |
| move a0, rSELF |
| move a1, rOBJ |
| JAL(dvmCleanupStackOverflow) # dvmCleanupStackOverflow(self, exception) |
| |
| 3: |
| # may want to show "not caught locally" debug messages here |
| #if DVM_SHOW_EXCEPTION >= 2 |
| /* call __android_log_print(prio, tag, format, ...) */ |
| /* "Exception %s from %s:%d not caught locally" */ |
| lw a0, offThread_method(rSELF) |
| lw a1, offMethod_insns(a0) |
| subu a1, rPC, a1 |
| sra a1, a1, 1 |
| JAL(dvmLineNumFromPC) |
| sw v0, 20(sp) |
| # dvmGetMethodSourceFile(method) |
| lw a0, offThread_method(rSELF) |
| JAL(dvmGetMethodSourceFile) |
| sw v0, 16(sp) |
| # exception->clazz->descriptor |
| lw a3, offObject_clazz(rOBJ) |
| lw a3, offClassObject_descriptor(a3) |
| la a2, .LstrExceptionNotCaughtLocally |
| la a1, .LstrLogTag |
| li a0, 3 |
| JAL(__android_log_print) |
| #endif |
| sw rOBJ, offThread_exception(rSELF) |
| move a0, rOBJ |
| move a1, rSELF |
| JAL(dvmReleaseTrackedAlloc) |
| b common_gotoBail |
| |
| /* |
| * Exception handling, calls through "glue code". |
| */ |
| .if 0 |
| .LexceptionOld: |
| SAVE_PC_TO_SELF() # export state |
| SAVE_FP_TO_SELF() |
| move a0, rSELF # arg to function |
| JAL(dvmMterp_exceptionThrown) |
| b common_resumeAfterGlueCall |
| .endif |
| |
| #if defined(WITH_JIT) |
| /* |
| * If the JIT is actively building a trace we need to make sure |
| * that the field is fully resolved before including the current |
| * instruction. |
| * |
| * On entry: |
| * rBIX: &dvmDex->pResFields[field] |
| * a0: field pointer (must preserve) |
| */ |
| common_verifyField: |
| lhu a3, offThread_subMode(rSELF) |
| andi a3, kSubModeJitTraceBuild |
| bnez a3, 1f # Not building trace, continue |
| jr ra |
| 1: |
| lw a1, (rBIX) |
| beqz a1, 2f # resolution complete ? |
| jr ra |
| 2: |
| SCRATCH_STORE(a0, 0) |
| SCRATCH_STORE(a1, 4) |
| SCRATCH_STORE(a2, 8) |
| SCRATCH_STORE(a3, 12) |
| SCRATCH_STORE(ra, 16) |
| move a0, rSELF |
| move a1, rPC |
| JAL(dvmJitEndTraceSelect) #(self,pc) end trace before this inst) |
| SCRATCH_LOAD(a0, 0) |
| SCRATCH_LOAD(a1, 4) |
| SCRATCH_LOAD(a2, 8) |
| SCRATCH_LOAD(a3, 12) |
| SCRATCH_LOAD(ra, 16) |
| jr ra # return |
| #endif |
| |
| /* |
| * After returning from a "glued" function, pull out the updated |
| * values and start executing at the next instruction. |
| */ |
| common_resumeAfterGlueCall: |
| LOAD_PC_FP_FROM_SELF() # pull rPC and rFP out of thread |
| lw rIBASE, offThread_curHandlerTable(rSELF) # refresh |
| FETCH_INST() # load rINST from rPC |
| GET_INST_OPCODE(t0) # extract opcode from rINST |
| GOTO_OPCODE(t0) # jump to next instruction |
| |
| /* |
| * Invalid array index. Note that our calling convention is strange; we use a1 |
| * and a3 because those just happen to be the registers all our callers are |
| * using. We move a3 before calling the C function, but a1 happens to match. |
| * a1: index |
| * a3: size |
| */ |
| common_errArrayIndex: |
| EXPORT_PC() |
| move a0, a3 |
| JAL(dvmThrowArrayIndexOutOfBoundsException) |
| b common_exceptionThrown |
| |
| /* |
| * Integer divide or mod by zero. |
| */ |
| common_errDivideByZero: |
| EXPORT_PC() |
| la a0, .LstrDivideByZero |
| JAL(dvmThrowArithmeticException) |
| b common_exceptionThrown |
| |
| /* |
| * Attempt to allocate an array with a negative size. |
| * On entry: length in a1 |
| */ |
| common_errNegativeArraySize: |
| EXPORT_PC() |
| move a0, a1 # arg0 <- len |
| JAL(dvmThrowNegativeArraySizeException) # (len) |
| b common_exceptionThrown |
| |
| /* |
| * Invocation of a non-existent method. |
| * On entry: method name in a1 |
| */ |
| common_errNoSuchMethod: |
| EXPORT_PC() |
| move a0, a1 |
| JAL(dvmThrowNoSuchMethodError) |
| b common_exceptionThrown |
| |
| /* |
| * We encountered a null object when we weren't expecting one. We |
| * export the PC, throw a NullPointerException, and goto the exception |
| * processing code. |
| */ |
| common_errNullObject: |
| EXPORT_PC() |
| li a0, 0 |
| JAL(dvmThrowNullPointerException) |
| b common_exceptionThrown |
| |
| /* |
| * For debugging, cause an immediate fault. The source address will be in ra. Use a jal to jump here. |
| */ |
| common_abort: |
| lw zero,-4(zero) # generate SIGSEGV |
| |
| /* |
| * Spit out a "we were here", preserving all registers. |
| */ |
| .macro SQUEAK num |
| common_squeak\num: |
| STACK_STORE_RA(); |
| la a0, .LstrSqueak |
| LOAD_IMM(a1, \num); |
| JAL(printf); |
| STACK_LOAD_RA(); |
| RETURN; |
| .endm |
| |
| SQUEAK 0 |
| SQUEAK 1 |
| SQUEAK 2 |
| SQUEAK 3 |
| SQUEAK 4 |
| SQUEAK 5 |
| |
| /* |
| * Spit out the number in a0, preserving registers. |
| */ |
| common_printNum: |
| STACK_STORE_RA() |
| MOVE_REG(a1, a0) |
| la a0, .LstrSqueak |
| JAL(printf) |
| STACK_LOAD_RA() |
| RETURN |
| |
| /* |
| * Print a newline, preserving registers. |
| */ |
| common_printNewline: |
| STACK_STORE_RA() |
| la a0, .LstrNewline |
| JAL(printf) |
| STACK_LOAD_RA() |
| RETURN |
| |
| /* |
| * Print the 32-bit quantity in a0 as a hex value, preserving registers. |
| */ |
| common_printHex: |
| STACK_STORE_RA() |
| MOVE_REG(a1, a0) |
| la a0, .LstrPrintHex |
| JAL(printf) |
| STACK_LOAD_RA() |
| RETURN; |
| |
| /* |
| * Print the 64-bit quantity in a0-a1, preserving registers. |
| */ |
| common_printLong: |
| STACK_STORE_RA() |
| MOVE_REG(a3, a1) |
| MOVE_REG(a2, a0) |
| la a0, .LstrPrintLong |
| JAL(printf) |
| STACK_LOAD_RA() |
| RETURN; |
| |
| /* |
| * Print full method info. Pass the Method* in a0. Preserves regs. |
| */ |
| common_printMethod: |
| STACK_STORE_RA() |
| JAL(dvmMterpPrintMethod) |
| STACK_LOAD_RA() |
| RETURN |
| |
| /* |
| * Call a C helper function that dumps regs and possibly some |
| * additional info. Requires the C function to be compiled in. |
| */ |
| .if 0 |
| common_dumpRegs: |
| STACK_STORE_RA() |
| JAL(dvmMterpDumpMipsRegs) |
| STACK_LOAD_RA() |
| RETURN |
| .endif |
| |
| /* |
| * Zero-terminated ASCII string data. |
| */ |
| .data |
| |
| .LstrBadEntryPoint: |
| .asciiz "Bad entry point %d\n" |
| .LstrDivideByZero: |
| .asciiz "divide by zero" |
| .LstrFilledNewArrayNotImpl: |
| .asciiz "filled-new-array only implemented for 'int'" |
| .LstrLogTag: |
| .asciiz "mterp" |
| .LstrExceptionNotCaughtLocally: |
| .asciiz "Exception %s from %s:%d not caught locally\n" |
| |
| .LstrNewline: |
| .asciiz "\n" |
| .LstrSqueak: |
| .asciiz "<%d>" |
| .LstrPrintHex: |
| .asciiz "<0x%x>" |
| .LstrPrintLong: |
| .asciiz "<%lld>" |