Skip to content

Commit a7ae2a6

Browse files
committed
coverage: Simplify the detection of reloop edges to be given expressions
1 parent 4f05e95 commit a7ae2a6

File tree

2 files changed

+57
-82
lines changed

2 files changed

+57
-82
lines changed

compiler/rustc_mir_transform/src/coverage/counters.rs

Lines changed: 43 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -510,18 +510,11 @@ impl<'a> MakeBcbCounters<'a> {
510510
traversal: &TraverseCoverageGraphWithLoops,
511511
branches: &[BcbBranch],
512512
) -> BcbBranch {
513-
let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
514-
515-
let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches);
516-
if let Some(reloop_branch_without_counter) =
517-
some_reloop_branch.filter(branch_needs_a_counter)
518-
{
519-
debug!(
520-
"Selecting reloop_branch={:?} that still needs a counter, to get the \
521-
`Expression`",
522-
reloop_branch_without_counter
523-
);
524-
reloop_branch_without_counter
513+
let good_reloop_branch = self.find_good_reloop_branch(traversal, &branches);
514+
if let Some(reloop_branch) = good_reloop_branch {
515+
assert!(self.branch_has_no_counter(&reloop_branch));
516+
debug!("Selecting reloop branch {reloop_branch:?} to get an expression");
517+
reloop_branch
525518
} else {
526519
let &branch_without_counter =
527520
branches.iter().find(|&branch| self.branch_has_no_counter(branch)).expect(
@@ -538,75 +531,52 @@ impl<'a> MakeBcbCounters<'a> {
538531
}
539532
}
540533

541-
/// At most, one of the branches (or its edge, from the branching_bcb, if the branch has
542-
/// multiple incoming edges) can have a counter computed by expression.
543-
///
544-
/// If at least one of the branches leads outside of a loop (`found_loop_exit` is
545-
/// true), and at least one other branch does not exit the loop (the first of which
546-
/// is captured in `some_reloop_branch`), it's likely any reloop branch will be
547-
/// executed far more often than loop exit branch, making the reloop branch a better
548-
/// candidate for an expression.
549-
fn find_some_reloop_branch(
534+
/// Tries to find a branch that leads back to the top of a loop, and that
535+
/// doesn't already have a counter. Such branches are good candidates to
536+
/// be given an expression (instead of a physical counter), because they
537+
/// will tend to be executed more times than a loop-exit branch.
538+
fn find_good_reloop_branch(
550539
&self,
551540
traversal: &TraverseCoverageGraphWithLoops,
552541
branches: &[BcbBranch],
553542
) -> Option<BcbBranch> {
554-
let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
555-
556-
let mut some_reloop_branch: Option<BcbBranch> = None;
557-
for context in traversal.context_stack.iter().rev() {
558-
if let Some((backedge_from_bcbs, _)) = &context.loop_backedges {
559-
let mut found_loop_exit = false;
560-
for &branch in branches.iter() {
561-
if backedge_from_bcbs.iter().any(|&backedge_from_bcb| {
562-
self.bcb_dominates(branch.target_bcb, backedge_from_bcb)
563-
}) {
564-
if let Some(reloop_branch) = some_reloop_branch {
565-
if self.branch_has_no_counter(&reloop_branch) {
566-
// we already found a candidate reloop_branch that still
567-
// needs a counter
568-
continue;
569-
}
570-
}
571-
// The path from branch leads back to the top of the loop. Set this
572-
// branch as the `reloop_branch`. If this branch already has a
573-
// counter, and we find another reloop branch that doesn't have a
574-
// counter yet, that branch will be selected as the `reloop_branch`
575-
// instead.
576-
some_reloop_branch = Some(branch);
577-
} else {
578-
// The path from branch leads outside this loop
579-
found_loop_exit = true;
580-
}
581-
if found_loop_exit
582-
&& some_reloop_branch.filter(branch_needs_a_counter).is_some()
583-
{
584-
// Found both a branch that exits the loop and a branch that returns
585-
// to the top of the loop (`reloop_branch`), and the `reloop_branch`
586-
// doesn't already have a counter.
587-
break;
543+
// Consider each loop on the current traversal context stack, top-down.
544+
for reloop_bcbs in traversal.reloop_bcbs_per_loop() {
545+
let mut all_branches_exit_this_loop = true;
546+
547+
// Try to find a branch that doesn't exit this loop and doesn't
548+
// already have a counter.
549+
for &branch in branches {
550+
// A branch is a reloop branch if it dominates any BCB that has
551+
// an edge back to the loop header. (Other branches are exits.)
552+
let is_reloop_branch = reloop_bcbs.iter().any(|&reloop_bcb| {
553+
self.basic_coverage_blocks.dominates(branch.target_bcb, reloop_bcb)
554+
});
555+
556+
if is_reloop_branch {
557+
all_branches_exit_this_loop = false;
558+
if self.branch_has_no_counter(&branch) {
559+
// We found a good branch to be given an expression.
560+
return Some(branch);
588561
}
562+
// Keep looking for another reloop branch without a counter.
563+
} else {
564+
// This branch exits the loop.
589565
}
590-
if !found_loop_exit {
591-
debug!(
592-
"No branches exit the loop, so any branch without an existing \
593-
counter can have the `Expression`."
594-
);
595-
break;
596-
}
597-
if some_reloop_branch.is_some() {
598-
debug!(
599-
"Found a branch that exits the loop and a branch the loops back to \
600-
the top of the loop (`reloop_branch`). The `reloop_branch` will \
601-
get the `Expression`, as long as it still needs a counter."
602-
);
603-
break;
604-
}
605-
// else all branches exited this loop context, so run the same checks with
606-
// the outer loop(s)
607566
}
567+
568+
if !all_branches_exit_this_loop {
569+
// We found one or more reloop branches, but all of them already
570+
// have counters. Let the caller choose one of the exit branches.
571+
debug!("All reloop branches had counters; skip checking the other loops");
572+
return None;
573+
}
574+
575+
// All of the branches exit this loop, so keep looking for a good
576+
// reloop branch for one of the outer loops.
608577
}
609-
some_reloop_branch
578+
579+
None
610580
}
611581

612582
#[inline]
@@ -652,9 +622,4 @@ impl<'a> MakeBcbCounters<'a> {
652622
fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool {
653623
self.bcb_predecessors(bcb).len() <= 1
654624
}
655-
656-
#[inline]
657-
fn bcb_dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
658-
self.basic_coverage_blocks.dominates(dom, node)
659-
}
660625
}

compiler/rustc_mir_transform/src/coverage/graph.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -386,17 +386,17 @@ fn bcb_filtered_successors<'a, 'tcx>(
386386
#[derive(Debug)]
387387
pub(super) struct TraversalContext {
388388
/// From one or more backedges returning to a loop header.
389-
pub loop_backedges: Option<(Vec<BasicCoverageBlock>, BasicCoverageBlock)>,
389+
loop_backedges: Option<(Vec<BasicCoverageBlock>, BasicCoverageBlock)>,
390390

391391
/// worklist, to be traversed, of CoverageGraph in the loop with the given loop
392392
/// backedges, such that the loop is the inner inner-most loop containing these
393393
/// CoverageGraph
394-
pub worklist: Vec<BasicCoverageBlock>,
394+
worklist: Vec<BasicCoverageBlock>,
395395
}
396396

397397
pub(super) struct TraverseCoverageGraphWithLoops {
398-
pub backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
399-
pub context_stack: Vec<TraversalContext>,
398+
backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
399+
context_stack: Vec<TraversalContext>,
400400
visited: BitSet<BasicCoverageBlock>,
401401
}
402402

@@ -414,6 +414,16 @@ impl TraverseCoverageGraphWithLoops {
414414
Self { backedges, context_stack, visited }
415415
}
416416

417+
/// For each loop on the loop context stack (top-down), yields a list of BCBs
418+
/// within that loop that have an outgoing edge back to the loop header.
419+
pub(super) fn reloop_bcbs_per_loop(&self) -> impl Iterator<Item = &[BasicCoverageBlock]> {
420+
self.context_stack
421+
.iter()
422+
.rev()
423+
.filter_map(|context| context.loop_backedges.as_ref())
424+
.map(|(from_bcbs, _to_bcb)| from_bcbs.as_slice())
425+
}
426+
417427
pub fn next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option<BasicCoverageBlock> {
418428
debug!(
419429
"TraverseCoverageGraphWithLoops::next - context_stack: {:?}",

0 commit comments

Comments
 (0)