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 Aug 2, 2024
2 parents 5367673 + 3c91854 commit c7f03c6
Show file tree
Hide file tree
Showing 23 changed files with 1,454 additions and 14 deletions.
81 changes: 78 additions & 3 deletions compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -876,14 +876,38 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
None
}

fn try_as_place_elem(
&mut self,
proj: ProjectionElem<VnIndex, Ty<'tcx>>,
loc: Location,
) -> Option<PlaceElem<'tcx>> {
Some(match proj {
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Field(idx, ty) => ProjectionElem::Field(idx, ty),
ProjectionElem::Index(idx) => {
return self.try_as_local(idx, loc).map(|l| ProjectionElem::Index(l));
}
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
ProjectionElem::ConstantIndex { offset, min_length, from_end }
}
ProjectionElem::Subslice { from, to, from_end } => {
ProjectionElem::Subslice { from, to, from_end }
}
ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx),
ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx),
})
}

fn simplify_aggregate(
&mut self,
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 @@ -898,8 +922,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 @@ -934,6 +957,58 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
.collect();
let mut fields = fields?;

if let AggregateTy::Def(_, _) = &mut ty
&& !fields.is_empty()
{
// All fields must correspond one-to-one and come from the same aggregate value.
if let Value::Projection(copy_from_value, _) = *self.get(fields[0])
&& fields.iter().enumerate().all(|(index, &v)| {
if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) =
*self.get(v)
&& copy_from_value == pointer
&& from_index.index() == index
{
return true;
}
false
})
{
let mut projection = SmallVec::<[PlaceElem<'tcx>; 1]>::new();
let mut copy_from_local_value = copy_from_value;
loop {
if let Some(local) = self.try_as_local(copy_from_local_value, location) {
projection.reverse();
if let Some(ProjectionElem::Downcast(_, _)) = projection.last() {
projection.pop();
}
let place =
Place { local, projection: tcx.mk_place_elems(projection.as_slice()) };
if rvalue_ty == place.ty(self.local_decls, tcx).ty {
self.reused_locals.insert(local);
*rvalue = Rvalue::Use(Operand::Copy(place));
return Some(copy_from_value);
}
break;
} else if let Value::Projection(pointer, proj) =
*self.get(copy_from_local_value)
&& let Some(proj) = self.try_as_place_elem(proj, location)
{
// The copied variant must be identical.
if let ProjectionElem::Downcast(_, read_variant) = proj
&& projection.is_empty()
&& variant_index != read_variant
{
break;
}
projection.push(proj);
copy_from_local_value = pointer;
} else {
break;
}
}
}
}

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()
}
5 changes: 3 additions & 2 deletions tests/codegen/enum/unreachable_enum_default_branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ pub fn implicit_match(x: Int) -> bool {
(x >= A && x <= B) || x == C
}

// Broken :(
// The code is from https://github.com/rust-lang/rust/issues/110097.
// We expect it to generate the same optimized code as a full match.
// CHECK-LABEL: @if_let(
// CHECK-NEXT: start:
// CHECK-NEXT: insertvalue
// CHECK: start:
// CHECK: insertvalue
// CHECK-NEXT: insertvalue
// CHECK-NEXT: ret
#[no_mangle]
Expand Down
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;
}
}

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

fn all_copy_2(_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];
let mut _8: &AllCopy;
let mut _9: &AllCopy;
let mut _10: &AllCopy;
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);
- _8 = deref_copy (*_1);
+ nop;
+ _8 = (*_1);
_2 = ((*_8).0: i32);
- StorageLive(_3);
- _9 = deref_copy (*_1);
- _3 = ((*_9).1: u64);
- StorageLive(_4);
- _10 = deref_copy (*_1);
- _4 = ((*_10).2: [i8; 3]);
+ nop;
+ _9 = _8;
+ _3 = ((*_8).1: u64);
+ nop;
+ _10 = _8;
+ _4 = ((*_8).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 = (*_8);
StorageDead(_7);
StorageDead(_6);
StorageDead(_5);
- StorageDead(_4);
- StorageDead(_3);
- StorageDead(_2);
+ nop;
+ nop;
+ nop;
return;
}
}

Loading

0 comments on commit c7f03c6

Please sign in to comment.