Skip to content

Commit 9e675b1

Browse files
committed
coverage: Remove the custom graph traversal from counter creation
The logic behind this traversal order sounds compelling, but in practice it doesn't seem to produce enough benefit to justify its complexity.
1 parent f432e15 commit 9e675b1

21 files changed

+1120
-1306
lines changed

Diff for: compiler/rustc_mir_transform/src/coverage/counters.rs

+4-19
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use rustc_index::IndexVec;
77
use rustc_index::bit_set::BitSet;
88
use rustc_middle::bug;
99
use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op};
10-
use tracing::{debug, debug_span, instrument};
10+
use tracing::{debug, instrument};
1111

12-
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops};
12+
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
1313

1414
/// The coverage counter or counter expression associated with a particular
1515
/// BCB node or BCB edge.
@@ -290,24 +290,9 @@ impl<'a> MakeBcbCounters<'a> {
290290

291291
// Traverse the coverage graph, ensuring that every node that needs a
292292
// coverage counter has one.
293-
//
294-
// The traversal tries to ensure that, when a loop is encountered, all
295-
// nodes within the loop are visited before visiting any nodes outside
296-
// the loop. It also keeps track of which loop(s) the traversal is
297-
// currently inside.
298-
let mut traversal = TraverseCoverageGraphWithLoops::new(self.basic_coverage_blocks);
299-
while let Some(bcb) = traversal.next() {
300-
let _span = debug_span!("traversal", ?bcb).entered();
301-
if self.bcb_needs_counter.contains(bcb) {
302-
self.make_node_counter_and_out_edge_counters(bcb);
303-
}
293+
for bcb in self.bcb_needs_counter.iter() {
294+
self.make_node_counter_and_out_edge_counters(bcb);
304295
}
305-
306-
assert!(
307-
traversal.is_complete(),
308-
"`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}",
309-
traversal.unvisited(),
310-
);
311296
}
312297

313298
/// Make sure the given node has a node counter, and then make sure each of

Diff for: compiler/rustc_mir_transform/src/coverage/graph.rs

+1-166
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
use std::cmp::Ordering;
2-
use std::collections::VecDeque;
32
use std::ops::{Index, IndexMut};
43

54
use rustc_data_structures::captures::Captures;
65
use rustc_data_structures::fx::FxHashSet;
6+
use rustc_data_structures::graph;
77
use rustc_data_structures::graph::dominators::{self, Dominators};
8-
use rustc_data_structures::graph::{self, DirectedGraph, StartNode};
98
use rustc_index::IndexVec;
109
use rustc_index::bit_set::BitSet;
11-
use rustc_middle::bug;
1210
use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind};
1311
use tracing::debug;
1412

@@ -151,11 +149,6 @@ impl CoverageGraph {
151149
if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None }
152150
}
153151

154-
#[inline(always)]
155-
pub(crate) fn dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
156-
self.dominators.as_ref().unwrap().dominates(dom, node)
157-
}
158-
159152
#[inline(always)]
160153
pub(crate) fn cmp_in_dominator_order(
161154
&self,
@@ -400,164 +393,6 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
400393
}
401394
}
402395

