about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJosh Triplett <josh@joshtriplett.org>2025-01-17 23:23:56 +0200
committerJosh Triplett <josh@joshtriplett.org>2025-06-06 19:12:00 -0700
commit1b23b64be4bf9e827cbc9d7c4012da77e8f05a85 (patch)
treecce3fb2305ccc406cc8aa828b69a428cded4a1de
parent44f415c1d617ebc7b931a243b7b321ef8a6ca47c (diff)
downloadrust-1b23b64be4bf9e827cbc9d7c4012da77e8f05a85.tar.gz
rust-1b23b64be4bf9e827cbc9d7c4012da77e8f05a85.zip
Add `-Z hint-mostly-unused` to tell rustc that most of a crate will go unused
This hint allows the compiler to 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)]`.

Some performance numbers, based on a crate with many dependencies having
just *one* large dependency set to `-Z hint-mostly-unused` (using
Cargo's `profile-rustflags` option):

A release build went from 4m07s to 2m04s.

A non-release build went from 2m26s to 1m28s.
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_mir_transform/src/cross_crate_inline.rs7
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--src/doc/unstable-book/src/compiler-flags/hint-mostly-unused.md33
4 files changed, 43 insertions, 0 deletions
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 558f13a832c..5d4dd163897 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 727d4a126d2..6d7b7e10ef6 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 12fa05118ca..7ded572a60e 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<bool> = (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 00000000000..80f5b1c4450
--- /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"]
+```