88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
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+
1135use rustc_data_structures:: bitvec:: BitVector ;
1236use rustc:: middle:: const_val:: ConstVal ;
1337use rustc:: ty:: TyCtxt ;
@@ -17,30 +41,29 @@ use rustc::mir::traversal;
1741use pretty;
1842use std:: mem;
1943
20- use super :: remove_dead_blocks :: RemoveDeadBlocks ;
44+ pub struct SimplifyCfg < ' a > { label : & ' a str }
2145
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 }
2749 }
2850}
2951
30- impl < ' tcx > MirPass < ' tcx > for SimplifyCfg {
52+ impl < ' l , ' tcx > MirPass < ' tcx > for SimplifyCfg < ' l > {
3153 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 ) ;
3255 simplify_branches ( mir) ;
33- RemoveDeadBlocks . run_pass ( tcx , src , mir) ;
56+ remove_dead_blocks ( mir) ;
3457 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 ) ;
3760
3861 // FIXME: Should probably be moved into some kind of pass manager
3962 mir. basic_blocks . shrink_to_fit ( ) ;
4063 }
4164}
4265
43- impl Pass for SimplifyCfg { }
66+ impl < ' l > Pass for SimplifyCfg < ' l > { }
4467
4568fn merge_consecutive_blocks ( mir : & mut Mir ) {
4669 // Build the precedecessor map for the MIR
@@ -202,3 +225,31 @@ fn simplify_branches(mir: &mut Mir) {
202225 }
203226 }
204227}
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