Auto merge of #150485 - dianqk:gvn-ssa-borrow, r=cjgillot
GVN: Only propagate borrows from SSA locals Fixes https://github.com/rust-lang/rust/issues/141313. This is a more principled fix than https://github.com/rust-lang/rust/pull/147886. Using a reference that is not a borrowing of an SSA local at a new location may be UB. The PR has two major changes. The first one, when introducing a new dereference at a new location, is that the reference must point to an SSA local or be an immutable argument. `dereference_address` has handled SSA locals. The second one, if we cannot guard to the reference point to an SSA local in `visit_assign`, we have to rewrite the value to opaque. This avoids unifying the following dereferences that also are references: ```rust let b: &T = *a; // ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime. // Unifying them will extend the lifetime of `b`. let c: &T = *a; ``` See also https://github.com/rust-lang/rust/issues/130853. This still allows unifying non-reference dereferences: ```rust let a: &T = ...; let b: T = *a; // ... a is NOT allowed to be modified. let c: T = *a; ``` r? @cjgillot
This commit is contained in:
commit
13c38730d9
33 changed files with 910 additions and 354 deletions
|
|
@ -710,28 +710,6 @@ impl<'tcx> TerminatorKind<'tcx> {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the terminator can write to memory.
|
||||
pub fn can_write_to_memory(&self) -> bool {
|
||||
match self {
|
||||
TerminatorKind::Goto { .. }
|
||||
| TerminatorKind::SwitchInt { .. }
|
||||
| TerminatorKind::UnwindResume
|
||||
| TerminatorKind::UnwindTerminate(_)
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Assert { .. }
|
||||
| TerminatorKind::CoroutineDrop
|
||||
| TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::FalseUnwind { .. }
|
||||
| TerminatorKind::Unreachable => false,
|
||||
TerminatorKind::Call { .. }
|
||||
| TerminatorKind::Drop { .. }
|
||||
| TerminatorKind::TailCall { .. }
|
||||
// Yield writes to the resume_arg place.
|
||||
| TerminatorKind::Yield { .. }
|
||||
| TerminatorKind::InlineAsm { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
|
|||
|
|
@ -129,24 +129,18 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
|
|||
let ssa = SsaLocals::new(tcx, body, typing_env);
|
||||
// Clone dominators because we need them while mutating the body.
|
||||
let dominators = body.basic_blocks.dominators().clone();
|
||||
let maybe_loop_headers = loops::maybe_loop_headers(body);
|
||||
|
||||
let arena = DroplessArena::default();
|
||||
let mut state =
|
||||
VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena);
|
||||
|
||||
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
|
||||
let opaque = state.new_opaque(body.local_decls[local].ty);
|
||||
let opaque = state.new_argument(body.local_decls[local].ty);
|
||||
state.assign(local, opaque);
|
||||
}
|
||||
|
||||
let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
|
||||
for bb in reverse_postorder {
|
||||
// N.B. With loops, reverse postorder cannot produce a valid topological order.
|
||||
// A statement or terminator from inside the loop, that is not processed yet, may have performed an indirect write.
|
||||
if maybe_loop_headers.contains(bb) {
|
||||
state.invalidate_derefs();
|
||||
}
|
||||
let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb];
|
||||
state.visit_basic_block_data(bb, data);
|
||||
}
|
||||
|
|
@ -204,8 +198,9 @@ enum AddressBase {
|
|||
enum Value<'a, 'tcx> {
|
||||
// Root values.
|
||||
/// Used to represent values we know nothing about.
|
||||
/// The `usize` is a counter incremented by `new_opaque`.
|
||||
Opaque(VnOpaque),
|
||||
/// The value is a argument.
|
||||
Argument(VnOpaque),
|
||||
/// Evaluated or unevaluated constant value.
|
||||
Constant {
|
||||
value: Const<'tcx>,
|
||||
|
|
@ -290,7 +285,7 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> {
|
|||
let value = value(VnOpaque);
|
||||
|
||||
debug_assert!(match value {
|
||||
Value::Opaque(_) | Value::Address { .. } => true,
|
||||
Value::Opaque(_) | Value::Argument(_) | Value::Address { .. } => true,
|
||||
Value::Constant { disambiguator, .. } => disambiguator.is_some(),
|
||||
_ => false,
|
||||
});
|
||||
|
|
@ -350,12 +345,6 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> {
|
|||
fn ty(&self, index: VnIndex) -> Ty<'tcx> {
|
||||
self.types[index]
|
||||
}
|
||||
|
||||
/// Replace the value associated with `index` with an opaque value.
|
||||
#[inline]
|
||||
fn forget(&mut self, index: VnIndex) {
|
||||
self.values[index] = Value::Opaque(VnOpaque);
|
||||
}
|
||||
}
|
||||
|
||||
struct VnState<'body, 'a, 'tcx> {
|
||||
|
|
@ -374,8 +363,6 @@ struct VnState<'body, 'a, 'tcx> {
|
|||
/// - `Some(None)` are values for which computation has failed;
|
||||
/// - `Some(Some(op))` are successful computations.
|
||||
evaluated: IndexVec<VnIndex, Option<Option<&'a OpTy<'tcx>>>>,
|
||||
/// Cache the deref values.
|
||||
derefs: Vec<VnIndex>,
|
||||
ssa: &'body SsaLocals,
|
||||
dominators: Dominators<BasicBlock>,
|
||||
reused_locals: DenseBitSet<Local>,
|
||||
|
|
@ -408,7 +395,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
rev_locals: IndexVec::with_capacity(num_values),
|
||||
values: ValueSet::new(num_values),
|
||||
evaluated: IndexVec::with_capacity(num_values),
|
||||
derefs: Vec::new(),
|
||||
ssa,
|
||||
dominators,
|
||||
reused_locals: DenseBitSet::new_empty(local_decls.len()),
|
||||
|
|
@ -455,6 +441,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
index
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn new_argument(&mut self, ty: Ty<'tcx>) -> VnIndex {
|
||||
let index = self.insert_unique(ty, Value::Argument);
|
||||
self.evaluated[index] = Some(None);
|
||||
index
|
||||
}
|
||||
|
||||
/// Create a new `Value::Address` distinct from all the others.
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option<VnIndex> {
|
||||
|
|
@ -472,8 +465,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
// Skip the initial `Deref`.
|
||||
projection.next();
|
||||
AddressBase::Deref(base)
|
||||
} else {
|
||||
} else if self.ssa.is_ssa(place.local) {
|
||||
// Only propagate the pointer of the SSA local.
|
||||
AddressBase::Local(place.local)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
// Do not try evaluating inside `Index`, this has been done by `simplify_place_projection`.
|
||||
let projection =
|
||||
|
|
@ -541,18 +537,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
self.insert(ty, Value::Aggregate(VariantIdx::ZERO, self.arena.alloc_slice(values)))
|
||||
}
|
||||
|
||||
fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex {
|
||||
let value = self.insert(ty, Value::Projection(value, ProjectionElem::Deref));
|
||||
self.derefs.push(value);
|
||||
value
|
||||
}
|
||||
|
||||
fn invalidate_derefs(&mut self) {
|
||||
for deref in std::mem::take(&mut self.derefs) {
|
||||
self.values.forget(deref);
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn eval_to_const_inner(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
|
||||
use Value::*;
|
||||
|
|
@ -566,7 +550,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
let op = match self.get(value) {
|
||||
_ if ty.is_zst() => ImmTy::uninit(ty).into(),
|
||||
|
||||
Opaque(_) => return None,
|
||||
Opaque(_) | Argument(_) => return None,
|
||||
// Keep runtime check constants as symbolic.
|
||||
RuntimeChecks(..) => return None,
|
||||
|
||||
|
|
@ -815,10 +799,24 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
{
|
||||
return Some((projection_ty, value));
|
||||
}
|
||||
// DO NOT reason the pointer value.
|
||||
// We cannot unify two pointers that dereference same local, because they may
|
||||
// have different lifetimes.
|
||||
// ```
|
||||
// let b: &T = *a;
|
||||
// ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime.
|
||||
// Unifying them will extend the lifetime of `b`.
|
||||
// let c: &T = *a;
|
||||
// ```
|
||||
if projection_ty.ty.is_ref() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// An immutable borrow `_x` always points to the same value for the
|
||||
// lifetime of the borrow, so we can merge all instances of `*_x`.
|
||||
return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
|
||||
let deref = self
|
||||
.insert(projection_ty.ty, Value::Projection(value, ProjectionElem::Deref));
|
||||
return Some((projection_ty, deref));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -1037,7 +1035,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
let op = self.simplify_operand(op, location)?;
|
||||
Value::Repeat(op, amount)
|
||||
}
|
||||
Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location),
|
||||
Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location),
|
||||
Rvalue::Ref(_, borrow_kind, ref mut place) => {
|
||||
self.simplify_place_projection(place, location);
|
||||
return self.new_pointer(*place, AddressKind::Ref(borrow_kind));
|
||||
|
|
@ -1148,7 +1146,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
|
||||
fn simplify_aggregate(
|
||||
&mut self,
|
||||
lhs: &Place<'tcx>,
|
||||
rvalue: &mut Rvalue<'tcx>,
|
||||
location: Location,
|
||||
) -> Option<VnIndex> {
|
||||
|
|
@ -1231,12 +1228,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
}
|
||||
|
||||
if let Some(value) = self.simplify_aggregate_to_copy(ty, variant_index, &fields) {
|
||||
// Allow introducing places with non-constant offsets, as those are still better than
|
||||
// reconstructing an aggregate. But avoid creating `*a = copy (*b)`, as they might be
|
||||
// aliases resulting in overlapping assignments.
|
||||
let allow_complex_projection =
|
||||
lhs.projection[..].iter().all(PlaceElem::is_stable_offset);
|
||||
if let Some(place) = self.try_as_place(value, location, allow_complex_projection) {
|
||||
if let Some(place) = self.try_as_place(value, location, true) {
|
||||
self.reused_locals.insert(place.local);
|
||||
*rvalue = Rvalue::Use(Operand::Copy(place));
|
||||
}
|
||||
|
|
@ -1890,6 +1882,17 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
|
|||
&& (allow_complex_projection || proj.is_stable_offset())
|
||||
&& let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc)
|
||||
{
|
||||
if proj == PlaceElem::Deref {
|
||||
// We can introduce a new dereference if the source value cannot be changed in the body.
|
||||
// Dereferencing an immutable argument always gives the same value in the body.
|
||||
match self.get(pointer) {
|
||||
Value::Argument(_)
|
||||
if let Some(Mutability::Not) = self.ty(pointer).ref_mutability() => {}
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
projection.push(proj);
|
||||
index = pointer;
|
||||
} else {
|
||||
|
|
@ -1916,10 +1919,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
|
|||
|
||||
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
|
||||
self.simplify_place_projection(place, location);
|
||||
if context.is_mutating_use() && place.is_indirect() {
|
||||
// Non-local mutation maybe invalidate deref.
|
||||
self.invalidate_derefs();
|
||||
}
|
||||
self.super_place(place, context, location);
|
||||
}
|
||||
|
||||
|
|
@ -1949,11 +1948,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
if lhs.is_indirect() {
|
||||
// Non-local mutation maybe invalidate deref.
|
||||
self.invalidate_derefs();
|
||||
}
|
||||
|
||||
if let Some(local) = lhs.as_local()
|
||||
&& self.ssa.is_ssa(local)
|
||||
&& let rvalue_ty = rvalue.ty(self.local_decls, self.tcx)
|
||||
|
|
@ -1976,10 +1970,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
|
|||
self.assign(local, opaque);
|
||||
}
|
||||
}
|
||||
// Terminators that can write to memory may invalidate (nested) derefs.
|
||||
if terminator.kind.can_write_to_memory() {
|
||||
self.invalidate_derefs();
|
||||
}
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,8 +75,7 @@
|
|||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
- StorageLive(_2);
|
||||
+ nop;
|
||||
StorageLive(_2);
|
||||
StorageLive(_3);
|
||||
StorageLive(_4);
|
||||
- _4 = ();
|
||||
|
|
@ -144,12 +143,10 @@
|
|||
StorageDead(_4);
|
||||
_2 = &_3;
|
||||
_1 = &(*_2);
|
||||
- StorageDead(_2);
|
||||
StorageDead(_2);
|
||||
- StorageLive(_5);
|
||||
- _10 = copy (*_1);
|
||||
+ nop;
|
||||
+ nop;
|
||||
+ _10 = copy (*_2);
|
||||
_10 = copy (*_1);
|
||||
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
|
||||
_5 = &raw const (*_11);
|
||||
- StorageLive(_6);
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@
|
|||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
- StorageLive(_2);
|
||||
+ nop;
|
||||
StorageLive(_2);
|
||||
StorageLive(_3);
|
||||
StorageLive(_4);
|
||||
- _4 = ();
|
||||
|
|
@ -51,12 +50,10 @@
|
|||
StorageDead(_4);
|
||||
_2 = &_3;
|
||||
_1 = &(*_2);
|
||||
- StorageDead(_2);
|
||||
StorageDead(_2);
|
||||
- StorageLive(_5);
|
||||
- _10 = copy (*_1);
|
||||
+ nop;
|
||||
+ nop;
|
||||
+ _10 = copy (*_2);
|
||||
_10 = copy (*_1);
|
||||
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
|
||||
_5 = &raw const (*_11);
|
||||
- StorageLive(_6);
|
||||
|
|
|
|||
|
|
@ -75,8 +75,7 @@
|
|||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
- StorageLive(_2);
|
||||
+ nop;
|
||||
StorageLive(_2);
|
||||
StorageLive(_3);
|
||||
StorageLive(_4);
|
||||
- _4 = ();
|
||||
|
|
@ -144,12 +143,10 @@
|
|||
StorageDead(_4);
|
||||
_2 = &_3;
|
||||
_1 = &(*_2);
|
||||
- StorageDead(_2);
|
||||
StorageDead(_2);
|
||||
- StorageLive(_5);
|
||||
- _10 = copy (*_1);
|
||||
+ nop;
|
||||
+ nop;
|
||||
+ _10 = copy (*_2);
|
||||
_10 = copy (*_1);
|
||||
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
|
||||
_5 = &raw const (*_11);
|
||||
- StorageLive(_6);
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@
|
|||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
- StorageLive(_2);
|
||||
+ nop;
|
||||
StorageLive(_2);
|
||||
StorageLive(_3);
|
||||
StorageLive(_4);
|
||||
- _4 = ();
|
||||
|
|
@ -51,12 +50,10 @@
|
|||
StorageDead(_4);
|
||||
_2 = &_3;
|
||||
_1 = &(*_2);
|
||||
- StorageDead(_2);
|
||||
StorageDead(_2);
|
||||
- StorageLive(_5);
|
||||
- _10 = copy (*_1);
|
||||
+ nop;
|
||||
+ nop;
|
||||
+ _10 = copy (*_2);
|
||||
_10 = copy (*_1);
|
||||
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
|
||||
_5 = &raw const (*_11);
|
||||
- StorageLive(_6);
|
||||
|
|
|
|||
37
tests/mir-opt/gvn.dereference_reborrow.GVN.panic-abort.diff
Normal file
37
tests/mir-opt/gvn.dereference_reborrow.GVN.panic-abort.diff
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
- // MIR for `dereference_reborrow` before GVN
|
||||
+ // MIR for `dereference_reborrow` after GVN
|
||||
|
||||
fn dereference_reborrow(_1: &mut u8) -> () {
|
||||
debug mut_a => _1;
|
||||
let mut _0: ();
|
||||
let _2: &u8;
|
||||
scope 1 {
|
||||
debug a => _2;
|
||||
let _3: u8;
|
||||
scope 2 {
|
||||
debug b => _3;
|
||||
let _4: u8;
|
||||
scope 3 {
|
||||
debug c => _4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_2);
|
||||
_2 = &(*_1);
|
||||
- StorageLive(_3);
|
||||
+ nop;
|
||||
_3 = copy (*_2);
|
||||
StorageLive(_4);
|
||||
- _4 = copy (*_2);
|
||||
+ _4 = copy _3;
|
||||
_0 = const ();
|
||||
StorageDead(_4);
|
||||
- StorageDead(_3);
|
||||
+ nop;
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
37
tests/mir-opt/gvn.dereference_reborrow.GVN.panic-unwind.diff
Normal file
37
tests/mir-opt/gvn.dereference_reborrow.GVN.panic-unwind.diff
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
- // MIR for `dereference_reborrow` before GVN
|
||||
+ // MIR for `dereference_reborrow` after GVN
|
||||
|
||||
fn dereference_reborrow(_1: &mut u8) -> () {
|
||||
debug mut_a => _1;
|
||||
let mut _0: ();
|
||||
let _2: &u8;
|
||||
scope 1 {
|
||||
debug a => _2;
|
||||
let _3: u8;
|
||||
scope 2 {
|
||||
debug b => _3;
|
||||
let _4: u8;
|
||||
scope 3 {
|
||||
debug c => _4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_2);
|
||||
_2 = &(*_1);
|
||||
- StorageLive(_3);
|
||||
+ nop;
|
||||
_3 = copy (*_2);
|
||||
StorageLive(_4);
|
||||
- _4 = copy (*_2);
|
||||
+ _4 = copy _3;
|
||||
_0 = const ();
|
||||
StorageDead(_4);
|
||||
- StorageDead(_3);
|
||||
+ nop;
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,18 +107,23 @@
|
|||
StorageLive(_18);
|
||||
_18 = &(*_1);
|
||||
StorageLive(_19);
|
||||
StorageLive(_20);
|
||||
- StorageLive(_20);
|
||||
+ nop;
|
||||
_20 = copy (*_18);
|
||||
_19 = opaque::<u32>(move _20) -> [return: bb7, unwind unreachable];
|
||||
- _19 = opaque::<u32>(move _20) -> [return: bb7, unwind unreachable];
|
||||
+ _19 = opaque::<u32>(copy _20) -> [return: bb7, unwind unreachable];
|
||||
}
|
||||
|
||||
bb7: {
|
||||
StorageDead(_20);
|
||||
- StorageDead(_20);
|
||||
+ nop;
|
||||
StorageDead(_19);
|
||||
StorageLive(_21);
|
||||
StorageLive(_22);
|
||||
_22 = copy (*_18);
|
||||
_21 = opaque::<u32>(move _22) -> [return: bb8, unwind unreachable];
|
||||
- _22 = copy (*_18);
|
||||
- _21 = opaque::<u32>(move _22) -> [return: bb8, unwind unreachable];
|
||||
+ _22 = copy _20;
|
||||
+ _21 = opaque::<u32>(copy _20) -> [return: bb8, unwind unreachable];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
|
|
@ -152,18 +157,23 @@
|
|||
StorageDead(_28);
|
||||
StorageDead(_27);
|
||||
StorageLive(_29);
|
||||
StorageLive(_30);
|
||||
- StorageLive(_30);
|
||||
+ nop;
|
||||
_30 = copy ((*_3).0: u32);
|
||||
_29 = opaque::<u32>(move _30) -> [return: bb12, unwind unreachable];
|
||||
- _29 = opaque::<u32>(move _30) -> [return: bb12, unwind unreachable];
|
||||
+ _29 = opaque::<u32>(copy _30) -> [return: bb12, unwind unreachable];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
StorageDead(_30);
|
||||
- StorageDead(_30);
|
||||
+ nop;
|
||||
StorageDead(_29);
|
||||
StorageLive(_31);
|
||||
StorageLive(_32);
|
||||
_32 = copy ((*_3).0: u32);
|
||||
_31 = opaque::<u32>(move _32) -> [return: bb13, unwind unreachable];
|
||||
- _32 = copy ((*_3).0: u32);
|
||||
- _31 = opaque::<u32>(move _32) -> [return: bb13, unwind unreachable];
|
||||
+ _32 = copy _30;
|
||||
+ _31 = opaque::<u32>(copy _30) -> [return: bb13, unwind unreachable];
|
||||
}
|
||||
|
||||
bb13: {
|
||||
|
|
|
|||
|
|
@ -107,18 +107,23 @@
|
|||
StorageLive(_18);
|
||||
_18 = &(*_1);
|
||||
StorageLive(_19);
|
||||
StorageLive(_20);
|
||||
- StorageLive(_20);
|
||||
+ nop;
|
||||
_20 = copy (*_18);
|
||||
_19 = opaque::<u32>(move _20) -> [return: bb7, unwind continue];
|
||||
- _19 = opaque::<u32>(move _20) -> [return: bb7, unwind continue];
|
||||
+ _19 = opaque::<u32>(copy _20) -> [return: bb7, unwind continue];
|
||||
}
|
||||
|
||||
bb7: {
|
||||
StorageDead(_20);
|
||||
- StorageDead(_20);
|
||||
+ nop;
|
||||
StorageDead(_19);
|
||||
StorageLive(_21);
|
||||
StorageLive(_22);
|
||||
_22 = copy (*_18);
|
||||
_21 = opaque::<u32>(move _22) -> [return: bb8, unwind continue];
|
||||
- _22 = copy (*_18);
|
||||
- _21 = opaque::<u32>(move _22) -> [return: bb8, unwind continue];
|
||||
+ _22 = copy _20;
|
||||
+ _21 = opaque::<u32>(copy _20) -> [return: bb8, unwind continue];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
|
|
@ -152,18 +157,23 @@
|
|||
StorageDead(_28);
|
||||
StorageDead(_27);
|
||||
StorageLive(_29);
|
||||
StorageLive(_30);
|
||||
- StorageLive(_30);
|
||||
+ nop;
|
||||
_30 = copy ((*_3).0: u32);
|
||||
_29 = opaque::<u32>(move _30) -> [return: bb12, unwind continue];
|
||||
- _29 = opaque::<u32>(move _30) -> [return: bb12, unwind continue];
|
||||
+ _29 = opaque::<u32>(copy _30) -> [return: bb12, unwind continue];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
StorageDead(_30);
|
||||
- StorageDead(_30);
|
||||
+ nop;
|
||||
StorageDead(_29);
|
||||
StorageLive(_31);
|
||||
StorageLive(_32);
|
||||
_32 = copy ((*_3).0: u32);
|
||||
_31 = opaque::<u32>(move _32) -> [return: bb13, unwind continue];
|
||||
- _32 = copy ((*_3).0: u32);
|
||||
- _31 = opaque::<u32>(move _32) -> [return: bb13, unwind continue];
|
||||
+ _32 = copy _30;
|
||||
+ _31 = opaque::<u32>(copy _30) -> [return: bb13, unwind continue];
|
||||
}
|
||||
|
||||
bb13: {
|
||||
|
|
|
|||
30
tests/mir-opt/gvn.field_borrow.GVN.panic-abort.diff
Normal file
30
tests/mir-opt/gvn.field_borrow.GVN.panic-abort.diff
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
- // MIR for `field_borrow` before GVN
|
||||
+ // MIR for `field_borrow` after GVN
|
||||
|
||||
fn field_borrow(_1: &FieldBorrow<'_>) -> () {
|
||||
debug a => _1;
|
||||
let mut _0: ();
|
||||
let _2: &u8;
|
||||
scope 1 {
|
||||
debug b => _2;
|
||||
let _3: &u8;
|
||||
scope 2 {
|
||||
debug c => _3;
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
- StorageLive(_2);
|
||||
+ nop;
|
||||
_2 = copy ((*_1).0: &u8);
|
||||
StorageLive(_3);
|
||||
- _3 = copy ((*_1).0: &u8);
|
||||
+ _3 = copy _2;
|
||||
_0 = const ();
|
||||
StorageDead(_3);
|
||||
- StorageDead(_2);
|
||||
+ nop;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
30
tests/mir-opt/gvn.field_borrow.GVN.panic-unwind.diff
Normal file
30
tests/mir-opt/gvn.field_borrow.GVN.panic-unwind.diff
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
- // MIR for `field_borrow` before GVN
|
||||
+ // MIR for `field_borrow` after GVN
|
||||
|
||||
fn field_borrow(_1: &FieldBorrow<'_>) -> () {
|
||||
debug a => _1;
|
||||
let mut _0: ();
|
||||
let _2: &u8;
|
||||
scope 1 {
|
||||
debug b => _2;
|
||||
let _3: &u8;
|
||||
scope 2 {
|
||||
debug c => _3;
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
- StorageLive(_2);
|
||||
+ nop;
|
||||
_2 = copy ((*_1).0: &u8);
|
||||
StorageLive(_3);
|
||||
- _3 = copy ((*_1).0: &u8);
|
||||
+ _3 = copy _2;
|
||||
_0 = const ();
|
||||
StorageDead(_3);
|
||||
- StorageDead(_2);
|
||||
+ nop;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
51
tests/mir-opt/gvn.field_borrow_2.GVN.panic-abort.diff
Normal file
51
tests/mir-opt/gvn.field_borrow_2.GVN.panic-abort.diff
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
- // MIR for `field_borrow_2` before GVN
|
||||
+ // MIR for `field_borrow_2` after GVN
|
||||
|
||||
fn field_borrow_2(_1: &&FieldBorrow<'_>) -> () {
|
||||
debug a => _1;
|
||||
let mut _0: ();
|
||||
let _2: &FieldBorrow<'_>;
|
||||
scope 1 {
|
||||
debug b => _2;
|
||||
let _3: &u8;
|
||||
scope 2 {
|
||||
debug c => _3;
|
||||
let _4: &FieldBorrow<'_>;
|
||||
scope 3 {
|
||||
debug d => _4;
|
||||
let _5: &u8;
|
||||
scope 4 {
|
||||
debug e => _5;
|
||||
let _6: &u8;
|
||||
scope 5 {
|
||||
debug f => _6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_2);
|
||||
_2 = copy (*_1);
|
||||
StorageLive(_3);
|
||||
_3 = copy ((*_2).0: &u8);
|
||||
StorageLive(_4);
|
||||
_4 = copy (*_1);
|
||||
- StorageLive(_5);
|
||||
+ nop;
|
||||
_5 = copy ((*_4).0: &u8);
|
||||
StorageLive(_6);
|
||||
- _6 = copy ((*_4).0: &u8);
|
||||
+ _6 = copy _5;
|
||||
_0 = const ();
|
||||
StorageDead(_6);
|
||||
- StorageDead(_5);
|
||||
+ nop;
|
||||
StorageDead(_4);
|
||||
StorageDead(_3);
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
51
tests/mir-opt/gvn.field_borrow_2.GVN.panic-unwind.diff
Normal file
51
tests/mir-opt/gvn.field_borrow_2.GVN.panic-unwind.diff
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
- // MIR for `field_borrow_2` before GVN
|
||||
+ // MIR for `field_borrow_2` after GVN
|
||||
|
||||
fn field_borrow_2(_1: &&FieldBorrow<'_>) -> () {
|
||||
debug a => _1;
|
||||
let mut _0: ();
|
||||
let _2: &FieldBorrow<'_>;
|
||||
scope 1 {
|
||||
debug b => _2;
|
||||
let _3: &u8;
|
||||
scope 2 {
|
||||
debug c => _3;
|
||||
let _4: &FieldBorrow<'_>;
|
||||
scope 3 {
|
||||
debug d => _4;
|
||||
let _5: &u8;
|
||||
scope 4 {
|
||||
debug e => _5;
|
||||
let _6: &u8;
|
||||
scope 5 {
|
||||
debug f => _6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_2);
|
||||
_2 = copy (*_1);
|
||||
StorageLive(_3);
|
||||
_3 = copy ((*_2).0: &u8);
|
||||
StorageLive(_4);
|
||||
_4 = copy (*_1);
|
||||
- StorageLive(_5);
|
||||
+ nop;
|
||||
_5 = copy ((*_4).0: &u8);
|
||||
StorageLive(_6);
|
||||
- _6 = copy ((*_4).0: &u8);
|
||||
+ _6 = copy _5;
|
||||
_0 = const ();
|
||||
StorageDead(_6);
|
||||
- StorageDead(_5);
|
||||
+ nop;
|
||||
StorageDead(_4);
|
||||
StorageDead(_3);
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -8,10 +8,10 @@
|
|||
let mut _3: fn(u8) -> u8;
|
||||
let _5: ();
|
||||
let mut _6: fn(u8) -> u8;
|
||||
let mut _9: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
let mut _9: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
let _10: ();
|
||||
let mut _11: fn();
|
||||
let mut _13: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
let mut _13: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
let _14: ();
|
||||
let mut _15: fn();
|
||||
scope 1 {
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
let _4: fn(u8) -> u8;
|
||||
scope 2 {
|
||||
debug g => _4;
|
||||
let _7: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
let _7: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
scope 3 {
|
||||
debug closure => _7;
|
||||
let _8: fn();
|
||||
|
|
@ -62,16 +62,16 @@
|
|||
StorageDead(_6);
|
||||
StorageDead(_5);
|
||||
- StorageLive(_7);
|
||||
- _7 = {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
- _7 = {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
- StorageLive(_8);
|
||||
+ nop;
|
||||
+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
+ nop;
|
||||
StorageLive(_9);
|
||||
- _9 = copy _7;
|
||||
- _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
StorageDead(_9);
|
||||
StorageLive(_10);
|
||||
StorageLive(_11);
|
||||
|
|
@ -88,8 +88,8 @@
|
|||
StorageLive(_13);
|
||||
- _13 = copy _7;
|
||||
- _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
StorageDead(_13);
|
||||
StorageLive(_14);
|
||||
StorageLive(_15);
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@
|
|||
let mut _3: fn(u8) -> u8;
|
||||
let _5: ();
|
||||
let mut _6: fn(u8) -> u8;
|
||||
let mut _9: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
let mut _9: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
let _10: ();
|
||||
let mut _11: fn();
|
||||
let mut _13: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
let mut _13: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
let _14: ();
|
||||
let mut _15: fn();
|
||||
scope 1 {
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
let _4: fn(u8) -> u8;
|
||||
scope 2 {
|
||||
debug g => _4;
|
||||
let _7: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
let _7: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
scope 3 {
|
||||
debug closure => _7;
|
||||
let _8: fn();
|
||||
|
|
@ -62,16 +62,16 @@
|
|||
StorageDead(_6);
|
||||
StorageDead(_5);
|
||||
- StorageLive(_7);
|
||||
- _7 = {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
- _7 = {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
- StorageLive(_8);
|
||||
+ nop;
|
||||
+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
+ nop;
|
||||
StorageLive(_9);
|
||||
- _9 = copy _7;
|
||||
- _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
StorageDead(_9);
|
||||
StorageLive(_10);
|
||||
StorageLive(_11);
|
||||
|
|
@ -88,8 +88,8 @@
|
|||
StorageLive(_13);
|
||||
- _13 = copy _7;
|
||||
- _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
|
||||
+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21};
|
||||
+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:629:19: 629:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
|
||||
StorageDead(_13);
|
||||
StorageLive(_14);
|
||||
StorageLive(_15);
|
||||
|
|
|
|||
|
|
@ -100,14 +100,14 @@ fn subexpression_elimination(x: u64, y: u64, mut z: u64) {
|
|||
opaque((x * y) - y);
|
||||
opaque((x * y) - y);
|
||||
|
||||
// We cannot substitute through an immutable reference.
|
||||
// We can substitute through an immutable reference.
|
||||
// CHECK: [[ref:_.*]] = &_3;
|
||||
// CHECK: [[deref:_.*]] = copy (*[[ref]]);
|
||||
// CHECK: [[addref:_.*]] = Add(move [[deref]], copy _1);
|
||||
// CHECK: opaque::<u64>(move [[addref]])
|
||||
// CHECK: [[deref2:_.*]] = copy (*[[ref]]);
|
||||
// CHECK: [[addref2:_.*]] = Add(move [[deref2]], copy _1);
|
||||
// CHECK: opaque::<u64>(move [[addref2]])
|
||||
// CHECK: [[addref:_.*]] = Add(copy [[deref]], copy _1);
|
||||
// CHECK: opaque::<u64>(copy [[addref]])
|
||||
// CHECK: [[deref2:_.*]] = copy [[deref]];
|
||||
// CHECK: [[addref2:_.*]] = copy [[addref]];
|
||||
// CHECK: opaque::<u64>(copy [[addref]])
|
||||
let a = &z;
|
||||
opaque(*a + x);
|
||||
opaque(*a + x);
|
||||
|
|
@ -140,15 +140,14 @@ fn subexpression_elimination(x: u64, y: u64, mut z: u64) {
|
|||
opaque(*d + x);
|
||||
}
|
||||
|
||||
// We still cannot substitute again, and never with the earlier computations.
|
||||
// Important: `e` is not `a`!
|
||||
// CHECK: [[ref2:_.*]] = &_3;
|
||||
// CHECK: [[deref2:_.*]] = copy (*[[ref2]]);
|
||||
// CHECK: [[addref2:_.*]] = Add(move [[deref2]], copy _1);
|
||||
// CHECK: opaque::<u64>(move [[addref2]])
|
||||
// CHECK: [[deref3:_.*]] = copy (*[[ref2]]);
|
||||
// CHECK: [[addref3:_.*]] = Add(move [[deref3]], copy _1);
|
||||
// CHECK: opaque::<u64>(move [[addref3]])
|
||||
// CHECK: [[addref2:_.*]] = Add(copy [[deref2]], copy _1);
|
||||
// CHECK: opaque::<u64>(copy [[addref2]])
|
||||
// CHECK: [[deref3:_.*]] = copy [[deref2]];
|
||||
// CHECK: [[addref3:_.*]] = copy [[addref2]];
|
||||
// CHECK: opaque::<u64>(copy [[addref2]])
|
||||
let e = &z;
|
||||
opaque(*e + x);
|
||||
opaque(*e + x);
|
||||
|
|
@ -452,20 +451,21 @@ fn references(mut x: impl Sized) {
|
|||
// CHECK: opaque::<*mut impl Sized>(move [[ref8]])
|
||||
opaque(&raw mut x);
|
||||
|
||||
// FIXME: ReferencePropagation transform this pattern.
|
||||
let r = &mut x;
|
||||
let s = S(r).0; // Obfuscate `r`. Following lines should still reborrow `r`.
|
||||
// CHECK: [[ref9:_.*]] = &mut _1;
|
||||
// CHECK: [[ref10:_.*]] = &(*[[ref9]]);
|
||||
// CHECK: opaque::<&impl Sized>(move [[ref10]])
|
||||
// COM: CHECK: [[ref10:_.*]] = &(*[[ref9]]);
|
||||
// COM: CHECK: opaque::<&impl Sized>(move [[ref10]])
|
||||
opaque(&*s);
|
||||
// CHECK: [[ref11:_.*]] = &mut (*[[ref9]]);
|
||||
// CHECK: opaque::<&mut impl Sized>(move [[ref11]])
|
||||
// COM: CHECK: [[ref11:_.*]] = &mut (*[[ref9]]);
|
||||
// COM: CHECK: opaque::<&mut impl Sized>(move [[ref11]])
|
||||
opaque(&mut *s);
|
||||
// CHECK: [[ref12:_.*]] = &raw const (*[[ref9]]);
|
||||
// CHECK: opaque::<*const impl Sized>(move [[ref12]])
|
||||
// COM: CHECK: [[ref12:_.*]] = &raw const (*[[ref9]]);
|
||||
// COM: CHECK: opaque::<*const impl Sized>(move [[ref12]])
|
||||
opaque(&raw const *s);
|
||||
// CHECK: [[ref12:_.*]] = &raw mut (*[[ref9]]);
|
||||
// CHECK: opaque::<*mut impl Sized>(move [[ref12]])
|
||||
// COM: CHECK: [[ref12:_.*]] = &raw mut (*[[ref9]]);
|
||||
// COM: CHECK: opaque::<*mut impl Sized>(move [[ref12]])
|
||||
opaque(&raw mut *s);
|
||||
}
|
||||
|
||||
|
|
@ -473,17 +473,21 @@ fn dereferences(t: &mut u32, u: &impl Copy, s: &S<u32>) {
|
|||
// CHECK-LABEL: fn dereferences(
|
||||
|
||||
// Do not reuse dereferences of `&mut`.
|
||||
// CHECK: bb0:
|
||||
// CHECK: [[st1:_.*]] = copy (*_1);
|
||||
// CHECK: opaque::<u32>(move [[st1]])
|
||||
// CHECK: bb1:
|
||||
// CHECK: [[st2:_.*]] = copy (*_1);
|
||||
// CHECK: opaque::<u32>(move [[st2]])
|
||||
opaque(*t);
|
||||
opaque(*t);
|
||||
|
||||
// Do not reuse dereferences of `*const`.
|
||||
// CHECK: bb2:
|
||||
// CHECK: [[raw:_.*]] = &raw const (*_1);
|
||||
// CHECK: [[st3:_.*]] = copy (*[[raw]]);
|
||||
// CHECK: opaque::<u32>(move [[st3]])
|
||||
// CHECK: bb3:
|
||||
// CHECK: [[st4:_.*]] = copy (*[[raw]]);
|
||||
// CHECK: opaque::<u32>(move [[st4]])
|
||||
let z = &raw const *t;
|
||||
|
|
@ -491,42 +495,49 @@ fn dereferences(t: &mut u32, u: &impl Copy, s: &S<u32>) {
|
|||
unsafe { opaque(*z) };
|
||||
|
||||
// Do not reuse dereferences of `*mut`.
|
||||
// CHECK: bb4:
|
||||
// CHECK: [[ptr:_.*]] = &raw mut (*_1);
|
||||
// CHECK: [[st5:_.*]] = copy (*[[ptr]]);
|
||||
// CHECK: opaque::<u32>(move [[st5]])
|
||||
// CHECK: bb5:
|
||||
// CHECK: [[st6:_.*]] = copy (*[[ptr]]);
|
||||
// CHECK: opaque::<u32>(move [[st6]])
|
||||
let z = &raw mut *t;
|
||||
unsafe { opaque(*z) };
|
||||
unsafe { opaque(*z) };
|
||||
|
||||
// Do not reuse dereferences of `&Freeze`.
|
||||
// CHECK: bb6:
|
||||
// CHECK: [[ref:_.*]] = &(*_1);
|
||||
// CHECK: [[st7:_.*]] = copy (*[[ref]]);
|
||||
// CHECK: opaque::<u32>(move [[st7]])
|
||||
// CHECK: [[st8:_.*]] = copy (*[[ref]]);
|
||||
// CHECK: opaque::<u32>(move [[st8]])
|
||||
// CHECK: opaque::<u32>(copy [[st7]])
|
||||
// CHECK: bb7:
|
||||
// CHECK: [[st8:_.*]] = copy [[st7]];
|
||||
// CHECK: opaque::<u32>(copy [[st7]])
|
||||
let z = &*t;
|
||||
opaque(*z);
|
||||
opaque(*z);
|
||||
// Not in reborrows either.
|
||||
// CHECK: bb8:
|
||||
// CHECK: [[reborrow:_.*]] = &(*[[ref]]);
|
||||
// CHECK: opaque::<&u32>(move [[reborrow]])
|
||||
opaque(&*z);
|
||||
|
||||
// `*u` is not Freeze, so we cannot reuse.
|
||||
// CHECK: bb9:
|
||||
// CHECK: [[st8:_.*]] = copy (*_2);
|
||||
// CHECK: opaque::<impl Copy>(move [[st8]])
|
||||
// CHECK: bb10:
|
||||
// CHECK: [[st9:_.*]] = copy (*_2);
|
||||
// CHECK: opaque::<impl Copy>(move [[st9]])
|
||||
opaque(*u);
|
||||
opaque(*u);
|
||||
|
||||
// `*s` is not Copy, but `(*s).0` is, but we still cannot reuse.
|
||||
// CHECK: bb11:
|
||||
// CHECK: [[st10:_.*]] = copy ((*_3).0: u32);
|
||||
// CHECK: opaque::<u32>(move [[st10]])
|
||||
// CHECK: [[st11:_.*]] = copy ((*_3).0: u32);
|
||||
// CHECK: opaque::<u32>(move [[st11]])
|
||||
// CHECK: opaque::<u32>(copy [[st10]])
|
||||
// CHECK: bb12:
|
||||
// CHECK: [[st11:_.*]] = copy [[st10]];
|
||||
// CHECK: opaque::<u32>(copy [[st10]])
|
||||
opaque(s.0);
|
||||
opaque(s.0);
|
||||
}
|
||||
|
|
@ -1089,6 +1100,53 @@ fn dereference_indexing(array: [u8; 2], index: usize) {
|
|||
opaque(*a);
|
||||
}
|
||||
|
||||
// EMIT_MIR gvn.dereference_reborrow.GVN.diff
|
||||
fn dereference_reborrow(mut_a: &mut u8) {
|
||||
// CHECK-LABEL: fn dereference_reborrow(
|
||||
// CHECK: debug a => [[a:_.*]];
|
||||
// CHECK: debug b => [[b:_.*]];
|
||||
// CHECK: debug c => [[c:_.*]];
|
||||
// CHECK: [[a]] = &(*_1);
|
||||
// CHECK: [[b]] = copy (*[[a]]);
|
||||
// CHECK: [[c]] = copy [[b]];
|
||||
let a = &*mut_a;
|
||||
let b = *a;
|
||||
let c = *a;
|
||||
}
|
||||
|
||||
struct FieldBorrow<'a>(&'a u8);
|
||||
|
||||
// EMIT_MIR gvn.field_borrow.GVN.diff
|
||||
fn field_borrow(a: &FieldBorrow<'_>) {
|
||||
// CHECK-LABEL: fn field_borrow(
|
||||
// CHECK: debug b => [[b:_.*]];
|
||||
// CHECK: debug c => [[c:_.*]];
|
||||
// CHECK: [[b]] = copy ((*_1).0: &u8);
|
||||
// CHECK: [[c]] = copy [[b]];
|
||||
let b = a.0;
|
||||
let c = a.0;
|
||||
}
|
||||
|
||||
// EMIT_MIR gvn.field_borrow_2.GVN.diff
|
||||
fn field_borrow_2(a: &&FieldBorrow<'_>) {
|
||||
// CHECK-LABEL: fn field_borrow_2(
|
||||
// CHECK: debug b => [[b:_.*]];
|
||||
// CHECK: debug c => [[c:_.*]];
|
||||
// CHECK: debug d => [[d:_.*]];
|
||||
// CHECK: debug e => [[e:_.*]];
|
||||
// CHECK: debug f => [[f:_.*]];
|
||||
// CHECK: [[b]] = copy (*_1);
|
||||
// CHECK: [[c]] = copy ((*[[b]]).0: &u8);
|
||||
// CHECK: [[d]] = copy (*_1);
|
||||
// CHECK: [[e]] = copy ((*[[d]]).0: &u8);
|
||||
// CHECK: [[f]] = copy [[e]];
|
||||
let b = *a;
|
||||
let c = b.0;
|
||||
let d = *a;
|
||||
let e = d.0;
|
||||
let f = d.0;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: fn main(
|
||||
fn main() {
|
||||
subexpression_elimination(2, 4, 5);
|
||||
|
|
@ -1118,6 +1176,9 @@ fn main() {
|
|||
meta_of_ref_to_slice(&42);
|
||||
slice_from_raw_parts_as_ptr(&123, 456);
|
||||
dereference_indexing([129, 14], 5);
|
||||
dereference_reborrow(&mut 5);
|
||||
field_borrow(&FieldBorrow(&0));
|
||||
field_borrow(&&FieldBorrow(&0));
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
|
|
|
|||
|
|
@ -758,32 +758,39 @@
|
|||
StorageLive(_126);
|
||||
_126 = &_3;
|
||||
StorageLive(_127);
|
||||
StorageLive(_128);
|
||||
StorageLive(_129);
|
||||
- StorageLive(_128);
|
||||
- StorageLive(_129);
|
||||
+ nop;
|
||||
+ nop;
|
||||
_129 = copy (*_126);
|
||||
StorageLive(_130);
|
||||
_130 = copy _1;
|
||||
- _128 = Add(move _129, move _130);
|
||||
+ _128 = Add(move _129, copy _1);
|
||||
+ _128 = Add(copy _129, copy _1);
|
||||
StorageDead(_130);
|
||||
StorageDead(_129);
|
||||
_127 = opaque::<u64>(move _128) -> [return: bb35, unwind unreachable];
|
||||
- StorageDead(_129);
|
||||
- _127 = opaque::<u64>(move _128) -> [return: bb35, unwind unreachable];
|
||||
+ nop;
|
||||
+ _127 = opaque::<u64>(copy _128) -> [return: bb35, unwind unreachable];
|
||||
}
|
||||
|
||||
bb35: {
|
||||
StorageDead(_128);
|
||||
- StorageDead(_128);
|
||||
+ nop;
|
||||
StorageDead(_127);
|
||||
StorageLive(_131);
|
||||
StorageLive(_132);
|
||||
StorageLive(_133);
|
||||
_133 = copy (*_126);
|
||||
- _133 = copy (*_126);
|
||||
+ _133 = copy _129;
|
||||
StorageLive(_134);
|
||||
_134 = copy _1;
|
||||
- _132 = Add(move _133, move _134);
|
||||
+ _132 = Add(move _133, copy _1);
|
||||
+ _132 = copy _128;
|
||||
StorageDead(_134);
|
||||
StorageDead(_133);
|
||||
_131 = opaque::<u64>(move _132) -> [return: bb36, unwind unreachable];
|
||||
- _131 = opaque::<u64>(move _132) -> [return: bb36, unwind unreachable];
|
||||
+ _131 = opaque::<u64>(copy _128) -> [return: bb36, unwind unreachable];
|
||||
}
|
||||
|
||||
bb36: {
|
||||
|
|
@ -899,32 +906,39 @@
|
|||
StorageLive(_163);
|
||||
_163 = &_3;
|
||||
StorageLive(_164);
|
||||
StorageLive(_165);
|
||||
StorageLive(_166);
|
||||
- StorageLive(_165);
|
||||
- StorageLive(_166);
|
||||
+ nop;
|
||||
+ nop;
|
||||
_166 = copy (*_163);
|
||||
StorageLive(_167);
|
||||
_167 = copy _1;
|
||||
- _165 = Add(move _166, move _167);
|
||||
+ _165 = Add(move _166, copy _1);
|
||||
+ _165 = Add(copy _166, copy _1);
|
||||
StorageDead(_167);
|
||||
StorageDead(_166);
|
||||
_164 = opaque::<u64>(move _165) -> [return: bb43, unwind unreachable];
|
||||
- StorageDead(_166);
|
||||
- _164 = opaque::<u64>(move _165) -> [return: bb43, unwind unreachable];
|
||||
+ nop;
|
||||
+ _164 = opaque::<u64>(copy _165) -> [return: bb43, unwind unreachable];
|
||||
}
|
||||
|
||||
bb43: {
|
||||
StorageDead(_165);
|
||||
- StorageDead(_165);
|
||||
+ nop;
|
||||
StorageDead(_164);
|
||||
StorageLive(_168);
|
||||
StorageLive(_169);
|
||||
StorageLive(_170);
|
||||
_170 = copy (*_163);
|
||||
- _170 = copy (*_163);
|
||||
+ _170 = copy _166;
|
||||
StorageLive(_171);
|
||||
_171 = copy _1;
|
||||
- _169 = Add(move _170, move _171);
|
||||
+ _169 = Add(move _170, copy _1);
|
||||
+ _169 = copy _165;
|
||||
StorageDead(_171);
|
||||
StorageDead(_170);
|
||||
_168 = opaque::<u64>(move _169) -> [return: bb44, unwind unreachable];
|
||||
- _168 = opaque::<u64>(move _169) -> [return: bb44, unwind unreachable];
|
||||
+ _168 = opaque::<u64>(copy _165) -> [return: bb44, unwind unreachable];
|
||||
}
|
||||
|
||||
bb44: {
|
||||
|
|
|
|||
|
|
@ -758,32 +758,39 @@
|
|||
StorageLive(_126);
|
||||
_126 = &_3;
|
||||
StorageLive(_127);
|
||||
StorageLive(_128);
|
||||
StorageLive(_129);
|
||||
- StorageLive(_128);
|
||||
- StorageLive(_129);
|
||||
+ nop;
|
||||
+ nop;
|
||||
_129 = copy (*_126);
|
||||
StorageLive(_130);
|
||||
_130 = copy _1;
|
||||
- _128 = Add(move _129, move _130);
|
||||
+ _128 = Add(move _129, copy _1);
|
||||
+ _128 = Add(copy _129, copy _1);
|
||||
StorageDead(_130);
|
||||
StorageDead(_129);
|
||||
_127 = opaque::<u64>(move _128) -> [return: bb35, unwind continue];
|
||||
- StorageDead(_129);
|
||||
- _127 = opaque::<u64>(move _128) -> [return: bb35, unwind continue];
|
||||
+ nop;
|
||||
+ _127 = opaque::<u64>(copy _128) -> [return: bb35, unwind continue];
|
||||
}
|
||||
|
||||
bb35: {
|
||||
StorageDead(_128);
|
||||
- StorageDead(_128);
|
||||
+ nop;
|
||||
StorageDead(_127);
|
||||
StorageLive(_131);
|
||||
StorageLive(_132);
|
||||
StorageLive(_133);
|
||||
_133 = copy (*_126);
|
||||
- _133 = copy (*_126);
|
||||
+ _133 = copy _129;
|
||||
StorageLive(_134);
|
||||
_134 = copy _1;
|
||||
- _132 = Add(move _133, move _134);
|
||||
+ _132 = Add(move _133, copy _1);
|
||||
+ _132 = copy _128;
|
||||
StorageDead(_134);
|
||||
StorageDead(_133);
|
||||
_131 = opaque::<u64>(move _132) -> [return: bb36, unwind continue];
|
||||
- _131 = opaque::<u64>(move _132) -> [return: bb36, unwind continue];
|
||||
+ _131 = opaque::<u64>(copy _128) -> [return: bb36, unwind continue];
|
||||
}
|
||||
|
||||
bb36: {
|
||||
|
|
@ -899,32 +906,39 @@
|
|||
StorageLive(_163);
|
||||
_163 = &_3;
|
||||
StorageLive(_164);
|
||||
StorageLive(_165);
|
||||
StorageLive(_166);
|
||||
- StorageLive(_165);
|
||||
- StorageLive(_166);
|
||||
+ nop;
|
||||
+ nop;
|
||||
_166 = copy (*_163);
|
||||
StorageLive(_167);
|
||||
_167 = copy _1;
|
||||
- _165 = Add(move _166, move _167);
|
||||
+ _165 = Add(move _166, copy _1);
|
||||
+ _165 = Add(copy _166, copy _1);
|
||||
StorageDead(_167);
|
||||
StorageDead(_166);
|
||||
_164 = opaque::<u64>(move _165) -> [return: bb43, unwind continue];
|
||||
- StorageDead(_166);
|
||||
- _164 = opaque::<u64>(move _165) -> [return: bb43, unwind continue];
|
||||
+ nop;
|
||||
+ _164 = opaque::<u64>(copy _165) -> [return: bb43, unwind continue];
|
||||
}
|
||||
|
||||
bb43: {
|
||||
StorageDead(_165);
|
||||
- StorageDead(_165);
|
||||
+ nop;
|
||||
StorageDead(_164);
|
||||
StorageLive(_168);
|
||||
StorageLive(_169);
|
||||
StorageLive(_170);
|
||||
_170 = copy (*_163);
|
||||
- _170 = copy (*_163);
|
||||
+ _170 = copy _166;
|
||||
StorageLive(_171);
|
||||
_171 = copy _1;
|
||||
- _169 = Add(move _170, move _171);
|
||||
+ _169 = Add(move _170, copy _1);
|
||||
+ _169 = copy _165;
|
||||
StorageDead(_171);
|
||||
StorageDead(_170);
|
||||
_168 = opaque::<u64>(move _169) -> [return: bb44, unwind continue];
|
||||
- _168 = opaque::<u64>(move _169) -> [return: bb44, unwind continue];
|
||||
+ _168 = opaque::<u64>(copy _165) -> [return: bb44, unwind continue];
|
||||
}
|
||||
|
||||
bb44: {
|
||||
|
|
|
|||
|
|
@ -29,17 +29,13 @@
|
|||
_8 = copy (*_1);
|
||||
_2 = copy ((*_8).0: i32);
|
||||
- StorageLive(_3);
|
||||
- _9 = copy (*_1);
|
||||
- _3 = copy ((*_9).1: u64);
|
||||
+ nop;
|
||||
_9 = copy (*_1);
|
||||
_3 = copy ((*_9).1: u64);
|
||||
- StorageLive(_4);
|
||||
- _10 = copy (*_1);
|
||||
- _4 = copy ((*_10).2: [i8; 3]);
|
||||
+ nop;
|
||||
+ _9 = copy _8;
|
||||
+ _3 = copy ((*_8).1: u64);
|
||||
+ nop;
|
||||
+ _10 = copy _8;
|
||||
+ _4 = copy ((*_8).2: [i8; 3]);
|
||||
_10 = copy (*_1);
|
||||
_4 = copy ((*_10).2: [i8; 3]);
|
||||
StorageLive(_5);
|
||||
_5 = copy _2;
|
||||
StorageLive(_6);
|
||||
|
|
@ -47,7 +43,7 @@
|
|||
StorageLive(_7);
|
||||
_7 = copy _4;
|
||||
- _0 = AllCopy { a: move _5, b: move _6, c: move _7 };
|
||||
+ _0 = copy (*_8);
|
||||
+ _0 = AllCopy { a: copy _2, b: copy _3, c: copy _4 };
|
||||
StorageDead(_7);
|
||||
StorageDead(_6);
|
||||
StorageDead(_5);
|
||||
|
|
|
|||
61
tests/mir-opt/gvn_copy_aggregate.all_copy_mut.GVN.diff
Normal file
61
tests/mir-opt/gvn_copy_aggregate.all_copy_mut.GVN.diff
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
- // MIR for `all_copy_mut` before GVN
|
||||
+ // MIR for `all_copy_mut` after GVN
|
||||
|
||||
fn all_copy_mut(_1: &mut AllCopy) -> AllCopy {
|
||||
debug mut_v => _1;
|
||||
let mut _0: AllCopy;
|
||||
let _2: &AllCopy;
|
||||
let mut _6: i32;
|
||||
let mut _7: u64;
|
||||
let mut _8: [i8; 3];
|
||||
scope 1 {
|
||||
debug v => _2;
|
||||
let _3: i32;
|
||||
scope 2 {
|
||||
debug a => _3;
|
||||
let _4: u64;
|
||||
scope 3 {
|
||||
debug b => _4;
|
||||
let _5: [i8; 3];
|
||||
scope 4 {
|
||||
debug c => _5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_2);
|
||||
_2 = &(*_1);
|
||||
- StorageLive(_3);
|
||||
+ nop;
|
||||
_3 = copy ((*_2).0: i32);
|
||||
- StorageLive(_4);
|
||||
+ nop;
|
||||
_4 = copy ((*_2).1: u64);
|
||||
- StorageLive(_5);
|
||||
+ nop;
|
||||
_5 = copy ((*_2).2: [i8; 3]);
|
||||
((*_1).0: i32) = const 0_i32;
|
||||
StorageLive(_6);
|
||||
_6 = copy _3;
|
||||
StorageLive(_7);
|
||||
_7 = copy _4;
|
||||
StorageLive(_8);
|
||||
_8 = copy _5;
|
||||
- _0 = AllCopy { a: move _6, b: move _7, c: move _8 };
|
||||
+ _0 = AllCopy { a: copy _3, b: copy _4, c: copy _5 };
|
||||
StorageDead(_8);
|
||||
StorageDead(_7);
|
||||
StorageDead(_6);
|
||||
- StorageDead(_5);
|
||||
- StorageDead(_4);
|
||||
- StorageDead(_3);
|
||||
+ nop;
|
||||
+ nop;
|
||||
+ nop;
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
48
tests/mir-opt/gvn_copy_aggregate.deref_nonssa.GVN.diff
Normal file
48
tests/mir-opt/gvn_copy_aggregate.deref_nonssa.GVN.diff
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
- // MIR for `deref_nonssa` before GVN
|
||||
+ // MIR for `deref_nonssa` after GVN
|
||||
|
||||
fn deref_nonssa() -> Single {
|
||||
let mut _0: Single;
|
||||
let mut _1: Single;
|
||||
let mut _4: Single;
|
||||
let mut _5: u8;
|
||||
scope 1 {
|
||||
debug a => _1;
|
||||
let _2: &Single;
|
||||
scope 2 {
|
||||
debug b => _2;
|
||||
let _3: u8;
|
||||
scope 3 {
|
||||
debug c => _3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
- _1 = Single(const 0_u8);
|
||||
+ _1 = const Single(0_u8);
|
||||
StorageLive(_2);
|
||||
_2 = &_1;
|
||||
- StorageLive(_3);
|
||||
+ nop;
|
||||
_3 = copy ((*_2).0: u8);
|
||||
StorageLive(_4);
|
||||
- _4 = Single(const 1_u8);
|
||||
- _1 = move _4;
|
||||
+ _4 = const Single(1_u8);
|
||||
+ _1 = const Single(1_u8);
|
||||
StorageDead(_4);
|
||||
StorageLive(_5);
|
||||
_5 = copy _3;
|
||||
- _0 = Single(move _5);
|
||||
+ _0 = Single(copy _3);
|
||||
StorageDead(_5);
|
||||
- StorageDead(_3);
|
||||
+ nop;
|
||||
StorageDead(_2);
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
68
tests/mir-opt/gvn_copy_aggregate.deref_nonssa_2.GVN.diff
Normal file
68
tests/mir-opt/gvn_copy_aggregate.deref_nonssa_2.GVN.diff
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
- // MIR for `deref_nonssa_2` before GVN
|
||||
+ // MIR for `deref_nonssa_2` after GVN
|
||||
|
||||
fn deref_nonssa_2() -> Single {
|
||||
let mut _0: Single;
|
||||
let mut _1: Single;
|
||||
let mut _3: &Single;
|
||||
let _4: &Single;
|
||||
let mut _7: Single;
|
||||
let mut _8: u8;
|
||||
scope 1 {
|
||||
debug a => _1;
|
||||
let _2: SingleRef<'_>;
|
||||
scope 2 {
|
||||
debug r => _2;
|
||||
let _5: &Single;
|
||||
scope 3 {
|
||||
debug b => _5;
|
||||
let _6: u8;
|
||||
scope 4 {
|
||||
debug c => _6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
- _1 = Single(const 0_u8);
|
||||
+ _1 = const Single(0_u8);
|
||||
StorageLive(_2);
|
||||
- StorageLive(_3);
|
||||
+ nop;
|
||||
StorageLive(_4);
|
||||
_4 = &_1;
|
||||
_3 = &(*_4);
|
||||
- _2 = SingleRef::<'_>(move _3);
|
||||
- StorageDead(_3);
|
||||
+ _2 = SingleRef::<'_>(copy _3);
|
||||
+ nop;
|
||||
StorageDead(_4);
|
||||
StorageLive(_5);
|
||||
- _5 = copy (_2.0: &Single);
|
||||
- StorageLive(_6);
|
||||
- _6 = copy ((*_5).0: u8);
|
||||
+ _5 = copy _3;
|
||||
+ nop;
|
||||
+ _6 = copy ((*_3).0: u8);
|
||||
StorageLive(_7);
|
||||
- _7 = Single(const 1_u8);
|
||||
- _1 = move _7;
|
||||
+ _7 = const Single(1_u8);
|
||||
+ _1 = const Single(1_u8);
|
||||
StorageDead(_7);
|
||||
StorageLive(_8);
|
||||
_8 = copy _6;
|
||||
- _0 = Single(move _8);
|
||||
+ _0 = Single(copy _6);
|
||||
StorageDead(_8);
|
||||
- StorageDead(_6);
|
||||
+ nop;
|
||||
StorageDead(_5);
|
||||
StorageDead(_2);
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,13 +24,25 @@ fn all_copy(v: &AllCopy) -> AllCopy {
|
|||
AllCopy { a, b, c }
|
||||
}
|
||||
|
||||
// EMIT_MIR gvn_copy_aggregate.all_copy_mut.GVN.diff
|
||||
fn all_copy_mut(mut_v: &mut AllCopy) -> AllCopy {
|
||||
// CHECK-LABEL: fn all_copy_mut(
|
||||
// CHECK: = AllCopy { {{.*}} };
|
||||
// CHECK-NOT: _0 = copy ({{.*}});
|
||||
let v = &*mut_v;
|
||||
let a = v.a;
|
||||
let b = v.b;
|
||||
let c = v.c;
|
||||
mut_v.a = 0;
|
||||
AllCopy { a, b, c }
|
||||
}
|
||||
|
||||
// Nested references may be modified.
|
||||
// EMIT_MIR gvn_copy_aggregate.all_copy_2.GVN.diff
|
||||
fn all_copy_2(v: &&AllCopy) -> AllCopy {
|
||||
// CHECK-LABEL: fn all_copy_2(
|
||||
// CHECK: bb0: {
|
||||
// CHECK-NOT: = AllCopy { {{.*}} };
|
||||
// CHECK: [[V1:_.*]] = copy (*_1);
|
||||
// CHECK: _0 = copy (*[[V1]]);
|
||||
// CHECK: _0 = AllCopy { {{.*}} };
|
||||
let a = v.a;
|
||||
let b = v.b;
|
||||
let c = v.c;
|
||||
|
|
@ -259,3 +271,31 @@ fn remove_storage_dead_from_index(f: fn() -> usize, v: [SameType; 5]) -> SameTyp
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Single(u8);
|
||||
|
||||
// EMIT_MIR gvn_copy_aggregate.deref_nonssa.GVN.diff
|
||||
fn deref_nonssa() -> Single {
|
||||
// CHECK-LABEL: fn deref_nonssa(
|
||||
// CHECK: _0 = Single(copy {{.*}});
|
||||
let mut a = Single(0);
|
||||
let b = &a;
|
||||
let c = (*b).0;
|
||||
a = Single(1);
|
||||
// GVN shouldn't replace `Single(c)` with `*b`.
|
||||
Single(c)
|
||||
}
|
||||
|
||||
pub struct SingleRef<'a>(&'a Single);
|
||||
|
||||
// EMIT_MIR gvn_copy_aggregate.deref_nonssa_2.GVN.diff
|
||||
pub fn deref_nonssa_2() -> Single {
|
||||
// CHECK-LABEL: fn deref_nonssa_2(
|
||||
// CHECK: _0 = Single(copy {{.*}});
|
||||
let mut a = Single(0);
|
||||
let r = SingleRef(&a);
|
||||
let b = r.0;
|
||||
let c = (*b).0;
|
||||
a = Single(1);
|
||||
Single(c)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
let mut _15: !;
|
||||
let mut _16: Value;
|
||||
scope 1 {
|
||||
debug val_alias => _2;
|
||||
debug val_alias => _3;
|
||||
let mut _5: bool;
|
||||
scope 2 {
|
||||
debug stop => _5;
|
||||
|
|
@ -33,23 +33,16 @@
|
|||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_2);
|
||||
- StorageLive(_3);
|
||||
+ nop;
|
||||
StorageLive(_4);
|
||||
_4 = &(*_1);
|
||||
_3 = get::<Value>(move _4) -> [return: bb1, unwind unreachable];
|
||||
_3 = get::<Value>(copy _4) -> [return: bb1, unwind unreachable];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
_2 = &(*_3);
|
||||
StorageDead(_4);
|
||||
- StorageDead(_3);
|
||||
+ nop;
|
||||
StorageLive(_5);
|
||||
_5 = const false;
|
||||
- _8 = discriminant((*_2));
|
||||
+ _8 = discriminant((*_3));
|
||||
_8 = discriminant((*_3));
|
||||
switchInt(move _8) -> [0: bb3, otherwise: bb2];
|
||||
}
|
||||
|
||||
|
|
@ -59,10 +52,8 @@
|
|||
|
||||
bb3: {
|
||||
- StorageLive(_7);
|
||||
- _7 = copy (((*_2) as V0).0: i32);
|
||||
+ nop;
|
||||
+ _7 = copy (((*_3) as V0).0: i32);
|
||||
StorageLive(_9);
|
||||
_7 = copy (((*_3) as V0).0: i32);
|
||||
goto -> bb4;
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +64,6 @@
|
|||
- _11 = Value::V0(move _12);
|
||||
+ _11 = Value::V0(copy _7);
|
||||
StorageDead(_12);
|
||||
StorageLive(_13);
|
||||
StorageLive(_14);
|
||||
_14 = copy _5;
|
||||
switchInt(move _14) -> [0: bb6, otherwise: bb5];
|
||||
|
|
@ -82,20 +72,15 @@
|
|||
bb5: {
|
||||
_0 = move _11;
|
||||
StorageDead(_14);
|
||||
StorageDead(_13);
|
||||
StorageDead(_11);
|
||||
StorageDead(_9);
|
||||
- StorageDead(_7);
|
||||
+ nop;
|
||||
StorageDead(_5);
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
|
||||
bb6: {
|
||||
_13 = const ();
|
||||
StorageDead(_14);
|
||||
StorageDead(_13);
|
||||
_5 = const true;
|
||||
StorageLive(_16);
|
||||
- _16 = Value::V1;
|
||||
|
|
@ -103,7 +88,6 @@
|
|||
+ _16 = const Value::V1;
|
||||
+ (*_1) = const Value::V1;
|
||||
StorageDead(_16);
|
||||
_10 = const ();
|
||||
StorageDead(_11);
|
||||
goto -> bb4;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//@ test-mir-pass: GVN
|
||||
//@ compile-flags: -Zdump-mir-exclude-alloc-bytes
|
||||
//@ compile-flags: -Zdump-mir-exclude-alloc-bytes -Zmir-enable-passes=+ReferencePropagation
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(core_intrinsics, rustc_attrs)]
|
||||
|
|
|
|||
18
tests/mir-opt/gvn_overlapping.copy_overlapping.GVN.diff
Normal file
18
tests/mir-opt/gvn_overlapping.copy_overlapping.GVN.diff
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
- // MIR for `copy_overlapping` before GVN
|
||||
+ // MIR for `copy_overlapping` after GVN
|
||||
|
||||
fn copy_overlapping() -> () {
|
||||
let mut _0: ();
|
||||
let mut _1: Adt;
|
||||
let mut _2: u32;
|
||||
let mut _3: &Adt;
|
||||
|
||||
bb0: {
|
||||
((_1 as variant#1).0: u32) = const 0_u32;
|
||||
_3 = &_1;
|
||||
_2 = copy (((*_3) as variant#1).0: u32);
|
||||
_1 = Adt::Some(copy _2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3,12 +3,12 @@
|
|||
|
||||
fn overlapping(_1: Adt) -> () {
|
||||
let mut _0: ();
|
||||
let mut _2: *mut Adt;
|
||||
let mut _2: &mut Adt;
|
||||
let mut _3: u32;
|
||||
let mut _4: &Adt;
|
||||
|
||||
bb0: {
|
||||
_2 = &raw mut _1;
|
||||
_2 = &mut _1;
|
||||
_4 = &(*_2);
|
||||
_3 = copy (((*_4) as variant#1).0: u32);
|
||||
(*_2) = Adt::Some(copy _3);
|
||||
|
|
|
|||
|
|
@ -7,19 +7,19 @@ use std::intrinsics::mir::*;
|
|||
// EMIT_MIR gvn_overlapping.overlapping.GVN.diff
|
||||
/// Check that we do not create overlapping assignments.
|
||||
#[custom_mir(dialect = "runtime")]
|
||||
fn overlapping(_17: Adt) {
|
||||
fn overlapping(_1: Adt) {
|
||||
// CHECK-LABEL: fn overlapping(
|
||||
// CHECK: let mut [[PTR:.*]]: *mut Adt;
|
||||
// CHECK: let mut [[PTR:.*]]: &mut Adt;
|
||||
// CHECK: (*[[PTR]]) = Adt::Some(copy {{.*}});
|
||||
mir! {
|
||||
let _33: *mut Adt;
|
||||
let _48: u32;
|
||||
let _73: &Adt;
|
||||
let _2: &mut Adt;
|
||||
let _3: u32;
|
||||
let _4: &Adt;
|
||||
{
|
||||
_33 = core::ptr::addr_of_mut!(_17);
|
||||
_73 = &(*_33);
|
||||
_48 = Field(Variant((*_73), 1), 0);
|
||||
(*_33) = Adt::Some(_48);
|
||||
_2 = &mut _1;
|
||||
_4 = &(*_2);
|
||||
_3 = Field(Variant((*_4), 1), 0);
|
||||
(*_2) = Adt::Some(_3);
|
||||
Return()
|
||||
}
|
||||
}
|
||||
|
|
@ -30,18 +30,19 @@ fn overlapping(_17: Adt) {
|
|||
#[custom_mir(dialect = "runtime")]
|
||||
fn stable_projection(_1: (Adt,)) {
|
||||
// CHECK-LABEL: fn stable_projection(
|
||||
// CHECK: let mut _2: *mut Adt;
|
||||
// CHECK: let mut _2: &Adt;
|
||||
// CHECK: let mut _4: &Adt;
|
||||
// CHECK: (_1.0: Adt) = copy (*_4);
|
||||
// CHECK: (_5.0: Adt) = copy (_1.0: Adt);
|
||||
mir! {
|
||||
let _2: *mut Adt;
|
||||
let _2: &Adt;
|
||||
let _3: u32;
|
||||
let _4: &Adt;
|
||||
let _5: (Adt,);
|
||||
{
|
||||
_2 = core::ptr::addr_of_mut!(_1.0);
|
||||
_2 = &_1.0;
|
||||
_4 = &(*_2);
|
||||
_3 = Field(Variant((*_4), 1), 0);
|
||||
_1.0 = Adt::Some(_3);
|
||||
_5.0 = Adt::Some(_3);
|
||||
Return()
|
||||
}
|
||||
}
|
||||
|
|
@ -64,6 +65,23 @@ fn fields(_1: (Adt, Adt)) {
|
|||
}
|
||||
}
|
||||
|
||||
// EMIT_MIR gvn_overlapping.copy_overlapping.GVN.diff
|
||||
#[custom_mir(dialect = "runtime")]
|
||||
fn copy_overlapping() {
|
||||
mir! {
|
||||
let _1;
|
||||
let _2;
|
||||
let _3;
|
||||
{
|
||||
place!(Field(Variant(_1, 1), 0)) = 0u32;
|
||||
_3 = &_1;
|
||||
_2 = Field(Variant(*_3, 1), 0);
|
||||
_1 = Adt::Some(_2);
|
||||
Return()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
overlapping(Adt::Some(0));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,18 @@
|
|||
|
||||
fn stable_projection(_1: (Adt,)) -> () {
|
||||
let mut _0: ();
|
||||
let mut _2: *mut Adt;
|
||||
let mut _2: &Adt;
|
||||
let mut _3: u32;
|
||||
let mut _4: &Adt;
|
||||
let mut _5: (Adt,);
|
||||
|
||||
bb0: {
|
||||
_2 = &raw mut (_1.0: Adt);
|
||||
_2 = &(_1.0: Adt);
|
||||
_4 = &(*_2);
|
||||
_3 = copy (((*_4) as variant#1).0: u32);
|
||||
- (_1.0: Adt) = Adt::Some(copy _3);
|
||||
+ (_1.0: Adt) = copy (*_4);
|
||||
- _3 = copy (((*_4) as variant#1).0: u32);
|
||||
- (_5.0: Adt) = Adt::Some(copy _3);
|
||||
+ _3 = copy (((_1.0: Adt) as variant#1).0: u32);
|
||||
+ (_5.0: Adt) = copy (_1.0: Adt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
|
||||
fn src(x: &&u8) -> bool {
|
||||
// CHECK-LABEL: fn src(
|
||||
// CHECK: debug y => [[Y:_.*]];
|
||||
// CHECK: bb0:
|
||||
// CHECK: [[BORROW_u8:_.*]] = copy (*_1);
|
||||
// CHECK: [[Y]] = copy (*[[BORROW_u8]]);
|
||||
// CHECK: bb1:
|
||||
// BORROW_u8 outside its lifetime in bb1.
|
||||
// CHECK-NOT: copy (*[[BORROW_u8]]);
|
||||
// CHECK: copy (*_1);
|
||||
// CHECK-NOT: _0 = const true;
|
||||
// CHECK: _0 = Eq({{.*}}, {{.*}});
|
||||
// CHECK-NOT: _0 = const true;
|
||||
|
|
|
|||
|
|
@ -3,101 +3,107 @@
|
|||
fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2: &&(usize, usize, usize, usize)) -> bool {
|
||||
let mut _0: bool;
|
||||
let mut _3: &(usize, usize, usize, usize);
|
||||
let mut _6: bool;
|
||||
let mut _4: &(usize, usize, usize, usize);
|
||||
let mut _5: &(usize, usize, usize, usize);
|
||||
let mut _6: &(usize, usize, usize, usize);
|
||||
let mut _9: bool;
|
||||
let mut _10: bool;
|
||||
let _13: &usize;
|
||||
let _14: &usize;
|
||||
let _15: &usize;
|
||||
let mut _12: bool;
|
||||
let mut _13: bool;
|
||||
let _16: &usize;
|
||||
let mut _17: &&usize;
|
||||
let mut _18: &&usize;
|
||||
let mut _19: &&usize;
|
||||
let _17: &usize;
|
||||
let _18: &usize;
|
||||
let _19: &usize;
|
||||
let mut _20: &&usize;
|
||||
let mut _21: &&usize;
|
||||
let mut _22: &&usize;
|
||||
let mut _23: &&usize;
|
||||
let mut _24: &&usize;
|
||||
let mut _25: &&usize;
|
||||
let mut _26: &&usize;
|
||||
let mut _27: &&usize;
|
||||
scope 1 {
|
||||
debug a => _13;
|
||||
debug b => _14;
|
||||
debug c => _15;
|
||||
debug d => _16;
|
||||
debug a => _16;
|
||||
debug b => _17;
|
||||
debug c => _18;
|
||||
debug d => _19;
|
||||
scope 2 (inlined std::cmp::impls::<impl PartialOrd for &usize>::le) {
|
||||
debug self => _17;
|
||||
debug other => _18;
|
||||
debug self => _20;
|
||||
debug other => _21;
|
||||
scope 3 (inlined std::cmp::impls::<impl PartialOrd for usize>::le) {
|
||||
debug self => _13;
|
||||
debug other => _15;
|
||||
let mut _4: usize;
|
||||
let mut _5: usize;
|
||||
}
|
||||
}
|
||||
scope 4 (inlined std::cmp::impls::<impl PartialOrd for &usize>::le) {
|
||||
debug self => _19;
|
||||
debug other => _20;
|
||||
scope 5 (inlined std::cmp::impls::<impl PartialOrd for usize>::le) {
|
||||
debug self => _16;
|
||||
debug other => _14;
|
||||
debug other => _18;
|
||||
let mut _7: usize;
|
||||
let mut _8: usize;
|
||||
}
|
||||
}
|
||||
scope 4 (inlined std::cmp::impls::<impl PartialOrd for &usize>::le) {
|
||||
debug self => _22;
|
||||
debug other => _23;
|
||||
scope 5 (inlined std::cmp::impls::<impl PartialOrd for usize>::le) {
|
||||
debug self => _19;
|
||||
debug other => _17;
|
||||
let mut _10: usize;
|
||||
let mut _11: usize;
|
||||
}
|
||||
}
|
||||
scope 6 (inlined std::cmp::impls::<impl PartialOrd for &usize>::le) {
|
||||
debug self => _21;
|
||||
debug other => _22;
|
||||
debug self => _24;
|
||||
debug other => _25;
|
||||
scope 7 (inlined std::cmp::impls::<impl PartialOrd for usize>::le) {
|
||||
debug self => _15;
|
||||
debug other => _13;
|
||||
debug self => _18;
|
||||
debug other => _16;
|
||||
}
|
||||
}
|
||||
scope 8 (inlined std::cmp::impls::<impl PartialOrd for &usize>::le) {
|
||||
debug self => _23;
|
||||
debug other => _24;
|
||||
debug self => _26;
|
||||
debug other => _27;
|
||||
scope 9 (inlined std::cmp::impls::<impl PartialOrd for usize>::le) {
|
||||
debug self => _14;
|
||||
debug other => _16;
|
||||
let mut _11: usize;
|
||||
let mut _12: usize;
|
||||
debug self => _17;
|
||||
debug other => _19;
|
||||
let mut _14: usize;
|
||||
let mut _15: usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
_3 = copy (*_2);
|
||||
// DBG: _13 = &((*_3).0: usize);
|
||||
// DBG: _14 = &((*_3).1: usize);
|
||||
// DBG: _15 = &((*_3).2: usize);
|
||||
// DBG: _16 = &((*_3).3: usize);
|
||||
StorageLive(_6);
|
||||
// DBG: _17 = &_13;
|
||||
// DBG: _18 = &?;
|
||||
_4 = copy ((*_3).0: usize);
|
||||
_5 = copy ((*_3).2: usize);
|
||||
_6 = Le(copy _4, copy _5);
|
||||
switchInt(move _6) -> [0: bb2, otherwise: bb1];
|
||||
// DBG: _16 = &((*_3).0: usize);
|
||||
_4 = copy (*_2);
|
||||
// DBG: _17 = &((*_4).1: usize);
|
||||
_5 = copy (*_2);
|
||||
// DBG: _18 = &((*_5).2: usize);
|
||||
_6 = copy (*_2);
|
||||
// DBG: _19 = &((*_6).3: usize);
|
||||
StorageLive(_9);
|
||||
// DBG: _20 = &_16;
|
||||
// DBG: _21 = &?;
|
||||
_7 = copy ((*_3).0: usize);
|
||||
_8 = copy ((*_5).2: usize);
|
||||
_9 = Le(copy _7, copy _8);
|
||||
switchInt(move _9) -> [0: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
StorageLive(_9);
|
||||
// DBG: _19 = &_16;
|
||||
// DBG: _20 = &?;
|
||||
StorageLive(_7);
|
||||
_7 = copy ((*_3).3: usize);
|
||||
StorageLive(_8);
|
||||
_8 = copy ((*_3).1: usize);
|
||||
_9 = Le(move _7, move _8);
|
||||
StorageDead(_8);
|
||||
StorageDead(_7);
|
||||
switchInt(move _9) -> [0: bb2, otherwise: bb6];
|
||||
StorageLive(_12);
|
||||
// DBG: _22 = &_19;
|
||||
// DBG: _23 = &?;
|
||||
StorageLive(_10);
|
||||
_10 = copy ((*_6).3: usize);
|
||||
StorageLive(_11);
|
||||
_11 = copy ((*_4).1: usize);
|
||||
_12 = Le(move _10, move _11);
|
||||
StorageDead(_11);
|
||||
StorageDead(_10);
|
||||
switchInt(move _12) -> [0: bb2, otherwise: bb6];
|
||||
}
|
||||
|
||||
bb2: {
|
||||
StorageLive(_10);
|
||||
// DBG: _21 = &_15;
|
||||
// DBG: _22 = &?;
|
||||
_10 = Le(copy _5, copy _4);
|
||||
switchInt(move _10) -> [0: bb3, otherwise: bb4];
|
||||
StorageLive(_13);
|
||||
// DBG: _24 = &_18;
|
||||
// DBG: _25 = &?;
|
||||
_13 = Le(copy _8, copy _7);
|
||||
switchInt(move _13) -> [0: bb3, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
|
|
@ -106,20 +112,20 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2
|
|||
}
|
||||
|
||||
bb4: {
|
||||
// DBG: _23 = &_14;
|
||||
// DBG: _24 = &?;
|
||||
StorageLive(_11);
|
||||
_11 = copy ((*_3).1: usize);
|
||||
StorageLive(_12);
|
||||
_12 = copy ((*_3).3: usize);
|
||||
_0 = Le(move _11, move _12);
|
||||
StorageDead(_12);
|
||||
StorageDead(_11);
|
||||
// DBG: _26 = &_17;
|
||||
// DBG: _27 = &?;
|
||||
StorageLive(_14);
|
||||
_14 = copy ((*_4).1: usize);
|
||||
StorageLive(_15);
|
||||
_15 = copy ((*_6).3: usize);
|
||||
_0 = Le(move _14, move _15);
|
||||
StorageDead(_15);
|
||||
StorageDead(_14);
|
||||
goto -> bb5;
|
||||
}
|
||||
|
||||
bb5: {
|
||||
StorageDead(_10);
|
||||
StorageDead(_13);
|
||||
goto -> bb7;
|
||||
}
|
||||
|
||||
|
|
@ -129,8 +135,8 @@ fn variant_a::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:7:25: 7:39}, _2
|
|||
}
|
||||
|
||||
bb7: {
|
||||
StorageDead(_12);
|
||||
StorageDead(_9);
|
||||
StorageDead(_6);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,40 +4,46 @@ fn variant_b::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:11:25: 11:41},
|
|||
let mut _0: bool;
|
||||
let mut _3: &(usize, usize, usize, usize);
|
||||
let _4: usize;
|
||||
let _5: usize;
|
||||
let mut _5: &(usize, usize, usize, usize);
|
||||
let _6: usize;
|
||||
let _7: usize;
|
||||
let mut _8: bool;
|
||||
let mut _9: bool;
|
||||
let mut _10: bool;
|
||||
let mut _7: &(usize, usize, usize, usize);
|
||||
let _8: usize;
|
||||
let mut _9: &(usize, usize, usize, usize);
|
||||
let _10: usize;
|
||||
let mut _11: bool;
|
||||
let mut _12: bool;
|
||||
let mut _13: bool;
|
||||
scope 1 {
|
||||
debug a => _4;
|
||||
debug b => _5;
|
||||
debug c => _6;
|
||||
debug d => _7;
|
||||
debug b => _6;
|
||||
debug c => _8;
|
||||
debug d => _10;
|
||||
}
|
||||
|
||||
bb0: {
|
||||
_3 = copy (*_2);
|
||||
_4 = copy ((*_3).0: usize);
|
||||
_5 = copy ((*_3).1: usize);
|
||||
_6 = copy ((*_3).2: usize);
|
||||
_7 = copy ((*_3).3: usize);
|
||||
StorageLive(_8);
|
||||
_8 = Le(copy _4, copy _6);
|
||||
switchInt(move _8) -> [0: bb2, otherwise: bb1];
|
||||
_5 = copy (*_2);
|
||||
_6 = copy ((*_5).1: usize);
|
||||
_7 = copy (*_2);
|
||||
_8 = copy ((*_7).2: usize);
|
||||
_9 = copy (*_2);
|
||||
_10 = copy ((*_9).3: usize);
|
||||
StorageLive(_11);
|
||||
_11 = Le(copy _4, copy _8);
|
||||
switchInt(move _11) -> [0: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
StorageLive(_9);
|
||||
_9 = Le(copy _7, copy _5);
|
||||
switchInt(move _9) -> [0: bb2, otherwise: bb6];
|
||||
StorageLive(_12);
|
||||
_12 = Le(copy _10, copy _6);
|
||||
switchInt(move _12) -> [0: bb2, otherwise: bb6];
|
||||
}
|
||||
|
||||
bb2: {
|
||||
StorageLive(_10);
|
||||
_10 = Le(copy _6, copy _4);
|
||||
switchInt(move _10) -> [0: bb3, otherwise: bb4];
|
||||
StorageLive(_13);
|
||||
_13 = Le(copy _8, copy _4);
|
||||
switchInt(move _13) -> [0: bb3, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
|
|
@ -46,12 +52,12 @@ fn variant_b::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:11:25: 11:41},
|
|||
}
|
||||
|
||||
bb4: {
|
||||
_0 = Le(copy _5, copy _7);
|
||||
_0 = Le(copy _6, copy _10);
|
||||
goto -> bb5;
|
||||
}
|
||||
|
||||
bb5: {
|
||||
StorageDead(_10);
|
||||
StorageDead(_13);
|
||||
goto -> bb7;
|
||||
}
|
||||
|
||||
|
|
@ -61,8 +67,8 @@ fn variant_b::{closure#0}(_1: &mut {closure@$DIR/slice_filter.rs:11:25: 11:41},
|
|||
}
|
||||
|
||||
bb7: {
|
||||
StorageDead(_9);
|
||||
StorageDead(_8);
|
||||
StorageDead(_12);
|
||||
StorageDead(_11);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue