9

我正在使用 ARM 程序集,我必须编写一个子程序,我遵循 ARM 调用约定(这必须与其他地方的一些单独的更高级别的实现集成)来传递参数和返回值。

现在这是我在使用装配时通常不确定的事情。

因此,按照惯例,如果我理解得很好,参数是按从寄存器 r0 - r4 开始的顺序传递的,然后使用其他参数堆栈。返回值被传递给 r0。

现在这就是我感到困惑的地方。如果我应该保存 r0 的上下文并在之后将其弹出,则无法返回结果,唯一的方法是破坏第一个参数。

有什么解决方法吗?

4

3 回答 3

7

当您在 r0 中传回返回值时,调用者希望您这样做。调用者不希望 r0 仍然包含与原始第一个参数相同的值,因为 r0 是返回值的具体位置。

通常,ARM 调用约定要求子例程保留 r4 到 r11,而不是 r0 到 r3。所以无论如何也没有矛盾。

于 2012-09-19T00:23:05.807 回答
6

为什么不自己尝试一下,看看编译器做了什么?

unsigned int fun ( unsigned int a, unsigned int b )
{
    return(a+b);
}

编译成对象并反汇编

arm-none-eabi-gcc -O2 -c fun.c -o fun.o
arm-none-eabi-objdump -D fun.o 

结果是

00000000 <fun>:
   0:   e0810000    add r0, r1, r0
   4:   e12fff1e    bx  lr

使用 r0 和 r1 传入两个输入 a 和 b。r0-r4 不一定要保存,特别是 r0 因为它的返回值是不能保存的。因此,根据 C 代码所需的两个操作数相加,并且根据调用约定,结果在 r0 中返回。r0 = r0 + r1。

编译器必须遵守约定,否则它生成的代码将无法工作,因此您可以简单地编译代码并反汇编,以了解有关特定编译器和目标的调用约定的大量信息。

于 2012-09-19T04:11:31.333 回答
0

如果我应该保存 r0 的上下文并在之后将其弹出

这听起来像是由术语 "caller-saved" 引起的混乱。它错误地暗示调用者实际上应该保存/恢复每个这样的寄存器,而不是让它被函数调用销毁(在这种情况下被返回值替换)。

我喜欢术语“call-clobbered”与“call-preserved”: 什么是被调用者和调用者保存的寄存器?

如果您想在函数调用中保留一个值,只需将其保存在不同的寄存器中,该寄存器由调用约定保留,而不是围绕每个函数调用进行保存/恢复。

于 2019-09-25T00:16:26.667 回答