Skip to content

Commit 1c18cfc

Browse files
committed
WIP
1 parent 2a2c29a commit 1c18cfc

File tree

3 files changed

+200
-97
lines changed

3 files changed

+200
-97
lines changed

compiler/rustc_expand/src/mbe/transcribe.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ impl MutVisitor for Marker {
3030
// it's some advanced case with macro-generated macros. So if we cache the marked version
3131
// of that context once, we'll typically have a 100% cache hit rate after that.
3232
let Marker(expn_id, transparency, ref mut cache) = *self;
33-
let data = span.data();
34-
let marked_ctxt = *cache
35-
.entry(data.ctxt)
36-
.or_insert_with(|| data.ctxt.apply_mark(expn_id.to_expn_id(), transparency));
37-
*span = data.with_ctxt(marked_ctxt);
33+
span.update_ctxt(|ctxt| {
34+
*cache
35+
.entry(ctxt)
36+
.or_insert_with(|| ctxt.apply_mark(expn_id.to_expn_id(), transparency))
37+
});
3838
}
3939
}
4040

compiler/rustc_span/src/lib.rs

+13-12
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ impl SpanData {
521521
Span::new(self.lo, hi, self.ctxt, self.parent)
522522
}
523523
#[inline]
524-
pub fn with_ctxt(&self, ctxt: SyntaxContext) -> Span {
524+
fn with_ctxt(&self, ctxt: SyntaxContext) -> Span {
525525
Span::new(self.lo, self.hi, ctxt, self.parent)
526526
}
527527
#[inline]
@@ -576,8 +576,9 @@ impl Span {
576576
self.data().with_hi(hi)
577577
}
578578
#[inline]
579-
pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
580-
self.data_untracked().with_ctxt(ctxt)
579+
pub fn with_ctxt(mut self, ctxt: SyntaxContext) -> Span {
580+
self.update_ctxt(|_| ctxt);
581+
self
581582
}
582583
#[inline]
583584
pub fn parent(self) -> Option<LocalDefId> {
@@ -1051,9 +1052,9 @@ impl Span {
10511052
}
10521053

10531054
#[inline]
1054-
pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span {
1055-
let span = self.data();
1056-
span.with_ctxt(span.ctxt.apply_mark(expn_id, transparency))
1055+
pub fn apply_mark(mut self, expn_id: ExpnId, transparency: Transparency) -> Span {
1056+
self.update_ctxt(|ctxt| ctxt.apply_mark(expn_id, transparency));
1057+
self
10571058
}
10581059

10591060
#[inline]
@@ -1101,15 +1102,15 @@ impl Span {
11011102
}
11021103

11031104
#[inline]
1104-
pub fn normalize_to_macros_2_0(self) -> Span {
1105-
let span = self.data();
1106-
span.with_ctxt(span.ctxt.normalize_to_macros_2_0())
1105+
pub fn normalize_to_macros_2_0(mut self) -> Span {
1106+
self.update_ctxt(|ctxt| ctxt.normalize_to_macros_2_0());
1107+
self
11071108
}
11081109

11091110
#[inline]
1110-
pub fn normalize_to_macro_rules(self) -> Span {
1111-
let span = self.data();
1112-
span.with_ctxt(span.ctxt.normalize_to_macro_rules())
1111+
pub fn normalize_to_macro_rules(mut self) -> Span {
1112+
self.update_ctxt(|ctxt| ctxt.normalize_to_macro_rules());
1113+
self
11131114
}
11141115
}
11151116

compiler/rustc_span/src/span_encoding.rs

+182-80
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use crate::SPAN_TRACK;
44
use crate::{BytePos, SpanData};
55

66
use rustc_data_structures::fx::FxIndexSet;
7-
87
// This code is very hot and uses lots of arithmetic, avoid overflow checks for performance.
98
// See https://github.com/rust-lang/rust/pull/119440#issuecomment-1874255727
109
use rustc_serialize::int_overflow::DebugStrictAdd;
10+
use std::mem::transmute;
1111

1212
/// A compressed span.
1313
///
@@ -87,6 +87,99 @@ pub struct Span {
8787
ctxt_or_parent_or_marker: u16,
8888
}
8989

90+
// Convenience structures for all span formats.
91+
#[derive(Clone, Copy)]
92+
struct InlineCtxt {
93+
lo: u32,
94+
len: u16,
95+
ctxt: u16,
96+
}
97+
#[derive(Clone, Copy)]
98+
struct InlineParent {
99+
lo: u32,
100+
len_with_tag: u16,
101+
parent: u16,
102+
}
103+
#[derive(Clone, Copy)]
104+
struct PartiallyInterned {
105+
index: u32,
106+
_marker1: u16,
107+
ctxt: u16,
108+
}
109+
#[derive(Clone, Copy)]
110+
struct Interned {
111+
index: u32,
112+
_marker1: u16,
113+
_marker2: u16,
114+
}
115+
enum Fmt<'a> {
116+
InlineCtxt(&'a mut InlineCtxt),
117+
InlineParent(&'a mut InlineParent),
118+
PartiallyInterned(&'a mut PartiallyInterned),
119+
Interned(&'a mut Interned),
120+
}
121+
122+
impl InlineCtxt {
123+
fn data(self) -> SpanData {
124+
let len = self.len as u32;
125+
debug_assert!(len <= MAX_LEN);
126+
SpanData {
127+
lo: BytePos(self.lo),
128+
hi: BytePos(self.lo.debug_strict_add(len)),
129+
ctxt: SyntaxContext::from_u32(self.ctxt as u32),
130+
parent: None,
131+
}
132+
}
133+
fn to_span(self) -> Span {
134+
unsafe { transmute(self) }
135+
}
136+
}
137+
impl InlineParent {
138+
fn data(self) -> SpanData {
139+
let len = (self.len_with_tag & !PARENT_TAG) as u32;
140+
debug_assert!(len <= MAX_LEN);
141+
let parent = LocalDefId { local_def_index: DefIndex::from_u32(self.parent as u32) };
142+
SpanData {
143+
lo: BytePos(self.lo),
144+
hi: BytePos(self.lo.debug_strict_add(len)),
145+
ctxt: SyntaxContext::root(),
146+
parent: Some(parent),
147+
}
148+
}
149+
fn to_span(self) -> Span {
150+
unsafe { transmute(self) }
151+
}
152+
}
153+
impl PartiallyInterned {
154+
fn data(self) -> SpanData {
155+
SpanData {
156+
ctxt: SyntaxContext::from_u32(self.ctxt as u32),
157+
..with_span_interner(|interner| interner.spans[self.index as usize])
158+
}
159+
}
160+
fn to_span(self) -> Span {
161+
unsafe { transmute(self) }
162+
}
163+
}
164+
impl Interned {
165+
fn data(self) -> SpanData {
166+
with_span_interner(|interner| interner.spans[self.index as usize])
167+
}
168+
fn to_span(self) -> Span {
169+
unsafe { transmute(self) }
170+
}
171+
}
172+
impl Fmt<'_> {
173+
fn data(self) -> SpanData {
174+
match self {
175+
Fmt::InlineCtxt(span) => span.data(),
176+
Fmt::InlineParent(span) => span.data(),
177+
Fmt::PartiallyInterned(span) => span.data(),
178+
Fmt::Interned(span) => span.data(),
179+
}
180+
}
181+
}
182+
90183
// `MAX_LEN` is chosen so that `PARENT_TAG | MAX_LEN` is distinct from
91184
// `BASE_LEN_INTERNED_MARKER`. (If `MAX_LEN` was 1 higher, this wouldn't be true.)
92185
const MAX_LEN: u32 = 0b0111_1111_1111_1110;
@@ -111,42 +204,48 @@ impl Span {
111204
std::mem::swap(&mut lo, &mut hi);
112205
}
113206

114-
let (lo2, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32());
115-
207+
// Small len may enable one of fully inline formats (or may not).
208+
let (len, ctxt32) = (hi.0 - lo.0, ctxt.as_u32());
116209
if len <= MAX_LEN {
117-
if ctxt2 <= MAX_CTXT && parent.is_none() {
118-
// Inline-context format.
119-
return Span {
120-
lo_or_index: lo2,
121-
len_with_tag_or_marker: len as u16,
122-
ctxt_or_parent_or_marker: ctxt2 as u16,
123-
};
124-
} else if ctxt2 == SyntaxContext::root().as_u32()
210+
if ctxt32 <= MAX_CTXT && parent.is_none() {
211+
return InlineCtxt { lo: lo.0, len: len as u16, ctxt: ctxt32 as u16 }.to_span();
212+
} else if ctxt32 == 0
125213
&& let Some(parent) = parent
126-
&& let parent2 = parent.local_def_index.as_u32()
127-
&& parent2 <= MAX_CTXT
214+
&& let parent32 = parent.local_def_index.as_u32()
215+
&& parent32 <= MAX_CTXT
128216
{
129-
// Inline-parent format.
130-
return Span {
131-
lo_or_index: lo2,
132-
len_with_tag_or_marker: PARENT_TAG | len as u16,
133-
ctxt_or_parent_or_marker: parent2 as u16,
134-
};
217+
let len_with_tag = PARENT_TAG | len as u16;
218+
return InlineParent { lo: lo.0, len_with_tag, parent: parent32 as u16 }.to_span();
135219
}
136220
}
137221

138-
// Partially-interned or fully-interned format.
139-
let index =
140-
with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }));
141-
let ctxt_or_parent_or_marker = if ctxt2 <= MAX_CTXT {
142-
ctxt2 as u16 // partially-interned
143-
} else {
144-
CTXT_INTERNED_MARKER // fully-interned
222+
// Otherwise small ctxt may enable the partially inline format.
223+
let index = |ctxt| {
224+
with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }))
145225
};
146-
Span {
147-
lo_or_index: index,
148-
len_with_tag_or_marker: BASE_LEN_INTERNED_MARKER,
149-
ctxt_or_parent_or_marker,
226+
if ctxt32 <= MAX_CTXT {
227+
let index = index(SyntaxContext::from_u32(u32::MAX)); // any value, should never be read
228+
PartiallyInterned { index, _marker1: BASE_LEN_INTERNED_MARKER, ctxt: ctxt32 as u16 }
229+
.to_span()
230+
} else {
231+
let index = index(ctxt);
232+
Interned { index, _marker1: BASE_LEN_INTERNED_MARKER, _marker2: CTXT_INTERNED_MARKER }
233+
.to_span()
234+
}
235+
}
236+
237+
#[inline]
238+
fn fmt(&mut self) -> Fmt<'_> {
239+
if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
240+
if self.len_with_tag_or_marker & PARENT_TAG == 0 {
241+
Fmt::InlineCtxt(unsafe { transmute(self) })
242+
} else {
243+
Fmt::InlineParent(unsafe { transmute(self) })
244+
}
245+
} else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
246+
Fmt::PartiallyInterned(unsafe { transmute(self) })
247+
} else {
248+
Fmt::Interned(unsafe { transmute(self) })
150249
}
151250
}
152251

