x86_64架构下叶函数未递减rsp寄存器的技术问询
x86_64叶函数栈操作的疑问解答
系统环境
- 运行Intel x86_64架构的Ubuntu 22.04.3系统
- GCC版本11.4.0
问题背景
我发现叶函数中并未像非叶函数那样执行递减rsp寄存器的操作。示例C代码test.c如下:
int fx(){ int x = 30; int y = 34; int z = 45; return 30; } int main(){ int a = 10; int b = 20; int c = fx(); }
使用以下命令编译:
gcc test.c -fno-stack-protector -o test
执行反汇编命令查看结果:
objdump -dw -M suffix test
得到的反汇编输出:
0000000000001129 <fx>: 1129: f3 0f 1e fa endbr64 112d: 55 pushq %rbp 112e: 48 89 e5 movq %rsp,%rbp 1131: c7 45 fc 1e 00 00 00 movl $0x1e,-0x4(%rbp) 1138: c7 45 f8 22 00 00 00 movl $0x22,-0x8(%rbp) 113f: c7 45 f4 2d 00 00 00 movl $0x2d,-0xc(%rbp) 1146: b8 1e 00 00 00 movl $0x1e,%eax 114b: 5d popq %rbp 114c: c3 retq 000000000000114d <main>: 114d: f3 0f 1e fa endbr64 1151: 55 pushq %rbp 1152: 48 89 e5 movq %rsp,%rbp 1155: 48 83 ec 10 subq $0x10,%rsp 1159: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp) 1160: c7 45 f8 14 00 00 00 movl $0x14,-0x8(%rbp) 1167: b8 00 00 00 00 movl $0x0,%eax 116c: e8 b8 ff ff ff callq 1129 <fx> 1171: 89 45 f4 movl %eax,-0xc(%rbp) 1174: b8 00 00 00 00 movl $0x0,%eax 1179: c9 leaveq 117a: c3 retq
可以看到main函数中有subq $0x10,%rsp指令,但叶函数fx中没有对应的栈指针调整操作,针对这一现象,以下是相关疑问的解答:
疑问解答
1. 该行为是否与GCC遵循的System V ABI有关,还是仅为编译器优化?
这是System V AMD64 ABI允许的编译器优化行为。ABI只规定了栈帧布局、调用约定、寄存器使用规则等核心规范,并没有强制要求所有函数必须通过subq调整rsp来分配局部变量空间。叶函数直接通过rbp的负偏移访问局部变量,完全符合ABI的要求,属于编译器在合规范围内做的性能优化。
2. 该行为的合理原因是什么?
- 减少指令开销:叶函数不需要调用其他函数,直接用rbp偏移访问局部变量可以省去
subq(分配栈空间)和对应的恢复指令(如addq或leave隐含的操作),减少了指令执行的总周期,提升了运行效率。 - 栈空间利用更高效:进入叶函数时,
pushq %rbp操作已经让rsp保持16字节对齐(符合System V ABI的对齐要求),直接使用rbp的负偏移存放局部变量,不需要额外调整rsp来满足对齐或预留空间的需求,避免了不必要的栈空间浪费。
3. 是否可以告知编译器不要对叶函数采用该行为?
可以,有两种常用方式:
- 全局编译选项:使用
-O0 -fno-optimize-sibling-calls编译,或者直接添加-fno-stack-reuse选项,强制编译器为所有函数生成标准的栈帧布局,包括通过subq调整rsp分配局部变量空间。 - 函数级属性:给目标叶函数添加
__attribute__((nooptimize))属性,禁用该函数的所有优化,编译器会生成包含subq调整rsp的标准栈帧。例如修改后的fx函数:
__attribute__((nooptimize)) int fx(){ int x = 30; int y = 34; int z = 45; return 30; }
内容的提问来源于stack exchange,提问作者alessio solari
相关产品推荐
相关产品推荐

