aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/hipe/hipe_arm_glue.S
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/hipe/hipe_arm_glue.S')
-rw-r--r--erts/emulator/hipe/hipe_arm_glue.S417
1 files changed, 417 insertions, 0 deletions
diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S
new file mode 100644
index 0000000000..5d626a5f69
--- /dev/null
+++ b/erts/emulator/hipe/hipe_arm_glue.S
@@ -0,0 +1,417 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/* $Id$
+ */
+#include "hipe_arm_asm.h"
+#include "hipe_literals.h"
+#define ASM
+#include "hipe_mode_switch.h"
+
+ .text
+ .p2align 2
+
+/*
+ * Enter Erlang from C.
+ * Create a new frame on the C stack.
+ * Save C callee-save registers in the frame.
+ * Do not clobber the C argument registers.
+ * Retrieve the process pointer from the C argument registers.
+ *
+ * Our C frame includes:
+ * - 9*4 == 36 bytes for saving r4-r11 and lr
+ * - 2*4 == 8 bytes for calls to hipe_bs_put_{big_integer,small_float}.
+ * They take 5-6 parameter words: 4 in registers and 1-2 on the stack.
+ * (They take 5 regular parameters, and an additional P parameter on SMP.)
+ * - 4 bytes to pad the frame size to a multiple of 8
+ */
+#define ENTER_FROM_C \
+ stmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,lr}; \
+ sub sp, sp, #12; \
+ mov P, r0; \
+ RESTORE_CACHED_STATE
+
+/*
+ * Return to the calling C function.
+ * The return value is in r0.
+ *
+ * .nosave_exit saves no state
+ * .flush_exit saves NSP and other cached P state.
+ * .suspend_exit also saves RA.
+ */
+.suspend_exit:
+ /* save RA, so we can be resumed */
+ str lr, [P, #P_NRA]
+.flush_exit:
+ /* flush cached P state */
+ SAVE_CACHED_STATE
+.nosave_exit:
+ /* restore callee-save registers, drop frame, return */
+ add sp, sp, #12
+ ldmfd sp!, {r4,r5,r6,r7,r8,r9,r10,r11,pc}
+
+/*
+ * int hipe_arm_call_to_native(Process *p);
+ * Emulated code recursively calls native code.
+ */
+ .global hipe_arm_call_to_native
+hipe_arm_call_to_native:
+ ENTER_FROM_C
+ /* get argument registers */
+ LOAD_ARG_REGS
+ /* call the target */
+ mov lr, pc
+ ldr pc, [P, #P_NCALLEE]
+/* FALLTHROUGH
+ *
+ * We export this return address so that hipe_mode_switch() can discover
+ * when native code tailcalls emulated code.
+ *
+ * This is where native code returns to emulated code.
+ */
+ .global nbif_return
+nbif_return:
+ str r0, [P, #P_ARG0] /* save retval */
+ mov r0, #HIPE_MODE_SWITCH_RES_RETURN
+ b .flush_exit
+
+/*
+ * int hipe_arm_return_to_native(Process *p);
+ * Emulated code returns to its native code caller.
+ */
+ .global hipe_arm_return_to_native
+hipe_arm_return_to_native:
+ ENTER_FROM_C
+ /* get return value */
+ ldr r0, [P, #P_ARG0]
+ /*
+ * Return using the current return address.
+ * The parameters were popped at the original native-to-emulated
+ * call (hipe_call_from_native_is_recursive), so a plain ret suffices.
+ */
+ ldr pc, [P, #P_NRA]
+
+/*
+ * int hipe_arm_tailcall_to_native(Process *p);
+ * Emulated code tailcalls native code.
+ */
+ .global hipe_arm_tailcall_to_native
+hipe_arm_tailcall_to_native:
+ ENTER_FROM_C
+ /* get argument registers */
+ LOAD_ARG_REGS
+ /* restore return address */
+ ldr lr, [P, #P_NRA]
+ /* call the target */
+ ldr pc, [P, #P_NCALLEE]
+
+/*
+ * int hipe_arm_throw_to_native(Process *p);
+ * Emulated code throws an exception to its native code caller.
+ */
+ .global hipe_arm_throw_to_native
+hipe_arm_throw_to_native:
+ ENTER_FROM_C
+ /* invoke the handler */
+ ldr pc, [P, #P_NCALLEE] /* set by hipe_find_handler() */
+
+/*
+ * Native code calls emulated code via a stub
+ * which should look as follows:
+ *
+ * stub for f/N:
+ * <set r8 to f's BEAM code address>
+ * <set r0 to N>
+ * b nbif_callemu
+ *
+ * XXX: Different stubs for different number of register parameters?
+ */
+ .global nbif_callemu
+nbif_callemu:
+ str r8, [P, #P_BEAM_IP]
+ str r0, [P, #P_ARITY]
+ STORE_ARG_REGS
+ mov r0, #HIPE_MODE_SWITCH_RES_CALL
+ b .suspend_exit
+
+/*
+ * nbif_apply
+ */
+ .global nbif_apply
+nbif_apply:
+ STORE_ARG_REGS
+ mov r0, #HIPE_MODE_SWITCH_RES_APPLY
+ b .suspend_exit
+
+/*
+ * Native code calls an emulated-mode closure via a stub defined below.
+ *
+ * The closure is appended as the last actual parameter, and parameters
+ * beyond the first few passed in registers are pushed onto the stack in
+ * left-to-right order.
+ * Hence, the location of the closure parameter only depends on the number
+ * of parameters in registers, not the total number of parameters.
+ */
+#if NR_ARG_REGS >= 6
+ .global nbif_ccallemu6
+nbif_ccallemu6:
+ str ARG5, [P, #P_ARG5]
+#if NR_ARG_REGS > 6
+ mov ARG5, ARG6
+#else
+ ldr ARG5, [NSP, #0]
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 5
+ .global nbif_ccallemu5
+nbif_ccallemu5:
+ str ARG4, [P, #P_ARG4]
+#if NR_ARG_REGS > 5
+ mov ARG4, ARG5
+#else
+ ldr ARG4, [NSP, #0]
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 4
+ .global nbif_ccallemu4
+nbif_ccallemu4:
+ str ARG3, [P, #P_ARG3]
+#if NR_ARG_REGS > 4
+ mov ARG3, ARG4
+#else
+ ldr ARG3, [NSP, #0]
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 3
+ .global nbif_ccallemu3
+nbif_ccallemu3:
+ str ARG2, [P, #P_ARG2]
+#if NR_ARG_REGS > 3
+ mov ARG2, ARG3
+#else
+ ldr ARG2, [NSP, #0]
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 2
+ .global nbif_ccallemu2
+nbif_ccallemu2:
+ str ARG1, [P, #P_ARG1]
+#if NR_ARG_REGS > 2
+ mov ARG1, ARG2
+#else
+ ldr ARG1, [NSP, #0]
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 1
+ .global nbif_ccallemu1
+nbif_ccallemu1:
+ str ARG0, [P, #P_ARG0]
+#if NR_ARG_REGS > 1
+ mov ARG0, ARG1
+#else
+ ldr ARG0, [NSP, #0]
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+ .global nbif_ccallemu0
+nbif_ccallemu0:
+ /* We use r1 not ARG0 here because ARG0 is not
+ defined when NR_ARG_REGS == 0. */
+#if NR_ARG_REGS == 0
+ ldr r1, [NSP, #0] /* get the closure */
+#endif
+ str r1, [P, #P_CLOSURE] /* save the closure */
+ mov r0, #HIPE_MODE_SWITCH_RES_CALL_CLOSURE
+ b .suspend_exit
+
+/*
+ * This is where native code suspends.
+ */
+ .global nbif_suspend_0
+nbif_suspend_0:
+ mov r0, #HIPE_MODE_SWITCH_RES_SUSPEND
+ b .suspend_exit
+
+/*
+ * Suspend from a receive (waiting for a message)
+ */
+ .global nbif_suspend_msg
+nbif_suspend_msg:
+ mov r0, #HIPE_MODE_SWITCH_RES_WAIT
+ b .suspend_exit
+
+/*
+ * Suspend from a receive with a timeout (waiting for a message)
+ * if (!(p->flags & F_TIMO)) { suspend }
+ * else { return 0; }
+ */
+ .global nbif_suspend_msg_timeout
+nbif_suspend_msg_timeout:
+ ldr r1, [P, #P_FLAGS]
+ mov r0, #HIPE_MODE_SWITCH_RES_WAIT_TIMEOUT
+ /* this relies on F_TIMO (1<<2) fitting in a uimm16 */
+ tst r1, #F_TIMO
+ beq .suspend_exit
+ /* timeout has occurred */
+ mov r0, #0
+ mov pc, lr
+
+/*
+ * This is the default exception handler for native code.
+ */
+ .global nbif_fail
+nbif_fail:
+ mov r0, #HIPE_MODE_SWITCH_RES_THROW
+ b .flush_exit /* no need to save RA */
+
+ .global nbif_0_gc_after_bif
+ .global nbif_1_gc_after_bif
+ .global nbif_2_gc_after_bif
+ .global nbif_3_gc_after_bif
+nbif_0_gc_after_bif:
+ mov r1, #0
+ b .gc_after_bif
+nbif_1_gc_after_bif:
+ mov r1, #1
+ b .gc_after_bif
+nbif_2_gc_after_bif:
+ mov r1, #2
+ b .gc_after_bif
+nbif_3_gc_after_bif:
+ mov r1, #3
+ /*FALLTHROUGH*/
+.gc_after_bif:
+ str r1, [P, #P_NARITY]
+ str TEMP_LR, [P, #P_NRA]
+ str NSP, [P, #P_NSP]
+ mov TEMP_LR, lr
+ mov r1, r0
+ mov r0, P
+ bl erts_gc_after_bif_call
+ mov lr, TEMP_LR
+ ldr TEMP_LR, [P, #P_NRA]
+ mov r1, #0
+ str r1, [P, #P_NARITY]
+ mov pc, lr
+
+/*
+ * We end up here when a BIF called from native signals an
+ * exceptional condition.
+ * HP was just read from P.
+ * NSP has not been saved in P.
+ * TEMP_LR contains a copy of LR
+ */
+ .global nbif_0_simple_exception
+nbif_0_simple_exception:
+ mov r1, #0
+ b .nbif_simple_exception
+ .global nbif_1_simple_exception
+nbif_1_simple_exception:
+ mov r1, #1
+ b .nbif_simple_exception
+ .global nbif_2_simple_exception
+nbif_2_simple_exception:
+ mov r1, #2
+ b .nbif_simple_exception
+ .global nbif_3_simple_exception
+nbif_3_simple_exception:
+ mov r1, #3
+ /*FALLTHROUGH*/
+.nbif_simple_exception:
+ ldr r0, [P, #P_FREASON]
+ cmp r0, #FREASON_TRAP
+ beq .handle_trap
+ /*
+ * Find and invoke catch handler (it must exist).
+ * HP was just read from P.
+ * NSP has not been saved in P.
+ * TEMP_LR should contain the current call's return address.
+ * r1 should contain the current call's arity.
+ */
+ str NSP, [P, #P_NSP]
+ str TEMP_LR, [P, #P_NRA]
+ str r1, [P, #P_NARITY]
+ /* find and prepare to invoke the handler */
+ mov r0, P
+ bl hipe_handle_exception /* Note: hipe_handle_exception() conses */
+ RESTORE_CACHED_STATE /* NSP updated by hipe_find_handler() */
+ /* now invoke the handler */
+ ldr pc, [P, #P_NCALLEE] /* set by hipe_find_handler() */
+
+ /*
+ * A BIF failed with freason TRAP:
+ * - the BIF's arity is in r1
+ * - the native RA was saved in TEMP_LR before the BIF call
+ * - HP was just read from P
+ * - NSP has not been saved in P
+ */
+.handle_trap:
+ mov r0, #HIPE_MODE_SWITCH_RES_TRAP
+ str NSP, [P, #P_NSP]
+ str r1, [P, #P_NARITY]
+ str TEMP_LR, [P, #P_NRA]
+ b .nosave_exit
+
+/*
+ * nbif_stack_trap_ra: trap return address for maintaining
+ * the gray/white stack boundary
+ */
+ .global nbif_stack_trap_ra
+nbif_stack_trap_ra: /* a return address, not a function */
+ # This only handles a single return value.
+ # If we have more, we need to save them in the PCB.
+ mov TEMP_ARG0, r0 /* save retval */
+ str NSP, [P, #P_NSP]
+ mov r0, P
+ bl hipe_handle_stack_trap /* must not cons */
+ mov lr, r0 /* original RA */
+ mov r0, TEMP_ARG0 /* restore retval */
+ mov pc, lr /* resume at original RA */
+
+/*
+ * hipe_arm_inc_stack
+ * Caller saved its LR in TEMP_LR (== TEMP1) before calling us.
+ */
+ .global hipe_arm_inc_stack
+hipe_arm_inc_stack:
+ STORE_ARG_REGS
+ mov TEMP_ARG0, lr
+ str NSP, [P, #P_NSP]
+ mov r0, P
+ # hipe_inc_nstack reads and writes NSP and NSP_LIMIT,
+ # but does not access LR/RA, HP, or FCALLS.
+ bl hipe_inc_nstack
+ ldr NSP, [P, #P_NSP]
+ LOAD_ARG_REGS
+ # this relies on LOAD_ARG_REGS not clobbering TEMP_ARG0
+ mov pc, TEMP_ARG0
+
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif