c++ - 为什么 MSVC C++ 编译器将一个简单的 Hello World 扩展为 4000 行汇编代码?

标签 c++ visual-studio assembly visual-c++ compiler-explorer

最近,我正在深入优化我的 C++ 代码,因此开始尝试使用编译器资源管理器。由于我主要使用 Visual Studio 在 Windows 上进行开发,因此我使用了 msvc 编译器。

在某些时候 msvc 失控了。经过一番摆弄之后,我可以将其缩小到 iostream header ,这应该是 I/O 的首选( SL.io.3 )。

#include <iostream>
int main() {
    std::cout << "Hello World!\n";
    return 0;
}

而 gcc 或 clang 的总输出(main + 一个调用一些 ios_base init 函数的静态初始化器)总共有大约 20 行汇编代码(在 Godbolt 编译器浏览器过滤掉指令和注释之后)。
MSVC explodes it进入 4000。这些行中的大多数是单独的功能; MSVC对main的定义本身是 7 条指令,而 gcc/clang 则是 8 条指令。 (gcc/clang 使用 GNU/Linux libstdc++ 将额外长度的 arg 传递给 cout 运算符重载函数,而不是像 MSVC 在使用自己的 C++ 库时那样只传递 2 个指针。)

如果我使用类似 puts 的东西相反,MSVC 的总输出相当紧凑,可与 gcc/clang 相媲美,例如 here .

有人可以向我解释这里发生了什么,我做错了什么或指出我正确的方向吗?

为什么 MSVC asm 列表对于使用 C++ 库的简单函数如此臃肿?

最佳答案

这可能不是一个完整的答案,但我想我可以解释很多差异。

大部分标准库(例如 iostreams)都是模板繁重的代码。我相信 Microsoft 编译器会生成更多模板实例并依赖链接器来删除不必要的模板。我认为这是 Windows 链接器与大多数 Posix 链接器使用的不同策略的结果,但这也可能是简单地使用不同的标准库实现的结果。

如果您指定 /MD ,它告诉编译器您打算使用标准库的 DLL 版本,生成的代码从 4000 多行减少到少于 500 行。我不知道为什么会这样。也许 MSVC 知道 DLL 库具有所有必要的模板实例,而静态库依赖于来自编译器的模板实例。

您可以通过仅处理 C++ 异常(使用 /EHs )来获得增量改进。默认情况下,编译器也会生成处理异步系统异常的代码。虽然您的 hello-world 示例没有明确使用异常,但部分标准库可能会使用。在这一点上,看起来很多附加行都在设置堆栈展开表并调用析构函数。

MSVC 版本中的很多剩余部分看起来是为了在调用析构函数时展开堆栈而存在的,因此异常处理模型可能会有所不同。

我以为编译器资源管理器过去有一个“clang-cl”选项,但现在我看不到了。 clang-cl,一般来说,是一个命令驱动程序,它解释 cl.exe 选项并调整默认选项,使 clang 生成与 Microsoft 代码兼容的二进制 ABI 代码。看看它是否生成像常规 clang 一样的代码,或者它是否最终发出更像 MSVC 的代码会很有趣。

关于c++ - 为什么 MSVC C++ 编译器将一个简单的 Hello World 扩展为 4000 行汇编代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61879225/

相关文章:

c++ - 预期 { 在输入结束时

c# - 如何在删除 {} 括号时阻止 VisualStudio 展开所有内容

c# - 获得早期绑定(bind)关系

assembly - Linux 内核从实模式到保护模式的转换

assembly - 在 8086 微处理器上将 32 位两个数字相乘

c++ - 使用 C++ 在文件中进行字符串搜索/索引

c++ - 使用 C++ 解析文件,将值加载到结构中

c++ - 如何检测 W2A 转换是否完全成功

html - 如何在 Visual Studio Code/IDE 中搜索不包含其他术语的特定术语?

c - 跳过模拟器中的指令