3 use rustc_middle
::mir
::coverage
::*;
4 use rustc_middle
::mir
::visit
::Visitor
;
5 use rustc_middle
::mir
::{self, Coverage, CoverageInfo, Location}
;
6 use rustc_middle
::ty
::query
::Providers
;
7 use rustc_middle
::ty
::TyCtxt
;
8 use rustc_span
::def_id
::DefId
;
10 /// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each
11 /// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR).
12 pub(crate) fn provide(providers
: &mut Providers
) {
13 providers
.coverageinfo
= |tcx
, def_id
| coverageinfo_from_mir(tcx
, def_id
);
14 providers
.covered_file_name
= |tcx
, def_id
| covered_file_name(tcx
, def_id
);
15 providers
.covered_code_regions
= |tcx
, def_id
| covered_code_regions(tcx
, def_id
);
18 /// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in
19 /// other words, the number of counter value references injected into the MIR (plus 1 for the
20 /// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected
21 /// counters have a counter ID from `1..num_counters-1`.
23 /// `num_expressions` is the number of counter expressions added to the MIR body.
25 /// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend
26 /// code generate, to lookup counters and expressions by simple u32 indexes.
28 /// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code
29 /// including injected counters. (It is OK if some counters are optimized out, but those counters
30 /// are still included in the total `num_counters` or `num_expressions`.) Simply counting the
31 /// calls may not work; but computing the number of counters or expressions by adding `1` to the
32 /// highest ID (for a given instrumented function) is valid.
34 /// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum
35 /// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a
36 /// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression
37 /// IDs referenced by expression operands, if not already seen.
39 /// Ideally, each operand ID in a MIR `CoverageKind::Expression` will have a separate MIR `Coverage`
40 /// statement for the `Counter` or `Expression` with the referenced ID. but since current or future
41 /// MIR optimizations can theoretically optimize out segments of a MIR, it may not be possible to
42 /// guarantee this, so the second pass ensures the `CoverageInfo` counts include all referenced IDs.
43 struct CoverageVisitor
{
45 add_missing_operands
: bool
,
48 impl CoverageVisitor
{
49 /// Updates `num_counters` to the maximum encountered zero-based counter_id plus 1. Note the
50 /// final computed number of counters should be the number of all `CoverageKind::Counter`
51 /// statements in the MIR *plus one* for the implicit `ZERO` counter.
53 fn update_num_counters(&mut self, counter_id
: u32) {
54 self.info
.num_counters
= std
::cmp
::max(self.info
.num_counters
, counter_id
+ 1);
57 /// Computes an expression index for each expression ID, and updates `num_expressions` to the
58 /// maximum encountered index plus 1.
60 fn update_num_expressions(&mut self, expression_id
: u32) {
61 let expression_index
= u32::MAX
- expression_id
;
62 self.info
.num_expressions
= std
::cmp
::max(self.info
.num_expressions
, expression_index
+ 1);
65 fn update_from_expression_operand(&mut self, operand_id
: u32) {
66 if operand_id
>= self.info
.num_counters
{
67 let operand_as_expression_index
= u32::MAX
- operand_id
;
68 if operand_as_expression_index
>= self.info
.num_expressions
{
69 // The operand ID is outside the known range of counter IDs and also outside the
70 // known range of expression IDs. In either case, the result of a missing operand
71 // (if and when used in an expression) will be zero, so from a computation
72 // perspective, it doesn't matter whether it is interepretted as a counter or an
75 // However, the `num_counters` and `num_expressions` query results are used to
76 // allocate arrays when generating the coverage map (during codegen), so choose
77 // the type that grows either `num_counters` or `num_expressions` the least.
78 if operand_id
- self.info
.num_counters
79 < operand_as_expression_index
- self.info
.num_expressions
81 self.update_num_counters(operand_id
)
83 self.update_num_expressions(operand_id
)
90 impl Visitor
<'_
> for CoverageVisitor
{
91 fn visit_coverage(&mut self, coverage
: &Coverage
, _location
: Location
) {
92 if self.add_missing_operands
{
94 CoverageKind
::Expression { lhs, rhs, .. }
=> {
95 self.update_from_expression_operand(u32::from(lhs
));
96 self.update_from_expression_operand(u32::from(rhs
));
101 match coverage
.kind
{
102 CoverageKind
::Counter { id, .. }
=> {
103 self.update_num_counters(u32::from(id
));
105 CoverageKind
::Expression { id, .. }
=> {
106 self.update_num_expressions(u32::from(id
));
114 fn coverageinfo_from_mir
<'tcx
>(tcx
: TyCtxt
<'tcx
>, def_id
: DefId
) -> CoverageInfo
{
115 let mir_body
= tcx
.optimized_mir(def_id
);
117 let mut coverage_visitor
= CoverageVisitor
{
118 // num_counters always has at least the `ZERO` counter.
119 info
: CoverageInfo { num_counters: 1, num_expressions: 0 }
,
120 add_missing_operands
: false,
123 coverage_visitor
.visit_body(mir_body
);
125 coverage_visitor
.add_missing_operands
= true;
126 coverage_visitor
.visit_body(mir_body
);
128 coverage_visitor
.info
131 fn covered_file_name
<'tcx
>(tcx
: TyCtxt
<'tcx
>, def_id
: DefId
) -> Option
<Symbol
> {
132 let mir_body
= tcx
.optimized_mir(def_id
);
133 for bb_data
in mir_body
.basic_blocks().iter() {
134 for statement
in bb_data
.statements
.iter() {
135 if let StatementKind
::Coverage(box ref coverage
) = statement
.kind
{
136 if let Some(code_region
) = coverage
.code_region
.as_ref() {
137 return Some(code_region
.file_name
);
145 fn covered_code_regions
<'tcx
>(tcx
: TyCtxt
<'tcx
>, def_id
: DefId
) -> Vec
<&'tcx CodeRegion
> {
146 let mir_body
: &'tcx mir
::Body
<'tcx
> = tcx
.optimized_mir(def_id
);
151 data
.statements
.iter().filter_map(|statement
| match statement
.kind
{
152 StatementKind
::Coverage(box ref coverage
) => {
153 coverage
.code_region
.as_ref() // may be None