1 use super::Error;
2
3 use itertools::Itertools;
4 use rustc_data_structures::fx::FxHashMap;
5 use rustc_data_structures::graph::dominators::{self, Dominators};
6 use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode};
7 use rustc_index::bit_set::BitSet;
8 use rustc_index::{IndexSlice, IndexVec};
9 use rustc_middle::mir::coverage::*;
10 use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, TerminatorKind};
11
12 use std::cmp::Ordering;
13 use std::ops::{Index, IndexMut};
14
15 const ID_SEPARATOR: &str = ",";
16
17 /// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
18 /// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s, plus a
19 /// `CoverageKind` counter (to be added by `CoverageCounters::make_bcb_counters`), and an optional
20 /// set of additional counters--if needed--to count incoming edges, if there are more than one.
21 /// (These "edge counters" are eventually converted into new MIR `BasicBlock`s.)
22 #[derive(Debug)]
23 pub(super) struct CoverageGraph {
24 bcbs: IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
25 bb_to_bcb: IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
26 pub successors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
27 pub predecessors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
28 dominators: Option<Dominators<BasicCoverageBlock>>,
29 }
30
31 impl CoverageGraph {
from_mir(mir_body: &mir::Body<'_>) -> Self32 pub fn from_mir(mir_body: &mir::Body<'_>) -> Self {
33 let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body);
34
35 // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock
36 // equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the
37 // each predecessor of a BCB leader_bb should be in a unique BCB. It is possible for a
38 // `SwitchInt` to have multiple targets to the same destination `BasicBlock`, so
39 // de-duplication is required. This is done without reordering the successors.
40
41 let mut seen = IndexVec::from_elem(false, &bcbs);
42 let successors = IndexVec::from_fn_n(
43 |bcb| {
44 for b in seen.iter_mut() {
45 *b = false;
46 }
47 let bcb_data = &bcbs[bcb];
48 let mut bcb_successors = Vec::new();
49 for successor in
50 bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind)
51 .filter_map(|successor_bb| bb_to_bcb[successor_bb])
52 {
53 if !seen[successor] {
54 seen[successor] = true;
55 bcb_successors.push(successor);
56 }
57 }
58 bcb_successors
59 },
60 bcbs.len(),
61 );
62
63 let mut predecessors = IndexVec::from_elem(Vec::new(), &bcbs);
64 for (bcb, bcb_successors) in successors.iter_enumerated() {
65 for &successor in bcb_successors {
66 predecessors[successor].push(bcb);
67 }
68 }
69
70 let mut basic_coverage_blocks =
71 Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None };
72 let dominators = dominators::dominators(&basic_coverage_blocks);
73 basic_coverage_blocks.dominators = Some(dominators);
74 basic_coverage_blocks
75 }
76
compute_basic_coverage_blocks( mir_body: &mir::Body<'_>, ) -> ( IndexVec<BasicCoverageBlock, BasicCoverageBlockData>, IndexVec<BasicBlock, Option<BasicCoverageBlock>>, )77 fn compute_basic_coverage_blocks(
78 mir_body: &mir::Body<'_>,
79 ) -> (
80 IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
81 IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
82 ) {
83 let num_basic_blocks = mir_body.basic_blocks.len();
84 let mut bcbs = IndexVec::with_capacity(num_basic_blocks);
85 let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks);
86
87 // Walk the MIR CFG using a Preorder traversal, which starts from `START_BLOCK` and follows
88 // each block terminator's `successors()`. Coverage spans must map to actual source code,
89 // so compiler generated blocks and paths can be ignored. To that end, the CFG traversal
90 // intentionally omits unwind paths.
91 // FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and
92 // `catch_unwind()` handlers.
93 let mir_cfg_without_unwind = ShortCircuitPreorder::new(&mir_body, bcb_filtered_successors);
94
95 let mut basic_blocks = Vec::new();
96 for (bb, data) in mir_cfg_without_unwind {
97 if let Some(last) = basic_blocks.last() {
98 let predecessors = &mir_body.basic_blocks.predecessors()[bb];
99 if predecessors.len() > 1 || !predecessors.contains(last) {
100 // The `bb` has more than one _incoming_ edge, and should start its own
101 // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet
102 // include `bb`; it contains a sequence of one or more sequential basic_blocks
103 // with no intermediate branches in or out. Save these as a new
104 // `BasicCoverageBlockData` before starting the new one.)
105 Self::add_basic_coverage_block(
106 &mut bcbs,
107 &mut bb_to_bcb,
108 basic_blocks.split_off(0),
109 );
110 debug!(
111 " because {}",
112 if predecessors.len() > 1 {
113 "predecessors.len() > 1".to_owned()
114 } else {
115 format!("bb {} is not in predecessors: {:?}", bb.index(), predecessors)
116 }
117 );
118 }
119 }
120 basic_blocks.push(bb);
121
122 let term = data.terminator();
123
124 match term.kind {
125 TerminatorKind::Return { .. }
126 | TerminatorKind::Terminate
127 | TerminatorKind::Yield { .. }
128 | TerminatorKind::SwitchInt { .. } => {
129 // The `bb` has more than one _outgoing_ edge, or exits the function. Save the
130 // current sequence of `basic_blocks` gathered to this point, as a new
131 // `BasicCoverageBlockData`.
132 Self::add_basic_coverage_block(
133 &mut bcbs,
134 &mut bb_to_bcb,
135 basic_blocks.split_off(0),
136 );
137 debug!(" because term.kind = {:?}", term.kind);
138 // Note that this condition is based on `TerminatorKind`, even though it
139 // theoretically boils down to `successors().len() != 1`; that is, either zero
140 // (e.g., `Return`, `Terminate`) or multiple successors (e.g., `SwitchInt`), but
141 // since the BCB CFG ignores things like unwind branches (which exist in the
142 // `Terminator`s `successors()` list) checking the number of successors won't
143 // work.
144 }
145
146 // The following `TerminatorKind`s are either not expected outside an unwind branch,
147 // or they should not (under normal circumstances) branch. Coverage graphs are
148 // simplified by assuring coverage results are accurate for program executions that
149 // don't panic.
150 //
151 // Programs that panic and unwind may record slightly inaccurate coverage results
152 // for a coverage region containing the `Terminator` that began the panic. This
153 // is as intended. (See Issue #78544 for a possible future option to support
154 // coverage in test programs that panic.)
155 TerminatorKind::Goto { .. }
156 | TerminatorKind::Resume
157 | TerminatorKind::Unreachable
158 | TerminatorKind::Drop { .. }
159 | TerminatorKind::Call { .. }
160 | TerminatorKind::GeneratorDrop
161 | TerminatorKind::Assert { .. }
162 | TerminatorKind::FalseEdge { .. }
163 | TerminatorKind::FalseUnwind { .. }
164 | TerminatorKind::InlineAsm { .. } => {}
165 }
166 }
167
168 if !basic_blocks.is_empty() {
169 // process any remaining basic_blocks into a final `BasicCoverageBlockData`
170 Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0));
171 debug!(" because the end of the MIR CFG was reached while traversing");
172 }
173
174 (bcbs, bb_to_bcb)
175 }
176
add_basic_coverage_block( bcbs: &mut IndexVec<BasicCoverageBlock, BasicCoverageBlockData>, bb_to_bcb: &mut IndexSlice<BasicBlock, Option<BasicCoverageBlock>>, basic_blocks: Vec<BasicBlock>, )177 fn add_basic_coverage_block(
178 bcbs: &mut IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
179 bb_to_bcb: &mut IndexSlice<BasicBlock, Option<BasicCoverageBlock>>,
180 basic_blocks: Vec<BasicBlock>,
181 ) {
182 let bcb = bcbs.next_index();
183 for &bb in basic_blocks.iter() {
184 bb_to_bcb[bb] = Some(bcb);
185 }
186 let bcb_data = BasicCoverageBlockData::from(basic_blocks);
187 debug!("adding bcb{}: {:?}", bcb.index(), bcb_data);
188 bcbs.push(bcb_data);
189 }
190
191 #[inline(always)]
iter_enumerated( &self, ) -> impl Iterator<Item = (BasicCoverageBlock, &BasicCoverageBlockData)>192 pub fn iter_enumerated(
193 &self,
194 ) -> impl Iterator<Item = (BasicCoverageBlock, &BasicCoverageBlockData)> {
195 self.bcbs.iter_enumerated()
196 }
197
198 #[inline(always)]
iter_enumerated_mut( &mut self, ) -> impl Iterator<Item = (BasicCoverageBlock, &mut BasicCoverageBlockData)>199 pub fn iter_enumerated_mut(
200 &mut self,
201 ) -> impl Iterator<Item = (BasicCoverageBlock, &mut BasicCoverageBlockData)> {
202 self.bcbs.iter_enumerated_mut()
203 }
204
205 #[inline(always)]
bcb_from_bb(&self, bb: BasicBlock) -> Option<BasicCoverageBlock>206 pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option<BasicCoverageBlock> {
207 if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None }
208 }
209
210 #[inline(always)]
dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool211 pub fn dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
212 self.dominators.as_ref().unwrap().dominates(dom, node)
213 }
214
215 #[inline(always)]
rank_partial_cmp( &self, a: BasicCoverageBlock, b: BasicCoverageBlock, ) -> Option<Ordering>216 pub fn rank_partial_cmp(
217 &self,
218 a: BasicCoverageBlock,
219 b: BasicCoverageBlock,
220 ) -> Option<Ordering> {
221 self.dominators.as_ref().unwrap().rank_partial_cmp(a, b)
222 }
223 }
224
225 impl Index<BasicCoverageBlock> for CoverageGraph {
226 type Output = BasicCoverageBlockData;
227
228 #[inline]
index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData229 fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData {
230 &self.bcbs[index]
231 }
232 }
233
234 impl IndexMut<BasicCoverageBlock> for CoverageGraph {
235 #[inline]
index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData236 fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData {
237 &mut self.bcbs[index]
238 }
239 }
240
241 impl graph::DirectedGraph for CoverageGraph {
242 type Node = BasicCoverageBlock;
243 }
244
245 impl graph::WithNumNodes for CoverageGraph {
246 #[inline]
num_nodes(&self) -> usize247 fn num_nodes(&self) -> usize {
248 self.bcbs.len()
249 }
250 }
251
252 impl graph::WithStartNode for CoverageGraph {
253 #[inline]
start_node(&self) -> Self::Node254 fn start_node(&self) -> Self::Node {
255 self.bcb_from_bb(mir::START_BLOCK)
256 .expect("mir::START_BLOCK should be in a BasicCoverageBlock")
257 }
258 }
259
260 type BcbSuccessors<'graph> = std::slice::Iter<'graph, BasicCoverageBlock>;
261
262 impl<'graph> graph::GraphSuccessors<'graph> for CoverageGraph {
263 type Item = BasicCoverageBlock;
264 type Iter = std::iter::Cloned<BcbSuccessors<'graph>>;
265 }
266
267 impl graph::WithSuccessors for CoverageGraph {
268 #[inline]
successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter269 fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter {
270 self.successors[node].iter().cloned()
271 }
272 }
273
274 impl<'graph> graph::GraphPredecessors<'graph> for CoverageGraph {
275 type Item = BasicCoverageBlock;
276 type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicCoverageBlock>>;
277 }
278
279 impl graph::WithPredecessors for CoverageGraph {
280 #[inline]
predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter281 fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter {
282 self.predecessors[node].iter().copied()
283 }
284 }
285
286 rustc_index::newtype_index! {
287 /// A node in the control-flow graph of CoverageGraph.
288 #[debug_format = "bcb{}"]
289 pub(super) struct BasicCoverageBlock {
290 const START_BCB = 0;
291 }
292 }
293
294 /// `BasicCoverageBlockData` holds the data indexed by a `BasicCoverageBlock`.
295 ///
296 /// A `BasicCoverageBlock` (BCB) represents the maximal-length sequence of MIR `BasicBlock`s without
297 /// conditional branches, and form a new, simplified, coverage-specific Control Flow Graph, without
298 /// altering the original MIR CFG.
299 ///
300 /// Note that running the MIR `SimplifyCfg` transform is not sufficient (and therefore not
301 /// necessary). The BCB-based CFG is a more aggressive simplification. For example:
302 ///
303 /// * The BCB CFG ignores (trims) branches not relevant to coverage, such as unwind-related code,
304 /// that is injected by the Rust compiler but has no physical source code to count. This also
305 /// means a BasicBlock with a `Call` terminator can be merged into its primary successor target
306 /// block, in the same BCB. (But, note: Issue #78544: "MIR InstrumentCoverage: Improve coverage
307 /// of `#[should_panic]` tests and `catch_unwind()` handlers")
308 /// * Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are
309 /// not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as
310 /// a `Goto`, and merged with its successor into the same BCB.
311 ///
312 /// Each BCB with at least one computed `CoverageSpan` will have no more than one `Counter`.
313 /// In some cases, a BCB's execution count can be computed by `Expression`. Additional
314 /// disjoint `CoverageSpan`s in a BCB can also be counted by `Expression` (by adding `ZERO`
315 /// to the BCB's primary counter or expression).
316 ///
317 /// The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based
318 /// queries (`dominates()`, `predecessors`, `successors`, etc.) have branch (control flow)
319 /// significance.
320 #[derive(Debug, Clone)]
321 pub(super) struct BasicCoverageBlockData {
322 pub basic_blocks: Vec<BasicBlock>,
323 pub counter_kind: Option<CoverageKind>,
324 edge_from_bcbs: Option<FxHashMap<BasicCoverageBlock, CoverageKind>>,
325 }
326
327 impl BasicCoverageBlockData {
from(basic_blocks: Vec<BasicBlock>) -> Self328 pub fn from(basic_blocks: Vec<BasicBlock>) -> Self {
329 assert!(basic_blocks.len() > 0);
330 Self { basic_blocks, counter_kind: None, edge_from_bcbs: None }
331 }
332
333 #[inline(always)]
leader_bb(&self) -> BasicBlock334 pub fn leader_bb(&self) -> BasicBlock {
335 self.basic_blocks[0]
336 }
337
338 #[inline(always)]
last_bb(&self) -> BasicBlock339 pub fn last_bb(&self) -> BasicBlock {
340 *self.basic_blocks.last().unwrap()
341 }
342
343 #[inline(always)]
terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx>344 pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx> {
345 &mir_body[self.last_bb()].terminator()
346 }
347
set_counter( &mut self, counter_kind: CoverageKind, ) -> Result<ExpressionOperandId, Error>348 pub fn set_counter(
349 &mut self,
350 counter_kind: CoverageKind,
351 ) -> Result<ExpressionOperandId, Error> {
352 debug_assert!(
353 // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
354 // have an expression (to be injected into an existing `BasicBlock` represented by this
355 // `BasicCoverageBlock`).
356 self.edge_from_bcbs.is_none() || counter_kind.is_expression(),
357 "attempt to add a `Counter` to a BCB target with existing incoming edge counters"
358 );
359 let operand = counter_kind.as_operand_id();
360 if let Some(replaced) = self.counter_kind.replace(counter_kind) {
361 Error::from_string(format!(
362 "attempt to set a BasicCoverageBlock coverage counter more than once; \
363 {:?} already had counter {:?}",
364 self, replaced,
365 ))
366 } else {
367 Ok(operand)
368 }
369 }
370
371 #[inline(always)]
counter(&self) -> Option<&CoverageKind>372 pub fn counter(&self) -> Option<&CoverageKind> {
373 self.counter_kind.as_ref()
374 }
375
376 #[inline(always)]
take_counter(&mut self) -> Option<CoverageKind>377 pub fn take_counter(&mut self) -> Option<CoverageKind> {
378 self.counter_kind.take()
379 }
380
set_edge_counter_from( &mut self, from_bcb: BasicCoverageBlock, counter_kind: CoverageKind, ) -> Result<ExpressionOperandId, Error>381 pub fn set_edge_counter_from(
382 &mut self,
383 from_bcb: BasicCoverageBlock,
384 counter_kind: CoverageKind,
385 ) -> Result<ExpressionOperandId, Error> {
386 if level_enabled!(tracing::Level::DEBUG) {
387 // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
388 // have an expression (to be injected into an existing `BasicBlock` represented by this
389 // `BasicCoverageBlock`).
390 if self.counter_kind.as_ref().is_some_and(|c| !c.is_expression()) {
391 return Error::from_string(format!(
392 "attempt to add an incoming edge counter from {:?} when the target BCB already \
393 has a `Counter`",
394 from_bcb
395 ));
396 }
397 }
398 let operand = counter_kind.as_operand_id();
399 if let Some(replaced) =
400 self.edge_from_bcbs.get_or_insert_default().insert(from_bcb, counter_kind)
401 {
402 Error::from_string(format!(
403 "attempt to set an edge counter more than once; from_bcb: \
404 {:?} already had counter {:?}",
405 from_bcb, replaced,
406 ))
407 } else {
408 Ok(operand)
409 }
410 }
411
412 #[inline]
edge_counter_from(&self, from_bcb: BasicCoverageBlock) -> Option<&CoverageKind>413 pub fn edge_counter_from(&self, from_bcb: BasicCoverageBlock) -> Option<&CoverageKind> {
414 if let Some(edge_from_bcbs) = &self.edge_from_bcbs {
415 edge_from_bcbs.get(&from_bcb)
416 } else {
417 None
418 }
419 }
420
421 #[inline]
take_edge_counters( &mut self, ) -> Option<impl Iterator<Item = (BasicCoverageBlock, CoverageKind)>>422 pub fn take_edge_counters(
423 &mut self,
424 ) -> Option<impl Iterator<Item = (BasicCoverageBlock, CoverageKind)>> {
425 self.edge_from_bcbs.take().map(|m| m.into_iter())
426 }
427
id(&self) -> String428 pub fn id(&self) -> String {
429 format!("@{}", self.basic_blocks.iter().map(|bb| bb.index().to_string()).join(ID_SEPARATOR))
430 }
431 }
432
433 /// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`)
434 /// as either the successor BCB itself, if it has only one incoming edge, or the successor _plus_
435 /// the specific branching BCB, representing the edge between the two. The latter case
436 /// distinguishes this incoming edge from other incoming edges to the same `target_bcb`.
437 #[derive(Clone, Copy, PartialEq, Eq)]
438 pub(super) struct BcbBranch {
439 pub edge_from_bcb: Option<BasicCoverageBlock>,
440 pub target_bcb: BasicCoverageBlock,
441 }
442
443 impl BcbBranch {
from_to( from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, basic_coverage_blocks: &CoverageGraph, ) -> Self444 pub fn from_to(
445 from_bcb: BasicCoverageBlock,
446 to_bcb: BasicCoverageBlock,
447 basic_coverage_blocks: &CoverageGraph,
448 ) -> Self {
449 let edge_from_bcb = if basic_coverage_blocks.predecessors[to_bcb].len() > 1 {
450 Some(from_bcb)
451 } else {
452 None
453 };
454 Self { edge_from_bcb, target_bcb: to_bcb }
455 }
456
counter<'a>( &self, basic_coverage_blocks: &'a CoverageGraph, ) -> Option<&'a CoverageKind>457 pub fn counter<'a>(
458 &self,
459 basic_coverage_blocks: &'a CoverageGraph,
460 ) -> Option<&'a CoverageKind> {
461 if let Some(from_bcb) = self.edge_from_bcb {
462 basic_coverage_blocks[self.target_bcb].edge_counter_from(from_bcb)
463 } else {
464 basic_coverage_blocks[self.target_bcb].counter()
465 }
466 }
467
is_only_path_to_target(&self) -> bool468 pub fn is_only_path_to_target(&self) -> bool {
469 self.edge_from_bcb.is_none()
470 }
471 }
472
473 impl std::fmt::Debug for BcbBranch {
fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result474 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
475 if let Some(from_bcb) = self.edge_from_bcb {
476 write!(fmt, "{:?}->{:?}", from_bcb, self.target_bcb)
477 } else {
478 write!(fmt, "{:?}", self.target_bcb)
479 }
480 }
481 }
482
483 // Returns the `Terminator`s non-unwind successors.
484 // FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and
485 // `catch_unwind()` handlers.
bcb_filtered_successors<'a, 'tcx>( body: &'a mir::Body<'tcx>, term_kind: &'a TerminatorKind<'tcx>, ) -> Box<dyn Iterator<Item = BasicBlock> + 'a>486 fn bcb_filtered_successors<'a, 'tcx>(
487 body: &'a mir::Body<'tcx>,
488 term_kind: &'a TerminatorKind<'tcx>,
489 ) -> Box<dyn Iterator<Item = BasicBlock> + 'a> {
490 Box::new(
491 match &term_kind {
492 // SwitchInt successors are never unwind, and all of them should be traversed.
493 TerminatorKind::SwitchInt { ref targets, .. } => {
494 None.into_iter().chain(targets.all_targets().into_iter().copied())
495 }
496 // For all other kinds, return only the first successor, if any, and ignore unwinds.
497 // NOTE: `chain(&[])` is required to coerce the `option::iter` (from
498 // `next().into_iter()`) into the `mir::Successors` aliased type.
499 _ => term_kind.successors().next().into_iter().chain((&[]).into_iter().copied()),
500 }
501 .filter(move |&successor| body[successor].terminator().kind != TerminatorKind::Unreachable),
502 )
503 }
504
505 /// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the
506 /// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that
507 /// ensures a loop is completely traversed before processing Blocks after the end of the loop.
508 #[derive(Debug)]
509 pub(super) struct TraversalContext {
510 /// From one or more backedges returning to a loop header.
511 pub loop_backedges: Option<(Vec<BasicCoverageBlock>, BasicCoverageBlock)>,
512
513 /// worklist, to be traversed, of CoverageGraph in the loop with the given loop
514 /// backedges, such that the loop is the inner inner-most loop containing these
515 /// CoverageGraph
516 pub worklist: Vec<BasicCoverageBlock>,
517 }
518
519 pub(super) struct TraverseCoverageGraphWithLoops {
520 pub backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
521 pub context_stack: Vec<TraversalContext>,
522 visited: BitSet<BasicCoverageBlock>,
523 }
524
525 impl TraverseCoverageGraphWithLoops {
new(basic_coverage_blocks: &CoverageGraph) -> Self526 pub fn new(basic_coverage_blocks: &CoverageGraph) -> Self {
527 let start_bcb = basic_coverage_blocks.start_node();
528 let backedges = find_loop_backedges(basic_coverage_blocks);
529 let context_stack =
530 vec![TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }];
531 // `context_stack` starts with a `TraversalContext` for the main function context (beginning
532 // with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top
533 // of the stack as loops are entered, and popped off of the stack when a loop's worklist is
534 // exhausted.
535 let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes());
536 Self { backedges, context_stack, visited }
537 }
538
next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option<BasicCoverageBlock>539 pub fn next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option<BasicCoverageBlock> {
540 debug!(
541 "TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
542 self.context_stack.iter().rev().collect::<Vec<_>>()
543 );
544
545 while let Some(context) = self.context_stack.last_mut() {
546 if let Some(next_bcb) = context.worklist.pop() {
547 if !self.visited.insert(next_bcb) {
548 debug!("Already visited: {:?}", next_bcb);
549 continue;
550 }
551 debug!("Visiting {:?}", next_bcb);
552 if self.backedges[next_bcb].len() > 0 {
553 debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb);
554 self.context_stack.push(TraversalContext {
555 loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)),
556 worklist: Vec::new(),
557 });
558 }
559 self.extend_worklist(basic_coverage_blocks, next_bcb);
560 return Some(next_bcb);
561 } else {
562 // Strip contexts with empty worklists from the top of the stack
563 self.context_stack.pop();
564 }
565 }
566
567 None
568 }
569
extend_worklist( &mut self, basic_coverage_blocks: &CoverageGraph, bcb: BasicCoverageBlock, )570 pub fn extend_worklist(
571 &mut self,
572 basic_coverage_blocks: &CoverageGraph,
573 bcb: BasicCoverageBlock,
574 ) {
575 let successors = &basic_coverage_blocks.successors[bcb];
576 debug!("{:?} has {} successors:", bcb, successors.len());
577 for &successor in successors {
578 if successor == bcb {
579 debug!(
580 "{:?} has itself as its own successor. (Note, the compiled code will \
581 generate an infinite loop.)",
582 bcb
583 );
584 // Don't re-add this successor to the worklist. We are already processing it.
585 break;
586 }
587 for context in self.context_stack.iter_mut().rev() {
588 // Add successors of the current BCB to the appropriate context. Successors that
589 // stay within a loop are added to the BCBs context worklist. Successors that
590 // exit the loop (they are not dominated by the loop header) must be reachable
591 // from other BCBs outside the loop, and they will be added to a different
592 // worklist.
593 //
594 // Branching blocks (with more than one successor) must be processed before
595 // blocks with only one successor, to prevent unnecessarily complicating
596 // `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
597 // branching block would have given an `Expression` (or vice versa).
598 let (some_successor_to_add, some_loop_header) =
599 if let Some((_, loop_header)) = context.loop_backedges {
600 if basic_coverage_blocks.dominates(loop_header, successor) {
601 (Some(successor), Some(loop_header))
602 } else {
603 (None, None)
604 }
605 } else {
606 (Some(successor), None)
607 };
608 if let Some(successor_to_add) = some_successor_to_add {
609 if basic_coverage_blocks.successors[successor_to_add].len() > 1 {
610 debug!(
611 "{:?} successor is branching. Prioritize it at the beginning of \
612 the {}",
613 successor_to_add,
614 if let Some(loop_header) = some_loop_header {
615 format!("worklist for the loop headed by {:?}", loop_header)
616 } else {
617 String::from("non-loop worklist")
618 },
619 );
620 context.worklist.insert(0, successor_to_add);
621 } else {
622 debug!(
623 "{:?} successor is non-branching. Defer it to the end of the {}",
624 successor_to_add,
625 if let Some(loop_header) = some_loop_header {
626 format!("worklist for the loop headed by {:?}", loop_header)
627 } else {
628 String::from("non-loop worklist")
629 },
630 );
631 context.worklist.push(successor_to_add);
632 }
633 break;
634 }
635 }
636 }
637 }
638
is_complete(&self) -> bool639 pub fn is_complete(&self) -> bool {
640 self.visited.count() == self.visited.domain_size()
641 }
642
unvisited(&self) -> Vec<BasicCoverageBlock>643 pub fn unvisited(&self) -> Vec<BasicCoverageBlock> {
644 let mut unvisited_set: BitSet<BasicCoverageBlock> =
645 BitSet::new_filled(self.visited.domain_size());
646 unvisited_set.subtract(&self.visited);
647 unvisited_set.iter().collect::<Vec<_>>()
648 }
649 }
650
find_loop_backedges( basic_coverage_blocks: &CoverageGraph, ) -> IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>651 pub(super) fn find_loop_backedges(
652 basic_coverage_blocks: &CoverageGraph,
653 ) -> IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>> {
654 let num_bcbs = basic_coverage_blocks.num_nodes();
655 let mut backedges = IndexVec::from_elem_n(Vec::<BasicCoverageBlock>::new(), num_bcbs);
656
657 // Identify loops by their backedges.
658 for (bcb, _) in basic_coverage_blocks.iter_enumerated() {
659 for &successor in &basic_coverage_blocks.successors[bcb] {
660 if basic_coverage_blocks.dominates(successor, bcb) {
661 let loop_header = successor;
662 let backedge_from_bcb = bcb;
663 debug!(
664 "Found BCB backedge: {:?} -> loop_header: {:?}",
665 backedge_from_bcb, loop_header
666 );
667 backedges[loop_header].push(backedge_from_bcb);
668 }
669 }
670 }
671 backedges
672 }
673
674 pub struct ShortCircuitPreorder<
675 'a,
676 'tcx,
677 F: Fn(&'a mir::Body<'tcx>, &'a TerminatorKind<'tcx>) -> Box<dyn Iterator<Item = BasicBlock> + 'a>,
678 > {
679 body: &'a mir::Body<'tcx>,
680 visited: BitSet<BasicBlock>,
681 worklist: Vec<BasicBlock>,
682 filtered_successors: F,
683 }
684
685 impl<
686 'a,
687 'tcx,
688 F: Fn(&'a mir::Body<'tcx>, &'a TerminatorKind<'tcx>) -> Box<dyn Iterator<Item = BasicBlock> + 'a>,
689 > ShortCircuitPreorder<'a, 'tcx, F>
690 {
new( body: &'a mir::Body<'tcx>, filtered_successors: F, ) -> ShortCircuitPreorder<'a, 'tcx, F>691 pub fn new(
692 body: &'a mir::Body<'tcx>,
693 filtered_successors: F,
694 ) -> ShortCircuitPreorder<'a, 'tcx, F> {
695 let worklist = vec![mir::START_BLOCK];
696
697 ShortCircuitPreorder {
698 body,
699 visited: BitSet::new_empty(body.basic_blocks.len()),
700 worklist,
701 filtered_successors,
702 }
703 }
704 }
705
706 impl<
707 'a,
708 'tcx,
709 F: Fn(&'a mir::Body<'tcx>, &'a TerminatorKind<'tcx>) -> Box<dyn Iterator<Item = BasicBlock> + 'a>,
710 > Iterator for ShortCircuitPreorder<'a, 'tcx, F>
711 {
712 type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
713
next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)>714 fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
715 while let Some(idx) = self.worklist.pop() {
716 if !self.visited.insert(idx) {
717 continue;
718 }
719
720 let data = &self.body[idx];
721
722 if let Some(ref term) = data.terminator {
723 self.worklist.extend((self.filtered_successors)(&self.body, &term.kind));
724 }
725
726 return Some((idx, data));
727 }
728
729 None
730 }
731
size_hint(&self) -> (usize, Option<usize>)732 fn size_hint(&self) -> (usize, Option<usize>) {
733 let size = self.body.basic_blocks.len() - self.visited.count();
734 (size, Some(size))
735 }
736 }
737