11use super :: * ;
22
3+ use rustc_data_structures:: captures:: Captures ;
34use rustc_middle:: mir:: coverage:: * ;
45use rustc_middle:: mir:: { self , Body , Coverage , CoverageInfo } ;
56use rustc_middle:: query:: Providers ;
@@ -12,87 +13,62 @@ pub(crate) fn provide(providers: &mut Providers) {
1213 providers. covered_code_regions = |tcx, def_id| covered_code_regions ( tcx, def_id) ;
1314}
1415
15- /// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in
16- /// other words, the number of counter value references injected into the MIR (plus 1 for the
17- /// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected
18- /// counters have a counter ID from `1..num_counters-1`.
19- ///
20- /// `num_expressions` is the number of counter expressions added to the MIR body.
21- ///
22- /// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend
23- /// code generate, to lookup counters and expressions by simple u32 indexes.
16+ /// Coverage codegen needs to know the total number of counter IDs and expression IDs that have
17+ /// been used by a function's coverage mappings. These totals are used to create vectors to hold
18+ /// the relevant counter and expression data, and the maximum counter ID (+ 1) is also needed by
19+ /// the `llvm.instrprof.increment` intrinsic.
2420///
2521/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code
2622/// including injected counters. (It is OK if some counters are optimized out, but those counters
2723/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the
2824/// calls may not work; but computing the number of counters or expressions by adding `1` to the
2925/// highest ID (for a given instrumented function) is valid.
3026///
31- /// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum
32- /// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a
33- /// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression
34- /// IDs referenced by expression operands, if not already seen.
35- ///
36- /// Ideally, each operand ID in a MIR `CoverageKind::Expression` will have a separate MIR `Coverage`
37- /// statement for the `Counter` or `Expression` with the referenced ID. but since current or future
38- /// MIR optimizations can theoretically optimize out segments of a MIR, it may not be possible to
39- /// guarantee this, so the second pass ensures the `CoverageInfo` counts include all referenced IDs.
27+ /// It's possible for a coverage expression to remain in MIR while one or both of its operands
28+ /// have been optimized away. To avoid problems in codegen, we include those operands' IDs when
29+ /// determining the maximum counter/expression ID, even if the underlying counter/expression is
30+ /// no longer present.
4031struct CoverageVisitor {
41- info : CoverageInfo ,
42- add_missing_operands : bool ,
32+ max_counter_id : CounterId ,
33+ max_expression_id : ExpressionId ,
4334}
4435
4536impl CoverageVisitor {
46- /// Updates `num_counters ` to the maximum encountered counter ID plus 1 .
37+ /// Updates `max_counter_id ` to the maximum encountered counter ID.
4738 #[ inline( always) ]
48- fn update_num_counters ( & mut self , counter_id : CounterId ) {
49- let counter_id = counter_id. as_u32 ( ) ;
50- self . info . num_counters = std:: cmp:: max ( self . info . num_counters , counter_id + 1 ) ;
39+ fn update_max_counter_id ( & mut self , counter_id : CounterId ) {
40+ self . max_counter_id = self . max_counter_id . max ( counter_id) ;
5141 }
5242
53- /// Updates `num_expressions ` to the maximum encountered expression ID plus 1 .
43+ /// Updates `max_expression_id ` to the maximum encountered expression ID.
5444 #[ inline( always) ]
55- fn update_num_expressions ( & mut self , expression_id : ExpressionId ) {
56- let expression_id = expression_id. as_u32 ( ) ;
57- self . info . num_expressions = std:: cmp:: max ( self . info . num_expressions , expression_id + 1 ) ;
45+ fn update_max_expression_id ( & mut self , expression_id : ExpressionId ) {
46+ self . max_expression_id = self . max_expression_id . max ( expression_id) ;
5847 }
5948
6049 fn update_from_expression_operand ( & mut self , operand : Operand ) {
6150 match operand {
62- Operand :: Counter ( id) => self . update_num_counters ( id) ,
63- Operand :: Expression ( id) => self . update_num_expressions ( id) ,
51+ Operand :: Counter ( id) => self . update_max_counter_id ( id) ,
52+ Operand :: Expression ( id) => self . update_max_expression_id ( id) ,
6453 Operand :: Zero => { }
6554 }
6655 }
6756
6857 fn visit_body ( & mut self , body : & Body < ' _ > ) {
69- for bb_data in body. basic_blocks . iter ( ) {
70- for statement in bb_data. statements . iter ( ) {
71- if let StatementKind :: Coverage ( box ref coverage) = statement. kind {
72- if is_inlined ( body, statement) {
73- continue ;
74- }
75- self . visit_coverage ( coverage) ;
76- }
77- }
58+ for coverage in all_coverage_in_mir_body ( body) {
59+ self . visit_coverage ( coverage) ;
7860 }
7961 }
8062
8163 fn visit_coverage ( & mut self , coverage : & Coverage ) {
82- if self . add_missing_operands {
83- match coverage. kind {
84- CoverageKind :: Expression { lhs, rhs, .. } => {
85- self . update_from_expression_operand ( lhs) ;
86- self . update_from_expression_operand ( rhs) ;
87- }
88- _ => { }
89- }
90- } else {
91- match coverage. kind {
92- CoverageKind :: Counter { id, .. } => self . update_num_counters ( id) ,
93- CoverageKind :: Expression { id, .. } => self . update_num_expressions ( id) ,
94- _ => { }
64+ match coverage. kind {
65+ CoverageKind :: Counter { id, .. } => self . update_max_counter_id ( id) ,
66+ CoverageKind :: Expression { id, lhs, rhs, .. } => {
67+ self . update_max_expression_id ( id) ;
68+ self . update_from_expression_operand ( lhs) ;
69+ self . update_from_expression_operand ( rhs) ;
9570 }
71+ CoverageKind :: Unreachable => { }
9672 }
9773 }
9874}
@@ -101,37 +77,40 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) ->
10177 let mir_body = tcx. instance_mir ( instance_def) ;
10278
10379 let mut coverage_visitor = CoverageVisitor {
104- info : CoverageInfo { num_counters : 0 , num_expressions : 0 } ,
105- add_missing_operands : false ,
80+ max_counter_id : CounterId :: START ,
81+ max_expression_id : ExpressionId :: START ,
10682 } ;
10783
10884 coverage_visitor. visit_body ( mir_body) ;
10985
110- coverage_visitor. add_missing_operands = true ;
111- coverage_visitor. visit_body ( mir_body) ;
112-
113- coverage_visitor. info
86+ // Add 1 to the highest IDs to get the total number of IDs.
87+ CoverageInfo {
88+ num_counters : ( coverage_visitor. max_counter_id + 1 ) . as_u32 ( ) ,
89+ num_expressions : ( coverage_visitor. max_expression_id + 1 ) . as_u32 ( ) ,
90+ }
11491}
11592
11693fn covered_code_regions ( tcx : TyCtxt < ' _ > , def_id : DefId ) -> Vec < & CodeRegion > {
11794 let body = mir_body ( tcx, def_id) ;
118- body. basic_blocks
119- . iter ( )
120- . flat_map ( |data| {
121- data. statements . iter ( ) . filter_map ( |statement| match statement. kind {
122- StatementKind :: Coverage ( box ref coverage) => {
123- if is_inlined ( body, statement) {
124- None
125- } else {
126- coverage. code_region . as_ref ( ) // may be None
127- }
128- }
129- _ => None ,
130- } )
131- } )
95+ all_coverage_in_mir_body ( body)
96+ // Not all coverage statements have an attached code region.
97+ . filter_map ( |coverage| coverage. code_region . as_ref ( ) )
13298 . collect ( )
13399}
134100
101+ fn all_coverage_in_mir_body < ' a , ' tcx > (
102+ body : & ' a Body < ' tcx > ,
103+ ) -> impl Iterator < Item = & ' a Coverage > + Captures < ' tcx > {
104+ body. basic_blocks . iter ( ) . flat_map ( |bb_data| & bb_data. statements ) . filter_map ( |statement| {
105+ match statement. kind {
106+ StatementKind :: Coverage ( box ref coverage) if !is_inlined ( body, statement) => {
107+ Some ( coverage)
108+ }
109+ _ => None ,
110+ }
111+ } )
112+ }
113+
135114fn is_inlined ( body : & Body < ' _ > , statement : & Statement < ' _ > ) -> bool {
136115 let scope_data = & body. source_scopes [ statement. source_info . scope ] ;
137116 scope_data. inlined . is_some ( ) || scope_data. inlined_parent_scope . is_some ( )
0 commit comments