feat: cross-crate dead-function elimination via binary-unit split (-Zdead-fn-elimination)#17239
feat: cross-crate dead-function elimination via binary-unit split (-Zdead-fn-elimination)#17239yijunyu wants to merge 1 commit into
Conversation
…dead-fn-elimination) Implements option 1 of the cross-crate DCE design (rust-lang/rust#158220 follow-up): one `cargo build -Zdead-fn-elimination` produces a pruned first build. For each binary a Check-mode probe sibling emits per-dependency used-sets (depends only on dep rmeta, so it runs early); each eliminable dependency Build unit depends on the probe and prunes against its used-set. Acyclic: dep(check) -> probe -> dep(build,pruned) -> bin(link). - features.rs: -Zdead-fn-elimination unstable flag. - unit_dependencies.rs: inject_dead_fn_probe_units (the binary-unit split, probe interning, cycle-safe wait-edges, build-time-dep exclusion). - mod.rs: flag wiring (emit-used-set on check units, prune flags + per-crate .done-marker wait on dep Build units), -Zalways-encode-mir on check units so the binary probe's monomorphization collector can read dependency MIR. Measured on zeroclaw (~330 units): rc=0, binary links+runs, 2900 fns excluded on the first build, 0 undefined symbols, 0 cycles, no job-slot deadlock.
|
☔ The latest upstream changes made this pull request unmergeable. Please resolve the merge conflicts. |
|
This gives some details of what is being done. Some parts feel a bit jargon-y which makes it a little less clear what is happening. A big question that I have that is unaddressed in the PR description (again, maybe I didn't quite understand it) is why this needs a change to Cargo. What role is Cargo filling that the compiler couldn't do? Also, the PR description has the feel of being written by an AI. I know we don't have an AI policy at this time and you might be working with people on an experimental basis, but a big problem with PR descriptions is AIs focus on all of the wrong details for a human reviewer. For example, the |
What this is
A follow-up to rust-lang/rust#158220 and the #t-compiler discussion on cross-crate dead-function
elimination. #158220's binary-only
-Zdead-fn-eliminationis a no-op — a binary'smonomorphization collector is already exact. The real slack is cross-crate: a library is
compiled before its callers, so on its own it must emit its whole
pubclosure, 55–85% of whicha given binary never calls (measured via
nmrlib-vs-binary on nix/socket2/rustix/gix acrossnushell/helix/zed/zeroclaw).
This PR implements the binary-unit split — the Cargo-side scheduling change that lets a
single
cargo build -Zdead-fn-eliminationproduce a pruned first build (no LTO, no secondpass).
Approach
For each binary
Buildunit, a probe is interned: aCheck-mode clone of the same binary.The probe runs the frontend + monomorphization collector and emits a per-dependency used-set
(which of the dependency's functions the build reaches). It depends only on dependency
rmeta,so it runs early. Each eliminable dependency
Buildunit then depends on the probe and prunesagainst its used-set. The graph stays acyclic:
Changes
core/features.rs—-Zdead-fn-eliminationunstable flag.core/compiler/unit_dependencies.rs—inject_dead_fn_probe_units: the binary-unit split,probe interning, cycle-safe ordering edges, and build-time-dependency exclusion (a lib reachable
from a proc-macro / build script is never pruned).
core/compiler/mod.rs— flag wiring:-Zdead-fn-emit-used-seton check units,-Zdead-fn-elimination+ used-set consumption on eliminable dependency Build units, and-Zalways-encode-miron check units so the binary probe's collector can read dependency MIR.+441 lines, 3 files.
Result
Measured on
zeroclaw(~330-unit workspace): onecargo build -Zdead-fn-eliminationlinks aworking binary that runs, with 2900 functions excluded on the first build — 0 undefined
symbols, 0 ICE, 0 graph cycles, no job-slot deadlock. Validated end-to-end (0 undefined symbols,
binary builds and runs correctly) on: a 2000-fn synthetic (Main→A→B), two
clapbinaries, an.or_else(fn)higher-order case, a closure-call case, and an IMAP parser (nom combinators,imap-proto0.10 and 0.16 — parses correctly).Wherever completeness can't be guaranteed (missing used-set or an absent per-crate completeness
marker) the pass keeps the full public closure, so the binary always links and runs; correctness
takes priority over maximum elimination.
Full design document and measurements are available on request. Feedback on the scheduling
approach is very welcome.