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。值得注意的是 clang
和 icc
至少在 x86 上执行 "expected thing"。对于 VLA(如先前评论中所建议的),gcc
和 clang
可以正常工作,而 icc
会产生可憎的结果。
1 在这里,对 psink
的赋值只是为了使用 alloca
的结果,否则编译器会完全忽略它。
这是一个非常古老的正常优先级 bug。该代码工作正常。只是当大小大于1个字节时,多分配了16个字节。所以这不是一个正确性错误,而是一个小的效率错误。
在大多数平台上,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。值得注意的是 clang
和 icc
至少在 x86 上执行 "expected thing"。对于 VLA(如先前评论中所建议的),gcc
和 clang
可以正常工作,而 icc
会产生可憎的结果。
1 在这里,对 psink
的赋值只是为了使用 alloca
的结果,否则编译器会完全忽略它。
这是一个非常古老的正常优先级 bug。该代码工作正常。只是当大小大于1个字节时,多分配了16个字节。所以这不是一个正确性错误,而是一个小的效率错误。