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

.intel_syntax noprefix
#include "unixasmmacros.inc"
#include "asmconstants.h"

// EXTERN_C int __fastcall HelperMethodFrameRestoreState(
//         INDEBUG_COMMA(HelperMethodFrame *pFrame)
//         MachState *pState
//         )
LEAF_ENTRY HelperMethodFrameRestoreState, _TEXT

#ifdef _DEBUG
        mov     rdi, rsi
#endif

        // Check if the MachState is valid
        xor     eax, eax
        cmp     qword ptr [rdi + OFFSETOF__MachState___pRetAddr], rax
        jne      DoRestore
        REPRET
DoRestore:

        //
        // If a preserved register were pushed onto the stack between
        // the managed caller and the H_M_F, m_pReg will point to its
        // location on the stack and it would have been updated on the
        // stack by the GC already and it will be popped back into the
        // appropriate register when the appropriate epilog is run.
        //
        // Otherwise, the register is preserved across all the code
        // in this HCALL or FCALL, so we need to update those registers
        // here because the GC will have updated our copies in the
        // frame.
        //
        // So, if m_pReg points into the MachState, we need to update
        // the register here.  That's what this macro does.
        //
#define RestoreReg(reg, regnum) \
        lea     rax, [rdi + OFFSETOF__MachState__m_Capture + 8 * regnum]; \
        mov     rdx, [rdi + OFFSETOF__MachState__m_Ptrs + 8 * regnum]; \
        cmp     rax, rdx; \
        cmove   reg, [rax];

        // regnum has to match ENUM_CALLEE_SAVED_REGISTERS macro
        RestoreReg(R12, 0)
        RestoreReg(R13, 1)
        RestoreReg(R14, 2)
        RestoreReg(R15, 3)
        RestoreReg(Rbx, 4)
        RestoreReg(Rbp, 5)

        xor     eax, eax
        ret

LEAF_END HelperMethodFrameRestoreState, _TEXT

//////////////////////////////////////////////////////////////////////////
//
// NDirectImportThunk
//
// In addition to being called by the EE, this function can be called
//  directly from code generated by JIT64 for CRT optimized direct
//  P/Invoke calls. If it is modified, the JIT64 compiler's code
//  generation will need to altered accordingly.
//
// EXTERN_C VOID __stdcall NDirectImportThunk()//
NESTED_ENTRY NDirectImportThunk, _TEXT, NoHandler

        //
        // Save integer parameter registers.
        // Make sure to preserve r11 as well as it is used to pass the stack argument size from JIT
        // Make sure to preserve rax as well as it is used for the return buffer for Swift calls
        //
        PUSH_ARGUMENT_REGISTERS
        push_register r11
        push_register rax

        //
        // Allocate space for XMM parameter registers and alignment
        //
        alloc_stack     0x88

        SAVE_FLOAT_ARGUMENT_REGISTERS 0

    END_PROLOGUE

        //
        // Call NDirectImportWorker w/ the NDirectMethodDesc*
        //
        mov             rdi, METHODDESC_REGISTER
        call            C_FUNC(NDirectImportWorker)
        mov             r10, rax

        RESTORE_FLOAT_ARGUMENT_REGISTERS 0

        //
        // epilogue, r10 contains the native target address
        //
        free_stack      0x88

        //
        // Restore integer parameter registers, r11 and rax
        //
        pop_register rax
        pop_register r11
        POP_ARGUMENT_REGISTERS

        jmp r10
    
NESTED_END NDirectImportThunk, _TEXT

//------------------------------------------------
// JIT_RareDisableHelper
//
// The JIT expects this helper to preserve registers used for return values
//
NESTED_ENTRY JIT_RareDisableHelper, _TEXT, NoHandler

    // First integer return register
    push_register rax
    // Second integer return register
    push_register rdx
    alloc_stack         0x28
    END_PROLOGUE
    // First float return register
    movdqa              xmmword ptr [rsp], xmm0
    // Second float return register
    movdqa              xmmword ptr [rsp+0x10], xmm1

    call                C_FUNC(JIT_RareDisableHelperWorker)

    movdqa              xmm0, xmmword ptr [rsp]
    movdqa              xmm1, xmmword ptr [rsp+0x10]
    free_stack          0x28
    pop_register        rdx
    pop_register        rax
    ret

NESTED_END JIT_RareDisableHelper, _TEXT

#ifdef FEATURE_HIJACK

//------------------------------------------------
// OnHijackTripThread
//
NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler

    // Make room for the real return address (rip)
    push_register rax

    PUSH_CALLEE_SAVED_REGISTERS

    push_register rdx
    // Push rax again - this is where integer/pointer return values are returned
    push_register rax

    mov                 rdi, rsp

    alloc_stack         0x28

    // First float return register
    movdqa              [rsp], xmm0
    // Second float return register
    movdqa              [rsp+0x10], xmm1

    END_PROLOGUE

    call                C_FUNC(OnHijackWorker)

    movdqa              xmm0, [rsp]
    movdqa              xmm1, [rsp+0x10]
    free_stack          0x28
    pop_register        rax
    pop_register        rdx

    POP_CALLEE_SAVED_REGISTERS
    ret

NESTED_END OnHijackTripThread, _TEXT

#endif // FEATURE_HIJACK

LEAF_ENTRY SinglecastDelegateInvokeStub, _TEXT

        test    rdi, rdi
        jz      NullObject


        mov     rax, [rdi + OFFSETOF__DelegateObject___methodPtr]
        mov     rdi, [rdi + OFFSETOF__DelegateObject___target]  // replace "this" pointer

        jmp     rax

NullObject:
        mov     rdi, CORINFO_NullReferenceException_ASM
        jmp     C_FUNC(JIT_InternalThrow)

LEAF_END SinglecastDelegateInvokeStub, _TEXT

#ifdef FEATURE_TIERED_COMPILATION

NESTED_ENTRY OnCallCountThresholdReachedStub, _TEXT, NoHandler
        PROLOG_WITH_TRANSITION_BLOCK

        lea     rdi, [rsp + __PWTB_TransitionBlock] // TransitionBlock *
        mov     rsi, rax // stub-identifying token, see OnCallCountThresholdReachedStub
        call    C_FUNC(OnCallCountThresholdReached)

        EPILOG_WITH_TRANSITION_BLOCK_TAILCALL
        TAILJMP_RAX
NESTED_END OnCallCountThresholdReachedStub, _TEXT

#endif // FEATURE_TIERED_COMPILATION
