【发布时间】:2017-07-18 16:31:06
【问题描述】:
在大多数平台上,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 但它在add rdi, 30 处添加 30?这是怎么回事?
其次,我只希望alloca 的结果是新的rsp 值,它已经很好地对齐了。相反,gcc 这样做:
lea rax, [rsp+15]
and rax, -16
这似乎是在“重新对齐”rsp 的值以用作 alloca 的结果 - 但我们已经完成了将 rsp 对齐到 16 字节边界的工作。
这是怎么回事?
您可以使用代码on godbolt。值得注意的是,clang 和 icc 至少在 x86 上做了“预期的事情”。使用 VLA(如之前的 cmets 中所建议的),gcc 和 clang 可以正常工作,而 icc 会产生可恶的情况。
1 这里,psink 的赋值只是消耗alloca 的结果,否则编译器会完全忽略它。
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。