1use crate::{
2 block::Block, AnalysisResult, AnalysisResultT, AnalysisResults, BranchToWithArgs, Context,
3 Function, IrError, Pass, PassMutability, ScopedPass,
4};
5use indexmap::IndexSet;
6use rustc_hash::{FxHashMap, FxHashSet};
10use std::fmt::Write;
11use sway_types::{FxIndexMap, FxIndexSet};
12
13pub struct DomTreeNode {
15 pub parent: Option<Block>,
17 pub children: Vec<Block>,
19}
20
21impl DomTreeNode {
22 pub fn new(parent: Option<Block>) -> DomTreeNode {
23 DomTreeNode {
24 parent,
25 children: vec![],
26 }
27 }
28}
29
30#[derive(Default)]
32pub struct DomTree(FxIndexMap<Block, DomTreeNode>);
33impl AnalysisResultT for DomTree {}
34
35pub type DomFronts = FxIndexMap<Block, FxIndexSet<Block>>;
37impl AnalysisResultT for DomFronts {}
38
39pub struct PostOrder {
41 pub block_to_po: FxHashMap<Block, usize>,
42 pub po_to_block: Vec<Block>,
43}
44impl AnalysisResultT for PostOrder {}
45
46pub const POSTORDER_NAME: &str = "postorder";
47
48pub fn create_postorder_pass() -> Pass {
49 Pass {
50 name: POSTORDER_NAME,
51 descr: "Postorder traversal of the control-flow graph",
52 deps: vec![],
53 runner: ScopedPass::FunctionPass(PassMutability::Analysis(compute_post_order_pass)),
54 }
55}
56
57pub fn compute_post_order_pass(
58 context: &Context,
59 _: &AnalysisResults,
60 function: Function,
61) -> Result<AnalysisResult, IrError> {
62 Ok(Box::new(compute_post_order(context, &function)))
63}
64
65pub fn compute_post_order(context: &Context, function: &Function) -> PostOrder {
68 let mut res = PostOrder {
69 block_to_po: FxHashMap::default(),
70 po_to_block: Vec::default(),
71 };
72 let entry = function.get_entry_block(context);
73
74 let mut counter = 0;
75 let mut on_stack = FxHashSet::<Block>::default();
76 fn post_order(
77 context: &Context,
78 n: Block,
79 res: &mut PostOrder,
80 on_stack: &mut FxHashSet<Block>,
81 counter: &mut usize,
82 ) {
83 if on_stack.contains(&n) {
84 return;
85 }
86 on_stack.insert(n);
87 for BranchToWithArgs { block: n_succ, .. } in n.successors(context) {
88 post_order(context, n_succ, res, on_stack, counter);
89 }
90 res.block_to_po.insert(n, *counter);
91 res.po_to_block.push(n);
92 *counter += 1;
93 }
94 post_order(context, entry, &mut res, &mut on_stack, &mut counter);
95
96 assert!(res.po_to_block.last().unwrap() == &entry);
98
99 res
100}
101
102pub const DOMINATORS_NAME: &str = "dominators";
103
104pub fn create_dominators_pass() -> Pass {
105 Pass {
106 name: DOMINATORS_NAME,
107 descr: "Dominator tree computation",
108 deps: vec![POSTORDER_NAME],
109 runner: ScopedPass::FunctionPass(PassMutability::Analysis(compute_dom_tree)),
110 }
111}
112
113fn compute_dom_tree(
115 context: &Context,
116 analyses: &AnalysisResults,
117 function: Function,
118) -> Result<AnalysisResult, IrError> {
119 let po: &PostOrder = analyses.get_analysis_result(function);
120 let mut dom_tree = DomTree::default();
121 let entry = function.get_entry_block(context);
122
123 dom_tree.0.insert(entry, DomTreeNode::new(Some(entry)));
125 for b in po.po_to_block.iter().take(po.po_to_block.len() - 1) {
128 dom_tree.0.insert(*b, DomTreeNode::new(None));
129 }
130 let mut changed = true;
131
132 while changed {
133 changed = false;
134 for b in po.po_to_block.iter().rev().skip(1) {
136 let mut new_idom = b
138 .pred_iter(context)
139 .find(|p| {
140 po.block_to_po
142 .get(p)
143 .is_some_and(|p_po| *p_po > po.block_to_po[b])
144 })
145 .cloned()
146 .unwrap();
147 let picked_pred = new_idom;
148 for p in b
150 .pred_iter(context)
151 .filter(|p| **p != picked_pred && po.block_to_po.contains_key(p))
152 {
153 if dom_tree.0[p].parent.is_some() {
154 new_idom = intersect(po, &dom_tree, *p, new_idom);
156 }
157 }
158 let b_node = dom_tree.0.get_mut(b).unwrap();
159 match b_node.parent {
160 Some(idom) if idom == new_idom => {}
161 _ => {
162 b_node.parent = Some(new_idom);
163 changed = true;
164 }
165 }
166 }
167 }
168
169 fn intersect(
172 po: &PostOrder,
173 dom_tree: &DomTree,
174 mut finger1: Block,
175 mut finger2: Block,
176 ) -> Block {
177 while finger1 != finger2 {
178 while po.block_to_po[&finger1] < po.block_to_po[&finger2] {
179 finger1 = dom_tree.0[&finger1].parent.unwrap();
180 }
181 while po.block_to_po[&finger2] < po.block_to_po[&finger1] {
182 finger2 = dom_tree.0[&finger2].parent.unwrap();
183 }
184 }
185 finger1
186 }
187
188 dom_tree.0.get_mut(&entry).unwrap().parent = None;
190 let child_parent: Vec<_> = dom_tree
192 .0
193 .iter()
194 .filter_map(|(n, n_node)| n_node.parent.map(|n_parent| (*n, n_parent)))
195 .collect();
196 for (child, parent) in child_parent {
197 dom_tree.0.get_mut(&parent).unwrap().children.push(child);
198 }
199
200 Ok(Box::new(dom_tree))
201}
202
203impl DomTree {
204 pub fn dominates(&self, dominator: Block, dominatee: Block) -> bool {
206 let mut node_opt = Some(dominatee);
207 while let Some(node) = node_opt {
208 if node == dominator {
209 return true;
210 }
211 node_opt = self.0[&node].parent;
212 }
213 false
214 }
215
216 pub fn children(&self, node: Block) -> impl Iterator<Item = Block> + '_ {
218 self.0[&node].children.iter().cloned()
219 }
220
221 pub fn child(&self, node: Block, i: usize) -> Option<Block> {
223 self.0[&node].children.get(i).cloned()
224 }
225}
226
227pub const DOM_FRONTS_NAME: &str = "dominance-frontiers";
228
229pub fn create_dom_fronts_pass() -> Pass {
230 Pass {
231 name: DOM_FRONTS_NAME,
232 descr: "Dominance frontiers computation",
233 deps: vec![DOMINATORS_NAME],
234 runner: ScopedPass::FunctionPass(PassMutability::Analysis(compute_dom_fronts)),
235 }
236}
237
238fn compute_dom_fronts(
240 context: &Context,
241 analyses: &AnalysisResults,
242 function: Function,
243) -> Result<AnalysisResult, IrError> {
244 let dom_tree: &DomTree = analyses.get_analysis_result(function);
245 let mut res = DomFronts::default();
246 for (b, _) in dom_tree.0.iter() {
247 res.insert(*b, IndexSet::default());
248 }
249
250 for (b, _) in dom_tree.0.iter() {
252 if b.num_predecessors(context) > 1 {
254 let b_idom = dom_tree.0[b].parent.unwrap();
256 for p in b.pred_iter(context).filter(|&p| dom_tree.0.contains_key(p)) {
258 let mut runner = *p;
259 while runner != b_idom {
260 res.get_mut(&runner).unwrap().insert(*b);
262 runner = dom_tree.0[&runner].parent.unwrap();
263 }
264 }
265 }
266 }
267 Ok(Box::new(res))
268}
269
270pub fn print_dot(context: &Context, func_name: &str, dom_tree: &DomTree) -> String {
272 let mut res = format!("digraph {func_name} {{\n");
273 for (b, idom) in dom_tree.0.iter() {
274 if let Some(idom) = idom.parent {
275 let _ = writeln!(
276 res,
277 "\t{} -> {}",
278 idom.get_label(context),
279 b.get_label(context)
280 );
281 }
282 }
283 res += "}\n";
284 res
285}
286
287pub fn print_dom_fronts(context: &Context, func_name: &str, dom_fronts: &DomFronts) -> String {
289 let mut res = format!("Dominance frontiers set for {func_name}:\n");
290 for (b, dfs) in dom_fronts.iter() {
291 res += &("\t".to_string() + &b.get_label(context) + ": ");
292 for f in dfs {
293 res += &(f.get_label(context) + " ");
294 }
295 res += "\n";
296 }
297 res
298}