Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a9e4736

Browse files
committedNov 7, 2023
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 6cd8248 commit a9e4736

File tree

2 files changed

+70
-105
lines changed

2 files changed

+70
-105
lines changed
 

‎compiler/rustc_mir_transform/src/coverage/counters.rs

+70-66
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
@@ -257,49 +255,46 @@ impl<'a> MakeBcbCounters<'a> {
257255
// We might also use its term later to compute one of the branch counters.
258256
let from_bcb_operand = self.get_or_make_counter_operand(from_bcb);
259257

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

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

270270
debug!(
271271
"{from_bcb:?} has some branch(es) without counters:\n {}",
272-
branches
272+
branch_target_bcbs
273273
.iter()
274-
.map(|branch| { format!("{:?}: {:?}", branch, self.branch_counter(branch)) })
274+
.map(|&to_bcb| {
275+
format!("{from_bcb:?}->{to_bcb:?}: {:?}", self.branch_counter(from_bcb, to_bcb))
276+
})
275277
.collect::<Vec<_>>()
276278
.join("\n "),
277279
);
278280

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

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

@@ -381,10 +374,16 @@ impl<'a> MakeBcbCounters<'a> {
381374
from_bcb: BasicCoverageBlock,
382375
to_bcb: BasicCoverageBlock,
383376
) -> BcbCounter {
377+
// If the target BCB has only one in-edge (i.e. this one), then create
378+
// a node counter instead, since it will have the same value.
379+
if !self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
380+
assert_eq!([from_bcb].as_slice(), self.basic_coverage_blocks.predecessors[to_bcb]);
381+
return self.get_or_make_counter_operand(to_bcb);
382+
}
383+
384384
// If the source BCB has only one successor (assumed to be the given target), an edge
385385
// counter is unnecessary. Just get or make a counter for the source BCB.
386-
let successors = self.bcb_successors(from_bcb).iter();
387-
if successors.len() == 1 {
386+
if self.bcb_successors(from_bcb).len() == 1 {
388387
return self.get_or_make_counter_operand(from_bcb);
389388
}
390389

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

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

455459
if is_reloop_branch {
456460
all_branches_exit_this_loop = false;
457-
if self.branch_has_no_counter(&branch) {
461+
if self.branch_has_no_counter(from_bcb, branch_target_bcb) {
458462
// We found a good branch to be given an expression.
459-
return Some(branch);
463+
return Some(branch_target_bcb);
460464
}
461465
// Keep looking for another reloop branch without a counter.
462466
} else {
@@ -489,20 +493,20 @@ impl<'a> MakeBcbCounters<'a> {
489493
}
490494

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

503-
fn branch_counter(&self, branch: &BcbBranch) -> Option<&BcbCounter> {
504-
let to_bcb = branch.target_bcb;
505-
if let Some(from_bcb) = branch.edge_from_bcb {
504+
fn branch_counter(
505+
&self,
506+
from_bcb: BasicCoverageBlock,
507+
to_bcb: BasicCoverageBlock,
508+
) -> Option<&BcbCounter> {
509+
if self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
506510
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
507511
} else {
508512
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)
Please sign in to comment.