使用 GCC 但没有使用 Clang 的堆栈帧太大(过度对齐?)

Posted

技术标签:

【中文标题】使用 GCC 但没有使用 Clang 的堆栈帧太大(过度对齐?)【英文标题】:Too large (overaligned?) stack frame with GCC but not with Clang 【发布时间】:2021-04-20 09:35:07 【问题描述】:

考虑这个简单的代码:

class X 
  int i_;
public:
  X();
;

void f() 
  X x;

f 的堆栈帧在 GCC 中是 32 字节长,这是不必要的长。返回地址和x 只需要12 字节,根据Linux/x86_64 ABI 应该需要16 字节对齐。使用 Clang,只分配了 16 个字节。为什么 GCC 需要这么多的堆栈空间?

GCC 组装:

f():
  sub   rsp, 24
  lea   rdi, [rsp+12]
  call  X::X()
  add   rsp, 24
  ret

Clang 汇编:

f():       
  push  rax
  mov   rdi, rsp
  call  X::X()
  pop   rax
  ret

两者都是-O2。现场演示:https://godbolt.org/z/bcrWW36on

【问题讨论】:

【参考方案1】:

迷人的兔子洞,我的分析已经改了三遍了。

看来这确实是一个错过的优化。玩了一会儿,我发现了另一个错过的优化,这次是 clang:

如果你真的是use the x object,那么Clang使用rbx缓存x的地址而不是重新计算,这意味着它需要跨函数保存rbx,这扩展了堆栈中的已用空间8 帧(从 12 到 20),将对齐的堆栈帧增加到 32,与 gcc 相同。

从调试的角度来看,我更喜欢 clang 使用 sub rsp, 8 而不是 push rax 来为 x 分配内存,因此在 valgrind 中不会将内存标记为已初始化。

GCC 组装:

f():
    sub     rsp, 24
    lea     rdi, [rsp+12]
    call    X::X() [complete object constructor]
    lea     rdi, [rsp+12]
    call    g(X&)
    add     rsp, 24
    ret

Clang 汇编:

f():
    push    rbx
    sub     rsp, 16
    lea     rbx, [rsp + 8]
    mov     rdi, rbx
    call    X::X() [complete object constructor]
    mov     rdi, rbx
    call    g(X&)
    add     rsp, 16
    pop     rbx
    ret

我已经通过using a 32 byte vector as a data member检查了gcc是否可能使用32字节堆栈对齐,并且gcc和clang都生成代码来对齐堆栈指针,并使用基指针来实现可变长度堆栈帧。不过,我不知道为什么 Clang 在这里为对象分配 64 个字节。

GCC 组装:

f():
    push    rbp
    mov     rbp, rsp
    and     rsp, -32
    sub     rsp, 32
    mov     rdi, rsp
    call    X::X() [complete object constructor]
    leave
    ret

Clang 汇编:

f():                                  # @f()
    push    rbp
    mov     rbp, rsp
    and     rsp, -32
    sub     rsp, 64
    mov     rdi, rsp
    call    X::X() [complete object constructor]
    mov     rsp, rbp
    pop     rbp
    ret

如果不实际测量性能,很难判断哪个更好 -- -O2 将针对运行时进行优化,而不是针对堆栈帧大小进行优化,因此所有这些选择都可能有充分的理由。

【讨论】:

以上是关于使用 GCC 但没有使用 Clang 的堆栈帧太大(过度对齐?)的主要内容,如果未能解决你的问题,请参考以下文章

没有前向声明的嵌套函数模板实例化可以在 GCC 上编译,但不能在 clang 上编译

程序无法使用 GCC 运行,但可以使用 clang

两级嵌套 c++ 类适用于 GCC,但使用 Clang 失败

gcc 和 clang 抛出“没有匹配的函数调用”但 msvc (cl) 编译并按预期工作

使用 GCC/CLANG 追踪代码膨胀的工具

mac 使用gcc 为啥编译错误是clang 提示