]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_session/src/cgu_reuse_tracker.rs
Rollup merge of #98806 - GuillaumeGomez:decl-trailing-whitespace, r=notriddle
[rust.git] / compiler / rustc_session / src / cgu_reuse_tracker.rs
1 //! Some facilities for tracking how codegen-units are reused during incremental
2 //! compilation. This is used for incremental compilation tests and debug
3 //! output.
4
5 use rustc_data_structures::fx::FxHashMap;
6 use rustc_span::{Span, Symbol};
7 use std::sync::{Arc, Mutex};
8 use tracing::debug;
9
10 #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
11 pub enum CguReuse {
12     No,
13     PreLto,
14     PostLto,
15 }
16
17 #[derive(Copy, Clone, Debug, PartialEq)]
18 pub enum ComparisonKind {
19     Exact,
20     AtLeast,
21 }
22
23 struct TrackerData {
24     actual_reuse: FxHashMap<String, CguReuse>,
25     expected_reuse: FxHashMap<String, (String, SendSpan, CguReuse, ComparisonKind)>,
26 }
27
28 // Span does not implement `Send`, so we can't just store it in the shared
29 // `TrackerData` object. Instead of splitting up `TrackerData` into shared and
30 // non-shared parts (which would be complicated), we just mark the `Span` here
31 // explicitly as `Send`. That's safe because the span data here is only ever
32 // accessed from the main thread.
33 struct SendSpan(Span);
34 unsafe impl Send for SendSpan {}
35
36 #[derive(Clone)]
37 pub struct CguReuseTracker {
38     data: Option<Arc<Mutex<TrackerData>>>,
39 }
40
41 impl CguReuseTracker {
42     pub fn new() -> CguReuseTracker {
43         let data =
44             TrackerData { actual_reuse: Default::default(), expected_reuse: Default::default() };
45
46         CguReuseTracker { data: Some(Arc::new(Mutex::new(data))) }
47     }
48
49     pub fn new_disabled() -> CguReuseTracker {
50         CguReuseTracker { data: None }
51     }
52
53     pub fn set_actual_reuse(&self, cgu_name: &str, kind: CguReuse) {
54         if let Some(ref data) = self.data {
55             debug!("set_actual_reuse({cgu_name:?}, {kind:?})");
56
57             let prev_reuse = data.lock().unwrap().actual_reuse.insert(cgu_name.to_string(), kind);
58
59             if let Some(prev_reuse) = prev_reuse {
60                 // The only time it is legal to overwrite reuse state is when
61                 // we discover during ThinLTO that we can actually reuse the
62                 // post-LTO version of a CGU.
63                 assert_eq!(prev_reuse, CguReuse::PreLto);
64             }
65         }
66     }
67
68     pub fn set_expectation(
69         &self,
70         cgu_name: Symbol,
71         cgu_user_name: &str,
72         error_span: Span,
73         expected_reuse: CguReuse,
74         comparison_kind: ComparisonKind,
75     ) {
76         if let Some(ref data) = self.data {
77             debug!("set_expectation({cgu_name:?}, {expected_reuse:?}, {comparison_kind:?})");
78             let mut data = data.lock().unwrap();
79
80             data.expected_reuse.insert(
81                 cgu_name.to_string(),
82                 (cgu_user_name.to_string(), SendSpan(error_span), expected_reuse, comparison_kind),
83             );
84         }
85     }
86
87     pub fn check_expected_reuse(&self, diag: &rustc_errors::Handler) {
88         if let Some(ref data) = self.data {
89             let data = data.lock().unwrap();
90
91             for (cgu_name, &(ref cgu_user_name, ref error_span, expected_reuse, comparison_kind)) in
92                 &data.expected_reuse
93             {
94                 if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) {
95                     let (error, at_least) = match comparison_kind {
96                         ComparisonKind::Exact => (expected_reuse != actual_reuse, false),
97                         ComparisonKind::AtLeast => (actual_reuse < expected_reuse, true),
98                     };
99
100                     if error {
101                         let at_least = if at_least { "at least " } else { "" };
102                         let msg = format!(
103                             "CGU-reuse for `{cgu_user_name}` is `{actual_reuse:?}` but \
104                                            should be {at_least}`{expected_reuse:?}`"
105                         );
106                         diag.span_err(error_span.0, &msg);
107                     }
108                 } else {
109                     let msg = format!(
110                         "CGU-reuse for `{cgu_user_name}` (mangled: `{cgu_name}`) was \
111                                        not recorded"
112                     );
113                     diag.span_fatal(error_span.0, &msg)
114                 }
115             }
116         }
117     }
118 }