问题描述
ARM的C函数调用约定说:
- 呼叫者将在r0-r3中传递前4个参数。
- 调用方将在堆栈上传递任何其他参数。
- 呼叫者将从r0获得返回值。
我正在手工制作C调用的汇编函数。原型等效于此:
void s(void);
假设C函数c()
调用s()
。
由于s()
没有参数,也没有返回值。我相信编译器不会触及r0-r3
来生成c()
来调用s()
的调用序列。
假设s()
将使用r0-r12
完成其功能。 c()
也可能会使用这些寄存器。
我不确定是否必须明确保存并恢复所有在s()
中涉及的寄存器,例如r0-r12
。这样的内存操作将花费一些时间。
或者至少我不必为r0-r3
这么做吗?
解决方法
来自Procedure Call Standard for the Arm Architecture,第6.1.1节(第19页):
子例程必须保留寄存器r4-r8,r10,r11和SP(以及将r9指定为v6的PCS变体中的r9)的内容
是的,因为r0-r3是暂存寄存器,所以在s()
中使用它们之前无需保存它们,但是必须保存和恢复任何其他寄存器。
假定编译器符合ARM ABI,然后这样声明s()
:
extern void s(void);
就足够了,并且在调用c()
之后,编译器不应发出依赖于s()
函数中r0-r3先前值的代码(即c()
应该保存r0-如果需要,请在调用s()
之前还原r3,然后再还原它们),因为那样会破坏ABI遵从性。
通常,当混合使用C和asm时,您永远都不能对C代码使用的寄存器进行任何假设,除非保证通过调用约定可以堆叠这些代码。使用它们之前,请堆叠所有其他寄存器,然后稍后将其弹出。所有这些取决于编译器在调用汇编器函数时在内部进行或在内部不进行的假设。
这里有一些很好的信息:Mixing C,C++,and Assembly Language