]> git.proxmox.com Git - rustc.git/blob - src/librustc_codegen_ssa/coverageinfo/map.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_codegen_ssa / coverageinfo / map.rs
1 pub use super::ffi::*;
2
3 use rustc_index::vec::IndexVec;
4 use rustc_middle::mir::coverage::{
5 CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex,
6 MappedExpressionIndex, Op,
7 };
8 use rustc_middle::ty::Instance;
9 use rustc_middle::ty::TyCtxt;
10
11 #[derive(Clone, Debug)]
12 pub struct ExpressionRegion {
13 lhs: ExpressionOperandId,
14 op: Op,
15 rhs: ExpressionOperandId,
16 region: CodeRegion,
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),
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.
24 ///
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."
31 pub struct FunctionCoverage {
32 source_hash: u64,
33 counters: IndexVec<CounterValueReference, Option<CodeRegion>>,
34 expressions: IndexVec<InjectedExpressionIndex, Option<ExpressionRegion>>,
35 unreachable_regions: Vec<CodeRegion>,
36 }
37
38 impl FunctionCoverage {
39 pub fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
40 let coverageinfo = tcx.coverageinfo(instance.def_id());
41 Self {
42 source_hash: 0, // will be set with the first `add_counter()`
43 counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
44 expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
45 unreachable_regions: Vec::new(),
46 }
47 }
48
49 /// Adds a code region to be counted by an injected counter intrinsic.
50 /// The source_hash (computed during coverage instrumentation) should also be provided, and
51 /// should be the same for all counters in a given function.
52 pub fn add_counter(&mut self, source_hash: u64, id: CounterValueReference, region: CodeRegion) {
53 if self.source_hash == 0 {
54 self.source_hash = source_hash;
55 } else {
56 debug_assert_eq!(source_hash, self.source_hash);
57 }
58 self.counters[id].replace(region).expect_none("add_counter called with duplicate `id`");
59 }
60
61 /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
62 /// expressions. Expression IDs start from `u32::MAX` and go down, so the range of expression
63 /// IDs will not overlap with the range of counter IDs. Counters and expressions can be added in
64 /// any order, and expressions can still be assigned contiguous (though descending) IDs, without
65 /// knowing what the last counter ID will be.
66 ///
67 /// When storing the expression data in the `expressions` vector in the `FunctionCoverage`
68 /// struct, its vector index is computed, from the given expression ID, by subtracting from
69 /// `u32::MAX`.
70 ///
71 /// Since the expression operands (`lhs` and `rhs`) can reference either counters or
72 /// expressions, an operand that references an expression also uses its original ID, descending
73 /// from `u32::MAX`. Theses operands are translated only during code generation, after all
74 /// counters and expressions have been added.
75 pub fn add_counter_expression(
76 &mut self,
77 expression_id: InjectedExpressionIndex,
78 lhs: ExpressionOperandId,
79 op: Op,
80 rhs: ExpressionOperandId,
81 region: CodeRegion,
82 ) {
83 let expression_index = self.expression_index(u32::from(expression_id));
84 self.expressions[expression_index]
85 .replace(ExpressionRegion { lhs, op, rhs, region })
86 .expect_none("add_counter_expression called with duplicate `id_descending_from_max`");
87 }
88
89 /// Add a region that will be marked as "unreachable", with a constant "zero counter".
90 pub fn add_unreachable_region(&mut self, region: CodeRegion) {
91 self.unreachable_regions.push(region)
92 }
93
94 /// Return the source hash, generated from the HIR node structure, and used to indicate whether
95 /// or not the source code structure changed between different compilations.
96 pub fn source_hash(&self) -> u64 {
97 self.source_hash
98 }
99
100 /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their
101 /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
102 /// `CounterMappingRegion`s.
103 pub fn get_expressions_and_counter_regions<'a>(
104 &'a self,
105 ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) {
106 assert!(self.source_hash != 0);
107
108 let counter_regions = self.counter_regions();
109 let (counter_expressions, expression_regions) = self.expressions_with_regions();
110 let unreachable_regions = self.unreachable_regions();
111
112 let counter_regions =
113 counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions));
114 (counter_expressions, counter_regions)
115 }
116
117 fn counter_regions<'a>(&'a self) -> impl Iterator<Item = (Counter, &'a CodeRegion)> {
118 self.counters.iter_enumerated().filter_map(|(index, entry)| {
119 // Option::map() will return None to filter out missing counters. This may happen
120 // if, for example, a MIR-instrumented counter is removed during an optimization.
121 entry.as_ref().map(|region| {
122 (Counter::counter_value_reference(index as CounterValueReference), region)
123 })
124 })
125 }
126
127 fn expressions_with_regions(
128 &'a self,
129 ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &'a CodeRegion)>) {
130 let mut counter_expressions = Vec::with_capacity(self.expressions.len());
131 let mut expression_regions = Vec::with_capacity(self.expressions.len());
132 let mut new_indexes =
133 IndexVec::from_elem_n(MappedExpressionIndex::from(u32::MAX), self.expressions.len());
134 // Note, the initial value shouldn't matter since every index in use in `self.expressions`
135 // will be set, and after that, `new_indexes` will only be accessed using those same
136 // indexes.
137
138 // Note that an `ExpressionRegion`s at any given index can include other expressions as
139 // operands, but expression operands can only come from the subset of expressions having
140 // `expression_index`s lower than the referencing `ExpressionRegion`. Therefore, it is
141 // reasonable to look up the new index of an expression operand while the `new_indexes`
142 // vector is only complete up to the current `ExpressionIndex`.
143 let id_to_counter =
144 |new_indexes: &IndexVec<InjectedExpressionIndex, MappedExpressionIndex>,
145 id: ExpressionOperandId| {
146 if id.index() < self.counters.len() {
147 let index = CounterValueReference::from(id.index());
148 self.counters
149 .get(index)
150 .unwrap() // pre-validated
151 .as_ref()
152 .map(|_| Counter::counter_value_reference(index))
153 } else {
154 let index = self.expression_index(u32::from(id));
155 self.expressions
156 .get(index)
157 .expect("expression id is out of range")
158 .as_ref()
159 .map(|_| Counter::expression(new_indexes[index]))
160 }
161 };
162
163 for (original_index, expression_region) in
164 self.expressions.iter_enumerated().filter_map(|(original_index, entry)| {
165 // Option::map() will return None to filter out missing expressions. This may happen
166 // if, for example, a MIR-instrumented expression is removed during an optimization.
167 entry.as_ref().map(|region| (original_index, region))
168 })
169 {
170 let region = &expression_region.region;
171 let ExpressionRegion { lhs, op, rhs, .. } = *expression_region;
172
173 if let Some(Some((lhs_counter, rhs_counter))) =
174 id_to_counter(&new_indexes, lhs).map(|lhs_counter| {
175 id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter))
176 })
177 {
178 // Both operands exist. `Expression` operands exist in `self.expressions` and have
179 // been assigned a `new_index`.
180 let mapped_expression_index =
181 MappedExpressionIndex::from(counter_expressions.len());
182 counter_expressions.push(CounterExpression::new(
183 lhs_counter,
184 match op {
185 Op::Add => ExprKind::Add,
186 Op::Subtract => ExprKind::Subtract,
187 },
188 rhs_counter,
189 ));
190 new_indexes[original_index] = mapped_expression_index;
191 expression_regions.push((Counter::expression(mapped_expression_index), region));
192 }
193 }
194 (counter_expressions, expression_regions.into_iter())
195 }
196
197 fn unreachable_regions<'a>(&'a self) -> impl Iterator<Item = (Counter, &'a CodeRegion)> {
198 self.unreachable_regions.iter().map(|region| (Counter::zero(), region))
199 }
200
201 fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex {
202 debug_assert!(id_descending_from_max >= self.counters.len() as u32);
203 InjectedExpressionIndex::from(u32::MAX - id_descending_from_max)
204 }
205 }