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,
23 pub is_cleanup: bool,
25 pub next: FxHashSet<usize>,
27 pub scc: SccInfo,
32}
33
34impl CfgBlock {
35 pub fn new(index: usize, is_cleanup: bool) -> Self {
37 Self {
38 index,
39 is_cleanup,
40 next: FxHashSet::default(),
41 scc: SccInfo::new(index),
42 }
43 }
44
45 pub fn add_next(&mut self, index: usize) {
47 self.next.insert(index);
48 }
49}
50
51#[derive(Clone)]
55pub struct ControlFlowGraph<'tcx> {
56 pub def_id: DefId,
58 pub tcx: TyCtxt<'tcx>,
60 pub blocks: Vec<CfgBlock>,
62}
63
64impl<'tcx> ControlFlowGraph<'tcx> {
65 pub fn new(def_id: DefId, tcx: TyCtxt<'tcx>, blocks: Vec<CfgBlock>) -> Self {
67 Self {
68 def_id,
69 tcx,
70 blocks,
71 }
72 }
73
74 pub fn block(&self, index: usize) -> &CfgBlock {
76 &self.blocks[index]
77 }
78
79 pub fn block_mut(&mut self, index: usize) -> &mut CfgBlock {
81 &mut self.blocks[index]
82 }
83
84 pub fn terminator(&self, index: usize) -> Option<&Terminator<'tcx>> {
89 let body = self.tcx.optimized_mir(self.def_id);
90 body.basic_blocks
91 .get(BasicBlock::from(index))
92 .and_then(|bb| bb.terminator.as_ref())
93 }
94}
95
96fn record_root_exits<'tcx>(
98 graph: &mut ControlFlowGraph<'tcx>,
99 root: usize,
100 scc_components: &[usize],
101) {
102 let nexts = graph.block(root).next.clone();
103 for next in nexts {
104 if !scc_components.contains(&next) {
105 graph
106 .block_mut(root)
107 .scc
108 .exits
109 .insert(SccExit::new(root, next));
110 }
111 }
112}
113
114fn record_member_nodes<'tcx>(
116 graph: &mut ControlFlowGraph<'tcx>,
117 root: usize,
118 scc_components: &[usize],
119) {
120 for &node in &scc_components[1..] {
121 graph.block_mut(root).scc.nodes.insert(node);
123 graph.block_mut(node).scc.enter = root;
125
126 let nexts = graph.block(node).next.clone();
127 for next in nexts {
128 if !scc_components.contains(&next) {
130 graph
131 .block_mut(root)
132 .scc
133 .exits
134 .insert(SccExit::new(node, next));
135 }
136 if next == root && !graph.block(root).scc.backedges.contains(&(node, root)) {
138 graph.block_mut(root).scc.backedges.push((node, root));
139 }
140 }
141 }
142}
143
144fn rerun_scc_in_isolation<'tcx>(
153 graph: &mut ControlFlowGraph<'tcx>,
154 root: usize,
155 _scc_components: &[usize],
156) {
157 let scc_exits = graph.block(root).scc.exits.clone();
158 let backedges = graph.block(root).scc.backedges.clone();
159 let mut backups: Vec<(usize, FxHashSet<usize>)> = Vec::new();
160
161 let block0 = graph.block_mut(0);
164 backups.push((0, block0.next.clone()));
165 block0.next.clear();
166 block0.next.insert(root);
167
168 for &(node, target) in &backedges {
170 if target != root {
171 continue;
172 }
173 let block = graph.block_mut(node);
174 backups.push((node, block.next.clone()));
175 block.next.remove(&root);
176 }
177
178 for exit in &scc_exits {
180 let block_to = graph.block_mut(exit.to);
181 backups.push((exit.to, block_to.next.clone()));
182 block_to.next.clear();
183 }
184
185 graph.find_scc();
187
188 for (idx, saved_next) in backups {
190 graph.block_mut(idx).next = saved_next;
191 }
192}
193
194fn scc_handler<'tcx>(graph: &mut ControlFlowGraph<'tcx>, root: usize, scc_components: &[usize]) {
197 rap_debug!(
198 "Scc found: root = {}, components = {:?}",
199 root,
200 scc_components
201 );
202
203 graph.block_mut(root).scc.enter = root;
205
206 if scc_components.len() <= 1 {
208 return;
209 }
210
211 record_root_exits(graph, root, scc_components);
212 record_member_nodes(graph, root, scc_components);
213
214 rap_debug!("Scc Info: {:?}", graph.block(root).scc);
215 rerun_scc_in_isolation(graph, root, scc_components);
216}
217
218impl<'tcx> Scc for ControlFlowGraph<'tcx> {
219 fn on_scc_found(&mut self, root: usize, scc_components: &[usize]) {
221 scc_handler(self, root, scc_components);
222 }
223
224 fn get_next(&mut self, root: usize) -> FxHashSet<usize> {
226 self.block(root).next.clone()
227 }
228
229 fn get_size(&mut self) -> usize {
231 self.blocks.len()
232 }
233}