]> git.proxmox.com Git - rustc.git/blame - 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
CommitLineData
add651ee 1use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
f035d41b 2
781aab86
FG
3use rustc_data_structures::fx::FxIndexSet;
4use rustc_index::IndexVec;
5use rustc_middle::mir::coverage::{CodeRegion, CounterId, ExpressionId, Op, Operand};
3dfed10e
XL
6use rustc_middle::ty::Instance;
7use rustc_middle::ty::TyCtxt;
f035d41b 8
6a06907d 9#[derive(Clone, Debug, PartialEq)]
29967ef6 10pub struct Expression {
add651ee 11 lhs: Operand,
3dfed10e 12 op: Op,
add651ee 13 rhs: Operand,
29967ef6 14 region: Option<CodeRegion>,
f035d41b
XL
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),
add651ee 19/// for a given Function. This struct also stores the `function_source_hash`,
3dfed10e 20/// computed during instrumentation, and forwarded with counters.
f035d41b 21///
3dfed10e
XL
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."
17df50a5 28#[derive(Debug)]
29967ef6
XL
29pub struct FunctionCoverage<'tcx> {
30 instance: Instance<'tcx>,
3dfed10e 31 source_hash: u64,
cdc7bbd5 32 is_used: bool,
add651ee
FG
33 counters: IndexVec<CounterId, Option<CodeRegion>>,
34 expressions: IndexVec<ExpressionId, Option<Expression>>,
3dfed10e 35 unreachable_regions: Vec<CodeRegion>,
f035d41b
XL
36}
37
29967ef6 38impl<'tcx> FunctionCoverage<'tcx> {
cdc7bbd5 39 /// Creates a new set of coverage data for a used (called) function.
29967ef6 40 pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
cdc7bbd5
XL
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 {
17df50a5 50 let coverageinfo = tcx.coverageinfo(instance.def);
29967ef6 51 debug!(
17df50a5 52 "FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}",
cdc7bbd5 53 instance, coverageinfo, is_used
29967ef6 54 );
3dfed10e 55 Self {
29967ef6 56 instance,
3dfed10e 57 source_hash: 0, // will be set with the first `add_counter()`
cdc7bbd5 58 is_used,
3dfed10e
XL
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
cdc7bbd5
XL
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
29967ef6
XL
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) {
3dfed10e
XL
73 if self.source_hash == 0 {
74 self.source_hash = source_hash;
75 } else {
76 debug_assert_eq!(source_hash, self.source_hash);
77 }
29967ef6
XL
78 }
79
80 /// Adds a code region to be counted by an injected counter intrinsic.
add651ee 81 pub fn add_counter(&mut self, id: CounterId, region: CodeRegion) {
6a06907d
XL
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 }
f035d41b
XL
85 }
86
3dfed10e 87 /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
add651ee
FG
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.
f035d41b
XL
90 pub fn add_counter_expression(
91 &mut self,
add651ee
FG
92 expression_id: ExpressionId,
93 lhs: Operand,
3dfed10e 94 op: Op,
add651ee 95 rhs: Operand,
29967ef6 96 region: Option<CodeRegion>,
f035d41b 97 ) {
29967ef6
XL
98 debug!(
99 "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}",
100 expression_id, lhs, op, rhs, region
101 );
17df50a5 102 debug_assert!(
add651ee
FG
103 expression_id.as_usize() < self.expressions.len(),
104 "expression_id {} is out of range for expressions.len() = {}
17df50a5 105 for {:?}",
add651ee 106 expression_id.as_usize(),
17df50a5
XL
107 self.expressions.len(),
108 self,
109 );
add651ee 110 if let Some(previous_expression) = self.expressions[expression_id].replace(Expression {
6a06907d
XL
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 }
3dfed10e
XL
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
781aab86
FG
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
3dfed10e
XL
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
f035d41b
XL
185 }
186
3dfed10e
XL
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.
a2a8927a
XL
190 pub fn get_expressions_and_counter_regions(
191 &self,
192 ) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) {
29967ef6 193 assert!(
cdc7bbd5
XL
194 self.source_hash != 0 || !self.is_used,
195 "No counters provided the source_hash for used function: {:?}",
29967ef6
XL
196 self.instance
197 );
3dfed10e 198
781aab86
FG
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
3dfed10e 205 let counter_regions = self.counter_regions();
781aab86 206 let expression_regions = self.expression_regions();
3dfed10e
XL
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
a2a8927a 214 fn counter_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
3dfed10e
XL
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.
cdc7bbd5 218 entry.as_ref().map(|region| (Counter::counter_value_reference(index), region))
3dfed10e
XL
219 })
220 }
221
781aab86
FG
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`.)
3dfed10e 229
781aab86
FG
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
cdc7bbd5 239 }
781aab86
FG
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 )
29967ef6 250 }
781aab86
FG
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<_>>()
f035d41b
XL
266 }
267
a2a8927a 268 fn unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
781aab86 269 self.unreachable_regions.iter().map(|region| (Counter::ZERO, region))
f035d41b 270 }
f035d41b 271}