Skip to content

Commit 2ec7339

Browse files
committed
Improve SimplifyLocals pass so it can remove unused consts
The `ConstProp` can cause many locals to be initialized to a constant value and then never read from. `ConstProp` can also evaluate ZSTs into constant values. Previously, many of these would be removed by other parts of the MIR optimization pipeline. However, evaluating ZSTs (especially `()`) into constant values defeated those parts of the optimizer and so in a2e3ed5, I added a hack to `ConstProp` that skips evaluating ZSTs to avoid that regression. This commit changes `SimplifyLocals` so that it doesn't consider writes of const values to a local to be a use of that local. In doing so, `SimplifyLocals` is able to remove otherwise unused locals left behind by other optimization passes (`ConstProp` in particular).
1 parent 4592a9e commit 2ec7339

File tree

9 files changed

+154
-38
lines changed

9 files changed

+154
-38
lines changed

src/librustc_mir/transform/const_prop.rs

-7
Original file line numberDiff line numberDiff line change
@@ -540,13 +540,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
540540
}
541541
}
542542

543-
// Work around: avoid extra unnecessary locals. FIXME(wesleywiser)
544-
// Const eval will turn this into a `const Scalar(<ZST>)` that
545-
// `SimplifyLocals` doesn't know it can remove.
546-
Rvalue::Aggregate(_, operands) if operands.len() == 0 => {
547-
return None;
548-
}
549-
550543
_ => { }
551544
}
552545

src/librustc_mir/transform/simplify.rs

+54-22
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use rustc_index::bit_set::BitSet;
3131
use rustc_index::vec::{Idx, IndexVec};
3232
use rustc::ty::TyCtxt;
3333
use rustc::mir::*;
34-
use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext};
34+
use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext, MutatingUseContext};
3535
use rustc::session::config::DebugInfo;
3636
use std::borrow::Cow;
3737
use crate::transform::{MirPass, MirSource};
@@ -293,23 +293,31 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
293293
pub struct SimplifyLocals;
294294

