1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * JNI method invocation.  This is used to call a C/C++ JNI method.  The
19 * argument list has to be pushed onto the native stack according to
20 * local calling conventions.
21 *
22 * This version supports the "old" ARM ABI.
23 */
24
25#include <machine/cpu-features.h>
26
27#ifndef __ARM_EABI__
28
29/*
30Function prototype:
31
32void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
33    const u4* argv, const char* signature, void* func, JValue* pReturn)
34
35The method we are calling has the form:
36
37  return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
38    -or-
39  return_type func(JNIEnv* pEnv, Object* this, ...)
40
41We receive a collection of 32-bit values which correspond to arguments from
42the interpreter (e.g. float occupies one, double occupies two).  It's up to
43us to convert these into local calling conventions.
44 */
45
46/*
47ARM ABI notes:
48
49r0-r3 hold first 4 args to a method
50r9 is given special treatment in some situations, but not for us
51r10 (sl) seems to be generally available
52r11 (fp) is used by gcc
53r12 (ip) is scratch -- not preserved across method calls
54r13 (sp) should be managed carefully in case a signal arrives
55r14 (lr) must be preserved
56r15 (pc) can be tinkered with directly
57
58r0 holds returns <= 4 bytes
59r0-r1 hold returns of 5-8 bytes, low word in r0
60
61Stack is "full descending".  Only the arguments that don't fit in the first 4
62registers are placed on the stack.  "sp" points at the first stacked argument
63(i.e. the 5th arg).
64
65VFP: single-precision results in s0, double-precision results in d0.
66
67Happily we don't have to do anything special here -- the args from the
68interpreter work directly as C/C++ args on ARM (with the "classic" ABI).
69*/
70
71    .text
72    .align  2
73    .global dvmPlatformInvoke
74    .type   dvmPlatformInvoke, %function
75
76/*
77On entry:
78  r0  JNIEnv
79  r1  clazz (NULL for virtual method calls, non-NULL for static)
80  r2  arg info (ignored)
81  r3  argc
82  [sp]     argv
83  [sp,#4]  signature (ignored)
84  [sp,#8]  func
85  [sp,#12] pReturn
86*/
87dvmPlatformInvoke:
88    @ Standard gcc stack frame setup.  We don't need to push the original
89    @ sp or the current pc if "-fomit-frame-pointer" is in use for the
90    @ rest of the code.  If we don't plan to use a debugger we can speed
91    @ this up a little.
92    mov     ip, sp
93    stmfd   sp!, {r4, r5, r6, fp, ip, lr, pc}
94    sub     fp, ip, #4          @ set up fp, same way gdb does
95
96    @ We need to push a variable number of arguments onto the stack.
97    @ Rather than keep a count and pop them off after, we just hold on to
98    @ the stack pointers.
99    @
100    @ In theory we don't need to keep sp -- we can do an ldmdb instead of
101    @ an ldmia -- but we're doing the gcc frame trick where we push the
102    @ pc on with stmfd and don't pop it off.
103    mov     r4, ip
104    mov     r5, sp
105
106    @ argc is already in a scratch register (r3).  Put argv into one.  Note
107    @ argv can't go into r0-r3 because we need to use it to load those.
108    ldr     ip, [r4, #0]        @ ip <-- argv
109
110    @ Is this a static method?
111    cmp     r1, #0
112
113    @ No: set r1 to *argv++, and set argc--.
114    @ (r0=pEnv, r1=this)
115    ldreq   r1, [ip], #4
116    subeq   r3, r3, #1
117
118    @ While we still have the use of r2/r3, copy excess args from argv
119    @ to the stack.  We need to push the last item in argv first, and we
120    @ want the first two items in argv to end up in r2/r3.
121    subs    r3, r3, #2
122    ble     .Lno_copy
123
124    @ If there are N args, we want to skip 0 and 1, and push (N-1)..2.  We
125    @ have N-2 in r3.  If we set argv=argv+1, we can count from N-2 to 1
126    @ inclusive and get the right set of args.
127    add     r6, ip, #4
128
129.Lcopy:
130    @ *--sp = argv[count]
131    ldr     r2, [r6, r3, lsl #2]
132    str     r2, [sp, #-4]!
133    subs    r3, r3, #1
134    bne     .Lcopy
135
136.Lno_copy:
137    @ Load the last two args.  These are coming out of the interpreted stack,
138    @ and the VM preserves an overflow region at the bottom, so it should be
139    @ safe to load two items out of argv even if we're at the end.
140    ldr     r2, [ip]
141    ldr     r3, [ip, #4]
142
143    @ Show time.  Tuck the pc into lr and load the pc from the method
144    @ address supplied by the caller.  The value for "pc" is offset by 8
145    @ due to instruction prefetching.
146    @
147#ifdef __ARM_HAVE_PC_INTERWORK
148    mov     lr, pc
149    ldr     pc, [r4, #8]
150#else
151    ldr     ip, [r4, #8]
152    blx     ip
153#endif
154
155    @ We're back, result is in r0 or (for long/double) r0-r1.
156    @
157    @ In theory, we need to use the "return type" arg to figure out what
158    @ we have and how to return it.  However, unless we have an FPU,
159    @ all we need to do is copy r0-r1 into the JValue union.
160    ldr     ip, [r4, #12]
161    stmia   ip, {r0-r1}
162
163#ifdef __ARM_HAVE_PC_INTERWORK
164    @ Restore the registers we saved and return.  Note this remaps stuff,
165    @ so that "sp" comes from "ip", "pc" comes from "lr", and the "pc"
166    @ we pushed on evaporates when we restore "sp".
167    ldmfd   r5, {r4, r5, r6, fp, sp, pc}
168#else
169    ldmfd   r5, {r4, r5, r6, fp, sp, lr}
170    bx      lr
171#endif
172
173#endif /*__ARM_EABI__*/
174