about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-02-09 00:53:53 +0100
committerGitHub <noreply@github.com>2020-02-09 00:53:53 +0100
commit664d87f9b1e54421e9fa77988e92be53337821fd (patch)
treed8808088034babab5e2873d5327e90c0cedd4b67
parentcb87c958ef147f144d4b973e04e26a6bf9ddde9d (diff)
parent51b891ae2cf55ce0a265fa99e92bdb7b19244112 (diff)
downloadrust-664d87f9b1e54421e9fa77988e92be53337821fd.tar.gz
rust-664d87f9b1e54421e9fa77988e92be53337821fd.zip
Rollup merge of #68857 - Marwes:allocations, r=matthewjasper
perf: Reduce Vec allocations in normalization by passing &mut Vec

Complicates the code a bit but allocation/freeing were a few percent of the overall runtime in trait heavy code.
-rw-r--r--src/librustc/traits/mod.rs4
-rw-r--r--src/librustc/traits/project.rs81
-rw-r--r--src/librustc/traits/select.rs56
-rw-r--r--src/librustc/traits/wf.rs20
4 files changed, 100 insertions, 61 deletions
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 50068b89687..25bb54033f1 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -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;
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index fffcf66075f..a1d785cf444 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -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.
@@ -230,8 +245,24 @@ pub fn normalize_with_depth<'a, 'b, '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",
@@ -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,
 }
 
@@ -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 {
@@ -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,
@@ -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
     );
 
@@ -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;
@@ -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:
@@ -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
             );
 
@@ -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);
@@ -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 }
@@ -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![] };
@@ -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
@@ -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
             );
 
@@ -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());
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index e4ef68c167f..26f2a4ddb38 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -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;
@@ -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(
@@ -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;
@@ -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
@@ -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
     }
 }
 
diff --git a/src/librustc/traits/wf.rs b/src/librustc/traits/wf.rs
index 9fa3c874779..fbcb77a4031 100644
--- a/src/librustc/traits/wf.rs
+++ b/src/librustc/traits/wf.rs
@@ -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
@@ -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)
@@ -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`.