]>
Commit | Line | Data |
---|---|---|
0bf4aa26 | 1 | //! Some facilities for tracking how codegen-units are reused during incremental |
a1dfa0c6 | 2 | //! compilation. This is used for incremental compilation tests and debug |
0bf4aa26 XL |
3 | //! output. |
4 | ||
9fa01778 | 5 | use crate::session::Session; |
0bf4aa26 XL |
6 | use rustc_data_structures::fx::FxHashMap; |
7 | use std::sync::{Arc, Mutex}; | |
8 | use syntax_pos::Span; | |
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 = TrackerData { | |
44 | actual_reuse: Default::default(), | |
45 | expected_reuse: Default::default(), | |
46 | }; | |
47 | ||
48 | CguReuseTracker { | |
49 | data: Some(Arc::new(Mutex::new(data))), | |
50 | } | |
51 | } | |
52 | ||
53 | pub fn new_disabled() -> CguReuseTracker { | |
54 | CguReuseTracker { | |
55 | data: None, | |
56 | } | |
57 | } | |
58 | ||
59 | pub fn set_actual_reuse(&self, cgu_name: &str, kind: CguReuse) { | |
60 | if let Some(ref data) = self.data { | |
61 | debug!("set_actual_reuse({:?}, {:?})", cgu_name, kind); | |
62 | ||
63 | let prev_reuse = data.lock() | |
64 | .unwrap() | |
65 | .actual_reuse | |
66 | .insert(cgu_name.to_string(), kind); | |
67 | ||
68 | if let Some(prev_reuse) = prev_reuse { | |
69 | // The only time it is legal to overwrite reuse state is when | |
70 | // we discover during ThinLTO that we can actually reuse the | |
71 | // post-LTO version of a CGU. | |
72 | assert_eq!(prev_reuse, CguReuse::PreLto); | |
73 | } | |
74 | } | |
75 | } | |
76 | ||
77 | pub fn set_expectation(&self, | |
78 | cgu_name: &str, | |
79 | cgu_user_name: &str, | |
80 | error_span: Span, | |
81 | expected_reuse: CguReuse, | |
82 | comparison_kind: ComparisonKind) { | |
83 | if let Some(ref data) = self.data { | |
84 | debug!("set_expectation({:?}, {:?}, {:?})", cgu_name, | |
85 | expected_reuse, | |
86 | comparison_kind); | |
87 | let mut data = data.lock().unwrap(); | |
88 | ||
89 | data.expected_reuse.insert(cgu_name.to_string(), | |
90 | (cgu_user_name.to_string(), | |
91 | SendSpan(error_span), | |
92 | expected_reuse, | |
93 | comparison_kind)); | |
94 | } | |
95 | } | |
96 | ||
97 | pub fn check_expected_reuse(&self, sess: &Session) { | |
98 | if let Some(ref data) = self.data { | |
99 | let data = data.lock().unwrap(); | |
100 | ||
101 | for (cgu_name, &(ref cgu_user_name, | |
102 | ref error_span, | |
103 | expected_reuse, | |
104 | comparison_kind)) in &data.expected_reuse { | |
105 | if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) { | |
106 | let (error, at_least) = match comparison_kind { | |
107 | ComparisonKind::Exact => { | |
108 | (expected_reuse != actual_reuse, false) | |
109 | } | |
110 | ComparisonKind::AtLeast => { | |
111 | (actual_reuse < expected_reuse, true) | |
112 | } | |
113 | }; | |
114 | ||
115 | if error { | |
116 | let at_least = if at_least { "at least " } else { "" }; | |
117 | let msg = format!("CGU-reuse for `{}` is `{:?}` but \ | |
118 | should be {}`{:?}`", | |
119 | cgu_user_name, | |
120 | actual_reuse, | |
121 | at_least, | |
122 | expected_reuse); | |
123 | sess.span_err(error_span.0, &msg); | |
124 | } | |
125 | } else { | |
126 | let msg = format!("CGU-reuse for `{}` (mangled: `{}`) was \ | |
127 | not recorded", | |
128 | cgu_user_name, | |
129 | cgu_name); | |
130 | sess.span_fatal(error_span.0, &msg); | |
131 | } | |
132 | } | |
133 | } | |
134 | } | |
135 | } |