11use crate :: transform:: { MirPass , MirSource } ;
2- use rustc_index :: vec :: Idx ;
2+ use crate :: util :: patch :: MirPatch ;
33use rustc_middle:: mir:: interpret:: Scalar ;
44use rustc_middle:: mir:: * ;
5- use rustc_middle:: mir:: { Local , LocalDecl } ;
65use rustc_middle:: ty;
76use rustc_middle:: ty:: Ty ;
87use rustc_middle:: ty:: TyCtxt ;
@@ -16,69 +15,62 @@ pub struct InstrumentCoverage;
1615 * the intrinsic llvm.instrprof.increment.
1716 */
1817
19- // FIXME(richkadel): As a first step, counters are only injected at the top of each function.
20- // The complete solution will inject counters at each conditional code branch.
21-
2218impl < ' tcx > MirPass < ' tcx > for InstrumentCoverage {
2319 fn run_pass ( & self , tcx : TyCtxt < ' tcx > , src : MirSource < ' tcx > , body : & mut Body < ' tcx > ) {
2420 if tcx. sess . opts . debugging_opts . instrument_coverage {
25- if let Some ( callee_fn_def_id) = tcx. lang_items ( ) . count_code_region_fn ( ) {
26- debug ! ( "instrumenting {:?}" , src. def_id( ) ) ;
27- instrument_coverage ( tcx, callee_fn_def_id, body) ;
28- }
21+ debug ! ( "instrumenting {:?}" , src. def_id( ) ) ;
22+ instrument_coverage ( tcx, body) ;
2923 }
3024 }
3125}
3226
33- pub fn instrument_coverage < ' tcx > (
34- tcx : TyCtxt < ' tcx > ,
35- callee_fn_def_id : DefId ,
36- body : & mut Body < ' tcx > ,
37- ) {
27+ // The first counter (start of the function) is index zero.
28+ const INIT_FUNCTION_COUNTER : u128 = 0 ;
29+
30+ /// Injects calls to placeholder function `count_code_region()`.
31+ // FIXME(richkadel): As a first step, counters are only injected at the top of each function.
32+ // The complete solution will inject counters at each conditional code branch.
33+ pub fn instrument_coverage < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
3834 let span = body. span . shrink_to_lo ( ) ;
3935
40- let ret_ty = tcx. fn_sig ( callee_fn_def_id) . output ( ) ;
36+ let count_code_region_fn =
37+ function_handle ( tcx, span, tcx. lang_items ( ) . count_code_region_fn ( ) . unwrap ( ) ) ;
38+ let counter_index = const_int_operand ( tcx, span, tcx. types . u32 , INIT_FUNCTION_COUNTER ) ;
39+
40+ let mut patch = MirPatch :: new ( body) ;
41+
42+ let new_block = patch. new_block ( placeholder_block ( SourceInfo :: outermost ( body. span ) ) ) ;
43+ let next_block = START_BLOCK ;
44+
45+ let temp = patch. new_temp ( tcx. mk_unit ( ) , body. span ) ;
46+ patch. patch_terminator (
47+ new_block,
48+ TerminatorKind :: Call {
49+ func : count_code_region_fn,
50+ args : vec ! [ counter_index] ,
51+ // new_block will swapped with the next_block, after applying patch
52+ destination : Some ( ( Place :: from ( temp) , new_block) ) ,
53+ cleanup : None ,
54+ from_hir_call : false ,
55+ } ,
56+ ) ;
57+
58+ patch. add_statement ( new_block. start_location ( ) , StatementKind :: StorageLive ( temp) ) ;
59+ patch. add_statement ( next_block. start_location ( ) , StatementKind :: StorageDead ( temp) ) ;
60+
61+ patch. apply ( body) ;
62+
63+ // To insert the `new_block` in front of the first block in the counted branch (for example,
64+ // the START_BLOCK, at the top of the function), just swap the indexes, leaving the rest of the
65+ // graph unchanged.
66+ body. basic_blocks_mut ( ) . swap ( next_block, new_block) ;
67+ }
68+
69+ fn function_handle < ' tcx > ( tcx : TyCtxt < ' tcx > , span : Span , fn_def_id : DefId ) -> Operand < ' tcx > {
70+ let ret_ty = tcx. fn_sig ( fn_def_id) . output ( ) ;
4171 let ret_ty = ret_ty. no_bound_vars ( ) . unwrap ( ) ;
4272 let substs = tcx. mk_substs ( :: std:: iter:: once ( ty:: subst:: GenericArg :: from ( ret_ty) ) ) ;
43-
44- let count_code_region_fn: Operand < ' _ > =
45- Operand :: function_handle ( tcx, callee_fn_def_id, substs, span) ;
46-
47- let index = const_int_operand ( tcx, span. clone ( ) , tcx. types . u32 , 0 ) ;
48-
49- let args = vec ! [ index] ;
50-
51- let source_info = SourceInfo { span : span, scope : OUTERMOST_SOURCE_SCOPE } ;
52-
53- let new_block = START_BLOCK + body. basic_blocks ( ) . len ( ) ;
54-
55- let next_local = body. local_decls . len ( ) ;
56- let new_temp = Local :: new ( next_local) ;
57- let unit_temp = Place :: from ( new_temp) ;
58-
59- let storage_live = Statement { source_info, kind : StatementKind :: StorageLive ( new_temp) } ;
60- let storage_dead = Statement { source_info, kind : StatementKind :: StorageDead ( new_temp) } ;
61-
62- let count_code_region_call = TerminatorKind :: Call {
63- func : count_code_region_fn,
64- args,
65- destination : Some ( ( unit_temp, new_block) ) ,
66- cleanup : None ,
67- from_hir_call : false ,
68- } ;
69-
70- body. local_decls . push ( LocalDecl :: new ( tcx. mk_unit ( ) , body. span ) ) ;
71- body. basic_blocks_mut ( ) . push ( BasicBlockData {
72- statements : vec ! [ storage_live] ,
73- is_cleanup : false ,
74- terminator : Some ( Terminator { source_info, kind : count_code_region_call } ) ,
75- } ) ;
76-
77- body. basic_blocks_mut ( ) . swap ( START_BLOCK , new_block) ;
78- body[ new_block] . statements . push ( storage_dead) ;
79-
80- // FIXME(richkadel): ALSO add each computed Span for each conditional branch to the coverage map
81- // and provide that map to LLVM to encode in the final binary.
73+ Operand :: function_handle ( tcx, fn_def_id, substs, span)
8274}
8375
8476fn const_int_operand < ' tcx > (
@@ -98,3 +90,15 @@ fn const_int_operand<'tcx>(
9890 literal : ty:: Const :: from_scalar ( tcx, Scalar :: from_uint ( val, size) , ty) ,
9991 } )
10092}
93+
94+ fn placeholder_block < ' tcx > ( source_info : SourceInfo ) -> BasicBlockData < ' tcx > {
95+ BasicBlockData {
96+ statements : vec ! [ ] ,
97+ terminator : Some ( Terminator {
98+ source_info,
99+ // this gets overwritten by the counter Call
100+ kind : TerminatorKind :: Unreachable ,
101+ } ) ,
102+ is_cleanup : false ,
103+ }
104+ }
0 commit comments