On Tue, Dec 2, 2025 at 12:28 PM Alice Ryhl <[email protected]> wrote: > > From: Gary Guo <[email protected]> > > 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 <[email protected]> > Signed-off-by: Boqun Feng <[email protected]> > Co-developed-by: Matthew Maurer <[email protected]> > Signed-off-by: Matthew Maurer <[email protected]> > Signed-off-by: Gary Guo <[email protected]> > Co-developed-by: Alice Ryhl <[email protected]> > Signed-off-by: Alice Ryhl <[email protected]> > --- > 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 >
