这个答案总结了我在 GCC 和 MSVC 中构建启用 LTO 的项目时所涉及的一些复杂性的发现。
海合会
首先,根据GCC Wiki,为了正确构建启用 LTO 的项目,您必须:
- 确保
gcc-ar
使用而不是 binutils ar
;
- 确保
gcc-ranlib
使用而不是 binutils ranlib
;
- 确保
gcc-nm
使用而不是 binutils nm
;
- 编译和链接
-flto
。
这意味着在传统./configure && make
循环中,必须注意设置 的值AR=
,RANLIB=
并NM=
在相关时设置。就是这样。但是,这些步骤很容易被忽略,因为需要更改例如的值。AR
比较少见。
现在到问题:
在 GCC 4.8 和更早的版本中,编译器默认发出胖目标文件。这意味着即使编译后工具(链接器、归档器等)无法识别 LTO 对象,它们也会正常工作(但不会实际执行 LTO)。
在 GCC 4.9 及更高版本中,编译器默认发出 slim 对象文件,这意味着编译后工具必须识别 LTO 对象,否则工具将失败。这解释了为什么有时 LTO 构建在使用 GCC 4.8 时会通过,但在使用 GCC 4.9 及更高版本时会失败。
我还注意到构建脚本并不总是在需要时将某些配置指令的值正确传递给子脚本。例如,当libiconv
在 MinGW-w64 中使用 LTO 构建静态时,配置脚本仍然libtool
使用ar
而不是配置内部gcc-ar
,即使被告知AR=gcc-ar
.
LTO 构建倾向于发现隐藏的错误,特别是由静态 init order fiasco引起的错误。它们还可能妨碍其他优化,例如 ICF(由 Gold 执行)。
最后,显然 LTO 机器中仍然存在许多错误。在尝试使用启用了 LTO 和其他优化的 MinGW-w64 编译 ICU 时,我遇到了这个错误和内部编译器错误(internal compiler error: in splice_child_die, at dwarf2out.c
可能与使用-g
LTO 有关)。
所有这一切都意味着,由于工具链中的一些缺陷,使用 LTO 构建随机项目仍然不是一件容易的事。有些项目会成功构建,有些则不会。
MSVC
在MSVC中用LTO编译(称为LTCG),/GL
编译时/LTCG
必须使用,链接时必须使用,原来如此。
尽管如此,当在 MSVC 中启用 LTCG 时,编译器不会发出传统的 COFF 对象。相反,它发出一种特殊类型的目标文件,其中包含 IR,其标头 ( ANON_OBJECT_HEADER_BIGOBJ
) 与 COFF 标头 ( IMAGE_FILE_HEADER
) 不同。这在构建项目时显然应该没有什么区别,因为这些细节留给工具链处理。
现在,为什么在 MSVC 中启用 LTCG 时 ICU 不能正确构建?
ICU 有一个称为pkgdata
为给定架构生成目标代码的工具。在构建过程中,该工具用于构建包中的其他实用程序。但是,pkgdata
尝试通过检查给定的参考对象文件来猜测目标架构。在 Windows 中,该工具采用 COFF 标头,在 32 位构建中,它错误地确定目标是 64 位架构(由于内部的草率逻辑pkg_genc.c:getArchitecture()
)。因此,MSVC 32 位 LTCG 构建失败。