使用 GCC 编译但链接 LLVM LLD 时 LTO 是否有效?

Does LTO works when compiling with GCC but linking with LLVM LLD?

我最近发现了 LLVM 的 linker,lld 因非常快 linking 而受到称赞。事实上,我对其进行了测试,结果非常棒,与 [​​=13=].

相比,我的 linking 时间大大减少了

不过,说到link时的优化,我的见识有限。据我通过阅读互联网上的资料了解到,目标文件中产生了一些额外的代码,代表一些内部编译器结构,然后在 linking 阶段使用。因此,我担心 link 时间优化(及其好处)是否会受到这种 compiler/linker 混合的影响。我将不胜感激对此事的一些解释!

我使用了 gcc 版本 9.2.0lld 版本 10.0.0

我用来生成目标文件的命令:

/opt/gcc/9.2.0/bin/c++ -fPIE -flto -ffat-lto-objects -fuse-linker-plugin -m64 -O3 -g -DNDEBUG -o my_object.cpp.o -c my_source_file.cpp

对于 linking:

#-fuse-ld=gold
/opt/gcc/9.2.0/bin/c++ -fPIE -flto -ffat-lto-objects -fuse-linker-plugin -m64 -pie -fuse-ld=gold -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -static-libstdc++ -static-libgcc -Wl,--threads -Wl,--thread-count,1
#-fuse-ld=lld
/opt/gcc/9.2.0/bin/c++ -fPIE -flto -ffat-lto-objects -fuse-linker-plugin -m64 -pie -fuse-ld=lld -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -static-libstdc++ -static-libgcc -Wl,--threads -Wl,

我做了一些研究,最后自己得出结论,如果我们在用 gcc 编译时使用 lld,则不会完成 LTO。我做了什么:

基于这个有点模糊的表述:https://www.slideshare.net/chimerawang/gcc-lto,我发现 linker 不是直接进行优化,而是在读取所有目标文件中的所有符号之后,他将信息传递给 lto-wrapper,后者然后通过其他一些流程进行优化。因此,我使用 hello-world cpp 文件进行了测试,并使用 -v 标志对其进行了编译,实际上我看到了前面提到的连续调用 (collect2 (linker) -> lto-wrapper -> lto1)。但这是在使用默认的 linker 或 gold linker 时。当我使用 -fuse-ld=lld 标志时,只调用了 collect2 进程。而这第一件事让我相信 LTO 根本没有完成。

但是,嘿,也许 lld linker 内化了 LTO 流程,因此无需调用任何其他流程即可完成。所以我做了另一个测试,看看 LTO 是否完成(基于 this 文章)。基本上,我从一个 cpp 文件中调用了 100 000 000 次在其他 cpp 文件中定义的函数,该函数什么都不做。使用基本 -O2 优化,生成的二进制文件运行时间约为 200 毫秒,因为编译器无法优化无用的函数调用。当同时使用 -flto 标志和 ldgold linker 时,生成的二进制文件运行时间约为 2 毫秒。但是当使用 lld linker 时,生成的二进制文件也会在 ~200 毫秒内运行。因此 lld 使用 lto 的运行速度与不使用 lto 的 lld 一样慢。没有任何优化迹象。
这里要提到的是,使用lld linker,如果不使用-ffat-lto-objects编译对象,link命令将失败。此标志使目标文件更大,因为编译器不仅转储 lto 代码,而且转储无需 lto 即可 linked 的代码。

因此,考虑到用 lld 编译的二进制 link 的时间性能以及对象需要用 -ffat-lto-objects 编译的事实,我得出结论,当 lld使用了linker,根本没有实现LTO,但是lld为了link二进制文件,使用了编译器生成的非LTO代码。