295295
impl<'tcx> MirPass<'tcx> for SimplifyLocals {
296-
fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
297-
let mut marker = DeclMarker { locals: BitSet::new_empty(body.local_decls.len()) };
298-
marker.visit_body(body);
299-
// Return pointer and arguments are always live
300-
marker.locals.insert(RETURN_PLACE);
301-
for arg in body.args_iter() {
302-
marker.locals.insert(arg);
303-
}
296+
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
297+
trace!("running SimplifyLocals on {:?}", source);
298+
let locals = {
299+
let mut marker = DeclMarker {
300+
locals: BitSet::new_empty(body.local_decls.len()),
301+
body,
302+
};
303+
marker.visit_body(body);
304+
// Return pointer and arguments are always live
305+
marker.locals.insert(RETURN_PLACE);
306+
for arg in body.args_iter() {
307+
marker.locals.insert(arg);
308+
}
304309

305-
// We may need to keep dead user variables live for debuginfo.
306-
if tcx.sess.opts.debuginfo == DebugInfo::Full {
307-
for local in body.vars_iter() {
308-
marker.locals.insert(local);
310+
// We may need to keep dead user variables live for debuginfo.
311+
if tcx.sess.opts.debuginfo == DebugInfo::Full {
312+
for local in body.vars_iter() {
313+
marker.locals.insert(local);
314+
}
309315
}
310-
}
311316

312-
let map = make_local_map(&mut body.local_decls, marker.locals);
317+
marker.locals
318+
};
319+
320+
let map = make_local_map(&mut body.local_decls, locals);
313321
// Update references to all vars and tmps now
314322
LocalUpdater { map }.visit_body(body);
315323
body.local_decls.shrink_to_fit();
@@ -334,18 +342,35 @@ fn make_local_map<V>(
334342
map
335343
}
336344

337-
struct DeclMarker {
345+
struct DeclMarker<'a, 'tcx> {
338346
pub locals: BitSet<Local>,
347+
pub body: &'a Body<'tcx>,
339348
}
340349

341-
impl<'tcx> Visitor<'tcx> for DeclMarker {
342-
fn visit_local(&mut self, local: &Local, ctx: PlaceContext, _: Location) {
350+
impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
351+
fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) {
343352
// Ignore storage markers altogether, they get removed along with their otherwise unused
344353
// decls.
345354
// FIXME: Extend this to all non-uses.
346-
if !ctx.is_storage_marker() {
347-
self.locals.insert(*local);
355+
if ctx.is_storage_marker() {
356+
return;
348357
}
358+
359+
// Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many
360+
// of these locals. However, if the local is still needed, then it will be referenced in
361+
// another place and we'll mark it as being used there.
362+
if ctx == PlaceContext::MutatingUse(MutatingUseContext::Store) {
363+
let stmt =
364+
&self.body.basic_blocks()[location.block].statements[location.statement_index];
365+
if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(c)))) = &stmt.kind {
366+
if p.as_local().is_some() {
367+
trace!("skipping store of const value {:?} to {:?}", c, local);
368+
return;
369+
}
370+
}
371+
}
372+
373+
self.locals.insert(*local);
349374
}
350375
}
351376

@@ -357,9 +382,16 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater {
357382
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
358383
// Remove unnecessary StorageLive and StorageDead annotations.
359384
data.statements.retain(|stmt| {
360-
match stmt.kind {
385+
match &stmt.kind {
361386
StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
362-
self.map[l].is_some()
387+
self.map[*l].is_some()
388+
}
389+
StatementKind::Assign(box (place, _)) => {
390+
if let Some(local) = place.as_local() {
391+
self.map[local].is_some()
392+
} else {
393+
true
394+
}
363395
}
364396
_ => true
365397
}

src/test/incremental/hashes/for_loops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub fn change_loop_body() {
2525
}
2626

2727
#[cfg(not(cfail1))]
28-
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
28+
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
2929
#[rustc_clean(cfg="cfail3")]
3030
pub fn change_loop_body() {
3131
let mut _x = 0;

src/test/incremental/hashes/let_expressions.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub fn change_name() {
2222

2323
#[cfg(not(cfail1))]
2424
#[rustc_clean(cfg="cfail2",
25-
except="HirBody,mir_built,optimized_mir")]
25+
except="HirBody,mir_built")]
2626
#[rustc_clean(cfg="cfail3")]
2727
pub fn change_name() {
2828
let _y = 2u64;
@@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() {
8686

8787
#[cfg(not(cfail1))]
8888
#[rustc_clean(cfg="cfail2",
89-
except="HirBody,typeck_tables_of,mir_built,optimized_mir")]
89+
except="HirBody,typeck_tables_of,mir_built")]
9090
#[rustc_clean(cfg="cfail3")]
9191
pub fn change_mutability_of_slot() {
9292
let _x: u64 = 0;
@@ -182,7 +182,7 @@ pub fn add_initializer() {
182182

183183
#[cfg(not(cfail1))]
184184
#[rustc_clean(cfg="cfail2",
185-
except="HirBody,typeck_tables_of,mir_built,optimized_mir")]
185+
except="HirBody,typeck_tables_of,mir_built")]
186186
#[rustc_clean(cfg="cfail3")]
187187
pub fn add_initializer() {
188188
let _x: i16 = 3i16;
@@ -198,7 +198,7 @@ pub fn change_initializer() {
198198

199199
#[cfg(not(cfail1))]
200200
#[rustc_clean(cfg="cfail2",
201-
except="HirBody,mir_built,optimized_mir")]
201+
except="HirBody,mir_built")]
202202
#[rustc_clean(cfg="cfail3")]
203203
pub fn change_initializer() {
204204
let _x = 5u16;

src/test/incremental/hashes/loop_expressions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub fn change_loop_body() {
2525
}
2626

2727
#[cfg(not(cfail1))]
28-
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
28+
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
2929
#[rustc_clean(cfg="cfail3")]
3030
pub fn change_loop_body() {
3131
let mut _x = 0;

src/test/incremental/hashes/while_let_loops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub fn change_loop_body() {
2525
}
2626

2727
#[cfg(not(cfail1))]
28-
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
28+
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
2929
#[rustc_clean(cfg="cfail3")]
3030
pub fn change_loop_body() {
3131
let mut _x = 0;

src/test/incremental/hashes/while_loops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub fn change_loop_body() {
2525
}
2626

2727
#[cfg(not(cfail1))]
28-
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
28+
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
2929
#[rustc_clean(cfg="cfail3")]
3030
pub fn change_loop_body() {
3131
let mut _x = 0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// compile-flags: -C overflow-checks=no
2+
3+
fn use_zst(_: ((), ())) { }
4+
5+
struct Temp {
6+
x: u8
7+
}
8+
9+
fn use_u8(_: u8) { }
10+
11+
fn main() {
12+
let ((), ()) = ((), ());
13+
use_zst(((), ()));
14+
15+
use_u8((Temp { x : 40 }).x + 2);
16+
}
17+
18+
// END RUST SOURCE
19+
20+
// START rustc.main.SimplifyLocals.before.mir
21+
// let mut _0: ();
22+
// let mut _1: ((), ());
23+
// let mut _2: ();
24+
// let mut _3: ();
25+
// let _4: ();
26+
// let mut _5: ((), ());
27+
// let mut _6: ();
28+
// let mut _7: ();
29+
// let _8: ();
30+
// let mut _9: u8;
31+
// let mut _10: u8;
32+
// let mut _11: Temp;
33+
// scope 1 {
34+
// }
35+
// bb0: {
36+
// StorageLive(_1);
37+
// StorageLive(_2);
38+
// _2 = const Scalar(<ZST>) : ();
39+
// StorageLive(_3);
40+
// _3 = const Scalar(<ZST>) : ();
41+
// _1 = const Scalar(<ZST>) : ((), ());
42+
// StorageDead(_3);
43+
// StorageDead(_2);
44+
// StorageDead(_1);
45+
// StorageLive(_4);
46+
// StorageLive(_6);
47+
// _6 = const Scalar(<ZST>) : ();
48+
// StorageLive(_7);
49+
// _7 = const Scalar(<ZST>) : ();
50+
// StorageDead(_7);
51+
// StorageDead(_6);
52+
// _4 = const use_zst(const Scalar(<ZST>) : ((), ())) -> bb1;
53+
// }
54+
// bb1: {
55+
// StorageDead(_4);
56+
// StorageLive(_8);
57+
// StorageLive(_10);
58+
// StorageLive(_11);
59+
// _11 = const Scalar(0x28) : Temp;
60+
// _10 = const 40u8;
61+
// StorageDead(_10);
62+
// _8 = const use_u8(const 42u8) -> bb2;
63+
// }
64+
// bb2: {
65+
// StorageDead(_11);
66+
// StorageDead(_8);
67+
// return;
68+
// }
69+
// END rustc.main.SimplifyLocals.before.mir
70+
// START rustc.main.SimplifyLocals.after.mir
71+
// let mut _0: ();
72+
// let _1: ();
73+
// let _2: ();
74+
// scope 1 {
75+
// }
76+
// bb0: {
77+
// StorageLive(_1);
78+
// _1 = const use_zst(const Scalar(<ZST>) : ((), ())) -> bb1;
79+
// }
80+
// bb1: {
81+
// StorageDead(_1);
82+
// StorageLive(_2);
83+
// _2 = const use_u8(const 42u8) -> bb2;
84+
// }
85+
// bb2: {
86+
// StorageDead(_2);
87+
// return;
88+
// }
89+
// END rustc.main.SimplifyLocals.after.mir

src/test/mir-opt/slice-drop-shim.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
// compile-flags: -Zmir-opt-level=0
2+
13
fn main() {
2-
std::ptr::drop_in_place::<[String]> as unsafe fn(_);
4+
let _fn = std::ptr::drop_in_place::<[String]> as unsafe fn(_);
35
}
46

57
// END RUST SOURCE

0 commit comments

Comments
 (0)