summaryrefslogtreecommitdiff
path: root/vm/arch/arm/CallOldABI.S
diff options
context:
space:
mode:
Diffstat (limited to 'vm/arch/arm/CallOldABI.S')
-rw-r--r--vm/arch/arm/CallOldABI.S173
1 files changed, 173 insertions, 0 deletions
diff --git a/vm/arch/arm/CallOldABI.S b/vm/arch/arm/CallOldABI.S
new file mode 100644
index 0000000..2463d3c
--- /dev/null
+++ b/vm/arch/arm/CallOldABI.S
@@ -0,0 +1,173 @@
+/*
+ * 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__*/