diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 0cbd945095a95..b04297539df97 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -31,7 +31,7 @@ use std::ascii; use std::borrow::{Cow}; use std::cell::Ref; use std::fmt::{self, Debug, Formatter, Write}; -use std::{iter, u32}; +use std::{iter, mem, u32}; use std::ops::{Index, IndexMut}; use std::rc::Rc; use std::vec::IntoIter; @@ -998,6 +998,10 @@ impl<'tcx> Statement<'tcx> { pub fn make_nop(&mut self) { self.kind = StatementKind::Nop } + + pub fn replace_nop(&mut self) -> StatementKind<'tcx> { + mem::replace(&mut self.kind, StatementKind::Nop) + } } #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 418d3d220581e..3e7df1f209de5 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -228,6 +228,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx run_passes![tcx, mir, def_id, 2; no_landing_pads::NoLandingPads, simplify_branches::SimplifyBranches::new("initial"), + simplify::SimplifyTempMoves, // These next passes must be executed together add_call_guards::CriticalCallEdges, diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index 2e7c3714ffe12..b9790b0122113 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -10,13 +10,19 @@ //! A number of passes which remove various redundancies in the CFG. //! -//! The `SimplifyCfg` pass gets rid of unnecessary blocks in the CFG, whereas the `SimplifyLocals` -//! gets rid of all the unnecessary local variable declarations. +//! The `SimplifyCfg` pass gets rid of unnecessary blocks in the CFG, the `SimplifyTempMoves` pass +//! eliminates copies/moves into temporaries out of which the value is immediately moved, whereas +//! the `SimplifyLocals`the gets rid of all the unnecessary local variable declarations. //! //! The `SimplifyLocals` pass is kinda expensive and therefore not very suitable to be run often. //! Most of the passes should not care or be impacted in meaningful ways due to extra locals //! either, so running the pass once, right before translation, should suffice. //! +//! The `SimplifyTempMoves` pass is is a simple peephole optimization there only to clean up a +//! common foible of the HIR->MIR building algorithm, reducing the number of interesting statemnts +//! that other passes need to consider. It's probably not ever worth running a second time. +//! Instead, run the smarter but more expensive passes like lvalue reuse and copy propagation. +//! //! On the other side of the spectrum, the `SimplifyCfg` pass is considerably cheap to run, thus //! one should run it after every pass which may modify CFG in significant ways. This pass must //! also be run before any analysis passes because it removes dead blocks, and some of these can be @@ -311,6 +317,64 @@ pub fn remove_dead_blocks(mir: &mut Mir) { } +pub struct SimplifyTempMoves; + +impl MirPass for SimplifyTempMoves { + fn run_pass<'a, 'tcx>(&self, + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + _src: MirSource, + mir: &mut Mir<'tcx>) { + let last_arg = Local::new(mir.arg_count); + let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut(); + for block in basic_blocks { + for j in 1..block.statements.len() { + let i = j - 1; + + if block.statements[i].source_info.span != + block.statements[j].source_info.span + { continue } + + let temp_local; + match block.statements[i].kind { + StatementKind::Assign( + Place::Local(local), + Rvalue::Use(_) + ) if local > last_arg && !local_decls[local].is_user_variable => { + temp_local = local; + } + _ => { continue } + } + + match block.statements[j].kind { + StatementKind::Assign( + _, + Rvalue::Use(Operand::Move(Place::Local(local))) + ) if local == temp_local => { + // yay + } + _ => { continue } + } + + debug!("simplifying {:?}; {:?} // {:?}", + block.statements[i], block.statements[j], + block.statements[j].source_info.span); + let rvalue = + match block.statements[i].replace_nop() { + StatementKind::Assign(_, rvalue) => rvalue, + _ => bug!("No longer an assignment?"), + }; + let place = + match block.statements[j].replace_nop() { + StatementKind::Assign(place, _) => place, + _ => bug!("No longer an assignment?"), + }; + block.statements[j].kind = StatementKind::Assign(place, rvalue); + } + } + } +} + + pub struct SimplifyLocals; impl MirPass for SimplifyLocals { diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs index 50d8a5154c449..067a937b0b30c 100644 --- a/src/test/mir-opt/copy_propagation.rs +++ b/src/test/mir-opt/copy_propagation.rs @@ -22,12 +22,9 @@ fn main() { // START rustc.test.CopyPropagation.before.mir // bb0: { // ... -// _3 = _1; +// _2 = _1; // ... -// _2 = move _3; -// ... -// _4 = _2; -// _0 = move _4; +// _0 = _2; // ... // return; // } @@ -35,7 +32,7 @@ fn main() { // START rustc.test.CopyPropagation.after.mir // bb0: { // ... -// _0 = move _1; +// _0 = _1; // ... // return; // } diff --git a/src/test/mir-opt/inline-closure-borrows-arg.rs b/src/test/mir-opt/inline-closure-borrows-arg.rs index 3fb54f90984dd..6ce51be3ec5db 100644 --- a/src/test/mir-opt/inline-closure-borrows-arg.rs +++ b/src/test/mir-opt/inline-closure-borrows-arg.rs @@ -38,11 +38,9 @@ fn foo(_t: T, q: &i32) -> i32 { // ... // _7 = &(*_2); // _5 = (move _6, move _7); -// _9 = move (_5.0: &i32); -// _10 = move (_5.1: &i32); -// StorageLive(_8); -// _8 = (*_9); -// _0 = move _8; +// _8 = move (_5.0: &i32); +// _9 = move (_5.1: &i32); +// _0 = (*_8); // ... // return; // } diff --git a/src/test/mir-opt/inline-closure.rs b/src/test/mir-opt/inline-closure.rs index dc8ff13c03a88..22e7de31e90cf 100644 --- a/src/test/mir-opt/inline-closure.rs +++ b/src/test/mir-opt/inline-closure.rs @@ -36,7 +36,7 @@ fn foo(_t: T, q: i32) -> i32 { // _5 = (move _6, move _7); // _8 = move (_5.0: i32); // _9 = move (_5.1: i32); -// _0 = move _8; +// _0 = _8; // ... // return; // } diff --git a/src/test/mir-opt/simplify_temp_moves.rs b/src/test/mir-opt/simplify_temp_moves.rs new file mode 100644 index 0000000000000..37b2ce16ebdaf --- /dev/null +++ b/src/test/mir-opt/simplify_temp_moves.rs @@ -0,0 +1,81 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[inline(never)] +fn nop(_: T) {} + +fn test_return_move_local() -> String { + let x = String::new(); + x +} + +fn test_return_copy_local() -> i32 { + let x = 4; + x +} + +fn test_shadow_arg_mut(x: String) { + let mut x = x; + nop(&mut x); +} + +fn test_return_field(x: &(i32, u32)) -> i32 { + x.0 +} + +fn main() { + // Make sure the functions actually get instantiated. + test_return_move_local(); + test_return_copy_local(); + test_shadow_arg_mut(String::new()); + test_return_field(&(1, 2)); +} + +// END RUST SOURCE + +// START rustc.test_return_move_local.SimplifyTempMoves.before.mir +// _1 = const std::string::String::new() -> bb1; +// ... +// _2 = move _1; +// _0 = move _2; +// END rustc.test_return_move_local.SimplifyTempMoves.before.mir +// START rustc.test_return_move_local.SimplifyTempMoves.after.mir +// nop; +// _0 = move _1; +// END rustc.test_return_move_local.SimplifyTempMoves.after.mir + +// START rustc.test_return_copy_local.SimplifyTempMoves.before.mir +// _1 = const 4i32; +// ... +// _2 = _1; +// _0 = move _2; +// END rustc.test_return_copy_local.SimplifyTempMoves.before.mir +// START rustc.test_return_copy_local.SimplifyTempMoves.after.mir +// nop; +// _0 = _1; +// END rustc.test_return_copy_local.SimplifyTempMoves.after.mir + +// START rustc.test_shadow_arg_mut.SimplifyTempMoves.before.mir +// _3 = move _1; +// _2 = move _3; +// END rustc.test_shadow_arg_mut.SimplifyTempMoves.before.mir +// START rustc.test_shadow_arg_mut.SimplifyTempMoves.after.mir +// nop; +// _2 = move _1; +// END rustc.test_shadow_arg_mut.SimplifyTempMoves.after.mir + +// START rustc.test_return_field.SimplifyTempMoves.before.mir +// _2 = ((*_1).0: i32); +// _0 = move _2; +// END rustc.test_return_field.SimplifyTempMoves.before.mir +// START rustc.test_return_field.SimplifyTempMoves.after.mir +// nop; +// _0 = ((*_1).0: i32); +// END rustc.test_return_field.SimplifyTempMoves.after.mir diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs index e6cd535500055..2f3628210dd03 100644 --- a/src/test/mir-opt/validate_1.rs +++ b/src/test/mir-opt/validate_1.rs @@ -68,8 +68,7 @@ fn main() { // _3 = &ReErased (*_2); // Validate(Acquire, [(*_3): i32/ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 })) (imm)]); // StorageLive(_4); -// _4 = (*_3); -// _0 = move _4; +// _0 = (*_3); // StorageDead(_4); // EndRegion(ReScope(Remainder(BlockRemainder { block: ItemLocalId(22), first_statement_index: 0 }))); // StorageDead(_3);