403-
/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the
404-
/// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that
405-
/// ensures a loop is completely traversed before processing Blocks after the end of the loop.
406-
#[derive(Debug)]
407-
struct TraversalContext {
408-
/// BCB with one or more incoming loop backedges, indicating which loop
409-
/// this context is for.
410-
///
411-
/// If `None`, this is the non-loop context for the function as a whole.
412-
loop_header: Option<BasicCoverageBlock>,
413-
414-
/// Worklist of BCBs to be processed in this context.
415-
worklist: VecDeque<BasicCoverageBlock>,
416-
}
417-
418-
pub(crate) struct TraverseCoverageGraphWithLoops<'a> {
419-
basic_coverage_blocks: &'a CoverageGraph,
420-
421-
backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
422-
context_stack: Vec<TraversalContext>,
423-
visited: BitSet<BasicCoverageBlock>,
424-
}
425-
426-
impl<'a> TraverseCoverageGraphWithLoops<'a> {
427-
pub(crate) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self {
428-
let backedges = find_loop_backedges(basic_coverage_blocks);
429-
430-
let worklist = VecDeque::from([basic_coverage_blocks.start_node()]);
431-
let context_stack = vec![TraversalContext { loop_header: None, worklist }];
432-
433-
// `context_stack` starts with a `TraversalContext` for the main function context (beginning
434-
// with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top
435-
// of the stack as loops are entered, and popped off of the stack when a loop's worklist is
436-
// exhausted.
437-
let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes());
438-
Self { basic_coverage_blocks, backedges, context_stack, visited }
439-
}
440-
441-
pub(crate) fn next(&mut self) -> Option<BasicCoverageBlock> {
442-
debug!(
443-
"TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
444-
self.context_stack.iter().rev().collect::<Vec<_>>()
445-
);
446-
447-
while let Some(context) = self.context_stack.last_mut() {
448-
let Some(bcb) = context.worklist.pop_front() else {
449-
// This stack level is exhausted; pop it and try the next one.
450-
self.context_stack.pop();
451-
continue;
452-
};
453-
454-
if !self.visited.insert(bcb) {
455-
debug!("Already visited: {bcb:?}");
456-
continue;
457-
}
458-
debug!("Visiting {bcb:?}");
459-
460-
if self.backedges[bcb].len() > 0 {
461-
debug!("{bcb:?} is a loop header! Start a new TraversalContext...");
462-
self.context_stack
463-
.push(TraversalContext { loop_header: Some(bcb), worklist: VecDeque::new() });
464-
}
465-
self.add_successors_to_worklists(bcb);
466-
return Some(bcb);
467-
}
468-
469-
None
470-
}
471-
472-
fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) {
473-
let successors = &self.basic_coverage_blocks.successors[bcb];
474-
debug!("{:?} has {} successors:", bcb, successors.len());
475-
476-
for &successor in successors {
477-
if successor == bcb {
478-
debug!(
479-
"{:?} has itself as its own successor. (Note, the compiled code will \
480-
generate an infinite loop.)",
481-
bcb
482-
);
483-
// Don't re-add this successor to the worklist. We are already processing it.
484-
// FIXME: This claims to skip just the self-successor, but it actually skips
485-
// all other successors as well. Does that matter?
486-
break;
487-
}
488-
489-
// Add successors of the current BCB to the appropriate context. Successors that
490-
// stay within a loop are added to the BCBs context worklist. Successors that
491-
// exit the loop (they are not dominated by the loop header) must be reachable
492-
// from other BCBs outside the loop, and they will be added to a different
493-
// worklist.
494-
//
495-
// Branching blocks (with more than one successor) must be processed before
496-
// blocks with only one successor, to prevent unnecessarily complicating
497-
// `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
498-
// branching block would have given an `Expression` (or vice versa).
499-
500-
let context = self
501-
.context_stack
502-
.iter_mut()
503-
.rev()
504-
.find(|context| match context.loop_header {
505-
Some(loop_header) => {
506-
self.basic_coverage_blocks.dominates(loop_header, successor)
507-
}
508-
None => true,
509-
})
510-
.unwrap_or_else(|| bug!("should always fall back to the root non-loop context"));
511-
debug!("adding to worklist for {:?}", context.loop_header);
512-
513-
// FIXME: The code below had debug messages claiming to add items to a
514-
// particular end of the worklist, but was confused about which end was
515-
// which. The existing behaviour has been preserved for now, but it's
516-
// unclear what the intended behaviour was.
517-
518-
if self.basic_coverage_blocks.successors[successor].len() > 1 {
519-
context.worklist.push_back(successor);
520-
} else {
521-
context.worklist.push_front(successor);
522-
}
523-
}
524-
}
525-
526-
pub(crate) fn is_complete(&self) -> bool {
527-
self.visited.count() == self.visited.domain_size()
528-
}
529-
530-
pub(crate) fn unvisited(&self) -> Vec<BasicCoverageBlock> {
531-
let mut unvisited_set: BitSet<BasicCoverageBlock> =
532-
BitSet::new_filled(self.visited.domain_size());
533-
unvisited_set.subtract(&self.visited);
534-
unvisited_set.iter().collect::<Vec<_>>()
535-
}
536-
}
537-
538-
fn find_loop_backedges(
539-
basic_coverage_blocks: &CoverageGraph,
540-
) -> IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>> {
541-
let num_bcbs = basic_coverage_blocks.num_nodes();
542-
let mut backedges = IndexVec::from_elem_n(Vec::<BasicCoverageBlock>::new(), num_bcbs);
543-
544-
// Identify loops by their backedges.
545-
for (bcb, _) in basic_coverage_blocks.iter_enumerated() {
546-
for &successor in &basic_coverage_blocks.successors[bcb] {
547-
if basic_coverage_blocks.dominates(successor, bcb) {
548-
let loop_header = successor;
549-
let backedge_from_bcb = bcb;
550-
debug!(
551-
"Found BCB backedge: {:?} -> loop_header: {:?}",
552-
backedge_from_bcb, loop_header
553-
);
554-
backedges[loop_header].push(backedge_from_bcb);
555-
}
556-
}
557-
}
558-
backedges
559-
}
560-
561396
fn short_circuit_preorder<'a, 'tcx, F, Iter>(
562397
body: &'a mir::Body<'tcx>,
563398
filtered_successors: F,

Diff for: tests/coverage/async.cov-map

+8-8
Original file line numberDiff line numberDiff line change
@@ -155,25 +155,25 @@ Number of file 0 mappings: 1
155155
Max counter seen: c0
156156

157157
Function name: async::i::{closure#0}
158-
Raw bytes (63): 0x[01, 01, 02, 07, 19, 11, 15, 0b, 01, 2d, 13, 04, 0c, 09, 05, 09, 00, 0a, 01, 00, 0e, 00, 18, 05, 00, 1c, 00, 21, 09, 00, 27, 00, 30, 15, 01, 09, 00, 0a, 0d, 00, 0e, 00, 17, 1d, 00, 1b, 00, 20, 15, 00, 24, 00, 26, 19, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
158+
Raw bytes (63): 0x[01, 01, 02, 07, 15, 0d, 11, 0b, 01, 2d, 13, 04, 0c, 09, 05, 09, 00, 0a, 01, 00, 0e, 00, 18, 05, 00, 1c, 00, 21, 09, 00, 27, 00, 30, 11, 01, 09, 00, 0a, 19, 00, 0e, 00, 17, 1d, 00, 1b, 00, 20, 11, 00, 24, 00, 26, 15, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
159159
Number of files: 1
160160
- file 0 => global file 1
161161
Number of expressions: 2
162-
- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(6)
163-
- expression 1 operands: lhs = Counter(4), rhs = Counter(5)
162+
- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(5)
163+
- expression 1 operands: lhs = Counter(3), rhs = Counter(4)
164164
Number of file 0 mappings: 11
165165
- Code(Counter(0)) at (prev + 45, 19) to (start + 4, 12)
166166
- Code(Counter(2)) at (prev + 5, 9) to (start + 0, 10)
167167
- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 24)
168168
- Code(Counter(1)) at (prev + 0, 28) to (start + 0, 33)
169169
- Code(Counter(2)) at (prev + 0, 39) to (start + 0, 48)
170-
- Code(Counter(5)) at (prev + 1, 9) to (start + 0, 10)
171-
- Code(Counter(3)) at (prev + 0, 14) to (start + 0, 23)
170+
- Code(Counter(4)) at (prev + 1, 9) to (start + 0, 10)
171+
- Code(Counter(6)) at (prev + 0, 14) to (start + 0, 23)
172172
- Code(Counter(7)) at (prev + 0, 27) to (start + 0, 32)
173-
- Code(Counter(5)) at (prev + 0, 36) to (start + 0, 38)
174-
- Code(Counter(6)) at (prev + 1, 14) to (start + 0, 16)
173+
- Code(Counter(4)) at (prev + 0, 36) to (start + 0, 38)
174+
- Code(Counter(5)) at (prev + 1, 14) to (start + 0, 16)
175175
- Code(Expression(0, Add)) at (prev + 2, 1) to (start + 0, 2)
176-
= ((c4 + c5) + c6)
176+
= ((c3 + c4) + c5)
177177
Max counter seen: c7
178178

179179
Function name: async::j

Diff for: tests/coverage/branch/if.cov-map

+17-14
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
Function name: if::branch_and
2-
Raw bytes (56): 0x[01, 01, 04, 05, 09, 0d, 02, 11, 0f, 0d, 02, 08, 01, 2b, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 00, 0d, 00, 0e, 20, 11, 0d, 00, 0d, 00, 0e, 11, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02]
2+
Raw bytes (62): 0x[01, 01, 07, 05, 09, 09, 0d, 1a, 02, 09, 0d, 0d, 17, 1a, 02, 09, 0d, 08, 01, 2b, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 00, 0d, 00, 0e, 20, 0d, 1a, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 17, 02, 0c, 02, 06, 13, 03, 01, 00, 02]
33
Number of files: 1
44
- file 0 => global file 1
5-
Number of expressions: 4
5+
Number of expressions: 7
66
- expression 0 operands: lhs = Counter(1), rhs = Counter(2)
7-
- expression 1 operands: lhs = Counter(3), rhs = Expression(0, Sub)
8-
- expression 2 operands: lhs = Counter(4), rhs = Expression(3, Add)
9-
- expression 3 operands: lhs = Counter(3), rhs = Expression(0, Sub)
7+
- expression 1 operands: lhs = Counter(2), rhs = Counter(3)
8+
- expression 2 operands: lhs = Expression(6, Sub), rhs = Expression(0, Sub)
9+
- expression 3 operands: lhs = Counter(2), rhs = Counter(3)
10+
- expression 4 operands: lhs = Counter(3), rhs = Expression(5, Add)
11+
- expression 5 operands: lhs = Expression(6, Sub), rhs = Expression(0, Sub)
12+
- expression 6 operands: lhs = Counter(2), rhs = Counter(3)
1013
Number of file 0 mappings: 8
1114
- Code(Counter(0)) at (prev + 43, 1) to (start + 1, 16)
1215
- Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9)
1316
- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 8) to (start + 0, 9)
1417
true = c2
1518
false = (c1 - c2)
1619
- Code(Counter(2)) at (prev + 0, 13) to (start + 0, 14)
17-
- Branch { true: Counter(4), false: Counter(3) } at (prev + 0, 13) to (start + 0, 14)
18-
true = c4
19-
false = c3
20-
- Code(Counter(4)) at (prev + 0, 15) to (start + 2, 6)
21-
- Code(Expression(3, Add)) at (prev + 2, 12) to (start + 2, 6)
22-
= (c3 + (c1 - c2))
23-
- Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2)
24-
= (c4 + (c3 + (c1 - c2)))
25-
Max counter seen: c4
20+
- Branch { true: Counter(3), false: Expression(6, Sub) } at (prev + 0, 13) to (start + 0, 14)
21+
true = c3
22+
false = (c2 - c3)
23+
- Code(Counter(3)) at (prev + 0, 15) to (start + 2, 6)
24+
- Code(Expression(5, Add)) at (prev + 2, 12) to (start + 2, 6)
25+
= ((c2 - c3) + (c1 - c2))
26+
- Code(Expression(4, Add)) at (prev + 3, 1) to (start + 0, 2)
27+
= (c3 + ((c2 - c3) + (c1 - c2)))
28+
Max counter seen: c3
2629

2730
Function name: if::branch_not
2831
Raw bytes (116): 0x[01, 01, 07, 05, 09, 05, 0d, 05, 0d, 05, 11, 05, 11, 05, 15, 05, 15, 12, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, 05, 01, 08, 00, 0a, 20, 0a, 0d, 00, 08, 00, 0a, 0a, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 05, 01, 08, 00, 0b, 20, 11, 12, 00, 08, 00, 0b, 11, 00, 0c, 02, 06, 12, 02, 06, 00, 07, 05, 01, 08, 00, 0c, 20, 1a, 15, 00, 08, 00, 0c, 1a, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 05, 01, 01, 00, 02]

0 commit comments

Comments
 (0)