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