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

Same-day A/B on the same setup as #99 (instructions:u, jemalloc, 81 cells). Marginal effect of this commit against #99's head:

scenario marginal vs #99
full +0.02%
incr-full +0.00%
incr-unchanged +0.01%
incr-patched -0.34%
all cells -0.11%

No cell regresses by 0.25% or more. The win concentrates where the design predicts, in front-end-heavy rebuilds: serde incr-patched improves -1.61% to -1.98% across profiles, with hyper, regex-automata and cargo check incr-patched at -0.33% to -0.70%. 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.

Cumulative for the full reworked stack (#99 + #97 + #98) against the merge-base (fresh base run), 81 cells: full -0.15%, incr-full +0.07%, incr-unchanged -0.47%, incr-patched -4.03%, all cells -1.48%, with 47 cells improved by at least 0.25% and 8 regressed; every check-profile cell improves (serde incr-patched -19.0% to -20.7%). The largest residual is cargo debug incr-unchanged +9.4%, with the rest at or below +1%. See #99 for the trust-tiered anchor design and the staleness bugs the fail-red rework surfaced.

@xmakro
xmakro force-pushed the perf/anchor-respan branch from 4b14f71 to 4a62588 Compare August 8, 2026 00:43
@xmakro
xmakro force-pushed the perf/anchor-respan branch from 4a62588 to b59ed1e Compare August 8, 2026 10:59
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