2020-10-22 21:30:03 +00:00
|
|
|
use super::Error;
|
|
|
|
|
2020-10-23 03:28:16 +00:00
|
|
|
use super::debug;
|
2020-10-22 21:30:03 +00:00
|
|
|
use super::graph;
|
|
|
|
use super::spans;
|
2020-10-23 03:28:16 +00:00
|
|
|
|
2020-10-22 21:30:03 +00:00
|
|
|
use debug::{DebugCounters, NESTED_INDENT};
|
|
|
|
use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops};
|
|
|
|
use spans::CoverageSpan;
|
2020-10-23 03:28:16 +00:00
|
|
|
|
2020-10-22 21:30:03 +00:00
|
|
|
use rustc_data_structures::graph::WithNumNodes;
|
|
|
|
use rustc_index::bit_set::BitSet;
|
2020-10-23 07:45:07 +00:00
|
|
|
use rustc_middle::mir::coverage::*;
|
|
|
|
|
|
|
|
/// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR
|
|
|
|
/// `Coverage` statements.
|
2020-11-03 05:32:48 +00:00
|
|
|
pub(super) struct CoverageCounters {
|
2020-10-23 07:45:07 +00:00
|
|
|
function_source_hash: u64,
|
|
|
|
next_counter_id: u32,
|
|
|
|
num_expressions: u32,
|
2020-10-23 03:28:16 +00:00
|
|
|
pub debug_counters: DebugCounters,
|
2020-10-23 07:45:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CoverageCounters {
|
|
|
|
pub fn new(function_source_hash: u64) -> Self {
|
|
|
|
Self {
|
|
|
|
function_source_hash,
|
|
|
|
next_counter_id: CounterValueReference::START.as_u32(),
|
|
|
|
num_expressions: 0,
|
2020-10-23 03:28:16 +00:00
|
|
|
debug_counters: DebugCounters::new(),
|
2020-10-23 07:45:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-23 03:28:16 +00:00
|
|
|
/// Activate the `DebugCounters` data structures, to provide additional debug formatting
|
2021-01-19 14:42:18 +00:00
|
|
|
/// features when formatting `CoverageKind` (counter) values.
|
2020-10-23 03:28:16 +00:00
|
|
|
pub fn enable_debug(&mut self) {
|
|
|
|
self.debug_counters.enable();
|
|
|
|
}
|
|
|
|
|
2020-11-03 05:32:48 +00:00
|
|
|
/// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or
|
2020-10-22 21:30:03 +00:00
|
|
|
/// indirectly associated with `CoverageSpans`, and returns additional `Expression`s
|
|
|
|
/// representing intermediate values.
|
|
|
|
pub fn make_bcb_counters(
|
|
|
|
&mut self,
|
|
|
|
basic_coverage_blocks: &mut CoverageGraph,
|
2022-06-03 16:42:42 +00:00
|
|
|
coverage_spans: &[CoverageSpan],
|
2020-10-22 21:30:03 +00:00
|
|
|
) -> Result<Vec<CoverageKind>, Error> {
|
|
|
|
let mut bcb_counters = BcbCounters::new(self, basic_coverage_blocks);
|
|
|
|
bcb_counters.make_bcb_counters(coverage_spans)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_counter<F>(&mut self, debug_block_label_fn: F) -> CoverageKind
|
2020-10-23 03:28:16 +00:00
|
|
|
where
|
|
|
|
F: Fn() -> Option<String>,
|
|
|
|
{
|
|
|
|
let counter = CoverageKind::Counter {
|
2020-10-23 07:45:07 +00:00
|
|
|
function_source_hash: self.function_source_hash,
|
|
|
|
id: self.next_counter(),
|
2020-10-23 03:28:16 +00:00
|
|
|
};
|
|
|
|
if self.debug_counters.is_enabled() {
|
|
|
|
self.debug_counters.add_counter(&counter, (debug_block_label_fn)());
|
2020-10-23 07:45:07 +00:00
|
|
|
}
|
2020-10-23 03:28:16 +00:00
|
|
|
counter
|
2020-10-23 07:45:07 +00:00
|
|
|
}
|
|
|
|
|
2020-10-22 21:30:03 +00:00
|
|
|
fn make_expression<F>(
|
2020-10-23 07:45:07 +00:00
|
|
|
&mut self,
|
|
|
|
lhs: ExpressionOperandId,
|
|
|
|
op: Op,
|
|
|
|
rhs: ExpressionOperandId,
|
2020-10-23 03:28:16 +00:00
|
|
|
debug_block_label_fn: F,
|
|
|
|
) -> CoverageKind
|
|
|
|
where
|
|
|
|
F: Fn() -> Option<String>,
|
|
|
|
{
|
2020-10-23 07:45:07 +00:00
|
|
|
let id = self.next_expression();
|
2020-10-23 03:28:16 +00:00
|
|
|
let expression = CoverageKind::Expression { id, lhs, op, rhs };
|
|
|
|
if self.debug_counters.is_enabled() {
|
|
|
|
self.debug_counters.add_counter(&expression, (debug_block_label_fn)());
|
|
|
|
}
|
|
|
|
expression
|
2020-10-23 07:45:07 +00:00
|
|
|
}
|
|
|
|
|
2020-10-22 21:30:03 +00:00
|
|
|
pub fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind {
|
|
|
|
let some_debug_block_label = if self.debug_counters.is_enabled() {
|
|
|
|
self.debug_counters.some_block_label(counter_operand).cloned()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || {
|
|
|
|
some_debug_block_label.clone()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-10-23 07:45:07 +00:00
|
|
|
/// Counter IDs start from one and go up.
|
|
|
|
fn next_counter(&mut self) -> CounterValueReference {
|
|
|
|
assert!(self.next_counter_id < u32::MAX - self.num_expressions);
|
|
|
|
let next = self.next_counter_id;
|
|
|
|
self.next_counter_id += 1;
|
|
|
|
CounterValueReference::from(next)
|
|
|
|
}
|
|
|
|
|
2021-08-22 12:46:15 +00:00
|
|
|
/// Expression IDs start from u32::MAX and go down because an Expression can reference
|
2020-10-23 07:45:07 +00:00
|
|
|
/// (add or subtract counts) of both Counter regions and Expression regions. The counter
|
|
|
|
/// expression operand IDs must be unique across both types.
|
|
|
|
fn next_expression(&mut self) -> InjectedExpressionId {
|
|
|
|
assert!(self.next_counter_id < u32::MAX - self.num_expressions);
|
|
|
|
let next = u32::MAX - self.num_expressions;
|
|
|
|
self.num_expressions += 1;
|
|
|
|
InjectedExpressionId::from(next)
|
|
|
|
}
|
|
|
|
}
|
2020-10-22 21:30:03 +00:00
|
|
|
|
|
|
|
/// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be
|
|
|
|
/// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable expression
|
|
|
|
/// (adding or subtracting two other counters or expressions) can compute the same result as an
|
|
|
|
/// embedded counter, an `Expression` should be used.
|
|
|
|
struct BcbCounters<'a> {
|
|
|
|
coverage_counters: &'a mut CoverageCounters,
|
|
|
|
basic_coverage_blocks: &'a mut CoverageGraph,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> BcbCounters<'a> {
|
|
|
|
fn new(
|
|
|
|
coverage_counters: &'a mut CoverageCounters,
|
|
|
|
basic_coverage_blocks: &'a mut CoverageGraph,
|
|
|
|
) -> Self {
|
|
|
|
Self { coverage_counters, basic_coverage_blocks }
|
|
|
|
}
|
|
|
|
|
2020-10-30 23:09:05 +00:00
|
|
|
/// If two `BasicCoverageBlock`s branch from another `BasicCoverageBlock`, one of the branches
|
2020-10-22 21:30:03 +00:00
|
|
|
/// can be counted by `Expression` by subtracting the other branch from the branching
|
|
|
|
/// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`.
|
|
|
|
/// One way to predict which branch executes the least is by considering loops. A loop is exited
|
|
|
|
/// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost
|
|
|
|
/// always executed less than the branch that does not exit the loop.
|
|
|
|
///
|
|
|
|
/// Returns any non-code-span expressions created to represent intermediate values (such as to
|
|
|
|
/// add two counters so the result can be subtracted from another counter), or an Error with
|
|
|
|
/// message for subsequent debugging.
|
|
|
|
fn make_bcb_counters(
|
|
|
|
&mut self,
|
2020-12-30 11:59:07 +00:00
|
|
|
coverage_spans: &[CoverageSpan],
|
2020-10-22 21:30:03 +00:00
|
|
|
) -> Result<Vec<CoverageKind>, Error> {
|
|
|
|
debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock");
|
|
|
|
let num_bcbs = self.basic_coverage_blocks.num_nodes();
|
|
|
|
let mut collect_intermediate_expressions = Vec::with_capacity(num_bcbs);
|
|
|
|
|
|
|
|
let mut bcbs_with_coverage = BitSet::new_empty(num_bcbs);
|
|
|
|
for covspan in coverage_spans {
|
|
|
|
bcbs_with_coverage.insert(covspan.bcb);
|
|
|
|
}
|
|
|
|
|
2020-10-30 23:09:05 +00:00
|
|
|
// Walk the `CoverageGraph`. For each `BasicCoverageBlock` node with an associated
|
|
|
|
// `CoverageSpan`, add a counter. If the `BasicCoverageBlock` branches, add a counter or
|
|
|
|
// expression to each branch `BasicCoverageBlock` (if the branch BCB has only one incoming
|
|
|
|
// edge) or edge from the branching BCB to the branch BCB (if the branch BCB has multiple
|
|
|
|
// incoming edges).
|
|
|
|
//
|
|
|
|
// The `TraverseCoverageGraphWithLoops` traversal ensures that, when a loop is encountered,
|
|
|
|
// all `BasicCoverageBlock` nodes in the loop are visited before visiting any node outside
|
|
|
|
// the loop. The `traversal` state includes a `context_stack`, providing a way to know if
|
|
|
|
// the current BCB is in one or more nested loops or not.
|
2020-10-22 21:30:03 +00:00
|
|
|
let mut traversal = TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks);
|
|
|
|
while let Some(bcb) = traversal.next(self.basic_coverage_blocks) {
|
|
|
|
if bcbs_with_coverage.contains(bcb) {
|
|
|
|
debug!("{:?} has at least one `CoverageSpan`. Get or make its counter", bcb);
|
|
|
|
let branching_counter_operand =
|
|
|
|
self.get_or_make_counter_operand(bcb, &mut collect_intermediate_expressions)?;
|
|
|
|
|
|
|
|
if self.bcb_needs_branch_counters(bcb) {
|
|
|
|
self.make_branch_counters(
|
|
|
|
&mut traversal,
|
|
|
|
bcb,
|
|
|
|
branching_counter_operand,
|
|
|
|
&mut collect_intermediate_expressions,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
debug!(
|
|
|
|
"{:?} does not have any `CoverageSpan`s. A counter will only be added if \
|
|
|
|
and when a covered BCB has an expression dependency.",
|
|
|
|
bcb,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if traversal.is_complete() {
|
|
|
|
Ok(collect_intermediate_expressions)
|
|
|
|
} else {
|
|
|
|
Error::from_string(format!(
|
|
|
|
"`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}",
|
|
|
|
traversal.unvisited(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_branch_counters(
|
|
|
|
&mut self,
|
|
|
|
traversal: &mut TraverseCoverageGraphWithLoops,
|
|
|
|
branching_bcb: BasicCoverageBlock,
|
|
|
|
branching_counter_operand: ExpressionOperandId,
|
|
|
|
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let branches = self.bcb_branches(branching_bcb);
|
|
|
|
debug!(
|
|
|
|
"{:?} has some branch(es) without counters:\n {}",
|
|
|
|
branching_bcb,
|
|
|
|
branches
|
|
|
|
.iter()
|
|
|
|
.map(|branch| {
|
|
|
|
format!("{:?}: {:?}", branch, branch.counter(&self.basic_coverage_blocks))
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join("\n "),
|
|
|
|
);
|
|
|
|
|
2020-10-30 23:09:05 +00:00
|
|
|
// Use the `traversal` state to decide if a subset of the branches exit a loop, making it
|
|
|
|
// likely that branch is executed less than branches that do not exit the same loop. In this
|
|
|
|
// case, any branch that does not exit the loop (and has not already been assigned a
|
|
|
|
// counter) should be counted by expression, if possible. (If a preferred expression branch
|
|
|
|
// is not selected based on the loop context, select any branch without an existing
|
|
|
|
// counter.)
|
2020-10-22 21:30:03 +00:00
|
|
|
let expression_branch = self.choose_preferred_expression_branch(traversal, &branches);
|
2020-10-30 23:09:05 +00:00
|
|
|
|
|
|
|
// Assign a Counter or Expression to each branch, plus additional `Expression`s, as needed,
|
|
|
|
// to sum up intermediate results.
|
2020-10-22 21:30:03 +00:00
|
|
|
let mut some_sumup_counter_operand = None;
|
|
|
|
for branch in branches {
|
2020-10-30 23:09:05 +00:00
|
|
|
// Skip the selected `expression_branch`, if any. It's expression will be assigned after
|
|
|
|
// all others.
|
2020-10-22 21:30:03 +00:00
|
|
|
if branch != expression_branch {
|
|
|
|
let branch_counter_operand = if branch.is_only_path_to_target() {
|
|
|
|
debug!(
|
|
|
|
" {:?} has only one incoming edge (from {:?}), so adding a \
|
|
|
|
counter",
|
|
|
|
branch, branching_bcb
|
|
|
|
);
|
|
|
|
self.get_or_make_counter_operand(
|
|
|
|
branch.target_bcb,
|
|
|
|
collect_intermediate_expressions,
|
|
|
|
)?
|
|
|
|
} else {
|
|
|
|
debug!(" {:?} has multiple incoming edges, so adding an edge counter", branch);
|
|
|
|
self.get_or_make_edge_counter_operand(
|
|
|
|
branching_bcb,
|
|
|
|
branch.target_bcb,
|
|
|
|
collect_intermediate_expressions,
|
|
|
|
)?
|
|
|
|
};
|
|
|
|
if let Some(sumup_counter_operand) =
|
|
|
|
some_sumup_counter_operand.replace(branch_counter_operand)
|
|
|
|
{
|
|
|
|
let intermediate_expression = self.coverage_counters.make_expression(
|
|
|
|
branch_counter_operand,
|
|
|
|
Op::Add,
|
|
|
|
sumup_counter_operand,
|
|
|
|
|| None,
|
|
|
|
);
|
|
|
|
debug!(
|
|
|
|
" [new intermediate expression: {}]",
|
|
|
|
self.format_counter(&intermediate_expression)
|
|
|
|
);
|
|
|
|
let intermediate_expression_operand = intermediate_expression.as_operand_id();
|
|
|
|
collect_intermediate_expressions.push(intermediate_expression);
|
|
|
|
some_sumup_counter_operand.replace(intermediate_expression_operand);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-30 23:09:05 +00:00
|
|
|
|
|
|
|
// Assign the final expression to the `expression_branch` by subtracting the total of all
|
|
|
|
// other branches from the counter of the branching BCB.
|
2020-10-22 21:30:03 +00:00
|
|
|
let sumup_counter_operand =
|
|
|
|
some_sumup_counter_operand.expect("sumup_counter_operand should have a value");
|
|
|
|
debug!(
|
|
|
|
"Making an expression for the selected expression_branch: {:?} \
|
|
|
|
(expression_branch predecessors: {:?})",
|
|
|
|
expression_branch,
|
|
|
|
self.bcb_predecessors(expression_branch.target_bcb),
|
|
|
|
);
|
|
|
|
let expression = self.coverage_counters.make_expression(
|
|
|
|
branching_counter_operand,
|
|
|
|
Op::Subtract,
|
|
|
|
sumup_counter_operand,
|
|
|
|
|| Some(format!("{:?}", expression_branch)),
|
|
|
|
);
|
|
|
|
debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression));
|
|
|
|
let bcb = expression_branch.target_bcb;
|
|
|
|
if expression_branch.is_only_path_to_target() {
|
|
|
|
self.basic_coverage_blocks[bcb].set_counter(expression)?;
|
|
|
|
} else {
|
|
|
|
self.basic_coverage_blocks[bcb].set_edge_counter_from(branching_bcb, expression)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_or_make_counter_operand(
|
|
|
|
&mut self,
|
|
|
|
bcb: BasicCoverageBlock,
|
|
|
|
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
|
|
|
) -> Result<ExpressionOperandId, Error> {
|
|
|
|
self.recursive_get_or_make_counter_operand(bcb, collect_intermediate_expressions, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn recursive_get_or_make_counter_operand(
|
|
|
|
&mut self,
|
|
|
|
bcb: BasicCoverageBlock,
|
|
|
|
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
|
|
|
debug_indent_level: usize,
|
|
|
|
) -> Result<ExpressionOperandId, Error> {
|
2020-10-30 23:09:05 +00:00
|
|
|
// If the BCB already has a counter, return it.
|
|
|
|
if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() {
|
|
|
|
debug!(
|
|
|
|
"{}{:?} already has a counter: {}",
|
|
|
|
NESTED_INDENT.repeat(debug_indent_level),
|
|
|
|
bcb,
|
|
|
|
self.format_counter(counter_kind),
|
|
|
|
);
|
|
|
|
return Ok(counter_kind.as_operand_id());
|
|
|
|
}
|
|
|
|
|
|
|
|
// A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`).
|
|
|
|
// Also, a BCB that loops back to itself gets a simple `Counter`. This may indicate the
|
|
|
|
// program results in a tight infinite loop, but it should still compile.
|
|
|
|
let one_path_to_target = self.bcb_has_one_path_to_target(bcb);
|
|
|
|
if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) {
|
|
|
|
let counter_kind = self.coverage_counters.make_counter(|| Some(format!("{:?}", bcb)));
|
|
|
|
if one_path_to_target {
|
2020-10-22 21:30:03 +00:00
|
|
|
debug!(
|
2020-10-30 23:09:05 +00:00
|
|
|
"{}{:?} gets a new counter: {}",
|
2020-10-22 21:30:03 +00:00
|
|
|
NESTED_INDENT.repeat(debug_indent_level),
|
|
|
|
bcb,
|
2020-10-30 23:09:05 +00:00
|
|
|
self.format_counter(&counter_kind),
|
2020-10-22 21:30:03 +00:00
|
|
|
);
|
|
|
|
} else {
|
2020-10-30 23:09:05 +00:00
|
|
|
debug!(
|
|
|
|
"{}{:?} has itself as its own predecessor. It can't be part of its own \
|
|
|
|
Expression sum, so it will get its own new counter: {}. (Note, the compiled \
|
|
|
|
code will generate an infinite loop.)",
|
|
|
|
NESTED_INDENT.repeat(debug_indent_level),
|
|
|
|
bcb,
|
|
|
|
self.format_counter(&counter_kind),
|
|
|
|
);
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
2020-10-30 23:09:05 +00:00
|
|
|
return self.basic_coverage_blocks[bcb].set_counter(counter_kind);
|
|
|
|
}
|
|
|
|
|
|
|
|
// A BCB with multiple incoming edges can compute its count by `Expression`, summing up the
|
|
|
|
// counters and/or expressions of its incoming edges. This will recursively get or create
|
|
|
|
// counters for those incoming edges first, then call `make_expression()` to sum them up,
|
|
|
|
// with additional intermediate expressions as needed.
|
2022-06-03 16:42:42 +00:00
|
|
|
let mut predecessors = self.bcb_predecessors(bcb).to_owned().into_iter();
|
2020-10-30 23:09:05 +00:00
|
|
|
debug!(
|
|
|
|
"{}{:?} has multiple incoming edges and will get an expression that sums them up...",
|
|
|
|
NESTED_INDENT.repeat(debug_indent_level),
|
|
|
|
bcb,
|
|
|
|
);
|
|
|
|
let first_edge_counter_operand = self.recursive_get_or_make_edge_counter_operand(
|
|
|
|
predecessors.next().unwrap(),
|
|
|
|
bcb,
|
|
|
|
collect_intermediate_expressions,
|
|
|
|
debug_indent_level + 1,
|
|
|
|
)?;
|
|
|
|
let mut some_sumup_edge_counter_operand = None;
|
|
|
|
for predecessor in predecessors {
|
|
|
|
let edge_counter_operand = self.recursive_get_or_make_edge_counter_operand(
|
|
|
|
predecessor,
|
|
|
|
bcb,
|
|
|
|
collect_intermediate_expressions,
|
|
|
|
debug_indent_level + 1,
|
|
|
|
)?;
|
|
|
|
if let Some(sumup_edge_counter_operand) =
|
|
|
|
some_sumup_edge_counter_operand.replace(edge_counter_operand)
|
|
|
|
{
|
|
|
|
let intermediate_expression = self.coverage_counters.make_expression(
|
|
|
|
sumup_edge_counter_operand,
|
|
|
|
Op::Add,
|
|
|
|
edge_counter_operand,
|
|
|
|
|| None,
|
|
|
|
);
|
|
|
|
debug!(
|
|
|
|
"{}new intermediate expression: {}",
|
|
|
|
NESTED_INDENT.repeat(debug_indent_level),
|
|
|
|
self.format_counter(&intermediate_expression)
|
|
|
|
);
|
|
|
|
let intermediate_expression_operand = intermediate_expression.as_operand_id();
|
|
|
|
collect_intermediate_expressions.push(intermediate_expression);
|
|
|
|
some_sumup_edge_counter_operand.replace(intermediate_expression_operand);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let counter_kind = self.coverage_counters.make_expression(
|
|
|
|
first_edge_counter_operand,
|
|
|
|
Op::Add,
|
|
|
|
some_sumup_edge_counter_operand.unwrap(),
|
|
|
|
|| Some(format!("{:?}", bcb)),
|
|
|
|
);
|
|
|
|
debug!(
|
|
|
|
"{}{:?} gets a new counter (sum of predecessor counters): {}",
|
|
|
|
NESTED_INDENT.repeat(debug_indent_level),
|
|
|
|
bcb,
|
|
|
|
self.format_counter(&counter_kind)
|
|
|
|
);
|
|
|
|
self.basic_coverage_blocks[bcb].set_counter(counter_kind)
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_or_make_edge_counter_operand(
|
|
|
|
&mut self,
|
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
to_bcb: BasicCoverageBlock,
|
|
|
|
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
|
|
|
) -> Result<ExpressionOperandId, Error> {
|
|
|
|
self.recursive_get_or_make_edge_counter_operand(
|
|
|
|
from_bcb,
|
|
|
|
to_bcb,
|
|
|
|
collect_intermediate_expressions,
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn recursive_get_or_make_edge_counter_operand(
|
|
|
|
&mut self,
|
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
to_bcb: BasicCoverageBlock,
|
|
|
|
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
|
|
|
debug_indent_level: usize,
|
|
|
|
) -> Result<ExpressionOperandId, Error> {
|
2020-10-30 23:09:05 +00:00
|
|
|
// If the source BCB has only one successor (assumed to be the given target), an edge
|
|
|
|
// counter is unnecessary. Just get or make a counter for the source BCB.
|
|
|
|
let successors = self.bcb_successors(from_bcb).iter();
|
|
|
|
if successors.len() == 1 {
|
|
|
|
return self.recursive_get_or_make_counter_operand(
|
|
|
|
from_bcb,
|
|
|
|
collect_intermediate_expressions,
|
|
|
|
debug_indent_level + 1,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
debug!(
|
|
|
|
"{}Edge {:?}->{:?} already has a counter: {}",
|
|
|
|
NESTED_INDENT.repeat(debug_indent_level),
|
|
|
|
from_bcb,
|
|
|
|
to_bcb,
|
|
|
|
self.format_counter(counter_kind)
|
|
|
|
);
|
|
|
|
return Ok(counter_kind.as_operand_id());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a new counter to count this edge.
|
|
|
|
let counter_kind =
|
|
|
|
self.coverage_counters.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb)));
|
|
|
|
debug!(
|
|
|
|
"{}Edge {:?}->{:?} gets a new counter: {}",
|
|
|
|
NESTED_INDENT.repeat(debug_indent_level),
|
|
|
|
from_bcb,
|
|
|
|
to_bcb,
|
|
|
|
self.format_counter(&counter_kind)
|
|
|
|
);
|
|
|
|
self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind)
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2020-10-30 23:09:05 +00:00
|
|
|
/// Select a branch for the expression, either the recommended `reloop_branch`, or if none was
|
|
|
|
/// found, select any branch.
|
2020-10-22 21:30:03 +00:00
|
|
|
fn choose_preferred_expression_branch(
|
|
|
|
&self,
|
|
|
|
traversal: &TraverseCoverageGraphWithLoops,
|
2020-12-30 11:59:07 +00:00
|
|
|
branches: &[BcbBranch],
|
2020-10-22 21:30:03 +00:00
|
|
|
) -> BcbBranch {
|
|
|
|
let branch_needs_a_counter =
|
|
|
|
|branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none();
|
|
|
|
|
|
|
|
let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches);
|
|
|
|
if let Some(reloop_branch_without_counter) =
|
|
|
|
some_reloop_branch.filter(branch_needs_a_counter)
|
|
|
|
{
|
|
|
|
debug!(
|
|
|
|
"Selecting reloop_branch={:?} that still needs a counter, to get the \
|
|
|
|
`Expression`",
|
|
|
|
reloop_branch_without_counter
|
|
|
|
);
|
|
|
|
reloop_branch_without_counter
|
|
|
|
} else {
|
|
|
|
let &branch_without_counter = branches
|
|
|
|
.iter()
|
|
|
|
.find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none())
|
|
|
|
.expect(
|
|
|
|
"needs_branch_counters was `true` so there should be at least one \
|
|
|
|
branch",
|
|
|
|
);
|
|
|
|
debug!(
|
|
|
|
"Selecting any branch={:?} that still needs a counter, to get the \
|
|
|
|
`Expression` because there was no `reloop_branch`, or it already had a \
|
|
|
|
counter",
|
|
|
|
branch_without_counter
|
|
|
|
);
|
|
|
|
branch_without_counter
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-30 23:09:05 +00:00
|
|
|
/// At most, one of the branches (or its edge, from the branching_bcb, if the branch has
|
|
|
|
/// multiple incoming edges) can have a counter computed by expression.
|
2020-10-22 21:30:03 +00:00
|
|
|
///
|
|
|
|
/// If at least one of the branches leads outside of a loop (`found_loop_exit` is
|
|
|
|
/// true), and at least one other branch does not exit the loop (the first of which
|
|
|
|
/// is captured in `some_reloop_branch`), it's likely any reloop branch will be
|
|
|
|
/// executed far more often than loop exit branch, making the reloop branch a better
|
|
|
|
/// candidate for an expression.
|
|
|
|
fn find_some_reloop_branch(
|
|
|
|
&self,
|
|
|
|
traversal: &TraverseCoverageGraphWithLoops,
|
2020-12-30 11:59:07 +00:00
|
|
|
branches: &[BcbBranch],
|
2020-10-22 21:30:03 +00:00
|
|
|
) -> Option<BcbBranch> {
|
|
|
|
let branch_needs_a_counter =
|
|
|
|
|branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none();
|
|
|
|
|
|
|
|
let mut some_reloop_branch: Option<BcbBranch> = None;
|
|
|
|
for context in traversal.context_stack.iter().rev() {
|
|
|
|
if let Some((backedge_from_bcbs, _)) = &context.loop_backedges {
|
|
|
|
let mut found_loop_exit = false;
|
|
|
|
for &branch in branches.iter() {
|
|
|
|
if backedge_from_bcbs.iter().any(|&backedge_from_bcb| {
|
2023-01-20 00:00:00 +00:00
|
|
|
self.bcb_dominates(branch.target_bcb, backedge_from_bcb)
|
2020-10-22 21:30:03 +00:00
|
|
|
}) {
|
|
|
|
if let Some(reloop_branch) = some_reloop_branch {
|
|
|
|
if reloop_branch.counter(&self.basic_coverage_blocks).is_none() {
|
|
|
|
// we already found a candidate reloop_branch that still
|
|
|
|
// needs a counter
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// The path from branch leads back to the top of the loop. Set this
|
|
|
|
// branch as the `reloop_branch`. If this branch already has a
|
|
|
|
// counter, and we find another reloop branch that doesn't have a
|
|
|
|
// counter yet, that branch will be selected as the `reloop_branch`
|
|
|
|
// instead.
|
|
|
|
some_reloop_branch = Some(branch);
|
|
|
|
} else {
|
|
|
|
// The path from branch leads outside this loop
|
|
|
|
found_loop_exit = true;
|
|
|
|
}
|
|
|
|
if found_loop_exit
|
|
|
|
&& some_reloop_branch.filter(branch_needs_a_counter).is_some()
|
|
|
|
{
|
|
|
|
// Found both a branch that exits the loop and a branch that returns
|
|
|
|
// to the top of the loop (`reloop_branch`), and the `reloop_branch`
|
|
|
|
// doesn't already have a counter.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found_loop_exit {
|
|
|
|
debug!(
|
|
|
|
"No branches exit the loop, so any branch without an existing \
|
|
|
|
counter can have the `Expression`."
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if some_reloop_branch.is_some() {
|
|
|
|
debug!(
|
|
|
|
"Found a branch that exits the loop and a branch the loops back to \
|
|
|
|
the top of the loop (`reloop_branch`). The `reloop_branch` will \
|
|
|
|
get the `Expression`, as long as it still needs a counter."
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// else all branches exited this loop context, so run the same checks with
|
|
|
|
// the outer loop(s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
some_reloop_branch
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2022-06-03 16:42:42 +00:00
|
|
|
fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] {
|
2020-10-22 21:30:03 +00:00
|
|
|
&self.basic_coverage_blocks.predecessors[bcb]
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2022-06-03 16:42:42 +00:00
|
|
|
fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] {
|
2020-10-22 21:30:03 +00:00
|
|
|
&self.basic_coverage_blocks.successors[bcb]
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn bcb_branches(&self, from_bcb: BasicCoverageBlock) -> Vec<BcbBranch> {
|
|
|
|
self.bcb_successors(from_bcb)
|
|
|
|
.iter()
|
|
|
|
.map(|&to_bcb| BcbBranch::from_to(from_bcb, to_bcb, &self.basic_coverage_blocks))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn bcb_needs_branch_counters(&self, bcb: BasicCoverageBlock) -> bool {
|
|
|
|
let branch_needs_a_counter =
|
|
|
|
|branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none();
|
|
|
|
let branches = self.bcb_branches(bcb);
|
|
|
|
branches.len() > 1 && branches.iter().any(branch_needs_a_counter)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be
|
|
|
|
/// the entry point for the function.)
|
|
|
|
#[inline]
|
|
|
|
fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool {
|
|
|
|
self.bcb_predecessors(bcb).len() <= 1
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2023-01-20 00:00:00 +00:00
|
|
|
fn bcb_dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
|
|
|
|
self.basic_coverage_blocks.dominates(dom, node)
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn format_counter(&self, counter_kind: &CoverageKind) -> String {
|
|
|
|
self.coverage_counters.debug_counters.format_counter(counter_kind)
|
|
|
|
}
|
|
|
|
}
|