// 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.
#define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off
#define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off
#define cfi_restore(reg) .cfi_restore reg
#define cfi_def_cfa_register(reg) .cfi_def_cfa_register reg
////////////////////////////////////////////////////////////////////////////////
// MCC_N2CStub simply forwards arguments passed by runtime, i.e., arguments for compiled method are passed
// according to C/C++ calling convention, which usually means efficiency.
////////////////////////////////////////////////////////////////////////////////
#define StubFrameContextSize (4 * 38 - 4 * 2)
#define StubCalleeSaveAreaSize (4 * 14)
#define FuncAddrAndCpStacksizeOffset (4 * 12)
#define SafeStateOffset (4 * 8)
#define ThreadLocalDataOffset (4 * 10)
// R means runtime, while C means compiled method. XX indicates the return type of this method.
// On execution of "bl MCC_N2CStub", the frame layout of stack(growing downwards) looks like:
// x0~x7: hold the first 8 arguments arg0~arg7 if existed
// x30: return address of "bl MCC_N2CStub"
// funcAddr and cpStackSize are saved on the stack
// all on-stack arguments are addressable by SP as the frame layout shows.
// | ... |
// | lr | lr for the caller of MCC_C2NStub
// caller fp --> | r11 |
// | ... |
// | arg11 |
// | arg10 |
// | arg9 |
// | arg8 |
// | cpStackSize |
// caller sp --> | funcAddr |
// the frame layout of stack(growing downwards) after MCC_C2NStub frame is built looks like:
// | ... |
// | lr | lr for the caller of MCC_C2NStub
// caller fp --> | r11 |
// | ... |
// | arg11 |
// | arg10 |
// | arg9 |
// caller sp --> | arg8 |
// callee saved | d7(high) | <== MCC_C2NStub frame starts from here
// | d7(low) |
// | d6(high) |
// | d6(low) |
// | d5(high) |
// | d5(low) |
// | d4(high) |
// | d4(low) |
// | d3(high) |
// | d3(low) |
// | d2(high) |
// | d2(low) |
// | d1(high) |
// | d1(low) |
// | d0(high) |
// | d0(low) |
// | null |
// | r10 |
// | r9 |
// | r8 |
// | r7 |
// | r6 |
// | r5 |
// callee saved | r4 |
// | cpStackSize |
// | calleeAddr |
// | current sp |
// | threadData |
// | dirctCall |
// | entersafe |
// unwind context | direct call | directly invoke callee method
// | shadowframe | the information of caller frame which is interpreted
// | UC Status | unwind context status of caller frame
// | Context LR | LR of unwind context frame
// | Context FP | FP of unwind context frame
// unwind context | Context PC | PC of unwind context frame
// | lr |
// stub fp --> | r11 callerfp |
// | ... | <== copy caller Stack start here
// | arg11 |
// | arg10 |
// | arg9 |
// stub sp --> | arg8 | <== MCC_C2NStub frame ends at here
.text
.align 2
.global CJ_MCC_N2CStub
.type CJ_MCC_N2CStub, %function
CJ_MCC_N2CStub:
.cfi_startproc
add lr, lr, #2
// actual stack size is StubFrameContextSize + 8
// 8 means the size has been pre allocated: calleeAddr and cpStackSize
sub sp, sp, #StubFrameContextSize
// save calleeAddr and cpStackSize
ldr r12, [sp, #StubFrameContextSize] // calleeAddr
str r12, [sp, #FuncAddrAndCpStacksizeOffset]
ldr r12, [sp, #StubFrameContextSize+4] // cpStackSize
str r12, [sp, #FuncAddrAndCpStacksizeOffset+4]
str r11, [sp]
str lr, [sp, #4]
cfi_adjust_cfa_offset (StubFrameContextSize)
cfi_rel_offset (r11, 0)
cfi_rel_offset (lr, 4)
// save all used callee-saved registers
str r4, [sp, #StubCalleeSaveAreaSize]
str r5, [sp, #StubCalleeSaveAreaSize+4]
str r6, [sp, #StubCalleeSaveAreaSize+8]
str r7, [sp, #StubCalleeSaveAreaSize+12]
str r8, [sp, #StubCalleeSaveAreaSize+16]
str r9, [sp, #StubCalleeSaveAreaSize+20]
str r10, [sp, #StubCalleeSaveAreaSize+24]
cfi_rel_offset (r4, StubCalleeSaveAreaSize)
cfi_rel_offset (r5, StubCalleeSaveAreaSize+4)
cfi_rel_offset (r6, StubCalleeSaveAreaSize+8)
cfi_rel_offset (r7, StubCalleeSaveAreaSize+12)
cfi_rel_offset (r8, StubCalleeSaveAreaSize+16)
cfi_rel_offset (r9, StubCalleeSaveAreaSize+20)
cfi_rel_offset (r10, StubCalleeSaveAreaSize+24)
// save double arg registers
vstr d0, [sp, #StubCalleeSaveAreaSize+32]
vstr d1, [sp, #StubCalleeSaveAreaSize+40]
vstr d2, [sp, #StubCalleeSaveAreaSize+48]
vstr d3, [sp, #StubCalleeSaveAreaSize+56]
vstr d4, [sp, #StubCalleeSaveAreaSize+64]
vstr d5, [sp, #StubCalleeSaveAreaSize+72]
vstr d6, [sp, #StubCalleeSaveAreaSize+80]
vstr d7, [sp, #StubCalleeSaveAreaSize+88]
cfi_rel_offset (d0, StubCalleeSaveAreaSize+32)
cfi_rel_offset (d1, StubCalleeSaveAreaSize+40)
cfi_rel_offset (d2, StubCalleeSaveAreaSize+48)
cfi_rel_offset (d3, StubCalleeSaveAreaSize+56)
cfi_rel_offset (d4, StubCalleeSaveAreaSize+64)
cfi_rel_offset (d5, StubCalleeSaveAreaSize+72)
cfi_rel_offset (d6, StubCalleeSaveAreaSize+80)
cfi_rel_offset (d7, StubCalleeSaveAreaSize+88)
mov r5, r0
mov r6, r1
mov r7, r2
mov r8, r3
ldr r4, [sp, #FuncAddrAndCpStacksizeOffset+4] // cpStackSize
// r10 <- previous sp
add r10, sp, #StubFrameContextSize
add r10, r10, #8 // cpStackSize slot + calleeAddr slot
// r4 <- previous sp + cpStackSize
add r4, r10, r4
mov r11, sp
cfi_def_cfa_register (r11)
// store whether new CJThread result in sp+#SafeStateOffset+0x8
// and use it after calling Cangjie function.
bl MRT_NewForeignCJThread
str r0, [sp, #SafeStateOffset+4]
// mutator mustn't be in safe region before setting context.
// store whether leave saferegion result in sp+#SafeStateOffset
// and use it after calling Cangjie function.
bl MRT_LeaveSaferegion
str r0, [sp, #SafeStateOffset]
// frame info: tls -> stub
mov r0, r11
bl MRT_SaveTopManagedContextToN2CStub
mov r0, #0
bl MRT_SetStackGrow
bl MRT_GetThreadLocalData
str r0, [sp, #ThreadLocalDataOffset]
#ifdef __OHOS__
bl CJ_GetUIThreadStackTop
cmp r0, #0
beq .L_setstack_end
str r0, [sp, #ThreadLocalDataOffset+0x4]
mov r0, sp
bl CJ_PushUIThreadStackTop
ldr r0, [sp, #ThreadLocalDataOffset+0x4]
.L_setstack_end:
#endif
ldr r12, [sp, #FuncAddrAndCpStacksizeOffset] // calleeAddr
vldr d0, [sp, #StubCalleeSaveAreaSize+32]
vldr d1, [sp, #StubCalleeSaveAreaSize+40]
vldr d2, [sp, #StubCalleeSaveAreaSize+48]
vldr d3, [sp, #StubCalleeSaveAreaSize+56]
vldr d4, [sp, #StubCalleeSaveAreaSize+64]
vldr d5, [sp, #StubCalleeSaveAreaSize+72]
vldr d6, [sp, #StubCalleeSaveAreaSize+80]
vldr d7, [sp, #StubCalleeSaveAreaSize+88]
cfi_restore (d0)
cfi_restore (d1)
cfi_restore (d2)
cfi_restore (d3)
cfi_restore (d4)
cfi_restore (d5)
cfi_restore (d6)
cfi_restore (d7)
#ifdef __OHOS__
cmp r0, #0
movne sp, r0
#endif
// Calculate stack args size and alignment requires 16 byte-aligned
sub r9, r4, r10
sub r9, sp, r9
tst r9, #0xf
it ne
subne sp, sp, #8
cfi_adjust_cfa_offset (8)
// copy arg8, arg9, arg10, ... (if existed)
.L_copy:
cmp r4, r10
bls .L_copy_end
sub r4, r4, #8
ldr r1, [r4]
ldr r2, [r4, #4]
// SP is always 8 byte-aligned.
sub sp, sp, #8
str r1, [sp]
str r2, [sp, #4]
b .L_copy
.L_copy_end:
// prepare arguments for invoking target method
mov r0, r5
mov r1, r6
mov r2, r7
mov r3, r8
ldr r9, [r11, #FuncAddrAndCpStacksizeOffset] // calleeAddr
blx r9
.global unwindPCForN2CStub
unwindPCForN2CStub:
// keep potential return value
mov r6, r0
mov r7, r1
vstr d0, [r11, #StubCalleeSaveAreaSize+32]
vstr d1, [r11, #StubCalleeSaveAreaSize+40]
cfi_rel_offset(d0, StubCalleeSaveAreaSize+32)
cfi_rel_offset(d1, StubCalleeSaveAreaSize+40)
// Restore the value od rsp before copy arg8, arg9, arg10...
#ifdef __OHOS__
bl CJ_GetUIThreadStackTop
cmp r0, #0
beq .L_restore_sp
mov sp, r0
bl CJ_PopUIThreadStackTop
cfi_def_cfa_register (sp)
b .L_restore_sp_end
#endif
.L_restore_sp:
mov sp, r11
cfi_def_cfa_register (sp)
.L_restore_sp_end:
mov r0, #1
bl MRT_SetStackGrow
mov r0, r11
bl MRT_RestoreTopManagedContextFromN2CStub
ldr r0, [r11, #SafeStateOffset+4]
cmp r0, #0
beq .L_no_need_end
bl MRT_EndForeignCJThread
cmp r0, #1
beq .L_none_enter
.L_no_need_end:
ldr r0, [r11, #SafeStateOffset]
cmp r0, #0
beq .L_none_enter
mov r0, #0
bl MRT_EnterSaferegion
.L_none_enter:
/* set potential return value */
mov r0, r6
mov r1, r7
vldr d0, [sp, #StubCalleeSaveAreaSize+32]
vldr d1, [sp, #StubCalleeSaveAreaSize+40]
cfi_restore (d0)
cfi_restore (d1)
mov sp, r11
cfi_def_cfa_register (sp)
// restore all used callee-saved registers.
ldr r4, [sp, #StubCalleeSaveAreaSize]
ldr r5, [sp, #StubCalleeSaveAreaSize+4]
ldr r6, [sp, #StubCalleeSaveAreaSize+8]
ldr r7, [sp, #StubCalleeSaveAreaSize+12]
ldr r8, [sp, #StubCalleeSaveAreaSize+16]
ldr r9, [sp, #StubCalleeSaveAreaSize+20]
ldr r10, [sp, #StubCalleeSaveAreaSize+24]
cfi_restore (r4)
cfi_restore (r5)
cfi_restore (r6)
cfi_restore (r7)
cfi_restore (r8)
cfi_restore (r9)
cfi_restore (r10)
ldr r11, [sp]
ldr lr, [sp, 4]
add sp, sp, #StubFrameContextSize
cfi_adjust_cfa_offset (-StubFrameContextSize)
cfi_restore (r11)
cfi_restore (lr)
// restore for calleeAddr and cpStackSize slot in stack
add sp, sp, #8
cfi_adjust_cfa_offset (-8)
sub lr, lr, #2
bx lr
.cfi_endproc
.size CJ_MCC_N2CStub, .-CJ_MCC_N2CStub
.text
.align 2
.global ExecuteCangjieStub
.type ExecuteCangjieStub, %function
ExecuteCangjieStub:
.cfi_startproc
// Calculate stack args size and alignment requires 16 byte-aligned.
// If arm32 need tlData argument, the parameters need to be saved first .
mov r12, sp
tst sp, #0xf
it ne
subne sp, sp, #8
cfi_adjust_cfa_offset (8)
sub sp, sp, #16
str r11, [sp]
str lr, [sp, #4]
str r12, [sp, #8]
cfi_adjust_cfa_offset (16)
cfi_rel_offset (r11, 0)
cfi_rel_offset (lr, 4)
cfi_rel_offset (r12, 8)
blx r3
ldr r11, [sp]
ldr lr, [sp, #4]
ldr r12, [sp, #8]
add sp, sp, #16
cfi_adjust_cfa_offset (-16)
cfi_restore (r11)
cfi_restore (lr)
cfi_restore (r12)
tst r12, #0xf
it ne
addne sp, sp, #8
cfi_adjust_cfa_offset (-8)
bx lr
.cfi_endproc
.size ExecuteCangjieStub, .-ExecuteCangjieStub
.text
.align 2
.global InitCJLibraryStub
.type InitCJLibraryStub, %function
InitCJLibraryStub:
.cfi_startproc
sub sp, sp, #8
str r11, [sp]
str lr, [sp, #4]
cfi_adjust_cfa_offset (8)
cfi_rel_offset (r11, 0)
cfi_rel_offset (lr, 4)
mov r11, sp
mov r12, r0
sub sp, sp, #8
str r12, [sp]
mov r12, #0
str r12, [sp, #4]
bl CJ_MCC_N2CStub
ldr r11, [sp]
ldr lr, [sp, #4]
add sp, sp, #8
cfi_adjust_cfa_offset (8)
cfi_restore (r11)
cfi_restore (lr)
bx lr
.cfi_endproc
.size InitCJLibraryStub, .-InitCJLibraryStub
.text
.align 2
.global ResolveCycleRefStub
.type ResolveCycleRefStub, %function
// TODO
ResolveCycleRefStub:
.cfi_startproc
sub sp, sp, #8
str r11, [sp]
str lr, [sp, #4]
cfi_adjust_cfa_offset (8)
cfi_rel_offset (r11, 0)
cfi_rel_offset (lr, 4)
mov r11, sp
mov r12, r0
sub sp, sp, #8
str r12, [sp]
mov r12, #0
str r12, [sp, #4]
bl CJ_MCC_N2CStub
ldr r11, [sp]
ldr lr, [sp, #4]
add sp, sp, #8
cfi_adjust_cfa_offset (8)
cfi_restore (r11)
cfi_restore (lr)
bx lr
.cfi_endproc
.size ResolveCycleRefStub, .-ResolveCycleRefStub
#ifdef __OHOS__
.text
.align 2
.global CJ_MRT_ARKTS_CreateEngineStub
.type CJ_MRT_ARKTS_CreateEngineStub, %function
CJ_MRT_ARKTS_CreateEngineStub:
.cfi_startproc
sub sp, sp, #8
str r11, [sp]
str lr, [sp, #4]
cfi_adjust_cfa_offset(8)
cfi_rel_offset (r11, 0)
cfi_rel_offset (lr, 4)
mov r11, sp
mov r4, sp
bl CJ_MRT_ARKTS_CreateEngine
mov sp, r4
ldr r11, [sp]
ldr lr, [sp, #4]
add sp, sp, #8
cfi_adjust_cfa_offset (-8)
cfi_restore (lr)
cfi_restore (r11)
bx lr
.cfi_endproc
.size CJ_MRT_ARKTS_CreateEngineStub, .-CJ_MRT_ARKTS_CreateEngineStub
#endif
// TODO: The reflection function is not supported on the arm32. In future development,
// need to pay attention to the 16-byte alignment of sp before invoking the cangjie method.
.text
.align 2
.global ApplyCangjieMethodStub
.type ApplyCangjieMethodStub, %function
.global ApplyCangjieMethodStubFloat32
.type ApplyCangjieMethodStubFloat32, %function
.global ApplyCangjieMethodStubFloat64
.type ApplyCangjieMethodStubFloat64, %function
ApplyCangjieMethodStub:
ApplyCangjieMethodStubFloat32:
ApplyCangjieMethodStubFloat64:
.cfi_startproc
sub sp, sp, #StubFrameContextSize
str r11, [sp]
str lr, [sp, #4]
cfi_adjust_cfa_offset (StubFrameContextSize)
cfi_rel_offset (r11, 0)
cfi_rel_offset (lr, 4)
// save all used callee-saved registers
str r4, [sp, #StubCalleeSaveAreaSize]
str r5, [sp, #StubCalleeSaveAreaSize+4]
str r6, [sp, #StubCalleeSaveAreaSize+8]
str r7, [sp, #StubCalleeSaveAreaSize+12]
str r8, [sp, #StubCalleeSaveAreaSize+16]
str r9, [sp, #StubCalleeSaveAreaSize+20]
str r10, [sp, #StubCalleeSaveAreaSize+24]
cfi_rel_offset (r4, StubCalleeSaveAreaSize)
cfi_rel_offset (r5, StubCalleeSaveAreaSize+4)
cfi_rel_offset (r6, StubCalleeSaveAreaSize+8)
cfi_rel_offset (r7, StubCalleeSaveAreaSize+12)
cfi_rel_offset (r8, StubCalleeSaveAreaSize+16)
cfi_rel_offset (r9, StubCalleeSaveAreaSize+20)
cfi_rel_offset (r10, StubCalleeSaveAreaSize+24)
// save double arg registers
vstr d0, [sp, #StubCalleeSaveAreaSize+32]
vstr d1, [sp, #StubCalleeSaveAreaSize+40]
vstr d2, [sp, #StubCalleeSaveAreaSize+48]
vstr d3, [sp, #StubCalleeSaveAreaSize+56]
vstr d4, [sp, #StubCalleeSaveAreaSize+64]
vstr d5, [sp, #StubCalleeSaveAreaSize+72]
vstr d6, [sp, #StubCalleeSaveAreaSize+80]
vstr d7, [sp, #StubCalleeSaveAreaSize+88]
cfi_rel_offset (d0, StubCalleeSaveAreaSize+32)
cfi_rel_offset (d1, StubCalleeSaveAreaSize+40)
cfi_rel_offset (d2, StubCalleeSaveAreaSize+48)
cfi_rel_offset (d3, StubCalleeSaveAreaSize+56)
cfi_rel_offset (d4, StubCalleeSaveAreaSize+64)
cfi_rel_offset (d5, StubCalleeSaveAreaSize+72)
cfi_rel_offset (d6, StubCalleeSaveAreaSize+80)
cfi_rel_offset (d7, StubCalleeSaveAreaSize+88)
mov r11, sp
cfi_def_cfa_register (r11)
mov r5, r0 // save args
mov r6, r1 // stackSize
mov r7, r2 // save func
mov r8, r3 // threadData
// copy parameter to stack
// size align to 16 byte.
add r6, r6, #(8 - 1)
and r6, r6, #0xFFFFFFF8
mov r0, r5
add r0, r0, #80 // 4 * 4 + 8 * 8
mov r1, r5
add r1, r1, #80
add r1, r1, r6
.L_copy_args:
cmp r1, r0
bls .L_copy_args_end
sub r1, r1, #8
ldr r2, [r1]
ldr r3, [r1, #4]
// SP is always 8 byte-aligned
sub sp, sp, #8
str r2, [sp]
str r3, [sp, #4]
b .L_copy_args
.L_copy_args_end:
// mov dst, src
// prepare arguments for invoking target method
ldr r0, [r5]
ldr r1, [r5, #4]
ldr r2, [r5, #8]
ldr r3, [r5, #12]
add r5, r5, #16
vldr d0, [r5]
vldr d1, [r5, #8]
vldr d2, [r5, #16]
vldr d3, [r5, #24]
vldr d4, [r5, #32]
vldr d5, [r5, #40]
vldr d6, [r5, #48]
vldr d7, [r5, #56]
blx r7
// keep potential return value
mov r6, r0
mov r7, r1
vstr d0, [r11, #StubCalleeSaveAreaSize+32]
vstr d1, [r11, #StubCalleeSaveAreaSize+40]
cfi_rel_offset(d0, StubCalleeSaveAreaSize+32)
cfi_rel_offset(d1, StubCalleeSaveAreaSize+40)
/* set potential return value */
mov r0, r6
mov r1, r7
vldr d0, [r11, #StubCalleeSaveAreaSize+32]
vldr d1, [r11, #StubCalleeSaveAreaSize+40]
cfi_restore (d0)
cfi_restore (d1)
// restoring the SP Value. the stack which extended for invoking c method is useless now
mov sp, r11
cfi_def_cfa_register (sp)
// restore all used callee-saved registers
ldr r4, [sp, #StubCalleeSaveAreaSize]
ldr r5, [sp, #StubCalleeSaveAreaSize+4]
ldr r6, [sp, #StubCalleeSaveAreaSize+8]
ldr r7, [sp, #StubCalleeSaveAreaSize+12]
ldr r8, [sp, #StubCalleeSaveAreaSize+16]
ldr r9, [sp, #StubCalleeSaveAreaSize+20]
ldr r10, [sp, #StubCalleeSaveAreaSize+24]
cfi_restore (r4)
cfi_restore (r5)
cfi_restore (r6)
cfi_restore (r7)
cfi_restore (r8)
cfi_restore (r9)
cfi_restore (r10)
ldr r11, [sp]
ldr lr, [sp, 4]
add sp, sp, #StubFrameContextSize
cfi_adjust_cfa_offset (-StubFrameContextSize)
cfi_restore (r11)
cfi_restore (lr)
bx lr
.cfi_endproc
.size ApplyCangjieMethodStub, .-ApplyCangjieMethodStub