]>
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 | |
b7449926 | 24 | use rustc::hir::def_id::LOCAL_CRATE; |
0bf4aa26 | 25 | use rustc::dep_graph::cgu_reuse_tracker::*; |
b7449926 | 26 | use rustc::mir::mono::CodegenUnitNameBuilder; |
5bcae85e | 27 | use rustc::ty::TyCtxt; |
0bf4aa26 | 28 | use std::collections::BTreeSet; |
5bcae85e | 29 | use syntax::ast; |
48663c56 | 30 | use syntax::symbol::{Symbol, sym}; |
0bf4aa26 XL |
31 | use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_CODEGENED, |
32 | ATTR_EXPECTED_CGU_REUSE}; | |
5bcae85e | 33 | |
48663c56 XL |
34 | const MODULE: Symbol = sym::module; |
35 | const CFG: Symbol = sym::cfg; | |
36 | const KIND: Symbol = sym::kind; | |
3b2f2976 | 37 | |
dc9dc135 | 38 | pub fn assert_module_sources<'tcx>(tcx: TyCtxt<'tcx>) { |
2c00a5a8 XL |
39 | tcx.dep_graph.with_ignore(|| { |
40 | if tcx.sess.opts.incremental.is_none() { | |
41 | return; | |
42 | } | |
43 | ||
0bf4aa26 XL |
44 | let available_cgus = tcx |
45 | .collect_and_partition_mono_items(LOCAL_CRATE) | |
46 | .1 | |
47 | .iter() | |
48 | .map(|cgu| format!("{}", cgu.name())) | |
49 | .collect::<BTreeSet<String>>(); | |
50 | ||
51 | let ams = AssertModuleSource { | |
52 | tcx, | |
53 | available_cgus | |
54 | }; | |
55 | ||
0731742a | 56 | for attr in &tcx.hir().krate().attrs { |
2c00a5a8 XL |
57 | ams.check_attr(attr); |
58 | } | |
59 | }) | |
5bcae85e SL |
60 | } |
61 | ||
dc9dc135 XL |
62 | struct AssertModuleSource<'tcx> { |
63 | tcx: TyCtxt<'tcx>, | |
0bf4aa26 | 64 | available_cgus: BTreeSet<String>, |
5bcae85e SL |
65 | } |
66 | ||
dc9dc135 | 67 | impl AssertModuleSource<'tcx> { |
5bcae85e | 68 | fn check_attr(&self, attr: &ast::Attribute) { |
0bf4aa26 XL |
69 | let (expected_reuse, comp_kind) = if attr.check_name(ATTR_PARTITION_REUSED) { |
70 | (CguReuse::PreLto, ComparisonKind::AtLeast) | |
94b46f34 | 71 | } else if attr.check_name(ATTR_PARTITION_CODEGENED) { |
0bf4aa26 XL |
72 | (CguReuse::No, ComparisonKind::Exact) |
73 | } else if attr.check_name(ATTR_EXPECTED_CGU_REUSE) { | |
74 | match &self.field(attr, KIND).as_str()[..] { | |
75 | "no" => (CguReuse::No, ComparisonKind::Exact), | |
76 | "pre-lto" => (CguReuse::PreLto, ComparisonKind::Exact), | |
77 | "post-lto" => (CguReuse::PostLto, ComparisonKind::Exact), | |
78 | "any" => (CguReuse::PreLto, ComparisonKind::AtLeast), | |
79 | other => { | |
80 | self.tcx.sess.span_fatal( | |
81 | attr.span, | |
82 | &format!("unknown cgu-reuse-kind `{}` specified", other)); | |
83 | } | |
84 | } | |
5bcae85e SL |
85 | } else { |
86 | return; | |
87 | }; | |
88 | ||
0bf4aa26 XL |
89 | if !self.tcx.sess.opts.debugging_opts.query_dep_graph { |
90 | self.tcx.sess.span_fatal( | |
91 | attr.span, | |
92 | &format!("found CGU-reuse attribute but `-Zquery-dep-graph` \ | |
93 | was not specified")); | |
94 | } | |
95 | ||
5bcae85e SL |
96 | if !self.check_config(attr) { |
97 | debug!("check_attr: config does not match, ignoring attr"); | |
98 | return; | |
99 | } | |
100 | ||
b7449926 XL |
101 | let user_path = self.field(attr, MODULE).as_str().to_string(); |
102 | let crate_name = self.tcx.crate_name(LOCAL_CRATE).as_str().to_string(); | |
103 | ||
104 | if !user_path.starts_with(&crate_name) { | |
105 | let msg = format!("Found malformed codegen unit name `{}`. \ | |
106 | Codegen units names must always start with the name of the \ | |
107 | crate (`{}` in this case).", user_path, crate_name); | |
108 | self.tcx.sess.span_fatal(attr.span, &msg); | |
109 | } | |
110 | ||
111 | // Split of the "special suffix" if there is one. | |
112 | let (user_path, cgu_special_suffix) = if let Some(index) = user_path.rfind(".") { | |
113 | (&user_path[..index], Some(&user_path[index + 1 ..])) | |
114 | } else { | |
115 | (&user_path[..], None) | |
116 | }; | |
117 | ||
118 | let mut cgu_path_components = user_path.split('-').collect::<Vec<_>>(); | |
119 | ||
120 | // Remove the crate name | |
121 | assert_eq!(cgu_path_components.remove(0), crate_name); | |
122 | ||
123 | let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx); | |
124 | let cgu_name = cgu_name_builder.build_cgu_name(LOCAL_CRATE, | |
125 | cgu_path_components, | |
126 | cgu_special_suffix); | |
127 | ||
128 | debug!("mapping '{}' to cgu name '{}'", self.field(attr, MODULE), cgu_name); | |
5bcae85e | 129 | |
0bf4aa26 | 130 | if !self.available_cgus.contains(&cgu_name.as_str()[..]) { |
b7449926 | 131 | self.tcx.sess.span_err(attr.span, |
0bf4aa26 XL |
132 | &format!("no module named `{}` (mangled: {}). \ |
133 | Available modules: {}", | |
b7449926 XL |
134 | user_path, |
135 | cgu_name, | |
0bf4aa26 XL |
136 | self.available_cgus |
137 | .iter() | |
138 | .cloned() | |
139 | .collect::<Vec<_>>() | |
140 | .join(", "))); | |
5bcae85e | 141 | } |
0bf4aa26 XL |
142 | |
143 | self.tcx.sess.cgu_reuse_tracker.set_expectation(&cgu_name.as_str(), | |
144 | &user_path, | |
145 | attr.span, | |
146 | expected_reuse, | |
147 | comp_kind); | |
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(), |
5bcae85e SL |
158 | &format!("associated value expected for `{}`", name)); |
159 | } | |
160 | } | |
161 | } | |
162 | ||
163 | self.tcx.sess.span_fatal( | |
164 | attr.span, | |
165 | &format!("no field `{}`", name)); | |
166 | } | |
167 | ||
168 | /// Scan for a `cfg="foo"` attribute and check whether we have a | |
169 | /// cfg flag called `foo`. | |
170 | fn check_config(&self, attr: &ast::Attribute) -> bool { | |
c30ab7b3 | 171 | let config = &self.tcx.sess.parse_sess.config; |
5bcae85e SL |
172 | let value = self.field(attr, CFG); |
173 | debug!("check_config(config={:?}, value={:?})", config, value); | |
476ff2be | 174 | if config.iter().any(|&(name, _)| name == value) { |
5bcae85e SL |
175 | debug!("check_config: matched"); |
176 | return true; | |
177 | } | |
178 | debug!("check_config: no match found"); | |
179 | return false; | |
180 | } | |
5bcae85e | 181 | } |