arm-v8a 函数调用传参流程

传递

我们先来看汇编?(看汇编去这个网站挺不错的:https://godbolt.org/),

a(int, long long):                    // @a(int, long long)
        sub     sp, sp, #32           // 为局部变量分配32字节的栈空间
        str     w0, [sp, #28]         // 将第一个参数 w0 (int) 存储到栈上偏移28字节的位置
        str     x1, [sp, #16]         // 将第二个参数 x1 (long long) 存储到栈上偏移16字节的位置
        ldr     w8, [sp, #28]         // 从栈上偏移28字节的位置加载 w0 到 w8
        add     w8, w8, #1            // w8 = w8 + 1,即 w8 加1
        str     w8, [sp, #12]         // 将 w8 存储到栈上偏移12字节的位置
        ldr     x8, [sp, #16]         // 从栈上偏移16字节的位置加载 x1 到 x8
        add     x8, x8, #1            // x8 = x8 + 1,即 x8 加1
        str     x8, [sp]              // 将 x8 存储到栈上偏移0字节的位置
        ldrsw   x8, [sp, #12]         // 从栈上偏移12字节的位置加载 w8(带符号扩展)到 x8
        ldr     x9, [sp]              // 从栈上偏移0字节的位置加载 x8 到 x9
        add     x8, x8, x9            // x8 = x8 + x9,即 w8 + (x1 + 1)
        mov     w0, w8                // 将 w8 复制到 w0
        add     sp, sp, #32           // 释放32字节的栈空间
        ret                           // 返回

main:                                // @main
        sub     sp, sp, #32           // 为局部变量和调用保存栈帧分配32字节的栈空间
        stp     x29, x30, [sp, #16]   // 将帧指针和返回地址存储到栈上偏移16字节的位置
        add     x29, sp, #16          // 设置帧指针 x29 指向当前栈帧的偏移16字节的位置
        mov     w0, #1                // 将整数 1 赋值给 w0
        mov     x1, #2                // 将整数 2 赋值给 x1
        bl      a(int, long long)     // 调用函数 `a(int, long long)`
        stur    w0, [x29, #-4]        // 将函数 `a` 的返回值存储到栈上偏移 x29 - 4 字节的位置
        mov     w0, wzr               // 将 w0 清零
        ldp     x29, x30, [sp, #16]   // 从栈上恢复帧指针和返回地址
        add     sp, sp, #32           // 释放32字节的栈空间
        ret                           // 返回

sp

默认情况下,ARM64使用下降栈(Descending Stack),即栈顶在低地址方向增长。在这种情况下,SP寄存器指向栈顶元素的地址,压栈操作会将SP减小,而弹栈操作会将SP增大。说简单一点,不喜欢那种奇奇怪怪的概念,其实sp就是一个提前分配了一个指定大小的空间的尾指针,然后反着用罢了,至于为什么这样?反这样用可以让您的代码在溢出的时候快速的触发保护机制,终止进程。【1】【2】会告诉你为什么...

w0 or x0

有人问,那就补充一下吧,在 ARM64 中,w 寄存器是 x 寄存器的低 32 位部分。例如,w0x0 的低 32 位部分。因此,当传递 32 位整数时,可以使用 w 寄存器,这实际上只是操作 x 寄存器的低 32 位。

X registers

ARM64位参数调用规则遵循AAPCS64,规定堆栈为满递减堆栈。
寄存器调用规则如下:

  • X0~X7:用于传递子程序参数和结果,使用时不需要保存,多余参数采用堆栈传递,64位返回结果采用X0表示,128位返回结果采用X1:X0表示。

  • X8:用于保存子程序返回地址, 尽量不要使用

  • X9~X15:临时寄存器,使用时不需要保存。

  • X16~X17:子程序内部调用寄存器,使用时不需要保存,尽量不要使用

  • X18:平台寄存器,它的使用与平台相关,尽量不要使用

  • X19~X28:临时寄存器,使用时必须保存。

  • X29:帧指针寄存器,用于连接栈帧,使用时需要保存。

  • X30:链接寄存器LR

  • X31:堆栈指针寄存器SP或零寄存器ZXR

浮点数的传递

64位下NEON寄存器:

  • 32个B寄存器(B0~B31),8bit

  • 32个H寄存器(H0~H31),半字 16bit

  • 32个S寄存器(S0~S31),单子 32bit

  • 32个D寄存器(D0~D31),双字 64bit

  • 32个Q寄存器(V0~V31),四字 128bit

v0q0其实是同一个寄存器。

引用