rustc_query_system/dep_graph/graph.rs
1use std::assert_matches::assert_matches;
2use std::fmt::Debug;
3use std::hash::Hash;
4use std::marker::PhantomData;
5use std::sync::Arc;
6use std::sync::atomic::{AtomicU32, Ordering};
7
8use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint};
9use rustc_data_structures::fx::{FxHashMap, FxHashSet};
10use rustc_data_structures::outline;
11use rustc_data_structures::profiling::QueryInvocationId;
12use rustc_data_structures::sharded::{self, ShardedHashMap};
13use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
14use rustc_data_structures::sync::{AtomicU64, Lock};
15use rustc_data_structures::unord::UnordMap;
16use rustc_errors::DiagInner;
17use rustc_index::IndexVec;
18use rustc_macros::{Decodable, Encodable};
19use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
20use rustc_session::Session;
21use tracing::{debug, instrument};
22#[cfg(debug_assertions)]
23use {super::debug::EdgeFilter, std::env};
24
25use super::query::DepGraphQuery;
26use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex};
27use super::{DepContext, DepKind, DepNode, Deps, HasDepContext, WorkProductId};
28use crate::dep_graph::edges::EdgesVec;
29use crate::ich::StableHashingContext;
30use crate::query::{QueryContext, QuerySideEffect};
31
32#[derive(Clone)]
33pub struct DepGraph<D: Deps> {
34 data: Option<Arc<DepGraphData<D>>>,
35
36 /// This field is used for assigning DepNodeIndices when running in
37 /// non-incremental mode. Even in non-incremental mode we make sure that
38 /// each task has a `DepNodeIndex` that uniquely identifies it. This unique
39 /// ID is used for self-profiling.
40 virtual_dep_node_index: Arc<AtomicU32>,
41}
42
43rustc_index::newtype_index! {
44 pub struct DepNodeIndex {}
45}
46
47// We store a large collection of these in `prev_index_to_index` during
48// non-full incremental builds, and want to ensure that the element size
49// doesn't inadvertently increase.
50rustc_data_structures::static_assert_size!(Option<DepNodeIndex>, 4);
51
52impl DepNodeIndex {
53 const SINGLETON_ZERO_DEPS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO;
54 pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1);
55}
56
57impl From<DepNodeIndex> for QueryInvocationId {
58 #[inline(always)]
59 fn from(dep_node_index: DepNodeIndex) -> Self {
60 QueryInvocationId(dep_node_index.as_u32())
61 }
62}
63
64pub struct MarkFrame<'a> {
65 index: SerializedDepNodeIndex,
66 parent: Option<&'a MarkFrame<'a>>,
67}
68
69#[derive(Debug)]
70pub(super) enum DepNodeColor {
71 Green(DepNodeIndex),
72 Red,
73 Unknown,
74}
75
76pub(crate) struct DepGraphData<D: Deps> {
77 /// The new encoding of the dependency graph, optimized for red/green
78 /// tracking. The `current` field is the dependency graph of only the
79 /// current compilation session: We don't merge the previous dep-graph into
80 /// current one anymore, but we do reference shared data to save space.
81 current: CurrentDepGraph<D>,
82
83 /// The dep-graph from the previous compilation session. It contains all
84 /// nodes and edges as well as all fingerprints of nodes that have them.
85 previous: Arc<SerializedDepGraph>,
86
87 colors: DepNodeColorMap,
88
89 /// When we load, there may be `.o` files, cached MIR, or other such
90 /// things available to us. If we find that they are not dirty, we
91 /// load the path to the file storing those work-products here into
92 /// this map. We can later look for and extract that data.
93 previous_work_products: WorkProductMap,
94
95 dep_node_debug: Lock<FxHashMap<DepNode, String>>,
96
97 /// Used by incremental compilation tests to assert that
98 /// a particular query result was decoded from disk
99 /// (not just marked green)
100 debug_loaded_from_disk: Lock<FxHashSet<DepNode>>,
101}
102
103pub fn hash_result<R>(hcx: &mut StableHashingContext<'_>, result: &R) -> Fingerprint
104where
105 R: for<'a> HashStable<StableHashingContext<'a>>,
106{
107 let mut stable_hasher = StableHasher::new();
108 result.hash_stable(hcx, &mut stable_hasher);
109 stable_hasher.finish()
110}
111
112impl<D: Deps> DepGraph<D> {
113 pub fn new(
114 session: &Session,
115 prev_graph: Arc<SerializedDepGraph>,
116 prev_work_products: WorkProductMap,
117 encoder: FileEncoder,
118 ) -> DepGraph<D> {
119 let prev_graph_node_count = prev_graph.node_count();
120
121 let current =
122 CurrentDepGraph::new(session, prev_graph_node_count, encoder, Arc::clone(&prev_graph));
123
124 let colors = DepNodeColorMap::new(prev_graph_node_count);
125
126 // Instantiate a node with zero dependencies only once for anonymous queries.
127 let _green_node_index = current.alloc_new_node(
128 DepNode { kind: D::DEP_KIND_ANON_ZERO_DEPS, hash: current.anon_id_seed.into() },
129 EdgesVec::new(),
130 Fingerprint::ZERO,
131 );
132 assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE);
133
134 // Instantiate a dependy-less red node only once for anonymous queries.
135 let red_node_index = current.alloc_new_node(
136 DepNode { kind: D::DEP_KIND_RED, hash: Fingerprint::ZERO.into() },
137 EdgesVec::new(),
138 Fingerprint::ZERO,
139 );
140 assert_eq!(red_node_index, DepNodeIndex::FOREVER_RED_NODE);
141 if prev_graph_node_count > 0 {
142 colors.insert_red(SerializedDepNodeIndex::from_u32(
143 DepNodeIndex::FOREVER_RED_NODE.as_u32(),
144 ));
145 }
146
147 DepGraph {
148 data: Some(Arc::new(DepGraphData {
149 previous_work_products: prev_work_products,
150 dep_node_debug: Default::default(),
151 current,
152 previous: prev_graph,
153 colors,
154 debug_loaded_from_disk: Default::default(),
155 })),
156 virtual_dep_node_index: Arc::new(AtomicU32::new(0)),
157 }
158 }
159
160 pub fn new_disabled() -> DepGraph<D> {
161 DepGraph { data: None, virtual_dep_node_index: Arc::new(AtomicU32::new(0)) }
162 }
163
164 #[inline]
165 pub(crate) fn data(&self) -> Option<&DepGraphData<D>> {
166 self.data.as_deref()
167 }
168
169 /// Returns `true` if we are actually building the full dep-graph, and `false` otherwise.
170 #[inline]
171 pub fn is_fully_enabled(&self) -> bool {
172 self.data.is_some()
173 }
174
175 pub fn with_query(&self, f: impl Fn(&DepGraphQuery)) {
176 if let Some(data) = &self.data {
177 data.current.encoder.with_query(f)
178 }
179 }
180
181 pub fn assert_ignored(&self) {
182 if let Some(..) = self.data {
183 D::read_deps(|task_deps| {
184 assert_matches!(
185 task_deps,
186 TaskDepsRef::Ignore,
187 "expected no task dependency tracking"
188 );
189 })
190 }
191 }
192
193 pub fn with_ignore<OP, R>(&self, op: OP) -> R
194 where
195 OP: FnOnce() -> R,
196 {
197 D::with_deps(TaskDepsRef::Ignore, op)
198 }
199
200 /// Used to wrap the deserialization of a query result from disk,
201 /// This method enforces that no new `DepNodes` are created during
202 /// query result deserialization.
203 ///
204 /// Enforcing this makes the query dep graph simpler - all nodes
205 /// must be created during the query execution, and should be
206 /// created from inside the 'body' of a query (the implementation
207 /// provided by a particular compiler crate).
208 ///
209 /// Consider the case of three queries `A`, `B`, and `C`, where
210 /// `A` invokes `B` and `B` invokes `C`:
211 ///
212 /// `A -> B -> C`
213 ///
214 /// Suppose that decoding the result of query `B` required re-computing
215 /// the query `C`. If we did not create a fresh `TaskDeps` when
216 /// decoding `B`, we would still be using the `TaskDeps` for query `A`
217 /// (if we needed to re-execute `A`). This would cause us to create
218 /// a new edge `A -> C`. If this edge did not previously
219 /// exist in the `DepGraph`, then we could end up with a different
220 /// `DepGraph` at the end of compilation, even if there were no
221 /// meaningful changes to the overall program (e.g. a newline was added).
222 /// In addition, this edge might cause a subsequent compilation run
223 /// to try to force `C` before marking other necessary nodes green. If
224 /// `C` did not exist in the new compilation session, then we could
225 /// get an ICE. Normally, we would have tried (and failed) to mark
226 /// some other query green (e.g. `item_children`) which was used
227 /// to obtain `C`, which would prevent us from ever trying to force
228 /// a nonexistent `D`.
229 ///
230 /// It might be possible to enforce that all `DepNode`s read during
231 /// deserialization already exist in the previous `DepGraph`. In
232 /// the above example, we would invoke `D` during the deserialization
233 /// of `B`. Since we correctly create a new `TaskDeps` from the decoding
234 /// of `B`, this would result in an edge `B -> D`. If that edge already
235 /// existed (with the same `DepPathHash`es), then it should be correct
236 /// to allow the invocation of the query to proceed during deserialization
237 /// of a query result. We would merely assert that the dep-graph fragment
238 /// that would have been added by invoking `C` while decoding `B`
239 /// is equivalent to the dep-graph fragment that we already instantiated for B
240 /// (at the point where we successfully marked B as green).
241 ///
242 /// However, this would require additional complexity
243 /// in the query infrastructure, and is not currently needed by the
244 /// decoding of any query results. Should the need arise in the future,
245 /// we should consider extending the query system with this functionality.
246 pub fn with_query_deserialization<OP, R>(&self, op: OP) -> R
247 where
248 OP: FnOnce() -> R,
249 {
250 D::with_deps(TaskDepsRef::Forbid, op)
251 }
252
253 #[inline(always)]
254 pub fn with_task<Ctxt: HasDepContext<Deps = D>, A: Debug, R>(
255 &self,
256 key: DepNode,
257 cx: Ctxt,
258 arg: A,
259 task: fn(Ctxt, A) -> R,
260 hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
261 ) -> (R, DepNodeIndex) {
262 match self.data() {
263 Some(data) => data.with_task(key, cx, arg, task, hash_result),
264 None => (task(cx, arg), self.next_virtual_depnode_index()),
265 }
266 }
267
268 pub fn with_anon_task<Tcx: DepContext<Deps = D>, OP, R>(
269 &self,
270 cx: Tcx,
271 dep_kind: DepKind,
272 op: OP,
273 ) -> (R, DepNodeIndex)
274 where
275 OP: FnOnce() -> R,
276 {
277 match self.data() {
278 Some(data) => {
279 let (result, index) = data.with_anon_task_inner(cx, dep_kind, op);
280 self.read_index(index);
281 (result, index)
282 }
283 None => (op(), self.next_virtual_depnode_index()),
284 }
285 }
286}
287
288impl<D: Deps> DepGraphData<D> {
289 /// Starts a new dep-graph task. Dep-graph tasks are specified
290 /// using a free function (`task`) and **not** a closure -- this
291 /// is intentional because we want to exercise tight control over
292 /// what state they have access to. In particular, we want to
293 /// prevent implicit 'leaks' of tracked state into the task (which
294 /// could then be read without generating correct edges in the
295 /// dep-graph -- see the [rustc dev guide] for more details on
296 /// the dep-graph). To this end, the task function gets exactly two
297 /// pieces of state: the context `cx` and an argument `arg`. Both
298 /// of these bits of state must be of some type that implements
299 /// `DepGraphSafe` and hence does not leak.
300 ///
301 /// The choice of two arguments is not fundamental. One argument
302 /// would work just as well, since multiple values can be
303 /// collected using tuples. However, using two arguments works out
304 /// to be quite convenient, since it is common to need a context
305 /// (`cx`) and some argument (e.g., a `DefId` identifying what
306 /// item to process).
307 ///
308 /// For cases where you need some other number of arguments:
309 ///
310 /// - If you only need one argument, just use `()` for the `arg`
311 /// parameter.
312 /// - If you need 3+ arguments, use a tuple for the
313 /// `arg` parameter.
314 ///
315 /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html
316 #[inline(always)]
317 pub(crate) fn with_task<Ctxt: HasDepContext<Deps = D>, A: Debug, R>(
318 &self,
319 key: DepNode,
320 cx: Ctxt,
321 arg: A,
322 task: fn(Ctxt, A) -> R,
323 hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
324 ) -> (R, DepNodeIndex) {
325 // If the following assertion triggers, it can have two reasons:
326 // 1. Something is wrong with DepNode creation, either here or
327 // in `DepGraph::try_mark_green()`.
328 // 2. Two distinct query keys get mapped to the same `DepNode`
329 // (see for example #48923).
330 self.assert_dep_node_not_yet_allocated_in_current_session(&key, || {
331 format!(
332 "forcing query with already existing `DepNode`\n\
333 - query-key: {arg:?}\n\
334 - dep-node: {key:?}"
335 )
336 });
337
338 let with_deps = |task_deps| D::with_deps(task_deps, || task(cx, arg));
339 let (result, edges) = if cx.dep_context().is_eval_always(key.kind) {
340 (with_deps(TaskDepsRef::EvalAlways), EdgesVec::new())
341 } else {
342 let task_deps = Lock::new(TaskDeps {
343 #[cfg(debug_assertions)]
344 node: Some(key),
345 reads: EdgesVec::new(),
346 read_set: Default::default(),
347 phantom_data: PhantomData,
348 });
349 (with_deps(TaskDepsRef::Allow(&task_deps)), task_deps.into_inner().reads)
350 };
351
352 let dcx = cx.dep_context();
353 let dep_node_index = self.hash_result_and_alloc_node(dcx, key, edges, &result, hash_result);
354
355 (result, dep_node_index)
356 }
357
358 /// Executes something within an "anonymous" task, that is, a task the
359 /// `DepNode` of which is determined by the list of inputs it read from.
360 ///
361 /// NOTE: this does not actually count as a read of the DepNode here.
362 /// Using the result of this task without reading the DepNode will result
363 /// in untracked dependencies which may lead to ICEs as nodes are
364 /// incorrectly marked green.
365 ///
366 /// FIXME: This could perhaps return a `WithDepNode` to ensure that the
367 /// user of this function actually performs the read; we'll have to see
368 /// how to make that work with `anon` in `execute_job_incr`, though.
369 pub(crate) fn with_anon_task_inner<Tcx: DepContext<Deps = D>, OP, R>(
370 &self,
371 cx: Tcx,
372 dep_kind: DepKind,
373 op: OP,
374 ) -> (R, DepNodeIndex)
375 where
376 OP: FnOnce() -> R,
377 {
378 debug_assert!(!cx.is_eval_always(dep_kind));
379
380 let task_deps = Lock::new(TaskDeps::default());
381 let result = D::with_deps(TaskDepsRef::Allow(&task_deps), op);
382 let task_deps = task_deps.into_inner();
383 let task_deps = task_deps.reads;
384
385 let dep_node_index = match task_deps.len() {
386 0 => {
387 // Because the dep-node id of anon nodes is computed from the sets of its
388 // dependencies we already know what the ID of this dependency-less node is
389 // going to be (i.e. equal to the precomputed
390 // `SINGLETON_DEPENDENCYLESS_ANON_NODE`). As a consequence we can skip creating
391 // a `StableHasher` and sending the node through interning.
392 DepNodeIndex::SINGLETON_ZERO_DEPS_ANON_NODE
393 }
394 1 => {
395 // When there is only one dependency, don't bother creating a node.
396 task_deps[0]
397 }
398 _ => {
399 // The dep node indices are hashed here instead of hashing the dep nodes of the
400 // dependencies. These indices may refer to different nodes per session, but this isn't
401 // a problem here because we that ensure the final dep node hash is per session only by
402 // combining it with the per session random number `anon_id_seed`. This hash only need
403 // to map the dependencies to a single value on a per session basis.
404 let mut hasher = StableHasher::new();
405 task_deps.hash(&mut hasher);
406
407 let target_dep_node = DepNode {
408 kind: dep_kind,
409 // Fingerprint::combine() is faster than sending Fingerprint
410 // through the StableHasher (at least as long as StableHasher
411 // is so slow).
412 hash: self.current.anon_id_seed.combine(hasher.finish()).into(),
413 };
414
415 // The DepNodes generated by the process above are not unique. 2 queries could
416 // have exactly the same dependencies. However, deserialization does not handle
417 // duplicated nodes, so we do the deduplication here directly.
418 //
419 // As anonymous nodes are a small quantity compared to the full dep-graph, the
420 // memory impact of this `anon_node_to_index` map remains tolerable, and helps
421 // us avoid useless growth of the graph with almost-equivalent nodes.
422 self.current.anon_node_to_index.get_or_insert_with(target_dep_node, || {
423 self.current.alloc_new_node(target_dep_node, task_deps, Fingerprint::ZERO)
424 })
425 }
426 };
427
428 (result, dep_node_index)
429 }
430
431 /// Intern the new `DepNode` with the dependencies up-to-now.
432 fn hash_result_and_alloc_node<Ctxt: DepContext<Deps = D>, R>(
433 &self,
434 cx: &Ctxt,
435 node: DepNode,
436 edges: EdgesVec,
437 result: &R,
438 hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
439 ) -> DepNodeIndex {
440 let hashing_timer = cx.profiler().incr_result_hashing();
441 let current_fingerprint = hash_result.map(|hash_result| {
442 cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result))
443 });
444 let dep_node_index = self.alloc_and_color_node(node, edges, current_fingerprint);
445 hashing_timer.finish_with_query_invocation_id(dep_node_index.into());
446 dep_node_index
447 }
448}
449
450impl<D: Deps> DepGraph<D> {
451 #[inline]
452 pub fn read_index(&self, dep_node_index: DepNodeIndex) {
453 if let Some(ref data) = self.data {
454 D::read_deps(|task_deps| {
455 let mut task_deps = match task_deps {
456 TaskDepsRef::Allow(deps) => deps.lock(),
457 TaskDepsRef::EvalAlways => {
458 // We don't need to record dependencies of eval_always
459 // queries. They are re-evaluated unconditionally anyway.
460 return;
461 }
462 TaskDepsRef::Ignore => return,
463 TaskDepsRef::Forbid => {
464 // Reading is forbidden in this context. ICE with a useful error message.
465 panic_on_forbidden_read(data, dep_node_index)
466 }
467 };
468 let task_deps = &mut *task_deps;
469
470 if cfg!(debug_assertions) {
471 data.current.total_read_count.fetch_add(1, Ordering::Relaxed);
472 }
473
474 // As long as we only have a low number of reads we can avoid doing a hash
475 // insert and potentially allocating/reallocating the hashmap
476 let new_read = if task_deps.reads.len() < EdgesVec::INLINE_CAPACITY {
477 task_deps.reads.iter().all(|other| *other != dep_node_index)
478 } else {
479 task_deps.read_set.insert(dep_node_index)
480 };
481 if new_read {
482 task_deps.reads.push(dep_node_index);
483 if task_deps.reads.len() == EdgesVec::INLINE_CAPACITY {
484 // Fill `read_set` with what we have so far so we can use the hashset
485 // next time
486 task_deps.read_set.extend(task_deps.reads.iter().copied());
487 }
488
489 #[cfg(debug_assertions)]
490 {
491 if let Some(target) = task_deps.node
492 && let Some(ref forbidden_edge) = data.current.forbidden_edge
493 {
494 let src = forbidden_edge.index_to_node.lock()[&dep_node_index];
495 if forbidden_edge.test(&src, &target) {
496 panic!("forbidden edge {:?} -> {:?} created", src, target)
497 }
498 }
499 }
500 } else if cfg!(debug_assertions) {
501 data.current.total_duplicate_read_count.fetch_add(1, Ordering::Relaxed);
502 }
503 })
504 }
505 }
506
507 /// This encodes a diagnostic by creating a node with an unique index and assoicating
508 /// `diagnostic` with it, for use in the next session.
509 #[inline]
510 pub fn record_diagnostic<Qcx: QueryContext>(&self, qcx: Qcx, diagnostic: &DiagInner) {
511 if let Some(ref data) = self.data {
512 D::read_deps(|task_deps| match task_deps {
513 TaskDepsRef::EvalAlways | TaskDepsRef::Ignore => return,
514 TaskDepsRef::Forbid | TaskDepsRef::Allow(..) => {
515 self.read_index(data.encode_diagnostic(qcx, diagnostic));
516 }
517 })
518 }
519 }
520 /// This forces a diagnostic node green by running its side effect. `prev_index` would
521 /// refer to a node created used `encode_diagnostic` in the previous session.
522 #[inline]
523 pub fn force_diagnostic_node<Qcx: QueryContext>(
524 &self,
525 qcx: Qcx,
526 prev_index: SerializedDepNodeIndex,
527 ) {
528 if let Some(ref data) = self.data {
529 data.force_diagnostic_node(qcx, prev_index);
530 }
531 }
532
533 /// Create a node when we force-feed a value into the query cache.
534 /// This is used to remove cycles during type-checking const generic parameters.
535 ///
536 /// As usual in the query system, we consider the current state of the calling query
537 /// only depends on the list of dependencies up to now. As a consequence, the value
538 /// that this query gives us can only depend on those dependencies too. Therefore,
539 /// it is sound to use the current dependency set for the created node.
540 ///
541 /// During replay, the order of the nodes is relevant in the dependency graph.
542 /// So the unchanged replay will mark the caller query before trying to mark this one.
543 /// If there is a change to report, the caller query will be re-executed before this one.
544 ///
545 /// FIXME: If the code is changed enough for this node to be marked before requiring the
546 /// caller's node, we suppose that those changes will be enough to mark this node red and
547 /// force a recomputation using the "normal" way.
548 pub fn with_feed_task<Ctxt: DepContext<Deps = D>, R: Debug>(
549 &self,
550 node: DepNode,
551 cx: Ctxt,
552 result: &R,
553 hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
554 ) -> DepNodeIndex {
555 if let Some(data) = self.data.as_ref() {
556 // The caller query has more dependencies than the node we are creating. We may
557 // encounter a case where this created node is marked as green, but the caller query is
558 // subsequently marked as red or recomputed. In this case, we will end up feeding a
559 // value to an existing node.
560 //
561 // For sanity, we still check that the loaded stable hash and the new one match.
562 if let Some(prev_index) = data.previous.node_to_index_opt(&node) {
563 let dep_node_index = data.colors.current(prev_index);
564 if let Some(dep_node_index) = dep_node_index {
565 crate::query::incremental_verify_ich(
566 cx,
567 data,
568 result,
569 prev_index,
570 hash_result,
571 |value| format!("{value:?}"),
572 );
573
574 #[cfg(debug_assertions)]
575 if hash_result.is_some() {
576 data.current.record_edge(
577 dep_node_index,
578 node,
579 data.prev_fingerprint_of(prev_index),
580 );
581 }
582
583 return dep_node_index;
584 }
585 }
586
587 let mut edges = EdgesVec::new();
588 D::read_deps(|task_deps| match task_deps {
589 TaskDepsRef::Allow(deps) => edges.extend(deps.lock().reads.iter().copied()),
590 TaskDepsRef::EvalAlways => {
591 edges.push(DepNodeIndex::FOREVER_RED_NODE);
592 }
593 TaskDepsRef::Ignore => {}
594 TaskDepsRef::Forbid => {
595 panic!("Cannot summarize when dependencies are not recorded.")
596 }
597 });
598
599 data.hash_result_and_alloc_node(&cx, node, edges, result, hash_result)
600 } else {
601 // Incremental compilation is turned off. We just execute the task
602 // without tracking. We still provide a dep-node index that uniquely
603 // identifies the task so that we have a cheap way of referring to
604 // the query for self-profiling.
605 self.next_virtual_depnode_index()
606 }
607 }
608}
609
610impl<D: Deps> DepGraphData<D> {
611 fn assert_dep_node_not_yet_allocated_in_current_session<S: std::fmt::Display>(
612 &self,
613 dep_node: &DepNode,
614 msg: impl FnOnce() -> S,
615 ) {
616 if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) {
617 let current = self.colors.get(prev_index);
618 assert_matches!(current, DepNodeColor::Unknown, "{}", msg())
619 } else if let Some(nodes_in_current_session) = &self.current.nodes_in_current_session {
620 outline(|| {
621 let seen = nodes_in_current_session.lock().contains_key(dep_node);
622 assert!(!seen, "{}", msg());
623 });
624 }
625 }
626
627 fn node_color(&self, dep_node: &DepNode) -> DepNodeColor {
628 if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) {
629 self.colors.get(prev_index)
630 } else {
631 // This is a node that did not exist in the previous compilation session.
632 DepNodeColor::Unknown
633 }
634 }
635
636 /// Returns true if the given node has been marked as green during the
637 /// current compilation session. Used in various assertions
638 #[inline]
639 pub(crate) fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool {
640 matches!(self.colors.get(prev_index), DepNodeColor::Green(_))
641 }
642
643 #[inline]
644 pub(crate) fn prev_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint {
645 self.previous.fingerprint_by_index(prev_index)
646 }
647
648 #[inline]
649 pub(crate) fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> DepNode {
650 self.previous.index_to_node(prev_index)
651 }
652
653 pub(crate) fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) {
654 self.debug_loaded_from_disk.lock().insert(dep_node);
655 }
656
657 /// This encodes a diagnostic by creating a node with an unique index and assoicating
658 /// `diagnostic` with it, for use in the next session.
659 #[inline]
660 fn encode_diagnostic<Qcx: QueryContext>(
661 &self,
662 qcx: Qcx,
663 diagnostic: &DiagInner,
664 ) -> DepNodeIndex {
665 // Use `send_new` so we get an unique index, even though the dep node is not.
666 let dep_node_index = self.current.encoder.send_new(
667 DepNode {
668 kind: D::DEP_KIND_SIDE_EFFECT,
669 hash: PackedFingerprint::from(Fingerprint::ZERO),
670 },
671 Fingerprint::ZERO,
672 // We want the side effect node to always be red so it will be forced and emit the
673 // diagnostic.
674 std::iter::once(DepNodeIndex::FOREVER_RED_NODE).collect(),
675 );
676 let side_effect = QuerySideEffect::Diagnostic(diagnostic.clone());
677 qcx.store_side_effect(dep_node_index, side_effect);
678 dep_node_index
679 }
680
681 /// This forces a diagnostic node green by running its side effect. `prev_index` would
682 /// refer to a node created used `encode_diagnostic` in the previous session.
683 #[inline]
684 fn force_diagnostic_node<Qcx: QueryContext>(
685 &self,
686 qcx: Qcx,
687 prev_index: SerializedDepNodeIndex,
688 ) {
689 D::with_deps(TaskDepsRef::Ignore, || {
690 let side_effect = qcx.load_side_effect(prev_index).unwrap();
691
692 match &side_effect {
693 QuerySideEffect::Diagnostic(diagnostic) => {
694 qcx.dep_context().sess().dcx().emit_diagnostic(diagnostic.clone());
695 }
696 }
697
698 // Use `send_and_color` as `promote_node_and_deps_to_current` expects all
699 // green dependencies. `send_and_color` will also prevent multiple nodes
700 // being encoded for concurrent calls.
701 let dep_node_index = self.current.encoder.send_and_color(
702 prev_index,
703 &self.colors,
704 DepNode {
705 kind: D::DEP_KIND_SIDE_EFFECT,
706 hash: PackedFingerprint::from(Fingerprint::ZERO),
707 },
708 Fingerprint::ZERO,
709 std::iter::once(DepNodeIndex::FOREVER_RED_NODE).collect(),
710 true,
711 );
712 // This will just overwrite the same value for concurrent calls.
713 qcx.store_side_effect(dep_node_index, side_effect);
714 })
715 }
716
717 fn alloc_and_color_node(
718 &self,
719 key: DepNode,
720 edges: EdgesVec,
721 fingerprint: Option<Fingerprint>,
722 ) -> DepNodeIndex {
723 if let Some(prev_index) = self.previous.node_to_index_opt(&key) {
724 // Determine the color and index of the new `DepNode`.
725 let is_green = if let Some(fingerprint) = fingerprint {
726 if fingerprint == self.previous.fingerprint_by_index(prev_index) {
727 // This is a green node: it existed in the previous compilation,
728 // its query was re-executed, and it has the same result as before.
729 true
730 } else {
731 // This is a red node: it existed in the previous compilation, its query
732 // was re-executed, but it has a different result from before.
733 false
734 }
735 } else {
736 // This is a red node, effectively: it existed in the previous compilation
737 // session, its query was re-executed, but it doesn't compute a result hash
738 // (i.e. it represents a `no_hash` query), so we have no way of determining
739 // whether or not the result was the same as before.
740 false
741 };
742
743 let fingerprint = fingerprint.unwrap_or(Fingerprint::ZERO);
744
745 let dep_node_index = self.current.encoder.send_and_color(
746 prev_index,
747 &self.colors,
748 key,
749 fingerprint,
750 edges,
751 is_green,
752 );
753
754 self.current.record_node(dep_node_index, key, fingerprint);
755
756 dep_node_index
757 } else {
758 self.current.alloc_new_node(key, edges, fingerprint.unwrap_or(Fingerprint::ZERO))
759 }
760 }
761
762 fn promote_node_and_deps_to_current(&self, prev_index: SerializedDepNodeIndex) -> DepNodeIndex {
763 self.current.debug_assert_not_in_new_nodes(&self.previous, prev_index);
764
765 let dep_node_index = self.current.encoder.send_promoted(prev_index, &self.colors);
766
767 #[cfg(debug_assertions)]
768 self.current.record_edge(
769 dep_node_index,
770 self.previous.index_to_node(prev_index),
771 self.previous.fingerprint_by_index(prev_index),
772 );
773
774 dep_node_index
775 }
776}
777
778impl<D: Deps> DepGraph<D> {
779 /// Checks whether a previous work product exists for `v` and, if
780 /// so, return the path that leads to it. Used to skip doing work.
781 pub fn previous_work_product(&self, v: &WorkProductId) -> Option<WorkProduct> {
782 self.data.as_ref().and_then(|data| data.previous_work_products.get(v).cloned())
783 }
784
785 /// Access the map of work-products created during the cached run. Only
786 /// used during saving of the dep-graph.
787 pub fn previous_work_products(&self) -> &WorkProductMap {
788 &self.data.as_ref().unwrap().previous_work_products
789 }
790
791 pub fn debug_was_loaded_from_disk(&self, dep_node: DepNode) -> bool {
792 self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node)
793 }
794
795 #[cfg(debug_assertions)]
796 #[inline(always)]
797 pub(crate) fn register_dep_node_debug_str<F>(&self, dep_node: DepNode, debug_str_gen: F)
798 where
799 F: FnOnce() -> String,
800 {
801 let dep_node_debug = &self.data.as_ref().unwrap().dep_node_debug;
802
803 if dep_node_debug.borrow().contains_key(&dep_node) {
804 return;
805 }
806 let debug_str = self.with_ignore(debug_str_gen);
807 dep_node_debug.borrow_mut().insert(dep_node, debug_str);
808 }
809
810 pub fn dep_node_debug_str(&self, dep_node: DepNode) -> Option<String> {
811 self.data.as_ref()?.dep_node_debug.borrow().get(&dep_node).cloned()
812 }
813
814 fn node_color(&self, dep_node: &DepNode) -> DepNodeColor {
815 if let Some(ref data) = self.data {
816 return data.node_color(dep_node);
817 }
818
819 DepNodeColor::Unknown
820 }
821
822 pub fn try_mark_green<Qcx: QueryContext<Deps = D>>(
823 &self,
824 qcx: Qcx,
825 dep_node: &DepNode,
826 ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> {
827 self.data().and_then(|data| data.try_mark_green(qcx, dep_node))
828 }
829}
830
831impl<D: Deps> DepGraphData<D> {
832 /// Try to mark a node index for the node dep_node.
833 ///
834 /// A node will have an index, when it's already been marked green, or when we can mark it
835 /// green. This function will mark the current task as a reader of the specified node, when
836 /// a node index can be found for that node.
837 pub(crate) fn try_mark_green<Qcx: QueryContext<Deps = D>>(
838 &self,
839 qcx: Qcx,
840 dep_node: &DepNode,
841 ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> {
842 debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind));
843
844 // Return None if the dep node didn't exist in the previous session
845 let prev_index = self.previous.node_to_index_opt(dep_node)?;
846
847 match self.colors.get(prev_index) {
848 DepNodeColor::Green(dep_node_index) => Some((prev_index, dep_node_index)),
849 DepNodeColor::Red => None,
850 DepNodeColor::Unknown => {
851 // This DepNode and the corresponding query invocation existed
852 // in the previous compilation session too, so we can try to
853 // mark it as green by recursively marking all of its
854 // dependencies green.
855 self.try_mark_previous_green(qcx, prev_index, dep_node, None)
856 .map(|dep_node_index| (prev_index, dep_node_index))
857 }
858 }
859 }
860
861 #[instrument(skip(self, qcx, parent_dep_node_index, frame), level = "debug")]
862 fn try_mark_parent_green<Qcx: QueryContext<Deps = D>>(
863 &self,
864 qcx: Qcx,
865 parent_dep_node_index: SerializedDepNodeIndex,
866 frame: &MarkFrame<'_>,
867 ) -> Option<()> {
868 let get_dep_dep_node = || self.previous.index_to_node(parent_dep_node_index);
869
870 match self.colors.get(parent_dep_node_index) {
871 DepNodeColor::Green(_) => {
872 // This dependency has been marked as green before, we are
873 // still fine and can continue with checking the other
874 // dependencies.
875 //
876 // This path is extremely hot. We don't want to get the
877 // `dep_dep_node` unless it's necessary. Hence the
878 // `get_dep_dep_node` closure.
879 debug!("dependency {:?} was immediately green", get_dep_dep_node());
880 return Some(());
881 }
882 DepNodeColor::Red => {
883 // We found a dependency the value of which has changed
884 // compared to the previous compilation session. We cannot
885 // mark the DepNode as green and also don't need to bother
886 // with checking any of the other dependencies.
887 debug!("dependency {:?} was immediately red", get_dep_dep_node());
888 return None;
889 }
890 DepNodeColor::Unknown => {}
891 }
892
893 let dep_dep_node = &get_dep_dep_node();
894
895 // We don't know the state of this dependency. If it isn't
896 // an eval_always node, let's try to mark it green recursively.
897 if !qcx.dep_context().is_eval_always(dep_dep_node.kind) {
898 debug!(
899 "state of dependency {:?} ({}) is unknown, trying to mark it green",
900 dep_dep_node, dep_dep_node.hash,
901 );
902
903 let node_index =
904 self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, Some(frame));
905
906 if node_index.is_some() {
907 debug!("managed to MARK dependency {dep_dep_node:?} as green");
908 return Some(());
909 }
910 }
911
912 // We failed to mark it green, so we try to force the query.
913 debug!("trying to force dependency {dep_dep_node:?}");
914 if !qcx.dep_context().try_force_from_dep_node(*dep_dep_node, parent_dep_node_index, frame) {
915 // The DepNode could not be forced.
916 debug!("dependency {dep_dep_node:?} could not be forced");
917 return None;
918 }
919
920 match self.colors.get(parent_dep_node_index) {
921 DepNodeColor::Green(_) => {
922 debug!("managed to FORCE dependency {dep_dep_node:?} to green");
923 return Some(());
924 }
925 DepNodeColor::Red => {
926 debug!("dependency {dep_dep_node:?} was red after forcing");
927 return None;
928 }
929 DepNodeColor::Unknown => {}
930 }
931
932 if let None = qcx.dep_context().sess().dcx().has_errors_or_delayed_bugs() {
933 panic!("try_mark_previous_green() - Forcing the DepNode should have set its color")
934 }
935
936 // If the query we just forced has resulted in
937 // some kind of compilation error, we cannot rely on
938 // the dep-node color having been properly updated.
939 // This means that the query system has reached an
940 // invalid state. We let the compiler continue (by
941 // returning `None`) so it can emit error messages
942 // and wind down, but rely on the fact that this
943 // invalid state will not be persisted to the
944 // incremental compilation cache because of
945 // compilation errors being present.
946 debug!("dependency {dep_dep_node:?} resulted in compilation error");
947 return None;
948 }
949
950 /// Try to mark a dep-node which existed in the previous compilation session as green.
951 #[instrument(skip(self, qcx, prev_dep_node_index, frame), level = "debug")]
952 fn try_mark_previous_green<Qcx: QueryContext<Deps = D>>(
953 &self,
954 qcx: Qcx,
955 prev_dep_node_index: SerializedDepNodeIndex,
956 dep_node: &DepNode,
957 frame: Option<&MarkFrame<'_>>,
958 ) -> Option<DepNodeIndex> {
959 let frame = MarkFrame { index: prev_dep_node_index, parent: frame };
960
961 // We never try to mark eval_always nodes as green
962 debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind));
963
964 debug_assert_eq!(self.previous.index_to_node(prev_dep_node_index), *dep_node);
965
966 let prev_deps = self.previous.edge_targets_from(prev_dep_node_index);
967
968 for dep_dep_node_index in prev_deps {
969 self.try_mark_parent_green(qcx, dep_dep_node_index, &frame)?;
970 }
971
972 // If we got here without hitting a `return` that means that all
973 // dependencies of this DepNode could be marked as green. Therefore we
974 // can also mark this DepNode as green.
975
976 // There may be multiple threads trying to mark the same dep node green concurrently
977
978 // We allocating an entry for the node in the current dependency graph and
979 // adding all the appropriate edges imported from the previous graph
980 let dep_node_index = self.promote_node_and_deps_to_current(prev_dep_node_index);
981
982 // ... and finally storing a "Green" entry in the color map.
983 // Multiple threads can all write the same color here
984
985 debug!("successfully marked {dep_node:?} as green");
986 Some(dep_node_index)
987 }
988}
989
990impl<D: Deps> DepGraph<D> {
991 /// Returns true if the given node has been marked as red during the
992 /// current compilation session. Used in various assertions
993 pub fn is_red(&self, dep_node: &DepNode) -> bool {
994 matches!(self.node_color(dep_node), DepNodeColor::Red)
995 }
996
997 /// Returns true if the given node has been marked as green during the
998 /// current compilation session. Used in various assertions
999 pub fn is_green(&self, dep_node: &DepNode) -> bool {
1000 matches!(self.node_color(dep_node), DepNodeColor::Green(_))
1001 }
1002
1003 pub fn assert_dep_node_not_yet_allocated_in_current_session<S: std::fmt::Display>(
1004 &self,
1005 dep_node: &DepNode,
1006 msg: impl FnOnce() -> S,
1007 ) {
1008 if let Some(data) = &self.data {
1009 data.assert_dep_node_not_yet_allocated_in_current_session(dep_node, msg)
1010 }
1011 }
1012
1013 /// This method loads all on-disk cacheable query results into memory, so
1014 /// they can be written out to the new cache file again. Most query results
1015 /// will already be in memory but in the case where we marked something as
1016 /// green but then did not need the value, that value will never have been
1017 /// loaded from disk.
1018 ///
1019 /// This method will only load queries that will end up in the disk cache.
1020 /// Other queries will not be executed.
1021 pub fn exec_cache_promotions<Tcx: DepContext>(&self, tcx: Tcx) {
1022 let _prof_timer = tcx.profiler().generic_activity("incr_comp_query_cache_promotion");
1023
1024 let data = self.data.as_ref().unwrap();
1025 for prev_index in data.colors.values.indices() {
1026 match data.colors.get(prev_index) {
1027 DepNodeColor::Green(_) => {
1028 let dep_node = data.previous.index_to_node(prev_index);
1029 tcx.try_load_from_on_disk_cache(dep_node);
1030 }
1031 DepNodeColor::Unknown | DepNodeColor::Red => {
1032 // We can skip red nodes because a node can only be marked
1033 // as red if the query result was recomputed and thus is
1034 // already in memory.
1035 }
1036 }
1037 }
1038 }
1039
1040 pub fn finish_encoding(&self) -> FileEncodeResult {
1041 if let Some(data) = &self.data { data.current.encoder.finish(&data.current) } else { Ok(0) }
1042 }
1043
1044 pub(crate) fn next_virtual_depnode_index(&self) -> DepNodeIndex {
1045 debug_assert!(self.data.is_none());
1046 let index = self.virtual_dep_node_index.fetch_add(1, Ordering::Relaxed);
1047 DepNodeIndex::from_u32(index)
1048 }
1049}
1050
1051/// A "work product" is an intermediate result that we save into the
1052/// incremental directory for later re-use. The primary example are
1053/// the object files that we save for each partition at code
1054/// generation time.
1055///
1056/// Each work product is associated with a dep-node, representing the
1057/// process that produced the work-product. If that dep-node is found
1058/// to be dirty when we load up, then we will delete the work-product
1059/// at load time. If the work-product is found to be clean, then we
1060/// will keep a record in the `previous_work_products` list.
1061///
1062/// In addition, work products have an associated hash. This hash is
1063/// an extra hash that can be used to decide if the work-product from
1064/// a previous compilation can be re-used (in addition to the dirty
1065/// edges check).
1066///
1067/// As the primary example, consider the object files we generate for
1068/// each partition. In the first run, we create partitions based on
1069/// the symbols that need to be compiled. For each partition P, we
1070/// hash the symbols in P and create a `WorkProduct` record associated
1071/// with `DepNode::CodegenUnit(P)`; the hash is the set of symbols
1072/// in P.
1073///
1074/// The next time we compile, if the `DepNode::CodegenUnit(P)` is
1075/// judged to be clean (which means none of the things we read to
1076/// generate the partition were found to be dirty), it will be loaded
1077/// into previous work products. We will then regenerate the set of
1078/// symbols in the partition P and hash them (note that new symbols
1079/// may be added -- for example, new monomorphizations -- even if
1080/// nothing in P changed!). We will compare that hash against the
1081/// previous hash. If it matches up, we can reuse the object file.
1082#[derive(Clone, Debug, Encodable, Decodable)]
1083pub struct WorkProduct {
1084 pub cgu_name: String,
1085 /// Saved files associated with this CGU. In each key/value pair, the value is the path to the
1086 /// saved file and the key is some identifier for the type of file being saved.
1087 ///
1088 /// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to
1089 /// the object file's path, and "dwo" to the dwarf object file's path.
1090 pub saved_files: UnordMap<String, String>,
1091}
1092
1093pub type WorkProductMap = UnordMap<WorkProductId, WorkProduct>;
1094
1095// Index type for `DepNodeData`'s edges.
1096rustc_index::newtype_index! {
1097 struct EdgeIndex {}
1098}
1099
1100/// `CurrentDepGraph` stores the dependency graph for the current session. It
1101/// will be populated as we run queries or tasks. We never remove nodes from the
1102/// graph: they are only added.
1103///
1104/// The nodes in it are identified by a `DepNodeIndex`. We avoid keeping the nodes
1105/// in memory. This is important, because these graph structures are some of the
1106/// largest in the compiler.
1107///
1108/// For this reason, we avoid storing `DepNode`s more than once as map
1109/// keys. The `anon_node_to_index` map only contains nodes of anonymous queries not in the previous
1110/// graph, and we map nodes in the previous graph to indices via a two-step
1111/// mapping. `SerializedDepGraph` maps from `DepNode` to `SerializedDepNodeIndex`,
1112/// and the `prev_index_to_index` vector (which is more compact and faster than
1113/// using a map) maps from `SerializedDepNodeIndex` to `DepNodeIndex`.
1114///
1115/// This struct uses three locks internally. The `data`, `anon_node_to_index`,
1116/// and `prev_index_to_index` fields are locked separately. Operations that take
1117/// a `DepNodeIndex` typically just access the `data` field.
1118///
1119/// We only need to manipulate at most two locks simultaneously:
1120/// `anon_node_to_index` and `data`, or `prev_index_to_index` and `data`. When
1121/// manipulating both, we acquire `anon_node_to_index` or `prev_index_to_index`
1122/// first, and `data` second.
1123pub(super) struct CurrentDepGraph<D: Deps> {
1124 encoder: GraphEncoder<D>,
1125 anon_node_to_index: ShardedHashMap<DepNode, DepNodeIndex>,
1126
1127 /// This is used to verify that fingerprints do not change between the creation of a node
1128 /// and its recomputation.
1129 #[cfg(debug_assertions)]
1130 fingerprints: Lock<IndexVec<DepNodeIndex, Option<Fingerprint>>>,
1131
1132 /// Used to trap when a specific edge is added to the graph.
1133 /// This is used for debug purposes and is only active with `debug_assertions`.
1134 #[cfg(debug_assertions)]
1135 forbidden_edge: Option<EdgeFilter>,
1136
1137 /// Used to verify the absence of hash collisions among DepNodes.
1138 /// This field is only `Some` if the `-Z incremental_verify_ich` option is present
1139 /// or if `debug_assertions` are enabled.
1140 ///
1141 /// The map contains all DepNodes that have been allocated in the current session so far.
1142 nodes_in_current_session: Option<Lock<FxHashMap<DepNode, DepNodeIndex>>>,
1143
1144 /// Anonymous `DepNode`s are nodes whose IDs we compute from the list of
1145 /// their edges. This has the beneficial side-effect that multiple anonymous
1146 /// nodes can be coalesced into one without changing the semantics of the
1147 /// dependency graph. However, the merging of nodes can lead to a subtle
1148 /// problem during red-green marking: The color of an anonymous node from
1149 /// the current session might "shadow" the color of the node with the same
1150 /// ID from the previous session. In order to side-step this problem, we make
1151 /// sure that anonymous `NodeId`s allocated in different sessions don't overlap.
1152 /// This is implemented by mixing a session-key into the ID fingerprint of
1153 /// each anon node. The session-key is a hash of the number of previous sessions.
1154 anon_id_seed: Fingerprint,
1155
1156 /// These are simple counters that are for profiling and
1157 /// debugging and only active with `debug_assertions`.
1158 pub(super) total_read_count: AtomicU64,
1159 pub(super) total_duplicate_read_count: AtomicU64,
1160}
1161
1162impl<D: Deps> CurrentDepGraph<D> {
1163 fn new(
1164 session: &Session,
1165 prev_graph_node_count: usize,
1166 encoder: FileEncoder,
1167 previous: Arc<SerializedDepGraph>,
1168 ) -> Self {
1169 let mut stable_hasher = StableHasher::new();
1170 previous.session_count().hash(&mut stable_hasher);
1171 let anon_id_seed = stable_hasher.finish();
1172
1173 #[cfg(debug_assertions)]
1174 let forbidden_edge = match env::var("RUST_FORBID_DEP_GRAPH_EDGE") {
1175 Ok(s) => match EdgeFilter::new(&s) {
1176 Ok(f) => Some(f),
1177 Err(err) => panic!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err),
1178 },
1179 Err(_) => None,
1180 };
1181
1182 let new_node_count_estimate = 102 * prev_graph_node_count / 100 + 200;
1183
1184 let new_node_dbg =
1185 session.opts.unstable_opts.incremental_verify_ich || cfg!(debug_assertions);
1186
1187 CurrentDepGraph {
1188 encoder: GraphEncoder::new(session, encoder, prev_graph_node_count, previous),
1189 anon_node_to_index: ShardedHashMap::with_capacity(
1190 // FIXME: The count estimate is off as anon nodes are only a portion of the nodes.
1191 new_node_count_estimate / sharded::shards(),
1192 ),
1193 anon_id_seed,
1194 #[cfg(debug_assertions)]
1195 forbidden_edge,
1196 #[cfg(debug_assertions)]
1197 fingerprints: Lock::new(IndexVec::from_elem_n(None, new_node_count_estimate)),
1198 nodes_in_current_session: new_node_dbg.then(|| {
1199 Lock::new(FxHashMap::with_capacity_and_hasher(
1200 new_node_count_estimate,
1201 Default::default(),
1202 ))
1203 }),
1204 total_read_count: AtomicU64::new(0),
1205 total_duplicate_read_count: AtomicU64::new(0),
1206 }
1207 }
1208
1209 #[cfg(debug_assertions)]
1210 fn record_edge(&self, dep_node_index: DepNodeIndex, key: DepNode, fingerprint: Fingerprint) {
1211 if let Some(forbidden_edge) = &self.forbidden_edge {
1212 forbidden_edge.index_to_node.lock().insert(dep_node_index, key);
1213 }
1214 let previous = *self.fingerprints.lock().get_or_insert_with(dep_node_index, || fingerprint);
1215 assert_eq!(previous, fingerprint, "Unstable fingerprints for {:?}", key);
1216 }
1217
1218 #[inline(always)]
1219 fn record_node(
1220 &self,
1221 dep_node_index: DepNodeIndex,
1222 key: DepNode,
1223 _current_fingerprint: Fingerprint,
1224 ) {
1225 #[cfg(debug_assertions)]
1226 self.record_edge(dep_node_index, key, _current_fingerprint);
1227
1228 if let Some(ref nodes_in_current_session) = self.nodes_in_current_session {
1229 outline(|| {
1230 if nodes_in_current_session.lock().insert(key, dep_node_index).is_some() {
1231 panic!("Found duplicate dep-node {key:?}");
1232 }
1233 });
1234 }
1235 }
1236
1237 /// Writes the node to the current dep-graph and allocates a `DepNodeIndex` for it.
1238 /// Assumes that this is a node that has no equivalent in the previous dep-graph.
1239 #[inline(always)]
1240 fn alloc_new_node(
1241 &self,
1242 key: DepNode,
1243 edges: EdgesVec,
1244 current_fingerprint: Fingerprint,
1245 ) -> DepNodeIndex {
1246 let dep_node_index = self.encoder.send_new(key, current_fingerprint, edges);
1247
1248 self.record_node(dep_node_index, key, current_fingerprint);
1249
1250 dep_node_index
1251 }
1252
1253 #[inline]
1254 fn debug_assert_not_in_new_nodes(
1255 &self,
1256 prev_graph: &SerializedDepGraph,
1257 prev_index: SerializedDepNodeIndex,
1258 ) {
1259 if let Some(ref nodes_in_current_session) = self.nodes_in_current_session {
1260 debug_assert!(
1261 !nodes_in_current_session
1262 .lock()
1263 .contains_key(&prev_graph.index_to_node(prev_index)),
1264 "node from previous graph present in new node collection"
1265 );
1266 }
1267 }
1268}
1269
1270#[derive(Debug, Clone, Copy)]
1271pub enum TaskDepsRef<'a> {
1272 /// New dependencies can be added to the
1273 /// `TaskDeps`. This is used when executing a 'normal' query
1274 /// (no `eval_always` modifier)
1275 Allow(&'a Lock<TaskDeps>),
1276 /// This is used when executing an `eval_always` query. We don't
1277 /// need to track dependencies for a query that's always
1278 /// re-executed -- but we need to know that this is an `eval_always`
1279 /// query in order to emit dependencies to `DepNodeIndex::FOREVER_RED_NODE`
1280 /// when directly feeding other queries.
1281 EvalAlways,
1282 /// New dependencies are ignored. This is also used for `dep_graph.with_ignore`.
1283 Ignore,
1284 /// Any attempt to add new dependencies will cause a panic.
1285 /// This is used when decoding a query result from disk,
1286 /// to ensure that the decoding process doesn't itself
1287 /// require the execution of any queries.
1288 Forbid,
1289}
1290
1291#[derive(Debug)]
1292pub struct TaskDeps {
1293 #[cfg(debug_assertions)]
1294 node: Option<DepNode>,
1295 reads: EdgesVec,
1296 read_set: FxHashSet<DepNodeIndex>,
1297 phantom_data: PhantomData<DepNode>,
1298}
1299
1300impl Default for TaskDeps {
1301 fn default() -> Self {
1302 Self {
1303 #[cfg(debug_assertions)]
1304 node: None,
1305 reads: EdgesVec::new(),
1306 read_set: FxHashSet::with_capacity_and_hasher(128, Default::default()),
1307 phantom_data: PhantomData,
1308 }
1309 }
1310}
1311
1312// A data structure that stores Option<DepNodeColor> values as a contiguous
1313// array, using one u32 per entry.
1314pub(super) struct DepNodeColorMap {
1315 values: IndexVec<SerializedDepNodeIndex, AtomicU32>,
1316}
1317
1318// All values below `COMPRESSED_RED` are green.
1319const COMPRESSED_RED: u32 = u32::MAX - 1;
1320const COMPRESSED_UNKNOWN: u32 = u32::MAX;
1321
1322impl DepNodeColorMap {
1323 fn new(size: usize) -> DepNodeColorMap {
1324 debug_assert!(COMPRESSED_RED > DepNodeIndex::MAX_AS_U32);
1325 DepNodeColorMap { values: (0..size).map(|_| AtomicU32::new(COMPRESSED_UNKNOWN)).collect() }
1326 }
1327
1328 #[inline]
1329 pub(super) fn current(&self, index: SerializedDepNodeIndex) -> Option<DepNodeIndex> {
1330 let value = self.values[index].load(Ordering::Relaxed);
1331 if value <= DepNodeIndex::MAX_AS_U32 { Some(DepNodeIndex::from_u32(value)) } else { None }
1332 }
1333
1334 /// This tries to atomically mark a node green and assign `index` as the new
1335 /// index. This returns `Ok` if `index` gets assigned, otherwise it returns
1336 /// the already allocated index in `Err`.
1337 #[inline]
1338 pub(super) fn try_mark_green(
1339 &self,
1340 prev_index: SerializedDepNodeIndex,
1341 index: DepNodeIndex,
1342 ) -> Result<(), DepNodeIndex> {
1343 let value = &self.values[prev_index];
1344 match value.compare_exchange(
1345 COMPRESSED_UNKNOWN,
1346 index.as_u32(),
1347 Ordering::Relaxed,
1348 Ordering::Relaxed,
1349 ) {
1350 Ok(_) => Ok(()),
1351 Err(v) => Err(DepNodeIndex::from_u32(v)),
1352 }
1353 }
1354
1355 #[inline]
1356 pub(super) fn get(&self, index: SerializedDepNodeIndex) -> DepNodeColor {
1357 let value = self.values[index].load(Ordering::Acquire);
1358 // Green is by far the most common case. Check for that first so we can succeed with a
1359 // single comparison.
1360 if value < COMPRESSED_RED {
1361 DepNodeColor::Green(DepNodeIndex::from_u32(value))
1362 } else if value == COMPRESSED_RED {
1363 DepNodeColor::Red
1364 } else {
1365 debug_assert_eq!(value, COMPRESSED_UNKNOWN);
1366 DepNodeColor::Unknown
1367 }
1368 }
1369
1370 #[inline]
1371 pub(super) fn insert_red(&self, index: SerializedDepNodeIndex) {
1372 self.values[index].store(COMPRESSED_RED, Ordering::Release)
1373 }
1374}
1375
1376#[inline(never)]
1377#[cold]
1378pub(crate) fn print_markframe_trace<D: Deps>(graph: &DepGraph<D>, frame: &MarkFrame<'_>) {
1379 let data = graph.data.as_ref().unwrap();
1380
1381 eprintln!("there was a panic while trying to force a dep node");
1382 eprintln!("try_mark_green dep node stack:");
1383
1384 let mut i = 0;
1385 let mut current = Some(frame);
1386 while let Some(frame) = current {
1387 let node = data.previous.index_to_node(frame.index);
1388 eprintln!("#{i} {node:?}");
1389 current = frame.parent;
1390 i += 1;
1391 }
1392
1393 eprintln!("end of try_mark_green dep node stack");
1394}
1395
1396#[cold]
1397#[inline(never)]
1398fn panic_on_forbidden_read<D: Deps>(data: &DepGraphData<D>, dep_node_index: DepNodeIndex) -> ! {
1399 // We have to do an expensive reverse-lookup of the DepNode that
1400 // corresponds to `dep_node_index`, but that's OK since we are about
1401 // to ICE anyway.
1402 let mut dep_node = None;
1403
1404 // First try to find the dep node among those that already existed in the
1405 // previous session and has been marked green
1406 for prev_index in data.colors.values.indices() {
1407 if data.colors.current(prev_index) == Some(dep_node_index) {
1408 dep_node = Some(data.previous.index_to_node(prev_index));
1409 break;
1410 }
1411 }
1412
1413 if dep_node.is_none()
1414 && let Some(nodes) = &data.current.nodes_in_current_session
1415 {
1416 // Try to find it among the nodes allocated so far in this session
1417 // This is OK, there's only ever one node result possible so this is deterministic.
1418 #[allow(rustc::potential_query_instability)]
1419 if let Some((node, _)) = nodes.lock().iter().find(|&(_, index)| *index == dep_node_index) {
1420 dep_node = Some(*node);
1421 }
1422 }
1423
1424 let dep_node = dep_node.map_or_else(
1425 || format!("with index {:?}", dep_node_index),
1426 |dep_node| format!("`{:?}`", dep_node),
1427 );
1428
1429 panic!(
1430 "Error: trying to record dependency on DepNode {dep_node} in a \
1431 context that does not allow it (e.g. during query deserialization). \
1432 The most common case of recording a dependency on a DepNode `foo` is \
1433 when the corresponding query `foo` is invoked. Invoking queries is not \
1434 allowed as part of loading something from the incremental on-disk cache. \
1435 See <https://github.com/rust-lang/rust/pull/91919>."
1436 )
1437}