1use llvm_ir::{BlockId, Function};
8
9pub struct Cfg {
27 num_blocks: usize,
29 succs: Vec<Vec<BlockId>>,
31 preds: Vec<Vec<BlockId>>,
33 reachable: Vec<bool>,
35 reachable_count: usize,
37}
38
39impl Cfg {
40 pub fn compute(func: &Function) -> Self {
42 let n = func.num_blocks();
43 let mut succs = vec![Vec::new(); n];
44 let mut preds = vec![Vec::new(); n];
45
46 for (bi, bb) in func.blocks.iter().enumerate() {
47 let src = BlockId(bi as u32);
48 if let Some(tid) = bb.terminator {
49 for dst in func.instr(tid).kind.successors() {
50 succs[bi].push(dst);
51 preds[dst.0 as usize].push(src);
52 }
53 }
54 }
55
56 let mut reachable = vec![false; n];
58 let mut reachable_count = 0;
59 if n > 0 {
60 let mut stack = vec![0usize];
61 while let Some(b) = stack.pop() {
62 if reachable[b] {
63 continue;
64 }
65 reachable[b] = true;
66 reachable_count += 1;
67 for &succ in &succs[b] {
68 stack.push(succ.0 as usize);
69 }
70 }
71 }
72
73 Cfg {
74 num_blocks: n,
75 succs,
76 preds,
77 reachable,
78 reachable_count,
79 }
80 }
81
82 pub fn entry(&self) -> BlockId {
84 BlockId(0)
85 }
86
87 pub fn num_blocks(&self) -> usize {
92 self.num_blocks
93 }
94
95 pub fn num_reachable_blocks(&self) -> usize {
100 self.reachable_count
101 }
102
103 pub fn is_reachable(&self, bid: BlockId) -> bool {
111 self.reachable[bid.0 as usize]
112 }
113
114 pub fn successors(&self, bid: BlockId) -> &[BlockId] {
116 &self.succs[bid.0 as usize]
117 }
118
119 pub fn predecessors(&self, bid: BlockId) -> &[BlockId] {
121 &self.preds[bid.0 as usize]
122 }
123
124 pub fn post_order(&self) -> Vec<BlockId> {
129 let mut visited = vec![false; self.num_blocks];
130 let mut order = Vec::with_capacity(self.num_blocks);
131 self.dfs_post(self.entry(), &mut visited, &mut order);
132 order
133 }
134
135 pub fn rpo(&self) -> Vec<BlockId> {
141 let mut order = self.post_order();
142 order.reverse();
143 order
144 }
145
146 fn dfs_post(&self, bid: BlockId, visited: &mut Vec<bool>, order: &mut Vec<BlockId>) {
147 let idx = bid.0 as usize;
148 if visited[idx] {
149 return;
150 }
151 visited[idx] = true;
152 for &succ in &self.succs[idx] {
153 self.dfs_post(succ, visited, order);
154 }
155 order.push(bid);
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use llvm_ir::{BasicBlock, Context, Function, InstrKind, Instruction, Linkage, ValueRef};
163
164 fn build_func(num_blocks: usize, edges: &[(usize, Vec<usize>)]) -> (Context, Function) {
168 let mut ctx = Context::new();
169 let fn_ty = ctx.mk_fn_type(ctx.void_ty, vec![], false);
170 let mut func = Function::new("test", fn_ty, vec![], Linkage::External);
171
172 for i in 0..num_blocks {
173 func.add_block(BasicBlock::new(format!("b{}", i)));
174 }
175
176 let mut has_term = vec![false; num_blocks];
177 for &(src, ref dsts) in edges {
178 has_term[src] = true;
179 let kind = match dsts.as_slice() {
180 [] => InstrKind::Unreachable,
181 [dst] => InstrKind::Br {
182 dest: BlockId(*dst as u32),
183 },
184 [t, f] => {
185 let cond = ValueRef::Constant(ctx.const_int(ctx.i1_ty, 0));
186 InstrKind::CondBr {
187 cond,
188 then_dest: BlockId(*t as u32),
189 else_dest: BlockId(*f as u32),
190 }
191 }
192 _ => panic!("test only supports 0/1/2 successors"),
193 };
194 let iid = func.alloc_instr(Instruction {
195 name: None,
196 ty: ctx.void_ty,
197 kind,
198 });
199 func.blocks[src].set_terminator(iid);
200 }
201 for (i, &needs_term) in has_term.iter().enumerate() {
202 if !needs_term {
203 let iid = func.alloc_instr(Instruction {
204 name: None,
205 ty: ctx.void_ty,
206 kind: InstrKind::Unreachable,
207 });
208 func.blocks[i].set_terminator(iid);
209 }
210 }
211 (ctx, func)
212 }
213
214 #[test]
215 fn cfg_single_block() {
216 let (_ctx, func) = build_func(1, &[(0, vec![])]);
217 let cfg = Cfg::compute(&func);
218 assert_eq!(cfg.num_blocks(), 1);
219 assert!(cfg.successors(BlockId(0)).is_empty());
220 assert!(cfg.predecessors(BlockId(0)).is_empty());
221 assert_eq!(cfg.rpo(), vec![BlockId(0)]);
222 }
223
224 #[test]
225 fn cfg_linear() {
226 let (_ctx, func) = build_func(3, &[(0, vec![1]), (1, vec![2]), (2, vec![])]);
227 let cfg = Cfg::compute(&func);
228 assert_eq!(cfg.successors(BlockId(0)), &[BlockId(1)]);
229 assert_eq!(cfg.successors(BlockId(1)), &[BlockId(2)]);
230 assert!(cfg.successors(BlockId(2)).is_empty());
231 assert!(cfg.predecessors(BlockId(0)).is_empty());
232 assert_eq!(cfg.predecessors(BlockId(2)), &[BlockId(1)]);
233 assert_eq!(cfg.rpo(), vec![BlockId(0), BlockId(1), BlockId(2)]);
234 }
235
236 #[test]
237 fn cfg_diamond() {
238 let (_ctx, func) = build_func(
240 4,
241 &[(0, vec![1, 2]), (1, vec![3]), (2, vec![3]), (3, vec![])],
242 );
243 let cfg = Cfg::compute(&func);
244 assert_eq!(cfg.successors(BlockId(0)).len(), 2);
245 assert_eq!(cfg.predecessors(BlockId(3)).len(), 2);
246 let rpo = cfg.rpo();
247 assert_eq!(rpo[0], BlockId(0));
248 assert_eq!(*rpo.last().unwrap(), BlockId(3));
249 }
250
251 #[test]
252 fn cfg_loop() {
253 let (_ctx, func) = build_func(
255 4,
256 &[(0, vec![1]), (1, vec![2]), (2, vec![1, 3]), (3, vec![])],
257 );
258 let cfg = Cfg::compute(&func);
259 assert!(cfg.predecessors(BlockId(1)).contains(&BlockId(2)));
260 assert_eq!(cfg.rpo().len(), 4);
261 }
262
263 #[test]
264 fn cfg_unreachable_block() {
265 let (_ctx, func) = build_func(3, &[(0, vec![1]), (1, vec![]), (2, vec![])]);
267 let cfg = Cfg::compute(&func);
268 let rpo = cfg.rpo();
269 assert_eq!(rpo.len(), 2);
270 assert!(!rpo.contains(&BlockId(2)));
271 }
272
273 #[test]
274 fn cfg_is_reachable() {
275 let (_ctx, func) = build_func(3, &[(0, vec![1]), (1, vec![]), (2, vec![])]);
277 let cfg = Cfg::compute(&func);
278 assert!(cfg.is_reachable(BlockId(0)));
279 assert!(cfg.is_reachable(BlockId(1)));
280 assert!(!cfg.is_reachable(BlockId(2)));
281 }
282
283 #[test]
284 fn cfg_num_reachable_blocks() {
285 let (_ctx, func) = build_func(4, &[(0, vec![1]), (1, vec![]), (2, vec![3]), (3, vec![])]);
287 let cfg = Cfg::compute(&func);
288 assert_eq!(cfg.num_blocks(), 4);
289 assert_eq!(cfg.num_reachable_blocks(), 2);
290 assert_eq!(cfg.num_reachable_blocks(), cfg.rpo().len());
292 }
293
294 #[test]
295 fn cfg_empty_function_reachable_count() {
296 let mut ctx = Context::new();
298 let fn_ty = ctx.mk_fn_type(ctx.void_ty, vec![], false);
299 let func = Function::new("empty", fn_ty, vec![], Linkage::External);
300 let cfg = Cfg::compute(&func);
301 assert_eq!(cfg.num_blocks(), 0);
302 assert_eq!(cfg.num_reachable_blocks(), 0);
303 }
304}