Coverage Report

Created: 2026-02-14 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/cranelift-codegen-0.128.3/src/traversals.rs
Line
Count
Source
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
430k
    pub fn new() -> Self {
45
430k
        Self::default()
46
430k
    }
<cranelift_codegen::traversals::Dfs>::new
Line
Count
Source
44
96.6k
    pub fn new() -> Self {
45
96.6k
        Self::default()
46
96.6k
    }
<cranelift_codegen::traversals::Dfs>::new
Line
Count
Source
44
333k
    pub fn new() -> Self {
45
333k
        Self::default()
46
333k
    }
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
430k
    pub fn iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsIter<'a> {
55
430k
        self.clear();
56
430k
        if let Some(e) = func.layout.entry_block() {
57
430k
            self.stack.push((Event::Enter, e));
58
430k
        }
59
430k
        DfsIter { dfs: self, func }
60
430k
    }
<cranelift_codegen::traversals::Dfs>::iter
Line
Count
Source
54
96.6k
    pub fn iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsIter<'a> {
55
96.6k
        self.clear();
56
96.6k
        if let Some(e) = func.layout.entry_block() {
57
96.6k
            self.stack.push((Event::Enter, e));
58
96.6k
        }
59
96.6k
        DfsIter { dfs: self, func }
60
96.6k
    }
<cranelift_codegen::traversals::Dfs>::iter
Line
Count
Source
54
333k
    pub fn iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsIter<'a> {
55
333k
        self.clear();
56
333k
        if let Some(e) = func.layout.entry_block() {
57
333k
            self.stack.push((Event::Enter, e));
58
333k
        }
59
333k
        DfsIter { dfs: self, func }
60
333k
    }
61
62
    /// Perform a pre-order traversal over the given function.
63
    ///
64
    /// Yields `ir::Block` items.
65
430k
    pub fn pre_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPreOrderIter<'a> {
66
430k
        DfsPreOrderIter(self.iter(func))
67
430k
    }
<cranelift_codegen::traversals::Dfs>::pre_order_iter
Line
Count
Source
65
96.6k
    pub fn pre_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPreOrderIter<'a> {
66
96.6k
        DfsPreOrderIter(self.iter(func))
67
96.6k
    }
<cranelift_codegen::traversals::Dfs>::pre_order_iter
Line
Count
Source
65
333k
    pub fn pre_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPreOrderIter<'a> {
66
333k
        DfsPreOrderIter(self.iter(func))
67
333k
    }
68
69
    /// Perform a post-order traversal over the given function.
70
    ///
71
    /// Yields `ir::Block` items.
72
0
    pub fn post_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPostOrderIter<'a> {
73
0
        DfsPostOrderIter(self.iter(func))
74
0
    }
Unexecuted instantiation: <cranelift_codegen::traversals::Dfs>::post_order_iter
Unexecuted instantiation: <cranelift_codegen::traversals::Dfs>::post_order_iter
75
76
    /// Clear this DFS, but keep its allocations for future reuse.
77
860k
    pub fn clear(&mut self) {
78
860k
        let Dfs { stack, seen } = self;
79
860k
        stack.clear();
80
860k
        seen.clear();
81
860k
    }
<cranelift_codegen::traversals::Dfs>::clear
Line
Count
Source
77
193k
    pub fn clear(&mut self) {
78
193k
        let Dfs { stack, seen } = self;
79
193k
        stack.clear();
80
193k
        seen.clear();
81
193k
    }
