@@ -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,27 @@ 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 = 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
+
543
590
for data in body. basic_blocks . as_mut_preserves_cfg ( ) {
544
591
// Remove unnecessary StorageLive and StorageDead annotations.
545
592
data. statements . retain ( |statement| {
@@ -581,3 +628,20 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
581
628
* l = self . map [ * l] . unwrap ( ) ;
582
629
}
583
630
}
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