Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/traversals.rs
1693 views
1
//! Traversals over the IR.
2
3
use crate::ir;
4
use alloc::vec::Vec;
5
use core::fmt::Debug;
6
use core::hash::Hash;
7
use 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)]
12
pub 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)]
37
pub struct Dfs {
38
stack: Vec<(Event, ir::Block)>,
39
seen: EntitySet<ir::Block>,
40
}
41
42
impl 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.
86
pub struct DfsIter<'a> {
87
dfs: &'a mut Dfs,
88
func: &'a ir::Function,
89
}
90
91
impl Iterator for DfsIter<'_> {
92
type Item = (Event, ir::Block);
93
94
fn next(&mut self) -> Option<(Event, ir::Block)> {
95
loop {
96
let (event, block) = self.dfs.stack.pop()?;
97
98
if event == Event::Enter {
99
let first_time_seeing = self.dfs.seen.insert(block);
100
if !first_time_seeing {
101
continue;
102
}
103
104
self.dfs.stack.push((Event::Exit, block));
105
self.dfs.stack.extend(
106
self.func
107
.block_successors(block)
108
// Heuristic: chase the children in reverse. This puts
109
// the first successor block first in the postorder, all
110
// other things being equal, which tends to prioritize
111
// loop backedges over out-edges, putting the edge-block
112
// closer to the loop body and minimizing live-ranges in
113
// linear instruction space. This heuristic doesn't have
114
// any effect on the computation of dominators, and is
115
// purely for other consumers of the postorder we cache
116
// here.
117
.rev()
118
// This is purely an optimization to avoid additional
119
// iterations of the loop, and is not required; it's
120
// merely inlining the check from the outer conditional
121
// of this case to avoid the extra loop iteration. This
122
// also avoids potential excess stack growth.
123
.filter(|block| !self.dfs.seen.contains(*block))
124
.map(|block| (Event::Enter, block)),
125
);
126
}
127
128
return Some((event, block));
129
}
130
}
131
}
132
133
/// An iterator that yields `ir::Block` items during a depth-first, pre-order
134
/// traversal over its associated function.
135
pub struct DfsPreOrderIter<'a>(DfsIter<'a>);
136
137
impl Iterator for DfsPreOrderIter<'_> {
138
type Item = ir::Block;
139
140
fn next(&mut self) -> Option<Self::Item> {
141
loop {
142
match self.0.next()? {
143
(Event::Enter, b) => return Some(b),
144
(Event::Exit, _) => continue,
145
}
146
}
147
}
148
}
149
150
/// An iterator that yields `ir::Block` items during a depth-first, post-order
151
/// traversal over its associated function.
152
pub struct DfsPostOrderIter<'a>(DfsIter<'a>);
153
154
impl Iterator for DfsPostOrderIter<'_> {
155
type Item = ir::Block;
156
157
fn next(&mut self) -> Option<Self::Item> {
158
loop {
159
match self.0.next()? {
160
(Event::Exit, b) => return Some(b),
161
(Event::Enter, _) => continue,
162
}
163
}
164
}
165
}
166
167
#[cfg(test)]
168
mod tests {
169
use super::*;
170
use crate::cursor::{Cursor, FuncCursor};
171
use crate::ir::{Function, InstBuilder, TrapCode, types::I32};
172
173
#[test]
174
fn test_dfs_traversal() {
175
let _ = env_logger::try_init();
176
177
let mut func = Function::new();
178
179
let block0 = func.dfg.make_block();
180
let v0 = func.dfg.append_block_param(block0, I32);
181
let block1 = func.dfg.make_block();
182
let block2 = func.dfg.make_block();
183
let block3 = func.dfg.make_block();
184
185
let mut cur = FuncCursor::new(&mut func);
186
187
// block0(v0):
188
// br_if v0, block2, trap_block
189
cur.insert_block(block0);
190
cur.ins().brif(v0, block2, &[], block3, &[]);
191
192
// block3:
193
// trap user0
194
cur.insert_block(block3);
195
cur.ins().trap(TrapCode::unwrap_user(1));
196
197
// block1:
198
// v1 = iconst.i32 1
199
// v2 = iadd v0, v1
200
// jump block0(v2)
201
cur.insert_block(block1);
202
let v1 = cur.ins().iconst(I32, 1);
203
let v2 = cur.ins().iadd(v0, v1);
204
cur.ins().jump(block0, &[v2.into()]);
205
206
// block2:
207
// return v0
208
cur.insert_block(block2);
209
cur.ins().return_(&[v0]);
210
211
let mut dfs = Dfs::new();
212
213
assert_eq!(
214
dfs.iter(&func).collect::<Vec<_>>(),
215
vec![
216
(Event::Enter, block0),
217
(Event::Enter, block2),
218
(Event::Exit, block2),
219
(Event::Enter, block3),
220
(Event::Exit, block3),
221
(Event::Exit, block0)
222
],
223
);
224
}
225
226
#[test]
227
fn multiple_successors_to_the_same_block() {
228
let _ = env_logger::try_init();
229
230
let mut func = Function::new();
231
232
let block0 = func.dfg.make_block();
233
let block1 = func.dfg.make_block();
234
235
let mut cur = FuncCursor::new(&mut func);
236
237
// block0(v0):
238
// v1 = iconst.i32 36
239
// v2 = iconst.i32 42
240
// br_if v0, block1(v1), block1(v2)
241
cur.insert_block(block0);
242
let v0 = cur.func.dfg.append_block_param(block0, I32);
243
let v1 = cur.ins().iconst(ir::types::I32, 36);
244
let v2 = cur.ins().iconst(ir::types::I32, 42);
245
cur.ins()
246
.brif(v0, block1, &[v1.into()], block1, &[v2.into()]);
247
248
// block1(v3: i32):
249
// return v3
250
cur.insert_block(block1);
251
let v3 = cur.func.dfg.append_block_param(block1, I32);
252
cur.ins().return_(&[v3]);
253
254
let mut dfs = Dfs::new();
255
256
// We should only enter `block1` once.
257
assert_eq!(
258
dfs.iter(&func).collect::<Vec<_>>(),
259
vec![
260
(Event::Enter, block0),
261
(Event::Enter, block1),
262
(Event::Exit, block1),
263
(Event::Exit, block0),
264
],
265
);
266
267
// We should only iterate over `block1` once in a pre-order traversal.
268
assert_eq!(
269
dfs.pre_order_iter(&func).collect::<Vec<_>>(),
270
vec![block0, block1],
271
);
272
273
// We should only iterate over `block1` once in a post-order traversal.
274
assert_eq!(
275
dfs.post_order_iter(&func).collect::<Vec<_>>(),
276
vec![block1, block0],
277
);
278
}
279
}
280
281