rustc_mir_transform/coverage/
graph.rs
1use std::cmp::Ordering;
2use std::ops::{Index, IndexMut};
3use std::{mem, slice};
4
5use rustc_data_structures::captures::Captures;
6use rustc_data_structures::fx::FxHashSet;
7use rustc_data_structures::graph::dominators::Dominators;
8use rustc_data_structures::graph::{self, DirectedGraph, StartNode};
9use rustc_index::IndexVec;
10use rustc_index::bit_set::DenseBitSet;
11pub(crate) use rustc_middle::mir::coverage::{BasicCoverageBlock, START_BCB};
12use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind};
13use tracing::debug;
14
15#[derive(Debug)]
18pub(crate) struct CoverageGraph {
19 bcbs: IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
20 bb_to_bcb: IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
21 pub(crate) successors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
22 pub(crate) predecessors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
23
24 dominators: Option<Dominators<BasicCoverageBlock>>,
25 dominator_order_rank: IndexVec<BasicCoverageBlock, u32>,
29 is_loop_header: DenseBitSet<BasicCoverageBlock>,
31 enclosing_loop_header: IndexVec<BasicCoverageBlock, Option<BasicCoverageBlock>>,
34}
35
36impl CoverageGraph {
37 pub(crate) fn from_mir(mir_body: &mir::Body<'_>) -> Self {
38 let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body);
39
40 let successors = IndexVec::from_fn_n(
47 |bcb| {
48 let mut seen_bcbs = FxHashSet::default();
49 let terminator = mir_body[bcbs[bcb].last_bb()].terminator();
50 bcb_filtered_successors(terminator)
51 .into_iter()
52 .filter_map(|successor_bb| bb_to_bcb[successor_bb])
53 .filter(|&successor_bcb| seen_bcbs.insert(successor_bcb))
55 .collect::<Vec<_>>()
56 },
57 bcbs.len(),
58 );
59
60 let mut predecessors = IndexVec::from_elem(Vec::new(), &bcbs);
61 for (bcb, bcb_successors) in successors.iter_enumerated() {
62 for &successor in bcb_successors {
63 predecessors[successor].push(bcb);
64 }
65 }
66
67 let num_nodes = bcbs.len();
68 let mut this = Self {
69 bcbs,
70 bb_to_bcb,
71 successors,
72 predecessors,
73 dominators: None,
74 dominator_order_rank: IndexVec::from_elem_n(0, num_nodes),
75 is_loop_header: DenseBitSet::new_empty(num_nodes),
76 enclosing_loop_header: IndexVec::from_elem_n(None, num_nodes),
77 };
78 assert_eq!(num_nodes, this.num_nodes());
79
80 this.dominators = Some(graph::dominators::dominators(&this));
82
83 let dominator_order = graph::iterate::reverse_post_order(&this, this.start_node());
86 assert_eq!(dominator_order.len(), this.num_nodes());
88 for (rank, bcb) in (0u32..).zip(dominator_order) {
89 this.dominator_order_rank[bcb] = rank;
91
92 if this.reloop_predecessors(bcb).next().is_some() {
94 this.is_loop_header.insert(bcb);
95 }
96
97 if let Some(dom) = this.dominators().immediate_dominator(bcb) {
101 this.enclosing_loop_header[bcb] = this
102 .is_loop_header
103 .contains(dom)
104 .then_some(dom)
105 .or_else(|| this.enclosing_loop_header[dom]);
106 }
107 }
108
109 assert!(this[START_BCB].leader_bb() == mir::START_BLOCK);
114 assert!(this.predecessors[START_BCB].is_empty());
115
116 this
117 }
118
119 fn compute_basic_coverage_blocks(
120 mir_body: &mir::Body<'_>,
121 ) -> (
122 IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
123 IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
124 ) {
125 let num_basic_blocks = mir_body.basic_blocks.len();
126 let mut bcbs = IndexVec::<BasicCoverageBlock, _>::with_capacity(num_basic_blocks);
127 let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks);
128
129 let mut flush_chain_into_new_bcb = |current_chain: &mut Vec<BasicBlock>| {
130 let basic_blocks = mem::take(current_chain);
133
134 let bcb = bcbs.next_index();
135 for &bb in basic_blocks.iter() {
136 bb_to_bcb[bb] = Some(bcb);
137 }
138
139 let is_out_summable = basic_blocks.last().is_some_and(|&bb| {
140 bcb_filtered_successors(mir_body[bb].terminator()).is_out_summable()
141 });
142 let bcb_data = BasicCoverageBlockData { basic_blocks, is_out_summable };
143 debug!("adding {bcb:?}: {bcb_data:?}");
144 bcbs.push(bcb_data);
145 };
146
147 let mut current_chain = vec![];
154
155 let subgraph = CoverageRelevantSubgraph::new(&mir_body.basic_blocks);
156 for bb in graph::depth_first_search(subgraph, mir::START_BLOCK)
157 .filter(|&bb| mir_body[bb].terminator().kind != TerminatorKind::Unreachable)
158 {
159 if let Some(&prev) = current_chain.last() {
160 let can_chain = subgraph.coverage_successors(prev).is_out_chainable()
164 && mir_body.basic_blocks.predecessors()[bb].as_slice() == &[prev];
165 if !can_chain {
166 flush_chain_into_new_bcb(&mut current_chain);
169 }
170 }
171
172 current_chain.push(bb);
173 }
174
175 if !current_chain.is_empty() {
176 debug!("flushing accumulated blocks into one last BCB");
177 flush_chain_into_new_bcb(&mut current_chain);
178 }
179
180 (bcbs, bb_to_bcb)
181 }
182
183 #[inline(always)]
184 pub(crate) fn iter_enumerated(
185 &self,
186 ) -> impl Iterator<Item = (BasicCoverageBlock, &BasicCoverageBlockData)> {
187 self.bcbs.iter_enumerated()
188 }
189
190 #[inline(always)]
191 pub(crate) fn bcb_from_bb(&self, bb: BasicBlock) -> Option<BasicCoverageBlock> {
192 if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None }
193 }
194
195 #[inline(always)]
196 fn dominators(&self) -> &Dominators<BasicCoverageBlock> {
197 self.dominators.as_ref().unwrap()
198 }
199
200 #[inline(always)]
201 pub(crate) fn dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
202 self.dominators().dominates(dom, node)
203 }
204
205 #[inline(always)]
206 pub(crate) fn cmp_in_dominator_order(
207 &self,
208 a: BasicCoverageBlock,
209 b: BasicCoverageBlock,
210 ) -> Ordering {
211 self.dominator_order_rank[a].cmp(&self.dominator_order_rank[b])
212 }
213
214 pub(crate) fn reloop_predecessors(
219 &self,
220 to_bcb: BasicCoverageBlock,
221 ) -> impl Iterator<Item = BasicCoverageBlock> + Captures<'_> {
222 self.predecessors[to_bcb].iter().copied().filter(move |&pred| self.dominates(to_bcb, pred))
223 }
224}
225
226impl Index<BasicCoverageBlock> for CoverageGraph {
227 type Output = BasicCoverageBlockData;
228
229 #[inline]
230 fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData {
231 &self.bcbs[index]
232 }
233}
234
235impl IndexMut<BasicCoverageBlock> for CoverageGraph {
236 #[inline]
237 fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData {
238 &mut self.bcbs[index]
239 }
240}
241
242impl graph::DirectedGraph for CoverageGraph {
243 type Node = BasicCoverageBlock;
244
245 #[inline]
246 fn num_nodes(&self) -> usize {
247 self.bcbs.len()
248 }
249}
250
251impl graph::StartNode for CoverageGraph {
252 #[inline]
253 fn start_node(&self) -> Self::Node {
254 self.bcb_from_bb(mir::START_BLOCK)
255 .expect("mir::START_BLOCK should be in a BasicCoverageBlock")
256 }
257}
258
259impl graph::Successors for CoverageGraph {
260 #[inline]
261 fn successors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
262 self.successors[node].iter().copied()
263 }
264}
265
266impl graph::Predecessors for CoverageGraph {
267 #[inline]
268 fn predecessors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
269 self.predecessors[node].iter().copied()
270 }
271}
272
273#[derive(Debug, Clone)]
300pub(crate) struct BasicCoverageBlockData {
301 pub(crate) basic_blocks: Vec<BasicBlock>,
302
303 pub(crate) is_out_summable: bool,
309}
310
311impl BasicCoverageBlockData {
312 #[inline(always)]
313 pub(crate) fn leader_bb(&self) -> BasicBlock {
314 self.basic_blocks[0]
315 }
316
317 #[inline(always)]
318 pub(crate) fn last_bb(&self) -> BasicBlock {
319 *self.basic_blocks.last().unwrap()
320 }
321}
322
323#[derive(Clone, Copy, Debug)]
327struct CoverageSuccessors<'a> {
328 targets: &'a [BasicBlock],
331 is_yield: bool,
334}
335
336impl CoverageSuccessors<'_> {
337 fn is_out_chainable(&self) -> bool {
340 self.is_out_summable() && self.targets.len() == 1
343 }
344
345 fn is_out_summable(&self) -> bool {
348 !self.is_yield && !self.targets.is_empty()
349 }
350}
351
352impl IntoIterator for CoverageSuccessors<'_> {
353 type Item = BasicBlock;
354 type IntoIter = impl DoubleEndedIterator<Item = Self::Item>;
355
356 fn into_iter(self) -> Self::IntoIter {
357 self.targets.iter().copied()
358 }
359}
360
361fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> CoverageSuccessors<'a> {
366 use TerminatorKind::*;
367 let mut is_yield = false;
368 let targets = match &terminator.kind {
369 SwitchInt { targets, .. } => targets.all_targets(),
371
372 Yield { resume, .. } => {
375 is_yield = true;
376 slice::from_ref(resume)
377 }
378
379 Assert { target, .. }
382 | Drop { target, .. }
383 | FalseEdge { real_target: target, .. }
384 | FalseUnwind { real_target: target, .. }
385 | Goto { target } => slice::from_ref(target),
386
387 Call { target: maybe_target, .. } => maybe_target.as_slice(),
390
391 InlineAsm { targets, .. } => &targets,
394
395 CoroutineDrop
397 | Return
398 | TailCall { .. }
399 | Unreachable
400 | UnwindResume
401 | UnwindTerminate(_) => &[],
402 };
403
404 CoverageSuccessors { targets, is_yield }
405}
406
407#[derive(Clone, Copy)]
411struct CoverageRelevantSubgraph<'a, 'tcx> {
412 basic_blocks: &'a mir::BasicBlocks<'tcx>,
413}
414impl<'a, 'tcx> CoverageRelevantSubgraph<'a, 'tcx> {
415 fn new(basic_blocks: &'a mir::BasicBlocks<'tcx>) -> Self {
416 Self { basic_blocks }
417 }
418
419 fn coverage_successors(&self, bb: BasicBlock) -> CoverageSuccessors<'_> {
420 bcb_filtered_successors(self.basic_blocks[bb].terminator())
421 }
422}
423impl<'a, 'tcx> graph::DirectedGraph for CoverageRelevantSubgraph<'a, 'tcx> {
424 type Node = BasicBlock;
425
426 fn num_nodes(&self) -> usize {
427 self.basic_blocks.num_nodes()
428 }
429}
430impl<'a, 'tcx> graph::Successors for CoverageRelevantSubgraph<'a, 'tcx> {
431 fn successors(&self, bb: Self::Node) -> impl Iterator<Item = Self::Node> {
432 self.coverage_successors(bb).into_iter()
433 }
434}