@@ -92,34 +92,41 @@ impl Iterator for DfsIter<'_> {
92
92
type Item = ( Event , ir:: Block ) ;
93
93
94
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
- }
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
+ }
121
127
122
- Some ( ( event, block) )
128
+ return Some ( ( event, block) ) ;
129
+ }
123
130
}
124
131
}
125
132
@@ -215,4 +222,58 @@ mod tests {
215
222
] ,
216
223
) ;
217
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
+ }
218
279
}
0 commit comments