@@ -31,6 +31,7 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
31
31
use rustc_middle:: mir:: visit:: { MutVisitor , MutatingUseContext , PlaceContext , Visitor } ;
32
32
use rustc_middle:: mir:: * ;
33
33
use rustc_middle:: ty:: TyCtxt ;
34
+ use rustc_session:: config:: { DebugInfo , Options } ;
34
35
use smallvec:: SmallVec ;
35
36
36
37
pub enum SimplifyCfg {
@@ -373,9 +374,15 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
373
374
}
374
375
}
375
376
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
+
377
384
// 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 ) ;
379
386
380
387
// Next, we're going to remove any `Local` with zero actual uses. When we remove those
381
388
// `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>) {
385
392
remove_unused_definitions_helper ( & mut used_locals, body) ;
386
393
}
387
394
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
+
389
405
// 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 ) ;
391
407
392
408
// Next, we're going to remove any `Local` with zero actual uses. When we remove those
393
409
// `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
@@ -438,15 +454,17 @@ struct UsedLocals {
438
454
increment : bool ,
439
455
arg_count : u32 ,
440
456
use_count : IndexVec < Local , u32 > ,
457
+ preserve_debug : bool ,
441
458
}
442
459
443
460
impl UsedLocals {
444
461
/// 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 {
446
463
let mut this = Self {
447
464
increment : true ,
448
465
arg_count : body. arg_count . try_into ( ) . unwrap ( ) ,
449
466
use_count : IndexVec :: from_elem ( 0 , & body. local_decls ) ,
467
+ preserve_debug,
450
468
} ;
451
469
this. visit_body ( body) ;
452
470
this
@@ -527,6 +545,14 @@ impl<'tcx> Visitor<'tcx> for UsedLocals {
527
545
self . use_count [ local] -= 1 ;
528
546
}
529
547
}
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
+ }
530
556
}
531
557
532
558
/// Removes unused definitions. Updates the used locals to reflect the changes made.
@@ -540,6 +566,23 @@ fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Bod
540
566
while modified {
541
567
modified = false ;
542
568
569
+ if !used_locals. preserve_debug {
570
+ body. var_debug_info . retain ( |info| {
571
+ let keep = debug_info_is_for_simple_local ( info)
572
+ . is_some_and ( |local| used_locals. is_used ( local) ) ;
573
+
574
+ if !keep {
575
+ trace ! ( "removing var_debug_info {:?}" , info) ;
576
+
577
+ // While we did modify the debug info, we don't need to set the
578
+ // `modified` flag, as we didn't change `used_locals`, and thus
579
+ // we don't need to re-run the loop to look again.
580
+ }
581
+
582
+ keep
583
+ } ) ;
584
+ }
585
+
543
586
for data in body. basic_blocks . as_mut_preserves_cfg ( ) {
544
587
// Remove unnecessary StorageLive and StorageDead annotations.
545
588
data. statements . retain ( |statement| {
@@ -581,3 +624,20 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
581
624
* l = self . map [ * l] . unwrap ( ) ;
582
625
}
583
626
}
627
+
628
+ fn preserve_debug_even_if_never_generated ( opts : & Options ) -> bool {
629
+ if let Some ( p) = opts. unstable_opts . inline_mir_preserve_debug {
630
+ return p;
631
+ }
632
+
633
+ match opts. debuginfo {
634
+ DebugInfo :: None | DebugInfo :: LineDirectivesOnly | DebugInfo :: LineTablesOnly => false ,
635
+ DebugInfo :: Limited | DebugInfo :: Full => true ,
636
+ }
637
+ }
638
+
639
+ // For now we only remove basic debuginfo, like `foo => _3`, and don't attempt
640
+ // to clean up more complicated things like `foo => Foo { .0 => _2, .1 => _4 }`
641
+ fn debug_info_is_for_simple_local ( info : & VarDebugInfo < ' _ > ) -> Option < Local > {
642
+ if let VarDebugInfoContents :: Place ( place) = info. value { place. as_local ( ) } else { None }
643
+ }
0 commit comments