在大多数平台上,alloca
只是归结为堆栈指针的内联调整(例如,从rsp
在 x64 上,加上一些维护堆栈对齐的逻辑)。
I was looking at the code that gcc
generates for alloca and it is pretty weird. Take the following simple example1:
#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
但相反它增加了 30add rdi, 30
?那是怎么回事?
其次,我只期望结果alloca
成为新的rsp
值,这已经很好地对齐了。相反,gcc 这样做:
lea rax, [rsp+15]
and rax, -16
这似乎正在“重新调整”rsp
使用作为结果alloca
- 但我们已经做了调整工作rsp
首先到 16 字节边界。
那是怎么回事?
你可以玩一下代码在上帝螺栓上 https://godbolt.org/g/USKS2M。值得一提的是clang
and icc
至少在 x86 上做“预期的事情”。对于 VLA(如之前评论中所建议的),gcc
and clang
效果很好,同时icc
产生可憎之物。
1 此处,分配给psink
只是为了消耗结果alloca
否则编译器会完全忽略它。