]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_transform/src/coverage/query.rs
Rollup merge of #90132 - joshtriplett:stabilize-instrument-coverage, r=wesleywiser
[rust.git] / compiler / rustc_mir_transform / src / coverage / query.rs
1 use super::*;
2
3 use rustc_middle::mir::coverage::*;
4 use rustc_middle::mir::{self, Body, Coverage, CoverageInfo};
5 use rustc_middle::ty::query::Providers;
6 use rustc_middle::ty::{self, TyCtxt};
7 use rustc_span::def_id::DefId;
8
9 /// A `query` provider for retrieving coverage information injected into MIR.
10 pub(crate) fn provide(providers: &mut Providers) {
11     providers.coverageinfo = |tcx, def_id| coverageinfo(tcx, def_id);
12     providers.covered_code_regions = |tcx, def_id| covered_code_regions(tcx, def_id);
13 }
14
15 /// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in
16 /// other words, the number of counter value references injected into the MIR (plus 1 for the
17 /// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected
18 /// counters have a counter ID from `1..num_counters-1`.
19 ///
20 /// `num_expressions` is the number of counter expressions added to the MIR body.
21 ///
22 /// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend
23 /// code generate, to lookup counters and expressions by simple u32 indexes.
24 ///
25 /// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code
26 /// including injected counters. (It is OK if some counters are optimized out, but those counters
27 /// are still included in the total `num_counters` or `num_expressions`.) Simply counting the
28 /// calls may not work; but computing the number of counters or expressions by adding `1` to the
29 /// highest ID (for a given instrumented function) is valid.
30 ///
31 /// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum
32 /// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a
33 /// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression
34 /// IDs referenced by expression operands, if not already seen.
35 ///
36 /// Ideally, each operand ID in a MIR `CoverageKind::Expression` will have a separate MIR `Coverage`
37 /// statement for the `Counter` or `Expression` with the referenced ID. but since current or future
38 /// MIR optimizations can theoretically optimize out segments of a MIR, it may not be possible to
39 /// guarantee this, so the second pass ensures the `CoverageInfo` counts include all referenced IDs.
40 struct CoverageVisitor {
41     info: CoverageInfo,
42     add_missing_operands: bool,
43 }
44
45 impl CoverageVisitor {
46     /// Updates `num_counters` to the maximum encountered zero-based counter_id plus 1. Note the
47     /// final computed number of counters should be the number of all `CoverageKind::Counter`
48     /// statements in the MIR *plus one* for the implicit `ZERO` counter.
49     #[inline(always)]
50     fn update_num_counters(&mut self, counter_id: u32) {
51         self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1);
52     }
53
54     /// Computes an expression index for each expression ID, and updates `num_expressions` to the
55     /// maximum encountered index plus 1.
56     #[inline(always)]
57     fn update_num_expressions(&mut self, expression_id: u32) {
58         let expression_index = u32::MAX - expression_id;
59         self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1);
60     }
61
62     fn update_from_expression_operand(&mut self, operand_id: u32) {
63         if operand_id >= self.info.num_counters {
64             let operand_as_expression_index = u32::MAX - operand_id;
65             if operand_as_expression_index >= self.info.num_expressions {
66                 // The operand ID is outside the known range of counter IDs and also outside the
67                 // known range of expression IDs. In either case, the result of a missing operand
68                 // (if and when used in an expression) will be zero, so from a computation
69                 // perspective, it doesn't matter whether it is interepretted as a counter or an
70                 // expression.
71                 //
72                 // However, the `num_counters` and `num_expressions` query results are used to
73                 // allocate arrays when generating the coverage map (during codegen), so choose
74                 // the type that grows either `num_counters` or `num_expressions` the least.
75                 if operand_id - self.info.num_counters
76                     < operand_as_expression_index - self.info.num_expressions
77                 {
78                     self.update_num_counters(operand_id)
79                 } else {
80                     self.update_num_expressions(operand_id)
81                 }
82             }
83         }
84     }
85
86     fn visit_body(&mut self, body: &Body<'_>) {
87         for bb_data in body.basic_blocks().iter() {
88             for statement in bb_data.statements.iter() {
89                 if let StatementKind::Coverage(box ref coverage) = statement.kind {
90                     if is_inlined(body, statement) {
91                         continue;
92                     }
93                     self.visit_coverage(coverage);
94                 }
95             }
96         }
97     }
98
99     fn visit_coverage(&mut self, coverage: &Coverage) {
100         if self.add_missing_operands {
101             match coverage.kind {
102                 CoverageKind::Expression { lhs, rhs, .. } => {
103                     self.update_from_expression_operand(u32::from(lhs));
104                     self.update_from_expression_operand(u32::from(rhs));
105                 }
106                 _ => {}
107             }
108         } else {
109             match coverage.kind {
110                 CoverageKind::Counter { id, .. } => {
111                     self.update_num_counters(u32::from(id));
112                 }
113                 CoverageKind::Expression { id, .. } => {
114                     self.update_num_expressions(u32::from(id));
115                 }
116                 _ => {}
117             }
118         }
119     }
120 }
121
122 fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) -> CoverageInfo {
123     let mir_body = tcx.instance_mir(instance_def);
124
125     let mut coverage_visitor = CoverageVisitor {
126         // num_counters always has at least the `ZERO` counter.
127         info: CoverageInfo { num_counters: 1, num_expressions: 0 },
128         add_missing_operands: false,
129     };
130
131     coverage_visitor.visit_body(mir_body);
132
133     coverage_visitor.add_missing_operands = true;
134     coverage_visitor.visit_body(mir_body);
135
136     coverage_visitor.info
137 }
138
139 fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx CodeRegion> {
140     let body = mir_body(tcx, def_id);
141     body.basic_blocks()
142         .iter()
143         .map(|data| {
144             data.statements.iter().filter_map(|statement| match statement.kind {
145                 StatementKind::Coverage(box ref coverage) => {
146                     if is_inlined(body, statement) {
147                         None
148                     } else {
149                         coverage.code_region.as_ref() // may be None
150                     }
151                 }
152                 _ => None,
153             })
154         })
155         .flatten()
156         .collect()
157 }
158
159 fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool {
160     let scope_data = &body.source_scopes[statement.source_info.scope];
161     scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some()
162 }
163
164 /// This function ensures we obtain the correct MIR for the given item irrespective of
165 /// whether that means const mir or runtime mir. For `const fn` this opts for runtime
166 /// mir.
167 fn mir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx mir::Body<'tcx> {
168     let id = ty::WithOptConstParam::unknown(def_id);
169     let def = ty::InstanceDef::Item(id);
170     tcx.instance_mir(def)
171 }