@@ -3,19 +3,22 @@ use crate::util::patch::MirPatch;
3
3
use rustc_data_structures:: fingerprint:: Fingerprint ;
4
4
use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher } ;
5
5
use rustc_hir:: lang_items;
6
+ use rustc_index:: vec:: IndexVec ;
6
7
use rustc_middle:: hir;
7
8
use rustc_middle:: ich:: StableHashingContext ;
8
- use rustc_middle:: mir:: interpret:: { ConstValue , Scalar } ;
9
+ use rustc_middle:: mir:: interpret:: { ConstValue , InterpResult , Scalar } ;
9
10
use rustc_middle:: mir:: {
10
- self , traversal, BasicBlock , BasicBlockData , CoverageData , Operand , Place , SourceInfo ,
11
- StatementKind , Terminator , TerminatorKind , START_BLOCK ,
11
+ self , traversal, BasicBlock , BasicBlockData , CoverageData , CoverageRegion , Operand , Place ,
12
+ SourceInfo , StatementKind , Terminator , TerminatorKind , START_BLOCK ,
12
13
} ;
13
14
use rustc_middle:: ty;
14
15
use rustc_middle:: ty:: query:: Providers ;
15
16
use rustc_middle:: ty:: TyCtxt ;
16
17
use rustc_middle:: ty:: { ConstKind , FnDef } ;
17
18
use rustc_span:: def_id:: DefId ;
18
- use rustc_span:: Span ;
19
+ use rustc_span:: { Pos , Span } ;
20
+
21
+ use rustc_middle:: mir:: count_code_region_args:: { COUNTER_INDEX , END_BYTE_POS , START_BYTE_POS } ;
19
22
20
23
/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with
21
24
/// the intrinsic llvm.instrprof.increment.
@@ -24,46 +27,96 @@ pub struct InstrumentCoverage;
24
27
/// The `query` provider for `CoverageData`, requested by `codegen_intrinsic_call()` when
25
28
/// constructing the arguments for `llvm.instrprof.increment`.
26
29
pub ( crate ) fn provide ( providers : & mut Providers < ' _ > ) {
27
- providers. coverage_data = |tcx, def_id| {
28
- let mir_body = tcx. optimized_mir ( def_id) ;
29
- // FIXME(richkadel): The current implementation assumes the MIR for the given DefId
30
- // represents a single function. Validate and/or correct if inlining and/or monomorphization
31
- // invalidates these assumptions.
32
- let count_code_region_fn =
33
- tcx. require_lang_item ( lang_items:: CountCodeRegionFnLangItem , None ) ;
34
- let mut num_counters: u32 = 0 ;
35
- // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected
36
- // counters, with each counter having an index from `0..num_counters-1`. MIR optimization
37
- // may split and duplicate some BasicBlock sequences. Simply counting the calls may not
38
- // not work; but computing the num_counters by adding `1` to the highest index (for a given
39
- // instrumented function) is valid.
40
- for ( _, data) in traversal:: preorder ( mir_body) {
41
- if let Some ( terminator) = & data. terminator {
42
- if let TerminatorKind :: Call { func : Operand :: Constant ( func) , args, .. } =
43
- & terminator. kind
44
- {
45
- if let FnDef ( called_fn_def_id, _) = func. literal . ty . kind {
46
- if called_fn_def_id == count_code_region_fn {
47
- if let Operand :: Constant ( constant) =
48
- args. get ( 0 ) . expect ( "count_code_region has at least one arg" )
49
- {
50
- if let ConstKind :: Value ( ConstValue :: Scalar ( value) ) =
51
- constant. literal . val
52
- {
53
- let index = value
54
- . to_u32 ( )
55
- . expect ( "count_code_region index at arg0 is u32" ) ;
56
- num_counters = std:: cmp:: max ( num_counters, index + 1 ) ;
57
- }
58
- }
59
- }
60
- }
30
+ providers. coverage_data = |tcx, def_id| coverage_data_from_mir ( tcx, def_id) ;
31
+ }
32
+
33
+ fn coverage_data_from_mir < ' tcx > ( tcx : TyCtxt < ' tcx > , mir_def_id : DefId ) -> & ' tcx CoverageData {
34
+ let mir_body = tcx. optimized_mir ( mir_def_id) ;
35
+ // FIXME(richkadel): The current implementation assumes the MIR for the given DefId
36
+ // represents a single function. Validate and/or correct if inlining and/or monomorphization
37
+ // invalidates these assumptions.
38
+ let count_code_region_fn = tcx. require_lang_item ( lang_items:: CountCodeRegionFnLangItem , None ) ;
39
+
40
+ // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected
41
+ // counters, with each counter having an index from `0..num_counters-1`. MIR optimization
42
+ // may split and duplicate some BasicBlock sequences. Simply counting the calls may not
43
+ // not work; but computing the num_counters by adding `1` to the highest index (for a given
44
+ // instrumented function) is valid.
45
+ let mut indexed_regions = vec ! [ ] ;
46
+ let mut num_counters: u32 = 0 ;
47
+ //for terminator in count_code_region_terminators(tcx, mir_body) {
48
+ for terminator in traversal:: preorder ( mir_body)
49
+ . map ( |( _, data) | ( data, count_code_region_fn) )
50
+ . filter_map ( count_code_region_terminator_filter)
51
+ {
52
+ // FIXME(richkadel): When analyzing the MIR and identifying CodeRegions to count by LLVM
53
+ // counter_expressions, inject (and here, filter for) an additional call, such as:
54
+ //
55
+ // code_region_counter_expression(
56
+ // left_counter: i32, signed_right_counter: i64,
57
+ // start_byte_pos, end_byte_pos
58
+ // )
59
+ //
60
+ // `abs(signed_right_counter)` (perhaps there's a better name?) is the index of the counter's
61
+ // count to be added to or subtracted from (depending on the sign) the `left_counter`
62
+ // index.
63
+ //
64
+ // Then store the expressions in a different IndexVec.
65
+ // We may also want to consider "GapRegions" (clang had them... do we need them too?)
66
+ if let TerminatorKind :: Call { args, .. } = & terminator. kind {
67
+ let ( index, start_byte_pos, end_byte_pos) = count_code_region_args ( & args) ;
68
+ indexed_regions. push ( ( index, CoverageRegion { start_byte_pos, end_byte_pos } ) ) ;
69
+ num_counters = std:: cmp:: max ( num_counters, index + 1 ) ;
70
+ }
71
+ }
72
+ let hash = if num_counters > 0 { hash_mir_source ( tcx, mir_def_id) } else { 0 } ;
73
+ let mut coverage_regions = IndexVec :: < u32 , CoverageRegion > :: from_elem_n (
74
+ CoverageRegion { start_byte_pos : 0 , end_byte_pos : 0 } ,
75
+ indexed_regions. len ( ) ,
76
+ ) ;
77
+ for ( index, region) in indexed_regions {
78
+ coverage_regions[ index] = region;
79
+ }
80
+ tcx. arena . alloc ( CoverageData { num_counters, hash, coverage_regions } )
81
+ }
82
+
83
+ fn count_code_region_terminator_filter (
84
+ ( data, count_code_region_fn) : ( & ' tcx BasicBlockData < ' tcx > , DefId ) ,
85
+ ) -> Option < & ' tcx Terminator < ' tcx > > {
86
+ if let Some ( terminator) = & data. terminator {
87
+ if let TerminatorKind :: Call { func : Operand :: Constant ( func) , .. } = & terminator. kind {
88
+ if let FnDef ( called_fn_def_id, _) = func. literal . ty . kind {
89
+ if called_fn_def_id == count_code_region_fn {
90
+ return Some ( & terminator) ;
61
91
}
62
92
}
63
93
}
64
- let hash = if num_counters > 0 { hash_mir_source ( tcx, def_id) } else { 0 } ;
65
- CoverageData { num_counters, hash }
66
- } ;
94
+ }
95
+ None
96
+ }
97
+
98
+ fn arg < T , F > ( to_val : F , args : & Vec < Operand < ' tcx > > , pos : usize ) -> T
99
+ where
100
+ F : FnOnce ( Scalar ) -> InterpResult < ' static , T > ,
101
+ {
102
+ match args. get ( pos) . unwrap_or_else ( || bug ! ( "count_code_region arg{} not found" , pos) ) {
103
+ Operand :: Constant ( constant) => match constant. literal . val {
104
+ ConstKind :: Value ( ConstValue :: Scalar ( scalar) ) => to_val ( scalar) . unwrap_or_else ( |err| {
105
+ bug ! ( "count_code_region arg{}, {:?}: {:?}" , pos, scalar, err) ;
106
+ } ) ,
107
+ _ => bug ! ( "count_code_region arg{}: ConstKind::Value expected" , pos) ,
108
+ } ,
109
+ _ => bug ! ( "count_code_region arg{}: Operand::Constant expected" , pos) ,
110
+ }
111
+ }
112
+
113
+ fn count_code_region_args ( args : & Vec < Operand < ' tcx > > ) -> ( u32 , u32 , u32 ) {
114
+ let to_u32 = |scalar : Scalar | scalar. to_u32 ( ) ;
115
+ (
116
+ arg ( to_u32, args, COUNTER_INDEX ) ,
117
+ arg ( to_u32, args, START_BYTE_POS ) ,
118
+ arg ( to_u32, args, END_BYTE_POS ) ,
119
+ )
67
120
}
68
121
69
122
struct Instrumentor < ' tcx > {
@@ -102,17 +155,16 @@ impl<'tcx> Instrumentor<'tcx> {
102
155
fn inject_counters ( & mut self , mir_body : & mut mir:: Body < ' tcx > ) {
103
156
// FIXME(richkadel): As a first step, counters are only injected at the top of each
104
157
// function. The complete solution will inject counters at each conditional code branch.
105
- let top_of_function = START_BLOCK ;
106
- let entire_function = mir_body. span ;
107
-
108
- self . inject_counter ( mir_body, top_of_function, entire_function) ;
158
+ let code_region = mir_body. span ;
159
+ let next_block = START_BLOCK ;
160
+ self . inject_counter ( mir_body, code_region, next_block) ;
109
161
}
110
162
111
163
fn inject_counter (
112
164
& mut self ,
113
165
mir_body : & mut mir:: Body < ' tcx > ,
114
- next_block : BasicBlock ,
115
166
code_region : Span ,
167
+ next_block : BasicBlock ,
116
168
) {
117
169
let injection_point = code_region. shrink_to_lo ( ) ;
118
170
@@ -121,12 +173,19 @@ impl<'tcx> Instrumentor<'tcx> {
121
173
self . tcx . require_lang_item ( lang_items:: CountCodeRegionFnLangItem , None ) ,
122
174
injection_point,
123
175
) ;
124
- let counter_index = Operand :: const_from_scalar (
125
- self . tcx ,
126
- self . tcx . types . u32 ,
127
- Scalar :: from_u32 ( self . next_counter ( ) ) ,
128
- injection_point,
129
- ) ;
176
+
177
+ let index = self . next_counter ( ) ;
178
+
179
+ let mut args = Vec :: new ( ) ;
180
+
181
+ assert_eq ! ( COUNTER_INDEX , args. len( ) ) ;
182
+ args. push ( self . const_u32 ( index, injection_point) ) ;
183
+
184
+ assert_eq ! ( START_BYTE_POS , args. len( ) ) ;
185
+ args. push ( self . const_u32 ( code_region. lo ( ) . to_u32 ( ) , injection_point) ) ;
186
+
187
+ assert_eq ! ( END_BYTE_POS , args. len( ) ) ;
188
+ args. push ( self . const_u32 ( code_region. hi ( ) . to_u32 ( ) , injection_point) ) ;
130
189
131
190
let mut patch = MirPatch :: new ( mir_body) ;
132
191
@@ -136,7 +195,7 @@ impl<'tcx> Instrumentor<'tcx> {
136
195
new_block,
137
196
TerminatorKind :: Call {
138
197
func : count_code_region_fn,
139
- args : vec ! [ counter_index ] ,
198
+ args,
140
199
// new_block will swapped with the next_block, after applying patch
141
200
destination : Some ( ( Place :: from ( temp) , new_block) ) ,
142
201
cleanup : None ,
@@ -154,6 +213,10 @@ impl<'tcx> Instrumentor<'tcx> {
154
213
// `next_block`), just swap the indexes, leaving the rest of the graph unchanged.
155
214
mir_body. basic_blocks_mut ( ) . swap ( next_block, new_block) ;
156
215
}
216
+
217
+ fn const_u32 ( & self , value : u32 , span : Span ) -> Operand < ' tcx > {
218
+ Operand :: const_from_scalar ( self . tcx , self . tcx . types . u32 , Scalar :: from_u32 ( value) , span)
219
+ }
157
220
}
158
221
159
222
fn function_handle < ' tcx > ( tcx : TyCtxt < ' tcx > , fn_def_id : DefId , span : Span ) -> Operand < ' tcx > {
0 commit comments