]>
Commit | Line | Data |
---|---|---|
5bcae85e SL |
1 | //! This pass is only used for UNIT TESTS related to incremental |
2 | //! compilation. It tests whether a particular `.o` file will be re-used | |
3 | //! from a previous compilation or whether it must be regenerated. | |
4 | //! | |
5 | //! The user adds annotations to the crate of the following form: | |
6 | //! | |
7 | //! ``` | |
04454e1e | 8 | //! # #![feature(rustc_attrs)] |
add651ee | 9 | //! # #![allow(internal_features)] |
5bcae85e | 10 | //! #![rustc_partition_reused(module="spike", cfg="rpass2")] |
94b46f34 | 11 | //! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")] |
5bcae85e SL |
12 | //! ``` |
13 | //! | |
14 | //! The first indicates (in the cfg `rpass2`) that `spike.o` will be | |
15 | //! reused, the second that `spike-x.o` will be recreated. If these | |
16 | //! annotations are inaccurate, errors are reported. | |
17 | //! | |
18 | //! The reason that we use `cfg=...` and not `#[cfg_attr]` is so that | |
19 | //! the HIR doesn't change as a result of the annotations, which might | |
20 | //! perturb the reuse results. | |
0bf4aa26 | 21 | //! |
9ffffee4 | 22 | //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")]` |
0bf4aa26 XL |
23 | //! allows for doing a more fine-grained check to see if pre- or post-lto data |
24 | //! was re-used. | |
5bcae85e | 25 | |
9ffffee4 | 26 | use crate::errors; |
3dfed10e | 27 | use rustc_ast as ast; |
c620b35d | 28 | use rustc_data_structures::unord::UnordMap; |
fe692bf9 | 29 | use rustc_data_structures::unord::UnordSet; |
c620b35d | 30 | use rustc_errors::{DiagArgValue, IntoDiagArg}; |
dfeec247 | 31 | use rustc_hir::def_id::LOCAL_CRATE; |
ba9703b0 XL |
32 | use rustc_middle::mir::mono::CodegenUnitNameBuilder; |
33 | use rustc_middle::ty::TyCtxt; | |
ed00b5ec FG |
34 | use rustc_session::Session; |
35 | use rustc_span::symbol::sym; | |
36 | use rustc_span::{Span, Symbol}; | |
37 | use std::borrow::Cow; | |
38 | use std::fmt; | |
9ffffee4 | 39 | use thin_vec::ThinVec; |
5bcae85e | 40 | |
a2a8927a | 41 | #[allow(missing_docs)] |
ed00b5ec | 42 | pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTracker)) { |
2c00a5a8 XL |
43 | tcx.dep_graph.with_ignore(|| { |
44 | if tcx.sess.opts.incremental.is_none() { | |
45 | return; | |
46 | } | |
47 | ||
04454e1e FG |
48 | let available_cgus = |
49 | tcx.collect_and_partition_mono_items(()).1.iter().map(|cgu| cgu.name()).collect(); | |
0bf4aa26 | 50 | |
ed00b5ec FG |
51 | let mut ams = AssertModuleSource { |
52 | tcx, | |
53 | available_cgus, | |
54 | cgu_reuse_tracker: if tcx.sess.opts.unstable_opts.query_dep_graph { | |
55 | CguReuseTracker::new() | |
56 | } else { | |
57 | CguReuseTracker::new_disabled() | |
58 | }, | |
59 | }; | |
0bf4aa26 | 60 | |
6a06907d | 61 | for attr in tcx.hir().attrs(rustc_hir::CRATE_HIR_ID) { |
2c00a5a8 XL |
62 | ams.check_attr(attr); |
63 | } | |
ed00b5ec FG |
64 | |
65 | set_reuse(&mut ams.cgu_reuse_tracker); | |
66 | ||
67 | ams.cgu_reuse_tracker.check_expected_reuse(tcx.sess); | |
68 | }); | |
5bcae85e SL |
69 | } |
70 | ||
dc9dc135 XL |
71 | struct AssertModuleSource<'tcx> { |
72 | tcx: TyCtxt<'tcx>, | |
fe692bf9 | 73 | available_cgus: UnordSet<Symbol>, |
ed00b5ec | 74 | cgu_reuse_tracker: CguReuseTracker, |
5bcae85e SL |
75 | } |
76 | ||
a2a8927a | 77 | impl<'tcx> AssertModuleSource<'tcx> { |
ed00b5ec | 78 | fn check_attr(&mut self, attr: &ast::Attribute) { |
94222f64 XL |
79 | let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) { |
80 | (CguReuse::PreLto, ComparisonKind::AtLeast) | |
81 | } else if attr.has_name(sym::rustc_partition_codegened) { | |
82 | (CguReuse::No, ComparisonKind::Exact) | |
83 | } else if attr.has_name(sym::rustc_expected_cgu_reuse) { | |
84 | match self.field(attr, sym::kind) { | |
85 | sym::no => (CguReuse::No, ComparisonKind::Exact), | |
86 | sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact), | |
87 | sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact), | |
88 | sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast), | |
89 | other => { | |
9ffffee4 | 90 | self.tcx |
c0240ec0 | 91 | .dcx() |
9ffffee4 | 92 | .emit_fatal(errors::UnknownReuseKind { span: attr.span, kind: other }); |
0bf4aa26 | 93 | } |
94222f64 XL |
94 | } |
95 | } else { | |
96 | return; | |
97 | }; | |
5bcae85e | 98 | |
064997fb | 99 | if !self.tcx.sess.opts.unstable_opts.query_dep_graph { |
c0240ec0 | 100 | self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span }); |
0bf4aa26 XL |
101 | } |
102 | ||
5bcae85e SL |
103 | if !self.check_config(attr) { |
104 | debug!("check_attr: config does not match, ignoring attr"); | |
105 | return; | |
106 | } | |
107 | ||
60c5eb7d XL |
108 | let user_path = self.field(attr, sym::module).to_string(); |
109 | let crate_name = self.tcx.crate_name(LOCAL_CRATE).to_string(); | |
b7449926 XL |
110 | |
111 | if !user_path.starts_with(&crate_name) { | |
c0240ec0 | 112 | self.tcx.dcx().emit_fatal(errors::MalformedCguName { |
9ffffee4 FG |
113 | span: attr.span, |
114 | user_path, | |
115 | crate_name, | |
116 | }); | |
b7449926 XL |
117 | } |
118 | ||
119 | // Split of the "special suffix" if there is one. | |
74b04a01 | 120 | let (user_path, cgu_special_suffix) = if let Some(index) = user_path.rfind('.') { |
dfeec247 | 121 | (&user_path[..index], Some(&user_path[index + 1..])) |
b7449926 XL |
122 | } else { |
123 | (&user_path[..], None) | |
124 | }; | |
125 | ||
29967ef6 | 126 | let mut iter = user_path.split('-'); |
b7449926 XL |
127 | |
128 | // Remove the crate name | |
29967ef6 XL |
129 | assert_eq!(iter.next().unwrap(), crate_name); |
130 | ||
131 | let cgu_path_components = iter.collect::<Vec<_>>(); | |
b7449926 XL |
132 | |
133 | let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx); | |
dfeec247 XL |
134 | let cgu_name = |
135 | cgu_name_builder.build_cgu_name(LOCAL_CRATE, cgu_path_components, cgu_special_suffix); | |
b7449926 | 136 | |
60c5eb7d | 137 | debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name); |
5bcae85e | 138 | |
04454e1e | 139 | if !self.available_cgus.contains(&cgu_name) { |
fe692bf9 FG |
140 | let cgu_names: Vec<&str> = |
141 | self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(); | |
c0240ec0 | 142 | self.tcx.dcx().emit_err(errors::NoModuleNamed { |
9ffffee4 FG |
143 | span: attr.span, |
144 | user_path, | |
145 | cgu_name, | |
146 | cgu_names: cgu_names.join(", "), | |
147 | }); | |
5bcae85e | 148 | } |
0bf4aa26 | 149 | |
ed00b5ec | 150 | self.cgu_reuse_tracker.set_expectation( |
3dfed10e | 151 | cgu_name, |
4b012472 | 152 | user_path, |
dfeec247 XL |
153 | attr.span, |
154 | expected_reuse, | |
155 | comp_kind, | |
156 | ); | |
5bcae85e SL |
157 | } |
158 | ||
f9f354fc | 159 | fn field(&self, attr: &ast::Attribute, name: Symbol) -> Symbol { |
9ffffee4 | 160 | for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { |
3dfed10e | 161 | if item.has_name(name) { |
5bcae85e SL |
162 | if let Some(value) = item.value_str() { |
163 | return value; | |
164 | } else { | |
c0240ec0 | 165 | self.tcx.dcx().emit_fatal(errors::FieldAssociatedValueExpected { |
9ffffee4 FG |
166 | span: item.span(), |
167 | name, | |
168 | }); | |
5bcae85e SL |
169 | } |
170 | } | |
171 | } | |
172 | ||
c0240ec0 | 173 | self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span, name }); |
5bcae85e SL |
174 | } |
175 | ||
176 | /// Scan for a `cfg="foo"` attribute and check whether we have a | |
177 | /// cfg flag called `foo`. | |
178 | fn check_config(&self, attr: &ast::Attribute) -> bool { | |
c620b35d | 179 | let config = &self.tcx.sess.psess.config; |
60c5eb7d | 180 | let value = self.field(attr, sym::cfg); |
5bcae85e | 181 | debug!("check_config(config={:?}, value={:?})", config, value); |
476ff2be | 182 | if config.iter().any(|&(name, _)| name == value) { |
5bcae85e SL |
183 | debug!("check_config: matched"); |
184 | return true; | |
185 | } | |
186 | debug!("check_config: no match found"); | |
ba9703b0 | 187 | false |
5bcae85e | 188 | } |
5bcae85e | 189 | } |
ed00b5ec FG |
190 | |
191 | #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] | |
192 | pub enum CguReuse { | |
193 | No, | |
194 | PreLto, | |
195 | PostLto, | |
196 | } | |
197 | ||
198 | impl fmt::Display for CguReuse { | |
199 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
200 | match *self { | |
201 | CguReuse::No => write!(f, "No"), | |
4b012472 FG |
202 | CguReuse::PreLto => write!(f, "PreLto"), |
203 | CguReuse::PostLto => write!(f, "PostLto"), | |
ed00b5ec FG |
204 | } |
205 | } | |
206 | } | |
207 | ||
c620b35d FG |
208 | impl IntoDiagArg for CguReuse { |
209 | fn into_diag_arg(self) -> DiagArgValue { | |
210 | DiagArgValue::Str(Cow::Owned(self.to_string())) | |
ed00b5ec FG |
211 | } |
212 | } | |
213 | ||
214 | #[derive(Copy, Clone, Debug, PartialEq)] | |
215 | pub enum ComparisonKind { | |
216 | Exact, | |
217 | AtLeast, | |
218 | } | |
219 | ||
220 | struct TrackerData { | |
c620b35d FG |
221 | actual_reuse: UnordMap<String, CguReuse>, |
222 | expected_reuse: UnordMap<String, (String, Span, CguReuse, ComparisonKind)>, | |
ed00b5ec FG |
223 | } |
224 | ||
225 | pub struct CguReuseTracker { | |
226 | data: Option<TrackerData>, | |
227 | } | |
228 | ||
229 | impl CguReuseTracker { | |
230 | fn new() -> CguReuseTracker { | |
231 | let data = | |
232 | TrackerData { actual_reuse: Default::default(), expected_reuse: Default::default() }; | |
233 | ||
234 | CguReuseTracker { data: Some(data) } | |
235 | } | |
236 | ||
237 | fn new_disabled() -> CguReuseTracker { | |
238 | CguReuseTracker { data: None } | |
239 | } | |
240 | ||
241 | pub fn set_actual_reuse(&mut self, cgu_name: &str, kind: CguReuse) { | |
242 | if let Some(data) = &mut self.data { | |
243 | debug!("set_actual_reuse({cgu_name:?}, {kind:?})"); | |
244 | ||
245 | let prev_reuse = data.actual_reuse.insert(cgu_name.to_string(), kind); | |
246 | assert!(prev_reuse.is_none()); | |
247 | } | |
248 | } | |
249 | ||
250 | fn set_expectation( | |
251 | &mut self, | |
252 | cgu_name: Symbol, | |
253 | cgu_user_name: &str, | |
254 | error_span: Span, | |
255 | expected_reuse: CguReuse, | |
256 | comparison_kind: ComparisonKind, | |
257 | ) { | |
258 | if let Some(data) = &mut self.data { | |
259 | debug!("set_expectation({cgu_name:?}, {expected_reuse:?}, {comparison_kind:?})"); | |
260 | ||
261 | data.expected_reuse.insert( | |
262 | cgu_name.to_string(), | |
263 | (cgu_user_name.to_string(), error_span, expected_reuse, comparison_kind), | |
264 | ); | |
265 | } | |
266 | } | |
267 | ||
268 | fn check_expected_reuse(&self, sess: &Session) { | |
269 | if let Some(ref data) = self.data { | |
c620b35d | 270 | let keys = data.expected_reuse.keys().into_sorted_stable_ord(); |
c0240ec0 FG |
271 | for cgu_name in keys { |
272 | let &(ref cgu_user_name, ref error_span, expected_reuse, comparison_kind) = | |
273 | data.expected_reuse.get(cgu_name).unwrap(); | |
274 | ||
ed00b5ec FG |
275 | if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) { |
276 | let (error, at_least) = match comparison_kind { | |
277 | ComparisonKind::Exact => (expected_reuse != actual_reuse, false), | |
278 | ComparisonKind::AtLeast => (actual_reuse < expected_reuse, true), | |
279 | }; | |
280 | ||
281 | if error { | |
282 | let at_least = if at_least { 1 } else { 0 }; | |
c0240ec0 | 283 | sess.dcx().emit_err(errors::IncorrectCguReuseType { |
ed00b5ec FG |
284 | span: *error_span, |
285 | cgu_user_name, | |
286 | actual_reuse, | |
287 | expected_reuse, | |
288 | at_least, | |
4b012472 | 289 | }); |
ed00b5ec FG |
290 | } |
291 | } else { | |
c0240ec0 | 292 | sess.dcx().emit_fatal(errors::CguNotRecorded { cgu_user_name, cgu_name }); |
ed00b5ec FG |
293 | } |
294 | } | |
295 | } | |
296 | } | |
297 | } |