Skip to content

Commit

Permalink
Auto merge of #70755 - wesleywiser:simplify_locals_2_electric_boogalo…
Browse files Browse the repository at this point in the history
…o, r=oli-obk

[mir-opt] Run SimplifyLocals to a fixedpoint and handle most rvalues

Follow up to review feedback left on #70595.
  • Loading branch information
bors committed Apr 16, 2020
2 parents 4e4d49d + 9666d31 commit 7fb5187
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 154 deletions.
206 changes: 167 additions & 39 deletions src/librustc_mir/transform/simplify.rs
Expand Up @@ -306,49 +306,82 @@ pub struct SimplifyLocals;
impl<'tcx> MirPass<'tcx> for SimplifyLocals {
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
trace!("running SimplifyLocals on {:?}", source);
let locals = {

// First, we're going to get a count of *actual* uses for every `Local`.
// Take a look at `DeclMarker::visit_local()` to see exactly what is ignored.
let mut used_locals = {
let read_only_cache = read_only!(body);
let mut marker = DeclMarker { locals: BitSet::new_empty(body.local_decls.len()), body };
let mut marker = DeclMarker::new(body);
marker.visit_body(&read_only_cache);
// Return pointer and arguments are always live
marker.locals.insert(RETURN_PLACE);
for arg in body.args_iter() {
marker.locals.insert(arg);
}

marker.locals
marker.local_counts
};

let map = make_local_map(&mut body.local_decls, locals);
// Update references to all vars and tmps now
LocalUpdater { map, tcx }.visit_body(body);
body.local_decls.shrink_to_fit();
let arg_count = body.arg_count;

// Next, we're going to remove any `Local` with zero actual uses. When we remove those
// `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
// count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from
// `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a
// fixedpoint where there are no more unused locals.
loop {
let mut remove_statements = RemoveStatements::new(&mut used_locals, arg_count, tcx);
remove_statements.visit_body(body);

if !remove_statements.modified {
break;
}
}

// Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s.
let map = make_local_map(&mut body.local_decls, used_locals, arg_count);

// Only bother running the `LocalUpdater` if we actually found locals to remove.
if map.iter().any(Option::is_none) {
// Update references to all vars and tmps now
let mut updater = LocalUpdater { map, tcx };
updater.visit_body(body);

body.local_decls.shrink_to_fit();
}
}
}

/// Construct the mapping while swapping out unused stuff out from the `vec`.
fn make_local_map<V>(
vec: &mut IndexVec<Local, V>,
mask: BitSet<Local>,
local_decls: &mut IndexVec<Local, V>,
used_locals: IndexVec<Local, usize>,
arg_count: usize,
) -> IndexVec<Local, Option<Local>> {
let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*vec);
let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*local_decls);
let mut used = Local::new(0);
for alive_index in mask.iter() {
for (alive_index, count) in used_locals.iter_enumerated() {
// The `RETURN_PLACE` and arguments are always live.
if alive_index.as_usize() > arg_count && *count == 0 {
continue;
}

map[alive_index] = Some(used);
if alive_index != used {
vec.swap(alive_index, used);
local_decls.swap(alive_index, used);
}
used.increment_by(1);
}
vec.truncate(used.index());
local_decls.truncate(used.index());
map
}

struct DeclMarker<'a, 'tcx> {
pub locals: BitSet<Local>,
pub local_counts: IndexVec<Local, usize>,
pub body: &'a Body<'tcx>,
}

impl<'a, 'tcx> DeclMarker<'a, 'tcx> {
pub fn new(body: &'a Body<'tcx>) -> Self {
Self { local_counts: IndexVec::from_elem(0, &body.local_decls), body }
}
}

impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) {
// Ignore storage markers altogether, they get removed along with their otherwise unused
Expand All @@ -368,51 +401,146 @@ impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
if location.statement_index != block.statements.len() {
let stmt = &block.statements[location.statement_index];

fn can_skip_constant(c: &ty::Const<'tcx>) -> bool {
// Keep assignments from unevaluated constants around, since the
// evaluation may report errors, even if the use of the constant
// is dead code.
!matches!(c.val, ty::ConstKind::Unevaluated(..))
}

fn can_skip_operand(o: &Operand<'_>) -> bool {
match o {
Operand::Copy(_) | Operand::Move(_) => true,
Operand::Constant(c) => can_skip_constant(c.literal),
}
}

if let StatementKind::Assign(box (dest, rvalue)) = &stmt.kind {
if !dest.is_indirect() && dest.local == *local {
if let Rvalue::Use(Operand::Constant(c)) = rvalue {
match c.literal.val {
// Keep assignments from unevaluated constants around, since the
// evaluation may report errors, even if the use of the constant
// is dead code.
ty::ConstKind::Unevaluated(..) => {}
_ => {
trace!("skipping store of const value {:?} to {:?}", c, dest);
return;
}
let can_skip = match rvalue {
Rvalue::Use(op) => can_skip_operand(op),
Rvalue::Discriminant(_) => true,
Rvalue::BinaryOp(_, l, r) | Rvalue::CheckedBinaryOp(_, l, r) => {
can_skip_operand(l) && can_skip_operand(r)
}
} else if let Rvalue::Discriminant(d) = rvalue {
trace!("skipping store of discriminant value {:?} to {:?}", d, dest);
Rvalue::Repeat(op, c) => can_skip_operand(op) && can_skip_constant(c),
Rvalue::AddressOf(_, _) => true,
Rvalue::Len(_) => true,
Rvalue::UnaryOp(_, op) => can_skip_operand(op),
Rvalue::Aggregate(_, operands) => operands.iter().all(can_skip_operand),

_ => false,
};

if can_skip {
trace!("skipping store of {:?} to {:?}", rvalue, dest);
return;
}
}
}
}
}

self.locals.insert(*local);
self.local_counts[*local] += 1;
}
}

struct LocalUpdater<'tcx> {
map: IndexVec<Local, Option<Local>>,
struct StatementDeclMarker<'a, 'tcx> {
used_locals: &'a mut IndexVec<Local, usize>,
statement: &'a Statement<'tcx>,
}

impl<'a, 'tcx> StatementDeclMarker<'a, 'tcx> {
pub fn new(
used_locals: &'a mut IndexVec<Local, usize>,
statement: &'a Statement<'tcx>,
) -> Self {
Self { used_locals, statement }
}
}

impl<'a, 'tcx> Visitor<'tcx> for StatementDeclMarker<'a, 'tcx> {
fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) {
// Skip the lvalue for assignments
if let StatementKind::Assign(box (p, _)) = self.statement.kind {
if p.local == *local && context.is_place_assignment() {
return;
}
}

let use_count = &mut self.used_locals[*local];
// If this is the local we're removing...
if *use_count != 0 {
*use_count -= 1;
}
}
}

struct RemoveStatements<'a, 'tcx> {
used_locals: &'a mut IndexVec<Local, usize>,
arg_count: usize,
tcx: TyCtxt<'tcx>,
modified: bool,
}

impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
impl<'a, 'tcx> RemoveStatements<'a, 'tcx> {
fn new(
used_locals: &'a mut IndexVec<Local, usize>,
arg_count: usize,
tcx: TyCtxt<'tcx>,
) -> Self {
Self { used_locals, arg_count, tcx, modified: false }
}

fn keep_local(&self, l: Local) -> bool {
trace!("keep_local({:?}): count: {:?}", l, self.used_locals[l]);
l.as_usize() <= self.arg_count || self.used_locals[l] != 0
}
}

impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}

fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
// Remove unnecessary StorageLive and StorageDead annotations.
data.statements.retain(|stmt| match &stmt.kind {
StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => self.map[*l].is_some(),
StatementKind::Assign(box (place, _)) => self.map[place.local].is_some(),
_ => true,
let mut i = 0usize;
data.statements.retain(|stmt| {
let keep = match &stmt.kind {
StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
self.keep_local(*l)
}
StatementKind::Assign(box (place, _)) => self.keep_local(place.local),
_ => true,
};

if !keep {
trace!("removing statement {:?}", stmt);
self.modified = true;

let mut visitor = StatementDeclMarker::new(self.used_locals, stmt);
visitor.visit_statement(stmt, Location { block, statement_index: i });
}

i += 1;

keep
});

self.super_basic_block_data(block, data);
}
}

struct LocalUpdater<'tcx> {
map: IndexVec<Local, Option<Local>>,
tcx: TyCtxt<'tcx>,
}

impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}

fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
*l = self.map[*l].unwrap();
Expand Down
Expand Up @@ -30,30 +30,30 @@ fn main() -> () {
}

alloc0 (static: FOO, size: 4, align: 4) {
alloc10+0╼ │ ╾──╼
alloc9+0─╼ │ ╾──╼
}

alloc10 (size: 168, align: 1) {
alloc9 (size: 168, align: 1) {
0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................
0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾alloc5+0─╼ │ ............╾──╼
0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾alloc4+0─╼ │ ............╾──╼
0x20 │ 01 ef cd ab 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾alloc7+0─╼ 00 00 │ ..........╾──╼..
0x90 │ ╾alloc8+99╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............
0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾alloc6+0─╼ 00 00 │ ..........╾──╼..
0x90 │ ╾alloc7+99╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............
0xa0 │ 00 00 00 00 00 00 00 00 │ ........
}

alloc5 (size: 4, align: 4) {
alloc4 (size: 4, align: 4) {
2a 00 00 00 │ *...
}

alloc7 (fn: main)
alloc6 (fn: main)

alloc8 (size: 100, align: 1) {
alloc7 (size: 100, align: 1) {
0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
Expand Down
Expand Up @@ -30,31 +30,31 @@ fn main() -> () {
}

alloc0 (static: FOO, size: 8, align: 8) {
╾──────alloc10+0──────╼ │ ╾──────╼
╾──────alloc9+0───────╼ │ ╾──────╼
}

alloc10 (size: 180, align: 1) {
alloc9 (size: 180, align: 1) {
0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................
0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc5+0─ │ ............╾───
0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc4+0─ │ ............╾───
0x20 │ ──────────╼ 01 ef cd ab 00 00 00 00 00 00 00 00 │ ───╼............
0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x80 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ╾──── │ ..............╾─
0x90 │ ────alloc7+0────╼ 00 00 ╾──────alloc8+99──────╼ │ ─────╼..╾──────╼
0x90 │ ────alloc6+0────╼ 00 00 ╾──────alloc7+99──────╼ │ ─────╼..╾──────╼
0xa0 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0xb0 │ 00 00 00 00 │ ....
}

alloc5 (size: 4, align: 4) {
alloc4 (size: 4, align: 4) {
2a 00 00 00 │ *...
}

alloc7 (fn: main)
alloc6 (fn: main)

alloc8 (size: 100, align: 1) {
alloc7 (size: 100, align: 1) {
0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
Expand Down

0 comments on commit 7fb5187

Please sign in to comment.