#include "globals.h"
#include "lispregs.h"
#include "validate.h"
#include "genesis/closure.h"
#include "genesis/static-symbols.h"
#ifdef LISP_FEATURE_SB_THREAD
#include "genesis/thread.h"

// For LDP/STP to work right
#if THREAD_CONTROL_STACK_POINTER_OFFSET - THREAD_CONTROL_FRAME_POINTER_OFFSET != 8
#error "THREAD_CONTROL_FRAME_POINTER_OFFSET - THREAD_CONTROL_STACK_POINTER_OFFSET not sequential"
#endif

#endif

#ifdef LISP_FEATURE_DARWIN
#define TYPE(name)
#define SIZE(name)
#define GNAME(var) _##var

.macro  LOAD_GNAME, dest, symbol
    adrp    \dest, _\symbol@PAGE
    ldr     \dest, [\dest, _\symbol@PAGEOFF]
.endm

#elif defined(LISP_FEATURE_WIN32)
#define TYPE(name)
#define SIZE(name)
#define GNAME(var) var
.macro  LOAD_GNAME, dest, symbol
    adrp    \dest, \symbol
    ldr     \dest, [\dest, #:lo12:\symbol]
.endm

#else
#define TYPE(name) .type name,%function
#define SIZE(name) .size name,.-name
#define GNAME(var) var

#ifdef LIBSBCL
.macro  LOAD_GNAME, dest, symbol
    adrp    \dest, :got:\symbol
    ldr     \dest, [\dest, #:got_lo12:\symbol]
    ldr     \dest, [\dest]
.endm
#else
.macro  LOAD_GNAME, dest, symbol
    adrp    \dest, \symbol
    ldr     \dest, [\dest, #:lo12:\symbol]
.endm
#endif
#endif


#ifdef LISP_FEATURE_SB_THREAD
.macro ENTER_PA
        str reg_wNULL,[reg_THREAD,THREAD_PSEUDO_ATOMIC_BITS_OFFSET]
.endm

.macro LEAVE_PA
        str wzr,[reg_THREAD,THREAD_PSEUDO_ATOMIC_BITS_OFFSET]
        ldr reg_wTMP,[reg_THREAD,THREAD_PSEUDO_ATOMIC_BITS_OFFSET+4]
        cbz reg_wTMP,1f
	brk trap_PendingInterrupt
1:
.endm
#else
#define LOAD_STATIC_SYMBOL_VALUE(value,sym) \
        mov reg_TMP,((sym)-NIL+SYMBOL_VALUE_OFFSET) ;\
        ldr value,[reg_NULL, reg_TMP]

#define STORE_STATIC_SYMBOL_VALUE(value,sym) \
        mov reg_TMP,((sym)-NIL+SYMBOL_VALUE_OFFSET) ;\
        str value,[reg_NULL, reg_TMP]
#define ENTER_PA \
        STORE_STATIC_SYMBOL_VALUE(reg_CFP,PSEUDO_ATOMIC_ATOMIC)

#define LEAVE_PA \
        STORE_STATIC_SYMBOL_VALUE(reg_NULL,PSEUDO_ATOMIC_ATOMIC) ;\
        LOAD_STATIC_SYMBOL_VALUE(reg_TMP,PSEUDO_ATOMIC_INTERRUPTED) ;\
        cbz reg_TMP, 1f     ; \
	brk trap_PendingInterrupt         ; \
1:
#endif

#define THREAD_SAVED_CSP_OFFSET (- N_WORD_BYTES)

#ifdef LISP_FEATURE_OS_THREAD_STACK
	.align 2
	.global GNAME(funcall1_switching_stack)
	TYPE(funcall1_switching_stack)
GNAME(funcall1_switching_stack):
        /* The arguments are switched, funcall1_switching_stack(arg, function)
           to avoid shuffling registers
        */
	/* save FP and LR to old stack */
	stp	x29, x30, [sp, #-16]!
	mov	x29, sp
	/* switch to new stack */
	ldr	x9, [x0, #THREAD_ALIEN_STACK_START_OFFSET]
	add	x9, x9, #ALIEN_STACK_SIZE >> 12, lsl #12
#if ALIEN_STACK_SIZE % 0x1000 != 0
	add	x9, x9, #ALIEN_STACK_SIZE & 0xfff
#endif
	sub	sp, x9, #16
	/* call function */
 	blr	x1
	/* restore old SP, FP, LR and return */
	add	sp, x29, #16
	ldp	x29, x30, [x29]
	ret
	SIZE(funcall1_switching_stack)
#endif


	.align 2
	.global	GNAME(call_into_lisp)
	TYPE(call_into_lisp)
GNAME(call_into_lisp):
	// At this point, we have:
	// X0 - function
	// X1 - pointer to args
	// X2 - number of args (unboxed)
	// There will be no more than three args, so we don't need to
	// worry about parameters to be passed on the stack.

	// X19-X28 are callee-saved registers.

        stp     x29,x30, [sp,#-160]! // save the return address in x30 aka LR
        stp     x19,x20, [sp,#16]
        stp     x21,x22, [sp,#32]
        stp     x23,x24, [sp,#48]
        stp     x25,x26, [sp,#64]
        stp     x27,x28, [sp,#80]

        stp     d8,d9, [sp,#96]
        stp     d10,d11, [sp,#112]
        stp     d12,d13, [sp,#128]
        stp     d14,d15, [sp,#144]

	// Start by finding NIL.
#ifdef LISP_FEATURE_RELOCATABLE_STATIC_SPACE
#ifdef LISP_FEATURE_DARWIN
        adrp    reg_NULL, _STATIC_SPACE_START@PAGE
        ldr     reg_NULL, [reg_NULL, _STATIC_SPACE_START@PAGEOFF]
#else
        ldr     reg_NULL, STATIC_SPACE_START
#endif
        add     reg_NULL, reg_NULL, #NIL_VALUE_OFFSET
#else
	ldr	reg_NULL, =NIL
#endif

	// Set up NARGS.
	lsl	reg_NARGS, x2, #N_FIXNUM_TAG_BITS

	// Move args pointer out of the way of the args to be loaded.
	mov	reg_R9, x1

	// Move the function to its passing location.
	mov	reg_LEXENV, x0

#ifdef LISP_FEATURE_SB_THREAD
#if defined(LISP_FEATURE_WIN32)
	// Load OUR_TLS_INDEX (sbcl_thread_tls_index) and call TlsGetValue
	adrp	x0, sbcl_thread_tls_index
	ldr	w0, [x0, #:lo12:sbcl_thread_tls_index]
	bl	TlsGetValue
	mov	reg_THREAD, x0
//        ldp     x29,x30, [sp]
#else
#ifdef LISP_FEATURE_GCC_TLS
#ifdef LISP_FEATURE_DARWIN

        adrp    x0, _current_thread@TLVPPAGE
        ldr     x0, [x0, _current_thread@TLVPPAGEOFF]
        ldr     x8, [x0]
        blr     x8
        ldr     reg_THREAD, [x0]
#elif defined LISP_FEATURE_OPENBSD
        adrp    x0, :got:__emutls_v.current_thread
        ldr     x0, [x0, :got_lo12:__emutls_v.current_thread]
        bl      __emutls_get_address
        ldr     reg_THREAD, [x0]
#else
	adrp	x0, :gottprel:current_thread
	ldr	x0, [x0, #:gottprel_lo12:current_thread]
	mrs	reg_THREAD, tpidr_el0
	ldr	reg_THREAD, [reg_THREAD,x0]
#endif
#else
	adrp	x0, :got:current_thread
	ldr	x0, [x0, :got_lo12:current_thread]
	ldr	x0, [x0]
        bl      pthread_getspecific
        mov     reg_THREAD, x0
#endif
#endif
#endif
	// Clear the boxed registers that don't already have something
	// in them.
        mov     reg_R0, #0
        mov     reg_R1, #0
        mov     reg_R2, #0
        mov     reg_R3, #0
        mov     reg_R4, #0
        mov     reg_R5, #0
        mov     reg_R6, #0
        mov     reg_R7, #0
#if !defined(LISP_FEATURE_DARWIN) && !defined(LISP_FEATURE_WIN32)
        // x18 is reserved on Darwin (macOS) and Windows (TEB pointer)
        mov     reg_R8, #0
#endif
        mov     reg_R10, #0
#ifndef LISP_FEATURE_SB_THREAD
        mov     reg_R11, #0
#endif

	// Find the lisp stack and frame pointers.  We're allocating a
	// new lisp stack frame, so load the stack pointer into CFP.
#ifdef LISP_FEATURE_SB_THREAD
	ldp     x8, reg_CSP, [reg_THREAD, THREAD_CONTROL_FRAME_POINTER_OFFSET]
#else
	ldr	x8, =GNAME(current_control_frame_pointer)
	ldr	reg_CSP, =GNAME(current_control_stack_pointer)
	ldr	x8, [x8]
	ldr	reg_CSP, [reg_CSP]
#endif

	// Clear FFCA, so the runtime knows that we're "in lisp".
#ifdef LISP_FEATURE_SB_THREAD
#ifdef LISP_FEATURE_NONSTOP_FOREIGN_CALL
	ldr     x3, [reg_THREAD, THREAD_SAVED_CSP_OFFSET]
        str     x3, [sp,#-16]!
        cbz     x3, 1f
	str     xzr, [reg_THREAD, THREAD_SAVED_CSP_OFFSET]
1:

#endif
        str     xzr,[reg_THREAD, THREAD_CONTROL_STACK_POINTER_OFFSET]
#else
	ldr     reg_NL3, =GNAME(foreign_function_call_active)
	str     xzr, [reg_NL3]
#endif

	// Set up the "frame link"
	str     x8, [reg_CSP]
        mov     reg_CFP, reg_CSP

	// Load our function args.
	cbz reg_NARGS, Lno_args
        cmp reg_NARGS, #2
        beq Ltwo_args
        bmi Lone_arg
Lthree_args:
	ldr	reg_R2, [reg_R9, #16]
Ltwo_args:
	ldr	reg_R1, [reg_R9, #8]
Lone_arg:
	ldr	reg_R0, [reg_R9]
Lno_args:

        // load CARDTABLE-TN. reg_NAME macros aren't autogenerated for native asm code
        // and it hardly seems worth #defining it to use in one assembly statement.
        LOAD_GNAME x28, gc_card_mark

        // Load the closure-fun (or simple-fun-self), in case we're
	// trying to call a closure.
        ldr     reg_LR, [reg_LEXENV, #CLOSURE_FUN_OFFSET]
        blr     reg_LR

	// Correct stack pointer for return processing.
        csel reg_CSP, reg_OCFP, reg_CSP, eq

        // Return value
        mov     x0, reg_R0

// Save the lisp stack and frame pointers.
#ifdef LISP_FEATURE_SB_THREAD
	stp     reg_CFP,reg_CSP, [reg_THREAD, THREAD_CONTROL_FRAME_POINTER_OFFSET]
#ifdef LISP_FEATURE_NONSTOP_FOREIGN_CALL
        ldr     x3, [sp],#16
        cbz     x3, 1f
	str     x3, [reg_THREAD, THREAD_SAVED_CSP_OFFSET]
1:
#endif
#else
	ENTER_PA

	ldr	reg_NFP, =GNAME(current_control_frame_pointer)
	str	reg_CFP, [reg_NFP]
	ldr	reg_OCFP, =GNAME(current_control_stack_pointer)
	str	reg_CSP, [reg_OCFP]

	// Set FFCA, so the runtime knows that we're not "in lisp".
	ldr     reg_OCFP, =GNAME(foreign_function_call_active)
	str     reg_OCFP, [reg_OCFP]

        LEAVE_PA
#endif

	// Restore saved registers.

        ldp     d14,d15, [sp,#144]
        ldp     d12,d13, [sp,#128]
        ldp     d10,d11, [sp,#112]
        ldp     d8,d9, [sp,#96]


        ldp     x27,x28, [sp,#80]
        ldp     x25,x26, [sp,#64]
        ldp     x23,x24, [sp,#48]
        ldp     x21,x22, [sp,#32]
        ldp     x19,x20, [sp,#16]
        ldp     x29,x30, [sp],#160

	ret
	SIZE(call_into_lisp)

#ifdef LISP_FEATURE_SB_THREAD
	.align 2
	.global	GNAME(call_into_c)
	TYPE(call_into_c)
GNAME(call_into_c):
	// At this point, we have:
	// R9 -- C function to call.
        // LR -- Return address within the code component.
        // X0-X7 arguments
        // All other C arguments are already stashed on the C stack.

        // Build a Lisp stack frame.
        // Can store two values above the stack pointer, interrupts ignore them.
        stp     reg_CFP, reg_LR, [reg_CSP]
        add     reg_R10, reg_CSP, #2*8

        mov     reg_LEXENV, reg_LR

        // Save the lisp stack and frame pointers.
#ifdef LISP_FEATURE_SB_THREAD
        stp     reg_CSP, reg_R10, [reg_THREAD, THREAD_CONTROL_FRAME_POINTER_OFFSET]
#else
        ENTER_PA
	ldr	reg_NFP, =GNAME(current_control_stack_pointer)
	str	reg_CSP, [reg_NFP]
	ldr	reg_NFP, =GNAME(current_control_frame_pointer)
	str	reg_R10, [reg_NFP]

        // Set FFCA, so the runtime knows that we're not "in lisp".
	ldr     reg_OCFP, =GNAME(foreign_function_call_active)
	str     reg_OCFP, [reg_OCFP]
        LEAVE_PA
#endif

#if defined LISP_FEATURE_SB_SAFEPOINT || defined LISP_FEATURE_NONSTOP_FOREIGN_CALL
	/* OK to run GC without stopping this thread from this point on. */
	str     reg_CSP, [reg_THREAD, THREAD_SAVED_CSP_OFFSET]
#endif

        // And call the C function.
        //
        // R9 is important for undefined_alien_function.
        blr      reg_R9

        // We're back.  Our main tasks are to move the C return value
        // to where Lisp expects it, and to re-establish the Lisp
        // environment.

        // Blank the boxed registers. R9, R10, LEXENV do not need to be
        // blanked because its saved and restored by C and the above
        // code puts fixnums into it.

        mov     reg_R0, #0
        mov     reg_R1, #0
        mov     reg_R2, #0
        mov     reg_R3, #0
        mov     reg_R4, #0
        mov     reg_R5, #0
        mov     reg_R6, #0
        mov     reg_R7, #0
#if !defined(LISP_FEATURE_DARWIN) && !defined(LISP_FEATURE_WIN32)
        // x18 is reserved on Darwin (macOS) and Windows (TEB pointer)
        mov     reg_R8,#0
#endif
#ifndef LISP_FEATURE_SB_THREAD
        mov     reg_R11,#0
#endif

#if defined LISP_FEATURE_SB_SAFEPOINT || defined LISP_FEATURE_NONSTOP_FOREIGN_CALL
	/* No longer OK to run GC except at safepoints. */
	str     xzr, [reg_THREAD, THREAD_SAVED_CSP_OFFSET]
#endif

#ifdef LISP_FEATURE_SB_THREAD
        str     xzr, [reg_THREAD, THREAD_CONTROL_STACK_POINTER_OFFSET]
#else
	// Clear FFCA, so the runtime knows that we're "in lisp".
	str     xzr, [reg_OCFP]
#endif
        mov     reg_LR, reg_LEXENV

        ret

	SIZE(call_into_c)
#endif

        .global GNAME(fun_end_breakpoint_guts)
        TYPE(fun_end_breakpoint_guts)
GNAME(fun_end_breakpoint_guts):
        /* Multiple Value return */
        b.eq Lmultiple_value_return
        /* Single value return: The eventual return will now use the
           multiple values return convention but with a return values
           count of one. */

        /* Multiple values are stored relative to reg_OCFP, which we
           set to be the current top-of-stack. */
        mov reg_OCFP, reg_CSP

        /* Reserve a save location for the one value we have. */
        add reg_CSP, reg_CSP, N_WORD_BYTES

        /* Record the number of values we have as a FIXNUM. */
        mov reg_NARGS, (1 << N_FIXNUM_TAG_BITS)

        /* Blank the remaining arg-passing registers. */
        mov reg_R1, reg_NULL
        mov reg_R2, reg_NULL
        mov reg_R3, reg_NULL

Lmultiple_value_return:

        .global	GNAME(fun_end_breakpoint_trap)
        TYPE(fun_end_breakpoint_trap)
        /* The actual magic trap. */
GNAME(fun_end_breakpoint_trap):
        brk trap_FunEndBreakpoint

        /* Finally, the debugger needs to know where the end of the
        fun_end_breakpoint_guts are, so that it may calculate its size
        in order to populate out a suitably-sized code object. */
        .global GNAME(fun_end_breakpoint_end)
GNAME(fun_end_breakpoint_end):

        .align 2
	.global	GNAME(do_pending_interrupt)
	TYPE(do_pending_interrupt)
GNAME(do_pending_interrupt):
        brk trap_PendingInterrupt
	ret

#ifdef __ELF__
// Mark the object as not requiring an executable stack.
.section .note.GNU-stack,"",%progbits
#endif
