summaryrefslogtreecommitdiff
path: root/vm/mterp/armv5te/footer.S
diff options
context:
space:
mode:
Diffstat (limited to 'vm/mterp/armv5te/footer.S')
-rw-r--r--vm/mterp/armv5te/footer.S1251
1 files changed, 1251 insertions, 0 deletions
diff --git a/vm/mterp/armv5te/footer.S b/vm/mterp/armv5te/footer.S
new file mode 100644
index 0000000..4afe471
--- /dev/null
+++ b/vm/mterp/armv5te/footer.S
@@ -0,0 +1,1251 @@
+/*
+ * ===========================================================================
+ * 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:
+ mov rSELF, r0 @ restore self
+ mov rPC, r1 @ restore Dalvik pc
+ mov rFP, r2 @ restore Dalvik fp
+ ldr r10, [rSELF,#offThread_jitResumeNPC] @ resume address
+ mov r2, #0
+ str r2, [rSELF,#offThread_jitResumeNPC] @ reset resume address
+ ldr sp, [rSELF,#offThread_jitResumeNSP] @ cut back native stack
+ b jitSVShadowRunStart @ resume as if cache hit
+ @ expects resume addr in r10
+
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ mov r2,#kSVSPunt @ r2<- interpreter entry point
+ mov r3, #0
+ str r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ mov rPC, r0 @ set up dalvik pc
+ EXPORT_PC()
+ str lr, [rSELF,#offThread_jitResumeNPC]
+ str sp, [rSELF,#offThread_jitResumeNSP]
+ str r1, [rSELF,#offThread_jitResumeDPC]
+ mov r2,#kSVSSingleStep @ r2<- interpreter entry point
+ b jitSVShadowRunEnd @ doesn't return
+
+
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoProfile @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSBackwardBranch @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSNormal @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoChain @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+#else
+
+/*
+ * "longjmp" to a translation after single-stepping.
+ */
+ .global dvmJitResumeTranslation @ (Thread* self, u4* dFP)
+dvmJitResumeTranslation:
+ mov rSELF, r0 @ restore self
+ mov rPC, r1 @ restore Dalvik pc
+ mov rFP, r2 @ restore Dalvik fp
+ ldr r0, [rSELF,#offThread_jitResumeNPC]
+ mov r2, #0
+ str r2, [rSELF,#offThread_jitResumeNPC] @ reset resume address
+ ldr sp, [rSELF,#offThread_jitResumeNSP] @ cut back native stack
+ bx r0 @ 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:
+ mov rPC, r0
+#if defined(WITH_JIT_TUNING)
+ mov r0,lr
+ bl dvmBumpPunt;
+#endif
+ EXPORT_PC()
+ mov r0, #0
+ str r0, [rSELF, #offThread_inJitCodeCache] @ Back to the interp land
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable]
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * We'll use the normal single-stepping mechanism via interpBreak,
+ * but also save the native pc of the resume point in the translation
+ * and the native sp so that we can later do the equivalent of a
+ * longjmp() to resume.
+ * On entry:
+ * dPC <= Dalvik PC of instrucion to interpret
+ * lr <= resume point in translation
+ * r1 <= Dalvik PC of next instruction
+ */
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ mov rPC, r0 @ set up dalvik pc
+ EXPORT_PC()
+ str lr, [rSELF,#offThread_jitResumeNPC]
+ str sp, [rSELF,#offThread_jitResumeNSP]
+ str r1, [rSELF,#offThread_jitResumeDPC]
+ mov r1, #1
+ str r1, [rSELF,#offThread_singleStepCount] @ just step once
+ mov r0, rSELF
+ mov r1, #kSubModeCountedStep
+ bl dvmEnableSubMode @ (self, newMode)
+ ldr rIBASE, [rSELF,#offThread_curHandlerTable]
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used for callees.
+ */
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ mov r0,rPC
+ mov r1,rSELF
+ bl dvmJitGetTraceAddrThread @ (pc, self)
+ str r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ !0 means translation exists
+ bxne r0 @ continue native execution if so
+ b 2f @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used following
+ * invokes.
+ */
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr rPC,[lr, #-1] @ get our target PC
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST, #-4 @ .. which is 9 bytes back
+ mov r0,rPC
+ mov r1,rSELF
+ bl dvmJitGetTraceAddrThread @ (pc, self)
+ str r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq 2f
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable]
+ ldr r0, [rSELF, #offThread_pJitProfTable]
+ FETCH_INST()
+ cmp r0, #0
+ movne r2,#kJitTSelectRequestHot @ ask for trace selection
+ bne common_selectTrace
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * 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:
+ ldr rPC,[lr, #-1] @ get our target PC
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST,#-4 @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNormal
+#endif
+ mov r0,rPC
+ mov r1,rSELF
+ bl dvmJitGetTraceAddrThread @ (pc, self)
+ str r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq toInterpreter @ go if not, otherwise do chain
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/*
+ * 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)
+ bl dvmBumpNoChain
+#endif
+ mov r0,rPC
+ mov r1,rSELF
+ bl dvmJitGetTraceAddrThread @ (pc, self)
+ str r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+ EXPORT_PC()
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable]
+ FETCH_INST()
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ 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:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ mov r0,rPC
+ mov r1,rSELF
+ bl dvmJitGetTraceAddrThread @ (pc, self)
+ str r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+#endif
+
+/*
+ * 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()
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable]
+ FETCH_INST()
+ ldr r0, [rSELF, #offThread_pJitProfTable]
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable]
+ @ 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:
+ cmp r0, #0 @ JIT switched off?
+ beq 4f @ return to interp if so
+
+/*
+ * 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:
+ eor r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+ lsl r3,r3,#(32 - JIT_PROF_SIZE_LOG_2) @ shift out excess bits
+ ldrb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+ GET_INST_OPCODE(ip)
+ subs r1,r1,#1 @ decrement counter
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+ GOTO_OPCODE_IFNE(ip) @ if not threshold, fallthrough otherwise */
+
+ /* Looks good, reset the counter */
+ ldr r1, [rSELF, #offThread_jitThreshold]
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+ EXPORT_PC()
+ mov r0,rPC
+ mov r1,rSELF
+ bl dvmJitGetTraceAddrThread @ (pc, self)
+ str r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne r0 @ jump to the translation
+ mov r2,#kJitTSelectRequest @ ask for trace selection
+ @ fall-through to common_selectTrace
+#else
+ moveq r2,#kJitTSelectRequest @ ask for trace selection
+ beq common_selectTrace
+ /*
+ * 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.
+ */
+ mov r10, r0 @ save target
+ bl dvmCompilerGetInterpretTemplate
+ cmp r0, r10 @ special case?
+ bne jitSVShadowRunStart @ set up self verification shadow space
+ @ Need to clear the inJitCodeCache flag
+ mov r3, #0 @ 0 means not in the JIT code cache
+ str r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+ /* no return */
+#endif
+
+/*
+ * On entry:
+ * r2 is jit state.
+ */
+common_selectTrace:
+ ldrh r0,[rSELF,#offThread_subMode]
+ ands r0, #(kSubModeJitTraceBuild | kSubModeJitSV)
+ bne 3f @ already doing JIT work, continue
+ str r2,[rSELF,#offThread_jitState]
+ mov r0, 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_FP_TO_SELF() @ copy of pc/fp to Thread
+ bl dvmJitCheckTraceRequest
+3:
+ FETCH_INST()
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable]
+4:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip)
+ /* 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:
+ mov r0,rPC @ r0<- program counter
+ mov r1,rFP @ r1<- frame pointer
+ mov r2,rSELF @ r2<- self (Thread) pointer
+ mov r3,r10 @ r3<- target translation
+ bl dvmSelfVerificationSaveState @ save registers to shadow space
+ ldr rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+ bx r10 @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpreter state to original values
+ * before jumping back to the interpreter.
+ * On entry:
+ * r0: dPC
+ * r2: self verification state
+ */
+jitSVShadowRunEnd:
+ mov r1,rFP @ pass ending fp
+ mov r3,rSELF @ pass self ptr for convenience
+ bl dvmSelfVerificationRestoreState @ restore pc and fp values
+ LOAD_PC_FP_FROM_SELF() @ restore pc, fp
+ ldr r1,[r0,#offShadowSpace_svState] @ get self verification state
+ cmp r1,#0 @ check for punt condition
+ beq 1f
+ @ Set up SV single-stepping
+ mov r0, rSELF
+ mov r1, #kSubModeJitSV
+ bl dvmEnableSubMode @ (self, subMode)
+ mov r2,#kJitSelfVerification @ ask for self verification
+ str r2,[rSELF,#offThread_jitState]
+ @ intentional fallthrough
+1: @ exit to interpreter without check
+ EXPORT_PC()
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable]
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#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
+ */
+common_gotoBail:
+ SAVE_PC_FP_TO_SELF() @ export state to "thread"
+ mov r0, rSELF @ r0<- self ptr
+ b dvmMterpStdBail @ call(self, changeInterp)
+
+/*
+ * 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:
+ cmp r9, #0
+ ldrne r9, [r9, #offObject_clazz]
+ str r0, [rSELF, #offThread_methodToCall]
+ str r9, [rSELF, #offThread_callsiteClass]
+ bx lr
+#endif
+
+/*
+ * Common code for jumbo method invocation.
+ * NOTE: this adjusts rPC to account for the difference in instruction width.
+ * As a result, the savedPc in the stack frame will not be wholly accurate. So
+ * long as that is only used for source file line number calculations, we're
+ * okay.
+ */
+common_invokeMethodJumboNoThis:
+#if defined(WITH_JIT)
+ /* On entry: r0 is "Method* methodToCall */
+ mov r9, #0 @ clear "this"
+#endif
+common_invokeMethodJumbo:
+ /* On entry: r0 is "Method* methodToCall, r9 is "this" */
+.LinvokeNewJumbo:
+#if defined(WITH_JIT)
+ ldrh r1, [rSELF, #offThread_subMode]
+ ands r1, #kSubModeJitTraceBuild
+ blne save_callsiteinfo
+#endif
+ @ prepare to copy args to "outs" area of current frame
+ add rPC, rPC, #4 @ adjust pc to make return consistent
+ FETCH(r2, 1) @ r2<- BBBB (arg count)
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ cmp r2, #0 @ no args?
+ beq .LinvokeArgsDone @ if no args, skip the rest
+ FETCH(r1, 2) @ r1<- CCCC
+ b .LinvokeRangeArgs @ handle args like invoke range
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+#if defined(WITH_JIT)
+ ldrh r1, [rSELF, #offThread_subMode]
+ ands r1, #kSubModeJitTraceBuild
+ blne save_callsiteinfo
+#endif
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #8 @ r2<- AA (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ beq .LinvokeArgsDone @ if no args, skip the rest
+ FETCH(r1, 2) @ r1<- CCCC
+
+.LinvokeRangeArgs:
+ @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+ @ (very few methods have > 10 args; could unroll for common cases)
+ add r3, rFP, r1, lsl #2 @ r3<- &fp[CCCC]
+ sub r10, r10, r2, lsl #2 @ r10<- "outs" area, for call args
+1: ldr r1, [r3], #4 @ val = *fp++
+ subs r2, r2, #1 @ count--
+ str r1, [r10], #4 @ *outs++ = val
+ bne 1b @ ...while count != 0
+ b .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", r9 is "this"
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+#if defined(WITH_JIT)
+ ldrh r1, [rSELF, #offThread_subMode]
+ ands r1, #kSubModeJitTraceBuild
+ blne save_callsiteinfo
+#endif
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #12 @ r2<- B (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ FETCH(r1, 2) @ r1<- GFED (load here to hide latency)
+ beq .LinvokeArgsDone
+
+ @ r0=methodToCall, r1=GFED, r2=count, r10=outs
+.LinvokeNonRange:
+ rsb r2, r2, #5 @ r2<- 5-r2
+ add pc, pc, r2, lsl #4 @ computed goto, 4 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+5: and ip, rINST, #0x0f00 @ isolate A
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vA (shift right 8, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vA
+4: and ip, r1, #0xf000 @ isolate G
+ ldr r2, [rFP, ip, lsr #10] @ r2<- vG (shift right 12, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vG
+3: and ip, r1, #0x0f00 @ isolate F
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vF
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vF
+2: and ip, r1, #0x00f0 @ isolate E
+ ldr r2, [rFP, ip, lsr #2] @ r2<- vE
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vE
+1: and ip, r1, #0x000f @ isolate D
+ ldr r2, [rFP, ip, lsl #2] @ r2<- vD
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vD
+0: @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ ldr r2, [r0, #offMethod_insns] @ r2<- method->insns
+ ldr rINST, [r0, #offMethod_clazz] @ rINST<- method->clazz
+ @ find space for the new stack frame, check for overflow
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r9, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- newSaveArea
+@ bl common_dumpRegs
+ ldr r9, [rSELF, #offThread_interpStackEnd] @ r9<- interpStackEnd
+ sub r3, r10, r3, lsl #2 @ r3<- bottom (newsave - outsSize)
+ cmp r3, r9 @ bottom < interpStackEnd?
+ ldrh lr, [rSELF, #offThread_subMode]
+ ldr r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+ blo .LstackOverflow @ yes, this frame will overflow stack
+
+ @ set up newSaveArea
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP(ip, rFP) @ ip<- stack save area
+ str ip, [r10, #offStackSaveArea_prevSave]
+#endif
+ str rFP, [r10, #offStackSaveArea_prevFrame]
+ str rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+ mov r9, #0
+ str r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+ str r0, [r10, #offStackSaveArea_method]
+
+ @ Profiling?
+ cmp lr, #0 @ any special modes happening?
+ bne 2f @ go if so
+1:
+ tst r3, #ACC_NATIVE
+ bne .LinvokeNative
+
+ /*
+ stmfd sp!, {r0-r3}
+ bl common_printNewline
+ mov r0, rFP
+ mov r1, #0
+ bl dvmDumpFp
+ ldmfd sp!, {r0-r3}
+ stmfd sp!, {r0-r3}
+ mov r0, r1
+ mov r1, r10
+ bl dvmDumpFp
+ bl common_printNewline
+ ldmfd sp!, {r0-r3}
+ */
+
+ ldrh r9, [r2] @ r9 <- load INST from new PC
+ ldr r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ mov rPC, r2 @ publish new rPC
+
+ @ Update state values for the new method
+ @ r0=methodToCall, r1=newFp, r3=newMethodClass, r9=newINST
+ str r0, [rSELF, #offThread_method] @ self->method = methodToCall
+ str r3, [rSELF, #offThread_methodClassDex] @ self->methodClassDex = ...
+ mov r2, #1
+ str r2, [rSELF, #offThread_debugIsMethodEntry]
+#if defined(WITH_JIT)
+ ldr r0, [rSELF, #offThread_pJitProfTable]
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [rSELF, #offThread_curFrame] @ curFrame = newFp
+ cmp r0,#0
+ bne common_updateProfile
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [rSELF, #offThread_curFrame] @ curFrame = newFp
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+2:
+ @ Profiling - record method entry. r0: methodToCall
+ stmfd sp!, {r0-r3} @ preserve r0-r3
+ str rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+ mov r1, r0
+ mov r0, rSELF
+ bl dvmReportInvoke @ (self, method)
+ ldmfd sp!, {r0-r3} @ restore r0-r3
+ b 1b
+
+.LinvokeNative:
+ @ Prep for the native call
+ @ r0=methodToCall, r1=newFp, r10=newSaveArea
+ ldrh lr, [rSELF, #offThread_subMode]
+ ldr r9, [rSELF, #offThread_jniLocal_topCookie]@r9<-thread->localRef->...
+ str r1, [rSELF, #offThread_curFrame] @ curFrame = newFp
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFp (points to args)
+ add r1, rSELF, #offThread_retval @ r1<- &retval
+ mov r3, rSELF @ arg3<- self
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ b .Lskip
+ .type dalvik_mterp, %function
+dalvik_mterp:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+.Lskip:
+#endif
+
+ cmp lr, #0 @ any special SubModes active?
+ bne 11f @ go handle them if so
+ mov lr, pc @ set return addr
+ ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+7:
+
+ @ native return; r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+ ldr r1, [rSELF, #offThread_exception] @ check for exception
+ str rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [rSELF, #offThread_jniLocal_topCookie] @ new top <- old top
+ bne common_exceptionThrown @ no, handle exception
+
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+11:
+ @ r0=newFp, r1=&retval, r2=methodToCall, r3=self, lr=subModes
+ stmfd sp!, {r0-r3} @ save all but subModes
+ mov r0, r2 @ r0<- methodToCall
+ mov r1, rSELF
+ mov r2, rFP
+ bl dvmReportPreNativeInvoke @ (methodToCall, self, fp)
+ ldmfd sp, {r0-r3} @ refresh. NOTE: no sp autoincrement
+
+ @ Call the native method
+ mov lr, pc @ set return addr
+ ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+
+ @ Restore the pre-call arguments
+ ldmfd sp!, {r0-r3} @ r2<- methodToCall (others unneeded)
+
+ @ Finish up any post-invoke subMode requirements
+ mov r0, r2 @ r0<- methodToCall
+ mov r1, rSELF
+ mov r2, rFP
+ bl dvmReportPostNativeInvoke @ (methodToCall, self, fp)
+ b 7b @ resume
+
+.LstackOverflow: @ r0=methodToCall
+ mov r1, r0 @ r1<- methodToCall
+ mov r0, rSELF @ r0<- self
+ bl dvmHandleStackOverflow
+ b common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+ .fnend
+ .size dalvik_mterp, .-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
+ */
+ .if 0
+.LinvokeOld:
+ sub sp, sp, #8 @ space for args + pad
+ FETCH(ip, 2) @ ip<- FEDC or CCCC
+ mov r2, r0 @ A2<- methodToCall
+ mov r0, rSELF @ A0<- self
+ SAVE_PC_FP_TO_SELF() @ export state to "self"
+ mov r1, r9 @ A1<- methodCallRange
+ mov r3, rINST, lsr #8 @ A3<- AA
+ str ip, [sp, #0] @ A4<- ip
+ bl dvmMterp_invokeMethod @ call the C invokeMethod
+ add sp, sp, #8 @ remove arg area
+ b common_resumeAfterGlueCall @ continue to next instruction
+ .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+ ldrh lr, [rSELF, #offThread_subMode]
+ SAVEAREA_FROM_FP(r0, rFP)
+ ldr r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+ cmp lr, #0 @ any special subMode handling needed?
+ bne 19f
+14:
+ ldr rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+ ldr r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ cmp r2, #0 @ is this a break frame?
+#if defined(WORKAROUND_CORTEX_A9_745320)
+ /* Don't use conditional loads if the HW defect exists */
+ beq 15f
+ ldr r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+15:
+#else
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+#endif
+ beq common_gotoBail @ break frame, bail out completely
+
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh rIBASE
+ PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+ str r2, [rSELF, #offThread_method]@ self->method = newSave->method
+ ldr r1, [r10, #offClassObject_pDvmDex] @ r1<- method->clazz->pDvmDex
+ str rFP, [rSELF, #offThread_curFrame] @ curFrame = fp
+#if defined(WITH_JIT)
+ ldr r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+ mov rPC, r9 @ publish new rPC
+ str r1, [rSELF, #offThread_methodClassDex]
+ str r10, [rSELF, #offThread_inJitCodeCache] @ may return to JIT'ed land
+ cmp r10, #0 @ caller is compiled code
+ blxne r10
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ mov rPC, r9 @ publish new rPC
+ str r1, [rSELF, #offThread_methodClassDex]
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+19:
+ @ Handle special actions
+ @ On entry, r0: StackSaveArea
+ ldr r1, [r0, #offStackSaveArea_prevFrame] @ r2<- prevFP
+ str rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+ str r1, [rSELF, #offThread_curFrame] @ update interpSave.curFrame
+ mov r0, rSELF
+ bl dvmReportReturn @ (self)
+ SAVEAREA_FROM_FP(r0, rFP) @ restore StackSaveArea
+ b 14b @ continue
+
+ /*
+ * Return handling, calls through "glue code".
+ */
+ .if 0
+.LreturnOld:
+ SAVE_PC_FP_TO_SELF() @ export state
+ mov r0, rSELF @ arg to function
+ bl 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()
+
+ mov r0, rSELF
+ bl dvmCheckSuspendPending
+
+ ldr r9, [rSELF, #offThread_exception] @ r9<- self->exception
+ mov r1, rSELF @ r1<- self
+ mov r0, r9 @ r0<- exception
+ bl dvmAddTrackedAlloc @ don't let the exception be GCed
+ ldrh r2, [rSELF, #offThread_subMode] @ get subMode flags
+ mov r3, #0 @ r3<- NULL
+ str r3, [rSELF, #offThread_exception] @ self->exception = NULL
+
+ @ Special subMode?
+ cmp r2, #0 @ any special subMode handling needed?
+ bne 7f @ go if so
+8:
+ /* set up args and a local for "&fp" */
+ /* (str sp, [sp, #-4]! would be perfect here, but is discouraged) */
+ str rFP, [sp, #-4]! @ *--sp = fp
+ mov ip, sp @ ip<- &fp
+ mov r3, #0 @ r3<- false
+ str ip, [sp, #-4]! @ *--sp = &fp
+ ldr r1, [rSELF, #offThread_method] @ r1<- self->method
+ mov r0, rSELF @ r0<- self
+ ldr r1, [r1, #offMethod_insns] @ r1<- method->insns
+ ldrh lr, [rSELF, #offThread_subMode] @ lr<- subMode flags
+ mov r2, r9 @ r2<- exception
+ sub r1, rPC, r1 @ r1<- pc - method->insns
+ mov r1, r1, asr #1 @ r1<- offset in code units
+
+ /* call, r0 gets catchRelPc (a code-unit offset) */
+ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
+
+ /* fix earlier stack overflow if necessary; may trash rFP */
+ ldrb r1, [rSELF, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ beq 1f @ no, skip ahead
+ mov rFP, r0 @ save relPc result in rFP
+ mov r0, rSELF @ r0<- self
+ mov r1, r9 @ r1<- exception
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, rFP @ restore result
+1:
+
+ /* update frame pointer and check result from dvmFindCatchBlock */
+ ldr rFP, [sp, #4] @ retrieve the updated rFP
+ cmp r0, #0 @ is catchRelPc < 0?
+ add sp, sp, #8 @ restore stack
+ bmi .LnotCaughtLocally
+
+ /* adjust locals to match self->interpSave.curFrame and updated PC */
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area
+ ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method
+ str r1, [rSELF, #offThread_method] @ self->method = new method
+ ldr r2, [r1, #offMethod_clazz] @ r2<- method->clazz
+ ldr r3, [r1, #offMethod_insns] @ r3<- method->insns
+ ldr r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+ add rPC, r3, r0, asl #1 @ rPC<- method->insns + catchRelPc
+ str r2, [rSELF, #offThread_methodClassDex] @ self->pDvmDex = meth...
+
+ /* release the tracked alloc on the exception */
+ mov r0, r9 @ r0<- exception
+ mov r1, rSELF @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+
+ /* restore the exception if the handler wants it */
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh rIBASE
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ cmp ip, #OP_MOVE_EXCEPTION @ is it "move-exception"?
+ streq r9, [rSELF, #offThread_exception] @ yes, restore the exception
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ @ Manage debugger bookkeeping
+7:
+ str rPC, [rSELF, #offThread_pc] @ update interpSave.pc
+ str rFP, [rSELF, #offThread_curFrame] @ update interpSave.curFrame
+ mov r0, rSELF @ arg0<- self
+ mov r1, r9 @ arg1<- exception
+ bl dvmReportExceptionThrow @ (self, exception)
+ b 8b @ resume with normal handling
+
+.LnotCaughtLocally: @ r9=exception
+ /* fix stack overflow if necessary */
+ ldrb r1, [rSELF, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ movne r0, rSELF @ if yes: r0<- self
+ movne r1, r9 @ if yes: r1<- exception
+ blne dvmCleanupStackOverflow @ if yes: call(self)
+
+ @ 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" */
+ @ dvmLineNumFromPC(method, pc - method->insns)
+ ldr r0, [rSELF, #offThread_method]
+ ldr r1, [r0, #offMethod_insns]
+ sub r1, rPC, r1
+ asr r1, r1, #1
+ bl dvmLineNumFromPC
+ str r0, [sp, #-4]!
+ @ dvmGetMethodSourceFile(method)
+ ldr r0, [rSELF, #offThread_method]
+ bl dvmGetMethodSourceFile
+ str r0, [sp, #-4]!
+ @ exception->clazz->descriptor
+ ldr r3, [r9, #offObject_clazz]
+ ldr r3, [r3, #offClassObject_descriptor]
+ @
+ ldr r2, strExceptionNotCaughtLocally
+ ldr r1, strLogTag
+ mov r0, #3 @ LOG_DEBUG
+ bl __android_log_print
+#endif
+ str r9, [rSELF, #offThread_exception] @ restore exception
+ mov r0, r9 @ r0<- exception
+ mov r1, rSELF @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+ b common_gotoBail @ bail out
+
+
+ /*
+ * Exception handling, calls through "glue code".
+ */
+ .if 0
+.LexceptionOld:
+ SAVE_PC_FP_TO_SELF() @ export state
+ mov r0, rSELF @ arg to function
+ bl 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:
+ * r10: &dvmDex->pResFields[field]
+ * r0: field pointer (must preserve)
+ */
+common_verifyField:
+ ldrh r3, [rSELF, #offThread_subMode] @ r3 <- submode byte
+ ands r3, #kSubModeJitTraceBuild
+ bxeq lr @ Not building trace, continue
+ ldr r1, [r10] @ r1<- reload resolved StaticField ptr
+ cmp r1, #0 @ resolution complete?
+ bxne lr @ yes, continue
+ stmfd sp!, {r0-r2,lr} @ save regs
+ mov r0, rSELF
+ mov r1, rPC
+ bl dvmJitEndTraceSelect @ (self,pc) end trace before this inst
+ ldmfd sp!, {r0-r2, lr}
+ bx lr @ 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
+ ldr rIBASE, [rSELF, #offThread_curHandlerTable] @ refresh
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Invalid array index. Note that our calling convention is strange; we use r1
+ * and r3 because those just happen to be the registers all our callers are
+ * using. We move r3 before calling the C function, but r1 happens to match.
+ * r1: index
+ * r3: size
+ */
+common_errArrayIndex:
+ EXPORT_PC()
+ mov r0, r3
+ bl dvmThrowArrayIndexOutOfBoundsException
+ b common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+ EXPORT_PC()
+ ldr r0, strDivideByZero
+ bl dvmThrowArithmeticException
+ b common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ * On entry: length in r1
+ */
+common_errNegativeArraySize:
+ EXPORT_PC()
+ mov r0, r1 @ arg0 <- len
+ bl dvmThrowNegativeArraySizeException @ (len)
+ b common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ * On entry: method name in r1
+ */
+common_errNoSuchMethod:
+ EXPORT_PC()
+ mov r0, r1
+ bl 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()
+ mov r0, #0
+ bl dvmThrowNullPointerException
+ b common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+ ldr pc, .LdeadFood
+.LdeadFood:
+ .word 0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers. (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+ .macro SQUEAK num
+common_squeak\num:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strSqueak
+ mov r1, #\num
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endm
+
+ SQUEAK 0
+ SQUEAK 1
+ SQUEAK 2
+ SQUEAK 3
+ SQUEAK 4
+ SQUEAK 5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strSqueak
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strNewline
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+ /*
+ * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+ */
+common_printHex:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strPrintHex
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r3, r1
+ mov r2, r0
+ ldr r0, strPrintLong
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print full method info. Pass the Method* in r0. Preserves regs.
+ */
+common_printMethod:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpPrintMethod
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * 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:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpDumpArmRegs
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+ and r0, r0, r1 @ make sure no stray bits are set
+ fmrx r2, fpscr @ get VFP reg
+ mvn r1, r1 @ bit-invert mask
+ and r2, r2, r1 @ clear masked bits
+ orr r2, r2, r0 @ set specified bits
+ fmxr fpscr, r2 @ set VFP reg
+ mov r0, r2 @ return new value
+ bx lr
+
+ .align 2
+ .global dvmConfigureFP
+ .type dvmConfigureFP, %function
+dvmConfigureFP:
+ stmfd sp!, {ip, lr}
+ /* 0x03000000 sets DN/FZ */
+ /* 0x00009f00 clears the six exception enable flags */
+ bl common_squeak0
+ mov r0, #0x03000000 @ r0<- 0x03000000
+ add r1, r0, #0x9f00 @ r1<- 0x03009f00
+ bl setFPSCR
+ ldmfd sp!, {ip, pc}
+#endif
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+ .align 2
+strDivideByZero:
+ .word .LstrDivideByZero
+strLogTag:
+ .word .LstrLogTag
+strExceptionNotCaughtLocally:
+ .word .LstrExceptionNotCaughtLocally
+
+strNewline:
+ .word .LstrNewline
+strSqueak:
+ .word .LstrSqueak
+strPrintHex:
+ .word .LstrPrintHex
+strPrintLong:
+ .word .LstrPrintLong
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly. ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+ .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+.LstrFilledNewArrayNotImpl:
+ .asciz "filled-new-array only implemented for objects and 'int'"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrLogTag:
+ .asciz "mterp"
+.LstrExceptionNotCaughtLocally:
+ .asciz "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+ .asciz "\n"
+.LstrSqueak:
+ .asciz "<%d>"
+.LstrPrintHex:
+ .asciz "<%#x>"
+.LstrPrintLong:
+ .asciz "<%lld>"