16
16
//! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html
17
17
18
18
use infer:: canonical:: {
19
- Canonical , CanonicalTyVarKind , CanonicalVarInfo , CanonicalVarKind , CanonicalVarValues ,
20
- Canonicalized ,
19
+ Canonical , CanonicalTyVarKind , CanonicalVarInfo , CanonicalVarKind , Canonicalized ,
20
+ SmallCanonicalVarValues ,
21
21
} ;
22
22
use infer:: InferCtxt ;
23
23
use std:: sync:: atomic:: Ordering ;
@@ -26,7 +26,8 @@ use ty::subst::Kind;
26
26
use ty:: { self , CanonicalVar , Lift , Slice , Ty , TyCtxt , TypeFlags } ;
27
27
28
28
use rustc_data_structures:: fx:: FxHashMap ;
29
- use rustc_data_structures:: indexed_vec:: IndexVec ;
29
+ use rustc_data_structures:: indexed_vec:: Idx ;
30
+ use rustc_data_structures:: small_vec:: SmallVec ;
30
31
31
32
impl < ' cx , ' gcx , ' tcx > InferCtxt < ' cx , ' gcx , ' tcx > {
32
33
/// Canonicalizes a query value `V`. When we canonicalize a query,
@@ -47,7 +48,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
47
48
pub fn canonicalize_query < V > (
48
49
& self ,
49
50
value : & V ,
50
- ) -> ( Canonicalized < ' gcx , V > , CanonicalVarValues < ' tcx > )
51
+ var_values : & mut SmallCanonicalVarValues < ' tcx >
52
+ ) -> Canonicalized < ' gcx , V >
51
53
where
52
54
V : TypeFoldable < ' tcx > + Lift < ' gcx > ,
53
55
{
@@ -65,6 +67,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
65
67
static_region : true ,
66
68
other_free_regions : true ,
67
69
} ,
70
+ var_values,
68
71
)
69
72
}
70
73
@@ -96,10 +99,11 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
96
99
pub fn canonicalize_response < V > (
97
100
& self ,
98
101
value : & V ,
99
- ) -> ( Canonicalized < ' gcx , V > , CanonicalVarValues < ' tcx > )
102
+ ) -> Canonicalized < ' gcx , V >
100
103
where
101
104
V : TypeFoldable < ' tcx > + Lift < ' gcx > ,
102
105
{
106
+ let mut var_values = SmallVec :: new ( ) ;
103
107
Canonicalizer :: canonicalize (
104
108
value,
105
109
Some ( self ) ,
@@ -108,6 +112,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
108
112
static_region : false ,
109
113
other_free_regions : false ,
110
114
} ,
115
+ & mut var_values
111
116
)
112
117
}
113
118
@@ -123,7 +128,8 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
123
128
pub fn canonicalize_hr_query_hack < V > (
124
129
& self ,
125
130
value : & V ,
126
- ) -> ( Canonicalized < ' gcx , V > , CanonicalVarValues < ' tcx > )
131
+ var_values : & mut SmallCanonicalVarValues < ' tcx >
132
+ ) -> Canonicalized < ' gcx , V >
127
133
where
128
134
V : TypeFoldable < ' tcx > + Lift < ' gcx > ,
129
135
{
@@ -141,6 +147,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
141
147
static_region : false ,
142
148
other_free_regions : true ,
143
149
} ,
150
+ var_values
144
151
)
145
152
}
146
153
}
@@ -163,9 +170,11 @@ impl CanonicalizeRegionMode {
163
170
struct Canonicalizer < ' cx , ' gcx : ' tcx , ' tcx : ' cx > {
164
171
infcx : Option < & ' cx InferCtxt < ' cx , ' gcx , ' tcx > > ,
165
172
tcx : TyCtxt < ' cx , ' gcx , ' tcx > ,
166
- variables : IndexVec < CanonicalVar , CanonicalVarInfo > ,
173
+ variables : SmallVec < [ CanonicalVarInfo ; 8 ] > ,
174
+ var_values : & ' cx mut SmallCanonicalVarValues < ' tcx > ,
175
+ // Note that indices is only used once `var_values` is big enough to be
176
+ // heap-allocated.
167
177
indices : FxHashMap < Kind < ' tcx > , CanonicalVar > ,
168
- var_values : IndexVec < CanonicalVar , Kind < ' tcx > > ,
169
178
canonicalize_region_mode : CanonicalizeRegionMode ,
170
179
needs_canonical_flags : TypeFlags ,
171
180
}
@@ -295,7 +304,8 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
295
304
infcx : Option < & ' cx InferCtxt < ' cx , ' gcx , ' tcx > > ,
296
305
tcx : TyCtxt < ' cx , ' gcx , ' tcx > ,
297
306
canonicalize_region_mode : CanonicalizeRegionMode ,
298
- ) -> ( Canonicalized < ' gcx , V > , CanonicalVarValues < ' tcx > )
307
+ var_values : & ' cx mut SmallCanonicalVarValues < ' tcx >
308
+ ) -> Canonicalized < ' gcx , V >
299
309
where
300
310
V : TypeFoldable < ' tcx > + Lift < ' gcx > ,
301
311
{
@@ -320,20 +330,17 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
320
330
variables : Slice :: empty ( ) ,
321
331
value : out_value,
322
332
} ;
323
- let values = CanonicalVarValues {
324
- var_values : IndexVec :: default ( ) ,
325
- } ;
326
- return ( canon_value, values) ;
333
+ return canon_value;
327
334
}
328
335
329
336
let mut canonicalizer = Canonicalizer {
330
337
infcx,
331
338
tcx,
332
339
canonicalize_region_mode,
333
340
needs_canonical_flags,
334
- variables : IndexVec :: default ( ) ,
341
+ variables : SmallVec :: new ( ) ,
342
+ var_values,
335
343
indices : FxHashMap :: default ( ) ,
336
- var_values : IndexVec :: default ( ) ,
337
344
} ;
338
345
let out_value = value. fold_with ( & mut canonicalizer) ;
339
346
@@ -348,16 +355,12 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
348
355
)
349
356
} ) ;
350
357
351
- let canonical_variables = tcx. intern_canonical_var_infos ( & canonicalizer. variables . raw ) ;
358
+ let canonical_variables = tcx. intern_canonical_var_infos ( & canonicalizer. variables ) ;
352
359
353
- let canonical_value = Canonical {
360
+ Canonical {
354
361
variables : canonical_variables,
355
362
value : out_value,
356
- } ;
357
- let canonical_var_values = CanonicalVarValues {
358
- var_values : canonicalizer. var_values ,
359
- } ;
360
- ( canonical_value, canonical_var_values)
363
+ }
361
364
}
362
365
363
366
/// Creates a canonical variable replacing `kind` from the input,
@@ -366,21 +369,54 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
366
369
/// potentially a free region).
367
370
fn canonical_var ( & mut self , info : CanonicalVarInfo , kind : Kind < ' tcx > ) -> CanonicalVar {
368
371
let Canonicalizer {
369
- indices,
370
372
variables,
371
373
var_values,
374
+ indices,
372
375
..
373
376
} = self ;
374
377
375
- indices
376
- . entry ( kind)
377
- . or_insert_with ( || {
378
- let cvar1 = variables. push ( info) ;
379
- let cvar2 = var_values. push ( kind) ;
380
- assert_eq ! ( cvar1, cvar2) ;
381
- cvar1
382
- } )
383
- . clone ( )
378
+ // This code is hot. `variables` and `var_values` are usually small
379
+ // (fewer than 8 elements ~95% of the time). They are SmallVec's to
380
+ // avoid allocations in those cases. We also don't use `indices` to
381
+ // determine if a kind has been seen before until the limit of 8 has
382
+ // been exceeded, to also avoid allocations for `indices`.
383
+ if var_values. is_array ( ) {
384
+ // `var_values` is stack-allocated. `indices` isn't used yet. Do a
385
+ // direct linear search of `var_values`.
386
+ if let Some ( idx) = var_values. iter ( ) . position ( |& k| k == kind) {
387
+ // `kind` is already present in `var_values`.
388
+ CanonicalVar :: new ( idx)
389
+ } else {
390
+ // `kind` isn't present in `var_values`. Append it. Likewise
391
+ // for `info` and `variables`.
392
+ variables. push ( info) ;
393
+ var_values. push ( kind) ;
394
+ assert_eq ! ( variables. len( ) , var_values. len( ) ) ;
395
+
396
+ // If `var_values` has become big enough to be heap-allocated,
397
+ // fill up `indices` to facilitate subsequent lookups.
398
+ if !var_values. is_array ( ) {
399
+ assert ! ( indices. is_empty( ) ) ;
400
+ * indices =
401
+ var_values. iter ( )
402
+ . enumerate ( )
403
+ . map ( |( i, & kind) | ( kind, CanonicalVar :: new ( i) ) )
404
+ . collect ( ) ;
405
+ }
406
+ // The cv is the index of the appended element.
407
+ CanonicalVar :: new ( var_values. len ( ) - 1 )
408
+ }
409
+ } else {
410
+ // `var_values` is large. Do a hashmap search via `indices`.
411
+ * indices
412
+ . entry ( kind)
413
+ . or_insert_with ( || {
414
+ variables. push ( info) ;
415
+ var_values. push ( kind) ;
416
+ assert_eq ! ( variables. len( ) , var_values. len( ) ) ;
417
+ CanonicalVar :: new ( variables. len ( ) - 1 )
418
+ } )
419
+ }
384
420
}
385
421
386
422
/// Given a type variable `ty_var` of the given kind, first check
0 commit comments