From: xur@google.com
To: Masahiro Yamada <masahiroy@kernel.org>,
Nathan Chancellor <nathan@kernel.org>,
Nicolas Schier <nicolas.schier@linux.dev>,
Nick Desaulniers <nick.desaulniers+lkml@gmail.com>,
Bill Wendling <morbo@google.com>,
Justin Stitt <justinstitt@google.com>,
Miguel Ojeda <ojeda@kernel.org>,
Thomas Gleixner <tglx@linutronix.de>,
Alice Ryhl <aliceryhl@google.com>,
Sami Tolvanen <samitolvanen@google.com>,
"Mike Rapoport (Microsoft)" <rppt@kernel.org>,
Rafael Aquini <aquini@redhat.com>,
Michael Ellerman <mpe@ellerman.id.au>,
Stafford Horne <shorne@gmail.com>,
Christophe Leroy <christophe.leroy@csgroup.eu>,
Piotr Gorski <piotrgorski@cachyos.org>, Rong Xu <xur@google.com>,
Venkat Rao Bagalkote <venkat88@linux.ibm.com>,
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>,
Teresa Johnson <tejohnson@google.com>
Cc: linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org,
llvm@lists.linux.dev
Subject: [PATCH v6 2/2] kbuild: distributed build support for Clang ThinLTO
Date: Mon, 16 Mar 2026 21:29:30 +0000 [thread overview]
Message-ID: <20260316212930.120438-3-xur@google.com> (raw)
In-Reply-To: <20260316212930.120438-1-xur@google.com>
From: Rong Xu <xur@google.com>
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>
Reviewed-by: Nathan Chancellor <nathan@kernel.org>
Tested-by: Nathan Chancellor <nathan@kernel.org>
---
.gitignore | 2 ++
Makefile | 9 +++++----
arch/Kconfig | 19 ++++++++++++++++++
scripts/Makefile.lib | 7 +++++++
scripts/Makefile.thinlto | 40 ++++++++++++++++++++++++++++++++++++++
scripts/Makefile.vmlinux_a | 37 +++++++++++++++++++++++++++++++++++
scripts/mod/modpost.c | 15 +++++++++++---
7 files changed, 122 insertions(+), 7 deletions(-)
create mode 100644 scripts/Makefile.thinlto
diff --git a/.gitignore b/.gitignore
index 3a7241c941f5..c12bdf5a97f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,6 +56,7 @@
*.zst
Module.symvers
dtbs-list
+builtin.order
modules.order
#
@@ -67,6 +68,7 @@ modules.order
/vmlinux.32
/vmlinux.map
/vmlinux.symvers
+/vmlinux.thinlto-index
/vmlinux.unstripped
/vmlinux-gdb.py
/vmlinuz
diff --git a/Makefile b/Makefile
index ba5eee68d5d2..4a399b7f3b48 100644
--- a/Makefile
+++ b/Makefile
@@ -1033,10 +1033,10 @@ export CC_FLAGS_SCS
endif
ifdef CONFIG_LTO_CLANG
-ifdef CONFIG_LTO_CLANG_THIN
-CC_FLAGS_LTO := -flto=thin -fsplit-lto-unit
-else
+ifdef CONFIG_LTO_CLANG_FULL
CC_FLAGS_LTO := -flto
+else
+CC_FLAGS_LTO := -flto=thin -fsplit-lto-unit
endif
CC_FLAGS_LTO += -fvisibility=hidden
@@ -1648,6 +1648,7 @@ endif # CONFIG_MODULES
CLEAN_FILES += vmlinux.symvers modules-only.symvers \
modules.builtin modules.builtin.modinfo modules.nsdeps \
modules.builtin.ranges vmlinux.o.map vmlinux.unstripped \
+ vmlinux.thinlto-index builtin.order \
compile_commands.json rust/test \
rust-project.json .vmlinux.objs .vmlinux.export.c \
.builtin-dtbs-list .builtin-dtb.S
@@ -2109,7 +2110,7 @@ clean: $(clean-dirs)
$(call cmd,rmfiles)
@find . $(RCS_FIND_IGNORE) \
\( -name '*.[aios]' -o -name '*.rsi' -o -name '*.ko' -o -name '.*.cmd' \
- -o -name '*.ko.*' \
+ -o -name '*.ko.*' -o -name '*.o.thinlto.bc' \
-o -name '*.dtb' -o -name '*.dtbo' \
-o -name '*.dtb.S' -o -name '*.dtbo.S' \
-o -name '*.dt.yaml' -o -name 'dtbs-list' \
diff --git a/arch/Kconfig b/arch/Kconfig
index 102ddbd4298e..340550e44c50 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -861,6 +861,25 @@ config LTO_CLANG_THIN
https://clang.llvm.org/docs/ThinLTO.html
If unsure, say Y.
+
+config LTO_CLANG_THIN_DIST
+ bool "Clang ThinLTO in distributed mode (EXPERIMENTAL)"
+ depends on HAS_LTO_CLANG && ARCH_SUPPORTS_LTO_CLANG_THIN
+ select LTO_CLANG
+ help
+ This option enables Clang's ThinLTO in distributed build mode.
+ In this mode, the linker performs the thin-link, generating
+ ThinLTO index files. Subsequently, the build system explicitly
+ invokes ThinLTO backend compilation using these index files
+ and pre-linked IR objects. The resulting native object files
+ are with the .thinlto-native.o suffix.
+
+ This build mode offers improved visibility into the ThinLTO
+ process through explicit subcommand exposure. It also makes
+ final native object files directly available, benefiting
+ tools like objtool and kpatch. Additionally, it provides
+ crucial granular control over back-end options, enabling
+ module-specific compiler options, and simplifies debugging.
endchoice
config ARCH_SUPPORTS_AUTOFDO_CLANG
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 0718e39cedda..b36c7c6817bd 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -249,6 +249,12 @@ 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 =
+ cmd_save_c_flags = printf '\n%s\n' 'saved_c_flags_$@ := $(call escsq,$(_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 +262,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)
diff --git a/scripts/Makefile.thinlto b/scripts/Makefile.thinlto
new file mode 100644
index 000000000000..03349ac69de5
--- /dev/null
+++ b/scripts/Makefile.thinlto
@@ -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)
diff --git a/scripts/Makefile.vmlinux_a b/scripts/Makefile.vmlinux_a
index 650d44330d1f..15d92ee82dee 100644
--- a/scripts/Makefile.vmlinux_a
+++ b/scripts/Makefile.vmlinux_a
@@ -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 -q $${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.
# ---------------------------------------------------------------------------
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 0c25b5ad497b..b3c3cf42c9eb 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -1482,13 +1482,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;
--
2.53.0.851.ga537e3e6e9-goog
next prev parent reply other threads:[~2026-03-16 21:29 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-16 21:29 [PATCH v6 0/2] kbuild: distributed build support for Clang ThinLTO xur
2026-03-16 21:29 ` [PATCH v6 1/2] kbuild: move vmlinux.a build rule to scripts/Makefile.vmlinux_a xur
2026-03-25 9:35 ` Nicolas Schier
2026-03-25 16:34 ` Rong Xu
2026-03-16 21:29 ` xur [this message]
2026-03-27 7:59 ` [PATCH v6 2/2] kbuild: distributed build support for Clang ThinLTO Nicolas Schier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260316212930.120438-3-xur@google.com \
--to=xur@google.com \
--cc=aliceryhl@google.com \
--cc=aquini@redhat.com \
--cc=christophe.leroy@csgroup.eu \
--cc=justinstitt@google.com \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=llvm@lists.linux.dev \
--cc=masahiroy@kernel.org \
--cc=miguel.ojeda.sandonis@gmail.com \
--cc=morbo@google.com \
--cc=mpe@ellerman.id.au \
--cc=nathan@kernel.org \
--cc=nick.desaulniers+lkml@gmail.com \
--cc=nicolas.schier@linux.dev \
--cc=ojeda@kernel.org \
--cc=piotrgorski@cachyos.org \
--cc=rppt@kernel.org \
--cc=samitolvanen@google.com \
--cc=shorne@gmail.com \
--cc=tejohnson@google.com \
--cc=tglx@linutronix.de \
--cc=venkat88@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox