@@ -2,19 +2,11 @@ use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
22
33use rustc_data_structures:: fx:: FxIndexSet ;
44use rustc_index:: bit_set:: BitSet ;
5- use rustc_index:: IndexVec ;
65use rustc_middle:: mir:: coverage:: {
7- CodeRegion , CounterId , CovTerm , ExpressionId , FunctionCoverageInfo , Mapping , Op ,
6+ CodeRegion , CounterId , CovTerm , Expression , ExpressionId , FunctionCoverageInfo , Mapping , Op ,
87} ;
98use rustc_middle:: ty:: Instance ;
109
11- #[ derive( Clone , Debug , PartialEq ) ]
12- pub struct Expression {
13- lhs : CovTerm ,
14- op : Op ,
15- rhs : CovTerm ,
16- }
17-
1810/// Holds all of the coverage mapping data associated with a function instance,
1911/// collected during traversal of `Coverage` statements in the function's MIR.
2012#[ derive( Debug ) ]
@@ -26,7 +18,9 @@ pub struct FunctionCoverage<'tcx> {
2618 /// Tracks which counters have been seen, so that we can identify mappings
2719 /// to counters that were optimized out, and set them to zero.
2820 counters_seen : BitSet < CounterId > ,
29- expressions : IndexVec < ExpressionId , Option < Expression > > ,
21+ /// Tracks which expressions have one or more associated mappings, but have
22+ /// not yet been seen. This lets us detect that they were optimized out.
23+ expressions_not_seen : BitSet < ExpressionId > ,
3024 /// Tracks which expressions are known to always have a value of zero.
3125 /// Only updated during the finalize step.
3226 zero_expressions : FxIndexSet < ExpressionId > ,
@@ -55,16 +49,27 @@ impl<'tcx> FunctionCoverage<'tcx> {
5549 is_used : bool ,
5650 ) -> Self {
5751 let num_counters = function_coverage_info. num_counters ;
58- let num_expressions = function_coverage_info. num_expressions ;
52+ let num_expressions = function_coverage_info. expressions . len ( ) ;
5953 debug ! (
6054 "FunctionCoverage::create(instance={instance:?}) has \
6155 num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}"
6256 ) ;
57+
58+ // Every expression that has an associated mapping should have a
59+ // corresponding statement in MIR. If we don't see that statement, we
60+ // know that it was removed by MIR optimizations.
61+ let mut expressions_not_seen = BitSet :: new_empty ( num_expressions) ;
62+ for Mapping { term, .. } in & function_coverage_info. mappings {
63+ if let & CovTerm :: Expression ( id) = term {
64+ expressions_not_seen. insert ( id) ;
65+ }
66+ }
67+
6368 Self {
6469 function_coverage_info,
6570 is_used,
6671 counters_seen : BitSet :: new_empty ( num_counters) ,
67- expressions : IndexVec :: from_elem_n ( None , num_expressions ) ,
72+ expressions_not_seen ,
6873 zero_expressions : FxIndexSet :: default ( ) ,
6974 }
7075 }
@@ -80,35 +85,10 @@ impl<'tcx> FunctionCoverage<'tcx> {
8085 self . counters_seen . insert ( id) ;
8186 }
8287
83- /// Adds information about a coverage expression .
88+ /// Marks an expression ID as having been seen .
8489 #[ instrument( level = "debug" , skip( self ) ) ]
85- pub ( crate ) fn add_counter_expression (
86- & mut self ,
87- expression_id : ExpressionId ,
88- lhs : CovTerm ,
89- op : Op ,
90- rhs : CovTerm ,
91- ) {
92- debug_assert ! (
93- expression_id. as_usize( ) < self . expressions. len( ) ,
94- "expression_id {} is out of range for expressions.len() = {}
95- for {:?}" ,
96- expression_id. as_usize( ) ,
97- self . expressions. len( ) ,
98- self ,
99- ) ;
100-
101- let expression = Expression { lhs, op, rhs } ;
102- let slot = & mut self . expressions [ expression_id] ;
103- match slot {
104- None => * slot = Some ( expression) ,
105- // If this expression ID slot has already been filled, it should
106- // contain identical information.
107- Some ( ref previous_expression) => assert_eq ! (
108- previous_expression, & expression,
109- "add_counter_expression: expression for id changed"
110- ) ,
111- }
90+ pub ( crate ) fn add_counter_expression ( & mut self , expression_id : ExpressionId ) {
91+ self . expressions_not_seen . remove ( expression_id) ;
11292 }
11393
11494 pub ( crate ) fn finalize ( & mut self ) {
@@ -133,13 +113,13 @@ impl<'tcx> FunctionCoverage<'tcx> {
133113 // and then update the set of always-zero expressions if necessary.
134114 // (By construction, expressions can only refer to other expressions
135115 // that have lower IDs, so one pass is sufficient.)
136- for ( id, maybe_expression ) in self . expressions . iter_enumerated ( ) {
137- let Some ( expression ) = maybe_expression else {
138- // If an expression is missing , it must have been optimized away,
116+ for ( id, expression ) in self . function_coverage_info . expressions . iter_enumerated ( ) {
117+ if self . expressions_not_seen . contains ( id ) {
118+ // If an expression was not seen , it must have been optimized away,
139119 // so any operand that refers to it can be replaced with zero.
140120 zero_expressions. insert ( id) ;
141121 continue ;
142- } ;
122+ }
143123
144124 // We don't need to simplify the actual expression data in the
145125 // expressions list; we can just simplify a temporary copy and then
@@ -191,17 +171,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
191171 _ => Counter :: from_term ( operand) ,
192172 } ;
193173
194- self . expressions
195- . iter ( )
196- . map ( |expression| match expression {
197- None => {
174+ self . function_coverage_info
175+ . expressions
176+ . iter_enumerated ( )
177+ . map ( |( id, & Expression { lhs, op, rhs } ) | {
178+ if self . expressions_not_seen . contains ( id) {
198179 // This expression ID was allocated, but we never saw the
199180 // actual expression, so it must have been optimized out.
200181 // Replace it with a dummy expression, and let LLVM take
201182 // care of omitting it from the expression list.
202183 CounterExpression :: DUMMY
203- }
204- & Some ( Expression { lhs, op, rhs, .. } ) => {
184+ } else {
205185 // Convert the operands and operator as normal.
206186 CounterExpression :: new (
207187 counter_from_operand ( lhs) ,
0 commit comments