Skip to content

Commit 851c319

Browse files
committed
coverage: Store the number of counters/expressions in function coverage info
Coverage codegen can now allocate arrays based on the number of counters/expressions originally used by the instrumentor. The existing query that inspects coverage statements is still used for determining the number of counters passed to `llvm.instrprof.increment`. If some high-numbered counters were removed by MIR optimizations, the instrumented binary can potentially use less memory and disk space at runtime.
1 parent a2045c4 commit 851c319

File tree

8 files changed

+71
-102
lines changed

8 files changed

+71
-102
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use rustc_middle::mir::coverage::{
66
CodeRegion, CounterId, ExpressionId, FunctionCoverageInfo, Op, Operand,
77
};
88
use rustc_middle::ty::Instance;
9-
use rustc_middle::ty::TyCtxt;
109

1110
#[derive(Clone, Debug, PartialEq)]
1211
pub struct Expression {
@@ -40,38 +39,36 @@ pub struct FunctionCoverage<'tcx> {
4039
impl<'tcx> FunctionCoverage<'tcx> {
4140
/// Creates a new set of coverage data for a used (called) function.
4241
pub fn new(
43-
tcx: TyCtxt<'tcx>,
4442
instance: Instance<'tcx>,
4543
function_coverage_info: &'tcx FunctionCoverageInfo,
4644
) -> Self {
47-
Self::create(tcx, instance, function_coverage_info, true)
45+
Self::create(instance, function_coverage_info, true)
4846
}
4947

5048
/// Creates a new set of coverage data for an unused (never called) function.
5149
pub fn unused(
52-
tcx: TyCtxt<'tcx>,
5350
instance: Instance<'tcx>,
5451
function_coverage_info: &'tcx FunctionCoverageInfo,
5552
) -> Self {
56-
Self::create(tcx, instance, function_coverage_info, false)
53+
Self::create(instance, function_coverage_info, false)
5754
}
5855

5956
fn create(
60-
tcx: TyCtxt<'tcx>,
6157
instance: Instance<'tcx>,
6258
function_coverage_info: &'tcx FunctionCoverageInfo,
6359
is_used: bool,
6460
) -> Self {
65-
let coverageinfo = tcx.coverageinfo(instance.def);
61+
let num_counters = function_coverage_info.num_counters;
62+
let num_expressions = function_coverage_info.num_expressions;
6663
debug!(
67-
"FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}",
68-
instance, coverageinfo, is_used
64+
"FunctionCoverage::create(instance={instance:?}) has \
65+
num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}"
6966
);
7067
Self {
7168
function_coverage_info,
7269
is_used,
73-
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
74-
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
70+
counters: IndexVec::from_elem_n(None, num_counters),
71+
expressions: IndexVec::from_elem_n(None, num_expressions),
7572
unreachable_regions: Vec::new(),
7673
}
7774
}

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
116116
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
117117
let func_coverage = coverage_map
118118
.entry(instance)
119-
.or_insert_with(|| FunctionCoverage::new(bx.tcx(), instance, function_coverage_info));
119+
.or_insert_with(|| FunctionCoverage::new(instance, function_coverage_info));
120120

121121
let Coverage { kind, code_regions } = coverage;
122122
match *kind {
@@ -126,11 +126,22 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
126126
// as that needs an exclusive borrow.
127127
drop(coverage_map);
128128

129-
let coverageinfo = bx.tcx().coverageinfo(instance.def);
129+
// The number of counters passed to `llvm.instrprof.increment` might
130+
// be smaller than the number originally inserted by the instrumentor,
131+
// if some high-numbered counters were removed by MIR optimizations.
132+
let num_counters =
133+
bx.tcx().coverage_ids_info(instance.def).max_counter_id.as_u32() + 1;
134+
if num_counters as usize > function_coverage_info.num_counters {
135+
// But it should never be larger.
136+
bug!(
137+
"num_counters disagreement: query says {num_counters} but function info only has {}",
138+
function_coverage_info.num_counters
139+
);
140+
}
130141

131142
let fn_name = bx.get_pgo_func_name_var(instance);
132143
let hash = bx.const_u64(function_coverage_info.function_source_hash);
133-
let num_counters = bx.const_u32(coverageinfo.num_counters);
144+
let num_counters = bx.const_u32(num_counters);
134145
let index = bx.const_u32(id.as_u32());
135146
debug!(
136147
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
@@ -210,7 +221,7 @@ fn add_unused_function_coverage<'tcx>(
210221
) {
211222
let tcx = cx.tcx;
212223

213-
let mut function_coverage = FunctionCoverage::unused(tcx, instance, function_coverage_info);
224+
let mut function_coverage = FunctionCoverage::unused(instance, function_coverage_info);
214225
for &code_region in tcx.covered_code_regions(def_id) {
215226
let code_region = std::slice::from_ref(code_region);
216227
function_coverage.add_unreachable_regions(code_region);

compiler/rustc_middle/src/mir/coverage.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,6 @@ impl Op {
140140
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
141141
pub struct FunctionCoverageInfo {
142142
pub function_source_hash: u64,
143+
pub num_counters: usize,
144+
pub num_expressions: usize,
143145
}

compiler/rustc_middle/src/mir/query.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Values computed by queries that use MIR.
22
3+
use crate::mir;
34
use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
45
use rustc_data_structures::fx::FxIndexMap;
56
use rustc_data_structures::unord::UnordSet;
@@ -445,14 +446,19 @@ pub struct DestructuredConstant<'tcx> {
445446
pub fields: &'tcx [(ConstValue<'tcx>, Ty<'tcx>)],
446447
}
447448

448-
/// Coverage information summarized from a MIR if instrumented for source code coverage (see
449-
/// compiler option `-Cinstrument-coverage`). This information is generated by the
450-
/// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query.
449+
/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
450+
/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
451+
/// have had a chance to potentially remove some of them.
452+
///
453+
/// Used by the `coverage_ids_info` query.
451454
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)]
452-
pub struct CoverageInfo {
453-
/// The total number of coverage region counters added to the MIR `Body`.
454-
pub num_counters: u32,
455-
456-
/// The total number of coverage region counter expressions added to the MIR `Body`.
457-
pub num_expressions: u32,
455+
pub struct CoverageIdsInfo {
456+
/// Coverage codegen needs to know the highest counter ID that is ever
457+
/// incremented within a function, so that it can set the `num-counters`
458+
/// argument of the `llvm.instrprof.increment` intrinsic.
459+
///
460+
/// This may be less than the highest counter ID emitted by the
461+
/// InstrumentCoverage MIR pass, if the highest-numbered counter increments
462+
/// were removed by MIR optimizations.
463+
pub max_counter_id: mir::coverage::CounterId,
458464
}

compiler/rustc_middle/src/query/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -573,10 +573,11 @@ rustc_queries! {
573573
separate_provide_extern
574574
}
575575

576-
/// Returns coverage summary info for a function, after executing the `InstrumentCoverage`
577-
/// MIR pass (assuming the -Cinstrument-coverage option is enabled).
578-
query coverageinfo(key: ty::InstanceDef<'tcx>) -> &'tcx mir::CoverageInfo {
579-
desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key.def_id()) }
576+
/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
577+
/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
578+
/// have had a chance to potentially remove some of them.
579+
query coverage_ids_info(key: ty::InstanceDef<'tcx>) -> &'tcx mir::CoverageIdsInfo {
580+
desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) }
580581
arena_cache
581582
}
582583

compiler/rustc_mir_transform/src/coverage/counters.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ impl CoverageCounters {
126126
next
127127
}
128128

129+
pub(super) fn num_counters(&self) -> usize {
130+
self.next_counter_id.as_usize()
131+
}
132+
133+
pub(super) fn num_expressions(&self) -> usize {
134+
self.next_expression_id.as_usize()
135+
}
136+
129137
fn set_bcb_counter(
130138
&mut self,
131139
bcb: BasicCoverageBlock,

compiler/rustc_mir_transform/src/coverage/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
214214

215215
self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
216216
function_source_hash: self.function_source_hash,
217+
num_counters: self.coverage_counters.num_counters(),
218+
num_expressions: self.coverage_counters.num_expressions(),
217219
}));
218220
}
219221

