Skip to content

Commit 15ac698

Browse files
committed
canonicalizer: rm region uniquification, add caching
1 parent 1b5aa96 commit 15ac698

1 file changed

Lines changed: 138 additions & 109 deletions

File tree

‎compiler/rustc_next_trait_solver/src/canonicalizer.rs‎

Lines changed: 138 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::cmp::Ordering;
22

3+
use rustc_type_ir::data_structures::HashMap;
34
use rustc_type_ir::fold::{TypeFoldable,TypeFolder,TypeSuperFoldable};
45
use rustc_type_ir::inherent::*;
56
use rustc_type_ir::visit::TypeVisitableExt;
@@ -44,8 +45,15 @@ pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
4445
canonicalize_mode:CanonicalizeMode,
4546

4647
variables:&'amutVec<I::GenericArg>,
48+
variable_lookup_table:HashMap<I::GenericArg,usize>,
49+
4750
primitive_var_infos:Vec<CanonicalVarInfo<I>>,
4851
binder_index: ty::DebruijnIndex,
52+
53+
/// We only use the debruijn index during lookup as all other fields
54+
/// should not be impacted by whether a type is folded once or multiple
55+
/// times.
56+
cache:HashMap<(ty::DebruijnIndex,I::Ty),I::Ty>,
4957
}
5058

5159
impl<'a,D:SolverDelegate<Interner = I>,I:Interner>Canonicalizer<'a,D,I>{
@@ -60,12 +68,14 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
6068
canonicalize_mode,
6169

6270
variables,
71+
variable_lookup_table:Default::default(),
6372
primitive_var_infos:Vec::new(),
6473
binder_index: ty::INNERMOST,
74+
75+
cache:Default::default(),
6576
};
6677

6778
let value = value.fold_with(&mut canonicalizer);
68-
// FIXME: Restore these assertions. Should we uplift type flags?
6979
assert!(!value.has_infer(),"unexpected infer in {value:?}");
7080
assert!(!value.has_placeholders(),"unexpected placeholders in {value:?}");
7181

@@ -75,6 +85,37 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
7585
Canonical{ defining_opaque_types, max_universe, variables, value }
7686
}
7787

88+
fnget_or_insert_bound_var(
89+
&mutself,
90+
arg:implInto<I::GenericArg>,
91+
canonical_var_info:CanonicalVarInfo<I>,
92+
) -> ty::BoundVar{
93+
// FIXME: 16 is made up and arbitrary. We should look at some
94+
// perf data here.
95+
let arg = arg.into();
96+
let idx = ifself.variables.len() > 16{
97+
ifself.variable_lookup_table.is_empty(){
98+
self.variable_lookup_table.extend(self.variables.iter().copied().zip(0..));
99+
}
100+
101+
*self.variable_lookup_table.entry(arg).or_insert_with(|| {
102+
let var = self.variables.len();
103+
self.variables.push(arg);
104+
self.primitive_var_infos.push(canonical_var_info);
105+
var
106+
})
107+
}else{
108+
self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| {
109+
let var = self.variables.len();
110+
self.variables.push(arg);
111+
self.primitive_var_infos.push(canonical_var_info);
112+
var
113+
})
114+
};
115+
116+
ty::BoundVar::from(idx)
117+
}
118+
78119
fnfinalize(self) -> (ty::UniverseIndex,I::CanonicalVars){
79120
letmut var_infos = self.primitive_var_infos;
80121
// See the rustc-dev-guide section about how we deal with universes
@@ -124,8 +165,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
124165
// - var_infos: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
125166
// - var_infos: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
126167
//
127-
// This algorithm runs in `O()` where `n` is the number of different universe
128-
// indices in the input. This should be fine as `n` is expected to be small.
168+
// This algorithm runs in `O(mn)` where `n` is the number of different universes and
169+
// `m` the number of variables. This should be fine as both are expected to be small.
129170
letmut curr_compressed_uv = ty::UniverseIndex::ROOT;
130171
letmut existential_in_new_uv = None;
131172
letmut next_orig_uv = Some(ty::UniverseIndex::ROOT);
@@ -185,14 +226,16 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
185226
for var in var_infos.iter_mut(){
186227
// We simply put all regions from the input into the highest
187228
// compressed universe, so we only deal with them at the end.
188-
if !var.is_region() && is_existential == var.is_existential(){
189-
update_uv(var, orig_uv, is_existential)
229+
if !var.is_region(){
230+
if is_existential == var.is_existential(){
231+
update_uv(var, orig_uv, is_existential)
232+
}
190233
}
191234
}
192235
}
193236
}
194237

