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