Skip to content

Commit 5ca30c4

Browse files
committed
Store BCB counters externally, not directly in the BCB graph
Storing coverage counter information in `CoverageCounters` has a few advantages over storing it directly inside BCB graph nodes: - The graph doesn't need to be mutable when making the counters, making it easier to see that the graph itself is not modified during this step. - All of the counter data is clearly visible in one place. - It becomes possible to use a representation that doesn't correspond 1:1 to graph nodes, e.g. storing all the edge counters in a single hashmap instead of several.
1 parent 5302c9d commit 5ca30c4

File tree

5 files changed

+157
-158
lines changed

5 files changed

+157
-158
lines changed

compiler/rustc_mir_transform/src/coverage/counters.rs

+125-27
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,28 @@ use debug::{DebugCounters, NESTED_INDENT};
88
use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops};
99
use spans::CoverageSpan;
1010

11+
use rustc_data_structures::fx::FxHashMap;
1112
use rustc_data_structures::graph::WithNumNodes;
1213
use rustc_index::bit_set::BitSet;
14+
use rustc_index::IndexVec;
1315
use rustc_middle::mir::coverage::*;
1416

15-
/// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR
16-
/// `Coverage` statements.
17+
/// Generates and stores coverage counter and coverage expression information
18+
/// associated with nodes/edges in the BCB graph.
1719
pub(super) struct CoverageCounters {
1820
function_source_hash: u64,
1921
next_counter_id: CounterId,
2022
next_expression_id: ExpressionId,
2123

24+
/// Coverage counters/expressions that are associated with individual BCBs.
25+
bcb_counters: IndexVec<BasicCoverageBlock, Option<CoverageKind>>,
26+
/// Coverage counters/expressions that are associated with the control-flow
27+
/// edge between two BCBs.
28+
bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), CoverageKind>,
29+
/// Tracks which BCBs have a counter associated with some incoming edge.
30+
/// Only used by debug assertions, to verify that BCBs with incoming edge
31+
/// counters do not have their own physical counters (expressions are allowed).
32+
bcb_has_incoming_edge_counters: BitSet<BasicCoverageBlock>,
2233
/// Expression nodes that are not directly associated with any particular
2334
/// BCB/edge, but are needed as operands to more complex expressions.
2435
/// These are always `CoverageKind::Expression`.
@@ -28,12 +39,17 @@ pub(super) struct CoverageCounters {
2839
}
2940

