Skip to content

Commit

Permalink
Auto merge of rust-lang#128299 - DianQK:clone-copy, r=<try>
Browse files Browse the repository at this point in the history
Simplify the canonical clone method and the copy-like forms to copy

Fixes rust-lang#128081.

r? `@cjgillot`
  • Loading branch information
bors committed Jul 31, 2024
2 parents e552c16 + b52bb11 commit 197f924
Show file tree
Hide file tree
Showing 17 changed files with 900 additions and 8 deletions.
60 changes: 57 additions & 3 deletions compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -863,9 +863,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
rvalue: &mut Rvalue<'tcx>,
location: Location,
) -> Option<VnIndex> {
let tcx = self.tcx;
let rvalue_ty = rvalue.ty(self.local_decls, tcx);
let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() };

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

if is_zst {
let ty = rvalue.ty(self.local_decls, tcx);
return self.insert_constant(Const::zero_sized(ty));
return self.insert_constant(Const::zero_sized(rvalue_ty));
}
}

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

if let AggregateTy::Def(_, _) = &mut ty
&& !fields.is_empty()
{
let copy_from = if let Value::Projection(first_pointer, ProjectionElem::Field(_, _)) =
*self.get(fields[0])
{
let mut copy_from_value = first_pointer;
while let Value::Projection(pointer, ProjectionElem::Deref) =
*self.get(copy_from_value)
{
copy_from_value = pointer;
}
if let Some(local) = self.try_as_local(copy_from_value, location) {
let from_ty = self.local_decls[local].ty;
if rvalue_ty == from_ty {
Some((first_pointer, local, false))
} else if Some(rvalue_ty) == from_ty.builtin_deref(false) {
Some((first_pointer, local, true))
} else {
None
}
} else {
None
}
} else {
None
};
if let Some((first_pointer, local, need_deref)) = copy_from {
// All fields must correspond one-to-one and come from the same aggregate value.
if std::iter::zip(field_ops.iter_enumerated(), fields.iter()).all(
|((index, _), &v)| {
if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) =
*self.get(v)
&& first_pointer == pointer
&& from_index == index
{
return true;
}
false
},
) {
*rvalue = Rvalue::Use(Operand::Copy(Place {
local,
projection: tcx.mk_place_elems(if need_deref {
&[ProjectionElem::Deref]
} else {
&[]
}),
}));
return Some(first_pointer);
}
}
}

