1
Fork 0

Represent diagnostic side effects as dep nodes

This commit is contained in:
John Kåre Alsaker 2024-03-07 06:47:08 +01:00
parent f7b4354283
commit 3ca5220114
11 changed files with 138 additions and 180 deletions

View file

@ -14,6 +14,7 @@ use std::fmt;
use rustc_errors::{DiagInner, TRACK_DIAGNOSTIC}; use rustc_errors::{DiagInner, TRACK_DIAGNOSTIC};
use rustc_middle::dep_graph::{DepNodeExt, TaskDepsRef}; use rustc_middle::dep_graph::{DepNodeExt, TaskDepsRef};
use rustc_middle::ty::tls; use rustc_middle::ty::tls;
use rustc_query_impl::QueryCtxt;
use rustc_query_system::dep_graph::dep_node::default_dep_kind_debug; use rustc_query_system::dep_graph::dep_node::default_dep_kind_debug;
use rustc_query_system::dep_graph::{DepContext, DepKind, DepNode}; use rustc_query_system::dep_graph::{DepContext, DepKind, DepNode};
@ -41,9 +42,7 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
fn track_diagnostic<R>(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { fn track_diagnostic<R>(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
tls::with_context_opt(|icx| { tls::with_context_opt(|icx| {
if let Some(icx) = icx { if let Some(icx) = icx {
if let Some(diagnostics) = icx.diagnostics { icx.tcx.dep_graph.record_diagnostic(QueryCtxt::new(icx.tcx), &diagnostic);
diagnostics.lock().extend(Some(diagnostic.clone()));
}
// Diagnostics are tracked, we can ignore the dependency. // Diagnostics are tracked, we can ignore the dependency.
let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() }; let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() };

View file

@ -79,6 +79,7 @@ rustc_query_append!(define_dep_nodes![
[] fn Null() -> (), [] fn Null() -> (),
/// We use this to create a forever-red node. /// We use this to create a forever-red node.
[] fn Red() -> (), [] fn Red() -> (),
[] fn SideEffect() -> (),
[] fn TraitSelect() -> (), [] fn TraitSelect() -> (),
[] fn CompileCodegenUnit() -> (), [] fn CompileCodegenUnit() -> (),
[] fn CompileMonoItem() -> (), [] fn CompileMonoItem() -> (),

View file

@ -46,6 +46,7 @@ impl Deps for DepsType {
const DEP_KIND_NULL: DepKind = dep_kinds::Null; const DEP_KIND_NULL: DepKind = dep_kinds::Null;
const DEP_KIND_RED: DepKind = dep_kinds::Red; const DEP_KIND_RED: DepKind = dep_kinds::Red;
const DEP_KIND_SIDE_EFFECT: DepKind = dep_kinds::SideEffect;
const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1; const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1;
} }

View file

@ -357,11 +357,10 @@ impl OnDiskCache {
&self, &self,
tcx: TyCtxt<'_>, tcx: TyCtxt<'_>,
dep_node_index: SerializedDepNodeIndex, dep_node_index: SerializedDepNodeIndex,
) -> QuerySideEffects { ) -> Option<QuerySideEffects> {
let side_effects: Option<QuerySideEffects> = let side_effects: Option<QuerySideEffects> =
self.load_indexed(tcx, dep_node_index, &self.prev_side_effects_index); self.load_indexed(tcx, dep_node_index, &self.prev_side_effects_index);
side_effects
side_effects.unwrap_or_default()
} }
/// Stores a `QuerySideEffects` emitted during the current compilation session. /// Stores a `QuerySideEffects` emitted during the current compilation session.
@ -395,21 +394,6 @@ impl OnDiskCache {
opt_value opt_value
} }
/// Stores side effect emitted during computation of an anonymous query.
/// Since many anonymous queries can share the same `DepNode`, we aggregate
/// them -- as opposed to regular queries where we assume that there is a
/// 1:1 relationship between query-key and `DepNode`.
pub fn store_side_effects_for_anon_node(
&self,
dep_node_index: DepNodeIndex,
side_effects: QuerySideEffects,
) {
let mut current_side_effects = self.current_side_effects.borrow_mut();
let x = current_side_effects.entry(dep_node_index).or_default();
x.append(side_effects);
}
fn load_indexed<'tcx, T>( fn load_indexed<'tcx, T>(
&self, &self,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,

View file

@ -1,8 +1,6 @@
use std::{mem, ptr}; use std::{mem, ptr};
use rustc_data_structures::sync::{self, Lock}; use rustc_data_structures::sync;
use rustc_errors::DiagInner;
use thin_vec::ThinVec;
use super::{GlobalCtxt, TyCtxt}; use super::{GlobalCtxt, TyCtxt};
use crate::dep_graph::TaskDepsRef; use crate::dep_graph::TaskDepsRef;
@ -22,10 +20,6 @@ pub struct ImplicitCtxt<'a, 'tcx> {
/// `ty::query::plumbing` when executing a query. /// `ty::query::plumbing` when executing a query.
pub query: Option<QueryJobId>, pub query: Option<QueryJobId>,
/// Where to store diagnostics for the current query job, if any.
/// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query.
pub diagnostics: Option<&'a Lock<ThinVec<DiagInner>>>,
/// Used to prevent queries from calling too deeply. /// Used to prevent queries from calling too deeply.
pub query_depth: usize, pub query_depth: usize,
@ -37,13 +31,7 @@ pub struct ImplicitCtxt<'a, 'tcx> {
impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> {
pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self { pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self {
let tcx = TyCtxt { gcx }; let tcx = TyCtxt { gcx };
ImplicitCtxt { ImplicitCtxt { tcx, query: None, query_depth: 0, task_deps: TaskDepsRef::Ignore }
tcx,
query: None,
diagnostics: None,
query_depth: 0,
task_deps: TaskDepsRef::Ignore,
}
} }
} }

View file

@ -5,9 +5,7 @@
use std::num::NonZero; use std::num::NonZero;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::Lock;
use rustc_data_structures::unord::UnordMap; use rustc_data_structures::unord::UnordMap;
use rustc_errors::DiagInner;
use rustc_hashes::Hash64; use rustc_hashes::Hash64;
use rustc_index::Idx; use rustc_index::Idx;
use rustc_middle::bug; use rustc_middle::bug;
@ -32,7 +30,6 @@ use rustc_query_system::{QueryOverflow, QueryOverflowNote};
use rustc_serialize::{Decodable, Encodable}; use rustc_serialize::{Decodable, Encodable};
use rustc_session::Limit; use rustc_session::Limit;
use rustc_span::def_id::LOCAL_CRATE; use rustc_span::def_id::LOCAL_CRATE;
use thin_vec::ThinVec;
use crate::QueryConfigRestored; use crate::QueryConfigRestored;
@ -92,12 +89,14 @@ impl QueryContext for QueryCtxt<'_> {
} }
// Interactions with on_disk_cache // Interactions with on_disk_cache
fn load_side_effects(self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects { fn load_side_effects(
self,
prev_dep_node_index: SerializedDepNodeIndex,
) -> Option<QuerySideEffects> {
self.query_system self.query_system
.on_disk_cache .on_disk_cache
.as_ref() .as_ref()
.map(|c| c.load_side_effects(self.tcx, prev_dep_node_index)) .and_then(|c| c.load_side_effects(self.tcx, prev_dep_node_index))
.unwrap_or_default()
} }
#[inline(never)] #[inline(never)]
@ -108,27 +107,13 @@ impl QueryContext for QueryCtxt<'_> {
} }
} }
#[inline(never)]
#[cold]
fn store_side_effects_for_anon_node(
self,
dep_node_index: DepNodeIndex,
side_effects: QuerySideEffects,
) {
if let Some(c) = self.query_system.on_disk_cache.as_ref() {
c.store_side_effects_for_anon_node(dep_node_index, side_effects)
}
}
/// Executes a job by changing the `ImplicitCtxt` to point to the /// Executes a job by changing the `ImplicitCtxt` to point to the
/// new query job while it executes. It returns the diagnostics /// new query job while it executes.
/// captured during execution and the actual result.
#[inline(always)] #[inline(always)]
fn start_query<R>( fn start_query<R>(
self, self,
token: QueryJobId, token: QueryJobId,
depth_limit: bool, depth_limit: bool,
diagnostics: Option<&Lock<ThinVec<DiagInner>>>,
compute: impl FnOnce() -> R, compute: impl FnOnce() -> R,
) -> R { ) -> R {
// The `TyCtxt` stored in TLS has the same global interner lifetime // The `TyCtxt` stored in TLS has the same global interner lifetime
@ -143,7 +128,6 @@ impl QueryContext for QueryCtxt<'_> {
let new_icx = ImplicitCtxt { let new_icx = ImplicitCtxt {
tcx: self.tcx, tcx: self.tcx,
query: Some(token), query: Some(token),
diagnostics,
query_depth: current_icx.query_depth + depth_limit as usize, query_depth: current_icx.query_depth + depth_limit as usize,
task_deps: current_icx.task_deps, task_deps: current_icx.task_deps,
}; };
@ -500,7 +484,7 @@ where
is_anon, is_anon,
is_eval_always, is_eval_always,
fingerprint_style, fingerprint_style,
force_from_dep_node: Some(|tcx, dep_node| { force_from_dep_node: Some(|tcx, dep_node, _| {
force_from_dep_node(Q::config(tcx), tcx, dep_node) force_from_dep_node(Q::config(tcx), tcx, dep_node)
}), }),
try_load_from_on_disk_cache: Some(|tcx, dep_node| { try_load_from_on_disk_cache: Some(|tcx, dep_node| {
@ -802,7 +786,7 @@ macro_rules! define_queries {
is_anon: false, is_anon: false,
is_eval_always: false, is_eval_always: false,
fingerprint_style: FingerprintStyle::Unit, fingerprint_style: FingerprintStyle::Unit,
force_from_dep_node: Some(|_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node)), force_from_dep_node: Some(|_, dep_node, _| bug!("force_from_dep_node: encountered {:?}", dep_node)),
try_load_from_on_disk_cache: None, try_load_from_on_disk_cache: None,
name: &"Null", name: &"Null",
} }
@ -814,12 +798,26 @@ macro_rules! define_queries {
is_anon: false, is_anon: false,
is_eval_always: false, is_eval_always: false,
fingerprint_style: FingerprintStyle::Unit, fingerprint_style: FingerprintStyle::Unit,
force_from_dep_node: Some(|_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node)), force_from_dep_node: Some(|_, dep_node, _| bug!("force_from_dep_node: encountered {:?}", dep_node)),
try_load_from_on_disk_cache: None, try_load_from_on_disk_cache: None,
name: &"Red", name: &"Red",
} }
} }
pub(crate) fn SideEffect<'tcx>() -> DepKindStruct<'tcx> {
DepKindStruct {
is_anon: false,
is_eval_always: false,
fingerprint_style: FingerprintStyle::Unit,
force_from_dep_node: Some(|tcx, _, prev_index| {
tcx.dep_graph.force_diagnostic_node(QueryCtxt::new(tcx), prev_index);
true
}),
try_load_from_on_disk_cache: None,
name: &"SideEffect",
}
}
pub(crate) fn TraitSelect<'tcx>() -> DepKindStruct<'tcx> { pub(crate) fn TraitSelect<'tcx>() -> DepKindStruct<'tcx> {
DepKindStruct { DepKindStruct {
is_anon: true, is_anon: true,

View file

@ -64,7 +64,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd,
use rustc_hir::definitions::DefPathHash; use rustc_hir::definitions::DefPathHash;
use rustc_macros::{Decodable, Encodable}; use rustc_macros::{Decodable, Encodable};
use super::{DepContext, FingerprintStyle}; use super::{DepContext, FingerprintStyle, SerializedDepNodeIndex};
use crate::ich::StableHashingContext; use crate::ich::StableHashingContext;
/// This serves as an index into arrays built by `make_dep_kind_array`. /// This serves as an index into arrays built by `make_dep_kind_array`.
@ -275,7 +275,8 @@ pub struct DepKindStruct<Tcx: DepContext> {
/// with kind `MirValidated`, we know that the GUID/fingerprint of the `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 /// is actually a `DefPathHash`, and can therefore just look up the corresponding
/// `DefId` in `tcx.def_path_hash_to_def_id`. /// `DefId` in `tcx.def_path_hash_to_def_id`.
pub force_from_dep_node: Option<fn(tcx: Tcx, dep_node: DepNode) -> bool>, pub force_from_dep_node:
Option<fn(tcx: Tcx, dep_node: DepNode, prev_index: SerializedDepNodeIndex) -> bool>,
/// Invoke a query to put the on-disk cached value in memory. /// Invoke a query to put the on-disk cached value in memory.
pub try_load_from_on_disk_cache: Option<fn(Tcx, DepNode)>, pub try_load_from_on_disk_cache: Option<fn(Tcx, DepNode)>,

View file

@ -5,13 +5,14 @@ use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint};
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::profiling::{QueryInvocationId, SelfProfilerRef}; use rustc_data_structures::profiling::{QueryInvocationId, SelfProfilerRef};
use rustc_data_structures::sharded::{self, ShardedHashMap}; use rustc_data_structures::sharded::{self, ShardedHashMap};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::{AtomicU64, Lock}; use rustc_data_structures::sync::{AtomicU64, Lock};
use rustc_data_structures::unord::UnordMap; use rustc_data_structures::unord::UnordMap;
use rustc_errors::DiagInner;
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_macros::{Decodable, Encodable}; use rustc_macros::{Decodable, Encodable};
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
@ -91,8 +92,6 @@ pub(crate) struct DepGraphData<D: Deps> {
colors: DepNodeColorMap, colors: DepNodeColorMap,
processed_side_effects: Lock<FxHashSet<DepNodeIndex>>,
/// When we load, there may be `.o` files, cached MIR, or other such /// When we load, there may be `.o` files, cached MIR, or other such
/// things available to us. If we find that they are not dirty, we /// things available to us. If we find that they are not dirty, we
/// load the path to the file storing those work-products here into /// load the path to the file storing those work-products here into
@ -174,7 +173,6 @@ impl<D: Deps> DepGraph<D> {
previous_work_products: prev_work_products, previous_work_products: prev_work_products,
dep_node_debug: Default::default(), dep_node_debug: Default::default(),
current, current,
processed_side_effects: Default::default(),
previous: prev_graph, previous: prev_graph,
colors, colors,
debug_loaded_from_disk: Default::default(), debug_loaded_from_disk: Default::default(),
@ -535,6 +533,24 @@ impl<D: Deps> DepGraph<D> {
} }
} }
#[inline]
pub fn record_diagnostic<Qcx: QueryContext>(&self, qcx: Qcx, diagnostic: &DiagInner) {
if let Some(ref data) = self.data {
self.read_index(data.encode_diagnostic(qcx, diagnostic));
}
}
#[inline]
pub fn force_diagnostic_node<Qcx: QueryContext>(
&self,
qcx: Qcx,
prev_index: SerializedDepNodeIndex,
) {
if let Some(ref data) = self.data {
data.force_diagnostic_node(qcx, prev_index);
}
}
/// Create a node when we force-feed a value into the query cache. /// Create a node when we force-feed a value into the query cache.
/// This is used to remove cycles during type-checking const generic parameters. /// This is used to remove cycles during type-checking const generic parameters.
/// ///
@ -656,6 +672,48 @@ impl<D: Deps> DepGraphData<D> {
pub(crate) fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) { pub(crate) fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) {
self.debug_loaded_from_disk.lock().insert(dep_node); self.debug_loaded_from_disk.lock().insert(dep_node);
} }
#[inline]
fn encode_diagnostic<Qcx: QueryContext>(
&self,
qcx: Qcx,
diagnostic: &DiagInner,
) -> DepNodeIndex {
let dep_node_index = self.current.encoder.send(
DepNode {
kind: D::DEP_KIND_SIDE_EFFECT,
hash: PackedFingerprint::from(Fingerprint::ZERO),
},
Fingerprint::ZERO,
// We want the side effect node to always be red so it will be forced and emit the
// diagnostic.
std::iter::once(DepNodeIndex::FOREVER_RED_NODE).collect(),
);
let side_effects = QuerySideEffects { diagnostic: diagnostic.clone() };
qcx.store_side_effects(dep_node_index, side_effects);
dep_node_index
}
#[inline]
fn force_diagnostic_node<Qcx: QueryContext>(
&self,
qcx: Qcx,
prev_index: SerializedDepNodeIndex,
) {
D::with_deps(TaskDepsRef::Ignore, || {
let side_effects = qcx.load_side_effects(prev_index).unwrap();
qcx.dep_context().sess().dcx().emit_diagnostic(side_effects.diagnostic.clone());
// Promote the previous diagnostics to the current session.
let index = self.current.promote_node_and_deps_to_current(&self.previous, prev_index);
// FIXME: Can this race with a parallel compiler?
qcx.store_side_effects(index, side_effects);
// Mark the node as green.
self.colors.insert(prev_index, DepNodeColor::Green(index));
})
}
} }
impl<D: Deps> DepGraph<D> { impl<D: Deps> DepGraph<D> {
@ -794,7 +852,7 @@ impl<D: Deps> DepGraphData<D> {
// We failed to mark it green, so we try to force the query. // We failed to mark it green, so we try to force the query.
debug!("trying to force dependency {dep_dep_node:?}"); debug!("trying to force dependency {dep_dep_node:?}");
if !qcx.dep_context().try_force_from_dep_node(*dep_dep_node, frame) { if !qcx.dep_context().try_force_from_dep_node(*dep_dep_node, parent_dep_node_index, frame) {
// The DepNode could not be forced. // The DepNode could not be forced.
debug!("dependency {dep_dep_node:?} could not be forced"); debug!("dependency {dep_dep_node:?} could not be forced");
return None; return None;
@ -867,16 +925,6 @@ impl<D: Deps> DepGraphData<D> {
// ... emitting any stored diagnostic ... // ... emitting any stored diagnostic ...
// FIXME: Store the fact that a node has diagnostics in a bit in the dep graph somewhere
// Maybe store a list on disk and encode this fact in the DepNodeState
let side_effects = qcx.load_side_effects(prev_dep_node_index);
if side_effects.maybe_any() {
qcx.dep_context().dep_graph().with_query_deserialization(|| {
self.emit_side_effects(qcx, dep_node_index, side_effects)
});
}
// ... and finally storing a "Green" entry in the color map. // ... and finally storing a "Green" entry in the color map.
// Multiple threads can all write the same color here // Multiple threads can all write the same color here
self.colors.insert(prev_dep_node_index, DepNodeColor::Green(dep_node_index)); self.colors.insert(prev_dep_node_index, DepNodeColor::Green(dep_node_index));
@ -884,33 +932,6 @@ impl<D: Deps> DepGraphData<D> {
debug!("successfully marked {dep_node:?} as green"); debug!("successfully marked {dep_node:?} as green");
Some(dep_node_index) Some(dep_node_index)
} }
/// Atomically emits some loaded diagnostics.
/// This may be called concurrently on multiple threads for the same dep node.
#[cold]
#[inline(never)]
fn emit_side_effects<Qcx: QueryContext<Deps = D>>(
&self,
qcx: Qcx,
dep_node_index: DepNodeIndex,
side_effects: QuerySideEffects,
) {
let mut processed = self.processed_side_effects.lock();
if processed.insert(dep_node_index) {
// We were the first to insert the node in the set so this thread
// must process side effects
// Promote the previous diagnostics to the current session.
qcx.store_side_effects(dep_node_index, side_effects.clone());
let dcx = qcx.dep_context().sess().dcx();
for diagnostic in side_effects.diagnostics {
dcx.emit_diagnostic(diagnostic);
}
}
}
} }
impl<D: Deps> DepGraph<D> { impl<D: Deps> DepGraph<D> {

View file

@ -58,10 +58,15 @@ pub trait DepContext: Copy {
/// dep-node or when the query kind outright does not support it. /// dep-node or when the query kind outright does not support it.
#[inline] #[inline]
#[instrument(skip(self, frame), level = "debug")] #[instrument(skip(self, frame), level = "debug")]
fn try_force_from_dep_node(self, dep_node: DepNode, frame: Option<&MarkFrame<'_>>) -> bool { fn try_force_from_dep_node(
self,
dep_node: DepNode,
prev_index: SerializedDepNodeIndex,
frame: Option<&MarkFrame<'_>>,
) -> bool {
let cb = self.dep_kind_info(dep_node.kind); let cb = self.dep_kind_info(dep_node.kind);
if let Some(f) = cb.force_from_dep_node { if let Some(f) = cb.force_from_dep_node {
match panic::catch_unwind(panic::AssertUnwindSafe(|| f(self, dep_node))) { match panic::catch_unwind(panic::AssertUnwindSafe(|| f(self, dep_node, prev_index))) {
Err(value) => { Err(value) => {
if !value.is::<rustc_errors::FatalErrorMarker>() { if !value.is::<rustc_errors::FatalErrorMarker>() {
print_markframe_trace(self.dep_graph(), frame); print_markframe_trace(self.dep_graph(), frame);
@ -101,6 +106,9 @@ pub trait Deps {
/// We use this to create a forever-red node. /// We use this to create a forever-red node.
const DEP_KIND_RED: DepKind; const DEP_KIND_RED: DepKind;
/// We use this to create a side effect node.
const DEP_KIND_SIDE_EFFECT: DepKind;
/// This is the highest value a `DepKind` can have. It's used during encoding to /// This is the highest value a `DepKind` can have. It's used during encoding to
/// pack information into the unused bits. /// pack information into the unused bits.
const DEP_KIND_MAX: u16; const DEP_KIND_MAX: u16;

View file

@ -11,14 +11,12 @@ mod caches;
pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache}; pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache};
mod config; mod config;
use rustc_data_structures::sync::Lock;
use rustc_errors::DiagInner; use rustc_errors::DiagInner;
use rustc_hashes::Hash64; use rustc_hashes::Hash64;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_macros::{Decodable, Encodable}; use rustc_macros::{Decodable, Encodable};
use rustc_span::Span; use rustc_span::Span;
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use thin_vec::ThinVec;
pub use self::config::{HashResult, QueryConfig}; pub use self::config::{HashResult, QueryConfig};
use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
@ -70,27 +68,12 @@ impl QueryStackFrame {
/// This allows us to 'replay' changes to global state /// This allows us to 'replay' changes to global state
/// that would otherwise only occur if we actually /// that would otherwise only occur if we actually
/// executed the query method. /// executed the query method.
#[derive(Debug, Clone, Default, Encodable, Decodable)] #[derive(Debug, Encodable, Decodable)]
pub struct QuerySideEffects { pub struct QuerySideEffects {
/// Stores any diagnostics emitted during query execution. /// Stores any diagnostics emitted during query execution.
/// These diagnostics will be re-emitted if we mark /// These diagnostics will be re-emitted if we mark
/// the query as green. /// the query as green.
pub(super) diagnostics: ThinVec<DiagInner>, pub(super) diagnostic: DiagInner,
}
impl QuerySideEffects {
/// Returns true if there might be side effects.
#[inline]
pub fn maybe_any(&self) -> bool {
let QuerySideEffects { diagnostics } = self;
// Use `has_capacity` so that the destructor for `self.diagnostics` can be skipped
// if `maybe_any` is known to be false.
diagnostics.has_capacity()
}
pub fn append(&mut self, other: QuerySideEffects) {
let QuerySideEffects { diagnostics } = self;
diagnostics.extend(other.diagnostics);
}
} }
pub trait QueryContext: HasDepContext { pub trait QueryContext: HasDepContext {
@ -102,28 +85,18 @@ pub trait QueryContext: HasDepContext {
fn collect_active_jobs(self) -> QueryMap; fn collect_active_jobs(self) -> QueryMap;
/// Load side effects associated to the node in the previous session. /// Load side effects associated to the node in the previous session.
fn load_side_effects(self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects; fn load_side_effects(
self,
prev_dep_node_index: SerializedDepNodeIndex,
) -> Option<QuerySideEffects>;
/// Register diagnostics for the given node, for use in next session. /// Register diagnostics for the given node, for use in next session.
fn store_side_effects(self, dep_node_index: DepNodeIndex, side_effects: QuerySideEffects); fn store_side_effects(self, dep_node_index: DepNodeIndex, side_effects: QuerySideEffects);
/// Register diagnostics for the given node, for use in next session.
fn store_side_effects_for_anon_node(
self,
dep_node_index: DepNodeIndex,
side_effects: QuerySideEffects,
);
/// Executes a job by changing the `ImplicitCtxt` to point to the /// Executes a job by changing the `ImplicitCtxt` to point to the
/// new query job while it executes. It returns the diagnostics /// new query job while it executes.
/// captured during execution and the actual result. fn start_query<R>(self, token: QueryJobId, depth_limit: bool, compute: impl FnOnce() -> R)
fn start_query<R>( -> R;
self,
token: QueryJobId,
depth_limit: bool,
diagnostics: Option<&Lock<ThinVec<DiagInner>>>,
compute: impl FnOnce() -> R,
) -> R;
fn depth_limit_error(self, job: QueryJobId); fn depth_limit_error(self, job: QueryJobId);
} }

View file

@ -12,11 +12,9 @@ use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sharded::Sharded; use rustc_data_structures::sharded::Sharded;
use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::Lock;
use rustc_data_structures::{outline, sync}; use rustc_data_structures::{outline, sync};
use rustc_errors::{Diag, FatalError, StashKey}; use rustc_errors::{Diag, FatalError, StashKey};
use rustc_span::{DUMMY_SP, Span}; use rustc_span::{DUMMY_SP, Span};
use thin_vec::ThinVec;
use tracing::instrument; use tracing::instrument;
use super::QueryConfig; use super::QueryConfig;
@ -25,9 +23,7 @@ use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeP
use crate::ich::StableHashingContext; use crate::ich::StableHashingContext;
use crate::query::caches::QueryCache; use crate::query::caches::QueryCache;
use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle}; use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle};
use crate::query::{ use crate::query::{QueryContext, QueryMap, QueryStackFrame, SerializedDepNodeIndex};
QueryContext, QueryMap, QuerySideEffects, QueryStackFrame, SerializedDepNodeIndex,
};
pub struct QueryState<K> { pub struct QueryState<K> {
active: Sharded<FxHashMap<K, QueryResult>>, active: Sharded<FxHashMap<K, QueryResult>>,
@ -470,7 +466,7 @@ where
} }
let prof_timer = qcx.dep_context().profiler().query_provider(); let prof_timer = qcx.dep_context().profiler().query_provider();
let result = qcx.start_query(job_id, query.depth_limit(), None, || query.compute(qcx, key)); let result = qcx.start_query(job_id, query.depth_limit(), || query.compute(qcx, key));
let dep_node_index = qcx.dep_context().dep_graph().next_virtual_depnode_index(); let dep_node_index = qcx.dep_context().dep_graph().next_virtual_depnode_index();
prof_timer.finish_with_query_invocation_id(dep_node_index.into()); prof_timer.finish_with_query_invocation_id(dep_node_index.into());
@ -507,7 +503,7 @@ where
// The diagnostics for this query will be promoted to the current session during // The diagnostics for this query will be promoted to the current session during
// `try_mark_green()`, so we can ignore them here. // `try_mark_green()`, so we can ignore them here.
if let Some(ret) = qcx.start_query(job_id, false, None, || { if let Some(ret) = qcx.start_query(job_id, false, || {
try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, dep_node) try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, dep_node)
}) { }) {
return ret; return ret;
@ -515,43 +511,31 @@ where
} }
let prof_timer = qcx.dep_context().profiler().query_provider(); let prof_timer = qcx.dep_context().profiler().query_provider();
let diagnostics = Lock::new(ThinVec::new());
let (result, dep_node_index) = let (result, dep_node_index) = qcx.start_query(job_id, query.depth_limit(), || {
qcx.start_query(job_id, query.depth_limit(), Some(&diagnostics), || { if query.anon() {
if query.anon() { return dep_graph_data.with_anon_task_inner(
return dep_graph_data.with_anon_task_inner( *qcx.dep_context(),
*qcx.dep_context(), query.dep_kind(),
query.dep_kind(), || query.compute(qcx, key),
|| query.compute(qcx, key), );
); }
}
// `to_dep_node` is expensive for some `DepKind`s. // `to_dep_node` is expensive for some `DepKind`s.
let dep_node = let dep_node =
dep_node_opt.unwrap_or_else(|| query.construct_dep_node(*qcx.dep_context(), &key)); dep_node_opt.unwrap_or_else(|| query.construct_dep_node(*qcx.dep_context(), &key));
dep_graph_data.with_task( dep_graph_data.with_task(
dep_node, dep_node,
(qcx, query), (qcx, query),
key, key,
|(qcx, query), key| query.compute(qcx, key), |(qcx, query), key| query.compute(qcx, key),
query.hash_result(), query.hash_result(),
) )
}); });
prof_timer.finish_with_query_invocation_id(dep_node_index.into()); prof_timer.finish_with_query_invocation_id(dep_node_index.into());
let side_effects = QuerySideEffects { diagnostics: diagnostics.into_inner() };
if std::intrinsics::unlikely(side_effects.maybe_any()) {
if query.anon() {
qcx.store_side_effects_for_anon_node(dep_node_index, side_effects);
} else {
qcx.store_side_effects(dep_node_index, side_effects);
}
}
(result, dep_node_index) (result, dep_node_index)
} }