From f1e1d90fecac1872def327eb01395c8f6ed131c6 Mon Sep 17 00:00:00 2001 From: DianQK Date: Sat, 27 Jul 2024 07:30:23 +0800 Subject: [PATCH 1/2] Simplify the canonical clone method to copy The optimized clone method ends up as the following MIR: ``` _2 = ((*_1).0: i32); _3 = ((*_1).1: u64); _4 = ((*_1).2: [i8; 3]); _0 = Foo { a: move _2, b: move _3, c: move _4 }; ``` We can transform this to: ``` _0 = (*_1); ``` --- compiler/rustc_index/src/vec.rs | 10 +++ .../rustc_mir_transform/src/instsimplify.rs | 80 ++++++++++++++++++- tests/codegen/clone_as_copy.rs | 40 ++++++++++ tests/mir-opt/instsimplify/clone.rs | 17 ++++ ...-clone.InstSimplify-after-simplifycfg.diff | 32 ++++++++ ...as_copy.clone_as_copy.PreCodegen.after.mir | 21 +++++ tests/mir-opt/pre-codegen/clone_as_copy.rs | 28 +++++++ ..._clone.{impl#0}-clone.PreCodegen.after.mir | 6 +- 8 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 tests/codegen/clone_as_copy.rs create mode 100644 tests/mir-opt/instsimplify/clone.rs create mode 100644 tests/mir-opt/instsimplify/clone.{impl#0}-clone.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir create mode 100644 tests/mir-opt/pre-codegen/clone_as_copy.rs diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs index 7438c97eb581c..37cbabdacb67f 100644 --- a/compiler/rustc_index/src/vec.rs +++ b/compiler/rustc_index/src/vec.rs @@ -188,6 +188,16 @@ impl IndexVec { let min_new_len = elem.index() + 1; self.raw.resize_with(min_new_len, fill_value); } + + #[inline] + pub fn reset_all(&mut self, elem: T) + where + T: Copy, + { + for e in self.raw.iter_mut() { + *e = elem; + } + } } /// `IndexVec` is often used as a map, so it provides some map-like APIs. diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 2fc5f7e536ba8..51bc648bf95a9 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -2,6 +2,7 @@ use rustc_ast::attr; use rustc_hir::LangItem; +use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::layout::ValidityRequirement; @@ -60,8 +61,8 @@ impl<'tcx> MirPass<'tcx> for InstSimplify { _ => {} } } - ctx.simplify_primitive_clone(block.terminator.as_mut().unwrap(), &mut block.statements); + ctx.simplify_copy_like(&mut block.statements); ctx.simplify_intrinsic_assert(block.terminator.as_mut().unwrap()); ctx.simplify_nounwind_call(block.terminator.as_mut().unwrap()); simplify_duplicate_switch_targets(block.terminator.as_mut().unwrap()); @@ -207,6 +208,83 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } } + /// Transform `Aggregate(Adt, [(*_1).0, (*_1).1])` ==> `Copy(*_1)`. + /// This comes from the simplification of the clone method by `simplify_primitive_clone`. + fn simplify_copy_like(&self, statements: &mut Vec>) { + let mut assignments = IndexVec::from_elem(None::>, self.local_decls); + for statement in statements { + match statement.kind { + StatementKind::Assign(box (dest, ref mut rvalue)) => { + if let Rvalue::Aggregate(_, fields) = rvalue { + let mut from_local = None; + if fields.iter_enumerated().all(|(index, field)| { + let Some(from_place) = field + .place() + .and_then(|p| p.as_local()) + .and_then(|l| assignments[l]) + else { + return false; + }; + // All fields must come from the same local. + if let Some(from_local) = from_local { + if from_place.local != from_local { + return false; + } + } else { + // We can only copy the same type. + let Some(from_ty) = + self.local_decls[from_place.local].ty.builtin_deref(false) + else { + return false; + }; + let dest_ty = dest.ty(self.local_decls, self.tcx).ty; + if dest_ty != from_ty { + return false; + }; + from_local = Some(from_place.local); + } + // For more complex scenarios, we expect to get this simplified projection within a complete pipeline. + let [ProjectionElem::Deref, ProjectionElem::Field(from_index, _)] = + *from_place.projection.as_slice() + else { + return false; + }; + from_index == index + }) { + if let Some(local) = from_local { + if self.should_simplify(&statement.source_info, rvalue) { + *rvalue = Rvalue::Use(Operand::Copy(Place { + local, + projection: self + .tcx + .mk_place_elems(&[ProjectionElem::Deref]), + })); + } + } + } + } + // Collect available assignments, including those transformed from `Aggregate`. + if let Some(local) = dest.as_local() { + assignments[local] = if let Rvalue::Use(Operand::Copy(place)) = rvalue { + Some(*place) + } else { + // This assignment generally comes from debuginfo (e.g., Ref), + // but we still need to check if a local is being overridden. + None + }; + } else { + // We don't handle projection, so we drop all previous assignments. + assignments.reset_all(None); + } + } + StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => {} + _ => { + assignments.reset_all(None); + } + } + } + } + fn simplify_cast(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Cast(kind, operand, cast_ty) = rvalue { let operand_ty = operand.ty(self.local_decls, self.tcx); diff --git a/tests/codegen/clone_as_copy.rs b/tests/codegen/clone_as_copy.rs new file mode 100644 index 0000000000000..36a59ae56b72b --- /dev/null +++ b/tests/codegen/clone_as_copy.rs @@ -0,0 +1,40 @@ +//@ revisions: DEBUGINFO NODEBUGINFO +//@ compile-flags: -O -Cno-prepopulate-passes +//@ [DEBUGINFO] compile-flags: -Cdebuginfo=full + +// From https://github.com/rust-lang/rust/issues/128081. +// Ensure that we only generate a memcpy instruction. + +#![crate_type = "lib"] + +#[derive(Clone)] +struct SubCloneAndCopy { + v1: u32, + v2: u32, +} + +#[derive(Clone)] +struct CloneOnly { + v1: u8, + v2: u8, + v3: u8, + v4: u8, + v5: u8, + v6: u8, + v7: u8, + v8: u8, + v9: u8, + v_sub: SubCloneAndCopy, + v_large: [u8; 256], +} + +// CHECK-LABEL: define {{.*}}@clone_only( +#[no_mangle] +pub fn clone_only(v: &CloneOnly) -> CloneOnly { + // CHECK-NOT: call {{.*}}clone + // CHECK-NOT: store i8 + // CHECK-NOT: store i32 + // CHECK: call void @llvm.memcpy + // CHECK-NEXT: ret void + v.clone() +} diff --git a/tests/mir-opt/instsimplify/clone.rs b/tests/mir-opt/instsimplify/clone.rs new file mode 100644 index 0000000000000..a5ea6edfc6275 --- /dev/null +++ b/tests/mir-opt/instsimplify/clone.rs @@ -0,0 +1,17 @@ +//@ test-mir-pass: InstSimplify-after-simplifycfg +//@ compile-flags: -Zmir-enable-passes=+InstSimplify-before-inline,+ReferencePropagation,+SimplifyCfg-after-unreachable-enum-branching + +// Check if we have transformed the default clone to copy in the specific pipeline. + +// EMIT_MIR clone.{impl#0}-clone.InstSimplify-after-simplifycfg.diff + +// CHECK-LABEL: ::clone( +// CHECK-NOT: = AllCopy { {{.*}} }; +// CHECK: _0 = (*_1); +// CHECK: return; +#[derive(Clone)] +struct AllCopy { + a: i32, + b: u64, + c: [i8; 3], +} diff --git a/tests/mir-opt/instsimplify/clone.{impl#0}-clone.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/clone.{impl#0}-clone.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..72bb78dbd3c6d --- /dev/null +++ b/tests/mir-opt/instsimplify/clone.{impl#0}-clone.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,32 @@ +- // MIR for `::clone` before InstSimplify-after-simplifycfg ++ // MIR for `::clone` after InstSimplify-after-simplifycfg + + fn ::clone(_1: &AllCopy) -> AllCopy { + debug self => _1; + let mut _0: AllCopy; + let mut _2: i32; + let mut _3: &i32; + let _4: &i32; + let mut _5: u64; + let mut _6: &u64; + let _7: &u64; + let mut _8: [i8; 3]; + let mut _9: &[i8; 3]; + let _10: &[i8; 3]; + + bb0: { + StorageLive(_2); + _2 = ((*_1).0: i32); + StorageLive(_5); + _5 = ((*_1).1: u64); + StorageLive(_8); + _8 = ((*_1).2: [i8; 3]); +- _0 = AllCopy { a: move _2, b: move _5, c: move _8 }; ++ _0 = (*_1); + StorageDead(_8); + StorageDead(_5); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir new file mode 100644 index 0000000000000..1eae614e2dc7b --- /dev/null +++ b/tests/mir-opt/pre-codegen/clone_as_copy.clone_as_copy.PreCodegen.after.mir @@ -0,0 +1,21 @@ +// MIR for `clone_as_copy` after PreCodegen + +fn clone_as_copy(_1: &CopyLikeStruct) -> CopyLikeStruct { + debug v => _1; + let mut _0: CopyLikeStruct; + scope 1 (inlined ::clone) { + debug self => _1; + let _2: &AllCopy; + scope 2 (inlined ::clone) { + debug self => _2; + } + } + + bb0: { + StorageLive(_2); + _2 = &((*_1).1: AllCopy); + _0 = (*_1); + StorageDead(_2); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.rs b/tests/mir-opt/pre-codegen/clone_as_copy.rs new file mode 100644 index 0000000000000..2ef1187484872 --- /dev/null +++ b/tests/mir-opt/pre-codegen/clone_as_copy.rs @@ -0,0 +1,28 @@ +//@ compile-flags: -Cdebuginfo=full + +// Check if we have transformed the nested clone to the copy in the complete pipeline. + +#[derive(Clone)] +struct AllCopy { + a: i32, + b: u64, + c: [i8; 3], +} + +#[derive(Clone)] +struct CopyLikeStruct { + a: i32, + b: AllCopy, + c: [i8; 3], +} + +// EMIT_MIR clone_as_copy.clone_as_copy.PreCodegen.after.mir +#[inline(never)] +fn clone_as_copy(v: &CopyLikeStruct) -> CopyLikeStruct { + // CHECK-LABEL: fn clone_as_copy( + // CHECK-NOT: = AllCopy { {{.*}} }; + // CHECK-NOT: = CopyLikeStruct { {{.*}} }; + // CHECK: _0 = (*_1); + // CHECK: return; + v.clone() +} diff --git a/tests/mir-opt/pre-codegen/no_inlined_clone.{impl#0}-clone.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/no_inlined_clone.{impl#0}-clone.PreCodegen.after.mir index 71898daa1bfa2..f5a9eb557c494 100644 --- a/tests/mir-opt/pre-codegen/no_inlined_clone.{impl#0}-clone.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/no_inlined_clone.{impl#0}-clone.PreCodegen.after.mir @@ -3,13 +3,9 @@ fn ::clone(_1: &Foo) -> Foo { debug self => _1; let mut _0: Foo; - let mut _2: i32; bb0: { - StorageLive(_2); - _2 = ((*_1).0: i32); - _0 = Foo { a: move _2 }; - StorageDead(_2); + _0 = (*_1); return; } } From a7d48fcc15f9f71917927144e1e1ece4f6f5e5a4 Mon Sep 17 00:00:00 2001 From: DianQK Date: Sun, 28 Jul 2024 13:28:44 +0800 Subject: [PATCH 2/2] Simplify some of the copy-like forms --- .../rustc_mir_transform/src/instsimplify.rs | 18 +- ...l_copy.InstSimplify-after-simplifycfg.diff | 47 +++++ ...t_type.InstSimplify-after-simplifycfg.diff | 46 +++++ ...hanged.InstSimplify-after-simplifycfg.diff | 47 +++++ ...y_move.InstSimplify-after-simplifycfg.diff | 46 +++++ ..._ret_2.InstSimplify-after-simplifycfg.diff | 68 ++++++++ ...hanged.InstSimplify-after-simplifycfg.diff | 53 ++++++ ...nged_2.InstSimplify-after-simplifycfg.diff | 52 ++++++ ...ruct_1.InstSimplify-after-simplifycfg.diff | 13 ++ ...ruct_2.InstSimplify-after-simplifycfg.diff | 13 ++ ...t_copy.InstSimplify-after-simplifycfg.diff | 68 ++++++++ tests/mir-opt/instsimplify/copy_like.rs | 160 ++++++++++++++++++ ..._index.InstSimplify-after-simplifycfg.diff | 35 ++++ 13 files changed, 663 insertions(+), 3 deletions(-) create mode 100644 tests/mir-opt/instsimplify/copy_like.all_copy.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.all_copy_different_type.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.all_copy_has_changed.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.all_copy_move.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.all_copy_ret_2.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.all_copy_use_changed.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.all_copy_use_changed_2.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.empty_struct_1.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.empty_struct_2.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.nest_copy.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/copy_like.rs create mode 100644 tests/mir-opt/instsimplify/copy_like.same_type_different_index.InstSimplify-after-simplifycfg.diff diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 51bc648bf95a9..c84fb32992bbe 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -265,8 +265,18 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } // Collect available assignments, including those transformed from `Aggregate`. if let Some(local) = dest.as_local() { - assignments[local] = if let Rvalue::Use(Operand::Copy(place)) = rvalue { - Some(*place) + assignments[local] = if let Rvalue::Use(operand) = rvalue + && let Some(place) = operand.place() + { + if let Some(rvalue_local) = place.as_local() { + let place = assignments[rvalue_local]; + if operand.is_move() { + assignments[rvalue_local] = None; + } + place + } else { + Some(place) + } } else { // This assignment generally comes from debuginfo (e.g., Ref), // but we still need to check if a local is being overridden. @@ -277,7 +287,9 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { assignments.reset_all(None); } } - StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => {} + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop => {} _ => { assignments.reset_all(None); } diff --git a/tests/mir-opt/instsimplify/copy_like.all_copy.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.all_copy.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..0f0408cc4c1e9 --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.all_copy.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,47 @@ +- // MIR for `all_copy` before InstSimplify-after-simplifycfg ++ // MIR for `all_copy` after InstSimplify-after-simplifycfg + + fn all_copy(_1: &AllCopy) -> AllCopy { + debug v => _1; + let mut _0: AllCopy; + let _2: i32; + let mut _5: i32; + let mut _6: u64; + let mut _7: [i8; 3]; + scope 1 { + debug a => _2; + let _3: u64; + scope 2 { + debug b => _3; + let _4: [i8; 3]; + scope 3 { + debug c => _4; + } + } + } + + bb0: { + StorageLive(_2); + _2 = ((*_1).0: i32); + StorageLive(_3); + _3 = ((*_1).1: u64); + StorageLive(_4); + _4 = ((*_1).2: [i8; 3]); + StorageLive(_5); + _5 = _2; + StorageLive(_6); + _6 = _3; + StorageLive(_7); + _7 = _4; +- _0 = AllCopy { a: move _5, b: move _6, c: move _7 }; ++ _0 = (*_1); + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.all_copy_different_type.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.all_copy_different_type.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..5bbe3328fbb53 --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.all_copy_different_type.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,46 @@ +- // MIR for `all_copy_different_type` before InstSimplify-after-simplifycfg ++ // MIR for `all_copy_different_type` after InstSimplify-after-simplifycfg + + fn all_copy_different_type(_1: &AllCopy) -> AllCopy2 { + debug v => _1; + let mut _0: AllCopy2; + let _2: i32; + let mut _5: i32; + let mut _6: u64; + let mut _7: [i8; 3]; + scope 1 { + debug a => _2; + let _3: u64; + scope 2 { + debug b => _3; + let _4: [i8; 3]; + scope 3 { + debug c => _4; + } + } + } + + bb0: { + StorageLive(_2); + _2 = ((*_1).0: i32); + StorageLive(_3); + _3 = ((*_1).1: u64); + StorageLive(_4); + _4 = ((*_1).2: [i8; 3]); + StorageLive(_5); + _5 = _2; + StorageLive(_6); + _6 = _3; + StorageLive(_7); + _7 = _4; + _0 = AllCopy2 { a: move _5, b: move _6, c: move _7 }; + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.all_copy_has_changed.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.all_copy_has_changed.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..037c114da0318 --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.all_copy_has_changed.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,47 @@ +- // MIR for `all_copy_has_changed` before InstSimplify-after-simplifycfg ++ // MIR for `all_copy_has_changed` after InstSimplify-after-simplifycfg + + fn all_copy_has_changed(_1: &mut AllCopy) -> AllCopy { + debug v => _1; + let mut _0: AllCopy; + let _2: i32; + let mut _5: i32; + let mut _6: u64; + let mut _7: [i8; 3]; + scope 1 { + debug a => _2; + let _3: u64; + scope 2 { + debug b => _3; + let _4: [i8; 3]; + scope 3 { + debug c => _4; + } + } + } + + bb0: { + StorageLive(_2); + _2 = ((*_1).0: i32); + StorageLive(_3); + _3 = ((*_1).1: u64); + StorageLive(_4); + _4 = ((*_1).2: [i8; 3]); + ((*_1).0: i32) = const 1_i32; + StorageLive(_5); + _5 = _2; + StorageLive(_6); + _6 = _3; + StorageLive(_7); + _7 = _4; + _0 = AllCopy { a: move _5, b: move _6, c: move _7 }; + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.all_copy_move.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.all_copy_move.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..0fde7c3d4e53b --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.all_copy_move.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,46 @@ +- // MIR for `all_copy_move` before InstSimplify-after-simplifycfg ++ // MIR for `all_copy_move` after InstSimplify-after-simplifycfg + + fn all_copy_move(_1: AllCopy) -> AllCopy { + debug v => _1; + let mut _0: AllCopy; + let _2: i32; + let mut _5: i32; + let mut _6: u64; + let mut _7: [i8; 3]; + scope 1 { + debug a => _2; + let _3: u64; + scope 2 { + debug b => _3; + let _4: [i8; 3]; + scope 3 { + debug c => _4; + } + } + } + + bb0: { + StorageLive(_2); + _2 = (_1.0: i32); + StorageLive(_3); + _3 = (_1.1: u64); + StorageLive(_4); + _4 = (_1.2: [i8; 3]); + StorageLive(_5); + _5 = _2; + StorageLive(_6); + _6 = _3; + StorageLive(_7); + _7 = _4; + _0 = AllCopy { a: move _5, b: move _6, c: move _7 }; + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.all_copy_ret_2.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.all_copy_ret_2.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..f6c91b6535fed --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.all_copy_ret_2.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,68 @@ +- // MIR for `all_copy_ret_2` before InstSimplify-after-simplifycfg ++ // MIR for `all_copy_ret_2` after InstSimplify-after-simplifycfg + + fn all_copy_ret_2(_1: &AllCopy) -> (AllCopy, AllCopy) { + debug v => _1; + let mut _0: (AllCopy, AllCopy); + let _2: i32; + let mut _5: AllCopy; + let mut _6: i32; + let mut _7: u64; + let mut _8: [i8; 3]; + let mut _9: AllCopy; + let mut _10: i32; + let mut _11: u64; + let mut _12: [i8; 3]; + scope 1 { + debug a => _2; + let _3: u64; + scope 2 { + debug b => _3; + let _4: [i8; 3]; + scope 3 { + debug c => _4; + } + } + } + + bb0: { + StorageLive(_2); + _2 = ((*_1).0: i32); + StorageLive(_3); + _3 = ((*_1).1: u64); + StorageLive(_4); + _4 = ((*_1).2: [i8; 3]); + StorageLive(_5); + StorageLive(_6); + _6 = _2; + StorageLive(_7); + _7 = _3; + StorageLive(_8); + _8 = _4; +- _5 = AllCopy { a: move _6, b: move _7, c: move _8 }; ++ _5 = (*_1); + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + StorageLive(_9); + StorageLive(_10); + _10 = _2; + StorageLive(_11); + _11 = _3; + StorageLive(_12); + _12 = _4; +- _9 = AllCopy { a: move _10, b: move _11, c: move _12 }; ++ _9 = (*_1); + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); + _0 = (move _5, move _9); + StorageDead(_9); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.all_copy_use_changed.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.all_copy_use_changed.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..b7ea5b62eb0f0 --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.all_copy_use_changed.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,53 @@ +- // MIR for `all_copy_use_changed` before InstSimplify-after-simplifycfg ++ // MIR for `all_copy_use_changed` after InstSimplify-after-simplifycfg + + fn all_copy_use_changed(_1: &mut AllCopy) -> AllCopy { + debug v => _1; + let mut _0: AllCopy; + let mut _2: i32; + let mut _3: i32; + let mut _6: i32; + let mut _7: u64; + let mut _8: [i8; 3]; + scope 1 { + debug a => _2; + let _4: u64; + scope 2 { + debug b => _4; + let _5: [i8; 3]; + scope 3 { + debug c => _5; + } + } + } + + bb0: { + StorageLive(_2); + _2 = ((*_1).0: i32); + ((*_1).0: i32) = const 1_i32; + StorageLive(_3); + _3 = ((*_1).0: i32); + _2 = move _3; + StorageDead(_3); + StorageLive(_4); + _4 = ((*_1).1: u64); + StorageLive(_5); + _5 = ((*_1).2: [i8; 3]); + StorageLive(_6); + _6 = _2; + StorageLive(_7); + _7 = _4; + StorageLive(_8); + _8 = _5; +- _0 = AllCopy { a: move _6, b: move _7, c: move _8 }; ++ _0 = (*_1); + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.all_copy_use_changed_2.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.all_copy_use_changed_2.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..c1a2e6c977623 --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.all_copy_use_changed_2.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,52 @@ +- // MIR for `all_copy_use_changed_2` before InstSimplify-after-simplifycfg ++ // MIR for `all_copy_use_changed_2` after InstSimplify-after-simplifycfg + + fn all_copy_use_changed_2(_1: &mut AllCopy) -> AllCopy { + debug v => _1; + let mut _0: AllCopy; + let mut _2: i32; + let mut _5: i32; + let mut _6: i32; + let mut _7: u64; + let mut _8: [i8; 3]; + scope 1 { + debug a => _2; + let _3: u64; + scope 2 { + debug b => _3; + let _4: [i8; 3]; + scope 3 { + debug c => _4; + } + } + } + + bb0: { + StorageLive(_2); + _2 = ((*_1).0: i32); + StorageLive(_3); + _3 = ((*_1).1: u64); + StorageLive(_4); + _4 = ((*_1).2: [i8; 3]); + ((*_1).0: i32) = const 1_i32; + StorageLive(_5); + _5 = ((*_1).0: i32); + _2 = move _5; + StorageDead(_5); + StorageLive(_6); + _6 = _2; + StorageLive(_7); + _7 = _3; + StorageLive(_8); + _8 = _4; + _0 = AllCopy { a: move _6, b: move _7, c: move _8 }; + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.empty_struct_1.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.empty_struct_1.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..e39dec643fa2a --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.empty_struct_1.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,13 @@ +- // MIR for `empty_struct_1` before InstSimplify-after-simplifycfg ++ // MIR for `empty_struct_1` after InstSimplify-after-simplifycfg + + fn empty_struct_1(_1: &EmptyStruct1) -> EmptyStruct1 { + debug v => _1; + let mut _0: EmptyStruct1; + + bb0: { + _0 = EmptyStruct1; + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.empty_struct_2.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.empty_struct_2.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..fbf2b0133f5ed --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.empty_struct_2.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,13 @@ +- // MIR for `empty_struct_2` before InstSimplify-after-simplifycfg ++ // MIR for `empty_struct_2` after InstSimplify-after-simplifycfg + + fn empty_struct_2(_1: &EmptyStruct2) -> EmptyStruct2 { + debug v => _1; + let mut _0: EmptyStruct2; + + bb0: { + _0 = EmptyStruct2; + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.nest_copy.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.nest_copy.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..aba8b651be31a --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.nest_copy.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,68 @@ +- // MIR for `nest_copy` before InstSimplify-after-simplifycfg ++ // MIR for `nest_copy` after InstSimplify-after-simplifycfg + + fn nest_copy(_1: &NestCopy) -> NestCopy { + debug v => _1; + let mut _0: NestCopy; + let _2: i32; + let mut _6: i32; + let mut _7: u64; + let mut _8: [i8; 3]; + let mut _10: i32; + let mut _11: AllCopy; + scope 1 { + debug a => _2; + let _3: u64; + scope 2 { + debug b => _3; + let _4: [i8; 3]; + scope 3 { + debug c => _4; + let _5: AllCopy; + scope 4 { + debug all_copy => _5; + let _9: i32; + scope 5 { + debug d => _9; + } + } + } + } + } + + bb0: { + StorageLive(_2); + _2 = (((*_1).1: AllCopy).0: i32); + StorageLive(_3); + _3 = (((*_1).1: AllCopy).1: u64); + StorageLive(_4); + _4 = (((*_1).1: AllCopy).2: [i8; 3]); + StorageLive(_5); + StorageLive(_6); + _6 = _2; + StorageLive(_7); + _7 = _3; + StorageLive(_8); + _8 = _4; + _5 = AllCopy { a: move _6, b: move _7, c: move _8 }; + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + StorageLive(_9); + _9 = ((*_1).0: i32); + StorageLive(_10); + _10 = _9; + StorageLive(_11); + _11 = move _5; + _0 = NestCopy { d: move _10, all_copy: move _11 }; + StorageDead(_11); + StorageDead(_10); + StorageDead(_9); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/copy_like.rs b/tests/mir-opt/instsimplify/copy_like.rs new file mode 100644 index 0000000000000..143474d0f0c6f --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.rs @@ -0,0 +1,160 @@ +//@ test-mir-pass: InstSimplify-after-simplifycfg + +struct AllCopy { + a: i32, + b: u64, + c: [i8; 3], +} + +// EMIT_MIR copy_like.all_copy.InstSimplify-after-simplifycfg.diff +fn all_copy(v: &AllCopy) -> AllCopy { + // CHECK-LABEL: fn all_copy( + // CHECK: bb0: { + // CHECK-NOT: = AllCopy { {{.*}} }; + // CHECK: _0 = (*_1); + let a = v.a; + let b = v.b; + let c = v.c; + AllCopy { a, b, c } +} + +// FIXME: This can be transformed into `Copy`. +// EMIT_MIR copy_like.all_copy_move.InstSimplify-after-simplifycfg.diff +fn all_copy_move(v: AllCopy) -> AllCopy { + // CHECK-LABEL: fn all_copy_move( + // CHECK: bb0: { + // CHECK-NOT: _0 = (*_1); + // CHECK: = AllCopy { {{.*}} }; + let a = v.a; + let b = v.b; + let c = v.c; + AllCopy { a, b, c } +} + +// EMIT_MIR copy_like.all_copy_ret_2.InstSimplify-after-simplifycfg.diff +fn all_copy_ret_2(v: &AllCopy) -> (AllCopy, AllCopy) { + // CHECK-LABEL: fn all_copy_ret_2( + // CHECK: bb0: { + // CHECK-NOT: = AllCopy { {{.*}} }; + // CHECK: [[V1:_.*]] = (*_1); + // CHECK: [[V2:_.*]] = (*_1); + // CHECK: _0 = (move [[V1]], move [[V2]]); + let a = v.a; + let b = v.b; + let c = v.c; + (AllCopy { a, b, c }, AllCopy { a, b, c }) +} + +struct AllCopy2 { + a: i32, + b: u64, + c: [i8; 3], +} + +// EMIT_MIR copy_like.all_copy_different_type.InstSimplify-after-simplifycfg.diff +fn all_copy_different_type(v: &AllCopy) -> AllCopy2 { + // CHECK-LABEL: fn all_copy_different_type( + // CHECK: bb0: { + // CHECK: _0 = AllCopy2 { {{.*}} }; + let a = v.a; + let b = v.b; + let c = v.c; + AllCopy2 { a, b, c } +} + +struct SameType { + a: i32, + b: i32, +} + +// EMIT_MIR copy_like.same_type_different_index.InstSimplify-after-simplifycfg.diff +fn same_type_different_index(v: &SameType) -> SameType { + // CHECK-LABEL: fn same_type_different_index( + // CHECK: bb0: { + // CHECK: _0 = SameType { {{.*}} }; + let a = v.b; + let b = v.a; + SameType { a, b } +} + +// EMIT_MIR copy_like.all_copy_has_changed.InstSimplify-after-simplifycfg.diff +fn all_copy_has_changed(v: &mut AllCopy) -> AllCopy { + // CHECK-LABEL: fn all_copy_has_changed( + // CHECK: bb0: { + // CHECK: _0 = AllCopy { {{.*}} }; + let a = v.a; + let b = v.b; + let c = v.c; + v.a = 1; + AllCopy { a, b, c } +} + +// EMIT_MIR copy_like.all_copy_use_changed.InstSimplify-after-simplifycfg.diff +fn all_copy_use_changed(v: &mut AllCopy) -> AllCopy { + // CHECK-LABEL: fn all_copy_use_changed( + // CHECK: bb0: { + // CHECK-NOT: = AllCopy { {{.*}} }; + // CHECK: _0 = (*_1); + let mut a = v.a; + v.a = 1; + a = v.a; + let b = v.b; + let c = v.c; + AllCopy { a, b, c } +} + +// We deleted the records for b and c, +// because we couldn't easily determine which field had been updated. +// EMIT_MIR copy_like.all_copy_use_changed_2.InstSimplify-after-simplifycfg.diff +fn all_copy_use_changed_2(v: &mut AllCopy) -> AllCopy { + // CHECK-LABEL: fn all_copy_use_changed_2( + // CHECK: bb0: { + // CHECK-NOT: _0 = (*_1); + // CHECK: = AllCopy { {{.*}} }; + let mut a = v.a; + let b = v.b; + let c = v.c; + v.a = 1; + a = v.a; + AllCopy { a, b, c } +} + +struct NestCopy { + d: i32, + all_copy: AllCopy, +} + +// FIXME: Consider supporting multi-level fields? +// (Note: clone becomes a single-level field after inlining) +// EMIT_MIR copy_like.nest_copy.InstSimplify-after-simplifycfg.diff +fn nest_copy(v: &NestCopy) -> NestCopy { + // CHECK-LABEL: fn nest_copy( + // CHECK: bb0: { + // CHECK: = AllCopy { {{.*}} }; + // CHECK: = NestCopy { {{.*}} }; + let a = v.all_copy.a; + let b = v.all_copy.b; + let c = v.all_copy.c; + let all_copy = AllCopy { a, b, c }; + let d = v.d; + NestCopy { d, all_copy } +} + +struct EmptyStruct1; +struct EmptyStruct2 {} + +// EMIT_MIR copy_like.empty_struct_1.InstSimplify-after-simplifycfg.diff +fn empty_struct_1(v: &EmptyStruct1) -> EmptyStruct1 { + // CHECK-LABEL: fn empty_struct_1( + // CHECK: bb0: { + // CHECK: _0 = EmptyStruct1; + EmptyStruct1 +} + +// EMIT_MIR copy_like.empty_struct_2.InstSimplify-after-simplifycfg.diff +fn empty_struct_2(v: &EmptyStruct2) -> EmptyStruct2 { + // CHECK-LABEL: fn empty_struct_2( + // CHECK: bb0: { + // CHECK: _0 = EmptyStruct2; + EmptyStruct2 {} +} diff --git a/tests/mir-opt/instsimplify/copy_like.same_type_different_index.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/copy_like.same_type_different_index.InstSimplify-after-simplifycfg.diff new file mode 100644 index 0000000000000..4e53266bc42bc --- /dev/null +++ b/tests/mir-opt/instsimplify/copy_like.same_type_different_index.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,35 @@ +- // MIR for `same_type_different_index` before InstSimplify-after-simplifycfg ++ // MIR for `same_type_different_index` after InstSimplify-after-simplifycfg + + fn same_type_different_index(_1: &SameType) -> SameType { + debug v => _1; + let mut _0: SameType; + let _2: i32; + let mut _4: i32; + let mut _5: i32; + scope 1 { + debug a => _2; + let _3: i32; + scope 2 { + debug b => _3; + } + } + + bb0: { + StorageLive(_2); + _2 = ((*_1).1: i32); + StorageLive(_3); + _3 = ((*_1).0: i32); + StorageLive(_4); + _4 = _2; + StorageLive(_5); + _5 = _3; + _0 = SameType { a: move _4, b: move _5 }; + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + } +