Skip to content

Commit ee9d00f

Browse files
committed
coverage: Let each coverage statement hold a vector of code regions
This makes it possible for a `StatementKind::Coverage` to hold more than one code region, but that capability is not yet used.
1 parent 1355e1f commit ee9d00f

File tree

9 files changed

+96
-90
lines changed

9 files changed

+96
-90
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

+58-35
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub struct Expression {
1111
lhs: Operand,
1212
op: Op,
1313
rhs: Operand,
14-
region: Option<CodeRegion>,
14+
code_regions: Vec<CodeRegion>,
1515
}
1616

1717
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
@@ -30,7 +30,7 @@ pub struct FunctionCoverage<'tcx> {
3030
instance: Instance<'tcx>,
3131
source_hash: u64,
3232
is_used: bool,
33-
counters: IndexVec<CounterId, Option<CodeRegion>>,
33+
counters: IndexVec<CounterId, Option<Vec<CodeRegion>>>,
3434
expressions: IndexVec<ExpressionId, Option<Expression>>,
3535
unreachable_regions: Vec<CodeRegion>,
3636
}
@@ -77,28 +77,40 @@ impl<'tcx> FunctionCoverage<'tcx> {
7777
}
7878
}
7979

80-
/// Adds a code region to be counted by an injected counter intrinsic.
81-
pub fn add_counter(&mut self, id: CounterId, region: CodeRegion) {
82-
if let Some(previous_region) = self.counters[id].replace(region.clone()) {
83-
assert_eq!(previous_region, region, "add_counter: code region for id changed");
80+
/// Adds code regions to be counted by an injected counter intrinsic.
81+
#[instrument(level = "debug", skip(self))]
82+
pub(crate) fn add_counter(&mut self, id: CounterId, code_regions: &[CodeRegion]) {
83+
if code_regions.is_empty() {
84+
return;
85+
}
86+
87+
let slot = &mut self.counters[id];
88+
match slot {
89+
None => *slot = Some(code_regions.to_owned()),
90+
// If this counter ID slot has already been filled, it should
91+
// contain identical information.
92+
Some(ref previous_regions) => assert_eq!(
93+
previous_regions, code_regions,
94+
"add_counter: code regions for id changed"
95+
),
8496
}
8597
}
8698

99+
/// Adds information about a coverage expression, along with zero or more
100+
/// code regions mapped to that expression.
101+
///
87102
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
88103
/// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity
89104
/// between operands that are counter IDs and operands that are expression IDs.
90-
pub fn add_counter_expression(
105+
#[instrument(level = "debug", skip(self))]
106+
pub(crate) fn add_counter_expression(
91107
&mut self,
92108
expression_id: ExpressionId,
93109
lhs: Operand,
94110
op: Op,
95111
rhs: Operand,
96-
region: Option<CodeRegion>,
112+
code_regions: &[CodeRegion],
97113
) {
98-
debug!(
99-
"add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}",
100-
expression_id, lhs, op, rhs, region
101-
);
102114
debug_assert!(
103115
expression_id.as_usize() < self.expressions.len(),
104116
"expression_id {} is out of range for expressions.len() = {}
@@ -107,23 +119,25 @@ impl<'tcx> FunctionCoverage<'tcx> {
107119
self.expressions.len(),
108120
self,
109121
);
110-
if let Some(previous_expression) = self.expressions[expression_id].replace(Expression {
111-
lhs,
112-
op,
113-
rhs,
114-
region: region.clone(),
115-
}) {
116-
assert_eq!(
117-
previous_expression,
118-
Expression { lhs, op, rhs, region },
122+
123+
let expression = Expression { lhs, op, rhs, code_regions: code_regions.to_owned() };
124+
let slot = &mut self.expressions[expression_id];
125+
match slot {
126+
None => *slot = Some(expression),
127+
// If this expression ID slot has already been filled, it should
128+
// contain identical information.
129+
Some(ref previous_expression) => assert_eq!(
130+
previous_expression, &expression,
119131
"add_counter_expression: expression for id changed"
120-
);
132+
),
121133
}
122134
}
123135

124-
/// Add a region that will be marked as "unreachable", with a constant "zero counter".
125-
pub fn add_unreachable_region(&mut self, region: CodeRegion) {
126-
self.unreachable_regions.push(region)
136+
/// Adds regions that will be marked as "unreachable", with a constant "zero counter".
137+
#[instrument(level = "debug", skip(self))]
138+
pub(crate) fn add_unreachable_regions(&mut self, code_regions: &[CodeRegion]) {
139+
assert!(!code_regions.is_empty(), "unreachable regions always have code regions");
140+
self.unreachable_regions.extend_from_slice(code_regions);
127141
}
128142

129143
/// Perform some simplifications to make the final coverage mappings
@@ -212,11 +226,16 @@ impl<'tcx> FunctionCoverage<'tcx> {
212226
}
213227

214228
fn counter_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
215-
self.counters.iter_enumerated().filter_map(|(index, entry)| {
216-
// Option::map() will return None to filter out missing counters. This may happen
217-
// if, for example, a MIR-instrumented counter is removed during an optimization.
218-
entry.as_ref().map(|region| (Counter::counter_value_reference(index), region))
219-
})
229+
self.counters
230+
.iter_enumerated()
231+
// Filter out counter IDs that we never saw during MIR traversal.
232+
// This can happen if a counter was optimized out by MIR transforms
233+
// (and replaced with `CoverageKind::Unreachable` instead).
234+
.filter_map(|(id, maybe_code_regions)| Some((id, maybe_code_regions.as_ref()?)))
235+
.flat_map(|(id, code_regions)| {
236+
let counter = Counter::counter_value_reference(id);
237+
code_regions.iter().map(move |region| (counter, region))
238+
})
220239
}
221240

222241
/// Convert this function's coverage expression data into a form that can be
@@ -254,13 +273,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
254273

255274
fn expression_regions(&self) -> Vec<(Counter, &CodeRegion)> {
256275
// Find all of the expression IDs that weren't optimized out AND have
257-
// an attached code region, and return the corresponding mapping as a
258-
// counter/region pair.
276+
// one or more attached code regions, and return the corresponding
277+
// mappings as counter/region pairs.
259278
self.expressions
260279
.iter_enumerated()
261-
.filter_map(|(id, expression)| {
262-
let code_region = expression.as_ref()?.region.as_ref()?;
263-
Some((Counter::expression(id), code_region))
280+
.filter_map(|(id, maybe_expression)| {
281+
let code_regions = &maybe_expression.as_ref()?.code_regions;
282+
Some((id, code_regions))
283+
})
284+
.flat_map(|(id, code_regions)| {
285+
let counter = Counter::expression(id);
286+
code_regions.iter().map(move |code_region| (counter, code_region))
264287
})
265288
.collect::<Vec<_>>()
266289
}

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+7-26
Original file line numberDiff line numberDiff line change
@@ -108,25 +108,15 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
108108
.entry(instance)
109109
.or_insert_with(|| FunctionCoverage::new(bx.tcx(), instance));
110110

111-
let Coverage { kind, code_region } = coverage.clone();
112-
match kind {
111+
let Coverage { kind, code_regions } = coverage;
112+
match *kind {
113113
CoverageKind::Counter { function_source_hash, id } => {
114114
debug!(
115115
"ensuring function source hash is set for instance={:?}; function_source_hash={}",
116116
instance, function_source_hash,
117117
);
118118
func_coverage.set_function_source_hash(function_source_hash);
119-
120-
if let Some(code_region) = code_region {
121-
// Note: Some counters do not have code regions, but may still be referenced
122-
// from expressions. In that case, don't add the counter to the coverage map,
123-
// but do inject the counter intrinsic.
124-
debug!(
125-
"adding counter to coverage_map: instance={:?}, id={:?}, region={:?}",
126-
instance, id, code_region,
127-
);
128-
func_coverage.add_counter(id, code_region);
129-
}
119+
func_coverage.add_counter(id, code_regions);
130120
// We need to explicitly drop the `RefMut` before calling into `instrprof_increment`,
131121
// as that needs an exclusive borrow.
132122
drop(coverage_map);
@@ -144,20 +134,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
144134
bx.instrprof_increment(fn_name, hash, num_counters, index);
145135
}
146136
CoverageKind::Expression { id, lhs, op, rhs } => {
147-
debug!(
148-
"adding counter expression to coverage_map: instance={:?}, id={:?}, {:?} {:?} {:?}; region: {:?}",
149-
instance, id, lhs, op, rhs, code_region,
150-
);
151-
func_coverage.add_counter_expression(id, lhs, op, rhs, code_region);
137+
func_coverage.add_counter_expression(id, lhs, op, rhs, code_regions);
152138
}
153139
CoverageKind::Unreachable => {
154-
let code_region =
155-
code_region.expect("unreachable regions always have code regions");
156-
debug!(
157-
"adding unreachable code to coverage_map: instance={:?}, at {:?}",
158-
instance, code_region,
159-
);
160-
func_coverage.add_unreachable_region(code_region);
140+
func_coverage.add_unreachable_regions(code_regions);
161141
}
162142
}
163143
}
@@ -226,7 +206,8 @@ fn add_unused_function_coverage<'tcx>(
226206

227207
let mut function_coverage = FunctionCoverage::unused(tcx, instance);
228208
for &code_region in tcx.covered_code_regions(def_id) {
229-
function_coverage.add_unreachable_region(code_region.clone());
209+
let code_region = std::slice::from_ref(code_region);
210+
function_coverage.add_unreachable_regions(code_region);
230211
}
231212

232213
if let Some(coverage_context) = cx.coverage_context() {

compiler/rustc_middle/src/mir/pretty.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_middle::mir::interpret::{
1616
Pointer, Provenance,
1717
};
1818
use rustc_middle::mir::visit::Visitor;
19-
use rustc_middle::mir::*;
19+
use rustc_middle::mir::{self, *};
2020
use rustc_middle::ty::{self, TyCtxt};
2121
use rustc_target::abi::Size;
2222

@@ -685,10 +685,13 @@ impl Debug for Statement<'_> {
685685
AscribeUserType(box (ref place, ref c_ty), ref variance) => {
686686
write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
687687
}
688-
Coverage(box self::Coverage { ref kind, code_region: Some(ref rgn) }) => {
689-
write!(fmt, "Coverage::{kind:?} for {rgn:?}")
688+
Coverage(box mir::Coverage { ref kind, ref code_regions }) => {
689+
if code_regions.is_empty() {
690+
write!(fmt, "Coverage::{kind:?}")
691+
} else {
692+
write!(fmt, "Coverage::{kind:?} for {code_regions:?}")
693+
}
690694
}
691-
Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind),
692695
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
693696
ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
694697
Nop => write!(fmt, "nop"),

compiler/rustc_middle/src/mir/syntax.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ pub enum FakeReadCause {
514514
#[derive(TypeFoldable, TypeVisitable)]
515515
pub struct Coverage {
516516
pub kind: CoverageKind,
517-
pub code_region: Option<CodeRegion>,
517+
pub code_regions: Vec<CodeRegion>,
518518
}
519519

520520
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]

compiler/rustc_mir_transform/src/coverage/mod.rs

+9-12
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
243243
self.mir_body,
244244
self.make_mir_coverage_kind(&counter_kind),
245245
self.bcb_leader_bb(bcb),
246-
Some(code_region),
246+
vec![code_region],
247247
);
248248
}
249249
}
@@ -302,7 +302,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
302302
self.mir_body,
303303
self.make_mir_coverage_kind(&counter_kind),
304304
inject_to_bb,
305-
None,
305+
Vec::new(),
306306
);
307307
}
308308
BcbCounter::Expression { .. } => inject_intermediate_expression(
@@ -367,20 +367,14 @@ fn inject_statement(
367367
mir_body: &mut mir::Body<'_>,
368368
counter_kind: CoverageKind,
369369
bb: BasicBlock,
370-
some_code_region: Option<CodeRegion>,
370+
code_regions: Vec<CodeRegion>,
371371
) {
372-
debug!(
373-
" injecting statement {:?} for {:?} at code region: {:?}",
374-
counter_kind, bb, some_code_region
375-
);
372+
debug!(" injecting statement {counter_kind:?} for {bb:?} at code regions: {code_regions:?}");
376373
let data = &mut mir_body[bb];
377374
let source_info = data.terminator().source_info;
378375
let statement = Statement {
379376
source_info,
380-
kind: StatementKind::Coverage(Box::new(Coverage {
381-
kind: counter_kind,
382-
code_region: some_code_region,
383-
})),
377+
kind: StatementKind::Coverage(Box::new(Coverage { kind: counter_kind, code_regions })),
384378
};
385379
data.statements.insert(0, statement);
386380
}
@@ -394,7 +388,10 @@ fn inject_intermediate_expression(mir_body: &mut mir::Body<'_>, expression: Cove
394388
let source_info = data.terminator().source_info;
395389
let statement = Statement {
396390
source_info,
397-
kind: StatementKind::Coverage(Box::new(Coverage { kind: expression, code_region: None })),
391+
kind: StatementKind::Coverage(Box::new(Coverage {
392+
kind: expression,
393+
code_regions: Vec::new(),
394+
})),
398395
};
399396
data.statements.push(statement);
400397
}

compiler/rustc_mir_transform/src/coverage/query.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) ->
9393
fn covered_code_regions(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<&CodeRegion> {
9494
let body = mir_body(tcx, def_id);
9595
all_coverage_in_mir_body(body)
96-
// Not all coverage statements have an attached code region.
97-
.filter_map(|coverage| coverage.code_region.as_ref())
96+
// Coverage statements have a list of code regions (possibly empty).
97+
.flat_map(|coverage| coverage.code_regions.as_slice())
9898
.collect()
9999
}
100100

compiler/rustc_mir_transform/src/simplify.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -441,21 +441,23 @@ fn save_unreachable_coverage(
441441
let dead_block = &basic_blocks[dead_block];
442442
for statement in &dead_block.statements {
443443
let StatementKind::Coverage(coverage) = &statement.kind else { continue };
444-
let Some(code_region) = &coverage.code_region else { continue };
444+
if coverage.code_regions.is_empty() {
445+
continue;
446+
};
445447
let instance = statement.source_info.scope.inlined_instance(source_scopes);
446448
if live.contains(&instance) {
447-
retained_coverage.push((statement.source_info, code_region.clone()));
449+
retained_coverage.push((statement.source_info, coverage.code_regions.clone()));
448450
}
449451
}
450452
}
451453

452454
let start_block = &mut basic_blocks[START_BLOCK];
453455
start_block.statements.extend(retained_coverage.into_iter().map(
454-
|(source_info, code_region)| Statement {
456+
|(source_info, code_regions)| Statement {
455457
source_info,
456458
kind: StatementKind::Coverage(Box::new(Coverage {
457459
kind: CoverageKind::Unreachable,
458-
code_region: Some(code_region),
460+
code_regions,
459461
})),
460462
},
461463
));

tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
let mut _0: bool;
66

77
bb0: {
8-
+ Coverage::Counter(0) for /the/src/instrument_coverage.rs:20:1 - 22:2;
8+
+ Coverage::Counter(0) for [/the/src/instrument_coverage.rs:20:1 - 22:2];
99
_0 = const true;
1010
return;
1111
}

tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
let mut _3: !;
99

1010
bb0: {
11-
+ Coverage::Counter(0) for /the/src/instrument_coverage.rs:11:1 - 11:11;
11+
+ Coverage::Counter(0) for [/the/src/instrument_coverage.rs:11:1 - 11:11];
1212
goto -> bb1;
1313
}
1414

1515
bb1: {
16-
+ Coverage::Expression(0) = Counter(0) + Counter(1) for /the/src/instrument_coverage.rs:12:5 - 13:17;
16+
+ Coverage::Expression(0) = Counter(0) + Counter(1) for [/the/src/instrument_coverage.rs:12:5 - 13:17];
1717
falseUnwind -> [real: bb2, unwind: bb6];
1818
}
1919

@@ -27,15 +27,15 @@
2727
}
2828

2929
bb4: {
30-
+ Coverage::Expression(2) = Expression(1) + Zero for /the/src/instrument_coverage.rs:17:1 - 17:2;
31-
+ Coverage::Expression(1) = Expression(0) - Counter(1) for /the/src/instrument_coverage.rs:14:13 - 14:18;
30+
+ Coverage::Expression(2) = Expression(1) + Zero for [/the/src/instrument_coverage.rs:17:1 - 17:2];
31+
+ Coverage::Expression(1) = Expression(0) - Counter(1) for [/the/src/instrument_coverage.rs:14:13 - 14:18];
3232
_0 = const ();
3333
StorageDead(_2);
3434
return;
3535
}
3636

3737
bb5: {
38-
+ Coverage::Counter(1) for /the/src/instrument_coverage.rs:15:10 - 15:11;
38+
+ Coverage::Counter(1) for [/the/src/instrument_coverage.rs:15:10 - 15:11];
3939
_1 = const ();
4040
StorageDead(_2);
4141
goto -> bb1;

0 commit comments

Comments
 (0)