2023-10-10 15:44:33 +11:00
|
|
|
use rustc_data_structures::captures::Captures;
|
2023-05-17 10:37:29 +00:00
|
|
|
use rustc_data_structures::graph::dominators::{self, Dominators};
|
2020-10-22 14:30:03 -07:00
|
|
|
use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode};
|
2020-10-23 00:45:07 -07:00
|
|
|
use rustc_index::bit_set::BitSet;
|
2023-04-19 10:57:17 +00:00
|
|
|
use rustc_index::{IndexSlice, IndexVec};
|
2023-10-10 15:09:04 +11:00
|
|
|
use rustc_middle::mir::{self, BasicBlock, TerminatorKind};
|
2020-10-22 23:11:38 -07:00
|
|
|
|
2023-01-21 00:00:00 +00:00
|
|
|
use std::cmp::Ordering;
|
2023-10-12 12:32:21 +11:00
|
|
|
use std::collections::VecDeque;
|
2020-10-22 23:11:38 -07:00
|
|
|
use std::ops::{Index, IndexMut};
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2020-10-22 23:11:38 -07:00
|
|
|
/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
|
2023-06-29 16:50:52 +10:00
|
|
|
/// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s.
|
2020-11-02 21:32:48 -08:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) struct CoverageGraph {
|
2020-10-22 23:11:38 -07:00
|
|
|
bcbs: IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
|
|
|
|
bb_to_bcb: IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
|
|
|
|
pub successors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
|
|
|
pub predecessors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
|
|
|
dominators: Option<Dominators<BasicCoverageBlock>>,
|
2020-10-23 00:45:07 -07:00
|
|
|
}
|
|
|
|
|
2020-10-22 23:11:38 -07:00
|
|
|
impl CoverageGraph {
|
2021-12-06 00:48:37 -08:00
|
|
|
pub fn from_mir(mir_body: &mir::Body<'_>) -> Self {
|
2020-10-22 23:11:38 -07:00
|
|
|
let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body);
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2020-10-22 23:11:38 -07:00
|
|
|
// Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock
|
|
|
|
// equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the
|
2020-12-11 18:28:37 -08:00
|
|
|
// each predecessor of a BCB leader_bb should be in a unique BCB. It is possible for a
|
|
|
|
// `SwitchInt` to have multiple targets to the same destination `BasicBlock`, so
|
|
|
|
// de-duplication is required. This is done without reordering the successors.
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2023-04-03 14:22:09 -07:00
|
|
|
let mut seen = IndexVec::from_elem(false, &bcbs);
|
2020-10-22 23:11:38 -07:00
|
|
|
let successors = IndexVec::from_fn_n(
|
|
|
|
|bcb| {
|
2020-12-11 18:28:37 -08:00
|
|
|
for b in seen.iter_mut() {
|
|
|
|
*b = false;
|
|
|
|
}
|
2020-10-22 23:11:38 -07:00
|
|
|
let bcb_data = &bcbs[bcb];
|
2020-12-11 18:28:37 -08:00
|
|
|
let mut bcb_successors = Vec::new();
|
2023-11-21 20:07:32 +01:00
|
|
|
for successor in bcb_filtered_successors(mir_body, bcb_data.last_bb())
|
2023-10-10 15:09:04 +11:00
|
|
|
.filter_map(|successor_bb| bb_to_bcb[successor_bb])
|
2020-12-11 18:28:37 -08:00
|
|
|
{
|
|
|
|
if !seen[successor] {
|
|
|
|
seen[successor] = true;
|
|
|
|
bcb_successors.push(successor);
|
|
|
|
}
|
|
|
|
}
|
2020-10-22 23:11:38 -07:00
|
|
|
bcb_successors
|
|
|
|
},
|
|
|
|
bcbs.len(),
|
|
|
|
);
|
|
|
|
|
2023-04-03 14:22:09 -07:00
|
|
|
let mut predecessors = IndexVec::from_elem(Vec::new(), &bcbs);
|
2020-10-22 23:11:38 -07:00
|
|
|
for (bcb, bcb_successors) in successors.iter_enumerated() {
|
|
|
|
for &successor in bcb_successors {
|
|
|
|
predecessors[successor].push(bcb);
|
|
|
|
}
|
|
|
|
}
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2023-05-17 10:37:29 +00:00
|
|
|
let mut basic_coverage_blocks =
|
|
|
|
Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None };
|
|
|
|
let dominators = dominators::dominators(&basic_coverage_blocks);
|
2020-10-22 23:11:38 -07:00
|
|
|
basic_coverage_blocks.dominators = Some(dominators);
|
2023-11-05 22:21:22 +11:00
|
|
|
|
|
|
|
// The coverage graph's entry-point node (bcb0) always starts with bb0,
|
|
|
|
// which never has predecessors. Any other blocks merged into bcb0 can't
|
|
|
|
// have multiple (coverage-relevant) predecessors, so bcb0 always has
|
|
|
|
// zero in-edges.
|
|
|
|
assert!(basic_coverage_blocks[START_BCB].leader_bb() == mir::START_BLOCK);
|
|
|
|
assert!(basic_coverage_blocks.predecessors[START_BCB].is_empty());
|
|
|
|
|
2020-10-23 00:45:07 -07:00
|
|
|
basic_coverage_blocks
|
|
|
|
}
|
|
|
|
|
2020-10-22 23:11:38 -07:00
|
|
|
fn compute_basic_coverage_blocks(
|
2021-12-06 00:48:37 -08:00
|
|
|
mir_body: &mir::Body<'_>,
|
2020-10-22 23:11:38 -07:00
|
|
|
) -> (
|
|
|
|
IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
|
|
|
|
IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
|
|
|
|
) {
|
2022-07-04 00:00:00 +00:00
|
|
|
let num_basic_blocks = mir_body.basic_blocks.len();
|
2020-10-22 23:11:38 -07:00
|
|
|
let mut bcbs = IndexVec::with_capacity(num_basic_blocks);
|
|
|
|
let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks);
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2020-10-22 23:11:38 -07:00
|
|
|
// Walk the MIR CFG using a Preorder traversal, which starts from `START_BLOCK` and follows
|
2020-10-23 00:45:07 -07:00
|
|
|
// each block terminator's `successors()`. Coverage spans must map to actual source code,
|
2020-10-22 23:11:38 -07:00
|
|
|
// so compiler generated blocks and paths can be ignored. To that end, the CFG traversal
|
2020-10-23 00:45:07 -07:00
|
|
|
// intentionally omits unwind paths.
|
2020-10-30 16:09:05 -07:00
|
|
|
// FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and
|
|
|
|
// `catch_unwind()` handlers.
|
2020-10-22 23:11:38 -07:00
|
|
|
|
|
|
|
let mut basic_blocks = Vec::new();
|
2023-10-10 15:44:33 +11:00
|
|
|
for bb in short_circuit_preorder(mir_body, bcb_filtered_successors) {
|
2020-10-22 23:11:38 -07:00
|
|
|
if let Some(last) = basic_blocks.last() {
|
2022-07-05 00:00:00 +00:00
|
|
|
let predecessors = &mir_body.basic_blocks.predecessors()[bb];
|
2020-10-23 00:45:07 -07:00
|
|
|
if predecessors.len() > 1 || !predecessors.contains(last) {
|
|
|
|
// The `bb` has more than one _incoming_ edge, and should start its own
|
2020-10-22 23:11:38 -07:00
|
|
|
// `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet
|
|
|
|
// include `bb`; it contains a sequence of one or more sequential basic_blocks
|
|
|
|
// with no intermediate branches in or out. Save these as a new
|
|
|
|
// `BasicCoverageBlockData` before starting the new one.)
|
|
|
|
Self::add_basic_coverage_block(
|
|
|
|
&mut bcbs,
|
|
|
|
&mut bb_to_bcb,
|
|
|
|
basic_blocks.split_off(0),
|
|
|
|
);
|
2020-10-23 00:45:07 -07:00
|
|
|
debug!(
|
|
|
|
" because {}",
|
|
|
|
if predecessors.len() > 1 {
|
|
|
|
"predecessors.len() > 1".to_owned()
|
|
|
|
} else {
|
2023-04-09 17:35:02 -04:00
|
|
|
format!("bb {} is not in predecessors: {:?}", bb.index(), predecessors)
|
2020-10-23 00:45:07 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-10-22 23:11:38 -07:00
|
|
|
basic_blocks.push(bb);
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2023-10-10 15:44:33 +11:00
|
|
|
let term = mir_body[bb].terminator();
|
2020-10-23 00:45:07 -07:00
|
|
|
|
|
|
|
match term.kind {
|
|
|
|
TerminatorKind::Return { .. }
|
2023-08-21 09:57:10 +02:00
|
|
|
| TerminatorKind::UnwindTerminate(_)
|
2020-10-23 00:45:07 -07:00
|
|
|
| TerminatorKind::Yield { .. }
|
|
|
|
| TerminatorKind::SwitchInt { .. } => {
|
|
|
|
// The `bb` has more than one _outgoing_ edge, or exits the function. Save the
|
2020-10-22 23:11:38 -07:00
|
|
|
// current sequence of `basic_blocks` gathered to this point, as a new
|
|
|
|
// `BasicCoverageBlockData`.
|
|
|
|
Self::add_basic_coverage_block(
|
|
|
|
&mut bcbs,
|
|
|
|
&mut bb_to_bcb,
|
|
|
|
basic_blocks.split_off(0),
|
|
|
|
);
|
2020-10-23 00:45:07 -07:00
|
|
|
debug!(" because term.kind = {:?}", term.kind);
|
|
|
|
// Note that this condition is based on `TerminatorKind`, even though it
|
|
|
|
// theoretically boils down to `successors().len() != 1`; that is, either zero
|
2022-10-31 01:01:24 +00:00
|
|
|
// (e.g., `Return`, `Terminate`) or multiple successors (e.g., `SwitchInt`), but
|
2020-10-22 23:11:38 -07:00
|
|
|
// since the BCB CFG ignores things like unwind branches (which exist in the
|
|
|
|
// `Terminator`s `successors()` list) checking the number of successors won't
|
|
|
|
// work.
|
2020-10-23 00:45:07 -07:00
|
|
|
}
|
2020-11-30 23:58:08 -08:00
|
|
|
|
|
|
|
// The following `TerminatorKind`s are either not expected outside an unwind branch,
|
|
|
|
// or they should not (under normal circumstances) branch. Coverage graphs are
|
2020-12-01 23:01:26 -08:00
|
|
|
// simplified by assuring coverage results are accurate for program executions that
|
|
|
|
// don't panic.
|
|
|
|
//
|
2020-11-30 23:58:08 -08:00
|
|
|
// Programs that panic and unwind may record slightly inaccurate coverage results
|
|
|
|
// for a coverage region containing the `Terminator` that began the panic. This
|
|
|
|
// is as intended. (See Issue #78544 for a possible future option to support
|
|
|
|
// coverage in test programs that panic.)
|
2020-10-23 00:45:07 -07:00
|
|
|
TerminatorKind::Goto { .. }
|
2023-08-19 13:10:25 +02:00
|
|
|
| TerminatorKind::UnwindResume
|
2020-10-23 00:45:07 -07:00
|
|
|
| TerminatorKind::Unreachable
|
|
|
|
| TerminatorKind::Drop { .. }
|
|
|
|
| TerminatorKind::Call { .. }
|
2023-10-19 16:06:43 +00:00
|
|
|
| TerminatorKind::CoroutineDrop
|
2020-11-30 23:58:08 -08:00
|
|
|
| TerminatorKind::Assert { .. }
|
2020-10-23 00:45:07 -07:00
|
|
|
| TerminatorKind::FalseEdge { .. }
|
|
|
|
| TerminatorKind::FalseUnwind { .. }
|
|
|
|
| TerminatorKind::InlineAsm { .. } => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-22 23:11:38 -07:00
|
|
|
if !basic_blocks.is_empty() {
|
|
|
|
// process any remaining basic_blocks into a final `BasicCoverageBlockData`
|
|
|
|
Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0));
|
|
|
|
debug!(" because the end of the MIR CFG was reached while traversing");
|
|
|
|
}
|
|
|
|
|
|
|
|
(bcbs, bb_to_bcb)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_basic_coverage_block(
|
|
|
|
bcbs: &mut IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
|
2023-03-31 00:32:44 -07:00
|
|
|
bb_to_bcb: &mut IndexSlice<BasicBlock, Option<BasicCoverageBlock>>,
|
2020-10-22 23:11:38 -07:00
|
|
|
basic_blocks: Vec<BasicBlock>,
|
|
|
|
) {
|
2023-03-31 00:32:44 -07:00
|
|
|
let bcb = bcbs.next_index();
|
2020-10-22 23:11:38 -07:00
|
|
|
for &bb in basic_blocks.iter() {
|
|
|
|
bb_to_bcb[bb] = Some(bcb);
|
2020-10-23 00:45:07 -07:00
|
|
|
}
|
2020-10-22 23:11:38 -07:00
|
|
|
let bcb_data = BasicCoverageBlockData::from(basic_blocks);
|
|
|
|
debug!("adding bcb{}: {:?}", bcb.index(), bcb_data);
|
|
|
|
bcbs.push(bcb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn iter_enumerated(
|
|
|
|
&self,
|
|
|
|
) -> impl Iterator<Item = (BasicCoverageBlock, &BasicCoverageBlockData)> {
|
|
|
|
self.bcbs.iter_enumerated()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
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 }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2023-01-20 00:00:00 +00:00
|
|
|
pub fn dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
|
|
|
|
self.dominators.as_ref().unwrap().dominates(dom, node)
|
2020-10-22 23:11:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2023-09-17 14:21:19 +10:00
|
|
|
pub fn cmp_in_dominator_order(&self, a: BasicCoverageBlock, b: BasicCoverageBlock) -> Ordering {
|
|
|
|
self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b)
|
2020-10-22 23:11:38 -07:00
|
|
|
}
|
2023-10-30 20:54:19 +11:00
|
|
|
|
|
|
|
/// Returns true if the given node has 2 or more in-edges, i.e. 2 or more
|
|
|
|
/// predecessors.
|
|
|
|
///
|
|
|
|
/// This property is interesting to code that assigns counters to nodes and
|
|
|
|
/// edges, because if a node _doesn't_ have multiple in-edges, then there's
|
|
|
|
/// no benefit in having a separate counter for its in-edge, because it
|
|
|
|
/// would have the same value as the node's own counter.
|
|
|
|
///
|
|
|
|
/// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]?
|
|
|
|
#[inline(always)]
|
|
|
|
pub(super) fn bcb_has_multiple_in_edges(&self, bcb: BasicCoverageBlock) -> bool {
|
2023-11-05 22:21:22 +11:00
|
|
|
// Even though bcb0 conceptually has an extra virtual in-edge due to
|
|
|
|
// being the entry point, we've already asserted that it has no _other_
|
|
|
|
// in-edges, so there's no possibility of it having _multiple_ in-edges.
|
|
|
|
// (And since its virtual in-edge doesn't exist in the graph, that edge
|
|
|
|
// can't have a separate counter anyway.)
|
2023-10-30 20:54:19 +11:00
|
|
|
self.predecessors[bcb].len() > 1
|
|
|
|
}
|
2020-10-22 23:11:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Index<BasicCoverageBlock> for CoverageGraph {
|
|
|
|
type Output = BasicCoverageBlockData;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData {
|
|
|
|
&self.bcbs[index]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IndexMut<BasicCoverageBlock> for CoverageGraph {
|
|
|
|
#[inline]
|
|
|
|
fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData {
|
|
|
|
&mut self.bcbs[index]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl graph::DirectedGraph for CoverageGraph {
|
|
|
|
type Node = BasicCoverageBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl graph::WithNumNodes for CoverageGraph {
|
|
|
|
#[inline]
|
|
|
|
fn num_nodes(&self) -> usize {
|
|
|
|
self.bcbs.len()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl graph::WithStartNode for CoverageGraph {
|
|
|
|
#[inline]
|
|
|
|
fn start_node(&self) -> Self::Node {
|
|
|
|
self.bcb_from_bb(mir::START_BLOCK)
|
|
|
|
.expect("mir::START_BLOCK should be in a BasicCoverageBlock")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type BcbSuccessors<'graph> = std::slice::Iter<'graph, BasicCoverageBlock>;
|
|
|
|
|
|
|
|
impl<'graph> graph::GraphSuccessors<'graph> for CoverageGraph {
|
|
|
|
type Item = BasicCoverageBlock;
|
|
|
|
type Iter = std::iter::Cloned<BcbSuccessors<'graph>>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl graph::WithSuccessors for CoverageGraph {
|
|
|
|
#[inline]
|
|
|
|
fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
|
|
|
|
self.successors[node].iter().cloned()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-06 00:48:37 -08:00
|
|
|
impl<'graph> graph::GraphPredecessors<'graph> for CoverageGraph {
|
2020-10-22 23:11:38 -07:00
|
|
|
type Item = BasicCoverageBlock;
|
2021-05-07 21:00:03 -04:00
|
|
|
type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicCoverageBlock>>;
|
2020-10-22 23:11:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl graph::WithPredecessors for CoverageGraph {
|
|
|
|
#[inline]
|
|
|
|
fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter {
|
2021-05-07 21:00:03 -04:00
|
|
|
self.predecessors[node].iter().copied()
|
2020-10-23 00:45:07 -07:00
|
|
|
}
|
2020-10-22 23:11:38 -07:00
|
|
|
}
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2020-10-22 23:11:38 -07:00
|
|
|
rustc_index::newtype_index! {
|
2022-02-24 17:16:36 -05:00
|
|
|
/// A node in the control-flow graph of CoverageGraph.
|
2023-11-21 17:35:46 +11:00
|
|
|
#[orderable]
|
2022-12-18 21:37:38 +01:00
|
|
|
#[debug_format = "bcb{}"]
|
2020-11-02 21:32:48 -08:00
|
|
|
pub(super) struct BasicCoverageBlock {
|
2022-12-18 21:47:28 +01:00
|
|
|
const START_BCB = 0;
|
2020-10-23 00:45:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-07 17:41:53 -08:00
|
|
|
/// `BasicCoverageBlockData` holds the data indexed by a `BasicCoverageBlock`.
|
|
|
|
///
|
|
|
|
/// A `BasicCoverageBlock` (BCB) represents the maximal-length sequence of MIR `BasicBlock`s without
|
2020-10-22 23:11:38 -07:00
|
|
|
/// conditional branches, and form a new, simplified, coverage-specific Control Flow Graph, without
|
|
|
|
/// altering the original MIR CFG.
|
|
|
|
///
|
|
|
|
/// Note that running the MIR `SimplifyCfg` transform is not sufficient (and therefore not
|
|
|
|
/// necessary). The BCB-based CFG is a more aggressive simplification. For example:
|
|
|
|
///
|
|
|
|
/// * The BCB CFG ignores (trims) branches not relevant to coverage, such as unwind-related code,
|
|
|
|
/// that is injected by the Rust compiler but has no physical source code to count. This also
|
|
|
|
/// means a BasicBlock with a `Call` terminator can be merged into its primary successor target
|
2020-10-30 16:09:05 -07:00
|
|
|
/// block, in the same BCB. (But, note: Issue #78544: "MIR InstrumentCoverage: Improve coverage
|
|
|
|
/// of `#[should_panic]` tests and `catch_unwind()` handlers")
|
2020-10-22 23:11:38 -07:00
|
|
|
/// * Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are
|
|
|
|
/// not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as
|
|
|
|
/// a `Goto`, and merged with its successor into the same BCB.
|
|
|
|
///
|
2023-09-17 22:22:21 +10:00
|
|
|
/// Each BCB with at least one computed coverage span will have no more than one `Counter`.
|
2020-10-22 23:11:38 -07:00
|
|
|
/// In some cases, a BCB's execution count can be computed by `Expression`. Additional
|
2023-09-17 22:22:21 +10:00
|
|
|
/// disjoint coverage spans in a BCB can also be counted by `Expression` (by adding `ZERO`
|
2020-10-22 23:11:38 -07:00
|
|
|
/// to the BCB's primary counter or expression).
|
|
|
|
///
|
|
|
|
/// The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based
|
2023-01-20 00:00:00 +00:00
|
|
|
/// queries (`dominates()`, `predecessors`, `successors`, etc.) have branch (control flow)
|
2020-10-22 23:11:38 -07:00
|
|
|
/// significance.
|
|
|
|
#[derive(Debug, Clone)]
|
2020-11-02 21:32:48 -08:00
|
|
|
pub(super) struct BasicCoverageBlockData {
|
2020-10-22 23:11:38 -07:00
|
|
|
pub basic_blocks: Vec<BasicBlock>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BasicCoverageBlockData {
|
|
|
|
pub fn from(basic_blocks: Vec<BasicBlock>) -> Self {
|
|
|
|
assert!(basic_blocks.len() > 0);
|
2023-06-29 16:50:52 +10:00
|
|
|
Self { basic_blocks }
|
2020-10-22 23:11:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn leader_bb(&self) -> BasicBlock {
|
|
|
|
self.basic_blocks[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn last_bb(&self) -> BasicBlock {
|
|
|
|
*self.basic_blocks.last().unwrap()
|
|
|
|
}
|
|
|
|
}
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2023-10-10 15:09:04 +11:00
|
|
|
// Returns the subset of a block's successors that are relevant to the coverage
|
|
|
|
// graph, i.e. those that do not represent unwinds or unreachable branches.
|
2020-10-30 16:09:05 -07:00
|
|
|
// FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and
|
|
|
|
// `catch_unwind()` handlers.
|
2020-10-22 23:11:38 -07:00
|
|
|
fn bcb_filtered_successors<'a, 'tcx>(
|
2022-05-22 12:48:19 -07:00
|
|
|
body: &'a mir::Body<'tcx>,
|
2023-10-10 15:09:04 +11:00
|
|
|
bb: BasicBlock,
|
|
|
|
) -> impl Iterator<Item = BasicBlock> + Captures<'a> + Captures<'tcx> {
|
|
|
|
let terminator = body[bb].terminator();
|
|
|
|
|
|
|
|
let take_n_successors = match terminator.kind {
|
|
|
|
// SwitchInt successors are never unwinds, so all of them should be traversed.
|
|
|
|
TerminatorKind::SwitchInt { .. } => usize::MAX,
|
|
|
|
// For all other kinds, return only the first successor (if any), ignoring any
|
|
|
|
// unwind successors.
|
|
|
|
_ => 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
terminator
|
|
|
|
.successors()
|
|
|
|
.take(take_n_successors)
|
|
|
|
.filter(move |&successor| body[successor].terminator().kind != TerminatorKind::Unreachable)
|
2020-10-23 00:45:07 -07:00
|
|
|
}
|
|
|
|
|
2020-10-22 14:30:03 -07:00
|
|
|
/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the
|
|
|
|
/// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that
|
|
|
|
/// ensures a loop is completely traversed before processing Blocks after the end of the loop.
|
|
|
|
#[derive(Debug)]
|
2020-11-02 21:32:48 -08:00
|
|
|
pub(super) struct TraversalContext {
|
2023-10-11 20:33:26 +11:00
|
|
|
/// BCB with one or more incoming loop backedges, indicating which loop
|
|
|
|
/// this context is for.
|
|
|
|
///
|
|
|
|
/// If `None`, this is the non-loop context for the function as a whole.
|
|
|
|
loop_header: Option<BasicCoverageBlock>,
|
2020-10-22 14:30:03 -07:00
|
|
|
|
2023-10-12 12:32:21 +11:00
|
|
|
/// Worklist of BCBs to be processed in this context.
|
|
|
|
worklist: VecDeque<BasicCoverageBlock>,
|
2020-10-22 14:30:03 -07:00
|
|
|
}
|
|
|
|
|
2023-10-11 17:40:37 +11:00
|
|
|
pub(super) struct TraverseCoverageGraphWithLoops<'a> {
|
|
|
|
basic_coverage_blocks: &'a CoverageGraph,
|
|
|
|
|
2023-10-12 17:41:11 +11:00
|
|
|
backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
|
|
|
context_stack: Vec<TraversalContext>,
|
2020-10-22 14:30:03 -07:00
|
|
|
visited: BitSet<BasicCoverageBlock>,
|
|
|
|
}
|
|
|
|
|
2023-10-11 17:40:37 +11:00
|
|
|
impl<'a> TraverseCoverageGraphWithLoops<'a> {
|
|
|
|
pub(super) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self {
|
2020-10-22 14:30:03 -07:00
|
|
|
let backedges = find_loop_backedges(basic_coverage_blocks);
|
2023-10-12 12:32:21 +11:00
|
|
|
|
|
|
|
let worklist = VecDeque::from([basic_coverage_blocks.start_node()]);
|
2023-10-11 20:33:26 +11:00
|
|
|
let context_stack = vec![TraversalContext { loop_header: None, worklist }];
|
2023-10-12 12:32:21 +11:00
|
|
|
|
2020-10-22 14:30:03 -07:00
|
|
|
// `context_stack` starts with a `TraversalContext` for the main function context (beginning
|
|
|
|
// with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top
|
|
|
|
// of the stack as loops are entered, and popped off of the stack when a loop's worklist is
|
|
|
|
// exhausted.
|
|
|
|
let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes());
|
2023-10-11 17:40:37 +11:00
|
|
|
Self { basic_coverage_blocks, backedges, context_stack, visited }
|
2020-10-22 14:30:03 -07:00
|
|
|
}
|
|
|
|
|
2023-10-12 17:41:11 +11:00
|
|
|
/// For each loop on the loop context stack (top-down), yields a list of BCBs
|
|
|
|
/// within that loop that have an outgoing edge back to the loop header.
|
|
|
|
pub(super) fn reloop_bcbs_per_loop(&self) -> impl Iterator<Item = &[BasicCoverageBlock]> {
|
|
|
|
self.context_stack
|
|
|
|
.iter()
|
|
|
|
.rev()
|
2023-10-11 20:33:26 +11:00
|
|
|
.filter_map(|context| context.loop_header)
|
|
|
|
.map(|header_bcb| self.backedges[header_bcb].as_slice())
|
2023-10-12 17:41:11 +11:00
|
|
|
}
|
|
|
|
|
2023-10-11 17:40:37 +11:00
|
|
|
pub(super) fn next(&mut self) -> Option<BasicCoverageBlock> {
|
2020-10-22 14:30:03 -07:00
|
|
|
debug!(
|
|
|
|
"TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
|
|
|
|
self.context_stack.iter().rev().collect::<Vec<_>>()
|
|
|
|
);
|
2023-03-09 12:53:03 +01:00
|
|
|
|
|
|
|
while let Some(context) = self.context_stack.last_mut() {
|
2023-10-12 12:32:21 +11:00
|
|
|
if let Some(bcb) = context.worklist.pop_front() {
|
2023-10-12 12:59:11 +11:00
|
|
|
if !self.visited.insert(bcb) {
|
|
|
|
debug!("Already visited: {bcb:?}");
|
2023-03-09 12:53:03 +01:00
|
|
|
continue;
|
|
|
|
}
|
2023-10-12 12:59:11 +11:00
|
|
|
debug!("Visiting {bcb:?}");
|
|
|
|
|
|
|
|
if self.backedges[bcb].len() > 0 {
|
|
|
|
debug!("{bcb:?} is a loop header! Start a new TraversalContext...");
|
2023-03-09 12:53:03 +01:00
|
|
|
self.context_stack.push(TraversalContext {
|
2023-10-11 20:33:26 +11:00
|
|
|
loop_header: Some(bcb),
|
2023-10-12 12:32:21 +11:00
|
|
|
worklist: VecDeque::new(),
|
2023-03-09 12:53:03 +01:00
|
|
|
});
|
|
|
|
}
|
2023-10-12 13:31:35 +11:00
|
|
|
self.add_successors_to_worklists(bcb);
|
2023-10-12 12:59:11 +11:00
|
|
|
return Some(bcb);
|
2023-03-09 12:53:03 +01:00
|
|
|
} else {
|
|
|
|
// Strip contexts with empty worklists from the top of the stack
|
2020-10-22 14:30:03 -07:00
|
|
|
self.context_stack.pop();
|
|
|
|
}
|
|
|
|
}
|
2023-03-09 12:53:03 +01:00
|
|
|
|
2020-10-22 14:30:03 -07:00
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2023-10-12 13:31:35 +11:00
|
|
|
pub fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) {
|
|
|
|
let successors = &self.basic_coverage_blocks.successors[bcb];
|
2020-10-22 14:30:03 -07:00
|
|
|
debug!("{:?} has {} successors:", bcb, successors.len());
|
2023-10-12 13:31:35 +11:00
|
|
|
|
2020-10-22 14:30:03 -07:00
|
|
|
for &successor in successors {
|
|
|
|
if successor == bcb {
|
|
|
|
debug!(
|
|
|
|
"{:?} has itself as its own successor. (Note, the compiled code will \
|
|
|
|
generate an infinite loop.)",
|
|
|
|
bcb
|
|
|
|
);
|
|
|
|
// Don't re-add this successor to the worklist. We are already processing it.
|
2023-10-12 13:31:35 +11:00
|
|
|
// FIXME: This claims to skip just the self-successor, but it actually skips
|
|
|
|
// all other successors as well. Does that matter?
|
2020-10-22 14:30:03 -07:00
|
|
|
break;
|
|
|
|
}
|
2023-10-12 13:31:35 +11:00
|
|
|
|
|
|
|
// Add successors of the current BCB to the appropriate context. Successors that
|
|
|
|
// stay within a loop are added to the BCBs context worklist. Successors that
|
|
|
|
// exit the loop (they are not dominated by the loop header) must be reachable
|
|
|
|
// from other BCBs outside the loop, and they will be added to a different
|
|
|
|
// worklist.
|
|
|
|
//
|
|
|
|
// Branching blocks (with more than one successor) must be processed before
|
|
|
|
// blocks with only one successor, to prevent unnecessarily complicating
|
|
|
|
// `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
|
|
|
|
// branching block would have given an `Expression` (or vice versa).
|
|
|
|
|
|
|
|
let context = self
|
|
|
|
.context_stack
|
|
|
|
.iter_mut()
|
|
|
|
.rev()
|
|
|
|
.find(|context| match context.loop_header {
|
|
|
|
Some(loop_header) => {
|
|
|
|
self.basic_coverage_blocks.dominates(loop_header, successor)
|
2020-10-22 14:30:03 -07:00
|
|
|
}
|
2023-10-12 13:31:35 +11:00
|
|
|
None => true,
|
|
|
|
})
|
|
|
|
.unwrap_or_else(|| bug!("should always fall back to the root non-loop context"));
|
|
|
|
debug!("adding to worklist for {:?}", context.loop_header);
|
|
|
|
|
|
|
|
// FIXME: The code below had debug messages claiming to add items to a
|
|
|
|
// particular end of the worklist, but was confused about which end was
|
|
|
|
// which. The existing behaviour has been preserved for now, but it's
|
|
|
|
// unclear what the intended behaviour was.
|
|
|
|
|
|
|
|
if self.basic_coverage_blocks.successors[successor].len() > 1 {
|
|
|
|
context.worklist.push_back(successor);
|
|
|
|
} else {
|
|
|
|
context.worklist.push_front(successor);
|
2020-10-22 14:30:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_complete(&self) -> bool {
|
|
|
|
self.visited.count() == self.visited.domain_size()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unvisited(&self) -> Vec<BasicCoverageBlock> {
|
|
|
|
let mut unvisited_set: BitSet<BasicCoverageBlock> =
|
|
|
|
BitSet::new_filled(self.visited.domain_size());
|
|
|
|
unvisited_set.subtract(&self.visited);
|
|
|
|
unvisited_set.iter().collect::<Vec<_>>()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-02 21:32:48 -08:00
|
|
|
pub(super) fn find_loop_backedges(
|
2020-10-22 14:30:03 -07:00
|
|
|
basic_coverage_blocks: &CoverageGraph,
|
|
|
|
) -> IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>> {
|
|
|
|
let num_bcbs = basic_coverage_blocks.num_nodes();
|
|
|
|
let mut backedges = IndexVec::from_elem_n(Vec::<BasicCoverageBlock>::new(), num_bcbs);
|
|
|
|
|
2020-10-30 16:09:05 -07:00
|
|
|
// Identify loops by their backedges.
|
2020-10-22 14:30:03 -07:00
|
|
|
for (bcb, _) in basic_coverage_blocks.iter_enumerated() {
|
|
|
|
for &successor in &basic_coverage_blocks.successors[bcb] {
|
2023-01-20 00:00:00 +00:00
|
|
|
if basic_coverage_blocks.dominates(successor, bcb) {
|
2020-10-22 14:30:03 -07:00
|
|
|
let loop_header = successor;
|
|
|
|
let backedge_from_bcb = bcb;
|
|
|
|
debug!(
|
|
|
|
"Found BCB backedge: {:?} -> loop_header: {:?}",
|
|
|
|
backedge_from_bcb, loop_header
|
|
|
|
);
|
|
|
|
backedges[loop_header].push(backedge_from_bcb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
backedges
|
|
|
|
}
|
|
|
|
|
2023-10-10 15:44:33 +11:00
|
|
|
fn short_circuit_preorder<'a, 'tcx, F, Iter>(
|
2022-05-22 12:48:19 -07:00
|
|
|
body: &'a mir::Body<'tcx>,
|
2020-10-23 00:45:07 -07:00
|
|
|
filtered_successors: F,
|
2023-10-10 15:44:33 +11:00
|
|
|
) -> impl Iterator<Item = BasicBlock> + Captures<'a> + Captures<'tcx>
|
|
|
|
where
|
2023-10-10 15:09:04 +11:00
|
|
|
F: Fn(&'a mir::Body<'tcx>, BasicBlock) -> Iter,
|
2023-10-10 15:44:33 +11:00
|
|
|
Iter: Iterator<Item = BasicBlock>,
|
2020-10-23 00:45:07 -07:00
|
|
|
{
|
2023-10-10 15:44:33 +11:00
|
|
|
let mut visited = BitSet::new_empty(body.basic_blocks.len());
|
|
|
|
let mut worklist = vec![mir::START_BLOCK];
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2023-10-10 15:44:33 +11:00
|
|
|
std::iter::from_fn(move || {
|
|
|
|
while let Some(bb) = worklist.pop() {
|
|
|
|
if !visited.insert(bb) {
|
2020-10-23 00:45:07 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-10-10 15:09:04 +11:00
|
|
|
worklist.extend(filtered_successors(body, bb));
|
2020-10-23 00:45:07 -07:00
|
|
|
|
2023-10-10 15:44:33 +11:00
|
|
|
return Some(bb);
|
2020-10-23 00:45:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
None
|
2023-10-10 15:44:33 +11:00
|
|
|
})
|
2020-10-23 00:45:07 -07:00
|
|
|
}
|