11use crate :: coverageinfo:: ffi:: { Counter , CounterExpression , ExprKind } ;
22
33use rustc_data_structures:: fx:: FxIndexSet ;
4+ use rustc_index:: bit_set:: BitSet ;
45use rustc_index:: IndexVec ;
56use rustc_middle:: mir:: coverage:: {
6- CodeRegion , CounterId , CovTerm , ExpressionId , FunctionCoverageInfo , Op ,
7+ CodeRegion , CounterId , CovTerm , ExpressionId , FunctionCoverageInfo , Mapping , Op ,
78} ;
89use rustc_middle:: ty:: Instance ;
910
@@ -12,28 +13,21 @@ pub struct Expression {
1213 lhs : CovTerm ,
1314 op : Op ,
1415 rhs : CovTerm ,
15- code_regions : Vec < CodeRegion > ,
1616}
1717
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.
2920#[ derive( Debug ) ]
3021pub struct FunctionCoverage < ' tcx > {
3122 /// Coverage info that was attached to this function by the instrumentor.
3223 function_coverage_info : & ' tcx FunctionCoverageInfo ,
3324 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 > ,
3529 expressions : IndexVec < ExpressionId , Option < Expression > > ,
36- unreachable_regions : Vec < CodeRegion > ,
30+ mappings : Vec < Mapping > ,
3731}
3832
3933impl < ' tcx > FunctionCoverage < ' tcx > {
@@ -67,9 +61,9 @@ impl<'tcx> FunctionCoverage<'tcx> {
6761 Self {
6862 function_coverage_info,
6963 is_used,
70- counters : IndexVec :: from_elem_n ( None , num_counters) ,
64+ counters_seen : BitSet :: new_empty ( num_counters) ,
7165 expressions : IndexVec :: from_elem_n ( None , num_expressions) ,
72- unreachable_regions : Vec :: new ( ) ,
66+ mappings : Vec :: new ( ) ,
7367 }
7468 }
7569
@@ -81,19 +75,8 @@ impl<'tcx> FunctionCoverage<'tcx> {
8175 /// Adds code regions to be counted by an injected counter intrinsic.
8276 #[ instrument( level = "debug" , skip( self ) ) ]
8377 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) ;
9780 }
9881 }
9982
@@ -121,10 +104,13 @@ impl<'tcx> FunctionCoverage<'tcx> {
121104 self ,
122105 ) ;
123106
124- let expression = Expression { lhs, op, rhs, code_regions : code_regions . to_owned ( ) } ;
107+ let expression = Expression { lhs, op, rhs } ;
125108 let slot = & mut self . expressions [ expression_id] ;
126109 match slot {
127- None => * slot = Some ( expression) ,
110+ None => {
111+ * slot = Some ( expression) ;
112+ self . add_mappings ( CovTerm :: Expression ( expression_id) , code_regions) ;
113+ }
128114 // If this expression ID slot has already been filled, it should
129115 // contain identical information.
130116 Some ( ref previous_expression) => assert_eq ! (
@@ -138,7 +124,25 @@ impl<'tcx> FunctionCoverage<'tcx> {
138124 #[ instrument( level = "debug" , skip( self ) ) ]
139125 pub ( crate ) fn add_unreachable_regions ( & mut self , code_regions : & [ CodeRegion ] ) {
140126 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+ // Reorder the collected mappings so that counter mappings are first and
140+ // zero mappings are last, matching the historical order.
141+ self . mappings . sort_by_key ( |mapping| match mapping. term {
142+ CovTerm :: Counter ( _) => 0 ,
143+ CovTerm :: Expression ( _) => 1 ,
144+ CovTerm :: Zero => u8:: MAX ,
145+ } ) ;
142146 }
143147
144148 /// Perform some simplifications to make the final coverage mappings
@@ -147,7 +151,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
147151 /// This method mainly exists to preserve the simplifications that were
148152 /// already being performed by the Rust-side expression renumbering, so that
149153 /// the resulting coverage mappings don't get worse.
150- pub ( crate ) fn simplify_expressions ( & mut self ) {
154+ fn simplify_expressions ( & mut self ) {
151155 // The set of expressions that either were optimized out entirely, or
152156 // have zero as both of their operands, and will therefore always have
153157 // a value of zero. Other expressions that refer to these as operands
@@ -212,27 +216,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
212216 assert_eq ! ( self . expressions. len( ) , counter_expressions. len( ) ) ;
213217
214218 let counter_regions = self . counter_regions ( ) ;
215- let expression_regions = self . expression_regions ( ) ;
216- let unreachable_regions = self . unreachable_regions ( ) ;
217219
218- let counter_regions =
219- counter_regions. chain ( expression_regions. into_iter ( ) . chain ( unreachable_regions) ) ;
220220 ( counter_expressions, counter_regions)
221221 }
222222
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-
236223 /// Convert this function's coverage expression data into a form that can be
237224 /// passed through FFI to LLVM.
238225 fn counter_expressions ( & self ) -> Vec < CounterExpression > {
@@ -266,24 +253,12 @@ impl<'tcx> FunctionCoverage<'tcx> {
266253 . collect :: < Vec < _ > > ( )
267254 }
268255
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) )
256+ /// Converts this function's coverage mappings into an intermediate form
257+ /// that will be used by `mapgen` when preparing for FFI.
258+ fn counter_regions ( & self ) -> impl Iterator < Item = ( Counter , & CodeRegion ) > {
259+ self . mappings . iter ( ) . map ( |& Mapping { term, ref code_region } | {
260+ let counter = Counter :: from_term ( term) ;
261+ ( counter, code_region)
262+ } )
288263 }
289264}
0 commit comments