Skip to content

Content-address span anchors so definitions that merely move stay green - #97

Draft
xmakro wants to merge 4 commits into
perf/offset-span-hashingfrom
perf/anchor-respan
Draft

Content-address span anchors so definitions that merely move stay green#97
xmakro wants to merge 4 commits into
perf/offset-span-hashingfrom
perf/anchor-respan

Conversation

@xmakro

@xmakro xmakro commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Stacked on #99 (the fused def_anchor design), which this depends on. Reworked from the earlier revision of this PR that sat on #91's bucketed channel; the def_position query that revision added is gone, subsumed by def_anchor.

source_span(def) is the anchor every consumer of a definition's relative spans depends on: the SPAN_TRACK callback records a read of it whenever a parented span is resolved inside a tracked task. Its result fingerprint was the absolute span, so an edit that merely shifted a definition down its file re-fingerprinted the anchor and re-executed every span-carrying consumer below the edit. Early cutoff stopped the cascade before codegen (relative-span fingerprints are position-independent), but the whole front end below the edit still re-ran.

This PR content-addresses the anchor. source_span now returns AnchorSpan, a wrapper whose stable hash covers (file, length) and deliberately excludes the offset, so a definition that merely moves keeps a green anchor and its consumers are never re-executed at all. That is sound because every remaining observer of a definition's absolute position is tracked explicitly:

  • Relative spans in reloaded results already re-anchor on decode against the current session's source_span value, so decode fidelity never depended on the fingerprint.
  • Line renderings (#[track_caller], debuginfo line tables, coverage, pretty-printed paths in cached diagnostics) depend on def_anchor (Definition-anchored line-table dependencies for offset-based span hashing #99), which hashes the rendered line index and column of the definition's start along with its extent's line structure. Those are exactly the values a renderer derives from the definition's position, so a move that changes any rendered value goes red through the one anchor node, and a move that changes no rendered value (byte shifts within unchanged line structure) stays green end to end. This is where the fusion pays: the previous revision needed a second per-definition query (def_position) next to the line-table dependency, recorded at every rendering site; here the single def_anchor edge that Definition-anchored line-table dependencies for offset-based span hashing #99 already records is sufficient, and Definition-anchored line-table dependencies for offset-based span hashing #99's automatic line-observation tracking guarantees that a rendering which somehow escapes the anchor turns its task unconditionally red rather than stale.
  • The SVH keeps hashing the inner absolute owner spans, unchanged.

A load-bearing upstream invariant deserves stating explicitly, because the soundness argument leans on it: under incremental compilation, rustc_expand parents every macro invocation span to its enclosing definition before ExpnData captures it as call_site (collect_invocations in rustc_expand/src/expand.rs). Body-level macro call sites are therefore parented spans: they hash relative to their enclosing definition, and when a line-rendering consumer reaches one (for example span_as_caller_location walking to expansion_cause), the tracked lookup records def_anchor of that enclosing definition. This is what keeps #[track_caller] lines and collapsed debuginfo correct when a function containing a macro invocation moves. The remaining position-bearing residue is ExpnData's genuinely parentless spans: macro definition sites, item-level invocation call sites, and the call sites of compiler desugarings. Those keep offset-based fingerprints in this PR (a shifted macro definition still re-fingerprints its uses); content-addressing them is the follow-up in the next PR of the stack.

Coverage keeps its single instance anchor. The invariant that makes one anchor per function sufficient (every parented mapping span shares the instance's typeck root) is enforced at runtime by #99's fallback: a mapping span outside the anchored extent costs that codegen task's reuse instead of emitting stale coordinates.

tests/run-make/incr-anchor-move pins the correctness edge: it swaps two byte-identical-length functions, so the line table, both functions' contents, and their relative spans are all unchanged and only their positions swap. Nothing about them re-fingerprints; the def_anchor dependency, through the anchor line index it hashes, is the only thing that refreshes their #[track_caller] lines and DW_AT_decl_lines. The #99 tests and the full incremental suite stay green.

Performance

A/B on the same setup as #99 (instructions:u, jemalloc, same worktree and config, 81 cells). Marginal effect of this commit against #99's head, measured on the previous revision of the stack (this commit's payload is unchanged by the enforcement rework):

scenario marginal vs #99
full +0.06%
incr-full +0.04%
incr-unchanged -0.03%
incr-patched -0.35%
all cells -0.10%

9 cells improve by at least 0.25% and none regress. The win concentrates where the design predicts, in front-end-heavy rebuilds of large crates: serde incr-patched improves -1.97% (check), -1.64% (debug), -1.60% (opt); hyper check -0.73%, regex-automata check -0.66%. The suite's println patches understate the effect because early cutoff already protected codegen; the saving is the below-the-edit front-end re-execution, which grows with crate size and how high in the file the edit lands. The previous revision of this PR bought the same win shape on #91's channel at the cost of a second per-definition eval_always query; here it costs no new query at all.

Cumulative numbers for the reworked stack against the merge-base are being re-measured on a fresh same-day three-way run and will replace this line.

@xmakro
xmakro force-pushed the perf/anchor-respan branch from 118a0ee to 96ce91c Compare August 7, 2026 17:20
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