Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: Reduce Vec allocations in normalization by passing &mut Vec #68857

Merged
merged 1 commit into from
Feb 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/librustc/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::ObjectSafetyViolation;
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
pub use self::project::MismatchedProjectionTypes;
pub use self::project::{normalize, normalize_projection_type, poly_project_and_unify_type};
pub use self::project::{
normalize, normalize_projection_type, normalize_to, poly_project_and_unify_type,
};
pub use self::project::{Normalized, ProjectionCache, ProjectionCacheSnapshot};
pub use self::select::{IntercrateAmbiguityCause, SelectionContext};
pub use self::specialize::find_associated_item;
Expand Down
81 changes: 59 additions & 22 deletions src/librustc/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,22 @@ pub fn normalize<'a, 'b, 'tcx, T>(
where
T: TypeFoldable<'tcx>,
{
normalize_with_depth(selcx, param_env, cause, 0, value)
let mut obligations = Vec::new();
let value = normalize_to(selcx, param_env, cause, value, &mut obligations);
Normalized { value, obligations }
}

pub fn normalize_to<'a, 'b, 'tcx, T>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
value: &T,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> T
where
T: TypeFoldable<'tcx>,
{
normalize_with_depth_to(selcx, param_env, cause, 0, value, obligations)
}

/// As `normalize`, but with a custom depth.
Expand All @@ -227,11 +242,27 @@ pub fn normalize_with_depth<'a, 'b, 'tcx, T>(
depth: usize,
value: &T,
) -> Normalized<'tcx, T>
where
T: TypeFoldable<'tcx>,
{
let mut obligations = Vec::new();
let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations);
Normalized { value, obligations }
}

pub fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
value: &T,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("normalize_with_depth(depth={}, value={:?})", depth, value);
let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth);
let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations);
let result = normalizer.fold(value);
debug!(
"normalize_with_depth: depth={} result={:?} with {} obligations",
Expand All @@ -240,14 +271,14 @@ where
normalizer.obligations.len()
);
debug!("normalize_with_depth: depth={} obligations={:?}", depth, normalizer.obligations);
Normalized { value: result, obligations: normalizer.obligations }
result
}

struct AssocTypeNormalizer<'a, 'b, 'tcx> {
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
depth: usize,
}

Expand All @@ -257,8 +288,9 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
AssocTypeNormalizer { selcx, param_env, cause, obligations: vec![], depth }
AssocTypeNormalizer { selcx, param_env, cause, obligations, depth }
}

