3434//! The normal logic that a program with UB can be changed to do anything does not apply to
3535//! pre-"runtime" MIR!
3636
37+ use rustc_index:: bit_set:: DenseBitSet ;
3738use rustc_index:: { Idx , IndexSlice , IndexVec } ;
38- use rustc_middle:: mir:: visit:: {
39- MutVisitor , MutatingUseContext , NonUseContext , PlaceContext , Visitor ,
40- } ;
39+ use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
4140use rustc_middle:: mir:: * ;
4241use rustc_middle:: ty:: TyCtxt ;
42+ use rustc_mir_dataflow:: debuginfo:: debuginfo_locals;
4343use rustc_span:: DUMMY_SP ;
4444use smallvec:: SmallVec ;
4545use tracing:: { debug, trace} ;
@@ -344,7 +344,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
344344
345345 fn strip_nops ( & mut self ) {
346346 for blk in self . basic_blocks . iter_mut ( ) {
347- blk. retain_statements ( |stmt| ! matches ! ( stmt . kind , StatementKind :: Nop ) )
347+ blk. strip_nops ( ) ;
348348 }
349349 }
350350}
@@ -517,28 +517,39 @@ fn make_local_map<V>(
517517/// Keeps track of used & unused locals.
518518struct UsedLocals {
519519 increment : bool ,
520- arg_count : u32 ,
521520 use_count : IndexVec < Local , u32 > ,
521+ always_used : DenseBitSet < Local > ,
522522}
523523
524524impl UsedLocals {
525525 /// Determines which locals are used & unused in the given body.
526526 fn new ( body : & Body < ' _ > ) -> Self {
527+ let mut always_used = debuginfo_locals ( body) ;
528+ always_used. insert ( RETURN_PLACE ) ;
529+ for arg in body. args_iter ( ) {
530+ always_used. insert ( arg) ;
531+ }
527532 let mut this = Self {
528533 increment : true ,
529- arg_count : body. arg_count . try_into ( ) . unwrap ( ) ,
530534 use_count : IndexVec :: from_elem ( 0 , & body. local_decls ) ,
535+ always_used,
531536 } ;
532537 this. visit_body ( body) ;
533538 this
534539 }
535540
536541 /// Checks if local is used.
537542 ///
538- /// Return place and arguments are always considered used.
543+ /// Return place, arguments, var debuginfo are always considered used.
539544 fn is_used ( & self , local : Local ) -> bool {
540- trace ! ( "is_used({:?}): use_count: {:?}" , local, self . use_count[ local] ) ;
541- local. as_u32 ( ) <= self . arg_count || self . use_count [ local] != 0
545+ trace ! (
546+ "is_used({:?}): use_count: {:?}, always_used: {}" ,
547+ local,
548+ self . use_count[ local] ,
549+ self . always_used. contains( local)
550+ ) ;
551+ // To keep things simple, we don't handle debugging information here, these are in DSE.
552+ self . always_used . contains ( local) || self . use_count [ local] != 0
542553 }
543554
544555 /// Updates the use counts to reflect the removal of given statement.
@@ -583,13 +594,10 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
583594 StatementKind :: ConstEvalCounter
584595 | StatementKind :: Nop
585596 | StatementKind :: StorageLive ( ..)
586- | StatementKind :: StorageDead ( ..) => {
587- self . visit_statement_debuginfos ( & statement. debuginfos , location) ;
588- }
597+ | StatementKind :: StorageDead ( ..) => { }
589598
590599 StatementKind :: Assign ( box ( ref place, ref rvalue) ) => {
591600 if rvalue. is_safe_to_remove ( ) {
592- self . visit_statement_debuginfos ( & statement. debuginfos , location) ;
593601 self . visit_lhs ( place, location) ;
594602 self . visit_rvalue ( rvalue, location) ;
595603 } else {
@@ -600,16 +608,18 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
600608 StatementKind :: SetDiscriminant { ref place, variant_index : _ }
601609 | StatementKind :: Deinit ( ref place)
602610 | StatementKind :: BackwardIncompatibleDropHint { ref place, reason : _ } => {
603- self . visit_statement_debuginfos ( & statement. debuginfos , location) ;
604611 self . visit_lhs ( place, location) ;
605612 }
606613 }
607614 }
608615
609616 fn visit_local ( & mut self , local : Local , ctx : PlaceContext , _location : Location ) {
617+ if matches ! ( ctx, PlaceContext :: NonUse ( _) ) {
618+ return ;
619+ }
610620 if self . increment {
611621 self . use_count [ local] += 1 ;
612- } else if ctx != PlaceContext :: NonUse ( NonUseContext :: VarDebugInfo ) {
622+ } else {
613623 assert_ne ! ( self . use_count[ local] , 0 ) ;
614624 self . use_count [ local] -= 1 ;
615625 }
@@ -629,28 +639,26 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod
629639
630640 for data in body. basic_blocks . as_mut_preserves_cfg ( ) {
631641 // Remove unnecessary StorageLive and StorageDead annotations.
632- data. retain_statements ( |statement| {
633- let keep = match & statement. kind {
642+ for statement in data. statements . iter_mut ( ) {
643+ let keep_statement = match & statement. kind {
634644 StatementKind :: StorageLive ( local) | StatementKind :: StorageDead ( local) => {
635645 used_locals. is_used ( * local)
636646 }
637- StatementKind :: Assign ( box ( place, _) ) => used_locals. is_used ( place. local ) ,
638-
639- StatementKind :: SetDiscriminant { place, .. }
640- | StatementKind :: BackwardIncompatibleDropHint { place, reason : _ }
641- | StatementKind :: Deinit ( place) => used_locals. is_used ( place. local ) ,
642- StatementKind :: Nop => false ,
643- _ => true ,
647+ StatementKind :: Assign ( box ( place, _) )
648+ | StatementKind :: SetDiscriminant { box place, .. }
649+ | StatementKind :: BackwardIncompatibleDropHint { box place, .. }
650+ | StatementKind :: Deinit ( box place) => used_locals. is_used ( place. local ) ,
651+ _ => continue ,
644652 } ;
645-
646- if !keep {
647- trace ! ( "removing statement {:?}" , statement) ;
648- modified = true ;
649- used_locals. statement_removed ( statement) ;
653+ if keep_statement {
654+ continue ;
650655 }
651-
652- keep
653- } ) ;
656+ trace ! ( "removing statement {:?}" , statement) ;
657+ modified = true ;
658+ used_locals. statement_removed ( statement) ;
659+ statement. make_nop ( true ) ;
660+ }
661+ data. strip_nops ( ) ;
654662 }
655663 }
656664}
@@ -669,3 +677,42 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
669677 * l = self . map [ * l] . unwrap ( ) ;
670678 }
671679}
680+
681+ pub ( crate ) struct UsedInStmtLocals {
682+ pub ( crate ) locals : DenseBitSet < Local > ,
683+ }
684+
685+ impl UsedInStmtLocals {
686+ pub ( crate ) fn new ( body : & Body < ' _ > ) -> Self {
687+ let mut this = Self { locals : DenseBitSet :: new_empty ( body. local_decls . len ( ) ) } ;
688+ this. visit_body ( body) ;
689+ this
690+ }
691+
692+ pub ( crate ) fn remove_unused_storage_annotations < ' tcx > ( & self , body : & mut Body < ' tcx > ) {
693+ for data in body. basic_blocks . as_mut_preserves_cfg ( ) {
694+ // Remove unnecessary StorageLive and StorageDead annotations.
695+ for statement in data. statements . iter_mut ( ) {
696+ let keep_statement = match & statement. kind {
697+ StatementKind :: StorageLive ( local) | StatementKind :: StorageDead ( local) => {
698+ self . locals . contains ( * local)
699+ }
700+ _ => continue ,
701+ } ;
702+ if keep_statement {
703+ continue ;
704+ }
705+ statement. make_nop ( true ) ;
706+ }
707+ }
708+ }
709+ }
710+
711+ impl < ' tcx > Visitor < ' tcx > for UsedInStmtLocals {
712+ fn visit_local ( & mut self , local : Local , context : PlaceContext , _: Location ) {
713+ if matches ! ( context, PlaceContext :: NonUse ( _) ) {
714+ return ;
715+ }
716+ self . locals . insert ( local) ;
717+ }
718+ }
0 commit comments