在大多数平台上,alloca
归结为堆栈指针的内联调整(例如,rsp
在 x64 上减去,加上一些逻辑以保持堆栈对齐)。
我正在查看gcc
为 alloca 生成的代码,这很奇怪。以下面的简单示例1为例:
#include <alloca.h>
#include <stddef.h>
volatile void *psink;
void func(size_t x) {
psink = alloca(x);
}
这编译为以下程序集-O2
:
func(unsigned long):
push rbp
add rdi, 30
and rdi, -16
mov rbp, rsp
sub rsp, rdi
lea rax, [rsp+15]
and rax, -16
mov QWORD PTR psink[rip], rax
leave
ret
这里有几件令人困惑的事情。我知道gcc
需要将分配的大小四舍五入到 16 的倍数(以保持堆栈对齐),而通常的方法是(size + 15) & ~0xF
在add rdi, 30
? 那是怎么回事?
其次,我只希望结果alloca
是新的rsp
值,它已经很好地对齐了。相反,gcc 这样做:
lea rax, [rsp+15]
and rax, -16
这似乎是在“重新调整”rsp
使用的值作为结果alloca
- 但我们首先已经完成了与rsp
16 字节边界对齐的工作。
那是怎么回事?
你可以在 godbolt 上玩代码。值得注意的是clang
,icc
至少在 x86 上做“预期的事情”。使用 VLA(如之前的评论中所建议的那样),gcc
并且在产生可憎的clang
同时做得很好。icc
1 在这里,对的赋值psink
只是为了消耗的结果,alloca
否则编译器会完全忽略它。