]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/instrument_coverage.rs
27abe813b067dba96cbc99177759f4eb78707c05
[rust.git] / src / librustc_mir / transform / instrument_coverage.rs
1 use crate::transform::{MirPass, MirSource};
2 use crate::util::patch::MirPatch;
3 use rustc_middle::mir::interpret::Scalar;
4 use rustc_middle::mir::*;
5 use rustc_middle::ty;
6 use rustc_middle::ty::Ty;
7 use rustc_middle::ty::TyCtxt;
8 use rustc_span::def_id::DefId;
9 use rustc_span::Span;
10 use rustc_target::abi;
11
12 pub struct InstrumentCoverage;
13
14 /**
15  * Inserts call to count_code_region() as a placeholder to be replaced during code generation with
16  * the intrinsic llvm.instrprof.increment.
17  */
18
19 impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
20     fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
21         if tcx.sess.opts.debugging_opts.instrument_coverage {
22             debug!("instrumenting {:?}", src.def_id());
23             instrument_coverage(tcx, body);
24         }
25     }
26 }
27
28 // The first counter (start of the function) is index zero.
29 const INIT_FUNCTION_COUNTER: u32 = 0;
30
31 /// Injects calls to placeholder function `count_code_region()`.
32 // FIXME(richkadel): As a first step, counters are only injected at the top of each function.
33 // The complete solution will inject counters at each conditional code branch.
34 pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
35     let span = body.span.shrink_to_lo();
36
37     let count_code_region_fn =
38         function_handle(tcx, span, tcx.lang_items().count_code_region_fn().unwrap());
39     let counter_index =
40         const_int_operand(tcx, span, tcx.types.u32, Scalar::from_u32(INIT_FUNCTION_COUNTER));
41
42     let mut patch = MirPatch::new(body);
43
44     let new_block = patch.new_block(placeholder_block(SourceInfo::outermost(body.span)));
45     let next_block = START_BLOCK;
46
47     let temp = patch.new_temp(tcx.mk_unit(), body.span);
48     patch.patch_terminator(
49         new_block,
50         TerminatorKind::Call {
51             func: count_code_region_fn,
52             args: vec![counter_index],
53             // new_block will swapped with the next_block, after applying patch
54             destination: Some((Place::from(temp), new_block)),
55             cleanup: None,
56             from_hir_call: false,
57         },
58     );
59
60     patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp));
61     patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp));
62
63     patch.apply(body);
64
65     // To insert the `new_block` in front of the first block in the counted branch (for example,
66     // the START_BLOCK, at the top of the function), just swap the indexes, leaving the rest of the
67     // graph unchanged.
68     body.basic_blocks_mut().swap(next_block, new_block);
69 }
70
71 fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, span: Span, fn_def_id: DefId) -> Operand<'tcx> {
72     let ret_ty = tcx.fn_sig(fn_def_id).output();
73     let ret_ty = ret_ty.no_bound_vars().unwrap();
74     let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty)));
75     Operand::function_handle(tcx, fn_def_id, substs, span)
76 }
77
78 fn const_int_operand<'tcx>(
79     tcx: TyCtxt<'tcx>,
80     span: Span,
81     ty: Ty<'tcx>,
82     val: Scalar,
83 ) -> Operand<'tcx> {
84     debug_assert!({
85         let param_env_and_ty = ty::ParamEnv::empty().and(ty);
86         let type_size = tcx
87             .layout_of(param_env_and_ty)
88             .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e))
89             .size;
90         let scalar_size = abi::Size::from_bytes(match val {
91             Scalar::Raw { size, .. } => size,
92             _ => panic!("Invalid scalar type {:?}", val),
93         });
94         scalar_size == type_size
95     });
96     Operand::Constant(box Constant {
97         span,
98         user_ty: None,
99         literal: ty::Const::from_scalar(tcx, val, ty),
100     })
101 }
102
103 fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> {
104     BasicBlockData {
105         statements: vec![],
106         terminator: Some(Terminator {
107             source_info,
108             // this gets overwritten by the counter Call
109             kind: TerminatorKind::Unreachable,
110         }),
111         is_cleanup: false,
112     }
113 }