aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/hipe/hipe_sparc_glue.S
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/hipe/hipe_sparc_glue.S')
-rw-r--r--erts/emulator/hipe/hipe_sparc_glue.S448
1 files changed, 448 insertions, 0 deletions
diff --git a/erts/emulator/hipe/hipe_sparc_glue.S b/erts/emulator/hipe/hipe_sparc_glue.S
new file mode 100644
index 0000000000..d1af5c43f5
--- /dev/null
+++ b/erts/emulator/hipe/hipe_sparc_glue.S
@@ -0,0 +1,448 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2001-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_sparc_asm.h"
+#include "hipe_literals.h"
+#define ASM
+#include "hipe_mode_switch.h"
+
+ .section ".text"
+ .align 4
+
+/*
+ * Enter Erlang from C.
+ * Switch to a new register window.
+ * Create a new frame on the C stack.
+ * Save C return address in the frame.
+ * Retrieve the process pointer from the C argument registers.
+ */
+#define ENTER_FROM_C \
+ save %sp, -112, %sp; \
+ st %i7, [%sp+96]
+
+/*
+ * Return to the calling C function.
+ * The return value is in %o0.
+ *
+ * .flush_exit saves NSP and other cached P state.
+ * .suspend_exit also saves RA.
+ */
+.suspend_exit:
+ /* save RA, so we can be resumed */
+ st RA, [P+P_NRA]
+.flush_exit:
+ /* restore C return address (hoisted to avoid stall) */
+ ld [%sp+96], %i7
+ /* flush cached P state */
+ SAVE_CACHED_STATE
+ /* restore callee-save registers, drop frame, return */
+ jmp %i7+8 /* ret */
+ restore %g0, %o0, %o0 /* kills P, moves our %o0 to caller's %o0 */
+
+/*
+ * int hipe_sparc_call_to_native(Process *p);
+ * Emulated code recursively calls native code.
+ */
+ .global hipe_sparc_call_to_native
+ .type hipe_sparc_call_to_native, #function
+ .proc 04 /* ??? */
+hipe_sparc_call_to_native:
+ ENTER_FROM_C
+ /* prepare to call the target */
+ ld [P+P_NCALLEE], TEMP_ARG0
+ /* get argument registers */
+ LOAD_ARG_REGS
+ /* cache some P state in registers */
+ RESTORE_CACHED_STATE
+/* FALLTHROUGH
+ *
+ * We export this return address so that hipe_mode_switch() can discover
+ * when native code tailcalls emulated code.
+ * Note: this is SPARC, so the value in the return address register
+ * is the address of the call/jmpl instruction itself.
+ */
+ .global nbif_return
+nbif_return:
+ /* call the target */
+ jmpl TEMP_ARG0, RA
+ nop
+/* FALLTHROUGH
+ *
+ * This is where native code returns to emulated code.
+ */
+ st %o0, [P+P_ARG0] /* save retval */
+ ba .flush_exit
+ mov HIPE_MODE_SWITCH_RES_RETURN, %o0
+
+/*
+ * int hipe_sparc_return_to_native(Process *p);
+ * Emulated code returns to its native code caller.
+ */
+ .global hipe_sparc_return_to_native
+ .type hipe_sparc_return_to_native, #function
+ .proc 04 /* ??? */
+hipe_sparc_return_to_native:
+ ENTER_FROM_C
+ /* restore return address */
+ ld [P+P_NRA], RA
+ /* cache some P state in registers */
+ RESTORE_CACHED_STATE
+ /*
+ * 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.
+ */
+ jmp RA+8
+ ld [P+P_ARG0], %o0 /* delay slot: get return value */
+
+/*
+ * int hipe_sparc_tailcall_to_native(Process *);
+ * Emulated code tailcalls native code.
+ */
+ .global hipe_sparc_tailcall_to_native
+ .type hipe_sparc_tailcall_to_native, #function
+ .proc 04 /* ??? */
+hipe_sparc_tailcall_to_native:
+ ENTER_FROM_C
+ /* prepare to call the target */
+ ld [P+P_NCALLEE], TEMP_ARG0
+ /* get argument registers */
+ LOAD_ARG_REGS
+ /* cache some P state in registers */
+ RESTORE_CACHED_STATE
+ /* call the target */
+ jmp TEMP_ARG0
+ ld [P+P_NRA], RA /* delay slot: restore return address */
+
+/*
+ * int hipe_sparc_throw_to_native(Process *p);
+ * Emulated code throws an exception to its native code caller.
+ */
+ .align 4
+ .global hipe_sparc_throw_to_native
+ .type hipe_sparc_throw_to_native, #function
+ .proc 04 /* ??? */
+hipe_sparc_throw_to_native:
+ ENTER_FROM_C
+ /* prepare to invoke handler */
+ ld [P+P_NCALLEE], TEMP_ARG0 /* set by hipe_find_handler() */
+ /* cache some P state in registers */
+ RESTORE_CACHED_STATE
+ /* invoke the handler */
+ jmp TEMP_ARG0
+ nop
+
+/*
+ * Native code calls emulated code via a stub
+ * which should look as follows:
+ *
+ * stub for f/N:
+ * sethi %hi(f's BEAM code address), TEMP_ARG0
+ * mov RA, TEMP_RA ! because the call below clobbers RA (%o7)
+ * or TEMP_ARG0, %lo(f's BEAM code address), TEMP_ARG0
+ * call nbif_callemu ! clobbers RA!
+ * mov N, TEMP_ARG1 ! delay slot: TEMP_ARG1 := ARITY
+ *
+ * XXX. Different stubs for different number of register parameters?
+ */
+ .global nbif_callemu
+nbif_callemu:
+ st TEMP_ARG0, [P+P_BEAM_IP]
+ st TEMP_ARG1, [P+P_ARITY]
+ st TEMP_RA, [P+P_NRA]
+ STORE_ARG_REGS
+ ba .flush_exit
+ mov HIPE_MODE_SWITCH_RES_CALL, %o0
+
+/*
+ * nbif_apply
+ */
+ .global nbif_apply
+nbif_apply:
+ STORE_ARG_REGS
+ ba .suspend_exit
+ mov HIPE_MODE_SWITCH_RES_APPLY, %o0
+
+/*
+ * 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:
+ st ARG5, [P+P_ARG5]
+#if NR_ARG_REGS > 6
+ mov ARG6, ARG5
+#else
+ ld [NSP+0], ARG5
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 5
+ .global nbif_ccallemu5
+nbif_ccallemu5:
+ st ARG4, [P+P_ARG4]
+#if NR_ARG_REGS > 5
+ mov ARG5, ARG4
+#else
+ ld [NSP+0], ARG4
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 4
+ .global nbif_ccallemu4
+nbif_ccallemu4:
+ st ARG3, [P+P_ARG3]
+#if NR_ARG_REGS > 4
+ mov ARG4, ARG3
+#else
+ ld [NSP+0], ARG3
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 3
+ .global nbif_ccallemu3
+nbif_ccallemu3:
+ st ARG2, [P+P_ARG2]
+#if NR_ARG_REGS > 3
+ mov ARG3, ARG2
+#else
+ ld [NSP+0], ARG2
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 2
+ .global nbif_ccallemu2
+nbif_ccallemu2:
+ st ARG1, [P+P_ARG1]
+#if NR_ARG_REGS > 2
+ mov ARG2, ARG1
+#else
+ ld [NSP+0], ARG1
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 1
+ .global nbif_ccallemu1
+nbif_ccallemu1:
+ st ARG0, [P+P_ARG0]
+#if NR_ARG_REGS > 1
+ mov ARG1, ARG0
+#else
+ ld [NSP+0], ARG0
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+ .global nbif_ccallemu0
+nbif_ccallemu0:
+ /* We use %o1 not ARG0 here because ARG0 is not
+ defined when NR_ARG_REGS == 0. */
+#if NR_ARG_REGS == 0
+ ld [NSP+0], %o1 /* get the closure */
+#endif
+ st %o1, [P+P_CLOSURE] /* save the closure */
+ ba .suspend_exit
+ mov HIPE_MODE_SWITCH_RES_CALL_CLOSURE, %o0
+
+/*
+ * This is where native code suspends.
+ */
+ .global nbif_suspend_0
+nbif_suspend_0:
+ ba .suspend_exit
+ mov HIPE_MODE_SWITCH_RES_SUSPEND, %o0
+
+/*
+ * Suspend from a receive (waiting for a message)
+ */
+ .global nbif_suspend_msg
+nbif_suspend_msg:
+ ba .suspend_exit
+ mov HIPE_MODE_SWITCH_RES_WAIT, %o0
+
+/*
+ * 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:
+ ld [P+P_FLAGS], %o1
+ /* this relies on F_TIMO (1<<2) fitting in a simm13 */
+ andcc %o1, F_TIMO, %g0
+ bz,a .suspend_exit
+ mov HIPE_MODE_SWITCH_RES_WAIT_TIMEOUT, %o0 /* delay slot */
+ /* timeout has occurred */
+ jmp RA+8
+ mov 0, %o0
+
+/*
+ * This is the default exception handler for native code.
+ */
+ .global nbif_fail
+nbif_fail:
+ ba .flush_exit
+ mov HIPE_MODE_SWITCH_RES_THROW, %o0
+
+ .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:
+ ba .gc_after_bif
+ mov 0, %o1 /* delay slot */
+nbif_1_gc_after_bif:
+ ba .gc_after_bif
+ mov 1, %o1 /* delay slot */
+nbif_2_gc_after_bif:
+ ba .gc_after_bif
+ mov 2, %o1 /* delay slot */
+nbif_3_gc_after_bif:
+ mov 3, %o1
+ /*FALLTHROUGH*/
+.gc_after_bif:
+ st %o1, [P+P_NARITY]
+ st TEMP_RA, [P+P_NRA]
+ st NSP, [P+P_NSP]
+ mov RA, TEMP_RA
+ mov %o0, %o1
+ call erts_gc_after_bif_call
+ mov P, %o0 /* delay slot */
+ mov TEMP_RA, RA
+ ld [P+P_NRA], TEMP_RA
+ jmp RA+8
+ st %g0, [P+P_NARITY] /* delay slot */
+
+/*
+ * We end up here when a BIF called from native signals an
+ * exceptional condition.
+ * HP has not been 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:
+ ba .nbif_simple_exception
+ mov 0, %o1 /* delay slot */
+ .global nbif_1_simple_exception
+nbif_1_simple_exception:
+ ba .nbif_simple_exception
+ mov 1, %o1 /* delay slot */
+ .global nbif_2_simple_exception
+nbif_2_simple_exception:
+ ba .nbif_simple_exception
+ mov 2, %o1 /* delay slot */
+ .global nbif_3_simple_exception
+nbif_3_simple_exception:
+ mov 3, %o1
+ /*FALLTHROUGH*/
+.nbif_simple_exception:
+ ld [P+P_FREASON], %o0
+ cmp %o0, FREASON_TRAP
+ beq .handle_trap
+ nop
+ /*
+ * Find and invoke catch handler (it must exist).
+ * HP has not been read from P.
+ * NSP has not been saved in P.
+ * TEMP_RA should contain the current call's return address.
+ * %o1 should contain the current call's arity.
+ */
+ st NSP, [P+P_NSP]
+ st TEMP_RA, [P+P_NRA]
+ st %o1, [P+P_NARITY]
+ /* find and prepare to invoke the handler */
+ call hipe_handle_exception /* Note: hipe_handle_exception() conses */
+ mov P, %o0 /* delay slot */
+ /* prepare to invoke the handler */
+ ld [P+P_NCALLEE], %o0 /* set by hipe_find_handler() */
+ RESTORE_CACHED_STATE
+ /* now invoke the handler */
+ jmp %o0
+ nop
+
+ /*
+ * A BIF failed with freason TRAP:
+ * - the BIF's arity is in %o1
+ * - the native RA was saved in TEMP_RA before the BIF call
+ * - HP has not been read from P
+ * - NSP has not been saved in P
+ */
+.handle_trap:
+ mov HIPE_MODE_SWITCH_RES_TRAP, %o0
+.bif_exit:
+ /* restore C return address (hoisted to avoid stall) */
+ ld [%sp+96], %i7
+ st NSP, [P+P_NSP]
+ st %o1, [P+P_NARITY]
+ st TEMP_RA, [P+P_NRA]
+ jmp %i7+8
+ restore %g0, %o0, %o0
+
+/*
+ * 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 */
+ nop /* ditto */
+ nop /* ditto */
+ /* This only handles a single return value.
+ If we have more, we need to save them in the PCB. */
+ mov %o0, TEMP_ARG0 /* save retval */
+ st NSP, [P+P_NSP]
+ call hipe_handle_stack_trap /* must not cons */
+ mov P, %o0 /* delay slot */
+ mov %o0, RA /* original RA */
+ jmp RA+8 /* resume at original RA */
+ mov TEMP_ARG0, %o0 /* delay slot: restore retval */
+
+/*
+ * hipe_sparc_inc_stack
+ * Caller saved its RA in TEMP_RA (== TEMP1) before calling us.
+ */
+ .global hipe_sparc_inc_stack
+hipe_sparc_inc_stack:
+ STORE_ARG_REGS
+ mov RA, TEMP_ARG0
+ st NSP, [P+P_NSP]
+ /* hipe_inc_nstack reads and writes NSP and NSP_LIMIT,
+ but does not access LR/RA, HP, or FCALLS. */
+ call hipe_inc_nstack
+ mov P, %o0 /* delay slot */
+ LOAD_ARG_REGS
+ /* this relies on LOAD_ARG_REGS not clobbering TEMP_ARG0 */
+ jmp TEMP_ARG0+8
+ ld [P+P_NSP], NSP /* delay slot */
+
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif