gcc 对 alloca 的处理是怎么回事?

What's up with gcc's handling of alloca?

在大多数平台上,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 但它在 [=19 处增加 30 =]?这是怎么回事?

其次,我希望 alloca 的结果是新的 rsp 值,它已经很好地对齐了。相反,gcc 这样做:

    lea     rax, [rsp+15]
    and     rax, -16

这似乎是 "realigning" rsp 的值用作 alloca 的结果 - 但我们已经完成了将 rsp 对齐到 16 的工作-字节边界在第一位。

这是怎么回事?

你可以玩代码on godbolt。值得注意的是 clangicc 至少在 x86 上执行 "expected thing"。对于 VLA(如先前评论中所建议的),gccclang 可以正常工作,而 icc 会产生可憎的结果。


1 在这里,对 psink 的赋值只是为了使用 alloca 的结果,否则编译器会完全忽略它。

这是一个非常古老的正常优先级 bug。该代码工作正常。只是当大小大于1个字节时,多分配了16个字节。所以这不是一个正确性错误,而是一个小的效率错误。