Remove the need for no_force

This commit is contained in:
John Kåre Alsaker 2020-02-26 02:20:33 +01:00
parent 67012158a3
commit cbce21767b
6 changed files with 147 additions and 152 deletions

View file

@ -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<Self>;
}
impl RecoverKey<'tcx> for CrateNum {
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
dep_node.extract_def_id(tcx).map(|id| id.krate)
}
}
impl RecoverKey<'tcx> for DefId {
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
dep_node.extract_def_id(tcx)
}
}
impl RecoverKey<'tcx> for DefIndex {
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
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<Self>;
}
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<Self> {
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<Self> {
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<Self> {
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<Self> {
dep_node.extract_def_id(tcx).map(|id| id.krate)
}
}
impl<'tcx> DepNodeParams<'tcx> for (DefId, DefId) {

View file

@ -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;

View file

@ -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;

View file

@ -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)
}
}

View file

@ -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<Q: QueryDescription<'tcx> + 'tcx>(
pub(super) fn force_query<Q: QueryDescription<'tcx> + '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
}

View file

@ -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::<crate::ty::query::queries::#name<'_>>(
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
_ => (),
}