]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / compiler / rustc_codegen_llvm / src / coverageinfo / map_data.rs
1 use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
2
3 use rustc_data_structures::fx::FxIndexSet;
4 use rustc_index::IndexVec;
5 use rustc_middle::mir::coverage::{CodeRegion, CounterId, ExpressionId, Op, Operand};
6 use rustc_middle::ty::Instance;
7 use rustc_middle::ty::TyCtxt;
8
9 #[derive(Clone, Debug, PartialEq)]
10 pub struct Expression {
11 lhs: Operand,
12 op: Op,
13 rhs: Operand,
14 region: Option<CodeRegion>,
15 }
16
17 /// Collects all of the coverage regions associated with (a) injected counters, (b) counter
18 /// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
19 /// for a given Function. This struct also stores the `function_source_hash`,
20 /// computed during instrumentation, and forwarded with counters.
21 ///
22 /// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap
23 /// regions" (or "gap areas"). A gap region is a code region within a counted region (either counter
24 /// or expression), but the line or lines in the gap region are not executable (such as lines with
25 /// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
26 /// for a gap area is only used as the line execution count if there are no other regions on a
27 /// line."
28 #[derive(Debug)]
29 pub struct FunctionCoverage<'tcx> {
30 instance: Instance<'tcx>,
31 source_hash: u64,
32 is_used: bool,
33 counters: IndexVec<CounterId, Option<CodeRegion>>,
34 expressions: IndexVec<ExpressionId, Option<Expression>>,
35 unreachable_regions: Vec<CodeRegion>,
36 }
37
38 impl<'tcx> FunctionCoverage<'tcx> {
39 /// Creates a new set of coverage data for a used (called) function.
40 pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
41 Self::create(tcx, instance, true)
42 }
43
44 /// Creates a new set of coverage data for an unused (never called) function.
45 pub fn unused(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
46 Self::create(tcx, instance, false)
47 }
48
49 fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self {
50 let coverageinfo = tcx.coverageinfo(instance.def);
51 debug!(
52 "FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}",
53 instance, coverageinfo, is_used
54 );
55 Self {
56 instance,
57 source_hash: 0, // will be set with the first `add_counter()`
58 is_used,
59 counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
60 expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
61 unreachable_regions: Vec::new(),
62 }
63 }
64
65 /// Returns true for a used (called) function, and false for an unused function.
66 pub fn is_used(&self) -> bool {
67 self.is_used
68 }
69
70 /// Sets the function source hash value. If called multiple times for the same function, all
71 /// calls should have the same hash value.
72 pub fn set_function_source_hash(&mut self, source_hash: u64) {
73 if self.source_hash == 0 {
74 self.source_hash = source_hash;
75 } else {
76 debug_assert_eq!(source_hash, self.source_hash);
77 }
78 }
79
80 /// Adds a code region to be counted by an injected counter intrinsic.
81 pub fn add_counter(&mut self, id: CounterId, region: CodeRegion) {
82 if let Some(previous_region) = self.counters[id].replace(region.clone()) {
83 assert_eq!(previous_region, region, "add_counter: code region for id changed");
84 }
85 }
86
87 /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
88 /// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity
89 /// between operands that are counter IDs and operands that are expression IDs.
90 pub fn add_counter_expression(
91 &mut self,
92 expression_id: ExpressionId,
93 lhs: Operand,
94 op: Op,
95 rhs: Operand,
96 region: Option<CodeRegion>,
97 ) {
98 debug!(
99 "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}",
100 expression_id, lhs, op, rhs, region
101 );
102 debug_assert!(
103 expression_id.as_usize() < self.expressions.len(),
104 "expression_id {} is out of range for expressions.len() = {}
105 for {:?}",
106 expression_id.as_usize(),
107 self.expressions.len(),
108 self,
109 );
110 if let Some(previous_expression) = self.expressions[expression_id].replace(Expression {
111 lhs,
112 op,
113 rhs,
114 region: region.clone(),
115 }) {
116 assert_eq!(
117 previous_expression,
118 Expression { lhs, op, rhs, region },
119 "add_counter_expression: expression for id changed"
120 );
121 }
122 }
123
124 /// Add a region that will be marked as "unreachable", with a constant "zero counter".
125 pub fn add_unreachable_region(&mut self, region: CodeRegion) {
126 self.unreachable_regions.push(region)
127 }
128
129 /// Perform some simplifications to make the final coverage mappings
130 /// slightly smaller.
131 ///
132 /// This method mainly exists to preserve the simplifications that were
133 /// already being performed by the Rust-side expression renumbering, so that
134 /// the resulting coverage mappings don't get worse.
135 pub(crate) fn simplify_expressions(&mut self) {
136 // The set of expressions that either were optimized out entirely, or
137 // have zero as both of their operands, and will therefore always have
138 // a value of zero. Other expressions that refer to these as operands
139 // can have those operands replaced with `Operand::Zero`.
140 let mut zero_expressions = FxIndexSet::default();
141
142 // For each expression, perform simplifications based on lower-numbered
143 // expressions, and then update the set of always-zero expressions if
144 // necessary.
145 // (By construction, expressions can only refer to other expressions
146 // that have lower IDs, so one simplification pass is sufficient.)
147 for (id, maybe_expression) in self.expressions.iter_enumerated_mut() {
148 let Some(expression) = maybe_expression else {
149 // If an expression is missing, it must have been optimized away,
150 // so any operand that refers to it can be replaced with zero.
151 zero_expressions.insert(id);
152 continue;
153 };
154
155 // If an operand refers to an expression that is always zero, then
156 // that operand can be replaced with `Operand::Zero`.
157 let maybe_set_operand_to_zero = |operand: &mut Operand| match &*operand {
158 Operand::Expression(id) if zero_expressions.contains(id) => {
159 *operand = Operand::Zero;
160 }
161 _ => (),
162 };
163 maybe_set_operand_to_zero(&mut expression.lhs);
164 maybe_set_operand_to_zero(&mut expression.rhs);
165
166 // Coverage counter values cannot be negative, so if an expression
167 // involves subtraction from zero, assume that its RHS must also be zero.
168 // (Do this after simplifications that could set the LHS to zero.)
169 if let Expression { lhs: Operand::Zero, op: Op::Subtract, .. } = expression {
170 expression.rhs = Operand::Zero;
171 }
172
173 // After the above simplifications, if both operands are zero, then
174 // we know that this expression is always zero too.
175 if let Expression { lhs: Operand::Zero, rhs: Operand::Zero, .. } = expression {
176 zero_expressions.insert(id);
177 }
178 }
179 }
180
181 /// Return the source hash, generated from the HIR node structure, and used to indicate whether
182 /// or not the source code structure changed between different compilations.
183 pub fn source_hash(&self) -> u64 {
184 self.source_hash
185 }
186
187 /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their
188 /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
189 /// `CounterMappingRegion`s.
190 pub fn get_expressions_and_counter_regions(
191 &self,
192 ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) {
193 assert!(
194 self.source_hash != 0 || !self.is_used,
195 "No counters provided the source_hash for used function: {:?}",
196 self.instance
197 );
198
199 let counter_expressions = self.counter_expressions();
200 // Expression IDs are indices into `self.expressions`, and on the LLVM
201 // side they will be treated as indices into `counter_expressions`, so
202 // the two vectors should correspond 1:1.
203 assert_eq!(self.expressions.len(), counter_expressions.len());
204
205 let counter_regions = self.counter_regions();
206 let expression_regions = self.expression_regions();
207 let unreachable_regions = self.unreachable_regions();
208
209 let counter_regions =
210 counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions));
211 (counter_expressions, counter_regions)
212 }
213
214 fn counter_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
215 self.counters.iter_enumerated().filter_map(|(index, entry)| {
216 // Option::map() will return None to filter out missing counters. This may happen
217 // if, for example, a MIR-instrumented counter is removed during an optimization.
218 entry.as_ref().map(|region| (Counter::counter_value_reference(index), region))
219 })
220 }
221
222 /// Convert this function's coverage expression data into a form that can be
223 /// passed through FFI to LLVM.
224 fn counter_expressions(&self) -> Vec<CounterExpression> {
225 // We know that LLVM will optimize out any unused expressions before
226 // producing the final coverage map, so there's no need to do the same
227 // thing on the Rust side unless we're confident we can do much better.
228 // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
229
230 self.expressions
231 .iter()
232 .map(|expression| match expression {
233 None => {
234 // This expression ID was allocated, but we never saw the
235 // actual expression, so it must have been optimized out.
236 // Replace it with a dummy expression, and let LLVM take
237 // care of omitting it from the expression list.
238 CounterExpression::DUMMY
239 }
240 &Some(Expression { lhs, op, rhs, .. }) => {
241 // Convert the operands and operator as normal.
242 CounterExpression::new(
243 Counter::from_operand(lhs),
244 match op {
245 Op::Add => ExprKind::Add,
246 Op::Subtract => ExprKind::Subtract,
247 },
248 Counter::from_operand(rhs),
249 )
250 }
251 })
252 .collect::<Vec<_>>()
253 }
254
255 fn expression_regions(&self) -> Vec<(Counter, &CodeRegion)> {
256 // Find all of the expression IDs that weren't optimized out AND have
257 // an attached code region, and return the corresponding mapping as a
258 // counter/region pair.
259 self.expressions
260 .iter_enumerated()
261 .filter_map(|(id, expression)| {
262 let code_region = expression.as_ref()?.region.as_ref()?;
263 Some((Counter::expression(id), code_region))
264 })
265 .collect::<Vec<_>>()
266 }
267
268 fn unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
269 self.unreachable_regions.iter().map(|region| (Counter::ZERO, region))
270 }
271 }