Skip to content

Commit 15ac698

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

File tree

1 file changed

+138
-109
lines changed

1 file changed

+138
-109
lines changed

compiler/rustc_next_trait_solver/src/canonicalizer.rs

+138-109
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: &'a mut Vec<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+
fn get_or_insert_bound_var(
89+
&mut self,
90+
arg: impl Into<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 = if self.variables.len() > 16 {
97+
if self.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
fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) {
79120
let mut 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
let mut curr_compressed_uv = ty::UniverseIndex::ROOT;
130171
let mut existential_in_new_uv = None;
131172
let mut 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
let mut 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> for Canonicalizer<'_, D, I> {
214-
fn cx(&self) -> I {
215-
self.delegate.cx()
216-
}
217-
218-
fn fold_binder<T>(&mut self, 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-
fn fold_region(&mut self, 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(_) => match self.canonicalize_mode {
241-
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
242-
CanonicalizeMode::Response { .. } => return r,
243-
},
244-
245-
ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.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) => match self.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-
match self.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 = match self.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-
fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
255+
fn cached_fold_ty(&mut self, 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> for Canonicalizer<'_, D, I> {
341+
fn cx(&self) -> I {
342+
self.delegate.cx()
343+
}
344+
345+
fn fold_binder<T>(&mut self, 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+
fn fold_region(&mut self, 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(_) => match self.canonicalize_mode {
368+
CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
369+
CanonicalizeMode::Response { .. } => return r,
370+
},
371+
372+
ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.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) => match self.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+
match self.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+
fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
413+
if let Some(&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
fn fold_const(&mut self, 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)