Skip to content

Commit a2a324b

Browse files
committed
optimize CanonicalVarValues::instantiate
1 parent 739367a commit a2a324b

File tree

6 files changed

+55
-45
lines changed

6 files changed

+55
-45
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_infer/src/infer/canonical/mod.rs

Lines changed: 5 additions & 22 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,31 +67,14 @@ 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]);
70+
let canonical_inference_vars: ty::CanonicalVarValues<TyCtxt<'_>> =
71+
CanonicalVarValues::instantiate(self.tcx, &canonical.variables, |var_values, info| {
72+
self.instantiate_canonical_var(span, info, &var_values, |ui| universes[ui])
73+
});
7274
let result = canonical.instantiate(self.tcx, &canonical_inference_vars);
7375
(result, canonical_inference_vars)
7476
}
7577

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) }
93-
}
94-
9578
/// Given the "info" about a canonical variable, creates a fresh
9679
/// variable for it. If this is an existentially quantified
9780
/// variable, then you'll get a new inference variable; if it is a

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

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -459,16 +459,17 @@ impl<'tcx> InferCtxt<'tcx> {
459459
// Create result arguments: if we found a value for a
460460
// given variable in the loop above, use that. Otherwise, use
461461
// a fresh inference variable.
462-
let mut var_values = Vec::with_capacity(query_response.variables.len());
463-
for (index, kind) in query_response.variables.iter().enumerate() {
464-
let value = if kind.universe() != ty::UniverseIndex::ROOT {
462+
let tcx = self.tcx;
463+
let variables = query_response.variables;
464+
let var_values = CanonicalVarValues::instantiate(tcx, variables, |var_values, kind| {
465+
if kind.universe() != ty::UniverseIndex::ROOT {
465466
// A variable from inside a binder of the query. While ideally these shouldn't
466467
// exist at all, we have to deal with them for now.
467468
self.instantiate_canonical_var(cause.span, kind, &var_values, |u| {
468469
universe_map[u.as_usize()]
469470
})
470471
} else if kind.is_existential() {
471-
match opt_values[BoundVar::new(index)] {
472+
match opt_values[BoundVar::new(var_values.len())] {
472473
Some(k) => k,
473474
None => self.instantiate_canonical_var(cause.span, kind, &var_values, |u| {
474475
universe_map[u.as_usize()]
@@ -477,20 +478,17 @@ impl<'tcx> InferCtxt<'tcx> {
477478
} else {
478479
// For placeholders which were already part of the input, we simply map this
479480
// universal bound variable back the placeholder of the input.
480-
opt_values[BoundVar::new(index)]
481+
opt_values[BoundVar::new(var_values.len())]
481482
.expect("expected placeholder to be unified with itself during response")
482-
};
483-
var_values.push(value);
484-
}
485-
486-
let result_args = CanonicalVarValues { var_values: self.tcx.mk_args(&var_values) };
483+
}
484+
});
487485

488486
let mut obligations = PredicateObligations::new();
489487

490488
// Carry all newly resolved opaque types to the caller's scope
491489
for &(a, b) in &query_response.value.opaque_types {
492-
let a = instantiate_value(self.tcx, &result_args, a);
493-
let b = instantiate_value(self.tcx, &result_args, b);
490+
let a = instantiate_value(self.tcx, &var_values, a);
491+
let b = instantiate_value(self.tcx, &var_values, b);
494492
debug!(?a, ?b, "constrain opaque type");
495493
// We use equate here instead of, for example, just registering the
496494
// opaque type's hidden value directly, because the hidden type may have been an inference
@@ -507,7 +505,7 @@ impl<'tcx> InferCtxt<'tcx> {
507505
);
508506
}
509507

510-
Ok(InferOk { value: result_args, obligations })
508+
Ok(InferOk { value: var_values, obligations })
511509
}
512510

513511
/// 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
@@ -365,10 +365,8 @@ where
365365
}
366366
}
367367
}
368-
369-
let mut var_values = Vec::with_capacity(response.variables.len());
370-
for (index, kind) in response.variables.iter().enumerate() {
371-
let value = if kind.universe() != ty::UniverseIndex::ROOT {
368+
CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| {
369+
if kind.universe() != ty::UniverseIndex::ROOT {
372370
// A variable from inside a binder of the query. While ideally these shouldn't
373371
// exist at all (see the FIXME at the start of this method), we have to deal with
374372
// them for now.
@@ -383,7 +381,7 @@ where
383381
// more placeholders then they should be able to. However the inference variables have
384382
// to "come from somewhere", so by equating them with the original values of the caller
385383
// later on, we pull them down into their correct universe again.
386-
if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] {
384+
if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] {
387385
v
388386
} else {
389387
delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe)
@@ -392,11 +390,8 @@ where
392390
// For placeholders which were already part of the input, we simply map this
393391
// universal bound variable back the placeholder of the input.
394392
original_values[kind.expect_placeholder_index()]
395-
};
396-
var_values.push(value)
397-
}
398-
399-
CanonicalVarValues { var_values: delegate.cx().mk_args(&var_values) }
393+
}
394+
})
400395
}
401396

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

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};
@@ -302,6 +303,37 @@ impl<I: Interner> CanonicalVarValues<I> {
302303
CanonicalVarValues { var_values: Default::default() }
303304
}
304305

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

0 commit comments

Comments
 (0)