Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 8545b60

Browse files
committed
rustc_span: Add conveniences for working with span formats
1 parent d0227c6 commit 8545b60

File tree

2 files changed

+149
-100
lines changed

2 files changed

+149
-100
lines changed

compiler/rustc_span/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ impl SpanData {
520520
pub fn with_hi(&self, hi: BytePos) -> Span {
521521
Span::new(self.lo, hi, self.ctxt, self.parent)
522522
}
523+
/// Avoid if possible, `Span::update_ctxt` should be preferred.
523524
#[inline]
524525
fn with_ctxt(&self, ctxt: SyntaxContext) -> Span {
525526
Span::new(self.lo, self.hi, ctxt, self.parent)

compiler/rustc_span/src/span_encoding.rs

Lines changed: 148 additions & 100 deletions
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,45 +87,130 @@ pub struct Span {
8787
ctxt_or_parent_or_marker: u16,
8888
}
8989

90-
impl Span {
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+
98+
#[derive(Clone, Copy)]
99+
struct InlineParent {
100+
lo: u32,
101+
len_with_tag: u16,
102+
parent: u16,
103+
}
104+
105+
#[derive(Clone, Copy)]
106+
struct PartiallyInterned {
107+
index: u32,
108+
_marker1: u16,
109+
ctxt: u16,
110+
}
111+
112+
#[derive(Clone, Copy)]
113+
struct Interned {
114+
index: u32,
115+
_marker1: u16,
116+
_marker2: u16,
117+
}
118+
119+
impl InlineCtxt {
91120
#[inline]
92-
fn data_inline_ctxt(self) -> SpanData {
93-
let len = self.len_with_tag_or_marker as u32;
121+
fn data(self) -> SpanData {
122+
let len = self.len as u32;
94123
debug_assert!(len <= MAX_LEN);
95124
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),
125+
lo: BytePos(self.lo),
126+
hi: BytePos(self.lo.debug_strict_add(len)),
127+
ctxt: SyntaxContext::from_u32(self.ctxt as u32),
99128
parent: None,
100129
}
101130
}
102131
#[inline]
103-
fn data_inline_parent(self) -> SpanData {
104-
let len = (self.len_with_tag_or_marker & !PARENT_TAG) as u32;
132+
fn span(lo: u32, len: u16, ctxt: u16) -> Span {
133+
unsafe { transmute(InlineCtxt { lo, len, ctxt }) }
134+
}
135+
}
136+
137+
impl InlineParent {
138+
#[inline]
139+
fn data(self) -> SpanData {
140+
let len = (self.len_with_tag & !PARENT_TAG) as u32;
105141
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-
};
109142
SpanData {
110-
lo: BytePos(self.lo_or_index),
111-
hi: BytePos(self.lo_or_index.debug_strict_add(len)),
143+
lo: BytePos(self.lo),
144+
hi: BytePos(self.lo.debug_strict_add(len)),
112145
ctxt: SyntaxContext::root(),
113-
parent: Some(parent),
146+
parent: Some(LocalDefId { local_def_index: DefIndex::from_u32(self.parent as u32) }),
114147
}
115148
}
116149
#[inline]
117-
fn data_partially_interned(self) -> SpanData {
150+
fn span(lo: u32, len_with_tag: u16, parent: u16) -> Span {
151+
unsafe { transmute(InlineParent { lo, len_with_tag, parent }) }
152+
}
153+
}
154+
155+
impl PartiallyInterned {
156+
#[inline]
157+
fn data(self) -> SpanData {
118158
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])
159+
ctxt: SyntaxContext::from_u32(self.ctxt as u32),
160+
..with_span_interner(|interner| interner.spans[self.index as usize])
121161
}
122162
}
123163
#[inline]
124-
fn data_interned(self) -> SpanData {
125-
with_span_interner(|interner| interner.spans[self.lo_or_index as usize])
164+
fn span(index: u32, ctxt: u16) -> Span {
165+
unsafe { transmute(PartiallyInterned { index, _marker1: BASE_LEN_INTERNED_MARKER, ctxt }) }
126166
}
127167
}
128168

169+
impl Interned {
170+
#[inline]
171+
fn data(self) -> SpanData {
172+
with_span_interner(|interner| interner.spans[self.index as usize])
173+
}
174+
#[inline]
175+
fn span(index: u32) -> Span {
176+
let _marker1 = BASE_LEN_INTERNED_MARKER;
177+
unsafe { transmute(Interned { index, _marker1, _marker2: CTXT_INTERNED_MARKER }) }
178+
}
179+
}
180+
181+
// This code is very hot, and converting span to an enum and matching on it doesn't optimize away
182+
// properly. So we are using a macro emulating such a match, but expand it directly to an if-else
183+
// chain.
184+
macro_rules! match_span_kind {
185+
(
186+
$span:expr,
187+
InlineCtxt($span1:ident) => $arm1:expr,
188+
InlineParent($span2:ident) => $arm2:expr,
189+
PartiallyInterned($span3:ident) => $arm3:expr,
190+
Interned($span4:ident) => $arm4:expr,
191+
) => {
192+
if $span.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
193+
if $span.len_with_tag_or_marker & PARENT_TAG == 0 {
194+
// Inline-context format.
195+
let $span1: &mut InlineCtxt = unsafe { transmute(&mut *$span) };
196+
$arm1
197+
} else {
198+
// Inline-parent format.
199+
let $span2: &mut InlineParent = unsafe { transmute(&mut *$span) };
200+
$arm2
201+
}
202+
} else if $span.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
203+
// Partially-interned format.
204+
let $span3: &mut PartiallyInterned = unsafe { transmute(&mut *$span) };
205+
$arm3
206+
} else {
207+
// Interned format.
208+
let $span4: &mut Interned = unsafe { transmute(&mut *$span) };
209+
$arm4
210+
}
211+
};
212+
}
213+
129214
// `MAX_LEN` is chosen so that `PARENT_TAG | MAX_LEN` is distinct from
130215
// `BASE_LEN_INTERNED_MARKER`. (If `MAX_LEN` was 1 higher, this wouldn't be true.)
131216
const MAX_LEN: u32 = 0b0111_1111_1111_1110;
@@ -154,23 +239,13 @@ impl Span {
154239
let (len, ctxt32) = (hi.0 - lo.0, ctxt.as_u32());
155240
if len <= MAX_LEN {
156241
if ctxt32 <= MAX_CTXT && parent.is_none() {
157-
// Inline-context format.
158-
return Span {
159-
lo_or_index: lo.0,
160-
len_with_tag_or_marker: len as u16,
161-
ctxt_or_parent_or_marker: ctxt32 as u16,
162-
};
242+
return InlineCtxt::span(lo.0, len as u16, ctxt32 as u16);
163243
} else if ctxt32 == 0
164244
&& let Some(parent) = parent
165245
&& let parent32 = parent.local_def_index.as_u32()
166246
&& parent32 <= MAX_CTXT
167247
{
168-
// Inline-parent format.
169-
return Span {
170-
lo_or_index: lo.0,
171-
len_with_tag_or_marker: PARENT_TAG | len as u16,
172-
ctxt_or_parent_or_marker: parent32 as u16,
173-
};
248+
return InlineParent::span(lo.0, PARENT_TAG | len as u16, parent32 as u16);
174249
}
175250
}
176251

@@ -179,20 +254,10 @@ impl Span {
179254
with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }))
180255
};
181256
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-
}
257+
// Interned ctxt should never be read, so it can use any value.
258+
PartiallyInterned::span(index(SyntaxContext::from_u32(u32::MAX)), ctxt32 as u16)
189259
} 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-
}
260+
Interned::span(index(ctxt))
196261
}
197262
}
198263

@@ -208,21 +273,13 @@ impl Span {
208273
/// Internal function to translate between an encoded span and the expanded representation.
209274
/// This function must not be used outside the incremental engine.
210275
#[inline]
211-
pub fn data_untracked(self) -> SpanData {
212-
if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
213-
if self.len_with_tag_or_marker & PARENT_TAG == 0 {
214-
// Inline-context format.
215-
self.data_inline_ctxt()
216-
} else {
217-
// Inline-parent format.
218-
self.data_inline_parent()
219-
}
220-
} else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
221-
// Partially-interned format.
222-
self.data_partially_interned()
223-
} else {
224-
// Interned format.
225-
self.data_interned()
276+
pub fn data_untracked(mut self) -> SpanData {
277+
match_span_kind! {
278+
&mut self,
279+
InlineCtxt(span) => span.data(),
280+
InlineParent(span) => span.data(),
281+
PartiallyInterned(span) => span.data(),
282+
Interned(span) => span.data(),
226283
}
227284
}
228285

@@ -249,42 +306,41 @@ impl Span {
249306
#[inline]
250307
pub fn update_ctxt(&mut self, update: impl FnOnce(SyntaxContext) -> SyntaxContext) {
251308
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.
309+
match_span_kind! {
310+
self,
311+
InlineCtxt(span) => {
255312
updated_ctxt32 =
256-
update(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)).as_u32();
313+
update(SyntaxContext::from_u32(span.ctxt as u32)).as_u32();
257314
// Any small new context including zero will preserve the format.
258315
if updated_ctxt32 <= MAX_CTXT {
259-
self.ctxt_or_parent_or_marker = updated_ctxt32 as u16;
316+
span.ctxt = updated_ctxt32 as u16;
260317
return;
261318
}
262-
data = self.data_inline_ctxt();
263-
} else {
264-
// Inline-parent format.
319+
data = span.data();
320+
},
321+
InlineParent(span) => {
265322
updated_ctxt32 = update(SyntaxContext::root()).as_u32();
266323
// Only if the new context is zero the format will be preserved.
267324
if updated_ctxt32 == 0 {
268325
// Do nothing.
269326
return;
270327
}
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();
328+
data = span.data();
329+
},
330+
PartiallyInterned(span) => {
331+
updated_ctxt32 = update(SyntaxContext::from_u32(span.ctxt as u32)).as_u32();
332+
// Any small new context excluding zero will preserve the format.
333+
// Zero may change the format to `InlineParent` if parent and len are small enough.
334+
if updated_ctxt32 <= MAX_CTXT && updated_ctxt32 != 0 {
335+
span.ctxt = updated_ctxt32 as u16;
336+
return;
337+
}
338+
data = span.data();
339+
},
340+
Interned(span) => {
341+
data = span.data();
342+
updated_ctxt32 = update(data.ctxt).as_u32();
343+
},
288344
}
289345

290346
// We could not keep the span in the same inline format, fall back to the complete logic.
@@ -294,21 +350,13 @@ impl Span {
294350
// Returns either syntactic context, if it can be retrieved without taking the interner lock,
295351
// or an index into the interner if it cannot.
296352
#[inline]
297-
fn inline_ctxt(self) -> Result<SyntaxContext, usize> {
298-
if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
299-
if self.len_with_tag_or_marker & PARENT_TAG == 0 {
300-
// Inline-context format.
301-
Ok(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32))
302-
} else {
303-
// Inline-parent format.
304-
Ok(SyntaxContext::root())
305-
}
306-
} else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
307-
// Partially-interned format.
308-
Ok(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32))
309-
} else {
310-
// Interned format.
311-
Err(self.lo_or_index as usize)
353+
fn inline_ctxt(mut self) -> Result<SyntaxContext, usize> {
354+
match_span_kind! {
355+
&mut self,
356+
InlineCtxt(span) => Ok(SyntaxContext::from_u32(span.ctxt as u32)),
357+
InlineParent(_span) => Ok(SyntaxContext::root()),
358+
PartiallyInterned(span) => Ok(SyntaxContext::from_u32(span.ctxt as u32)),
359+
Interned(span) => Err(span.index as usize),
312360
}
313361
}
314362

0 commit comments

Comments
 (0)