在编写代码或构建脚本以使用LTO进行编译时,需要牢记哪些注意事项和陷阱?
这个问题背后的动机是更好地理解为什么启用LTO时为什么某些项目不能完全编译。特别是,无论是在MSVC还是在GCC中,我都无法在启用LTO的情况下构建ICU。在其他情况下,我可以使用给定的工具链版本启用LTO,但不能使用另一个(较新的)版本启用LTO。例如,使用libiconv就会发生这种情况。
在我遇到的所有失败案例中,都涉及到由于符号未解析而导致的链接失败。
为什么会这样呢?这是工具链,构建脚本还是源代码的问题?
最佳答案
这个答案总结了我的发现,涉及在GCC和MSVC中构建启用LTO的项目时涉及的一些复杂性。
海湾合作委员会
首先,按照GCC Wiki,要正确构建启用了LTO的项目,您必须:
gcc-ar
代替binutils ar
; gcc-ranlib
代替binutils ranlib
; gcc-nm
代替binutils nm
; -flto
。 这意味着在传统的
./configure && make
周期中,必须注意在相关时设置AR=
,RANLIB=
和NM=
的值。仅此而已;但是,这些步骤很容易被忽略,因为需要更改eg的值。 AR
很少见。现在到问题:
在GCC 4.8和更早版本中,编译器默认发出胖对象文件。这意味着,即使后编译工具(链接器,归档器等)无法识别LTO对象,它们也可以正常工作(但无需实际执行LTO)。
在GCC 4.9和更高版本中,编译器默认发出细长目标文件,这意味着编译后工具必须识别LTO对象,否则该工具将失败。这就解释了为什么有时在使用GCC 4.8时LTO构建会通过,而在使用GCC 4.9及更高版本时会失败。
我还注意到,构建脚本并不一定总是在需要时正确地将某些配置指令的值传递给子脚本。例如,当在MinGW-w64中使用LTO构建静态
libiconv
时,即使告知libtool
,configure脚本仍然使用ar
而不是gcc-ar
配置内部AR=gcc-ar
。LTO构建倾向于发现隐藏的错误,尤其是由static init order fiasco引起的错误。它们还可以妨碍其他优化,例如ICF(由Gold执行)。
最后,LTO机制中显然仍然存在许多错误。尝试使用启用了LTO和其他优化功能的MinGW-w64编译ICU时,我遇到了this bug和内部编译器错误(
internal compiler error: in splice_child_die, at dwarf2out.c
,可能与在LTO上使用-g
有关)。所有这些意味着,由于工具链中的一些缺陷,使用LTO建立随机项目仍然是不容易的。一些项目将成功构建,而另一些则不会。
MSVC
要在MSVC(称为LTCG)中使用LTO进行编译,在编译时必须使用
/GL
,在链接时必须使用/LTCG
,事实就是如此。但是,在MSVC中启用LTCG时,编译器不会发出传统的COFF对象。相反,它将发出包含IR的special kind of object file,该IR的 header (
ANON_OBJECT_HEADER_BIGOBJ
)与COFF header (IMAGE_FILE_HEADER
)不同。显然,这在构建项目时根本没有任何区别,因为这些细节留给了工具链来处理。现在,当在MSVC中启用LTCG时,为什么ICU无法正确构建?
ICU有一个称为
pkgdata
的工具,可以为给定的体系结构生成目标代码。在构建过程中,该工具用于构建软件包中的其他实用程序。但是,pkgdata
尝试通过检查给定的引用对象文件来猜测目标体系结构。在Windows中,该工具采用COFF header ,而在32位版本中,该工具错误地确定以64位体系结构为目标(由于pkg_genc.c:getArchitecture()
内部的草率逻辑)。因此,MSVC 32位LTCG构建失败。关于c++ - 如何编写启用LTO的代码?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42230141/