]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_query_system/src/query/plumbing.rs
Auto merge of #89541 - workingjubilee:abbrev-shufvec-t, r=Mark-Simulacrum
[rust.git] / compiler / rustc_query_system / src / query / plumbing.rs
1 //! The implementation of the query system itself. This defines the macros that
2 //! generate the actual methods on tcx which find and execute the provider,
3 //! manage the caches, and so forth.
4
5 use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex, DepNodeParams};
6 use crate::query::caches::QueryCache;
7 use crate::query::config::{QueryDescription, QueryVtable, QueryVtableExt};
8 use crate::query::job::{
9     report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryShardJobId,
10 };
11 use crate::query::{QueryContext, QueryMap, QuerySideEffects, QueryStackFrame};
12
13 use rustc_data_structures::fingerprint::Fingerprint;
14 use rustc_data_structures::fx::{FxHashMap, FxHasher};
15 #[cfg(parallel_compiler)]
16 use rustc_data_structures::profiling::TimingGuard;
17 use rustc_data_structures::sharded::{get_shard_index_by_hash, Sharded};
18 use rustc_data_structures::sync::{Lock, LockGuard};
19 use rustc_data_structures::thin_vec::ThinVec;
20 use rustc_errors::{DiagnosticBuilder, FatalError};
21 use rustc_span::{Span, DUMMY_SP};
22 use std::cell::Cell;
23 use std::collections::hash_map::Entry;
24 use std::fmt::Debug;
25 use std::hash::{Hash, Hasher};
26 use std::mem;
27 use std::num::NonZeroU32;
28 use std::ptr;
29 #[cfg(debug_assertions)]
30 use std::sync::atomic::{AtomicUsize, Ordering};
31
32 pub struct QueryCacheStore<C: QueryCache> {
33     cache: C,
34     shards: Sharded<C::Sharded>,
35     #[cfg(debug_assertions)]
36     pub cache_hits: AtomicUsize,
37 }
38
39 impl<C: QueryCache + Default> Default for QueryCacheStore<C> {
40     fn default() -> Self {
41         Self {
42             cache: C::default(),
43             shards: Default::default(),
44             #[cfg(debug_assertions)]
45             cache_hits: AtomicUsize::new(0),
46         }
47     }
48 }
49
50 /// Values used when checking a query cache which can be reused on a cache-miss to execute the query.
51 pub struct QueryLookup {
52     pub(super) key_hash: u64,
53     shard: usize,
54 }
55
56 // We compute the key's hash once and then use it for both the
57 // shard lookup and the hashmap lookup. This relies on the fact
58 // that both of them use `FxHasher`.
59 fn hash_for_shard<K: Hash>(key: &K) -> u64 {
60     let mut hasher = FxHasher::default();
61     key.hash(&mut hasher);
62     hasher.finish()
63 }
64
65 impl<C: QueryCache> QueryCacheStore<C> {
66     pub(super) fn get_lookup<'tcx>(
67         &'tcx self,
68         key: &C::Key,
69     ) -> (QueryLookup, LockGuard<'tcx, C::Sharded>) {
70         let key_hash = hash_for_shard(key);
71         let shard = get_shard_index_by_hash(key_hash);
72         let lock = self.shards.get_shard_by_index(shard).lock();
73         (QueryLookup { key_hash, shard }, lock)
74     }
75
76     pub fn iter_results(&self, f: &mut dyn FnMut(&C::Key, &C::Value, DepNodeIndex)) {
77         self.cache.iter(&self.shards, f)
78     }
79 }
80
81 struct QueryStateShard<D, K> {
82     active: FxHashMap<K, QueryResult<D>>,
83
84     /// Used to generate unique ids for active jobs.
85     jobs: u32,
86 }
87
88 impl<D, K> Default for QueryStateShard<D, K> {
89     fn default() -> QueryStateShard<D, K> {
90         QueryStateShard { active: Default::default(), jobs: 0 }
91     }
92 }
93
94 pub struct QueryState<D, K> {
95     shards: Sharded<QueryStateShard<D, K>>,
96 }
97
98 /// Indicates the state of a query for a given key in a query map.
99 enum QueryResult<D> {
100     /// An already executing query. The query job can be used to await for its completion.
101     Started(QueryJob<D>),
102
103     /// The query panicked. Queries trying to wait on this will raise a fatal error which will
104     /// silently panic.
105     Poisoned,
106 }
107
108 impl<D, K> QueryState<D, K>
109 where
110     D: Copy + Clone + Eq + Hash,
111     K: Eq + Hash + Clone + Debug,
112 {
113     pub fn all_inactive(&self) -> bool {
114         let shards = self.shards.lock_shards();
115         shards.iter().all(|shard| shard.active.is_empty())
116     }
117
118     pub fn try_collect_active_jobs<CTX: Copy>(
119         &self,
120         tcx: CTX,
121         kind: D,
122         make_query: fn(CTX, K) -> QueryStackFrame,
123         jobs: &mut QueryMap<D>,
124     ) -> Option<()> {
125         // We use try_lock_shards here since we are called from the
126         // deadlock handler, and this shouldn't be locked.
127         let shards = self.shards.try_lock_shards()?;
128         for (shard_id, shard) in shards.iter().enumerate() {
129             for (k, v) in shard.active.iter() {
130                 if let QueryResult::Started(ref job) = *v {
131                     let id = QueryJobId::new(job.id, shard_id, kind);
132                     let query = make_query(tcx, k.clone());
133                     jobs.insert(id, QueryJobInfo { query, job: job.clone() });
134                 }
135             }
136         }
137
138         Some(())
139     }
140 }
141
142 impl<D, K> Default for QueryState<D, K> {
143     fn default() -> QueryState<D, K> {
144         QueryState { shards: Default::default() }
145     }
146 }
147
148 /// A type representing the responsibility to execute the job in the `job` field.
149 /// This will poison the relevant query if dropped.
150 struct JobOwner<'tcx, D, K>
151 where
152     D: Copy + Clone + Eq + Hash,
153     K: Eq + Hash + Clone,
154 {
155     state: &'tcx QueryState<D, K>,
156     key: K,
157     id: QueryJobId<D>,
158 }
159
160 #[cold]
161 #[inline(never)]
162 fn mk_cycle<CTX, V, R>(
163     tcx: CTX,
164     error: CycleError,
165     handle_cycle_error: fn(CTX, DiagnosticBuilder<'_>) -> V,
166     cache: &dyn crate::query::QueryStorage<Value = V, Stored = R>,
167 ) -> R
168 where
169     CTX: QueryContext,
170     V: std::fmt::Debug,
171     R: Clone,
172 {
173     let error = report_cycle(tcx.dep_context().sess(), error);
174     let value = handle_cycle_error(tcx, error);
175     cache.store_nocache(value)
176 }
177
178 impl<'tcx, D, K> JobOwner<'tcx, D, K>
179 where
180     D: Copy + Clone + Eq + Hash,
181     K: Eq + Hash + Clone,
182 {
183     /// Either gets a `JobOwner` corresponding the query, allowing us to
184     /// start executing the query, or returns with the result of the query.
185     /// This function assumes that `try_get_cached` is already called and returned `lookup`.
186     /// If the query is executing elsewhere, this will wait for it and return the result.
187     /// If the query panicked, this will silently panic.
188     ///
189     /// This function is inlined because that results in a noticeable speed-up
190     /// for some compile-time benchmarks.
191     #[inline(always)]
192     fn try_start<'b, CTX>(
193         tcx: &'b CTX,
194         state: &'b QueryState<CTX::DepKind, K>,
195         span: Span,
196         key: K,
197         lookup: QueryLookup,
198         dep_kind: CTX::DepKind,
199     ) -> TryGetJob<'b, CTX::DepKind, K>
200     where
201         CTX: QueryContext,
202     {
203         let shard = lookup.shard;
204         let mut state_lock = state.shards.get_shard_by_index(shard).lock();
205         let lock = &mut *state_lock;
206
207         match lock.active.entry(key) {
208             Entry::Vacant(entry) => {
209                 // Generate an id unique within this shard.
210                 let id = lock.jobs.checked_add(1).unwrap();
211                 lock.jobs = id;
212                 let id = QueryShardJobId(NonZeroU32::new(id).unwrap());
213
214                 let job = tcx.current_query_job();
215                 let job = QueryJob::new(id, span, job);
216
217                 let key = entry.key().clone();
218                 entry.insert(QueryResult::Started(job));
219
220                 let global_id = QueryJobId::new(id, shard, dep_kind);
221                 let owner = JobOwner { state, id: global_id, key };
222                 return TryGetJob::NotYetStarted(owner);
223             }
224             Entry::Occupied(mut entry) => {
225                 match entry.get_mut() {
226                     #[cfg(not(parallel_compiler))]
227                     QueryResult::Started(job) => {
228                         let id = QueryJobId::new(job.id, shard, dep_kind);
229
230                         drop(state_lock);
231
232                         // If we are single-threaded we know that we have cycle error,
233                         // so we just return the error.
234                         return TryGetJob::Cycle(id.find_cycle_in_stack(
235                             tcx.try_collect_active_jobs().unwrap(),
236                             &tcx.current_query_job(),
237                             span,
238                         ));
239                     }
240                     #[cfg(parallel_compiler)]
241                     QueryResult::Started(job) => {
242                         // For parallel queries, we'll block and wait until the query running
243                         // in another thread has completed. Record how long we wait in the
244                         // self-profiler.
245                         let query_blocked_prof_timer = tcx.dep_context().profiler().query_blocked();
246
247                         // Get the latch out
248                         let latch = job.latch();
249
250                         drop(state_lock);
251
252                         // With parallel queries we might just have to wait on some other
253                         // thread.
254                         let result = latch.wait_on(tcx.current_query_job(), span);
255
256                         match result {
257                             Ok(()) => TryGetJob::JobCompleted(query_blocked_prof_timer),
258                             Err(cycle) => TryGetJob::Cycle(cycle),
259                         }
260                     }
261                     QueryResult::Poisoned => FatalError.raise(),
262                 }
263             }
264         }
265     }
266
267     /// Completes the query by updating the query cache with the `result`,
268     /// signals the waiter and forgets the JobOwner, so it won't poison the query
269     fn complete<C>(
270         self,
271         cache: &QueryCacheStore<C>,
272         result: C::Value,
273         dep_node_index: DepNodeIndex,
274     ) -> C::Stored
275     where
276         C: QueryCache<Key = K>,
277     {
278         // We can move out of `self` here because we `mem::forget` it below
279         let key = unsafe { ptr::read(&self.key) };
280         let state = self.state;
281
282         // Forget ourself so our destructor won't poison the query
283         mem::forget(self);
284
285         let (job, result) = {
286             let key_hash = hash_for_shard(&key);
287             let shard = get_shard_index_by_hash(key_hash);
288             let job = {
289                 let mut lock = state.shards.get_shard_by_index(shard).lock();
290                 match lock.active.remove(&key).unwrap() {
291                     QueryResult::Started(job) => job,
292                     QueryResult::Poisoned => panic!(),
293                 }
294             };
295             let result = {
296                 let mut lock = cache.shards.get_shard_by_index(shard).lock();
297                 cache.cache.complete(&mut lock, key, result, dep_node_index)
298             };
299             (job, result)
300         };
301
302         job.signal_complete();
303         result
304     }
305 }
306
307 impl<'tcx, D, K> Drop for JobOwner<'tcx, D, K>
308 where
309     D: Copy + Clone + Eq + Hash,
310     K: Eq + Hash + Clone,
311 {
312     #[inline(never)]
313     #[cold]
314     fn drop(&mut self) {
315         // Poison the query so jobs waiting on it panic.
316         let state = self.state;
317         let shard = state.shards.get_shard_by_value(&self.key);
318         let job = {
319             let mut shard = shard.lock();
320             let job = match shard.active.remove(&self.key).unwrap() {
321                 QueryResult::Started(job) => job,
322                 QueryResult::Poisoned => panic!(),
323             };
324             shard.active.insert(self.key.clone(), QueryResult::Poisoned);
325             job
326         };
327         // Also signal the completion of the job, so waiters
328         // will continue execution.
329         job.signal_complete();
330     }
331 }
332
333 #[derive(Clone)]
334 pub(crate) struct CycleError {
335     /// The query and related span that uses the cycle.
336     pub usage: Option<(Span, QueryStackFrame)>,
337     pub cycle: Vec<QueryInfo>,
338 }
339
340 /// The result of `try_start`.
341 enum TryGetJob<'tcx, D, K>
342 where
343     D: Copy + Clone + Eq + Hash,
344     K: Eq + Hash + Clone,
345 {
346     /// The query is not yet started. Contains a guard to the cache eventually used to start it.
347     NotYetStarted(JobOwner<'tcx, D, K>),
348
349     /// The query was already completed.
350     /// Returns the result of the query and its dep-node index
351     /// if it succeeded or a cycle error if it failed.
352     #[cfg(parallel_compiler)]
353     JobCompleted(TimingGuard<'tcx>),
354
355     /// Trying to execute the query resulted in a cycle.
356     Cycle(CycleError),
357 }
358
359 /// Checks if the query is already computed and in the cache.
360 /// It returns the shard index and a lock guard to the shard,
361 /// which will be used if the query is not in the cache and we need
362 /// to compute it.
363 #[inline]
364 pub fn try_get_cached<'a, CTX, C, R, OnHit>(
365     tcx: CTX,
366     cache: &'a QueryCacheStore<C>,
367     key: &C::Key,
368     // `on_hit` can be called while holding a lock to the query cache
369     on_hit: OnHit,
370 ) -> Result<R, QueryLookup>
371 where
372     C: QueryCache,
373     CTX: DepContext,
374     OnHit: FnOnce(&C::Stored) -> R,
375 {
376     cache.cache.lookup(cache, &key, |value, index| {
377         if unlikely!(tcx.profiler().enabled()) {
378             tcx.profiler().query_cache_hit(index.into());
379         }
380         #[cfg(debug_assertions)]
381         {
382             cache.cache_hits.fetch_add(1, Ordering::Relaxed);
383         }
384         tcx.dep_graph().read_index(index);
385         on_hit(value)
386     })
387 }
388
389 fn try_execute_query<CTX, C>(
390     tcx: CTX,
391     state: &QueryState<CTX::DepKind, C::Key>,
392     cache: &QueryCacheStore<C>,
393     span: Span,
394     key: C::Key,
395     lookup: QueryLookup,
396     dep_node: Option<DepNode<CTX::DepKind>>,
397     query: &QueryVtable<CTX, C::Key, C::Value>,
398     compute: fn(CTX::DepContext, C::Key) -> C::Value,
399 ) -> (C::Stored, Option<DepNodeIndex>)
400 where
401     C: QueryCache,
402     C::Key: Clone + DepNodeParams<CTX::DepContext>,
403     CTX: QueryContext,
404 {
405     match JobOwner::<'_, CTX::DepKind, C::Key>::try_start(
406         &tcx,
407         state,
408         span,
409         key.clone(),
410         lookup,
411         query.dep_kind,
412     ) {
413         TryGetJob::NotYetStarted(job) => {
414             let (result, dep_node_index) = execute_job(tcx, key, dep_node, query, job.id, compute);
415             let result = job.complete(cache, result, dep_node_index);
416             (result, Some(dep_node_index))
417         }
418         TryGetJob::Cycle(error) => {
419             let result = mk_cycle(tcx, error, query.handle_cycle_error, &cache.cache);
420             (result, None)
421         }
422         #[cfg(parallel_compiler)]
423         TryGetJob::JobCompleted(query_blocked_prof_timer) => {
424             let (v, index) = cache
425                 .cache
426                 .lookup(cache, &key, |value, index| (value.clone(), index))
427                 .unwrap_or_else(|_| panic!("value must be in cache after waiting"));
428
429             if unlikely!(tcx.dep_context().profiler().enabled()) {
430                 tcx.dep_context().profiler().query_cache_hit(index.into());
431             }
432             #[cfg(debug_assertions)]
433             {
434                 cache.cache_hits.fetch_add(1, Ordering::Relaxed);
435             }
436             query_blocked_prof_timer.finish_with_query_invocation_id(index.into());
437
438             (v, Some(index))
439         }
440     }
441 }
442
443 fn execute_job<CTX, K, V>(
444     tcx: CTX,
445     key: K,
446     mut dep_node_opt: Option<DepNode<CTX::DepKind>>,
447     query: &QueryVtable<CTX, K, V>,
448     job_id: QueryJobId<CTX::DepKind>,
449     compute: fn(CTX::DepContext, K) -> V,
450 ) -> (V, DepNodeIndex)
451 where
452     K: Clone + DepNodeParams<CTX::DepContext>,
453     V: Debug,
454     CTX: QueryContext,
455 {
456     let dep_graph = tcx.dep_context().dep_graph();
457
458     // Fast path for when incr. comp. is off.
459     if !dep_graph.is_fully_enabled() {
460         let prof_timer = tcx.dep_context().profiler().query_provider();
461         let result = tcx.start_query(job_id, None, || compute(*tcx.dep_context(), key));
462         let dep_node_index = dep_graph.next_virtual_depnode_index();
463         prof_timer.finish_with_query_invocation_id(dep_node_index.into());
464         return (result, dep_node_index);
465     }
466
467     if !query.anon && !query.eval_always {
468         // `to_dep_node` is expensive for some `DepKind`s.
469         let dep_node =
470             dep_node_opt.get_or_insert_with(|| query.to_dep_node(*tcx.dep_context(), &key));
471
472         // The diagnostics for this query will be promoted to the current session during
473         // `try_mark_green()`, so we can ignore them here.
474         if let Some(ret) = tcx.start_query(job_id, None, || {
475             try_load_from_disk_and_cache_in_memory(tcx, &key, &dep_node, query, compute)
476         }) {
477             return ret;
478         }
479     }
480
481     let prof_timer = tcx.dep_context().profiler().query_provider();
482     let diagnostics = Lock::new(ThinVec::new());
483
484     let (result, dep_node_index) = tcx.start_query(job_id, Some(&diagnostics), || {
485         if query.anon {
486             return dep_graph.with_anon_task(*tcx.dep_context(), query.dep_kind, || {
487                 compute(*tcx.dep_context(), key)
488             });
489         }
490
491         // `to_dep_node` is expensive for some `DepKind`s.
492         let dep_node = dep_node_opt.unwrap_or_else(|| query.to_dep_node(*tcx.dep_context(), &key));
493
494         dep_graph.with_task(dep_node, *tcx.dep_context(), key, compute, query.hash_result)
495     });
496
497     prof_timer.finish_with_query_invocation_id(dep_node_index.into());
498
499     let diagnostics = diagnostics.into_inner();
500     let side_effects = QuerySideEffects { diagnostics };
501
502     if unlikely!(!side_effects.is_empty()) {
503         if query.anon {
504             tcx.store_side_effects_for_anon_node(dep_node_index, side_effects);
505         } else {
506             tcx.store_side_effects(dep_node_index, side_effects);
507         }
508     }
509
510     (result, dep_node_index)
511 }
512
513 fn try_load_from_disk_and_cache_in_memory<CTX, K, V>(
514     tcx: CTX,
515     key: &K,
516     dep_node: &DepNode<CTX::DepKind>,
517     query: &QueryVtable<CTX, K, V>,
518     compute: fn(CTX::DepContext, K) -> V,
519 ) -> Option<(V, DepNodeIndex)>
520 where
521     K: Clone,
522     CTX: QueryContext,
523     V: Debug,
524 {
525     // Note this function can be called concurrently from the same query
526     // We must ensure that this is handled correctly.
527
528     let dep_graph = tcx.dep_context().dep_graph();
529     let (prev_dep_node_index, dep_node_index) = dep_graph.try_mark_green(tcx, &dep_node)?;
530
531     debug_assert!(dep_graph.is_green(dep_node));
532
533     // First we try to load the result from the on-disk cache.
534     // Some things are never cached on disk.
535     if query.cache_on_disk(tcx, key, None) {
536         let prof_timer = tcx.dep_context().profiler().incr_cache_loading();
537         let result = query.try_load_from_disk(tcx, prev_dep_node_index);
538         prof_timer.finish_with_query_invocation_id(dep_node_index.into());
539
540         // We always expect to find a cached result for things that
541         // can be forced from `DepNode`.
542         debug_assert!(
543             !dep_node.kind.fingerprint_style().reconstructible() || result.is_some(),
544             "missing on-disk cache entry for {:?}",
545             dep_node
546         );
547
548         if let Some(result) = result {
549             // If `-Zincremental-verify-ich` is specified, re-hash results from
550             // the cache and make sure that they have the expected fingerprint.
551             if unlikely!(tcx.dep_context().sess().opts.debugging_opts.incremental_verify_ich) {
552                 incremental_verify_ich(*tcx.dep_context(), &result, dep_node, query);
553             }
554
555             return Some((result, dep_node_index));
556         }
557     }
558
559     // We could not load a result from the on-disk cache, so
560     // recompute.
561     let prof_timer = tcx.dep_context().profiler().query_provider();
562
563     // The dep-graph for this computation is already in-place.
564     let result = dep_graph.with_ignore(|| compute(*tcx.dep_context(), key.clone()));
565
566     prof_timer.finish_with_query_invocation_id(dep_node_index.into());
567
568     // Verify that re-running the query produced a result with the expected hash
569     // This catches bugs in query implementations, turning them into ICEs.
570     // For example, a query might sort its result by `DefId` - since `DefId`s are
571     // not stable across compilation sessions, the result could get up getting sorted
572     // in a different order when the query is re-run, even though all of the inputs
573     // (e.g. `DefPathHash` values) were green.
574     //
575     // See issue #82920 for an example of a miscompilation that would get turned into
576     // an ICE by this check
577     incremental_verify_ich(*tcx.dep_context(), &result, dep_node, query);
578
579     Some((result, dep_node_index))
580 }
581
582 fn incremental_verify_ich<CTX, K, V: Debug>(
583     tcx: CTX::DepContext,
584     result: &V,
585     dep_node: &DepNode<CTX::DepKind>,
586     query: &QueryVtable<CTX, K, V>,
587 ) where
588     CTX: QueryContext,
589 {
590     assert!(
591         tcx.dep_graph().is_green(dep_node),
592         "fingerprint for green query instance not loaded from cache: {:?}",
593         dep_node,
594     );
595
596     debug!("BEGIN verify_ich({:?})", dep_node);
597     let mut hcx = tcx.create_stable_hashing_context();
598
599     let new_hash = query.hash_result(&mut hcx, result).unwrap_or(Fingerprint::ZERO);
600     debug!("END verify_ich({:?})", dep_node);
601
602     let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node);
603
604     if Some(new_hash) != old_hash {
605         let run_cmd = if let Some(crate_name) = &tcx.sess().opts.crate_name {
606             format!("`cargo clean -p {}` or `cargo clean`", crate_name)
607         } else {
608             "`cargo clean`".to_string()
609         };
610
611         // When we emit an error message and panic, we try to debug-print the `DepNode`
612         // and query result. Unforunately, this can cause us to run additional queries,
613         // which may result in another fingerprint mismatch while we're in the middle
614         // of processing this one. To avoid a double-panic (which kills the process
615         // before we can print out the query static), we print out a terse
616         // but 'safe' message if we detect a re-entrant call to this method.
617         thread_local! {
618             static INSIDE_VERIFY_PANIC: Cell<bool> = const { Cell::new(false) };
619         };
620
621         let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true));
622
623         if old_in_panic {
624             tcx.sess().struct_err("internal compiler error: re-entrant incremental verify failure, suppressing message")
625                 .emit();
626         } else {
627             tcx.sess().struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node))
628                 .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd))
629                 .note(&"Please follow the instructions below to create a bug report with the provided information")
630                 .note(&"See <https://github.com/rust-lang/rust/issues/84970> for more information")
631                 .emit();
632             panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result);
633         }
634
635         INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.set(old_in_panic));
636     }
637 }
638
639 /// Ensure that either this query has all green inputs or been executed.
640 /// Executing `query::ensure(D)` is considered a read of the dep-node `D`.
641 /// Returns true if the query should still run.
642 ///
643 /// This function is particularly useful when executing passes for their
644 /// side-effects -- e.g., in order to report errors for erroneous programs.
645 ///
646 /// Note: The optimization is only available during incr. comp.
647 #[inline(never)]
648 fn ensure_must_run<CTX, K, V>(
649     tcx: CTX,
650     key: &K,
651     query: &QueryVtable<CTX, K, V>,
652 ) -> (bool, Option<DepNode<CTX::DepKind>>)
653 where
654     K: crate::dep_graph::DepNodeParams<CTX::DepContext>,
655     CTX: QueryContext,
656 {
657     if query.eval_always {
658         return (true, None);
659     }
660
661     // Ensuring an anonymous query makes no sense
662     assert!(!query.anon);
663
664     let dep_node = query.to_dep_node(*tcx.dep_context(), key);
665
666     let dep_graph = tcx.dep_context().dep_graph();
667     match dep_graph.try_mark_green(tcx, &dep_node) {
668         None => {
669             // A None return from `try_mark_green` means that this is either
670             // a new dep node or that the dep node has already been marked red.
671             // Either way, we can't call `dep_graph.read()` as we don't have the
672             // DepNodeIndex. We must invoke the query itself. The performance cost
673             // this introduces should be negligible as we'll immediately hit the
674             // in-memory cache, or another query down the line will.
675             (true, Some(dep_node))
676         }
677         Some((_, dep_node_index)) => {
678             dep_graph.read_index(dep_node_index);
679             tcx.dep_context().profiler().query_cache_hit(dep_node_index.into());
680             (false, None)
681         }
682     }
683 }
684
685 #[inline(never)]
686 fn force_query_impl<CTX, C>(
687     tcx: CTX,
688     state: &QueryState<CTX::DepKind, C::Key>,
689     cache: &QueryCacheStore<C>,
690     key: C::Key,
691     dep_node: DepNode<CTX::DepKind>,
692     query: &QueryVtable<CTX, C::Key, C::Value>,
693     compute: fn(CTX::DepContext, C::Key) -> C::Value,
694 ) -> bool
695 where
696     C: QueryCache,
697     C::Key: DepNodeParams<CTX::DepContext>,
698     CTX: QueryContext,
699 {
700     debug_assert!(!query.anon);
701
702     // We may be concurrently trying both execute and force a query.
703     // Ensure that only one of them runs the query.
704     let cached = cache.cache.lookup(cache, &key, |_, index| {
705         if unlikely!(tcx.dep_context().profiler().enabled()) {
706             tcx.dep_context().profiler().query_cache_hit(index.into());
707         }
708         #[cfg(debug_assertions)]
709         {
710             cache.cache_hits.fetch_add(1, Ordering::Relaxed);
711         }
712     });
713
714     let lookup = match cached {
715         Ok(()) => return true,
716         Err(lookup) => lookup,
717     };
718
719     let _ =
720         try_execute_query(tcx, state, cache, DUMMY_SP, key, lookup, Some(dep_node), query, compute);
721     true
722 }
723
724 pub enum QueryMode {
725     Get,
726     Ensure,
727 }
728
729 pub fn get_query<Q, CTX>(
730     tcx: CTX,
731     span: Span,
732     key: Q::Key,
733     lookup: QueryLookup,
734     mode: QueryMode,
735 ) -> Option<Q::Stored>
736 where
737     Q: QueryDescription<CTX>,
738     Q::Key: DepNodeParams<CTX::DepContext>,
739     CTX: QueryContext,
740 {
741     let query = &Q::VTABLE;
742     let dep_node = if let QueryMode::Ensure = mode {
743         let (must_run, dep_node) = ensure_must_run(tcx, &key, query);
744         if !must_run {
745             return None;
746         }
747         dep_node
748     } else {
749         None
750     };
751
752     debug!("ty::query::get_query<{}>(key={:?}, span={:?})", Q::NAME, key, span);
753     let compute = Q::compute_fn(tcx, &key);
754     let (result, dep_node_index) = try_execute_query(
755         tcx,
756         Q::query_state(tcx),
757         Q::query_cache(tcx),
758         span,
759         key,
760         lookup,
761         dep_node,
762         query,
763         compute,
764     );
765     if let Some(dep_node_index) = dep_node_index {
766         tcx.dep_context().dep_graph().read_index(dep_node_index)
767     }
768     Some(result)
769 }
770
771 pub fn force_query<Q, CTX>(tcx: CTX, dep_node: &DepNode<CTX::DepKind>) -> bool
772 where
773     Q: QueryDescription<CTX>,
774     Q::Key: DepNodeParams<CTX::DepContext>,
775     CTX: QueryContext,
776 {
777     if Q::ANON {
778         return false;
779     }
780
781     if !<Q::Key as DepNodeParams<CTX::DepContext>>::fingerprint_style().reconstructible() {
782         return false;
783     }
784
785     let key = if let Some(key) =
786         <Q::Key as DepNodeParams<CTX::DepContext>>::recover(*tcx.dep_context(), &dep_node)
787     {
788         key
789     } else {
790         return false;
791     };
792
793     let compute = Q::compute_fn(tcx, &key);
794     force_query_impl(
795         tcx,
796         Q::query_state(tcx),
797         Q::query_cache(tcx),
798         key,
799         *dep_node,
800         &Q::VTABLE,
801         compute,
802     )
803 }