// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

;-----------------------------------------------------------------------------
; Macro used to assign an alternate name to a symbol containing characters normally disallowed in a symbol
; name (e.g. C++ decorated names).
    MACRO
      SETALIAS   $name, $symbol
        GBLS    $name
$name   SETS    "|$symbol|"
    MEND

;-----------------------------------------------------------------------------
; Basic extension of Assembler Macros- For Consistency

    MACRO
        EPILOG_BRANCH_REG $reg

        EPILOG_NOP br $reg
    MEND
;-----------------------------------------------------------------------------

    MACRO
        EPILOG_BRANCH $_target

        EPILOG_NOP b $_target
    MEND
;-----------------------------------------------------------------------------
; The following group of macros assist in implementing prologs and epilogs for methods that set up some
; subclass of TransitionFrame. They ensure that the SP is 16-byte aligned at the conclusion of the prolog

;-----------------------------------------------------------------------------
; Define the prolog for a TransitionFrame-based method. This macro should be called first in the method and
; comprises the entire prolog (i.e. don't modify SP after calling this).The locals must be 8 byte aligned
;
    MACRO
        PROLOG_WITH_TRANSITION_BLOCK $extraLocals, $SaveFPArgs

        GBLA __PWTB_FloatArgumentRegisters
        GBLA __PWTB_ArgumentRegisters
        GBLA __PWTB_ArgumentRegister_FirstArg ; We save the x8 register ahead of the first argument, so this
                                              ; is different from the start of the argument register save area.
        GBLA __PWTB_StackAlloc
        GBLA __PWTB_TransitionBlock
        GBLL __PWTB_SaveFPArgs

        IF "$SaveFPArgs" != ""
__PWTB_SaveFPArgs SETL $SaveFPArgs
        ELSE
__PWTB_SaveFPArgs SETL {true}
        ENDIF

        IF "$extraLocals" != ""
__PWTB_FloatArgumentRegisters SETA $extraLocals
        ELSE
__PWTB_FloatArgumentRegisters SETA 0
        ENDIF

        IF __PWTB_FloatArgumentRegisters:MOD:16 != 0
__PWTB_FloatArgumentRegisters SETA __PWTB_FloatArgumentRegisters + 8
        ENDIF

        IF __PWTB_SaveFPArgs
__PWTB_TransitionBlock SETA __PWTB_FloatArgumentRegisters + SIZEOF__FloatArgumentRegisters
        ELSE
__PWTB_TransitionBlock SETA __PWTB_FloatArgumentRegisters
        ENDIF

__PWTB_StackAlloc SETA __PWTB_TransitionBlock
__PWTB_ArgumentRegisters SETA __PWTB_StackAlloc + 104
__PWTB_ArgumentRegister_FirstArg SETA __PWTB_ArgumentRegisters + 8

        PROLOG_SAVE_REG_PAIR   fp, lr, #-176!
        ; Spill callee saved registers
        PROLOG_SAVE_REG_PAIR   x19, x20, #16
        PROLOG_SAVE_REG_PAIR   x21, x22, #32
        PROLOG_SAVE_REG_PAIR   x23, x24, #48
        PROLOG_SAVE_REG_PAIR   x25, x26, #64
        PROLOG_SAVE_REG_PAIR   x27, x28, #80

        ; Allocate space for the rest of the frame
        PROLOG_STACK_ALLOC  __PWTB_StackAlloc

        ; Spill argument registers.
        SAVE_ARGUMENT_REGISTERS        sp, __PWTB_ArgumentRegisters

        IF __PWTB_SaveFPArgs
        SAVE_FLOAT_ARGUMENT_REGISTERS  sp, __PWTB_FloatArgumentRegisters
        ENDIF

    MEND

;-----------------------------------------------------------------------------
; Provides a matching epilog to PROLOG_WITH_TRANSITION_BLOCK and returns to caller.
;
    MACRO
        EPILOG_WITH_TRANSITION_BLOCK_RETURN

        EPILOG_STACK_FREE                 __PWTB_StackAlloc

        EPILOG_RESTORE_REG_PAIR   x19, x20, #16
        EPILOG_RESTORE_REG_PAIR   x21, x22, #32
        EPILOG_RESTORE_REG_PAIR   x23, x24, #48
        EPILOG_RESTORE_REG_PAIR   x25, x26, #64
        EPILOG_RESTORE_REG_PAIR   x27, x28, #80
        EPILOG_RESTORE_REG_PAIR   fp, lr,   #176!
		EPILOG_RETURN
    MEND

;-----------------------------------------------------------------------------
; Provides a matching epilog to PROLOG_WITH_TRANSITION_BLOCK and ends by preparing for tail-calling.
; Since this is a tail call argument registers are restored.
;
    MACRO
        EPILOG_WITH_TRANSITION_BLOCK_TAILCALL

        IF __PWTB_SaveFPArgs
        RESTORE_FLOAT_ARGUMENT_REGISTERS  sp, __PWTB_FloatArgumentRegisters
        ENDIF

        RESTORE_ARGUMENT_REGISTERS        sp, __PWTB_ArgumentRegisters
        EPILOG_STACK_FREE                 __PWTB_StackAlloc

        EPILOG_RESTORE_REG_PAIR   x19, x20, #16
        EPILOG_RESTORE_REG_PAIR   x21, x22, #32
        EPILOG_RESTORE_REG_PAIR   x23, x24, #48
        EPILOG_RESTORE_REG_PAIR   x25, x26, #64
        EPILOG_RESTORE_REG_PAIR   x27, x28, #80
        EPILOG_RESTORE_REG_PAIR   fp, lr,   #176!
    MEND

;-----------------------------------------------------------------------------
; Macro used to end a function with explicit _End label
    MACRO
    LEAF_END_MARKED $FuncName

    LCLS __EndLabelName
__EndLabelName SETS "$FuncName":CC:"_End"
    EXPORT $__EndLabelName
$__EndLabelName

    LEAF_END $FuncName

    ; make sure this symbol gets its own address
    nop

    MEND
;-----------------------------------------------------------------------------
; Macro use for enabling C++ to know where to patch code at runtime.
    MACRO
    PATCH_LABEL $FuncName
$FuncName
    EXPORT $FuncName

    MEND

;-----------------------------------------------------------------------------
; Macro used to check (in debug builds only) whether the stack is 16-bytes aligned (a requirement before calling
; out into C++/OS code). Invoke this directly after your prolog (if the stack frame size is fixed) or directly
; before a call (if you have a frame pointer and a dynamic stack). A breakpoint will be invoked if the stack
; is misaligned.
;
    MACRO
        CHECK_STACK_ALIGNMENT

#ifdef _DEBUG
        add     x9, sp, xzr
        tst     x9, #15
        beq     %F0
        EMIT_BREAKPOINT
0
#endif
    MEND

;-----------------------------------------------------------------------------
; The Following sets of SAVE_*_REGISTERS expect the memory to be reserved and
; base address to be passed in $reg
;

; Reserve 72 bytes of memory before calling  SAVE_ARGUMENT_REGISTERS
    MACRO
       SAVE_ARGUMENT_REGISTERS $reg, $offset

       GBLA __PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET

       IF "$offset" != ""
__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET SETA $offset
       ELSE
__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET SETA 0
       ENDIF

        str                    x8, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET)]
        stp                    x0, x1, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 8)]
        stp                    x2, x3, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 24)]
        stp                    x4, x5, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 40)]
        stp                    x6, x7, [$reg, #(__PWTB_SAVE_ARGUMENT_REGISTERS_OFFSET + 56)]

    MEND

; Reserve 128 bytes of memory before calling  SAVE_FLOAT_ARGUMENT_REGISTERS
    MACRO
       SAVE_FLOAT_ARGUMENT_REGISTERS $reg, $offset

       GBLA __PWTB_SAVE_FLOAT_ARGUMENT_REGISTERS_OFFSET

       IF "$offset" != ""
__PWTB_SAVE_FLOAT_ARGUMENT_REGISTERS_OFFSET SETA $offset
       ELSE
__PWTB_SAVE_FLOAT_ARGUMENT_REGISTERS_OFFSET SETA 0
       ENDIF

        stp                    q0, q1, [$reg, #(__PWTB_SAVE_FLOAT_ARGUMENT_REGISTERS_OFFSET)]
        stp                    q2, q3, [$reg, #(__PWTB_SAVE_FLOAT_ARGUMENT_REGISTERS_OFFSET + 32)]
        stp                    q4, q5, [$reg, #(__PWTB_SAVE_FLOAT_ARGUMENT_REGISTERS_OFFSET + 64)]
        stp                    q6, q7, [$reg, #(__PWTB_SAVE_FLOAT_ARGUMENT_REGISTERS_OFFSET + 96)]
    MEND

    MACRO
       RESTORE_ARGUMENT_REGISTERS $reg, $offset

       GBLA __PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET

       IF "$offset" != ""
__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET SETA $offset
       ELSE
__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET SETA 0
       ENDIF

        ldr                    x8, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET)]
        ldp                    x0, x1, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 8)]
        ldp                    x2, x3, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 24)]
        ldp                    x4, x5, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 40)]
        ldp                    x6, x7, [$reg, #(__PWTB_RESTORE_ARGUMENT_REGISTERS_OFFSET + 56)]

    MEND

    MACRO
       RESTORE_FLOAT_ARGUMENT_REGISTERS $reg, $offset

       GBLA __PWTB_RESTORE_FLOAT_ARGUMENT_REGISTERS_OFFSET

       IF "$offset" != ""
__PWTB_RESTORE_FLOAT_ARGUMENT_REGISTERS_OFFSET SETA $offset
       ELSE
__PWTB_RESTORE_FLOAT_ARGUMENT_REGISTERS_OFFSET SETA 0
       ENDIF

        ldp                    q0, q1, [$reg, #(__PWTB_RESTORE_FLOAT_ARGUMENT_REGISTERS_OFFSET)]
        ldp                    q2, q3, [$reg, #(__PWTB_RESTORE_FLOAT_ARGUMENT_REGISTERS_OFFSET + 32)]
        ldp                    q4, q5, [$reg, #(__PWTB_RESTORE_FLOAT_ARGUMENT_REGISTERS_OFFSET + 64)]
        ldp                    q6, q7, [$reg, #(__PWTB_RESTORE_FLOAT_ARGUMENT_REGISTERS_OFFSET + 96)]
    MEND

; ------------------------------------------------------------------
; Macro to generate Redirection Stubs
;
; $reason : reason for redirection
;                     Eg. GCThreadControl
; NOTE: If you edit this macro, make sure you update GetCONTEXTFromRedirectedStubStackFrame.
; This function is used by both the personality routine and the debugger to retrieve the original CONTEXT.
        MACRO
        GenerateRedirectedHandledJITCaseStub $reason

        GBLS __RedirectionStubFuncName
        GBLS __RedirectionStubEndFuncName
        GBLS __RedirectionFuncName
__RedirectionStubFuncName SETS "RedirectedHandledJITCaseFor":CC:"$reason":CC:"_Stub"
__RedirectionStubEndFuncName SETS "RedirectedHandledJITCaseFor":CC:"$reason":CC:"_StubEnd"
__RedirectionFuncName SETS "|?RedirectedHandledJITCaseFor":CC:"$reason":CC:"@Thread@@CAXXZ|"

        IMPORT $__RedirectionFuncName

        NESTED_ENTRY $__RedirectionStubFuncName
        PROLOG_SAVE_REG_PAIR    fp, lr, #-16!
        sub sp, sp, #16                          ; stack slot for CONTEXT * and padding

        ;REDIRECTSTUB_SP_OFFSET_CONTEXT is defined in asmconstants.h and is used in GetCONTEXTFromRedirectedStubStackFrame
        ;If CONTEXT is not saved at 0 offset from SP it must be changed as well.
        ASSERT REDIRECTSTUB_SP_OFFSET_CONTEXT == 0

        ; Stack alignment. This check is necessary as this function can be
        ; entered before complete execution of the prolog of another function.
        and x8, fp, #15
        sub sp, sp, x8


        ;
        ; Save a copy of the redirect CONTEXT*.
        ; This is needed for the debugger to unwind the stack.
        ;
        bl GetCurrentSavedRedirectContext
        str x0, [sp]

        ;
        ; Fetch the interrupted pc and save it as our return address.
        ;
        ldr x1, [x0, #CONTEXT_Pc]
        str x1, [fp, #8]

        ;
        ; Call target, which will do whatever we needed to do in the context
        ; of the target thread, and will RtlRestoreContext when it is done.
        ;
        bl $__RedirectionFuncName

        EMIT_BREAKPOINT ; Unreachable

; Put a label here to tell the debugger where the end of this function is.
$__RedirectionStubEndFuncName
        EXPORT $__RedirectionStubEndFuncName

        NESTED_END

        MEND

;-----------------------------------------------------------------------------
; Macro to get a pointer to the Thread* object for the currently executing thread
;
__tls_array     equ 0x58    ;; offsetof(TEB, ThreadLocalStoragePointer)

    EXTERN _tls_index

    GBLS __SECTIONREL_gCurrentThreadInfo
__SECTIONREL_gCurrentThreadInfo SETS "SECTIONREL_gCurrentThreadInfo"

    MACRO
        INLINE_GETTHREAD $destReg, $trashReg

        ;; The following macro variables are just some assembler magic to get the name of the 32-bit version
        ;; of $trashReg. It does it by string manipulation. Replaces something like x3 with w3.
        LCLS TrashRegister32Bit
TrashRegister32Bit SETS "$trashReg"
TrashRegister32Bit SETS "w":CC:("$TrashRegister32Bit":RIGHT:((:LEN:TrashRegister32Bit) - 1))

        ldr         $trashReg, =_tls_index
        ldr         $TrashRegister32Bit, [$trashReg]
        ldr         $destReg, [xpr, #__tls_array]
        ldr         $destReg, [$destReg, $trashReg lsl #3]
        ldr         $trashReg, =$__SECTIONREL_gCurrentThreadInfo
        ldr         $trashReg, [$trashReg]
        ldr         $destReg, [$destReg, $trashReg]        ; return gCurrentThreadInfo.m_pThread
    MEND

;-----------------------------------------------------------------------------
; INLINE_GETTHREAD_CONSTANT_POOL macro has to be used after the last function in the .asm file that used
; INLINE_GETTHREAD. Optionally, it can be also used after any function that used INLINE_GETTHREAD
; to improve density, or to reduce distance between the constant pool and its use.
;
    SETALIAS gCurrentThreadInfo, ?gCurrentThreadInfo@@3UThreadLocalInfo@@A

    MACRO
        INLINE_GETTHREAD_CONSTANT_POOL

        EXTERN $gCurrentThreadInfo

    ;; Section relocs are 32 bits. Using an extra DCD initialized to zero for 8-byte alignment.
$__SECTIONREL_gCurrentThreadInfo
        DCD $gCurrentThreadInfo
        RELOC 8, $gCurrentThreadInfo    ;; SECREL
        DCD 0

__SECTIONREL_gCurrentThreadInfo SETS "$__SECTIONREL_gCurrentThreadInfo":CC:"_"

    MEND
