Skip to content

Commit 576b7b6

Browse files
committed
Auto merge of #69808 - cjgillot:vtbl, r=<try>
Avoid duplicating code for each query There are at the moment roughly 170 queries in librustc. The way `ty::query` is structured, a lot of code is duplicated for each query. I suspect this to be responsible for a part of librustc'c compile time. The first part of this PR reduces the amount of code generic on the query, replacing it by code generic on the key-value types. I can split it out if needed. In a second part, the non-inlined methods in the `QueryAccessors` and `QueryDescription` traits are made into a virtual dispatch table. This allows to reduce even more the number of generated functions. This allows to save 1.5s on check build, and 10% on the size of the librustc.rlib. (Attributed roughly half and half). My computer is not good enough to measure properly compiling time. I have no idea of the effect on performance. A perf run may be required. cc #65031
2 parents 823ff8c + f12bdf2 commit 576b7b6

File tree

7 files changed

+394
-259
lines changed

7 files changed

+394
-259
lines changed

src/librustc/ty/query/caches.rs

+35-25
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,93 @@
11
use crate::dep_graph::DepNodeIndex;
2-
use crate::ty::query::config::QueryAccessors;
32
use crate::ty::query::plumbing::{QueryLookup, QueryState, QueryStateShard};
4-
use crate::ty::TyCtxt;
53

64
use rustc_data_structures::fx::FxHashMap;
75
use rustc_data_structures::sharded::Sharded;
86
use std::default::Default;
97
use std::hash::Hash;
8+
use std::marker::PhantomData;
109

1110
pub(crate) trait CacheSelector<K, V> {
12-
type Cache: QueryCache<K, V>;
11+
type Cache: QueryCache<Key = K, Value = V>;
1312
}
1413

15-
pub(crate) trait QueryCache<K, V>: Default {
14+
pub(crate) trait QueryCache: Default {
15+
type Key;
16+
type Value;
1617
type Sharded: Default;
1718

1819
/// Checks if the query is already computed and in the cache.
1920
/// It returns the shard index and a lock guard to the shard,
2021
/// which will be used if the query is not in the cache and we need
2122
/// to compute it.
22-
fn lookup<'tcx, R, GetCache, OnHit, OnMiss, Q>(
23+
fn lookup<'tcx, R, GetCache, OnHit, OnMiss>(
2324
&self,
24-
state: &'tcx QueryState<'tcx, Q>,
25+
state: &'tcx QueryState<'tcx, Self>,
2526
get_cache: GetCache,
26-
key: K,
27+
key: Self::Key,
2728
// `on_hit` can be called while holding a lock to the query state shard.
2829
on_hit: OnHit,
2930
on_miss: OnMiss,
3031
) -> R
3132
where
32-
Q: QueryAccessors<'tcx>,
33-
GetCache: for<'a> Fn(&'a mut QueryStateShard<'tcx, Q>) -> &'a mut Self::Sharded,
34-
OnHit: FnOnce(&V, DepNodeIndex) -> R,
35-
OnMiss: FnOnce(K, QueryLookup<'tcx, Q>) -> R;
33+
GetCache: for<'a> Fn(
34+
&'a mut QueryStateShard<'tcx, Self::Key, Self::Sharded>,
35+
) -> &'a mut Self::Sharded,
36+
OnHit: FnOnce(&Self::Value, DepNodeIndex) -> R,
37+
OnMiss: FnOnce(Self::Key, QueryLookup<'tcx, Self::Key, Self::Sharded>) -> R;
3638

3739
fn complete(
3840
&self,
39-
tcx: TyCtxt<'tcx>,
4041
lock_sharded_storage: &mut Self::Sharded,
41-
key: K,
42-
value: V,
42+
key: Self::Key,
43+
value: Self::Value,
4344
index: DepNodeIndex,
4445
);
4546

4647
fn iter<R, L>(
4748
&self,
4849
shards: &Sharded<L>,
4950
get_shard: impl Fn(&mut L) -> &mut Self::Sharded,
50-
f: impl for<'a> FnOnce(Box<dyn Iterator<Item = (&'a K, &'a V, DepNodeIndex)> + 'a>) -> R,
51+
f: impl for<'a> FnOnce(
52+
Box<dyn Iterator<Item = (&'a Self::Key, &'a Self::Value, DepNodeIndex)> + 'a>,
53+
) -> R,
5154
) -> R;
5255
}
5356

