Skip to content

Commit f0decb2

Browse files
committed
coverage: Eliminate BcbBranch
`BcbBranch` represented an out-edge of a coverage graph node, but would silently refer to a node instead in cases where that node only had one in-edge. Instead we now refer to a graph edge as a `(from_bcb, to_bcb)` pair, or sometimes as just one of those nodes when the other node is implied by the surrounding context. The case of sole in-edges is handled by special code added directly to `get_or_make_edge_counter_operand`.
1 parent 4ed8ad8 commit f0decb2

File tree

2 files changed

+69
-104
lines changed

2 files changed

+69
-104
lines changed

compiler/rustc_mir_transform/src/coverage/counters.rs

+69-65
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
use super::graph;
2-
3-
use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops};
4-
51
use rustc_data_structures::fx::FxHashMap;
62
use rustc_data_structures::graph::WithNumNodes;
73
use rustc_index::bit_set::BitSet;
84
use rustc_index::IndexVec;
95
use rustc_middle::mir::coverage::*;
106

7+
use super::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops};
8+
119
use std::fmt::{self, Debug};
1210

1311
/// The coverage counter or counter expression associated with a particular
@@ -259,49 +257,46 @@ impl<'a> MakeBcbCounters<'a> {
259257
// We might also use its term later to compute one of the branch counters.
260258
let from_bcb_operand = self.get_or_make_counter_operand(from_bcb);
261259

262-
let branches = self.bcb_branches(from_bcb);
260+
let branch_target_bcbs = self.basic_coverage_blocks.successors[from_bcb].as_slice();
263261

264262
// If this node doesn't have multiple out-edges, or all of its out-edges
265263
// already have counters, then we don't need to create edge counters.
266-
let needs_branch_counters =
267-
branches.len() > 1 && branches.iter().any(|branch| self.branch_has_no_counter(branch));
264+
let needs_branch_counters = branch_target_bcbs.len() > 1
265+
&& branch_target_bcbs
266+
.iter()
267+
.any(|&to_bcb| self.branch_has_no_counter(from_bcb, to_bcb));
268268
if !needs_branch_counters {
269269
return;
270270
}
271271

272272
debug!(
273273
"{from_bcb:?} has some branch(es) without counters:\n {}",
274-
branches
274+
branch_target_bcbs
275275
.iter()
276-
.map(|branch| { format!("{:?}: {:?}", branch, self.branch_counter(branch)) })
276+
.map(|&to_bcb| {
277+
format!("{from_bcb:?}->{to_bcb:?}: {:?}", self.branch_counter(from_bcb, to_bcb))
278+
})
277279
.collect::<Vec<_>>()
278280
.join("\n "),
279281
);
280282

281-
// Use the `traversal` state to decide if a subset of the branches exit a loop, making it
282-
// likely that branch is executed less than branches that do not exit the same loop. In this
283-
// case, any branch that does not exit the loop (and has not already been assigned a
284-
// counter) should be counted by expression, if possible. (If a preferred expression branch
285-
// is not selected based on the loop context, select any branch without an existing
286-
// counter.)
287-
let expression_branch = self.choose_preferred_expression_branch(traversal, &branches);
283+
// Of the branch edges that don't have counters yet, one can be given an expression
284+
// (computed from the other edges) instead of a dedicated counter.
285+
let expression_to_bcb = self.choose_preferred_expression_branch(traversal, from_bcb);
288286

289287
// For each branch arm other than the one that was chosen to get an expression,
290288
// ensure that it has a counter (existing counter/expression or a new counter),
291289
// and accumulate the corresponding terms into a single sum term.
292290
let sum_of_all_other_branches: BcbCounter = {
293-
let _span = debug_span!("sum_of_all_other_branches", ?expression_branch).entered();
294-
branches
295-
.into_iter()
291+
let _span = debug_span!("sum_of_all_other_branches", ?expression_to_bcb).entered();
292+
branch_target_bcbs
293+
.iter()
294+
.copied()
296295
// Skip the chosen branch, since we'll calculate it from the other branches.
297-
.filter(|branch| branch != &expression_branch)
298-
.fold(None, |accum, branch| {
299-
let _span = debug_span!("branch", ?accum, ?branch).entered();
300-
let branch_counter = if branch.is_only_path_to_target() {
301-
self.get_or_make_counter_operand(branch.target_bcb)
302-
} else {
303-
self.get_or_make_edge_counter_operand(from_bcb, branch.target_bcb)
304-
};
296+
.filter(|&to_bcb| to_bcb != expression_to_bcb)
297+
.fold(None, |accum, to_bcb| {
298+
let _span = debug_span!("to_bcb", ?accum, ?to_bcb).entered();
299+
let branch_counter = self.get_or_make_edge_counter_operand(from_bcb, to_bcb);
305300
Some(self.coverage_counters.make_sum_expression(accum, branch_counter))
306301
})
307302
.expect("there must be at least one other branch")
@@ -311,22 +306,20 @@ impl<'a> MakeBcbCounters<'a> {
311306
// by taking the count of the node we're branching from, and subtracting the
312307
// sum of all the other branches.
313308
debug!(
314-
"Making an expression for the selected expression_branch: {:?} \
315-
(expression_branch predecessors: {:?})",
316-
expression_branch,
317-
self.bcb_predecessors(expression_branch.target_bcb),
309+
"Making an expression for the selected expression_branch: \
310+
{expression_to_bcb:?} (expression_branch predecessors: {:?})",
311+
self.bcb_predecessors(expression_to_bcb),
318312
);
319313
let expression = self.coverage_counters.make_expression(
320314
from_bcb_operand,
321315
Op::Subtract,
322316
sum_of_all_other_branches,
323317
);
324-
debug!("{:?} gets an expression: {:?}", expression_branch, expression);
325-
let bcb = expression_branch.target_bcb;
326-
if expression_branch.is_only_path_to_target() {
327-
self.coverage_counters.set_bcb_counter(bcb, expression);
318+
debug!("{expression_to_bcb:?} gets an expression: {expression:?}");
319+
if self.basic_coverage_blocks.bcb_has_multiple_in_edges(expression_to_bcb) {
320+
self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression);
328321
} else {
329-
self.coverage_counters.set_bcb_edge_counter(from_bcb, bcb, expression);
322+
self.coverage_counters.set_bcb_counter(expression_to_bcb, expression);
330323
}
331324
}
332325

@@ -383,10 +376,16 @@ impl<'a> MakeBcbCounters<'a> {
383376
from_bcb: BasicCoverageBlock,
384377
to_bcb: BasicCoverageBlock,
385378
) -> BcbCounter {
379+
// If the target BCB has only one in-edge (i.e. this one), then create
380+
// a node counter instead, since it will have the same value.
381+
if !self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
382+
assert_eq!([from_bcb].as_slice(), self.basic_coverage_blocks.predecessors[to_bcb]);
383+
return self.get_or_make_counter_operand(to_bcb);
384+
}
385+
386386
// If the source BCB has only one successor (assumed to be the given target), an edge
387387
// counter is unnecessary. Just get or make a counter for the source BCB.
388-
let successors = self.bcb_successors(from_bcb).iter();
389-
if successors.len() == 1 {
388+
if self.bcb_successors(from_bcb).len() == 1 {
390389
return self.get_or_make_counter_operand(from_bcb);
391390
}
392391

@@ -409,16 +408,19 @@ impl<'a> MakeBcbCounters<'a> {
409408
fn choose_preferred_expression_branch(
410409
&self,
411410
traversal: &TraverseCoverageGraphWithLoops<'_>,
412-
branches: &[BcbBranch],
413-
) -> BcbBranch {
414-
let good_reloop_branch = self.find_good_reloop_branch(traversal, &branches);
415-
if let Some(reloop_branch) = good_reloop_branch {
416-
assert!(self.branch_has_no_counter(&reloop_branch));
417-
debug!("Selecting reloop branch {reloop_branch:?} to get an expression");
418-
reloop_branch
411+
from_bcb: BasicCoverageBlock,
412+
) -> BasicCoverageBlock {
413+
let good_reloop_branch = self.find_good_reloop_branch(traversal, from_bcb);
414+
if let Some(reloop_target) = good_reloop_branch {
415+
assert!(self.branch_has_no_counter(from_bcb, reloop_target));
416+
debug!("Selecting reloop target {reloop_target:?} to get an expression");
417+
reloop_target
419418
} else {
420-
let &branch_without_counter =
421-
branches.iter().find(|&branch| self.branch_has_no_counter(branch)).expect(
419+
let &branch_without_counter = self
420+
.bcb_successors(from_bcb)
421+
.iter()
422+
.find(|&&to_bcb| self.branch_has_no_counter(from_bcb, to_bcb))
423+
.expect(
422424
"needs_branch_counters was `true` so there should be at least one \
423425
branch",
424426
);
@@ -439,26 +441,28 @@ impl<'a> MakeBcbCounters<'a> {
439441
fn find_good_reloop_branch(
440442
&self,
441443
traversal: &TraverseCoverageGraphWithLoops<'_>,
442-
branches: &[BcbBranch],
443-
) -> Option<BcbBranch> {
444+
from_bcb: BasicCoverageBlock,
445+
) -> Option<BasicCoverageBlock> {
446+
let branch_target_bcbs = self.bcb_successors(from_bcb);
447+
444448
// Consider each loop on the current traversal context stack, top-down.
445449
for reloop_bcbs in traversal.reloop_bcbs_per_loop() {
446450
let mut all_branches_exit_this_loop = true;
447451

448452
// Try to find a branch that doesn't exit this loop and doesn't
449453
// already have a counter.
450-
for &branch in branches {
454+
for &branch_target_bcb in branch_target_bcbs {
451455
// A branch is a reloop branch if it dominates any BCB that has
452456
// an edge back to the loop header. (Other branches are exits.)
453457
let is_reloop_branch = reloop_bcbs.iter().any(|&reloop_bcb| {
454-
self.basic_coverage_blocks.dominates(branch.target_bcb, reloop_bcb)
458+
self.basic_coverage_blocks.dominates(branch_target_bcb, reloop_bcb)
455459
});
456460

457461
if is_reloop_branch {
458462
all_branches_exit_this_loop = false;
459-
if self.branch_has_no_counter(&branch) {
463+
if self.branch_has_no_counter(from_bcb, branch_target_bcb) {
460464
// We found a good branch to be given an expression.
461-
return Some(branch);
465+
return Some(branch_target_bcb);
462466
}
463467
// Keep looking for another reloop branch without a counter.
464468
} else {
@@ -491,20 +495,20 @@ impl<'a> MakeBcbCounters<'a> {
491495
}
492496

493497
#[inline]
494-
fn bcb_branches(&self, from_bcb: BasicCoverageBlock) -> Vec<BcbBranch> {
495-
self.bcb_successors(from_bcb)
496-
.iter()
497-
.map(|&to_bcb| BcbBranch::from_to(from_bcb, to_bcb, &self.basic_coverage_blocks))
498-
.collect::<Vec<_>>()
499-
}
500-
501-
fn branch_has_no_counter(&self, branch: &BcbBranch) -> bool {
502-
self.branch_counter(branch).is_none()
498+
fn branch_has_no_counter(
499+
&self,
500+
from_bcb: BasicCoverageBlock,
501+
to_bcb: BasicCoverageBlock,
502+
) -> bool {
503+
self.branch_counter(from_bcb, to_bcb).is_none()
503504
}
504505

505-
fn branch_counter(&self, branch: &BcbBranch) -> Option<&BcbCounter> {
506-
let to_bcb = branch.target_bcb;
507-
if let Some(from_bcb) = branch.edge_from_bcb {
506+
fn branch_counter(
507+
&self,
508+
from_bcb: BasicCoverageBlock,
509+
to_bcb: BasicCoverageBlock,
510+
) -> Option<&BcbCounter> {
511+
if self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
508512
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
509513
} else {
510514
self.coverage_counters.bcb_counters[to_bcb].as_ref()

compiler/rustc_mir_transform/src/coverage/graph.rs

-39
Original file line numberDiff line numberDiff line change
@@ -332,45 +332,6 @@ impl BasicCoverageBlockData {
332332
}
333333
}
334334

335-
/// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`)
336-
/// as either the successor BCB itself, if it has only one incoming edge, or the successor _plus_
337-
/// the specific branching BCB, representing the edge between the two. The latter case
338-
/// distinguishes this incoming edge from other incoming edges to the same `target_bcb`.
339-
#[derive(Clone, Copy, PartialEq, Eq)]
340-
pub(super) struct BcbBranch {
341-
pub edge_from_bcb: Option<BasicCoverageBlock>,
342-
pub target_bcb: BasicCoverageBlock,
343-
}
344-
345-
impl BcbBranch {
346-
pub fn from_to(
347-
from_bcb: BasicCoverageBlock,
348-
to_bcb: BasicCoverageBlock,
349-
basic_coverage_blocks: &CoverageGraph,
350-
) -> Self {
351-
let edge_from_bcb = if basic_coverage_blocks.bcb_has_multiple_in_edges(from_bcb) {
352-
Some(from_bcb)
353-
} else {
354-
None
355-
};
356-
Self { edge_from_bcb, target_bcb: to_bcb }
357-
}
358-
359-
pub fn is_only_path_to_target(&self) -> bool {
360-
self.edge_from_bcb.is_none()
361-
}
362-
}
363-
364-
impl std::fmt::Debug for BcbBranch {
365-
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366-
if let Some(from_bcb) = self.edge_from_bcb {
367-
write!(fmt, "{:?}->{:?}", from_bcb, self.target_bcb)
368-
} else {
369-
write!(fmt, "{:?}", self.target_bcb)
370-
}
371-
}
372-
}
373-
374335
// Returns the subset of a block's successors that are relevant to the coverage
375336
// graph, i.e. those that do not represent unwinds or unreachable branches.
376337
// FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and

0 commit comments

Comments
 (0)