<cranelift_codegen::traversals::Dfs>::clear
Line
Count
Source
77
667k
    pub fn clear(&mut self) {
78
667k
        let Dfs { stack, seen } = self;
79
667k
        stack.clear();
80
667k
        seen.clear();
81
667k
    }
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
4.50M
    fn next(&mut self) -> Option<(Event, ir::Block)> {
95
        loop {
96
4.60M
            let (event, block) = self.dfs.stack.pop()?;
97
98
4.17M
            if event == Event::Enter {
99
2.14M
                let first_time_seeing = self.dfs.seen.insert(block);
100
2.14M
                if !first_time_seeing {
101
106k
                    continue;
102
2.03M
                }
103
104
2.03M
                self.dfs.stack.push((Event::Exit, block));
105
2.03M
                self.dfs.stack.extend(
106
2.03M
                    self.func
107
2.03M
                        .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
2.03M
                        .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
2.03M
                        .filter(|block| !self.dfs.seen.contains(*block))
<cranelift_codegen::traversals::DfsIter as core::iter::traits::iterator::Iterator>::next::{closure#0}
Line
Count
Source
123
158k
                        .filter(|block| !self.dfs.seen.contains(*block))
<cranelift_codegen::traversals::DfsIter as core::iter::traits::iterator::Iterator>::next::{closure#0}
Line
Count
Source
123
1.70M
                        .filter(|block| !self.dfs.seen.contains(*block))
124
2.03M
                        .map(|block| (Event::Enter, block)),
<cranelift_codegen::traversals::DfsIter as core::iter::traits::iterator::Iterator>::next::{closure#1}
Line
Count
Source
124
135k
                        .map(|block| (Event::Enter, block)),
<cranelift_codegen::traversals::DfsIter as core::iter::traits::iterator::Iterator>::next::{closure#1}
Line
Count
Source
124
1.57M
                        .map(|block| (Event::Enter, block)),
125
                );
126
2.03M
            }
127
128
4.07M
            return Some((event, block));
129
        }
130
4.50M
    }
<cranelift_codegen::traversals::DfsIter as core::iter::traits::iterator::Iterator>::next
Line
Count
Source
94
540k
    fn next(&mut self) -> Option<(Event, ir::Block)> {
95
        loop {
96
550k
            let (event, block) = self.dfs.stack.pop()?;
97
98
453k
            if event == Event::Enter {
99
231k
                let first_time_seeing = self.dfs.seen.insert(block);
100
231k
                if !first_time_seeing {
101
9.97k
                    continue;
102
221k
                }
103
104
221k
                self.dfs.stack.push((Event::Exit, block));
105
221k
                self.dfs.stack.extend(
106
221k
                    self.func
107
221k
                        .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
221k
                        .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
221k
                        .filter(|block| !self.dfs.seen.contains(*block))
124
221k
                        .map(|block| (Event::Enter, block)),
125
                );
126
221k
            }
127
128
443k
            return Some((event, block));
129
        }
130
540k
    }
<cranelift_codegen::traversals::DfsIter as core::iter::traits::iterator::Iterator>::next
Line
Count
Source
94
3.96M
    fn next(&mut self) -> Option<(Event, ir::Block)> {
95
        loop {
96
4.05M
            let (event, block) = self.dfs.stack.pop()?;
97
98
3.72M
            if event == Event::Enter {
99
1.90M
                let first_time_seeing = self.dfs.seen.insert(block);
100
1.90M
                if !first_time_seeing {
101
96.4k
                    continue;
102
1.81M
                }
103
104
1.81M
                self.dfs.stack.push((Event::Exit, block));
105
1.81M
                self.dfs.stack.extend(
106
1.81M
                    self.func
107
1.81M
                        .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
1.81M
                        .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
1.81M
                        .filter(|block| !self.dfs.seen.contains(*block))
124
1.81M
                        .map(|block| (Event::Enter, block)),
125
                );
126
1.81M
            }
127
128
3.62M
            return Some((event, block));
129
        }
130
3.96M
    }
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
2.46M
    fn next(&mut self) -> Option<Self::Item> {
141
        loop {
142
4.50M
            match self.0.next()? {
143
2.03M
                (Event::Enter, b) => return Some(b),
144
2.03M
                (Event::Exit, _) => continue,
145
            }
146
        }
147
2.46M
    }
<cranelift_codegen::traversals::DfsPreOrderIter as core::iter::traits::iterator::Iterator>::next
Line
Count
Source
140
318k
    fn next(&mut self) -> Option<Self::Item> {
141
        loop {
142
540k
            match self.0.next()? {
143
221k
                (Event::Enter, b) => return Some(b),
144
221k
                (Event::Exit, _) => continue,
145
            }
146
        }
147
318k
    }
<cranelift_codegen::traversals::DfsPreOrderIter as core::iter::traits::iterator::Iterator>::next
Line
Count
Source
140
2.14M
    fn next(&mut self) -> Option<Self::Item> {
141
        loop {
142
3.96M
            match self.0.next()? {
143
1.81M
                (Event::Enter, b) => return Some(b),
144
1.81M
                (Event::Exit, _) => continue,
145
            }
146
        }
147
2.14M
    }
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
0
    fn next(&mut self) -> Option<Self::Item> {
158
        loop {
159
0
            match self.0.next()? {
160
0
                (Event::Exit, b) => return Some(b),
161
0
                (Event::Enter, _) => continue,
162
            }
163
        }
164
0
    }
Unexecuted instantiation: <cranelift_codegen::traversals::DfsPostOrderIter as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <cranelift_codegen::traversals::DfsPostOrderIter as core::iter::traits::iterator::Iterator>::next
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
}