1use crate::compat::FxHashSet;
2use crate::graphs::scc::{Scc, SccExit, SccInfo};
3use rustc_middle::{
4 mir::{BasicBlock, Terminator},
5 ty::TyCtxt,
6};
7use rustc_span::def_id::DefId;
8
9#[derive(Debug, Clone)]
20pub struct CfgBlock {
21 pub index: usize,
24 pub def_id: DefId,
26 pub local_index: usize,
28 pub is_cleanup: bool,
30 pub next: FxHashSet<usize>,
32 pub scc: SccInfo,
37}
38
39impl CfgBlock {
40 pub fn new(def_id: DefId, index: usize, is_cleanup: bool) -> Self {
42 Self {
43 index,
44 def_id,
45 local_index: index,
46 is_cleanup,
47 next: FxHashSet::default(),
48 scc: SccInfo::new(index),
49 }
50 }
51
52 pub fn add_next(&mut self, index: usize) {
54 self.next.insert(index);
55 }
56}
57
58#[derive(Clone)]
62pub struct ControlFlowGraph<'tcx> {
63 pub def_id: DefId,
65 pub tcx: TyCtxt<'tcx>,
67 pub blocks: Vec<CfgBlock>,
69}
70
71impl<'tcx> ControlFlowGraph<'tcx> {
72 pub fn new(def_id: DefId, tcx: TyCtxt<'tcx>, blocks: Vec<CfgBlock>) -> Self {
74 Self {
75 def_id,
76 tcx,
77 blocks,
78 }
79 }
80
81 pub fn block(&self, index: usize) -> &CfgBlock {
83 &self.blocks[index]
84 }
85
86 pub fn block_mut(&mut self, index: usize) -> &mut CfgBlock {
88 &mut self.blocks[index]
89 }
90
91 pub fn terminator(&self, index: usize) -> Option<&Terminator<'tcx>> {
96 let block = self.blocks.get(index)?;
97 let body = self.tcx.optimized_mir(block.def_id);
98 body.basic_blocks
99 .get(BasicBlock::from(block.local_index))
100 .and_then(|bb| bb.terminator.as_ref())
101 }
102}
103
104fn record_root_exits<'tcx>(
106 graph: &mut ControlFlowGraph<'tcx>,
107 root: usize,
108 scc_components: &[usize],
109) {
110 let nexts = graph.block(root).next.clone();
111 for next in nexts {
112 if !scc_components.contains(&next) {
113 graph
114 .block_mut(root)
115 .scc
116 .exits
117 .insert(SccExit::new(root, next));
118 }
119 }
120}
121
122fn record_member_nodes<'tcx>(
124 graph: &mut ControlFlowGraph<'tcx>,
125 root: usize,
126 scc_components: &[usize],
127) {
128 for &node in &scc_components[1..] {
129 graph.block_mut(root).scc.nodes.insert(node);
131 graph.block_mut(node).scc.enter = root;
133
134 let nexts = graph.block(node).next.clone();
135 for next in nexts {
136 if !scc_components.contains(&next) {
138 graph
139 .block_mut(root)
140 .scc
141 .exits
142 .insert(SccExit::new(node, next));
143 }
144 if next == root && !graph.block(root).scc.backedges.contains(&(node, root)) {
146 graph.block_mut(root).scc.backedges.push((node, root));
147 }
148 }
149 }
150}
151
152fn rerun_scc_in_isolation<'tcx>(
161 graph: &mut ControlFlowGraph<'tcx>,
162 root: usize,
163) {
164 let scc_exits = graph.block(root).scc.exits.clone();
165 let backedges = graph.block(root).scc.backedges.clone();
166 let mut backups: Vec<(usize, FxHashSet<usize>)> = Vec::new();
167
168 let block0 = graph.block_mut(0);
171 backups.push((0, block0.next.clone()));
172 block0.next.clear();
173 block0.next.insert(root);
174
175 for &(node, target) in &backedges {
177 if target != root {
178 continue;
179 }
180 let block = graph.block_mut(node);
181 backups.push((node, block.next.clone()));
182 block.next.remove(&root);
183 }
184
185 for exit in &scc_exits {
187 let block_to = graph.block_mut(exit.to);
188 backups.push((exit.to, block_to.next.clone()));
189 block_to.next.clear();
190 }
191
192 graph.find_scc();
194
195 for (idx, saved_next) in backups {
197 graph.block_mut(idx).next = saved_next;
198 }
199}
200
201fn scc_handler<'tcx>(graph: &mut ControlFlowGraph<'tcx>, root: usize, scc_components: &[usize]) {
204 rap_debug!(
205 "Scc found: root = {}, components = {:?}",
206 root,
207 scc_components
208 );
209
210 graph.block_mut(root).scc.enter = root;
212
213 if scc_components.len() <= 1 {
215 return;
216 }
217
218 record_root_exits(graph, root, scc_components);
219 record_member_nodes(graph, root, scc_components);
220
221 rap_debug!("Scc Info: {:?}", graph.block(root).scc);
222 rerun_scc_in_isolation(graph, root);
223}
224
225impl<'tcx> Scc for ControlFlowGraph<'tcx> {
226 fn on_scc_found(&mut self, root: usize, scc_components: &[usize]) {
228 scc_handler(self, root, scc_components);
229 }
230
231 fn get_next(&mut self, root: usize) -> FxHashSet<usize> {
233 self.block(root).next.clone()
234 }
235
236 fn get_size(&mut self) -> usize {
238 self.blocks.len()
239 }
240}