为什么ARM的 GCC在一个很小的函数里要把临时寄存器R3入栈再出栈?

编程语言 2026-07-11

上下文是在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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章