// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

// stub need to be 16-byte aligned, so we should use (8 * even numbers)
#define C2NStubFrameSize           (8 * 30)

// On execution of "bl MCC_C2NStub", the frame layout of stack(growing downwards) looks like:
// Nativefunc address will be passed to %rax after MCC_C2NStub is built.
//                 |  ...         |
//                 |  ra          | return address for the caller of MCC_C2NStub
// caller rbp  --> |  rbp         |
//                 |  ...         |
//                 | arg9         |
//                 | arg8         |
//                 | arg7         |
//                 | arg6         |
//                 | callee addr  |
// caller rsp  --> | cpStackSize  |

// the frame layout of stack(growing downwards) after MCC_C2NStub frame is built looks like:
//                 |  ...         |
//                 |  ra          | return address for the caller of MCC_C2NStub
// caller rbp  --> |  rbp         |
//                 |  ...         |
//                 | arg9         |
//                 | arg8         |
//                 | arg7         |
//                 | arg6         |
//                 | callee addr  |
// caller rsp  --> | cpStackSize  |
//                 | ra           |
//   stub rbp  --> | caller rbp   | <== C2NStubStart
// callee register | %r15         |
//                 | %r14         |
//                 | %r13         |
//                 | %r12         |
//                 | %rbx         |
// arg0            | %rdi         |
// arg1            | %rsi         |
// arg2            | %rdx         |
// arg3            | %rcx         |
// arg4            | %r8          |
// arg5            | %r9          |
//                 | %rax         |
//                 | xmm0(high)   |
//                 | xmm0(low)    |
//                 | xmm1(high)   |
//                 | xmm1(low)    |
//                 | xmm2(high)   |
//                 | xmm2(low)    |
//                 | xmm3(high)   |
//                 | xmm3(low)    |
//                 | xmm4(high)   |
//                 | xmm4(low)    |
//                 | xmm5(high)   |
//                 | xmm5(low)    |
//                 | xmm6(high)   |
//                 | xmm6(low)    |
//                 | xmm7(high)   |
//                 | xmm7(low)    |
//                 | entersafe    | <== C2NStubFrameSize
//                 | ....         | <== copy caller Stack start here
//                 | arg9         |
//                 | arg8         |
//                 | arg7         |
//   stub rsp  --> | arg6         | <== MCC_C2NStub frame ends at here

    .text
    .align 2
    .global CJ_MCC_C2NStub
    .type CJ_MCC_C2NStub, @function
CJ_MCC_C2NStub:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $C2NStubFrameSize, %rsp

    movq    %r15, -8(%rbp)
    movq    %r14, -16(%rbp)
    movq    %r13, -24(%rbp)
    movq    %r12, -32(%rbp)
    movq    %rbx, -40(%rbp)
    .cfi_rel_offset %rbx, -40
    .cfi_rel_offset %r12, -32
    .cfi_rel_offset %r13, -24
    .cfi_rel_offset %r14, -16
    .cfi_rel_offset %r15, -8

    // save arg0~arg5 to stack
    movq    %rdi,  -48(%rbp)
    movq    %rsi,  -56(%rbp)
    movq    %rdx,  -64(%rbp)
    movq    %rcx,  -72(%rbp)
    movq    %r8,   -80(%rbp)
    movq    %r9,   -88(%rbp)
    movq    %rax,  -96(%rbp)
    movapd  %xmm0, -112(%rbp)
    movapd  %xmm1, -128(%rbp)
    movapd  %xmm2, -144(%rbp)
    movapd  %xmm3, -160(%rbp)
    movapd  %xmm4, -176(%rbp)
    movapd  %xmm5, -192(%rbp)
    movapd  %xmm6, -208(%rbp)
    movapd  %xmm7, -224(%rbp)

    callq   CJ_CJThreadStackGuardGet
    // rbx = cpStackSize
    movq    16(%rbp), %rbx
    addq    %rbx, %rax
    cmpq    %rax, %rsp
    jbe     .L_stack_check_fail

    movq    %rbp,  %rsi
    // Get the current pc address through the following two assembly instructions.
    call    .L_current_pc
.L_current_pc:
     .global unwindPCForC2NStub
unwindPCForC2NStub:
    pop     %rdi
    mov     %r15, %rdx
    callq   MRT_SaveC2NContext@PLT
    movq    $0, %rdi
    callq   MRT_EnterSaferegion@PLT
    movq    %rax, -232(%rbp)
