|
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. |
| 11 | +//! A number of passes which remove various redundancies in the CFG. |
14 | 12 | //!
|
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. |
| 13 | +//! The `SimplifyCfg` pass gets rid of unnecessary blocks in the CFG, whereas the `SimplifyLocals` |
| 14 | +//! gets rid of all the unnecessary local variable declarations. |
17 | 15 | //!
|
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). |
| 16 | +//! The `SimplifyLocals` pass is kinda expensive and therefore not very suitable to be run often. |
| 17 | +//! Most of the passes should not care or be impacted in meaningful ways due to extra locals |
| 18 | +//! either, so running the pass once, right before translation, should suffice. |
| 19 | +//! |
| 20 | +//! On the other side of the spectrum, the `SimplifyCfg` pass is considerably cheap to run, thus |
| 21 | +//! one should run it after every pass which may modify CFG in significant ways. This pass must |
| 22 | +//! also be run before any analysis passes because it removes dead blocks, and some of these can be |
| 23 | +//! ill-typed. |
| 24 | +//! |
| 25 | +//! The cause of this typing issue is typeck allowing most blocks whose end is not reachable have |
| 26 | +//! an arbitrary return type, rather than having the usual () return type (as a note, typeck's |
| 27 | +//! notion of reachability is in fact slightly weaker than MIR CFG reachability - see #31617). A |
| 28 | +//! standard example of the situation is: |
22 | 29 | //!
|
23 |
| -//! A standard example of the situation is: |
24 | 30 | //! ```rust
|
25 | 31 | //! fn example() {
|
26 | 32 | //! let _a: char = { return; };
|
27 | 33 | //! }
|
28 | 34 | //! ```
|
29 | 35 | //!
|
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 |
| -
|
| 36 | +//! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we |
| 37 | +//! naively generate still contains the `_a = ()` write in the unreachable block "after" the |
| 38 | +//! return. |
34 | 39 |
|
35 | 40 | use rustc_data_structures::bitvec::BitVector;
|
36 | 41 | use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
37 | 42 | use rustc::ty::TyCtxt;
|
38 | 43 | use rustc::mir::*;
|
39 | 44 | use rustc::mir::transform::{MirPass, MirSource, Pass};
|
| 45 | +use rustc::mir::visit::{MutVisitor, Visitor, LvalueContext}; |
40 | 46 | use std::fmt;
|
41 | 47 |
|
42 | 48 | pub struct SimplifyCfg<'a> { label: &'a str }
|
@@ -257,3 +263,87 @@ fn remove_dead_blocks(mir: &mut Mir) {
|
257 | 263 | }
|
258 | 264 | }
|
259 | 265 | }
|
| 266 | + |
| 267 | + |
| 268 | +pub struct SimplifyLocals; |
| 269 | + |
| 270 | +impl Pass for SimplifyLocals { |
| 271 | + fn name(&self) -> ::std::borrow::Cow<'static, str> { "SimplifyLocals".into() } |
| 272 | +} |
| 273 | + |
| 274 | +impl<'tcx> MirPass<'tcx> for SimplifyLocals { |
| 275 | + fn run_pass<'a>(&mut self, _: TyCtxt<'a, 'tcx, 'tcx>, _: MirSource, mir: &mut Mir<'tcx>) { |
| 276 | + let mut marker = DeclMarker { locals: BitVector::new(mir.local_decls.len()) }; |
| 277 | + marker.visit_mir(mir); |
| 278 | + // Return pointer and arguments are always live |
| 279 | + marker.locals.insert(0); |
| 280 | + for idx in mir.args_iter() { |
| 281 | + marker.locals.insert(idx.index()); |
| 282 | + } |
| 283 | + let map = make_local_map(&mut mir.local_decls, marker.locals); |
| 284 | + // Update references to all vars and tmps now |
| 285 | + LocalUpdater { map: map }.visit_mir(mir); |
| 286 | + mir.local_decls.shrink_to_fit(); |
| 287 | + } |
| 288 | +} |
| 289 | + |
| 290 | +/// Construct the mapping while swapping out unused stuff out from the `vec`. |
| 291 | +fn make_local_map<'tcx, I: Idx, V>(vec: &mut IndexVec<I, V>, mask: BitVector) -> Vec<usize> { |
| 292 | + let mut map: Vec<usize> = ::std::iter::repeat(!0).take(vec.len()).collect(); |
| 293 | + let mut used = 0; |
| 294 | + for alive_index in mask.iter() { |
| 295 | + map[alive_index] = used; |
| 296 | + if alive_index != used { |
| 297 | + vec.swap(alive_index, used); |
| 298 | + } |
| 299 | + used += 1; |
| 300 | + } |
| 301 | + vec.truncate(used); |
| 302 | + map |
| 303 | +} |
| 304 | + |
| 305 | +struct DeclMarker { |
| 306 | + pub locals: BitVector, |
| 307 | +} |
| 308 | + |
| 309 | +impl<'tcx> Visitor<'tcx> for DeclMarker { |
| 310 | + fn visit_lvalue(&mut self, lval: &Lvalue<'tcx>, ctx: LvalueContext<'tcx>, loc: Location) { |
| 311 | + if ctx == LvalueContext::StorageLive || ctx == LvalueContext::StorageDead { |
| 312 | + // ignore these altogether, they get removed along with their otherwise unused decls. |
| 313 | + return; |
| 314 | + } |
| 315 | + if let Lvalue::Local(ref v) = *lval { |
| 316 | + self.locals.insert(v.index()); |
| 317 | + } |
| 318 | + self.super_lvalue(lval, ctx, loc); |
| 319 | + } |
| 320 | +} |
| 321 | + |
| 322 | +struct LocalUpdater { |
| 323 | + map: Vec<usize>, |
| 324 | +} |
| 325 | + |
| 326 | +impl<'tcx> MutVisitor<'tcx> for LocalUpdater { |
| 327 | + fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { |
| 328 | + // Remove unnecessary StorageLive and StorageDead annotations. |
| 329 | + data.statements.retain(|stmt| { |
| 330 | + match stmt.kind { |
| 331 | + StatementKind::StorageLive(ref lval) | StatementKind::StorageDead(ref lval) => { |
| 332 | + match *lval { |
| 333 | + Lvalue::Local(l) => self.map[l.index()] != !0, |
| 334 | + _ => true |
| 335 | + } |
| 336 | + } |
| 337 | + _ => true |
| 338 | + } |
| 339 | + }); |
| 340 | + self.super_basic_block_data(block, data); |
| 341 | + } |
| 342 | + fn visit_lvalue(&mut self, lval: &mut Lvalue<'tcx>, ctx: LvalueContext<'tcx>, loc: Location) { |
| 343 | + match *lval { |
| 344 | + Lvalue::Local(ref mut l) => *l = Local::new(self.map[l.index()]), |
| 345 | + _ => (), |
| 346 | + }; |
| 347 | + self.super_lvalue(lval, ctx, loc); |
| 348 | + } |
| 349 | +} |
0 commit comments