diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index d90bf1b61a7d3..a50a9c819f6ec 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -376,7 +376,7 @@ macro_rules! make_mir_visitor { ref $($mutability)* inputs, asm: _ } => { for output in & $($mutability)* outputs[..] { - self.visit_place(output, PlaceContext::Store, location); + self.visit_place(output, PlaceContext::AsmOutput, location); } for input in & $($mutability)* inputs[..] { self.visit_operand(input, location); @@ -835,6 +835,11 @@ pub enum PlaceContext<'tcx> { // Appears as LHS of an assignment Store, + // Can often be treated as a Store, but needs to be separate because + // ASM is allowed to read outputs as well, so a Store-AsmOutput sequence + // cannot be simplified the way a Store-Store can be. + AsmOutput, + // Dest of a call Call, @@ -910,7 +915,7 @@ impl<'tcx> PlaceContext<'tcx> { /// Returns true if this place context represents a use that potentially changes the value. pub fn is_mutating_use(&self) -> bool { match *self { - PlaceContext::Store | PlaceContext::Call | + PlaceContext::Store | PlaceContext::AsmOutput | PlaceContext::Call | PlaceContext::Borrow { kind: BorrowKind::Mut, .. } | PlaceContext::Projection(Mutability::Mut) | PlaceContext::Drop => true, @@ -932,6 +937,7 @@ impl<'tcx> PlaceContext<'tcx> { PlaceContext::Projection(Mutability::Not) | PlaceContext::Copy | PlaceContext::Move => true, PlaceContext::Borrow { kind: BorrowKind::Mut, .. } | PlaceContext::Store | + PlaceContext::AsmOutput | PlaceContext::Call | PlaceContext::Projection(Mutability::Mut) | PlaceContext::Drop | PlaceContext::StorageLive | PlaceContext::StorageDead | PlaceContext::Validate => false, diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index c61a57cdda0e9..2504aa5ff378a 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -540,6 +540,10 @@ impl<'a, 'b, 'tcx> FindPlaceUses<'a, 'b, 'tcx> { // "deep" does validation go? PlaceContext::Validate => false, + // FIXME: This is here to not change behaviour from before + // AsmOutput existed, but it's not necessarily a pure overwrite. + // so it's possible this should activate the place. + PlaceContext::AsmOutput | // pure overwrites of an place do not activate it. (note // PlaceContext::Call is solely about dest place) PlaceContext::Store | PlaceContext::Call => false, diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 7833f4bbac7aa..d9ef5235d1939 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -173,6 +173,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { ty::TyAdt(adt, _) => { if adt.is_union() { if context == PlaceContext::Store || + context == PlaceContext::AsmOutput || context == PlaceContext::Drop { let elem_ty = match elem { diff --git a/src/librustc_mir/transform/last_use.rs b/src/librustc_mir/transform/last_use.rs new file mode 100644 index 0000000000000..5e0177da71206 --- /dev/null +++ b/src/librustc_mir/transform/last_use.rs @@ -0,0 +1,197 @@ +// 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. + +#![warn(warnings)] + +use rustc::mir::*; +use rustc::mir::visit::{MutVisitor, PlaceContext}; +use rustc::ty::TyCtxt; +use rustc_data_structures::control_flow_graph::iterate::post_order_from; +use rustc_data_structures::fx::{FxHashMap}; +use rustc_data_structures::indexed_set::{IdxSetBuf}; +use rustc_data_structures::indexed_vec::Idx; +use transform::{MirPass, MirSource}; + +pub struct WeakenLastUse; + +impl MirPass for WeakenLastUse { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + _src: MirSource, + mir: &mut Mir<'tcx>) { + // We only run when optimizing MIR (at any level). + if tcx.sess.opts.debugging_opts.mir_opt_level == 0 { + return + } + + let post_order = post_order_from(mir, START_BLOCK); + let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut(); + let locals_len = local_decls.len(); + let mut simplifier = CopySimplifier { + locals_len, + block_needs: FxHashMap::default(), + needs: None, + dups_check: IdxSetBuf::new_empty(locals_len), + bug: None, + }; + for bb in post_order { + simplifier.simplify(bb, &mut basic_blocks[bb]); + if let Some((local, location)) = simplifier.bug.take() { + bug!("Local {:?} copied twice in {:?}; last-use logic is wrong. Blocks: {:#?}", + local, location, basic_blocks); + } + } + } +} + +struct CopySimplifier { + locals_len: usize, + block_needs: FxHashMap>, + needs: Option>, + dups_check: IdxSetBuf, + bug: Option<(Local, Location)>, +} + +impl CopySimplifier { + fn simplify<'tcx>(&mut self, bb: BasicBlock, data: &mut BasicBlockData<'tcx>) { + self.needs = Some(self.succ_needs(data.terminator())); + debug!("Starting {:?} needing {:?}", bb, self.needs); + + let mut location = Location { + block: bb, + statement_index: data.statements.len(), + }; + self.dups_check.reset_to_empty(); + self.visit_terminator(bb, data.terminator.as_mut().unwrap(), location); + for statement in data.statements.iter_mut().rev() { + location.statement_index -= 1; + self.dups_check.reset_to_empty(); + self.visit_statement(bb, statement, location); + } + debug_assert_eq!(location.statement_index, 0); + + self.block_needs.insert(bb, self.needs.take().unwrap()); + } + + fn succ_needs<'tcx>(&mut self, terminator: &Terminator<'tcx>) -> IdxSetBuf { + let mut needs: Option> = None; + for bb in terminator.successors().iter() { + if let Some(succ_needs) = self.block_needs.get(bb) { + if let Some(ref mut needs) = needs { + needs.union(succ_needs); + } else { + needs = Some(succ_needs.clone()); + } + } else { + // Back edge, so assume it needs everything + return IdxSetBuf::new_filled(self.locals_len); + } + } + + needs.unwrap_or_else(|| IdxSetBuf::new_filled(self.locals_len)) + } + + fn needs(&mut self) -> &mut IdxSetBuf { + self.needs.as_mut().unwrap() + } +} + +impl<'tcx> MutVisitor<'tcx> for CopySimplifier { + fn visit_local( + &mut self, + local: &mut Local, + context: PlaceContext<'tcx>, + _location: Location, + ) { + match context { + PlaceContext::Store | + PlaceContext::StorageLive | + PlaceContext::StorageDead => { + self.needs().remove(local); + } + + // A call doesn't need its output populated, but also might not + // store a value if the callee panics, so just do nothing here. + // FIXME: Smarter handling of successors in call terminators + // would let this be more precise, but this is sound. + PlaceContext::Call => {} + + // While an InlineAsm output is expected to write to the output, + // they can be read-write, so assume we need the preceeding value. + PlaceContext::AsmOutput | + PlaceContext::Projection(..) | + PlaceContext::Borrow { .. } | + PlaceContext::Inspect | + PlaceContext::Copy | + PlaceContext::Move | + PlaceContext::Validate | + PlaceContext::Drop => { + self.needs().add(local); + } + } + } + + fn visit_statement( + &mut self, + block: BasicBlock, + statement: &mut Statement<'tcx>, + location: Location, + ) { + if let StatementKind::Assign(Place::Local(local), _) = statement.kind { + if !self.needs().contains(&local) { + // All rvalues are side-effect-free, so if nothing needs this + // local, we can just skip this. The local is being referenced + // directly, so must not be borrowed either. + statement.make_nop(); + return; + } + } + + self.super_statement(block, statement, location); + } + + fn visit_operand( + &mut self, + operand: &mut Operand<'tcx>, + location: Location, + ) { + if let Operand::Copy(Place::Local(local)) = *operand { + if self.dups_check.contains(&local) && self.bug.is_none() { + self.bug = Some((local, location)); + } + if !self.needs().contains(&local) { + *operand = Operand::Move(Place::Local(local)); + } + self.dups_check.add(&local); + } + + self.super_operand(operand, location) + } + + fn visit_terminator_kind( + &mut self, + block: BasicBlock, + kind: &mut TerminatorKind<'tcx>, + location: Location, + ) { + match *kind { + TerminatorKind::Unreachable => { + self.needs().clear(); + } + TerminatorKind::Return => { + self.needs().clear(); + self.needs().add(&Local::new(0)); + } + _ => { + self.super_terminator_kind(block, kind, location) + } + } + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index fb9daf07c71dc..e036f22fa19d5 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -43,6 +43,7 @@ pub mod instcombine; pub mod copy_prop; pub mod generator; pub mod inline; +pub mod last_use; pub mod lower_128bit; pub(crate) fn provide(providers: &mut Providers) { @@ -256,6 +257,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx // Optimizations begin. inline::Inline, instcombine::InstCombine, + last_use::WeakenLastUse, deaggregator::Deaggregator, copy_prop::CopyPropagation, remove_noop_landing_pads::RemoveNoopLandingPads, diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 1e5b0bc1392bc..1545040f2da79 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -103,6 +103,7 @@ impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> { if *temp == TempState::Undefined { match context { PlaceContext::Store | + PlaceContext::AsmOutput | PlaceContext::Call => { *temp = TempState::Defined { location, diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index 45c3fcd8a615d..0af08e1bc8ac9 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -240,6 +240,9 @@ impl<'tcx> Visitor<'tcx> for DefsUsesVisitor { PlaceContext::Store | + // This is potentially both a def and a use... + PlaceContext::AsmOutput | + // We let Call define the result in both the success and // unwind cases. This is not really correct, however it // does not seem to be observable due to the way that we diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index b5e5dd3b9ce16..cfe55aba0d3c5 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -193,6 +193,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { PlaceContext::Inspect | PlaceContext::Store | + PlaceContext::AsmOutput | PlaceContext::Borrow { .. } | PlaceContext::Projection(..) => { self.mark_as_memory(index); diff --git a/src/test/compile-fail/array_const_index-0.rs b/src/test/compile-fail/array_const_index-0.rs index 501c66e75cded..d24658104314c 100644 --- a/src/test/compile-fail/array_const_index-0.rs +++ b/src/test/compile-fail/array_const_index-0.rs @@ -14,5 +14,7 @@ const B: i32 = (&A)[1]; //~| index out of bounds: the len is 0 but the index is 1 fn main() { - let _ = B; + let b = B; + + std::mem::drop(b); // force use } diff --git a/src/test/compile-fail/array_const_index-1.rs b/src/test/compile-fail/array_const_index-1.rs index d3b43e83bfe52..105fe05cd12a6 100644 --- a/src/test/compile-fail/array_const_index-1.rs +++ b/src/test/compile-fail/array_const_index-1.rs @@ -14,5 +14,7 @@ const B: i32 = A[1]; //~| index out of bounds: the len is 0 but the index is 1 fn main() { - let _ = B; + let b = B; + + std::mem::drop(b); // force use } diff --git a/src/test/compile-fail/const-slice-oob.rs b/src/test/compile-fail/const-slice-oob.rs index b1b4bfe2d1c39..39a69b4f1d61f 100644 --- a/src/test/compile-fail/const-slice-oob.rs +++ b/src/test/compile-fail/const-slice-oob.rs @@ -14,5 +14,7 @@ const BAR: u32 = FOO[5]; //~| index out of bounds: the len is 3 but the index is 5 fn main() { - let _ = BAR; + let b = BAR; + + std::mem::drop(b); // force use } diff --git a/src/test/compile-fail/huge-array.rs b/src/test/compile-fail/huge-array.rs index 029e9651cb3cd..1da7880b22837 100644 --- a/src/test/compile-fail/huge-array.rs +++ b/src/test/compile-fail/huge-array.rs @@ -12,6 +12,7 @@ fn generic(t: T) { let s: [T; 1518600000] = [t; 1518600000]; + std::mem::drop(s[0]); // force use } fn main() { diff --git a/src/test/compile-fail/huge-enum.rs b/src/test/compile-fail/huge-enum.rs index 6e7c05370b99d..28d5277cc2e0e 100644 --- a/src/test/compile-fail/huge-enum.rs +++ b/src/test/compile-fail/huge-enum.rs @@ -15,9 +15,11 @@ #[cfg(target_pointer_width = "32")] fn main() { let big: Option<[u32; (1<<29)-1]> = None; + std::mem::drop(big); // force use } #[cfg(target_pointer_width = "64")] fn main() { let big: Option<[u32; (1<<45)-1]> = None; + std::mem::drop(big); // force use } diff --git a/src/test/compile-fail/huge-struct.rs b/src/test/compile-fail/huge-struct.rs index a10c61d6606d0..94a732476079d 100644 --- a/src/test/compile-fail/huge-struct.rs +++ b/src/test/compile-fail/huge-struct.rs @@ -51,4 +51,5 @@ struct S1M { val: S1k> } fn main() { let fat: Option>>> = None; + std::mem::drop(fat); // force use } diff --git a/src/test/compile-fail/issue-15919.rs b/src/test/compile-fail/issue-15919.rs index df7e7c102b213..3c0e611618ef0 100644 --- a/src/test/compile-fail/issue-15919.rs +++ b/src/test/compile-fail/issue-15919.rs @@ -13,9 +13,11 @@ #[cfg(target_pointer_width = "32")] fn main() { let x = [0usize; 0xffff_ffff]; + std::mem::drop(x[0]); // force use } #[cfg(target_pointer_width = "64")] fn main() { let x = [0usize; 0xffff_ffff_ffff_ffff]; + std::mem::drop(x[0]); // force use } diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs index 50d8a5154c449..bd8b67216cf3b 100644 --- a/src/test/mir-opt/copy_propagation.rs +++ b/src/test/mir-opt/copy_propagation.rs @@ -22,11 +22,11 @@ fn main() { // START rustc.test.CopyPropagation.before.mir // bb0: { // ... -// _3 = _1; +// _3 = move _1; // ... // _2 = move _3; // ... -// _4 = _2; +// _4 = move _2; // _0 = move _4; // ... // return; diff --git a/src/test/mir-opt/copy_propagation_arg.rs b/src/test/mir-opt/copy_propagation_arg.rs index 35bb231df5adf..3ba4df10931fd 100644 --- a/src/test/mir-opt/copy_propagation_arg.rs +++ b/src/test/mir-opt/copy_propagation_arg.rs @@ -15,25 +15,28 @@ fn dummy(x: u8) -> u8 { x } -fn foo(mut x: u8) { +fn foo(mut x: u8) -> u8 { // calling `dummy` to make an use of `x` that copyprop cannot eliminate x = dummy(x); // this will assign a local to `x` + x } -fn bar(mut x: u8) { +fn bar(mut x: u8) -> u8 { dummy(x); x = 5; + x } -fn baz(mut x: i32) { +fn baz(mut x: i32) -> i32 { // self-assignment to a function argument should be eliminated x = x; + x } -fn arg_src(mut x: i32) -> i32 { +fn arg_src(mut x: i32) -> (i32, i32) { let y = x; x = 123; // Don't propagate this assignment to `y` - y + (y, x) } fn main() { @@ -48,44 +51,53 @@ fn main() { // START rustc.foo.CopyPropagation.before.mir // bb0: { // ... -// _3 = _1; +// _3 = move _1; // _2 = const dummy(move _3) -> bb1; // } // bb1: { // ... // _1 = move _2; // ... +// _4 = move _1; +// _0 = move _4; +// ... // } // END rustc.foo.CopyPropagation.before.mir // START rustc.foo.CopyPropagation.after.mir // bb0: { // ... -// _3 = _1; +// _3 = move _1; // _2 = const dummy(move _3) -> bb1; // } // bb1: { // ... // _1 = move _2; // ... +// _4 = move _1; +// _0 = move _4; +// ... // } // END rustc.foo.CopyPropagation.after.mir // START rustc.bar.CopyPropagation.before.mir // bb0: { // StorageLive(_3); -// _3 = _1; +// _3 = move _1; // _2 = const dummy(move _3) -> bb1; // } // bb1: { // StorageDead(_3); // _1 = const 5u8; -// _0 = (); +// ... +// _4 = move _1; +// _0 = move _4; +// ... // return; // } // END rustc.bar.CopyPropagation.before.mir // START rustc.bar.CopyPropagation.after.mir // bb0: { // ... -// _3 = _1; +// _3 = move _1; // _2 = const dummy(move _3) -> bb1; // } // bb1: { @@ -97,17 +109,20 @@ fn main() { // START rustc.baz.CopyPropagation.before.mir // bb0: { // StorageLive(_2); -// _2 = _1; +// _2 = move _1; // _1 = move _2; // StorageDead(_2); -// _0 = (); +// StorageLive(_3); +// _3 = move _1; +// _0 = move _3; +// StorageDead(_3); // return; // } // END rustc.baz.CopyPropagation.before.mir // START rustc.baz.CopyPropagation.after.mir // bb0: { // ... -// _2 = _1; +// _2 = move _1; // _1 = move _2; // ... // } @@ -115,13 +130,14 @@ fn main() { // START rustc.arg_src.CopyPropagation.before.mir // bb0: { // ... -// _3 = _1; +// _3 = move _1; // _2 = move _3; // ... // _1 = const 123i32; // ... -// _4 = _2; -// _0 = move _4; +// _4 = move _2; +// ... +// _0 = (move _4, move _5); // ... // return; // } @@ -129,11 +145,12 @@ fn main() { // START rustc.arg_src.CopyPropagation.after.mir // bb0: { // ... -// _3 = _1; +// _3 = move _1; // ... // _1 = const 123i32; // ... -// _0 = move _3; +// _5 = move _1; +// _0 = (move _3, move _5); // ... // return; // } diff --git a/src/test/mir-opt/deaggregator_test.rs b/src/test/mir-opt/deaggregator_test.rs index c918bef129ad1..b192083f3ae13 100644 --- a/src/test/mir-opt/deaggregator_test.rs +++ b/src/test/mir-opt/deaggregator_test.rs @@ -27,7 +27,7 @@ fn main() { // START rustc.bar.Deaggregator.before.mir // bb0: { // ... -// _2 = _1; +// _2 = move _1; // ... // _0 = Baz { x: move _2, y: const 0f32, z: const false }; // ... @@ -37,7 +37,7 @@ fn main() { // START rustc.bar.Deaggregator.after.mir // bb0: { // ... -// _2 = _1; +// _2 = move _1; // ... // (_0.0: usize) = move _2; // (_0.1: f32) = const 0f32; diff --git a/src/test/mir-opt/deaggregator_test_enum.rs b/src/test/mir-opt/deaggregator_test_enum.rs index 8af56b7c01173..4b4d8f4dcbe5e 100644 --- a/src/test/mir-opt/deaggregator_test_enum.rs +++ b/src/test/mir-opt/deaggregator_test_enum.rs @@ -29,7 +29,7 @@ fn main() { // START rustc.bar.Deaggregator.before.mir // bb0: { // StorageLive(_2); -// _2 = _1; +// _2 = move _1; // _0 = Baz::Foo { x: move _2 }; // StorageDead(_2); // return; @@ -38,7 +38,7 @@ fn main() { // START rustc.bar.Deaggregator.after.mir // bb0: { // StorageLive(_2); -// _2 = _1; +// _2 = move _1; // ((_0 as Foo).0: usize) = move _2; // discriminant(_0) = 1; // StorageDead(_2); diff --git a/src/test/mir-opt/deaggregator_test_enum_2.rs b/src/test/mir-opt/deaggregator_test_enum_2.rs index b6505de22f3b6..501eeaecfa1e4 100644 --- a/src/test/mir-opt/deaggregator_test_enum_2.rs +++ b/src/test/mir-opt/deaggregator_test_enum_2.rs @@ -32,14 +32,14 @@ fn main() { // START rustc.test1.Deaggregator.before.mir // bb1: { // StorageLive(_4); -// _4 = _2; +// _4 = move _2; // _0 = Foo::A(move _4,); // StorageDead(_4); // goto -> bb3; // } // bb2: { // StorageLive(_5); -// _5 = _2; +// _5 = move _2; // _0 = Foo::B(move _5,); // StorageDead(_5); // goto -> bb3; @@ -48,7 +48,7 @@ fn main() { // START rustc.test1.Deaggregator.after.mir // bb1: { // StorageLive(_4); -// _4 = _2; +// _4 = move _2; // ((_0 as A).0: i32) = move _4; // discriminant(_0) = 0; // StorageDead(_4); @@ -56,7 +56,7 @@ fn main() { // } // bb2: { // StorageLive(_5); -// _5 = _2; +// _5 = move _2; // ((_0 as B).0: i32) = move _5; // discriminant(_0) = 1; // StorageDead(_5); diff --git a/src/test/mir-opt/deaggregator_test_multiple.rs b/src/test/mir-opt/deaggregator_test_multiple.rs index 3a9a458fd464d..752cadb0ddde6 100644 --- a/src/test/mir-opt/deaggregator_test_multiple.rs +++ b/src/test/mir-opt/deaggregator_test_multiple.rs @@ -32,7 +32,7 @@ fn main() { // ... // _2 = Foo::A(move _3,); // ... -// _5 = _1; +// _5 = move _1; // _4 = Foo::A(move _5,); // ... // _0 = [move _2, move _4]; @@ -48,7 +48,7 @@ fn main() { // ((_2 as A).0: i32) = move _3; // discriminant(_2) = 0; // ... -// _5 = _1; +// _5 = move _1; // ((_4 as A).0: i32) = move _5; // discriminant(_4) = 0; // ... diff --git a/src/test/mir-opt/weaken_last_use.rs b/src/test/mir-opt/weaken_last_use.rs new file mode 100644 index 0000000000000..f72d64a16540d --- /dev/null +++ b/src/test/mir-opt/weaken_last_use.rs @@ -0,0 +1,278 @@ +// 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 mystery(_: T) {} + +fn return_arg(x: u8) -> u8 { + x +} + +fn assign_op(mut x: u8) -> u8 { + x += 1; + x +} + +fn array_indexing(i: usize) { + let a = [1, 2, 3]; + let _x = a[i]; // can remove the read, but need to keep the length assert +} + +fn copy_to_locals(x: u8) -> u8 { + let y = x; // cannot move + let z = x; // last use; can move + y + z +} + +fn use_on_both_paths(b: bool, x: u8) -> u8 { + // both uses are the last + if b { x+1 } else { x-1 } +} + +fn unneeded_rvalue(x: u8) -> u8 { + let mut y = x; + let z = y; + y = 4; // no uses, so removable + z +} + +fn unneeded_complex_rvalue(x: u8) -> u8 { + let mut y = x; + let z = y; + y = z+4+z; // no uses, so removable + z +} + +fn call_with_unneeded_result(x: u8) -> u8 { + let y = mystery(4); // call might have side effects + x +} + +fn unneeded_drop_flag_update(b: bool) { + let s = String::new(); + if b { + mystery(s); + } +} + +fn loop_body(mut x: u8) { + let mut y = 0; + while y != 10 { + x += 1; + y = x; // not the last use because loop + } +} + +fn main() { + // Make sure the functions actually get instantiated. + return_arg(0); + assign_op(0); + array_indexing(0); + copy_to_locals(0); + use_on_both_paths(true, 1); + unneeded_rvalue(0); + unneeded_complex_rvalue(0); + call_with_unneeded_result(0); + unneeded_drop_flag_update(true); + loop_body(0); +} + +// ignore-tidy-linelength The assertions below are too long to fit, and can't wrap + +// END RUST SOURCE + +// START rustc.return_arg.WeakenLastUse.before.mir +// _2 = _1; +// _0 = move _2; +// END rustc.return_arg.WeakenLastUse.before.mir +// START rustc.return_arg.WeakenLastUse.after.mir +// _2 = move _1; +// _0 = move _2; +// END rustc.return_arg.WeakenLastUse.after.mir + +// START rustc.assign_op.WeakenLastUse.before.mir +// _1 = Add(_1, const 1u8); +// END rustc.assign_op.WeakenLastUse.before.mir +// START rustc.assign_op.WeakenLastUse.after.mir +// _1 = Add(move _1, const 1u8); +// END rustc.assign_op.WeakenLastUse.after.mir + +// START rustc.array_indexing.WeakenLastUse.before.mir +// _5 = _1; +// _6 = const 3usize; +// _7 = Lt(_5, _6); +// assert(move _7, "index out of bounds: the len is {} but the index is {}", move _6, _5) -> bb1; +// } +// bb1: { +// _4 = _2[_5]; +// _3 = move _4; +// END rustc.array_indexing.WeakenLastUse.before.mir +// START rustc.array_indexing.WeakenLastUse.after.mir +// _5 = move _1; +// _6 = const 3usize; +// _7 = Lt(_5, _6); +// assert(move _7, "index out of bounds: the len is {} but the index is {}", move _6, move _5) -> bb1; +// } +// bb1: { +// nop; +// nop; +// END rustc.array_indexing.WeakenLastUse.after.mir + +// START rustc.copy_to_locals.WeakenLastUse.before.mir +// _3 = _1; +// _2 = move _3; +// ... +// _5 = _1; +// _4 = move _5; +// ... +// _6 = _2; +// ... +// _7 = _4; +// ... +// _0 = Add(move _6, move _7); +// END rustc.copy_to_locals.WeakenLastUse.before.mir +// START rustc.copy_to_locals.WeakenLastUse.after.mir +// _3 = _1; +// _2 = move _3; +// ... +// _5 = move _1; +// _4 = move _5; +// ... +// _6 = move _2; +// ... +// _7 = move _4; +// ... +// _0 = Add(move _6, move _7); +// END rustc.copy_to_locals.WeakenLastUse.after.mir + +// START rustc.use_on_both_paths.WeakenLastUse.before.mir +// bb0: { +// StorageLive(_3); +// _3 = _1; +// switchInt(move _3) -> [0u8: bb2, otherwise: bb1]; +// } +// bb1: { +// StorageLive(_4); +// _4 = _2; +// _0 = Add(move _4, const 1u8); +// StorageDead(_4); +// goto -> bb3; +// } +// bb2: { +// StorageLive(_5); +// _5 = _2; +// _0 = Sub(move _5, const 1u8); +// StorageDead(_5); +// goto -> bb3; +// } +// END rustc.use_on_both_paths.WeakenLastUse.before.mir +// START rustc.use_on_both_paths.WeakenLastUse.after.mir +// _4 = move _2; +// ... +// _5 = move _2; +// END rustc.use_on_both_paths.WeakenLastUse.after.mir + +// START rustc.unneeded_rvalue.WeakenLastUse.before.mir +// _3 = _1; +// _2 = move _3; +// ... +// _5 = _2; +// _4 = move _5; +// ... +// _2 = const 4u8; +// ... +// _6 = _4; +// _0 = move _6; +// END rustc.unneeded_rvalue.WeakenLastUse.before.mir +// START rustc.unneeded_rvalue.WeakenLastUse.after.mir +// _3 = move _1; +// _2 = move _3; +// ... +// _5 = move _2; +// _4 = move _5; +// ... +// nop; +// ... +// _6 = move _4; +// _0 = move _6; +// END rustc.unneeded_rvalue.WeakenLastUse.after.mir + +// START rustc.unneeded_complex_rvalue.WeakenLastUse.before.mir +// _3 = _1; +// _2 = move _3; +// ... +// _5 = _2; +// _4 = move _5; +// ... +// _7 = _4; +// _6 = Add(move _7, const 4u8); +// ... +// _8 = _4; +// _2 = Add(move _6, move _8); +// ... +// _9 = _4; +// _0 = move _9; +// END rustc.unneeded_complex_rvalue.WeakenLastUse.before.mir +// START rustc.unneeded_complex_rvalue.WeakenLastUse.after.mir +// _3 = move _1; +// _2 = move _3; +// ... +// _5 = move _2; +// _4 = move _5; +// ... +// nop; +// nop; +// ... +// nop; +// nop; +// ... +// _9 = move _4; +// _0 = move _9; +// END rustc.unneeded_complex_rvalue.WeakenLastUse.after.mir + +// START rustc.call_with_unneeded_result.WeakenLastUse.before.mir +// bb0: { +// StorageLive(_2); +// _2 = const mystery(const 4i32) -> bb1; +// } +// bb1: { +// StorageLive(_3); +// _3 = _1; +// _0 = move _3; +// StorageDead(_3); +// StorageDead(_2); +// return; +// } +// END rustc.call_with_unneeded_result.WeakenLastUse.before.mir +// START rustc.call_with_unneeded_result.WeakenLastUse.after.mir +// _2 = const mystery(const 4i32) -> bb1; +// END rustc.call_with_unneeded_result.WeakenLastUse.after.mir + +// START rustc.unneeded_drop_flag_update.WeakenLastUse.before.mir +// bb6: { +// _6 = const false; +// StorageDead(_2); +// return; +// END rustc.unneeded_drop_flag_update.WeakenLastUse.before.mir +// START rustc.unneeded_drop_flag_update.WeakenLastUse.after.mir +// bb6: { +// nop; +// StorageDead(_2); +// return; +// END rustc.unneeded_drop_flag_update.WeakenLastUse.after.mir + +// START rustc.loop_body.WeakenLastUse.before.mir +// _6 = _1; +// _2 = move _6; +// END rustc.loop_body.WeakenLastUse.before.mir +// START rustc.loop_body.WeakenLastUse.after.mir +// _6 = _1; +// _2 = move _6; +// END rustc.loop_body.WeakenLastUse.after.mir diff --git a/src/test/ui/print_type_sizes/generics.rs b/src/test/ui/print_type_sizes/generics.rs index 7bc4822359e2a..d34f2f67a832d 100644 --- a/src/test/ui/print_type_sizes/generics.rs +++ b/src/test/ui/print_type_sizes/generics.rs @@ -67,8 +67,10 @@ pub fn f1(x: T) { } pub fn main() { - let _b: Pair = Pair::new(0, 0); - let _s: Pair = Pair::new(SevenBytes::new(), SevenBytes::new()); - let _z: ZeroSized = ZeroSized; + let b: Pair = Pair::new(0, 0); + let s: Pair = Pair::new(SevenBytes::new(), SevenBytes::new()); + let z: ZeroSized = ZeroSized; f1::(SevenBytes::new()); + + std::mem::drop((b, s, z)); } diff --git a/src/test/ui/print_type_sizes/niche-filling.rs b/src/test/ui/print_type_sizes/niche-filling.rs index 08b58704022ec..803cc7b67e5d6 100644 --- a/src/test/ui/print_type_sizes/niche-filling.rs +++ b/src/test/ui/print_type_sizes/niche-filling.rs @@ -77,14 +77,16 @@ pub enum Enum4 { } pub fn main() { - let _x: MyOption> = Default::default(); - let _y: EmbeddedDiscr = Default::default(); - let _z: MyOption> = Default::default(); - let _a: MyOption = Default::default(); - let _b: MyOption = Default::default(); - let _c: MyOption = Default::default(); - let _b: MyOption> = Default::default(); - let _e: Enum4<(), char, (), ()> = Enum4::One(()); - let _f: Enum4<(), (), bool, ()> = Enum4::One(()); - let _g: Enum4<(), (), (), MyOption> = Enum4::One(()); + let x: MyOption> = Default::default(); + let y: EmbeddedDiscr = Default::default(); + let z: MyOption> = Default::default(); + let a: MyOption = Default::default(); + let b: MyOption = Default::default(); + let c: MyOption = Default::default(); + let b: MyOption> = Default::default(); + let e: Enum4<(), char, (), ()> = Enum4::One(()); + let f: Enum4<(), (), bool, ()> = Enum4::One(()); + let g: Enum4<(), (), (), MyOption> = Enum4::One(()); + + std::mem::drop((x, y, z, a, b, c, e, f, g)); } diff --git a/src/test/ui/print_type_sizes/no_duplicates.rs b/src/test/ui/print_type_sizes/no_duplicates.rs index 40c41aae9109d..514e1f4d031c2 100644 --- a/src/test/ui/print_type_sizes/no_duplicates.rs +++ b/src/test/ui/print_type_sizes/no_duplicates.rs @@ -18,9 +18,11 @@ pub struct SevenBytes([u8; 7]); pub fn f1() { - let _s: SevenBytes = SevenBytes([0; 7]); + let s: SevenBytes = SevenBytes([0; 7]); + std::mem::drop(s); } pub fn main() { - let _s: SevenBytes = SevenBytes([0; 7]); + let s: SevenBytes = SevenBytes([0; 7]); + std::mem::drop(s); } diff --git a/src/test/ui/print_type_sizes/uninhabited.rs b/src/test/ui/print_type_sizes/uninhabited.rs index fae6cd4009c30..7982d3a020133 100644 --- a/src/test/ui/print_type_sizes/uninhabited.rs +++ b/src/test/ui/print_type_sizes/uninhabited.rs @@ -14,6 +14,7 @@ #![feature(never_type)] pub fn main() { - let _x: Option = None; - let _y: Result = Ok(42); + let x: Option = None; + let y: Result = Ok(42); + std::mem::drop((x, y)); }