@@ -11,7 +11,7 @@ pub struct Expression {
1111 lhs : Operand ,
1212 op : Op ,
1313 rhs : Operand ,
14- region : Option < CodeRegion > ,
14+ code_regions : Vec < CodeRegion > ,
1515}
1616
1717/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
@@ -30,7 +30,7 @@ pub struct FunctionCoverage<'tcx> {
3030 instance : Instance < ' tcx > ,
3131 source_hash : u64 ,
3232 is_used : bool ,
33- counters : IndexVec < CounterId , Option < CodeRegion > > ,
33+ counters : IndexVec < CounterId , Option < Vec < CodeRegion > > > ,
3434 expressions : IndexVec < ExpressionId , Option < Expression > > ,
3535 unreachable_regions : Vec < CodeRegion > ,
3636}
@@ -77,28 +77,40 @@ impl<'tcx> FunctionCoverage<'tcx> {
7777 }
7878 }
7979
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+ ) ,
8496 }
8597 }
8698
99+ /// Adds information about a coverage expression, along with zero or more
100+ /// code regions mapped to that expression.
101+ ///
87102 /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
88103 /// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity
89104 /// 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 (
91107 & mut self ,
92108 expression_id : ExpressionId ,
93109 lhs : Operand ,
94110 op : Op ,
95111 rhs : Operand ,
96- region : Option < CodeRegion > ,
112+ code_regions : & [ CodeRegion ] ,
97113 ) {
98- debug ! (
99- "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}" ,
100- expression_id, lhs, op, rhs, region
101- ) ;
102114 debug_assert ! (
103115 expression_id. as_usize( ) < self . expressions. len( ) ,
104116 "expression_id {} is out of range for expressions.len() = {}
@@ -107,23 +119,25 @@ impl<'tcx> FunctionCoverage<'tcx> {
107119 self . expressions. len( ) ,
108120 self ,
109121 ) ;
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 ,
119131 "add_counter_expression: expression for id changed"
120- ) ;
132+ ) ,
121133 }
122134 }
123135
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) ;
127141 }
128142
129143 /// Perform some simplifications to make the final coverage mappings
@@ -212,11 +226,16 @@ impl<'tcx> FunctionCoverage<'tcx> {
212226 }
213227
214228 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+ } )
220239 }
221240
222241 /// Convert this function's coverage expression data into a form that can be
@@ -254,13 +273,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
254273
255274 fn expression_regions ( & self ) -> Vec < ( Counter , & CodeRegion ) > {
256275 // 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 .
259278 self . expressions
260279 . 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) )
264287 } )
265288 . collect :: < Vec < _ > > ( )
266289 }
0 commit comments