5457
pub struct DefaultCacheSelector;
5558

5659
impl<K: Eq + Hash, V: Clone> CacheSelector<K, V> for DefaultCacheSelector {
57-
type Cache = DefaultCache;
60+
type Cache = DefaultCache<K, V>;
5861
}
5962

60-
#[derive(Default)]
61-
pub struct DefaultCache;
63+
pub struct DefaultCache<K, V>(PhantomData<(K, V)>);
6264

63-
impl<K: Eq + Hash, V: Clone> QueryCache<K, V> for DefaultCache {
65+
impl<K, V> Default for DefaultCache<K, V> {
66+
fn default() -> Self {
67+
DefaultCache(PhantomData)
68+
}
69+
}
70+
71+
impl<K: Eq + Hash, V: Clone> QueryCache for DefaultCache<K, V> {
72+
type Key = K;
73+
type Value = V;
6474
type Sharded = FxHashMap<K, (V, DepNodeIndex)>;
6575

6676
#[inline(always)]
67-
fn lookup<'tcx, R, GetCache, OnHit, OnMiss, Q>(
77+
fn lookup<'tcx, R, GetCache, OnHit, OnMiss>(
6878
&self,
69-
state: &'tcx QueryState<'tcx, Q>,
79+
state: &'tcx QueryState<'tcx, Self>,
7080
get_cache: GetCache,
7181
key: K,
82+
// `on_hit` can be called while holding a lock to the query state shard.
7283
on_hit: OnHit,
7384
on_miss: OnMiss,
7485
) -> R
7586
where
76-
Q: QueryAccessors<'tcx>,
77-
GetCache: for<'a> Fn(&'a mut QueryStateShard<'tcx, Q>) -> &'a mut Self::Sharded,
87+
GetCache:
88+
for<'a> Fn(&'a mut QueryStateShard<'tcx, K, Self::Sharded>) -> &'a mut Self::Sharded,
7889
OnHit: FnOnce(&V, DepNodeIndex) -> R,
79-
OnMiss: FnOnce(K, QueryLookup<'tcx, Q>) -> R,
90+
OnMiss: FnOnce(K, QueryLookup<'tcx, K, Self::Sharded>) -> R,
8091
{
8192
let mut lookup = state.get_lookup(&key);
8293
let lock = &mut *lookup.lock;
@@ -89,7 +100,6 @@ impl<K: Eq + Hash, V: Clone> QueryCache<K, V> for DefaultCache {
89100
#[inline]
90101
fn complete(
91102
&self,
92-
_: TyCtxt<'tcx>,
93103
lock_sharded_storage: &mut Self::Sharded,
94104
key: K,
95105
value: V,

src/librustc/ty/query/config.rs

+63-12
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::dep_graph::{DepKind, DepNode};
33
use crate::ty::query::caches::QueryCache;
44
use crate::ty::query::plumbing::CycleError;
55
use crate::ty::query::queries;
6-
use crate::ty::query::{Query, QueryState};
6+
use crate::ty::query::QueryState;
77
use crate::ty::TyCtxt;
88
use rustc_data_structures::profiling::ProfileCategory;
99
use rustc_hir::def_id::{CrateNum, DefId};
@@ -26,23 +26,65 @@ pub trait QueryConfig<'tcx> {
2626
type Value: Clone;
2727
}
2828

29+
pub(crate) struct QueryVtable<'tcx, K, V> {
30+
pub anon: bool,
31+
pub dep_kind: DepKind,
32+
pub eval_always: bool,
33+
pub name: &'static str,
34+
35+
// Don't use this method to compute query results, instead use the methods on TyCtxt
36+
pub compute: fn(TyCtxt<'tcx>, K) -> V,
37+
38+
pub hash_result: fn(&mut StableHashingContext<'_>, &V) -> Option<Fingerprint>,
39+
pub handle_cycle_error: fn(TyCtxt<'tcx>, CycleError<'tcx>) -> V,
40+
pub cache_on_disk: fn(TyCtxt<'tcx>, K, Option<&V>) -> bool,
41+
pub try_load_from_disk: fn(TyCtxt<'tcx>, SerializedDepNodeIndex) -> Option<V>,
42+
}
43+
44+
impl<'tcx, K, V> QueryVtable<'tcx, K, V> {
45+
pub(crate) fn compute(&self, tcx: TyCtxt<'tcx>, key: K) -> V {
46+
(self.compute)(tcx, key)
47+
}
48+
49+
pub(crate) fn hash_result(
50+
&self,
51+
hcx: &mut StableHashingContext<'_>,
52+
value: &V,
53+
) -> Option<Fingerprint> {
54+
(self.hash_result)(hcx, value)
55+
}
56+
57+
pub(crate) fn handle_cycle_error(&self, tcx: TyCtxt<'tcx>, error: CycleError<'tcx>) -> V {
58+
(self.handle_cycle_error)(tcx, error)
59+
}
60+
61+
pub(crate) fn cache_on_disk(&self, tcx: TyCtxt<'tcx>, key: K, value: Option<&V>) -> bool {
62+
(self.cache_on_disk)(tcx, key, value)
63+
}
64+
65+
pub(crate) fn try_load_from_disk(
66+
&self,
67+
tcx: TyCtxt<'tcx>,
68+
index: SerializedDepNodeIndex,
69+
) -> Option<V> {
70+
(self.try_load_from_disk)(tcx, index)
71+
}
72+
}
73+
2974
pub(crate) trait QueryAccessors<'tcx>: QueryConfig<'tcx> {
3075
const ANON: bool;
3176
const EVAL_ALWAYS: bool;
77+
const DEP_KIND: DepKind;
3278

33-
type Cache: QueryCache<Self::Key, Self::Value>;
34-
35-
fn query(key: Self::Key) -> Query<'tcx>;
79+
type Cache: QueryCache<Key = Self::Key, Value = Self::Value>;
3680

3781
// Don't use this method to access query results, instead use the methods on TyCtxt
38-
fn query_state<'a>(tcx: TyCtxt<'tcx>) -> &'a QueryState<'tcx, Self>;
82+
fn query_state<'a>(tcx: TyCtxt<'tcx>) -> &'a QueryState<'tcx, Self::Cache>;
3983

4084
fn to_dep_node(tcx: TyCtxt<'tcx>, key: &Self::Key) -> DepNode;
4185

42-
fn dep_kind() -> DepKind;
43-
4486
// Don't use this method to compute query results, instead use the methods on TyCtxt
45-
fn compute(tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value;
87+
const COMPUTE_FN: fn(TyCtxt<'tcx>, Self::Key) -> Self::Value;
4688

4789
fn hash_result(hcx: &mut StableHashingContext<'_>, result: &Self::Value)
4890
-> Option<Fingerprint>;
@@ -61,12 +103,21 @@ pub(crate) trait QueryDescription<'tcx>: QueryAccessors<'tcx> {
61103
fn try_load_from_disk(_: TyCtxt<'tcx>, _: SerializedDepNodeIndex) -> Option<Self::Value> {
62104
bug!("QueryDescription::load_from_disk() called for an unsupported query.")
63105
}
106+
107+
const VTABLE: QueryVtable<'tcx, Self::Key, Self::Value> = QueryVtable {
108+
anon: Self::ANON,
109+
dep_kind: Self::DEP_KIND,
110+
eval_always: Self::EVAL_ALWAYS,
111+
name: Self::NAME,
112+
compute: Self::COMPUTE_FN,
113+
hash_result: Self::hash_result,
114+
handle_cycle_error: Self::handle_cycle_error,
115+
cache_on_disk: Self::cache_on_disk,
116+
try_load_from_disk: Self::try_load_from_disk,
117+
};
64118
}
65119

66-
impl<'tcx, M: QueryAccessors<'tcx, Key = DefId>> QueryDescription<'tcx> for M
67-
where
68-
<M as QueryAccessors<'tcx>>::Cache: QueryCache<DefId, <M as QueryConfig<'tcx>>::Value>,
69-
{
120+
impl<'tcx, M: QueryAccessors<'tcx, Key = DefId>> QueryDescription<'tcx> for M {
70121
default fn describe(tcx: TyCtxt<'_>, def_id: DefId) -> Cow<'static, str> {
71122
if !tcx.sess.verbose() {
72123
format!("processing `{}`", tcx.def_path_str(def_id)).into()

src/librustc/ty/query/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ use rustc_attr as attr;
5454
use rustc_span::symbol::Symbol;
5555
use rustc_span::{Span, DUMMY_SP};
5656
use std::borrow::Cow;
57-
use std::convert::TryFrom;
5857
use std::ops::Deref;
5958
use std::sync::Arc;
6059

src/librustc/ty/query/on_disk_cache.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::mir::{self, interpret};
66
use crate::session::{CrateDisambiguator, Session};
77
use crate::ty::codec::{self as ty_codec, TyDecoder, TyEncoder};
88
use crate::ty::context::TyCtxt;
9+
use crate::ty::query::config::{QueryAccessors, QueryDescription, QueryVtable};
910
use crate::ty::{self, Ty};
1011
use rustc_ast::ast::Ident;
1112
use rustc_data_structures::fx::FxHashMap;
@@ -205,8 +206,10 @@ impl<'sess> OnDiskCache<'sess> {
205206
macro_rules! encode_queries {
206207
($($query:ident,)*) => {
207208
$(
208-
encode_query_results::<ty::query::queries::$query<'_>, _>(
209+
encode_query_results(
209210
tcx,
211+
ty::query::queries::$query::query_state(tcx),
212+
&ty::query::queries::$query::VTABLE,
210213
enc,
211214
qri
212215
)?;
@@ -1022,26 +1025,27 @@ impl<'a> SpecializedDecoder<IntEncodedWithFixedSize> for opaque::Decoder<'a> {
10221025
}
10231026
}
10241027

1025-
fn encode_query_results<'a, 'tcx, Q, E>(
1028+
fn encode_query_results<'a, 'tcx, C, E>(
10261029
tcx: TyCtxt<'tcx>,
1030+
state: &'a super::plumbing::QueryState<'tcx, C>,
1031+
query: &QueryVtable<'tcx, C::Key, C::Value>,
10271032
encoder: &mut CacheEncoder<'a, 'tcx, E>,
10281033
query_result_index: &mut EncodedQueryResultIndex,
10291034
) -> Result<(), E::Error>
10301035
where
1031-
Q: super::config::QueryDescription<'tcx, Value: Encodable>,
1036+
C: super::caches::QueryCache,
10321037
E: 'a + TyEncoder,
1038+
C::Key: Clone,
1039+
C::Value: Encodable,
10331040
{
1034-
let _timer = tcx
1035-
.sess
1036-
.prof
1037-
.extra_verbose_generic_activity("encode_query_results_for", ::std::any::type_name::<Q>());
1041+
let _timer =
1042+
tcx.sess.prof.extra_verbose_generic_activity("encode_query_results_for", query.name);
10381043

1039-
let state = Q::query_state(tcx);
10401044
assert!(state.all_inactive());
10411045

10421046
state.iter_results(|results| {
10431047
for (key, value, dep_node) in results {
1044-
if Q::cache_on_disk(tcx, key.clone(), Some(&value)) {
1048+
if query.cache_on_disk(tcx, key.clone(), Some(&value)) {
10451049
let dep_node = SerializedDepNodeIndex::new(dep_node.index());
10461050

10471051
// Record position of the cache entry.

0 commit comments

Comments
 (0)