#ifdef __OHOS__
    callq   CJ_GetUIThreadStackTop
    test    %rax, %rax
    jz      .L_setstack_end
    movq    %rax, -240(%rbp)
    movq    %rsp, %rdi
    callq   CJ_PushUIThreadStackTop
    movq    -240(%rbp), %rax
.L_setstack_end:
#endif

    // Store rsp before coping arg6, arg7...
    movq    %rsp, -240(%rbp)

    // rbx = cpStackSize, r12 = calleeAddr
    movq    16(%rbp), %rbx
    movq    24(%rbp), %r12

    // Let the r14 register(current rbp) point to the starting point of the stack to be copied
    movq    %rbp, %r14
    addq    $32,  %r14
    addq    %rbx, %r14
    // Let the r13 register(current rbp) point to the ending point of the stack to be copied
    movq    %rbp, %r13
    addq    $32,  %r13

#ifdef __OHOS__
    // rax is the return value of CJ_GetUIThreadStackTop
    TEST    %rax, %rax
    jz      .L_copy
    movq    %rax, %rsp
#endif
    // copy arg6, arg7, ...(if existed)
.L_copy:
    cmpq    %r13,    %r14
    jle     .L_copy_end
    subq    $8,      %r14
    movq    0(%r14), %r15
    pushq   %r15
    jmp     .L_copy
.L_copy_end:

    // Prepare arg0 to arg6.
    movq    -48(%rbp),  %rdi
    movq    -56(%rbp),  %rsi
    movq    -64(%rbp),  %rdx
    movq    -72(%rbp),  %rcx
    movq    -80(%rbp),  %r8
    movq    -88(%rbp),  %r9
    movq    -96(%rbp),  %rax
    movapd  -112(%rbp), %xmm0
    movapd  -128(%rbp), %xmm1
    movapd  -144(%rbp), %xmm2
    movapd  -160(%rbp), %xmm3
    movapd  -176(%rbp), %xmm4
    movapd  -192(%rbp), %xmm5
    movapd  -208(%rbp), %xmm6
    movapd  -224(%rbp), %xmm7

    callq   *%r12

    // keep potential return value
    // rax is 1st return register, rdx is 2nd return register
    // xmm0-xmm1 are used to return floating point arguments
    movq    %rdx,  -48(%rbp)
    movq    %rax,  -56(%rbp)
    movapd  %xmm0, -80(%rbp)
    movapd  %xmm1, -96(%rbp)

    movq   -232(%rbp), %rax
    cmpq    $0, %rax
    je     .L_none_leave
    callq   MRT_LeaveSaferegion@PLT
    callq   MRT_GetThreadLocalData@PLT
    movq    %rax, -8(%rbp)
.L_none_leave:
    movq    %rax, %rdi
    callq   MRT_DeleteC2NContext@PLT

    mov     $0, %rdi
    callq   MRT_ThrowPendingException@PLT

    // Restore the value of rsp before coping arg6, arg7...
#ifdef __OHOS__
    callq   CJ_GetUIThreadStackTop
    test    %rax, %rax
    jz      .L_restore_sp
    mov     %rax, %rsp
    callq   CJ_PopUIThreadStackTop
    jmp     .L_restore_sp_end
#endif
.L_restore_sp:
    movq    -240(%rbp), %rsp
.L_restore_sp_end:

    // set potential return value
    movq    -48(%rbp), %rdx
    movq    -56(%rbp), %rax
    movapd  -80(%rbp), %xmm0
    movapd  -96(%rbp), %xmm1

    // delete callee&cpStackSize, Restore the caller stack.
.L_stack_check:
    movq    0(%rbp),   %r12
    movq    %r12,      16(%rbp)
    movq    8(%rbp),   %r12
    movq    %r12,      24(%rbp)
    addq    $16, %rsp

    addq    $C2NStubFrameSize, %rsp

    movq    -8(%rbp),  %r15
    movq    -16(%rbp), %r14
    movq    -24(%rbp), %r13
    movq    -32(%rbp), %r12
    movq    -40(%rbp), %rbx

    popq    %rbp
    .cfi_def_cfa %rsp, 8
    retq
.L_stack_check_fail:
    call    MCC_ThrowStackOverflowError
    callq   MRT_GetThreadLocalData@PLT
    movq    %rax, -8(%rbp)
    jmp     .L_stack_check
    .cfi_endproc
    .size CJ_MCC_C2NStub, .-CJ_MCC_C2NStub