Alice Ryhl
2025-Dec-02 20:27 UTC
[PATCH 4/4] build: rust: provide an option to inline C helpers into Rust
From: Gary Guo <gary at garyguo.net>
A new experimental Kconfig option, `RUST_INLINE_HELPERS` is added to
allow C helpers (which were created to allow Rust to call into
inline/macro C functions without having to re-implement the logic in
Rust) to be inlined into Rust crates without performing global LTO.
If the option is enabled, the following is performed:
* For helpers, instead of compiling them to an object file to be linked
into vmlinux, they're compiled to LLVM IR.
* The LLVM IR is compiled to bitcode (This is step is not necessary, but
is a performance optimisation to prevent LLVM from always have to
reparse the same IR).
* When a Rust crate is compiled, instead of generating an object file, we
ask LLVM bitcode to be generated.
* llvm-link is invoked with --internalize to combine the helper bitcode
with the crate bitcode. This step is similar to LTO, but this is much
faster since it only needs to inline the helpers.
* clang is invoked to turn the combined bitcode into a final object file.
The --internalize flag tells llvm-link to treat all symbols in
helpers.bc using `internal` linkage. This matches the behavior of
`clang` on `static inline` functions, and avoids exporting the symbol
from the object file.
To ensure that RUST_INLINE_HELPERS is not incompatible with BTF, we pass
the -g0 flag when building helpers. See commit 5daa0c35a1f0 ("rust:
Disallow BTF generation with Rust + LTO") for details.
We have an intended triple mismatch of `aarch64-unknown-none` vs
`aarch64-unknown-linux-gnu`, so we suppress the warning.
Co-developed-by: Boqun Feng <boqun.feng at gmail.com>
Signed-off-by: Boqun Feng <boqun.feng at gmail.com>
Co-developed-by: Matthew Maurer <mmaurer at google.com>
Signed-off-by: Matthew Maurer <mmaurer at google.com>
Signed-off-by: Gary Guo <gary at garyguo.net>
Co-developed-by: Alice Ryhl <aliceryhl at google.com>
Signed-off-by: Alice Ryhl <aliceryhl at google.com>
---
Makefile | 4 +++-
lib/Kconfig.debug | 15 +++++++++++++++
rust/Makefile | 26 ++++++++++++++++++++++----
rust/exports.c | 5 ++++-
scripts/Makefile.build | 5 ++++-
5 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/Makefile b/Makefile
index
96ddbaae7e12de71bcfabef4639de3a13a6e4815..5834bfd568548d1bee34b328dccce5d60f85526f
100644
--- a/Makefile
+++ b/Makefile
@@ -517,6 +517,8 @@ OBJCOPY = $(LLVM_PREFIX)llvm-objcopy$(LLVM_SUFFIX)
OBJDUMP = $(LLVM_PREFIX)llvm-objdump$(LLVM_SUFFIX)
READELF = $(LLVM_PREFIX)llvm-readelf$(LLVM_SUFFIX)
STRIP = $(LLVM_PREFIX)llvm-strip$(LLVM_SUFFIX)
+LLVM_LINK = $(LLVM_PREFIX)llvm-link$(LLVM_SUFFIX)
+LLVM_AS = $(LLVM_PREFIX)llvm-as$(LLVM_SUFFIX)
else
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
@@ -625,7 +627,7 @@ export RUSTC_BOOTSTRAP := 1
export CLIPPY_CONF_DIR := $(srctree)
export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD
CC HOSTPKG_CONFIG
-export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN
+export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN
LLVM_LINK LLVM_AS
export HOSTRUSTC KBUILD_HOSTRUSTFLAGS
export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC
AWK INSTALLKERNEL
export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index
3034e294d50df55c4003c5fa5df442f59e711bd8..e63c5eb57b049aff988419ccd12dfd99d59f5080
100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3427,6 +3427,21 @@ config RUST_KERNEL_DOCTESTS
If unsure, say N.
+config RUST_INLINE_HELPERS
+ bool "Inline C helpers into Rust crates (EXPERIMENTAL)"
+ depends on RUST && RUSTC_CLANG_LLVM_COMPATIBLE
+ depends on EXPERT
+ help
+ Links C helpers into Rust crates through LLVM IR.
+
+ If this option is enabled, instead of generating object files directly,
+ rustc is asked to produce LLVM IR, which is then linked together with
+ the LLVM IR of C helpers, before object file is generated.
+
+ This requires a matching LLVM version for Clang and rustc.
+
+ If unsure, say N.
+
endmenu # "Rust"
endmenu # Kernel hacking
diff --git a/rust/Makefile b/rust/Makefile
index
d7d19c21b671dea10242b1772a8bcf0bf5dcc1cd..2344e2662ce29280582215954132c09f63cd8c9d
100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -6,15 +6,19 @@ rustdoc_output := $(objtree)/Documentation/output/rust/rustdoc
obj-$(CONFIG_RUST) += core.o compiler_builtins.o ffi.o
always-$(CONFIG_RUST) += exports_core_generated.h
+ifdef CONFIG_RUST_INLINE_HELPERS
+always-$(CONFIG_RUST) += helpers/helpers.bc
+else
+obj-$(CONFIG_RUST) += helpers/helpers.o
+always-$(CONFIG_RUST) += exports_helpers_generated.h
+endif
# Missing prototypes are expected in the helpers since these are exported
# for Rust only, thus there is no header nor prototypes.
-obj-$(CONFIG_RUST) += helpers/helpers.o
CFLAGS_REMOVE_helpers/helpers.o = -Wmissing-prototypes -Wmissing-declarations
always-$(CONFIG_RUST) += bindings/bindings_generated.rs
bindings/bindings_helpers_generated.rs
obj-$(CONFIG_RUST) += bindings.o pin_init.o kernel.o
-always-$(CONFIG_RUST) += exports_helpers_generated.h \
- exports_bindings_generated.h exports_kernel_generated.h
+always-$(CONFIG_RUST) += exports_bindings_generated.h
exports_kernel_generated.h
always-$(CONFIG_RUST) += uapi/uapi_generated.rs
obj-$(CONFIG_RUST) += uapi.o
@@ -468,6 +472,13 @@ $(obj)/bindings/bindings_helpers_generated.rs: private
bindgen_target_extra = ;
$(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE
$(call if_changed_dep,bindgen)
+quiet_cmd_rust_helper = HELPER $@
+ cmd_rust_helper = \
+ $(CC) $(filter-out $(CFLAGS_REMOVE_helpers/helpers.o), $(c_flags)) -c -g0
$< -emit-llvm -o $@
+
+$(obj)/helpers/helpers.bc: $(obj)/helpers/helpers.c FORCE
+ +$(call if_changed_dep,rust_helper)
+
rust_exports = $(NM) -p --defined-only $(1) | awk '$$2~/(T|R|D|B)/
&& $$3!~/__(pfx|cfi|odr_asan)/ { printf $(2),$$3 }'
quiet_cmd_exports = EXPORTS $@
@@ -547,11 +558,13 @@ quiet_cmd_rustc_library = $(if
$(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L
OBJTREE=$(abspath $(objtree)) \
$(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \
$(filter-out $(skip_flags),$(rust_flags)) $(rustc_target_flags) \
- --emit=dep-info=$(depfile) --emit=obj=$@ \
+ --emit=dep-info=$(depfile) --emit=$(if $(link_helper),llvm-bc=$(patsubst
%.o,%.bc,$@),obj=$@) \
--emit=metadata=$(dir $@)$(patsubst %.o,lib%.rmeta,$(notdir $@)) \
--crate-type rlib -L$(objtree)/$(obj) \
--crate-name $(patsubst %.o,%,$(notdir $@)) $< \
--sysroot=/dev/null \
+ $(if $(link_helper),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst
%.o,%.bc,$@) $(obj)/helpers/helpers.bc -o $(patsubst %.o,%.m.bc,$@); \
+ $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst
%.o,%.m.bc,$@) -o $@) \
$(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@) \
$(cmd_objtool)
@@ -678,4 +691,9 @@ $(obj)/kernel.o: $(obj)/kernel/generated_arch_warn_asm.rs
$(obj)/kernel/generate
endif
endif
+ifdef CONFIG_RUST_INLINE_HELPERS
+$(obj)/kernel.o: private link_helper = 1
+$(obj)/kernel.o: $(obj)/helpers/helpers.bc
+endif
+
endif # CONFIG_RUST
diff --git a/rust/exports.c b/rust/exports.c
index
587f0e776aba52854080f15aa91094b55996c072..1b52460b0f4eeef6df9081abb9b7e054a28c3c21
100644
--- a/rust/exports.c
+++ b/rust/exports.c
@@ -16,10 +16,13 @@
#define EXPORT_SYMBOL_RUST_GPL(sym) extern int sym; EXPORT_SYMBOL_GPL(sym)
#include "exports_core_generated.h"
-#include "exports_helpers_generated.h"
#include "exports_bindings_generated.h"
#include "exports_kernel_generated.h"
+#ifndef CONFIG_RUST_INLINE_HELPERS
+#include "exports_helpers_generated.h"
+#endif
+
// For modules using `rust/build_error.rs`.
#ifdef CONFIG_RUST_BUILD_ASSERT_ALLOW
EXPORT_SYMBOL_RUST_GPL(rust_build_error);
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index
d0ee33a487be95f8ba9a5c964ebecfbebc6c4bf8..04eaf2b4fbca2245f904a6dc7875cb3275aa7df6
100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -343,7 +343,10 @@ rust_common_cmd = \
# would not match each other.
quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
- cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $< $(cmd_objtool)
+ cmd_rustc_o_rs = $(rust_common_cmd) --emit=$(if
$(CONFIG_RUST_INLINE_HELPERS),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) $< \
+ $(if $(CONFIG_RUST_INLINE_HELPERS),;$(LLVM_LINK) --internalize
--suppress-warnings $(patsubst %.o,%.bc,$@) $(objtree)/rust/helpers/helpers.bc
-o $(patsubst %.o,%.m.bc,$@); \
+ $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst
%.o,%.m.bc,$@) -o $@) \
+ $(cmd_objtool)
define rule_rustc_o_rs
$(call cmd_and_fixdep,rustc_o_rs)
--
2.52.0.158.g65b55ccf14-goog
Matthew Maurer
2025-Dec-03 00:40 UTC
[PATCH 4/4] build: rust: provide an option to inline C helpers into Rust
On Tue, Dec 2, 2025 at 12:28?PM Alice Ryhl <aliceryhl at google.com> wrote:> > From: Gary Guo <gary at garyguo.net> > > A new experimental Kconfig option, `RUST_INLINE_HELPERS` is added to > allow C helpers (which were created to allow Rust to call into > inline/macro C functions without having to re-implement the logic in > Rust) to be inlined into Rust crates without performing global LTO. > > If the option is enabled, the following is performed: > * For helpers, instead of compiling them to an object file to be linked > into vmlinux, they're compiled to LLVM IR. > * The LLVM IR is compiled to bitcode (This is step is not necessary, but > is a performance optimisation to prevent LLVM from always have to > reparse the same IR). > * When a Rust crate is compiled, instead of generating an object file, we > ask LLVM bitcode to be generated. > * llvm-link is invoked with --internalize to combine the helper bitcode > with the crate bitcode. This step is similar to LTO, but this is much > faster since it only needs to inline the helpers. > * clang is invoked to turn the combined bitcode into a final object file. > > The --internalize flag tells llvm-link to treat all symbols in > helpers.bc using `internal` linkage. This matches the behavior of > `clang` on `static inline` functions, and avoids exporting the symbol > from the object file.I've filed a PR with LLVM [1] to clarify that this is the intended operation of the tool. [1]: https://github.com/llvm/llvm-project/pull/170397> > To ensure that RUST_INLINE_HELPERS is not incompatible with BTF, we pass > the -g0 flag when building helpers. See commit 5daa0c35a1f0 ("rust: > Disallow BTF generation with Rust + LTO") for details. > > We have an intended triple mismatch of `aarch64-unknown-none` vs > `aarch64-unknown-linux-gnu`, so we suppress the warning. > > Co-developed-by: Boqun Feng <boqun.feng at gmail.com> > Signed-off-by: Boqun Feng <boqun.feng at gmail.com> > Co-developed-by: Matthew Maurer <mmaurer at google.com> > Signed-off-by: Matthew Maurer <mmaurer at google.com> > Signed-off-by: Gary Guo <gary at garyguo.net> > Co-developed-by: Alice Ryhl <aliceryhl at google.com> > Signed-off-by: Alice Ryhl <aliceryhl at google.com> > --- > Makefile | 4 +++- > lib/Kconfig.debug | 15 +++++++++++++++ > rust/Makefile | 26 ++++++++++++++++++++++---- > rust/exports.c | 5 ++++- > scripts/Makefile.build | 5 ++++- > 5 files changed, 48 insertions(+), 7 deletions(-) > > diff --git a/Makefile b/Makefile > index 96ddbaae7e12de71bcfabef4639de3a13a6e4815..5834bfd568548d1bee34b328dccce5d60f85526f 100644 > --- a/Makefile > +++ b/Makefile > @@ -517,6 +517,8 @@ OBJCOPY = $(LLVM_PREFIX)llvm-objcopy$(LLVM_SUFFIX) > OBJDUMP = $(LLVM_PREFIX)llvm-objdump$(LLVM_SUFFIX) > READELF = $(LLVM_PREFIX)llvm-readelf$(LLVM_SUFFIX) > STRIP = $(LLVM_PREFIX)llvm-strip$(LLVM_SUFFIX) > +LLVM_LINK = $(LLVM_PREFIX)llvm-link$(LLVM_SUFFIX) > +LLVM_AS = $(LLVM_PREFIX)llvm-as$(LLVM_SUFFIX) > else > CC = $(CROSS_COMPILE)gcc > LD = $(CROSS_COMPILE)ld > @@ -625,7 +627,7 @@ export RUSTC_BOOTSTRAP := 1 > export CLIPPY_CONF_DIR := $(srctree) > > export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG > -export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN > +export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN LLVM_LINK LLVM_AS > export HOSTRUSTC KBUILD_HOSTRUSTFLAGS > export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL > export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX > diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug > index 3034e294d50df55c4003c5fa5df442f59e711bd8..e63c5eb57b049aff988419ccd12dfd99d59f5080 100644 > --- a/lib/Kconfig.debug > +++ b/lib/Kconfig.debug > @@ -3427,6 +3427,21 @@ config RUST_KERNEL_DOCTESTS > > If unsure, say N. > > +config RUST_INLINE_HELPERS > + bool "Inline C helpers into Rust crates (EXPERIMENTAL)" > + depends on RUST && RUSTC_CLANG_LLVM_COMPATIBLE > + depends on EXPERT > + help > + Links C helpers into Rust crates through LLVM IR. > + > + If this option is enabled, instead of generating object files directly, > + rustc is asked to produce LLVM IR, which is then linked together with > + the LLVM IR of C helpers, before object file is generated. > + > + This requires a matching LLVM version for Clang and rustc. > + > + If unsure, say N. > + > endmenu # "Rust" > > endmenu # Kernel hacking > diff --git a/rust/Makefile b/rust/Makefile > index d7d19c21b671dea10242b1772a8bcf0bf5dcc1cd..2344e2662ce29280582215954132c09f63cd8c9d 100644 > --- a/rust/Makefile > +++ b/rust/Makefile > @@ -6,15 +6,19 @@ rustdoc_output := $(objtree)/Documentation/output/rust/rustdoc > obj-$(CONFIG_RUST) += core.o compiler_builtins.o ffi.o > always-$(CONFIG_RUST) += exports_core_generated.h > > +ifdef CONFIG_RUST_INLINE_HELPERS > +always-$(CONFIG_RUST) += helpers/helpers.bc > +else > +obj-$(CONFIG_RUST) += helpers/helpers.o > +always-$(CONFIG_RUST) += exports_helpers_generated.h > +endif > # Missing prototypes are expected in the helpers since these are exported > # for Rust only, thus there is no header nor prototypes. > -obj-$(CONFIG_RUST) += helpers/helpers.o > CFLAGS_REMOVE_helpers/helpers.o = -Wmissing-prototypes -Wmissing-declarations > > always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs > obj-$(CONFIG_RUST) += bindings.o pin_init.o kernel.o > -always-$(CONFIG_RUST) += exports_helpers_generated.h \ > - exports_bindings_generated.h exports_kernel_generated.h > +always-$(CONFIG_RUST) += exports_bindings_generated.h exports_kernel_generated.h > > always-$(CONFIG_RUST) += uapi/uapi_generated.rs > obj-$(CONFIG_RUST) += uapi.o > @@ -468,6 +472,13 @@ $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; > $(obj)/bindings/bindings_helpers_generated.rs: $(src)/helpers/helpers.c FORCE > $(call if_changed_dep,bindgen) > > +quiet_cmd_rust_helper = HELPER $@ > + cmd_rust_helper = \ > + $(CC) $(filter-out $(CFLAGS_REMOVE_helpers/helpers.o), $(c_flags)) -c -g0 $< -emit-llvm -o $@ > + > +$(obj)/helpers/helpers.bc: $(obj)/helpers/helpers.c FORCE > + +$(call if_changed_dep,rust_helper) > + > rust_exports = $(NM) -p --defined-only $(1) | awk '$$2~/(T|R|D|B)/ && $$3!~/__(pfx|cfi|odr_asan)/ { printf $(2),$$3 }' > > quiet_cmd_exports = EXPORTS $@ > @@ -547,11 +558,13 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L > OBJTREE=$(abspath $(objtree)) \ > $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \ > $(filter-out $(skip_flags),$(rust_flags)) $(rustc_target_flags) \ > - --emit=dep-info=$(depfile) --emit=obj=$@ \ > + --emit=dep-info=$(depfile) --emit=$(if $(link_helper),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) \ > --emit=metadata=$(dir $@)$(patsubst %.o,lib%.rmeta,$(notdir $@)) \ > --crate-type rlib -L$(objtree)/$(obj) \ > --crate-name $(patsubst %.o,%,$(notdir $@)) $< \ > --sysroot=/dev/null \ > + $(if $(link_helper),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) $(obj)/helpers/helpers.bc -o $(patsubst %.o,%.m.bc,$@); \ > + $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@) \ > $(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@) \ > $(cmd_objtool) > > @@ -678,4 +691,9 @@ $(obj)/kernel.o: $(obj)/kernel/generated_arch_warn_asm.rs $(obj)/kernel/generate > endif > endif > > +ifdef CONFIG_RUST_INLINE_HELPERS > +$(obj)/kernel.o: private link_helper = 1 > +$(obj)/kernel.o: $(obj)/helpers/helpers.bc > +endif > + > endif # CONFIG_RUST > diff --git a/rust/exports.c b/rust/exports.c > index 587f0e776aba52854080f15aa91094b55996c072..1b52460b0f4eeef6df9081abb9b7e054a28c3c21 100644 > --- a/rust/exports.c > +++ b/rust/exports.c > @@ -16,10 +16,13 @@ > #define EXPORT_SYMBOL_RUST_GPL(sym) extern int sym; EXPORT_SYMBOL_GPL(sym) > > #include "exports_core_generated.h" > -#include "exports_helpers_generated.h" > #include "exports_bindings_generated.h" > #include "exports_kernel_generated.h" > > +#ifndef CONFIG_RUST_INLINE_HELPERS > +#include "exports_helpers_generated.h" > +#endif > + > // For modules using `rust/build_error.rs`. > #ifdef CONFIG_RUST_BUILD_ASSERT_ALLOW > EXPORT_SYMBOL_RUST_GPL(rust_build_error); > diff --git a/scripts/Makefile.build b/scripts/Makefile.build > index d0ee33a487be95f8ba9a5c964ebecfbebc6c4bf8..04eaf2b4fbca2245f904a6dc7875cb3275aa7df6 100644 > --- a/scripts/Makefile.build > +++ b/scripts/Makefile.build > @@ -343,7 +343,10 @@ rust_common_cmd = \ > # would not match each other. > > quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ > - cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $< $(cmd_objtool) > + cmd_rustc_o_rs = $(rust_common_cmd) --emit=$(if $(CONFIG_RUST_INLINE_HELPERS),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) $< \ > + $(if $(CONFIG_RUST_INLINE_HELPERS),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) $(objtree)/rust/helpers/helpers.bc -o $(patsubst %.o,%.m.bc,$@); \ > + $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@) \ > + $(cmd_objtool) > > define rule_rustc_o_rs > $(call cmd_and_fixdep,rustc_o_rs) > > -- > 2.52.0.158.g65b55ccf14-goog >
Nathan Chancellor
2025-Dec-03 21:25 UTC
[PATCH 4/4] build: rust: provide an option to inline C helpers into Rust
Hi Alice, On Tue, Dec 02, 2025 at 08:27:59PM +0000, Alice Ryhl wrote: ...> diff --git a/Makefile b/Makefile > index 96ddbaae7e12de71bcfabef4639de3a13a6e4815..5834bfd568548d1bee34b328dccce5d60f85526f 100644 > --- a/Makefile > +++ b/Makefile > @@ -517,6 +517,8 @@ OBJCOPY = $(LLVM_PREFIX)llvm-objcopy$(LLVM_SUFFIX) > OBJDUMP = $(LLVM_PREFIX)llvm-objdump$(LLVM_SUFFIX) > READELF = $(LLVM_PREFIX)llvm-readelf$(LLVM_SUFFIX) > STRIP = $(LLVM_PREFIX)llvm-strip$(LLVM_SUFFIX) > +LLVM_LINK = $(LLVM_PREFIX)llvm-link$(LLVM_SUFFIX) > +LLVM_AS = $(LLVM_PREFIX)llvm-as$(LLVM_SUFFIX)Please keep the binutils alphabetized (i.e, put LLVM_LINK between AR and NM). Other than that, this seems fine from a Kbuild perspective (but I did not look too hard).> else > CC = $(CROSS_COMPILE)gcc > LD = $(CROSS_COMPILE)ld > @@ -625,7 +627,7 @@ export RUSTC_BOOTSTRAP := 1 > export CLIPPY_CONF_DIR := $(srctree) > > export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG > -export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN > +export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN LLVM_LINK LLVM_AS > export HOSTRUSTC KBUILD_HOSTRUSTFLAGS > export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL > export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX > diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug > index 3034e294d50df55c4003c5fa5df442f59e711bd8..e63c5eb57b049aff988419ccd12dfd99d59f5080 100644 > --- a/lib/Kconfig.debug > +++ b/lib/Kconfig.debug > @@ -3427,6 +3427,21 @@ config RUST_KERNEL_DOCTESTS > > If unsure, say N. > > +config RUST_INLINE_HELPERS > + bool "Inline C helpers into Rust crates (EXPERIMENTAL)" > + depends on RUST && RUSTC_CLANG_LLVM_COMPATIBLE > + depends on EXPERT > + help > + Links C helpers into Rust crates through LLVM IR. > + > + If this option is enabled, instead of generating object files directly, > + rustc is asked to produce LLVM IR, which is then linked together with > + the LLVM IR of C helpers, before object file is generated. > + > + This requires a matching LLVM version for Clang and rustc. > + > + If unsure, say N. > +I am just curious, why would someone want (or not) to do this? This help text does not really indicate the point of the option, just what it does. Is it just the standard tradeoffs with inlining (potential improvements in performance due to better optimization opportunities versus text size increase and icache pressure) or something else? Cheers, Nathan
Peter Zijlstra
2025-Dec-04 10:07 UTC
[PATCH 4/4] build: rust: provide an option to inline C helpers into Rust
On Tue, Dec 02, 2025 at 08:27:59PM +0000, Alice Ryhl wrote:> From: Gary Guo <gary at garyguo.net> > > A new experimental Kconfig option, `RUST_INLINE_HELPERS` is added to > allow C helpers (which were created to allow Rust to call into > inline/macro C functions without having to re-implement the logic in > Rust) to be inlined into Rust crates without performing global LTO. > > If the option is enabled, the following is performed: > * For helpers, instead of compiling them to an object file to be linked > into vmlinux, they're compiled to LLVM IR. > * The LLVM IR is compiled to bitcode (This is step is not necessary, but > is a performance optimisation to prevent LLVM from always have to > reparse the same IR). > * When a Rust crate is compiled, instead of generating an object file, we > ask LLVM bitcode to be generated. > * llvm-link is invoked with --internalize to combine the helper bitcode > with the crate bitcode. This step is similar to LTO, but this is much > faster since it only needs to inline the helpers. > * clang is invoked to turn the combined bitcode into a final object file. > > The --internalize flag tells llvm-link to treat all symbols in > helpers.bc using `internal` linkage. This matches the behavior of > `clang` on `static inline` functions, and avoids exporting the symbol > from the object file. > > To ensure that RUST_INLINE_HELPERS is not incompatible with BTF, we pass > the -g0 flag when building helpers. See commit 5daa0c35a1f0 ("rust: > Disallow BTF generation with Rust + LTO") for details. > > We have an intended triple mismatch of `aarch64-unknown-none` vs > `aarch64-unknown-linux-gnu`, so we suppress the warning.So if I understand this correctly, it will consume the helpers twice, once for bindgen to generate the rust ffi glue, and then a second time to 'compile' to IR. Then the IR is 'linked' into the rust translation units allowing the actual inlining to take place once 'LTO' runs. And while this works, this still has the downside of requiring those rust helper files and using bindgen. The other day [*] I proposed extending Rust such that it would be able to consume a clang precompiled header directly, this would allow doing away with most of all this. No more helpers and no more bindgen. Would that not be a much saner approach to all this? [*] https://lkml.kernel.org/r/20251124163315.GL4068168 at noisy.programming.kicks-ass.net