]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | pub use super::ffi::*; |
f035d41b | 2 | |
3dfed10e XL |
3 | use rustc_index::vec::IndexVec; |
4 | use rustc_middle::mir::coverage::{ | |
29967ef6 XL |
5 | CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, |
6 | InjectedExpressionIndex, MappedExpressionIndex, Op, | |
3dfed10e XL |
7 | }; |
8 | use rustc_middle::ty::Instance; | |
9 | use rustc_middle::ty::TyCtxt; | |
f035d41b | 10 | |
6a06907d | 11 | #[derive(Clone, Debug, PartialEq)] |
29967ef6 | 12 | pub struct Expression { |
3dfed10e XL |
13 | lhs: ExpressionOperandId, |
14 | op: Op, | |
15 | rhs: ExpressionOperandId, | |
29967ef6 | 16 | region: Option<CodeRegion>, |
f035d41b XL |
17 | } |
18 | ||
19 | /// Collects all of the coverage regions associated with (a) injected counters, (b) counter | |
20 | /// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero), | |
3dfed10e XL |
21 | /// for a given Function. Counters and counter expressions have non-overlapping `id`s because they |
22 | /// can both be operands in an expression. This struct also stores the `function_source_hash`, | |
23 | /// computed during instrumentation, and forwarded with counters. | |
f035d41b | 24 | /// |
3dfed10e XL |
25 | /// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap |
26 | /// regions" (or "gap areas"). A gap region is a code region within a counted region (either counter | |
27 | /// or expression), but the line or lines in the gap region are not executable (such as lines with | |
28 | /// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count | |
29 | /// for a gap area is only used as the line execution count if there are no other regions on a | |
30 | /// line." | |
29967ef6 XL |
31 | pub struct FunctionCoverage<'tcx> { |
32 | instance: Instance<'tcx>, | |
3dfed10e XL |
33 | source_hash: u64, |
34 | counters: IndexVec<CounterValueReference, Option<CodeRegion>>, | |
29967ef6 | 35 | expressions: IndexVec<InjectedExpressionIndex, Option<Expression>>, |
3dfed10e | 36 | unreachable_regions: Vec<CodeRegion>, |
f035d41b XL |
37 | } |
38 | ||
29967ef6 XL |
39 | impl<'tcx> FunctionCoverage<'tcx> { |
40 | pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { | |
3dfed10e | 41 | let coverageinfo = tcx.coverageinfo(instance.def_id()); |
29967ef6 XL |
42 | debug!( |
43 | "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}", | |
44 | instance, coverageinfo | |
45 | ); | |
3dfed10e | 46 | Self { |
29967ef6 | 47 | instance, |
3dfed10e XL |
48 | source_hash: 0, // will be set with the first `add_counter()` |
49 | counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), | |
50 | expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), | |
51 | unreachable_regions: Vec::new(), | |
52 | } | |
53 | } | |
54 | ||
29967ef6 XL |
55 | /// Sets the function source hash value. If called multiple times for the same function, all |
56 | /// calls should have the same hash value. | |
57 | pub fn set_function_source_hash(&mut self, source_hash: u64) { | |
3dfed10e XL |
58 | if self.source_hash == 0 { |
59 | self.source_hash = source_hash; | |
60 | } else { | |
61 | debug_assert_eq!(source_hash, self.source_hash); | |
62 | } | |
29967ef6 XL |
63 | } |
64 | ||
65 | /// Adds a code region to be counted by an injected counter intrinsic. | |
66 | pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) { | |
6a06907d XL |
67 | if let Some(previous_region) = self.counters[id].replace(region.clone()) { |
68 | assert_eq!(previous_region, region, "add_counter: code region for id changed"); | |
69 | } | |
f035d41b XL |
70 | } |
71 | ||
3dfed10e XL |
72 | /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other |
73 | /// expressions. Expression IDs start from `u32::MAX` and go down, so the range of expression | |
74 | /// IDs will not overlap with the range of counter IDs. Counters and expressions can be added in | |
75 | /// any order, and expressions can still be assigned contiguous (though descending) IDs, without | |
76 | /// knowing what the last counter ID will be. | |
77 | /// | |
78 | /// When storing the expression data in the `expressions` vector in the `FunctionCoverage` | |
79 | /// struct, its vector index is computed, from the given expression ID, by subtracting from | |
80 | /// `u32::MAX`. | |
81 | /// | |
82 | /// Since the expression operands (`lhs` and `rhs`) can reference either counters or | |
83 | /// expressions, an operand that references an expression also uses its original ID, descending | |
84 | /// from `u32::MAX`. Theses operands are translated only during code generation, after all | |
85 | /// counters and expressions have been added. | |
f035d41b XL |
86 | pub fn add_counter_expression( |
87 | &mut self, | |
29967ef6 | 88 | expression_id: InjectedExpressionId, |
3dfed10e XL |
89 | lhs: ExpressionOperandId, |
90 | op: Op, | |
91 | rhs: ExpressionOperandId, | |
29967ef6 | 92 | region: Option<CodeRegion>, |
f035d41b | 93 | ) { |
29967ef6 XL |
94 | debug!( |
95 | "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", | |
96 | expression_id, lhs, op, rhs, region | |
97 | ); | |
3dfed10e | 98 | let expression_index = self.expression_index(u32::from(expression_id)); |
6a06907d XL |
99 | if let Some(previous_expression) = self.expressions[expression_index].replace(Expression { |
100 | lhs, | |
101 | op, | |
102 | rhs, | |
103 | region: region.clone(), | |
104 | }) { | |
105 | assert_eq!( | |
106 | previous_expression, | |
107 | Expression { lhs, op, rhs, region }, | |
108 | "add_counter_expression: expression for id changed" | |
109 | ); | |
110 | } | |
3dfed10e XL |
111 | } |
112 | ||
113 | /// Add a region that will be marked as "unreachable", with a constant "zero counter". | |
114 | pub fn add_unreachable_region(&mut self, region: CodeRegion) { | |
115 | self.unreachable_regions.push(region) | |
116 | } | |
117 | ||
118 | /// Return the source hash, generated from the HIR node structure, and used to indicate whether | |
119 | /// or not the source code structure changed between different compilations. | |
120 | pub fn source_hash(&self) -> u64 { | |
121 | self.source_hash | |
f035d41b XL |
122 | } |
123 | ||
3dfed10e XL |
124 | /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their |
125 | /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create | |
126 | /// `CounterMappingRegion`s. | |
127 | pub fn get_expressions_and_counter_regions<'a>( | |
128 | &'a self, | |
129 | ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) { | |
29967ef6 XL |
130 | assert!( |
131 | self.source_hash != 0, | |
132 | "No counters provided the source_hash for function: {:?}", | |
133 | self.instance | |
134 | ); | |
3dfed10e XL |
135 | |
136 | let counter_regions = self.counter_regions(); | |
137 | let (counter_expressions, expression_regions) = self.expressions_with_regions(); | |
138 | let unreachable_regions = self.unreachable_regions(); | |
139 | ||
140 | let counter_regions = | |
141 | counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions)); | |
142 | (counter_expressions, counter_regions) | |
143 | } | |
144 | ||
145 | fn counter_regions<'a>(&'a self) -> impl Iterator<Item = (Counter, &'a CodeRegion)> { | |
146 | self.counters.iter_enumerated().filter_map(|(index, entry)| { | |
147 | // Option::map() will return None to filter out missing counters. This may happen | |
148 | // if, for example, a MIR-instrumented counter is removed during an optimization. | |
149 | entry.as_ref().map(|region| { | |
150 | (Counter::counter_value_reference(index as CounterValueReference), region) | |
151 | }) | |
152 | }) | |
153 | } | |
154 | ||
155 | fn expressions_with_regions( | |
156 | &'a self, | |
157 | ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) { | |
158 | let mut counter_expressions = Vec::with_capacity(self.expressions.len()); | |
159 | let mut expression_regions = Vec::with_capacity(self.expressions.len()); | |
29967ef6 | 160 | let mut new_indexes = IndexVec::from_elem_n(None, self.expressions.len()); |
3dfed10e | 161 | |
29967ef6 XL |
162 | // This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or |
163 | // `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type | |
164 | // and value. Operand ID value `0` maps to `CounterKind::Zero`; values in the known range | |
165 | // of injected LLVM counters map to `CounterKind::CounterValueReference` (and the value | |
166 | // matches the injected counter index); and any other value is converted into a | |
167 | // `CounterKind::Expression` with the expression's `new_index`. | |
168 | // | |
169 | // Expressions will be returned from this function in a sequential vector (array) of | |
170 | // `CounterExpression`, so the expression IDs must be mapped from their original, | |
171 | // potentially sparse set of indexes, originally in reverse order from `u32::MAX`. | |
172 | // | |
173 | // An `Expression` as an operand will have already been encountered as an `Expression` with | |
174 | // operands, so its new_index will already have been generated (as a 1-up index value). | |
175 | // (If an `Expression` as an operand does not have a corresponding new_index, it was | |
176 | // probably optimized out, after the expression was injected into the MIR, so it will | |
177 | // get a `CounterKind::Zero` instead.) | |
178 | // | |
179 | // In other words, an `Expression`s at any given index can include other expressions as | |
3dfed10e | 180 | // operands, but expression operands can only come from the subset of expressions having |
29967ef6 | 181 | // `expression_index`s lower than the referencing `Expression`. Therefore, it is |
3dfed10e XL |
182 | // reasonable to look up the new index of an expression operand while the `new_indexes` |
183 | // vector is only complete up to the current `ExpressionIndex`. | |
5869c6ff XL |
184 | let id_to_counter = |new_indexes: &IndexVec< |
185 | InjectedExpressionIndex, | |
186 | Option<MappedExpressionIndex>, | |
187 | >, | |
188 | id: ExpressionOperandId| { | |
189 | if id == ExpressionOperandId::ZERO { | |
190 | Some(Counter::zero()) | |
191 | } else if id.index() < self.counters.len() { | |
192 | // Note: Some codegen-injected Counters may be only referenced by `Expression`s, | |
193 | // and may not have their own `CodeRegion`s, | |
194 | let index = CounterValueReference::from(id.index()); | |
195 | Some(Counter::counter_value_reference(index)) | |
196 | } else { | |
197 | let index = self.expression_index(u32::from(id)); | |
198 | self.expressions | |
199 | .get(index) | |
200 | .expect("expression id is out of range") | |
201 | .as_ref() | |
202 | // If an expression was optimized out, assume it would have produced a count | |
203 | // of zero. This ensures that expressions dependent on optimized-out | |
204 | // expressions are still valid. | |
205 | .map_or(Some(Counter::zero()), |_| new_indexes[index].map(Counter::expression)) | |
206 | } | |
207 | }; | |
3dfed10e | 208 | |
29967ef6 | 209 | for (original_index, expression) in |
3dfed10e XL |
210 | self.expressions.iter_enumerated().filter_map(|(original_index, entry)| { |
211 | // Option::map() will return None to filter out missing expressions. This may happen | |
212 | // if, for example, a MIR-instrumented expression is removed during an optimization. | |
29967ef6 | 213 | entry.as_ref().map(|expression| (original_index, expression)) |
3dfed10e XL |
214 | }) |
215 | { | |
29967ef6 XL |
216 | let optional_region = &expression.region; |
217 | let Expression { lhs, op, rhs, .. } = *expression; | |
3dfed10e XL |
218 | |
219 | if let Some(Some((lhs_counter, rhs_counter))) = | |
220 | id_to_counter(&new_indexes, lhs).map(|lhs_counter| { | |
221 | id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) | |
222 | }) | |
223 | { | |
29967ef6 XL |
224 | debug_assert!( |
225 | (lhs_counter.id as usize) | |
226 | < usize::max(self.counters.len(), self.expressions.len()) | |
227 | ); | |
228 | debug_assert!( | |
229 | (rhs_counter.id as usize) | |
230 | < usize::max(self.counters.len(), self.expressions.len()) | |
231 | ); | |
3dfed10e XL |
232 | // Both operands exist. `Expression` operands exist in `self.expressions` and have |
233 | // been assigned a `new_index`. | |
234 | let mapped_expression_index = | |
235 | MappedExpressionIndex::from(counter_expressions.len()); | |
29967ef6 | 236 | let expression = CounterExpression::new( |
3dfed10e XL |
237 | lhs_counter, |
238 | match op { | |
239 | Op::Add => ExprKind::Add, | |
240 | Op::Subtract => ExprKind::Subtract, | |
241 | }, | |
242 | rhs_counter, | |
29967ef6 XL |
243 | ); |
244 | debug!( | |
245 | "Adding expression {:?} = {:?}, region: {:?}", | |
246 | mapped_expression_index, expression, optional_region | |
247 | ); | |
248 | counter_expressions.push(expression); | |
249 | new_indexes[original_index] = Some(mapped_expression_index); | |
250 | if let Some(region) = optional_region { | |
251 | expression_regions.push((Counter::expression(mapped_expression_index), region)); | |
252 | } | |
253 | } else { | |
254 | debug!( | |
255 | "Ignoring expression with one or more missing operands: \ | |
256 | original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", | |
257 | original_index, lhs, op, rhs, optional_region, | |
258 | ) | |
3dfed10e XL |
259 | } |
260 | } | |
261 | (counter_expressions, expression_regions.into_iter()) | |
f035d41b XL |
262 | } |
263 | ||
3dfed10e XL |
264 | fn unreachable_regions<'a>(&'a self) -> impl Iterator<Item = (Counter, &'a CodeRegion)> { |
265 | self.unreachable_regions.iter().map(|region| (Counter::zero(), region)) | |
f035d41b XL |
266 | } |
267 | ||
3dfed10e XL |
268 | fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex { |
269 | debug_assert!(id_descending_from_max >= self.counters.len() as u32); | |
270 | InjectedExpressionIndex::from(u32::MAX - id_descending_from_max) | |
f035d41b XL |
271 | } |
272 | } |