Skip to content

Commit c479bc7

Browse files
committed
coverage: Attach an optional FunctionCoverageInfo to mir::Body
This allows coverage information to be attached to the function as a whole when appropriate, instead of being smuggled through coverage statements in the function's basic blocks. As an example, this patch moves the `function_source_hash` value out of individual `CoverageKind::Counter` statements and into the per-function info. When synthesizing unused functions for coverage purposes, the absence of this info is taken to indicate that a function was not eligible for coverage and should not be synthesized.
1 parent 6d7160c commit c479bc7

File tree

7 files changed

+74
-54
lines changed

7 files changed

+74
-54
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

+25-27
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
22

33
use rustc_data_structures::fx::FxIndexSet;
44
use rustc_index::IndexVec;
5-
use rustc_middle::mir::coverage::{CodeRegion, CounterId, ExpressionId, Op, Operand};
5+
use rustc_middle::mir::coverage::{
6+
CodeRegion, CounterId, ExpressionId, FunctionCoverageInfo, Op, Operand,
7+
};
68
use rustc_middle::ty::Instance;
79
use rustc_middle::ty::TyCtxt;
810

@@ -27,8 +29,8 @@ pub struct Expression {
2729
/// line."
2830
#[derive(Debug)]
2931
pub struct FunctionCoverage<'tcx> {
30-
instance: Instance<'tcx>,
31-
source_hash: u64,
32+
/// Coverage info that was attached to this function by the instrumentor.
33+
function_coverage_info: &'tcx FunctionCoverageInfo,
3234
is_used: bool,
3335
counters: IndexVec<CounterId, Option<Vec<CodeRegion>>>,
3436
expressions: IndexVec<ExpressionId, Option<Expression>>,
@@ -37,24 +39,36 @@ pub struct FunctionCoverage<'tcx> {
3739

3840
impl<'tcx> FunctionCoverage<'tcx> {
3941
/// Creates a new set of coverage data for a used (called) function.
40-
pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
41-
Self::create(tcx, instance, true)
42+
pub fn new(
43+
tcx: TyCtxt<'tcx>,
44+
instance: Instance<'tcx>,
45+
function_coverage_info: &'tcx FunctionCoverageInfo,
46+
) -> Self {
47+
Self::create(tcx, instance, function_coverage_info, true)
4248
}
4349

4450
/// Creates a new set of coverage data for an unused (never called) function.
45-
pub fn unused(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
46-
Self::create(tcx, instance, false)
51+
pub fn unused(
52+
tcx: TyCtxt<'tcx>,
53+
instance: Instance<'tcx>,
54+
function_coverage_info: &'tcx FunctionCoverageInfo,
55+
) -> Self {
56+
Self::create(tcx, instance, function_coverage_info, false)
4757
}
4858

49-
fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self {
59+
fn create(
60+
tcx: TyCtxt<'tcx>,
61+
instance: Instance<'tcx>,
62+
function_coverage_info: &'tcx FunctionCoverageInfo,
63+
is_used: bool,
64+
) -> Self {
5065
let coverageinfo = tcx.coverageinfo(instance.def);
5166
debug!(
5267
"FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}",
5368
instance, coverageinfo, is_used
5469
);
5570
Self {
56-
instance,
57-
source_hash: 0, // will be set with the first `add_counter()`
71+
function_coverage_info,
5872
is_used,
5973
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
6074
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
@@ -67,16 +81,6 @@ impl<'tcx> FunctionCoverage<'tcx> {
6781
self.is_used
6882
}
6983

70-
/// Sets the function source hash value. If called multiple times for the same function, all
71-
/// calls should have the same hash value.
72-
pub fn set_function_source_hash(&mut self, source_hash: u64) {
73-
if self.source_hash == 0 {
74-
self.source_hash = source_hash;
75-
} else {
76-
debug_assert_eq!(source_hash, self.source_hash);
77-
}
78-
}
79-
8084
/// Adds code regions to be counted by an injected counter intrinsic.
8185
#[instrument(level = "debug", skip(self))]
8286
pub(crate) fn add_counter(&mut self, id: CounterId, code_regions: &[CodeRegion]) {
@@ -195,7 +199,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
195199
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
196200
/// or not the source code structure changed between different compilations.
197201
pub fn source_hash(&self) -> u64 {
198-
self.source_hash
202+
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
199203
}
200204

201205
/// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their
@@ -204,12 +208,6 @@ impl<'tcx> FunctionCoverage<'tcx> {
204208
pub fn get_expressions_and_counter_regions(
205209
&self,
206210
) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) {
207-
assert!(
208-
self.source_hash != 0 || !self.is_used,
209-
"No counters provided the source_hash for used function: {:?}",
210-
self.instance
211-
);
212-
213211
let counter_expressions = self.counter_expressions();
214212
// Expression IDs are indices into `self.expressions`, and on the LLVM
215213
// side they will be treated as indices into `counter_expressions`, so

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ use rustc_hir::def::DefKind;
1010
use rustc_hir::def_id::DefId;
1111
use rustc_index::IndexVec;
1212
use rustc_middle::bug;
13-
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1413
use rustc_middle::mir::coverage::CodeRegion;
15-
use rustc_middle::ty::TyCtxt;
14+
use rustc_middle::ty::{self, TyCtxt};
1615
use rustc_span::Symbol;
1716

1817
/// Generates and exports the Coverage Map.
@@ -331,16 +330,14 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
331330
for non_codegenned_def_id in
332331
eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id))
333332
{
334-
let codegen_fn_attrs = tcx.codegen_fn_attrs(non_codegenned_def_id);
335-
336-
// If a function is marked `#[coverage(off)]`, then skip generating a
337-
// dead code stub for it.
338-
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
339-
debug!("skipping unused fn marked #[coverage(off)]: {:?}", non_codegenned_def_id);
333+
// Skip any function that didn't have coverage data added to it by the
334+
// coverage instrumentor.
335+
let body = tcx.instance_mir(ty::InstanceDef::Item(non_codegenned_def_id));
336+
let Some(function_coverage_info) = body.function_coverage_info.as_deref() else {
340337
continue;
341-
}
338+
};
342339

343340
debug!("generating unused fn: {:?}", non_codegenned_def_id);
344-
cx.define_unused_fn(non_codegenned_def_id);
341+
cx.define_unused_fn(non_codegenned_def_id, function_coverage_info);
345342
}
346343
}

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+16-12
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_hir as hir;
1616
use rustc_hir::def_id::DefId;
1717
use rustc_llvm::RustString;
1818
use rustc_middle::bug;
19-
use rustc_middle::mir::coverage::{CounterId, CoverageKind};
19+
use rustc_middle::mir::coverage::{CounterId, CoverageKind, FunctionCoverageInfo};
2020
use rustc_middle::mir::Coverage;
2121
use rustc_middle::ty;
2222
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
@@ -91,31 +91,34 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
9191
/// codegenned, collect the coverage `CodeRegion`s from the MIR and add
9292
/// them. Since the function is never called, all of its `CodeRegion`s can be
9393
/// added as `unreachable_region`s.
94-
fn define_unused_fn(&self, def_id: DefId) {
94+
fn define_unused_fn(&self, def_id: DefId, function_coverage_info: &'tcx FunctionCoverageInfo) {
9595
let instance = declare_unused_fn(self, def_id);
9696
codegen_unused_fn_and_counter(self, instance);
97-
add_unused_function_coverage(self, instance, def_id);
97+
add_unused_function_coverage(self, instance, def_id, function_coverage_info);
9898
}
9999
}
100100

101101
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
102+
#[instrument(level = "debug", skip(self))]
102103
fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) {
103104
let bx = self;
104105

106+
let Some(function_coverage_info) =
107+
bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
108+
else {
109+
debug!("function has a coverage statement but no coverage info");
110+
return;
111+
};
112+
105113
let Some(coverage_context) = bx.coverage_context() else { return };
106114
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
107115
let func_coverage = coverage_map
108116
.entry(instance)
109-
.or_insert_with(|| FunctionCoverage::new(bx.tcx(), instance));
117+
.or_insert_with(|| FunctionCoverage::new(bx.tcx(), instance, function_coverage_info));
110118

111119
let Coverage { kind, code_regions } = coverage;
112120
match *kind {
113-
CoverageKind::Counter { function_source_hash, id } => {
114-
debug!(
115-
"ensuring function source hash is set for instance={:?}; function_source_hash={}",
116-
instance, function_source_hash,
117-
);
118-
func_coverage.set_function_source_hash(function_source_hash);
121+
CoverageKind::Counter { id } => {
119122
func_coverage.add_counter(id, code_regions);
120123
// We need to explicitly drop the `RefMut` before calling into `instrprof_increment`,
121124
// as that needs an exclusive borrow.
@@ -124,7 +127,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
124127
let coverageinfo = bx.tcx().coverageinfo(instance.def);
125128

126129
let fn_name = bx.get_pgo_func_name_var(instance);
127-
let hash = bx.const_u64(function_source_hash);
130+
let hash = bx.const_u64(function_coverage_info.function_source_hash);
128131
let num_counters = bx.const_u32(coverageinfo.num_counters);
129132
let index = bx.const_u32(id.as_u32());
130133
debug!(
@@ -201,10 +204,11 @@ fn add_unused_function_coverage<'tcx>(
201204
cx: &CodegenCx<'_, 'tcx>,
202205
instance: Instance<'tcx>,
203206
def_id: DefId,
207+
function_coverage_info: &'tcx FunctionCoverageInfo,
204208
) {
205209
let tcx = cx.tcx;
206210

207-
let mut function_coverage = FunctionCoverage::unused(tcx, instance);
211+
let mut function_coverage = FunctionCoverage::unused(tcx, instance, function_coverage_info);
208212
for &code_region in tcx.covered_code_regions(def_id) {
209213
let code_region = std::slice::from_ref(code_region);
210214
function_coverage.add_unreachable_regions(code_region);

compiler/rustc_middle/src/mir/coverage.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ impl Debug for Operand {
6060
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
6161
pub enum CoverageKind {
6262
Counter {
63-
function_source_hash: u64,
6463
/// ID of this counter within its enclosing function.
6564
/// Expressions in the same function can refer to it as an operand.
6665
id: CounterId,
@@ -80,7 +79,7 @@ impl Debug for CoverageKind {
8079
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
8180
use CoverageKind::*;
8281
match self {
83-
Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
82+
Counter { id } => write!(fmt, "Counter({:?})", id.index()),
8483
Expression { id, lhs, op, rhs } => write!(
8584
fmt,
8685
"Expression({:?}) = {:?} {} {:?}",
@@ -133,3 +132,12 @@ impl Op {
133132
matches!(self, Self::Subtract)
134133
}
135134
}
135+
136+
/// Stores per-function coverage information attached to a `mir::Body`,
137+
/// to be used in conjunction with the individual coverage statements injected
138+
/// into the function's basic blocks.
139+
#[derive(Clone, Debug)]
140+
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
141+
pub struct FunctionCoverageInfo {
142+
pub function_source_hash: u64,
143+
}

compiler/rustc_middle/src/mir/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,14 @@ pub struct Body<'tcx> {
345345
pub injection_phase: Option<MirPhase>,
346346

347347
pub tainted_by_errors: Option<ErrorGuaranteed>,
348+
349+
/// Per-function coverage information added by the `InstrumentCoverage`
350+
/// pass, to be used in conjunction with the coverage statements injected
351+
/// into this body's blocks.
352+
///
353+
/// If `-Cinstrument-coverage` is not active, or if an individual function
354+
/// is not eligible for coverage, then this should always be `None`.
355+
pub function_coverage_info: Option<Box<coverage::FunctionCoverageInfo>>,
348356
}
349357

350358
impl<'tcx> Body<'tcx> {
@@ -392,6 +400,7 @@ impl<'tcx> Body<'tcx> {
392400
is_polymorphic: false,
393401
injection_phase: None,
394402
tainted_by_errors,
403+
function_coverage_info: None,
395404
};
396405
body.is_polymorphic = body.has_non_region_param();
397406
body
@@ -420,6 +429,7 @@ impl<'tcx> Body<'tcx> {
420429
is_polymorphic: false,
421430
injection_phase: None,
422431
tainted_by_errors: None,
432+
function_coverage_info: None,
423433
};
424434
body.is_polymorphic = body.has_non_region_param();
425435
body

compiler/rustc_mir_build/src/build/custom/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub(super) fn build_custom_mir<'tcx>(
6060
tainted_by_errors: None,
6161
injection_phase: None,
6262
pass_count: 0,
63+
function_coverage_info: None,
6364
};
6465

6566
body.local_decls.push(LocalDecl::new(return_ty, return_ty_span));

compiler/rustc_mir_transform/src/coverage/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
211211
self.make_mir_coverage_kind(intermediate_expression),
212212
);
213213
}
214+
215+
self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
216+
function_source_hash: self.function_source_hash,
217+
}));
214218
}
215219

216220
/// Injects a single [`StatementKind::Coverage`] for each BCB that has one
@@ -323,9 +327,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
323327

324328
fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind {
325329
match *counter_kind {
326-
BcbCounter::Counter { id } => {
327-
CoverageKind::Counter { function_source_hash: self.function_source_hash, id }
328-
}
330+
BcbCounter::Counter { id } => CoverageKind::Counter { id },
329331
BcbCounter::Expression { id, lhs, op, rhs } => {
330332
CoverageKind::Expression { id, lhs, op, rhs }
331333
}

0 commit comments

Comments
 (0)