Skip to content

Hash ExpnData without absolute positions [isolated: -3.8% incr-patched, serde -18%, 0 regressions] - #90

Closed
xmakro wants to merge 2 commits into
mainfrom
perf/expn-hash-position-free
Closed

Hash ExpnData without absolute positions [isolated: -3.8% incr-patched, serde -18%, 0 regressions]#90
xmakro wants to merge 2 commits into
mainfrom
perf/expn-hash-position-free

Conversation

@xmakro

@xmakro xmakro commented Aug 4, 2026

Copy link
Copy Markdown
Owner

ExpnData hashed the absolute positions of its call site and def site. ExpnHash feeds the SyntaxContext hash of every macro-expanded span, so one inserted line at the top of a file changed the hash of every expansion below it and reddened all macro-expanded HIR in the crate.

The manual StableHash impl now hashes the two sites' hygiene context and skips their positions. Uniqueness comes from update_disambiguator, which numbers colliding expansions in expansion order. An edit that reorders identical expansions is a conservative miss.

Unsound as written, do not merge

The call-site position is itself a codegen input: walk_chain_collapsed collapses debuginfo to ExpnData::call_site, and #[track_caller] materializes Location from source_callsite(). Both read hygiene data untracked, so their only dep-graph coverage was the call-site position inside ExpnHash. With that removed, shifting a macro invocation leaves optimized_mir and downstream CGUs green, and reused object code keeps the old line numbers.

Repro (stage2 of this branch vs its base, same config):

// m.rs, insert a blank line above the invocation between sessions
macro_rules! mk { () => { pub fn foo<T>() { panic!("boom") } }; }
mk!();

// main.rs, not edited
mod m;
fn main() { m::foo::<u8>(); }

foo::<u8> instantiates into main's CGU, whose remaining inputs are untouched by the edit. After the incremental rebuild this branch panics at the stale m.rs:8:1 while a fresh build of the same source reports m.rs:9:1. The base compiler reports m.rs:9:1 both ways. Collapsed DWARF line info goes stale the same way. #[rustc_clean] confirms the flip: on the base compiler the edit dirties hir_owner(foo) and optimized_mir(foo), on this branch both stay clean.

Single-file tests and the incremental suite miss this because any edit changes the enclosing module's span, which reddens that module's own CGU and re-runs codegen, hiding the stale values. The cross-module instantiation above is the shape that escapes.

Since panic locations and debug line tables are observable outputs derived from the call-site position, codegen reuse must be invalidated when that position changes. A position-free ExpnHash cannot provide that on its own; the analysis-level reuse this PR buys is real, but it needs a separate mechanism to cover the codegen reads before it can land.

@xmakro xmakro closed this Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant