Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 39db211

Browse files
committedJul 29, 2024·
Auto merge of rust-lang#128299 - DianQK:clone-copy, r=<try>
Simplify the canonical clone method and the copy-like forms to copy Fixes rust-lang#128081. Currently being blocked by rust-lang#128265. `@rustbot` label +S-blocked r? `@saethlin`
2 parents 66e5852 + a7d48fc commit 39db211

20 files changed

+888
-6
lines changed
 

‎compiler/rustc_index/src/vec.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,16 @@ impl<I: Idx, T> IndexVec<I, T> {
188188
let min_new_len = elem.index() + 1;
189189
self.raw.resize_with(min_new_len, fill_value);
190190
}
191+
192+
#[inline]
193+
pub fn reset_all(&mut self, elem: T)
194+
where
195+
T: Copy,
196+
{
197+
for e in self.raw.iter_mut() {
198+
*e = elem;
199+
}
200+
}
191201
}
192202

193203
/// `IndexVec` is often used as a map, so it provides some map-like APIs.

‎compiler/rustc_mir_transform/src/instsimplify.rs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use rustc_ast::attr;
44
use rustc_hir::LangItem;
5+
use rustc_index::IndexVec;
56
use rustc_middle::bug;
67
use rustc_middle::mir::*;
78
use rustc_middle::ty::layout::ValidityRequirement;
@@ -60,8 +61,8 @@ impl<'tcx> MirPass<'tcx> for InstSimplify {
6061
_ => {}
6162
}
6263
}
63-
6464
ctx.simplify_primitive_clone(block.terminator.as_mut().unwrap(), &mut block.statements);
65+
ctx.simplify_copy_like(&mut block.statements);
6566
ctx.simplify_intrinsic_assert(block.terminator.as_mut().unwrap());
6667
ctx.simplify_nounwind_call(block.terminator.as_mut().unwrap());
6768
simplify_duplicate_switch_targets(block.terminator.as_mut().unwrap());
@@ -207,6 +208,95 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
207208
}
208209
}
209210

