3

在大多数平台上,alloca归结为堆栈指针的内联调整(例如,rsp在 x64 上减去,加上一些逻辑以保持堆栈对齐)。

我正在查看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) & ~0xFadd rdi, 30? 那是怎么回事?

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

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

这似乎是在“重新调整”rsp使用的值作为结果alloca- 但我们首先已经完成了与rsp16 字节边界对齐的工作。

那是怎么回事?

你可以在 godbolt 上玩代码。值得注意的是clangicc至少在 x86 上做“预期的事情”。使用 VLA(如之前的评论中所建议的那样),gcc并且在产生可憎的clang同时做得很好。icc


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

4

1 回答 1

7

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

于 2017-03-08T10:37:48.963 回答