diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_query_impl/Cargo.toml | 4 | ||||
-rw-r--r-- | compiler/rustc_query_impl/src/keys.rs | 42 | ||||
-rw-r--r-- | compiler/rustc_query_impl/src/lib.rs | 23 | ||||
-rw-r--r-- | compiler/rustc_query_impl/src/on_disk_cache.rs | 89 | ||||
-rw-r--r-- | compiler/rustc_query_impl/src/plumbing.rs | 561 | ||||
-rw-r--r-- | compiler/rustc_query_impl/src/profiling_support.rs | 25 | ||||
-rw-r--r-- | compiler/rustc_query_impl/src/values.rs | 45 |
7 files changed, 499 insertions, 290 deletions
diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index 5673bb83b..e7f12caaf 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -8,7 +8,6 @@ doctest = false [dependencies] measureme = "10.0.0" -rustc-rayon-core = { version = "0.4.0", optional = true } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -17,9 +16,12 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } +rustc-rayon-core = { version = "0.4.0", optional = true } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +thin-vec = "0.2.8" tracing = "0.1" [features] diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs index 49175e97f..8be2e2be8 100644 --- a/compiler/rustc_query_impl/src/keys.rs +++ b/compiler/rustc_query_impl/src/keys.rs @@ -1,6 +1,7 @@ //! Defines the set of legal keys that can be used in queries. use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use rustc_hir::hir_id::{HirId, OwnerId}; use rustc_middle::infer::canonical::Canonical; use rustc_middle::mir; use rustc_middle::traits; @@ -26,6 +27,10 @@ pub trait Key { fn key_as_def_id(&self) -> Option<DefId> { None } + + fn ty_adt_id(&self) -> Option<DefId> { + None + } } impl Key for () { @@ -104,6 +109,19 @@ impl Key for CrateNum { } } +impl Key for OwnerId { + #[inline(always)] + fn query_crate_is_local(&self) -> bool { + true + } + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + self.to_def_id().default_span(tcx) + } + fn key_as_def_id(&self) -> Option<DefId> { + Some(self.to_def_id()) + } +} + impl Key for LocalDefId { #[inline(always)] fn query_crate_is_local(&self) -> bool { @@ -275,7 +293,7 @@ impl<'tcx> Key for (DefId, SubstsRef<'tcx>) { } } -impl<'tcx> Key for (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>) { +impl<'tcx> Key for (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>) { #[inline(always)] fn query_crate_is_local(&self) -> bool { (self.0).def.did.krate == LOCAL_CRATE @@ -393,6 +411,12 @@ impl<'tcx> Key for Ty<'tcx> { fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } + fn ty_adt_id(&self) -> Option<DefId> { + match self.kind() { + ty::Adt(adt, _) => Some(adt.did()), + _ => None, + } + } } impl<'tcx> Key for TyAndLayout<'tcx> { @@ -543,3 +567,19 @@ impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) { DUMMY_SP } } + +impl Key for HirId { + #[inline(always)] + fn query_crate_is_local(&self) -> bool { + true + } + + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + tcx.hir().span(*self) + } + + #[inline(always)] + fn key_as_def_id(&self) -> Option<DefId> { + None + } +} diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index eda61df77..11d4c97e7 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -1,26 +1,28 @@ //! Support for serializing the dep-graph and reloading it. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +// this shouldn't be necessary, but the check for `&mut _` is too naive and denies returning a function pointer that takes a mut ref +#![feature(const_mut_refs)] #![feature(min_specialization)] #![feature(never_type)] #![feature(once_cell)] #![feature(rustc_attrs)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate rustc_macros; #[macro_use] extern crate rustc_middle; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; -use rustc_middle::dep_graph::{self, DepKindStruct, SerializedDepNodeIndex}; +use rustc_middle::dep_graph::{self, DepKindStruct}; use rustc_middle::ty::query::{query_keys, query_storage, query_stored, query_values}; use rustc_middle::ty::query::{ExternProviders, Providers, QueryEngine}; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::def_id::{LocalDefId, LOCAL_CRATE}; +use rustc_middle::ty::TyCtxt; use rustc_span::Span; #[macro_use] @@ -33,9 +35,6 @@ pub use rustc_query_system::query::{deadlock, QueryContext}; mod keys; use keys::Key; -mod values; -use self::values::Value; - pub use rustc_query_system::query::QueryConfig; pub(crate) use rustc_query_system::query::{QueryDescription, QueryVTable}; @@ -45,15 +44,7 @@ pub use on_disk_cache::OnDiskCache; mod profiling_support; pub use self::profiling_support::alloc_self_profile_query_strings; -fn describe_as_module(def_id: LocalDefId, tcx: TyCtxt<'_>) -> String { - if def_id.is_top_level_module() { - "top-level module".to_string() - } else { - format!("module `{}`", tcx.def_path_str(def_id.to_def_id())) - } -} - -rustc_query_append! { [define_queries!][<'tcx>] } +rustc_query_append! { define_queries! } impl<'tcx> Queries<'tcx> { // Force codegen in the dyn-trait transformation in this crate. diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 56fd90c98..9000f81d9 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -1,8 +1,9 @@ use crate::QueryCtxt; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, RwLock}; use rustc_data_structures::unhash::UnhashMap; +use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, StableCrateId, LOCAL_CRATE}; use rustc_hir::definitions::DefPathHash; use rustc_index::vec::{Idx, IndexVec}; @@ -22,8 +23,9 @@ use rustc_span::hygiene::{ ExpnId, HygieneDecodeContext, HygieneEncodeContext, SyntaxContext, SyntaxContextData, }; use rustc_span::source_map::{SourceMap, StableSourceFileId}; -use rustc_span::CachingSourceMapView; use rustc_span::{BytePos, ExpnData, ExpnHash, Pos, SourceFile, Span}; +use rustc_span::{CachingSourceMapView, Symbol}; +use std::collections::hash_map::Entry; use std::io; use std::mem; @@ -38,6 +40,11 @@ const TAG_RELATIVE_SPAN: u8 = 2; const TAG_SYNTAX_CONTEXT: u8 = 0; const TAG_EXPN_DATA: u8 = 1; +// Tags for encoding Symbol's +const SYMBOL_STR: u8 = 0; +const SYMBOL_OFFSET: u8 = 1; +const SYMBOL_PREINTERNED: u8 = 2; + /// Provides an interface to incremental compilation data cached from the /// previous compilation session. This data will eventually include the results /// of a few selected queries (like `typeck` and `mir_optimized`) and @@ -112,12 +119,11 @@ pub type EncodedDepNodeIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; struct SourceFileIndex(u32); #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Encodable, Decodable)] -pub struct AbsoluteBytePos(u32); +pub struct AbsoluteBytePos(u64); impl AbsoluteBytePos { fn new(pos: usize) -> AbsoluteBytePos { - debug_assert!(pos <= u32::MAX as usize); - AbsoluteBytePos(pos as u32) + AbsoluteBytePos(pos.try_into().expect("Incremental cache file size overflowed u64.")) } fn to_usize(self) -> usize { @@ -254,6 +260,7 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> { source_map: CachingSourceMapView::new(tcx.sess.source_map()), file_to_file_index, hygiene_context: &hygiene_encode_context, + symbol_table: Default::default(), }; // Encode query results. @@ -714,6 +721,40 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for Span { } } +// copy&paste impl from rustc_metadata +impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for Symbol { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + let tag = d.read_u8(); + + match tag { + SYMBOL_STR => { + let s = d.read_str(); + Symbol::intern(s) + } + SYMBOL_OFFSET => { + // read str offset + let pos = d.read_usize(); + let old_pos = d.opaque.position(); + + // move to str ofset and read + d.opaque.set_position(pos); + let s = d.read_str(); + let sym = Symbol::intern(s); + + // restore position + d.opaque.set_position(old_pos); + + sym + } + SYMBOL_PREINTERNED => { + let symbol_index = d.read_u32(); + Symbol::new_from_decoded(symbol_index) + } + _ => unreachable!(), + } + } +} + impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for CrateNum { fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { let stable_id = StableCrateId::decode(d); @@ -751,7 +792,13 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for DefId { } } -impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx FxHashSet<LocalDefId> { +impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx UnordSet<LocalDefId> { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + +impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx FxHashMap<DefId, Ty<'tcx>> { fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { RefDecodable::decode(d) } @@ -801,6 +848,7 @@ impl_ref_decoder! {<'tcx> rustc_span::def_id::DefId, rustc_span::def_id::LocalDefId, (rustc_middle::middle::exported_symbols::ExportedSymbol<'tcx>, rustc_middle::middle::exported_symbols::SymbolExportInfo), + ty::DeducedParamAttrs, } //- ENCODING ------------------------------------------------------------------- @@ -815,6 +863,7 @@ pub struct CacheEncoder<'a, 'tcx> { source_map: CachingSourceMapView<'tcx>, file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>, hygiene_context: &'a HygieneEncodeContext, + symbol_table: FxHashMap<Symbol, usize>, } impl<'a, 'tcx> CacheEncoder<'a, 'tcx> { @@ -899,6 +948,32 @@ impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for Span { } } +// copy&paste impl from rustc_metadata +impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for Symbol { + fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) { + // if symbol preinterned, emit tag and symbol index + if self.is_preinterned() { + s.encoder.emit_u8(SYMBOL_PREINTERNED); + s.encoder.emit_u32(self.as_u32()); + } else { + // otherwise write it as string or as offset to it + match s.symbol_table.entry(*self) { + Entry::Vacant(o) => { + s.encoder.emit_u8(SYMBOL_STR); + let pos = s.encoder.position(); + o.insert(pos); + s.emit_str(self.as_str()); + } + Entry::Occupied(o) => { + let x = o.get().clone(); + s.emit_u8(SYMBOL_OFFSET); + s.emit_usize(x); + } + } + } + } +} + impl<'a, 'tcx> TyEncoder for CacheEncoder<'a, 'tcx> { type I = TyCtxt<'tcx>; const CLEAR_CROSS_CRATE: bool = false; @@ -993,7 +1068,7 @@ pub fn encode_query_results<'a, 'tcx, CTX, Q>( let _timer = tcx .dep_context() .profiler() - .extra_verbose_generic_activity("encode_query_results_for", std::any::type_name::<Q>()); + .verbose_generic_activity_with_arg("encode_query_results_for", std::any::type_name::<Q>()); assert!(Q::query_state(tcx).all_inactive()); let cache = Q::query_cache(tcx); diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index eda4401c8..1d17f4221 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -2,19 +2,31 @@ //! generate the actual methods on tcx which find and execute the provider, //! manage the caches, and so forth. +use crate::keys::Key; +use crate::on_disk_cache::{CacheDecoder, CacheEncoder, EncodedDepNodeIndex}; +use crate::profiling_support::QueryKeyStringCache; use crate::{on_disk_cache, Queries}; -use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; -use rustc_middle::ty::tls::{self, ImplicitCtxt}; -use rustc_middle::ty::TyCtxt; -use rustc_query_system::dep_graph::HasDepContext; -use rustc_query_system::query::{QueryContext, QueryJobId, QueryMap, QuerySideEffects}; - -use rustc_data_structures::sync::Lock; -use rustc_data_structures::thin_vec::ThinVec; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::{AtomicU64, Lock}; use rustc_errors::{Diagnostic, Handler}; - +use rustc_middle::dep_graph::{ + self, DepKind, DepKindStruct, DepNode, DepNodeIndex, SerializedDepNodeIndex, +}; +use rustc_middle::ty::tls::{self, ImplicitCtxt}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext}; +use rustc_query_system::ich::StableHashingContext; +use rustc_query_system::query::{ + force_query, QueryConfig, QueryContext, QueryDescription, QueryJobId, QueryMap, + QuerySideEffects, QueryStackFrame, +}; +use rustc_query_system::{LayoutOfDepth, QueryOverflow, Value}; +use rustc_serialize::Decodable; +use rustc_session::Limit; +use rustc_span::def_id::LOCAL_CRATE; use std::any::Any; use std::num::NonZeroU64; +use thin_vec::ThinVec; #[derive(Copy, Clone)] pub struct QueryCtxt<'tcx> { @@ -91,6 +103,7 @@ impl QueryContext for QueryCtxt<'_> { fn start_query<R>( &self, token: QueryJobId, + depth_limit: bool, diagnostics: Option<&Lock<ThinVec<Diagnostic>>>, compute: impl FnOnce() -> R, ) -> R { @@ -98,12 +111,16 @@ impl QueryContext for QueryCtxt<'_> { // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes // when accessing the `ImplicitCtxt`. tls::with_related_context(**self, move |current_icx| { + if depth_limit && !self.recursion_limit().value_within_limit(current_icx.query_depth) { + self.depth_limit_error(token); + } + // Update the `ImplicitCtxt` to point to our new query job. let new_icx = ImplicitCtxt { tcx: **self, query: Some(token), diagnostics, - layout_depth: current_icx.layout_depth, + query_depth: current_icx.query_depth + depth_limit as usize, task_deps: current_icx.task_deps, }; @@ -113,6 +130,29 @@ impl QueryContext for QueryCtxt<'_> { }) }) } + + fn depth_limit_error(&self, job: QueryJobId) { + let mut span = None; + let mut layout_of_depth = None; + if let Some(map) = self.try_collect_active_jobs() { + if let Some((info, depth)) = job.try_find_layout_root(map) { + span = Some(info.job.span); + layout_of_depth = Some(LayoutOfDepth { desc: info.query.description, depth }); + } + } + + let suggested_limit = match self.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + + self.sess.emit_fatal(QueryOverflow { + span, + layout_of_depth, + suggested_limit, + crate_name: self.crate_name(LOCAL_CRATE), + }); + } } impl<'tcx> QueryCtxt<'tcx> { @@ -134,22 +174,14 @@ impl<'tcx> QueryCtxt<'tcx> { pub(super) fn encode_query_results( self, - encoder: &mut on_disk_cache::CacheEncoder<'_, 'tcx>, - query_result_index: &mut on_disk_cache::EncodedDepNodeIndex, + encoder: &mut CacheEncoder<'_, 'tcx>, + query_result_index: &mut EncodedDepNodeIndex, ) { - macro_rules! encode_queries { - ($($query:ident,)*) => { - $( - on_disk_cache::encode_query_results::<_, super::queries::$query<'_>>( - self, - encoder, - query_result_index - ); - )* + for query in &self.queries.query_structs { + if let Some(encode) = query.encode_query_results { + encode(self, encoder, query_result_index); } } - - rustc_cached_queries!(encode_queries!); } pub fn try_print_query_stack( @@ -162,22 +194,26 @@ impl<'tcx> QueryCtxt<'tcx> { } } +#[derive(Clone, Copy)] +pub(crate) struct QueryStruct<'tcx> { + pub try_collect_active_jobs: fn(QueryCtxt<'tcx>, &mut QueryMap) -> Option<()>, + pub alloc_self_profile_query_strings: fn(TyCtxt<'tcx>, &mut QueryKeyStringCache), + pub encode_query_results: + Option<fn(QueryCtxt<'tcx>, &mut CacheEncoder<'_, 'tcx>, &mut EncodedDepNodeIndex)>, +} + macro_rules! handle_cycle_error { - ([][$tcx: expr, $error:expr]) => {{ - $error.emit(); - Value::from_cycle_error($tcx) + ([]) => {{ + rustc_query_system::HandleCycleError::Error }}; - ([(fatal_cycle) $($rest:tt)*][$tcx:expr, $error:expr]) => {{ - $error.emit(); - $tcx.sess.abort_if_errors(); - unreachable!() + ([(fatal_cycle) $($rest:tt)*]) => {{ + rustc_query_system::HandleCycleError::Fatal }}; - ([(cycle_delay_bug) $($rest:tt)*][$tcx:expr, $error:expr]) => {{ - $error.delay_as_bug(); - Value::from_cycle_error($tcx) + ([(cycle_delay_bug) $($rest:tt)*]) => {{ + rustc_query_system::HandleCycleError::DelayBug }}; - ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { - handle_cycle_error!([$($modifiers)*][$($args)*]) + ([$other:tt $($modifiers:tt)*]) => { + handle_cycle_error!([$($modifiers)*]) }; } @@ -205,6 +241,18 @@ macro_rules! is_eval_always { }; } +macro_rules! depth_limit { + ([]) => {{ + false + }}; + ([(depth_limit) $($rest:tt)*]) => {{ + true + }}; + ([$other:tt $($modifiers:tt)*]) => { + depth_limit!([$($modifiers)*]) + }; +} + macro_rules! hash_result { ([]) => {{ Some(dep_graph::hash_result) @@ -233,106 +281,207 @@ macro_rules! get_provider { }; } -macro_rules! opt_remap_env_constness { - ([][$name:ident]) => {}; - ([(remap_env_constness) $($rest:tt)*][$name:ident]) => { - let $name = $name.without_const(); +macro_rules! should_ever_cache_on_disk { + ([]) => {{ + None + }}; + ([(cache) $($rest:tt)*]) => {{ + Some($crate::plumbing::try_load_from_disk::<Self::Value>) + }}; + ([$other:tt $($modifiers:tt)*]) => { + should_ever_cache_on_disk!([$($modifiers)*]) + }; +} + +pub(crate) fn create_query_frame< + 'tcx, + K: Copy + Key + for<'a> HashStable<StableHashingContext<'a>>, +>( + tcx: QueryCtxt<'tcx>, + do_describe: fn(TyCtxt<'tcx>, K) -> String, + key: K, + kind: DepKind, + name: &'static str, +) -> QueryStackFrame { + // Disable visible paths printing for performance reasons. + // Showing visible path instead of any path is not that important in production. + let description = ty::print::with_no_visible_paths!( + // Force filename-line mode to avoid invoking `type_of` query. + ty::print::with_forced_impl_filename_line!(do_describe(tcx.tcx, key)) + ); + let description = + if tcx.sess.verbose() { format!("{} [{}]", description, name) } else { description }; + let span = if kind == dep_graph::DepKind::def_span { + // The `def_span` query is used to calculate `default_span`, + // so exit to avoid infinite recursion. + None + } else { + Some(key.default_span(*tcx)) }; - ([$other:tt $($modifiers:tt)*][$name:ident]) => { - opt_remap_env_constness!([$($modifiers)*][$name]) + let def_id = key.key_as_def_id(); + let def_kind = if kind == dep_graph::DepKind::opt_def_kind { + // Try to avoid infinite recursion. + None + } else { + def_id.and_then(|def_id| def_id.as_local()).and_then(|def_id| tcx.opt_def_kind(def_id)) + }; + let hash = || { + tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + std::mem::discriminant(&kind).hash_stable(&mut hcx, &mut hasher); + key.hash_stable(&mut hcx, &mut hasher); + hasher.finish::<u64>() + }) + }; + let ty_adt_id = key.ty_adt_id(); + + QueryStackFrame::new(name, description, span, def_id, def_kind, ty_adt_id, hash) +} + +fn try_load_from_on_disk_cache<'tcx, Q>(tcx: TyCtxt<'tcx>, dep_node: DepNode) +where + Q: QueryDescription<QueryCtxt<'tcx>>, + Q::Key: DepNodeParams<TyCtxt<'tcx>>, +{ + debug_assert!(tcx.dep_graph.is_green(&dep_node)); + + let key = Q::Key::recover(tcx, &dep_node).unwrap_or_else(|| { + panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash) + }); + if Q::cache_on_disk(tcx, &key) { + let _ = Q::execute_query(tcx, key); + } +} + +pub(crate) fn try_load_from_disk<'tcx, V>( + tcx: QueryCtxt<'tcx>, + id: SerializedDepNodeIndex, +) -> Option<V> +where + V: for<'a> Decodable<CacheDecoder<'a, 'tcx>>, +{ + tcx.on_disk_cache().as_ref()?.try_load_query_result(*tcx, id) +} + +fn force_from_dep_node<'tcx, Q>(tcx: TyCtxt<'tcx>, dep_node: DepNode) -> bool +where + Q: QueryDescription<QueryCtxt<'tcx>>, + Q::Key: DepNodeParams<TyCtxt<'tcx>>, + Q::Value: Value<TyCtxt<'tcx>>, +{ + // We must avoid ever having to call `force_from_dep_node()` for a + // `DepNode::codegen_unit`: + // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we + // would always end up having to evaluate the first caller of the + // `codegen_unit` query that *is* reconstructible. This might very well be + // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just + // to re-trigger calling the `codegen_unit` query with the right key. At + // that point we would already have re-done all the work we are trying to + // avoid doing in the first place. + // The solution is simple: Just explicitly call the `codegen_unit` query for + // each CGU, right after partitioning. This way `try_mark_green` will always + // hit the cache instead of having to go through `force_from_dep_node`. + // This assertion makes sure, we actually keep applying the solution above. + debug_assert!( + dep_node.kind != DepKind::codegen_unit, + "calling force_from_dep_node() on DepKind::codegen_unit" + ); + + if let Some(key) = Q::Key::recover(tcx, &dep_node) { + #[cfg(debug_assertions)] + let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); + let tcx = QueryCtxt::from_tcx(tcx); + force_query::<Q, _>(tcx, key, dep_node); + true + } else { + false + } +} + +pub(crate) fn query_callback<'tcx, Q: QueryConfig>( + is_anon: bool, + is_eval_always: bool, +) -> DepKindStruct<'tcx> +where + Q: QueryDescription<QueryCtxt<'tcx>>, + Q::Key: DepNodeParams<TyCtxt<'tcx>>, +{ + let fingerprint_style = Q::Key::fingerprint_style(); + + if is_anon || !fingerprint_style.reconstructible() { + return DepKindStruct { + is_anon, + is_eval_always, + fingerprint_style, + force_from_dep_node: None, + try_load_from_on_disk_cache: None, + }; + } + + DepKindStruct { + is_anon, + is_eval_always, + fingerprint_style, + force_from_dep_node: Some(force_from_dep_node::<Q>), + try_load_from_on_disk_cache: Some(try_load_from_on_disk_cache::<Q>), + } +} + +macro_rules! expand_if_cached { + ([], $tokens:expr) => {{ + None + }}; + ([(cache) $($rest:tt)*], $tokens:expr) => {{ + Some($tokens) + }}; + ([$other:tt $($modifiers:tt)*], $tokens:expr) => { + expand_if_cached!([$($modifiers)*], $tokens) }; } +// NOTE: `$V` isn't used here, but we still need to match on it so it can be passed to other macros +// invoked by `rustc_query_append`. macro_rules! define_queries { - (<$tcx:tt> + ( $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { - define_queries_struct! { - tcx: $tcx, input: ($(([$($modifiers)*] [$($attr)*] [$name]))*) } - mod make_query { - use super::*; - - // Create an eponymous constructor for each query. - $(#[allow(nonstandard_style)] $(#[$attr])* - pub fn $name<$tcx>(tcx: QueryCtxt<$tcx>, key: query_keys::$name<$tcx>) -> QueryStackFrame { - opt_remap_env_constness!([$($modifiers)*][key]); - let kind = dep_graph::DepKind::$name; - let name = stringify!($name); - // Disable visible paths printing for performance reasons. - // Showing visible path instead of any path is not that important in production. - let description = ty::print::with_no_visible_paths!( - // Force filename-line mode to avoid invoking `type_of` query. - ty::print::with_forced_impl_filename_line!( - queries::$name::describe(tcx, key) - ) - ); - let description = if tcx.sess.verbose() { - format!("{} [{}]", description, name) - } else { - description - }; - let span = if kind == dep_graph::DepKind::def_span { - // The `def_span` query is used to calculate `default_span`, - // so exit to avoid infinite recursion. - None - } else { - Some(key.default_span(*tcx)) - }; - let def_kind = if kind == dep_graph::DepKind::opt_def_kind { - // Try to avoid infinite recursion. - None - } else { - key.key_as_def_id() - .and_then(|def_id| def_id.as_local()) - .and_then(|def_id| tcx.opt_def_kind(def_id)) - }; - let hash = || { - tcx.with_stable_hashing_context(|mut hcx|{ - let mut hasher = StableHasher::new(); - std::mem::discriminant(&kind).hash_stable(&mut hcx, &mut hasher); - key.hash_stable(&mut hcx, &mut hasher); - hasher.finish::<u64>() - }) - }; - - QueryStackFrame::new(name, description, span, def_kind, hash) - })* - } - #[allow(nonstandard_style)] mod queries { use std::marker::PhantomData; - $(pub struct $name<$tcx> { - data: PhantomData<&$tcx ()> + $(pub struct $name<'tcx> { + data: PhantomData<&'tcx ()> })* } - $(impl<$tcx> QueryConfig for queries::$name<$tcx> { - type Key = query_keys::$name<$tcx>; - type Value = query_values::$name<$tcx>; - type Stored = query_stored::$name<$tcx>; + $(impl<'tcx> QueryConfig for queries::$name<'tcx> { + type Key = query_keys::$name<'tcx>; + type Value = query_values::$name<'tcx>; + type Stored = query_stored::$name<'tcx>; const NAME: &'static str = stringify!($name); } - impl<$tcx> QueryDescription<QueryCtxt<$tcx>> for queries::$name<$tcx> { - rustc_query_description! { $name<$tcx> } + impl<'tcx> QueryDescription<QueryCtxt<'tcx>> for queries::$name<'tcx> { + #[inline] + fn cache_on_disk(tcx: TyCtxt<'tcx>, key: &Self::Key) -> bool { + ::rustc_middle::query::cached::$name(tcx, key) + } - type Cache = query_storage::$name<$tcx>; + type Cache = query_storage::$name<'tcx>; #[inline(always)] - fn query_state<'a>(tcx: QueryCtxt<$tcx>) -> &'a QueryState<Self::Key> - where QueryCtxt<$tcx>: 'a + fn query_state<'a>(tcx: QueryCtxt<'tcx>) -> &'a QueryState<Self::Key> + where QueryCtxt<'tcx>: 'a { &tcx.queries.$name } #[inline(always)] - fn query_cache<'a>(tcx: QueryCtxt<$tcx>) -> &'a Self::Cache + fn query_cache<'a>(tcx: QueryCtxt<'tcx>) -> &'a Self::Cache where 'tcx:'a { &tcx.query_caches.$name @@ -340,34 +489,34 @@ macro_rules! define_queries { #[inline] fn make_vtable(tcx: QueryCtxt<'tcx>, key: &Self::Key) -> - QueryVTable<QueryCtxt<$tcx>, Self::Key, Self::Value> + QueryVTable<QueryCtxt<'tcx>, Self::Key, Self::Value> { let compute = get_provider!([$($modifiers)*][tcx, $name, key]); let cache_on_disk = Self::cache_on_disk(tcx.tcx, key); QueryVTable { anon: is_anon!([$($modifiers)*]), eval_always: is_eval_always!([$($modifiers)*]), + depth_limit: depth_limit!([$($modifiers)*]), dep_kind: dep_graph::DepKind::$name, hash_result: hash_result!([$($modifiers)*]), - handle_cycle_error: |tcx, mut error| handle_cycle_error!([$($modifiers)*][tcx, error]), + handle_cycle_error: handle_cycle_error!([$($modifiers)*]), compute, - cache_on_disk, - try_load_from_disk: Self::TRY_LOAD_FROM_DISK, + try_load_from_disk: if cache_on_disk { should_ever_cache_on_disk!([$($modifiers)*]) } else { None }, } } + + fn execute_query(tcx: TyCtxt<'tcx>, k: Self::Key) -> Self::Stored { + tcx.$name(k) + } })* #[allow(nonstandard_style)] mod query_callbacks { use super::*; - use rustc_middle::dep_graph::DepNode; - use rustc_middle::ty::query::query_keys; - use rustc_query_system::dep_graph::DepNodeParams; - use rustc_query_system::query::{force_query, QueryDescription}; use rustc_query_system::dep_graph::FingerprintStyle; // We use this for most things when incr. comp. is turned off. - pub fn Null() -> DepKindStruct { + pub fn Null<'tcx>() -> DepKindStruct<'tcx> { DepKindStruct { is_anon: false, is_eval_always: false, @@ -378,7 +527,7 @@ macro_rules! define_queries { } // We use this for the forever-red node. - pub fn Red() -> DepKindStruct { + pub fn Red<'tcx>() -> DepKindStruct<'tcx> { DepKindStruct { is_anon: false, is_eval_always: false, @@ -388,7 +537,7 @@ macro_rules! define_queries { } } - pub fn TraitSelect() -> DepKindStruct { + pub fn TraitSelect<'tcx>() -> DepKindStruct<'tcx> { DepKindStruct { is_anon: true, is_eval_always: false, @@ -398,7 +547,7 @@ macro_rules! define_queries { } } - pub fn CompileCodegenUnit() -> DepKindStruct { + pub fn CompileCodegenUnit<'tcx>() -> DepKindStruct<'tcx> { DepKindStruct { is_anon: false, is_eval_always: false, @@ -408,7 +557,7 @@ macro_rules! define_queries { } } - pub fn CompileMonoItem() -> DepKindStruct { + pub fn CompileMonoItem<'tcx>() -> DepKindStruct<'tcx> { DepKindStruct { is_anon: false, is_eval_always: false, @@ -418,111 +567,120 @@ macro_rules! define_queries { } } - $(pub(crate) fn $name()-> DepKindStruct { - let is_anon = is_anon!([$($modifiers)*]); - let is_eval_always = is_eval_always!([$($modifiers)*]); - - let fingerprint_style = - <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::fingerprint_style(); - - if is_anon || !fingerprint_style.reconstructible() { - return DepKindStruct { - is_anon, - is_eval_always, - fingerprint_style, - force_from_dep_node: None, - try_load_from_on_disk_cache: None, - } - } + $(pub(crate) fn $name<'tcx>()-> DepKindStruct<'tcx> { + $crate::plumbing::query_callback::<queries::$name<'tcx>>( + is_anon!([$($modifiers)*]), + is_eval_always!([$($modifiers)*]), + ) + })* + } - #[inline(always)] - fn recover<'tcx>(tcx: TyCtxt<'tcx>, dep_node: DepNode) -> Option<query_keys::$name<'tcx>> { - <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, &dep_node) - } + mod query_structs { + use rustc_middle::ty::TyCtxt; + use $crate::plumbing::{QueryStruct, QueryCtxt}; + use $crate::profiling_support::QueryKeyStringCache; + use rustc_query_system::query::QueryMap; - fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: DepNode) -> bool { - if let Some(key) = recover(tcx, dep_node) { - #[cfg(debug_assertions)] - let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); - let tcx = QueryCtxt::from_tcx(tcx); - force_query::<queries::$name<'_>, _>(tcx, key, dep_node); - true - } else { - false - } + pub(super) const fn dummy_query_struct<'tcx>() -> QueryStruct<'tcx> { + fn noop_try_collect_active_jobs(_: QueryCtxt<'_>, _: &mut QueryMap) -> Option<()> { + None } + fn noop_alloc_self_profile_query_strings(_: TyCtxt<'_>, _: &mut QueryKeyStringCache) {} - fn try_load_from_on_disk_cache(tcx: TyCtxt<'_>, dep_node: DepNode) { - debug_assert!(tcx.dep_graph.is_green(&dep_node)); - - let key = recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); - if queries::$name::cache_on_disk(tcx, &key) { - let _ = tcx.$name(key); - } + QueryStruct { + try_collect_active_jobs: noop_try_collect_active_jobs, + alloc_self_profile_query_strings: noop_alloc_self_profile_query_strings, + encode_query_results: None, } + } - DepKindStruct { - is_anon, - is_eval_always, - fingerprint_style, - force_from_dep_node: Some(force_from_dep_node), - try_load_from_on_disk_cache: Some(try_load_from_on_disk_cache), - } - })* + pub(super) use dummy_query_struct as Null; + pub(super) use dummy_query_struct as Red; + pub(super) use dummy_query_struct as TraitSelect; + pub(super) use dummy_query_struct as CompileCodegenUnit; + pub(super) use dummy_query_struct as CompileMonoItem; + + $( + pub(super) const fn $name<'tcx>() -> QueryStruct<'tcx> { QueryStruct { + try_collect_active_jobs: |tcx, qmap| { + let make_query = |tcx, key| { + let kind = rustc_middle::dep_graph::DepKind::$name; + let name = stringify!($name); + $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) + }; + tcx.queries.$name.try_collect_active_jobs( + tcx, + make_query, + qmap, + ) + }, + alloc_self_profile_query_strings: |tcx, string_cache| { + $crate::profiling_support::alloc_self_profile_query_strings_for_query_cache( + tcx, + stringify!($name), + &tcx.query_caches.$name, + string_cache, + ) + }, + encode_query_results: expand_if_cached!([$($modifiers)*], |tcx, encoder, query_result_index| + $crate::on_disk_cache::encode_query_results::<_, super::queries::$name<'_>>(tcx, encoder, query_result_index) + ), + }})* } - pub fn query_callbacks<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindStruct] { + pub fn query_callbacks<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindStruct<'tcx>] { arena.alloc_from_iter(make_dep_kind_array!(query_callbacks)) } } } -// FIXME(eddyb) this macro (and others?) use `$tcx` and `'tcx` interchangeably. -// We should either not take `$tcx` at all and use `'tcx` everywhere, or use -// `$tcx` everywhere (even if that isn't necessary due to lack of hygiene). +use crate::{ExternProviders, OnDiskCache, Providers}; + +impl<'tcx> Queries<'tcx> { + pub fn new( + local_providers: Providers, + extern_providers: ExternProviders, + on_disk_cache: Option<OnDiskCache<'tcx>>, + ) -> Self { + use crate::query_structs; + Queries { + local_providers: Box::new(local_providers), + extern_providers: Box::new(extern_providers), + query_structs: make_dep_kind_array!(query_structs).to_vec(), + on_disk_cache, + jobs: AtomicU64::new(1), + ..Queries::default() + } + } +} + macro_rules! define_queries_struct { - (tcx: $tcx:tt, + ( input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => { - pub struct Queries<$tcx> { + #[derive(Default)] + pub struct Queries<'tcx> { local_providers: Box<Providers>, extern_providers: Box<ExternProviders>, + query_structs: Vec<$crate::plumbing::QueryStruct<'tcx>>, - pub on_disk_cache: Option<OnDiskCache<$tcx>>, + pub on_disk_cache: Option<OnDiskCache<'tcx>>, jobs: AtomicU64, - $($(#[$attr])* $name: QueryState<query_keys::$name<$tcx>>,)* + $($(#[$attr])* $name: QueryState<<queries::$name<'tcx> as QueryConfig>::Key>,)* } - impl<$tcx> Queries<$tcx> { - pub fn new( - local_providers: Providers, - extern_providers: ExternProviders, - on_disk_cache: Option<OnDiskCache<$tcx>>, - ) -> Self { - Queries { - local_providers: Box::new(local_providers), - extern_providers: Box::new(extern_providers), - on_disk_cache, - jobs: AtomicU64::new(1), - $($name: Default::default()),* - } - } - + impl<'tcx> Queries<'tcx> { pub(crate) fn try_collect_active_jobs( - &$tcx self, - tcx: TyCtxt<$tcx>, + &'tcx self, + tcx: TyCtxt<'tcx>, ) -> Option<QueryMap> { let tcx = QueryCtxt { tcx, queries: self }; let mut jobs = QueryMap::default(); - $( - self.$name.try_collect_active_jobs( - tcx, - make_query::$name, - &mut jobs, - )?; - )* + for query in &self.query_structs { + (query.try_collect_active_jobs)(tcx, &mut jobs); + } Some(jobs) } @@ -541,17 +699,16 @@ macro_rules! define_queries_struct { $($(#[$attr])* #[inline(always)] - #[tracing::instrument(level = "trace", skip(self, tcx))] + #[tracing::instrument(level = "trace", skip(self, tcx), ret)] fn $name( &'tcx self, - tcx: TyCtxt<$tcx>, + tcx: TyCtxt<'tcx>, span: Span, - key: query_keys::$name<$tcx>, + key: <queries::$name<'tcx> as QueryConfig>::Key, mode: QueryMode, - ) -> Option<query_stored::$name<$tcx>> { - opt_remap_env_constness!([$($modifiers)*][key]); + ) -> Option<query_stored::$name<'tcx>> { let qcx = QueryCtxt { tcx, queries: self }; - get_query::<queries::$name<$tcx>, _>(qcx, span, key, mode) + get_query::<queries::$name<'tcx>, _>(qcx, span, key, mode) })* } }; diff --git a/compiler/rustc_query_impl/src/profiling_support.rs b/compiler/rustc_query_impl/src/profiling_support.rs index 551f09420..2cc311d48 100644 --- a/compiler/rustc_query_impl/src/profiling_support.rs +++ b/compiler/rustc_query_impl/src/profiling_support.rs @@ -1,3 +1,4 @@ +use crate::QueryCtxt; use measureme::{StringComponent, StringId}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfiler; @@ -8,7 +9,7 @@ use rustc_query_system::query::QueryCache; use std::fmt::Debug; use std::io::Write; -struct QueryKeyStringCache { +pub(crate) struct QueryKeyStringCache { def_id_cache: FxHashMap<DefId, StringId>, } @@ -226,7 +227,7 @@ where /// Allocate the self-profiling query strings for a single query cache. This /// method is called from `alloc_self_profile_query_strings` which knows all /// the queries via macro magic. -fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( +pub(crate) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( tcx: TyCtxt<'tcx>, query_name: &'static str, query_cache: &C, @@ -298,27 +299,15 @@ fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( /// If we are recording only summary data, the ids will point to /// just the query names. If we are recording query keys too, we /// allocate the corresponding strings here. -pub fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) { +pub fn alloc_self_profile_query_strings<'tcx>(tcx: TyCtxt<'tcx>) { if !tcx.prof.enabled() { return; } let mut string_cache = QueryKeyStringCache::new(); + let queries = QueryCtxt::from_tcx(tcx); - macro_rules! alloc_once { - (<$tcx:tt> - $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($K:ty) -> $V:ty,)* - ) => { - $({ - alloc_self_profile_query_strings_for_query_cache( - tcx, - stringify!($name), - &tcx.query_caches.$name, - &mut string_cache, - ); - })* - } + for query in &queries.queries.query_structs { + (query.alloc_self_profile_query_strings)(tcx, &mut string_cache); } - - rustc_query_append! { [alloc_once!][<'tcx>] } } diff --git a/compiler/rustc_query_impl/src/values.rs b/compiler/rustc_query_impl/src/values.rs deleted file mode 100644 index 718a2971c..000000000 --- a/compiler/rustc_query_impl/src/values.rs +++ /dev/null @@ -1,45 +0,0 @@ -use super::QueryCtxt; -use rustc_middle::ty::{self, AdtSizedConstraint, Ty}; - -pub(super) trait Value<'tcx>: Sized { - fn from_cycle_error(tcx: QueryCtxt<'tcx>) -> Self; -} - -impl<'tcx, T> Value<'tcx> for T { - default fn from_cycle_error(tcx: QueryCtxt<'tcx>) -> T { - tcx.sess.abort_if_errors(); - bug!("Value::from_cycle_error called without errors"); - } -} - -impl<'tcx> Value<'tcx> for Ty<'_> { - fn from_cycle_error(tcx: QueryCtxt<'tcx>) -> Self { - // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. - // FIXME: Represent the above fact in the trait system somehow. - unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(tcx.ty_error()) } - } -} - -impl<'tcx> Value<'tcx> for ty::SymbolName<'_> { - fn from_cycle_error(tcx: QueryCtxt<'tcx>) -> Self { - // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`. - // FIXME: Represent the above fact in the trait system somehow. - unsafe { - std::mem::transmute::<ty::SymbolName<'tcx>, ty::SymbolName<'_>>(ty::SymbolName::new( - *tcx, "<error>", - )) - } - } -} - -impl<'tcx> Value<'tcx> for AdtSizedConstraint<'_> { - fn from_cycle_error(tcx: QueryCtxt<'tcx>) -> Self { - // SAFETY: This is never called when `Self` is not `AdtSizedConstraint<'tcx>`. - // FIXME: Represent the above fact in the trait system somehow. - unsafe { - std::mem::transmute::<AdtSizedConstraint<'tcx>, AdtSizedConstraint<'_>>( - AdtSizedConstraint(tcx.intern_type_list(&[tcx.ty_error()])), - ) - } - } -} |