diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index abe2dc496a630..f48ad039b4ff9 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -38,6 +38,7 @@ pub mod nrvo; pub mod promote_consts; pub mod qualify_min_const_fn; pub mod remove_noop_landing_pads; +pub mod remove_unneeded_drops; pub mod required_consts; pub mod rustc_peek; pub mod simplify; @@ -461,6 +462,7 @@ fn run_optimization_passes<'tcx>( // The main optimizations that we do on MIR. let optimizations: &[&dyn MirPass<'tcx>] = &[ + &remove_unneeded_drops::RemoveUnneededDrops, &match_branches::MatchBranchSimplification, // inst combine is after MatchBranchSimplification to clean up Ne(_1, false) &instcombine::InstCombine, diff --git a/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs new file mode 100644 index 0000000000000..f027f5e33a187 --- /dev/null +++ b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs @@ -0,0 +1,59 @@ +//! 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; + +use super::simplify::simplify_cfg; + +pub struct RemoveUnneededDrops; + +impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + trace!("Running RemoveUnneededDrops on {:?}", source); + let mut opt_finder = RemoveUnneededDropsOptimizationFinder { + tcx, + body, + optimizations: vec![], + def_id: source.def_id().expect_local(), + }; + opt_finder.visit_body(body); + let should_simplify = !opt_finder.optimizations.is_empty(); + 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 }; + } + + // if we applied optimizations, we potentially have some cfg to cleanup to + // make it easier for further passes + if should_simplify { + simplify_cfg(body); + } + } +} + +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, .. } + | TerminatorKind::DropAndReplace { 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, +} diff --git a/src/test/mir-opt/dest-prop/cycle.main.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/cycle.main.DestinationPropagation.diff index dd717c1b9c324..881c296cee77a 100644 --- a/src/test/mir-opt/dest-prop/cycle.main.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/cycle.main.DestinationPropagation.diff @@ -57,10 +57,6 @@ - _6 = _1; // scope 3 at $DIR/cycle.rs:14:10: 14:11 + _6 = _4; // scope 3 at $DIR/cycle.rs:14:10: 14:11 _5 = const (); // scope 4 at $SRC_DIR/core/src/mem/mod.rs:LL:COL - drop(_6) -> bb2; // scope 4 at $SRC_DIR/core/src/mem/mod.rs:LL:COL - } - - bb2: { StorageDead(_6); // scope 3 at $DIR/cycle.rs:14:11: 14:12 StorageDead(_5); // scope 3 at $DIR/cycle.rs:14:12: 14:13 _0 = const (); // scope 0 at $DIR/cycle.rs:8:11: 15:2 diff --git a/src/test/mir-opt/dest-prop/union.main.DestinationPropagation.diff b/src/test/mir-opt/dest-prop/union.main.DestinationPropagation.diff index 871f6e35043ec..f15e7bcb2fba6 100644 --- a/src/test/mir-opt/dest-prop/union.main.DestinationPropagation.diff +++ b/src/test/mir-opt/dest-prop/union.main.DestinationPropagation.diff @@ -32,10 +32,6 @@ StorageLive(_4); // scope 1 at $DIR/union.rs:15:10: 15:26 _4 = (_1.0: u32); // scope 2 at $DIR/union.rs:15:19: 15:24 _3 = const (); // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL - drop(_4) -> bb2; // scope 3 at $SRC_DIR/core/src/mem/mod.rs:LL:COL - } - - bb2: { StorageDead(_4); // scope 1 at $DIR/union.rs:15:26: 15:27 StorageDead(_3); // scope 1 at $DIR/union.rs:15:27: 15:28 _0 = const (); // scope 0 at $DIR/union.rs:8:11: 16:2 diff --git a/src/test/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff b/src/test/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff new file mode 100644 index 0000000000000..787cf6f97c1f2 --- /dev/null +++ b/src/test/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff @@ -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:20:26: 20:27 + let mut _0: (); // return place in scope 0 at $DIR/remove_unneeded_drops.rs:20:32: 20:32 + let _2: (); // in scope 0 at $DIR/remove_unneeded_drops.rs:21:5: 21:12 + let mut _3: T; // in scope 0 at $DIR/remove_unneeded_drops.rs:21:10: 21: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:21:5: 21:12 + StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:21:10: 21:11 + _3 = move _1; // scope 0 at $DIR/remove_unneeded_drops.rs:21:10: 21: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:20:1: 22:2 + } + + bb2: { + StorageDead(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:21:11: 21:12 + StorageDead(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:21:12: 21:13 + _0 = const (); // scope 0 at $DIR/remove_unneeded_drops.rs:20:32: 22:2 + return; // scope 0 at $DIR/remove_unneeded_drops.rs:22:2: 22:2 + } + } + diff --git a/src/test/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff b/src/test/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff new file mode 100644 index 0000000000000..52e182eeb4a8a --- /dev/null +++ b/src/test/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff @@ -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:8:13: 8:14 + let mut _0: (); // return place in scope 0 at $DIR/remove_unneeded_drops.rs:8:27: 8:27 + let _2: (); // in scope 0 at $DIR/remove_unneeded_drops.rs:9:5: 9:12 + let mut _3: std::vec::Vec<bool>; // in scope 0 at $DIR/remove_unneeded_drops.rs:9:10: 9: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:9:5: 9:12 + StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:9:10: 9:11 + _3 = move _1; // scope 0 at $DIR/remove_unneeded_drops.rs:9:10: 9: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:8:1: 10:2 + } + + bb2: { + StorageDead(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:9:11: 9:12 + StorageDead(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:9:12: 9:13 + _0 = const (); // scope 0 at $DIR/remove_unneeded_drops.rs:8:27: 10:2 + return; // scope 0 at $DIR/remove_unneeded_drops.rs:10:2: 10:2 + } + } + diff --git a/src/test/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff b/src/test/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff new file mode 100644 index 0000000000000..bc9e1344f3176 --- /dev/null +++ b/src/test/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff @@ -0,0 +1,28 @@ +- // 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:3:8: 3:9 + let mut _0: (); // return place in scope 0 at $DIR/remove_unneeded_drops.rs:3:17: 3:17 + let _2: (); // in scope 0 at $DIR/remove_unneeded_drops.rs:4:5: 4:12 + let mut _3: bool; // in scope 0 at $DIR/remove_unneeded_drops.rs:4:10: 4: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:4:5: 4:12 + StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:4:10: 4:11 + _3 = _1; // scope 0 at $DIR/remove_unneeded_drops.rs:4:10: 4: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 +- } +- +- bb1: { + StorageDead(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:4:11: 4:12 + StorageDead(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:4:12: 4:13 + _0 = const (); // scope 0 at $DIR/remove_unneeded_drops.rs:3:17: 5:2 + return; // scope 0 at $DIR/remove_unneeded_drops.rs:5:2: 5:2 + } + } + diff --git a/src/test/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff b/src/test/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff new file mode 100644 index 0000000000000..5c8b1d1372115 --- /dev/null +++ b/src/test/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff @@ -0,0 +1,28 @@ +- // 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:13:30: 13:31 + let mut _0: (); // return place in scope 0 at $DIR/remove_unneeded_drops.rs:13:36: 13:36 + let _2: (); // in scope 0 at $DIR/remove_unneeded_drops.rs:14:5: 14:12 + let mut _3: T; // in scope 0 at $DIR/remove_unneeded_drops.rs:14:10: 14: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:14:5: 14:12 + StorageLive(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:14:10: 14:11 + _3 = _1; // scope 0 at $DIR/remove_unneeded_drops.rs:14:10: 14: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 +- } +- +- bb1: { + StorageDead(_3); // scope 0 at $DIR/remove_unneeded_drops.rs:14:11: 14:12 + StorageDead(_2); // scope 0 at $DIR/remove_unneeded_drops.rs:14:12: 14:13 + _0 = const (); // scope 0 at $DIR/remove_unneeded_drops.rs:13:36: 15:2 + return; // scope 0 at $DIR/remove_unneeded_drops.rs:15:2: 15:2 + } + } + diff --git a/src/test/mir-opt/remove_unneeded_drops.rs b/src/test/mir-opt/remove_unneeded_drops.rs new file mode 100644 index 0000000000000..1052f2886770d --- /dev/null +++ b/src/test/mir-opt/remove_unneeded_drops.rs @@ -0,0 +1,29 @@ +// ignore-wasm32-bare compiled with panic=abort by default +// 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]); +}