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-10-06 11:44:14 +00:00
|
|
|
use rustc_index::bit_set::BitSet;
|
2024-03-20 07:25:06 +00:00
|
|
|
use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op};
|
2024-08-28 05:03:14 +00:00
|
|
|
use tracing::{debug, debug_span, instrument};
|
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)]
|
2024-10-06 09:59:24 +00:00
|
|
|
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 {
|
2024-10-06 09:59:24 +00:00
|
|
|
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-11-27 02:21:03 +00:00
|
|
|
/// Enum representing either a node or an edge in the coverage graph.
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub(super) enum Site {
|
2024-01-25 02:35:40 +00:00
|
|
|
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.
|
2024-11-27 02:21:03 +00:00
|
|
|
counter_increment_sites: IndexVec<CounterId, Site>,
|
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.
|
2024-10-19 07:02:03 +00:00
|
|
|
node_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.
|
2024-10-19 07:02:03 +00:00
|
|
|
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 {
|
2024-09-10 10:16:43 +00:00
|
|
|
/// Ensures that each BCB node needing a counter has one, by creating physical
|
|
|
|
/// counters or counter expressions for nodes and edges as required.
|
2023-12-30 11:36:11 +00:00
|
|
|
pub(super) fn make_bcb_counters(
|
2024-10-19 07:02:03 +00:00
|
|
|
graph: &CoverageGraph,
|
2024-10-06 11:44:14 +00:00
|
|
|
bcb_needs_counter: &BitSet<BasicCoverageBlock>,
|
2023-12-30 11:36:11 +00:00
|
|
|
) -> Self {
|
2024-10-19 07:02:03 +00:00
|
|
|
let mut builder = CountersBuilder::new(graph, bcb_needs_counter);
|
|
|
|
builder.make_bcb_counters();
|
2023-06-29 06:50:52 +00:00
|
|
|
|
2024-10-19 07:02:03 +00:00
|
|
|
builder.counters
|
2024-10-05 07:08:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn with_num_bcbs(num_bcbs: usize) -> Self {
|
|
|
|
Self {
|
2024-01-25 02:35:40 +00:00
|
|
|
counter_increment_sites: IndexVec::new(),
|
2024-10-19 07:02:03 +00:00
|
|
|
node_counters: IndexVec::from_elem_n(None, num_bcbs),
|
|
|
|
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(),
|
2024-10-05 07:08:07 +00:00
|
|
|
}
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2024-12-01 04:42:59 +00:00
|
|
|
/// Creates a new physical counter for a BCB node or edge.
|
|
|
|
fn make_phys_counter(&mut self, site: Site) -> BcbCounter {
|
2024-01-25 02:35:40 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2024-09-11 07:24:31 +00:00
|
|
|
/// Creates a counter that is the sum of the given counters.
|
2023-10-31 06:44:26 +00:00
|
|
|
///
|
2024-09-11 07:24:31 +00:00
|
|
|
/// Returns `None` if the given list of counters was empty.
|
|
|
|
fn make_sum(&mut self, counters: &[BcbCounter]) -> Option<BcbCounter> {
|
|
|
|
counters
|
|
|
|
.iter()
|
|
|
|
.copied()
|
|
|
|
.reduce(|accum, counter| self.make_expression(accum, Op::Add, counter))
|
2023-10-31 06:44:26 +00:00
|
|
|
}
|
|
|
|
|
2024-10-16 10:25:09 +00:00
|
|
|
/// Creates a counter whose value is `lhs - SUM(rhs)`.
|
|
|
|
fn make_subtracted_sum(&mut self, lhs: BcbCounter, rhs: &[BcbCounter]) -> BcbCounter {
|
|
|
|
let Some(rhs_sum) = self.make_sum(rhs) else { return lhs };
|
|
|
|
self.make_expression(lhs, Op::Subtract, rhs_sum)
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2024-10-19 07:02:03 +00:00
|
|
|
fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: BcbCounter) -> BcbCounter {
|
|
|
|
let existing = self.node_counters[bcb].replace(counter);
|
|
|
|
assert!(
|
|
|
|
existing.is_none(),
|
|
|
|
"node {bcb:?} already has a counter: {existing:?} => {counter:?}"
|
|
|
|
);
|
|
|
|
counter
|
2023-06-29 06:50:52 +00:00
|
|
|
}
|
|
|
|
|
2024-10-19 07:02:03 +00:00
|
|
|
fn set_edge_counter(
|
2023-06-29 06:50:52 +00:00
|
|
|
&mut self,
|
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
to_bcb: BasicCoverageBlock,
|
2024-10-19 07:02:03 +00:00
|
|
|
counter: BcbCounter,
|
2023-11-01 00:23:27 +00:00
|
|
|
) -> BcbCounter {
|
2024-10-19 07:02:03 +00:00
|
|
|
let existing = self.edge_counters.insert((from_bcb, to_bcb), counter);
|
|
|
|
assert!(
|
|
|
|
existing.is_none(),
|
|
|
|
"edge ({from_bcb:?} -> {to_bcb:?}) already has a counter: {existing:?} => {counter:?}"
|
|
|
|
);
|
|
|
|
counter
|
2023-06-29 06:50:52 +00:00
|
|
|
}
|
|
|
|
|
2024-10-06 09:59:24 +00:00
|
|
|
pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option<CovTerm> {
|
2024-10-19 07:02:03 +00:00
|
|
|
self.node_counters[bcb].map(|counter| counter.as_term())
|
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-11-27 02:21:03 +00:00
|
|
|
) -> impl Iterator<Item = (CounterId, Site)> + Captures<'_> {
|
|
|
|
self.counter_increment_sites.iter_enumerated().map(|(id, &site)| (id, site))
|
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<'_> {
|
2024-10-19 07:02:03 +00:00
|
|
|
self.node_counters.iter_enumerated().filter_map(|(bcb, &counter)| match counter {
|
2024-01-25 02:35:40 +00:00
|
|
|
// 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
|
|
|
|
2024-10-19 07:02:03 +00:00
|
|
|
/// Helper struct that allows counter creation to inspect the BCB graph, and
|
|
|
|
/// the set of nodes that need counters.
|
|
|
|
struct CountersBuilder<'a> {
|
|
|
|
counters: CoverageCounters,
|
|
|
|
graph: &'a CoverageGraph,
|
2024-10-06 11:44:14 +00:00
|
|
|
bcb_needs_counter: &'a BitSet<BasicCoverageBlock>,
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2024-10-19 07:02:03 +00:00
|
|
|
impl<'a> CountersBuilder<'a> {
|
|
|
|
fn new(graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet<BasicCoverageBlock>) -> Self {
|
|
|
|
assert_eq!(graph.num_nodes(), bcb_needs_counter.domain_size());
|
2024-10-05 07:08:07 +00:00
|
|
|
Self {
|
2024-10-19 07:02:03 +00:00
|
|
|
counters: CoverageCounters::with_num_bcbs(graph.num_nodes()),
|
|
|
|
graph,
|
2024-10-06 11:44:14 +00:00
|
|
|
bcb_needs_counter,
|
2024-10-05 07:08:07 +00:00
|
|
|
}
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2024-10-06 11:44:14 +00:00
|
|
|
fn make_bcb_counters(&mut self) {
|
2020-10-22 21:30:03 +00:00
|
|
|
debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock");
|
|
|
|
|
2024-09-10 10:16:43 +00:00
|
|
|
// Traverse the coverage graph, ensuring that every node that needs a
|
|
|
|
// coverage counter has one.
|
2020-10-30 23:09:05 +00:00
|
|
|
//
|
2024-09-10 10:16:43 +00:00
|
|
|
// The traversal tries to ensure that, when a loop is encountered, all
|
|
|
|
// nodes within the loop are visited before visiting any nodes outside
|
2024-10-23 10:02:18 +00:00
|
|
|
// the loop.
|
2024-10-19 07:02:03 +00:00
|
|
|
let mut traversal = TraverseCoverageGraphWithLoops::new(self.graph);
|
2023-10-11 06:40:37 +00:00
|
|
|
while let Some(bcb) = traversal.next() {
|
2024-09-10 10:16:43 +00:00
|
|
|
let _span = debug_span!("traversal", ?bcb).entered();
|
2024-10-06 11:44:14 +00:00
|
|
|
if self.bcb_needs_counter.contains(bcb) {
|
2024-10-23 10:02:18 +00:00
|
|
|
self.make_node_counter_and_out_edge_counters(bcb);
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2024-09-10 10:44:15 +00:00
|
|
|
/// Make sure the given node has a node counter, and then make sure each of
|
|
|
|
/// its out-edges has an edge counter (if appropriate).
|
2024-10-23 10:02:18 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
|
|
|
fn make_node_counter_and_out_edge_counters(&mut self, from_bcb: BasicCoverageBlock) {
|
2023-10-30 10:42:10 +00:00
|
|
|
// First, ensure that this node has a counter of some kind.
|
2024-09-10 10:44:15 +00:00
|
|
|
// We might also use that counter to compute one of the out-edge counters.
|
2024-09-10 10:48:28 +00:00
|
|
|
let node_counter = self.get_or_make_node_counter(from_bcb);
|
2023-10-30 10:42:10 +00:00
|
|
|
|
2024-10-19 07:02:03 +00:00
|
|
|
let successors = self.graph.successors[from_bcb].as_slice();
|
2023-10-30 10:40:08 +00:00
|
|
|
|
2024-09-13 05:36:46 +00:00
|
|
|
// If this node's out-edges won't sum to the node's counter,
|
|
|
|
// then there's no reason to create edge counters here.
|
2024-10-19 07:02:03 +00:00
|
|
|
if !self.graph[from_bcb].is_out_summable {
|
2024-09-13 05:36:46 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-10-30 10:40:08 +00:00
|
|
|
|
2024-10-05 08:18:47 +00:00
|
|
|
// When choosing which out-edge should be given a counter expression, ignore edges that
|
|
|
|
// already have counters, or could use the existing counter of their target node.
|
|
|
|
let out_edge_has_counter = |to_bcb| {
|
2024-10-19 07:02:03 +00:00
|
|
|
if self.counters.edge_counters.contains_key(&(from_bcb, to_bcb)) {
|
2024-10-05 08:18:47 +00:00
|
|
|
return true;
|
|
|
|
}
|
2024-10-19 07:02:03 +00:00
|
|
|
self.graph.sole_predecessor(to_bcb) == Some(from_bcb)
|
|
|
|
&& self.counters.node_counters[to_bcb].is_some()
|
2024-10-05 08:18:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Determine the set of out-edges that could benefit from being given an expression.
|
2024-10-19 07:02:03 +00:00
|
|
|
let candidate_successors = self.graph.successors[from_bcb]
|
2024-09-13 06:34:49 +00:00
|
|
|
.iter()
|
|
|
|
.copied()
|
2024-10-05 08:18:47 +00:00
|
|
|
.filter(|&to_bcb| !out_edge_has_counter(to_bcb))
|
2024-09-13 06:34:49 +00:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
debug!(?candidate_successors);
|
2020-10-22 21:30:03 +00:00
|
|
|
|
2024-09-13 06:34:49 +00:00
|
|
|
// If there are out-edges without counters, choose one to be given an expression
|
|
|
|
// (computed from this node and the other out-edges) instead of a physical counter.
|
2024-10-23 10:02:18 +00:00
|
|
|
let Some(target_bcb) = self.choose_out_edge_for_expression(from_bcb, &candidate_successors)
|
2024-09-13 06:34:49 +00:00
|
|
|
else {
|
|
|
|
return;
|
|
|
|
};
|
2020-10-30 23:09:05 +00:00
|
|
|
|
2024-09-10 10:44:15 +00:00
|
|
|
// For each out-edge other than the one that was chosen to get an expression,
|
2024-10-16 10:25:09 +00:00
|
|
|
// ensure that it has a counter (existing counter/expression or a new counter).
|
2024-09-11 07:16:14 +00:00
|
|
|
let other_out_edge_counters = successors
|
|
|
|
.iter()
|
|
|
|
.copied()
|
|
|
|
// Skip the chosen edge, since we'll calculate its count from this sum.
|
2024-10-19 07:02:03 +00:00
|
|
|
.filter(|&edge_target_bcb| edge_target_bcb != target_bcb)
|
2024-09-11 07:16:14 +00:00
|
|
|
.map(|to_bcb| self.get_or_make_edge_counter(from_bcb, to_bcb))
|
|
|
|
.collect::<Vec<_>>();
|
2023-10-31 06:44:26 +00:00
|
|
|
|
2024-09-10 10:44:15 +00:00
|
|
|
// Now create an expression for the chosen edge, by taking the counter
|
|
|
|
// for its source node and subtracting the sum of its sibling out-edges.
|
2024-10-16 10:25:09 +00:00
|
|
|
let expression = self.counters.make_subtracted_sum(node_counter, &other_out_edge_counters);
|
2024-09-10 10:44:15 +00:00
|
|
|
|
2024-10-19 07:02:03 +00:00
|
|
|
debug!("{target_bcb:?} gets an expression: {expression:?}");
|
|
|
|
self.counters.set_edge_counter(from_bcb, target_bcb, expression);
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2023-10-29 09:50:47 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2024-09-10 10:48:28 +00:00
|
|
|
fn get_or_make_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter {
|
2020-10-30 23:09:05 +00:00
|
|
|
// If the BCB already has a counter, return it.
|
2024-10-19 07:02:03 +00:00
|
|
|
if let Some(counter) = self.counters.node_counters[bcb] {
|
|
|
|
debug!("{bcb:?} already has a counter: {counter:?}");
|
|
|
|
return counter;
|
2020-10-30 23:09:05 +00:00
|
|
|
}
|
|
|
|
|
2024-10-05 08:18:47 +00:00
|
|
|
let counter = self.make_node_counter_inner(bcb);
|
2024-10-19 07:02:03 +00:00
|
|
|
self.counters.set_node_counter(bcb, counter)
|
2024-10-05 08:18:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn make_node_counter_inner(&mut self, bcb: BasicCoverageBlock) -> BcbCounter {
|
|
|
|
// If the node's sole in-edge already has a counter, use that.
|
2024-10-19 07:02:03 +00:00
|
|
|
if let Some(sole_pred) = self.graph.sole_predecessor(bcb)
|
|
|
|
&& let Some(&edge_counter) = self.counters.edge_counters.get(&(sole_pred, bcb))
|
2024-10-05 08:18:47 +00:00
|
|
|
{
|
|
|
|
return edge_counter;
|
|
|
|
}
|
|
|
|
|
2024-10-19 07:02:03 +00:00
|
|
|
let predecessors = self.graph.predecessors[bcb].as_slice();
|
2024-09-15 01:29:38 +00:00
|
|
|
|
|
|
|
// Handle cases where we can't compute a node's count from its in-edges:
|
|
|
|
// - START_BCB has no in-edges, so taking the sum would panic (or be wrong).
|
|
|
|
// - For nodes with one in-edge, or that directly loop to themselves,
|
|
|
|
// trying to get the in-edge counts would require this node's counter,
|
|
|
|
// leading to infinite recursion.
|
|
|
|
if predecessors.len() <= 1 || predecessors.contains(&bcb) {
|
|
|
|
debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor");
|
2024-12-01 04:42:59 +00:00
|
|
|
let counter = self.counters.make_phys_counter(Site::Node { bcb });
|
2024-10-05 08:18:47 +00:00
|
|
|
debug!(?bcb, ?counter, "node gets a physical counter");
|
|
|
|
return counter;
|
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.
|
2024-09-15 01:29:38 +00:00
|
|
|
let in_edge_counters = predecessors
|
2024-09-11 07:16:14 +00:00
|
|
|
.iter()
|
|
|
|
.copied()
|
|
|
|
.map(|from_bcb| self.get_or_make_edge_counter(from_bcb, bcb))
|
|
|
|
.collect::<Vec<_>>();
|
2024-10-19 07:02:03 +00:00
|
|
|
let sum_of_in_edges: BcbCounter =
|
|
|
|
self.counters.make_sum(&in_edge_counters).expect("there must be at least one in-edge");
|
2023-10-31 06:44:26 +00:00
|
|
|
|
|
|
|
debug!("{bcb:?} gets a new counter (sum of predecessor counters): {sum_of_in_edges:?}");
|
2024-10-05 08:18:47 +00:00
|
|
|
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))]
|
2024-09-10 10:48:28 +00:00
|
|
|
fn get_or_make_edge_counter(
|
2020-10-22 21:30:03 +00:00
|
|
|
&mut self,
|
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
to_bcb: BasicCoverageBlock,
|
2024-10-05 08:18:47 +00:00
|
|
|
) -> BcbCounter {
|
|
|
|
// If the edge already has a counter, return it.
|
2024-10-19 07:02:03 +00:00
|
|
|
if let Some(&counter) = self.counters.edge_counters.get(&(from_bcb, to_bcb)) {
|
|
|
|
debug!("Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter:?}");
|
|
|
|
return counter;
|
2024-10-05 08:18:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let counter = self.make_edge_counter_inner(from_bcb, to_bcb);
|
2024-10-19 07:02:03 +00:00
|
|
|
self.counters.set_edge_counter(from_bcb, to_bcb, counter)
|
2024-10-05 08:18:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn make_edge_counter_inner(
|
|
|
|
&mut self,
|
|
|
|
from_bcb: BasicCoverageBlock,
|
|
|
|
to_bcb: BasicCoverageBlock,
|
2023-11-01 00:23:27 +00:00
|
|
|
) -> BcbCounter {
|
2024-09-13 10:34:12 +00:00
|
|
|
// If the target node has exactly one in-edge (i.e. this one), then just
|
|
|
|
// use the node's counter, since it will have the same value.
|
2024-10-19 07:02:03 +00:00
|
|
|
if let Some(sole_pred) = self.graph.sole_predecessor(to_bcb) {
|
2024-09-13 10:34:12 +00:00
|
|
|
assert_eq!(sole_pred, from_bcb);
|
|
|
|
// This call must take care not to invoke `get_or_make_edge` for
|
|
|
|
// this edge, since that would result in infinite recursion!
|
2024-09-10 10:48:28 +00:00
|
|
|
return self.get_or_make_node_counter(to_bcb);
|
2023-11-01 00:40:12 +00:00
|
|
|
}
|
|
|
|
|
2024-09-13 05:36:46 +00:00
|
|
|
// If the source node has exactly one out-edge (i.e. this one) and would have
|
|
|
|
// the same execution count as that edge, then just use the node's counter.
|
2024-10-19 07:02:03 +00:00
|
|
|
if let Some(simple_succ) = self.graph.simple_successor(from_bcb) {
|
2024-09-13 05:36:46 +00:00
|
|
|
assert_eq!(simple_succ, to_bcb);
|
2024-09-10 10:48:28 +00:00
|
|
|
return self.get_or_make_node_counter(from_bcb);
|
2020-10-30 23:09:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make a new counter to count this edge.
|
2024-12-01 04:42:59 +00:00
|
|
|
let counter = self.counters.make_phys_counter(Site::Edge { from_bcb, to_bcb });
|
2024-10-05 08:18:47 +00:00
|
|
|
debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter");
|
|
|
|
counter
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2024-09-13 06:34:49 +00:00
|
|
|
/// Given a set of candidate out-edges (represented by their successor node),
|
|
|
|
/// choose one to be given a counter expression instead of a physical counter.
|
2024-09-10 10:44:15 +00:00
|
|
|
fn choose_out_edge_for_expression(
|
2020-10-22 21:30:03 +00:00
|
|
|
&self,
|
2024-10-23 10:02:18 +00:00
|
|
|
from_bcb: BasicCoverageBlock,
|
2024-09-13 06:34:49 +00:00
|
|
|
candidate_successors: &[BasicCoverageBlock],
|
|
|
|
) -> Option<BasicCoverageBlock> {
|
|
|
|
// Try to find a candidate that leads back to the top of a loop,
|
|
|
|
// because reloop edges tend to be executed more times than loop-exit edges.
|
2024-10-23 10:02:18 +00:00
|
|
|
if let Some(reloop_target) = self.find_good_reloop_edge(from_bcb, &candidate_successors) {
|
2023-11-01 00:40:12 +00:00
|
|
|
debug!("Selecting reloop target {reloop_target:?} to get an expression");
|
2024-09-13 06:34:49 +00:00
|
|
|
return Some(reloop_target);
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
2024-09-10 10:44:15 +00:00
|
|
|
|
2024-09-13 06:34:49 +00:00
|
|
|
// We couldn't identify a "good" edge, so just choose an arbitrary one.
|
|
|
|
let arbitrary_target = candidate_successors.first().copied()?;
|
2024-09-10 10:44:15 +00:00
|
|
|
debug!(?arbitrary_target, "selecting arbitrary out-edge to get an expression");
|
2024-09-13 06:34:49 +00:00
|
|
|
Some(arbitrary_target)
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2024-09-13 06:34:49 +00:00
|
|
|
/// Given a set of candidate out-edges (represented by their successor node),
|
|
|
|
/// tries to find one that leads back to the top of a loop.
|
|
|
|
///
|
|
|
|
/// Reloop edges are good candidates for counter expressions, because they
|
|
|
|
/// will tend to be executed more times than a loop-exit edge, so it's nice
|
|
|
|
/// for them to be able to avoid a physical counter increment.
|
2024-09-10 10:44:15 +00:00
|
|
|
fn find_good_reloop_edge(
|
2020-10-22 21:30:03 +00:00
|
|
|
&self,
|
2024-10-23 10:02:18 +00:00
|
|
|
from_bcb: BasicCoverageBlock,
|
2024-09-13 06:34:49 +00:00
|
|
|
candidate_successors: &[BasicCoverageBlock],
|
2023-11-01 00:40:12 +00:00
|
|
|
) -> Option<BasicCoverageBlock> {
|
2024-09-13 06:34:49 +00:00
|
|
|
// If there are no candidates, avoid iterating over the loop stack.
|
|
|
|
if candidate_successors.is_empty() {
|
|
|
|
return None;
|
|
|
|
}
|
2023-11-01 00:40:12 +00:00
|
|
|
|
2023-10-12 06:41:11 +00:00
|
|
|
// Consider each loop on the current traversal context stack, top-down.
|
2024-10-23 10:02:18 +00:00
|
|
|
for loop_header_node in self.graph.loop_headers_containing(from_bcb) {
|
2024-09-13 06:34:49 +00:00
|
|
|
// Try to find a candidate edge that doesn't exit this loop.
|
|
|
|
for &target_bcb in candidate_successors {
|
2024-09-10 10:44:15 +00:00
|
|
|
// An edge is a reloop edge if its target dominates any BCB that has
|
|
|
|
// an edge back to the loop header. (Otherwise it's an exit edge.)
|
2024-10-23 10:02:18 +00:00
|
|
|
let is_reloop_edge = self
|
|
|
|
.graph
|
|
|
|
.reloop_predecessors(loop_header_node)
|
|
|
|
.any(|reloop_bcb| self.graph.dominates(target_bcb, reloop_bcb));
|
2024-09-10 10:44:15 +00:00
|
|
|
if is_reloop_edge {
|
2024-09-13 06:34:49 +00:00
|
|
|
// We found a good out-edge to be given an expression.
|
|
|
|
return Some(target_bcb);
|
2020-10-22 21:30:03 +00:00
|
|
|
}
|
|
|
|
}
|
2023-10-12 06:41:11 +00:00
|
|
|
|
2024-09-13 06:34:49 +00:00
|
|
|
// All of the candidate edges exit this loop, so keep looking
|
|
|
|
// for a good reloop edge 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
|
|
|
}
|
|
|
|
}
|