2024-03-20 07:25:06 +00:00
|
|
|
use std::fmt::{self, Debug};
|
|
|
|
|
2024-01-25 02:35:40 +00:00
|
|
|
use rustc_data_structures::captures::Captures;
|
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
2024-04-14 15:15:03 +00:00
|
|
|
use rustc_data_structures::graph::DirectedGraph;
|
2023-06-29 06:50:52 +00:00
|
|
|
use rustc_index::IndexVec;
|
2024-05-08 09:46:29 +00:00
|
|
|
use rustc_middle::bug;
|
2024-03-20 07:25:06 +00:00
|
|
|
use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op};
|
2020-10-23 07:45:07 +00:00
|
|
|
|
2024-03-20 07:25:06 +00:00
|
|
|
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops};
|
2023-07-08 03:43:29 +00:00
|
|
|
|
|
|
|
/// The coverage counter or counter expression associated with a particular
|
|
|
|
/// BCB node or BCB edge.
|
2024-04-21 08:11:57 +00:00
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
2023-07-08 03:43:29 +00:00
|
|
|
pub(super) enum BcbCounter {
|
2023-08-14 02:16:29 +00:00
|
|
|
Counter { id: CounterId },
|
2023-09-13 03:20:13 +00:00
|
|
|
Expression { id: ExpressionId },
|
2023-07-08 03:43:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BcbCounter {
|
2023-08-31 06:03:12 +00:00
|
|
|
pub(super) fn as_term(&self) -> CovTerm {
|
2023-07-08 03:43:29 +00:00
|
|
|
match *self {
|
2023-08-31 06:03:12 +00:00
|
|
|
BcbCounter::Counter { id, .. } => CovTerm::Counter(id),
|
|
|
|
BcbCounter::Expression { id, .. } => CovTerm::Expression(id),
|
2023-07-08 03:43:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for BcbCounter {
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
|
2023-09-13 03:20:13 +00:00
|
|
|
Self::Expression { id } => write!(fmt, "Expression({:?})", id.index()),
|
2023-07-08 03:43:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-21 08:11:57 +00:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
2024-04-21 08:05:02 +00:00
|
|
|
struct BcbExpression {
|
|
|
|
lhs: BcbCounter,
|
|
|
|
op: Op,
|
|
|
|
rhs: BcbCounter,
|
|
|
|
}
|
|
|
|
|
2024-01-25 02:35:40 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) enum CounterIncrementSite {
|
|
|
|
Node { bcb: BasicCoverageBlock },
|
|
|
|
Edge { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock },
|
|
|
|
}
|
|
|
|
|
2023-06-29 06:50:52 +00:00
|
|
|
/// Generates and stores coverage counter and coverage expression information
|
|
|
|
/// associated with nodes/edges in the BCB graph.
|
2020-11-03 05:32:48 +00:00
|
|
|
pub(super) struct CoverageCounters {
|
2024-01-25 02:35:40 +00:00
|
|
|
/// List of places where a counter-increment statement should be injected
|
|
|
|
/// into MIR, each with its corresponding counter ID.
|
|
|
|
counter_increment_sites: IndexVec<CounterId, CounterIncrementSite>,
|
2023-06-29 04:25:28 +00:00
|
|
|
|
2023-06-29 06:50:52 +00:00
|
|
|
/// Coverage counters/expressions that are associated with individual BCBs.
|
2023-07-08 03:43:29 +00:00
|
|
|
bcb_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>,
|
2023-06-29 06:50:52 +00:00
|
|
|
/// Coverage counters/expressions that are associated with the control-flow
|
|
|
|
/// edge between two BCBs.
|
2023-12-29 01:00:48 +00:00
|
|
|
///
|
2024-01-25 02:35:40 +00:00
|
|
|
/// We currently don't iterate over this map, but if we do in the future,
|
|
|
|
/// switch it back to `FxIndexMap` to avoid query stability hazards.
|
|
|
|
bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>,
|
2024-04-21 08:11:57 +00:00
|
|
|
|
2023-09-13 03:20:13 +00:00
|
|
|
/// Table of expression data, associating each expression ID with its
|
|
|
|
/// corresponding operator (+ or -) and its LHS/RHS operands.
|
2024-04-21 08:05:02 +00:00
|
|
|
expressions: IndexVec<ExpressionId, BcbExpression>,
|
2024-04-21 08:11:57 +00:00
|
|
|
/// Remember expressions that have already been created (or simplified),
|
|
|
|
/// so that we don't create unnecessary duplicates.
|
|
|
|
expressions_memo: FxHashMap<BcbExpression, BcbCounter>,
|
2020-10-23 07:45:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CoverageCounters {
|
2023-12-30 11:36:11 +00:00
|
|
|
/// Makes [`BcbCounter`] `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or
|
|
|
|
/// indirectly associated with coverage spans, and accumulates additional `Expression`s
|
|
|
|
/// representing intermediate values.
|
|
|
|
pub(super) fn make_bcb_counters(
|
|
|
|
basic_coverage_blocks: &CoverageGraph,
|
|
|
|
bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool,
|
|
|
|
) -> Self {
|
2023-06-29 06:50:52 +00:00
|
|
|
let num_bcbs = basic_coverage_blocks.num_nodes();
|
|
|
|
|
2023-12-30 11:36:11 +00:00
|
|
|
let mut this = Self {
|
2024-01-25 02:35:40 +00:00
|
|
|
counter_increment_sites: IndexVec::new(),
|
2023-06-29 06:50:52 +00:00
|
|
|
bcb_counters: IndexVec::from_elem_n(None, num_bcbs),
|
2024-01-25 02:35:40 +00:00
|
|
|
bcb_edge_counters: FxHashMap::default(),
|
2023-09-13 03:20:13 +00:00
|
|
|
expressions: IndexVec::new(),
|
2024-04-21 08:11:57 +00:00
|
|
|
expressions_memo: FxHashMap::default(),
|
2023-12-30 11:36:11 +00:00
|
|
|
};
|
2020-10-23 07:45:07 +00:00
|
|
|
|
2023-12-30 11:36:11 +00:00
|
|
|
MakeBcbCounters::new(&mut this, basic_coverage_blocks)
|
|
|
|
.make_bcb_counters(bcb_has_coverage_spans);
|
|
|
|
|
|
|
|
this
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2024-01-25 02:35:40 +00:00
|
|
|
fn make_counter(&mut self, site: CounterIncrementSite) -> BcbCounter {
|
|
|
|
let id = self.counter_increment_sites.push(site);
|
2023-09-19 10:26:23 +00:00
|
|
|
BcbCounter::Counter { id }
|
2020-10-23 07:45:07 +00:00
|
|
|
}
|
|
|
|
|
2023-11-01 00:23:27 +00:00
|
|
|
fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
|
2024-04-21 08:11:57 +00:00
|
|
|
let new_expr = BcbExpression { lhs, op, rhs };
|
|
|
|
*self
|
|
|
|
.expressions_memo
|
|
|
|
.entry(new_expr)
|
|
|
|
.or_insert_with(|| Self::make_expression_inner(&mut self.expressions, new_expr))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This is an associated function so that we can call it while borrowing
|
|
|
|
/// `&mut self.expressions_memo`.
|
|
|
|
fn make_expression_inner(
|
|
|
|
expressions: &mut IndexVec<ExpressionId, BcbExpression>,
|
|
|
|
new_expr: BcbExpression,
|
|
|
|
) -> BcbCounter {
|
2024-05-14 03:51:24 +00:00
|
|
|
// Simplify expressions using basic algebra.
|
|
|
|
//
|
|
|
|
// Some of these cases might not actually occur in practice, depending
|
|
|
|
// on the details of how the instrumentor builds expressions.
|
|
|
|
let BcbExpression { lhs, op, rhs } = new_expr;
|
|
|
|
|
|
|
|
if let BcbCounter::Expression { id } = lhs {
|
|
|
|
let lhs_expr = &expressions[id];
|
|
|
|
|
|
|
|
// Simplify `(a - b) + b` to `a`.
|
|
|
|
if lhs_expr.op == Op::Subtract && op == Op::Add && lhs_expr.rhs == rhs {
|
|
|
|
return lhs_expr.lhs;
|
|
|
|
}
|
|
|
|
// Simplify `(a + b) - b` to `a`.
|
|
|
|
if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.rhs == rhs {
|
|
|
|
return lhs_expr.lhs;
|
|
|
|
}
|
|
|
|
// Simplify `(a + b) - a` to `b`.
|
|
|
|
if lhs_expr.op == Op::Add && op == Op::Subtract && lhs_expr.lhs == rhs {
|
|
|
|
return lhs_expr.rhs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let BcbCounter::Expression { id } = rhs {
|
|
|
|
let rhs_expr = &expressions[id];
|
|
|
|
|
|
|
|
// Simplify `a + (b - a)` to `b`.
|
|
|
|
if op == Op::Add && rhs_expr.op == Op::Subtract && lhs == rhs_expr.rhs {
|
|
|
|
return rhs_expr.lhs;
|
|
|
|
}
|
|
|
|
// Simplify `a - (a - b)` to `b`.
|
|
|
|
if op == Op::Subtract && rhs_expr.op == Op::Subtract && lhs == rhs_expr.lhs {
|
|
|
|
return rhs_expr.rhs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simplification failed, so actually create the new expression.
|
2024-04-21 08:11:57 +00:00
|
|
|
let id = expressions.push(new_expr);
|
2023-09-13 03:20:13 +00:00
|
|
|
BcbCounter::Expression { id }
|
2020-10-23 07:45:07 +00:00
|
|
|
}
|
|
|
|
|
2023-10-31 06:44:26 +00:00
|
|
|
/// Variant of `make_expression` that makes `lhs` optional and assumes [`Op::Add`].
|
|
|
|
///
|
|
|
|
/// This is useful when using [`Iterator::fold`] to build an arbitrary-length sum.
|
|
|
|
fn make_sum_expression(&mut self, lhs: Option<BcbCounter>, rhs: BcbCounter) -> BcbCounter {
|
|
|
|
let Some(lhs) = lhs else { return rhs };
|
|
|
|
self.make_expression(lhs, Op::Add, rhs)
|
|
|
|
}
|
|
|
|
|
2023-09-13 02:15:40 +00:00
|
|
|
pub(super) fn num_counters(&self) -> usize {
|
2024-01-25 02:35:40 +00:00
|
|
|
self.counter_increment_sites.len()
|
2023-09-13 02:15:40 +00:00
|
|
|
}
|
|
|
|
|
2023-11-01 00:23:27 +00:00
|
|
|
fn set_bcb_counter(&mut self, bcb: BasicCoverageBlock, counter_kind: BcbCounter) -> BcbCounter {
|
2023-06-29 06:50:52 +00:00
|
|
|
if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) {
|
2023-10-29 11:33:29 +00:00
|
|
|
bug!(
|
2023-06-29 06:50:52 +00:00
|
|
|
"attempt to set a BasicCoverageBlock coverage counter more than once; \
|
|
|
|
{bcb:?} already had counter {replaced:?}",
|
2023-10-29 11:33:29 +00:00
|
|
|
);
|
2023-06-29 06:50:52 +00:00
|
|
|
} else {
|
2023-11-01 00:23:27 +00:00
|
|
|
counter_kind
|
2023-06-29 06:50:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_bcb_edge_counter(
|
|
|
|
&mut self,
|
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
to_bcb: BasicCoverageBlock,
|
2023-07-08 03:43:29 +00:00
|
|
|
counter_kind: BcbCounter,
|
2023-11-01 00:23:27 +00:00
|
|
|
) -> BcbCounter {
|
2023-06-29 06:50:52 +00:00
|
|
|
if let Some(replaced) = self.bcb_edge_counters.insert((from_bcb, to_bcb), counter_kind) {
|
2023-10-29 11:33:29 +00:00
|
|
|
bug!(
|
2023-06-29 06:50:52 +00:00
|
|
|
"attempt to set an edge counter more than once; from_bcb: \
|
|
|
|
{from_bcb:?} already had counter {replaced:?}",
|
2023-10-29 11:33:29 +00:00
|
|
|
);
|
2023-06-29 06:50:52 +00:00
|
|
|
} else {
|
2023-11-01 00:23:27 +00:00
|
|
|
counter_kind
|
2023-06-29 06:50:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-31 06:44:26 +00:00
|
|
|
pub(super) fn bcb_counter(&self, bcb: BasicCoverageBlock) -> Option<BcbCounter> {
|
|
|
|
self.bcb_counters[bcb]
|
2023-06-29 06:50:52 +00:00
|
|
|
}
|
|
|
|
|
2024-01-25 02:35:40 +00:00
|
|
|
/// Returns an iterator over all the nodes/edges in the coverage graph that
|
|
|
|
/// should have a counter-increment statement injected into MIR, along with
|
|
|
|
/// each site's corresponding counter ID.
|
|
|
|
pub(super) fn counter_increment_sites(
|
2023-09-22 07:36:01 +00:00
|
|
|
&self,
|
2024-01-25 02:35:40 +00:00
|
|
|
) -> impl Iterator<Item = (CounterId, &CounterIncrementSite)> {
|
|
|
|
self.counter_increment_sites.iter_enumerated()
|
2023-09-22 07:36:01 +00:00
|
|
|
}
|
|
|
|
|
2024-01-25 02:35:40 +00:00
|
|
|
/// Returns an iterator over the subset of BCB nodes that have been associated
|
|
|
|
/// with a counter *expression*, along with the ID of that expression.
|
|
|
|
pub(super) fn bcb_nodes_with_coverage_expressions(
|
2023-09-22 07:36:01 +00:00
|
|
|
&self,
|
2024-01-25 02:35:40 +00:00
|
|
|
) -> impl Iterator<Item = (BasicCoverageBlock, ExpressionId)> + Captures<'_> {
|
|
|
|
self.bcb_counters.iter_enumerated().filter_map(|(bcb, &counter_kind)| match counter_kind {
|
|
|
|
// Yield the BCB along with its associated expression ID.
|
|
|
|
Some(BcbCounter::Expression { id }) => Some((bcb, id)),
|
|
|
|
// This BCB is associated with a counter or nothing, so skip it.
|
|
|
|
Some(BcbCounter::Counter { .. }) | None => None,
|
|
|
|
})
|
2023-06-29 06:50:52 +00:00
|
|
|
}
|
2023-09-13 03:20:13 +00:00
|
|
|
|
2023-12-30 11:36:11 +00:00
|
|
|
pub(super) fn into_expressions(self) -> IndexVec<ExpressionId, Expression> {
|
2024-04-21 08:05:02 +00:00
|
|
|
let old_len = self.expressions.len();
|
|
|
|
let expressions = self
|
|
|
|
.expressions
|
|
|
|
.into_iter()
|
|
|
|
.map(|BcbExpression { lhs, op, rhs }| Expression {
|
|
|
|
lhs: lhs.as_term(),
|
|
|
|
op,
|
|
|
|
rhs: rhs.as_term(),
|
|
|
|
})
|
|
|
|
.collect::<IndexVec<ExpressionId, _>>();
|
|
|
|
|
|
|
|
// Expression IDs are indexes into this vector, so make sure we didn't
|
|
|
|
// accidentally invalidate them by changing its length.
|
|
|
|
assert_eq!(old_len, expressions.len());
|
|
|
|
expressions
|
2023-09-13 03:20:13 +00:00
|
|
|
}
|
2020-10-23 07:45:07 +00:00
|
|
|
}
|
2020-10-22 21:30:03 +00:00
|
|
|
|
|
|
|
/// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be
|
2023-09-17 12:22:21 +00:00
|
|
|
/// injected with coverage spans. `Expressions` have no runtime overhead, so if a viable expression
|
2020-10-22 21:30:03 +00:00
|
|
|
/// (adding or subtracting two other counters or expressions) can compute the same result as an
|
|
|
|
/// embedded counter, an `Expression` should be used.
|
2023-06-29 04:02:08 +00:00
|
|
|
struct MakeBcbCounters<'a> {
|
2020-10-22 21:30:03 +00:00
|
|
|
coverage_counters: &'a mut CoverageCounters,
|
2023-06-29 06:50:52 +00:00
|
|
|
basic_coverage_blocks: &'a CoverageGraph,
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2023-06-29 04:02:08 +00:00
|
|
|
impl<'a> MakeBcbCounters<'a> {
|
2020-10-22 21:30:03 +00:00
|
|
|
fn new(
|
|
|
|
coverage_counters: &'a mut CoverageCounters,
|
2023-06-29 06:50:52 +00:00
|
|
|
basic_coverage_blocks: &'a CoverageGraph,
|
2020-10-22 21:30:03 +00:00
|
|
|
) -> 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.
|
2023-10-29 11:33:29 +00:00
|
|
|
fn make_bcb_counters(&mut self, bcb_has_coverage_spans: impl Fn(BasicCoverageBlock) -> bool) {
|
2020-10-22 21:30:03 +00:00
|
|
|
debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock");
|
|
|
|
|
2020-10-30 23:09:05 +00:00
|
|
|
// Walk the `CoverageGraph`. For each `BasicCoverageBlock` node with an associated
|
2023-09-17 12:22:21 +00:00
|
|
|
// coverage span, add a counter. If the `BasicCoverageBlock` branches, add a counter or
|
2020-10-30 23:09:05 +00:00
|
|
|
// 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.
|
2023-11-21 19:07:32 +00:00
|
|
|
let mut traversal = TraverseCoverageGraphWithLoops::new(self.basic_coverage_blocks);
|
2023-10-11 06:40:37 +00:00
|
|
|
while let Some(bcb) = traversal.next() {
|
2023-09-17 12:22:21 +00:00
|
|
|
if bcb_has_coverage_spans(bcb) {
|
2023-09-17 12:22:21 +00:00
|
|
|
debug!("{:?} has at least one coverage span. Get or make its counter", bcb);
|
2023-10-30 10:42:10 +00:00
|
|
|
self.make_node_and_branch_counters(&traversal, bcb);
|
2020-10-22 21:30:03 +00:00
|
|
|
} else {
|
|
|
|
debug!(
|
2023-09-17 12:22:21 +00:00
|
|
|
"{:?} does not have any coverage spans. A counter will only be added if \
|
2020-10-22 21:30:03 +00:00
|
|
|
and when a covered BCB has an expression dependency.",
|
|
|
|
bcb,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-29 11:33:29 +00:00
|
|
|
assert!(
|
|
|
|
traversal.is_complete(),
|
|
|
|
"`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}",
|
|
|
|
traversal.unvisited(),
|
|
|
|
);
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2023-10-30 10:42:10 +00:00
|
|
|
fn make_node_and_branch_counters(
|
2020-10-22 21:30:03 +00:00
|
|
|
&mut self,
|
2023-10-11 06:40:37 +00:00
|
|
|
traversal: &TraverseCoverageGraphWithLoops<'_>,
|
2023-10-30 10:37:13 +00:00
|
|
|
from_bcb: BasicCoverageBlock,
|
2023-10-29 11:33:29 +00:00
|
|
|
) {
|
2023-10-30 10:42:10 +00:00
|
|
|
// First, ensure that this node has a counter of some kind.
|
|
|
|
// We might also use its term later to compute one of the branch counters.
|
|
|
|
let from_bcb_operand = self.get_or_make_counter_operand(from_bcb);
|
|
|
|
|
2023-11-01 00:40:12 +00:00
|
|
|
let branch_target_bcbs = self.basic_coverage_blocks.successors[from_bcb].as_slice();
|
2023-10-30 10:40:08 +00:00
|
|
|
|
|
|
|
// If this node doesn't have multiple out-edges, or all of its out-edges
|
|
|
|
// already have counters, then we don't need to create edge counters.
|
2023-11-01 00:40:12 +00:00
|
|
|
let needs_branch_counters = branch_target_bcbs.len() > 1
|
|
|
|
&& branch_target_bcbs
|
|
|
|
.iter()
|
|
|
|
.any(|&to_bcb| self.branch_has_no_counter(from_bcb, to_bcb));
|
2023-10-30 10:40:08 +00:00
|
|
|
if !needs_branch_counters {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-22 21:30:03 +00:00
|
|
|
debug!(
|
2023-10-30 10:37:13 +00:00
|
|
|
"{from_bcb:?} has some branch(es) without counters:\n {}",
|
2023-11-01 00:40:12 +00:00
|
|
|
branch_target_bcbs
|
2020-10-22 21:30:03 +00:00
|
|
|
.iter()
|
2023-11-01 00:40:12 +00:00
|
|
|
.map(|&to_bcb| {
|
|
|
|
format!("{from_bcb:?}->{to_bcb:?}: {:?}", self.branch_counter(from_bcb, to_bcb))
|
|
|
|
})
|
2020-10-22 21:30:03 +00:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join("\n "),
|
|
|
|
);
|
|
|
|
|
2023-11-01 00:40:12 +00:00
|
|
|
// Of the branch edges that don't have counters yet, one can be given an expression
|
|
|
|
// (computed from the other edges) instead of a dedicated counter.
|
|
|
|
let expression_to_bcb = self.choose_preferred_expression_branch(traversal, from_bcb);
|
2020-10-30 23:09:05 +00:00
|
|
|
|
2023-10-31 06:44:26 +00:00
|
|
|
// For each branch arm other than the one that was chosen to get an expression,
|
|
|
|
// ensure that it has a counter (existing counter/expression or a new counter),
|
|
|
|
// and accumulate the corresponding terms into a single sum term.
|
|
|
|
let sum_of_all_other_branches: BcbCounter = {
|
2023-11-01 00:40:12 +00:00
|
|
|
let _span = debug_span!("sum_of_all_other_branches", ?expression_to_bcb).entered();
|
|
|
|
branch_target_bcbs
|
|
|
|
.iter()
|
|
|
|
.copied()
|
2023-10-31 06:44:26 +00:00
|
|
|
// Skip the chosen branch, since we'll calculate it from the other branches.
|
2023-11-01 00:40:12 +00:00
|
|
|
.filter(|&to_bcb| to_bcb != expression_to_bcb)
|
|
|
|
.fold(None, |accum, to_bcb| {
|
|
|
|
let _span = debug_span!("to_bcb", ?accum, ?to_bcb).entered();
|
|
|
|
let branch_counter = self.get_or_make_edge_counter_operand(from_bcb, to_bcb);
|
2023-10-31 06:44:26 +00:00
|
|
|
Some(self.coverage_counters.make_sum_expression(accum, branch_counter))
|
|
|
|
})
|
|
|
|
.expect("there must be at least one other branch")
|
|
|
|
};
|
|
|
|
|
|
|
|
// For the branch that was chosen to get an expression, create that expression
|
|
|
|
// by taking the count of the node we're branching from, and subtracting the
|
|
|
|
// sum of all the other branches.
|
2020-10-22 21:30:03 +00:00
|
|
|
debug!(
|
2023-11-01 00:40:12 +00:00
|
|
|
"Making an expression for the selected expression_branch: \
|
|
|
|
{expression_to_bcb:?} (expression_branch predecessors: {:?})",
|
|
|
|
self.bcb_predecessors(expression_to_bcb),
|
2020-10-22 21:30:03 +00:00
|
|
|
);
|
|
|
|
let expression = self.coverage_counters.make_expression(
|
2023-10-30 10:42:10 +00:00
|
|
|
from_bcb_operand,
|
2020-10-22 21:30:03 +00:00
|
|
|
Op::Subtract,
|
2023-10-31 06:44:26 +00:00
|
|
|
sum_of_all_other_branches,
|
2020-10-22 21:30:03 +00:00
|
|
|
);
|
2023-11-01 00:40:12 +00:00
|
|
|
debug!("{expression_to_bcb:?} gets an expression: {expression:?}");
|
|
|
|
if self.basic_coverage_blocks.bcb_has_multiple_in_edges(expression_to_bcb) {
|
|
|
|
self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression);
|
2020-10-22 21:30:03 +00:00
|
|
|
} else {
|
2023-11-01 00:40:12 +00:00
|
|
|
self.coverage_counters.set_bcb_counter(expression_to_bcb, expression);
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-29 09:50:47 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2023-11-01 00:23:27 +00:00
|
|
|
fn get_or_make_counter_operand(&mut self, bcb: BasicCoverageBlock) -> BcbCounter {
|
2020-10-30 23:09:05 +00:00
|
|
|
// If the BCB already has a counter, return it.
|
2023-11-01 00:23:27 +00:00
|
|
|
if let Some(counter_kind) = self.coverage_counters.bcb_counters[bcb] {
|
2023-10-29 09:50:47 +00:00
|
|
|
debug!("{bcb:?} already has a counter: {counter_kind:?}");
|
2023-11-01 00:23:27 +00:00
|
|
|
return counter_kind;
|
2020-10-30 23:09:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2023-10-30 09:54:19 +00:00
|
|
|
let one_path_to_target = !self.basic_coverage_blocks.bcb_has_multiple_in_edges(bcb);
|
2020-10-30 23:09:05 +00:00
|
|
|
if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) {
|
2024-01-25 02:35:40 +00:00
|
|
|
let counter_kind =
|
|
|
|
self.coverage_counters.make_counter(CounterIncrementSite::Node { bcb });
|
2020-10-30 23:09:05 +00:00
|
|
|
if one_path_to_target {
|
2023-10-29 09:50:47 +00:00
|
|
|
debug!("{bcb:?} gets a new counter: {counter_kind:?}");
|
2020-10-22 21:30:03 +00:00
|
|
|
} else {
|
2020-10-30 23:09:05 +00:00
|
|
|
debug!(
|
2023-10-29 09:50:47 +00:00
|
|
|
"{bcb:?} has itself as its own predecessor. It can't be part of its own \
|
|
|
|
Expression sum, so it will get its own new counter: {counter_kind:?}. \
|
|
|
|
(Note, the compiled code will generate an infinite loop.)",
|
2020-10-30 23:09:05 +00:00
|
|
|
);
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
2023-06-29 06:50:52 +00:00
|
|
|
return self.coverage_counters.set_bcb_counter(bcb, counter_kind);
|
2020-10-30 23:09:05 +00:00
|
|
|
}
|
|
|
|
|
2023-10-31 06:44:26 +00:00
|
|
|
// A BCB with multiple incoming edges can compute its count by ensuring that counters
|
|
|
|
// exist for each of those edges, and then adding them up to get a total count.
|
|
|
|
let sum_of_in_edges: BcbCounter = {
|
|
|
|
let _span = debug_span!("sum_of_in_edges", ?bcb).entered();
|
|
|
|
// We avoid calling `self.bcb_predecessors` here so that we can
|
|
|
|
// call methods on `&mut self` inside the fold.
|
|
|
|
self.basic_coverage_blocks.predecessors[bcb]
|
|
|
|
.iter()
|
|
|
|
.copied()
|
|
|
|
.fold(None, |accum, from_bcb| {
|
|
|
|
let _span = debug_span!("from_bcb", ?accum, ?from_bcb).entered();
|
|
|
|
let edge_counter = self.get_or_make_edge_counter_operand(from_bcb, bcb);
|
|
|
|
Some(self.coverage_counters.make_sum_expression(accum, edge_counter))
|
|
|
|
})
|
|
|
|
.expect("there must be at least one in-edge")
|
|
|
|
};
|
|
|
|
|
|
|
|
debug!("{bcb:?} gets a new counter (sum of predecessor counters): {sum_of_in_edges:?}");
|
|
|
|
self.coverage_counters.set_bcb_counter(bcb, sum_of_in_edges)
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2023-10-29 09:50:47 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2023-10-29 09:53:17 +00:00
|
|
|
fn get_or_make_edge_counter_operand(
|
2020-10-22 21:30:03 +00:00
|
|
|
&mut self,
|
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
to_bcb: BasicCoverageBlock,
|
2023-11-01 00:23:27 +00:00
|
|
|
) -> BcbCounter {
|
2023-11-01 00:40:12 +00:00
|
|
|
// If the target BCB has only one in-edge (i.e. this one), then create
|
|
|
|
// a node counter instead, since it will have the same value.
|
|
|
|
if !self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
|
|
|
|
assert_eq!([from_bcb].as_slice(), self.basic_coverage_blocks.predecessors[to_bcb]);
|
|
|
|
return self.get_or_make_counter_operand(to_bcb);
|
|
|
|
}
|
|
|
|
|
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.
|
2023-11-01 00:40:12 +00:00
|
|
|
if self.bcb_successors(from_bcb).len() == 1 {
|
2023-10-29 09:53:17 +00:00
|
|
|
return self.get_or_make_counter_operand(from_bcb);
|
2020-10-30 23:09:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the edge already has a counter, return it.
|
2023-11-01 00:23:27 +00:00
|
|
|
if let Some(&counter_kind) =
|
2023-06-29 06:50:52 +00:00
|
|
|
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
|
|
|
|
{
|
2023-10-29 09:50:47 +00:00
|
|
|
debug!("Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter_kind:?}");
|
2023-11-01 00:23:27 +00:00
|
|
|
return counter_kind;
|
2020-10-30 23:09:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make a new counter to count this edge.
|
2024-01-25 02:35:40 +00:00
|
|
|
let counter_kind =
|
|
|
|
self.coverage_counters.make_counter(CounterIncrementSite::Edge { from_bcb, to_bcb });
|
2023-10-29 09:50:47 +00:00
|
|
|
debug!("Edge {from_bcb:?}->{to_bcb:?} gets a new counter: {counter_kind:?}");
|
2023-06-29 06:50:52 +00:00
|
|
|
self.coverage_counters.set_bcb_edge_counter(from_bcb, to_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,
|
2023-10-11 06:40:37 +00:00
|
|
|
traversal: &TraverseCoverageGraphWithLoops<'_>,
|
2023-11-01 00:40:12 +00:00
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
) -> BasicCoverageBlock {
|
|
|
|
let good_reloop_branch = self.find_good_reloop_branch(traversal, from_bcb);
|
|
|
|
if let Some(reloop_target) = good_reloop_branch {
|
|
|
|
assert!(self.branch_has_no_counter(from_bcb, reloop_target));
|
|
|
|
debug!("Selecting reloop target {reloop_target:?} to get an expression");
|
|
|
|
reloop_target
|
2020-10-22 21:30:03 +00:00
|
|
|
} else {
|
2023-11-01 00:40:12 +00:00
|
|
|
let &branch_without_counter = self
|
|
|
|
.bcb_successors(from_bcb)
|
|
|
|
.iter()
|
|
|
|
.find(|&&to_bcb| self.branch_has_no_counter(from_bcb, to_bcb))
|
|
|
|
.expect(
|
2020-10-22 21:30:03 +00:00
|
|
|
"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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-12 06:41:11 +00:00
|
|
|
/// Tries to find a branch that leads back to the top of a loop, and that
|
|
|
|
/// doesn't already have a counter. Such branches are good candidates to
|
|
|
|
/// be given an expression (instead of a physical counter), because they
|
|
|
|
/// will tend to be executed more times than a loop-exit branch.
|
|
|
|
fn find_good_reloop_branch(
|
2020-10-22 21:30:03 +00:00
|
|
|
&self,
|
2023-10-11 06:40:37 +00:00
|
|
|
traversal: &TraverseCoverageGraphWithLoops<'_>,
|
2023-11-01 00:40:12 +00:00
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
) -> Option<BasicCoverageBlock> {
|
|
|
|
let branch_target_bcbs = self.bcb_successors(from_bcb);
|
|
|
|
|
2023-10-12 06:41:11 +00:00
|
|
|
// Consider each loop on the current traversal context stack, top-down.
|
|
|
|
for reloop_bcbs in traversal.reloop_bcbs_per_loop() {
|
|
|
|
let mut all_branches_exit_this_loop = true;
|
|
|
|
|
|
|
|
// Try to find a branch that doesn't exit this loop and doesn't
|
|
|
|
// already have a counter.
|
2023-11-01 00:40:12 +00:00
|
|
|
for &branch_target_bcb in branch_target_bcbs {
|
2023-10-12 06:41:11 +00:00
|
|
|
// A branch is a reloop branch if it dominates any BCB that has
|
|
|
|
// an edge back to the loop header. (Other branches are exits.)
|
|
|
|
let is_reloop_branch = reloop_bcbs.iter().any(|&reloop_bcb| {
|
2023-11-01 00:40:12 +00:00
|
|
|
self.basic_coverage_blocks.dominates(branch_target_bcb, reloop_bcb)
|
2023-10-12 06:41:11 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if is_reloop_branch {
|
|
|
|
all_branches_exit_this_loop = false;
|
2023-11-01 00:40:12 +00:00
|
|
|
if self.branch_has_no_counter(from_bcb, branch_target_bcb) {
|
2023-10-12 06:41:11 +00:00
|
|
|
// We found a good branch to be given an expression.
|
2023-11-01 00:40:12 +00:00
|
|
|
return Some(branch_target_bcb);
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
2023-10-12 06:41:11 +00:00
|
|
|
// Keep looking for another reloop branch without a counter.
|
|
|
|
} else {
|
|
|
|
// This branch exits the loop.
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
}
|
2023-10-12 06:41:11 +00:00
|
|
|
|
|
|
|
if !all_branches_exit_this_loop {
|
|
|
|
// We found one or more reloop branches, but all of them already
|
|
|
|
// have counters. Let the caller choose one of the exit branches.
|
|
|
|
debug!("All reloop branches had counters; skip checking the other loops");
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// All of the branches exit this loop, so keep looking for a good
|
|
|
|
// reloop branch for one of the outer loops.
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
2023-10-12 06:41:11 +00:00
|
|
|
|
|
|
|
None
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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]
|
2023-11-01 00:40:12 +00:00
|
|
|
fn branch_has_no_counter(
|
|
|
|
&self,
|
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
to_bcb: BasicCoverageBlock,
|
|
|
|
) -> bool {
|
|
|
|
self.branch_counter(from_bcb, to_bcb).is_none()
|
2023-06-29 06:50:52 +00:00
|
|
|
}
|
|
|
|
|
2023-11-01 00:40:12 +00:00
|
|
|
fn branch_counter(
|
|
|
|
&self,
|
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
to_bcb: BasicCoverageBlock,
|
|
|
|
) -> Option<&BcbCounter> {
|
|
|
|
if self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) {
|
2023-06-29 06:50:52 +00:00
|
|
|
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
|
|
|
|
} else {
|
|
|
|
self.coverage_counters.bcb_counters[to_bcb].as_ref()
|
|
|
|
}
|
|
|
|
}
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|