为什么ARM的 GCC在一个很小的函数里要把临时寄存器R3入栈再出栈?
上下文是在STM32H7(Cortex M7)的裸机开发,使用arm-none-eabi_gcc v 10.2.1
我在看一个基本函数的汇编代码
void my_function(void)
{
my_sub_function();
}
也就是
my_function:
push {r3, lr}
bl my_sub_function
pop {r3, pc}
GCC的选项很简单:优化等级 -O1,没什么花哨的。
我不明白为什么把r3推到栈里。r3是一个临时寄存器,因此 my\_function 不应该需要为保存和恢复这个寄存器费心(当然,前提是它自己调用了子函数并需要存储该值,或者需要保存r3,但此处并非如此)。
编辑:在godbolt上复现 https://godbolt.org/z/51Wr5zvMo
解决方案
这很可能是为了 堆栈对齐。
AAPCS调用约定 在“公共接口”处要求8 字节对齐,这意味着进入函数时,栈指针必须移动为8 字节的倍数。如果我们只推送单个寄存器 lr(4字节),就需要额外的指令来对其进行调整,例如
push {lr}
sub sp, #4
bl my_sub_function
add sp, #4
pop {pc}
多推入/弹出一个额外寄存器就避免了额外指令的需要,因此无疑让代码更短。
至于速度,ARM似乎没有公布该核的指令时序,但很可能将额外寄存器的推入/弹出与对 sp 的额外算术运算相比,并不慢。特别要注意,额外的push/pop必然访问我们已经用于 lr 的同一缓存行。
另见 什么是“堆栈对齐”?
(值得注意的是,当使用 -O2 或更高时,将应用尾调用优化,根本不需要推送任何东西或调整栈:函数将直接编译为 b my_sub_function。 在godbolt上试试),version:4).)
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。