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
;
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
);
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`.
22 /// `num_expressions` is the number of counter expressions added to the MIR body.
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.
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.
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.
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
{
44 add_missing_operands
: bool
,
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.
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);
56 /// Computes an expression index for each expression ID, and updates `num_expressions` to the
57 /// maximum encountered index plus 1.
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);
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
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
80 self.update_num_counters(operand_id
)
82 self.update_num_expressions(operand_id
)
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
) {
95 self.visit_coverage(coverage
);
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
));
111 match coverage
.kind
{
112 CoverageKind
::Counter { id, .. }
=> {
113 self.update_num_counters(u32::from(id
));
115 CoverageKind
::Expression { id, .. }
=> {
116 self.update_num_expressions(u32::from(id
));
124 fn coverageinfo_from_mir
<'tcx
>(tcx
: TyCtxt
<'tcx
>, def_id
: DefId
) -> CoverageInfo
{
125 let mir_body
= mir_body(tcx
, def_id
);
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,
133 coverage_visitor
.visit_body(mir_body
);
135 coverage_visitor
.add_missing_operands
= true;
136 coverage_visitor
.visit_body(mir_body
);
138 coverage_visitor
.info
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
) {
150 return Some(code_region
.file_name
);
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
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
)
167 fn covered_code_regions
<'tcx
>(tcx
: TyCtxt
<'tcx
>, def_id
: DefId
) -> Vec
<&'tcx CodeRegion
> {
168 let body
= mir_body(tcx
, def_id
);
172 data
.statements
.iter().filter_map(|statement
| match statement
.kind
{
173 StatementKind
::Coverage(box ref coverage
) => {
174 if is_inlined(body
, statement
) {
177 coverage
.code_region
.as_ref() // may be None
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()