From 29dad1a280f0f346bcbd013239ffb45b2866c2aa Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 17 May 2016 12:05:02 -0400 Subject: [PATCH] introduce a specializes cache This query is frequently used during trait selection and caching the result can be a reasonable performance win. --- src/librustc/traits/mod.rs | 1 + src/librustc/traits/specialize/mod.rs | 32 +++++++++++++++++++++++++-- src/librustc/ty/context.rs | 3 +++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 65df056fd424b..c5db2a8a7807b 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 d43d2de1f1fbc..b2d14dab9a0b0 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 { + 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 39fe744c67d01..aa50266977795 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, + 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,