1
Fork 0

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.
This commit is contained in:
Zalathar 2023-06-29 16:50:52 +10:00
parent 5302c9d451
commit 5ca30c4646
5 changed files with 157 additions and 158 deletions

View file

@ -8,17 +8,28 @@ use debug::{DebugCounters, NESTED_INDENT};
use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops}; use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops};
use spans::CoverageSpan; use spans::CoverageSpan;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::graph::WithNumNodes;
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
use rustc_index::IndexVec;
use rustc_middle::mir::coverage::*; use rustc_middle::mir::coverage::*;
/// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR /// Generates and stores coverage counter and coverage expression information
/// `Coverage` statements. /// associated with nodes/edges in the BCB graph.
pub(super) struct CoverageCounters { pub(super) struct CoverageCounters {
function_source_hash: u64, function_source_hash: u64,
next_counter_id: CounterId, next_counter_id: CounterId,
next_expression_id: ExpressionId, next_expression_id: ExpressionId,
/// Coverage counters/expressions that are associated with individual BCBs.
bcb_counters: IndexVec<BasicCoverageBlock, Option<CoverageKind>>,
/// Coverage counters/expressions that are associated with the control-flow
/// edge between two BCBs.
bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), CoverageKind>,
/// Tracks which BCBs have a counter associated with some incoming edge.
/// Only used by debug assertions, to verify that BCBs with incoming edge
/// counters do not have their own physical counters (expressions are allowed).
bcb_has_incoming_edge_counters: BitSet<BasicCoverageBlock>,
/// Expression nodes that are not directly associated with any particular /// Expression nodes that are not directly associated with any particular
/// BCB/edge, but are needed as operands to more complex expressions. /// BCB/edge, but are needed as operands to more complex expressions.
/// These are always `CoverageKind::Expression`. /// These are always `CoverageKind::Expression`.
@ -28,12 +39,17 @@ pub(super) struct CoverageCounters {
} }
impl CoverageCounters { impl CoverageCounters {
pub fn new(function_source_hash: u64) -> Self { pub(super) fn new(function_source_hash: u64, basic_coverage_blocks: &CoverageGraph) -> Self {
let num_bcbs = basic_coverage_blocks.num_nodes();
Self { Self {
function_source_hash, function_source_hash,
next_counter_id: CounterId::START, next_counter_id: CounterId::START,
next_expression_id: ExpressionId::START, next_expression_id: ExpressionId::START,
bcb_counters: IndexVec::from_elem_n(None, num_bcbs),
bcb_edge_counters: FxHashMap::default(),
bcb_has_incoming_edge_counters: BitSet::new_empty(num_bcbs),
intermediate_expressions: Vec::new(), intermediate_expressions: Vec::new(),
debug_counters: DebugCounters::new(), debug_counters: DebugCounters::new(),
@ -51,7 +67,7 @@ impl CoverageCounters {
/// representing intermediate values. /// representing intermediate values.
pub fn make_bcb_counters( pub fn make_bcb_counters(
&mut self, &mut self,
basic_coverage_blocks: &mut CoverageGraph, basic_coverage_blocks: &CoverageGraph,
coverage_spans: &[CoverageSpan], coverage_spans: &[CoverageSpan],
) -> Result<(), Error> { ) -> Result<(), Error> {
MakeBcbCounters::new(self, basic_coverage_blocks).make_bcb_counters(coverage_spans) MakeBcbCounters::new(self, basic_coverage_blocks).make_bcb_counters(coverage_spans)
@ -114,6 +130,80 @@ impl CoverageCounters {
self.next_expression_id = next.next_id(); self.next_expression_id = next.next_id();
next next
} }
fn set_bcb_counter(
&mut self,
bcb: BasicCoverageBlock,
counter_kind: CoverageKind,
) -> Result<Operand, Error> {
debug_assert!(
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
// have an expression (to be injected into an existing `BasicBlock` represented by this
// `BasicCoverageBlock`).
counter_kind.is_expression() || !self.bcb_has_incoming_edge_counters.contains(bcb),
"attempt to add a `Counter` to a BCB target with existing incoming edge counters"
);
let operand = counter_kind.as_operand();
if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) {
Error::from_string(format!(
"attempt to set a BasicCoverageBlock coverage counter more than once; \
{bcb:?} already had counter {replaced:?}",
))
} else {
Ok(operand)
}
}
fn set_bcb_edge_counter(
&mut self,
from_bcb: BasicCoverageBlock,
to_bcb: BasicCoverageBlock,
counter_kind: CoverageKind,
) -> Result<Operand, Error> {
if level_enabled!(tracing::Level::DEBUG) {
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
// have an expression (to be injected into an existing `BasicBlock` represented by this
// `BasicCoverageBlock`).
if self.bcb_counter(to_bcb).is_some_and(|c| !c.is_expression()) {
return Error::from_string(format!(
"attempt to add an incoming edge counter from {from_bcb:?} when the target BCB already \
has a `Counter`"
));
}
}
self.bcb_has_incoming_edge_counters.insert(to_bcb);
let operand = counter_kind.as_operand();
if let Some(replaced) = self.bcb_edge_counters.insert((from_bcb, to_bcb), counter_kind) {
Error::from_string(format!(
"attempt to set an edge counter more than once; from_bcb: \
{from_bcb:?} already had counter {replaced:?}",
))
} else {
Ok(operand)
}
}
pub(super) fn bcb_counter(&self, bcb: BasicCoverageBlock) -> Option<&CoverageKind> {
self.bcb_counters[bcb].as_ref()
}
pub(super) fn take_bcb_counter(&mut self, bcb: BasicCoverageBlock) -> Option<CoverageKind> {
self.bcb_counters[bcb].take()
}
pub(super) fn drain_bcb_counters(
&mut self,
) -> impl Iterator<Item = (BasicCoverageBlock, CoverageKind)> + '_ {
self.bcb_counters
.iter_enumerated_mut()
.filter_map(|(bcb, counter)| Some((bcb, counter.take()?)))
}
pub(super) fn drain_bcb_edge_counters(
&mut self,
) -> impl Iterator<Item = ((BasicCoverageBlock, BasicCoverageBlock), CoverageKind)> + '_ {
self.bcb_edge_counters.drain()
}
} }
/// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be /// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be
@ -122,13 +212,13 @@ impl CoverageCounters {
/// embedded counter, an `Expression` should be used. /// embedded counter, an `Expression` should be used.
struct MakeBcbCounters<'a> { struct MakeBcbCounters<'a> {
coverage_counters: &'a mut CoverageCounters, coverage_counters: &'a mut CoverageCounters,
basic_coverage_blocks: &'a mut CoverageGraph, basic_coverage_blocks: &'a CoverageGraph,
} }
impl<'a> MakeBcbCounters<'a> { impl<'a> MakeBcbCounters<'a> {
fn new( fn new(
coverage_counters: &'a mut CoverageCounters, coverage_counters: &'a mut CoverageCounters,
basic_coverage_blocks: &'a mut CoverageGraph, basic_coverage_blocks: &'a CoverageGraph,
) -> Self { ) -> Self {
Self { coverage_counters, basic_coverage_blocks } Self { coverage_counters, basic_coverage_blocks }
} }
@ -202,9 +292,7 @@ impl<'a> MakeBcbCounters<'a> {
branching_bcb, branching_bcb,
branches branches
.iter() .iter()
.map(|branch| { .map(|branch| { format!("{:?}: {:?}", branch, self.branch_counter(branch)) })
format!("{:?}: {:?}", branch, branch.counter(&self.basic_coverage_blocks))
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n "), .join("\n "),
); );
@ -274,9 +362,9 @@ impl<'a> MakeBcbCounters<'a> {
debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression)); debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression));
let bcb = expression_branch.target_bcb; let bcb = expression_branch.target_bcb;
if expression_branch.is_only_path_to_target() { if expression_branch.is_only_path_to_target() {
self.basic_coverage_blocks[bcb].set_counter(expression)?; self.coverage_counters.set_bcb_counter(bcb, expression)?;
} else { } else {
self.basic_coverage_blocks[bcb].set_edge_counter_from(branching_bcb, expression)?; self.coverage_counters.set_bcb_edge_counter(branching_bcb, bcb, expression)?;
} }
Ok(()) Ok(())
} }
@ -291,7 +379,7 @@ impl<'a> MakeBcbCounters<'a> {
debug_indent_level: usize, debug_indent_level: usize,
) -> Result<Operand, Error> { ) -> Result<Operand, Error> {
// If the BCB already has a counter, return it. // If the BCB already has a counter, return it.
if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { if let Some(counter_kind) = &self.coverage_counters.bcb_counters[bcb] {
debug!( debug!(
"{}{:?} already has a counter: {}", "{}{:?} already has a counter: {}",
NESTED_INDENT.repeat(debug_indent_level), NESTED_INDENT.repeat(debug_indent_level),
@ -324,7 +412,7 @@ impl<'a> MakeBcbCounters<'a> {
self.format_counter(&counter_kind), self.format_counter(&counter_kind),
); );
} }
return self.basic_coverage_blocks[bcb].set_counter(counter_kind); return self.coverage_counters.set_bcb_counter(bcb, counter_kind);
} }
// A BCB with multiple incoming edges can compute its count by `Expression`, summing up the // A BCB with multiple incoming edges can compute its count by `Expression`, summing up the
@ -380,7 +468,7 @@ impl<'a> MakeBcbCounters<'a> {
bcb, bcb,
self.format_counter(&counter_kind) self.format_counter(&counter_kind)
); );
self.basic_coverage_blocks[bcb].set_counter(counter_kind) self.coverage_counters.set_bcb_counter(bcb, counter_kind)
} }
fn get_or_make_edge_counter_operand( fn get_or_make_edge_counter_operand(
@ -405,7 +493,9 @@ impl<'a> MakeBcbCounters<'a> {
} }
// If the edge already has a counter, return it. // If the edge already has a counter, return it.
if let Some(counter_kind) = self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) { if let Some(counter_kind) =
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
{
debug!( debug!(
"{}Edge {:?}->{:?} already has a counter: {}", "{}Edge {:?}->{:?} already has a counter: {}",
NESTED_INDENT.repeat(debug_indent_level), NESTED_INDENT.repeat(debug_indent_level),
@ -426,7 +516,7 @@ impl<'a> MakeBcbCounters<'a> {
to_bcb, to_bcb,
self.format_counter(&counter_kind) self.format_counter(&counter_kind)
); );
self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind) self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind)
} }
/// Select a branch for the expression, either the recommended `reloop_branch`, or if none was /// Select a branch for the expression, either the recommended `reloop_branch`, or if none was
@ -436,8 +526,7 @@ impl<'a> MakeBcbCounters<'a> {
traversal: &TraverseCoverageGraphWithLoops, traversal: &TraverseCoverageGraphWithLoops,
branches: &[BcbBranch], branches: &[BcbBranch],
) -> BcbBranch { ) -> BcbBranch {
let branch_needs_a_counter = let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
|branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none();
let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches); let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches);
if let Some(reloop_branch_without_counter) = if let Some(reloop_branch_without_counter) =
@ -450,10 +539,8 @@ impl<'a> MakeBcbCounters<'a> {
); );
reloop_branch_without_counter reloop_branch_without_counter
} else { } else {
let &branch_without_counter = branches let &branch_without_counter =
.iter() branches.iter().find(|&branch| self.branch_has_no_counter(branch)).expect(
.find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none())
.expect(
"needs_branch_counters was `true` so there should be at least one \ "needs_branch_counters was `true` so there should be at least one \
branch", branch",
); );
@ -480,8 +567,7 @@ impl<'a> MakeBcbCounters<'a> {
traversal: &TraverseCoverageGraphWithLoops, traversal: &TraverseCoverageGraphWithLoops,
branches: &[BcbBranch], branches: &[BcbBranch],
) -> Option<BcbBranch> { ) -> Option<BcbBranch> {
let branch_needs_a_counter = let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
|branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none();
let mut some_reloop_branch: Option<BcbBranch> = None; let mut some_reloop_branch: Option<BcbBranch> = None;
for context in traversal.context_stack.iter().rev() { for context in traversal.context_stack.iter().rev() {
@ -492,7 +578,7 @@ impl<'a> MakeBcbCounters<'a> {
self.bcb_dominates(branch.target_bcb, backedge_from_bcb) self.bcb_dominates(branch.target_bcb, backedge_from_bcb)
}) { }) {
if let Some(reloop_branch) = some_reloop_branch { if let Some(reloop_branch) = some_reloop_branch {
if reloop_branch.counter(&self.basic_coverage_blocks).is_none() { if self.branch_has_no_counter(&reloop_branch) {
// we already found a candidate reloop_branch that still // we already found a candidate reloop_branch that still
// needs a counter // needs a counter
continue; continue;
@ -558,12 +644,24 @@ impl<'a> MakeBcbCounters<'a> {
} }
fn bcb_needs_branch_counters(&self, bcb: BasicCoverageBlock) -> bool { fn bcb_needs_branch_counters(&self, bcb: BasicCoverageBlock) -> bool {
let branch_needs_a_counter = let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
|branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none();
let branches = self.bcb_branches(bcb); let branches = self.bcb_branches(bcb);
branches.len() > 1 && branches.iter().any(branch_needs_a_counter) branches.len() > 1 && branches.iter().any(branch_needs_a_counter)
} }
fn branch_has_no_counter(&self, branch: &BcbBranch) -> bool {
self.branch_counter(branch).is_none()
}
fn branch_counter(&self, branch: &BcbBranch) -> Option<&CoverageKind> {
let to_bcb = branch.target_bcb;
if let Some(from_bcb) = branch.edge_from_bcb {
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
} else {
self.coverage_counters.bcb_counters[to_bcb].as_ref()
}
}
/// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be /// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be
/// the entry point for the function.) /// the entry point for the function.)
#[inline] #[inline]

View file

@ -108,6 +108,7 @@
//! recursively, generating labels with nested operations, enclosed in parentheses //! recursively, generating labels with nested operations, enclosed in parentheses
//! (for example: `bcb2 + (bcb0 - bcb1)`). //! (for example: `bcb2 + (bcb0 - bcb1)`).
use super::counters::CoverageCounters;
use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
use super::spans::CoverageSpan; use super::spans::CoverageSpan;
@ -659,18 +660,21 @@ pub(super) fn dump_coverage_graphviz<'tcx>(
mir_body: &mir::Body<'tcx>, mir_body: &mir::Body<'tcx>,
pass_name: &str, pass_name: &str,
basic_coverage_blocks: &CoverageGraph, basic_coverage_blocks: &CoverageGraph,
debug_counters: &DebugCounters, coverage_counters: &CoverageCounters,
graphviz_data: &GraphvizData, graphviz_data: &GraphvizData,
intermediate_expressions: &[CoverageKind], intermediate_expressions: &[CoverageKind],
debug_used_expressions: &UsedExpressions, debug_used_expressions: &UsedExpressions,
) { ) {
let debug_counters = &coverage_counters.debug_counters;
let mir_source = mir_body.source; let mir_source = mir_body.source;
let def_id = mir_source.def_id(); let def_id = mir_source.def_id();
let node_content = |bcb| { let node_content = |bcb| {
bcb_to_string_sections( bcb_to_string_sections(
tcx, tcx,
mir_body, mir_body,
debug_counters, coverage_counters,
bcb,
&basic_coverage_blocks[bcb], &basic_coverage_blocks[bcb],
graphviz_data.get_bcb_coverage_spans_with_counters(bcb), graphviz_data.get_bcb_coverage_spans_with_counters(bcb),
graphviz_data.get_bcb_dependency_counters(bcb), graphviz_data.get_bcb_dependency_counters(bcb),
@ -736,12 +740,15 @@ pub(super) fn dump_coverage_graphviz<'tcx>(
fn bcb_to_string_sections<'tcx>( fn bcb_to_string_sections<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
mir_body: &mir::Body<'tcx>, mir_body: &mir::Body<'tcx>,
debug_counters: &DebugCounters, coverage_counters: &CoverageCounters,
bcb: BasicCoverageBlock,
bcb_data: &BasicCoverageBlockData, bcb_data: &BasicCoverageBlockData,
some_coverage_spans_with_counters: Option<&[(CoverageSpan, CoverageKind)]>, some_coverage_spans_with_counters: Option<&[(CoverageSpan, CoverageKind)]>,
some_dependency_counters: Option<&[CoverageKind]>, some_dependency_counters: Option<&[CoverageKind]>,
some_intermediate_expressions: Option<&[CoverageKind]>, some_intermediate_expressions: Option<&[CoverageKind]>,
) -> Vec<String> { ) -> Vec<String> {
let debug_counters = &coverage_counters.debug_counters;
let len = bcb_data.basic_blocks.len(); let len = bcb_data.basic_blocks.len();
let mut sections = Vec::new(); let mut sections = Vec::new();
if let Some(collect_intermediate_expressions) = some_intermediate_expressions { if let Some(collect_intermediate_expressions) = some_intermediate_expressions {
@ -777,7 +784,7 @@ fn bcb_to_string_sections<'tcx>(
.join(" \n"), .join(" \n"),
)); ));
} }
if let Some(counter_kind) = &bcb_data.counter_kind { if let Some(counter_kind) = coverage_counters.bcb_counter(bcb) {
sections.push(format!("{counter_kind:?}")); sections.push(format!("{counter_kind:?}"));
} }
let non_term_blocks = bcb_data.basic_blocks[0..len - 1] let non_term_blocks = bcb_data.basic_blocks[0..len - 1]

View file

@ -1,12 +1,8 @@
use super::Error;
use itertools::Itertools; use itertools::Itertools;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::graph::dominators::{self, Dominators}; use rustc_data_structures::graph::dominators::{self, Dominators};
use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode};
use rustc_index::bit_set::BitSet; use rustc_index::bit_set::BitSet;
use rustc_index::{IndexSlice, IndexVec}; use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::coverage::*;
use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, TerminatorKind}; use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, TerminatorKind};
use std::cmp::Ordering; use std::cmp::Ordering;
@ -15,10 +11,7 @@ use std::ops::{Index, IndexMut};
const ID_SEPARATOR: &str = ","; const ID_SEPARATOR: &str = ",";
/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s /// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
/// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s, plus a /// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s.
/// `CoverageKind` counter (to be added by `CoverageCounters::make_bcb_counters`), and an optional
/// set of additional counters--if needed--to count incoming edges, if there are more than one.
/// (These "edge counters" are eventually converted into new MIR `BasicBlock`s.)
#[derive(Debug)] #[derive(Debug)]
pub(super) struct CoverageGraph { pub(super) struct CoverageGraph {
bcbs: IndexVec<BasicCoverageBlock, BasicCoverageBlockData>, bcbs: IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
@ -195,13 +188,6 @@ impl CoverageGraph {
self.bcbs.iter_enumerated() self.bcbs.iter_enumerated()
} }
#[inline(always)]
pub fn iter_enumerated_mut(
&mut self,
) -> impl Iterator<Item = (BasicCoverageBlock, &mut BasicCoverageBlockData)> {
self.bcbs.iter_enumerated_mut()
}
#[inline(always)] #[inline(always)]
pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option<BasicCoverageBlock> { pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option<BasicCoverageBlock> {
if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None } if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None }
@ -320,14 +306,12 @@ rustc_index::newtype_index! {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(super) struct BasicCoverageBlockData { pub(super) struct BasicCoverageBlockData {
pub basic_blocks: Vec<BasicBlock>, pub basic_blocks: Vec<BasicBlock>,
pub counter_kind: Option<CoverageKind>,
edge_from_bcbs: Option<FxHashMap<BasicCoverageBlock, CoverageKind>>,
} }
impl BasicCoverageBlockData { impl BasicCoverageBlockData {
pub fn from(basic_blocks: Vec<BasicBlock>) -> Self { pub fn from(basic_blocks: Vec<BasicBlock>) -> Self {
assert!(basic_blocks.len() > 0); assert!(basic_blocks.len() > 0);
Self { basic_blocks, counter_kind: None, edge_from_bcbs: None } Self { basic_blocks }
} }
#[inline(always)] #[inline(always)]
@ -345,80 +329,6 @@ impl BasicCoverageBlockData {
&mir_body[self.last_bb()].terminator() &mir_body[self.last_bb()].terminator()
} }
pub fn set_counter(&mut self, counter_kind: CoverageKind) -> Result<Operand, Error> {
debug_assert!(
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
// have an expression (to be injected into an existing `BasicBlock` represented by this
// `BasicCoverageBlock`).
self.edge_from_bcbs.is_none() || counter_kind.is_expression(),
"attempt to add a `Counter` to a BCB target with existing incoming edge counters"
);
let operand = counter_kind.as_operand();
if let Some(replaced) = self.counter_kind.replace(counter_kind) {
Error::from_string(format!(
"attempt to set a BasicCoverageBlock coverage counter more than once; \
{self:?} already had counter {replaced:?}",
))
} else {
Ok(operand)
}
}
#[inline(always)]
pub fn counter(&self) -> Option<&CoverageKind> {
self.counter_kind.as_ref()
}
#[inline(always)]
pub fn take_counter(&mut self) -> Option<CoverageKind> {
self.counter_kind.take()
}
pub fn set_edge_counter_from(
&mut self,
from_bcb: BasicCoverageBlock,
counter_kind: CoverageKind,
) -> Result<Operand, Error> {
if level_enabled!(tracing::Level::DEBUG) {
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
// have an expression (to be injected into an existing `BasicBlock` represented by this
// `BasicCoverageBlock`).
if self.counter_kind.as_ref().is_some_and(|c| !c.is_expression()) {
return Error::from_string(format!(
"attempt to add an incoming edge counter from {from_bcb:?} when the target BCB already \
has a `Counter`"
));
}
}
let operand = counter_kind.as_operand();
if let Some(replaced) =
self.edge_from_bcbs.get_or_insert_default().insert(from_bcb, counter_kind)
{
Error::from_string(format!(
"attempt to set an edge counter more than once; from_bcb: \
{from_bcb:?} already had counter {replaced:?}",
))
} else {
Ok(operand)
}
}
#[inline]
pub fn edge_counter_from(&self, from_bcb: BasicCoverageBlock) -> Option<&CoverageKind> {
if let Some(edge_from_bcbs) = &self.edge_from_bcbs {
edge_from_bcbs.get(&from_bcb)
} else {
None
}
}
#[inline]
pub fn take_edge_counters(
&mut self,
) -> Option<impl Iterator<Item = (BasicCoverageBlock, CoverageKind)>> {
self.edge_from_bcbs.take().map(|m| m.into_iter())
}
pub fn id(&self) -> String { pub fn id(&self) -> String {
format!("@{}", self.basic_blocks.iter().map(|bb| bb.index().to_string()).join(ID_SEPARATOR)) format!("@{}", self.basic_blocks.iter().map(|bb| bb.index().to_string()).join(ID_SEPARATOR))
} }
@ -448,17 +358,6 @@ impl BcbBranch {
Self { edge_from_bcb, target_bcb: to_bcb } Self { edge_from_bcb, target_bcb: to_bcb }
} }
pub fn counter<'a>(
&self,
basic_coverage_blocks: &'a CoverageGraph,
) -> Option<&'a CoverageKind> {
if let Some(from_bcb) = self.edge_from_bcb {
basic_coverage_blocks[self.target_bcb].edge_counter_from(from_bcb)
} else {
basic_coverage_blocks[self.target_bcb].counter()
}
}
pub fn is_only_path_to_target(&self) -> bool { pub fn is_only_path_to_target(&self) -> bool {
self.edge_from_bcb.is_none() self.edge_from_bcb.is_none()
} }

View file

@ -137,6 +137,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
let function_source_hash = hash_mir_source(tcx, hir_body); let function_source_hash = hash_mir_source(tcx, hir_body);
let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); let basic_coverage_blocks = CoverageGraph::from_mir(mir_body);
let coverage_counters = CoverageCounters::new(function_source_hash, &basic_coverage_blocks);
Self { Self {
pass_name, pass_name,
tcx, tcx,
@ -145,7 +147,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
fn_sig_span, fn_sig_span,
body_span, body_span,
basic_coverage_blocks, basic_coverage_blocks,
coverage_counters: CoverageCounters::new(function_source_hash), coverage_counters,
} }
} }
@ -250,7 +252,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
self.mir_body, self.mir_body,
self.pass_name, self.pass_name,
&self.basic_coverage_blocks, &self.basic_coverage_blocks,
&self.coverage_counters.debug_counters, &self.coverage_counters,
&graphviz_data, &graphviz_data,
&self.coverage_counters.intermediate_expressions, &self.coverage_counters.intermediate_expressions,
&debug_used_expressions, &debug_used_expressions,
@ -298,7 +300,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
let span = covspan.span; let span = covspan.span;
let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() { let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() {
self.coverage_counters.make_identity_counter(counter_operand) self.coverage_counters.make_identity_counter(counter_operand)
} else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() { } else if let Some(counter_kind) = self.coverage_counters.take_bcb_counter(bcb) {
bcb_counters[bcb] = Some(counter_kind.as_operand()); bcb_counters[bcb] = Some(counter_kind.as_operand());
debug_used_expressions.add_expression_operands(&counter_kind); debug_used_expressions.add_expression_operands(&counter_kind);
counter_kind counter_kind
@ -338,19 +340,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
debug_used_expressions: &mut debug::UsedExpressions, debug_used_expressions: &mut debug::UsedExpressions,
) { ) {
let mut bcb_counters_without_direct_coverage_spans = Vec::new(); let mut bcb_counters_without_direct_coverage_spans = Vec::new();
for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { for (target_bcb, counter_kind) in self.coverage_counters.drain_bcb_counters() {
if let Some(counter_kind) = target_bcb_data.take_counter() { bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind));
bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind)); }
} for ((from_bcb, target_bcb), counter_kind) in
if let Some(edge_counters) = target_bcb_data.take_edge_counters() { self.coverage_counters.drain_bcb_edge_counters()
for (from_bcb, counter_kind) in edge_counters { {
bcb_counters_without_direct_coverage_spans.push(( bcb_counters_without_direct_coverage_spans.push((
Some(from_bcb), Some(from_bcb),
target_bcb, target_bcb,
counter_kind, counter_kind,
)); ));
}
}
} }
// If debug is enabled, validate that every BCB or edge counter not directly associated // If debug is enabled, validate that every BCB or edge counter not directly associated
@ -425,11 +425,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
&self.basic_coverage_blocks[bcb] &self.basic_coverage_blocks[bcb]
} }
#[inline]
fn bcb_data_mut(&mut self, bcb: BasicCoverageBlock) -> &mut BasicCoverageBlockData {
&mut self.basic_coverage_blocks[bcb]
}
#[inline] #[inline]
fn format_counter(&self, counter_kind: &CoverageKind) -> String { fn format_counter(&self, counter_kind: &CoverageKind) -> String {
self.coverage_counters.debug_counters.format_counter(counter_kind) self.coverage_counters.debug_counters.format_counter(counter_kind)

View file

@ -675,7 +675,7 @@ fn test_make_bcb_counters() {
)); ));
} }
} }
let mut coverage_counters = counters::CoverageCounters::new(0); let mut coverage_counters = counters::CoverageCounters::new(0, &basic_coverage_blocks);
let () = coverage_counters let () = coverage_counters
.make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans) .make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans)
.expect("should be Ok"); .expect("should be Ok");
@ -684,7 +684,7 @@ fn test_make_bcb_counters() {
let_bcb!(1); let_bcb!(1);
assert_eq!( assert_eq!(
0, // bcb1 has a `Counter` with id = 0 0, // bcb1 has a `Counter` with id = 0
match basic_coverage_blocks[bcb1].counter().expect("should have a counter") { match coverage_counters.bcb_counter(bcb1).expect("should have a counter") {
CoverageKind::Counter { id, .. } => id, CoverageKind::Counter { id, .. } => id,
_ => panic!("expected a Counter"), _ => panic!("expected a Counter"),
} }
@ -694,7 +694,7 @@ fn test_make_bcb_counters() {
let_bcb!(2); let_bcb!(2);
assert_eq!( assert_eq!(
1, // bcb2 has a `Counter` with id = 1 1, // bcb2 has a `Counter` with id = 1
match basic_coverage_blocks[bcb2].counter().expect("should have a counter") { match coverage_counters.bcb_counter(bcb2).expect("should have a counter") {
CoverageKind::Counter { id, .. } => id, CoverageKind::Counter { id, .. } => id,
_ => panic!("expected a Counter"), _ => panic!("expected a Counter"),
} }