@@ -4,10 +4,10 @@ use crate::SPAN_TRACK;
4
4
use crate :: { BytePos , SpanData } ;
5
5
6
6
use rustc_data_structures:: fx:: FxIndexSet ;
7
-
8
7
// This code is very hot and uses lots of arithmetic, avoid overflow checks for performance.
9
8
// See https://github.com/rust-lang/rust/pull/119440#issuecomment-1874255727
10
9
use rustc_serialize:: int_overflow:: DebugStrictAdd ;
10
+ use std:: mem:: transmute;
11
11
12
12
/// A compressed span.
13
13
///
@@ -87,45 +87,130 @@ pub struct Span {
87
87
ctxt_or_parent_or_marker : u16 ,
88
88
}
89
89
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 {
91
120
#[ 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 ;
94
123
debug_assert ! ( len <= MAX_LEN ) ;
95
124
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 ) ,
99
128
parent : None ,
100
129
}
101
130
}
102
131
#[ 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 ;
105
141
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
142
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) ) ,
112
145
ctxt : SyntaxContext :: root ( ) ,
113
- parent : Some ( parent) ,
146
+ parent : Some ( LocalDefId { local_def_index : DefIndex :: from_u32 ( self . parent as u32 ) } ) ,
114
147
}
115
148
}
116
149
#[ 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 {
118
158
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 ] )
121
161
}
122
162
}
123
163
#[ 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 } ) }
126
166
}
127
167
}
128
168
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
+
129
214
// `MAX_LEN` is chosen so that `PARENT_TAG | MAX_LEN` is distinct from
130
215
// `BASE_LEN_INTERNED_MARKER`. (If `MAX_LEN` was 1 higher, this wouldn't be true.)
131
216
const MAX_LEN : u32 = 0b0111_1111_1111_1110 ;
@@ -154,23 +239,13 @@ impl Span {
154
239
let ( len, ctxt32) = ( hi. 0 - lo. 0 , ctxt. as_u32 ( ) ) ;
155
240
if len <= MAX_LEN {
156
241
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 ) ;
163
243
} else if ctxt32 == 0
164
244
&& let Some ( parent) = parent
165
245
&& let parent32 = parent. local_def_index . as_u32 ( )
166
246
&& parent32 <= MAX_CTXT
167
247
{
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 ) ;
174
249
}
175
250
}
176
251
@@ -179,20 +254,10 @@ impl Span {
179
254
with_span_interner ( |interner| interner. intern ( & SpanData { lo, hi, ctxt, parent } ) )
180
255
} ;
181
256
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 )
189
259
} 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) )
196
261
}
197
262
}
198
263
@@ -208,21 +273,13 @@ impl Span {
208
273
/// Internal function to translate between an encoded span and the expanded representation.
209
274
/// This function must not be used outside the incremental engine.
210
275
#[ 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( ) ,
226
283
}
227
284
}
228
285
@@ -249,42 +306,41 @@ impl Span {
249
306
#[ inline]
250
307
pub fn update_ctxt ( & mut self , update : impl FnOnce ( SyntaxContext ) -> SyntaxContext ) {
251
308
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 ) => {
255
312
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( ) ;
257
314
// Any small new context including zero will preserve the format.
258
315
if updated_ctxt32 <= MAX_CTXT {
259
- self . ctxt_or_parent_or_marker = updated_ctxt32 as u16 ;
316
+ span . ctxt = updated_ctxt32 as u16 ;
260
317
return ;
261
318
}
262
- data = self . data_inline_ctxt ( ) ;
263
- } else {
264
- // Inline-parent format.
319
+ data = span . data ( ) ;
320
+ } ,
321
+ InlineParent ( span ) => {
265
322
updated_ctxt32 = update( SyntaxContext :: root( ) ) . as_u32( ) ;
266
323
// Only if the new context is zero the format will be preserved.
267
324
if updated_ctxt32 == 0 {
268
325
// Do nothing.
269
326
return ;
270
327
}
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
+ } ,
288
344
}
289
345
290
346
// We could not keep the span in the same inline format, fall back to the complete logic.
@@ -294,21 +350,13 @@ impl Span {
294
350
// Returns either syntactic context, if it can be retrieved without taking the interner lock,
295
351
// or an index into the interner if it cannot.
296
352
#[ 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 ) ,
312
360
}
313
361
}
314
362
0 commit comments