1use super::{
4 cfg::ControlFlowGraph,
5 domtree::{DFSet, DomTree},
6};
7
8use sonatina_ir::{Block, Function};
9
10#[derive(Debug)]
11pub struct PostDomTree {
12 entry: Block,
14 exit: Block,
17
18 rcfg: ControlFlowGraph,
20
21 domtree: DomTree,
23}
24
25impl Default for PostDomTree {
26 fn default() -> Self {
27 Self {
28 entry: Block(0),
29 exit: Block(0),
30 rcfg: ControlFlowGraph::default(),
31 domtree: DomTree::default(),
32 }
33 }
34}
35
36impl PostDomTree {
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn compute(&mut self, func: &Function) {
42 self.clear();
43
44 self.rcfg.compute(func);
45 if self.rcfg.entry().is_none() {
46 return;
47 }
48 let real_entry = self.rcfg.entry().unwrap();
49
50 self.entry = Block(func.dfg.blocks.len() as u32);
51 self.exit = Block(self.entry.0 + 1);
52
53 self.rcfg.add_edge(self.entry, real_entry);
55 self.rcfg.add_edge(self.entry, self.exit);
56
57 let real_exits = std::mem::take(&mut self.rcfg.exits);
59 for exit in &real_exits {
60 self.rcfg.add_edge(*exit, self.exit);
61 }
62
63 self.rcfg.reverse_edges(self.exit, &[self.entry]);
64 self.domtree.compute(&self.rcfg);
65 }
66
67 pub fn idom_of(&self, block: Block) -> Option<PDTIdom> {
68 match self.domtree.idom_of(block)? {
69 block if block == self.entry => Some(PDTIdom::DummyEntry(self.entry)),
70 block if block == self.exit => Some(PDTIdom::DummyExit(self.exit)),
71 other => Some(PDTIdom::Real(other)),
72 }
73 }
74
75 pub fn clear(&mut self) {
76 self.rcfg.clear();
77 self.domtree.clear();
78 }
79
80 pub fn compute_df(&self) -> PDFSet {
82 let df_set = self.domtree.compute_df(&self.rcfg);
83
84 PDFSet {
85 entry: self.entry,
86 exit: self.exit,
87 df_set,
88 }
89 }
90
91 pub fn is_reachable(&self, block: Block) -> bool {
93 self.domtree.is_reachable(block)
94 }
95}
96
97#[derive(Debug, Clone, Copy)]
98pub enum PDTIdom {
99 DummyEntry(Block),
100 DummyExit(Block),
101 Real(Block),
102}
103
104#[derive(Debug)]
106pub struct PDFSet {
107 entry: Block,
109
110 exit: Block,
112
113 df_set: DFSet,
114}
115
116impl PDFSet {
117 pub fn frontiers(&self, block: Block) -> impl Iterator<Item = &Block> {
118 self.df_set
119 .frontiers(block)
120 .filter(|block| **block != self.entry && **block != self.exit)
121 }
122
123 pub fn in_frontier_of(&self, block: Block, of: Block) -> bool {
124 self.df_set.in_frontier_of(block, of)
125 }
126
127 pub fn frontier_num_of(&self, of: Block) -> usize {
128 self.frontiers(of).count()
129 }
130
131 pub fn clear(&mut self) {
132 self.df_set.clear();
133 }
134}
135
136impl Default for PDFSet {
137 fn default() -> Self {
138 Self {
139 entry: Block(0),
140 exit: Block(0),
141 df_set: DFSet::default(),
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 #![allow(clippy::many_single_char_names)]
149
150 use super::*;
151 use sonatina_ir::{builder::test_util::*, Type};
152
153 fn calc_dom(func: &Function) -> (PostDomTree, PDFSet) {
154 let mut post_dom_tree = PostDomTree::new();
155 post_dom_tree.compute(func);
156 let pdf = post_dom_tree.compute_df();
157 (post_dom_tree, pdf)
158 }
159
160 fn test_pdf(pdf: &PDFSet, of: Block, frontieres: &[Block]) -> bool {
161 if pdf.frontier_num_of(of) != frontieres.len() {
162 return false;
163 }
164
165 for &block in frontieres {
166 if !pdf.in_frontier_of(block, of) {
167 return false;
168 }
169 }
170
171 true
172 }
173
174 #[test]
175 fn pd_if_else() {
176 let mut test_module_builder = TestModuleBuilder::new();
177 let mut builder = test_module_builder.func_builder(&[Type::I64], Type::Void);
178
179 let entry_block = builder.append_block();
180 let then_block = builder.append_block();
181 let else_block = builder.append_block();
182 let merge_block = builder.append_block();
183
184 let arg0 = builder.args()[0];
185
186 builder.switch_to_block(entry_block);
187 builder.br(arg0, then_block, else_block);
188
189 builder.switch_to_block(then_block);
190 let v1 = builder.make_imm_value(1i64);
191 builder.jump(merge_block);
192
193 builder.switch_to_block(else_block);
194 let v2 = builder.make_imm_value(2i64);
195 builder.jump(merge_block);
196
197 builder.switch_to_block(merge_block);
198 let v3 = builder.phi(&[(v1, then_block), (v2, else_block)]);
199 builder.add(v3, arg0);
200 builder.ret(None);
201
202 builder.seal_all();
203 let func_ref = builder.finish();
204
205 let module = test_module_builder.build();
206 let func = &module.funcs[func_ref];
207 let (post_dom_tree, pdf) = calc_dom(func);
208
209 assert!(post_dom_tree.is_reachable(entry_block));
210 assert!(post_dom_tree.is_reachable(else_block));
211 assert!(post_dom_tree.is_reachable(then_block));
212 assert!(post_dom_tree.is_reachable(merge_block));
213
214 assert!(test_pdf(&pdf, entry_block, &[]));
215 assert!(test_pdf(&pdf, then_block, &[entry_block]));
216 assert!(test_pdf(&pdf, else_block, &[entry_block]));
217 assert!(test_pdf(&pdf, merge_block, &[]));
218 }
219
220 #[test]
221 fn infinite_loop() {
222 let mut test_module_builder = TestModuleBuilder::new();
223 let mut builder = test_module_builder.func_builder(&[], Type::Void);
224
225 let a = builder.append_block();
226 builder.switch_to_block(a);
227 builder.jump(a);
228
229 builder.seal_all();
230 let func_ref = builder.finish();
231
232 let module = test_module_builder.build();
233 let func = &module.funcs[func_ref];
234 let (post_dom_tree, pdf) = calc_dom(func);
235
236 assert!(!post_dom_tree.is_reachable(a));
237 assert!(test_pdf(&pdf, a, &[]));
238 }
239
240 #[test]
241 fn test_multiple_return() {
242 let mut test_module_builder = TestModuleBuilder::new();
243 let mut builder = test_module_builder.func_builder(&[], Type::Void);
244
245 let a = builder.append_block();
246 let b = builder.append_block();
247 let c = builder.append_block();
248 let d = builder.append_block();
249 let e = builder.append_block();
250
251 builder.switch_to_block(a);
252 let v0 = builder.make_imm_value(1i8);
253 builder.br(v0, b, c);
254
255 builder.switch_to_block(b);
256 builder.ret(None);
257
258 builder.switch_to_block(c);
259 builder.br(v0, d, e);
260
261 builder.switch_to_block(d);
262 builder.ret(None);
263
264 builder.switch_to_block(e);
265 builder.ret(None);
266
267 builder.seal_all();
268 let func_ref = builder.finish();
269
270 let module = test_module_builder.build();
271 let func = &module.funcs[func_ref];
272 let (post_dom_tree, pdf) = calc_dom(func);
273
274 assert!(post_dom_tree.is_reachable(a));
275 assert!(post_dom_tree.is_reachable(b));
276 assert!(post_dom_tree.is_reachable(c));
277 assert!(post_dom_tree.is_reachable(d));
278 assert!(post_dom_tree.is_reachable(e));
279
280 assert!(test_pdf(&pdf, a, &[]));
281 assert!(test_pdf(&pdf, b, &[a]));
282 assert!(test_pdf(&pdf, c, &[a]));
283 assert!(test_pdf(&pdf, d, &[c]));
284 assert!(test_pdf(&pdf, e, &[c]));
285 }
286
287 #[test]
288 fn pd_complex() {
289 let mut test_module_builder = TestModuleBuilder::new();
290 let mut builder = test_module_builder.func_builder(&[], Type::Void);
291
292 let a = builder.append_block();
293 let b = builder.append_block();
294 let c = builder.append_block();
295 let d = builder.append_block();
296 let e = builder.append_block();
297 let f = builder.append_block();
298 let g = builder.append_block();
299 let h = builder.append_block();
300
301 builder.switch_to_block(a);
302 let v0 = builder.make_imm_value(1i8);
303 builder.br(v0, b, c);
304
305 builder.switch_to_block(b);
306 builder.jump(g);
307
308 builder.switch_to_block(c);
309 builder.br(v0, d, e);
310
311 builder.switch_to_block(d);
312 builder.jump(f);
313
314 builder.switch_to_block(e);
315 builder.jump(f);
316
317 builder.switch_to_block(f);
318 builder.jump(g);
319
320 builder.switch_to_block(g);
321 builder.br(v0, a, h);
322
323 builder.switch_to_block(h);
324 builder.ret(None);
325
326 builder.seal_all();
327 let func_ref = builder.finish();
328
329 let module = test_module_builder.build();
330 let func = &module.funcs[func_ref];
331 let (post_dom_tree, pdf) = calc_dom(func);
332
333 assert!(post_dom_tree.is_reachable(a));
334 assert!(post_dom_tree.is_reachable(b));
335 assert!(post_dom_tree.is_reachable(c));
336 assert!(post_dom_tree.is_reachable(d));
337 assert!(post_dom_tree.is_reachable(e));
338 assert!(post_dom_tree.is_reachable(f));
339 assert!(post_dom_tree.is_reachable(g));
340 assert!(post_dom_tree.is_reachable(h));
341
342 assert!(test_pdf(&pdf, a, &[g]));
343 assert!(test_pdf(&pdf, b, &[a]));
344 assert!(test_pdf(&pdf, c, &[a]));
345 assert!(test_pdf(&pdf, d, &[c]));
346 assert!(test_pdf(&pdf, e, &[c]));
347 assert!(test_pdf(&pdf, f, &[a]));
348 assert!(test_pdf(&pdf, g, &[g]));
349 assert!(test_pdf(&pdf, h, &[]));
350 }
351}