@@ -4,10 +4,10 @@ use crate::SPAN_TRACK;
44use crate :: { BytePos , SpanData } ;
55
66use 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
109use 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.)
131216const 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