compiler/rustc_mir_transform/src/coverage/query.rs

Lines changed: 16 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,92 +2,34 @@ use super::*;
22

33
use rustc_data_structures::captures::Captures;
44
use rustc_middle::mir::coverage::*;
5-
use rustc_middle::mir::{self, Body, Coverage, CoverageInfo};
5+
use rustc_middle::mir::{self, Body, Coverage, CoverageIdsInfo};
66
use rustc_middle::query::Providers;
77
use rustc_middle::ty::{self, TyCtxt};
88
use rustc_span::def_id::DefId;
99

1010
/// A `query` provider for retrieving coverage information injected into MIR.
1111
pub(crate) fn provide(providers: &mut Providers) {
12-
providers.coverageinfo = |tcx, def_id| coverageinfo(tcx, def_id);
12+
providers.coverage_ids_info = |tcx, def_id| coverage_ids_info(tcx, def_id);
1313
providers.covered_code_regions = |tcx, def_id| covered_code_regions(tcx, def_id);
1414
}
1515

16-
/// Coverage codegen needs to know the total number of counter IDs and expression IDs that have
17-
/// been used by a function's coverage mappings. These totals are used to create vectors to hold
18-
/// the relevant counter and expression data, and the maximum counter ID (+ 1) is also needed by
19-
/// the `llvm.instrprof.increment` intrinsic.
20-
///
21-
/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code
22-
/// including injected counters. (It is OK if some counters are optimized out, but those counters
23-
/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the
24-
/// calls may not work; but computing the number of counters or expressions by adding `1` to the
25-
/// highest ID (for a given instrumented function) is valid.
26-
///
27-
/// It's possible for a coverage expression to remain in MIR while one or both of its operands
28-
/// have been optimized away. To avoid problems in codegen, we include those operands' IDs when
29-
/// determining the maximum counter/expression ID, even if the underlying counter/expression is
30-
/// no longer present.
31-
struct CoverageVisitor {
32-
max_counter_id: CounterId,
33-
max_expression_id: ExpressionId,
34-
}
35-
36-
impl CoverageVisitor {
37-
/// Updates `max_counter_id` to the maximum encountered counter ID.
38-
#[inline(always)]
39-
fn update_max_counter_id(&mut self, counter_id: CounterId) {
40-
self.max_counter_id = self.max_counter_id.max(counter_id);
41-
}
42-
43-
/// Updates `max_expression_id` to the maximum encountered expression ID.
44-
#[inline(always)]
45-
fn update_max_expression_id(&mut self, expression_id: ExpressionId) {
46-
self.max_expression_id = self.max_expression_id.max(expression_id);
47-
}
48-
49-
fn update_from_expression_operand(&mut self, operand: Operand) {
50-
match operand {
51-
Operand::Counter(id) => self.update_max_counter_id(id),
52-
Operand::Expression(id) => self.update_max_expression_id(id),
53-
Operand::Zero => {}
54-
}
55-
}
56-
57-
fn visit_body(&mut self, body: &Body<'_>) {
58-
for coverage in all_coverage_in_mir_body(body) {
59-
self.visit_coverage(coverage);
60-
}
61-
}
62-
63-
fn visit_coverage(&mut self, coverage: &Coverage) {
64-
match coverage.kind {
65-
CoverageKind::Counter { id, .. } => self.update_max_counter_id(id),
66-
CoverageKind::Expression { id, lhs, rhs, .. } => {
67-
self.update_max_expression_id(id);
68-
self.update_from_expression_operand(lhs);
69-
self.update_from_expression_operand(rhs);
70-
}
71-
CoverageKind::Unreachable => {}
72-
}
73-
}
74-
}
75-
76-
fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) -> CoverageInfo {
16+
/// Query implementation for `coverage_ids_info`.
17+
fn coverage_ids_info<'tcx>(
18+
tcx: TyCtxt<'tcx>,
19+
instance_def: ty::InstanceDef<'tcx>,
20+
) -> CoverageIdsInfo {
7721
let mir_body = tcx.instance_mir(instance_def);
7822

79-
let mut coverage_visitor = CoverageVisitor {
80-
max_counter_id: CounterId::START,
81-
max_expression_id: ExpressionId::START,
82-
};
83-
84-
coverage_visitor.visit_body(mir_body);
23+
let max_counter_id = all_coverage_in_mir_body(mir_body)
24+
.filter_map(|coverage| match coverage.kind {
25+
CoverageKind::Counter { id } => Some(id),
26+
CoverageKind::Expression { .. } => None,
27+
CoverageKind::Unreachable => None,
28+
})
29+
.max()
30+
.unwrap_or(CounterId::START);
8531

86-
// Add 1 to the highest IDs to get the total number of IDs.
87-
CoverageInfo {
88-
num_counters: (coverage_visitor.max_counter_id + 1).as_u32(),
89-
num_expressions: (coverage_visitor.max_expression_id + 1).as_u32(),
90-
}
32+
CoverageIdsInfo { max_counter_id }
9133
}
9234

9335
fn covered_code_regions(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<&CodeRegion> {

0 commit comments

Comments
 (0)