Skip to content

Commit 197f924

Browse files
committed
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. r? `@cjgillot`
2 parents e552c16 + b52bb11 commit 197f924

17 files changed

+900
-8
lines changed

compiler/rustc_mir_transform/src/gvn.rs

+57-3
Original file line numberDiff line numberDiff line change
@@ -863,9 +863,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
863863
rvalue: &mut Rvalue<'tcx>,
864864
location: Location,
865865
) -> Option<VnIndex> {
866+
let tcx = self.tcx;
867+
let rvalue_ty = rvalue.ty(self.local_decls, tcx);
866868
let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() };
867869

868-
let tcx = self.tcx;
869870
if field_ops.is_empty() {
870871
let is_zst = match *kind {
871872
AggregateKind::Array(..)
@@ -880,8 +881,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
880881
};
881882

882883
if is_zst {
883-
let ty = rvalue.ty(self.local_decls, tcx);
884-
return self.insert_constant(Const::zero_sized(ty));
884+
return self.insert_constant(Const::zero_sized(rvalue_ty));
885885
}
886886
}
887887

@@ -916,6 +916,60 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
916916
.collect();
917917
let mut fields = fields?;
918918

919+
if let AggregateTy::Def(_, _) = &mut ty
920+
&& !fields.is_empty()
921+
{
922+
let copy_from = if let Value::Projection(first_pointer, ProjectionElem::Field(_, _)) =
923+
*self.get(fields[0])
924+
{
925+
let mut copy_from_value = first_pointer;
926+
while let Value::Projection(pointer, ProjectionElem::Deref) =
927+
*self.get(copy_from_value)
928+
{
929+
copy_from_value = pointer;
930+
}
931+
if let Some(local) = self.try_as_local(copy_from_value, location) {
932+
let from_ty = self.local_decls[local].ty;
933+
if rvalue_ty == from_ty {
934+
Some((first_pointer, local, false))
935+
} else if Some(rvalue_ty) == from_ty.builtin_deref(false) {
936+
Some((first_pointer, local, true))
937+
} else {
938+
None
939+
}
940+
} else {
941+
None
942+
}
943+
} else {
944+
None
945+
};
946+
if let Some((first_pointer, local, need_deref)) = copy_from {
947+
// All fields must correspond one-to-one and come from the same aggregate value.
948+
if std::iter::zip(field_ops.iter_enumerated(), fields.iter()).all(
949+
|((index, _), &v)| {
950+
if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) =
951+
*self.get(v)
952+
&& first_pointer == pointer
953+
&& from_index == index
954+
{
955+
return true;
956+
}
957+
false
958+
},
959+
) {
960+
*rvalue = Rvalue::Use(Operand::Copy(Place {
961+
local,
962+
projection: tcx.mk_place_elems(if need_deref {
963+
&[ProjectionElem::Deref]
964+
} else {
965+
&[]
966+
}),
967+
}));
968+
return Some(first_pointer);
969+
}
970+
}
971+
}
972+
919973
if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty {
920974
let mut was_updated = false;
921975

tests/codegen/clone_as_copy.rs

+40
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/gvn_clone.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ test-mir-pass: GVN
2+
//@ compile-flags: -Zmir-enable-passes=+InstSimplify-before-inline
3+
4+
// Check if we have transformed the default clone to copy in the specific pipeline.
5+
6+
// EMIT_MIR gvn_clone.{impl#0}-clone.GVN.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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
- // MIR for `<impl at $DIR/gvn_clone.rs:12:10: 12:15>::clone` before GVN
2+
+ // MIR for `<impl at $DIR/gvn_clone.rs:12:10: 12:15>::clone` after GVN
3+
4+
fn <impl at $DIR/gvn_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+
StorageLive(_3);
20+
- StorageLive(_4);
21+
+ nop;
22+
_4 = &((*_1).0: i32);
23+
_3 = _4;
24+
- _2 = (*_3);
25+
+ _2 = ((*_1).0: i32);
26+
goto -> bb1;
27+
}
28+
29+
bb1: {
30+
StorageDead(_3);
31+
StorageLive(_5);
32+
StorageLive(_6);
33+
- StorageLive(_7);
34+
+ nop;
35+
_7 = &((*_1).1: u64);
36+
_6 = _7;
37+
- _5 = (*_6);
38+
+ _5 = ((*_1).1: u64);
39+
goto -> bb2;
40+
}
41+
42+
bb2: {
43+
StorageDead(_6);
44+
StorageLive(_8);
45+
StorageLive(_9);
46+
- StorageLive(_10);
47+
+ nop;
48+
_10 = &((*_1).2: [i8; 3]);
49+
_9 = _10;
50+
- _8 = (*_9);
51+
+ _8 = ((*_1).2: [i8; 3]);
52+
goto -> bb3;
53+
}
54+
55+
bb3: {
56+
StorageDead(_9);
57+
- _0 = AllCopy { a: move _2, b: move _5, c: move _8 };
58+
+ _0 = (*_1);
59+
StorageDead(_8);
60+
StorageDead(_5);
61+
StorageDead(_2);
62+
- StorageDead(_10);
63+
- StorageDead(_7);
64+
- StorageDead(_4);
65+
+ nop;
66+
+ nop;
67+
+ nop;
68+
return;
69+
}
70+
}
71+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
- // MIR for `all_copy` before GVN
2+
+ // MIR for `all_copy` after GVN
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+
+ nop;
26+
_2 = ((*_1).0: i32);
27+
- StorageLive(_3);
28+
+ nop;
29+
_3 = ((*_1).1: u64);
30+
- StorageLive(_4);
31+
+ nop;
32+
_4 = ((*_1).2: [i8; 3]);
33+
StorageLive(_5);
34+
_5 = _2;
35+
StorageLive(_6);
36+
_6 = _3;
37+
StorageLive(_7);
38+
_7 = _4;
39+
- _0 = AllCopy { a: move _5, b: move _6, c: move _7 };
40+
+ _0 = (*_1);
41+
StorageDead(_7);
42+
StorageDead(_6);
43+
StorageDead(_5);
44+
- StorageDead(_4);
45+
- StorageDead(_3);
46+
- StorageDead(_2);
47+
+ nop;
48+
+ nop;
49+
+ nop;
50+
return;
51+
}
52+
}
53+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
- // MIR for `all_copy_different_type` before GVN
2+
+ // MIR for `all_copy_different_type` after GVN
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+
+ nop;
26+
_2 = ((*_1).0: i32);
27+
- StorageLive(_3);
28+
+ nop;
29+
_3 = ((*_1).1: u64);
30+
- StorageLive(_4);
31+
+ nop;
32+
_4 = ((*_1).2: [i8; 3]);
33+
StorageLive(_5);
34+
_5 = _2;
35+
StorageLive(_6);
36+
_6 = _3;
37+
StorageLive(_7);
38+
_7 = _4;
39+
- _0 = AllCopy2 { a: move _5, b: move _6, c: move _7 };
40+
+ _0 = AllCopy2 { a: _2, b: _3, c: _4 };
41+
StorageDead(_7);
42+
StorageDead(_6);
43+
StorageDead(_5);
44+
- StorageDead(_4);
45+
- StorageDead(_3);
46+
- StorageDead(_2);
47+
+ nop;
48+
+ nop;
49+
+ nop;
50+
return;
51+
}
52+
}
53+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
- // MIR for `all_copy_has_changed` before GVN
2+
+ // MIR for `all_copy_has_changed` after GVN
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+
+ nop;
26+
_2 = ((*_1).0: i32);
27+
- StorageLive(_3);
28+
+ nop;
29+
_3 = ((*_1).1: u64);
30+
- StorageLive(_4);
31+
+ nop;
32+
_4 = ((*_1).2: [i8; 3]);
33+
((*_1).0: i32) = const 1_i32;
34+
StorageLive(_5);
35+
_5 = _2;
36+
StorageLive(_6);
37+
_6 = _3;
38+
StorageLive(_7);
39+
_7 = _4;
40+
- _0 = AllCopy { a: move _5, b: move _6, c: move _7 };
41+
+ _0 = AllCopy { a: _2, b: _3, c: _4 };
42+
StorageDead(_7);
43+
StorageDead(_6);
44+
StorageDead(_5);
45+
- StorageDead(_4);
46+
- StorageDead(_3);
47+
- StorageDead(_2);
48+
+ nop;
49+
+ nop;
50+
+ nop;
51+
return;
52+
}
53+
}
54+

0 commit comments

Comments
 (0)