@@ -35,6 +35,7 @@ use rustc_middle::mir::*;
3535use rustc_middle:: ty:: TyCtxt ;
3636use smallvec:: SmallVec ;
3737use std:: borrow:: Cow ;
38+ use std:: convert:: TryInto ;
3839
3940pub struct SimplifyCfg {
4041 label : String ,
@@ -322,32 +323,17 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
322323 trace ! ( "running SimplifyLocals on {:?}" , body. source) ;
323324
324325 // First, we're going to get a count of *actual* uses for every `Local`.
325- // Take a look at `DeclMarker::visit_local()` to see exactly what is ignored.
326- let mut used_locals = {
327- let mut marker = DeclMarker :: new ( body) ;
328- marker. visit_body ( & body) ;
329-
330- marker. local_counts
331- } ;
332-
333- let arg_count = body. arg_count ;
326+ let mut used_locals = UsedLocals :: new ( body) ;
334327
335328 // Next, we're going to remove any `Local` with zero actual uses. When we remove those
336329 // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
337330 // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from
338331 // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a
339332 // fixedpoint where there are no more unused locals.
340- loop {
341- let mut remove_statements = RemoveStatements :: new ( & mut used_locals, arg_count, tcx) ;
342- remove_statements. visit_body ( body) ;
343-
344- if !remove_statements. modified {
345- break ;
346- }
347- }
333+ remove_unused_definitions ( & mut used_locals, body) ;
348334
349335 // Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s.
350- let map = make_local_map ( & mut body. local_decls , used_locals, arg_count ) ;
336+ let map = make_local_map ( & mut body. local_decls , & used_locals) ;
351337
352338 // Only bother running the `LocalUpdater` if we actually found locals to remove.
353339 if map. iter ( ) . any ( Option :: is_none) {
@@ -363,14 +349,14 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
363349/// Construct the mapping while swapping out unused stuff out from the `vec`.
364350fn make_local_map < V > (
365351 local_decls : & mut IndexVec < Local , V > ,
366- used_locals : IndexVec < Local , usize > ,
367- arg_count : usize ,
352+ used_locals : & UsedLocals ,
368353) -> IndexVec < Local , Option < Local > > {
369354 let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , & * local_decls) ;
370355 let mut used = Local :: new ( 0 ) ;
371- for ( alive_index, count) in used_locals. iter_enumerated ( ) {
372- // The `RETURN_PLACE` and arguments are always live.
373- if alive_index. as_usize ( ) > arg_count && * count == 0 {
356+
357+ for alive_index in local_decls. indices ( ) {
358+ // `is_used` treats the `RETURN_PLACE` and arguments as used.
359+ if !used_locals. is_used ( alive_index) {
374360 continue ;
375361 }
376362
@@ -384,149 +370,130 @@ fn make_local_map<V>(
384370 map
385371}
386372
387- struct DeclMarker < ' a , ' tcx > {
388- pub local_counts : IndexVec < Local , usize > ,
389- pub body : & ' a Body < ' tcx > ,
373+ /// Keeps track of used & unused locals.
374+ struct UsedLocals {
375+ increment : bool ,
376+ arg_count : u32 ,
377+ use_count : IndexVec < Local , u32 > ,
390378}
391379
392- impl < ' a , ' tcx > DeclMarker < ' a , ' tcx > {
393- pub fn new ( body : & ' a Body < ' tcx > ) -> Self {
394- Self { local_counts : IndexVec :: from_elem ( 0 , & body. local_decls ) , body }
380+ impl UsedLocals {
381+ /// Determines which locals are used & unused in the given body.
382+ fn new ( body : & Body < ' _ > ) -> Self {
383+ let mut this = Self {
384+ increment : true ,
385+ arg_count : body. arg_count . try_into ( ) . unwrap ( ) ,
386+ use_count : IndexVec :: from_elem ( 0 , & body. local_decls ) ,
387+ } ;
388+ this. visit_body ( body) ;
389+ this
395390 }
396- }
397391
398- impl < ' a , ' tcx > Visitor < ' tcx > for DeclMarker < ' a , ' tcx > {
399- fn visit_local ( & mut self , local : & Local , ctx : PlaceContext , location : Location ) {
400- // Ignore storage markers altogether, they get removed along with their otherwise unused
401- // decls.
402- // FIXME: Extend this to all non-uses.
403- if ctx. is_storage_marker ( ) {
404- return ;
405- }
392+ /// Checks if local is used.
393+ ///
394+ /// Return place and arguments are always considered used.
395+ fn is_used ( & self , local : Local ) -> bool {
396+ trace ! ( "is_used({:?}): use_count: {:?}" , local, self . use_count[ local] ) ;
397+ local. as_u32 ( ) <= self . arg_count || self . use_count [ local] != 0
398+ }
406399
407- // Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many
408- // of these locals. However, if the local is still needed, then it will be referenced in
409- // another place and we'll mark it as being used there.
410- if ctx == PlaceContext :: MutatingUse ( MutatingUseContext :: Store )
411- || ctx == PlaceContext :: MutatingUse ( MutatingUseContext :: Projection )
412- {
413- let block = & self . body . basic_blocks ( ) [ location. block ] ;
414- if location. statement_index != block. statements . len ( ) {
415- let stmt = & block. statements [ location. statement_index ] ;
416-
417- if let StatementKind :: Assign ( box ( dest, rvalue) ) = & stmt. kind {
418- if !dest. is_indirect ( ) && dest. local == * local {
419- let can_skip = match rvalue {
420- Rvalue :: Use ( _)
421- | Rvalue :: Discriminant ( _)
422- | Rvalue :: BinaryOp ( _, _, _)
423- | Rvalue :: CheckedBinaryOp ( _, _, _)
424- | Rvalue :: Repeat ( _, _)
425- | Rvalue :: AddressOf ( _, _)
426- | Rvalue :: Len ( _)
427- | Rvalue :: UnaryOp ( _, _)
428- | Rvalue :: Aggregate ( _, _) => true ,
429-
430- _ => false ,
431- } ;
432-
433- if can_skip {
434- trace ! ( "skipping store of {:?} to {:?}" , rvalue, dest) ;
435- return ;
436- }
437- }
438- }
439- }
440- }
400+ /// Updates the use counts to reflect the removal of given statement.
401+ fn statement_removed ( & mut self , statement : & Statement < ' tcx > ) {
402+ self . increment = false ;
441403
442- self . local_counts [ * local] += 1 ;
404+ // The location of the statement is irrelevant.
405+ let location = Location { block : START_BLOCK , statement_index : 0 } ;
406+ self . visit_statement ( statement, location) ;
443407 }
444- }
445-
446- struct StatementDeclMarker < ' a , ' tcx > {
447- used_locals : & ' a mut IndexVec < Local , usize > ,
448- statement : & ' a Statement < ' tcx > ,
449- }
450408
451- impl < ' a , ' tcx > StatementDeclMarker < ' a , ' tcx > {
452- pub fn new (
453- used_locals : & ' a mut IndexVec < Local , usize > ,
454- statement : & ' a Statement < ' tcx > ,
455- ) -> Self {
456- Self { used_locals, statement }
409+ /// Visits a left-hand side of an assignment.
410+ fn visit_lhs ( & mut self , place : & Place < ' tcx > , location : Location ) {
411+ if place. is_indirect ( ) {
412+ // A use, not a definition.
413+ self . visit_place ( place, PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) , location) ;
414+ } else {
415+ // A definition. Although, it still might use other locals for indexing.
416+ self . super_projection (
417+ place. local ,
418+ & place. projection ,
419+ PlaceContext :: MutatingUse ( MutatingUseContext :: Projection ) ,
420+ location,
421+ ) ;
422+ }
457423 }
458424}
459425
460- impl < ' a , ' tcx > Visitor < ' tcx > for StatementDeclMarker < ' a , ' tcx > {
461- fn visit_local ( & mut self , local : & Local , context : PlaceContext , _location : Location ) {
462- // Skip the lvalue for assignments
463- if let StatementKind :: Assign ( box ( p, _) ) = self . statement . kind {
464- if p. local == * local && context. is_place_assignment ( ) {
465- return ;
426+ impl Visitor < ' _ > for UsedLocals {
427+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
428+ match statement. kind {
429+ StatementKind :: LlvmInlineAsm ( ..)
430+ | StatementKind :: Retag ( ..)
431+ | StatementKind :: Coverage ( ..)
432+ | StatementKind :: FakeRead ( ..)
433+ | StatementKind :: AscribeUserType ( ..) => {
434+ self . super_statement ( statement, location) ;
466435 }
467- }
468436
469- let use_count = & mut self . used_locals [ * local] ;
470- // If this is the local we're removing...
471- if * use_count != 0 {
472- * use_count -= 1 ;
473- }
474- }
475- }
437+ StatementKind :: Nop => { }
476438
477- struct RemoveStatements < ' a , ' tcx > {
478- used_locals : & ' a mut IndexVec < Local , usize > ,
479- arg_count : usize ,
480- tcx : TyCtxt < ' tcx > ,
481- modified : bool ,
482- }
439+ StatementKind :: StorageLive ( _local) | StatementKind :: StorageDead ( _local) => { }
483440
484- impl < ' a , ' tcx > RemoveStatements < ' a , ' tcx > {
485- fn new (
486- used_locals : & ' a mut IndexVec < Local , usize > ,
487- arg_count : usize ,
488- tcx : TyCtxt < ' tcx > ,
489- ) -> Self {
490- Self { used_locals, arg_count, tcx, modified : false }
491- }
441+ StatementKind :: Assign ( box ( ref place, ref rvalue) ) => {
442+ self . visit_lhs ( place, location) ;
443+ self . visit_rvalue ( rvalue, location) ;
444+ }
492445
493- fn keep_local ( & self , l : Local ) -> bool {
494- trace ! ( "keep_local({:?}): count: {:?}" , l, self . used_locals[ l] ) ;
495- l. as_usize ( ) <= self . arg_count || self . used_locals [ l] != 0
446+ StatementKind :: SetDiscriminant { ref place, variant_index : _ } => {
447+ self . visit_lhs ( place, location) ;
448+ }
449+ }
496450 }
497- }
498451
499- impl < ' a , ' tcx > MutVisitor < ' tcx > for RemoveStatements < ' a , ' tcx > {
500- fn tcx ( & self ) -> TyCtxt < ' tcx > {
501- self . tcx
452+ fn visit_local ( & mut self , local : & Local , _ctx : PlaceContext , _location : Location ) {
453+ if self . increment {
454+ self . use_count [ * local] += 1 ;
455+ } else {
456+ assert_ne ! ( self . use_count[ * local] , 0 ) ;
457+ self . use_count [ * local] -= 1 ;
458+ }
502459 }
460+ }
503461
504- fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & mut BasicBlockData < ' tcx > ) {
505- // Remove unnecessary StorageLive and StorageDead annotations.
506- let mut i = 0usize ;
507- data . statements . retain ( |stmt| {
508- let keep = match & stmt . kind {
509- StatementKind :: StorageLive ( l ) | StatementKind :: StorageDead ( l ) => {
510- self . keep_local ( * l )
511- }
512- StatementKind :: Assign ( box ( place , _ ) ) => self . keep_local ( place . local ) ,
513- _ => true ,
514- } ;
515-
516- if !keep {
517- trace ! ( "removing statement {:?}" , stmt ) ;
518- self . modified = true ;
519-
520- let mut visitor = StatementDeclMarker :: new ( self . used_locals , stmt ) ;
521- visitor . visit_statement ( stmt , Location { block , statement_index : i } ) ;
522- }
462+ /// Removes unused definitions. Updates the used locals to reflect the changes made.
463+ fn remove_unused_definitions < ' a , ' tcx > ( used_locals : & ' a mut UsedLocals , body : & mut Body < ' tcx > ) {
464+ // The use counts are updated as we remove the statements. A local might become unused
465+ // during the retain operation, leading to a temporary inconsistency (storage statements or
466+ // definitions referencing the local might remain). For correctness it is crucial that this
467+ // computation reaches a fixed point.
468+
469+ let mut modified = true ;
470+ while modified {
471+ modified = false ;
472+
473+ for data in body . basic_blocks_mut ( ) {
474+ // Remove unnecessary StorageLive and StorageDead annotations.
475+ data . statements . retain ( | statement| {
476+ let keep = match & statement . kind {
477+ StatementKind :: StorageLive ( local ) | StatementKind :: StorageDead ( local ) => {
478+ used_locals . is_used ( * local )
479+ }
480+ StatementKind :: Assign ( box ( place , _ ) ) => used_locals . is_used ( place . local ) ,
523481
524- i += 1 ;
482+ StatementKind :: SetDiscriminant { ref place, .. } => {
483+ used_locals. is_used ( place. local )
484+ }
485+ _ => true ,
486+ } ;
525487
526- keep
527- } ) ;
488+ if !keep {
489+ trace ! ( "removing statement {:?}" , statement) ;
490+ modified = true ;
491+ used_locals. statement_removed ( statement) ;
492+ }
528493
529- self . super_basic_block_data ( block, data) ;
494+ keep
495+ } ) ;
496+ }
530497 }
531498}
532499
0 commit comments