Skip to content

Commit 6ddbf3b

Browse files
committed
Don't treat a local as used just because it's mentioned in debug info, if the session doesn't want variable-level debug info
1 parent 9f877c9 commit 6ddbf3b

File tree

1 file changed

+69
-5
lines changed

1 file changed

+69
-5
lines changed

compiler/rustc_mir_transform/src/simplify.rs

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
3131
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
3232
use rustc_middle::mir::*;
3333
use rustc_middle::ty::TyCtxt;
34+
use rustc_session::config::{DebugInfo, Options};
3435
use smallvec::SmallVec;
3536

3637
pub 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

443460
impl 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

Comments
 (0)