1
Fork 0

Auto merge of #84762 - cjgillot:resolve-span-opt, r=petrochenkov

Encode spans relative to the enclosing item -- enable on nightly

Follow-up to #84373 with the flag `-Zincremental-relative-spans` set by default.

This PR seeks to remove one of the main shortcomings of incremental: the handling of spans.
Changing the contents of a function may require redoing part of the compilation process for another function in another file because of span information is changed.
Within one file: all the spans in HIR change, so typechecking had to be re-done.
Between files: spans of associated types/consts/functions change, so type-based resolution needs to be re-done (hygiene information is stored in the span).

The flag `-Zincremental-relative-spans` encodes local spans relative to the span of an item, stored inside the `source_span` query.

Trap: stashed diagnostics are referenced by the "raw" span, so stealing them requires to remove the span's parent.

In order to avoid too much traffic in the span interner, span encoding uses the `ctxt_or_tag` field to encode:
- the parent when the `SyntaxContext` is 0;
- the `SyntaxContext` when the parent is `None`.
Even with this, the PR creates a lot of traffic to the Span interner, when a Span has both a LocalDefId parent and a non-root SyntaxContext. They appear in lowering, when we add a parent to all spans, including those which come from macros, and during inlining when we mark inlined spans.

The last commit changes how queries of `LocalDefId` manage their cache. I can put this in a separate PR if required.

Possible future directions:
- validate that all spans are marked in HIR validation;
- mark macro-expanded spans relative to the def-site and not the use-site.
This commit is contained in:
bors 2023-01-02 13:10:16 +00:00
commit fb9dfa8cef
43 changed files with 169 additions and 204 deletions

View file

@ -104,6 +104,10 @@ fn assert_default_hashing_controls<CTX: HashStableContext>(ctx: &CTX, msg: &str)
// `-Z incremental-ignore-spans` option. Normally, this option is disabled,
// which will cause us to require that this method always be called with `Span` hashing
// enabled.
//
// Span hashing can also be disabled without `-Z incremental-ignore-spans`.
// This is the case for instance when building a hash for name mangling.
// Such configuration must not be used for metadata.
HashingControls { hash_spans }
if hash_spans == !ctx.unstable_opts_incremental_ignore_spans() => {}
other => panic!("Attempted hashing of {msg} with non-default HashingControls: {:?}", other),

View file

