Skip to content

Commit a316785

Browse files
committed
Auto merge of #125017 - petrochenkov:upctxt, r=cjgillot
rustc_span: Optimize syntax context updates in spans By updating inline contexts in place, without touching span interner, when possible.
2 parents 0de24a5 + d5dd2d8 commit a316785

File tree

3 files changed

+149
-73
lines changed

3 files changed

+149
-73
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> {
@@ -1058,9 +1059,9 @@ impl Span {
10581059
}
10591060

10601061
#[inline]
1061-
pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span {
1062-
let span = self.data();
1063-
span.with_ctxt(span.ctxt.apply_mark(expn_id, transparency))
1062+
pub fn apply_mark(mut self, expn_id: ExpnId, transparency: Transparency) -> Span {
1063+
self.update_ctxt(|ctxt| ctxt.apply_mark(expn_id, transparency));
1064+
self
10641065
}
10651066

10661067
#[inline]
@@ -1108,15 +1109,15 @@ impl Span {
11081109
}
11091110

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

11161117
#[inline]
1117-
pub fn normalize_to_macro_rules(self) -> Span {
1118-
let span = self.data();
1119-
span.with_ctxt(span.ctxt.normalize_to_macro_rules())
1118+
pub fn normalize_to_macro_rules(mut self) -> Span {
1119+
self.update_ctxt(|ctxt| ctxt.normalize_to_macro_rules());
1120+
self
11201121
}
11211122
}
11221123

compiler/rustc_span/src/span_encoding.rs

+131-56
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,45 @@ pub struct Span {
8787
ctxt_or_parent_or_marker: u16,
8888
}
8989

90+
impl Span {
91+
#[inline]
92+
fn data_inline_ctxt(self) -> SpanData {
93+
let len = self.len_with_tag_or_marker as u32;
94+
debug_assert!(len <= MAX_LEN);
95+
SpanData {
96+
lo: BytePos(self.lo_or_index),
97+
hi: BytePos(self.lo_or_index.debug_strict_add(len)),
98+
ctxt: SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32),
99+
parent: None,
100+
}
101+
}
102+
#[inline]
103+
fn data_inline_parent(self) -> SpanData {
104+
let len = (self.len_with_tag_or_marker & !PARENT_TAG) as u32;
105+
debug_assert!(len <= MAX_LEN);
106+
let parent = LocalDefId {
107+
local_def_index: DefIndex::from_u32(self.ctxt_or_parent_or_marker as u32),
108+
};
109+
SpanData {
110+
lo: BytePos(self.lo_or_index),
111+
hi: BytePos(self.lo_or_index.debug_strict_add(len)),
112+
ctxt: SyntaxContext::root(),
113+
parent: Some(parent),
114+
}
115+
}
116+
#[inline]
117+
fn data_partially_interned(self) -> SpanData {
118+
SpanData {
119+
ctxt: SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32),
120+
..with_span_interner(|interner| interner.spans[self.lo_or_index as usize])
121+
}
122+
}
123+
#[inline]
124+
fn data_interned(self) -> SpanData {
125+
with_span_interner(|interner| interner.spans[self.lo_or_index as usize])
126+
}
127+
}
128+
90129
// `MAX_LEN` is chosen so that `PARENT_TAG | MAX_LEN` is distinct from
91130
// `BASE_LEN_INTERNED_MARKER`. (If `MAX_LEN` was 1 higher, this wouldn't be true.)
92131
const MAX_LEN: u32 = 0b0111_1111_1111_1110;
@@ -111,42 +150,49 @@ impl Span {
111150
std::mem::swap(&mut lo, &mut hi);
112151
}
113152

114-
let (lo2, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32());
115-
153+
// Small len may enable one of fully inline formats (or may not).
154+
let (len, ctxt32) = (hi.0 - lo.0, ctxt.as_u32());
116155
if len <= MAX_LEN {
117-
if ctxt2 <= MAX_CTXT && parent.is_none() {
156+
if ctxt32 <= MAX_CTXT && parent.is_none() {
118157
// Inline-context format.
119158
return Span {
120-
lo_or_index: lo2,
159+
lo_or_index: lo.0,
121160
len_with_tag_or_marker: len as u16,
122-
ctxt_or_parent_or_marker: ctxt2 as u16,
161+
ctxt_or_parent_or_marker: ctxt32 as u16,
123162
};
124-
} else if ctxt2 == SyntaxContext::root().as_u32()
163+
} else if ctxt32 == 0
125164
&& let Some(parent) = parent
126-
&& let parent2 = parent.local_def_index.as_u32()
127-
&& parent2 <= MAX_CTXT
165+
&& let parent32 = parent.local_def_index.as_u32()
166+
&& parent32 <= MAX_CTXT
128167
{
129168
// Inline-parent format.
130169
return Span {
131-
lo_or_index: lo2,
170+
lo_or_index: lo.0,
132171
len_with_tag_or_marker: PARENT_TAG | len as u16,
133-
ctxt_or_parent_or_marker: parent2 as u16,
172+
ctxt_or_parent_or_marker: parent32 as u16,
134173
};
135174
}
136175
}
137176

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
177+
// Otherwise small ctxt may enable the partially inline format.
178+
let index = |ctxt| {
179+
with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }))
145180
};
146-
Span {
147-
lo_or_index: index,
148-
len_with_tag_or_marker: BASE_LEN_INTERNED_MARKER,
149-
ctxt_or_parent_or_marker,
181+
if ctxt32 <= MAX_CTXT {
182+
// Partially-interned format.
183+
Span {
184+
// Interned ctxt should never be read, so it can use any value.
185+
lo_or_index: index(SyntaxContext::from_u32(u32::MAX)),
186+
len_with_tag_or_marker: BASE_LEN_INTERNED_MARKER,
187+
ctxt_or_parent_or_marker: ctxt32 as u16,
188+
}
189+
} else {
190+
// Interned format.
191+
Span {
192+
lo_or_index: index(ctxt),
193+
len_with_tag_or_marker: BASE_LEN_INTERNED_MARKER,
194+
ctxt_or_parent_or_marker: CTXT_INTERNED_MARKER,
195+
}
150196
}
151197
}
152198

