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
- use rustc_middle:: mir:: coverage:: { CodeRegion , CounterId , CovTerm , ExpressionId , Op } ;
6
+ use rustc_middle:: mir:: coverage:: { CodeRegion , CounterId , CovTerm , ExpressionId , Mapping , Op } ;
6
7
use rustc_middle:: ty:: Instance ;
7
8
use rustc_middle:: ty:: TyCtxt ;
8
9
@@ -11,28 +12,21 @@ pub struct Expression {
11
12
lhs : CovTerm ,
12
13
op : Op ,
13
14
rhs : CovTerm ,
14
- code_regions : Vec < CodeRegion > ,
15
15
}
16
16
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.
21
- ///
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
27
- /// line."
17
+ /// Holds all of the coverage mapping data associated with a function instance,
18
+ /// collected during traversal of `Coverage` statements in the function's MIR.
28
19
#[ derive( Debug ) ]
29
20
pub struct FunctionCoverage < ' tcx > {
30
21
instance : Instance < ' tcx > ,
31
22
source_hash : u64 ,
32
23
is_used : bool ,
33
- counters : IndexVec < CounterId , Option < Vec < CodeRegion > > > ,
24
+
25
+ /// Tracks which counters have been seen, to avoid duplicate mappings
26
+ /// that might be introduced by MIR inlining.
27
+ counters_seen : BitSet < CounterId > ,
34
28
expressions : IndexVec < ExpressionId , Option < Expression > > ,
35
- unreachable_regions : Vec < CodeRegion > ,
29
+ mappings : Vec < Mapping > ,
36
30
}
37
31
38
32
impl < ' tcx > FunctionCoverage < ' tcx > {
@@ -56,9 +50,9 @@ impl<'tcx> FunctionCoverage<'tcx> {
56
50
instance,
57
51
source_hash : 0 , // will be set with the first `add_counter()`
58
52
is_used,
59
- counters : IndexVec :: from_elem_n ( None , coverageinfo. num_counters as usize ) ,
53
+ counters_seen : BitSet :: new_empty ( coverageinfo. num_counters as usize ) ,
60
54
expressions : IndexVec :: from_elem_n ( None , coverageinfo. num_expressions as usize ) ,
61
- unreachable_regions : Vec :: new ( ) ,
55
+ mappings : Vec :: new ( ) ,
62
56
}
63
57
}
64
58
@@ -80,19 +74,8 @@ impl<'tcx> FunctionCoverage<'tcx> {
80
74
/// Adds code regions to be counted by an injected counter intrinsic.
81
75
#[ instrument( level = "debug" , skip( self ) ) ]
82
76
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
- ) ,
77
+ if self . counters_seen . insert ( id) {
78
+ self . add_mappings ( CovTerm :: Counter ( id) , code_regions) ;
96
79
}
97
80
}
98
81
@@ -120,10 +103,13 @@ impl<'tcx> FunctionCoverage<'tcx> {
120
103
self ,
121
104
) ;
122
105
123
- let expression = Expression { lhs, op, rhs, code_regions : code_regions . to_owned ( ) } ;
106
+ let expression = Expression { lhs, op, rhs } ;
124
107
let slot = & mut self . expressions [ expression_id] ;
125
108
match slot {
126
- None => * slot = Some ( expression) ,
109
+ None => {
110
+ * slot = Some ( expression) ;
111
+ self . add_mappings ( CovTerm :: Expression ( expression_id) , code_regions) ;
112
+ }
127
113
// If this expression ID slot has already been filled, it should
128
114
// contain identical information.
129
115
Some ( ref previous_expression) => assert_eq ! (
@@ -137,7 +123,22 @@ impl<'tcx> FunctionCoverage<'tcx> {
137
123
#[ instrument( level = "debug" , skip( self ) ) ]
138
124
pub ( crate ) fn add_unreachable_regions ( & mut self , code_regions : & [ CodeRegion ] ) {
139
125
assert ! ( !code_regions. is_empty( ) , "unreachable regions always have code regions" ) ;
140
- self . unreachable_regions . extend_from_slice ( code_regions) ;
126
+ self . add_mappings ( CovTerm :: Zero , code_regions) ;
127
+ }
128
+
129
+ #[ instrument( level = "debug" , skip( self ) ) ]
130
+ fn add_mappings ( & mut self , kind : CovTerm , code_regions : & [ CodeRegion ] ) {
131
+ self . mappings
132
+ . extend ( code_regions. iter ( ) . cloned ( ) . map ( |code_region| Mapping { kind, code_region } ) ) ;
133
+ }
134
+
135
+ pub ( crate ) fn finalize ( & mut self ) {
136
+ self . assert_source_hash_is_set ( ) ;
137
+
138
+ self . simplify_expressions ( ) ;
139
+
140
+ // Sort all of the collected mappings into a predictable order.
141
+ self . mappings . sort_unstable ( ) ;
141
142
}
142
143
143
144
/// Perform some simplifications to make the final coverage mappings
@@ -146,7 +147,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
146
147
/// This method mainly exists to preserve the simplifications that were
147
148
/// already being performed by the Rust-side expression renumbering, so that
148
149
/// the resulting coverage mappings don't get worse.
149
- pub ( crate ) fn simplify_expressions ( & mut self ) {
150
+ fn simplify_expressions ( & mut self ) {
150
151
// The set of expressions that either were optimized out entirely, or
151
152
// have zero as both of their operands, and will therefore always have
152
153
// a value of zero. Other expressions that refer to these as operands
@@ -198,49 +199,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
198
199
self . source_hash
199
200
}
200
201
201
- /// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their
202
- /// associated `Regions` (from which the LLVM-specific `CoverageMapGenerator` will create
203
- /// `CounterMappingRegion`s.
204
- pub fn get_expressions_and_counter_regions (
205
- & self ,
206
- ) -> ( Vec < CounterExpression > , impl Iterator < Item = ( Counter , & CodeRegion ) > ) {
202
+ fn assert_source_hash_is_set ( & self ) {
207
203
assert ! (
208
204
self . source_hash != 0 || !self . is_used,
209
205
"No counters provided the source_hash for used function: {:?}" ,
210
206
self . instance
211
207
) ;
212
-
213
- let counter_expressions = self . counter_expressions ( ) ;
214
- // Expression IDs are indices into `self.expressions`, and on the LLVM
215
- // side they will be treated as indices into `counter_expressions`, so
216
- // the two vectors should correspond 1:1.
217
- assert_eq ! ( self . expressions. len( ) , counter_expressions. len( ) ) ;
218
-
219
- let counter_regions = self . counter_regions ( ) ;
220
- let expression_regions = self . expression_regions ( ) ;
221
- let unreachable_regions = self . unreachable_regions ( ) ;
222
-
223
- let counter_regions =
224
- counter_regions. chain ( expression_regions. into_iter ( ) . chain ( unreachable_regions) ) ;
225
- ( counter_expressions, counter_regions)
226
- }
227
-
228
- fn counter_regions ( & self ) -> impl Iterator < Item = ( Counter , & CodeRegion ) > {
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
- } )
239
208
}
240
209
241
210
/// Convert this function's coverage expression data into a form that can be
242
211
/// passed through FFI to LLVM.
243
- fn counter_expressions ( & self ) -> Vec < CounterExpression > {
212
+ pub ( crate ) fn expressions_for_ffi ( & self ) -> Vec < CounterExpression > {
244
213
// We know that LLVM will optimize out any unused expressions before
245
214
// producing the final coverage map, so there's no need to do the same
246
215
// thing on the Rust side unless we're confident we can do much better.
@@ -268,24 +237,12 @@ impl<'tcx> FunctionCoverage<'tcx> {
268
237
. collect :: < Vec < _ > > ( )
269
238
}
270
239
271
- fn expression_regions ( & self ) -> Vec < ( Counter , & CodeRegion ) > {
272
- // Find all of the expression IDs that weren't optimized out AND have
273
- // one or more attached code regions, and return the corresponding
274
- // mappings as counter/region pairs.
275
- self . expressions
276
- . iter_enumerated ( )
277
- . filter_map ( |( id, maybe_expression) | {
278
- let code_regions = & maybe_expression. as_ref ( ) ?. code_regions ;
279
- Some ( ( id, code_regions) )
280
- } )
281
- . flat_map ( |( id, code_regions) | {
282
- let counter = Counter :: expression ( id) ;
283
- code_regions. iter ( ) . map ( move |code_region| ( counter, code_region) )
284
- } )
285
- . collect :: < Vec < _ > > ( )
286
- }
287
-
288
- fn unreachable_regions ( & self ) -> impl Iterator < Item = ( Counter , & CodeRegion ) > {
289
- self . unreachable_regions . iter ( ) . map ( |region| ( Counter :: ZERO , region) )
240
+ /// Converts this function's coverage mappings into an intermediate form
241
+ /// that will be used by `mapgen` when preparing for FFI.
242
+ pub ( crate ) fn mappings_for_ffi ( & self ) -> impl Iterator < Item = ( Counter , & CodeRegion ) > {
243
+ self . mappings . iter ( ) . map ( |& Mapping { kind, ref code_region } | {
244
+ let counter = Counter :: from_term ( kind) ;
245
+ ( counter, code_region)
246
+ } )
290
247
}
291
248
}
0 commit comments