1use std::cell::Cell;
6use std::collections::hash_map::Entry;
7use std::fmt::Debug;
8use std::hash::Hash;
9use std::mem;
10
11use rustc_data_structures::fingerprint::Fingerprint;
12use rustc_data_structures::fx::FxHashMap;
13use rustc_data_structures::sharded::Sharded;
14use rustc_data_structures::stack::ensure_sufficient_stack;
15use rustc_data_structures::{outline, sync};
16use rustc_errors::{Diag, FatalError, StashKey};
17use rustc_span::{DUMMY_SP, Span};
18use tracing::instrument;
19
20use super::QueryConfig;
21use crate::HandleCycleError;
22use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams};
23use crate::ich::StableHashingContext;
24use crate::query::caches::QueryCache;
25use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle};
26use crate::query::{QueryContext, QueryMap, QueryStackFrame, SerializedDepNodeIndex};
27
28pub struct QueryState<K> {
29 active: Sharded<FxHashMap<K, QueryResult>>,
30}
31
32enum QueryResult {
34 Started(QueryJob),
36
37 Poisoned,
40}
41
42impl QueryResult {
43 fn expect_job(self) -> QueryJob {
45 match self {
46 Self::Started(job) => job,
47 Self::Poisoned => {
48 panic!("job for query failed to start and was poisoned")
49 }
50 }
51 }
52}
53
54impl<K> QueryState<K>
55where
56 K: Eq + Hash + Copy + Debug,
57{
58 pub fn all_inactive(&self) -> bool {
59 self.active.lock_shards().all(|shard| shard.is_empty())
60 }
61
62 pub fn try_collect_active_jobs<Qcx: Copy>(
63 &self,
64 qcx: Qcx,
65 make_query: fn(Qcx, K) -> QueryStackFrame,
66 jobs: &mut QueryMap,
67 ) -> Option<()> {
68 let mut active = Vec::new();
69
70 for shard in self.active.try_lock_shards() {
73 for (k, v) in shard?.iter() {
74 if let QueryResult::Started(ref job) = *v {
75 active.push((*k, job.clone()));
76 }
77 }
78 }
79
80 for (key, job) in active {
83 let query = make_query(qcx, key);
84 jobs.insert(job.id, QueryJobInfo { query, job });
85 }
86
87 Some(())
88 }
89}
90
91impl<K> Default for QueryState<K> {
92 fn default() -> QueryState<K> {
93 QueryState { active: Default::default() }
94 }
95}
96
97struct JobOwner<'tcx, K>
100where
101 K: Eq + Hash + Copy,
102{
103 state: &'tcx QueryState<K>,
104 key: K,
105}
106
107#[cold]
108#[inline(never)]
109fn mk_cycle<Q, Qcx>(query: Q, qcx: Qcx, cycle_error: CycleError) -> Q::Value
110where
111 Q: QueryConfig<Qcx>,
112 Qcx: QueryContext,
113{
114 let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
115 handle_cycle_error(query, qcx, &cycle_error, error)
116}
117
118fn handle_cycle_error<Q, Qcx>(
119 query: Q,
120 qcx: Qcx,
121 cycle_error: &CycleError,
122 error: Diag<'_>,
123) -> Q::Value
124where
125 Q: QueryConfig<Qcx>,
126 Qcx: QueryContext,
127{
128 use HandleCycleError::*;
129 match query.handle_cycle_error() {
130 Error => {
131 let guar = error.emit();
132 query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar)
133 }
134 Fatal => {
135 error.emit();
136 qcx.dep_context().sess().dcx().abort_if_errors();
137 unreachable!()
138 }
139 DelayBug => {
140 let guar = error.delay_as_bug();
141 query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar)
142 }
143 Stash => {
144 let guar = if let Some(root) = cycle_error.cycle.first()
145 && let Some(span) = root.query.span
146 {
147 error.stash(span, StashKey::Cycle).unwrap()
148 } else {
149 error.emit()
150 };
151 query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar)
152 }
153 }
154}
155
156impl<'tcx, K> JobOwner<'tcx, K>
157where
158 K: Eq + Hash + Copy,
159{
160 fn complete<C>(self, cache: &C, result: C::Value, dep_node_index: DepNodeIndex)
163 where
164 C: QueryCache<Key = K>,
165 {
166 let key = self.key;
167 let state = self.state;
168
169 mem::forget(self);
171
172 cache.complete(key, result, dep_node_index);
175
176 let job = {
177 let val = {
178 let mut lock = state.active.lock_shard_by_value(&key);
183 lock.remove(&key)
184 };
185 val.unwrap().expect_job()
186 };
187
188 job.signal_complete();
189 }
190}
191
192impl<'tcx, K> Drop for JobOwner<'tcx, K>
193where
194 K: Eq + Hash + Copy,
195{
196 #[inline(never)]
197 #[cold]
198 fn drop(&mut self) {
199 let state = self.state;
201 let job = {
202 let mut shard = state.active.lock_shard_by_value(&self.key);
203 let job = shard.remove(&self.key).unwrap().expect_job();
204
205 shard.insert(self.key, QueryResult::Poisoned);
206 job
207 };
208 job.signal_complete();
211 }
212}
213
214#[derive(Clone, Debug)]
215pub struct CycleError {
216 pub usage: Option<(Span, QueryStackFrame)>,
218 pub cycle: Vec<QueryInfo>,
219}
220
221#[inline(always)]
226pub fn try_get_cached<Tcx, C>(tcx: Tcx, cache: &C, key: &C::Key) -> Option<C::Value>
227where
228 C: QueryCache,
229 Tcx: DepContext,
230{
231 match cache.lookup(key) {
232 Some((value, index)) => {
233 tcx.profiler().query_cache_hit(index.into());
234 tcx.dep_graph().read_index(index);
235 Some(value)
236 }
237 None => None,
238 }
239}
240
241#[cold]
242#[inline(never)]
243fn cycle_error<Q, Qcx>(
244 query: Q,
245 qcx: Qcx,
246 try_execute: QueryJobId,
247 span: Span,
248) -> (Q::Value, Option<DepNodeIndex>)
249where
250 Q: QueryConfig<Qcx>,
251 Qcx: QueryContext,
252{
253 let error =
254 try_execute.find_cycle_in_stack(qcx.collect_active_jobs(), &qcx.current_query_job(), span);
255 (mk_cycle(query, qcx, error), None)
256}
257
258#[inline(always)]
259fn wait_for_query<Q, Qcx>(
260 query: Q,
261 qcx: Qcx,
262 span: Span,
263 key: Q::Key,
264 latch: QueryLatch,
265 current: Option<QueryJobId>,
266) -> (Q::Value, Option<DepNodeIndex>)
267where
268 Q: QueryConfig<Qcx>,
269 Qcx: QueryContext,
270{
271 let query_blocked_prof_timer = qcx.dep_context().profiler().query_blocked();
275
276 let result = latch.wait_on(current, span);
279
280 match result {
281 Ok(()) => {
282 let Some((v, index)) = query.query_cache(qcx).lookup(&key) else {
283 outline(|| {
284 let lock = query.query_state(qcx).active.get_shard_by_value(&key).lock();
287
288 match lock.get(&key) {
289 Some(QueryResult::Poisoned) => FatalError.raise(),
291 _ => panic!(
292 "query '{}' result must be in the cache or the query must be poisoned after a wait",
293 query.name()
294 ),
295 }
296 })
297 };
298
299 qcx.dep_context().profiler().query_cache_hit(index.into());
300 query_blocked_prof_timer.finish_with_query_invocation_id(index.into());
301
302 (v, Some(index))
303 }
304 Err(cycle) => (mk_cycle(query, qcx, cycle), None),
305 }
306}
307
308#[inline(never)]
309fn try_execute_query<Q, Qcx, const INCR: bool>(
310 query: Q,
311 qcx: Qcx,
312 span: Span,
313 key: Q::Key,
314 dep_node: Option<DepNode>,
315) -> (Q::Value, Option<DepNodeIndex>)
316where
317 Q: QueryConfig<Qcx>,
318 Qcx: QueryContext,
319{
320 let state = query.query_state(qcx);
321 let mut state_lock = state.active.lock_shard_by_value(&key);
322
323 if qcx.dep_context().sess().threads() > 1 {
330 if let Some((value, index)) = query.query_cache(qcx).lookup(&key) {
331 qcx.dep_context().profiler().query_cache_hit(index.into());
332 return (value, Some(index));
333 }
334 }
335
336 let current_job_id = qcx.current_query_job();
337
338 match state_lock.entry(key) {
339 Entry::Vacant(entry) => {
340 let id = qcx.next_job_id();
343 let job = QueryJob::new(id, span, current_job_id);
344 entry.insert(QueryResult::Started(job));
345
346 drop(state_lock);
348
349 execute_job::<_, _, INCR>(query, qcx, state, key, id, dep_node)
350 }
351 Entry::Occupied(mut entry) => {
352 match entry.get_mut() {
353 QueryResult::Started(job) => {
354 if sync::is_dyn_thread_safe() {
355 let latch = job.latch();
357 drop(state_lock);
358
359 return wait_for_query(query, qcx, span, key, latch, current_job_id);
362 }
363
364 let id = job.id;
365 drop(state_lock);
366
367 cycle_error(query, qcx, id, span)
370 }
371 QueryResult::Poisoned => FatalError.raise(),
372 }
373 }
374 }
375}
376
377#[inline(always)]
378fn execute_job<Q, Qcx, const INCR: bool>(
379 query: Q,
380 qcx: Qcx,
381 state: &QueryState<Q::Key>,
382 key: Q::Key,
383 id: QueryJobId,
384 dep_node: Option<DepNode>,
385) -> (Q::Value, Option<DepNodeIndex>)
386where
387 Q: QueryConfig<Qcx>,
388 Qcx: QueryContext,
389{
390 let job_owner = JobOwner { state, key };
392
393 debug_assert_eq!(qcx.dep_context().dep_graph().is_fully_enabled(), INCR);
394
395 let (result, dep_node_index) = if INCR {
396 execute_job_incr(
397 query,
398 qcx,
399 qcx.dep_context().dep_graph().data().unwrap(),
400 key,
401 dep_node,
402 id,
403 )
404 } else {
405 execute_job_non_incr(query, qcx, key, id)
406 };
407
408 let cache = query.query_cache(qcx);
409 if query.feedable() {
410 if let Some((cached_result, _)) = cache.lookup(&key) {
415 let Some(hasher) = query.hash_result() else {
416 panic!(
417 "no_hash fed query later has its value computed.\n\
418 Remove `no_hash` modifier to allow recomputation.\n\
419 The already cached value: {}",
420 (query.format_value())(&cached_result)
421 );
422 };
423
424 let (old_hash, new_hash) = qcx.dep_context().with_stable_hashing_context(|mut hcx| {
425 (hasher(&mut hcx, &cached_result), hasher(&mut hcx, &result))
426 });
427 let formatter = query.format_value();
428 if old_hash != new_hash {
429 assert!(
432 qcx.dep_context().sess().dcx().has_errors().is_some(),
433 "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\
434 computed={:#?}\nfed={:#?}",
435 query.dep_kind(),
436 key,
437 formatter(&result),
438 formatter(&cached_result),
439 );
440 }
441 }
442 }
443 job_owner.complete(cache, result, dep_node_index);
444
445 (result, Some(dep_node_index))
446}
447
448#[inline(always)]
450fn execute_job_non_incr<Q, Qcx>(
451 query: Q,
452 qcx: Qcx,
453 key: Q::Key,
454 job_id: QueryJobId,
455) -> (Q::Value, DepNodeIndex)
456where
457 Q: QueryConfig<Qcx>,
458 Qcx: QueryContext,
459{
460 debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled());
461
462 if cfg!(debug_assertions) {
465 let _ = key.to_fingerprint(*qcx.dep_context());
466 }
467
468 let prof_timer = qcx.dep_context().profiler().query_provider();
469 let result = qcx.start_query(job_id, query.depth_limit(), || query.compute(qcx, key));
470 let dep_node_index = qcx.dep_context().dep_graph().next_virtual_depnode_index();
471 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
472
473 if cfg!(debug_assertions)
476 && let Some(hash_result) = query.hash_result()
477 {
478 qcx.dep_context().with_stable_hashing_context(|mut hcx| {
479 hash_result(&mut hcx, &result);
480 });
481 }
482
483 (result, dep_node_index)
484}
485
486#[inline(always)]
487fn execute_job_incr<Q, Qcx>(
488 query: Q,
489 qcx: Qcx,
490 dep_graph_data: &DepGraphData<Qcx::Deps>,
491 key: Q::Key,
492 mut dep_node_opt: Option<DepNode>,
493 job_id: QueryJobId,
494) -> (Q::Value, DepNodeIndex)
495where
496 Q: QueryConfig<Qcx>,
497 Qcx: QueryContext,
498{
499 if !query.anon() && !query.eval_always() {
500 let dep_node =
502 dep_node_opt.get_or_insert_with(|| query.construct_dep_node(*qcx.dep_context(), &key));
503
504 if let Some(ret) = qcx.start_query(job_id, false, || {
507 try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, dep_node)
508 }) {
509 return ret;
510 }
511 }
512
513 let prof_timer = qcx.dep_context().profiler().query_provider();
514
515 let (result, dep_node_index) = qcx.start_query(job_id, query.depth_limit(), || {
516 if query.anon() {
517 return dep_graph_data.with_anon_task_inner(
518 *qcx.dep_context(),
519 query.dep_kind(),
520 || query.compute(qcx, key),
521 );
522 }
523
524 let dep_node =
526 dep_node_opt.unwrap_or_else(|| query.construct_dep_node(*qcx.dep_context(), &key));
527
528 dep_graph_data.with_task(
529 dep_node,
530 (qcx, query),
531 key,
532 |(qcx, query), key| query.compute(qcx, key),
533 query.hash_result(),
534 )
535 });
536
537 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
538
539 (result, dep_node_index)
540}
541
542#[inline(always)]
543fn try_load_from_disk_and_cache_in_memory<Q, Qcx>(
544 query: Q,
545 dep_graph_data: &DepGraphData<Qcx::Deps>,
546 qcx: Qcx,
547 key: &Q::Key,
548 dep_node: &DepNode,
549) -> Option<(Q::Value, DepNodeIndex)>
550where
551 Q: QueryConfig<Qcx>,
552 Qcx: QueryContext,
553{
554 let (prev_dep_node_index, dep_node_index) = dep_graph_data.try_mark_green(qcx, dep_node)?;
558
559 debug_assert!(dep_graph_data.is_index_green(prev_dep_node_index));
560
561 if let Some(result) = query.try_load_from_disk(qcx, key, prev_dep_node_index, dep_node_index) {
564 if std::intrinsics::unlikely(qcx.dep_context().sess().opts.unstable_opts.query_dep_graph) {
565 dep_graph_data.mark_debug_loaded_from_disk(*dep_node)
566 }
567
568 let prev_fingerprint = dep_graph_data.prev_fingerprint_of(prev_dep_node_index);
569 let try_verify = prev_fingerprint.split().1.as_u64() % 32 == 0;
577 if std::intrinsics::unlikely(
578 try_verify || qcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich,
579 ) {
580 incremental_verify_ich(
581 *qcx.dep_context(),
582 dep_graph_data,
583 &result,
584 prev_dep_node_index,
585 query.hash_result(),
586 query.format_value(),
587 );
588 }
589
590 return Some((result, dep_node_index));
591 }
592
593 debug_assert!(
596 !query.cache_on_disk(*qcx.dep_context(), key)
597 || !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(),
598 "missing on-disk cache entry for {dep_node:?}"
599 );
600
601 debug_assert!(
604 !query.loadable_from_disk(qcx, key, prev_dep_node_index),
605 "missing on-disk cache entry for loadable {dep_node:?}"
606 );
607
608 let prof_timer = qcx.dep_context().profiler().query_provider();
611
612 let result = qcx.dep_context().dep_graph().with_ignore(|| query.compute(qcx, *key));
614
615 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
616
617 incremental_verify_ich(
627 *qcx.dep_context(),
628 dep_graph_data,
629 &result,
630 prev_dep_node_index,
631 query.hash_result(),
632 query.format_value(),
633 );
634
635 Some((result, dep_node_index))
636}
637
638#[inline]
639#[instrument(skip(tcx, dep_graph_data, result, hash_result, format_value), level = "debug")]
640pub(crate) fn incremental_verify_ich<Tcx, V>(
641 tcx: Tcx,
642 dep_graph_data: &DepGraphData<Tcx::Deps>,
643 result: &V,
644 prev_index: SerializedDepNodeIndex,
645 hash_result: Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>,
646 format_value: fn(&V) -> String,
647) where
648 Tcx: DepContext,
649{
650 if !dep_graph_data.is_index_green(prev_index) {
651 incremental_verify_ich_not_green(tcx, prev_index)
652 }
653
654 let new_hash = hash_result.map_or(Fingerprint::ZERO, |f| {
655 tcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result))
656 });
657
658 let old_hash = dep_graph_data.prev_fingerprint_of(prev_index);
659
660 if new_hash != old_hash {
661 incremental_verify_ich_failed(tcx, prev_index, &|| format_value(result));
662 }
663}
664
665#[cold]
666#[inline(never)]
667fn incremental_verify_ich_not_green<Tcx>(tcx: Tcx, prev_index: SerializedDepNodeIndex)
668where
669 Tcx: DepContext,
670{
671 panic!(
672 "fingerprint for green query instance not loaded from cache: {:?}",
673 tcx.dep_graph().data().unwrap().prev_node_of(prev_index)
674 )
675}
676
677#[cold]
681#[inline(never)]
682fn incremental_verify_ich_failed<Tcx>(
683 tcx: Tcx,
684 prev_index: SerializedDepNodeIndex,
685 result: &dyn Fn() -> String,
686) where
687 Tcx: DepContext,
688{
689 thread_local! {
696 static INSIDE_VERIFY_PANIC: Cell<bool> = const { Cell::new(false) };
697 };
698
699 let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true));
700
701 if old_in_panic {
702 tcx.sess().dcx().emit_err(crate::error::Reentrant);
703 } else {
704 let run_cmd = if let Some(crate_name) = &tcx.sess().opts.crate_name {
705 format!("`cargo clean -p {crate_name}` or `cargo clean`")
706 } else {
707 "`cargo clean`".to_string()
708 };
709
710 let dep_node = tcx.dep_graph().data().unwrap().prev_node_of(prev_index);
711 tcx.sess().dcx().emit_err(crate::error::IncrementCompilation {
712 run_cmd,
713 dep_node: format!("{dep_node:?}"),
714 });
715 panic!("Found unstable fingerprints for {dep_node:?}: {}", result());
716 }
717
718 INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.set(old_in_panic));
719}
720
721#[inline(never)]
730fn ensure_must_run<Q, Qcx>(
731 query: Q,
732 qcx: Qcx,
733 key: &Q::Key,
734 check_cache: bool,
735) -> (bool, Option<DepNode>)
736where
737 Q: QueryConfig<Qcx>,
738 Qcx: QueryContext,
739{
740 if query.eval_always() {
741 return (true, None);
742 }
743
744 assert!(!query.anon());
746
747 let dep_node = query.construct_dep_node(*qcx.dep_context(), key);
748
749 let dep_graph = qcx.dep_context().dep_graph();
750 let serialized_dep_node_index = match dep_graph.try_mark_green(qcx, &dep_node) {
751 None => {
752 return (true, Some(dep_node));
759 }
760 Some((serialized_dep_node_index, dep_node_index)) => {
761 dep_graph.read_index(dep_node_index);
762 qcx.dep_context().profiler().query_cache_hit(dep_node_index.into());
763 serialized_dep_node_index
764 }
765 };
766
767 if !check_cache {
769 return (false, None);
770 }
771
772 let loadable = query.loadable_from_disk(qcx, key, serialized_dep_node_index);
773 (!loadable, Some(dep_node))
774}
775
776#[derive(Debug)]
777pub enum QueryMode {
778 Get,
779 Ensure { check_cache: bool },
780}
781
782#[inline(always)]
783pub fn get_query_non_incr<Q, Qcx>(query: Q, qcx: Qcx, span: Span, key: Q::Key) -> Q::Value
784where
785 Q: QueryConfig<Qcx>,
786 Qcx: QueryContext,
787{
788 debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled());
789
790 ensure_sufficient_stack(|| try_execute_query::<Q, Qcx, false>(query, qcx, span, key, None).0)
791}
792
793#[inline(always)]
794pub fn get_query_incr<Q, Qcx>(
795 query: Q,
796 qcx: Qcx,
797 span: Span,
798 key: Q::Key,
799 mode: QueryMode,
800) -> Option<Q::Value>
801where
802 Q: QueryConfig<Qcx>,
803 Qcx: QueryContext,
804{
805 debug_assert!(qcx.dep_context().dep_graph().is_fully_enabled());
806
807 let dep_node = if let QueryMode::Ensure { check_cache } = mode {
808 let (must_run, dep_node) = ensure_must_run(query, qcx, &key, check_cache);
809 if !must_run {
810 return None;
811 }
812 dep_node
813 } else {
814 None
815 };
816
817 let (result, dep_node_index) = ensure_sufficient_stack(|| {
818 try_execute_query::<_, _, true>(query, qcx, span, key, dep_node)
819 });
820 if let Some(dep_node_index) = dep_node_index {
821 qcx.dep_context().dep_graph().read_index(dep_node_index)
822 }
823 Some(result)
824}
825
826pub fn force_query<Q, Qcx>(query: Q, qcx: Qcx, key: Q::Key, dep_node: DepNode)
827where
828 Q: QueryConfig<Qcx>,
829 Qcx: QueryContext,
830{
831 if let Some((_, index)) = query.query_cache(qcx).lookup(&key) {
834 qcx.dep_context().profiler().query_cache_hit(index.into());
835 return;
836 }
837
838 debug_assert!(!query.anon());
839
840 ensure_sufficient_stack(|| {
841 try_execute_query::<_, _, true>(query, qcx, DUMMY_SP, key, Some(dep_node))
842 });
843}