sonatina_codegen/
cfg.rs

1use std::collections::BTreeSet;
2
3use cranelift_entity::{packed_option::PackedOption, SecondaryMap};
4
5use sonatina_ir::{Block, Function, Insn};
6
7#[derive(Default, Debug, Clone, PartialEq, Eq)]
8pub struct ControlFlowGraph {
9    entry: PackedOption<Block>,
10    blocks: SecondaryMap<Block, BlockNode>,
11    pub(super) exits: smallvec::SmallVec<[Block; 8]>,
12}
13
14impl ControlFlowGraph {
15    pub fn new() -> Self {
16        Self::default()
17    }
18
19    pub fn compute(&mut self, func: &Function) {
20        self.clear();
21
22        self.entry = func.layout.entry_block().into();
23
24        for block in func.layout.iter_block() {
25            if let Some(last_insn) = func.layout.last_insn_of(block) {
26                self.analyze_insn(func, last_insn);
27            }
28        }
29    }
30
31    pub fn preds_of(&self, block: Block) -> impl Iterator<Item = &Block> {
32        self.blocks[block].preds()
33    }
34
35    pub fn succs_of(&self, block: Block) -> impl Iterator<Item = &Block> {
36        self.blocks[block].succs()
37    }
38
39    pub fn pred_num_of(&self, block: Block) -> usize {
40        self.blocks[block].pred_num()
41    }
42
43    pub fn succ_num_of(&self, block: Block) -> usize {
44        self.blocks[block].succ_num()
45    }
46
47    pub fn entry(&self) -> Option<Block> {
48        self.entry.expand()
49    }
50
51    pub fn post_order(&self) -> CfgPostOrder {
52        CfgPostOrder::new(self)
53    }
54
55    pub fn add_edge(&mut self, from: Block, to: Block) {
56        self.blocks[to].push_pred(from);
57        self.blocks[from].push_succ(to);
58    }
59
60    pub fn remove_edge(&mut self, from: Block, to: Block) {
61        self.blocks[to].remove_pred(from);
62        self.blocks[from].remove_succ(to);
63    }
64
65    pub(super) fn reverse_edges(&mut self, new_entry: Block, new_exits: &[Block]) {
66        for node in self.blocks.values_mut() {
67            node.reverse_edge();
68        }
69        self.entry = new_entry.into();
70        self.exits = new_exits.into();
71    }
72
73    pub fn clear(&mut self) {
74        self.entry = None.into();
75        self.blocks.clear();
76        self.exits.clear();
77    }
78
79    fn analyze_insn(&mut self, func: &Function, insn: Insn) {
80        if func.dfg.is_return(insn) {
81            let exit = func.layout.insn_block(insn);
82            self.exits.push(exit);
83        }
84
85        for dest in func.dfg.analyze_branch(insn).iter_dests() {
86            let block = func.layout.insn_block(insn);
87            self.add_edge(block, dest);
88        }
89    }
90}
91
92#[derive(Default, Clone, Debug, PartialEq, Eq)]
93struct BlockNode {
94    preds: BTreeSet<Block>,
95    succs: BTreeSet<Block>,
96}
97
98impl BlockNode {
99    fn push_pred(&mut self, pred: Block) {
100        self.preds.insert(pred);
101    }
102
103    fn push_succ(&mut self, succ: Block) {
104        self.succs.insert(succ);
105    }
106
107    fn remove_pred(&mut self, pred: Block) {
108        self.preds.remove(&pred);
109    }
110
111    fn remove_succ(&mut self, succ: Block) {
112        self.succs.remove(&succ);
113    }
114
115    fn preds(&self) -> impl Iterator<Item = &Block> {
116        self.preds.iter()
117    }
118
119    fn succs(&self) -> impl Iterator<Item = &Block> {
120        self.succs.iter()
121    }
122
123    fn pred_num(&self) -> usize {
124        self.preds.len()
125    }
126
127    fn succ_num(&self) -> usize {
128        self.succs.len()
129    }
130
131    fn reverse_edge(&mut self) {
132        std::mem::swap(&mut self.preds, &mut self.succs);
133    }
134}
135
136pub struct CfgPostOrder<'a> {
137    cfg: &'a ControlFlowGraph,
138    node_state: SecondaryMap<Block, NodeState>,
139    stack: Vec<Block>,
140}
141
142impl<'a> CfgPostOrder<'a> {
143    fn new(cfg: &'a ControlFlowGraph) -> Self {
144        let mut stack = Vec::new();
145
146        if let Some(entry) = cfg.entry() {
147            stack.push(entry);
148        }
149
150        Self {
151            cfg,
152            node_state: SecondaryMap::default(),
153            stack,
154        }
155    }
156}
157
158impl<'a> Iterator for CfgPostOrder<'a> {
159    type Item = Block;
160
161    fn next(&mut self) -> Option<Block> {
162        while let Some(&block) = self.stack.last() {
163            if self.node_state[block].is_unvisited() {
164                self.node_state[block].set_visited();
165                for &pred in self.cfg.succs_of(block) {
166                    if self.node_state[pred].is_unvisited() {
167                        self.stack.push(pred);
168                    }
169                }
170            } else {
171                self.stack.pop().unwrap();
172                if !self.node_state[block].has_finished() {
173                    self.node_state[block].set_finished();
174                    return Some(block);
175                }
176            }
177        }
178
179        None
180    }
181}
182
183#[derive(Default, Debug, Clone, Copy)]
184struct NodeState(u8);
185
186impl NodeState {
187    fn is_unvisited(self) -> bool {
188        self.0 == 0
189    }
190
191    fn has_finished(self) -> bool {
192        self.0 == 2
193    }
194
195    fn set_visited(&mut self) {
196        self.0 = 1;
197    }
198
199    fn set_finished(&mut self) {
200        self.0 = 2;
201    }
202}