Skip to content

Commit 51a0c95

Browse files
committed
optimize CanonicalVarValues::instantiate
1 parent 6e95ffe commit 51a0c95

File tree

9 files changed

+63
-56
lines changed

9 files changed

+63
-56
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4754,6 +4754,7 @@ dependencies = [
47544754
name = "rustc_type_ir"
47554755
version = "0.0.0"
47564756
dependencies = [
4757+
"arrayvec",
47574758
"bitflags",
47584759
"derive-where",
47594760
"ena",

compiler/rustc_hir_typeck/src/method/probe.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -403,15 +403,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
403403
// special handling for this "trivial case" is a good idea.
404404

405405
let infcx = &self.infcx;
406-
let (ParamEnvAnd { param_env: _, value: self_ty }, canonical_inference_vars) =
406+
let (ParamEnvAnd { param_env: _, value: self_ty }, var_values) =
407407
infcx.instantiate_canonical(span, &query_input.canonical);
408408
debug!(?self_ty, ?query_input, "probe_op: Mode::Path");
409409
MethodAutoderefStepsResult {
410410
steps: infcx.tcx.arena.alloc_from_iter([CandidateStep {
411-
self_ty: self.make_query_response_ignoring_pending_obligations(
412-
canonical_inference_vars,
413-
self_ty,
414-
),
411+
self_ty: self
412+
.make_query_response_ignoring_pending_obligations(var_values, self_ty),
415413
autoderefs: 0,
416414
from_unsafe_deref: false,
417415
unsize: false,

compiler/rustc_infer/src/infer/canonical/mod.rs

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
pub use instantiate::CanonicalExt;
2525
use rustc_index::IndexVec;
2626
pub use rustc_middle::infer::canonical::*;
27-
use rustc_middle::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFoldable};
27+
use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable};
2828
use rustc_span::Span;
2929

3030
use crate::infer::{InferCtxt, RegionVariableOrigin};
@@ -67,29 +67,12 @@ impl<'tcx> InferCtxt<'tcx> {
6767
.chain((1..=canonical.max_universe.as_u32()).map(|_| self.create_next_universe()))
6868
.collect();
6969

70-
let canonical_inference_vars =
71-
self.instantiate_canonical_vars(span, canonical.variables, |ui| universes[ui]);
72-
let result = canonical.instantiate(self.tcx, &canonical_inference_vars);
73-
(result, canonical_inference_vars)
74-
}
75-
76-
/// Given the "infos" about the canonical variables from some
77-
/// canonical, creates fresh variables with the same
78-
/// characteristics (see `instantiate_canonical_var` for
79-
/// details). You can then use `instantiate` to instantiate the
80-
/// canonical variable with these inference variables.
81-
fn instantiate_canonical_vars(
82-
&self,
83-
span: Span,
84-
variables: &List<CanonicalVarKind<'tcx>>,
85-
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
86-
) -> CanonicalVarValues<'tcx> {
87-
let mut var_values = Vec::with_capacity(variables.len());
88-
for info in variables.iter() {
89-
let value = self.instantiate_canonical_var(span, info, &var_values, &universe_map);
90-
var_values.push(value);
91-
}
92-
CanonicalVarValues { var_values: self.tcx.mk_args(&var_values) }
70+
let var_values =
71+
CanonicalVarValues::instantiate(self.tcx, &canonical.variables, |var_values, info| {
72+
self.instantiate_canonical_var(span, info, &var_values, |ui| universes[ui])
73+
});
74+
let result = canonical.instantiate(self.tcx, &var_values);
75+
(result, var_values)
9376
}
9477

9578
/// Given the "info" about a canonical variable, creates a fresh

compiler/rustc_infer/src/infer/canonical/query_response.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -453,16 +453,17 @@ impl<'tcx> InferCtxt<'tcx> {
453453
// Create result arguments: if we found a value for a
454454
// given variable in the loop above, use that. Otherwise, use
455455
// a fresh inference variable.
456-
let mut var_values = Vec::with_capacity(query_response.variables.len());
457-
for (index, kind) in query_response.variables.iter().enumerate() {
458-
let value = if kind.universe() != ty::UniverseIndex::ROOT {
456+
let tcx = self.tcx;
457+
let variables = query_response.variables;
458+
let var_values = CanonicalVarValues::instantiate(tcx, variables, |var_values, kind| {
459+
if kind.universe() != ty::UniverseIndex::ROOT {
459460
// A variable from inside a binder of the query. While ideally these shouldn't
460461
// exist at all, we have to deal with them for now.
461462
self.instantiate_canonical_var(cause.span, kind, &var_values, |u| {
462463
universe_map[u.as_usize()]
463464
})
464465
} else if kind.is_existential() {
465-
match opt_values[BoundVar::new(index)] {
466+
match opt_values[BoundVar::new(var_values.len())] {
466467
Some(k) => k,
467468
None => self.instantiate_canonical_var(cause.span, kind, &var_values, |u| {
468469
universe_map[u.as_usize()]
@@ -471,20 +472,17 @@ impl<'tcx> InferCtxt<'tcx> {
471472
} else {
472473
// For placeholders which were already part of the input, we simply map this
473474
// universal bound variable back the placeholder of the input.
474-
opt_values[BoundVar::new(index)]
475+
opt_values[BoundVar::new(var_values.len())]
475476
.expect("expected placeholder to be unified with itself during response")
476-
};
477-
var_values.push(value);
478-
}
479-
480-
let result_args = CanonicalVarValues { var_values: self.tcx.mk_args(&var_values) };
477+
}
478+
});
481479

482480
let mut obligations = PredicateObligations::new();
483481

484482
// Carry all newly resolved opaque types to the caller's scope
485483
for &(a, b) in &query_response.value.opaque_types {
486-
let a = instantiate_value(self.tcx, &result_args, a);
487-
let b = instantiate_value(self.tcx, &result_args, b);
484+
let a = instantiate_value(self.tcx, &var_values, a);
485+
let b = instantiate_value(self.tcx, &var_values, b);
488486
debug!(?a, ?b, "constrain opaque type");
489487
// We use equate here instead of, for example, just registering the
490488
// opaque type's hidden value directly, because the hidden type may have been an inference
@@ -501,7 +499,7 @@ impl<'tcx> InferCtxt<'tcx> {
501499
);
502500
}
503501

504-
Ok(InferOk { value: result_args, obligations })
502+
Ok(InferOk { value: var_values, obligations })
505503
}
506504

507505
/// Given a "guess" at the values for the canonical variables in

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -369,10 +369,8 @@ where
369369
}
370370
}
371371
}
372-
373-
let mut var_values = Vec::with_capacity(response.variables.len());
374-
for (index, kind) in response.variables.iter().enumerate() {
375-
let value = if kind.universe() != ty::UniverseIndex::ROOT {
372+
CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| {
373+
if kind.universe() != ty::UniverseIndex::ROOT {
376374
// A variable from inside a binder of the query. While ideally these shouldn't
377375
// exist at all (see the FIXME at the start of this method), we have to deal with
378376
// them for now.
@@ -387,7 +385,7 @@ where
387385
// more placeholders then they should be able to. However the inference variables have
388386
// to "come from somewhere", so by equating them with the original values of the caller
389387
// later on, we pull them down into their correct universe again.
390-
if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] {
388+
if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] {
391389
v
392390
} else {
393391
delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe)
@@ -396,11 +394,8 @@ where
396394
// For placeholders which were already part of the input, we simply map this
397395
// universal bound variable back the placeholder of the input.
398396
original_values[kind.expect_placeholder_index()]
399-
};
400-
var_values.push(value)
401-
}
402-
403-
CanonicalVarValues { var_values: delegate.cx().mk_args(&var_values) }
397+
}
398+
})
404399
}
405400

406401
/// Unify the `original_values` with the `var_values` returned by the canonical query..

compiler/rustc_trait_selection/src/infer.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,9 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
184184
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
185185
Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
186186
{
187-
let (infcx, key, canonical_inference_vars) =
188-
self.build_with_canonical(DUMMY_SP, canonical_key);
187+
let (infcx, key, var_values) = self.build_with_canonical(DUMMY_SP, canonical_key);
189188
let ocx = ObligationCtxt::new(&infcx);
190189
let value = operation(&ocx, key)?;
191-
ocx.make_canonicalized_query_response(canonical_inference_vars, value)
190+
ocx.make_canonicalized_query_response(var_values, value)
192191
}
193192
}

compiler/rustc_traits/src/evaluate_obligation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn evaluate_obligation<'tcx>(
1919
) -> Result<EvaluationResult, OverflowError> {
2020
assert!(!tcx.next_trait_solver_globally());
2121
debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal);
22-
let (ref infcx, goal, _canonical_inference_vars) =
22+
let (ref infcx, goal, _var_values) =
2323
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
2424
debug!("evaluate_obligation: goal={:#?}", goal);
2525
let ParamEnvAnd { param_env, value: predicate } = goal;

