34
34
35
35
use rustc_data_structures:: bitvec:: BitVector ;
36
36
use rustc_data_structures:: indexed_vec:: { Idx , IdxVec } ;
37
- use rustc:: middle:: const_val:: ConstVal ;
38
37
use rustc:: ty:: TyCtxt ;
39
38
use rustc:: mir:: repr:: * ;
40
39
use rustc:: mir:: transform:: { MirPass , MirSource , Pass } ;
41
40
use std:: fmt;
42
- use std:: mem;
43
41
44
42
use traversal;
45
43
@@ -53,9 +51,7 @@ impl<'a> SimplifyCfg<'a> {
53
51
54
52
impl < ' l , ' tcx > MirPass < ' tcx > for SimplifyCfg < ' l > {
55
53
fn run_pass < ' a > ( & mut self , _tcx : TyCtxt < ' a , ' tcx , ' tcx > , _src : MirSource , mir : & mut Mir < ' tcx > ) {
56
- simplify_branches ( mir) ;
57
- remove_dead_blocks ( mir) ;
58
- merge_consecutive_blocks ( mir) ;
54
+ CfgSimplifier :: new ( mir) . simplify ( ) ;
59
55
remove_dead_blocks ( mir) ;
60
56
61
57
// FIXME: Should probably be moved into some kind of pass manager
@@ -70,155 +66,155 @@ impl<'l> Pass for SimplifyCfg<'l> {
70
66
}
71
67
}
72
68
73
- fn merge_consecutive_blocks ( mir : & mut Mir ) {
74
- let mut pred_count: IdxVec < _ , _ > =
75
- mir. predecessors ( ) . iter ( ) . map ( |ps| ps. len ( ) ) . collect ( ) ;
76
-
77
- loop {
78
- let mut changed = false ;
79
- let mut seen = BitVector :: new ( mir. basic_blocks ( ) . len ( ) ) ;
80
- let mut worklist = vec ! [ START_BLOCK ] ;
81
- while let Some ( bb) = worklist. pop ( ) {
82
- // Temporarily take ownership of the terminator we're modifying to keep borrowck happy
83
- let mut terminator = mir[ bb] . terminator . take ( ) . expect ( "invalid terminator state" ) ;
84
-
85
- // See if we can merge the target block into this one
86
- loop {
87
- let mut inner_change = false ;
88
-
89
- if let TerminatorKind :: Goto { target } = terminator. kind {
90
- // Don't bother trying to merge a block into itself
91
- if target == bb {
92
- break ;
93
- }
94
-
95
- let num_insts = mir[ target] . statements . len ( ) ;
96
- match mir[ target] . terminator ( ) . kind {
97
- TerminatorKind :: Goto { target : new_target } if num_insts == 0 => {
98
- inner_change = true ;
99
- terminator. kind = TerminatorKind :: Goto { target : new_target } ;
100
- pred_count[ target] -= 1 ;
101
- pred_count[ new_target] += 1 ;
102
- }
103
- _ if pred_count[ target] == 1 => {
104
- inner_change = true ;
105
- let mut stmts = Vec :: new ( ) ;
106
- {
107
- let target_data = & mut mir[ target] ;
108
- mem:: swap ( & mut stmts, & mut target_data. statements ) ;
109
- mem:: swap ( & mut terminator, target_data. terminator_mut ( ) ) ;
110
- }
111
-
112
- mir[ bb] . statements . append ( & mut stmts) ;
113
- }
114
- _ => { }
115
- } ;
116
- }
117
-
118
- for target in terminator. successors_mut ( ) {
119
- let new_target = match final_target ( mir, * target) {
120
- Some ( new_target) => new_target,
121
- None if mir[ bb] . statements . is_empty ( ) => bb,
122
- None => continue
123
- } ;
124
- if * target != new_target {
125
- inner_change = true ;
126
- pred_count[ * target] -= 1 ;
127
- pred_count[ new_target] += 1 ;
128
- * target = new_target;
129
- }
130
- }
131
-
132
- changed |= inner_change;
133
- if !inner_change {
134
- break ;
135
- }
136
- }
69
+ pub struct CfgSimplifier < ' a , ' tcx : ' a > {
70
+ basic_blocks : & ' a mut IdxVec < BasicBlock , BasicBlockData < ' tcx > > ,
71
+ pred_count : IdxVec < BasicBlock , u32 >
72
+ }
137
73
138
- mir[ bb] . terminator = Some ( terminator) ;
74
+ impl < ' a , ' tcx : ' a > CfgSimplifier < ' a , ' tcx > {
75
+ fn new ( mir : & ' a mut Mir < ' tcx > ) -> Self {
76
+ let mut pred_count = IdxVec :: from_elem ( 0u32 , mir. basic_blocks ( ) ) ;
139
77
140
- for succ in mir[ bb] . terminator ( ) . successors ( ) . iter ( ) {
141
- if seen. insert ( succ. index ( ) ) {
142
- worklist. push ( * succ) ;
78
+ // we can't use mir.predecessors() here because that counts
79
+ // dead blocks, which we don't want to.
80
+ for ( _, data) in traversal:: preorder ( mir) {
81
+ if let Some ( ref term) = data. terminator {
82
+ for & tgt in term. successors ( ) . iter ( ) {
83
+ pred_count[ tgt] += 1 ;
143
84
}
144
85
}
145
86
}
146
87
147
- if !changed {
148
- break ;
88
+ let basic_blocks = mir. basic_blocks_mut ( ) ;
89
+
90
+ CfgSimplifier {
91
+ basic_blocks : basic_blocks,
92
+ pred_count : pred_count
149
93
}
150
94
}
151
- }
152
95
153
- // Find the target at the end of the jump chain, return None if there is a loop
154
- fn final_target ( mir : & Mir , mut target : BasicBlock ) -> Option < BasicBlock > {
155
- // Keep track of already seen blocks to detect loops
156
- let mut seen: Vec < BasicBlock > = Vec :: with_capacity ( 8 ) ;
157
-
158
- while mir[ target] . statements . is_empty ( ) {
159
- // NB -- terminator may have been swapped with `None` in
160
- // merge_consecutive_blocks, in which case we have a cycle and just want
161
- // to stop
162
- match mir[ target] . terminator {
163
- Some ( Terminator { kind : TerminatorKind :: Goto { target : next } , .. } ) => {
164
- if seen. contains ( & next) {
165
- return None ;
96
+ fn simplify ( mut self ) {
97
+ loop {
98
+ let mut changed = false ;
99
+
100
+ for bb in ( 0 ..self . basic_blocks . len ( ) ) . map ( BasicBlock :: new) {
101
+ if self . pred_count [ bb] == 0 {
102
+ continue
103
+ }
104
+
105
+ debug ! ( "simplifying {:?}" , bb) ;
106
+
107
+ let mut terminator = self . basic_blocks [ bb] . terminator . take ( )
108
+ . expect ( "invalid terminator state" ) ;
109
+
110
+ for successor in terminator. successors_mut ( ) {
111
+ self . collapse_goto_chain ( successor, & mut changed) ;
166
112
}
167
- seen. push ( next) ;
168
- target = next;
113
+
114
+ let mut new_stmts = vec ! [ ] ;
115
+ let mut inner_changed = true ;
116
+ while inner_changed {
117
+ inner_changed = false ;
118
+ inner_changed |= self . simplify_branch ( & mut terminator) ;
119
+ inner_changed |= self . merge_successor ( & mut new_stmts, & mut terminator) ;
120
+ changed |= inner_changed;
121
+ }
122
+
123
+ self . basic_blocks [ bb] . statements . extend ( new_stmts) ;
124
+ self . basic_blocks [ bb] . terminator = Some ( terminator) ;
125
+
126
+ changed |= inner_changed;
169
127
}
170
- _ => break
128
+
129
+ if !changed { break }
171
130
}
172
131
}
173
132
174
- Some ( target)
175
- }
133
+ // Collapse a goto chain starting from `start`
134
+ fn collapse_goto_chain ( & mut self , start : & mut BasicBlock , changed : & mut bool ) {
135
+ let mut terminator = match self . basic_blocks [ * start] {
136
+ BasicBlockData {
137
+ ref statements,
138
+ terminator : ref mut terminator @ Some ( Terminator {
139
+ kind : TerminatorKind :: Goto { .. } , ..
140
+ } ) , ..
141
+ } if statements. is_empty ( ) => terminator. take ( ) ,
142
+ // if `terminator` is None, this means we are in a loop. In that
143
+ // case, let all the loop collapse to its entry.
144
+ _ => return
145
+ } ;
146
+
147
+ let target = match terminator {
148
+ Some ( Terminator { kind : TerminatorKind :: Goto { ref mut target } , .. } ) => {
149
+ self . collapse_goto_chain ( target, changed) ;
150
+ * target
151
+ }
152
+ _ => unreachable ! ( )
153
+ } ;
154
+ self . basic_blocks [ * start] . terminator = terminator;
176
155
177
- fn simplify_branches ( mir : & mut Mir ) {
178
- loop {
179
- let mut changed = false ;
156
+ debug ! ( "collapsing goto chain from {:?} to {:?}" , * start, target) ;
180
157
181
- for ( _, basic_block) in mir. basic_blocks_mut ( ) . iter_enumerated_mut ( ) {
182
- let mut terminator = basic_block. terminator_mut ( ) ;
183
- terminator. kind = match terminator. kind {
184
- TerminatorKind :: If { ref targets, .. } if targets. 0 == targets. 1 => {
185
- changed = true ;
186
- TerminatorKind :: Goto { target : targets. 0 }
187
- }
158
+ * changed |= * start != target;
159
+ self . pred_count [ target] += 1 ;
160
+ self . pred_count [ * start] -= 1 ;
161
+ * start = target;
162
+ }
188
163
189
- TerminatorKind :: If { ref targets, cond : Operand :: Constant ( Constant {
190
- literal : Literal :: Value {
191
- value : ConstVal :: Bool ( cond)
192
- } , ..
193
- } ) } => {
194
- changed = true ;
195
- if cond {
196
- TerminatorKind :: Goto { target : targets. 0 }
197
- } else {
198
- TerminatorKind :: Goto { target : targets. 1 }
199
- }
200
- }
164
+ // merge a block with 1 `goto` predecessor to its parent
165
+ fn merge_successor ( & mut self ,
166
+ new_stmts : & mut Vec < Statement < ' tcx > > ,
167
+ terminator : & mut Terminator < ' tcx > )
168
+ -> bool
169
+ {
170
+ let target = match terminator. kind {
171
+ TerminatorKind :: Goto { target }
172
+ if self . pred_count [ target] == 1
173
+ => target,
174
+ _ => return false
175
+ } ;
176
+
177
+ debug ! ( "merging block {:?} into {:?}" , target, terminator) ;
178
+ * terminator = match self . basic_blocks [ target] . terminator . take ( ) {
179
+ Some ( terminator) => terminator,
180
+ None => {
181
+ // unreachable loop - this should not be possible, as we
182
+ // don't strand blocks, but handle it correctly.
183
+ return false
184
+ }
185
+ } ;
186
+ new_stmts. extend ( self . basic_blocks [ target] . statements . drain ( ..) ) ;
187
+ self . pred_count [ target] = 0 ;
201
188
202
- TerminatorKind :: Assert { target, cond : Operand :: Constant ( Constant {
203
- literal : Literal :: Value {
204
- value : ConstVal :: Bool ( cond)
205
- } , ..
206
- } ) , expected, .. } if cond == expected => {
207
- changed = true ;
208
- TerminatorKind :: Goto { target : target }
209
- }
189
+ true
190
+ }
210
191
211
- TerminatorKind :: SwitchInt { ref targets, .. } if targets. len ( ) == 1 => {
212
- changed = true ;
213
- TerminatorKind :: Goto { target : targets[ 0 ] }
192
+ // turn a branch with all successors identical to a goto
193
+ fn simplify_branch ( & mut self , terminator : & mut Terminator < ' tcx > ) -> bool {
194
+ match terminator. kind {
195
+ TerminatorKind :: If { .. } |
196
+ TerminatorKind :: Switch { .. } |
197
+ TerminatorKind :: SwitchInt { .. } => { } ,
198
+ _ => return false
199
+ } ;
200
+
201
+ let first_succ = {
202
+ let successors = terminator. successors ( ) ;
203
+ if let Some ( & first_succ) = terminator. successors ( ) . get ( 0 ) {
204
+ if successors. iter ( ) . all ( |s| * s == first_succ) {
205
+ self . pred_count [ first_succ] -= ( successors. len ( ) -1 ) as u32 ;
206
+ first_succ
207
+ } else {
208
+ return false
214
209
}
215
- _ => continue
210
+ } else {
211
+ return false
216
212
}
217
- }
213
+ } ;
218
214
219
- if !changed {
220
- break ;
221
- }
215
+ debug ! ( "simplifying branch {:?}" , terminator ) ;
216
+ terminator . kind = TerminatorKind :: Goto { target : first_succ } ;
217
+ true
222
218
}
223
219
}
224
220
0 commit comments