CesarB 和 Pavel 的回答提供了 AAPCS 的引用,但未解决的问题仍然存在。被调用者是否保存 r9? r12呢? r14呢?此外,答案非常笼统,并没有按照要求专门针对 arm-eabi 工具链。这里有一个实用的方法来找出哪些寄存器是被调用者保存的,哪些不是。
以下 C 代码包含一个内联汇编块,声称修改寄存器 r0-r12 和 r14。编译器将生成代码以保存 ABI 所需的寄存器。
void foo() {
asm volatile ( "nop" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14");
}
使用命令行arm-eabi-gcc-4.7 -O2 -S -o - foo.c
并为您的平台添加开关(例如-mcpu=arm7tdmi)。
该命令将在 STDOUT 上打印生成的汇编代码。它可能看起来像这样:
foo:
stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
nop
ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
bx lr
请注意,编译器生成的代码会保存并恢复 r4-r11。编译器不保存 r0-r3、r12。它恢复 r14(别名 lr)纯属偶然,因为我从经验中知道退出代码也可能将保存的 lr 加载到 r0 中,然后执行“bx r0”而不是“bx lr”。通过添加-mcpu=arm7tdmi -mno-thumb-interwork 或使用-mcpu=cortex-m4 -mthumb,我们获得了稍微不同的汇编代码,如下所示:
foo:
stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
nop
ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}
同样,r4-r11 被保存和恢复。但是 r14(别名 lr)没有恢复。
总结一下:
- r0-r3 不是被调用者保存
- r4-r11 被调用者保存
- r12(别名 ip)不是被调用者保存
- r13(别名 sp)被调用者保存
- r14(别名 lr)不是被调用者保存的
- r15(别名 pc)是程序计数器,在函数调用之前设置为 lr 的值
这至少适用于 arm-eabi-gcc 的默认值。有一些命令行开关(特别是 -mabi 开关)可能会影响结果。