fn fold<T: TypeFoldable<'tcx>>(&mut self, value: &T) -> T {
Expand Down Expand Up @@ -343,7 +375,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
);
debug!(
"AssocTypeNormalizer: depth={} normalized {:?} to {:?}, \
now with {} obligations",
now with {} obligations",
self.depth,
ty,
normalized_ty,
Expand Down Expand Up @@ -441,8 +473,8 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(

debug!(
"opt_normalize_projection_type(\
projection_ty={:?}, \
depth={})",
projection_ty={:?}, \
depth={})",
projection_ty, depth
);

Expand All @@ -469,7 +501,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
// changes
debug!(
"opt_normalize_projection_type: \
found cache entry: ambiguous"
found cache entry: ambiguous"
);
if !projection_ty.has_closure_types() {
return None;
Expand Down Expand Up @@ -498,7 +530,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(

debug!(
"opt_normalize_projection_type: \
found cache entry: in-progress"
found cache entry: in-progress"
);

// But for now, let's classify this as an overflow:
Expand All @@ -521,7 +553,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
// evaluations can causes ICEs (e.g., #43132).
debug!(
"opt_normalize_projection_type: \
found normalized ty `{:?}`",
found normalized ty `{:?}`",
ty
);

Expand All @@ -546,7 +578,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
Err(ProjectionCacheEntry::Error) => {
debug!(
"opt_normalize_projection_type: \
found error"
found error"
);
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
obligations.extend(result.obligations);
Expand All @@ -567,23 +599,28 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(

debug!(
"opt_normalize_projection_type: \
projected_ty={:?} \
depth={} \
projected_obligations={:?}",
projected_ty={:?} \
depth={} \
projected_obligations={:?}",
projected_ty, depth, projected_obligations
);

let result = if projected_ty.has_projections() {
let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth + 1);
let mut normalizer = AssocTypeNormalizer::new(
selcx,
param_env,
cause,
depth + 1,
&mut projected_obligations,
);
let normalized_ty = normalizer.fold(&projected_ty);

debug!(
"opt_normalize_projection_type: \
normalized_ty={:?} depth={}",
normalized_ty={:?} depth={}",
normalized_ty, depth
);

projected_obligations.extend(normalizer.obligations);
Normalized { value: normalized_ty, obligations: projected_obligations }
} else {
Normalized { value: projected_ty, obligations: projected_obligations }
Expand All @@ -597,7 +634,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
Ok(ProjectedTy::NoProgress(projected_ty)) => {
debug!(
"opt_normalize_projection_type: \
projected_ty={:?} no progress",
projected_ty={:?} no progress",
projected_ty
);
let result = Normalized { value: projected_ty, obligations: vec![] };
Expand All @@ -608,7 +645,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
Err(ProjectionTyError::TooManyCandidates) => {
debug!(
"opt_normalize_projection_type: \
too many candidates"
too many candidates"
);
infcx.projection_cache.borrow_mut().ambiguous(cache_key);
None
Expand Down Expand Up @@ -930,7 +967,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx, I>(

debug!(
"assemble_candidates_from_predicates: candidate={:?} \
is_match={} same_def_id={}",
is_match={} same_def_id={}",
data, is_match, same_def_id
);

Expand Down Expand Up @@ -1192,7 +1229,7 @@ fn confirm_object_candidate<'cx, 'tcx>(
None => {
debug!(
"confirm_object_candidate: no env-predicate \
found in object type `{:?}`; ill-formed",
found in object type `{:?}`; ill-formed",
object_ty
);
return Progress::error(selcx.tcx());
Expand Down
56 changes: 28 additions & 28 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use self::SelectionCandidate::*;

use super::coherence::{self, Conflict};
use super::project;
use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
use super::project::{
normalize_with_depth, normalize_with_depth_to, Normalized, ProjectionCacheKey,
};
use super::util;
use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def};
use super::wf;
Expand Down Expand Up @@ -1019,7 +1021,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if let Some(value) = value {
debug!(
"filter_negative_and_reservation_impls: \
reservation impl ambiguity on {:?}",
reservation impl ambiguity on {:?}",
def_id
);
intercrate_ambiguity_clauses.push(
Expand Down Expand Up @@ -1317,7 +1319,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if !self.can_cache_candidate(&candidate) {
debug!(
"insert_candidate_cache(trait_ref={:?}, candidate={:?} -\
candidate is not cacheable",
candidate is not cacheable",
trait_ref, candidate
);
return;
Expand Down Expand Up @@ -3484,25 +3486,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// that order.
let predicates = tcx.predicates_of(def_id);
assert_eq!(predicates.parent, None);
let mut predicates: Vec<_> = predicates
.predicates
.iter()
.flat_map(|(predicate, _)| {
let predicate = normalize_with_depth(
self,
param_env,
cause.clone(),
recursion_depth,
&predicate.subst(tcx, substs),
);
predicate.obligations.into_iter().chain(Some(Obligation {
cause: cause.clone(),
recursion_depth,
param_env,
predicate: predicate.value,
}))
})
.collect();
let mut obligations = Vec::new();
for (predicate, _) in predicates.predicates {
let predicate = normalize_with_depth_to(
self,
param_env,
cause.clone(),
recursion_depth,
&predicate.subst(tcx, substs),
&mut obligations,
);
obligations.push(Obligation {
cause: cause.clone(),
recursion_depth,
param_env,
predicate,
});
}

// We are performing deduplication here to avoid exponential blowups
// (#38528) from happening, but the real cause of the duplication is
Expand All @@ -3513,20 +3513,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// This code is hot enough that it's worth avoiding the allocation
// required for the FxHashSet when possible. Special-casing lengths 0,
// 1 and 2 covers roughly 75-80% of the cases.
if predicates.len() <= 1 {
if obligations.len() <= 1 {
// No possibility of duplicates.
} else if predicates.len() == 2 {
} else if obligations.len() == 2 {
// Only two elements. Drop the second if they are equal.
if predicates[0] == predicates[1] {
predicates.truncate(1);
if obligations[0] == obligations[1] {
obligations.truncate(1);
}
} else {
// Three or more elements. Use a general deduplication process.
let mut seen = FxHashSet::default();
predicates.retain(|i| seen.insert(i.clone()));
obligations.retain(|i| seen.insert(i.clone()));
}

predicates
obligations
}
}

Expand Down
20 changes: 10 additions & 10 deletions src/librustc/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_span::symbol::{kw, Ident};
use rustc_span::Span;
use std::iter::once;

/// Returns the set of obligations needed to make `ty` well-formed.
/// If `ty` contains unresolved inference variables, this may include
Expand All @@ -26,6 +25,7 @@ pub fn obligations<'a, 'tcx>(
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
if wf.compute(ty) {
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);

let result = wf.normalize();
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
Some(result)
Expand Down Expand Up @@ -143,15 +143,15 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let cause = self.cause(traits::MiscObligation);
let infcx = &mut self.infcx;
let param_env = self.param_env;
self.out
.iter()
.inspect(|pred| assert!(!pred.has_escaping_bound_vars()))
.flat_map(|pred| {
let mut selcx = traits::SelectionContext::new(infcx);
let pred = traits::normalize(&mut selcx, param_env, cause.clone(), pred);
once(pred.value).chain(pred.obligations)
})
.collect()
let mut obligations = Vec::new();
self.out.iter().inspect(|pred| assert!(!pred.has_escaping_bound_vars())).for_each(|pred| {
let mut selcx = traits::SelectionContext::new(infcx);
let i = obligations.len();
let value =
traits::normalize_to(&mut selcx, param_env, cause.clone(), pred, &mut obligations);
obligations.insert(i, value);
});
obligations
}

/// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
Expand Down