]>
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)] |
5bcae85e | 9 | //! #![rustc_partition_reused(module="spike", cfg="rpass2")] |
94b46f34 | 10 | //! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")] |
5bcae85e SL |
11 | //! ``` |
12 | //! | |
13 | //! The first indicates (in the cfg `rpass2`) that `spike.o` will be | |
14 | //! reused, the second that `spike-x.o` will be recreated. If these | |
15 | //! annotations are inaccurate, errors are reported. | |
16 | //! | |
17 | //! The reason that we use `cfg=...` and not `#[cfg_attr]` is so that | |
18 | //! the HIR doesn't change as a result of the annotations, which might | |
19 | //! perturb the reuse results. | |
0bf4aa26 XL |
20 | //! |
21 | //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")] | |
22 | //! allows for doing a more fine-grained check to see if pre- or post-lto data | |
23 | //! was re-used. | |
5bcae85e | 24 | |
3dfed10e | 25 | use rustc_ast as ast; |
064997fb | 26 | use rustc_data_structures::fx::FxHashSet; |
dfeec247 | 27 | use rustc_hir::def_id::LOCAL_CRATE; |
ba9703b0 XL |
28 | use rustc_middle::mir::mono::CodegenUnitNameBuilder; |
29 | use rustc_middle::ty::TyCtxt; | |
dfeec247 XL |
30 | use rustc_session::cgu_reuse_tracker::*; |
31 | use rustc_span::symbol::{sym, Symbol}; | |
5bcae85e | 32 | |
a2a8927a | 33 | #[allow(missing_docs)] |
416331ca | 34 | pub fn assert_module_sources(tcx: TyCtxt<'_>) { |
2c00a5a8 XL |
35 | tcx.dep_graph.with_ignore(|| { |
36 | if tcx.sess.opts.incremental.is_none() { | |
37 | return; | |
38 | } | |
39 | ||
04454e1e FG |
40 | let available_cgus = |
41 | tcx.collect_and_partition_mono_items(()).1.iter().map(|cgu| cgu.name()).collect(); | |
0bf4aa26 | 42 | |
dfeec247 | 43 | let ams = AssertModuleSource { tcx, available_cgus }; |
0bf4aa26 | 44 | |
6a06907d | 45 | for attr in tcx.hir().attrs(rustc_hir::CRATE_HIR_ID) { |
2c00a5a8 XL |
46 | ams.check_attr(attr); |
47 | } | |
48 | }) | |
5bcae85e SL |
49 | } |
50 | ||
dc9dc135 XL |
51 | struct AssertModuleSource<'tcx> { |
52 | tcx: TyCtxt<'tcx>, | |
04454e1e | 53 | available_cgus: FxHashSet<Symbol>, |
5bcae85e SL |
54 | } |
55 | ||
a2a8927a | 56 | impl<'tcx> AssertModuleSource<'tcx> { |
5bcae85e | 57 | fn check_attr(&self, attr: &ast::Attribute) { |
94222f64 XL |
58 | let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) { |
59 | (CguReuse::PreLto, ComparisonKind::AtLeast) | |
60 | } else if attr.has_name(sym::rustc_partition_codegened) { | |
61 | (CguReuse::No, ComparisonKind::Exact) | |
62 | } else if attr.has_name(sym::rustc_expected_cgu_reuse) { | |
63 | match self.field(attr, sym::kind) { | |
64 | sym::no => (CguReuse::No, ComparisonKind::Exact), | |
65 | sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact), | |
66 | sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact), | |
67 | sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast), | |
68 | other => { | |
69 | self.tcx.sess.span_fatal( | |
70 | attr.span, | |
71 | &format!("unknown cgu-reuse-kind `{}` specified", other), | |
72 | ); | |
0bf4aa26 | 73 | } |
94222f64 XL |
74 | } |
75 | } else { | |
76 | return; | |
77 | }; | |
5bcae85e | 78 | |
064997fb | 79 | if !self.tcx.sess.opts.unstable_opts.query_dep_graph { |
0bf4aa26 XL |
80 | self.tcx.sess.span_fatal( |
81 | attr.span, | |
74b04a01 | 82 | "found CGU-reuse attribute but `-Zquery-dep-graph` was not specified", |
dfeec247 | 83 | ); |
0bf4aa26 XL |
84 | } |
85 | ||
5bcae85e SL |
86 | if !self.check_config(attr) { |
87 | debug!("check_attr: config does not match, ignoring attr"); | |
88 | return; | |
89 | } | |
90 | ||
60c5eb7d XL |
91 | let user_path = self.field(attr, sym::module).to_string(); |
92 | let crate_name = self.tcx.crate_name(LOCAL_CRATE).to_string(); | |
b7449926 XL |
93 | |
94 | if !user_path.starts_with(&crate_name) { | |
dfeec247 XL |
95 | let msg = format!( |
96 | "Found malformed codegen unit name `{}`. \ | |
b7449926 | 97 | Codegen units names must always start with the name of the \ |
dfeec247 XL |
98 | crate (`{}` in this case).", |
99 | user_path, crate_name | |
100 | ); | |
b7449926 XL |
101 | self.tcx.sess.span_fatal(attr.span, &msg); |
102 | } | |
103 | ||
104 | // Split of the "special suffix" if there is one. | |
74b04a01 | 105 | let (user_path, cgu_special_suffix) = if let Some(index) = user_path.rfind('.') { |
dfeec247 | 106 | (&user_path[..index], Some(&user_path[index + 1..])) |
b7449926 XL |
107 | } else { |
108 | (&user_path[..], None) | |
109 | }; | |
110 | ||
29967ef6 | 111 | let mut iter = user_path.split('-'); |
b7449926 XL |
112 | |
113 | // Remove the crate name | |
29967ef6 XL |
114 | assert_eq!(iter.next().unwrap(), crate_name); |
115 | ||
116 | let cgu_path_components = iter.collect::<Vec<_>>(); | |
b7449926 XL |
117 | |
118 | let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx); | |
dfeec247 XL |
119 | let cgu_name = |
120 | cgu_name_builder.build_cgu_name(LOCAL_CRATE, cgu_path_components, cgu_special_suffix); | |
b7449926 | 121 | |
60c5eb7d | 122 | debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name); |
5bcae85e | 123 | |
04454e1e FG |
124 | if !self.available_cgus.contains(&cgu_name) { |
125 | let mut cgu_names: Vec<&str> = | |
126 | self.available_cgus.iter().map(|cgu| cgu.as_str()).collect(); | |
127 | cgu_names.sort(); | |
dfeec247 XL |
128 | self.tcx.sess.span_err( |
129 | attr.span, | |
130 | &format!( | |
3dfed10e | 131 | "no module named `{}` (mangled: {}). Available modules: {}", |
b7449926 XL |
132 | user_path, |
133 | cgu_name, | |
04454e1e | 134 | cgu_names.join(", ") |
dfeec247 XL |
135 | ), |
136 | ); | |
5bcae85e | 137 | } |
0bf4aa26 | 138 | |
dfeec247 | 139 | self.tcx.sess.cgu_reuse_tracker.set_expectation( |
3dfed10e | 140 | cgu_name, |
dfeec247 XL |
141 | &user_path, |
142 | attr.span, | |
143 | expected_reuse, | |
144 | comp_kind, | |
145 | ); | |
5bcae85e SL |
146 | } |
147 | ||
f9f354fc | 148 | fn field(&self, attr: &ast::Attribute, name: Symbol) -> Symbol { |
cc61c64b | 149 | for item in attr.meta_item_list().unwrap_or_else(Vec::new) { |
3dfed10e | 150 | if item.has_name(name) { |
5bcae85e SL |
151 | if let Some(value) = item.value_str() { |
152 | return value; | |
153 | } else { | |
154 | self.tcx.sess.span_fatal( | |
532ac7d7 | 155 | item.span(), |
dfeec247 XL |
156 | &format!("associated value expected for `{}`", name), |
157 | ); | |
5bcae85e SL |
158 | } |
159 | } | |
160 | } | |
161 | ||
dfeec247 | 162 | self.tcx.sess.span_fatal(attr.span, &format!("no field `{}`", name)); |
5bcae85e SL |
163 | } |
164 | ||
165 | /// Scan for a `cfg="foo"` attribute and check whether we have a | |
166 | /// cfg flag called `foo`. | |
167 | fn check_config(&self, attr: &ast::Attribute) -> bool { | |
c30ab7b3 | 168 | let config = &self.tcx.sess.parse_sess.config; |
60c5eb7d | 169 | let value = self.field(attr, sym::cfg); |
5bcae85e | 170 | debug!("check_config(config={:?}, value={:?})", config, value); |
476ff2be | 171 | if config.iter().any(|&(name, _)| name == value) { |
5bcae85e SL |
172 | debug!("check_config: matched"); |
173 | return true; | |
174 | } | |
175 | debug!("check_config: no match found"); | |
ba9703b0 | 176 | false |
5bcae85e | 177 | } |
5bcae85e | 178 | } |