Skip to content

Commit de3cf6e

Browse files
committed
Run SimplifyLocals iteratively until we get to a fixedpoint
1 parent 7c0802b commit de3cf6e

File tree

1 file changed

+131
-27
lines changed

1 file changed

+131
-27
lines changed

src/librustc_mir/transform/simplify.rs

+131-27
Original file line numberDiff line numberDiff line change
@@ -306,49 +306,74 @@ pub struct SimplifyLocals;
306306
impl<'tcx> MirPass<'tcx> for SimplifyLocals {
307307
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
308308
trace!("running SimplifyLocals on {:?}", source);
309-
let locals = {
309+
310+
let mut used_locals = {
310311
let read_only_cache = read_only!(body);
311-
let mut marker = DeclMarker { locals: BitSet::new_empty(body.local_decls.len()), body };
312+
let mut marker = DeclMarker::new(body);
312313
marker.visit_body(&read_only_cache);
313-
// Return pointer and arguments are always live
314-
marker.locals.insert(RETURN_PLACE);
315-
for arg in body.args_iter() {
316-
marker.locals.insert(arg);
317-
}
318314

319-
marker.locals
315+
marker.local_counts
320316
};
321317

322-
let map = make_local_map(&mut body.local_decls, locals);
323-
// Update references to all vars and tmps now
324-
LocalUpdater { map, tcx }.visit_body(body);
325-
body.local_decls.shrink_to_fit();
318+
let arg_count = body.arg_count;
319+
320+
loop {
321+
let mut remove_statements = RemoveStatements::new(&mut used_locals, arg_count, tcx);
322+
remove_statements.visit_body(body);
323+
324+
if !remove_statements.modified {
325+
break;
326+
}
327+
}
328+
329+
let map = make_local_map(&mut body.local_decls, used_locals, arg_count);
330+
331+
// Only bother running the `LocalUpdater` if we actually found locals to remove.
332+
if map.iter().any(Option::is_none) {
333+
// Update references to all vars and tmps now
334+
let mut updater = LocalUpdater { map, tcx };
335+
updater.visit_body(body);
336+
337+
body.local_decls.shrink_to_fit();
338+
}
326339
}
327340
}
328341

329342
/// Construct the mapping while swapping out unused stuff out from the `vec`.
330343
fn make_local_map<V>(
331-
vec: &mut IndexVec<Local, V>,
332-
mask: BitSet<Local>,
344+
local_decls: &mut IndexVec<Local, V>,
345+
used_locals: IndexVec<Local, usize>,
346+
arg_count: usize,
333347
) -> IndexVec<Local, Option<Local>> {
334-
let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*vec);
348+
let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*local_decls);
335349
let mut used = Local::new(0);
336-
for alive_index in mask.iter() {
350+
for (alive_index, count) in used_locals.iter_enumerated() {
351+
// The `RETURN_PLACE` and arguments are always live.
352+
if alive_index.as_usize() > arg_count && *count == 0 {
353+
continue;
354+
}
355+
337356
map[alive_index] = Some(used);
338357
if alive_index != used {
339-
vec.swap(alive_index, used);
358+
local_decls.swap(alive_index, used);
340359
}
341360
used.increment_by(1);
342361
}
343-
vec.truncate(used.index());
362+
local_decls.truncate(used.index());
344363
map
345364
}
346365

347366
struct DeclMarker<'a, 'tcx> {
348-
pub locals: BitSet<Local>,
367+
pub local_counts: IndexVec<Local, usize>,
349368
pub body: &'a Body<'tcx>,
350369
}
351370

371+
impl<'a, 'tcx> DeclMarker<'a, 'tcx> {
372+
pub fn new(body: &'a Body<'tcx>) -> Self {
373+
Self { local_counts: IndexVec::from_elem(0, &body.local_decls), body }
374+
}
375+
}
376+
352377
impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
353378
fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) {
354379
// Ignore storage markers altogether, they get removed along with their otherwise unused
@@ -408,29 +433,108 @@ impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
408433
}
409434
}
410435

