Skip to content

Commit 1a7fdc7

Browse files
authored
Rollup merge of #130380 - Zalathar:counters, r=jieyouxu
coverage: Clarify some parts of coverage counter creation This is a series of semi-related changes that are trying to make the `counters` module easier to read, understand, and modify. For example, the existing code happens to avoid ever using the count for a `TerminatorKind::Yield` node as the count for its sole out-edge (since doing so would be incorrect), but doesn't do so explicitly, so seemingly-innocent changes can result in baffling test failures. This PR also takes the opportunity to simplify some debug-logging code that was making its surrounding code disproportionately hard to read. There should be no changes to the resulting coverage instrumentation/mappings, as demonstrated by the absence of changes to the coverage test suite.
2 parents bde6bf2 + 2a3e17c commit 1a7fdc7

File tree

2 files changed

+170
-151
lines changed

2 files changed

+170
-151
lines changed

compiler/rustc_mir_transform/src/coverage/counters.rs

+109-122
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,33 @@ impl CoverageCounters {
9595
this
9696
}
9797

98-
fn make_counter(&mut self, site: CounterIncrementSite) -> BcbCounter {
98+
/// Shared helper used by [`Self::make_phys_node_counter`] and
99+
/// [`Self::make_phys_edge_counter`]. Don't call this directly.
100+
fn make_counter_inner(&mut self, site: CounterIncrementSite) -> BcbCounter {
99101
let id = self.counter_increment_sites.push(site);
100102
BcbCounter::Counter { id }
101103
}
102104

105+
/// Creates a new physical counter attached a BCB node.
106+
/// The node must not already have a counter.
107+
fn make_phys_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter {
108+
let counter = self.make_counter_inner(CounterIncrementSite::Node { bcb });
109+
debug!(?bcb, ?counter, "node gets a physical counter");
110+
self.set_bcb_counter(bcb, counter)
111+
}
112+
113+
/// Creates a new physical counter attached to a BCB edge.
114+
/// The edge must not already have a counter.
115+
fn make_phys_edge_counter(
116+
&mut self,
117+
from_bcb: BasicCoverageBlock,
118+
to_bcb: BasicCoverageBlock,
119+
) -> BcbCounter {
120+
let counter = self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb });
121+
debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter");
122+
self.set_bcb_edge_counter(from_bcb, to_bcb, counter)
123+
}
124+
103125
fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
104126
let new_expr = BcbExpression { lhs, op, rhs };
105127
*self
@@ -294,25 +316,27 @@ impl<'a> MakeBcbCounters<'a> {
294316

295317
let successors = self.basic_coverage_blocks.successors[from_bcb].as_slice();
296318

297-
// If this node doesn't have multiple out-edges, or all of its out-edges
298-
// already have counters, then we don't need to create edge counters.
299-
let needs_out_edge_counters = successors.len() > 1
300-
&& successors.iter().any(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb));
301-
if !needs_out_edge_counters {
319+
// If this node's out-edges won't sum to the node's counter,
320+
// then there's no reason to create edge counters here.
321+
if !self.basic_coverage_blocks[from_bcb].is_out_summable {
302322
return;
303323
}
304324

305-
if tracing::enabled!(tracing::Level::DEBUG) {
306-
let _span =
307-
debug_span!("node has some out-edges without counters", ?from_bcb).entered();
308-
for &to_bcb in successors {
309-
debug!(?to_bcb, counter=?self.edge_counter(from_bcb, to_bcb));
310-
}
311-
}
325+
// Determine the set of out-edges that don't yet have edge counters.
326+
let candidate_successors = self.basic_coverage_blocks.successors[from_bcb]
327+
.iter()
328+
.copied()
329+
.filter(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb))
330+
.collect::<Vec<_>>();
331+
debug!(?candidate_successors);
312332

313-
// Of the out-edges that don't have counters yet, one can be given an expression
314-
// (computed from the other out-edges) instead of a dedicated counter.
315-
let expression_to_bcb = self.choose_out_edge_for_expression(traversal, from_bcb);
333+
// If there are out-edges without counters, choose one to be given an expression
334+
// (computed from this node and the other out-edges) instead of a physical counter.
335+
let Some(expression_to_bcb) =
336+
self.choose_out_edge_for_expression(traversal, &candidate_successors)
337+
else {
338+
return;
339+
};
316340

