11use crate :: coverageinfo:: ffi:: { Counter , CounterExpression , ExprKind } ;
22
33use rustc_data_structures:: fx:: FxIndexSet ;
4- use rustc_index:: { IndexSlice , IndexVec } ;
5- use rustc_middle:: bug;
6- use rustc_middle:: mir:: coverage:: {
7- CodeRegion , CounterId , ExpressionId , MappedExpressionIndex , Op , Operand ,
8- } ;
4+ use rustc_index:: IndexVec ;
5+ use rustc_middle:: mir:: coverage:: { CodeRegion , CounterId , ExpressionId , Op , Operand } ;
96use rustc_middle:: ty:: Instance ;
107use rustc_middle:: ty:: TyCtxt ;
118
@@ -199,8 +196,14 @@ impl<'tcx> FunctionCoverage<'tcx> {
199196 self . instance
200197 ) ;
201198
199+ let counter_expressions = self . counter_expressions ( ) ;
200+ // Expression IDs are indices into `self.expressions`, and on the LLVM
201+ // side they will be treated as indices into `counter_expressions`, so
202+ // the two vectors should correspond 1:1.
203+ assert_eq ! ( self . expressions. len( ) , counter_expressions. len( ) ) ;
204+
202205 let counter_regions = self . counter_regions ( ) ;
203- let ( counter_expressions , expression_regions) = self . expressions_with_regions ( ) ;
206+ let expression_regions = self . expression_regions ( ) ;
204207 let unreachable_regions = self . unreachable_regions ( ) ;
205208
206209 let counter_regions =
@@ -216,146 +219,50 @@ impl<'tcx> FunctionCoverage<'tcx> {
216219 } )
217220 }
218221
219- fn expressions_with_regions (
220- & self ,
221- ) -> ( Vec < CounterExpression > , impl Iterator < Item = ( Counter , & CodeRegion ) > ) {
222- let mut counter_expressions = Vec :: with_capacity ( self . expressions . len ( ) ) ;
223- let mut expression_regions = Vec :: with_capacity ( self . expressions . len ( ) ) ;
224- let mut new_indexes = IndexVec :: from_elem_n ( None , self . expressions . len ( ) ) ;
225-
226- // This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or
227- // `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type
228- // and value.
229- //
230- // Expressions will be returned from this function in a sequential vector (array) of
231- // `CounterExpression`, so the expression IDs must be mapped from their original,
232- // potentially sparse set of indexes.
233- //
234- // An `Expression` as an operand will have already been encountered as an `Expression` with
235- // operands, so its new_index will already have been generated (as a 1-up index value).
236- // (If an `Expression` as an operand does not have a corresponding new_index, it was
237- // probably optimized out, after the expression was injected into the MIR, so it will
238- // get a `CounterKind::Zero` instead.)
239- //
240- // In other words, an `Expression`s at any given index can include other expressions as
241- // operands, but expression operands can only come from the subset of expressions having
242- // `expression_index`s lower than the referencing `Expression`. Therefore, it is
243- // reasonable to look up the new index of an expression operand while the `new_indexes`
244- // vector is only complete up to the current `ExpressionIndex`.
245- type NewIndexes = IndexSlice < ExpressionId , Option < MappedExpressionIndex > > ;
246- let id_to_counter = |new_indexes : & NewIndexes , operand : Operand | match operand {
247- Operand :: Zero => Some ( Counter :: ZERO ) ,
248- Operand :: Counter ( id) => Some ( Counter :: counter_value_reference ( id) ) ,
249- Operand :: Expression ( id) => {
250- self . expressions
251- . get ( id)
252- . expect ( "expression id is out of range" )
253- . as_ref ( )
254- // If an expression was optimized out, assume it would have produced a count
255- // of zero. This ensures that expressions dependent on optimized-out
256- // expressions are still valid.
257- . map_or ( Some ( Counter :: ZERO ) , |_| new_indexes[ id] . map ( Counter :: expression) )
258- }
259- } ;
260-
261- for ( original_index, expression) in
262- self . expressions . iter_enumerated ( ) . filter_map ( |( original_index, entry) | {
263- // Option::map() will return None to filter out missing expressions. This may happen
264- // if, for example, a MIR-instrumented expression is removed during an optimization.
265- entry. as_ref ( ) . map ( |expression| ( original_index, expression) )
266- } )
267- {
268- let optional_region = & expression. region ;
269- let Expression { lhs, op, rhs, .. } = * expression;
222+ /// Convert this function's coverage expression data into a form that can be
223+ /// passed through FFI to LLVM.
224+ fn counter_expressions ( & self ) -> Vec < CounterExpression > {
225+ // We know that LLVM will optimize out any unused expressions before
226+ // producing the final coverage map, so there's no need to do the same
227+ // thing on the Rust side unless we're confident we can do much better.
228+ // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
270229
271- if let Some ( Some ( ( lhs_counter, mut rhs_counter) ) ) = id_to_counter ( & new_indexes, lhs)
272- . map ( |lhs_counter| {
273- id_to_counter ( & new_indexes, rhs) . map ( |rhs_counter| ( lhs_counter, rhs_counter) )
274- } )
275- {
276- if lhs_counter. is_zero ( ) && op. is_subtract ( ) {
277- // The left side of a subtraction was probably optimized out. As an example,
278- // a branch condition might be evaluated as a constant expression, and the
279- // branch could be removed, dropping unused counters in the process.
280- //
281- // Since counters are unsigned, we must assume the result of the expression
282- // can be no more and no less than zero. An expression known to evaluate to zero
283- // does not need to be added to the coverage map.
284- //
285- // Coverage test `loops_branches.rs` includes multiple variations of branches
286- // based on constant conditional (literal `true` or `false`), and demonstrates
287- // that the expected counts are still correct.
288- debug ! (
289- "Expression subtracts from zero (assume unreachable): \
290- original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}",
291- original_index, lhs, op, rhs, optional_region,
292- ) ;
293- rhs_counter = Counter :: ZERO ;
230+ self . expressions
231+ . iter ( )
232+ . map ( |expression| match expression {
233+ None => {
234+ // This expression ID was allocated, but we never saw the
235+ // actual expression, so it must have been optimized out.
236+ // Replace it with a dummy expression, and let LLVM take
237+ // care of omitting it from the expression list.
238+ CounterExpression :: DUMMY
294239 }
295- debug_assert ! (
296- lhs_counter. is_zero( )
297- // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16`
298- || ( ( lhs_counter. zero_based_id( ) as usize )
299- <= usize :: max( self . counters. len( ) , self . expressions. len( ) ) ) ,
300- "lhs id={} > both counters.len()={} and expressions.len()={}
301- ({:?} {:?} {:?})" ,
302- lhs_counter. zero_based_id( ) ,
303- self . counters. len( ) ,
304- self . expressions. len( ) ,
305- lhs_counter,
306- op,
307- rhs_counter,
308- ) ;
309-
310- debug_assert ! (
311- rhs_counter. is_zero( )
312- // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16`
313- || ( ( rhs_counter. zero_based_id( ) as usize )
314- <= usize :: max( self . counters. len( ) , self . expressions. len( ) ) ) ,
315- "rhs id={} > both counters.len()={} and expressions.len()={}
316- ({:?} {:?} {:?})" ,
317- rhs_counter. zero_based_id( ) ,
318- self . counters. len( ) ,
319- self . expressions. len( ) ,
320- lhs_counter,
321- op,
322- rhs_counter,
323- ) ;
324-
325- // Both operands exist. `Expression` operands exist in `self.expressions` and have
326- // been assigned a `new_index`.
327- let mapped_expression_index =
328- MappedExpressionIndex :: from ( counter_expressions. len ( ) ) ;
329- let expression = CounterExpression :: new (
330- lhs_counter,
331- match op {
332- Op :: Add => ExprKind :: Add ,
333- Op :: Subtract => ExprKind :: Subtract ,
334- } ,
335- rhs_counter,
336- ) ;
337- debug ! (
338- "Adding expression {:?} = {:?}, region: {:?}" ,
339- mapped_expression_index, expression, optional_region
340- ) ;
341- counter_expressions. push ( expression) ;
342- new_indexes[ original_index] = Some ( mapped_expression_index) ;
343- if let Some ( region) = optional_region {
344- expression_regions. push ( ( Counter :: expression ( mapped_expression_index) , region) ) ;
240+ & Some ( Expression { lhs, op, rhs, .. } ) => {
241+ // Convert the operands and operator as normal.
242+ CounterExpression :: new (
243+ Counter :: from_operand ( lhs) ,
244+ match op {
245+ Op :: Add => ExprKind :: Add ,
246+ Op :: Subtract => ExprKind :: Subtract ,
247+ } ,
248+ Counter :: from_operand ( rhs) ,
249+ )
345250 }
346- } else {
347- bug ! (
348- "expression has one or more missing operands \
349- original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}",
350- original_index,
351- lhs,
352- op,
353- rhs,
354- optional_region,
355- ) ;
356- }
357- }
358- ( counter_expressions, expression_regions. into_iter ( ) )
251+ } )
252+ . collect :: < Vec < _ > > ( )
253+ }
254+
255+ fn expression_regions ( & self ) -> Vec < ( Counter , & CodeRegion ) > {
256+ // 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.
259+ self . expressions
260+ . iter_enumerated ( )
261+ . filter_map ( |( id, expression) | {
262+ let code_region = expression. as_ref ( ) ?. region . as_ref ( ) ?;
263+ Some ( ( Counter :: expression ( id) , code_region) )
264+ } )
265+ . collect :: < Vec < _ > > ( )
359266 }
360267
361268 fn unreachable_regions ( & self ) -> impl Iterator < Item = ( Counter , & CodeRegion ) > {
0 commit comments