@@ -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,23 +323,15 @@ 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.
340333 loop {
341- let mut remove_statements = RemoveStatements :: new ( & mut used_locals, arg_count , tcx) ;
334+ let mut remove_statements = RemoveStatements :: new ( & mut used_locals, tcx) ;
342335 remove_statements. visit_body ( body) ;
343336
344337 if !remove_statements. modified {
@@ -347,7 +340,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
347340 }
348341
349342 // 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 ) ;
343+ let map = make_local_map ( & mut body. local_decls , & used_locals) ;
351344
352345 // Only bother running the `LocalUpdater` if we actually found locals to remove.
353346 if map. iter ( ) . any ( Option :: is_none) {
@@ -363,14 +356,14 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
363356/// Construct the mapping while swapping out unused stuff out from the `vec`.
364357fn make_local_map < V > (
365358 local_decls : & mut IndexVec < Local , V > ,
366- used_locals : IndexVec < Local , u32 > ,
367- arg_count : usize ,
359+ used_locals : & UsedLocals ,
368360) -> IndexVec < Local , Option < Local > > {
369361 let mut map: IndexVec < Local , Option < Local > > = IndexVec :: from_elem ( None , & * local_decls) ;
370362 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 {
363+
364+ for alive_index in local_decls. indices ( ) {
365+ // `is_used` treats the `RETURN_PLACE` and arguments as used.
366+ if !used_locals. is_used ( alive_index) {
374367 continue ;
375368 }
376369
@@ -384,115 +377,125 @@ fn make_local_map<V>(
384377 map
385378}
386379
387- struct DeclMarker < ' a , ' tcx > {
388- pub local_counts : IndexVec < Local , u32 > ,
389- pub body : & ' a Body < ' tcx > ,
380+ /// Keeps track of used & unused locals.
381+ struct UsedLocals {
382+ increment : bool ,
383+ arg_count : u32 ,
384+ use_count : IndexVec < Local , u32 > ,
390385}
391386
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 }
387+ impl UsedLocals {
388+ /// Determines which locals are used & unused in the given body.
389+ fn new ( body : & Body < ' _ > ) -> Self {
390+ let mut this = Self {
391+ increment : true ,
392+ arg_count : body. arg_count . try_into ( ) . unwrap ( ) ,
393+ use_count : IndexVec :: from_elem ( 0 , & body. local_decls ) ,
394+ } ;
395+ this. visit_body ( body) ;
396+ this
395397 }
396- }
397398
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- }
399+ /// Checks if local is used.
400+ ///
401+ /// Return place and arguments are always considered used.
402+ fn is_used ( & self , local : Local ) -> bool {
403+ trace ! ( "is_used({:?}): use_count: {:?}" , local, self . use_count[ local] ) ;
404+ local. as_u32 ( ) <= self . arg_count || self . use_count [ local] != 0
405+ }
406406
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- }
407+ /// Updates the use counts to reflect the removal of given statement.
408+ fn statement_removed ( & mut self , statement : & Statement < ' tcx > ) {
409+ self . increment = false ;
441410
442- self . local_counts [ * local] += 1 ;
411+ // The location of the statement is irrelevant.
412+ let location = Location { block : START_BLOCK , statement_index : 0 } ;
413+ self . visit_statement ( statement, location) ;
443414 }
444- }
445-
446- struct StatementDeclMarker < ' a , ' tcx > {
447- used_locals : & ' a mut IndexVec < Local , u32 > ,
448- statement : & ' a Statement < ' tcx > ,
449- }
450415
451- impl < ' a , ' tcx > StatementDeclMarker < ' a , ' tcx > {
452- pub fn new (
453- used_locals : & ' a mut IndexVec < Local , u32 > ,
454- statement : & ' a Statement < ' tcx > ,
455- ) -> Self {
456- Self { used_locals, statement }
416+ /// Visits a left-hand side of an assignment.
417+ fn visit_lhs ( & mut self , place : & Place < ' tcx > , location : Location ) {
418+ if place. is_indirect ( ) {
419+ // A use, not a definition.
420+ self . visit_place ( place, PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) , location) ;
421+ } else {
422+ // A definition. Although, it still might use other locals for indexing.
423+ self . super_projection (
424+ place. local ,
425+ & place. projection ,
426+ PlaceContext :: MutatingUse ( MutatingUseContext :: Projection ) ,
427+ location,
428+ ) ;
429+ }
457430 }
458431}
459432
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 ;
433+ impl Visitor < ' _ > for UsedLocals {
434+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
435+ match statement. kind {
436+ StatementKind :: LlvmInlineAsm ( ..)
437+ | StatementKind :: SetDiscriminant { .. } // FIXME: Try to remove those as well.
438+ | StatementKind :: Retag ( ..)
439+ | StatementKind :: Coverage ( ..)
440+ | StatementKind :: FakeRead ( ..)
441+ | StatementKind :: AscribeUserType ( ..) => {
442+ self . super_statement ( statement, location) ;
443+ }
444+
445+ StatementKind :: Nop => { }
446+
447+ StatementKind :: StorageLive ( _local) | StatementKind :: StorageDead ( _local) => { }
448+
449+ StatementKind :: Assign ( box ( ref place, ref rvalue) ) => {
450+ let can_skip = match rvalue {
451+ Rvalue :: Use ( _)
452+ | Rvalue :: Discriminant ( _)
453+ | Rvalue :: BinaryOp ( _, _, _)
454+ | Rvalue :: CheckedBinaryOp ( _, _, _)
455+ | Rvalue :: Repeat ( _, _)
456+ | Rvalue :: AddressOf ( _, _)
457+ | Rvalue :: Len ( _)
458+ | Rvalue :: UnaryOp ( _, _)
459+ | Rvalue :: Aggregate ( _, _) => true ,
460+
461+ Rvalue :: Ref ( ..)
462+ | Rvalue :: ThreadLocalRef ( ..)
463+ | Rvalue :: Cast ( ..)
464+ | Rvalue :: NullaryOp ( ..) => false ,
465+ } ;
466+ if can_skip {
467+ self . visit_lhs ( place, location) ;
468+ } else {
469+ self . visit_place (
470+ place,
471+ PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) ,
472+ location,
473+ ) ;
474+ }
475+ self . visit_rvalue ( rvalue, location) ;
466476 }
467477 }
478+ }
468479
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 ;
480+ fn visit_local ( & mut self , local : & Local , _ctx : PlaceContext , _location : Location ) {
481+ if self . increment {
482+ self . use_count [ * local] += 1 ;
483+ } else {
484+ assert_ne ! ( self . use_count[ * local] , 0 ) ;
485+ self . use_count [ * local] -= 1 ;
473486 }
474487 }
475488}
476489
477490struct RemoveStatements < ' a , ' tcx > {
478- used_locals : & ' a mut IndexVec < Local , u32 > ,
479- arg_count : usize ,
491+ used_locals : & ' a mut UsedLocals ,
480492 tcx : TyCtxt < ' tcx > ,
481493 modified : bool ,
482494}
483495
484496impl < ' a , ' tcx > RemoveStatements < ' a , ' tcx > {
485- fn new (
486- used_locals : & ' a mut IndexVec < Local , u32 > ,
487- arg_count : usize ,
488- tcx : TyCtxt < ' tcx > ,
489- ) -> Self {
490- Self { used_locals, arg_count, tcx, modified : false }
491- }
492-
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
497+ fn new ( used_locals : & ' a mut UsedLocals , tcx : TyCtxt < ' tcx > ) -> Self {
498+ Self { used_locals, tcx, modified : false }
496499 }
497500}
498501
@@ -503,26 +506,21 @@ impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> {
503506
504507 fn visit_basic_block_data ( & mut self , block : BasicBlock , data : & mut BasicBlockData < ' tcx > ) {
505508 // 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)
509+ data. statements . retain ( |statement| {
510+ let keep = match & statement. kind {
511+ StatementKind :: StorageLive ( local) | StatementKind :: StorageDead ( local) => {
512+ self . used_locals . is_used ( * local)
511513 }
512- StatementKind :: Assign ( box ( place, _) ) => self . keep_local ( place. local ) ,
514+ StatementKind :: Assign ( box ( place, _) ) => self . used_locals . is_used ( place. local ) ,
513515 _ => true ,
514516 } ;
515517
516518 if !keep {
517- trace ! ( "removing statement {:?}" , stmt ) ;
519+ trace ! ( "removing statement {:?}" , statement ) ;
518520 self . modified = true ;
519-
520- let mut visitor = StatementDeclMarker :: new ( self . used_locals , stmt) ;
521- visitor. visit_statement ( stmt, Location { block, statement_index : i } ) ;
521+ self . used_locals . statement_removed ( statement) ;
522522 }
523523
524- i += 1 ;
525-
526524 keep
527525 } ) ;
528526
0 commit comments