| /* |
| * 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. |
| @ |
| #ifdef __ARM_HAVE_PC_INTERWORK |
| mov lr, pc |
| ldr pc, [r4, #8] |
| #else |
| ldr ip, [r4, #8] |
| blx ip |
| #endif |
| |
| @ 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} |
| |
| #ifdef __ARM_HAVE_PC_INTERWORK |
| @ 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} |
| #else |
| ldmfd r5, {r4, r5, r6, fp, sp, lr} |
| bx lr |
| #endif |
| |
| #endif /*__ARM_EABI__*/ |