1 use crate::coverageinfo
::ffi
::{Counter, CounterExpression, ExprKind}
;
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
;
9 #[derive(Clone, Debug, PartialEq)]
10 pub struct Expression
{
14 region
: Option
<CodeRegion
>,
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.
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
29 pub struct FunctionCoverage
<'tcx
> {
30 instance
: Instance
<'tcx
>,
33 counters
: IndexVec
<CounterId
, Option
<CodeRegion
>>,
34 expressions
: IndexVec
<ExpressionId
, Option
<Expression
>>,
35 unreachable_regions
: Vec
<CodeRegion
>,
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)
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)
49 fn create(tcx
: TyCtxt
<'tcx
>, instance
: Instance
<'tcx
>, is_used
: bool
) -> Self {
50 let coverageinfo
= tcx
.coverageinfo(instance
.def
);
52 "FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}",
53 instance
, coverageinfo
, is_used
57 source_hash
: 0, // will be set with the first `add_counter()`
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(),
65 /// Returns true for a used (called) function, and false for an unused function.
66 pub fn is_used(&self) -> bool
{
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
;
76 debug_assert_eq
!(source_hash
, self.source_hash
);
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");
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(
92 expression_id
: ExpressionId
,
96 region
: Option
<CodeRegion
>,
99 "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}",
100 expression_id
, lhs
, op
, rhs
, region
103 expression_id
.as_usize() < self.expressions
.len(),
104 "expression_id {} is out of range for expressions.len() = {}
106 expression_id
.as_usize(),
107 self.expressions
.len(),
110 if let Some(previous_expression
) = self.expressions
[expression_id
].replace(Expression
{
114 region
: region
.clone(),
118 Expression { lhs, op, rhs, region }
,
119 "add_counter_expression: expression for id changed"
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
)
129 /// Perform some simplifications to make the final coverage mappings
130 /// slightly smaller.
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();
142 // For each expression, perform simplifications based on lower-numbered
143 // expressions, and then update the set of always-zero expressions if
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
);
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
;
163 maybe_set_operand_to_zero(&mut expression
.lhs
);
164 maybe_set_operand_to_zero(&mut expression
.rhs
);
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
;
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
);
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 {
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(
192 ) -> (Vec
<CounterExpression
>, impl Iterator
<Item
= (Counter
, &CodeRegion
)>) {
194 self.source_hash
!= 0 || !self.is_used
,
195 "No counters provided the source_hash for used function: {:?}",
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());
205 let counter_regions
= self.counter_regions();
206 let expression_regions
= self.expression_regions();
207 let unreachable_regions
= self.unreachable_regions();
209 let counter_regions
=
210 counter_regions
.chain(expression_regions
.into_iter().chain(unreachable_regions
));
211 (counter_expressions
, counter_regions
)
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
))
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`.)
232 .map(|expression
| match expression
{
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
240 &Some(Expression { lhs, op, rhs, .. }
) => {
241 // Convert the operands and operator as normal.
242 CounterExpression
::new(
243 Counter
::from_operand(lhs
),
245 Op
::Add
=> ExprKind
::Add
,
246 Op
::Subtract
=> ExprKind
::Subtract
,
248 Counter
::from_operand(rhs
),
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.
261 .filter_map(|(id
, expression
)| {
262 let code_region
= expression
.as_ref()?
.region
.as_ref()?
;
263 Some((Counter
::expression(id
), code_region
))
268 fn unreachable_regions(&self) -> impl Iterator
<Item
= (Counter
, &CodeRegion
)> {
269 self.unreachable_regions
.iter().map(|region
| (Counter
::ZERO
, region
))