Skip to content

Commit fc033fb

Browse files
committed
Auto merge of rust-lang#128299 - DianQK:clone-copy, r=cjgillot
Simplify the canonical clone method and the copy-like forms to copy Fixes rust-lang#128081. r? `@cjgillot`
2 parents f167efa + b25a4b9 commit fc033fb

26 files changed

+1628
-32
lines changed

compiler/rustc_mir_transform/src/gvn.rs

+97-1
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,95 @@ 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+
let Some(local) = self.try_as_local(idx, loc) else {
889+
return None;
890+
};
891+
self.reused_locals.insert(local);
892+
ProjectionElem::Index(local)
893+
}
894+
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
895+
ProjectionElem::ConstantIndex { offset, min_length, from_end }
896+
}
897+
ProjectionElem::Subslice { from, to, from_end } => {
898+
ProjectionElem::Subslice { from, to, from_end }
899+
}
900+
ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
901+
ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx),
902+
ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx),
903+
})
904+
}
905+
906+
fn simplify_aggregate_to_copy(
907+
&mut self,
908+
rvalue: &mut Rvalue<'tcx>,
909+
location: Location,
910+
fields: &[VnIndex],
911+
variant_index: VariantIdx,
912+
) -> Option<VnIndex> {
913+
let Some(&first_field) = fields.first() else {
914+
return None;
915+
};
916+
let Value::Projection(copy_from_value, _) = *self.get(first_field) else {
917+
return None;
918+
};
919+
// All fields must correspond one-to-one and come from the same aggregate value.
920+
if fields.iter().enumerate().any(|(index, &v)| {
921+
if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = *self.get(v)
922+
&& copy_from_value == pointer
923+
&& from_index.index() == index
924+
{
925+
return false;
926+
}
927+
true
928+
}) {
929+
return None;
930+
}
931+
932+
let mut copy_from_local_value = copy_from_value;
933+
if let Value::Projection(pointer, proj) = *self.get(copy_from_value)
934+
&& let ProjectionElem::Downcast(_, read_variant) = proj
935+
{
936+
if variant_index == read_variant {
937+
// When copying a variant, there is no need to downcast.
938+
copy_from_local_value = pointer;
939+
} else {
940+
// The copied variant must be identical.
941+
return None;
942+
}
943+
}
944+
945+
let tcx = self.tcx;
946+
let mut projection = SmallVec::<[PlaceElem<'tcx>; 1]>::new();
947+
loop {
948+
if let Some(local) = self.try_as_local(copy_from_local_value, location) {
949+
projection.reverse();
950+
let place = Place { local, projection: tcx.mk_place_elems(projection.as_slice()) };
951+
if rvalue.ty(self.local_decls, tcx) == place.ty(self.local_decls, tcx).ty {
952+
self.reused_locals.insert(local);
953+
*rvalue = Rvalue::Use(Operand::Copy(place));
954+
return Some(copy_from_value);
955+
}
956+
return None;
957+
} else if let Value::Projection(pointer, proj) = *self.get(copy_from_local_value)
958+
&& let Some(proj) = self.try_as_place_elem(proj, location)
959+
{
960+
projection.push(proj);
961+
copy_from_local_value = pointer;
962+
} else {
963+
return None;
964+
}
965+
}
966+
}
967+
879968
fn simplify_aggregate(
880969
&mut self,
881970
rvalue: &mut Rvalue<'tcx>,
@@ -972,6 +1061,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
9721061
}
9731062
}
9741063

1064+
if let AggregateTy::Def(_, _) = ty
1065+
&& let Some(value) =
1066+
self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index)
1067+
{
1068+
return Some(value);
1069+
}
1070+
9751071
Some(self.insert(Value::Aggregate(ty, variant_index, fields)))
9761072
}
9771073

@@ -1485,7 +1581,7 @@ impl<'tcx> VnState<'_, 'tcx> {
14851581
}
14861582

14871583
/// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
1488-
/// return it.
1584+
/// return it. If you used this local, add it to `reused_locals` to remove storage statements.
14891585
fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option<Local> {
14901586
let other = self.rev_locals.get(index)?;
14911587
other

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ pub fn implicit_match(x: Int) -> bool {
2828
// The code is from https://github.com/rust-lang/rust/issues/110097.
2929
// We expect it to generate the same optimized code as a full match.
3030
// CHECK-LABEL: @if_let(
31-
// CHECK-NEXT: start:
31+
// CHECK: start:
32+
// CHECK-NOT: zext
33+
// CHECK: select
3234
// CHECK-NEXT: insertvalue
3335
// CHECK-NEXT: insertvalue
3436
// CHECK-NEXT: ret
3537
#[no_mangle]
3638
pub fn if_let(val: Result<i32, ()>) -> Result<i32, ()> {
37-
if let Ok(x) = val { Ok(x) } else { Err(()) }
39+
if let Ok(x) = val { Ok(x * 2) } else { Err(()) }
3840
}

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 = copy (*_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 = copy _4;
24+
- _2 = copy (*_3);
25+
+ _2 = copy ((*_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 = copy _7;
37+
- _5 = copy (*_6);
38+
+ _5 = copy ((*_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 = copy _10;
50+
- _8 = copy (*_9);
51+
+ _8 = copy ((*_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 = copy (*_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 = copy ((*_1).0: i32);
27+
- StorageLive(_3);
28+
+ nop;
29+
_3 = copy ((*_1).1: u64);
30+
- StorageLive(_4);
31+
+ nop;
32+
_4 = copy ((*_1).2: [i8; 3]);
33+
StorageLive(_5);
34+
_5 = copy _2;
35+
StorageLive(_6);
36+
_6 = copy _3;
37+
StorageLive(_7);
38+
_7 = copy _4;
39+
- _0 = AllCopy { a: move _5, b: move _6, c: move _7 };
40+
+ _0 = copy (*_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 = copy (*_1);
31+
_2 = copy ((*_8).0: i32);
32+
- StorageLive(_3);
33+
- _9 = deref_copy (*_1);
34+
- _3 = copy ((*_9).1: u64);
35+
- StorageLive(_4);
36+
- _10 = deref_copy (*_1);
37+
- _4 = copy ((*_10).2: [i8; 3]);
38+
+ nop;
39+
+ _9 = copy _8;
40+
+ _3 = copy ((*_8).1: u64);
41+
+ nop;
42+
+ _10 = copy _8;
43+
+ _4 = copy ((*_8).2: [i8; 3]);
44+
StorageLive(_5);
45+
_5 = copy _2;
46+
StorageLive(_6);
47+
_6 = copy _3;
48+
StorageLive(_7);
49+
_7 = copy _4;
50+
- _0 = AllCopy { a: move _5, b: move _6, c: move _7 };
51+
+ _0 = copy (*_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)