8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
+ //! A pass that removes various redundancies in the CFG. It should be
12
+ //! called after every significant CFG modification to tidy things
13
+ //! up.
14
+ //!
15
+ //! This pass must also be run before any analysis passes because it removes
16
+ //! dead blocks, and some of these can be ill-typed.
17
+ //!
18
+ //! The cause of that is that typeck lets most blocks whose end is not
19
+ //! reachable have an arbitrary return type, rather than having the
20
+ //! usual () return type (as a note, typeck's notion of reachability
21
+ //! is in fact slightly weaker than MIR CFG reachability - see #31617).
22
+ //!
23
+ //! A standard example of the situation is:
24
+ //! ```rust
25
+ //! fn example() {
26
+ //! let _a: char = { return; };
27
+ //! }
28
+ //! ```
29
+ //!
30
+ //! Here the block (`{ return; }`) has the return type `char`,
31
+ //! rather than `()`, but the MIR we naively generate still contains
32
+ //! the `_a = ()` write in the unreachable block "after" the return.
33
+
34
+
11
35
use rustc_data_structures:: bitvec:: BitVector ;
12
36
use rustc:: middle:: const_val:: ConstVal ;
13
37
use rustc:: ty:: TyCtxt ;
@@ -17,30 +41,29 @@ use rustc::mir::traversal;
17
41
use pretty;
18
42
use std:: mem;
19
43
20
- use super :: remove_dead_blocks :: RemoveDeadBlocks ;
44
+ pub struct SimplifyCfg < ' a > { label : & ' a str }
21
45
22
- pub struct SimplifyCfg ;
23
-
24
- impl SimplifyCfg {
25
- pub fn new ( ) -> SimplifyCfg {
26
- SimplifyCfg
46
+ impl < ' a > SimplifyCfg < ' a > {
47
+ pub fn new ( label : & ' a str ) -> Self {
48
+ SimplifyCfg { label : label }
27
49
}
28
50
}
29
51
30
- impl < ' tcx > MirPass < ' tcx > for SimplifyCfg {
52
+ impl < ' l , ' tcx > MirPass < ' tcx > for SimplifyCfg < ' l > {
31
53
fn run_pass < ' a > ( & mut self , tcx : TyCtxt < ' a , ' tcx , ' tcx > , src : MirSource , mir : & mut Mir < ' tcx > ) {
54
+ pretty:: dump_mir ( tcx, "simplify_cfg" , & format ! ( "{}-before" , self . label) , src, mir, None ) ;
32
55
simplify_branches ( mir) ;
33
- RemoveDeadBlocks . run_pass ( tcx , src , mir) ;
56
+ remove_dead_blocks ( mir) ;
34
57
merge_consecutive_blocks ( mir) ;
35
- RemoveDeadBlocks . run_pass ( tcx , src , mir) ;
36
- pretty:: dump_mir ( tcx, "simplify_cfg" , & 0 , src, mir, None ) ;
58
+ remove_dead_blocks ( mir) ;
59
+ pretty:: dump_mir ( tcx, "simplify_cfg" , & format ! ( "{}-after" , self . label ) , src, mir, None ) ;
37
60
38
61
// FIXME: Should probably be moved into some kind of pass manager
39
62
mir. basic_blocks . shrink_to_fit ( ) ;
40
63
}
41
64
}
42
65
43
- impl Pass for SimplifyCfg { }
66
+ impl < ' l > Pass for SimplifyCfg < ' l > { }
44
67
45
68
fn merge_consecutive_blocks ( mir : & mut Mir ) {
46
69
// Build the precedecessor map for the MIR
@@ -202,3 +225,31 @@ fn simplify_branches(mir: &mut Mir) {
202
225
}
203
226
}
204
227
}
228
+
229
+ fn remove_dead_blocks ( mir : & mut Mir ) {
230
+ let mut seen = BitVector :: new ( mir. basic_blocks . len ( ) ) ;
231
+ for ( bb, _) in traversal:: preorder ( mir) {
232
+ seen. insert ( bb. index ( ) ) ;
233
+ }
234
+
235
+ let num_blocks = mir. basic_blocks . len ( ) ;
236
+
237
+ let mut replacements: Vec < _ > = ( 0 ..num_blocks) . map ( BasicBlock :: new) . collect ( ) ;
238
+ let mut used_blocks = 0 ;
239
+ for alive_index in seen. iter ( ) {
240
+ replacements[ alive_index] = BasicBlock :: new ( used_blocks) ;
241
+ if alive_index != used_blocks {
242
+ // Swap the next alive block data with the current available slot. Since alive_index is
243
+ // non-decreasing this is a valid operation.
244
+ mir. basic_blocks . swap ( alive_index, used_blocks) ;
245
+ }
246
+ used_blocks += 1 ;
247
+ }
248
+ mir. basic_blocks . truncate ( used_blocks) ;
249
+
250
+ for bb in mir. all_basic_blocks ( ) {
251
+ for target in mir. basic_block_data_mut ( bb) . terminator_mut ( ) . successors_mut ( ) {
252
+ * target = replacements[ target. index ( ) ] ;
253
+ }
254
+ }
255
+ }
0 commit comments