@@ -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
/// Tracks which expressions are known to always have a value of zero.
31
25
/// Only updated during the finalize step.
32
26
zero_expressions : FxIndexSet < ExpressionId > ,
@@ -55,16 +49,27 @@ impl<'tcx> FunctionCoverage<'tcx> {
55
49
is_used : bool ,
56
50
) -> Self {
57
51
let num_counters = function_coverage_info. num_counters ;
58
- let num_expressions = function_coverage_info. num_expressions ;
52
+ let num_expressions = function_coverage_info. expressions . len ( ) ;
59
53
debug ! (
60
54
"FunctionCoverage::create(instance={instance:?}) has \
61
55
num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}"
62
56
) ;
57
+
58
+ // Every expression that has an associated mapping should have a
59
+ // corresponding statement in MIR. If we don't see that statement, we
60
+ // know that it was removed by MIR optimizations.
61
+ let mut expressions_not_seen = BitSet :: new_empty ( num_expressions) ;
62
+ for Mapping { term, .. } in & function_coverage_info. mappings {
63
+ if let & CovTerm :: Expression ( id) = term {
64
+ expressions_not_seen. insert ( id) ;
65
+ }
66
+ }
67
+
63
68
Self {
64
69
function_coverage_info,
65
70
is_used,
66
71
counters_seen : BitSet :: new_empty ( num_counters) ,
67
- expressions : IndexVec :: from_elem_n ( None , num_expressions ) ,
72
+ expressions_not_seen ,
68
73
zero_expressions : FxIndexSet :: default ( ) ,
69
74
}
70
75
}
@@ -80,35 +85,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
80
85
self . counters_seen . insert ( id) ;
81
86
}
82
87
83
- /// Adds information about a coverage expression .
88
+ /// Marks an expression ID as having been seen .
84
89
#[ instrument( level = "debug" , skip( self ) ) ]
85
- pub ( crate ) fn add_counter_expression (
86
- & mut self ,
87
- expression_id : ExpressionId ,
88
- lhs : CovTerm ,
89
- op : Op ,
90
- rhs : CovTerm ,
91
- ) {
92
- debug_assert ! (
93
- expression_id. as_usize( ) < self . expressions. len( ) ,
94
- "expression_id {} is out of range for expressions.len() = {}
95
- for {:?}" ,
96
- expression_id. as_usize( ) ,
97
- self . expressions. len( ) ,
98
- self ,
99
- ) ;
100
-
101
- let expression = Expression { lhs, op, rhs } ;
102
- let slot = & mut self . expressions [ expression_id] ;
103
- match slot {
104
- None => * slot = Some ( expression) ,
105
- // If this expression ID slot has already been filled, it should
106
- // contain identical information.
107
- Some ( ref previous_expression) => assert_eq ! (
108
- previous_expression, & expression,
109
- "add_counter_expression: expression for id changed"
110
- ) ,
111
- }
90
+ pub ( crate ) fn add_counter_expression ( & mut self , expression_id : ExpressionId ) {
91
+ self . expressions_not_seen . remove ( expression_id) ;
112
92
}
113
93
114
94
pub ( crate ) fn finalize ( & mut self ) {
@@ -133,13 +113,13 @@ impl<'tcx> FunctionCoverage<'tcx> {
133
113
// and then update the set of always-zero expressions if necessary.
134
114
// (By construction, expressions can only refer to other expressions
135
115
// that have lower IDs, so one pass is sufficient.)
136
- for ( id, maybe_expression ) in self . expressions . iter_enumerated ( ) {
137
- let Some ( expression ) = maybe_expression else {
138
- // If an expression is missing , it must have been optimized away,
116
+ for ( id, expression ) in self . function_coverage_info . expressions . iter_enumerated ( ) {
117
+ if self . expressions_not_seen . contains ( id ) {
118
+ // If an expression was not seen , it must have been optimized away,
139
119
// so any operand that refers to it can be replaced with zero.
140
120
zero_expressions. insert ( id) ;
141
121
continue ;
142
- } ;
122
+ }
143
123
144
124
// We don't need to simplify the actual expression data in the
145
125
// expressions list; we can just simplify a temporary copy and then
@@ -191,17 +171,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
191
171
_ => Counter :: from_term ( operand) ,
192
172
} ;
193
173
194
- self . expressions
195
- . iter ( )
196
- . map ( |expression| match expression {
197
- None => {
174
+ self . function_coverage_info
175
+ . expressions
176
+ . iter_enumerated ( )
177
+ . map ( |( id, & Expression { lhs, op, rhs } ) | {
178
+ if self . expressions_not_seen . contains ( id) {
198
179
// This expression ID was allocated, but we never saw the
199
180
// actual expression, so it must have been optimized out.
200
181
// Replace it with a dummy expression, and let LLVM take
201
182
// care of omitting it from the expression list.
202
183
CounterExpression :: DUMMY
203
- }
204
- & Some ( Expression { lhs, op, rhs, .. } ) => {
184
+ } else {
205
185
// Convert the operands and operator as normal.
206
186
CounterExpression :: new (
207
187
counter_from_operand ( lhs) ,
0 commit comments