211+
/// Transform `Aggregate(Adt, [(*_1).0, (*_1).1])` ==> `Copy(*_1)`.
212+
/// This comes from the simplification of the clone method by `simplify_primitive_clone`.
213+
fn simplify_copy_like(&self, statements: &mut Vec<Statement<'tcx>>) {
214+
let mut assignments = IndexVec::from_elem(None::<Place<'tcx>>, self.local_decls);
215+
for statement in statements {
216+
match statement.kind {
217+
StatementKind::Assign(box (dest, ref mut rvalue)) => {
218+
if let Rvalue::Aggregate(_, fields) = rvalue {
219+
let mut from_local = None;
220+
if fields.iter_enumerated().all(|(index, field)| {
221+
let Some(from_place) = field
222+
.place()
223+
.and_then(|p| p.as_local())
224+
.and_then(|l| assignments[l])
225+
else {
226+
return false;
227+
};
228+
// All fields must come from the same local.
229+
if let Some(from_local) = from_local {
230+
if from_place.local != from_local {
231+
return false;
232+
}
233+
} else {
234+
// We can only copy the same type.
235+
let Some(from_ty) =
236+
self.local_decls[from_place.local].ty.builtin_deref(false)
237+
else {
238+
return false;
239+
};
240+
let dest_ty = dest.ty(self.local_decls, self.tcx).ty;
241+
if dest_ty != from_ty {
242+
return false;
243+
};
244+
from_local = Some(from_place.local);
245+
}
246+
// For more complex scenarios, we expect to get this simplified projection within a complete pipeline.
247+
let [ProjectionElem::Deref, ProjectionElem::Field(from_index, _)] =
248+
*from_place.projection.as_slice()
249+
else {
250+
return false;
251+
};
252+
from_index == index
253+
}) {
254+
if let Some(local) = from_local {
255+
if self.should_simplify(&statement.source_info, rvalue) {
256+
*rvalue = Rvalue::Use(Operand::Copy(Place {
257+
local,
258+
projection: self
259+
.tcx
260+
.mk_place_elems(&[ProjectionElem::Deref]),
261+
}));
262+
}
263+
}
264+
}
265+
}
266+
// Collect available assignments, including those transformed from `Aggregate`.
267+
if let Some(local) = dest.as_local() {
268+
assignments[local] = if let Rvalue::Use(operand) = rvalue
269+
&& let Some(place) = operand.place()
270+
{
271+
if let Some(rvalue_local) = place.as_local() {
272+
let place = assignments[rvalue_local];
273+
if operand.is_move() {
274+
assignments[rvalue_local] = None;
275+
}
276+
place
277+
} else {
278+
Some(place)
279+
}
280+
} else {
281+
// This assignment generally comes from debuginfo (e.g., Ref),
282+
// but we still need to check if a local is being overridden.
283+
None
284+
};
285+
} else {
286+
// We don't handle projection, so we drop all previous assignments.
287+
assignments.reset_all(None);
288+
}
289+
}
290+
StatementKind::StorageLive(_)
291+
| StatementKind::StorageDead(_)
292+
| StatementKind::Nop => {}
293+
_ => {
294+
assignments.reset_all(None);
295+
}
296+
}
297+
}
298+
}
299+
210300
fn simplify_cast(&self, rvalue: &mut Rvalue<'tcx>) {
211301
if let Rvalue::Cast(kind, operand, cast_ty) = rvalue {
212302
let operand_ty = operand.ty(self.local_decls, self.tcx);

‎tests/codegen/clone_as_copy.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//@ revisions: DEBUGINFO NODEBUGINFO
2+
//@ compile-flags: -O -Cno-prepopulate-passes
3+
//@ [DEBUGINFO] compile-flags: -Cdebuginfo=full
4+
5+
// From https://github.com/rust-lang/rust/issues/128081.
6+
// Ensure that we only generate a memcpy instruction.
7+
8+
#![crate_type = "lib"]
9+
10+
#[derive(Clone)]
11+
struct SubCloneAndCopy {
12+
v1: u32,
13+
v2: u32,
14+
}
15+
16+
#[derive(Clone)]
17+
struct CloneOnly {
18+
v1: u8,
19+
v2: u8,
20+
v3: u8,
21+
v4: u8,
22+
v5: u8,
23+
v6: u8,
24+
v7: u8,
25+
v8: u8,
26+
v9: u8,
27+
v_sub: SubCloneAndCopy,
28+
v_large: [u8; 256],
29+
}
30+
31+
// CHECK-LABEL: define {{.*}}@clone_only(
32+
#[no_mangle]
33+
pub fn clone_only(v: &CloneOnly) -> CloneOnly {
34+
// CHECK-NOT: call {{.*}}clone
35+
// CHECK-NOT: store i8
36+
// CHECK-NOT: store i32
37+
// CHECK: call void @llvm.memcpy
38+
// CHECK-NEXT: ret void
39+
v.clone()
40+
}

‎tests/mir-opt/instsimplify/clone.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ test-mir-pass: InstSimplify-after-simplifycfg
2+
//@ compile-flags: -Zmir-enable-passes=+InstSimplify-before-inline,+ReferencePropagation,+SimplifyCfg-after-unreachable-enum-branching
3+
4+
// Check if we have transformed the default clone to copy in the specific pipeline.
5+
6+
// EMIT_MIR clone.{impl#0}-clone.InstSimplify-after-simplifycfg.diff
7+
8+
// CHECK-LABEL: ::clone(
9+
// CHECK-NOT: = AllCopy { {{.*}} };
10+
// CHECK: _0 = (*_1);
11+
// CHECK: return;
12+
#[derive(Clone)]
13+
struct AllCopy {
14+
a: i32,
15+
b: u64,
16+
c: [i8; 3],
17+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
- // MIR for `<impl at $DIR/clone.rs:12:10: 12:15>::clone` before InstSimplify-after-simplifycfg
2+
+ // MIR for `<impl at $DIR/clone.rs:12:10: 12:15>::clone` after InstSimplify-after-simplifycfg
3+
4+
fn <impl at $DIR/clone.rs:12:10: 12:15>::clone(_1: &AllCopy) -> AllCopy {
5+
debug self => _1;
6+
let mut _0: AllCopy;
7+
let mut _2: i32;
8+
let mut _3: &i32;
9+
let _4: &i32;
10+
let mut _5: u64;
11+
let mut _6: &u64;
12+
let _7: &u64;
13+
let mut _8: [i8; 3];
14+
let mut _9: &[i8; 3];
15+
let _10: &[i8; 3];
16+
17+
bb0: {
18+
StorageLive(_2);
19+
_2 = ((*_1).0: i32);
20+
StorageLive(_5);
21+
_5 = ((*_1).1: u64);
22+
StorageLive(_8);
23+
_8 = ((*_1).2: [i8; 3]);
24+
- _0 = AllCopy { a: move _2, b: move _5, c: move _8 };
25+
+ _0 = (*_1);
26+
StorageDead(_8);
27+
StorageDead(_5);
28+
StorageDead(_2);
29+
return;
30+
}
31+
}
32+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
- // MIR for `all_copy` before InstSimplify-after-simplifycfg
2+
+ // MIR for `all_copy` after InstSimplify-after-simplifycfg
3+
4+
fn all_copy(_1: &AllCopy) -> AllCopy {
5+
debug v => _1;
6+
let mut _0: AllCopy;
7+
let _2: i32;
8+
let mut _5: i32;
9+
let mut _6: u64;
10+
let mut _7: [i8; 3];
11+
scope 1 {
12+
debug a => _2;
13+
let _3: u64;
14+
scope 2 {
15+
debug b => _3;
16+
let _4: [i8; 3];
17+
scope 3 {
18+
debug c => _4;
19+
}
20+
}
21+
}
22+
23+
bb0: {
24+
StorageLive(_2);
25+
_2 = ((*_1).0: i32);
26+
StorageLive(_3);
27+
_3 = ((*_1).1: u64);
28+
StorageLive(_4);
29+
_4 = ((*_1).2: [i8; 3]);
30+
StorageLive(_5);
31+
_5 = _2;
32+
StorageLive(_6);
33+
_6 = _3;
34+
StorageLive(_7);
35+
_7 = _4;
36+
- _0 = AllCopy { a: move _5, b: move _6, c: move _7 };
37+
+ _0 = (*_1);
38+
StorageDead(_7);
39+
StorageDead(_6);
40+
StorageDead(_5);
41+
StorageDead(_4);
42+
StorageDead(_3);
43+
StorageDead(_2);
44+
return;
45+
}
46+
}
47+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
- // MIR for `all_copy_different_type` before InstSimplify-after-simplifycfg
2+
+ // MIR for `all_copy_different_type` after InstSimplify-after-simplifycfg
3+
4+
fn all_copy_different_type(_1: &AllCopy) -> AllCopy2 {
5+
debug v => _1;
6+
let mut _0: AllCopy2;
7+
let _2: i32;
8+
let mut _5: i32;
9+
let mut _6: u64;
10+
let mut _7: [i8; 3];
11+
scope 1 {
12+
debug a => _2;
13+
let _3: u64;
14+
scope 2 {
15+
debug b => _3;
16+
let _4: [i8; 3];
17+
scope 3 {
18+
debug c => _4;
19+
}
20+
}
21+
}
22+
23+
bb0: {
24+
StorageLive(_2);
25+
_2 = ((*_1).0: i32);
26+
StorageLive(_3);
27+
_3 = ((*_1).1: u64);
28+
StorageLive(_4);
29+
_4 = ((*_1).2: [i8; 3]);
30+
StorageLive(_5);
31+
_5 = _2;
32+
StorageLive(_6);
33+
_6 = _3;
34+
StorageLive(_7);
35+
_7 = _4;
36+
_0 = AllCopy2 { a: move _5, b: move _6, c: move _7 };
37+
StorageDead(_7);
38+
StorageDead(_6);
39+
StorageDead(_5);
40+
StorageDead(_4);
41+
StorageDead(_3);
42+
StorageDead(_2);
43+
return;
44+
}
45+
}
46+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
- // MIR for `all_copy_has_changed` before InstSimplify-after-simplifycfg
2+
+ // MIR for `all_copy_has_changed` after InstSimplify-after-simplifycfg
3+
4+
fn all_copy_has_changed(_1: &mut AllCopy) -> AllCopy {
5+
debug v => _1;
6+
let mut _0: AllCopy;
7+
let _2: i32;
8+
let mut _5: i32;
9+
let mut _6: u64;
10+
let mut _7: [i8; 3];
11+
scope 1 {
12+
debug a => _2;
13+
let _3: u64;
14+
scope 2 {
15+
debug b => _3;
16+
let _4: [i8; 3];
17+
scope 3 {
18+
debug c => _4;
19+
}
20+
}
21+
}
22+
23+
bb0: {
24+
StorageLive(_2);
25+
_2 = ((*_1).0: i32);
26+
StorageLive(_3);
27+
_3 = ((*_1).1: u64);
28+
StorageLive(_4);
29+
_4 = ((*_1).2: [i8; 3]);
30+
((*_1).0: i32) = const 1_i32;
31+
StorageLive(_5);
32+
_5 = _2;
33+
StorageLive(_6);
34+
_6 = _3;
35+
StorageLive(_7);
36+
_7 = _4;
37+
_0 = AllCopy { a: move _5, b: move _6, c: move _7 };
38+
StorageDead(_7);
39+
StorageDead(_6);
40+
StorageDead(_5);
41+
StorageDead(_4);
42+
StorageDead(_3);
43+
StorageDead(_2);
44+
return;
45+
}
46+
}
47+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
- // MIR for `all_copy_move` before InstSimplify-after-simplifycfg
2+
+ // MIR for `all_copy_move` after InstSimplify-after-simplifycfg
3+
4+
fn all_copy_move(_1: AllCopy) -> AllCopy {
5+
debug v => _1;
6+
let mut _0: AllCopy;
7+
let _2: i32;
8+
let mut _5: i32;
9+
let mut _6: u64;
10+
let mut _7: [i8; 3];
11+
scope 1 {
12+
debug a => _2;
13+
let _3: u64;
14+
scope 2 {
15+
debug b => _3;
16+
let _4: [i8; 3];
17+
scope 3 {
18+
debug c => _4;
19+
}
20+
}
21+
}
22+
23+
bb0: {
24+
StorageLive(_2);
25+
_2 = (_1.0: i32);
26+
StorageLive(_3);
27+
_3 = (_1.1: u64);
28+
StorageLive(_4);
29+
_4 = (_1.2: [i8; 3]);
30+
StorageLive(_5);
31+
_5 = _2;
32+
StorageLive(_6);
33+
_6 = _3;
34+
StorageLive(_7);
35+
_7 = _4;
36+
_0 = AllCopy { a: move _5, b: move _6, c: move _7 };
37+
StorageDead(_7);
38+
StorageDead(_6);
39+
StorageDead(_5);
40+
StorageDead(_4);
41+
StorageDead(_3);
42+
StorageDead(_2);
43+
return;
44+
}
45+
}
46+
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
- // MIR for `all_copy_ret_2` before InstSimplify-after-simplifycfg
2+
+ // MIR for `all_copy_ret_2` after InstSimplify-after-simplifycfg
3+
4+
fn all_copy_ret_2(_1: &AllCopy) -> (AllCopy, AllCopy) {
5+
debug v => _1;
6+
let mut _0: (AllCopy, AllCopy);
7+
let _2: i32;
8+
let mut _5: AllCopy;
9+
let mut _6: i32;
10+
let mut _7: u64;
11+
let mut _8: [i8; 3];
12+
let mut _9: AllCopy;
13+
let mut _10: i32;
14+
let mut _11: u64;
15+
let mut _12: [i8; 3];
16+
scope 1 {
17+
debug a => _2;
18+
let _3: u64;
19+
scope 2 {
20+
debug b => _3;
21+
let _4: [i8; 3];
22+
scope 3 {
23+
debug c => _4;
24+
}
25+
}
26+
}
27+
28+
bb0: {
29+
StorageLive(_2);
30+
_2 = ((*_1).0: i32);
31+
StorageLive(_3);
32+
_3 = ((*_1).1: u64);
33+
StorageLive(_4);
34+
_4 = ((*_1).2: [i8; 3]);
35+
StorageLive(_5);
36+
StorageLive(_6);
37+
_6 = _2;
38+
StorageLive(_7);
39+
_7 = _3;
40+
StorageLive(_8);
41+
_8 = _4;
42+
- _5 = AllCopy { a: move _6, b: move _7, c: move _8 };
43+
+ _5 = (*_1);
44+
StorageDead(_8);
45+
StorageDead(_7);
46+
StorageDead(_6);
47+
StorageLive(_9);
48+
StorageLive(_10);
49+
_10 = _2;
50+
StorageLive(_11);
51+
_11 = _3;
52+
StorageLive(_12);
53+
_12 = _4;
54+
- _9 = AllCopy { a: move _10, b: move _11, c: move _12 };
55+
+ _9 = (*_1);
56+
StorageDead(_12);
57+
StorageDead(_11);
58+
StorageDead(_10);
59+
_0 = (move _5, move _9);
60+
StorageDead(_9);
61+
StorageDead(_5);
62+
StorageDead(_4);
63+
StorageDead(_3);
64+
StorageDead(_2);
65+
return;
66+
}
67+
}
68+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
- // MIR for `all_copy_use_changed` before InstSimplify-after-simplifycfg
2+
+ // MIR for `all_copy_use_changed` after InstSimplify-after-simplifycfg
3+
4+
fn all_copy_use_changed(_1: &mut AllCopy) -> AllCopy {
5+
debug v => _1;
6+
let mut _0: AllCopy;
7+
let mut _2: i32;
8+
let mut _3: i32;
9+
let mut _6: i32;
10+
let mut _7: u64;
11+
let mut _8: [i8; 3];
12+
scope 1 {
13+
debug a => _2;
14+
let _4: u64;
15+
scope 2 {
16+
debug b => _4;
17+
let _5: [i8; 3];
18+
scope 3 {
19+
debug c => _5;
20+
}
21+
}
22+
}
23+
24+
bb0: {
25+
StorageLive(_2);
26+
_2 = ((*_1).0: i32);
27+
((*_1).0: i32) = const 1_i32;
28+
StorageLive(_3);
29+
_3 = ((*_1).0: i32);
30+
_2 = move _3;
31+
StorageDead(_3);
32+
StorageLive(_4);
33+
_4 = ((*_1).1: u64);
34+
StorageLive(_5);
35+
_5 = ((*_1).2: [i8; 3]);
36+
StorageLive(_6);
37+
_6 = _2;
38+
StorageLive(_7);
39+
_7 = _4;
40+
StorageLive(_8);
41+
_8 = _5;
42+
- _0 = AllCopy { a: move _6, b: move _7, c: move _8 };
43+
+ _0 = (*_1);
44+
StorageDead(_8);
45+
StorageDead(_7);
46+
StorageDead(_6);
47+
StorageDead(_5);
48+
StorageDead(_4);
49+
StorageDead(_2);
50+
return;
51+
}
52+
}
53+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
- // MIR for `all_copy_use_changed_2` before InstSimplify-after-simplifycfg
2+
+ // MIR for `all_copy_use_changed_2` after InstSimplify-after-simplifycfg
3+
4+
fn all_copy_use_changed_2(_1: &mut AllCopy) -> AllCopy {
5+
debug v => _1;
6+
let mut _0: AllCopy;
7+
let mut _2: i32;
8+
let mut _5: i32;
9+
let mut _6: i32;
10+
let mut _7: u64;
11+
let mut _8: [i8; 3];
12+
scope 1 {
13+
debug a => _2;
14+
let _3: u64;
15+
scope 2 {
16+
debug b => _3;
17+
let _4: [i8; 3];
18+
scope 3 {
19+
debug c => _4;
20+
}
21+
}
22+
}
23+
24+
bb0: {
25+
StorageLive(_2);
26+
_2 = ((*_1).0: i32);
27+
StorageLive(_3);
28+
_3 = ((*_1).1: u64);
29+
StorageLive(_4);
30+
_4 = ((*_1).2: [i8; 3]);
31+
((*_1).0: i32) = const 1_i32;
32+
StorageLive(_5);
33+
_5 = ((*_1).0: i32);
34+
_2 = move _5;
35+
StorageDead(_5);
36+
StorageLive(_6);
37+
_6 = _2;
38+
StorageLive(_7);
39+
_7 = _3;
40+
StorageLive(_8);
41+
_8 = _4;
42+
_0 = AllCopy { a: move _6, b: move _7, c: move _8 };
43+
StorageDead(_8);
44+
StorageDead(_7);
45+
StorageDead(_6);
46+
StorageDead(_4);
47+
StorageDead(_3);
48+
StorageDead(_2);
49+
return;
50+
}
51+
}
52+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
- // MIR for `empty_struct_1` before InstSimplify-after-simplifycfg
2+
+ // MIR for `empty_struct_1` after InstSimplify-after-simplifycfg
3+
4+
fn empty_struct_1(_1: &EmptyStruct1) -> EmptyStruct1 {
5+
debug v => _1;
6+
let mut _0: EmptyStruct1;
7+
8+
bb0: {
9+
_0 = EmptyStruct1;
10+
return;
11+
}
12+
}
13+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
- // MIR for `empty_struct_2` before InstSimplify-after-simplifycfg
2+
+ // MIR for `empty_struct_2` after InstSimplify-after-simplifycfg
3+
4+
fn empty_struct_2(_1: &EmptyStruct2) -> EmptyStruct2 {
5+
debug v => _1;
6+
let mut _0: EmptyStruct2;
7+
8+
bb0: {
9+
_0 = EmptyStruct2;
10+
return;
11+
}
12+
}
13+
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
- // MIR for `nest_copy` before InstSimplify-after-simplifycfg
2+
+ // MIR for `nest_copy` after InstSimplify-after-simplifycfg
3+
4+
fn nest_copy(_1: &NestCopy) -> NestCopy {
5+
debug v => _1;
6+
let mut _0: NestCopy;
7+
let _2: i32;
8+
let mut _6: i32;
9+
let mut _7: u64;
10+
let mut _8: [i8; 3];
11+
let mut _10: i32;
12+
let mut _11: AllCopy;
13+
scope 1 {
14+
debug a => _2;
15+
let _3: u64;
16+
scope 2 {
17+
debug b => _3;
18+
let _4: [i8; 3];
19+
scope 3 {
20+
debug c => _4;
21+
let _5: AllCopy;
22+
scope 4 {
23+
debug all_copy => _5;
24+
let _9: i32;
25+
scope 5 {
26+
debug d => _9;
27+
}
28+
}
29+
}
30+
}
31+
}
32+
33+
bb0: {
34+
StorageLive(_2);
35+
_2 = (((*_1).1: AllCopy).0: i32);
36+
StorageLive(_3);
37+
_3 = (((*_1).1: AllCopy).1: u64);
38+
StorageLive(_4);
39+
_4 = (((*_1).1: AllCopy).2: [i8; 3]);
40+
StorageLive(_5);
41+
StorageLive(_6);
42+
_6 = _2;
43+
StorageLive(_7);
44+
_7 = _3;
45+
StorageLive(_8);
46+
_8 = _4;
47+
_5 = AllCopy { a: move _6, b: move _7, c: move _8 };
48+
StorageDead(_8);
49+
StorageDead(_7);
50+
StorageDead(_6);
51+
StorageLive(_9);
52+
_9 = ((*_1).0: i32);
53+
StorageLive(_10);
54+
_10 = _9;
55+
StorageLive(_11);
56+
_11 = move _5;
57+
_0 = NestCopy { d: move _10, all_copy: move _11 };
58+
StorageDead(_11);
59+
StorageDead(_10);
60+
StorageDead(_9);
61+
StorageDead(_5);
62+
StorageDead(_4);
63+
StorageDead(_3);
64+
StorageDead(_2);
65+
return;
66+
}
67+
}
68+
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
//@ test-mir-pass: InstSimplify-after-simplifycfg
2+
3+
struct AllCopy {
4+
a: i32,
5+
b: u64,
6+
c: [i8; 3],
7+
}
8+
9+
// EMIT_MIR copy_like.all_copy.InstSimplify-after-simplifycfg.diff
10+
fn all_copy(v: &AllCopy) -> AllCopy {
11+
// CHECK-LABEL: fn all_copy(
12+
// CHECK: bb0: {
13+
// CHECK-NOT: = AllCopy { {{.*}} };
14+
// CHECK: _0 = (*_1);
15+
let a = v.a;
16+
let b = v.b;
17+
let c = v.c;
18+
AllCopy { a, b, c }
19+
}
20+
21+
// FIXME: This can be transformed into `Copy`.
22+
// EMIT_MIR copy_like.all_copy_move.InstSimplify-after-simplifycfg.diff
23+
fn all_copy_move(v: AllCopy) -> AllCopy {
24+
// CHECK-LABEL: fn all_copy_move(
25+
// CHECK: bb0: {
26+
// CHECK-NOT: _0 = (*_1);
27+
// CHECK: = AllCopy { {{.*}} };
28+
let a = v.a;
29+
let b = v.b;
30+
let c = v.c;
31+
AllCopy { a, b, c }
32+
}
33+
34+
// EMIT_MIR copy_like.all_copy_ret_2.InstSimplify-after-simplifycfg.diff
35+
fn all_copy_ret_2(v: &AllCopy) -> (AllCopy, AllCopy) {
36+
// CHECK-LABEL: fn all_copy_ret_2(
37+
// CHECK: bb0: {
38+
// CHECK-NOT: = AllCopy { {{.*}} };
39+
// CHECK: [[V1:_.*]] = (*_1);
40+
// CHECK: [[V2:_.*]] = (*_1);
41+
// CHECK: _0 = (move [[V1]], move [[V2]]);
42+
let a = v.a;
43+
let b = v.b;
44+
let c = v.c;
45+
(AllCopy { a, b, c }, AllCopy { a, b, c })
46+
}
47+
48+
struct AllCopy2 {
49+
a: i32,
50+
b: u64,
51+
c: [i8; 3],
52+
}
53+
54+
// EMIT_MIR copy_like.all_copy_different_type.InstSimplify-after-simplifycfg.diff
55+
fn all_copy_different_type(v: &AllCopy) -> AllCopy2 {
56+
// CHECK-LABEL: fn all_copy_different_type(
57+
// CHECK: bb0: {
58+
// CHECK: _0 = AllCopy2 { {{.*}} };
59+
let a = v.a;
60+
let b = v.b;
61+
let c = v.c;
62+
AllCopy2 { a, b, c }
63+
}
64+
65+
struct SameType {
66+
a: i32,
67+
b: i32,
68+
}
69+
70+
// EMIT_MIR copy_like.same_type_different_index.InstSimplify-after-simplifycfg.diff
71+
fn same_type_different_index(v: &SameType) -> SameType {
72+
// CHECK-LABEL: fn same_type_different_index(
73+
// CHECK: bb0: {
74+
// CHECK: _0 = SameType { {{.*}} };
75+
let a = v.b;
76+
let b = v.a;
77+
SameType { a, b }
78+
}
79+
80+
// EMIT_MIR copy_like.all_copy_has_changed.InstSimplify-after-simplifycfg.diff
81+
fn all_copy_has_changed(v: &mut AllCopy) -> AllCopy {
82+
// CHECK-LABEL: fn all_copy_has_changed(
83+
// CHECK: bb0: {
84+
// CHECK: _0 = AllCopy { {{.*}} };
85+
let a = v.a;
86+
let b = v.b;
87+
let c = v.c;
88+
v.a = 1;
89+
AllCopy { a, b, c }
90+
}
91+
92+
// EMIT_MIR copy_like.all_copy_use_changed.InstSimplify-after-simplifycfg.diff
93+
fn all_copy_use_changed(v: &mut AllCopy) -> AllCopy {
94+
// CHECK-LABEL: fn all_copy_use_changed(
95+
// CHECK: bb0: {
96+
// CHECK-NOT: = AllCopy { {{.*}} };
97+
// CHECK: _0 = (*_1);
98+
let mut a = v.a;
99+
v.a = 1;
100+
a = v.a;
101+
let b = v.b;
102+
let c = v.c;
103+
AllCopy { a, b, c }
104+
}
105+
106+
// We deleted the records for b and c,
107+
// because we couldn't easily determine which field had been updated.
108+
// EMIT_MIR copy_like.all_copy_use_changed_2.InstSimplify-after-simplifycfg.diff
109+
fn all_copy_use_changed_2(v: &mut AllCopy) -> AllCopy {
110+
// CHECK-LABEL: fn all_copy_use_changed_2(
111+
// CHECK: bb0: {
112+
// CHECK-NOT: _0 = (*_1);
113+
// CHECK: = AllCopy { {{.*}} };
114+
let mut a = v.a;
115+
let b = v.b;
116+
let c = v.c;
117+
v.a = 1;
118+
a = v.a;
119+
AllCopy { a, b, c }
120+
}
121+
122+
struct NestCopy {
123+
d: i32,
124+
all_copy: AllCopy,
125+
}
126+
127+
// FIXME: Consider supporting multi-level fields?
128+
// (Note: clone becomes a single-level field after inlining)
129+
// EMIT_MIR copy_like.nest_copy.InstSimplify-after-simplifycfg.diff
130+
fn nest_copy(v: &NestCopy) -> NestCopy {
131+
// CHECK-LABEL: fn nest_copy(
132+
// CHECK: bb0: {
133+
// CHECK: = AllCopy { {{.*}} };
134+
// CHECK: = NestCopy { {{.*}} };
135+
let a = v.all_copy.a;
136+
let b = v.all_copy.b;
137+
let c = v.all_copy.c;
138+
let all_copy = AllCopy { a, b, c };
139+
let d = v.d;
140+
NestCopy { d, all_copy }
141+
}
142+
143+
struct EmptyStruct1;
144+
struct EmptyStruct2 {}
145+
146+
// EMIT_MIR copy_like.empty_struct_1.InstSimplify-after-simplifycfg.diff
147+
fn empty_struct_1(v: &EmptyStruct1) -> EmptyStruct1 {
148+
// CHECK-LABEL: fn empty_struct_1(
149+
// CHECK: bb0: {
150+
// CHECK: _0 = EmptyStruct1;
151+
EmptyStruct1
152+
}
153+
154+
// EMIT_MIR copy_like.empty_struct_2.InstSimplify-after-simplifycfg.diff
155+
fn empty_struct_2(v: &EmptyStruct2) -> EmptyStruct2 {
156+
// CHECK-LABEL: fn empty_struct_2(
157+
// CHECK: bb0: {
158+
// CHECK: _0 = EmptyStruct2;
159+
EmptyStruct2 {}
160+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
- // MIR for `same_type_different_index` before InstSimplify-after-simplifycfg
2+
+ // MIR for `same_type_different_index` after InstSimplify-after-simplifycfg
3+
4+
fn same_type_different_index(_1: &SameType) -> SameType {
5+
debug v => _1;
6+
let mut _0: SameType;
7+
let _2: i32;
8+
let mut _4: i32;
9+
let mut _5: i32;
10+
scope 1 {
11+
debug a => _2;
12+
let _3: i32;
13+
scope 2 {
14+
debug b => _3;
15+
}
16+
}
17+
18+
bb0: {
19+
StorageLive(_2);
20+
_2 = ((*_1).1: i32);
21+
StorageLive(_3);
22+
_3 = ((*_1).0: i32);
23+
StorageLive(_4);
24+
_4 = _2;
25+
StorageLive(_5);
26+
_5 = _3;
27+
_0 = SameType { a: move _4, b: move _5 };
28+
StorageDead(_5);
29+
StorageDead(_4);
30+
StorageDead(_3);
31+
StorageDead(_2);
32+
return;
33+
}
34+
}
35+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// MIR for `clone_as_copy` after PreCodegen
2+
3+
fn clone_as_copy(_1: &CopyLikeStruct) -> CopyLikeStruct {
4+
debug v => _1;
5+
let mut _0: CopyLikeStruct;
6+
scope 1 (inlined <CopyLikeStruct as Clone>::clone) {
7+
debug self => _1;
8+
let _2: &AllCopy;
9+
scope 2 (inlined <AllCopy as Clone>::clone) {
10+
debug self => _2;
11+
}
12+
}
13+
14+
bb0: {
15+
StorageLive(_2);
16+
_2 = &((*_1).1: AllCopy);
17+
_0 = (*_1);
18+
StorageDead(_2);
19+
return;
20+
}
21+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ compile-flags: -Cdebuginfo=full
2+
3+
// Check if we have transformed the nested clone to the copy in the complete pipeline.
4+
5+
#[derive(Clone)]
6+
struct AllCopy {
7+
a: i32,
8+
b: u64,
9+
c: [i8; 3],
10+
}
11+
12+
#[derive(Clone)]
13+
struct CopyLikeStruct {
14+
a: i32,
15+
b: AllCopy,
16+
c: [i8; 3],
17+
}
18+
19+
// EMIT_MIR clone_as_copy.clone_as_copy.PreCodegen.after.mir
20+
#[inline(never)]
21+
fn clone_as_copy(v: &CopyLikeStruct) -> CopyLikeStruct {
22+
// CHECK-LABEL: fn clone_as_copy(
23+
// CHECK-NOT: = AllCopy { {{.*}} };
24+
// CHECK-NOT: = CopyLikeStruct { {{.*}} };
25+
// CHECK: _0 = (*_1);
26+
// CHECK: return;
27+
v.clone()
28+
}

‎tests/mir-opt/pre-codegen/no_inlined_clone.{impl#0}-clone.PreCodegen.after.mir

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@
33
fn <impl at $DIR/no_inlined_clone.rs:9:10: 9:15>::clone(_1: &Foo) -> Foo {
44
debug self => _1;
55
let mut _0: Foo;
6-
let mut _2: i32;
76

87
bb0: {
9-
StorageLive(_2);
10-
_2 = ((*_1).0: i32);
11-
_0 = Foo { a: move _2 };
12-
StorageDead(_2);
8+
_0 = (*_1);
139
return;
1410
}
1511
}

0 commit comments

Comments
 (0)
This repository has been archived.