@@ -162,39 +261,8 @@ impl Span {
162261
/// Internal function to translate between an encoded span and the expanded representation.
163262
/// This function must not be used outside the incremental engine.
164263
#[inline]
165-
pub fn data_untracked(self) -> SpanData {
166-
if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
167-
if self.len_with_tag_or_marker & PARENT_TAG == 0 {
168-
// Inline-context format.
169-
let len = self.len_with_tag_or_marker as u32;
170-
debug_assert!(len <= MAX_LEN);
171-
SpanData {
172-
lo: BytePos(self.lo_or_index),
173-
hi: BytePos(self.lo_or_index.debug_strict_add(len)),
174-
ctxt: SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32),
175-
parent: None,
176-
}
177-
} else {
178-
// Inline-parent format.
179-
let len = (self.len_with_tag_or_marker & !PARENT_TAG) as u32;
180-
debug_assert!(len <= MAX_LEN);
181-
let parent = LocalDefId {
182-
local_def_index: DefIndex::from_u32(self.ctxt_or_parent_or_marker as u32),
183-
};
184-
SpanData {
185-
lo: BytePos(self.lo_or_index),
186-
hi: BytePos(self.lo_or_index.debug_strict_add(len)),
187-
ctxt: SyntaxContext::root(),
188-
parent: Some(parent),
189-
}
190-
}
191-
} else {
192-
// Fully-interned or partially-interned format. In either case,
193-
// the interned value contains all the data, so we don't need to
194-
// distinguish them.
195-
let index = self.lo_or_index;
196-
with_span_interner(|interner| interner.spans[index as usize])
197-
}
264+
pub fn data_untracked(mut self) -> SpanData {
265+
self.fmt().data()
198266
}
199267

