about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-05-17 12:05:02 -0400
committerNiko Matsakis <niko@alum.mit.edu>2016-05-17 15:52:03 -0400
commit29dad1a280f0f346bcbd013239ffb45b2866c2aa (patch)
tree2b8370b9128dcfd2c62ea2da36252d5cc0336938
parent5d12502d3a54aaad5e1af48111ecc81a1c2cbf2b (diff)
downloadrust-29dad1a280f0f346bcbd013239ffb45b2866c2aa.tar.gz
rust-29dad1a280f0f346bcbd013239ffb45b2866c2aa.zip
introduce a specializes cache
This query is frequently used during trait selection and caching the
result can be a reasonable performance win.
-rw-r--r--src/librustc/traits/mod.rs1
-rw-r--r--src/librustc/traits/specialize/mod.rs32
-rw-r--r--src/librustc/ty/context.rs3
3 files changed, 34 insertions, 2 deletions
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 65df056fd42..c5db2a8a780 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -38,6 +38,7 @@ pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
 pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};
 pub use self::select::{MethodMatchedData}; // intentionally don't export variants
 pub use self::specialize::{OverlapError, specialization_graph, specializes, translate_substs};
+pub use self::specialize::{SpecializesCache};
 pub use self::util::elaborate_predicates;
 pub use self::util::supertraits;
 pub use self::util::Supertraits;
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index d43d2de1f1f..b2d14dab9a0 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -20,6 +20,7 @@
 use super::{SelectionContext, FulfillmentContext};
 use super::util::{fresh_type_vars_for_impl, impl_trait_ref_and_oblig};
 
+use rustc_data_structures::fnv::FnvHashMap;
 use hir::def_id::DefId;
 use infer::{InferCtxt, TypeOrigin};
 use middle::region;
@@ -111,6 +112,10 @@ pub fn translate_substs<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
 pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              impl1_def_id: DefId,
                              impl2_def_id: DefId) -> bool {
+    if let Some(r) = tcx.specializes_cache.borrow().check(impl1_def_id, impl2_def_id) {
+        return r;
+    }
+
     // The feature gate should prevent introducing new specializations, but not
     // taking advantage of upstream ones.
     if !tcx.sess.features.borrow().specialization &&
@@ -146,7 +151,7 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              .unwrap()
                              .subst(tcx, &penv.free_substs);
 
-    tcx.normalizing_infer_ctxt(ProjectionMode::Topmost).enter(|mut infcx| {
+    let result = tcx.normalizing_infer_ctxt(ProjectionMode::Topmost).enter(|mut infcx| {
         // Normalize the trait reference, adding any obligations
         // that arise into the impl1 assumptions.
         let Normalized { value: impl1_trait_ref, obligations: normalization_obligations } = {
@@ -167,7 +172,10 @@ pub fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
         // Attempt to prove that impl2 applies, given all of the above.
         fulfill_implication(&infcx, impl1_trait_ref, impl2_def_id).is_ok()
-    })
+    });
+
+    tcx.specializes_cache.borrow_mut().insert(impl1_def_id, impl2_def_id, result);
+    result
 }
 
 /// Attempt to fulfill all obligations of `target_impl` after unification with
@@ -225,3 +233,23 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
         }
     })
 }
+
+pub struct SpecializesCache {
+    map: FnvHashMap<(DefId, DefId), bool>
+}
+
+impl SpecializesCache {
+    pub fn new() -> Self {
+        SpecializesCache {
+            map: FnvHashMap()
+        }
+    }
+
+    pub fn check(&self, a: DefId, b: DefId) -> Option<bool> {
+        self.map.get(&(a, b)).cloned()
+    }
+
+    pub fn insert(&mut self, a: DefId, b: DefId, result: bool) {
+        self.map.insert((a, b), result);
+    }
+}
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 39fe744c67d..aa502669777 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -291,6 +291,8 @@ impl<'a, 'gcx, 'tcx> Deref for TyCtxt<'a, 'gcx, 'tcx> {
 pub struct GlobalCtxt<'tcx> {
     global_interners: CtxtInterners<'tcx>,
 
+    pub specializes_cache: RefCell<traits::SpecializesCache>,
+
     pub dep_graph: DepGraph,
 
     /// Common types, pre-interned for your convenience.
@@ -637,6 +639,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         let dep_graph = map.dep_graph.clone();
         let fulfilled_predicates = traits::GlobalFulfilledPredicates::new(dep_graph.clone());
         tls::enter_global(GlobalCtxt {
+            specializes_cache: RefCell::new(traits::SpecializesCache::new()),
             global_interners: interners,
             dep_graph: dep_graph.clone(),
             types: common_types,