@@ -21,28 +21,7 @@ impl SimplifyCfg {
21
21
SimplifyCfg
22
22
}
23
23
24
- fn remove_dead_blocks ( & self , mir : & mut Mir ) {
25
- let mut seen = vec ! [ false ; mir. basic_blocks. len( ) ] ;
26
-
27
- // These blocks are always required.
28
- seen[ START_BLOCK . index ( ) ] = true ;
29
- seen[ END_BLOCK . index ( ) ] = true ;
30
- seen[ DIVERGE_BLOCK . index ( ) ] = true ;
31
-
32
- let mut worklist = vec ! [ START_BLOCK ] ;
33
- while let Some ( bb) = worklist. pop ( ) {
34
- for succ in mir. basic_block_data ( bb) . terminator . successors ( ) {
35
- if !seen[ succ. index ( ) ] {
36
- seen[ succ. index ( ) ] = true ;
37
- worklist. push ( * succ) ;
38
- }
39
- }
40
- }
41
-
42
- util:: retain_basic_blocks ( mir, & seen) ;
43
- }
44
-
45
- fn remove_goto_chains ( & self , mir : & mut Mir ) -> bool {
24
+ fn merge_consecutive_blocks ( & self , mir : & mut Mir ) -> bool {
46
25
47
26
// Find the target at the end of the jump chain, return None if there is a loop
48
27
fn final_target ( mir : & Mir , mut target : BasicBlock ) -> Option < BasicBlock > {
@@ -65,25 +44,71 @@ impl SimplifyCfg {
65
44
Some ( target)
66
45
}
67
46
47
+ let mut predecessor_map = util:: build_predecessor_map ( mir) ;
48
+
68
49
let mut changed = false ;
69
- for bb in mir. all_basic_blocks ( ) {
50
+ let mut seen = vec ! [ false ; mir. basic_blocks. len( ) ] ;
51
+ let mut worklist = vec ! [ START_BLOCK ] ;
52
+ while let Some ( bb) = worklist. pop ( ) {
70
53
// Temporarily swap out the terminator we're modifying to keep borrowck happy
71
54
let mut terminator = Terminator :: Diverge ;
72
55
mem:: swap ( & mut terminator, & mut mir. basic_block_data_mut ( bb) . terminator ) ;
73
56
57
+ // Shortcut chains of empty blocks that just jump from one to the next
74
58
for target in terminator. successors_mut ( ) {
75
59
let new_target = match final_target ( mir, * target) {
76
60
Some ( new_target) => new_target,
77
61
None if mir. basic_block_data ( bb) . statements . is_empty ( ) => bb,
78
62
None => continue
79
63
} ;
80
- changed |= * target != new_target;
81
- * target = new_target;
64
+
65
+ if * target != new_target {
66
+ changed = true ;
67
+ predecessor_map. remove_predecessor ( * target, bb) ;
68
+ predecessor_map. add_predecessor ( new_target, bb) ;
69
+ * target = new_target;
70
+ }
82
71
}
83
72
84
- mir. basic_block_data_mut ( bb) . terminator = terminator;
73
+ // See if we can merge the target block into this one
74
+ match terminator {
75
+ Terminator :: Goto { target } if target. index ( ) > DIVERGE_BLOCK . index ( ) &&
76
+ predecessor_map. predecessors ( target) . len ( ) == 1 => {
77
+ changed = true ;
78
+ let mut other_data = BasicBlockData {
79
+ statements : Vec :: new ( ) ,
80
+ terminator : Terminator :: Goto { target : target}
81
+ } ;
82
+ mem:: swap ( & mut other_data, mir. basic_block_data_mut ( target) ) ;
83
+
84
+ predecessor_map. replace_predecessor ( target, bb, target) ;
85
+ for succ in other_data. terminator . successors ( ) {
86
+ predecessor_map. replace_predecessor ( * succ, target, bb) ;
87
+ }
88
+
89
+ let data = mir. basic_block_data_mut ( bb) ;
90
+ data. statements . append ( & mut other_data. statements ) ;
91
+ mem:: swap ( & mut data. terminator , & mut other_data. terminator ) ;
92
+ }
93
+ _ => mir. basic_block_data_mut ( bb) . terminator = terminator
94
+ }
95
+
96
+ for succ in mir. basic_block_data ( bb) . terminator . successors ( ) {
97
+ if !seen[ succ. index ( ) ] {
98
+ seen[ succ. index ( ) ] = true ;
99
+ worklist. push ( * succ) ;
100
+ }
101
+ }
85
102
}
86
103
104
+ // These blocks must be retained, so mark them seen even if we didn't see them
105
+ seen[ START_BLOCK . index ( ) ] = true ;
106
+ seen[ END_BLOCK . index ( ) ] = true ;
107
+ seen[ DIVERGE_BLOCK . index ( ) ] = true ;
108
+
109
+ // Now get rid of all the blocks we never saw
110
+ util:: retain_basic_blocks ( mir, & seen) ;
111
+
87
112
changed
88
113
}
89
114
@@ -125,8 +150,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
125
150
let mut changed = true ;
126
151
while changed {
127
152
changed = self . simplify_branches ( mir) ;
128
- changed |= self . remove_goto_chains ( mir) ;
129
- self . remove_dead_blocks ( mir) ;
153
+ changed |= self . merge_consecutive_blocks ( mir) ;
130
154
}
131
155
132
156
// FIXME: Should probably be moved into some kind of pass manager
0 commit comments