@@ -3,19 +3,22 @@ use crate::util::patch::MirPatch;
33use rustc_data_structures:: fingerprint:: Fingerprint ;
44use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher } ;
55use rustc_hir:: lang_items;
6+ use rustc_index:: vec:: IndexVec ;
67use rustc_middle:: hir;
78use rustc_middle:: ich:: StableHashingContext ;
8- use rustc_middle:: mir:: interpret:: { ConstValue , Scalar } ;
9+ use rustc_middle:: mir:: interpret:: { ConstValue , InterpResult , Scalar } ;
910use 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 ,
1213} ;
1314use rustc_middle:: ty;
1415use rustc_middle:: ty:: query:: Providers ;
1516use rustc_middle:: ty:: TyCtxt ;
1617use rustc_middle:: ty:: { ConstKind , FnDef } ;
1718use 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 } ;
1922
2023/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with
2124/// the intrinsic llvm.instrprof.increment.
@@ -24,46 +27,96 @@ pub struct InstrumentCoverage;
2427/// The `query` provider for `CoverageData`, requested by `codegen_intrinsic_call()` when
2528/// constructing the arguments for `llvm.instrprof.increment`.
2629pub ( 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) ;
6191 }
6292 }
6393 }
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+ )
67120}
68121
69122struct Instrumentor < ' tcx > {
@@ -102,17 +155,16 @@ impl<'tcx> Instrumentor<'tcx> {
102155 fn inject_counters ( & mut self , mir_body : & mut mir:: Body < ' tcx > ) {
103156 // FIXME(richkadel): As a first step, counters are only injected at the top of each
104157 // 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) ;
109161 }
110162
111163 fn inject_counter (
112164 & mut self ,
113165 mir_body : & mut mir:: Body < ' tcx > ,
114- next_block : BasicBlock ,
115166 code_region : Span ,
167+ next_block : BasicBlock ,
116168 ) {
117169 let injection_point = code_region. shrink_to_lo ( ) ;
118170
@@ -121,12 +173,19 @@ impl<'tcx> Instrumentor<'tcx> {
121173 self . tcx . require_lang_item ( lang_items:: CountCodeRegionFnLangItem , None ) ,
122174 injection_point,
123175 ) ;
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) ) ;
130189
131190 let mut patch = MirPatch :: new ( mir_body) ;
132191
@@ -136,7 +195,7 @@ impl<'tcx> Instrumentor<'tcx> {
136195 new_block,
137196 TerminatorKind :: Call {
138197 func : count_code_region_fn,
139- args : vec ! [ counter_index ] ,
198+ args,
140199 // new_block will swapped with the next_block, after applying patch
141200 destination : Some ( ( Place :: from ( temp) , new_block) ) ,
142201 cleanup : None ,
@@ -154,6 +213,10 @@ impl<'tcx> Instrumentor<'tcx> {
154213 // `next_block`), just swap the indexes, leaving the rest of the graph unchanged.
155214 mir_body. basic_blocks_mut ( ) . swap ( next_block, new_block) ;
156215 }
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+ }
157220}
158221
159222fn function_handle < ' tcx > ( tcx : TyCtxt < ' tcx > , fn_def_id : DefId , span : Span ) -> Operand < ' tcx > {
0 commit comments