use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use crate::assert_matches;
use core::cell::Cell;
use core::fmt::Debug;
use core::hash::Hash;
use alloc::sync::Arc;
use core::sync::atomic::{AtomicU32, Ordering};
use crate::rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint};
use crate::rustc_data_structures::fx::FxHashSet;
use crate::rustc_data_structures::profiling::QueryInvocationId;
use crate::rustc_data_structures::sharded::ShardedHashMap;
use crate::rustc_data_structures::stable_hash::{StableHash, StableHasher};
use crate::rustc_data_structures::sync::{AtomicU64, Lock, WorkerLocal};
use crate::rustc_data_structures::unord::UnordMap;
use crate::rustc_errors::DiagInner;
use crate::rustc_index::IndexVec;
use rustc_macros::{Decodable, Encodable};
use crate::rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
use crate::rustc_session::Session;
use crate::rustc_span::Symbol;
use smallvec::SmallVec;
use tracing::instrument;
#[cfg(debug_assertions)]
use {super::debug::EdgeFilter, eko::env};
use super::edges::{ReadsRecorder, SMALL_READS_MAX, TaskReads};
use super::retained::RetainedDepGraph;
use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex};
use super::{DepKind, DepNode, WorkProductId, read_deps, with_deps};
use crate::rustc_middle::ich::StableHashState;
use crate::rustc_middle::ty::TyCtxt;
use crate::rustc_middle::verify_ich::incremental_verify_ich;
#[derive(Debug, Encodable, Decodable)]
pub enum QuerySideEffect {
Diagnostic(DiagInner),
CheckFeature { symbol: Symbol },
}
#[derive(Clone)]
pub struct DepGraph {
data: Option<Arc<DepGraphData>>,
virtual_dep_node_index: Arc<AtomicU32>,
}
crate::rustc_index::newtype_index! {
pub struct DepNodeIndex {}
}
crate::static_assert_size!(Option<DepNodeIndex>, 8);
impl DepNodeIndex {
const SINGLETON_ZERO_DEPS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO;
pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1);
}
impl From<DepNodeIndex> for QueryInvocationId {
#[inline(always)]
fn from(dep_node_index: DepNodeIndex) -> Self {
QueryInvocationId(dep_node_index.as_u32())
}
}
pub(crate) struct MarkFrame<'a> {
index: SerializedDepNodeIndex,
parent: Option<&'a MarkFrame<'a>>,
}
struct EdgeFrame<'a> {
buf: &'a mut Vec<DepNodeIndex>,
start: usize,
}
impl<'a> EdgeFrame<'a> {
#[inline]
fn new(buf: &'a mut Vec<DepNodeIndex>) -> Self {
EdgeFrame { start: buf.len(), buf }
}
#[inline]
fn push(&mut self, edge: DepNodeIndex) {
self.buf.push(edge);
}
#[inline]
fn get(&self) -> &[DepNodeIndex] {
&self.buf[self.start..]
}
}
impl Drop for EdgeFrame<'_> {
#[inline]
fn drop(&mut self) {
self.buf.truncate(self.start);
}
}
#[derive(Debug)]
pub(super) enum DepNodeColor {
Green(DepNodeIndex),
Red,
Unknown,
}
pub struct DepGraphData {
current: CurrentDepGraph,
previous: Arc<SerializedDepGraph>,
colors: DepNodeColorMap,
previous_work_products: WorkProductMap,
debug_loaded_from_disk: Lock<FxHashSet<DepNode>>,
green_edge_buf: WorkerLocal<Cell<Vec<DepNodeIndex>>>,
read_recorder_pool: Lock<Vec<ReadsRecorder>>,
}
pub fn hash_result<R>(hcx: &mut StableHashState<'_>, result: &R) -> Fingerprint
where
R: StableHash,
{
let mut stable_hasher = StableHasher::new();
result.stable_hash(hcx, &mut stable_hasher);
stable_hasher.finish()
}
impl DepGraph {
pub fn new(
session: &Session,
prev_graph: Arc<SerializedDepGraph>,
prev_work_products: WorkProductMap,
encoder: FileEncoder<'static>,
) -> DepGraph {
let prev_index_space_len = prev_graph.index_space_len();
let current =
CurrentDepGraph::new(session, prev_index_space_len, encoder, Arc::clone(&prev_graph));
let colors = DepNodeColorMap::new(prev_index_space_len);
let _green_node_index = current.alloc_new_node(
DepNode { kind: DepKind::AnonZeroDeps, key_fingerprint: current.anon_id_seed.into() },
&[],
Fingerprint::ZERO,
);
assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE);
let red_node_index = current.alloc_new_node(
DepNode { kind: DepKind::Red, key_fingerprint: Fingerprint::ZERO.into() },
&[],
Fingerprint::ZERO,
);
assert_eq!(red_node_index, DepNodeIndex::FOREVER_RED_NODE);
if prev_index_space_len > 0 {
let prev_index =
const { SerializedDepNodeIndex::from_u32(DepNodeIndex::FOREVER_RED_NODE.as_u32()) };
let result = colors.try_set_color(prev_index, DesiredColor::Red);
assert_matches!(result, TrySetColorResult::Success);
}
DepGraph {
data: Some(Arc::new(DepGraphData {
previous_work_products: prev_work_products,
current,
previous: prev_graph,
colors,
debug_loaded_from_disk: Default::default(),
green_edge_buf: WorkerLocal::default(),
read_recorder_pool: Lock::new(Vec::new()),
})),
virtual_dep_node_index: Arc::new(AtomicU32::new(0)),
}
}
pub fn new_disabled() -> DepGraph {
DepGraph { data: None, virtual_dep_node_index: Arc::new(AtomicU32::new(0)) }
}
#[inline]
pub fn data(&self) -> Option<&DepGraphData> {
self.data.as_deref()
}
#[inline]
pub fn is_fully_enabled(&self) -> bool {
self.data.is_some()
}
pub fn retained_dep_graph(&self) -> Option<RetainedDepGraph> {
self.data.as_ref().and_then(|data| data.current.encoder.retained_dep_graph())
}
pub fn assert_ignored(&self) {
if let Some(..) = self.data {
read_deps(|task_deps| {
assert_matches!(
task_deps,
TaskDepsRef::Ignore,
"expected no task dependency tracking"
);
})
}
}
pub fn assert_eval_always(&self) {
if self.data.is_some() {
read_deps(|deps| {
assert_matches!(deps, TaskDepsRef::EvalAlways, "expected eval always context")
});
}
}
pub fn with_ignore<OP, R>(&self, op: OP) -> R
where
OP: FnOnce() -> R,
{
with_deps(TaskDepsRef::Ignore, op)
}
pub fn with_query_deserialization<OP, R>(&self, op: OP) -> R
where
OP: FnOnce() -> R,
{
with_deps(TaskDepsRef::Forbid, op)
}
#[inline(always)]
pub fn with_task<'tcx, OP, R>(
&self,
dep_node: DepNode,
tcx: TyCtxt<'tcx>,
op: OP,
hash_result: Option<fn(&mut StableHashState<'_>, &R) -> Fingerprint>,
) -> (R, DepNodeIndex)
where
OP: FnOnce() -> R,
{
match self.data() {
Some(data) => data.with_task(dep_node, tcx, op, hash_result),
None => (op(), self.next_virtual_depnode_index()),
}
}
pub fn with_anon_task<'tcx, OP, R>(
&self,
tcx: TyCtxt<'tcx>,
dep_kind: DepKind,
op: OP,
) -> (R, DepNodeIndex)
where
OP: FnOnce() -> R,
{
match self.data() {
Some(data) => {
let (result, index) = data.with_anon_task_inner(tcx, dep_kind, op);
self.read_index(index);
(result, index)
}
None => (op(), self.next_virtual_depnode_index()),
}
}
}
impl DepGraphData {
#[inline(always)]
pub fn with_task<'tcx, OP, R>(
&self,
dep_node: DepNode,
tcx: TyCtxt<'tcx>,
op: OP,
hash_result: Option<fn(&mut StableHashState<'_>, &R) -> Fingerprint>,
) -> (R, DepNodeIndex)
where
OP: FnOnce() -> R,
{
self.assert_dep_node_not_yet_allocated_in_current_session(tcx.sess, &dep_node, || {
format!("forcing query with already existing `DepNode`: {dep_node:?}")
});
let (result, task_deps) = if tcx.is_eval_always(dep_node.kind) {
(with_deps(TaskDepsRef::EvalAlways, op), None)
} else {
let task_deps = Lock::new(TaskDeps::new(
#[cfg(debug_assertions)]
Some(dep_node),
));
(with_deps(TaskDepsRef::Allow(&task_deps), op), Some(task_deps.into_inner()))
};
let edges: &[DepNodeIndex] = task_deps.as_ref().map_or(&[], |deps| deps.edges());
let dep_node_index =
self.hash_result_and_alloc_node(tcx, dep_node, edges, &result, hash_result);
if let Some(TaskDeps { reads: TaskReads::Recorded(recorder), .. }) = task_deps {
recorder.release(&self.read_recorder_pool);
}
(result, dep_node_index)
}
fn with_anon_task_inner<'tcx, OP, R>(
&self,
tcx: TyCtxt<'tcx>,
dep_kind: DepKind,
op: OP,
) -> (R, DepNodeIndex)
where
OP: FnOnce() -> R,
{
debug_assert!(!tcx.is_eval_always(dep_kind));
let task_deps = Lock::new(TaskDeps::new(
#[cfg(debug_assertions)]
None,
));
let result = with_deps(TaskDepsRef::Allow(&task_deps), op);
let task_deps = task_deps.into_inner();
let reads = task_deps.edges();
let dep_node_index = match reads.len() {
0 => {
DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE
}
1 => {
reads[0]
}
_ => {
let mut hasher = StableHasher::new();
reads.hash(&mut hasher);
let target_dep_node = DepNode {
kind: dep_kind,
key_fingerprint: self.current.anon_id_seed.combine(hasher.finish()).into(),
};
self.current.anon_node_to_index.get_or_insert_with(target_dep_node, || {
self.current.alloc_new_node(target_dep_node, reads, Fingerprint::ZERO)
})
}
};
if let TaskReads::Recorded(recorder) = task_deps.reads {
recorder.release(&self.read_recorder_pool);
}
(result, dep_node_index)
}
fn hash_result_and_alloc_node<'tcx, R>(
&self,
tcx: TyCtxt<'tcx>,
node: DepNode,
edges: &[DepNodeIndex],
result: &R,
hash_result: Option<fn(&mut StableHashState<'_>, &R) -> Fingerprint>,
) -> DepNodeIndex {
let hashing_timer = tcx.prof.incr_result_hashing();
let current_fingerprint = hash_result.map(|hash_result| {
tcx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result))
});
let dep_node_index = self.alloc_and_color_node(node, edges, current_fingerprint);
hashing_timer.finish_with_query_invocation_id(dep_node_index.into());
dep_node_index
}
}
impl DepGraph {
#[inline]
pub fn read_index(&self, dep_node_index: DepNodeIndex) {
if let Some(ref data) = self.data {
read_deps(|task_deps| {
let mut task_deps = match task_deps {
TaskDepsRef::Allow(deps) => deps.lock(),
TaskDepsRef::EvalAlways => {
return;
}
TaskDepsRef::Ignore => return,
TaskDepsRef::Forbid => {
panic_on_forbidden_read(data, dep_node_index)
}
};
let task_deps = &mut *task_deps;
if cfg!(debug_assertions) {
data.current.total_read_count.fetch_add(1, Ordering::Relaxed);
}
let new_read = task_deps.reads.insert(dep_node_index, &data.read_recorder_pool);
if new_read {
#[cfg(debug_assertions)]
{
if let Some(target) = task_deps.node
&& let Some(ref forbidden_edge) = data.current.forbidden_edge
{
let src = forbidden_edge.index_to_node.lock()[&dep_node_index];
if forbidden_edge.test(&src, &target) {
panic!("forbidden edge {:?} -> {:?} created", src, target)
}
}
}
} else if cfg!(debug_assertions) {
data.current.total_duplicate_read_count.fetch_add(1, Ordering::Relaxed);
}
})
}
}
#[inline]
pub fn record_diagnostic<'tcx>(&self, tcx: TyCtxt<'tcx>, diagnostic: &DiagInner) {
if let Some(ref data) = self.data {
read_deps(|task_deps| match task_deps {
TaskDepsRef::EvalAlways | TaskDepsRef::Ignore => return,
TaskDepsRef::Forbid | TaskDepsRef::Allow(..) => {
let dep_node_index = data
.encode_side_effect(tcx, QuerySideEffect::Diagnostic(diagnostic.clone()));
self.read_index(dep_node_index);
}
})
}
}
#[inline]
pub fn force_side_effect<'tcx>(&self, tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) {
if let Some(ref data) = self.data {
data.force_side_effect(tcx, prev_index);
}
}
#[inline]
pub fn encode_side_effect<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
side_effect: QuerySideEffect,
) -> DepNodeIndex {
if let Some(ref data) = self.data {
data.encode_side_effect(tcx, side_effect)
} else {
self.next_virtual_depnode_index()
}
}
pub fn with_feed_task<'tcx, R>(
&self,
node: DepNode,
tcx: TyCtxt<'tcx>,
result: &R,
hash_result: Option<fn(&mut StableHashState<'_>, &R) -> Fingerprint>,
format_value_fn: fn(&R) -> String,
) -> DepNodeIndex {
if let Some(data) = self.data.as_ref() {
if let Some(prev_index) = data.previous.node_to_index_opt(&node) {
let dep_node_index = data.colors.current(prev_index);
if let Some(dep_node_index) = dep_node_index {
incremental_verify_ich(
tcx,
data,
result,
prev_index,
hash_result,
format_value_fn,
);
#[cfg(debug_assertions)]
if hash_result.is_some() {
data.current.record_edge(
dep_node_index,
node,
data.prev_value_fingerprint_of(prev_index),
);
}
return dep_node_index;
}
}
let mut reads = SmallVec::<[DepNodeIndex; SMALL_READS_MAX]>::new();
read_deps(|task_deps| match task_deps {
TaskDepsRef::Allow(deps) => {
reads = SmallVec::from_slice(deps.lock().edges());
}
TaskDepsRef::EvalAlways => {
reads.push(DepNodeIndex::FOREVER_RED_NODE);
}
TaskDepsRef::Ignore => {}
TaskDepsRef::Forbid => {
panic!("Cannot summarize when dependencies are not recorded.")
}
});
data.hash_result_and_alloc_node(tcx, node, &reads, result, hash_result)
} else {
self.next_virtual_depnode_index()
}
}
}
impl DepGraphData {
fn assert_dep_node_not_yet_allocated_in_current_session<S: core::fmt::Display>(
&self,
sess: &Session,
dep_node: &DepNode,
msg: impl FnOnce() -> S,
) {
if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) {
let color = self.colors.get(prev_index);
let ok = match color {
DepNodeColor::Unknown => true,
DepNodeColor::Red => false,
DepNodeColor::Green(..) => sess.opts.jobs.frontend.is_some(), };
if !ok {
panic!("{}", msg())
}
}
}
fn node_color(&self, dep_node: &DepNode) -> DepNodeColor {
if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) {
self.colors.get(prev_index)
} else {
DepNodeColor::Unknown
}
}
#[inline]
pub fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool {
matches!(self.colors.get(prev_index), DepNodeColor::Green(_))
}
#[inline]
pub fn prev_value_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint {
self.previous.value_fingerprint_for_index(prev_index)
}
#[inline]
pub fn session_count(&self) -> u64 {
self.previous.session_count()
}
#[inline]
pub(crate) fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> &DepNode {
self.previous.index_to_node(prev_index)
}
pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) {
self.debug_loaded_from_disk.lock().insert(dep_node);
}
#[inline]
fn encode_side_effect<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
side_effect: QuerySideEffect,
) -> DepNodeIndex {
let dep_node_index = self.current.encoder.send_new(
DepNode {
kind: DepKind::SideEffect,
key_fingerprint: PackedFingerprint::from(Fingerprint::ZERO),
},
Fingerprint::ZERO,
&[DepNodeIndex::FOREVER_RED_NODE],
);
tcx.query_system.side_effects.borrow_mut().insert(dep_node_index, side_effect);
dep_node_index
}
#[inline]
fn force_side_effect<'tcx>(&self, tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) {
with_deps(TaskDepsRef::Ignore, || {
let side_effect = tcx
.query_system
.on_disk_cache
.as_ref()
.unwrap()
.load_side_effect(tcx, prev_index)
.unwrap();
let dep_node_index = self.current.encoder.send_and_color(
prev_index,
&self.colors,
DepNode {
kind: DepKind::SideEffect,
key_fingerprint: PackedFingerprint::from(Fingerprint::ZERO),
},
Fingerprint::ZERO,
&[DepNodeIndex::FOREVER_RED_NODE],
true,
);
match &side_effect {
QuerySideEffect::Diagnostic(diagnostic) => {
tcx.dcx().emit_diagnostic(diagnostic.clone());
}
QuerySideEffect::CheckFeature { symbol } => {
tcx.query_system.used_features.lock().insert(*symbol, dep_node_index);
}
}
tcx.query_system.side_effects.borrow_mut().insert(dep_node_index, side_effect);
})
}
fn alloc_and_color_node(
&self,
key: DepNode,
edges: &[DepNodeIndex],
value_fingerprint: Option<Fingerprint>,
) -> DepNodeIndex {
if let Some(prev_index) = self.previous.node_to_index_opt(&key) {
let is_green = if let Some(value_fingerprint) = value_fingerprint {
if value_fingerprint == self.previous.value_fingerprint_for_index(prev_index) {
true
} else {
false
}
} else {
false
};
let value_fingerprint = value_fingerprint.unwrap_or(Fingerprint::ZERO);
let dep_node_index = self.current.encoder.send_and_color(
prev_index,
&self.colors,
key,
value_fingerprint,
edges,
is_green,
);
#[cfg(debug_assertions)]
self.current.record_edge(dep_node_index, key, value_fingerprint);
dep_node_index
} else {
self.current.alloc_new_node(key, edges, value_fingerprint.unwrap_or(Fingerprint::ZERO))
}
}
fn promote_node_and_deps_to_current(
&self,
prev_index: SerializedDepNodeIndex,
edges: &[DepNodeIndex],
) -> Option<DepNodeIndex> {
let dep_node_index = self.current.encoder.send_promoted(prev_index, &self.colors, edges);
#[cfg(debug_assertions)]
if let Some(dep_node_index) = dep_node_index {
self.current.record_edge(
dep_node_index,
*self.previous.index_to_node(prev_index),
self.previous.value_fingerprint_for_index(prev_index),
);
}
dep_node_index
}
}
impl DepGraph {
pub fn previous_work_product(&self, v: &WorkProductId) -> Option<WorkProduct> {
self.data.as_ref().and_then(|data| data.previous_work_products.get(v).cloned())
}
pub fn previous_work_products(&self) -> &WorkProductMap {
&self.data.as_ref().unwrap().previous_work_products
}
pub fn debug_was_loaded_from_disk(&self, dep_node: DepNode) -> bool {
self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node)
}
pub fn debug_dep_kind_was_loaded_from_disk(&self, dep_kind: DepKind) -> bool {
self.data
.as_ref()
.unwrap()
.debug_loaded_from_disk
.lock()
.iter()
.any(|node| node.kind == dep_kind)
}
fn node_color(&self, dep_node: &DepNode) -> DepNodeColor {
if let Some(ref data) = self.data {
return data.node_color(dep_node);
}
DepNodeColor::Unknown
}
pub fn try_mark_green<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
dep_node: &DepNode,
) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> {
self.data()?.try_mark_green(tcx, dep_node)
}
}
impl DepGraphData {
pub fn try_mark_green<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
dep_node: &DepNode,
) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> {
debug_assert!(!tcx.is_eval_always(dep_node.kind));
let prev_index = self.previous.node_to_index_opt(dep_node)?;
debug_assert_eq!(self.previous.index_to_node(prev_index), dep_node);
match self.colors.get(prev_index) {
DepNodeColor::Green(dep_node_index) => Some((prev_index, dep_node_index)),
DepNodeColor::Red => None,
DepNodeColor::Unknown => {
let mut edge_buf = self.green_edge_buf.take();
let result = self.try_mark_previous_green(tcx, prev_index, None, &mut edge_buf);
debug_assert!(edge_buf.is_empty());
self.green_edge_buf.set(edge_buf);
result.map(|dep_node_index| (prev_index, dep_node_index))
}
}
}
#[instrument(skip(self, tcx, prev_dep_node_index, frame, edge_buf), level = "debug")]
fn try_mark_previous_green<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
prev_dep_node_index: SerializedDepNodeIndex,
frame: Option<&MarkFrame<'_>>,
edge_buf: &mut Vec<DepNodeIndex>,
) -> Option<DepNodeIndex> {
let mut edges = EdgeFrame::new(edge_buf);
let frame = MarkFrame { index: prev_dep_node_index, parent: frame };
debug_assert!(!tcx.is_eval_always(self.previous.index_to_node(prev_dep_node_index).kind));
for parent_dep_node_index in self.previous.edge_targets_from(prev_dep_node_index) {
match self.colors.get(parent_dep_node_index) {
DepNodeColor::Green(parent_index) => {
edges.push(parent_index);
continue;
}
DepNodeColor::Red => return None,
DepNodeColor::Unknown => {}
}
let parent_dep_node = self.previous.index_to_node(parent_dep_node_index);
if !tcx.is_eval_always(parent_dep_node.kind)
&& let Some(parent_index) = self.try_mark_previous_green(
tcx,
parent_dep_node_index,
Some(&frame),
edges.buf,
)
{
edges.push(parent_index);
continue;
}
if !tcx.try_force_from_dep_node(*parent_dep_node, parent_dep_node_index, &frame) {
return None;
}
match self.colors.get(parent_dep_node_index) {
DepNodeColor::Green(parent_index) => {
edges.push(parent_index);
continue;
}
DepNodeColor::Red => return None,
DepNodeColor::Unknown => {}
}
if tcx.dcx().has_errors_or_delayed_bugs().is_none() {
panic!("try_mark_previous_green() - forcing failed to set a color");
}
return None;
}
let dep_node_index =
self.promote_node_and_deps_to_current(prev_dep_node_index, edges.get())?;
Some(dep_node_index)
}
}
impl DepGraph {
pub fn is_red(&self, dep_node: &DepNode) -> bool {
matches!(self.node_color(dep_node), DepNodeColor::Red)
}
pub fn is_green(&self, dep_node: &DepNode) -> bool {
matches!(self.node_color(dep_node), DepNodeColor::Green(_))
}
pub fn assert_dep_node_not_yet_allocated_in_current_session<S: core::fmt::Display>(
&self,
sess: &Session,
dep_node: &DepNode,
msg: impl FnOnce() -> S,
) {
if let Some(data) = &self.data {
data.assert_dep_node_not_yet_allocated_in_current_session(sess, dep_node, msg)
}
}
pub fn exec_cache_promotions<'tcx>(&self, tcx: TyCtxt<'tcx>) {
let _prof_timer = tcx.prof.generic_activity("incr_comp_query_cache_promotion");
let data = self.data.as_ref().unwrap();
for prev_index in data.colors.values.indices() {
match data.colors.get(prev_index) {
DepNodeColor::Green(dep_node_index) => {
let dep_node = data.previous.index_to_node(prev_index);
if let Some(promote_fn) =
tcx.dep_kind_vtable(dep_node.kind).promote_from_disk_fn
{
promote_fn(tcx, *dep_node, prev_index, dep_node_index)
};
}
DepNodeColor::Unknown | DepNodeColor::Red => {
}
}
}
}
pub(crate) fn finish_encoding(&self) -> FileEncodeResult {
if let Some(data) = &self.data { data.current.encoder.finish(&data.current) } else { Ok(0) }
}
pub fn next_virtual_depnode_index(&self) -> DepNodeIndex {
debug_assert!(self.data.is_none());
let index = self.virtual_dep_node_index.fetch_add(1, Ordering::Relaxed);
DepNodeIndex::from_u32(index)
}
}
#[derive(Clone, Debug, Encodable, Decodable)]
pub struct WorkProduct {
pub cgu_name: String,
pub saved_files: UnordMap<String, String>,
}
pub type WorkProductMap = UnordMap<WorkProductId, WorkProduct>;
crate::rustc_index::newtype_index! {
struct EdgeIndex {}
}
pub(super) struct CurrentDepGraph {
encoder: GraphEncoder,
anon_node_to_index: ShardedHashMap<DepNode, DepNodeIndex>,
#[cfg(debug_assertions)]
value_fingerprints: Lock<IndexVec<DepNodeIndex, Option<Fingerprint>>>,
#[cfg(debug_assertions)]
forbidden_edge: Option<EdgeFilter>,
anon_id_seed: Fingerprint,
pub(super) total_read_count: AtomicU64,
pub(super) total_duplicate_read_count: AtomicU64,
}
impl CurrentDepGraph {
fn new(
session: &Session,
prev_index_space_len: usize,
encoder: FileEncoder<'static>,
previous: Arc<SerializedDepGraph>,
) -> Self {
let mut stable_hasher = StableHasher::new();
previous.session_count().hash(&mut stable_hasher);
let anon_id_seed = stable_hasher.finish();
#[cfg(debug_assertions)]
let forbidden_edge = match env::var("RUST_FORBID_DEP_GRAPH_EDGE") {
Some(s) => match EdgeFilter::new(&s) {
Ok(f) => Some(f),
Err(err) => panic!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err),
},
None => None,
};
let new_node_count_estimate = 102 * previous.live_node_count() / 100 + 200;
CurrentDepGraph {
encoder: GraphEncoder::new(session, encoder, prev_index_space_len, previous),
anon_node_to_index: ShardedHashMap::with_capacity(
3 * new_node_count_estimate / 100, ),
anon_id_seed,
#[cfg(debug_assertions)]
forbidden_edge,
#[cfg(debug_assertions)]
value_fingerprints: Lock::new(IndexVec::from_elem_n(None, new_node_count_estimate)),
total_read_count: AtomicU64::new(0),
total_duplicate_read_count: AtomicU64::new(0),
}
}
#[cfg(debug_assertions)]
fn record_edge(
&self,
dep_node_index: DepNodeIndex,
key: DepNode,
value_fingerprint: Fingerprint,
) {
if let Some(forbidden_edge) = &self.forbidden_edge {
forbidden_edge.index_to_node.lock().insert(dep_node_index, key);
}
let prior_value_fingerprint = *self
.value_fingerprints
.lock()
.get_or_insert_with(dep_node_index, || value_fingerprint);
assert_eq!(prior_value_fingerprint, value_fingerprint, "Unstable fingerprints for {key:?}");
}
#[inline(always)]
fn alloc_new_node(
&self,
key: DepNode,
edges: &[DepNodeIndex],
value_fingerprint: Fingerprint,
) -> DepNodeIndex {
let dep_node_index = self.encoder.send_new(key, value_fingerprint, edges);
#[cfg(debug_assertions)]
self.record_edge(dep_node_index, key, value_fingerprint);
dep_node_index
}
}
#[derive(Debug, Clone, Copy)]
pub enum TaskDepsRef<'a> {
Allow(&'a Lock<TaskDeps>),
EvalAlways,
Ignore,
Forbid,
}
#[derive(Debug)]
pub struct TaskDeps {
#[cfg(debug_assertions)]
node: Option<DepNode>,
reads: TaskReads,
}
impl TaskDeps {
#[inline]
fn new(#[cfg(debug_assertions)] node: Option<DepNode>) -> Self {
TaskDeps {
#[cfg(debug_assertions)]
node,
reads: TaskReads::new(),
}
}
#[inline]
fn edges(&self) -> &[DepNodeIndex] {
self.reads.edges()
}
}
pub(super) struct DepNodeColorMap {
values: IndexVec<SerializedDepNodeIndex, AtomicU32>,
}
const COMPRESSED_RED: u32 = u32::MAX - 1;
const COMPRESSED_UNKNOWN: u32 = u32::MAX;
impl DepNodeColorMap {
fn new(size: usize) -> DepNodeColorMap {
debug_assert!(COMPRESSED_RED > DepNodeIndex::MAX_AS_U32);
DepNodeColorMap { values: (0..size).map(|_| AtomicU32::new(COMPRESSED_UNKNOWN)).collect() }
}
#[inline]
pub(super) fn current(&self, index: SerializedDepNodeIndex) -> Option<DepNodeIndex> {
let value = self.values[index].load(Ordering::Relaxed);
if value <= DepNodeIndex::MAX_AS_U32 { Some(DepNodeIndex::from_u32(value)) } else { None }
}
#[inline(always)]
pub(super) fn try_set_color(
&self,
prev_index: SerializedDepNodeIndex,
color: DesiredColor,
) -> TrySetColorResult {
match self.values[prev_index].compare_exchange(
COMPRESSED_UNKNOWN,
match color {
DesiredColor::Red => COMPRESSED_RED,
DesiredColor::Green { index } => index.as_u32(),
},
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => TrySetColorResult::Success,
Err(COMPRESSED_RED) => TrySetColorResult::AlreadyRed,
Err(index) => TrySetColorResult::AlreadyGreen { index: DepNodeIndex::from_u32(index) },
}
}
#[inline]
pub(super) fn get(&self, index: SerializedDepNodeIndex) -> DepNodeColor {
let value = self.values[index].load(Ordering::Acquire);
if value < COMPRESSED_RED {
DepNodeColor::Green(DepNodeIndex::from_u32(value))
} else if value == COMPRESSED_RED {
DepNodeColor::Red
} else {
debug_assert_eq!(value, COMPRESSED_UNKNOWN);
DepNodeColor::Unknown
}
}
}
#[derive(Clone, Copy, Debug)]
pub(super) enum DesiredColor {
Red,
Green { index: DepNodeIndex },
}
#[derive(Clone, Copy, Debug)]
pub(super) enum TrySetColorResult {
Success,
AlreadyRed,
AlreadyGreen { index: DepNodeIndex },
}
#[inline(never)]
#[cold]
pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: &MarkFrame<'_>) {
let data = graph.data.as_ref().unwrap();
eko::eprintln!("there was a panic while trying to force a dep node");
eko::eprintln!("try_mark_green dep node stack:");
let mut i = 0;
let mut current = Some(frame);
while let Some(frame) = current {
let node = data.previous.index_to_node(frame.index);
eko::eprintln!("#{i} {node:?}");
current = frame.parent;
i += 1;
}
eko::eprintln!("end of try_mark_green dep node stack");
}
#[cold]
#[inline(never)]
fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepNodeIndex) -> ! {
let mut dep_node = None;
for prev_index in data.colors.values.indices() {
if data.colors.current(prev_index) == Some(dep_node_index) {
dep_node = Some(*data.previous.index_to_node(prev_index));
break;
}
}
let dep_node = dep_node.map_or_else(
|| format!("with index {:?}", dep_node_index),
|dep_node| format!("`{:?}`", dep_node),
);
panic!(
"Error: trying to record dependency on DepNode {dep_node} in a \
context that does not allow it (e.g. during query deserialization). \
The most common case of recording a dependency on a DepNode `foo` is \
when the corresponding query `foo` is invoked. Invoking queries is not \
allowed as part of loading something from the incremental on-disk cache. \
See <https://github.com/rust-lang/rust/pull/91919>."
)
}
impl<'tcx> TyCtxt<'tcx> {
#[inline(always)]
fn is_eval_always(self, kind: DepKind) -> bool {
self.dep_kind_vtable(kind).is_eval_always
}
}