411-
self.locals.insert(*local);
436+
self.local_counts[*local] += 1;
412437
}
413438
}
414439

415-
struct LocalUpdater<'tcx> {
416-
map: IndexVec<Local, Option<Local>>,
440+
struct StatementDeclMarker<'a, 'tcx> {
441+
used_locals: IndexVec<Local, usize>,
442+
statement: &'a Statement<'tcx>,
443+
}
444+
445+
impl<'a, 'tcx> StatementDeclMarker<'a, 'tcx> {
446+
pub fn new(local_count: usize, statement: &'a Statement<'tcx>) -> Self {
447+
Self { used_locals: IndexVec::from_elem_n(0, local_count), statement }
448+
}
449+
}
450+
451+
impl<'a, 'tcx> Visitor<'tcx> for StatementDeclMarker<'a, 'tcx> {
452+
fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) {
453+
// Skip the lvalue for assignments
454+
if let StatementKind::Assign(box (p, _)) = self.statement.kind {
455+
if p.local == *local && context.is_place_assignment() {
456+
return;
457+
}
458+
}
459+
460+
self.used_locals[*local] += 1;
461+
}
462+
}
463+
464+
struct RemoveStatements<'a, 'tcx> {
465+
used_locals: &'a mut IndexVec<Local, usize>,
466+
arg_count: usize,
417467
tcx: TyCtxt<'tcx>,
468+
modified: bool,
418469
}
419470

420-
impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
471+
impl<'a, 'tcx> RemoveStatements<'a, 'tcx> {
472+
fn new(
473+
used_locals: &'a mut IndexVec<Local, usize>,
474+
arg_count: usize,
475+
tcx: TyCtxt<'tcx>,
476+
) -> Self {
477+
Self { used_locals, arg_count, tcx, modified: false }
478+
}
479+
480+
fn keep_local(&self, l: Local) -> bool {
481+
trace!("keep_local({:?}): count: {:?}", l, self.used_locals[l]);
482+
l.as_usize() <= self.arg_count || self.used_locals[l] != 0
483+
}
484+
}
485+
486+
impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> {
421487
fn tcx(&self) -> TyCtxt<'tcx> {
422488
self.tcx
423489
}
424490

425491
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
426492
// Remove unnecessary StorageLive and StorageDead annotations.
427-
data.statements.retain(|stmt| match &stmt.kind {
428-
StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => self.map[*l].is_some(),
429-
StatementKind::Assign(box (place, _)) => self.map[place.local].is_some(),
430-
_ => true,
493+
let mut i = 0usize;
494+
data.statements.retain(|stmt| {
495+
let keep = match &stmt.kind {
496+
StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
497+
self.keep_local(*l)
498+
}
499+
StatementKind::Assign(box (place, _)) => self.keep_local(place.local),
500+
_ => true,
501+
};
502+
503+
if !keep {
504+
trace!("removing statement {:?}", stmt);
505+
self.modified = true;
506+
507+
let mut visitor = StatementDeclMarker::new(self.used_locals.len(), stmt);
508+
visitor.visit_statement(stmt, Location { block, statement_index: i });
509+
510+
for (local, count) in visitor.used_locals.iter_enumerated() {
511+
let used_count = &mut self.used_locals[local];
512+
513+
// If this is the local we're removing...
514+
if *used_count != 0 {
515+
*used_count -= count;
516+
}
517+
}
518+
}
519+
520+
i += 1;
521+
522+
keep
431523
});
524+
432525
self.super_basic_block_data(block, data);
433526
}
527+
}
528+
529+
struct LocalUpdater<'tcx> {
530+
map: IndexVec<Local, Option<Local>>,
531+
tcx: TyCtxt<'tcx>,
532+
}
533+
534+
impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
535+
fn tcx(&self) -> TyCtxt<'tcx> {
536+
self.tcx
537+
}
434538

435539
fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
436540
*l = self.map[*l].unwrap();

0 commit comments

Comments
 (0)