]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_codegen_ssa/src/coverageinfo/map.rs
Merge tag 'debian/1.52.1+dfsg1-1_exp2' into proxmox/buster
[rustc.git] / compiler / rustc_codegen_ssa / src / coverageinfo / map.rs
CommitLineData
3dfed10e 1pub use super::ffi::*;
f035d41b 2
3dfed10e
XL
3use rustc_index::vec::IndexVec;
4use rustc_middle::mir::coverage::{
29967ef6
XL
5 CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId,
6 InjectedExpressionIndex, MappedExpressionIndex, Op,
3dfed10e
XL
7};
8use rustc_middle::ty::Instance;
9use rustc_middle::ty::TyCtxt;
f035d41b 10
6a06907d 11#[derive(Clone, Debug, PartialEq)]
29967ef6 12pub 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
31pub 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
39impl<'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}