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

@@ -64,7 +64,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd,
use rustc_hir::definitions::DefPathHash;
use rustc_macros::{Decodable, Encodable};
use super::{DepContext, FingerprintStyle};
use super::{DepContext, FingerprintStyle, SerializedDepNodeIndex};
use crate::ich::StableHashingContext;
/// 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`
/// is actually a `DefPathHash`, and can therefore just look up the corresponding
/// `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.
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::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::profiling::{QueryInvocationId, SelfProfilerRef};
use rustc_data_structures::sharded::{self, ShardedHashMap};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::{AtomicU64, Lock};
use rustc_data_structures::unord::UnordMap;
use rustc_errors::DiagInner;
use rustc_index::IndexVec;
use rustc_macros::{Decodable, Encodable};
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
@@ -91,8 +92,6 @@ pub(crate) struct DepGraphData<D: Deps> {
colors: DepNodeColorMap,
processed_side_effects: Lock<FxHashSet<DepNodeIndex>>,
/// 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
/// 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,
dep_node_debug: Default::default(),
current,
processed_side_effects: Default::default(),
previous: prev_graph,
colors,
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.
/// 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) {
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> {
@@ -794,7 +852,7 @@ impl<D: Deps> DepGraphData<D> {
// We failed to mark it green, so we try to force the query.
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.
debug!("dependency {dep_dep_node:?} could not be forced");
return None;
@@ -867,16 +925,6 @@ impl<D: Deps> DepGraphData<D> {
// ... 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.
// Multiple threads can all write the same color here
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");
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> {

View File

@@ -58,10 +58,15 @@ pub trait DepContext: Copy {
/// dep-node or when the query kind outright does not support it.
#[inline]
#[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);
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) => {
if !value.is::<rustc_errors::FatalErrorMarker>() {
print_markframe_trace(self.dep_graph(), frame);
@@ -101,6 +106,9 @@ pub trait Deps {
/// We use this to create a forever-red node.
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
/// pack information into the unused bits.
const DEP_KIND_MAX: u16;