sonatina_codegen/
critical_edge.rs1use super::cfg::ControlFlowGraph;
2
3use sonatina_ir::{
4 func_cursor::{CursorLocation, FuncCursor, InsnInserter},
5 insn::InsnData,
6 Block, Function, Insn,
7};
8
9#[derive(Debug)]
10pub struct CriticalEdgeSplitter {
11 critical_edges: Vec<CriticalEdge>,
12}
13
14impl Default for CriticalEdgeSplitter {
15 fn default() -> Self {
16 Self::new()
17 }
18}
19
20impl CriticalEdgeSplitter {
21 pub fn new() -> Self {
22 Self {
23 critical_edges: Vec::default(),
24 }
25 }
26
27 pub fn run(&mut self, func: &mut Function, cfg: &mut ControlFlowGraph) {
28 self.clear();
29
30 for block in func.layout.iter_block() {
31 if let Some(last_insn) = func.layout.last_insn_of(block) {
32 self.add_critical_edges(last_insn, func, cfg);
33 }
34 }
35
36 let edges = std::mem::take(&mut self.critical_edges);
37 for edge in edges {
38 self.split_edge(edge, func, cfg);
39 }
40 }
41
42 pub fn clear(&mut self) {
43 self.critical_edges.clear();
44 }
45
46 fn add_critical_edges(&mut self, insn: Insn, func: &Function, cfg: &ControlFlowGraph) {
47 let branch_info = func.dfg.analyze_branch(insn);
48 if branch_info.dests_num() < 2 {
49 return;
50 }
51
52 for dest in branch_info.iter_dests() {
53 if cfg.pred_num_of(dest) > 1 {
54 self.critical_edges.push(CriticalEdge::new(insn, dest));
55 }
56 }
57 }
58
59 fn split_edge(&mut self, edge: CriticalEdge, func: &mut Function, cfg: &mut ControlFlowGraph) {
60 let insn = edge.insn;
61 let original_dest = edge.to;
62 let source_block = func.layout.insn_block(insn);
63
64 let inserted_dest = func.dfg.make_block();
67 let jump = func.dfg.make_insn(InsnData::jump(original_dest));
68 let mut cursor = InsnInserter::new(func, CursorLocation::BlockTop(original_dest));
69 cursor.append_block(inserted_dest);
70 cursor.set_loc(CursorLocation::BlockTop(inserted_dest));
71 cursor.append_insn(jump);
72
73 func.dfg
75 .rewrite_branch_dest(insn, original_dest, inserted_dest);
76 self.modify_cfg(cfg, source_block, original_dest, inserted_dest);
77 self.modify_phi_blocks(func, original_dest, inserted_dest);
78 }
79
80 fn modify_phi_blocks(&self, func: &mut Function, original_dest: Block, inserted_dest: Block) {
81 for insn in func.layout.iter_insn(original_dest) {
82 if !func.dfg.is_phi(insn) {
83 continue;
84 }
85
86 for block in func.dfg.phi_blocks_mut(insn) {
87 if *block == original_dest {
88 *block = inserted_dest;
89 }
90 }
91 }
92 }
93
94 fn modify_cfg(
95 &self,
96 cfg: &mut ControlFlowGraph,
97 source_block: Block,
98 original_dest: Block,
99 inserted_dest: Block,
100 ) {
101 cfg.remove_edge(source_block, original_dest);
102 cfg.add_edge(source_block, inserted_dest);
103 cfg.add_edge(inserted_dest, original_dest);
104 }
105}
106
107#[derive(Debug)]
108struct CriticalEdge {
109 insn: Insn,
110 to: Block,
111}
112
113impl CriticalEdge {
114 fn new(insn: Insn, to: Block) -> Self {
115 Self { insn, to }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use sonatina_ir::{builder::test_util::*, Type};
123
124 #[test]
125 fn critical_edge_basic() {
126 let mut test_module_builder = TestModuleBuilder::new();
127 let mut builder = test_module_builder.func_builder(&[], Type::Void);
128
129 let a = builder.append_block();
130 let b = builder.append_block();
131 let c = builder.append_block();
132
133 builder.switch_to_block(a);
134 let v0 = builder.make_imm_value(1i32);
135 builder.br(v0, c, b);
136
137 builder.switch_to_block(b);
138 builder.jump(c);
139
140 builder.switch_to_block(c);
141 builder.ret(None);
142
143 builder.seal_all();
144 let func_ref = builder.finish();
145
146 let mut module = test_module_builder.build();
147 let func = &mut module.funcs[func_ref];
148 let mut cfg = ControlFlowGraph::default();
149 cfg.compute(func);
150 CriticalEdgeSplitter::new().run(func, &mut cfg);
151
152 assert_eq!(
153 dump_func(func),
154 "func public %test_func() -> void:
155 block0:
156 br 1.i32 block3 block1;
157
158 block1:
159 jump block2;
160
161 block2:
162 return;
163
164 block3:
165 jump block2;
166
167"
168 );
169
170 let mut cfg_split = ControlFlowGraph::default();
171 cfg_split.compute(func);
172 assert_eq!(cfg, cfg_split);
173 }
174
175 #[test]
176 #[allow(clippy::many_single_char_names)]
177 fn critical_edge_to_same_block() {
178 let mut test_module_builder = TestModuleBuilder::new();
179 let mut builder = test_module_builder.func_builder(&[], Type::Void);
180
181 let a = builder.append_block();
182 let b = builder.append_block();
183 let c = builder.append_block();
184 let d = builder.append_block();
185 let e = builder.append_block();
186
187 builder.switch_to_block(a);
188 let v0 = builder.make_imm_value(1i8);
189 builder.br(v0, d, b);
190
191 builder.switch_to_block(b);
192 builder.jump(c);
193
194 builder.switch_to_block(c);
195 builder.br(v0, e, d);
196
197 builder.switch_to_block(d);
198 builder.ret(None);
199
200 builder.switch_to_block(e);
201 builder.ret(None);
202
203 builder.seal_all();
204 let func_ref = builder.finish();
205
206 let mut module = test_module_builder.build();
207 let func = &mut module.funcs[func_ref];
208 let mut cfg = ControlFlowGraph::default();
209 cfg.compute(func);
210 CriticalEdgeSplitter::new().run(func, &mut cfg);
211
212 assert_eq!(
213 dump_func(func),
214 "func public %test_func() -> void:
215 block0:
216 br 1.i8 block5 block1;
217
218 block1:
219 jump block2;
220
221 block2:
222 br 1.i8 block4 block6;
223
224 block3:
225 return;
226
227 block4:
228 return;
229
230 block5:
231 jump block3;
232
233 block6:
234 jump block3;
235
236"
237 );
238
239 let mut cfg_split = ControlFlowGraph::default();
240 cfg_split.compute(func);
241 assert_eq!(cfg, cfg_split);
242 }
243
244 #[test]
245 fn critical_edge_phi() {
246 let mut test_module_builder = TestModuleBuilder::new();
247 let mut builder = test_module_builder.func_builder(&[], Type::Void);
248
249 let a = builder.append_block();
250 let b = builder.append_block();
251 let c = builder.append_block();
252
253 builder.switch_to_block(a);
254 let v1 = builder.make_imm_value(1i8);
255 builder.jump(b);
256
257 builder.switch_to_block(b);
258 let phi_value = builder.phi(&[(v1, a)]);
259 let v2 = builder.add(phi_value, v1);
260 builder.append_phi_arg(phi_value, v2, b);
261 builder.br(phi_value, c, b);
262
263 builder.switch_to_block(c);
264 builder.ret(None);
265
266 builder.seal_all();
267 let func_ref = builder.finish();
268
269 let mut module = test_module_builder.build();
270 let func = &mut module.funcs[func_ref];
271 let mut cfg = ControlFlowGraph::default();
272 cfg.compute(func);
273 CriticalEdgeSplitter::new().run(func, &mut cfg);
274
275 assert_eq!(
276 dump_func(func),
277 "func public %test_func() -> void:
278 block0:
279 jump block1;
280
281 block1:
282 v1.i8 = phi (1.i8 block0) (v2 block3);
283 v2.i8 = add v1 1.i8;
284 br v1 block2 block3;
285
286 block2:
287 return;
288
289 block3:
290 jump block1;
291
292"
293 );
294
295 let mut cfg_split = ControlFlowGraph::default();
296 cfg_split.compute(func);
297 assert_eq!(cfg, cfg_split);
298 }
299
300 #[test]
301 fn critical_edge_br_table() {
302 let mut test_module_builder = TestModuleBuilder::new();
303 let mut builder = test_module_builder.func_builder(&[], Type::Void);
304
305 let a = builder.append_block();
306 let b = builder.append_block();
307 let c = builder.append_block();
308 let d = builder.append_block();
309 let e = builder.append_block();
310
311 builder.switch_to_block(a);
312 let cond = builder.make_imm_value(true);
313 builder.br(cond, b, e);
314
315 builder.switch_to_block(b);
316 let v0 = builder.make_imm_value(0i32);
317 let v1 = builder.make_imm_value(1i32);
318 let v2 = builder.make_imm_value(2i32);
319 builder.br_table(v0, Some(c), &[(v1, d), (v2, e)]);
320
321 builder.switch_to_block(c);
322 builder.jump(b);
323
324 builder.switch_to_block(d);
325 builder.ret(None);
326
327 builder.switch_to_block(e);
328 builder.ret(None);
329
330 builder.seal_all();
331 let func_ref = builder.finish();
332
333 let mut module = test_module_builder.build();
334 let func = &mut module.funcs[func_ref];
335 let mut cfg = ControlFlowGraph::default();
336 cfg.compute(func);
337 CriticalEdgeSplitter::new().run(func, &mut cfg);
338
339 assert_eq!(
340 dump_func(func),
341 "func public %test_func() -> void:
342 block0:
343 br -1.i1 block5 block6;
344
345 block1:
346 br_table 0.i32 block2 (1.i32 block3) (2.i32 block7);
347
348 block2:
349 jump block1;
350
351 block3:
352 return;
353
354 block4:
355 return;
356
357 block5:
358 jump block1;
359
360 block6:
361 jump block4;
362
363 block7:
364 jump block4;
365
366"
367 );
368
369 let mut cfg_split = ControlFlowGraph::default();
370 cfg_split.compute(func);
371 assert_eq!(cfg, cfg_split);
372 }
373}