@ -4,7 +4,7 @@
// The encoding format for inline spans were obtained by optimizing over crates in rustc/libstd.
// See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28
use crate::def_id::LocalDefId;
use crate::def_id::{DefIndex, LocalDefId};
use crate::hygiene::SyntaxContext;
use crate::SPAN_TRACK;
use crate::{BytePos, SpanData};
@ -13,8 +13,8 @@ use rustc_data_structures::fx::FxIndexSet;
/// A compressed span.
///
/// Whereas [`SpanData`] is 12 bytes, which is a bit too big to stick everywhere, `Span`
/// is a form that only takes up 8 bytes, with less space for the length and
/// Whereas [`SpanData`] is 16 bytes, which is a bit too big to stick everywhere, `Span`
/// is a form that only takes up 8 bytes, with less space for the length, parent and
/// context. The vast majority (99.9%+) of `SpanData` instances will fit within
/// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are
/// stored in a separate interner table, and the `Span` will index into that
@ -25,7 +25,7 @@ use rustc_data_structures::fx::FxIndexSet;
/// slower because only 80--90% of spans could be stored inline (even less in
/// very large crates) and so the interner was used a lot more.
///
/// Inline (compressed) format:
/// Inline (compressed) format with no parent:
/// - `span.base_or_index == span_data.lo`
/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
/// - `span.ctxt_or_tag == span_data.ctxt` (must be `<= MAX_CTXT`)
@ -35,6 +35,12 @@ use rustc_data_structures::fx::FxIndexSet;
/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
/// - `span.ctxt_or_tag == span_data.ctxt` (must be `<= MAX_CTXT`)
///
/// Inline (compressed) format with root context:
/// - `span.base_or_index == span_data.lo`
/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
/// - `span.len_or_tag` has top bit (`PARENT_MASK`) set
/// - `span.ctxt == span_data.parent` (must be `<= MAX_CTXT`)
///
/// Interned format:
/// - `span.base_or_index == index` (indexes into the interner table)
/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
@ -73,7 +79,8 @@ pub struct Span {
ctxt_or_tag: u16,
}
const LEN_TAG: u16 = 0b1000_0000_0000_0000;
const LEN_TAG: u16 = 0b1111_1111_1111_1111;
const PARENT_MASK: u16 = 0b1000_0000_0000_0000;
const MAX_LEN: u32 = 0b0111_1111_1111_1111;
const CTXT_TAG: u32 = 0b1111_1111_1111_1111;
const MAX_CTXT: u32 = CTXT_TAG - 1;
@ -95,16 +102,32 @@ impl Span {
let (base, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32());
if len <= MAX_LEN && ctxt2 <= MAX_CTXT && parent.is_none() {
// Inline format.
Span { base_or_index: base, len_or_tag: len as u16, ctxt_or_tag: ctxt2 as u16 }
} else {
// Interned format.
let index =
with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }));
let ctxt_or_tag = if ctxt2 <= MAX_CTXT { ctxt2 } else { CTXT_TAG } as u16;
Span { base_or_index: index, len_or_tag: LEN_TAG, ctxt_or_tag }
if len <= MAX_LEN && ctxt2 <= MAX_CTXT {
let len_or_tag = len as u16;
debug_assert_eq!(len_or_tag & PARENT_MASK, 0);
if let Some(parent) = parent {
// Inline format with parent.
let len_or_tag = len_or_tag | PARENT_MASK;
let parent2 = parent.local_def_index.as_u32();
if ctxt2 == SyntaxContext::root().as_u32() && parent2 <= MAX_CTXT {
return Span { base_or_index: base, len_or_tag, ctxt_or_tag: parent2 as u16 };
}
} else {
// Inline format with ctxt.
return Span {
base_or_index: base,
len_or_tag: len as u16,
ctxt_or_tag: ctxt2 as u16,
};
}
}
// Interned format.
let index =
with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }));
let ctxt_or_tag = if ctxt2 <= MAX_CTXT { ctxt2 } else { CTXT_TAG } as u16;
Span { base_or_index: index, len_or_tag: LEN_TAG, ctxt_or_tag }
}
#[inline]
@ -122,12 +145,25 @@ impl Span {
pub fn data_untracked(self) -> SpanData {
if self.len_or_tag != LEN_TAG {
// Inline format.
debug_assert!(self.len_or_tag as u32 <= MAX_LEN);
SpanData {
lo: BytePos(self.base_or_index),
hi: BytePos(self.base_or_index + self.len_or_tag as u32),
ctxt: SyntaxContext::from_u32(self.ctxt_or_tag as u32),
parent: None,
if self.len_or_tag & PARENT_MASK == 0 {
debug_assert!(self.len_or_tag as u32 <= MAX_LEN);
SpanData {
lo: BytePos(self.base_or_index),
hi: BytePos(self.base_or_index + self.len_or_tag as u32),
ctxt: SyntaxContext::from_u32(self.ctxt_or_tag as u32),
parent: None,
}
} else {
let len = self.len_or_tag & !PARENT_MASK;
debug_assert!(len as u32 <= MAX_LEN);
let parent =
LocalDefId { local_def_index: DefIndex::from_u32(self.ctxt_or_tag as u32) };
SpanData {
lo: BytePos(self.base_or_index),
hi: BytePos(self.base_or_index + len as u32),
ctxt: SyntaxContext::root(),
parent: Some(parent),
}
}
} else {
// Interned format.
@ -141,8 +177,14 @@ impl Span {
pub fn ctxt(self) -> SyntaxContext {
let ctxt_or_tag = self.ctxt_or_tag as u32;
if ctxt_or_tag <= MAX_CTXT {
// Inline format or interned format with inline ctxt.
SyntaxContext::from_u32(ctxt_or_tag)
if self.len_or_tag == LEN_TAG || self.len_or_tag & PARENT_MASK == 0 {
// Inline format or interned format with inline ctxt.
SyntaxContext::from_u32(ctxt_or_tag)
} else {
// Inline format or interned format with inline parent.
// We know that the SyntaxContext is root.
SyntaxContext::root()
}
} else {
// Interned format.
let index = self.base_or_index;