summaryrefslogtreecommitdiffstats
path: root/vm/arch/arm/CallOldABI.S
blob: d75f6f793da9bb9d2c5b3ec2ce1bfeabdcc9609e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * JNI method invocation.  This is used to call a C/C++ JNI method.  The
 * argument list has to be pushed onto the native stack according to
 * local calling conventions.
 *
 * This version supports the "old" ARM ABI.
 */

#include <machine/cpu-features.h>

#ifndef __ARM_EABI__

/*
Function prototype:

void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
    const u4* argv, const char* signature, void* func, JValue* pReturn)

The method we are calling has the form:

  return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
    -or-
  return_type func(JNIEnv* pEnv, Object* this, ...)

We receive a collection of 32-bit values which correspond to arguments from
the interpreter (e.g. float occupies one, double occupies two).  It's up to
us to convert these into local calling conventions.
 */

/*
ARM ABI notes:

r0-r3 hold first 4 args to a method
r9 is given special treatment in some situations, but not for us
r10 (sl) seems to be generally available
r11 (fp) is used by gcc
r12 (ip) is scratch -- not preserved across method calls
r13 (sp) should be managed carefully in case a signal arrives
r14 (lr) must be preserved
r15 (pc) can be tinkered with directly

r0 holds returns <= 4 bytes
r0-r1 hold returns of 5-8 bytes, low word in r0

Stack is "full descending".  Only the arguments that don't fit in the first 4
registers are placed on the stack.  "sp" points at the first stacked argument
(i.e. the 5th arg).

VFP: single-precision results in s0, double-precision results in d0.

Happily we don't have to do anything special here -- the args from the
interpreter work directly as C/C++ args on ARM (with the "classic" ABI).
*/

    .text
    .align  2
    .global dvmPlatformInvoke
    .type   dvmPlatformInvoke, %function

/*
On entry:
  r0  JNIEnv
  r1  clazz (NULL for virtual method calls, non-NULL for static)
  r2  arg info (ignored)
  r3  argc
  [sp]     argv
  [sp,#4]  signature (ignored)
  [sp,#8]  func
  [sp,#12] pReturn
*/
dvmPlatformInvoke:
    @ Standard gcc stack frame setup.  We don't need to push the original
    @ sp or the current pc if "-fomit-frame-pointer" is in use for the
    @ rest of the code.  If we don't plan to use a debugger we can speed
    @ this up a little.
    mov     ip, sp
    stmfd   sp!, {r4, r5, r6, fp, ip, lr, pc}
    sub     fp, ip, #4          @ set up fp, same way gdb does

    @ We need to push a variable number of arguments onto the stack.
    @ Rather than keep a count and pop them off after, we just hold on to
    @ the stack pointers.
    @
    @ In theory we don't need to keep sp -- we can do an ldmdb instead of
    @ an ldmia -- but we're doing the gcc frame trick where we push the
    @ pc on with stmfd and don't pop it off.
    mov     r4, ip
    mov     r5, sp

    @ argc is already in a scratch register (r3).  Put argv into one.  Note
    @ argv can't go into r0-r3 because we need to use it to load those.
    ldr     ip, [r4, #0]        @ ip <-- argv

    @ Is this a static method?
    cmp     r1, #0

    @ No: set r1 to *argv++, and set argc--.
    @ (r0=pEnv, r1=this)
    ldreq   r1, [ip], #4
    subeq   r3, r3, #1

    @ While we still have the use of r2/r3, copy excess args from argv
    @ to the stack.  We need to push the last item in argv first, and we
    @ want the first two items in argv to end up in r2/r3.
    subs    r3, r3, #2
    ble     .Lno_copy

    @ If there are N args, we want to skip 0 and 1, and push (N-1)..2.  We
    @ have N-2 in r3.  If we set argv=argv+1, we can count from N-2 to 1
    @ inclusive and get the right set of args.
    add     r6, ip, #4

.Lcopy:
    @ *--sp = argv[count]
    ldr     r2, [r6, r3, lsl #2]
    str     r2, [sp, #-4]!
    subs    r3, r3, #1
    bne     .Lcopy

.Lno_copy:
    @ Load the last two args.  These are coming out of the interpreted stack,
    @ and the VM preserves an overflow region at the bottom, so it should be
    @ safe to load two items out of argv even if we're at the end.
    ldr     r2, [ip]
    ldr     r3, [ip, #4]

    @ Show time.  Tuck the pc into lr and load the pc from the method
    @ address supplied by the caller.  The value for "pc" is offset by 8
    @ due to instruction prefetching.
    @
    mov     lr, pc
    ldr     pc, [r4, #8]

    @ We're back, result is in r0 or (for long/double) r0-r1.
    @
    @ In theory, we need to use the "return type" arg to figure out what
    @ we have and how to return it.  However, unless we have an FPU,
    @ all we need to do is copy r0-r1 into the JValue union.
    ldr     ip, [r4, #12]
    stmia   ip, {r0-r1}

    @ Restore the registers we saved and return.  Note this remaps stuff,
    @ so that "sp" comes from "ip", "pc" comes from "lr", and the "pc"
    @ we pushed on evaporates when we restore "sp".
    ldmfd   r5, {r4, r5, r6, fp, sp, pc}

#endif /*__ARM_EABI__*/