Skip to content

Commit 2e212b7

Browse files
committed
coverage: Split out counter increment sites from BCB node/edge counters
This makes it possible for two nodes/edges in the coverage graph to share the same counter, without causing the instrumentor to inject unwanted duplicate counter-increment statements.
1 parent 11f32b7 commit 2e212b7

File tree

2 files changed

+79
-80
lines changed

2 files changed

+79
-80
lines changed

compiler/rustc_mir_transform/src/coverage/counters.rs

+39-31
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use rustc_data_structures::fx::FxIndexMap;
1+
use rustc_data_structures::captures::Captures;
2+
use rustc_data_structures::fx::FxHashMap;
23
use rustc_data_structures::graph::WithNumNodes;
34
use rustc_index::bit_set::BitSet;
45
use rustc_index::IndexVec;
@@ -38,19 +39,27 @@ impl Debug for BcbCounter {
3839
}
3940
}
4041

42+
#[derive(Debug)]
43+
pub(super) enum CounterIncrementSite {
44+
Node { bcb: BasicCoverageBlock },
45+
Edge { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock },
46+
}
47+
4148
/// Generates and stores coverage counter and coverage expression information
4249
/// associated with nodes/edges in the BCB graph.
4350
pub(super) struct CoverageCounters {
44-
next_counter_id: CounterId,
51+
/// List of places where a counter-increment statement should be injected
52+
/// into MIR, each with its corresponding counter ID.
53+
counter_increment_sites: IndexVec<CounterId, CounterIncrementSite>,
4554

4655
/// Coverage counters/expressions that are associated with individual BCBs.
4756
bcb_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>,
4857
/// Coverage counters/expressions that are associated with the control-flow
4958
/// edge between two BCBs.
5059
///
51-
/// The iteration order of this map can affect the precise contents of MIR,
52-
/// so we use `FxIndexMap` to avoid query stability hazards.
53-
bcb_edge_counters: FxIndexMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>,
60+
/// We currently don't iterate over this map, but if we do in the future,
61+
/// switch it back to `FxIndexMap` to avoid query stability hazards.
62+
bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>,
5463
/// Tracks which BCBs have a counter associated with some incoming edge.
5564
/// Only used by assertions, to verify that BCBs with incoming edge
5665
/// counters do not have their own physical counters (expressions are allowed).
@@ -71,9 +80,9 @@ impl CoverageCounters {
7180
let num_bcbs = basic_coverage_blocks.num_nodes();
7281

7382
let mut this = Self {
74-
next_counter_id: CounterId::START,
83+
counter_increment_sites: IndexVec::new(),
7584
bcb_counters: IndexVec::from_elem_n(None, num_bcbs),
76-
bcb_edge_counters: FxIndexMap::default(),
85+
bcb_edge_counters: FxHashMap::default(),
7786
bcb_has_incoming_edge_counters: BitSet::new_empty(num_bcbs),
7887
expressions: IndexVec::new(),
7988
};
@@ -84,8 +93,8 @@ impl CoverageCounters {
8493
this
8594
}
8695

87-
fn make_counter(&mut self) -> BcbCounter {
88-
let id = self.next_counter();
96+
fn make_counter(&mut self, site: CounterIncrementSite) -> BcbCounter {
97+
let id = self.counter_increment_sites.push(site);
8998
BcbCounter::Counter { id }
9099
}
91100

@@ -103,15 +112,8 @@ impl CoverageCounters {
103112
self.make_expression(lhs, Op::Add, rhs)
104113
}
105114

106-
/// Counter IDs start from one and go up.
107-
fn next_counter(&mut self) -> CounterId {
108-
let next = self.next_counter_id;
109-
self.next_counter_id = self.next_counter_id + 1;
110-
next
111-
}
112-
113115
pub(super) fn num_counters(&self) -> usize {
114-
self.next_counter_id.as_usize()
116+
self.counter_increment_sites.len()
115117
}
116118

117119
#[cfg(test)]
@@ -171,22 +173,26 @@ impl CoverageCounters {
171173
self.bcb_counters[bcb]
172174
}
173175

174-
pub(super) fn bcb_node_counters(
176+
/// Returns an iterator over all the nodes/edges in the coverage graph that
177+
/// should have a counter-increment statement injected into MIR, along with
178+
/// each site's corresponding counter ID.
179+
pub(super) fn counter_increment_sites(
175180
&self,
176-
) -> impl Iterator<Item = (BasicCoverageBlock, &BcbCounter)> {
177-
self.bcb_counters
178-
.iter_enumerated()
179-
.filter_map(|(bcb, counter_kind)| Some((bcb, counter_kind.as_ref()?)))
181+
) -> impl Iterator<Item = (CounterId, &CounterIncrementSite)> {
182+
self.counter_increment_sites.iter_enumerated()
180183
}
181184

182-
/// For each edge in the BCB graph that has an associated counter, yields
183-
/// that edge's *from* and *to* nodes, and its counter.
184-
pub(super) fn bcb_edge_counters(
185+
/// Returns an iterator over the subset of BCB nodes that have been associated
186+
/// with a counter *expression*, along with the ID of that expression.
187+
pub(super) fn bcb_nodes_with_coverage_expressions(
185188
&self,
186-
) -> impl Iterator<Item = (BasicCoverageBlock, BasicCoverageBlock, &BcbCounter)> {
187-
self.bcb_edge_counters
188-
.iter()
189-
.map(|(&(from_bcb, to_bcb), counter_kind)| (from_bcb, to_bcb, counter_kind))
189+
) -> impl Iterator<Item = (BasicCoverageBlock, ExpressionId)> + Captures<'_> {
190+
self.bcb_counters.iter_enumerated().filter_map(|(bcb, &counter_kind)| match counter_kind {
191+
// Yield the BCB along with its associated expression ID.
192+
Some(BcbCounter::Expression { id }) => Some((bcb, id)),
193+
// This BCB is associated with a counter or nothing, so skip it.
194+
Some(BcbCounter::Counter { .. }) | None => None,
195+
})
190196
}
191197

192198
pub(super) fn into_expressions(self) -> IndexVec<ExpressionId, Expression> {
@@ -339,7 +345,8 @@ impl<'a> MakeBcbCounters<'a> {
339345
// program results in a tight infinite loop, but it should still compile.
340346
let one_path_to_target = !self.basic_coverage_blocks.bcb_has_multiple_in_edges(bcb);
341347
if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) {
342-
let counter_kind = self.coverage_counters.make_counter();
348+
let counter_kind =
349+
self.coverage_counters.make_counter(CounterIncrementSite::Node { bcb });
343350
if one_path_to_target {
344351
debug!("{bcb:?} gets a new counter: {counter_kind:?}");
345352
} else {
@@ -401,7 +408,8 @@ impl<'a> MakeBcbCounters<'a> {
401408
}
402409

403410
// Make a new counter to count this edge.
404-
let counter_kind = self.coverage_counters.make_counter();
411+
let counter_kind =
412+
self.coverage_counters.make_counter(CounterIncrementSite::Edge { from_bcb, to_bcb });
405413
debug!("Edge {from_bcb:?}->{to_bcb:?} gets a new counter: {counter_kind:?}");
406414
self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind)
407415
}

compiler/rustc_mir_transform/src/coverage/mod.rs

+40-49
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod spans;
77
#[cfg(test)]
88
mod tests;
99

10-
use self::counters::{BcbCounter, CoverageCounters};
10+
use self::counters::{CounterIncrementSite, CoverageCounters};
1111
use self::graph::{BasicCoverageBlock, CoverageGraph};
1212
use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans};
1313

@@ -155,61 +155,52 @@ fn inject_coverage_statements<'tcx>(
155155
bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool,
156156
coverage_counters: &CoverageCounters,
157157
) {
158-
// Process the counters associated with BCB nodes.
159-
for (bcb, counter_kind) in coverage_counters.bcb_node_counters() {
160-
let do_inject = match counter_kind {
161-
// Counter-increment statements always need to be injected.
162-
BcbCounter::Counter { .. } => true,
163-
// The only purpose of expression-used statements is to detect
164-
// when a mapping is unreachable, so we only inject them for
165-
// expressions with one or more mappings.
166-
BcbCounter::Expression { .. } => bcb_has_coverage_spans(bcb),
167-
};
168-
if do_inject {
169-
inject_statement(
170-
mir_body,
171-
make_mir_coverage_kind(counter_kind),
172-
basic_coverage_blocks[bcb].leader_bb(),
173-
);
174-
}
175-
}
176-
177-
// Process the counters associated with BCB edges.
178-
for (from_bcb, to_bcb, counter_kind) in coverage_counters.bcb_edge_counters() {
179-
let do_inject = match counter_kind {
180-
// Counter-increment statements always need to be injected.
181-
BcbCounter::Counter { .. } => true,
182-
// BCB-edge expressions never have mappings, so they never need
183-
// a corresponding statement.
184-
BcbCounter::Expression { .. } => false,
158+
// Inject counter-increment statements into MIR.
159+
for (id, counter_increment_site) in coverage_counters.counter_increment_sites() {
160+
// Determine the block to inject a counter-increment statement into.
161+
// For BCB nodes this is just their first block, but for edges we need
162+
// to create a new block between the two BCBs, and inject into that.
163+
let target_bb = match *counter_increment_site {
164+
CounterIncrementSite::Node { bcb } => basic_coverage_blocks[bcb].leader_bb(),
165+
CounterIncrementSite::Edge { from_bcb, to_bcb } => {
166+
// Create a new block between the last block of `from_bcb` and
167+
// the first block of `to_bcb`.
168+
let from_bb = basic_coverage_blocks[from_bcb].last_bb();
169+
let to_bb = basic_coverage_blocks[to_bcb].leader_bb();
170+
171+
let new_bb = inject_edge_counter_basic_block(mir_body, from_bb, to_bb);
172+
debug!(
173+
"Edge {from_bcb:?} (last {from_bb:?}) -> {to_bcb:?} (leader {to_bb:?}) \
174+
requires a new MIR BasicBlock {new_bb:?} for counter increment {id:?}",
175+
);
176+
new_bb
177+
}
185178
};
186-
if !do_inject {
187-
continue;
188-
}
189-
190-
// We need to inject a coverage statement into a new BB between the
191-
// last BB of `from_bcb` and the first BB of `to_bcb`.
192-
let from_bb = basic_coverage_blocks[from_bcb].last_bb();
193-
let to_bb = basic_coverage_blocks[to_bcb].leader_bb();
194179

195-
let new_bb = inject_edge_counter_basic_block(mir_body, from_bb, to_bb);
196-
debug!(
197-
"Edge {from_bcb:?} (last {from_bb:?}) -> {to_bcb:?} (leader {to_bb:?}) \
198-
requires a new MIR BasicBlock {new_bb:?} for edge counter {counter_kind:?}",
199-
);
200-
201-
// Inject a counter into the newly-created BB.
202-
inject_statement(mir_body, make_mir_coverage_kind(counter_kind), new_bb);
180+
inject_statement(mir_body, CoverageKind::CounterIncrement { id }, target_bb);
203181
}
204-
}
205182

206-
fn make_mir_coverage_kind(counter_kind: &BcbCounter) -> CoverageKind {
207-
match *counter_kind {
208-
BcbCounter::Counter { id } => CoverageKind::CounterIncrement { id },
209-
BcbCounter::Expression { id } => CoverageKind::ExpressionUsed { id },
183+
// For each counter expression that is directly associated with at least one
184+
// span, we inject an "expression-used" statement, so that coverage codegen
185+
// can check whether the injected statement survived MIR optimization.
186+
// (BCB edges can't have spans, so we only need to process BCB nodes here.)
187+
//
188+
// See the code in `rustc_codegen_llvm::coverageinfo::map_data` that deals
189+
// with "expressions seen" and "zero terms".
190+
for (bcb, expression_id) in coverage_counters
191+
.bcb_nodes_with_coverage_expressions()
192+
.filter(|&(bcb, _)| bcb_has_coverage_spans(bcb))
193+
{
194+
inject_statement(
195+
mir_body,
196+
CoverageKind::ExpressionUsed { id: expression_id },
197+
basic_coverage_blocks[bcb].leader_bb(),
198+
);
210199
}
211200
}
212201

202+
/// Given two basic blocks that have a control-flow edge between them, creates
203+
/// and returns a new block that sits between those blocks.
213204
fn inject_edge_counter_basic_block(
214205
mir_body: &mut mir::Body<'_>,
215206
from_bb: BasicBlock,

0 commit comments

Comments
 (0)