aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/hipe/hipe_amd64_glue.S
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/hipe/hipe_amd64_glue.S')
-rw-r--r--erts/emulator/hipe/hipe_amd64_glue.S443
1 files changed, 443 insertions, 0 deletions
diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S
new file mode 100644
index 0000000000..872c5dc9e3
--- /dev/null
+++ b/erts/emulator/hipe/hipe_amd64_glue.S
@@ -0,0 +1,443 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-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_amd64_asm.h"
+#include "hipe_literals.h"
+#define ASM
+#include "hipe_mode_switch.h"
+
+/*
+ * Note: the mode-switch entry points in hipe_amd64_glue.S have
+ * the same names as in hipe_x86_glue.S. This is intentional,
+ * as it allows using hipe_x86_glue.h with AMD64.
+ */
+
+/*
+ * Set up frame on C stack,
+ * save C callee-save registers,
+ * retrieve the process pointer from the parameters from C,
+ * SWITCH_C_TO_ERLANG.
+ *
+ * The end of the frame must be 16-byte aligned, otherwise
+ * calls to C may break. %rsp+8 is 16-byte aligned on entry,
+ * and six registers are to be saved, so a seventh word is
+ * added to make the resulting %rsp 16-byte aligned.
+ */
+#define ENTER_FROM_C \
+ /* save C callee-save registers on the C stack */ \
+ subq $(7*8), %rsp; \
+ movq %r15, 40(%rsp); \
+ movq %r14, 32(%rsp); \
+ movq %r13, 24(%rsp); \
+ movq %r12, 16(%rsp); \
+ movq %rbx, 8(%rsp); \
+ movq %rbp, (%rsp); \
+ /* get the process pointer */ \
+ movq %rdi, P; \
+ /* switch to native stack */ \
+ SWITCH_C_TO_ERLANG
+
+ .section ".text"
+
+/*
+ * int x86_call_to_native(Process *p);
+ * Emulated code recursively calls native code.
+ */
+ .align 4
+ .global x86_call_to_native
+ .global nbif_return
+x86_call_to_native:
+ ENTER_FROM_C
+ /* get argument registers */
+ LOAD_ARG_REGS
+ /* call the target */
+ NSP_CALL(*P_NCALLEE(P))
+/*
+ * 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.
+ */
+nbif_return:
+ movq %rax, P_ARG0(P) # save retval
+ movl $HIPE_MODE_SWITCH_RES_RETURN, %eax
+/* FALLTHROUGH to .flush_exit
+ *
+ * Return to the calling C function with result token in %eax.
+ *
+ * .nosave_exit saves no state
+ * .flush_exit saves cached P state
+ * .suspend_exit also saves RA
+ */
+.suspend_exit:
+ /* save RA, no-op on x86 */
+.flush_exit:
+ /* flush cached P state */
+ SAVE_CACHED_STATE
+.nosave_exit:
+ /* switch to C stack */
+ SWITCH_ERLANG_TO_C_QUICK
+ /* restore C callee-save registers, drop frame, return */
+ movq (%rsp), %rbp # kills P
+ movq 8(%rsp), %rbx
+ movq 16(%rsp), %r12
+ movq 24(%rsp), %r13
+ movq 32(%rsp), %r14
+ movq 40(%rsp), %r15 # kills HP
+ addq $(7*8), %rsp
+ ret
+
+/*
+ * Native code calls emulated code via a linker-generated
+ * stub (hipe_x86_loader.erl) which should look as follows:
+ *
+ * stub for f/N:
+ * movq $<f's BEAM code address>, P_BEAM_IP(P)
+ * movb $<N>, P_ARITY(P)
+ * jmp nbif_callemu
+ *
+ * XXX: Different stubs for different number of register parameters?
+ */
+ .align 4
+ .global nbif_callemu
+nbif_callemu:
+ STORE_ARG_REGS
+ movl $HIPE_MODE_SWITCH_RES_CALL, %eax
+ jmp .suspend_exit
+
+/*
+ * nbif_apply
+ */
+ .align 4
+ .global nbif_apply
+nbif_apply:
+ STORE_ARG_REGS
+ movl $HIPE_MODE_SWITCH_RES_APPLY, %eax
+ jmp .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
+ .align 4
+ .global nbif_ccallemu6
+nbif_ccallemu6:
+ movq ARG5, P_ARG5(P)
+#if NR_ARG_REGS > 6
+ movq ARG6, ARG5
+#else
+ movq 8(NSP), ARG5
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 5
+ .align 4
+ .global nbif_ccallemu5
+nbif_ccallemu5:
+ movq ARG4, P_ARG4(P)
+#if NR_ARG_REGS > 5
+ movq ARG5, ARG4
+#else
+ movq 8(NSP), ARG4
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 4
+ .align 4
+ .global nbif_ccallemu4
+nbif_ccallemu4:
+ movq ARG3, P_ARG3(P)
+#if NR_ARG_REGS > 4
+ movq ARG4, ARG3
+#else
+ movq 8(NSP), ARG3
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 3
+ .align 4
+ .global nbif_ccallemu3
+nbif_ccallemu3:
+ movq ARG2, P_ARG2(P)
+#if NR_ARG_REGS > 3
+ movq ARG3, ARG2
+#else
+ movq 8(NSP), ARG2
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 2
+ .align 4
+ .global nbif_ccallemu2
+nbif_ccallemu2:
+ movq ARG1, P_ARG1(P)
+#if NR_ARG_REGS > 2
+ movq ARG2, ARG1
+#else
+ movq 8(NSP), ARG1
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+#if NR_ARG_REGS >= 1
+ .align 4
+ .global nbif_ccallemu1
+nbif_ccallemu1:
+ movq ARG0, P_ARG0(P)
+#if NR_ARG_REGS > 1
+ movq ARG1, ARG0
+#else
+ movq 8(NSP), ARG0
+#endif
+ /*FALLTHROUGH*/
+#endif
+
+ .align 4
+ .global nbif_ccallemu0
+nbif_ccallemu0:
+ /* We use %rsi not ARG0 here because ARG0 is not
+ defined when NR_ARG_REGS == 0. */
+#if NR_ARG_REGS == 0
+ movq 8(NSP), %rsi
+#endif
+ movq %rsi, P_CLOSURE(P)
+ movl $HIPE_MODE_SWITCH_RES_CALL_CLOSURE, %eax
+ jmp .suspend_exit
+
+/*
+ * This is where native code suspends.
+ */
+ .align 4
+ .global nbif_suspend_0
+nbif_suspend_0:
+ movl $HIPE_MODE_SWITCH_RES_SUSPEND, %eax
+ jmp .suspend_exit
+
+/*
+ * Suspend from a receive (waiting for a message)
+ */
+ .align 4
+ .global nbif_suspend_msg
+nbif_suspend_msg:
+ movl $HIPE_MODE_SWITCH_RES_WAIT, %eax
+ jmp .suspend_exit
+
+/*
+ * Suspend from a receive with a timeout (waiting for a message)
+ * if (!(p->flags & F_TIMO)) { suspend }
+ * else { return 0; }
+ */
+ .align 4
+ .global nbif_suspend_msg_timeout
+nbif_suspend_msg_timeout:
+ movq P_FLAGS(P), %rax
+ /* this relies on F_TIMO (1<<2) fitting in a byte */
+ testb $F_TIMO, %al # F_TIMO set?
+ jz .no_timeout # if not set, suspend
+ /* timeout has occurred */
+ xorl %eax, %eax # return 0 to signal timeout
+ NSP_RET0
+.no_timeout:
+ movl $HIPE_MODE_SWITCH_RES_WAIT_TIMEOUT, %eax
+ jmp .suspend_exit
+
+/*
+ * int x86_return_to_native(Process *p);
+ * Emulated code returns to its native code caller.
+ */
+ .align 4
+ .global x86_return_to_native
+x86_return_to_native:
+ ENTER_FROM_C
+ /* get return value */
+ movq P_ARG0(P), %rax
+ /*
+ * Return using the stacked return address.
+ * The parameters were popped at the original native-to-emulated
+ * call (hipe_call_from_native_is_recursive), so a plain ret suffices.
+ */
+ NSP_RET0
+
+/*
+ * int x86_tailcall_to_native(Process *p);
+ * Emulated code tailcalls native code.
+ */
+ .align 4
+ .global x86_tailcall_to_native
+x86_tailcall_to_native:
+ ENTER_FROM_C
+ /* get argument registers */
+ LOAD_ARG_REGS
+ /* jump to the target label */
+ jmp *P_NCALLEE(P)
+
+/*
+ * int x86_throw_to_native(Process *p);
+ * Emulated code throws an exception to its native code caller.
+ */
+ .align 4
+ .global x86_throw_to_native
+x86_throw_to_native:
+ ENTER_FROM_C
+ /* invoke the handler */
+ jmp *P_NCALLEE(P) # set by hipe_find_handler()
+
+/*
+ * This is the default exception handler for native code.
+ */
+ .align 4
+ .global nbif_fail
+nbif_fail:
+ movl $HIPE_MODE_SWITCH_RES_THROW, %eax
+ jmp .flush_exit
+
+ .global nbif_0_gc_after_bif
+ .global nbif_1_gc_after_bif
+ .global nbif_2_gc_after_bif
+ .global nbif_3_gc_after_bif
+ .align 4
+nbif_0_gc_after_bif:
+ xorl %edx, %edx
+ jmp .gc_after_bif
+ .align 4
+nbif_1_gc_after_bif:
+ movl $1, %edx
+ jmp .gc_after_bif
+ .align 4
+nbif_2_gc_after_bif:
+ movl $2, %edx
+ jmp .gc_after_bif
+ .align 4
+nbif_3_gc_after_bif:
+ movl $3, %edx
+ /*FALLTHROUGH*/
+ .align 4
+.gc_after_bif:
+ movl %edx, P_NARITY(P) # Note: narity is a 32-bit field
+ subq $(16-8), %rsp
+ movq P, %rdi
+ movq %rax, %rsi
+ call erts_gc_after_bif_call
+ addq $(16-8), %rsp
+ movl $0, P_NARITY(P) # Note: narity is a 32-bit field
+ ret
+
+/*
+ * We end up here when a BIF called from native signals an
+ * exceptional condition.
+ * The stack/heap registers were just read from P.
+ */
+ .global nbif_0_simple_exception
+ .global nbif_1_simple_exception
+ .global nbif_2_simple_exception
+ .global nbif_3_simple_exception
+ .align 4
+nbif_0_simple_exception:
+ xorl %eax, %eax
+ jmp .nbif_simple_exception
+ .align 4
+nbif_1_simple_exception:
+ movl $1, %eax
+ jmp .nbif_simple_exception
+ .align 4
+nbif_2_simple_exception:
+ movl $2, %eax
+ jmp .nbif_simple_exception
+ .align 4
+nbif_3_simple_exception:
+ movl $3, %eax
+ /*FALLTHROUGH*/
+ .align 4
+.nbif_simple_exception:
+ cmpq $FREASON_TRAP, P_FREASON(P)
+ je .handle_trap
+ /*
+ * Find and invoke catch handler (it must exist).
+ * The stack/heap registers were just read from P.
+ * - %eax should contain the current call's arity
+ */
+ movl %eax, P_NARITY(P) # Note: narity is a 32-bit field
+ /* find and prepare to invoke the handler */
+ SWITCH_ERLANG_TO_C_QUICK # The cached state is clean and need not be saved.
+ movq P, %rdi
+ call hipe_handle_exception # Note: hipe_handle_exception() conses
+ SWITCH_C_TO_ERLANG # %rsp updated by hipe_find_handler()
+ /* now invoke the handler */
+ jmp *P_NCALLEE(P) # set by hipe_find_handler()
+
+ /*
+ * A BIF failed with freason TRAP:
+ * - the BIF's arity is in %rax
+ * - the native heap/stack/reds registers are saved in P
+ */
+.handle_trap:
+ movq %rax, P_NARITY(P)
+ movl $HIPE_MODE_SWITCH_RES_TRAP, %eax
+ jmp .nosave_exit
+
+/*
+ * nbif_stack_trap_ra: trap return address for maintaining
+ * the gray/white stack boundary
+ */
+ .global nbif_stack_trap_ra
+ .align 4
+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.
+ movq %rax, TEMP_RV # save retval
+ SWITCH_ERLANG_TO_C_QUICK
+ movq P, %rdi
+ call hipe_handle_stack_trap # must not cons; preserves TEMP_RV
+ movq %rax, %rdx # original RA
+ SWITCH_C_TO_ERLANG_QUICK
+ movq TEMP_RV, %rax # restore retval
+ jmp *%rdx # resume at original RA
+
+/*
+ * nbif_inc_stack_0
+ */
+ .global nbif_inc_stack_0
+ .align 4
+nbif_inc_stack_0:
+ SWITCH_ERLANG_TO_C_QUICK
+ STORE_ARG_REGS
+ movq P, %rdi
+ # hipe_inc_nstack reads and writes NSP and NSP_LIMIT,
+ # but does not access HP or FCALLS (or the non-amd64 NRA).
+ call hipe_inc_nstack
+ LOAD_ARG_REGS
+ SWITCH_C_TO_ERLANG_QUICK
+ NSP_RET0
+
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif