Skip to content

Commit eb709e5

Browse files
Auto merge of #141451 - lcnr:canonicalize-env-cache, r=<try>
add more `TypeFlags` fast paths, cache `param_env` canonicalization BLocked on #141581
2 parents c31cccb + 758f4c9 commit eb709e5

File tree

4 files changed

+109
-28
lines changed

4 files changed

+109
-28
lines changed

compiler/rustc_middle/src/ty/context.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,17 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
179179
f(&mut *self.new_solver_evaluation_cache.lock())
180180
}
181181

182+
fn canonical_param_env_cache_get_or_insert<R>(
183+
self,
184+
param_env: ty::ParamEnv<'tcx>,
185+
f: impl FnOnce() -> ty::CanonicalParamEnvCacheEntry<Self>,
186+
from_entry: impl FnOnce(&ty::CanonicalParamEnvCacheEntry<Self>) -> R,
187+
) -> R {
188+
let mut cache = self.new_solver_canonical_param_env_cache.lock();
189+
let entry = cache.entry(param_env).or_insert_with(f);
190+
from_entry(entry)
191+
}
192+
182193
fn evaluation_is_concurrent(&self) -> bool {
183194
self.sess.threads() > 1
184195
}
@@ -1444,6 +1455,8 @@ pub struct GlobalCtxt<'tcx> {
14441455

14451456
/// Caches the results of goal evaluation in the new solver.
14461457
pub new_solver_evaluation_cache: Lock<search_graph::GlobalCache<TyCtxt<'tcx>>>,
1458+
pub new_solver_canonical_param_env_cache:
1459+
Lock<FxHashMap<ty::ParamEnv<'tcx>, ty::CanonicalParamEnvCacheEntry<TyCtxt<'tcx>>>>,
14471460

14481461
pub canonical_param_env_cache: CanonicalParamEnvCache<'tcx>,
14491462

@@ -1692,6 +1705,7 @@ impl<'tcx> TyCtxt<'tcx> {
16921705
selection_cache: Default::default(),
16931706
evaluation_cache: Default::default(),
16941707
new_solver_evaluation_cache: Default::default(),
1708+
new_solver_canonical_param_env_cache: Default::default(),
16951709
canonical_param_env_cache: Default::default(),
16961710
data_layout,
16971711
alloc_map: interpret::AllocMap::new(),

compiler/rustc_next_trait_solver/src/canonicalizer.rs

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack};
44
use rustc_type_ir::inherent::*;
55
use rustc_type_ir::solve::{Goal, QueryInput};
66
use rustc_type_ir::{
7-
self as ty, Canonical, CanonicalTyVarKind, CanonicalVarKind, Flags, InferCtxtLike, Interner,
8-
TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
7+
self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalTyVarKind, CanonicalVarKind,
8+
Flags, InferCtxtLike, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable,
9+
TypeVisitableExt,
910
};
1011

1112
use crate::delegate::SolverDelegate;
@@ -100,6 +101,76 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
100101
Canonical { max_universe, variables, value }
101102
}
102103

104+
fn canonicalize_param_env(
105+
delegate: &'a D,
106+
variables: &'a mut Vec<I::GenericArg>,
107+
param_env: I::ParamEnv,
108+
) -> (I::ParamEnv, HashMap<I::GenericArg, usize>, Vec<CanonicalVarKind<I>>) {
109+
if !param_env.has_type_flags(NEEDS_CANONICAL) {
110+
return (param_env, Default::default(), Vec::new());
111+
}
112+
113+
// Check whether we can use the global cache for this param_env. As we only use
114+
// the `param_env` itself as the cache key, considering any additional information
115+
// durnig its canonicalization would be incorrect. We always canonicalize region
116+
// inference variables in a separate universe, so these are fine. However, we do
117+
// track the universe of type and const inference variables so these must not be
118+
// globally cached. We don't rely on any additional information when canonicalizing
119+
// placeholders.
120+
if !param_env.has_non_region_infer() {
121+
delegate.cx().canonical_param_env_cache_get_or_insert(
122+
param_env,
123+
|| {
124+
let mut variables = Vec::new();
125+
let mut env_canonicalizer = Canonicalizer {
126+
delegate,
127+
canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
128+
129+
variables: &mut variables,
130+
variable_lookup_table: Default::default(),
131+
var_kinds: Vec::new(),
132+
binder_index: ty::INNERMOST,
133+
134+
cache: Default::default(),
135+
};
136+
let param_env = param_env.fold_with(&mut env_canonicalizer);
137+
debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
138+
CanonicalParamEnvCacheEntry {
139+
param_env,
140+
variable_lookup_table: env_canonicalizer.variable_lookup_table,
141+
var_kinds: env_canonicalizer.var_kinds,
142+
variables,
143+
}
144+
},
145+
|&CanonicalParamEnvCacheEntry {
146+
param_env,
147+
variables: ref cache_variables,
148+
ref variable_lookup_table,
149+
ref var_kinds,
150+
}| {
151+
debug_assert!(variables.is_empty());
152+
variables.extend(cache_variables.iter().copied());
153+
(param_env, variable_lookup_table.clone(), var_kinds.clone())
154+
},
155+
)
156+
} else {
157+
let mut env_canonicalizer = Canonicalizer {
158+
delegate,
159+
canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
160+
161+
variables,
162+
variable_lookup_table: Default::default(),
163+
var_kinds: Vec::new(),
164+
binder_index: ty::INNERMOST,
165+
166+
cache: Default::default(),
167+
};
168+
let param_env = param_env.fold_with(&mut env_canonicalizer);
169+
debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
170+
(param_env, env_canonicalizer.variable_lookup_table, env_canonicalizer.var_kinds)
171+
}
172+
}
173+
103174
/// When canonicalizing query inputs, we keep `'static` in the `param_env`
104175
/// but erase it everywhere else. We generally don't want to depend on region
105176
/// identity, so while it should not matter whether `'static` is kept in the
@@ -114,37 +185,17 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
114185
input: QueryInput<I, P>,
115186
) -> ty::Canonical<I, QueryInput<I, P>> {
116187
// First canonicalize the `param_env` while keeping `'static`
117-
let mut env_canonicalizer = Canonicalizer {
118-
delegate,
119-
canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
120-
121-
variables,
122-
variable_lookup_table: Default::default(),
123-
var_kinds: Vec::new(),
124-
binder_index: ty::INNERMOST,
125-
126-
cache: Default::default(),
127-
};
128-
129-
let param_env = input.goal.param_env;
130-
let param_env = if param_env.has_type_flags(NEEDS_CANONICAL) {
131-
param_env.fold_with(&mut env_canonicalizer)
132-
} else {
133-
param_env
134-
};
135-
136-
debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
188+
let (param_env, variable_lookup_table, var_kinds) =
189+
Canonicalizer::canonicalize_param_env(delegate, variables, input.goal.param_env);
137190
// Then canonicalize the rest of the input without keeping `'static`
138191
// while *mostly* reusing the canonicalizer from above.
139192
let mut rest_canonicalizer = Canonicalizer {
140193
delegate,
141194
canonicalize_mode: CanonicalizeMode::Input { keep_static: false },
142195

143-
variables: env_canonicalizer.variables,
144-
// We're able to reuse the `variable_lookup_table` as whether or not
145-
// it already contains an entry for `'static` does not matter.
146-
variable_lookup_table: env_canonicalizer.variable_lookup_table,
147-
var_kinds: env_canonicalizer.var_kinds,
196+
variables,
197+
variable_lookup_table,
198+
var_kinds,
148199
binder_index: ty::INNERMOST,
149200

150201
// We do not reuse the cache as it may contain entries whose canonicalized

compiler/rustc_type_ir/src/canonical.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use derive_where::derive_where;
77
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
88
use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
99

10+
use crate::data_structures::HashMap;
1011
use crate::inherent::*;
1112
use crate::{self as ty, Interner, TypingMode, UniverseIndex};
1213

@@ -333,3 +334,11 @@ impl<I: Interner> Index<ty::BoundVar> for CanonicalVarValues<I> {
333334
&self.var_values.as_slice()[value.as_usize()]
334335
}
335336
}
337+
338+
#[derive_where(Clone, Debug; I: Interner)]
339+
pub struct CanonicalParamEnvCacheEntry<I: Interner> {
340+
pub param_env: I::ParamEnv,
341+
pub variables: Vec<I::GenericArg>,
342+
pub variable_lookup_table: HashMap<I::GenericArg, usize>,
343+
pub var_kinds: Vec<CanonicalVarKind<I>>,
344+
}

compiler/rustc_type_ir/src/interner.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::lang_items::TraitSolverLangItem;
1212
use crate::relate::Relate;
1313
use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult};
1414
use crate::visit::{Flags, TypeVisitable};
15-
use crate::{self as ty, search_graph};
15+
use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph};
1616

1717
#[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")]
1818
pub trait Interner:
@@ -149,6 +149,13 @@ pub trait Interner:
149149

150150
fn with_global_cache<R>(self, f: impl FnOnce(&mut search_graph::GlobalCache<Self>) -> R) -> R;
151151

152+
fn canonical_param_env_cache_get_or_insert<R>(
153+
self,
154+
param_env: Self::ParamEnv,
155+
f: impl FnOnce() -> CanonicalParamEnvCacheEntry<Self>,
156+
from_entry: impl FnOnce(&CanonicalParamEnvCacheEntry<Self>) -> R,
157+
) -> R;
158+
152159
fn evaluation_is_concurrent(&self) -> bool;
153160

154161
fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;

0 commit comments

Comments
 (0)