Skip to content

Commit 39db211

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. 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

+10
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

+91-1
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

+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/instsimplify/clone.rs

+17
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+
}
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+
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+
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+
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+

0 commit comments

Comments
 (0)