317341
// For each out-edge other than the one that was chosen to get an expression,
318342
// ensure that it has a counter (existing counter/expression or a new counter),
@@ -324,10 +348,11 @@ impl<'a> MakeBcbCounters<'a> {
324348
.filter(|&to_bcb| to_bcb != expression_to_bcb)
325349
.map(|to_bcb| self.get_or_make_edge_counter(from_bcb, to_bcb))
326350
.collect::<Vec<_>>();
327-
let sum_of_all_other_out_edges: BcbCounter = self
328-
.coverage_counters
329-
.make_sum(&other_out_edge_counters)
330-
.expect("there must be at least one other out-edge");
351+
let Some(sum_of_all_other_out_edges) =
352+
self.coverage_counters.make_sum(&other_out_edge_counters)
353+
else {
354+
return;
355+
};
331356

332357
// Now create an expression for the chosen edge, by taking the counter
333358
// for its source node and subtracting the sum of its sibling out-edges.
@@ -338,10 +363,13 @@ impl<'a> MakeBcbCounters<'a> {
338363
);
339364

340365
debug!("{expression_to_bcb:?} gets an expression: {expression:?}");
341-
if self.basic_coverage_blocks.bcb_has_multiple_in_edges(expression_to_bcb) {
342-
self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression);
343-
} else {
366+
if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(expression_to_bcb) {
367+
// This edge normally wouldn't get its own counter, so attach the expression
368+
// to its target node instead, so that `edge_has_no_counter` can see it.
369+
assert_eq!(sole_pred, from_bcb);
344370
self.coverage_counters.set_bcb_counter(expression_to_bcb, expression);
371+
} else {
372+
self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression);
345373
}
346374
}
347375

@@ -353,28 +381,21 @@ impl<'a> MakeBcbCounters<'a> {
353381
return counter_kind;
354382
}
355383

356-
// A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`).
357-
// Also, a BCB that loops back to itself gets a simple `Counter`. This may indicate the
358-
// program results in a tight infinite loop, but it should still compile.
359-
let one_path_to_target = !self.basic_coverage_blocks.bcb_has_multiple_in_edges(bcb);
360-
if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) {
361-
let counter_kind =
362-
self.coverage_counters.make_counter(CounterIncrementSite::Node { bcb });
363-
if one_path_to_target {
364-
debug!("{bcb:?} gets a new counter: {counter_kind:?}");
365-
} else {
366-
debug!(
367-
"{bcb:?} has itself as its own predecessor. It can't be part of its own \
368-
Expression sum, so it will get its own new counter: {counter_kind:?}. \
369-
(Note, the compiled code will generate an infinite loop.)",
370-
);
371-
}
372-
return self.coverage_counters.set_bcb_counter(bcb, counter_kind);
384+
let predecessors = self.basic_coverage_blocks.predecessors[bcb].as_slice();
385+
386+
// Handle cases where we can't compute a node's count from its in-edges:
387+
// - START_BCB has no in-edges, so taking the sum would panic (or be wrong).
388+
// - For nodes with one in-edge, or that directly loop to themselves,
389+
// trying to get the in-edge counts would require this node's counter,
390+
// leading to infinite recursion.
391+
if predecessors.len() <= 1 || predecessors.contains(&bcb) {
392+
debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor");
393+
return self.coverage_counters.make_phys_node_counter(bcb);
373394
}
374395

375396
// A BCB with multiple incoming edges can compute its count by ensuring that counters
376397
// exist for each of those edges, and then adding them up to get a total count.
377-
let in_edge_counters = self.basic_coverage_blocks.predecessors[bcb]
398+
let in_edge_counters = predecessors
378399
.iter()
379400
.copied()
380401
.map(|from_bcb| self.get_or_make_edge_counter(from_bcb, bcb))
@@ -394,16 +415,19 @@ impl<'a> MakeBcbCounters<'a> {
394415
from_bcb: BasicCoverageBlock,
395416
to_bcb: BasicCoverageBlock,
396417
) -> BcbCounter {
397-
// If the target BCB has only one in-edge (i.e. this one), then create
398-
// a node counter instead, since it will have the same value.
399-
if !self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
400-
assert_eq!([from_bcb].as_slice(), self.basic_coverage_blocks.predecessors[to_bcb]);
418+
// If the target node has exactly one in-edge (i.e. this one), then just
419+
// use the node's counter, since it will have the same value.
420+
if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) {
421+
assert_eq!(sole_pred, from_bcb);
422+
// This call must take care not to invoke `get_or_make_edge` for
423+
// this edge, since that would result in infinite recursion!
401424
return self.get_or_make_node_counter(to_bcb);
402425
}
403426

404-
// If the source BCB has only one successor (assumed to be the given target), an edge
405-
// counter is unnecessary. Just get or make a counter for the source BCB.
406-
if self.bcb_successors(from_bcb).len() == 1 {
427+
// If the source node has exactly one out-edge (i.e. this one) and would have
428+
// the same execution count as that edge, then just use the node's counter.
429+
if let Some(simple_succ) = self.basic_coverage_blocks.simple_successor(from_bcb) {
430+
assert_eq!(simple_succ, to_bcb);
407431
return self.get_or_make_node_counter(from_bcb);
408432
}
409433

@@ -416,118 +440,81 @@ impl<'a> MakeBcbCounters<'a> {
416440
}
417441

418442
// Make a new counter to count this edge.
419-
let counter_kind =
420-
self.coverage_counters.make_counter(CounterIncrementSite::Edge { from_bcb, to_bcb });
421-
debug!("Edge {from_bcb:?}->{to_bcb:?} gets a new counter: {counter_kind:?}");
422-
self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind)
443+
self.coverage_counters.make_phys_edge_counter(from_bcb, to_bcb)
423444
}
424445

425-
/// Choose one of the out-edges of `from_bcb` to receive an expression
426-
/// instead of a physical counter, and returns that edge's target node.
427-
///
428-
/// - Precondition: The node must have at least one out-edge without a counter.
429-
/// - Postcondition: The selected edge does not have an edge counter.
446+
/// Given a set of candidate out-edges (represented by their successor node),
447+
/// choose one to be given a counter expression instead of a physical counter.
430448
fn choose_out_edge_for_expression(
431449
&self,
432450
traversal: &TraverseCoverageGraphWithLoops<'_>,
433-
from_bcb: BasicCoverageBlock,
434-
) -> BasicCoverageBlock {
435-
if let Some(reloop_target) = self.find_good_reloop_edge(traversal, from_bcb) {
436-
assert!(self.edge_has_no_counter(from_bcb, reloop_target));
451+
candidate_successors: &[BasicCoverageBlock],
452+
) -> Option<BasicCoverageBlock> {
453+
// Try to find a candidate that leads back to the top of a loop,
454+
// because reloop edges tend to be executed more times than loop-exit edges.
455+
if let Some(reloop_target) = self.find_good_reloop_edge(traversal, &candidate_successors) {
437456
debug!("Selecting reloop target {reloop_target:?} to get an expression");
438-
return reloop_target;
457+
return Some(reloop_target);
439458
}
440459

441-
// We couldn't identify a "good" edge, so just choose any edge that
442-
// doesn't already have a counter.
443-
let arbitrary_target = self
444-
.bcb_successors(from_bcb)
445-
.iter()
446-
.copied()
447-
.find(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb))
448-
.expect("precondition: at least one out-edge without a counter");
460+
// We couldn't identify a "good" edge, so just choose an arbitrary one.
461+
let arbitrary_target = candidate_successors.first().copied()?;
449462
debug!(?arbitrary_target, "selecting arbitrary out-edge to get an expression");
450-
arbitrary_target
463+
Some(arbitrary_target)
451464
}
452465

