1
1
use crate :: coverageinfo:: ffi:: { Counter , CounterExpression , ExprKind } ;
2
2
3
3
use rustc_data_structures:: fx:: FxIndexSet ;
4
+ use rustc_index:: bit_set:: BitSet ;
4
5
use rustc_index:: IndexVec ;
5
6
use rustc_middle:: mir:: coverage:: {
6
- CodeRegion , CounterId , CovTerm , ExpressionId , FunctionCoverageInfo , Op ,
7
+ CodeRegion , CounterId , CovTerm , ExpressionId , FunctionCoverageInfo , Mapping , Op ,
7
8
} ;
8
9
use rustc_middle:: ty:: Instance ;
9
10
@@ -12,28 +13,21 @@ pub struct Expression {
12
13
lhs : CovTerm ,
13
14
op : Op ,
14
15
rhs : CovTerm ,
15
- code_regions : Vec < CodeRegion > ,
16
16
}
17
17
18
- /// Collects all of the coverage regions associated with (a) injected counters, (b) counter
19
- /// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
20
- /// for a given Function. This struct also stores the `function_source_hash`,
21
- /// computed during instrumentation, and forwarded with counters.
22
- ///
23
- /// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap
24
- /// regions" (or "gap areas"). A gap region is a code region within a counted region (either counter
25
- /// or expression), but the line or lines in the gap region are not executable (such as lines with
26
- /// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
27
- /// for a gap area is only used as the line execution count if there are no other regions on a
28
- /// line."
18
+ /// Holds all of the coverage mapping data associated with a function instance,
19
+ /// collected during traversal of `Coverage` statements in the function's MIR.
29
20
#[ derive( Debug ) ]
30
21
pub struct FunctionCoverage < ' tcx > {
31
22
/// Coverage info that was attached to this function by the instrumentor.
32
23
function_coverage_info : & ' tcx FunctionCoverageInfo ,
33
24
is_used : bool ,
34
- counters : IndexVec < CounterId , Option < Vec < CodeRegion > > > ,
25
+
26
+ /// Tracks which counters have been seen, to avoid duplicate mappings
27
+ /// that might be introduced by MIR inlining.
28
+ counters_seen : BitSet < CounterId > ,
35
29
expressions : IndexVec < ExpressionId , Option < Expression > > ,
36
- unreachable_regions : Vec < CodeRegion > ,
30
+ mappings : Vec < Mapping > ,
37
31
}
38
32
39
33
impl < ' tcx > FunctionCoverage < ' tcx > {
@@ -67,9 +61,9 @@ impl<'tcx> FunctionCoverage<'tcx> {
67
61
Self {
68
62
function_coverage_info,
69
63
is_used,
70
- counters : IndexVec :: from_elem_n ( None , num_counters) ,
64
+ counters_seen : BitSet :: new_empty ( num_counters) ,
71
65
expressions : IndexVec :: from_elem_n ( None , num_expressions) ,
72
- unreachable_regions : Vec :: new ( ) ,
66
+ mappings : Vec :: new ( ) ,
73
67
}
74
68
}
75
69
@@ -81,19 +75,8 @@ impl<'tcx> FunctionCoverage<'tcx> {
81
75
/// Adds code regions to be counted by an injected counter intrinsic.
82
76
#[ instrument( level = "debug" , skip( self ) ) ]
83
77
pub ( crate ) fn add_counter ( & mut self , id : CounterId , code_regions : & [ CodeRegion ] ) {
84
- if code_regions. is_empty ( ) {
85
- return ;
86
- }
87
-
88
- let slot = & mut self . counters [ id] ;
89
- match slot {
90
- None => * slot = Some ( code_regions. to_owned ( ) ) ,
91
- // If this counter ID slot has already been filled, it should
92
- // contain identical information.
93
- Some ( ref previous_regions) => assert_eq ! (
94
- previous_regions, code_regions,
95
- "add_counter: code regions for id changed"
96
- ) ,
78
+ if self . counters_seen . insert ( id) {
79
+ self . add_mappings ( CovTerm :: Counter ( id) , code_regions) ;
97
80
}
98
81
}
99
82
@@ -121,10 +104,13 @@ impl<'tcx> FunctionCoverage<'tcx> {
121
104
self ,
122
105
) ;
123
106
124
- let expression = Expression { lhs, op, rhs, code_regions : code_regions . to_owned ( ) } ;
107
+ let expression = Expression { lhs, op, rhs } ;
125
108
let slot = & mut self . expressions [ expression_id] ;
126
109
match slot {
127
- None => * slot = Some ( expression) ,
110
+ None => {
111
+ * slot = Some ( expression) ;
112
+ self . add_mappings ( CovTerm :: Expression ( expression_id) , code_regions) ;
113
+ }
128
114
// If this expression ID slot has already been filled, it should
129
115
// contain identical information.
130
116
Some ( ref previous_expression) => assert_eq ! (
@@ -138,7 +124,21 @@ impl<'tcx> FunctionCoverage<'tcx> {
138
124
#[ instrument( level = "debug" , skip( self ) ) ]
139
125
pub ( crate ) fn add_unreachable_regions ( & mut self , code_regions : & [ CodeRegion ] ) {
140
126
assert ! ( !code_regions. is_empty( ) , "unreachable regions always have code regions" ) ;
141
- self . unreachable_regions . extend_from_slice ( code_regions) ;
127
+ self . add_mappings ( CovTerm :: Zero , code_regions) ;
128
+ }
129
+
130
+ #[ instrument( level = "debug" , skip( self ) ) ]
131
+ fn add_mappings ( & mut self , term : CovTerm , code_regions : & [ CodeRegion ] ) {
132
+ self . mappings
133
+ . extend ( code_regions. iter ( ) . cloned ( ) . map ( |code_region| Mapping { term, code_region } ) ) ;
134
+ }
135
+
136
+ pub ( crate ) fn finalize ( & mut self ) {
137
+ self . simplify_expressions ( ) ;
138
+
139
+ // Sort all of the collected mappings into a predictable order.
140
+ // (Mappings have a total order, so an unstable sort should be fine.)
141
+ self . mappings . sort_unstable ( ) ;
142
142
}
143
143
144
144
/// Perform some simplifications to make the final coverage mappings
@@ -147,7 +147,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
147
147
/// This method mainly exists to preserve the simplifications that were
148
148
/// already being performed by the Rust-side expression renumbering, so that
149
149
/// the resulting coverage mappings don't get worse.
150
- pub ( crate ) fn simplify_expressions ( & mut self ) {
150
+ fn simplify_expressions ( & mut self ) {
151
151
// The set of expressions that either were optimized out entirely, or
152
152
// have zero as both of their operands, and will therefore always have
153
153
// a value of zero. Other expressions that refer to these as operands
@@ -199,43 +199,9 @@ impl<'tcx> FunctionCoverage<'tcx> {
199
199
if self . is_used { self . function_coverage_info . function_source_hash } else { 0 }
200
200
}
201
201
202
- /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their
203
- /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
204
- /// `CounterMappingRegion`s.
205
- pub fn get_expressions_and_counter_regions (
206
- & self ,
207
- ) -> ( Vec < CounterExpression > , impl Iterator < Item = ( Counter , & CodeRegion ) > ) {
208
- let counter_expressions = self . counter_expressions ( ) ;
209
- // Expression IDs are indices into `self.expressions`, and on the LLVM
210
- // side they will be treated as indices into `counter_expressions`, so
211
- // the two vectors should correspond 1:1.
212
- assert_eq ! ( self . expressions. len( ) , counter_expressions. len( ) ) ;
213
-
214
- let counter_regions = self . counter_regions ( ) ;
215
- let expression_regions = self . expression_regions ( ) ;
216
- let unreachable_regions = self . unreachable_regions ( ) ;
217
-
218
- let counter_regions =
219
- counter_regions. chain ( expression_regions. into_iter ( ) . chain ( unreachable_regions) ) ;
220
- ( counter_expressions, counter_regions)
221
- }
222
-
223
- fn counter_regions ( & self ) -> impl Iterator < Item = ( Counter , & CodeRegion ) > {
224
- self . counters
225
- . iter_enumerated ( )
226
- // Filter out counter IDs that we never saw during MIR traversal.
227
- // This can happen if a counter was optimized out by MIR transforms
228
- // (and replaced with `CoverageKind::Unreachable` instead).
229
- . filter_map ( |( id, maybe_code_regions) | Some ( ( id, maybe_code_regions. as_ref ( ) ?) ) )
230
- . flat_map ( |( id, code_regions) | {
231
- let counter = Counter :: counter_value_reference ( id) ;
232
- code_regions. iter ( ) . map ( move |region| ( counter, region) )
233
- } )
234
- }
235
-
236
202
/// Convert this function's coverage expression data into a form that can be
237
203
/// passed through FFI to LLVM.
238
- fn counter_expressions ( & self ) -> Vec < CounterExpression > {
204
+ pub ( crate ) fn expressions_for_ffi ( & self ) -> Vec < CounterExpression > {
239
205
// We know that LLVM will optimize out any unused expressions before
240
206
// producing the final coverage map, so there's no need to do the same
241
207
// thing on the Rust side unless we're confident we can do much better.
@@ -266,24 +232,12 @@ impl<'tcx> FunctionCoverage<'tcx> {
266
232
. collect :: < Vec < _ > > ( )
267
233
}
268
234
269
- fn expression_regions ( & self ) -> Vec < ( Counter , & CodeRegion ) > {
270
- // Find all of the expression IDs that weren't optimized out AND have
271
- // one or more attached code regions, and return the corresponding
272
- // mappings as counter/region pairs.
273
- self . expressions
274
- . iter_enumerated ( )
275
- . filter_map ( |( id, maybe_expression) | {
276
- let code_regions = & maybe_expression. as_ref ( ) ?. code_regions ;
277
- Some ( ( id, code_regions) )
278
- } )
279
- . flat_map ( |( id, code_regions) | {
280
- let counter = Counter :: expression ( id) ;
281
- code_regions. iter ( ) . map ( move |code_region| ( counter, code_region) )
282
- } )
283
- . collect :: < Vec < _ > > ( )
284
- }
285
-
286
- fn unreachable_regions ( & self ) -> impl Iterator < Item = ( Counter , & CodeRegion ) > {
287
- self . unreachable_regions . iter ( ) . map ( |region| ( Counter :: ZERO , region) )
235
+ /// Converts this function's coverage mappings into an intermediate form
236
+ /// that will be used by `mapgen` when preparing for FFI.
237
+ pub ( crate ) fn mappings_for_ffi ( & self ) -> impl Iterator < Item = ( Counter , & CodeRegion ) > {
238
+ self . mappings . iter ( ) . map ( |& Mapping { term, ref code_region } | {
239
+ let counter = Counter :: from_term ( term) ;
240
+ ( counter, code_region)
241
+ } )
288
242
}
289
243
}
0 commit comments