@@ -166,34 +212,17 @@ impl Span {
166212
if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
167213
if self.len_with_tag_or_marker & PARENT_TAG == 0 {
168214
// 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-
}
215+
self.data_inline_ctxt()
177216
} else {
178217
// 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-
}
218+
self.data_inline_parent()
190219
}
220+
} else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
221+
// Partially-interned format.
222+
self.data_partially_interned()
191223
} 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])
224+
// Interned format.
225+
self.data_interned()
197226
}
198227
}
199228

@@ -214,27 +243,73 @@ impl Span {
214243
}
215244
}
216245

246+
// For optimization we are interested in cases in which the context is inline and the context
247+
// update doesn't change format. All non-inline or format changing scenarios require accessing
248+
// interner and can fall back to `Span::new`.
249+
#[inline]
250+
pub fn update_ctxt(&mut self, update: impl FnOnce(SyntaxContext) -> SyntaxContext) {
251+
let (updated_ctxt32, data);
252+
if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
253+
if self.len_with_tag_or_marker & PARENT_TAG == 0 {
254+
// Inline-context format.
255+
updated_ctxt32 =
256+
update(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)).as_u32();
257+
// Any small new context including zero will preserve the format.
258+
if updated_ctxt32 <= MAX_CTXT {
259+
self.ctxt_or_parent_or_marker = updated_ctxt32 as u16;
260+
return;
261+
}
262+
data = self.data_inline_ctxt();
263+
} else {
264+
// Inline-parent format.
265+
updated_ctxt32 = update(SyntaxContext::root()).as_u32();
266+
// Only if the new context is zero the format will be preserved.
267+
if updated_ctxt32 == 0 {
268+
// Do nothing.
269+
return;
270+
}
271+
data = self.data_inline_parent();
272+
}
273+
} else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
274+
// Partially-interned format.
275+
updated_ctxt32 =
276+
update(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)).as_u32();
277+
// Any small new context excluding zero will preserve the format.
278+
// Zero may change the format to `InlineParent` if parent and len are small enough.
279+
if updated_ctxt32 <= MAX_CTXT && updated_ctxt32 != 0 {
280+
self.ctxt_or_parent_or_marker = updated_ctxt32 as u16;
281+
return;
282+
}
283+
data = self.data_partially_interned();
284+
} else {
285+
// Interned format.
286+
data = self.data_interned();
287+
updated_ctxt32 = update(data.ctxt).as_u32();
288+
}
289+
290+
// We could not keep the span in the same inline format, fall back to the complete logic.
291+
*self = data.with_ctxt(SyntaxContext::from_u32(updated_ctxt32));
292+
}
293+
217294
// Returns either syntactic context, if it can be retrieved without taking the interner lock,
218295
// or an index into the interner if it cannot.
219296
#[inline]
220297
fn inline_ctxt(self) -> Result<SyntaxContext, usize> {
221-
Ok(if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
298+
if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
222299
if self.len_with_tag_or_marker & PARENT_TAG == 0 {
223300
// Inline-context format.
224-
SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
301+
Ok(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32))
225302
} else {
226-
// Inline-parent format. We know that the SyntaxContext is root.
227-
SyntaxContext::root()
303+
// Inline-parent format.
304+
Ok(SyntaxContext::root())
228305
}
229306
} else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
230-
// Partially-interned format. This path avoids looking up the
231-
// interned value, and is the whole point of the
232-
// partially-interned format.
233-
SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
307+
// Partially-interned format.
308+
Ok(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32))
234309
} else {
235-
// Fully-interned format.
236-
return Err(self.lo_or_index as usize);
237-
})
310+
// Interned format.
311+
Err(self.lo_or_index as usize)
312+
}
238313
}
239314

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

0 commit comments

Comments
 (0)