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