453-
/// Tries to find an edge that leads back to the top of a loop, and that
454-
/// doesn't already have a counter. Such edges are good candidates to
455-
/// be given an expression (instead of a physical counter), because they
456-
/// will tend to be executed more times than a loop-exit edge.
466+
/// Given a set of candidate out-edges (represented by their successor node),
467+
/// tries to find one that leads back to the top of a loop.
468+
///
469+
/// Reloop edges are good candidates for counter expressions, because they
470+
/// will tend to be executed more times than a loop-exit edge, so it's nice
471+
/// for them to be able to avoid a physical counter increment.
457472
fn find_good_reloop_edge(
458473
&self,
459474
traversal: &TraverseCoverageGraphWithLoops<'_>,
460-
from_bcb: BasicCoverageBlock,
475+
candidate_successors: &[BasicCoverageBlock],
461476
) -> Option<BasicCoverageBlock> {
462-
let successors = self.bcb_successors(from_bcb);
477+
// If there are no candidates, avoid iterating over the loop stack.
478+
if candidate_successors.is_empty() {
479+
return None;
480+
}
463481

464482
// Consider each loop on the current traversal context stack, top-down.
465483
for reloop_bcbs in traversal.reloop_bcbs_per_loop() {
466-
let mut all_edges_exit_this_loop = true;
467-
468-
// Try to find an out-edge that doesn't exit this loop and doesn't
469-
// already have a counter.
470-
for &target_bcb in successors {
484+
// Try to find a candidate edge that doesn't exit this loop.
485+
for &target_bcb in candidate_successors {
471486
// An edge is a reloop edge if its target dominates any BCB that has
472487
// an edge back to the loop header. (Otherwise it's an exit edge.)
473488
let is_reloop_edge = reloop_bcbs.iter().any(|&reloop_bcb| {
474489
self.basic_coverage_blocks.dominates(target_bcb, reloop_bcb)
475490
});
476-
477491
if is_reloop_edge {
478-
all_edges_exit_this_loop = false;
479-
if self.edge_has_no_counter(from_bcb, target_bcb) {
480-
// We found a good out-edge to be given an expression.
481-
return Some(target_bcb);
482-
}
483-
// Keep looking for another reloop edge without a counter.
484-
} else {
485-
// This edge exits the loop.
492+
// We found a good out-edge to be given an expression.
493+
return Some(target_bcb);
486494
}
487495
}
488496

489-
if !all_edges_exit_this_loop {
490-
// We found one or more reloop edges, but all of them already
491-
// have counters. Let the caller choose one of the other edges.
492-
debug!("All reloop edges had counters; skipping the other loops");
493-
return None;
494-
}
495-
496-
// All of the out-edges exit this loop, so keep looking for a good
497-
// reloop edge for one of the outer loops.
497+
// All of the candidate edges exit this loop, so keep looking
498+
// for a good reloop edge for one of the outer loops.
498499
}
499500

500501
None
501502
}
502503

503-
#[inline]
504-
fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] {
505-
&self.basic_coverage_blocks.predecessors[bcb]
506-
}
507-
508-
#[inline]
509-
fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] {
510-
&self.basic_coverage_blocks.successors[bcb]
511-
}
512-
513504
#[inline]
514505
fn edge_has_no_counter(
515506
&self,
516507
from_bcb: BasicCoverageBlock,
517508
to_bcb: BasicCoverageBlock,
518509
) -> bool {
519-
self.edge_counter(from_bcb, to_bcb).is_none()
520-
}
510+
let edge_counter =
511+
if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) {
512+
assert_eq!(sole_pred, from_bcb);
513+
self.coverage_counters.bcb_counters[to_bcb]
514+
} else {
515+
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)).copied()
516+
};
521517

522-
fn edge_counter(
523-
&self,
524-
from_bcb: BasicCoverageBlock,
525-
to_bcb: BasicCoverageBlock,
526-
) -> Option<&BcbCounter> {
527-
if self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
528-
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
529-
} else {
530-
self.coverage_counters.bcb_counters[to_bcb].as_ref()
531-
}
518+
edge_counter.is_none()
532519
}
533520
}

0 commit comments

Comments
 (0)