在程序集中传递子例程的参数和返回值



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

现在,这是我在使用汇编时通常不确定的事情。

因此,如果我理解得很好,根据约定,参数按顺序从寄存器 r0 - r4 开始传递,然后对于其他参数使用堆栈。返回值将传递给 r0。

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

有没有解决方法?

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

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

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

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

两个输入 a 和 b 使用 r0 和 r1 传入。 R0-R4不必保留,特别是R0,因为它的返回值无法保留。 因此,由于 C 代码需要将两个操作数相加,并且调用约定要求的结果在 r0 中返回。 r0 = r0 + r1。

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

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

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

我喜欢术语"呼叫破坏"与"呼叫保留":什么是被叫方和呼叫方保存的寄存器?

如果要在函数调用中保留一个

值,只需将其保存在不同的寄存器中,该寄存器由调用约定保留,而不是在每个函数调用周围执行保存/还原。

最新更新