1
1
use super :: * ;
2
2
3
+ use rustc_data_structures:: captures:: Captures ;
3
4
use rustc_middle:: mir:: coverage:: * ;
4
5
use rustc_middle:: mir:: { self , Body , Coverage , CoverageInfo } ;
5
6
use rustc_middle:: query:: Providers ;
@@ -12,87 +13,62 @@ pub(crate) fn provide(providers: &mut Providers) {
12
13
providers. covered_code_regions = |tcx, def_id| covered_code_regions ( tcx, def_id) ;
13
14
}
14
15
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.
24
20
///
25
21
/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code
26
22
/// including injected counters. (It is OK if some counters are optimized out, but those counters
27
23
/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the
28
24
/// calls may not work; but computing the number of counters or expressions by adding `1` to the
29
25
/// highest ID (for a given instrumented function) is valid.
30
26
///
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.
40
31
struct CoverageVisitor {
41
- info : CoverageInfo ,
42
- add_missing_operands : bool ,
32
+ max_counter_id : CounterId ,
33
+ max_expression_id : ExpressionId ,
43
34
}
44
35
45
36
impl CoverageVisitor {
46
- /// Updates `num_counters ` to the maximum encountered counter ID plus 1 .
37
+ /// Updates `max_counter_id ` to the maximum encountered counter ID.
47
38
#[ 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) ;
51
41
}
52
42
53
- /// Updates `num_expressions ` to the maximum encountered expression ID plus 1 .
43
+ /// Updates `max_expression_id ` to the maximum encountered expression ID.
54
44
#[ 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) ;
58
47
}
59
48
60
49
fn update_from_expression_operand ( & mut self , operand : Operand ) {
61
50
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) ,
64
53
Operand :: Zero => { }
65
54
}
66
55
}
67
56
68
57
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) ;
78
60
}
79
61
}
80
62
81
63
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) ;
95
70
}
71
+ CoverageKind :: Unreachable => { }
96
72
}
97
73
}
98
74
}
@@ -101,37 +77,40 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) ->
101
77
let mir_body = tcx. instance_mir ( instance_def) ;
102
78
103
79
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 ,
106
82
} ;
107
83
108
84
coverage_visitor. visit_body ( mir_body) ;
109
85
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
+ }
114
91
}
115
92
116
93
fn covered_code_regions ( tcx : TyCtxt < ' _ > , def_id : DefId ) -> Vec < & CodeRegion > {
117
94
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 ( ) )
132
98
. collect ( )
133
99
}
134
100
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
+
135
114
fn is_inlined ( body : & Body < ' _ > , statement : & Statement < ' _ > ) -> bool {
136
115
let scope_data = & body. source_scopes [ statement. source_info . scope ] ;
137
116
scope_data. inlined . is_some ( ) || scope_data. inlined_parent_scope . is_some ( )
0 commit comments