]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_query_system/src/query/job.rs
Auto merge of #89541 - workingjubilee:abbrev-shufvec-t, r=Mark-Simulacrum
[rust.git] / compiler / rustc_query_system / src / query / job.rs
1 use crate::dep_graph::DepContext;
2 use crate::query::plumbing::CycleError;
3 use crate::query::{QueryContext, QueryStackFrame, SimpleDefKind};
4
5 use rustc_data_structures::fx::FxHashMap;
6 use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, Handler, Level};
7 use rustc_session::Session;
8 use rustc_span::Span;
9
10 use std::convert::TryFrom;
11 use std::hash::Hash;
12 use std::num::NonZeroU32;
13
14 #[cfg(parallel_compiler)]
15 use {
16     crate::dep_graph::DepKind,
17     parking_lot::{Condvar, Mutex},
18     rustc_data_structures::fx::FxHashSet,
19     rustc_data_structures::sync::Lock,
20     rustc_data_structures::sync::Lrc,
21     rustc_data_structures::{jobserver, OnDrop},
22     rustc_rayon_core as rayon_core,
23     rustc_span::DUMMY_SP,
24     std::iter::{self, FromIterator},
25     std::{mem, process},
26 };
27
28 /// Represents a span and a query key.
29 #[derive(Clone, Debug)]
30 pub struct QueryInfo {
31     /// The span corresponding to the reason for which this query was required.
32     pub span: Span,
33     pub query: QueryStackFrame,
34 }
35
36 pub type QueryMap<D> = FxHashMap<QueryJobId<D>, QueryJobInfo<D>>;
37
38 /// A value uniquely identifying an active query job within a shard in the query cache.
39 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
40 pub struct QueryShardJobId(pub NonZeroU32);
41
42 /// A value uniquely identifying an active query job.
43 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
44 pub struct QueryJobId<D> {
45     /// Which job within a shard is this
46     pub job: QueryShardJobId,
47
48     /// In which shard is this job
49     pub shard: u16,
50
51     /// What kind of query this job is.
52     pub kind: D,
53 }
54
55 impl<D> QueryJobId<D>
56 where
57     D: Copy + Clone + Eq + Hash,
58 {
59     pub fn new(job: QueryShardJobId, shard: usize, kind: D) -> Self {
60         QueryJobId { job, shard: u16::try_from(shard).unwrap(), kind }
61     }
62
63     fn query(self, map: &QueryMap<D>) -> QueryStackFrame {
64         map.get(&self).unwrap().query.clone()
65     }
66
67     #[cfg(parallel_compiler)]
68     fn span(self, map: &QueryMap<D>) -> Span {
69         map.get(&self).unwrap().job.span
70     }
71
72     #[cfg(parallel_compiler)]
73     fn parent(self, map: &QueryMap<D>) -> Option<QueryJobId<D>> {
74         map.get(&self).unwrap().job.parent
75     }
76
77     #[cfg(parallel_compiler)]
78     fn latch<'a>(self, map: &'a QueryMap<D>) -> Option<&'a QueryLatch<D>> {
79         map.get(&self).unwrap().job.latch.as_ref()
80     }
81 }
82
83 pub struct QueryJobInfo<D> {
84     pub query: QueryStackFrame,
85     pub job: QueryJob<D>,
86 }
87
88 /// Represents an active query job.
89 #[derive(Clone)]
90 pub struct QueryJob<D> {
91     pub id: QueryShardJobId,
92
93     /// The span corresponding to the reason for which this query was required.
94     pub span: Span,
95
96     /// The parent query job which created this job and is implicitly waiting on it.
97     pub parent: Option<QueryJobId<D>>,
98
99     /// The latch that is used to wait on this job.
100     #[cfg(parallel_compiler)]
101     latch: Option<QueryLatch<D>>,
102 }
103
104 impl<D> QueryJob<D>
105 where
106     D: Copy + Clone + Eq + Hash,
107 {
108     /// Creates a new query job.
109     pub fn new(id: QueryShardJobId, span: Span, parent: Option<QueryJobId<D>>) -> Self {
110         QueryJob {
111             id,
112             span,
113             parent,
114             #[cfg(parallel_compiler)]
115             latch: None,
116         }
117     }
118
119     #[cfg(parallel_compiler)]
120     pub(super) fn latch(&mut self) -> QueryLatch<D> {
121         if self.latch.is_none() {
122             self.latch = Some(QueryLatch::new());
123         }
124         self.latch.as_ref().unwrap().clone()
125     }
126
127     /// Signals to waiters that the query is complete.
128     ///
129     /// This does nothing for single threaded rustc,
130     /// as there are no concurrent jobs which could be waiting on us
131     pub fn signal_complete(self) {
132         #[cfg(parallel_compiler)]
133         {
134             if let Some(latch) = self.latch {
135                 latch.set();
136             }
137         }
138     }
139 }
140
141 #[cfg(not(parallel_compiler))]
142 impl<D> QueryJobId<D>
143 where
144     D: Copy + Clone + Eq + Hash,
145 {
146     #[cold]
147     #[inline(never)]
148     pub(super) fn find_cycle_in_stack(
149         &self,
150         query_map: QueryMap<D>,
151         current_job: &Option<QueryJobId<D>>,
152         span: Span,
153     ) -> CycleError {
154         // Find the waitee amongst `current_job` parents
155         let mut cycle = Vec::new();
156         let mut current_job = Option::clone(current_job);
157
158         while let Some(job) = current_job {
159             let info = query_map.get(&job).unwrap();
160             cycle.push(QueryInfo { span: info.job.span, query: info.query.clone() });
161
162             if job == *self {
163                 cycle.reverse();
164
165                 // This is the end of the cycle
166                 // The span entry we included was for the usage
167                 // of the cycle itself, and not part of the cycle
168                 // Replace it with the span which caused the cycle to form
169                 cycle[0].span = span;
170                 // Find out why the cycle itself was used
171                 let usage = info
172                     .job
173                     .parent
174                     .as_ref()
175                     .map(|parent| (info.job.span, parent.query(&query_map)));
176                 return CycleError { usage, cycle };
177             }
178
179             current_job = info.job.parent;
180         }
181
182         panic!("did not find a cycle")
183     }
184 }
185
186 #[cfg(parallel_compiler)]
187 struct QueryWaiter<D> {
188     query: Option<QueryJobId<D>>,
189     condvar: Condvar,
190     span: Span,
191     cycle: Lock<Option<CycleError>>,
192 }
193
194 #[cfg(parallel_compiler)]
195 impl<D> QueryWaiter<D> {
196     fn notify(&self, registry: &rayon_core::Registry) {
197         rayon_core::mark_unblocked(registry);
198         self.condvar.notify_one();
199     }
200 }
201
202 #[cfg(parallel_compiler)]
203 struct QueryLatchInfo<D> {
204     complete: bool,
205     waiters: Vec<Lrc<QueryWaiter<D>>>,
206 }
207
208 #[cfg(parallel_compiler)]
209 #[derive(Clone)]
210 pub(super) struct QueryLatch<D> {
211     info: Lrc<Mutex<QueryLatchInfo<D>>>,
212 }
213
214 #[cfg(parallel_compiler)]
215 impl<D: Eq + Hash> QueryLatch<D> {
216     fn new() -> Self {
217         QueryLatch {
218             info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
219         }
220     }
221 }
222
223 #[cfg(parallel_compiler)]
224 impl<D> QueryLatch<D> {
225     /// Awaits for the query job to complete.
226     pub(super) fn wait_on(
227         &self,
228         query: Option<QueryJobId<D>>,
229         span: Span,
230     ) -> Result<(), CycleError> {
231         let waiter =
232             Lrc::new(QueryWaiter { query, span, cycle: Lock::new(None), condvar: Condvar::new() });
233         self.wait_on_inner(&waiter);
234         // FIXME: Get rid of this lock. We have ownership of the QueryWaiter
235         // although another thread may still have a Lrc reference so we cannot
236         // use Lrc::get_mut
237         let mut cycle = waiter.cycle.lock();
238         match cycle.take() {
239             None => Ok(()),
240             Some(cycle) => Err(cycle),
241         }
242     }
243
244     /// Awaits the caller on this latch by blocking the current thread.
245     fn wait_on_inner(&self, waiter: &Lrc<QueryWaiter<D>>) {
246         let mut info = self.info.lock();
247         if !info.complete {
248             // We push the waiter on to the `waiters` list. It can be accessed inside
249             // the `wait` call below, by 1) the `set` method or 2) by deadlock detection.
250             // Both of these will remove it from the `waiters` list before resuming
251             // this thread.
252             info.waiters.push(waiter.clone());
253
254             // If this detects a deadlock and the deadlock handler wants to resume this thread
255             // we have to be in the `wait` call. This is ensured by the deadlock handler
256             // getting the self.info lock.
257             rayon_core::mark_blocked();
258             jobserver::release_thread();
259             waiter.condvar.wait(&mut info);
260             // Release the lock before we potentially block in `acquire_thread`
261             mem::drop(info);
262             jobserver::acquire_thread();
263         }
264     }
265
266     /// Sets the latch and resumes all waiters on it
267     fn set(&self) {
268         let mut info = self.info.lock();
269         debug_assert!(!info.complete);
270         info.complete = true;
271         let registry = rayon_core::Registry::current();
272         for waiter in info.waiters.drain(..) {
273             waiter.notify(&registry);
274         }
275     }
276
277     /// Removes a single waiter from the list of waiters.
278     /// This is used to break query cycles.
279     fn extract_waiter(&self, waiter: usize) -> Lrc<QueryWaiter<D>> {
280         let mut info = self.info.lock();
281         debug_assert!(!info.complete);
282         // Remove the waiter from the list of waiters
283         info.waiters.remove(waiter)
284     }
285 }
286
287 /// A resumable waiter of a query. The usize is the index into waiters in the query's latch
288 #[cfg(parallel_compiler)]
289 type Waiter<D> = (QueryJobId<D>, usize);
290
291 /// Visits all the non-resumable and resumable waiters of a query.
292 /// Only waiters in a query are visited.
293 /// `visit` is called for every waiter and is passed a query waiting on `query_ref`
294 /// and a span indicating the reason the query waited on `query_ref`.
295 /// If `visit` returns Some, this function returns.
296 /// For visits of non-resumable waiters it returns the return value of `visit`.
297 /// For visits of resumable waiters it returns Some(Some(Waiter)) which has the
298 /// required information to resume the waiter.
299 /// If all `visit` calls returns None, this function also returns None.
300 #[cfg(parallel_compiler)]
301 fn visit_waiters<D, F>(
302     query_map: &QueryMap<D>,
303     query: QueryJobId<D>,
304     mut visit: F,
305 ) -> Option<Option<Waiter<D>>>
306 where
307     D: Copy + Clone + Eq + Hash,
308     F: FnMut(Span, QueryJobId<D>) -> Option<Option<Waiter<D>>>,
309 {
310     // Visit the parent query which is a non-resumable waiter since it's on the same stack
311     if let Some(parent) = query.parent(query_map) {
312         if let Some(cycle) = visit(query.span(query_map), parent) {
313             return Some(cycle);
314         }
315     }
316
317     // Visit the explicit waiters which use condvars and are resumable
318     if let Some(latch) = query.latch(query_map) {
319         for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
320             if let Some(waiter_query) = waiter.query {
321                 if visit(waiter.span, waiter_query).is_some() {
322                     // Return a value which indicates that this waiter can be resumed
323                     return Some(Some((query, i)));
324                 }
325             }
326         }
327     }
328
329     None
330 }
331
332 /// Look for query cycles by doing a depth first search starting at `query`.
333 /// `span` is the reason for the `query` to execute. This is initially DUMMY_SP.
334 /// If a cycle is detected, this initial value is replaced with the span causing
335 /// the cycle.
336 #[cfg(parallel_compiler)]
337 fn cycle_check<D>(
338     query_map: &QueryMap<D>,
339     query: QueryJobId<D>,
340     span: Span,
341     stack: &mut Vec<(Span, QueryJobId<D>)>,
342     visited: &mut FxHashSet<QueryJobId<D>>,
343 ) -> Option<Option<Waiter<D>>>
344 where
345     D: Copy + Clone + Eq + Hash,
346 {
347     if !visited.insert(query) {
348         return if let Some(p) = stack.iter().position(|q| q.1 == query) {
349             // We detected a query cycle, fix up the initial span and return Some
350
351             // Remove previous stack entries
352             stack.drain(0..p);
353             // Replace the span for the first query with the cycle cause
354             stack[0].0 = span;
355             Some(None)
356         } else {
357             None
358         };
359     }
360
361     // Query marked as visited is added it to the stack
362     stack.push((span, query));
363
364     // Visit all the waiters
365     let r = visit_waiters(query_map, query, |span, successor| {
366         cycle_check(query_map, successor, span, stack, visited)
367     });
368
369     // Remove the entry in our stack if we didn't find a cycle
370     if r.is_none() {
371         stack.pop();
372     }
373
374     r
375 }
376
377 /// Finds out if there's a path to the compiler root (aka. code which isn't in a query)
378 /// from `query` without going through any of the queries in `visited`.
379 /// This is achieved with a depth first search.
380 #[cfg(parallel_compiler)]
381 fn connected_to_root<D>(
382     query_map: &QueryMap<D>,
383     query: QueryJobId<D>,
384     visited: &mut FxHashSet<QueryJobId<D>>,
385 ) -> bool
386 where
387     D: Copy + Clone + Eq + Hash,
388 {
389     // We already visited this or we're deliberately ignoring it
390     if !visited.insert(query) {
391         return false;
392     }
393
394     // This query is connected to the root (it has no query parent), return true
395     if query.parent(query_map).is_none() {
396         return true;
397     }
398
399     visit_waiters(query_map, query, |_, successor| {
400         connected_to_root(query_map, successor, visited).then_some(None)
401     })
402     .is_some()
403 }
404
405 // Deterministically pick an query from a list
406 #[cfg(parallel_compiler)]
407 fn pick_query<'a, D, T, F>(query_map: &QueryMap<D>, queries: &'a [T], f: F) -> &'a T
408 where
409     D: Copy + Clone + Eq + Hash,
410     F: Fn(&T) -> (Span, QueryJobId<D>),
411 {
412     // Deterministically pick an entry point
413     // FIXME: Sort this instead
414     queries
415         .iter()
416         .min_by_key(|v| {
417             let (span, query) = f(v);
418             let hash = query.query(query_map).hash;
419             // Prefer entry points which have valid spans for nicer error messages
420             // We add an integer to the tuple ensuring that entry points
421             // with valid spans are picked first
422             let span_cmp = if span == DUMMY_SP { 1 } else { 0 };
423             (span_cmp, hash)
424         })
425         .unwrap()
426 }
427
428 /// Looks for query cycles starting from the last query in `jobs`.
429 /// If a cycle is found, all queries in the cycle is removed from `jobs` and
430 /// the function return true.
431 /// If a cycle was not found, the starting query is removed from `jobs` and
432 /// the function returns false.
433 #[cfg(parallel_compiler)]
434 fn remove_cycle<D: DepKind>(
435     query_map: &QueryMap<D>,
436     jobs: &mut Vec<QueryJobId<D>>,
437     wakelist: &mut Vec<Lrc<QueryWaiter<D>>>,
438 ) -> bool {
439     let mut visited = FxHashSet::default();
440     let mut stack = Vec::new();
441     // Look for a cycle starting with the last query in `jobs`
442     if let Some(waiter) =
443         cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited)
444     {
445         // The stack is a vector of pairs of spans and queries; reverse it so that
446         // the earlier entries require later entries
447         let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip();
448
449         // Shift the spans so that queries are matched with the span for their waitee
450         spans.rotate_right(1);
451
452         // Zip them back together
453         let mut stack: Vec<_> = iter::zip(spans, queries).collect();
454
455         // Remove the queries in our cycle from the list of jobs to look at
456         for r in &stack {
457             if let Some(pos) = jobs.iter().position(|j| j == &r.1) {
458                 jobs.remove(pos);
459             }
460         }
461
462         // Find the queries in the cycle which are
463         // connected to queries outside the cycle
464         let entry_points = stack
465             .iter()
466             .filter_map(|&(span, query)| {
467                 if query.parent(query_map).is_none() {
468                     // This query is connected to the root (it has no query parent)
469                     Some((span, query, None))
470                 } else {
471                     let mut waiters = Vec::new();
472                     // Find all the direct waiters who lead to the root
473                     visit_waiters(query_map, query, |span, waiter| {
474                         // Mark all the other queries in the cycle as already visited
475                         let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1));
476
477                         if connected_to_root(query_map, waiter, &mut visited) {
478                             waiters.push((span, waiter));
479                         }
480
481                         None
482                     });
483                     if waiters.is_empty() {
484                         None
485                     } else {
486                         // Deterministically pick one of the waiters to show to the user
487                         let waiter = *pick_query(query_map, &waiters, |s| *s);
488                         Some((span, query, Some(waiter)))
489                     }
490                 }
491             })
492             .collect::<Vec<(Span, QueryJobId<D>, Option<(Span, QueryJobId<D>)>)>>();
493
494         // Deterministically pick an entry point
495         let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1));
496
497         // Shift the stack so that our entry point is first
498         let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point);
499         if let Some(pos) = entry_point_pos {
500             stack.rotate_left(pos);
501         }
502
503         let usage = usage.as_ref().map(|(span, query)| (*span, query.query(query_map)));
504
505         // Create the cycle error
506         let error = CycleError {
507             usage,
508             cycle: stack
509                 .iter()
510                 .map(|&(s, ref q)| QueryInfo { span: s, query: q.query(query_map) })
511                 .collect(),
512         };
513
514         // We unwrap `waiter` here since there must always be one
515         // edge which is resumable / waited using a query latch
516         let (waitee_query, waiter_idx) = waiter.unwrap();
517
518         // Extract the waiter we want to resume
519         let waiter = waitee_query.latch(query_map).unwrap().extract_waiter(waiter_idx);
520
521         // Set the cycle error so it will be picked up when resumed
522         *waiter.cycle.lock() = Some(error);
523
524         // Put the waiter on the list of things to resume
525         wakelist.push(waiter);
526
527         true
528     } else {
529         false
530     }
531 }
532
533 /// Detects query cycles by using depth first search over all active query jobs.
534 /// If a query cycle is found it will break the cycle by finding an edge which
535 /// uses a query latch and then resuming that waiter.
536 /// There may be multiple cycles involved in a deadlock, so this searches
537 /// all active queries for cycles before finally resuming all the waiters at once.
538 #[cfg(parallel_compiler)]
539 pub fn deadlock<CTX: QueryContext>(tcx: CTX, registry: &rayon_core::Registry) {
540     let on_panic = OnDrop(|| {
541         eprintln!("deadlock handler panicked, aborting process");
542         process::abort();
543     });
544
545     let mut wakelist = Vec::new();
546     let query_map = tcx.try_collect_active_jobs().unwrap();
547     let mut jobs: Vec<QueryJobId<CTX::DepKind>> = query_map.keys().cloned().collect();
548
549     let mut found_cycle = false;
550
551     while jobs.len() > 0 {
552         if remove_cycle(&query_map, &mut jobs, &mut wakelist) {
553             found_cycle = true;
554         }
555     }
556
557     // Check that a cycle was found. It is possible for a deadlock to occur without
558     // a query cycle if a query which can be waited on uses Rayon to do multithreading
559     // internally. Such a query (X) may be executing on 2 threads (A and B) and A may
560     // wait using Rayon on B. Rayon may then switch to executing another query (Y)
561     // which in turn will wait on X causing a deadlock. We have a false dependency from
562     // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
563     // only considers the true dependency and won't detect a cycle.
564     assert!(found_cycle);
565
566     // FIXME: Ensure this won't cause a deadlock before we return
567     for waiter in wakelist.into_iter() {
568         waiter.notify(registry);
569     }
570
571     on_panic.disable();
572 }
573
574 #[inline(never)]
575 #[cold]
576 pub(crate) fn report_cycle<'a>(
577     sess: &'a Session,
578     CycleError { usage, cycle: stack }: CycleError,
579 ) -> DiagnosticBuilder<'a> {
580     assert!(!stack.is_empty());
581
582     let fix_span = |span: Span, query: &QueryStackFrame| {
583         sess.source_map().guess_head_span(query.default_span(span))
584     };
585
586     let span = fix_span(stack[1 % stack.len()].span, &stack[0].query);
587     let mut err =
588         struct_span_err!(sess, span, E0391, "cycle detected when {}", stack[0].query.description);
589
590     for i in 1..stack.len() {
591         let query = &stack[i].query;
592         let span = fix_span(stack[(i + 1) % stack.len()].span, query);
593         err.span_note(span, &format!("...which requires {}...", query.description));
594     }
595
596     if stack.len() == 1 {
597         err.note(&format!("...which immediately requires {} again", stack[0].query.description));
598     } else {
599         err.note(&format!(
600             "...which again requires {}, completing the cycle",
601             stack[0].query.description
602         ));
603     }
604
605     if stack.iter().all(|entry| {
606         entry.query.def_kind.map_or(false, |def_kind| {
607             matches!(def_kind, SimpleDefKind::TyAlias | SimpleDefKind::TraitAlias)
608         })
609     }) {
610         if stack.iter().all(|entry| {
611             entry
612                 .query
613                 .def_kind
614                 .map_or(false, |def_kind| matches!(def_kind, SimpleDefKind::TyAlias))
615         }) {
616             err.note("type aliases cannot be recursive");
617             err.help("consider using a struct, enum, or union instead to break the cycle");
618             err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information");
619         } else {
620             err.note("trait aliases cannot be recursive");
621         }
622     }
623
624     if let Some((span, query)) = usage {
625         err.span_note(fix_span(span, &query), &format!("cycle used when {}", query.description));
626     }
627
628     err
629 }
630
631 pub fn print_query_stack<CTX: QueryContext>(
632     tcx: CTX,
633     mut current_query: Option<QueryJobId<CTX::DepKind>>,
634     handler: &Handler,
635     num_frames: Option<usize>,
636 ) -> usize {
637     // Be careful relying on global state here: this code is called from
638     // a panic hook, which means that the global `Handler` may be in a weird
639     // state if it was responsible for triggering the panic.
640     let mut i = 0;
641     let query_map = tcx.try_collect_active_jobs();
642
643     while let Some(query) = current_query {
644         if Some(i) == num_frames {
645             break;
646         }
647         let query_info = if let Some(info) = query_map.as_ref().and_then(|map| map.get(&query)) {
648             info
649         } else {
650             break;
651         };
652         let mut diag = Diagnostic::new(
653             Level::FailureNote,
654             &format!("#{} [{}] {}", i, query_info.query.name, query_info.query.description),
655         );
656         diag.span =
657             tcx.dep_context().sess().source_map().guess_head_span(query_info.job.span).into();
658         handler.force_print_diagnostic(diag);
659
660         current_query = query_info.job.parent;
661         i += 1;
662     }
663
664     i
665 }