mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
kbuild: distributed build support for Clang ThinLTO
Add distributed ThinLTO build support for the Linux kernel.
This new mode offers several advantages: (1) Increased
flexibility in handling user-specified build options.
(2) Improved user-friendliness for developers. (3) Greater
convenience for integrating with objtool and livepatch.
Note that "distributed" in this context refers to a term
that differentiates in-process ThinLTO builds by invoking
backend compilation through the linker, not necessarily
building in distributed environments.
Distributed ThinLTO is enabled via the
`CONFIG_LTO_CLANG_THIN_DIST` Kconfig option. For example:
> make LLVM=1 defconfig
> scripts/config -e LTO_CLANG_THIN_DIST
> make LLVM=1 oldconfig
> make LLVM=1 vmlinux -j <..>
The build flow proceeds in four stages:
1. Perform FE compilation, mirroring the in-process ThinLTO mode.
2. Thin-link the generated IR files and object files.
3. Find all IR files and perform BE compilation, using the flags
stored in the .*.o.cmd files.
4. Link the BE results to generate the final vmlinux.o.
NOTE: This patch currently implements the build for the main kernel
image (vmlinux) only. Kernel module support is planned for a
subsequent patch.
Tested on the following arch: x86, arm64, loongarch, and
riscv.
The earlier implementation details can be found here:
https://discourse.llvm.org/t/rfc-distributed-thinlto-build-for-kernel/85934
Signed-off-by: Rong Xu <xur@google.com>
Co-developed-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
Tested-by: Piotr Gorski <piotrgorski@cachyos.org>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Link: https://patch.msgid.link/20260529185347.2418373-4-xur@google.com
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
This commit is contained in:
committed by
Nathan Chancellor
parent
9c72d26e9f
commit
9f2aee8f7d
@@ -249,6 +249,13 @@ ifdef CONFIG_LTO_CLANG
|
||||
cmd_ld_single = $(if $(objtool-enabled)$(is-single-obj-m), ; $(LD) $(ld_flags) -r -o $(tmp-target) $@; mv $(tmp-target) $@)
|
||||
endif
|
||||
|
||||
ifdef CONFIG_LTO_CLANG_THIN_DIST
|
||||
# Save the _c_flags, sliently.
|
||||
quiet_cmd_save_c_flags =
|
||||
saved_c_flags = $(_c_flags) $(modkern_cflags)
|
||||
cmd_save_c_flags = printf '\n%s\n' 'saved_c_flags_$@ := $(call escsq,$(saved_c_flags))' >> $(dot-target).cmd
|
||||
endif
|
||||
|
||||
quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
|
||||
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< \
|
||||
$(cmd_ld_single) \
|
||||
@@ -256,6 +263,7 @@ quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
|
||||
|
||||
define rule_cc_o_c
|
||||
$(call cmd_and_fixdep,cc_o_c)
|
||||
$(call cmd,save_c_flags)
|
||||
$(call cmd,checksrc)
|
||||
$(call cmd,checkdoc)
|
||||
$(call cmd,gen_objtooldep)
|
||||
|
||||
40
scripts/Makefile.thinlto
Normal file
40
scripts/Makefile.thinlto
Normal file
@@ -0,0 +1,40 @@
|
||||
PHONY := __default
|
||||
__default:
|
||||
|
||||
include include/config/auto.conf
|
||||
include $(srctree)/scripts/Kbuild.include
|
||||
include $(srctree)/scripts/Makefile.lib
|
||||
|
||||
native-objs := $(patsubst %.o,%.thinlto-native.o,$(call read-file, vmlinux.thinlto-index))
|
||||
|
||||
__default: $(native-objs)
|
||||
|
||||
# Generate .thinlto-native.o (obj) from .o (bitcode) and .thinlto.bc (summary) files
|
||||
# ---------------------------------------------------------------------------
|
||||
quiet_cmd_cc_o_bc = CC $(quiet_modtag) $@
|
||||
be_flags = $(shell sed -n '/saved_c_flags_/s/.*:= //p' \
|
||||
$(dir $(<)).$(notdir $(<)).cmd)
|
||||
cmd_cc_o_bc = \
|
||||
$(CC) $(be_flags) -x ir -fno-lto -Wno-unused-command-line-argument \
|
||||
-fthinlto-index=$(word 2, $^) -c -o $@ $<
|
||||
|
||||
targets += $(native-objs)
|
||||
$(native-objs): %.thinlto-native.o: %.o %.o.thinlto.bc FORCE
|
||||
$(call if_changed,cc_o_bc)
|
||||
|
||||
# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
PHONY += FORCE
|
||||
FORCE:
|
||||
|
||||
# Read all saved command lines and dependencies for the $(targets) we
|
||||
# may be building above, using $(if_changed{,_dep}). As an
|
||||
# optimization, we don't need to read them if the target does not
|
||||
# exist, we will rebuild anyway in that case.
|
||||
|
||||
existing-targets := $(wildcard $(sort $(targets)))
|
||||
|
||||
-include $(foreach f, $(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
|
||||
|
||||
.PHONY: $(PHONY)
|
||||
@@ -21,6 +21,41 @@ targets += built-in-fixup.a
|
||||
built-in-fixup.a: $(KBUILD_VMLINUX_OBJS) scripts/head-object-list.txt FORCE
|
||||
$(call if_changed,ar_builtin_fixup)
|
||||
|
||||
ifdef CONFIG_LTO_CLANG_THIN_DIST
|
||||
|
||||
quiet_cmd_builtin.order = GEN $@
|
||||
cmd_builtin.order = $(AR) t $< > $@
|
||||
|
||||
targets += builtin.order
|
||||
builtin.order: built-in-fixup.a FORCE
|
||||
$(call if_changed,builtin.order)
|
||||
|
||||
quiet_cmd_ld_thinlto_index = LD $@
|
||||
cmd_ld_thinlto_index = \
|
||||
$(LD) $(KBUILD_LDFLAGS) -r --thinlto-index-only=$@ @$<
|
||||
|
||||
targets += vmlinux.thinlto-index
|
||||
vmlinux.thinlto-index: builtin.order FORCE
|
||||
$(call if_changed,ld_thinlto_index)
|
||||
|
||||
quiet_cmd_ar_vmlinux.a = GEN $@
|
||||
cmd_ar_vmlinux.a = \
|
||||
rm -f $@; \
|
||||
while read -r obj; do \
|
||||
if grep -Fqx $${obj} $(word 2, $^); then \
|
||||
echo $${obj%.o}.thinlto-native.o; \
|
||||
else \
|
||||
echo $${obj}; \
|
||||
fi; \
|
||||
done < $< | xargs $(AR) cDPrS --thin $@
|
||||
|
||||
targets += vmlinux.a
|
||||
vmlinux.a: builtin.order vmlinux.thinlto-index FORCE
|
||||
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.thinlto
|
||||
$(call if_changed,ar_vmlinux.a)
|
||||
|
||||
else
|
||||
|
||||
# vmlinux.a
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -28,6 +63,8 @@ targets += vmlinux.a
|
||||
vmlinux.a: built-in-fixup.a FORCE
|
||||
$(call if_changed,copy)
|
||||
|
||||
endif
|
||||
|
||||
# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1487,13 +1487,22 @@ static void extract_crcs_for_object(const char *object, struct module *mod)
|
||||
char cmd_file[PATH_MAX];
|
||||
char *buf, *p;
|
||||
const char *base;
|
||||
int dirlen, ret;
|
||||
int dirlen, baselen_without_suffix, ret;
|
||||
|
||||
base = get_basename(object);
|
||||
dirlen = base - object;
|
||||
|
||||
ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%s.cmd",
|
||||
dirlen, object, base);
|
||||
baselen_without_suffix = strlen(object) - dirlen - strlen(".o");
|
||||
|
||||
/*
|
||||
* When CONFIG_LTO_CLANG_THIN_DIST=y, the ELF is *.thinlto-native.o
|
||||
* but the symbol CRCs are recorded in *.o.cmd file.
|
||||
*/
|
||||
if (strends(object, ".thinlto-native.o"))
|
||||
baselen_without_suffix -= strlen(".thinlto-native");
|
||||
|
||||
ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%.*s.o.cmd",
|
||||
dirlen, object, baselen_without_suffix, base);
|
||||
if (ret >= sizeof(cmd_file)) {
|
||||
error("%s: too long path was truncated\n", cmd_file);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user