代码之家  ›  专栏  ›  技术社区  ›  SRobertJames

超过4个参数的ARM调用约定:信息冲突

  •  1
  • SRobertJames  · 技术社区  · 4 年前

    我试图理解Linux上的ARM调用约定,在gcc下,尤其是使用4个以上的参数时。堆栈溢出、维基百科和ARM文档中的信息有时会发生冲突。

    1. 据我所知,额外的参数被推送到堆栈上。谁把它们从堆栈上取下来?我找不到这个讨论,尽管打电话的人似乎需要讨论。

    2. 被调用者最终需要将这些弹出到寄存器中。但是,它似乎在使用 pop op是错误的,因为为了弹出到reg,被调用者必须首先推送reg来保存它。因此,被调用者需要执行以下操作:

    @ This func takes 6 args
    push {r4, r5, r6, lr} @ We don't use r6, but need to keep the stack 8 byte aligned
    ldr r4, [sp, #0xc]
    ldr r5, [sp, #8]
    ...
    pop {lr, r6, r5, r4}
    bx lr
    

    这是正确的吗?

    0 回复  |  直到 4 年前
        1
  •  3
  •   Peter Cordes    4 年前

    被调用者不需要 流行音乐 他们(也不应该),只是 负载 他们您可以将它们保留在内存中,直到您为它们做好准备,无需为它们保存/恢复更多保留调用的寄存器,除非您真的想同时在寄存器中存储更多数据。例如。

    int foo(int a, int b, int c, int d, int e, int f) {
        return a+b+c+d+e+f;
    }
    

    使用ARM GCC-O3编译( https://godbolt.org/z/v6v6ch4vf )

    foo(int, int, int, int, int, int):
            add     r0, r0, r1       @ a+=b
            ldr     r1, [sp]         @ load e into r1, since b is now dead
            add     r0, r0, r2       @ a+=c
            ldr     r2, [sp, #4]     @ load f since c is now dead.
            add     r0, r0, r3
            add     r0, r0, r1
            add     r0, r0, r2
            bx      lr
    

    通过尽快安排这些加载,编译器可以隐藏一些加载使用延迟,因为每次加载都尽可能早于加载结果的使用。

    r0..r3和r12 1. 是唯一的 call-clobbered 在GCC正在使用的标准AAPCS调用约定的版本中注册;看见 What registers to save in the ARM C calling convention? 嗯,LR被调用为clobbed,但如果我们真的clobbed了,我们就必须保存/恢复返回地址。


    堆栈指针保留调用;当控件返回给调用者时,它必须与进入函数时相同。(换句话说,堆栈参数(如果有的话)是调用方弹出的。)

    所以,如果你真的弹出了一些堆栈参数,你需要 sub sp, #8 在返回以还原它之前的某个时刻。(例如,如果您完成了r0..r3,并且可能会弹出到其中一些寄存器中)。


    脚注1: 我不知道为什么GCC没有更早地加载到r12中( ip );即使有 -marm 为了确保它的目标是ARM模式,它不使用它。(在Thumb模式中,高8寄存器不能与许多指令一起使用)。GCC不认为它的调用被保留:甚至强迫它用将值复制到该寄存器 register int y asm("r12") 和一个 asm 语句不会使其保存/恢复该寄存器。 https://godbolt.org/z/GMhbMscqT