1
1
use crate :: transform:: { MirPass , MirSource } ;
2
- use rustc_index :: vec :: Idx ;
2
+ use crate :: util :: patch :: MirPatch ;
3
3
use rustc_middle:: mir:: interpret:: Scalar ;
4
4
use rustc_middle:: mir:: * ;
5
- use rustc_middle:: mir:: { Local , LocalDecl } ;
6
5
use rustc_middle:: ty;
7
6
use rustc_middle:: ty:: Ty ;
8
7
use rustc_middle:: ty:: TyCtxt ;
@@ -16,69 +15,62 @@ pub struct InstrumentCoverage;
16
15
* the intrinsic llvm.instrprof.increment.
17
16
*/
18
17
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
-
22
18
impl < ' tcx > MirPass < ' tcx > for InstrumentCoverage {
23
19
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , src : MirSource < ' tcx > , body : & mut Body < ' tcx > ) {
24
20
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) ;
29
23
}
30
24
}
31
25
}
32
26
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 > ) {
38
34
let span = body. span . shrink_to_lo ( ) ;
39
35
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 ( ) ;
41
71
let ret_ty = ret_ty. no_bound_vars ( ) . unwrap ( ) ;
42
72
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)
82
74
}
83
75
84
76
fn const_int_operand < ' tcx > (
@@ -98,3 +90,15 @@ fn const_int_operand<'tcx>(
98
90
literal : ty:: Const :: from_scalar ( tcx, Scalar :: from_uint ( val, size) , ty) ,
99
91
} )
100
92
}
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