|  | /* | 
|  | * 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__*/ |