1
2/*
3 * ===========================================================================
4 *  Common subroutines and data
5 * ===========================================================================
6 */
7
8
9
10    .text
11    .align  2
12
13#if defined(WITH_JIT)
14#if defined(WITH_SELF_VERIFICATION)
15    .global dvmJitToInterpPunt
16dvmJitToInterpPunt:
17    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
18    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
19    mov    r3, #0
20    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
21    b      jitSVShadowRunEnd            @ doesn't return
22
23    .global dvmJitToInterpSingleStep
24dvmJitToInterpSingleStep:
25    str    lr,[rGLUE,#offGlue_jitResumeNPC]
26    str    r1,[rGLUE,#offGlue_jitResumeDPC]
27    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
28    b      jitSVShadowRunEnd            @ doesn't return
29
30    .global dvmJitToInterpNoChainNoProfile
31dvmJitToInterpNoChainNoProfile:
32    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
33    mov    r0,rPC                       @ pass our target PC
34    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
35    mov    r3, #0                       @ 0 means !inJitCodeCache
36    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
37    b      jitSVShadowRunEnd            @ doesn't return
38
39    .global dvmJitToInterpTraceSelectNoChain
40dvmJitToInterpTraceSelectNoChain:
41    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
42    mov    r0,rPC                       @ pass our target PC
43    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
44    mov    r3, #0                       @ 0 means !inJitCodeCache
45    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
46    b      jitSVShadowRunEnd            @ doesn't return
47
48    .global dvmJitToInterpTraceSelect
49dvmJitToInterpTraceSelect:
50    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
51    ldr    r0,[lr, #-1]                 @ pass our target PC
52    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
53    mov    r3, #0                       @ 0 means !inJitCodeCache
54    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
55    b      jitSVShadowRunEnd            @ doesn't return
56
57    .global dvmJitToInterpBackwardBranch
58dvmJitToInterpBackwardBranch:
59    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
60    ldr    r0,[lr, #-1]                 @ pass our target PC
61    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
62    mov    r3, #0                       @ 0 means !inJitCodeCache
63    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
64    b      jitSVShadowRunEnd            @ doesn't return
65
66    .global dvmJitToInterpNormal
67dvmJitToInterpNormal:
68    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
69    ldr    r0,[lr, #-1]                 @ pass our target PC
70    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
71    mov    r3, #0                       @ 0 means !inJitCodeCache
72    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
73    b      jitSVShadowRunEnd            @ doesn't return
74
75    .global dvmJitToInterpNoChain
76dvmJitToInterpNoChain:
77    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
78    mov    r0,rPC                       @ pass our target PC
79    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
80    mov    r3, #0                       @ 0 means !inJitCodeCache
81    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
82    b      jitSVShadowRunEnd            @ doesn't return
83#else
84/*
85 * Return from the translation cache to the interpreter when the compiler is
86 * having issues translating/executing a Dalvik instruction. We have to skip
87 * the code cache lookup otherwise it is possible to indefinitely bouce
88 * between the interpreter and the code cache if the instruction that fails
89 * to be compiled happens to be at a trace start.
90 */
91    .global dvmJitToInterpPunt
92dvmJitToInterpPunt:
93    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
94    mov    rPC, r0
95#if defined(WITH_JIT_TUNING)
96    mov    r0,lr
97    bl     dvmBumpPunt;
98#endif
99    EXPORT_PC()
100    mov    r0, #0
101    str    r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
102    adrl   rIBASE, dvmAsmInstructionStart
103    FETCH_INST()
104    GET_INST_OPCODE(ip)
105    GOTO_OPCODE(ip)
106
107/*
108 * Return to the interpreter to handle a single instruction.
109 * On entry:
110 *    r0 <= PC
111 *    r1 <= PC of resume instruction
112 *    lr <= resume point in translation
113 */
114    .global dvmJitToInterpSingleStep
115dvmJitToInterpSingleStep:
116    str    lr,[rGLUE,#offGlue_jitResumeNPC]
117    str    r1,[rGLUE,#offGlue_jitResumeDPC]
118    mov    r1,#kInterpEntryInstr
119    @ enum is 4 byte in aapcs-EABI
120    str    r1, [rGLUE, #offGlue_entryPoint]
121    mov    rPC,r0
122    EXPORT_PC()
123
124    adrl   rIBASE, dvmAsmInstructionStart
125    mov    r2,#kJitSingleStep     @ Ask for single step and then revert
126    str    r2,[rGLUE,#offGlue_jitState]
127    mov    r1,#1                  @ set changeInterp to bail to debug interp
128    b      common_gotoBail
129
130/*
131 * Return from the translation cache and immediately request
132 * a translation for the exit target.  Commonly used for callees.
133 */
134    .global dvmJitToInterpTraceSelectNoChain
135dvmJitToInterpTraceSelectNoChain:
136#if defined(WITH_JIT_TUNING)
137    bl     dvmBumpNoChain
138#endif
139    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
140    mov    r0,rPC
141    bl     dvmJitGetCodeAddr        @ Is there a translation?
142    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
143    mov    r1, rPC                  @ arg1 of translation may need this
144    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
145    cmp    r0,#0                    @ !0 means translation exists
146    bxne   r0                       @ continue native execution if so
147    b      2f                       @ branch over to use the interpreter
148
149/*
150 * Return from the translation cache and immediately request
151 * a translation for the exit target.  Commonly used following
152 * invokes.
153 */
154    .global dvmJitToInterpTraceSelect
155dvmJitToInterpTraceSelect:
156    ldr    rPC,[lr, #-1]           @ get our target PC
157    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
158    add    rINST,lr,#-5            @ save start of chain branch
159    add    rINST, #-4              @  .. which is 9 bytes back
160    mov    r0,rPC
161    bl     dvmJitGetCodeAddr       @ Is there a translation?
162    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
163    cmp    r0,#0
164    beq    2f
165    mov    r1,rINST
166    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
167    mov    r1, rPC                  @ arg1 of translation may need this
168    mov    lr, #0                   @ in case target is HANDLER_INTERPRET
169    cmp    r0,#0                    @ successful chain?
170    bxne   r0                       @ continue native execution
171    b      toInterpreter            @ didn't chain - resume with interpreter
172
173/* No translation, so request one if profiling isn't disabled*/
1742:
175    adrl   rIBASE, dvmAsmInstructionStart
176    GET_JIT_PROF_TABLE(r0)
177    FETCH_INST()
178    cmp    r0, #0
179    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
180    bne    common_selectTrace
181    GET_INST_OPCODE(ip)
182    GOTO_OPCODE(ip)
183
184/*
185 * Return from the translation cache to the interpreter.
186 * The return was done with a BLX from thumb mode, and
187 * the following 32-bit word contains the target rPC value.
188 * Note that lr (r14) will have its low-order bit set to denote
189 * its thumb-mode origin.
190 *
191 * We'll need to stash our lr origin away, recover the new
192 * target and then check to see if there is a translation available
193 * for our new target.  If so, we do a translation chain and
194 * go back to native execution.  Otherwise, it's back to the
195 * interpreter (after treating this entry as a potential
196 * trace start).
197 */
198    .global dvmJitToInterpNormal
199dvmJitToInterpNormal:
200    ldr    rPC,[lr, #-1]           @ get our target PC
201    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
202    add    rINST,lr,#-5            @ save start of chain branch
203    add    rINST,#-4               @ .. which is 9 bytes back
204#if defined(WITH_JIT_TUNING)
205    bl     dvmBumpNormal
206#endif
207    mov    r0,rPC
208    bl     dvmJitGetCodeAddr        @ Is there a translation?
209    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
210    cmp    r0,#0
211    beq    toInterpreter            @ go if not, otherwise do chain
212    mov    r1,rINST
213    bl     dvmJitChain              @ r0<- dvmJitChain(codeAddr,chainAddr)
214    mov    r1, rPC                  @ arg1 of translation may need this
215    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
216    cmp    r0,#0                    @ successful chain?
217    bxne   r0                       @ continue native execution
218    b      toInterpreter            @ didn't chain - resume with interpreter
219
220/*
221 * Return from the translation cache to the interpreter to do method invocation.
222 * Check if translation exists for the callee, but don't chain to it.
223 */
224    .global dvmJitToInterpNoChainNoProfile
225dvmJitToInterpNoChainNoProfile:
226#if defined(WITH_JIT_TUNING)
227    bl     dvmBumpNoChain
228#endif
229    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
230    mov    r0,rPC
231    bl     dvmJitGetCodeAddr        @ Is there a translation?
232    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
233    mov    r1, rPC                  @ arg1 of translation may need this
234    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
235    cmp    r0,#0
236    bxne   r0                       @ continue native execution if so
237    EXPORT_PC()
238    adrl   rIBASE, dvmAsmInstructionStart
239    FETCH_INST()
240    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
241    GOTO_OPCODE(ip)                     @ jump to next instruction
242
243/*
244 * Return from the translation cache to the interpreter to do method invocation.
245 * Check if translation exists for the callee, but don't chain to it.
246 */
247    .global dvmJitToInterpNoChain
248dvmJitToInterpNoChain:
249#if defined(WITH_JIT_TUNING)
250    bl     dvmBumpNoChain
251#endif
252    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
253    mov    r0,rPC
254    bl     dvmJitGetCodeAddr        @ Is there a translation?
255    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
256    mov    r1, rPC                  @ arg1 of translation may need this
257    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
258    cmp    r0,#0
259    bxne   r0                       @ continue native execution if so
260#endif
261
262/*
263 * No translation, restore interpreter regs and start interpreting.
264 * rGLUE & rFP were preserved in the translated code, and rPC has
265 * already been restored by the time we get here.  We'll need to set
266 * up rIBASE & rINST, and load the address of the JitTable into r0.
267 */
268toInterpreter:
269    EXPORT_PC()
270    adrl   rIBASE, dvmAsmInstructionStart
271    FETCH_INST()
272    GET_JIT_PROF_TABLE(r0)
273    @ NOTE: intended fallthrough
274
275/*
276 * Common code to update potential trace start counter, and initiate
277 * a trace-build if appropriate.  On entry, rPC should point to the
278 * next instruction to execute, and rINST should be already loaded with
279 * the next opcode word, and r0 holds a pointer to the jit profile
280 * table (pJitProfTable).
281 */
282common_testUpdateProfile:
283    cmp     r0,#0
284    GET_INST_OPCODE(ip)
285    GOTO_OPCODE_IFEQ(ip)       @ if not profiling, fallthrough otherwise */
286
287common_updateProfile:
288    eor     r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
289    lsl     r3,r3,#(32 - JIT_PROF_SIZE_LOG_2)          @ shift out excess bits
290    ldrb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
291    GET_INST_OPCODE(ip)
292    subs    r1,r1,#1           @ decrement counter
293    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
294    GOTO_OPCODE_IFNE(ip)       @ if not threshold, fallthrough otherwise */
295
296/*
297 * Here, we switch to the debug interpreter to request
298 * trace selection.  First, though, check to see if there
299 * is already a native translation in place (and, if so,
300 * jump to it now).
301 */
302    GET_JIT_THRESHOLD(r1)
303    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
304    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
305    EXPORT_PC()
306    mov     r0,rPC
307    bl      dvmJitGetCodeAddr           @ r0<- dvmJitGetCodeAddr(rPC)
308    str     r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
309    mov     r1, rPC                     @ arg1 of translation may need this
310    mov     lr, #0                      @  in case target is HANDLER_INTERPRET
311    cmp     r0,#0
312#if !defined(WITH_SELF_VERIFICATION)
313    bxne    r0                          @ jump to the translation
314    mov     r2,#kJitTSelectRequest      @ ask for trace selection
315    @ fall-through to common_selectTrace
316#else
317    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
318    beq     common_selectTrace
319    /*
320     * At this point, we have a target translation.  However, if
321     * that translation is actually the interpret-only pseudo-translation
322     * we want to treat it the same as no translation.
323     */
324    mov     r10, r0                     @ save target
325    bl      dvmCompilerGetInterpretTemplate
326    cmp     r0, r10                     @ special case?
327    bne     jitSVShadowRunStart         @ set up self verification shadow space
328    @ Need to clear the inJitCodeCache flag
329    ldr    r10, [rGLUE, #offGlue_self]  @ r10 <- glue->self
330    mov    r3, #0                       @ 0 means not in the JIT code cache
331    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
332    GET_INST_OPCODE(ip)
333    GOTO_OPCODE(ip)
334    /* no return */
335#endif
336
337/*
338 * On entry:
339 *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
340 */
341common_selectTrace:
342    str     r2,[rGLUE,#offGlue_jitState]
343    mov     r2,#kInterpEntryInstr       @ normal entry reason
344    str     r2,[rGLUE,#offGlue_entryPoint]
345    mov     r1,#1                       @ set changeInterp
346    b       common_gotoBail
347
348#if defined(WITH_SELF_VERIFICATION)
349/*
350 * Save PC and registers to shadow memory for self verification mode
351 * before jumping to native translation.
352 * On entry:
353 *    rPC, rFP, rGLUE: the values that they should contain
354 *    r10: the address of the target translation.
355 */
356jitSVShadowRunStart:
357    mov     r0,rPC                      @ r0<- program counter
358    mov     r1,rFP                      @ r1<- frame pointer
359    mov     r2,rGLUE                    @ r2<- InterpState pointer
360    mov     r3,r10                      @ r3<- target translation
361    bl      dvmSelfVerificationSaveState @ save registers to shadow space
362    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
363    add     rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
364    bx      r10                         @ jump to the translation
365
366/*
367 * Restore PC, registers, and interpState to original values
368 * before jumping back to the interpreter.
369 */
370jitSVShadowRunEnd:
371    mov    r1,rFP                        @ pass ending fp
372    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
373    ldr    rPC,[r0,#offShadowSpace_startPC] @ restore PC
374    ldr    rFP,[r0,#offShadowSpace_fp]   @ restore FP
375    ldr    rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
376    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
377    cmp    r1,#0                         @ check for punt condition
378    beq    1f
379    mov    r2,#kJitSelfVerification      @ ask for self verification
380    str    r2,[rGLUE,#offGlue_jitState]
381    mov    r2,#kInterpEntryInstr         @ normal entry reason
382    str    r2,[rGLUE,#offGlue_entryPoint]
383    mov    r1,#1                         @ set changeInterp
384    b      common_gotoBail
385
3861:                                       @ exit to interpreter without check
387    EXPORT_PC()
388    adrl   rIBASE, dvmAsmInstructionStart
389    FETCH_INST()
390    GET_INST_OPCODE(ip)
391    GOTO_OPCODE(ip)
392#endif
393
394#endif
395
396/*
397 * Common code when a backward branch is taken.
398 *
399 * TODO: we could avoid a branch by just setting r0 and falling through
400 * into the common_periodicChecks code, and having a test on r0 at the
401 * end determine if we should return to the caller or update & branch to
402 * the next instr.
403 *
404 * On entry:
405 *  r9 is PC adjustment *in bytes*
406 */
407common_backwardBranch:
408    mov     r0, #kInterpEntryInstr
409    bl      common_periodicChecks
410#if defined(WITH_JIT)
411    GET_JIT_PROF_TABLE(r0)
412    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
413    cmp     r0,#0
414    bne     common_updateProfile
415    GET_INST_OPCODE(ip)
416    GOTO_OPCODE(ip)
417#else
418    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
419    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
420    GOTO_OPCODE(ip)                     @ jump to next instruction
421#endif
422
423
424/*
425 * Need to see if the thread needs to be suspended or debugger/profiler
426 * activity has begun.  If so, we suspend the thread or side-exit to
427 * the debug interpreter as appropriate.
428 *
429 * The common case is no activity on any of these, so we want to figure
430 * that out quickly.  If something is up, we can then sort out what.
431 *
432 * We want to be fast if the VM was built without debugger or profiler
433 * support, but we also need to recognize that the system is usually
434 * shipped with both of these enabled.
435 *
436 * TODO: reduce this so we're just checking a single location.
437 *
438 * On entry:
439 *  r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
440 *  r9 is trampoline PC adjustment *in bytes*
441 */
442common_periodicChecks:
443    ldr     r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
444
445    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
446    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
447
448    ldr     ip, [r3]                    @ ip<- suspendCount (int)
449
450    cmp     r1, #0                      @ debugger enabled?
451    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
452    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
453    orrne   ip, ip, r1                  @ ip<- suspendCount | debuggerActive
454    orrs    ip, ip, r2                  @ ip<- suspend|debugger|profiler; set Z
455
456    bxeq    lr                          @ all zero, return
457
458    /*
459     * One or more interesting events have happened.  Figure out what.
460     *
461     * If debugging or profiling are compiled in, we need to disambiguate.
462     *
463     * r0 still holds the reentry type.
464     */
465    ldr     ip, [r3]                    @ ip<- suspendCount (int)
466    cmp     ip, #0                      @ want suspend?
467    beq     1f                          @ no, must be debugger/profiler
468
469    stmfd   sp!, {r0, lr}               @ preserve r0 and lr
470#if defined(WITH_JIT)
471    /*
472     * Refresh the Jit's cached copy of profile table pointer.  This pointer
473     * doubles as the Jit's on/off switch.
474     */
475    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
476    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
477    ldr     r3, [r3] @ r3 <- pJitProfTable
478    EXPORT_PC()                         @ need for precise GC
479    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
480#else
481    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
482    EXPORT_PC()                         @ need for precise GC
483#endif
484    bl      dvmCheckSuspendPending      @ do full check, suspend if necessary
485    ldmfd   sp!, {r0, lr}               @ restore r0 and lr
486
487    /*
488     * Reload the debugger/profiler enable flags.  We're checking to see
489     * if either of these got set while we were suspended.
490     *
491     * We can't really avoid the #ifdefs here, because the fields don't
492     * exist when the feature is disabled.
493     */
494    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
495    cmp     r1, #0                      @ debugger enabled?
496    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
497    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
498    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
499
500    orrs    r1, r1, r2
501    beq     2f
502
5031:  @ debugger/profiler enabled, bail out; glue->entryPoint was set above
504    str     r0, [rGLUE, #offGlue_entryPoint]    @ store r0, need for debug/prof
505    add     rPC, rPC, r9                @ update rPC
506    mov     r1, #1                      @ "want switch" = true
507    b       common_gotoBail             @ side exit
508
5092:
510    bx      lr                          @ nothing to do, return
511
512
513/*
514 * The equivalent of "goto bail", this calls through the "bail handler".
515 *
516 * State registers will be saved to the "glue" area before bailing.
517 *
518 * On entry:
519 *  r1 is "bool changeInterp", indicating if we want to switch to the
520 *     other interpreter or just bail all the way out
521 */
522common_gotoBail:
523    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
524    mov     r0, rGLUE                   @ r0<- glue ptr
525    b       dvmMterpStdBail             @ call(glue, changeInterp)
526
527    @add     r1, r1, #1                  @ using (boolean+1)
528    @add     r0, rGLUE, #offGlue_jmpBuf  @ r0<- &glue->jmpBuf
529    @bl      _longjmp                    @ does not return
530    @bl      common_abort
531
532
533/*
534 * Common code for method invocation with range.
535 *
536 * On entry:
537 *  r0 is "Method* methodToCall", the method we're trying to call
538 */
539common_invokeMethodRange:
540.LinvokeNewRange:
541    @ prepare to copy args to "outs" area of current frame
542    movs    r2, rINST, lsr #8           @ r2<- AA (arg count) -- test for zero
543    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
544    beq     .LinvokeArgsDone            @ if no args, skip the rest
545    FETCH(r1, 2)                        @ r1<- CCCC
546
547    @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
548    @ (very few methods have > 10 args; could unroll for common cases)
549    add     r3, rFP, r1, lsl #2         @ r3<- &fp[CCCC]
550    sub     r10, r10, r2, lsl #2        @ r10<- "outs" area, for call args
551    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
5521:  ldr     r1, [r3], #4                @ val = *fp++
553    subs    r2, r2, #1                  @ count--
554    str     r1, [r10], #4               @ *outs++ = val
555    bne     1b                          @ ...while count != 0
556    ldrh    r3, [r0, #offMethod_outsSize]   @ r3<- methodToCall->outsSize
557    b       .LinvokeArgsDone
558
559/*
560 * Common code for method invocation without range.
561 *
562 * On entry:
563 *  r0 is "Method* methodToCall", the method we're trying to call
564 */
565common_invokeMethodNoRange:
566.LinvokeNewNoRange:
567    @ prepare to copy args to "outs" area of current frame
568    movs    r2, rINST, lsr #12          @ r2<- B (arg count) -- test for zero
569    SAVEAREA_FROM_FP(r10, rFP)          @ r10<- stack save area
570    FETCH(r1, 2)                        @ r1<- GFED (load here to hide latency)
571    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
572    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
573    beq     .LinvokeArgsDone
574
575    @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, r10=outs
576.LinvokeNonRange:
577    rsb     r2, r2, #5                  @ r2<- 5-r2
578    add     pc, pc, r2, lsl #4          @ computed goto, 4 instrs each
579    bl      common_abort                @ (skipped due to ARM prefetch)
5805:  and     ip, rINST, #0x0f00          @ isolate A
581    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vA (shift right 8, left 2)
582    mov     r0, r0                      @ nop
583    str     r2, [r10, #-4]!             @ *--outs = vA
5844:  and     ip, r1, #0xf000             @ isolate G
585    ldr     r2, [rFP, ip, lsr #10]      @ r2<- vG (shift right 12, left 2)
586    mov     r0, r0                      @ nop
587    str     r2, [r10, #-4]!             @ *--outs = vG
5883:  and     ip, r1, #0x0f00             @ isolate F
589    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vF
590    mov     r0, r0                      @ nop
591    str     r2, [r10, #-4]!             @ *--outs = vF
5922:  and     ip, r1, #0x00f0             @ isolate E
593    ldr     r2, [rFP, ip, lsr #2]       @ r2<- vE
594    mov     r0, r0                      @ nop
595    str     r2, [r10, #-4]!             @ *--outs = vE
5961:  and     ip, r1, #0x000f             @ isolate D
597    ldr     r2, [rFP, ip, lsl #2]       @ r2<- vD
598    mov     r0, r0                      @ nop
599    str     r2, [r10, #-4]!             @ *--outs = vD
6000:  @ fall through to .LinvokeArgsDone
601
602.LinvokeArgsDone: @ r0=methodToCall, r3=outSize, r9=regSize
603    ldr     r2, [r0, #offMethod_insns]  @ r2<- method->insns
604    ldr     rINST, [r0, #offMethod_clazz]  @ rINST<- method->clazz
605    @ find space for the new stack frame, check for overflow
606    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- stack save area
607    sub     r1, r1, r9, lsl #2          @ r1<- newFp (old savearea - regsSize)
608    SAVEAREA_FROM_FP(r10, r1)           @ r10<- newSaveArea
609@    bl      common_dumpRegs
610    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
611    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
612    cmp     r3, r9                      @ bottom < interpStackEnd?
613    ldr     r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
614    blo     .LstackOverflow             @ yes, this frame will overflow stack
615
616    @ set up newSaveArea
617#ifdef EASY_GDB
618    SAVEAREA_FROM_FP(ip, rFP)           @ ip<- stack save area
619    str     ip, [r10, #offStackSaveArea_prevSave]
620#endif
621    str     rFP, [r10, #offStackSaveArea_prevFrame]
622    str     rPC, [r10, #offStackSaveArea_savedPc]
623#if defined(WITH_JIT)
624    mov     r9, #0
625    str     r9, [r10, #offStackSaveArea_returnAddr]
626#endif
627    str     r0, [r10, #offStackSaveArea_method]
628    tst     r3, #ACC_NATIVE
629    bne     .LinvokeNative
630
631    /*
632    stmfd   sp!, {r0-r3}
633    bl      common_printNewline
634    mov     r0, rFP
635    mov     r1, #0
636    bl      dvmDumpFp
637    ldmfd   sp!, {r0-r3}
638    stmfd   sp!, {r0-r3}
639    mov     r0, r1
640    mov     r1, r10
641    bl      dvmDumpFp
642    bl      common_printNewline
643    ldmfd   sp!, {r0-r3}
644    */
645
646    ldrh    r9, [r2]                        @ r9 <- load INST from new PC
647    ldr     r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
648    mov     rPC, r2                         @ publish new rPC
649    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
650
651    @ Update "glue" values for the new method
652    @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
653    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
654    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
655#if defined(WITH_JIT)
656    GET_JIT_PROF_TABLE(r0)
657    mov     rFP, r1                         @ fp = newFp
658    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
659    mov     rINST, r9                       @ publish new rINST
660    str     r1, [r2, #offThread_curFrame]   @ self->curFrame = newFp
661    cmp     r0,#0
662    bne     common_updateProfile
663    GOTO_OPCODE(ip)                         @ jump to next instruction
664#else
665    mov     rFP, r1                         @ fp = newFp
666    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
667    mov     rINST, r9                       @ publish new rINST
668    str     r1, [r2, #offThread_curFrame]   @ self->curFrame = newFp
669    GOTO_OPCODE(ip)                         @ jump to next instruction
670#endif
671
672.LinvokeNative:
673    @ Prep for the native call
674    @ r0=methodToCall, r1=newFp, r10=newSaveArea
675    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
676    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
677    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
678    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
679    mov     r9, r3                      @ r9<- glue->self (preserve)
680
681    mov     r2, r0                      @ r2<- methodToCall
682    mov     r0, r1                      @ r0<- newFp (points to args)
683    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
684
685#ifdef ASSIST_DEBUGGER
686    /* insert fake function header to help gdb find the stack frame */
687    b       .Lskip
688    .type   dalvik_mterp, %function
689dalvik_mterp:
690    .fnstart
691    MTERP_ENTRY1
692    MTERP_ENTRY2
693.Lskip:
694#endif
695
696    @mov     lr, pc                      @ set return addr
697    @ldr     pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
698    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
699
700#if defined(WITH_JIT)
701    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
702#endif
703
704    @ native return; r9=self, r10=newSaveArea
705    @ equivalent to dvmPopJniLocals
706    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
707    ldr     r1, [r9, #offThread_exception] @ check for exception
708#if defined(WITH_JIT)
709    ldr     r3, [r3]                    @ r3 <- gDvmJit.pProfTable
710#endif
711    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
712    cmp     r1, #0                      @ null?
713    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
714#if defined(WITH_JIT)
715    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
716#endif
717    bne     common_exceptionThrown      @ no, handle exception
718
719    FETCH_ADVANCE_INST(3)               @ advance rPC, load rINST
720    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
721    GOTO_OPCODE(ip)                     @ jump to next instruction
722
723.LstackOverflow:    @ r0=methodToCall
724    mov     r1, r0                      @ r1<- methodToCall
725    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- self
726    bl      dvmHandleStackOverflow
727    b       common_exceptionThrown
728#ifdef ASSIST_DEBUGGER
729    .fnend
730#endif
731
732
733    /*
734     * Common code for method invocation, calling through "glue code".
735     *
736     * TODO: now that we have range and non-range invoke handlers, this
737     *       needs to be split into two.  Maybe just create entry points
738     *       that set r9 and jump here?
739     *
740     * On entry:
741     *  r0 is "Method* methodToCall", the method we're trying to call
742     *  r9 is "bool methodCallRange", indicating if this is a /range variant
743     */
744     .if    0
745.LinvokeOld:
746    sub     sp, sp, #8                  @ space for args + pad
747    FETCH(ip, 2)                        @ ip<- FEDC or CCCC
748    mov     r2, r0                      @ A2<- methodToCall
749    mov     r0, rGLUE                   @ A0<- glue
750    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
751    mov     r1, r9                      @ A1<- methodCallRange
752    mov     r3, rINST, lsr #8           @ A3<- AA
753    str     ip, [sp, #0]                @ A4<- ip
754    bl      dvmMterp_invokeMethod       @ call the C invokeMethod
755    add     sp, sp, #8                  @ remove arg area
756    b       common_resumeAfterGlueCall  @ continue to next instruction
757    .endif
758
759
760
761/*
762 * Common code for handling a return instruction.
763 *
764 * This does not return.
765 */
766common_returnFromMethod:
767.LreturnNew:
768    mov     r0, #kInterpEntryReturn
769    mov     r9, #0
770    bl      common_periodicChecks
771
772    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
773    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
774    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
775    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
776                                        @ r2<- method we're returning to
777    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
778    cmp     r2, #0                      @ is this a break frame?
779    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
780    mov     r1, #0                      @ "want switch" = false
781    beq     common_gotoBail             @ break frame, bail out completely
782
783    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
784    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
785    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
786    str     rFP, [r3, #offThread_curFrame]  @ self->curFrame = fp
787#if defined(WITH_JIT)
788    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
789    mov     rPC, r9                     @ publish new rPC
790    str     r1, [rGLUE, #offGlue_methodClassDex]
791    str     r10, [r3, #offThread_inJitCodeCache]  @ may return to JIT'ed land
792    cmp     r10, #0                      @ caller is compiled code
793    blxne   r10
794    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
795    GOTO_OPCODE(ip)                     @ jump to next instruction
796#else
797    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
798    mov     rPC, r9                     @ publish new rPC
799    str     r1, [rGLUE, #offGlue_methodClassDex]
800    GOTO_OPCODE(ip)                     @ jump to next instruction
801#endif
802
803    /*
804     * Return handling, calls through "glue code".
805     */
806     .if    0
807.LreturnOld:
808    SAVE_PC_FP_TO_GLUE()                @ export state
809    mov     r0, rGLUE                   @ arg to function
810    bl      dvmMterp_returnFromMethod
811    b       common_resumeAfterGlueCall
812    .endif
813
814
815/*
816 * Somebody has thrown an exception.  Handle it.
817 *
818 * If the exception processing code returns to us (instead of falling
819 * out of the interpreter), continue with whatever the next instruction
820 * now happens to be.
821 *
822 * This does not return.
823 */
824     .global dvmMterpCommonExceptionThrown
825dvmMterpCommonExceptionThrown:
826common_exceptionThrown:
827.LexceptionNew:
828    mov     r0, #kInterpEntryThrow
829    mov     r9, #0
830    bl      common_periodicChecks
831
832    ldr     r10, [rGLUE, #offGlue_self] @ r10<- glue->self
833    ldr     r9, [r10, #offThread_exception] @ r9<- self->exception
834    mov     r1, r10                     @ r1<- self
835    mov     r0, r9                      @ r0<- exception
836    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
837    mov     r3, #0                      @ r3<- NULL
838    str     r3, [r10, #offThread_exception] @ self->exception = NULL
839
840    /* set up args and a local for "&fp" */
841    /* (str sp, [sp, #-4]!  would be perfect here, but is discouraged) */
842    str     rFP, [sp, #-4]!             @ *--sp = fp
843    mov     ip, sp                      @ ip<- &fp
844    mov     r3, #0                      @ r3<- false
845    str     ip, [sp, #-4]!              @ *--sp = &fp
846    ldr     r1, [rGLUE, #offGlue_method] @ r1<- glue->method
847    mov     r0, r10                     @ r0<- self
848    ldr     r1, [r1, #offMethod_insns]  @ r1<- method->insns
849    mov     r2, r9                      @ r2<- exception
850    sub     r1, rPC, r1                 @ r1<- pc - method->insns
851    mov     r1, r1, asr #1              @ r1<- offset in code units
852
853    /* call, r0 gets catchRelPc (a code-unit offset) */
854    bl      dvmFindCatchBlock           @ call(self, relPc, exc, scan?, &fp)
855
856    /* fix earlier stack overflow if necessary; may trash rFP */
857    ldrb    r1, [r10, #offThread_stackOverflowed]
858    cmp     r1, #0                      @ did we overflow earlier?
859    beq     1f                          @ no, skip ahead
860    mov     rFP, r0                     @ save relPc result in rFP
861    mov     r0, r10                     @ r0<- self
862    mov     r1, r9                      @ r1<- exception
863    bl      dvmCleanupStackOverflow     @ call(self)
864    mov     r0, rFP                     @ restore result
8651:
866
867    /* update frame pointer and check result from dvmFindCatchBlock */
868    ldr     rFP, [sp, #4]               @ retrieve the updated rFP
869    cmp     r0, #0                      @ is catchRelPc < 0?
870    add     sp, sp, #8                  @ restore stack
871    bmi     .LnotCaughtLocally
872
873    /* adjust locals to match self->curFrame and updated PC */
874    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
875    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
876    str     r1, [rGLUE, #offGlue_method]    @ glue->method = new method
877    ldr     r2, [r1, #offMethod_clazz]      @ r2<- method->clazz
878    ldr     r3, [r1, #offMethod_insns]      @ r3<- method->insns
879    ldr     r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
880    add     rPC, r3, r0, asl #1             @ rPC<- method->insns + catchRelPc
881    str     r2, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
882
883    /* release the tracked alloc on the exception */
884    mov     r0, r9                      @ r0<- exception
885    mov     r1, r10                     @ r1<- self
886    bl      dvmReleaseTrackedAlloc      @ release the exception
887
888    /* restore the exception if the handler wants it */
889    FETCH_INST()                        @ load rINST from rPC
890    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
891    cmp     ip, #OP_MOVE_EXCEPTION      @ is it "move-exception"?
892    streq   r9, [r10, #offThread_exception] @ yes, restore the exception
893    GOTO_OPCODE(ip)                     @ jump to next instruction
894
895.LnotCaughtLocally: @ r9=exception, r10=self
896    /* fix stack overflow if necessary */
897    ldrb    r1, [r10, #offThread_stackOverflowed]
898    cmp     r1, #0                      @ did we overflow earlier?
899    movne   r0, r10                     @ if yes: r0<- self
900    movne   r1, r9                      @ if yes: r1<- exception
901    blne    dvmCleanupStackOverflow     @ if yes: call(self)
902
903    @ may want to show "not caught locally" debug messages here
904#if DVM_SHOW_EXCEPTION >= 2
905    /* call __android_log_print(prio, tag, format, ...) */
906    /* "Exception %s from %s:%d not caught locally" */
907    @ dvmLineNumFromPC(method, pc - method->insns)
908    ldr     r0, [rGLUE, #offGlue_method]
909    ldr     r1, [r0, #offMethod_insns]
910    sub     r1, rPC, r1
911    asr     r1, r1, #1
912    bl      dvmLineNumFromPC
913    str     r0, [sp, #-4]!
914    @ dvmGetMethodSourceFile(method)
915    ldr     r0, [rGLUE, #offGlue_method]
916    bl      dvmGetMethodSourceFile
917    str     r0, [sp, #-4]!
918    @ exception->clazz->descriptor
919    ldr     r3, [r9, #offObject_clazz]
920    ldr     r3, [r3, #offClassObject_descriptor]
921    @
922    ldr     r2, strExceptionNotCaughtLocally
923    ldr     r1, strLogTag
924    mov     r0, #3                      @ LOG_DEBUG
925    bl      __android_log_print
926#endif
927    str     r9, [r10, #offThread_exception] @ restore exception
928    mov     r0, r9                      @ r0<- exception
929    mov     r1, r10                     @ r1<- self
930    bl      dvmReleaseTrackedAlloc      @ release the exception
931    mov     r1, #0                      @ "want switch" = false
932    b       common_gotoBail             @ bail out
933
934
935    /*
936     * Exception handling, calls through "glue code".
937     */
938    .if     0
939.LexceptionOld:
940    SAVE_PC_FP_TO_GLUE()                @ export state
941    mov     r0, rGLUE                   @ arg to function
942    bl      dvmMterp_exceptionThrown
943    b       common_resumeAfterGlueCall
944    .endif
945
946
947/*
948 * After returning from a "glued" function, pull out the updated
949 * values and start executing at the next instruction.
950 */
951common_resumeAfterGlueCall:
952    LOAD_PC_FP_FROM_GLUE()              @ pull rPC and rFP out of glue
953    FETCH_INST()                        @ load rINST from rPC
954    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
955    GOTO_OPCODE(ip)                     @ jump to next instruction
956
957/*
958 * Invalid array index.
959 */
960common_errArrayIndex:
961    EXPORT_PC()
962    ldr     r0, strArrayIndexException
963    mov     r1, #0
964    bl      dvmThrowException
965    b       common_exceptionThrown
966
967/*
968 * Invalid array value.
969 */
970common_errArrayStore:
971    EXPORT_PC()
972    ldr     r0, strArrayStoreException
973    mov     r1, #0
974    bl      dvmThrowException
975    b       common_exceptionThrown
976
977/*
978 * Integer divide or mod by zero.
979 */
980common_errDivideByZero:
981    EXPORT_PC()
982    ldr     r0, strArithmeticException
983    ldr     r1, strDivideByZero
984    bl      dvmThrowException
985    b       common_exceptionThrown
986
987/*
988 * Attempt to allocate an array with a negative size.
989 */
990common_errNegativeArraySize:
991    EXPORT_PC()
992    ldr     r0, strNegativeArraySizeException
993    mov     r1, #0
994    bl      dvmThrowException
995    b       common_exceptionThrown
996
997/*
998 * Invocation of a non-existent method.
999 */
1000common_errNoSuchMethod:
1001    EXPORT_PC()
1002    ldr     r0, strNoSuchMethodError
1003    mov     r1, #0
1004    bl      dvmThrowException
1005    b       common_exceptionThrown
1006
1007/*
1008 * We encountered a null object when we weren't expecting one.  We
1009 * export the PC, throw a NullPointerException, and goto the exception
1010 * processing code.
1011 */
1012common_errNullObject:
1013    EXPORT_PC()
1014    ldr     r0, strNullPointerException
1015    mov     r1, #0
1016    bl      dvmThrowException
1017    b       common_exceptionThrown
1018
1019/*
1020 * For debugging, cause an immediate fault.  The source address will
1021 * be in lr (use a bl instruction to jump here).
1022 */
1023common_abort:
1024    ldr     pc, .LdeadFood
1025.LdeadFood:
1026    .word   0xdeadf00d
1027
1028/*
1029 * Spit out a "we were here", preserving all registers.  (The attempt
1030 * to save ip won't work, but we need to save an even number of
1031 * registers for EABI 64-bit stack alignment.)
1032 */
1033    .macro  SQUEAK num
1034common_squeak\num:
1035    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
1036    ldr     r0, strSqueak
1037    mov     r1, #\num
1038    bl      printf
1039    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
1040    bx      lr
1041    .endm
1042
1043    SQUEAK  0
1044    SQUEAK  1
1045    SQUEAK  2
1046    SQUEAK  3
1047    SQUEAK  4
1048    SQUEAK  5
1049
1050/*
1051 * Spit out the number in r0, preserving registers.
1052 */
1053common_printNum:
1054    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
1055    mov     r1, r0
1056    ldr     r0, strSqueak
1057    bl      printf
1058    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
1059    bx      lr
1060
1061/*
1062 * Print a newline, preserving registers.
1063 */
1064common_printNewline:
1065    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
1066    ldr     r0, strNewline
1067    bl      printf
1068    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
1069    bx      lr
1070
1071    /*
1072     * Print the 32-bit quantity in r0 as a hex value, preserving registers.
1073     */
1074common_printHex:
1075    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
1076    mov     r1, r0
1077    ldr     r0, strPrintHex
1078    bl      printf
1079    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
1080    bx      lr
1081
1082/*
1083 * Print the 64-bit quantity in r0-r1, preserving registers.
1084 */
1085common_printLong:
1086    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
1087    mov     r3, r1
1088    mov     r2, r0
1089    ldr     r0, strPrintLong
1090    bl      printf
1091    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
1092    bx      lr
1093
1094/*
1095 * Print full method info.  Pass the Method* in r0.  Preserves regs.
1096 */
1097common_printMethod:
1098    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
1099    bl      dvmMterpPrintMethod
1100    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
1101    bx      lr
1102
1103/*
1104 * Call a C helper function that dumps regs and possibly some
1105 * additional info.  Requires the C function to be compiled in.
1106 */
1107    .if     0
1108common_dumpRegs:
1109    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
1110    bl      dvmMterpDumpArmRegs
1111    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
1112    bx      lr
1113    .endif
1114
1115#if 0
1116/*
1117 * Experiment on VFP mode.
1118 *
1119 * uint32_t setFPSCR(uint32_t val, uint32_t mask)
1120 *
1121 * Updates the bits specified by "mask", setting them to the values in "val".
1122 */
1123setFPSCR:
1124    and     r0, r0, r1                  @ make sure no stray bits are set
1125    fmrx    r2, fpscr                   @ get VFP reg
1126    mvn     r1, r1                      @ bit-invert mask
1127    and     r2, r2, r1                  @ clear masked bits
1128    orr     r2, r2, r0                  @ set specified bits
1129    fmxr    fpscr, r2                   @ set VFP reg
1130    mov     r0, r2                      @ return new value
1131    bx      lr
1132
1133    .align  2
1134    .global dvmConfigureFP
1135    .type   dvmConfigureFP, %function
1136dvmConfigureFP:
1137    stmfd   sp!, {ip, lr}
1138    /* 0x03000000 sets DN/FZ */
1139    /* 0x00009f00 clears the six exception enable flags */
1140    bl      common_squeak0
1141    mov     r0, #0x03000000             @ r0<- 0x03000000
1142    add     r1, r0, #0x9f00             @ r1<- 0x03009f00
1143    bl      setFPSCR
1144    ldmfd   sp!, {ip, pc}
1145#endif
1146
1147
1148/*
1149 * String references, must be close to the code that uses them.
1150 */
1151    .align  2
1152strArithmeticException:
1153    .word   .LstrArithmeticException
1154strArrayIndexException:
1155    .word   .LstrArrayIndexException
1156strArrayStoreException:
1157    .word   .LstrArrayStoreException
1158strDivideByZero:
1159    .word   .LstrDivideByZero
1160strNegativeArraySizeException:
1161    .word   .LstrNegativeArraySizeException
1162strNoSuchMethodError:
1163    .word   .LstrNoSuchMethodError
1164strNullPointerException:
1165    .word   .LstrNullPointerException
1166
1167strLogTag:
1168    .word   .LstrLogTag
1169strExceptionNotCaughtLocally:
1170    .word   .LstrExceptionNotCaughtLocally
1171
1172strNewline:
1173    .word   .LstrNewline
1174strSqueak:
1175    .word   .LstrSqueak
1176strPrintHex:
1177    .word   .LstrPrintHex
1178strPrintLong:
1179    .word   .LstrPrintLong
1180
1181/*
1182 * Zero-terminated ASCII string data.
1183 *
1184 * On ARM we have two choices: do like gcc does, and LDR from a .word
1185 * with the address, or use an ADR pseudo-op to get the address
1186 * directly.  ADR saves 4 bytes and an indirection, but it's using a
1187 * PC-relative addressing mode and hence has a limited range, which
1188 * makes it not work well with mergeable string sections.
1189 */
1190    .section .rodata.str1.4,"aMS",%progbits,1
1191
1192.LstrBadEntryPoint:
1193    .asciz  "Bad entry point %d\n"
1194.LstrArithmeticException:
1195    .asciz  "Ljava/lang/ArithmeticException;"
1196.LstrArrayIndexException:
1197    .asciz  "Ljava/lang/ArrayIndexOutOfBoundsException;"
1198.LstrArrayStoreException:
1199    .asciz  "Ljava/lang/ArrayStoreException;"
1200.LstrClassCastException:
1201    .asciz  "Ljava/lang/ClassCastException;"
1202.LstrDivideByZero:
1203    .asciz  "divide by zero"
1204.LstrFilledNewArrayNotImpl:
1205    .asciz  "filled-new-array only implemented for objects and 'int'"
1206.LstrInternalError:
1207    .asciz  "Ljava/lang/InternalError;"
1208.LstrInstantiationError:
1209    .asciz  "Ljava/lang/InstantiationError;"
1210.LstrNegativeArraySizeException:
1211    .asciz  "Ljava/lang/NegativeArraySizeException;"
1212.LstrNoSuchMethodError:
1213    .asciz  "Ljava/lang/NoSuchMethodError;"
1214.LstrNullPointerException:
1215    .asciz  "Ljava/lang/NullPointerException;"
1216
1217.LstrLogTag:
1218    .asciz  "mterp"
1219.LstrExceptionNotCaughtLocally:
1220    .asciz  "Exception %s from %s:%d not caught locally\n"
1221
1222.LstrNewline:
1223    .asciz  "\n"
1224.LstrSqueak:
1225    .asciz  "<%d>"
1226.LstrPrintHex:
1227    .asciz  "<0x%x>"
1228.LstrPrintLong:
1229    .asciz  "<%lld>"
1230