3041
impl CoverageCounters {
31-
pub fn new(function_source_hash: u64) -> Self {
42+
pub(super) fn new(function_source_hash: u64, basic_coverage_blocks: &CoverageGraph) -> Self {
43+
let num_bcbs = basic_coverage_blocks.num_nodes();
44+
3245
Self {
3346
function_source_hash,
3447
next_counter_id: CounterId::START,
3548
next_expression_id: ExpressionId::START,
3649

50+
bcb_counters: IndexVec::from_elem_n(None, num_bcbs),
51+
bcb_edge_counters: FxHashMap::default(),
52+
bcb_has_incoming_edge_counters: BitSet::new_empty(num_bcbs),
3753
intermediate_expressions: Vec::new(),
3854

3955
debug_counters: DebugCounters::new(),
@@ -51,7 +67,7 @@ impl CoverageCounters {
5167
/// representing intermediate values.
5268
pub fn make_bcb_counters(
5369
&mut self,
54-
basic_coverage_blocks: &mut CoverageGraph,
70+
basic_coverage_blocks: &CoverageGraph,
5571
coverage_spans: &[CoverageSpan],
5672
) -> Result<(), Error> {
5773
MakeBcbCounters::new(self, basic_coverage_blocks).make_bcb_counters(coverage_spans)
@@ -114,6 +130,80 @@ impl CoverageCounters {
114130
self.next_expression_id = next.next_id();
115131
next
116132
}
133+
134+
fn set_bcb_counter(
135+
&mut self,
136+
bcb: BasicCoverageBlock,
137+
counter_kind: CoverageKind,
138+
) -> Result<Operand, Error> {
139+
debug_assert!(
140+
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
141+
// have an expression (to be injected into an existing `BasicBlock` represented by this
142+
// `BasicCoverageBlock`).
143+
counter_kind.is_expression() || !self.bcb_has_incoming_edge_counters.contains(bcb),
144+
"attempt to add a `Counter` to a BCB target with existing incoming edge counters"
145+
);
146+
let operand = counter_kind.as_operand();
147+
if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) {
148+
Error::from_string(format!(
149+
"attempt to set a BasicCoverageBlock coverage counter more than once; \
150+
{bcb:?} already had counter {replaced:?}",
151+
))
152+
} else {
153+
Ok(operand)
154+
}
155+
}
156+
157+
fn set_bcb_edge_counter(
158+
&mut self,
159+
from_bcb: BasicCoverageBlock,
160+
to_bcb: BasicCoverageBlock,
161+
counter_kind: CoverageKind,
162+
) -> Result<Operand, Error> {
163+
if level_enabled!(tracing::Level::DEBUG) {
164+
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
165+
// have an expression (to be injected into an existing `BasicBlock` represented by this
166+
// `BasicCoverageBlock`).
167+
if self.bcb_counter(to_bcb).is_some_and(|c| !c.is_expression()) {
168+
return Error::from_string(format!(
169+
"attempt to add an incoming edge counter from {from_bcb:?} when the target BCB already \
170+
has a `Counter`"
171+
));
172+
}
173+
}
174+
self.bcb_has_incoming_edge_counters.insert(to_bcb);
175+
let operand = counter_kind.as_operand();
176+
if let Some(replaced) = self.bcb_edge_counters.insert((from_bcb, to_bcb), counter_kind) {
177+
Error::from_string(format!(
178+
"attempt to set an edge counter more than once; from_bcb: \
179+
{from_bcb:?} already had counter {replaced:?}",
180+
))
181+
} else {
182+
Ok(operand)
183+
}
184+
}
185+
186+
pub(super) fn bcb_counter(&self, bcb: BasicCoverageBlock) -> Option<&CoverageKind> {
187+
self.bcb_counters[bcb].as_ref()
188+
}
189+
190+
pub(super) fn take_bcb_counter(&mut self, bcb: BasicCoverageBlock) -> Option<CoverageKind> {
191+
self.bcb_counters[bcb].take()
192+
}
193+
194+
pub(super) fn drain_bcb_counters(
195+
&mut self,
196+
) -> impl Iterator<Item = (BasicCoverageBlock, CoverageKind)> + '_ {
197+
self.bcb_counters
198+
.iter_enumerated_mut()
199+
.filter_map(|(bcb, counter)| Some((bcb, counter.take()?)))
200+
}
201+
202+
pub(super) fn drain_bcb_edge_counters(
203+
&mut self,
204+
) -> impl Iterator<Item = ((BasicCoverageBlock, BasicCoverageBlock), CoverageKind)> + '_ {
205+
self.bcb_edge_counters.drain()
206+
}
117207
}
118208

119209
/// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be
@@ -122,13 +212,13 @@ impl CoverageCounters {
122212
/// embedded counter, an `Expression` should be used.
123213
struct MakeBcbCounters<'a> {
124214
coverage_counters: &'a mut CoverageCounters,
125-
basic_coverage_blocks: &'a mut CoverageGraph,
215+
basic_coverage_blocks: &'a CoverageGraph,
126216
}
127217

128218
impl<'a> MakeBcbCounters<'a> {
129219
fn new(
130220
coverage_counters: &'a mut CoverageCounters,
131-
basic_coverage_blocks: &'a mut CoverageGraph,
221+
basic_coverage_blocks: &'a CoverageGraph,
132222
) -> Self {
133223
Self { coverage_counters, basic_coverage_blocks }
134224
}
@@ -202,9 +292,7 @@ impl<'a> MakeBcbCounters<'a> {
202292
branching_bcb,
203293
branches
204294
.iter()
205-
.map(|branch| {
206-
format!("{:?}: {:?}", branch, branch.counter(&self.basic_coverage_blocks))
207-
})
295+
.map(|branch| { format!("{:?}: {:?}", branch, self.branch_counter(branch)) })
208296
.collect::<Vec<_>>()
209297
.join("\n "),
210298
);
@@ -274,9 +362,9 @@ impl<'a> MakeBcbCounters<'a> {
274362
debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression));
275363
let bcb = expression_branch.target_bcb;
276364
if expression_branch.is_only_path_to_target() {
277-
self.basic_coverage_blocks[bcb].set_counter(expression)?;
365+
self.coverage_counters.set_bcb_counter(bcb, expression)?;
278366
} else {
279-
self.basic_coverage_blocks[bcb].set_edge_counter_from(branching_bcb, expression)?;
367+
self.coverage_counters.set_bcb_edge_counter(branching_bcb, bcb, expression)?;
280368
}
281369
Ok(())
282370
}
@@ -291,7 +379,7 @@ impl<'a> MakeBcbCounters<'a> {
291379
debug_indent_level: usize,
292380
) -> Result<Operand, Error> {
293381
// If the BCB already has a counter, return it.
294-
if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() {
382+
if let Some(counter_kind) = &self.coverage_counters.bcb_counters[bcb] {
295383
debug!(
296384
"{}{:?} already has a counter: {}",
297385
NESTED_INDENT.repeat(debug_indent_level),
@@ -324,7 +412,7 @@ impl<'a> MakeBcbCounters<'a> {
324412
self.format_counter(&counter_kind),
325413
);
326414
}
327-
return self.basic_coverage_blocks[bcb].set_counter(counter_kind);
415+
return self.coverage_counters.set_bcb_counter(bcb, counter_kind);
328416
}
329417

330418
// A BCB with multiple incoming edges can compute its count by `Expression`, summing up the
@@ -380,7 +468,7 @@ impl<'a> MakeBcbCounters<'a> {
380468
bcb,
381469
self.format_counter(&counter_kind)
382470
);
383-
self.basic_coverage_blocks[bcb].set_counter(counter_kind)
471+
self.coverage_counters.set_bcb_counter(bcb, counter_kind)
384472
}
385473

386474
fn get_or_make_edge_counter_operand(
@@ -405,7 +493,9 @@ impl<'a> MakeBcbCounters<'a> {
405493
}
406494

407495
// If the edge already has a counter, return it.
408-
if let Some(counter_kind) = self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) {
496+
if let Some(counter_kind) =
497+
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
498+
{
409499
debug!(
410500
"{}Edge {:?}->{:?} already has a counter: {}",
411501
NESTED_INDENT.repeat(debug_indent_level),
@@ -426,7 +516,7 @@ impl<'a> MakeBcbCounters<'a> {
426516
to_bcb,
427517
self.format_counter(&counter_kind)
428518
);
429-
self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind)
519+
self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind)
430520
}
431521

432522
/// Select a branch for the expression, either the recommended `reloop_branch`, or if none was
@@ -436,8 +526,7 @@ impl<'a> MakeBcbCounters<'a> {
436526
traversal: &TraverseCoverageGraphWithLoops,
437527
branches: &[BcbBranch],
438528
) -> BcbBranch {
439-
let branch_needs_a_counter =
440-
|branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none();
529+
let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
441530

442531
let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches);
443532
if let Some(reloop_branch_without_counter) =
@@ -450,10 +539,8 @@ impl<'a> MakeBcbCounters<'a> {
450539
);
451540
reloop_branch_without_counter
452541
} else {
453-
let &branch_without_counter = branches
454-
.iter()
455-
.find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none())
456-
.expect(
542+
let &branch_without_counter =
543+
branches.iter().find(|&branch| self.branch_has_no_counter(branch)).expect(
457544
"needs_branch_counters was `true` so there should be at least one \
458545
branch",
459546
);
@@ -480,8 +567,7 @@ impl<'a> MakeBcbCounters<'a> {
480567
traversal: &TraverseCoverageGraphWithLoops,
481568
branches: &[BcbBranch],
482569
) -> Option<BcbBranch> {
483-
let branch_needs_a_counter =
484-
|branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none();
570+
let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
485571

486572
let mut some_reloop_branch: Option<BcbBranch> = None;
487573
for context in traversal.context_stack.iter().rev() {
@@ -492,7 +578,7 @@ impl<'a> MakeBcbCounters<'a> {
492578
self.bcb_dominates(branch.target_bcb, backedge_from_bcb)
493579
}) {
494580
if let Some(reloop_branch) = some_reloop_branch {
495-
if reloop_branch.counter(&self.basic_coverage_blocks).is_none() {
581+
if self.branch_has_no_counter(&reloop_branch) {
496582
// we already found a candidate reloop_branch that still
497583
// needs a counter
498584
continue;
@@ -558,12 +644,24 @@ impl<'a> MakeBcbCounters<'a> {
558644
}
559645

560646
fn bcb_needs_branch_counters(&self, bcb: BasicCoverageBlock) -> bool {
561-
let branch_needs_a_counter =
562-
|branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none();
647+
let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
563648
let branches = self.bcb_branches(bcb);
564649
branches.len() > 1 && branches.iter().any(branch_needs_a_counter)
565650
}
566651

652+
fn branch_has_no_counter(&self, branch: &BcbBranch) -> bool {
653+
self.branch_counter(branch).is_none()
654+
}
655+
656+
fn branch_counter(&self, branch: &BcbBranch) -> Option<&CoverageKind> {
657+
let to_bcb = branch.target_bcb;
658+
if let Some(from_bcb) = branch.edge_from_bcb {
659+
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
660+
} else {
661+
self.coverage_counters.bcb_counters[to_bcb].as_ref()
662+
}
663+
}
664+
567665
/// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be
568666
/// the entry point for the function.)
569667
#[inline]

compiler/rustc_mir_transform/src/coverage/debug.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
//! recursively, generating labels with nested operations, enclosed in parentheses
109109
//! (for example: `bcb2 + (bcb0 - bcb1)`).
110110
111+
use super::counters::CoverageCounters;
111112
use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
112113
use super::spans::CoverageSpan;
113114

@@ -659,18 +660,21 @@ pub(super) fn dump_coverage_graphviz<'tcx>(
659660
mir_body: &mir::Body<'tcx>,
660661
pass_name: &str,
661662
basic_coverage_blocks: &CoverageGraph,
662-
debug_counters: &DebugCounters,
663+
coverage_counters: &CoverageCounters,
663664
graphviz_data: &GraphvizData,
664665
intermediate_expressions: &[CoverageKind],
665666
debug_used_expressions: &UsedExpressions,
666667
) {
668+
let debug_counters = &coverage_counters.debug_counters;
669+
667670
let mir_source = mir_body.source;
668671
let def_id = mir_source.def_id();
669672
let node_content = |bcb| {
670673
bcb_to_string_sections(
671674
tcx,
672675
mir_body,
673-
debug_counters,
676+
coverage_counters,
677+
bcb,
674678
&basic_coverage_blocks[bcb],
675679
graphviz_data.get_bcb_coverage_spans_with_counters(bcb),
676680
graphviz_data.get_bcb_dependency_counters(bcb),
@@ -736,12 +740,15 @@ pub(super) fn dump_coverage_graphviz<'tcx>(
736740
fn bcb_to_string_sections<'tcx>(
737741
tcx: TyCtxt<'tcx>,
738742
mir_body: &mir::Body<'tcx>,
739-
debug_counters: &DebugCounters,
743+
coverage_counters: &CoverageCounters,
744+
bcb: BasicCoverageBlock,
740745
bcb_data: &BasicCoverageBlockData,
741746
some_coverage_spans_with_counters: Option<&[(CoverageSpan, CoverageKind)]>,
742747
some_dependency_counters: Option<&[CoverageKind]>,
743748
some_intermediate_expressions: Option<&[CoverageKind]>,
744749
) -> Vec<String> {
750+
let debug_counters = &coverage_counters.debug_counters;
751+
745752
let len = bcb_data.basic_blocks.len();
746753
let mut sections = Vec::new();
747754
if let Some(collect_intermediate_expressions) = some_intermediate_expressions {
@@ -777,7 +784,7 @@ fn bcb_to_string_sections<'tcx>(
777784
.join(" \n"),
778785
));
779786
}
780-
if let Some(counter_kind) = &bcb_data.counter_kind {
787+
if let Some(counter_kind) = coverage_counters.bcb_counter(bcb) {
781788
sections.push(format!("{counter_kind:?}"));
782789
}
783790
let non_term_blocks = bcb_data.basic_blocks[0..len - 1]

0 commit comments

Comments
 (0)