为什么在不使用堆栈内存的情况下会分配堆栈内存?

14

考虑以下例子:

struct vector {
    int  size() const;
    bool empty() const;
};

bool vector::empty() const
{
    return size() == 0;
}

通过优化的clang编译器生成的vector::empty函数的汇编代码:

push    rax
call    vector::size() const
test    eax, eax
sete    al
pop     rcx
ret

为什么它要分配栈空间?它根本没有被使用。我们可以省略 pushpop。MSVC 和 GCC 的优化构建也会为此函数使用栈空间(请参见 godbolt),因此一定有原因。


7
你有考虑到隐含的 this 参数吗? - dan04
1
@Bob__: 不,我为什么要这样做呢?vector::size()在示例中没有定义,以模拟它不是内联的。 - Dr. Gut
1
那么,编译器如何优化它不知道的东西呢? - Bob__
1
@Bob__: 返回值在 eax 中。不需要使用堆栈。 - Fred Larson
1
“push” 和 “pop” 不能省略,因为它们不使用相同的寄存器。 - Mark Ransom
显示剩余7条评论
1个回答

12
它分配栈空间,因此栈是16字节对齐的。这是必要的,因为返回地址占用8个字节,所以需要额外的8个字节来保持栈的16字节对齐。
可以使用某些编译器的命令行参数配置堆栈帧的对齐方式。
  • MSVC: 文档中指出栈始终具有16字节对齐。没有命令行参数可以改变这一点。godbolt示例显示,在函数开始时从rsp减去了40个字节,这意味着还有其他因素影响了它。
  • clang: -mstack-alignment选项指定堆栈对齐方式。似乎默认值为16,尽管未记录。如果将其设置为8,则堆栈分配(pushpop)将从生成的汇编代码中消失。
  • gcc: -mpreferred-stack-boundary选项指定堆栈对齐方式。如果给定的值是N,则表示2^N字节对齐。默认值为4,即16字节。如果将其设置为3(即8字节),则堆栈分配(subadd用于rsp)将从生成的汇编代码中消失。

godbolt上查看。


这就是为什么C++大师、专家一直在警告:将结构体/类成员按照从大到小的顺序排列...只有这样才能正确高效地实现。 - user12450543
@geza:谢谢。我为另外两个编译器做了一些研究,并将其写入了你的答案。你喜欢吗? - Dr. Gut
2
@Dr.Gut:谢谢,你让答案更好、更完整了。请注意,堆栈对齐通常在系统的ABI文档中有所说明(例如,对于某些系统,这里是文档链接:https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI)。 - geza
@geza:谢谢。 - Dr. Gut

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接