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:
parent
5302c9d451
commit
5ca30c4646
5 changed files with 157 additions and 158 deletions
|
@ -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]
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue