diff options
Diffstat (limited to 'erts/emulator/hipe/hipe_sparc_glue.S')
-rw-r--r-- | erts/emulator/hipe/hipe_sparc_glue.S | 448 |
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 |