@@ -2,19 +2,11 @@ use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
2
2
3
3
use rustc_data_structures:: fx:: FxIndexSet ;
4
4
use rustc_index:: bit_set:: BitSet ;
5
- use rustc_index:: IndexVec ;
6
5
use rustc_middle:: mir:: coverage:: {
7
- CodeRegion , CounterId , CovTerm , ExpressionId , FunctionCoverageInfo , Mapping , Op ,
6
+ CodeRegion , CounterId , CovTerm , Expression , ExpressionId , FunctionCoverageInfo , Mapping , Op ,
8
7
} ;
9
8
use rustc_middle:: ty:: Instance ;
10
9
11
- #[ derive( Clone , Debug , PartialEq ) ]
12
- pub struct Expression {
13
- lhs : CovTerm ,
14
- op : Op ,
15
- rhs : CovTerm ,
16
- }
17
-
18
10
/// Holds all of the coverage mapping data associated with a function instance,
19
11
/// collected during traversal of `Coverage` statements in the function's MIR.
20
12
#[ derive( Debug ) ]
@@ -26,7 +18,9 @@ pub struct FunctionCoverage<'tcx> {
26
18
/// Tracks which counters have been seen, so that we can identify mappings
27
19
/// to counters that were optimized out, and set them to zero.
28
20
counters_seen : BitSet < CounterId > ,
29
- expressions : IndexVec < ExpressionId , Option < Expression > > ,
21
+ /// Tracks which expressions have one or more associated mappings, but have
22
+ /// not yet been seen. This lets us detect that they were optimized out.
23
+ expressions_not_seen : BitSet < ExpressionId > ,
30
24
}
31
25
32
26
impl < ' tcx > FunctionCoverage < ' tcx > {
@@ -52,16 +46,27 @@ impl<'tcx> FunctionCoverage<'tcx> {
52
46
is_used : bool ,
53
47
) -> Self {
54
48
let num_counters = function_coverage_info. num_counters ;
55
- let num_expressions = function_coverage_info. num_expressions ;
49
+ let num_expressions = function_coverage_info. expressions . len ( ) ;
56
50
debug ! (
57
51
"FunctionCoverage::create(instance={instance:?}) has \
58
52
num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}"
59
53
) ;
54
+
55
+ // Every expression that has an associated mapping should have a
56
+ // corresponding statement in MIR. If we don't see that statement, we
57
+ // know that it was removed by MIR optimizations.
58
+ let mut expressions_not_seen = BitSet :: new_empty ( num_expressions) ;
59
+ for Mapping { term, .. } in & function_coverage_info. mappings {
60
+ if let & CovTerm :: Expression ( id) = term {
61
+ expressions_not_seen. insert ( id) ;
62
+ }
63
+ }
64
+
60
65
Self {
61
66
function_coverage_info,
62
67
is_used,
63
68
counters_seen : BitSet :: new_empty ( num_counters) ,
64
- expressions : IndexVec :: from_elem_n ( None , num_expressions ) ,
69
+ expressions_not_seen ,
65
70
}
66
71
}
67
72
@@ -76,35 +81,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
76
81
self . counters_seen . insert ( id) ;
77
82
}
78
83
79
- /// Adds information about a coverage expression .
84
+ /// Marks an expression ID as having been seen .
80
85
#[ instrument( level = "debug" , skip( self ) ) ]
81
- pub ( crate ) fn add_counter_expression (
82
- & mut self ,
83
- expression_id : ExpressionId ,
84
- lhs : CovTerm ,
85
- op : Op ,
86
- rhs : CovTerm ,
87
- ) {
88
- debug_assert ! (
89
- expression_id. as_usize( ) < self . expressions. len( ) ,
90
- "expression_id {} is out of range for expressions.len() = {}
91
- for {:?}" ,
92
- expression_id. as_usize( ) ,
93
- self . expressions. len( ) ,
94
- self ,
95
- ) ;
96
-
97
- let expression = Expression { lhs, op, rhs } ;
98
- let slot = & mut self . expressions [ expression_id] ;
99
- match slot {
100
- None => * slot = Some ( expression) ,
101
- // If this expression ID slot has already been filled, it should
102
- // contain identical information.
103
- Some ( ref previous_expression) => assert_eq ! (
104
- previous_expression, & expression,
105
- "add_counter_expression: expression for id changed"
106
- ) ,
107
- }
86
+ pub ( crate ) fn add_counter_expression ( & mut self , expression_id : ExpressionId ) {
87
+ self . expressions_not_seen . remove ( expression_id) ;
108
88
}
109
89
110
90
/// Identify expressions that will always have a value of zero, and note
@@ -125,13 +105,13 @@ impl<'tcx> FunctionCoverage<'tcx> {
125
105
// and then update the set of always-zero expressions if necessary.
126
106
// (By construction, expressions can only refer to other expressions
127
107
// that have lower IDs, so one pass is sufficient.)
128
- for ( id, maybe_expression ) in self . expressions . iter_enumerated ( ) {
129
- let Some ( expression ) = maybe_expression else {
130
- // If an expression is missing , it must have been optimized away,
108
+ for ( id, expression ) in self . function_coverage_info . expressions . iter_enumerated ( ) {
109
+ if self . expressions_not_seen . contains ( id ) {
110
+ // If an expression was not seen , it must have been optimized away,
131
111
// so any operand that refers to it can be replaced with zero.
132
112
zero_expressions. insert ( id) ;
133
113
continue ;
134
- } ;
114
+ }
135
115
136
116
// We don't need to simplify the actual expression data in the
137
117
// expressions list; we can just simplify a temporary copy and then
@@ -184,7 +164,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
184
164
// Expression IDs are indices into `self.expressions`, and on the LLVM
185
165
// side they will be treated as indices into `counter_expressions`, so
186
166
// the two vectors should correspond 1:1.
187
- assert_eq ! ( self . expressions. len( ) , counter_expressions. len( ) ) ;
167
+ assert_eq ! ( self . function_coverage_info . expressions. len( ) , counter_expressions. len( ) ) ;
188
168
189
169
let counter_regions = self . counter_regions ( zero_expressions) ;
190
170
@@ -204,17 +184,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
204
184
_ => Counter :: from_term ( operand) ,
205
185
} ;
206
186
207
- self . expressions
208
- . iter ( )
209
- . map ( |expression| match expression {
210
- None => {
187
+ self . function_coverage_info
188
+ . expressions
189
+ . iter_enumerated ( )
190
+ . map ( |( id, & Expression { lhs, op, rhs } ) | {
191
+ if self . expressions_not_seen . contains ( id) {
211
192
// This expression ID was allocated, but we never saw the
212
193
// actual expression, so it must have been optimized out.
213
194
// Replace it with a dummy expression, and let LLVM take
214
195
// care of omitting it from the expression list.
215
196
CounterExpression :: DUMMY
216
- }
217
- & Some ( Expression { lhs, op, rhs, .. } ) => {
197
+ } else {
218
198
// Convert the operands and operator as normal.
219
199
CounterExpression :: new (
220
200
counter_from_operand ( lhs) ,
0 commit comments