]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/instrument_coverage.rs
045cd03d1f7da21c12de8e2e58fdf9e4ccd7f522
[rust.git] / src / librustc_mir / transform / instrument_coverage.rs
1 use crate::transform::{MirPass, MirSource};
2 use rustc_index::vec::Idx;
3 use rustc_middle::mir::interpret::Scalar;
4 use rustc_middle::mir::*;
5 use rustc_middle::mir::{Local, LocalDecl};
6 use rustc_middle::ty;
7 use rustc_middle::ty::Ty;
8 use rustc_middle::ty::TyCtxt;
9 use rustc_span::def_id::DefId;
10 use rustc_span::Span;
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 // 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 impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
23     fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
24         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             }
29         }
30     }
31 }
32
33 pub fn instrument_coverage<'tcx>(
34     tcx: TyCtxt<'tcx>,
35     callee_fn_def_id: DefId,
36     body: &mut Body<'tcx>,
37 ) {
38     let span = body.span.shrink_to_lo();
39
40     let ret_ty = tcx.fn_sig(callee_fn_def_id).output();
41     let ret_ty = ret_ty.no_bound_vars().unwrap();
42     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.
82 }
83
84 fn const_int_operand<'tcx>(
85     tcx: TyCtxt<'tcx>,
86     span: Span,
87     ty: Ty<'tcx>,
88     val: u128,
89 ) -> Operand<'tcx> {
90     let param_env_and_ty = ty::ParamEnv::empty().and(ty);
91     let size = tcx
92         .layout_of(param_env_and_ty)
93         .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e))
94         .size;
95     Operand::Constant(box Constant {
96         span,
97         user_ty: None,
98         literal: ty::Const::from_scalar(tcx, Scalar::from_uint(val, size), ty),
99     })
100 }