diff options
author | Andi Kleen <ak@linux.intel.com> | 2014-04-02 21:49:27 +0200 |
---|---|---|
committer | Michal Marek <mmarek@suse.cz> | 2014-04-07 21:51:13 +0200 |
commit | 19a3cc83353e3bb4bc28769f8606139a3d350d2d (patch) | |
tree | b58a21b5ec4880b56dd4005cb9cfcf8c2f8a2821 /scripts | |
parent | 810361b9f65daa6144922ac88087a8426eeae817 (diff) |
Kbuild, lto: Add Link Time Optimization support v3
With LTO gcc will do whole program optimizations for
the whole kernel and each module. This increases compile time,
but can generate faster and smaller code and allows
the compiler to do global checking. For example the compiler
can complain now about type mismatches for symbols between
different files.
LTO allows gcc to inline functions between different files and
do various other optimization across the whole binary.
It might also trigger bugs due to more aggressive optimizations.
It allows gcc to drop unused code. It also allows it to check
types over the whole program.
The compile time is definitely slower. For gcc 4.8 on a
typical monolithic config it is about 58% slower. 4.9
drastically improved performance, with slowdown being
38% or so. Also incremenential rebuilds are somewhat
slower, as the whole kernel always needs to be reoptimized.
Very modular kernels have less build time slow down, as
the LTO will run for each module individually.
This adds the basic Kbuild plumbing for LTO:
- In Kbuild add a new scripts/Makefile.lto that checks
the tool chain (note the checks may not be fully bulletproof)
and when the tests pass sets the LTO options
Currently LTO is very finicky about the tool chain.
- Add a new LDFINAL variable that controls the final link
for vmlinux or module. In this case we call gcc-ld instead
of ld, to run the LTO step.
- For slim LTO builds (object files containing no backup
executable) force AR to gcc-ar
- Theoretically LTO should pass through compiler options from
the compiler to the link step, but this doesn't work for all options.
So the Makefile sets most of these options manually.
- Kconfigs:
Since LTO with allyesconfig needs more than 4G of memory (~8G)
and has the potential to makes people's system swap to death.
I used a nested config that ensures that a simple
allyesconfig disables LTO. It has to be explicitely
enabled.
- Some depencies on other Kconfigs:
MODVERSIONS, GCOV, FUNCTION_TRACER, KALLSYMS_ALL, single chain WCHAN are
incompatible with LTO currently, mostly because they
they require setting special compiler options
for specific files, which LTO currently doesn't support.
MODVERSIONS should in principle work with gcc 4.9, but still disabled.
FUNCTION_TRACER/GCOV can be fixed with a unmerged gcc patch.
- Also disable strict copy user checks because they trigger
errors with LTO.
- modpost symbol checking is downgraded to a warning,
as in some cases modpost runs before the final link
and it cannot resolve LTO symbols at this point.
For more information see Documentation/lto-build
Thanks to HJ Lu, Joe Mario, Honza Hubicka, Richard Guenther,
Don Zickus, Changlong Xie who helped with this project
(and probably some more who I forgot, sorry)
v2:
Merge documentation file into this patch
Improve documentation and Kconfig, fix a lot of obsolete comments.
Exclude READABLE_ASM
Some random fixes
v3:
Remove CONFIG_LTO_SLIM, is on by default.
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Michal Marek <mmarek@suse.cz>
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/Makefile.lto | 84 | ||||
-rw-r--r-- | scripts/Makefile.modpost | 7 | ||||
-rw-r--r-- | scripts/link-vmlinux.sh | 2 |
3 files changed, 89 insertions, 4 deletions
diff --git a/scripts/Makefile.lto b/scripts/Makefile.lto new file mode 100644 index 000000000000..b8e9e4836f4b --- /dev/null +++ b/scripts/Makefile.lto @@ -0,0 +1,84 @@ +# +# Support for gcc link time optimization +# + +DISABLE_LTO := +LTO_CFLAGS := + +export DISABLE_LTO +export LTO_CFLAGS + +ifdef CONFIG_LTO +# 4.7 works mostly, but it sometimes loses symbols on large builds +# This can be worked around by marking those symbols visible, +# but that is fairly ugly and the problem is gone with 4.8 +# So only allow it with 4.8 for now. +ifeq ($(call cc-ifversion, -ge, 0408,y),y) +ifneq ($(call cc-option,${LTO_CFLAGS},n),n) +# We need HJ Lu's Linux binutils because mainline binutils does not +# support mixing assembler and LTO code in the same ld -r object. +# XXX check if the gcc plugin ld is the expected one too +# XXX some Fedora binutils should also support it. How to check for that? +ifeq ($(call ld-ifversion,-ge,22710001,y),y) + LTO_CFLAGS := -flto -fno-toplevel-reorder + LTO_FINAL_CFLAGS := -fuse-linker-plugin + +# the -fno-toplevel-reorder is to preserve the order of initcalls +# everything else should tolerate reordering + LTO_FINAL_CFLAGS +=-fno-toplevel-reorder + +# enable LTO and set the jobs used by the LTO phase +# this should be -flto=jobserver to coordinate with the +# parent make, but work around +# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50639 +# use as many jobs as processors are online for now +# this actually seems to be a kernel bug with the pipe code + LTO_FINAL_CFLAGS := -flto=$(shell getconf _NPROCESSORS_ONLN) + #LTO_FINAL_CFLAGS := -flto=jobserver + + # requires plugin ar passed and very recent HJ binutils + LTO_CFLAGS += -fno-fat-lto-objects + +# Used to disable LTO for specific files (e.g. vdso) + DISABLE_LTO := -fno-lto + + LTO_FINAL_CFLAGS += ${LTO_CFLAGS} -fwhole-program + +ifdef CONFIG_LTO_DEBUG + LTO_FINAL_CFLAGS += -dH -fdump-ipa-cgraph -fdump-ipa-inline-details + # -Wl,-plugin-save-temps -save-temps + LTO_CFLAGS += +endif +ifdef CONFIG_LTO_CP_CLONE + LTO_FINAL_CFLAGS += -fipa-cp-clone + LTO_CFLAGS += -fipa-cp-clone +endif + + # In principle gcc should pass through options in the object files, + # but it doesn't always work. So do it here manually + # Note that special options for individual files does not + # work currently (except for some special cases that only + # affect the compiler frontend) + # The main offenders are FTRACE and GCOV -- we exclude + # those in the config. + LTO_FINAL_CFLAGS += $(filter -g%,${KBUILD_CFLAGS}) + LTO_FINAL_CFLAGS += $(filter -O%,${KBUILD_CFLAGS}) + LTO_FINAL_CFLAGS += $(filter -f%,${KBUILD_CFLAGS}) + LTO_FINAL_CFLAGS += $(filter -m%,${KBUILD_CFLAGS}) + LTO_FINAL_CFLAGS += $(filter -W%,${KBUILD_CFLAGS}) + + KBUILD_CFLAGS += ${LTO_CFLAGS} + + LDFINAL := ${CONFIG_SHELL} ${srctree}/scripts/gcc-ld \ + ${LTO_FINAL_CFLAGS} + +else + $(warning "WARNING: Too old linker version $(call ld-version) for kernel LTO. You need Linux binutils. CONFIG_LTO disabled.") +endif +else + $(warning "WARNING: Compiler/Linker does not support LTO/WHOPR with linker plugin. CONFIG_LTO disabled.") +endif +else + $(warning "WARNING: GCC $(call cc-version) too old for LTO/WHOPR. CONFIG_LTO disabled") +endif +endif diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 69f0a1417e9a..9c40daea846c 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -77,7 +77,8 @@ modpost = scripts/mod/modpost \ $(if $(KBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(KBUILD_EXTRA_SYMBOLS))) \ $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ - $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) + $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \ + $(if $(CONFIG_LTO),-w) MODPOST_OPT=$(subst -i,-n,$(filter -i,$(MAKEFLAGS))) @@ -115,8 +116,8 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE targets += $(modules:.ko=.mod.o) # Step 6), final link of the modules -quiet_cmd_ld_ko_o = LD [M] $@ - cmd_ld_ko_o = $(LD) -r $(LDFLAGS) \ +quiet_cmd_ld_ko_o = LDFINAL [M] $@ + cmd_ld_ko_o = $(LDFINAL) -r $(LDFLAGS) \ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ -o $@ $(filter-out FORCE,$^) diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 2dcb37736d84..774145df731c 100644 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -53,7 +53,7 @@ vmlinux_link() local lds="${objtree}/${KBUILD_LDS}" if [ "${SRCARCH}" != "um" ]; then - ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} \ + ${LDFINAL} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2} \ -T ${lds} ${KBUILD_VMLINUX_INIT} \ --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1} else |