compiler/rustc_type_ir/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2024"
55

66
[dependencies]
77
# tidy-alphabetical-start
8+
arrayvec = { version = "0.7", default-features = false }
89
bitflags = "2.4.1"
910
derive-where = "1.2.7"
1011
ena = "0.14.3"

compiler/rustc_type_ir/src/canonical.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::fmt;
22
use std::ops::Index;
33

4+
use arrayvec::ArrayVec;
45
use derive_where::derive_where;
56
#[cfg(feature = "nightly")]
67
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
@@ -306,6 +307,37 @@ impl<I: Interner> CanonicalVarValues<I> {
306307
CanonicalVarValues { var_values: Default::default() }
307308
}
308309

310+
pub fn instantiate(
311+
cx: I,
312+
variables: I::CanonicalVarKinds,
313+
mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind<I>) -> I::GenericArg,
314+
) -> CanonicalVarValues<I> {
315+
// Instantiating `CanonicalVarValues` is really hot, but limited to less than
316+
// 4 most of the time. Avoid creating a `Vec` here.
317+
if variables.len() <= 4 {
318+
let mut var_values = ArrayVec::<_, 4>::new();
319+
for info in variables.iter() {
320+
var_values.push(f(&var_values, info));
321+
}
322+
CanonicalVarValues { var_values: cx.mk_args(&var_values) }
323+
} else {
324+
CanonicalVarValues::instantiate_cold(cx, variables, f)
325+
}
326+
}
327+
328+
#[cold]
329+
fn instantiate_cold(
330+
cx: I,
331+
variables: I::CanonicalVarKinds,
332+
mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind<I>) -> I::GenericArg,
333+
) -> CanonicalVarValues<I> {
334+
let mut var_values = Vec::with_capacity(variables.len());
335+
for info in variables.iter() {
336+
var_values.push(f(&var_values, info));
337+
}
338+
CanonicalVarValues { var_values: cx.mk_args(&var_values) }
339+
}
340+
309341
#[inline]
310342
pub fn len(&self) -> usize {
311343
self.var_values.len()

0 commit comments

Comments
 (0)