@@ -31,6 +31,7 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
3131use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
3232use rustc_middle:: mir:: * ;
3333use rustc_middle:: ty:: TyCtxt ;
34+ use rustc_session:: config:: { DebugInfo , Options } ;
3435use smallvec:: SmallVec ;
3536
3637pub enum SimplifyCfg {
@@ -373,9 +374,15 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
373374 }
374375}
375376
376- pub fn remove_unused_definitions < ' tcx > ( body : & mut Body < ' tcx > ) {
377+ /// Go through the basic blocks and remove statements that assign locals that aren't read.
378+ ///
379+ /// This does *not* clean up the `local_decl`s. If you want it to do that too,
380+ /// call [`simplify_locals`] instead of this.
381+ pub ( crate ) fn remove_unused_definitions < ' tcx > ( body : & mut Body < ' tcx > ) {
382+ let preserve_debug = true ;
383+
377384 // First, we're going to get a count of *actual* uses for every `Local`.
378- let mut used_locals = UsedLocals :: new ( body) ;
385+ let mut used_locals = UsedLocals :: new ( body, preserve_debug ) ;
379386
380387 // Next, we're going to remove any `Local` with zero actual uses. When we remove those
381388 // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
@@ -385,9 +392,18 @@ pub fn remove_unused_definitions<'tcx>(body: &mut Body<'tcx>) {
385392 remove_unused_definitions_helper ( & mut used_locals, body) ;
386393}
387394
388- pub fn simplify_locals < ' tcx > ( body : & mut Body < ' tcx > , tcx : TyCtxt < ' tcx > ) {
395+ /// Go through the basic blocks and remove statements that assign locals that aren't read.
396+ ///
397+ /// Then go through and remove unneeded `local_decl`s, rewriting all mentions of them
398+ /// in all the statements.
399+ ///
400+ /// If you only want the (faster) statement pruning, call [`remove_unused_definitions`]
401+ /// instead of this.
402+ pub ( crate ) fn simplify_locals < ' tcx > ( body : & mut Body < ' tcx > , tcx : TyCtxt < ' tcx > ) {
403+ let preserve_debug = preserve_debug_even_if_never_generated ( & tcx. sess . opts ) ;
404+
389405 // First, we're going to get a count of *actual* uses for every `Local`.
390- let mut used_locals = UsedLocals :: new ( body) ;
406+ let mut used_locals = UsedLocals :: new ( body, preserve_debug ) ;
391407
392408 // Next, we're going to remove any `Local` with zero actual uses. When we remove those
393409 // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
@@ -438,15 +454,17 @@ struct UsedLocals {
438454 increment : bool ,
439455 arg_count : u32 ,
440456 use_count : IndexVec < Local , u32 > ,
457+ preserve_debug : bool ,
441458}
442459
443460impl UsedLocals {
444461 /// Determines which locals are used & unused in the given body.
445- fn new ( body : & Body < ' _ > ) -> Self {
462+ fn new ( body : & Body < ' _ > , preserve_debug : bool ) -> Self {
446463 let mut this = Self {
447464 increment : true ,
448465 arg_count : body. arg_count . try_into ( ) . unwrap ( ) ,
449466 use_count : IndexVec :: from_elem ( 0 , & body. local_decls ) ,
467+ preserve_debug,
450468 } ;
451469 this. visit_body ( body) ;
452470 this
@@ -527,6 +545,14 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
527545 self . use_count [ local] -= 1 ;
528546 }
529547 }
548+
549+ fn visit_var_debug_info ( & mut self , var_debug_info : & VarDebugInfo < ' tcx > ) {
550+ if !self . preserve_debug && debug_info_is_for_simple_local ( var_debug_info) . is_some ( ) {
551+ return ;
552+ }
553+
554+ self . super_var_debug_info ( var_debug_info) ;
555+ }
530556}
531557
532558/// Removes unused definitions. Updates the used locals to reflect the changes made.
@@ -540,6 +566,27 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod
540566 while modified {
541567 modified = false ;
542568
569+ if !used_locals. preserve_debug {
570+ body. var_debug_info . retain ( |info| {
571+ let keep = if let Some ( local) = debug_info_is_for_simple_local ( info) {
572+ used_locals. is_used ( local)
573+ } else {
574+ // Keep non-simple debuginfo no matter what
575+ true
576+ } ;
577+
578+ if !keep {
579+ trace ! ( "removing var_debug_info {:?}" , info) ;
580+
581+ // While we did modify the debug info, we don't need to set the
582+ // `modified` flag, as we didn't change `used_locals`, and thus
583+ // we don't need to re-run the loop to look again.
584+ }
585+
586+ keep
587+ } ) ;
588+ }
589+
543590 for data in body. basic_blocks . as_mut_preserves_cfg ( ) {
544591 // Remove unnecessary StorageLive and StorageDead annotations.
545592 data. statements . retain ( |statement| {
@@ -581,3 +628,20 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
581628 * l = self . map [ * l] . unwrap ( ) ;
582629 }
583630}
631+
632+ fn preserve_debug_even_if_never_generated ( opts : & Options ) -> bool {
633+ if let Some ( p) = opts. unstable_opts . inline_mir_preserve_debug {
634+ return p;
635+ }
636+
637+ match opts. debuginfo {
638+ DebugInfo :: None | DebugInfo :: LineDirectivesOnly | DebugInfo :: LineTablesOnly => false ,
639+ DebugInfo :: Limited | DebugInfo :: Full => true ,
640+ }
641+ }
642+
643+ // For now we only remove basic debuginfo, like `foo => _3`, and don't attempt
644+ // to clean up more complicated things like `foo => Foo { .0 => _2, .1 => _4 }`
645+ fn debug_info_is_for_simple_local ( info : & VarDebugInfo < ' _ > ) -> Option < Local > {
646+ if let VarDebugInfoContents :: Place ( place) = info. value { place. as_local ( ) } else { None }
647+ }
0 commit comments