Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions compiler/rustc_middle/src/mir/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,28 +695,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)]
Expand Down
140 changes: 86 additions & 54 deletions compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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,
});
Expand Down Expand Up @@ -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> {
Expand All @@ -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>,
Expand Down Expand Up @@ -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()),
Expand Down Expand Up @@ -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> {
Expand All @@ -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 =
Expand Down Expand Up @@ -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);
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad to see this gone.

#[instrument(level = "trace", skip(self), ret)]
fn eval_to_const_inner(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
use Value::*;
Expand All @@ -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,

Expand Down Expand Up @@ -818,7 +802,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {

// 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;
}
Expand Down Expand Up @@ -1037,7 +1023,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));
Expand Down Expand Up @@ -1148,7 +1134,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> {
Expand Down Expand Up @@ -1231,12 +1216,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));
}
Expand Down Expand Up @@ -1834,6 +1814,45 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
let mut projection = SmallVec::<[PlaceElem<'tcx>; 1]>::new();
loop {
if let Some(local) = self.try_as_local(index, loc) {
if projection.last() == Some(&PlaceElem::Deref) {
// We can introduce a new dereference if the source value cannot be changed in the body.
let mut deref_root = index;
loop {
let ty = self.ty(deref_root);
match self.get(deref_root) {
Value::Projection(base, _) => {
deref_root = base;
}
Value::Address {
base,
projection,
kind: AddressKind::Ref(BorrowKind::Shared),
..
} if projection.iter().all(ProjectionElem::is_stable_offset) => {
match base {
AddressBase::Local(_) => {
break;
}
AddressBase::Deref(index) => {
deref_root = index;
}
}
}
Value::Argument(_)
if matches!(ty.ref_mutability(), Some(Mutability::Not))
|| !ty.is_any_ptr() =>
{
break;
}
Value::Opaque(_) if ty.is_fn() => {
break;
}
_ => {
return None;
}
}
}
}
projection.reverse();
let place =
Place { local, projection: self.tcx.mk_place_elems(projection.as_slice()) };
Expand Down Expand Up @@ -1873,10 +1892,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);
}

Expand All @@ -1893,7 +1908,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
) {
self.simplify_place_projection(lhs, location);

let value = self.simplify_rvalue(lhs, rvalue, location);
let mut value = self.simplify_rvalue(lhs, rvalue, location);
if let Some(value) = value {
if let Some(const_) = self.try_as_constant(value) {
*rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
Expand All @@ -1906,14 +1921,35 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
}
}

if lhs.is_indirect() {
// Non-local mutation maybe invalidate deref.
self.invalidate_derefs();
let rvalue_ty = rvalue.ty(self.local_decls, self.tcx);
// DO NOT reason the pointer value if it may point to a non-SSA local.
// For instance, we cannot unify two pointers that dereference same local, because they may
// have different lifetimes.
if rvalue_ty.is_ref()
&& let Some(index) = value
{
match self.get(index) {
Value::Projection(..) => {
value = None;
}
Value::Opaque(_)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest change allows the opaque value to be propagated. For let a: &T = b;, a extends the lifetime of b.

| Value::Constant { .. }
| Value::Address { .. }
| Value::Argument(_)
| Value::RawPtr { .. }
| Value::BinaryOp(..)
| Value::Cast { .. }
| Value::Aggregate(..)
| Value::Union(..)
| Value::Repeat(..)
| Value::Discriminant(..)
| Value::RuntimeChecks(..)
| Value::UnaryOp(..) => {}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this does. Why are some values unreachable? A constant and a cast can be a reference, can't it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are not marked as unreachable. The new change should be clearer.


if let Some(local) = lhs.as_local()
&& self.ssa.is_ssa(local)
&& let rvalue_ty = rvalue.ty(self.local_decls, self.tcx)
// FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
// `local` as reusable if we have an exact type match.
&& self.local_decls[local].ty == rvalue_ty
Expand All @@ -1933,10 +1969,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);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@

bb0: {
StorageLive(_1);
- StorageLive(_2);
+ nop;
StorageLive(_2);
StorageLive(_3);
StorageLive(_4);
- _4 = ();
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@

bb0: {
StorageLive(_1);
- StorageLive(_2);
+ nop;
StorageLive(_2);
StorageLive(_3);
StorageLive(_4);
- _4 = ();
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@

bb0: {
StorageLive(_1);
- StorageLive(_2);
+ nop;
StorageLive(_2);
StorageLive(_3);
StorageLive(_4);
- _4 = ();
Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading