在大多数平台上,alloca
只是归结为堆栈指针的内联调整(例如,在x64上从rsp
减去,加上一些逻辑来维持堆栈对齐)。
我正在查看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
,但它在{{1}增加30 }}?怎么了?
其次,我希望add rdi, 30
的结果是新的alloca
值,它已经很好地对齐了。相反,gcc这样做:
rsp
这似乎是“重新调整” lea rax, [rsp+15]
and rax, -16
作为rsp
的结果使用的值 - 但我们已经完成了将alloca
与16字节边界对齐的工作第一名。
这是怎么回事?
您可以使用代码on godbolt。值得注意的是rsp
和clang
至少在x86上执行“预期的事情”。使用VLA(如之前的评论中所述),icc
和gcc
可以正常运行,clang
会产生憎恶。
1这里,对icc
的赋值只是为了消耗psink
的结果,否则编译器就会完全省略它。