200268
/// Returns `true` if this is a dummy span with any hygienic context.
@@ -214,26 +282,60 @@ impl Span {
214282
}
215283
}
216284

285+
// For optimization we are interested in cases in which the context is inline and the context
286+
// update doesn't change format. All non-inline or format changing scenarios require accessing
287+
// interner and can fall back to `Span::new`.
288+
pub fn update_ctxt(&mut self, update: impl FnOnce(SyntaxContext) -> SyntaxContext) {
289+
let (updated_ctxt32, data);
290+
match self.fmt() {
291+
Fmt::InlineCtxt(span) => {
292+
updated_ctxt32 = update(SyntaxContext::from_u32(span.ctxt as u32)).as_u32();
293+
// Any small new context including zero will preserve the format.
294+
if updated_ctxt32 <= MAX_CTXT {
295+
span.ctxt = updated_ctxt32 as u16;
296+
return;
297+
}
298+
data = span.data();
299+
}
300+
Fmt::PartiallyInterned(span) => {
301+
updated_ctxt32 = update(SyntaxContext::from_u32(span.ctxt as u32)).as_u32();
302+
// Any small new context excluding zero will preserve the format.
303+
// Zero may change the format to `InlineParent` if parent and len are small enough.
304+
if updated_ctxt32 <= MAX_CTXT && updated_ctxt32 != 0 {
305+
span.ctxt = updated_ctxt32 as u16;
306+
return;
307+
}
308+
data = span.data();
309+
}
310+
Fmt::InlineParent(span) => {
311+
updated_ctxt32 = update(SyntaxContext::root()).as_u32();
312+
// Only if the new context is zero the format will be preserved.
313+
if updated_ctxt32 == 0 {
314+
// Do nothing.
315+
return;
316+
}
317+
data = span.data();
318+
}
319+
Fmt::Interned(span) => {
320+
data = span.data();
321+
updated_ctxt32 = update(data.ctxt).as_u32();
322+
}
323+
}
324+
325+
*self = data.with_ctxt(SyntaxContext::from_u32(updated_ctxt32));
326+
}
327+
217328
// Returns either syntactic context, if it can be retrieved without taking the interner lock,
218329
// or an index into the interner if it cannot.
219-
fn inline_ctxt(self) -> Result<SyntaxContext, usize> {
220-
Ok(if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
221-
if self.len_with_tag_or_marker & PARENT_TAG == 0 {
222-
// Inline-context format.
223-
SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
224-
} else {
225-
// Inline-parent format. We know that the SyntaxContext is root.
226-
SyntaxContext::root()
330+
fn inline_ctxt(mut self) -> Result<SyntaxContext, usize> {
331+
match self.fmt() {
332+
Fmt::InlineCtxt(InlineCtxt { ctxt, .. })
333+
| Fmt::PartiallyInterned(PartiallyInterned { ctxt, .. }) => {
334+
Ok(SyntaxContext::from_u32(*ctxt as u32))
227335
}
228-
} else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
229-
// Partially-interned format. This path avoids looking up the
230-
// interned value, and is the whole point of the
231-
// partially-interned format.
232-
SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
233-
} else {
234-
// Fully-interned format.
235-
return Err(self.lo_or_index as usize);
236-
})
336+
Fmt::InlineParent(_) => Ok(SyntaxContext::root()),
337+
Fmt::Interned(span) => Err(span.index as usize),
338+
}
237339
}
238340

239341
/// This function is used as a fast path when decoding the full `SpanData` is not necessary.

0 commit comments

Comments
 (0)