diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 558f13a832c43..5d4dd16389731 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -801,6 +801,7 @@ fn test_unstable_options_tracking_hash() { tracked!(force_unstable_if_unmarked, true); tracked!(function_return, FunctionReturn::ThunkExtern); tracked!(function_sections, Some(false)); + tracked!(hint_mostly_unused, true); tracked!(human_readable_cgu_names, true); tracked!(incremental_ignore_spans, true); tracked!(inline_mir, Some(true)); diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 727d4a126d21e..6d7b7e10ef697 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -50,6 +50,13 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { _ => {} } + // If the crate is likely to be mostly unused, use cross-crate inlining to defer codegen until + // the function is referenced, in order to skip codegen for unused functions. This is + // intentionally after the check for `inline(never)`, so that `inline(never)` wins. + if tcx.sess.opts.unstable_opts.hint_mostly_unused { + return true; + } + let sig = tcx.fn_sig(def_id).instantiate_identity(); for ty in sig.inputs().skip_binder().iter().chain(std::iter::once(&sig.output().skip_binder())) { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 12fa05118caf5..7ded572a60e30 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2233,6 +2233,8 @@ options! { environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), has_thread_local: Option = (None, parse_opt_bool, [TRACKED], "explicitly enable the `cfg(target_thread_local)` directive"), + hint_mostly_unused: bool = (false, parse_bool, [TRACKED], + "hint that most of this crate will go unused, to minimize work for uncalled functions"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], "generate human-readable, predictable names for codegen units (default: no)"), identify_regions: bool = (false, parse_bool, [UNTRACKED], diff --git a/src/doc/unstable-book/src/compiler-flags/hint-mostly-unused.md b/src/doc/unstable-book/src/compiler-flags/hint-mostly-unused.md new file mode 100644 index 0000000000000..80f5b1c44500d --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/hint-mostly-unused.md @@ -0,0 +1,33 @@ +# `hint-mostly-unused` + +This flag hints to the compiler that most of the crate will probably go unused. +The compiler can optimize its operation based on this assumption, in order to +compile faster. This is a hint, and does not guarantee any particular behavior. + +This option can substantially speed up compilation if applied to a large +dependency where the majority of the dependency does not get used. This flag +may slow down compilation in other cases. + +Currently, this option makes the compiler defer as much code generation as +possible from functions in the crate, until later crates invoke those +functions. Functions that never get invoked will never have code generated for +them. For instance, if a crate provides thousands of functions, but only a few +of them will get called, this flag will result in the compiler only doing code +generation for the called functions. (This uses the same mechanisms as +cross-crate inlining of functions.) This does not affect `extern` functions, or +functions marked as `#[inline(never)]`. + +To try applying this flag to one dependency out of a dependency tree, use the +[`profile-rustflags`](https://doc.rust-lang.org/cargo/reference/unstable.html#profile-rustflags-option) +feature of nightly cargo: + +```toml +cargo-features = ["profile-rustflags"] + +# ... +[dependencies] +mostly-unused-dependency = "1.2.3" + +[profile.release.package.mostly-unused-dependency] +rustflags = ["-Zhint-mostly-unused"] +```