diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 05088dc41933..bcb270ff0a64 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -362,29 +362,7 @@ rustc_dep_node_append!([define_dep_nodes!][ <'tcx> [] CompileCodegenUnit(Symbol), ]); -pub trait RecoverKey<'tcx>: Sized { - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option; -} - -impl RecoverKey<'tcx> for CrateNum { - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - dep_node.extract_def_id(tcx).map(|id| id.krate) - } -} - -impl RecoverKey<'tcx> for DefId { - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - dep_node.extract_def_id(tcx) - } -} - -impl RecoverKey<'tcx> for DefIndex { - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - dep_node.extract_def_id(tcx).map(|id| id.index) - } -} - -trait DepNodeParams<'tcx>: fmt::Debug { +pub(crate) trait DepNodeParams<'tcx>: fmt::Debug + Sized { const CAN_RECONSTRUCT_QUERY_KEY: bool; /// This method turns the parameters of a DepNodeConstructor into an opaque @@ -398,6 +376,8 @@ trait DepNodeParams<'tcx>: fmt::Debug { fn to_debug_str(&self, _: TyCtxt<'tcx>) -> String { format!("{:?}", self) } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option; } impl<'tcx, T> DepNodeParams<'tcx> for T @@ -418,6 +398,10 @@ where default fn to_debug_str(&self, _: TyCtxt<'tcx>) -> String { format!("{:?}", *self) } + + default fn recover(_: TyCtxt<'tcx>, _: &DepNode) -> Option { + None + } } impl<'tcx> DepNodeParams<'tcx> for DefId { @@ -430,6 +414,10 @@ impl<'tcx> DepNodeParams<'tcx> for DefId { fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { tcx.def_path_str(*self) } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + dep_node.extract_def_id(tcx) + } } impl<'tcx> DepNodeParams<'tcx> for DefIndex { @@ -442,6 +430,10 @@ impl<'tcx> DepNodeParams<'tcx> for DefIndex { fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { tcx.def_path_str(DefId::local(*self)) } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + dep_node.extract_def_id(tcx).map(|id| id.index) + } } impl<'tcx> DepNodeParams<'tcx> for CrateNum { @@ -455,6 +447,10 @@ impl<'tcx> DepNodeParams<'tcx> for CrateNum { fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { tcx.crate_name(*self).to_string() } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + dep_node.extract_def_id(tcx).map(|id| id.krate) + } } impl<'tcx> DepNodeParams<'tcx> for (DefId, DefId) { diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs index eb377d20f596..1fbd90743f40 100644 --- a/src/librustc/dep_graph/mod.rs +++ b/src/librustc/dep_graph/mod.rs @@ -6,7 +6,8 @@ mod query; mod safe; mod serialized; -pub use self::dep_node::{label_strs, DepConstructor, DepKind, DepNode, RecoverKey, WorkProductId}; +pub(crate) use self::dep_node::DepNodeParams; +pub use self::dep_node::{label_strs, DepConstructor, DepKind, DepNode, WorkProductId}; pub use self::graph::WorkProductFileKind; pub use self::graph::{hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, WorkProduct}; pub use self::prev::PreviousDepGraph; diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index daf89cab8af6..c8155005480f 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -1,4 +1,4 @@ -use crate::dep_graph::{DepKind, DepNode, RecoverKey, SerializedDepNodeIndex}; +use crate::dep_graph::SerializedDepNodeIndex; use crate::mir; use crate::mir::interpret::{GlobalId, LitToConstInput}; use crate::traits; diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index f49c64a7b87a..8adb828fbebc 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -1,4 +1,4 @@ -use crate::dep_graph::{self, DepConstructor, DepNode}; +use crate::dep_graph::{self, DepConstructor, DepNode, DepNodeParams}; use crate::hir::exports::Export; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintLevelMap; @@ -60,8 +60,8 @@ use std::sync::Arc; #[macro_use] mod plumbing; +pub use self::plumbing::CycleError; use self::plumbing::*; -pub use self::plumbing::{force_from_dep_node, CycleError}; mod stats; pub use self::stats::print_stats; @@ -105,3 +105,104 @@ pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder // as they will raise an fatal error on query cycles instead. rustc_query_append! { [define_queries!][<'tcx>] } + +/// The red/green evaluation system will try to mark a specific DepNode in the +/// dependency graph as green by recursively trying to mark the dependencies of +/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` +/// where we don't know if it is red or green and we therefore actually have +/// to recompute its value in order to find out. Since the only piece of +/// information that we have at that point is the `DepNode` we are trying to +/// re-evaluate, we need some way to re-run a query from just that. This is what +/// `force_from_dep_node()` implements. +/// +/// In the general case, a `DepNode` consists of a `DepKind` and an opaque +/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint +/// is usually constructed by computing a stable hash of the query-key that the +/// `DepNode` corresponds to. Consequently, it is not in general possible to go +/// back from hash to query-key (since hash functions are not reversible). For +/// this reason `force_from_dep_node()` is expected to fail from time to time +/// because we just cannot find out, from the `DepNode` alone, what the +/// corresponding query-key is and therefore cannot re-run the query. +/// +/// The system deals with this case letting `try_mark_green` fail which forces +/// the root query to be re-evaluated. +/// +/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. +/// Fortunately, we can use some contextual information that will allow us to +/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we +/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a +/// valid `DefPathHash`. Since we also always build a huge table that maps every +/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have +/// everything we need to re-run the query. +/// +/// Take the `mir_validated` query as an example. Like many other queries, it +/// just has a single parameter: the `DefId` of the item it will compute the +/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` +/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` +/// is actually a `DefPathHash`, and can therefore just look up the corresponding +/// `DefId` in `tcx.def_path_hash_to_def_id`. +/// +/// When you implement a new query, it will likely have a corresponding new +/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As +/// a rule of thumb, if your query takes a `DefId` or `DefIndex` as sole parameter, +/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just +/// add it to the "We don't have enough information to reconstruct..." group in +/// the match below. +pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool { + use crate::dep_graph::DepKind; + + // 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 !dep_node.kind.can_reconstruct_query_key() { + return false; + } + + rustc_dep_node_force!([dep_node, tcx] + // These are inputs that are expected to be pre-allocated and that + // should therefore always be red or green already. + DepKind::AllLocalTraitImpls | + DepKind::CrateMetadata | + DepKind::HirBody | + DepKind::Hir | + + // These are anonymous nodes. + DepKind::TraitSelect | + + // We don't have enough information to reconstruct the query key of + // these. + DepKind::CompileCodegenUnit => { + bug!("force_from_dep_node: encountered {:?}", dep_node) + } + ); + + false +} + +impl DepNode { + /// Check whether the query invocation corresponding to the given + /// DepNode is eligible for on-disk-caching. If so, this is method + /// will execute the query corresponding to the given DepNode. + /// Also, as a sanity check, it expects that the corresponding query + /// invocation has been marked as green already. + pub fn try_load_from_on_disk_cache<'tcx>(&self, tcx: TyCtxt<'tcx>) { + use crate::dep_graph::DepKind; + + rustc_dep_node_try_load_from_on_disk_cache!(self, tcx) + } +} diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 1769a925ec30..acf67f52dcea 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -2,7 +2,7 @@ //! generate the actual methods on tcx which find and execute the provider, //! manage the caches, and so forth. -use crate::dep_graph::{DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex}; +use crate::dep_graph::{DepNode, DepNodeIndex, SerializedDepNodeIndex}; use crate::ty::query::caches::QueryCache; use crate::ty::query::config::{QueryAccessors, QueryDescription}; use crate::ty::query::job::{QueryInfo, QueryJob, QueryJobId, QueryShardJobId}; @@ -720,7 +720,7 @@ impl<'tcx> TyCtxt<'tcx> { } #[allow(dead_code)] - fn force_query + 'tcx>( + pub(super) fn force_query + 'tcx>( self, key: Q::Key, span: Span, @@ -1162,91 +1162,3 @@ macro_rules! define_provider_struct { } }; } - -/// The red/green evaluation system will try to mark a specific DepNode in the -/// dependency graph as green by recursively trying to mark the dependencies of -/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` -/// where we don't know if it is red or green and we therefore actually have -/// to recompute its value in order to find out. Since the only piece of -/// information that we have at that point is the `DepNode` we are trying to -/// re-evaluate, we need some way to re-run a query from just that. This is what -/// `force_from_dep_node()` implements. -/// -/// In the general case, a `DepNode` consists of a `DepKind` and an opaque -/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint -/// is usually constructed by computing a stable hash of the query-key that the -/// `DepNode` corresponds to. Consequently, it is not in general possible to go -/// back from hash to query-key (since hash functions are not reversible). For -/// this reason `force_from_dep_node()` is expected to fail from time to time -/// because we just cannot find out, from the `DepNode` alone, what the -/// corresponding query-key is and therefore cannot re-run the query. -/// -/// The system deals with this case letting `try_mark_green` fail which forces -/// the root query to be re-evaluated. -/// -/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. -/// Fortunately, we can use some contextual information that will allow us to -/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we -/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a -/// valid `DefPathHash`. Since we also always build a huge table that maps every -/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have -/// everything we need to re-run the query. -/// -/// Take the `mir_validated` query as an example. Like many other queries, it -/// just has a single parameter: the `DefId` of the item it will compute the -/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` -/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` -/// is actually a `DefPathHash`, and can therefore just look up the corresponding -/// `DefId` in `tcx.def_path_hash_to_def_id`. -/// -/// When you implement a new query, it will likely have a corresponding new -/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As -/// a rule of thumb, if your query takes a `DefId` or `DefIndex` as sole parameter, -/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just -/// add it to the "We don't have enough information to reconstruct..." group in -/// the match below. -pub fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: &DepNode) -> bool { - use crate::dep_graph::RecoverKey; - - // 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 !dep_node.kind.can_reconstruct_query_key() { - return false; - } - - rustc_dep_node_force!([dep_node, tcx] - // These are inputs that are expected to be pre-allocated and that - // should therefore always be red or green already. - DepKind::AllLocalTraitImpls | - DepKind::CrateMetadata | - DepKind::HirBody | - DepKind::Hir | - - // These are anonymous nodes. - DepKind::TraitSelect | - - // We don't have enough information to reconstruct the query key of - // these. - DepKind::CompileCodegenUnit => { - bug!("force_from_dep_node: encountered {:?}", dep_node) - } - ); - - true -} diff --git a/src/librustc_macros/src/query.rs b/src/librustc_macros/src/query.rs index 97b800decc59..c23095968fb3 100644 --- a/src/librustc_macros/src/query.rs +++ b/src/librustc_macros/src/query.rs @@ -425,7 +425,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let mut dep_node_def_stream = quote! {}; let mut dep_node_force_stream = quote! {}; let mut try_load_from_on_disk_cache_stream = quote! {}; - let mut no_force_queries = Vec::new(); let mut cached_queries = quote! {}; for group in groups.0 { @@ -444,19 +443,19 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { cached_queries.extend(quote! { #name, }); - } - if modifiers.cache.is_some() && !modifiers.no_force { try_load_from_on_disk_cache_stream.extend(quote! { DepKind::#name => { - debug_assert!(tcx.dep_graph - .node_color(self) - .map(|c| c.is_green()) - .unwrap_or(false)); + if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY { + debug_assert!($tcx.dep_graph + .node_color($dep_node) + .map(|c| c.is_green()) + .unwrap_or(false)); - let key = RecoverKey::recover(tcx, self).unwrap(); - if queries::#name::cache_on_disk(tcx, key, None) { - let _ = tcx.#name(key); + let key = <#arg as DepNodeParams>::recover($tcx, $dep_node).unwrap(); + if queries::#name::cache_on_disk($tcx, key, None) { + let _ = $tcx.#name(key); + } } } }); @@ -501,24 +500,21 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { [#attribute_stream] #name(#arg), }); - if modifiers.no_force { - no_force_queries.push(name.clone()); - } else { - // Add a match arm to force the query given the dep node - dep_node_force_stream.extend(quote! { - DepKind::#name => { - if let Some(key) = RecoverKey::recover($tcx, $dep_node) { + // Add a match arm to force the query given the dep node + dep_node_force_stream.extend(quote! { + DepKind::#name => { + if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY { + if let Some(key) = <#arg as DepNodeParams>::recover($tcx, $dep_node) { $tcx.force_query::>( key, DUMMY_SP, *$dep_node ); - } else { - return false; + return true; } } - }); - } + } + }); add_query_description_impl(&query, modifiers, &mut query_description_stream); } @@ -528,12 +524,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { }); } - // Add an arm for the no force queries to panic when trying to force them - for query in no_force_queries { - dep_node_force_stream.extend(quote! { - DepKind::#query | - }); - } dep_node_force_stream.extend(quote! { DepKind::Null => { bug!("Cannot force dep node: {:?}", $dep_node) @@ -577,14 +567,9 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { #query_description_stream - impl DepNode { - /// Check whether the query invocation corresponding to the given - /// DepNode is eligible for on-disk-caching. If so, this is method - /// will execute the query corresponding to the given DepNode. - /// Also, as a sanity check, it expects that the corresponding query - /// invocation has been marked as green already. - pub fn try_load_from_on_disk_cache(&self, tcx: TyCtxt<'_>) { - match self.kind { + macro_rules! rustc_dep_node_try_load_from_on_disk_cache { + ($dep_node:expr, $tcx:expr) => { + match $dep_node.kind { #try_load_from_on_disk_cache_stream _ => (), }