Skip to content

Commit 3c91854

Browse files
committed
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); ```
1 parent 5367673 commit 3c91854

23 files changed

+1454
-14
lines changed

compiler/rustc_mir_transform/src/gvn.rs

+78-3
Original file line numberDiff line numberDiff line change
@@ -876,14 +876,38 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
876876
None
877877
}
878878

879+
fn try_as_place_elem(
880+
&mut self,
881+
proj: ProjectionElem<VnIndex, Ty<'tcx>>,
882+
loc: Location,
883+
) -> Option<PlaceElem<'tcx>> {
884+
Some(match proj {
885+
ProjectionElem::Deref => ProjectionElem::Deref,
886+
ProjectionElem::Field(idx, ty) => ProjectionElem::Field(idx, ty),
887+
ProjectionElem::Index(idx) => {
888+
return self.try_as_local(idx, loc).map(|l| ProjectionElem::Index(l));
889+
}
890+
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
891+
ProjectionElem::ConstantIndex { offset, min_length, from_end }
892+
}
893+
ProjectionElem::Subslice { from, to, from_end } => {
894+
ProjectionElem::Subslice { from, to, from_end }
895+
}
896+
ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
897+
ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx),
898+
ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx),
899+
})
900+
}
901+
879902
fn simplify_aggregate(
880903
&mut self,
881904
rvalue: &mut Rvalue<'tcx>,
882905
location: Location,
883906
) -> Option<VnIndex> {
907+
let tcx = self.tcx;
908+
let rvalue_ty = rvalue.ty(self.local_decls, tcx);
884909
let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() };
885910

886-
let tcx = self.tcx;
887911
if field_ops.is_empty() {
888912
let is_zst = match *kind {
889913
AggregateKind::Array(..)
@@ -898,8 +922,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
898922
};
899923

900924
if is_zst {
901-
let ty = rvalue.ty(self.local_decls, tcx);
902-
return self.insert_constant(Const::zero_sized(ty));
925+
return self.insert_constant(Const::zero_sized(rvalue_ty));
903926
}
904927
}
905928

@@ -934,6 +957,58 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
934957
.collect();
935958
let mut fields = fields?;
936959

960+
if let AggregateTy::Def(_, _) = &mut ty
961+
&& !fields.is_empty()
962+
{
963+
// All fields must correspond one-to-one and come from the same aggregate value.
964+
if let Value::Projection(copy_from_value, _) = *self.get(fields[0])
965+
&& fields.iter().enumerate().all(|(index, &v)| {
966+
if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) =
967+
*self.get(v)
968+
&& copy_from_value == pointer
969+
&& from_index.index() == index
970+
{
971+
return true;
972+
}
973+
false
974+
})
975+
{
976+
let mut projection = SmallVec::<[PlaceElem<'tcx>; 1]>::new();
977+
let mut copy_from_local_value = copy_from_value;
978+
loop {
979+
if let Some(local) = self.try_as_local(copy_from_local_value, location) {
980+
projection.reverse();
981+
if let Some(ProjectionElem::Downcast(_, _)) = projection.last() {
982+
projection.pop();
983+
}
984+
let place =
985+
Place { local, projection: tcx.mk_place_elems(projection.as_slice()) };
986+
if rvalue_ty == place.ty(self.local_decls, tcx).ty {
987+
self.reused_locals.insert(local);
988+
*rvalue = Rvalue::Use(Operand::Copy(place));
989+
return Some(copy_from_value);
990+
}
991+
break;
992+
} else if let Value::Projection(pointer, proj) =
993+
*self.get(copy_from_local_value)
994+
&& let Some(proj) = self.try_as_place_elem(proj, location)
995+
{
996+
// The copied variant must be identical.
997+
if let ProjectionElem::Downcast(_, read_variant) = proj
998+
&& projection.is_empty()
999+
&& variant_index != read_variant
1000+
{
1001+
break;
1002+
}
1003+
projection.push(proj);
1004+
copy_from_local_value = pointer;
1005+
} else {
1006+
break;
1007+
}
1008+
}
1009+
}
1010+
}
1011+
9371012
if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty {
9381013
let mut was_updated = false;
9391014

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/codegen/enum/unreachable_enum_default_branch.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ pub fn implicit_match(x: Int) -> bool {
2525
(x >= A && x <= B) || x == C
2626
}
2727

28+
// Broken :(
2829
// The code is from https://github.com/rust-lang/rust/issues/110097.
2930
// We expect it to generate the same optimized code as a full match.
3031
// CHECK-LABEL: @if_let(
31-
// CHECK-NEXT: start:
32-
// CHECK-NEXT: insertvalue
32+
// CHECK: start:
33+
// CHECK: insertvalue
3334
// CHECK-NEXT: insertvalue
3435
// CHECK-NEXT: ret
3536
#[no_mangle]

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,64 @@
1+
- // MIR for `all_copy_2` before GVN
2+
+ // MIR for `all_copy_2` after GVN
3+
4+
fn all_copy_2(_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+
let mut _8: &AllCopy;
12+
let mut _9: &AllCopy;
13+
let mut _10: &AllCopy;
14+
scope 1 {
15+
debug a => _2;
16+
let _3: u64;
17+
scope 2 {
18+
debug b => _3;
19+
let _4: [i8; 3];
20+
scope 3 {
21+
debug c => _4;
22+
}
23+
}
24+
}
25+
26+
bb0: {
27+
- StorageLive(_2);
28+
- _8 = deref_copy (*_1);
29+
+ nop;
30+
+ _8 = (*_1);
31+
_2 = ((*_8).0: i32);
32+
- StorageLive(_3);
33+
- _9 = deref_copy (*_1);
34+
- _3 = ((*_9).1: u64);
35+
- StorageLive(_4);
36+
- _10 = deref_copy (*_1);
37+
- _4 = ((*_10).2: [i8; 3]);
38+
+ nop;
39+
+ _9 = _8;
40+
+ _3 = ((*_8).1: u64);
41+
+ nop;
42+
+ _10 = _8;
43+
+ _4 = ((*_8).2: [i8; 3]);
44+
StorageLive(_5);
45+
_5 = _2;
46+
StorageLive(_6);
47+
_6 = _3;
48+
StorageLive(_7);
49+
_7 = _4;
50+
- _0 = AllCopy { a: move _5, b: move _6, c: move _7 };
51+
+ _0 = (*_8);
52+
StorageDead(_7);
53+
StorageDead(_6);
54+
StorageDead(_5);
55+
- StorageDead(_4);
56+
- StorageDead(_3);
57+
- StorageDead(_2);
58+
+ nop;
59+
+ nop;
60+
+ nop;
61+
return;
62+
}
63+
}
64+

0 commit comments

Comments
 (0)