MIR pass to remove unneeded drops on types not needing drop
This is heavily dependent on MIR inlining running to actually see the drop statement
This commit is contained in:
parent
e0bc267512
commit
290dca1781
7 changed files with 209 additions and 0 deletions
|
@ -38,6 +38,7 @@ pub mod nrvo;
|
||||||
pub mod promote_consts;
|
pub mod promote_consts;
|
||||||
pub mod qualify_min_const_fn;
|
pub mod qualify_min_const_fn;
|
||||||
pub mod remove_noop_landing_pads;
|
pub mod remove_noop_landing_pads;
|
||||||
|
pub mod remove_unneeded_drops;
|
||||||
pub mod required_consts;
|
pub mod required_consts;
|
||||||
pub mod rustc_peek;
|
pub mod rustc_peek;
|
||||||
pub mod simplify;
|
pub mod simplify;
|
||||||
|
@ -461,6 +462,7 @@ fn run_optimization_passes<'tcx>(
|
||||||
|
|
||||||
// The main optimizations that we do on MIR.
|
// The main optimizations that we do on MIR.
|
||||||
let optimizations: &[&dyn MirPass<'tcx>] = &[
|
let optimizations: &[&dyn MirPass<'tcx>] = &[
|
||||||
|
&remove_unneeded_drops::RemoveUnneededDrops::new(def_id),
|
||||||
&match_branches::MatchBranchSimplification,
|
&match_branches::MatchBranchSimplification,
|
||||||
// inst combine is after MatchBranchSimplification to clean up Ne(_1, false)
|
// inst combine is after MatchBranchSimplification to clean up Ne(_1, false)
|
||||||
&instcombine::InstCombine,
|
&instcombine::InstCombine,
|
||||||
|
|
57
compiler/rustc_mir/src/transform/remove_unneeded_drops.rs
Normal file
57
compiler/rustc_mir/src/transform/remove_unneeded_drops.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
//! This pass replaces a drop of a type that does not need dropping, with a goto
|
||||||
|
|
||||||
|
use crate::transform::{MirPass, MirSource};
|
||||||
|
use rustc_hir::def_id::LocalDefId;
|
||||||
|
use rustc_middle::mir::visit::Visitor;
|
||||||
|
use rustc_middle::mir::*;
|
||||||
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
|
||||||
|
pub struct RemoveUnneededDrops {
|
||||||
|
def_id: LocalDefId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RemoveUnneededDrops {
|
||||||
|
pub fn new(def_id: LocalDefId) -> Self {
|
||||||
|
Self { def_id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
|
||||||
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
|
||||||
|
trace!("Running SimplifyComparisonIntegral on {:?}", source);
|
||||||
|
let mut opt_finder = RemoveUnneededDropsOptimizationFinder {
|
||||||
|
tcx,
|
||||||
|
body,
|
||||||
|
optimizations: vec![],
|
||||||
|
def_id: self.def_id,
|
||||||
|
};
|
||||||
|
opt_finder.visit_body(body);
|
||||||
|
for (loc, target) in opt_finder.optimizations {
|
||||||
|
let terminator = body.basic_blocks_mut()[loc.block].terminator_mut();
|
||||||
|
debug!("SUCCESS: replacing `drop` with goto({:?})", target);
|
||||||
|
terminator.kind = TerminatorKind::Goto { target };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> Visitor<'tcx> for RemoveUnneededDropsOptimizationFinder<'a, 'tcx> {
|
||||||
|
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||||
|
match terminator.kind {
|
||||||
|
TerminatorKind::Drop { place, target, .. } => {
|
||||||
|
let ty = place.ty(self.body, self.tcx);
|
||||||
|
let needs_drop = ty.ty.needs_drop(self.tcx, self.tcx.param_env(self.def_id));
|
||||||
|
if !needs_drop {
|
||||||
|
self.optimizations.push((location, target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
self.super_terminator(terminator, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct RemoveUnneededDropsOptimizationFinder<'a, 'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
body: &'a Body<'tcx>,
|
||||||
|
optimizations: Vec<(Location, BasicBlock)>,
|
||||||
|
def_id: LocalDefId,
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
- // MIR for `cannot_opt_generic` before RemoveUnneededDrops
|
||||||
|
+ // MIR for `cannot_opt_generic` after RemoveUnneededDrops
|
||||||
|
|
||||||
|
fn cannot_opt_generic(_1: T) -> () {
|
||||||
|
debug x => _1; // in scope 0 at $DIR/remove_unneeded_drops.rs:19:26: 19:27
|
||||||
|
let mut _0: (); // return place in scope 0 at $DIR/remove_unneeded_drops.rs:19:32: 19:32
|
||||||
|
let _2: (); // in scope 0 at $DIR/remove_unneeded_drops.rs:20:5: 20:12
|
||||||
|
let mut _3: T; // in scope 0 at $DIR/remove_unneeded_drops.rs:20:10: 20:11
|
||||||
|
scope 1 {
|
||||||
|
debug _x => _3; // in scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:20:5: 20:12
|
||||||
|
StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:20:10: 20:11
|
||||||
|
_3 = move _1; // scope 0 at $DIR/remove_unneeded_drops.rs:20:10: 20:11
|
||||||
|
_2 = const (); // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
drop(_3) -> [return: bb2, unwind: bb1]; // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1 (cleanup): {
|
||||||
|
resume; // scope 0 at $DIR/remove_unneeded_drops.rs:19:1: 21:2
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
StorageDead(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:20:11: 20:12
|
||||||
|
StorageDead(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:20:12: 20:13
|
||||||
|
_0 = const (); // scope 0 at $DIR/remove_unneeded_drops.rs:19:32: 21:2
|
||||||
|
return; // scope 0 at $DIR/remove_unneeded_drops.rs:21:2: 21:2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
- // MIR for `dont_opt` before RemoveUnneededDrops
|
||||||
|
+ // MIR for `dont_opt` after RemoveUnneededDrops
|
||||||
|
|
||||||
|
fn dont_opt(_1: Vec<bool>) -> () {
|
||||||
|
debug x => _1; // in scope 0 at $DIR/remove_unneeded_drops.rs:7:13: 7:14
|
||||||
|
let mut _0: (); // return place in scope 0 at $DIR/remove_unneeded_drops.rs:7:27: 7:27
|
||||||
|
let _2: (); // in scope 0 at $DIR/remove_unneeded_drops.rs:8:5: 8:12
|
||||||
|
let mut _3: std::vec::Vec<bool>; // in scope 0 at $DIR/remove_unneeded_drops.rs:8:10: 8:11
|
||||||
|
scope 1 {
|
||||||
|
debug _x => _3; // in scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:8:5: 8:12
|
||||||
|
StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:8:10: 8:11
|
||||||
|
_3 = move _1; // scope 0 at $DIR/remove_unneeded_drops.rs:8:10: 8:11
|
||||||
|
_2 = const (); // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
drop(_3) -> [return: bb2, unwind: bb1]; // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1 (cleanup): {
|
||||||
|
resume; // scope 0 at $DIR/remove_unneeded_drops.rs:7:1: 9:2
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
StorageDead(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:8:11: 8:12
|
||||||
|
StorageDead(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:8:12: 8:13
|
||||||
|
_0 = const (); // scope 0 at $DIR/remove_unneeded_drops.rs:7:27: 9:2
|
||||||
|
return; // scope 0 at $DIR/remove_unneeded_drops.rs:9:2: 9:2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
- // MIR for `opt` before RemoveUnneededDrops
|
||||||
|
+ // MIR for `opt` after RemoveUnneededDrops
|
||||||
|
|
||||||
|
fn opt(_1: bool) -> () {
|
||||||
|
debug x => _1; // in scope 0 at $DIR/remove_unneeded_drops.rs:2:8: 2:9
|
||||||
|
let mut _0: (); // return place in scope 0 at $DIR/remove_unneeded_drops.rs:2:17: 2:17
|
||||||
|
let _2: (); // in scope 0 at $DIR/remove_unneeded_drops.rs:3:5: 3:12
|
||||||
|
let mut _3: bool; // in scope 0 at $DIR/remove_unneeded_drops.rs:3:10: 3:11
|
||||||
|
scope 1 {
|
||||||
|
debug _x => _3; // in scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:3:5: 3:12
|
||||||
|
StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:3:10: 3:11
|
||||||
|
_3 = _1; // scope 0 at $DIR/remove_unneeded_drops.rs:3:10: 3:11
|
||||||
|
_2 = const (); // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
- drop(_3) -> bb1; // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
+ goto -> bb1; // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
StorageDead(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:3:11: 3:12
|
||||||
|
StorageDead(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:3:12: 3:13
|
||||||
|
_0 = const (); // scope 0 at $DIR/remove_unneeded_drops.rs:2:17: 4:2
|
||||||
|
return; // scope 0 at $DIR/remove_unneeded_drops.rs:4:2: 4:2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
- // MIR for `opt_generic_copy` before RemoveUnneededDrops
|
||||||
|
+ // MIR for `opt_generic_copy` after RemoveUnneededDrops
|
||||||
|
|
||||||
|
fn opt_generic_copy(_1: T) -> () {
|
||||||
|
debug x => _1; // in scope 0 at $DIR/remove_unneeded_drops.rs:12:30: 12:31
|
||||||
|
let mut _0: (); // return place in scope 0 at $DIR/remove_unneeded_drops.rs:12:36: 12:36
|
||||||
|
let _2: (); // in scope 0 at $DIR/remove_unneeded_drops.rs:13:5: 13:12
|
||||||
|
let mut _3: T; // in scope 0 at $DIR/remove_unneeded_drops.rs:13:10: 13:11
|
||||||
|
scope 1 {
|
||||||
|
debug _x => _3; // in scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:13:5: 13:12
|
||||||
|
StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:13:10: 13:11
|
||||||
|
_3 = _1; // scope 0 at $DIR/remove_unneeded_drops.rs:13:10: 13:11
|
||||||
|
_2 = const (); // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
- drop(_3) -> bb1; // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
+ goto -> bb1; // scope 1 at $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
StorageDead(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:13:11: 13:12
|
||||||
|
StorageDead(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:13:12: 13:13
|
||||||
|
_0 = const (); // scope 0 at $DIR/remove_unneeded_drops.rs:12:36: 14:2
|
||||||
|
return; // scope 0 at $DIR/remove_unneeded_drops.rs:14:2: 14:2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
28
src/test/mir-opt/remove_unneeded_drops.rs
Normal file
28
src/test/mir-opt/remove_unneeded_drops.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// EMIT_MIR remove_unneeded_drops.opt.RemoveUnneededDrops.diff
|
||||||
|
fn opt(x: bool) {
|
||||||
|
drop(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff
|
||||||
|
fn dont_opt(x: Vec<bool>) {
|
||||||
|
drop(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff
|
||||||
|
fn opt_generic_copy<T: Copy>(x: T) {
|
||||||
|
drop(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff
|
||||||
|
// since the pass is not running on monomorphisized code,
|
||||||
|
// we can't (but probably should) optimize this
|
||||||
|
fn cannot_opt_generic<T>(x: T) {
|
||||||
|
drop(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
opt(true);
|
||||||
|
opt_generic_copy(42);
|
||||||
|
cannot_opt_generic(42);
|
||||||
|
dont_opt(vec![true]);
|
||||||
|
}
|
Loading…
Reference in a new issue