cranelift_codegen/traversals.rs
1//! Traversals over the IR.
2
3use crate::ir;
4use alloc::vec::Vec;
5use core::fmt::Debug;
6use core::hash::Hash;
7use cranelift_entity::EntitySet;
8
9/// A low-level DFS traversal event: either entering or exiting the traversal of
10/// a block.
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub enum Event {
13 /// Entering traversal of a block.
14 ///
15 /// Processing a block upon this event corresponds to a pre-order,
16 /// depth-first traversal.
17 Enter,
18
19 /// Exiting traversal of a block.
20 ///
21 /// Processing a block upon this event corresponds to a post-order,
22 /// depth-first traversal.
23 Exit,
24}
25
26/// A depth-first traversal.
27///
28/// This is a fairly low-level traversal type, and is generally intended to be
29/// used as a building block for making specific pre-order or post-order
30/// traversals for whatever problem is at hand.
31///
32/// This type may be reused multiple times across different passes or functions
33/// and will internally reuse any heap allocations its already made.
34///
35/// Traversal is not recursive.
36#[derive(Debug, Default, Clone)]
37pub struct Dfs {
38 stack: Vec<(Event, ir::Block)>,
39 seen: EntitySet<ir::Block>,
40}
41
42impl Dfs {
43 /// Construct a new depth-first traversal.
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 /// Perform a depth-first traversal over the given function.
49 ///
50 /// Yields pairs of `(Event, ir::Block)`.
51 ///
52 /// This iterator can be used to perform either pre- or post-order
53 /// traversals, or a combination of the two.
54 pub fn iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsIter<'a> {
55 self.clear();
56 if let Some(e) = func.layout.entry_block() {
57 self.stack.push((Event::Enter, e));
58 }
59 DfsIter { dfs: self, func }
60 }
61
62 /// Perform a pre-order traversal over the given function.
63 ///
64 /// Yields `ir::Block` items.
65 pub fn pre_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPreOrderIter<'a> {
66 DfsPreOrderIter(self.iter(func))
67 }
68
69 /// Perform a post-order traversal over the given function.
70 ///
71 /// Yields `ir::Block` items.
72 pub fn post_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPostOrderIter<'a> {
73 DfsPostOrderIter(self.iter(func))
74 }
75
76 /// Clear this DFS, but keep its allocations for future reuse.
77 pub fn clear(&mut self) {
78 let Dfs { stack, seen } = self;
79 stack.clear();
80 seen.clear();
81 }
82}
83
84/// An iterator that yields pairs of `(Event, ir::Block)` items as it performs a
85/// depth-first traversal over its associated function.
86pub struct DfsIter<'a> {
87 dfs: &'a mut Dfs,
88 func: &'a ir::Function,
89}
90
91impl Iterator for DfsIter<'_> {
92 type Item = (Event, ir::Block);
93
94 fn next(&mut self) -> Option<(Event, ir::Block)> {
95 let (event, block) = self.dfs.stack.pop()?;
96
97 if event == Event::Enter && self.dfs.seen.insert(block) {
98 self.dfs.stack.push((Event::Exit, block));
99 self.dfs.stack.extend(
100 self.func
101 .block_successors(block)
102 // Heuristic: chase the children in reverse. This puts
103 // the first successor block first in the postorder, all
104 // other things being equal, which tends to prioritize
105 // loop backedges over out-edges, putting the edge-block
106 // closer to the loop body and minimizing live-ranges in
107 // linear instruction space. This heuristic doesn't have
108 // any effect on the computation of dominators, and is
109 // purely for other consumers of the postorder we cache
110 // here.
111 .rev()
112 // This is purely an optimization to avoid additional
113 // iterations of the loop, and is not required; it's
114 // merely inlining the check from the outer conditional
115 // of this case to avoid the extra loop iteration. This
116 // also avoids potential excess stack growth.
117 .filter(|block| !self.dfs.seen.contains(*block))
118 .map(|block| (Event::Enter, block)),
119 );
120 }
121
122 Some((event, block))
123 }
124}
125
126/// An iterator that yields `ir::Block` items during a depth-first, pre-order
127/// traversal over its associated function.
128pub struct DfsPreOrderIter<'a>(DfsIter<'a>);
129
130impl Iterator for DfsPreOrderIter<'_> {
131 type Item = ir::Block;
132
133 fn next(&mut self) -> Option<Self::Item> {
134 loop {
135 match self.0.next()? {
136 (Event::Enter, b) => return Some(b),
137 (Event::Exit, _) => continue,
138 }
139 }
140 }
141}
142
143/// An iterator that yields `ir::Block` items during a depth-first, post-order
144/// traversal over its associated function.
145pub struct DfsPostOrderIter<'a>(DfsIter<'a>);
146
147impl Iterator for DfsPostOrderIter<'_> {
148 type Item = ir::Block;
149
150 fn next(&mut self) -> Option<Self::Item> {
151 loop {
152 match self.0.next()? {
153 (Event::Exit, b) => return Some(b),
154 (Event::Enter, _) => continue,
155 }
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::cursor::{Cursor, FuncCursor};
164 use crate::ir::{types::I32, Function, InstBuilder, TrapCode};
165
166 #[test]
167 fn test_dfs_traversal() {
168 let _ = env_logger::try_init();
169
170 let mut func = Function::new();
171
172 let block0 = func.dfg.make_block();
173 let v0 = func.dfg.append_block_param(block0, I32);
174 let block1 = func.dfg.make_block();
175 let block2 = func.dfg.make_block();
176 let block3 = func.dfg.make_block();
177
178 let mut cur = FuncCursor::new(&mut func);
179
180 // block0(v0):
181 // br_if v0, block2, trap_block
182 cur.insert_block(block0);
183 cur.ins().brif(v0, block2, &[], block3, &[]);
184
185 // block3:
186 // trap user0
187 cur.insert_block(block3);
188 cur.ins().trap(TrapCode::unwrap_user(1));
189
190 // block1:
191 // v1 = iconst.i32 1
192 // v2 = iadd v0, v1
193 // jump block0(v2)
194 cur.insert_block(block1);
195 let v1 = cur.ins().iconst(I32, 1);
196 let v2 = cur.ins().iadd(v0, v1);
197 cur.ins().jump(block0, &[v2]);
198
199 // block2:
200 // return v0
201 cur.insert_block(block2);
202 cur.ins().return_(&[v0]);
203
204 let mut dfs = Dfs::new();
205
206 assert_eq!(
207 dfs.iter(&func).collect::<Vec<_>>(),
208 vec![
209 (Event::Enter, block0),
210 (Event::Enter, block2),
211 (Event::Exit, block2),
212 (Event::Enter, block3),
213 (Event::Exit, block3),
214 (Event::Exit, block0)
215 ],
216 );
217 }
218}