@@ -11,7 +11,7 @@ pub struct Expression {
11
11
lhs : Operand ,
12
12
op : Op ,
13
13
rhs : Operand ,
14
- region : Option < CodeRegion > ,
14
+ code_regions : Vec < CodeRegion > ,
15
15
}
16
16
17
17
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
@@ -30,7 +30,7 @@ pub struct FunctionCoverage<'tcx> {
30
30
instance : Instance < ' tcx > ,
31
31
source_hash : u64 ,
32
32
is_used : bool ,
33
- counters : IndexVec < CounterId , Option < CodeRegion > > ,
33
+ counters : IndexVec < CounterId , Option < Vec < CodeRegion > > > ,
34
34
expressions : IndexVec < ExpressionId , Option < Expression > > ,
35
35
unreachable_regions : Vec < CodeRegion > ,
36
36
}
@@ -77,28 +77,40 @@ impl<'tcx> FunctionCoverage<'tcx> {
77
77
}
78
78
}
79
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" ) ;
80
+ /// Adds code regions to be counted by an injected counter intrinsic.
81
+ #[ instrument( level = "debug" , skip( self ) ) ]
82
+ pub ( crate ) fn add_counter ( & mut self , id : CounterId , code_regions : & [ CodeRegion ] ) {
83
+ if code_regions. is_empty ( ) {
84
+ return ;
85
+ }
86
+
87
+ let slot = & mut self . counters [ id] ;
88
+ match slot {
89
+ None => * slot = Some ( code_regions. to_owned ( ) ) ,
90
+ // If this counter ID slot has already been filled, it should
91
+ // contain identical information.
92
+ Some ( ref previous_regions) => assert_eq ! (
93
+ previous_regions, code_regions,
94
+ "add_counter: code regions for id changed"
95
+ ) ,
84
96
}
85
97
}
86
98
99
+ /// Adds information about a coverage expression, along with zero or more
100
+ /// code regions mapped to that expression.
101
+ ///
87
102
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
88
103
/// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity
89
104
/// between operands that are counter IDs and operands that are expression IDs.
90
- pub fn add_counter_expression (
105
+ #[ instrument( level = "debug" , skip( self ) ) ]
106
+ pub ( crate ) fn add_counter_expression (
91
107
& mut self ,
92
108
expression_id : ExpressionId ,
93
109
lhs : Operand ,
94
110
op : Op ,
95
111
rhs : Operand ,
96
- region : Option < CodeRegion > ,
112
+ code_regions : & [ CodeRegion ] ,
97
113
) {
98
- debug ! (
99
- "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}" ,
100
- expression_id, lhs, op, rhs, region
101
- ) ;
102
114
debug_assert ! (
103
115
expression_id. as_usize( ) < self . expressions. len( ) ,
104
116
"expression_id {} is out of range for expressions.len() = {}
@@ -107,23 +119,25 @@ impl<'tcx> FunctionCoverage<'tcx> {
107
119
self . expressions. len( ) ,
108
120
self ,
109
121
) ;
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 } ,
122
+
123
+ let expression = Expression { lhs, op , rhs , code_regions : code_regions . to_owned ( ) } ;
124
+ let slot = & mut self . expressions [ expression_id ] ;
125
+ match slot {
126
+ None => * slot = Some ( expression ) ,
127
+ // If this expression ID slot has already been filled, it should
128
+ // contain identical information.
129
+ Some ( ref previous_expression ) => assert_eq ! (
130
+ previous_expression , & expression ,
119
131
"add_counter_expression: expression for id changed"
120
- ) ;
132
+ ) ,
121
133
}
122
134
}
123
135
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)
136
+ /// Adds regions that will be marked as "unreachable", with a constant "zero counter".
137
+ #[ instrument( level = "debug" , skip( self ) ) ]
138
+ pub ( crate ) fn add_unreachable_regions ( & mut self , code_regions : & [ CodeRegion ] ) {
139
+ assert ! ( !code_regions. is_empty( ) , "unreachable regions always have code regions" ) ;
140
+ self . unreachable_regions . extend_from_slice ( code_regions) ;
127
141
}
128
142
129
143
/// Perform some simplifications to make the final coverage mappings
@@ -212,11 +226,16 @@ impl<'tcx> FunctionCoverage<'tcx> {
212
226
}
213
227
214
228
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
- } )
229
+ self . counters
230
+ . iter_enumerated ( )
231
+ // Filter out counter IDs that we never saw during MIR traversal.
232
+ // This can happen if a counter was optimized out by MIR transforms
233
+ // (and replaced with `CoverageKind::Unreachable` instead).
234
+ . filter_map ( |( id, maybe_code_regions) | Some ( ( id, maybe_code_regions. as_ref ( ) ?) ) )
235
+ . flat_map ( |( id, code_regions) | {
236
+ let counter = Counter :: counter_value_reference ( id) ;
237
+ code_regions. iter ( ) . map ( move |region| ( counter, region) )
238
+ } )
220
239
}
221
240
222
241
/// Convert this function's coverage expression data into a form that can be
@@ -254,13 +273,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
254
273
255
274
fn expression_regions ( & self ) -> Vec < ( Counter , & CodeRegion ) > {
256
275
// 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 .
276
+ // one or more attached code regions , and return the corresponding
277
+ // mappings as counter/region pairs .
259
278
self . expressions
260
279
. iter_enumerated ( )
261
- . filter_map ( |( id, expression) | {
262
- let code_region = expression. as_ref ( ) ?. region . as_ref ( ) ?;
263
- Some ( ( Counter :: expression ( id) , code_region) )
280
+ . filter_map ( |( id, maybe_expression) | {
281
+ let code_regions = & maybe_expression. as_ref ( ) ?. code_regions ;
282
+ Some ( ( id, code_regions) )
283
+ } )
284
+ . flat_map ( |( id, code_regions) | {
285
+ let counter = Counter :: expression ( id) ;
286
+ code_regions. iter ( ) . map ( move |code_region| ( counter, code_region) )
264
287
} )
265
288
. collect :: < Vec < _ > > ( )
266
289
}
0 commit comments