Skip to content

Commit 229d098

Browse files
committed
coverage: Simplify the loop that combines blocks into BCBs
The old loop had two separate places where it would flush the acumulated list of straight-line blocks into a new BCB. One occurred at the start of the loop body when the current block couldn't be chained into, and the other occurred at the end of the loop body when the current block couldn't be chained from. The latter check can be hoisted to the start of the loop body by making it examine the previous block (which has added itself to the list) instead of the current block. With that done, we can combine the two separate flushes into one flush with two possible trigger conditions.
1 parent c412cd4 commit 229d098

File tree

1 file changed

+33
-57
lines changed
  • compiler/rustc_mir_transform/src/coverage

1 file changed

+33
-57
lines changed

compiler/rustc_mir_transform/src/coverage/graph.rs

+33-57
Original file line numberDiff line numberDiff line change
@@ -91,74 +91,41 @@ impl CoverageGraph {
9191
// FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and
9292
// `catch_unwind()` handlers.
9393

94+
// Accumulates a chain of blocks that will be combined into one BCB.
9495
let mut basic_blocks = Vec::new();
96+
9597
let filtered_successors = |bb| bcb_filtered_successors(mir_body[bb].terminator());
9698
for bb in short_circuit_preorder(mir_body, filtered_successors)
9799
.filter(|&bb| mir_body[bb].terminator().kind != TerminatorKind::Unreachable)
98100
{
99-
if let Some(last) = basic_blocks.last() {
100-
let predecessors = &mir_body.basic_blocks.predecessors()[bb];
101-
if predecessors.len() > 1 || !predecessors.contains(last) {
102-
// The `bb` has more than one _incoming_ edge, and should start its own
103-
// `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet
104-
// include `bb`; it contains a sequence of one or more sequential basic_blocks
105-
// with no intermediate branches in or out. Save these as a new
106-
// `BasicCoverageBlockData` before starting the new one.)
107-
Self::add_basic_coverage_block(
108-
&mut bcbs,
109-
&mut bb_to_bcb,
110-
basic_blocks.split_off(0),
111-
);
112-
debug!(
113-
" because {}",
114-
if predecessors.len() > 1 {
115-
"predecessors.len() > 1".to_owned()
116-
} else {
117-
format!("bb {} is not in predecessors: {:?}", bb.index(), predecessors)
118-
}
119-
);
120-
}
101+
// If the previous block can't be chained into `bb`, flush the accumulated
102+
// blocks into a new BCB, then start building the next chain.
103+
if let Some(&prev) = basic_blocks.last()
104+
&& (!filtered_successors(prev).is_chainable() || {
105+
// If `bb` has multiple predecessor blocks, or `prev` isn't
106+
// one of its predecessors, we can't chain and must flush.
107+
let predecessors = &mir_body.basic_blocks.predecessors()[bb];
108+
predecessors.len() > 1 || !predecessors.contains(&prev)
109+
})
110+
{
111+
debug!(
112+
terminator_kind = ?mir_body[prev].terminator().kind,
113+
predecessors = ?&mir_body.basic_blocks.predecessors()[bb],
114+
"can't chain from {prev:?} to {bb:?}"
115+
);
116+
Self::add_basic_coverage_block(
117+
&mut bcbs,
118+
&mut bb_to_bcb,
119+
basic_blocks.split_off(0),
120+
);
121121
}
122-
basic_blocks.push(bb);
123-
124-
let term = mir_body[bb].terminator();
125-
126-
match bcb_filtered_successors(term) {
127-
CoverageSuccessors::NotChainable(_) => {
128-
// The `bb` has more than one _outgoing_ edge, or exits the function. Save the
129-
// current sequence of `basic_blocks` gathered to this point, as a new
130-
// `BasicCoverageBlockData`.
131-
Self::add_basic_coverage_block(
132-
&mut bcbs,
133-
&mut bb_to_bcb,
134-
basic_blocks.split_off(0),
135-
);
136-
debug!(" because term.kind = {:?}", term.kind);
137-
// Note that this condition is based on `TerminatorKind`, even though it
138-
// theoretically boils down to `successors().len() != 1`; that is, either zero
139-
// (e.g., `Return`, `Terminate`) or multiple successors (e.g., `SwitchInt`), but
140-
// since the BCB CFG ignores things like unwind branches (which exist in the
141-
// `Terminator`s `successors()` list) checking the number of successors won't
142-
// work.
143-
}
144122

145-
// The following `TerminatorKind`s are either not expected outside an unwind branch,
146-
// or they should not (under normal circumstances) branch. Coverage graphs are
147-
// simplified by assuring coverage results are accurate for program executions that
148-
// don't panic.
149-
//
150-
// Programs that panic and unwind may record slightly inaccurate coverage results
151-
// for a coverage region containing the `Terminator` that began the panic. This
152-
// is as intended. (See Issue #78544 for a possible future option to support
153-
// coverage in test programs that panic.)
154-
CoverageSuccessors::Chainable(_) => {}
155-
}
123+
basic_blocks.push(bb);
156124
}
157125

158126
if !basic_blocks.is_empty() {
159-
// process any remaining basic_blocks into a final `BasicCoverageBlockData`
127+
debug!("flushing accumulated blocks into one last BCB");
160128
Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0));
161-
debug!(" because the end of the MIR CFG was reached while traversing");
162129
}
163130

164131
(bcbs, bb_to_bcb)
@@ -350,6 +317,15 @@ enum CoverageSuccessors<'a> {
350317
NotChainable(&'a [BasicBlock]),
351318
}
352319

320+
impl CoverageSuccessors<'_> {
321+
fn is_chainable(&self) -> bool {
322+
match self {
323+
Self::Chainable(_) => true,
324+
Self::NotChainable(_) => false,
325+
}
326+
}
327+
}
328+
353329
impl IntoIterator for CoverageSuccessors<'_> {
354330
type Item = BasicBlock;
355331
type IntoIter = impl DoubleEndedIterator<Item = Self::Item>;

0 commit comments

Comments
 (0)