195-
// We uniquify regions and always put them into their own universe
238+
// We put all regions into a separate universe.
196239
letmut first_region = true;
197240
for var in var_infos.iter_mut(){
198241
if var.is_region(){
@@ -208,93 +251,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
208251
let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos);
209252
(curr_compressed_uv, var_infos)
210253
}
211-
}
212-
213-
impl<D:SolverDelegate<Interner = I>,I:Interner>TypeFolder<I>forCanonicalizer<'_,D,I>{
214-
fncx(&self) -> I{
215-
self.delegate.cx()
216-
}
217-
218-
fnfold_binder<T>(&mutself,t: ty::Binder<I,T>) -> ty::Binder<I,T>
219-
where
220-
T:TypeFoldable<I>,
221-
{
222-
self.binder_index.shift_in(1);
223-
let t = t.super_fold_with(self);
224-
self.binder_index.shift_out(1);
225-
t
226-
}
227-
228-
fnfold_region(&mutself,r:I::Region) -> I::Region{
229-
let kind = match r.kind(){
230-
ty::ReBound(..) => return r,
231-
232-
// We may encounter `ReStatic` in item signatures or the hidden type
233-
// of an opaque. `ReErased` should only be encountered in the hidden
234-
// type of an opaque for regions that are ignored for the purposes of
235-
// captures.
236-
//
237-
// FIXME: We should investigate the perf implications of not uniquifying
238-
// `ReErased`. We may be able to short-circuit registering region
239-
// obligations if we encounter a `ReErased` on one side, for example.
240-
ty::ReStatic | ty::ReErased | ty::ReError(_) => matchself.canonicalize_mode{
241-
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
242-
CanonicalizeMode::Response{ .. } => return r,
243-
},
244-
245-
ty::ReEarlyParam(_) | ty::ReLateParam(_) => matchself.canonicalize_mode{
246-
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
247-
CanonicalizeMode::Response{ .. } => {
248-
panic!("unexpected region in response: {r:?}")
249-
}
250-
},
251-
252-
ty::RePlaceholder(placeholder) => matchself.canonicalize_mode{
253-
// We canonicalize placeholder regions as existentials in query inputs.
254-
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
255-
CanonicalizeMode::Response{ max_input_universe } => {
256-
// If we have a placeholder region inside of a query, it must be from
257-
// a new universe.
258-
if max_input_universe.can_name(placeholder.universe()){
259-
panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
260-
}
261-
CanonicalVarKind::PlaceholderRegion(placeholder)
262-
}
263-
},
264-
265-
ty::ReVar(vid) => {
266-
assert_eq!(
267-
self.delegate.opportunistic_resolve_lt_var(vid),
268-
r,
269-
"region vid should have been resolved fully before canonicalization"
270-
);
271-
matchself.canonicalize_mode{
272-
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
273-
CanonicalizeMode::Response{ .. } => {
274-
CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
275-
}
276-
}
277-
}
278-
};
279-
280-
let existing_bound_var = matchself.canonicalize_mode{
281-
CanonicalizeMode::Input => None,
282-
CanonicalizeMode::Response{ .. } => {
283-
self.variables.iter().position(|&v| v == r.into()).map(ty::BoundVar::from)
284-
}
285-
};
286-
287-
let var = existing_bound_var.unwrap_or_else(|| {
288-
let var = ty::BoundVar::from(self.variables.len());
289-
self.variables.push(r.into());
290-
self.primitive_var_infos.push(CanonicalVarInfo{ kind });
291-
var
292-
});
293254

294-
Region::new_anon_bound(self.cx(),self.binder_index, var)
295-
}
296-
297-
fnfold_ty(&mutself,t:I::Ty) -> I::Ty{
255+
fncached_fold_ty(&mutself,t:I::Ty) -> I::Ty{
298256
let kind = match t.kind(){
299257
ty::Infer(i) => match i {
300258
ty::TyVar(vid) => {
@@ -368,20 +326,98 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
368326
| ty::Tuple(_)
369327
| ty::Alias(_, _)
370328
| ty::Bound(_, _)
371-
| ty::Error(_) => return t.super_fold_with(self),
329+
| ty::Error(_) => {
330+
return t.super_fold_with(self);
331+
}
372332
};
373333

374-
let var = ty::BoundVar::from(
375-
self.variables.iter().position(|&v| v == t.into()).unwrap_or_else(|| {
376-
let var = self.variables.len();
377-
self.variables.push(t.into());
378-
self.primitive_var_infos.push(CanonicalVarInfo{ kind });
379-
var
380-
}),
381-
);
334+
let var = self.get_or_insert_bound_var(t,CanonicalVarInfo{ kind });
382335

383336
Ty::new_anon_bound(self.cx(),self.binder_index, var)
384337
}
338+
}
339+
340+
impl<D:SolverDelegate<Interner = I>,I:Interner>TypeFolder<I>forCanonicalizer<'_,D,I>{
341+
fncx(&self) -> I{
342+
self.delegate.cx()
343+
}
344+
345+
fnfold_binder<T>(&mutself,t: ty::Binder<I,T>) -> ty::Binder<I,T>
346+
where
347+
T:TypeFoldable<I>,
348+
{
349+
self.binder_index.shift_in(1);
350+
let t = t.super_fold_with(self);
351+
self.binder_index.shift_out(1);
352+
t
353+
}
354+
355+
fnfold_region(&mutself,r:I::Region) -> I::Region{
356+
let kind = match r.kind(){
357+
ty::ReBound(..) => return r,
358+
359+
// We may encounter `ReStatic` in item signatures or the hidden type
360+
// of an opaque. `ReErased` should only be encountered in the hidden
361+
// type of an opaque for regions that are ignored for the purposes of
362+
// captures.
363+
//
364+
// FIXME: We should investigate the perf implications of not uniquifying
365+
// `ReErased`. We may be able to short-circuit registering region
366+
// obligations if we encounter a `ReErased` on one side, for example.
367+
ty::ReStatic | ty::ReErased | ty::ReError(_) => matchself.canonicalize_mode{
368+
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
369+
CanonicalizeMode::Response{ .. } => return r,
370+
},
371+
372+
ty::ReEarlyParam(_) | ty::ReLateParam(_) => matchself.canonicalize_mode{
373+
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
374+
CanonicalizeMode::Response{ .. } => {
375+
panic!("unexpected region in response: {r:?}")
376+
}
377+
},
378+
379+
ty::RePlaceholder(placeholder) => matchself.canonicalize_mode{
380+
// We canonicalize placeholder regions as existentials in query inputs.
381+
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
382+
CanonicalizeMode::Response{ max_input_universe } => {
383+
// If we have a placeholder region inside of a query, it must be from
384+
// a new universe.
385+
if max_input_universe.can_name(placeholder.universe()){
386+
panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
387+
}
388+
CanonicalVarKind::PlaceholderRegion(placeholder)
389+
}
390+
},
391+
392+
ty::ReVar(vid) => {
393+
assert_eq!(
394+
self.delegate.opportunistic_resolve_lt_var(vid),
395+
r,
396+
"region vid should have been resolved fully before canonicalization"
397+
);
398+
matchself.canonicalize_mode{
399+
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
400+
CanonicalizeMode::Response{ .. } => {
401+
CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
402+
}
403+
}
404+
}
405+
};
406+
407+
let var = self.get_or_insert_bound_var(r,CanonicalVarInfo{ kind });
408+
409+
Region::new_anon_bound(self.cx(),self.binder_index, var)
410+
}
411+
412+
fnfold_ty(&mutself,t:I::Ty) -> I::Ty{
413+
ifletSome(&ty) = self.cache.get(&(self.binder_index, t)){
414+
ty
415+
}else{
416+
let res = self.cached_fold_ty(t);
417+
assert!(self.cache.insert((self.binder_index, t), res).is_none());
418+
res
419+
}
420+
}
385421

386422
fnfold_const(&mutself,c:I::Const) -> I::Const{
387423
let kind = match c.kind(){
@@ -419,14 +455,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
419455
| ty::ConstKind::Expr(_) => return c.super_fold_with(self),
420456
};
421457

422-
let var = ty::BoundVar::from(
423-
self.variables.iter().position(|&v| v == c.into()).unwrap_or_else(|| {
424-
let var = self.variables.len();
425-
self.variables.push(c.into());
426-
self.primitive_var_infos.push(CanonicalVarInfo{ kind });
427-
var
428-
}),
429-
);
458+
let var = self.get_or_insert_bound_var(c,CanonicalVarInfo{ kind });
430459

431460
Const::new_anon_bound(self.cx(),self.binder_index, var)
432461
}

0 commit comments

Comments
 (0)