在大多数平台上,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 。值得注意的是 clangicc 至少在 x86 上做了“预期的事情”。使用 VLA(如之前的评论中所建议的),gccclang 效果很好,而 icc 会产生可憎的效果。

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

最佳答案

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

关于c - gcc 对 alloca 的处理是怎么回事?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42496286/

10-12 16:14