]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir_transform/src/coverage/query.rs
New upstream version 1.73.0+dfsg1
[rustc.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::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 counter ID plus 1.
47 #[inline(always)]
48 fn update_num_counters(&mut self, counter_id: CounterId) {
49 let counter_id = counter_id.as_u32();
50 self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1);
51 }
52
53 /// Updates `num_expressions` to the maximum encountered expression ID plus 1.
54 #[inline(always)]
55 fn update_num_expressions(&mut self, expression_id: ExpressionId) {
56 let expression_id = expression_id.as_u32();
57 self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_id + 1);
58 }
59
60 fn update_from_expression_operand(&mut self, operand: Operand) {
61 match operand {
62 Operand::Counter(id) => self.update_num_counters(id),
63 Operand::Expression(id) => self.update_num_expressions(id),
64 Operand::Zero => {}
65 }
66 }
67
68 fn visit_body(&mut self, body: &Body<'_>) {
69 for bb_data in body.basic_blocks.iter() {
70 for statement in bb_data.statements.iter() {
71 if let StatementKind::Coverage(box ref coverage) = statement.kind {
72 if is_inlined(body, statement) {
73 continue;
74 }
75 self.visit_coverage(coverage);
76 }
77 }
78 }
79 }
80
81 fn visit_coverage(&mut self, coverage: &Coverage) {
82 if self.add_missing_operands {
83 match coverage.kind {
84 CoverageKind::Expression { lhs, rhs, .. } => {
85 self.update_from_expression_operand(lhs);
86 self.update_from_expression_operand(rhs);
87 }
88 _ => {}
89 }
90 } else {
91 match coverage.kind {
92 CoverageKind::Counter { id, .. } => self.update_num_counters(id),
93 CoverageKind::Expression { id, .. } => self.update_num_expressions(id),
94 _ => {}
95 }
96 }
97 }
98 }
99
100 fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) -> CoverageInfo {
101 let mir_body = tcx.instance_mir(instance_def);
102
103 let mut coverage_visitor = CoverageVisitor {
104 info: CoverageInfo { num_counters: 0, num_expressions: 0 },
105 add_missing_operands: false,
106 };
107
108 coverage_visitor.visit_body(mir_body);
109
110 coverage_visitor.add_missing_operands = true;
111 coverage_visitor.visit_body(mir_body);
112
113 coverage_visitor.info
114 }
115
116 fn covered_code_regions(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<&CodeRegion> {
117 let body = mir_body(tcx, def_id);
118 body.basic_blocks
119 .iter()
120 .flat_map(|data| {
121 data.statements.iter().filter_map(|statement| match statement.kind {
122 StatementKind::Coverage(box ref coverage) => {
123 if is_inlined(body, statement) {
124 None
125 } else {
126 coverage.code_region.as_ref() // may be None
127 }
128 }
129 _ => None,
130 })
131 })
132 .collect()
133 }
134
135 fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool {
136 let scope_data = &body.source_scopes[statement.source_info.scope];
137 scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some()
138 }
139
140 /// This function ensures we obtain the correct MIR for the given item irrespective of
141 /// whether that means const mir or runtime mir. For `const fn` this opts for runtime
142 /// mir.
143 fn mir_body(tcx: TyCtxt<'_>, def_id: DefId) -> &mir::Body<'_> {
144 let def = ty::InstanceDef::Item(def_id);
145 tcx.instance_mir(def)
146 }