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