This answer summarizes my findings with regards to some of the complications involved when building projects with LTO enabled, both in GCC and in MSVC.
GCC
First of all, as per the GCC Wiki, in order to properly build a project with LTO enabled, you must:
- Make sure
gcc-ar
is used instead of binutils ar
;
- Make sure
gcc-ranlib
is used instead of binutils ranlib
;
- Make sure
gcc-nm
is used instead of binutils nm
;
- Compile and link with
-flto
.
This means that in the traditional ./configure && make
cycle, one must mind setting the value of AR=
, RANLIB=
and NM=
when relevant. That's mostly it; however, these steps are easily overlooked, because needing to change the value of eg. AR
is rather uncommon.
Now to the issues:
In GCC 4.8 and earlier versions, the compiler emits fat object files as default. This means that even if the post-compilation tools (linker, archiver, etc.) do not recognize LTO objects, they'll work normally (but without actually performing LTO).
In GCC 4.9 and later versions, the compiler emits slim object files as default, meaning that post-compilation tools must recognize LTO objects, or the tools will fail. This explains why sometimes an LTO build passes when using GCC 4.8, but fails when using GCC 4.9 and later.
I have also noticed that build scripts do not always pass correctly the values of some configuration directives to sub-scripts when needed. For example, when building static libiconv
with LTO in MinGW-w64, the configure script still configures the internal libtool
with ar
instead of gcc-ar
, even when told that AR=gcc-ar
.
LTO builds tend to uncover hidden errors, in particular errors caused by static init order fiasco. They can also get in the way of other optimizations, such as ICF (performed by Gold).
Lastly, there are still a number of bugs apparently persisting in the LTO machinery. When attempting to compile ICU with MinGW-w64 with LTO and other optimizations enabled, I've faced this bug and internal compiler errors (internal compiler error: in splice_child_die, at dwarf2out.c
, possibly related to using -g
with LTO).
All of this means that due to several imperfections in the toolchain, building a random project with LTO can still be non-trivial. Some projects will build successfully and some others won't.
MSVC
To compile with LTO in MSVC (which is called LTCG), /GL
must be used when compiling and /LTCG
must be used when linking, and that's really it.
Nonetheless, when LTCG is enabled in MSVC, the compiler does not emit traditional COFF objects. It emits instead a special kind of object file containing IR whose header (ANON_OBJECT_HEADER_BIGOBJ
) is different from the COFF header (IMAGE_FILE_HEADER
). This obviously should make no difference at all when building a project, since these details are left for the toolchain to handle.
Now, why doesn't ICU build properly when LTCG is enabled in MSVC?
ICU has a tool called pkgdata
which generates object code for a given architecture. During the build process, the tool is used to build other utilities in the package. However, pkgdata
tries to guess the target architecture by inspecting a given reference object file. In Windows, the tool assumes a COFF header, and in 32-bit builds it wrongly determines that a 64-bit architecture is being targeted (due to the sloppy logic inside pkg_genc.c:getArchitecture()
). Hence the MSVC 32-bit LTCG build fails.
const
as appropriate. – Thrombin_uconvmsg_dat
in some cases,_icudt58_dat
in others). I'm using several build flags for that matter (/MT /Oxy- /fp:fast /Zi
), however the build always passes without LTCG (/GL
and/LTCG
). – Sheridansherie