if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty {
let mut was_updated = false;

Expand Down
40 changes: 40 additions & 0 deletions tests/codegen/clone_as_copy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//@ revisions: DEBUGINFO NODEBUGINFO
//@ compile-flags: -O -Cno-prepopulate-passes
//@ [DEBUGINFO] compile-flags: -Cdebuginfo=full

// From https://github.com/rust-lang/rust/issues/128081.
// Ensure that we only generate a memcpy instruction.

#![crate_type = "lib"]

#[derive(Clone)]
struct SubCloneAndCopy {
v1: u32,
v2: u32,
}

#[derive(Clone)]
struct CloneOnly {
v1: u8,
v2: u8,
v3: u8,
v4: u8,
v5: u8,
v6: u8,
v7: u8,
v8: u8,
v9: u8,
v_sub: SubCloneAndCopy,
v_large: [u8; 256],
}

// CHECK-LABEL: define {{.*}}@clone_only(
#[no_mangle]
pub fn clone_only(v: &CloneOnly) -> CloneOnly {
// CHECK-NOT: call {{.*}}clone
// CHECK-NOT: store i8
// CHECK-NOT: store i32
// CHECK: call void @llvm.memcpy
// CHECK-NEXT: ret void
v.clone()
}
17 changes: 17 additions & 0 deletions tests/mir-opt/gvn_clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//@ test-mir-pass: GVN
//@ compile-flags: -Zmir-enable-passes=+InstSimplify-before-inline

// Check if we have transformed the default clone to copy in the specific pipeline.

// EMIT_MIR gvn_clone.{impl#0}-clone.GVN.diff

// CHECK-LABEL: ::clone(
// CHECK-NOT: = AllCopy { {{.*}} };
// CHECK: _0 = (*_1);
// CHECK: return;
#[derive(Clone)]
struct AllCopy {
a: i32,
b: u64,
c: [i8; 3],
}
71 changes: 71 additions & 0 deletions tests/mir-opt/gvn_clone.{impl#0}-clone.GVN.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
- // MIR for `<impl at $DIR/gvn_clone.rs:12:10: 12:15>::clone` before GVN
+ // MIR for `<impl at $DIR/gvn_clone.rs:12:10: 12:15>::clone` after GVN

fn <impl at $DIR/gvn_clone.rs:12:10: 12:15>::clone(_1: &AllCopy) -> AllCopy {
debug self => _1;
let mut _0: AllCopy;
let mut _2: i32;
let mut _3: &i32;
let _4: &i32;
let mut _5: u64;
let mut _6: &u64;
let _7: &u64;
let mut _8: [i8; 3];
let mut _9: &[i8; 3];
let _10: &[i8; 3];

bb0: {
StorageLive(_2);
StorageLive(_3);
- StorageLive(_4);
+ nop;
_4 = &((*_1).0: i32);
_3 = _4;
- _2 = (*_3);
+ _2 = ((*_1).0: i32);
goto -> bb1;
}

bb1: {
StorageDead(_3);
StorageLive(_5);
StorageLive(_6);
- StorageLive(_7);
+ nop;
_7 = &((*_1).1: u64);
_6 = _7;
- _5 = (*_6);
+ _5 = ((*_1).1: u64);
goto -> bb2;
}

bb2: {
StorageDead(_6);
StorageLive(_8);
StorageLive(_9);
- StorageLive(_10);
+ nop;
_10 = &((*_1).2: [i8; 3]);
_9 = _10;
- _8 = (*_9);
+ _8 = ((*_1).2: [i8; 3]);
goto -> bb3;
}

bb3: {
StorageDead(_9);
- _0 = AllCopy { a: move _2, b: move _5, c: move _8 };
+ _0 = (*_1);
StorageDead(_8);
StorageDead(_5);
StorageDead(_2);
- StorageDead(_10);
- StorageDead(_7);
- StorageDead(_4);
+ nop;
+ nop;
+ nop;
return;
}
}

53 changes: 53 additions & 0 deletions tests/mir-opt/gvn_copy_aggregate.all_copy.GVN.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
- // MIR for `all_copy` before GVN
+ // MIR for `all_copy` after GVN

fn all_copy(_1: &AllCopy) -> AllCopy {
debug v => _1;
let mut _0: AllCopy;
let _2: i32;
let mut _5: i32;
let mut _6: u64;
let mut _7: [i8; 3];
scope 1 {
debug a => _2;
let _3: u64;
scope 2 {
debug b => _3;
let _4: [i8; 3];
scope 3 {
debug c => _4;
}
}
}

bb0: {
- StorageLive(_2);
+ nop;
_2 = ((*_1).0: i32);
- StorageLive(_3);
+ nop;
_3 = ((*_1).1: u64);
- StorageLive(_4);
+ nop;
_4 = ((*_1).2: [i8; 3]);
StorageLive(_5);
_5 = _2;
StorageLive(_6);
_6 = _3;
StorageLive(_7);
_7 = _4;
- _0 = AllCopy { a: move _5, b: move _6, c: move _7 };
+ _0 = (*_1);
StorageDead(_7);
StorageDead(_6);
StorageDead(_5);
- StorageDead(_4);
- StorageDead(_3);
- StorageDead(_2);
+ nop;
+ nop;
+ nop;
return;
}
}

53 changes: 53 additions & 0 deletions tests/mir-opt/gvn_copy_aggregate.all_copy_different_type.GVN.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
- // MIR for `all_copy_different_type` before GVN
+ // MIR for `all_copy_different_type` after GVN

fn all_copy_different_type(_1: &AllCopy) -> AllCopy2 {
debug v => _1;
let mut _0: AllCopy2;
let _2: i32;
let mut _5: i32;
let mut _6: u64;
let mut _7: [i8; 3];
scope 1 {
debug a => _2;
let _3: u64;
scope 2 {
debug b => _3;
let _4: [i8; 3];
scope 3 {
debug c => _4;
}
}
}

bb0: {
- StorageLive(_2);
+ nop;
_2 = ((*_1).0: i32);
- StorageLive(_3);
+ nop;
_3 = ((*_1).1: u64);
- StorageLive(_4);
+ nop;
_4 = ((*_1).2: [i8; 3]);
StorageLive(_5);
_5 = _2;
StorageLive(_6);
_6 = _3;
StorageLive(_7);
_7 = _4;
- _0 = AllCopy2 { a: move _5, b: move _6, c: move _7 };
+ _0 = AllCopy2 { a: _2, b: _3, c: _4 };
StorageDead(_7);
StorageDead(_6);
StorageDead(_5);
- StorageDead(_4);
- StorageDead(_3);
- StorageDead(_2);
+ nop;
+ nop;
+ nop;
return;
}
}

54 changes: 54 additions & 0 deletions tests/mir-opt/gvn_copy_aggregate.all_copy_has_changed.GVN.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
- // MIR for `all_copy_has_changed` before GVN
+ // MIR for `all_copy_has_changed` after GVN

fn all_copy_has_changed(_1: &mut AllCopy) -> AllCopy {
debug v => _1;
let mut _0: AllCopy;
let _2: i32;
let mut _5: i32;
let mut _6: u64;
let mut _7: [i8; 3];
scope 1 {
debug a => _2;
let _3: u64;
scope 2 {
debug b => _3;
let _4: [i8; 3];
scope 3 {
debug c => _4;
}
}
}

bb0: {
- StorageLive(_2);
+ nop;
_2 = ((*_1).0: i32);
- StorageLive(_3);
+ nop;
_3 = ((*_1).1: u64);
- StorageLive(_4);
+ nop;
_4 = ((*_1).2: [i8; 3]);
((*_1).0: i32) = const 1_i32;
StorageLive(_5);
_5 = _2;
StorageLive(_6);
_6 = _3;
StorageLive(_7);
_7 = _4;
- _0 = AllCopy { a: move _5, b: move _6, c: move _7 };
+ _0 = AllCopy { a: _2, b: _3, c: _4 };
StorageDead(_7);
StorageDead(_6);
StorageDead(_5);
- StorageDead(_4);
- StorageDead(_3);
- StorageDead(_2);
+ nop;
+ nop;
+ nop;
return;
}
}

Loading

0 comments on commit 197f924

Please sign in to comment.