More GVN for PtrMetadata
`PtrMetadata` doesn't care about `*const`/`*mut`/`&`/`&mut`, so GVN away those casts in its argument. This includes updating MIR to allow calling PtrMetadata on references too, not just raw pointers. That means that `[T]::len` can be just `_0 = PtrMetadata(_1)`, for example. # Conflicts: # tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir # tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
This commit is contained in:
parent
31d8696ac9
commit
4a7b6c0e6c
14 changed files with 319 additions and 78 deletions
|
@ -836,12 +836,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
}
|
||||
Value::BinaryOp(op, lhs, rhs)
|
||||
}
|
||||
Rvalue::UnaryOp(op, ref mut arg) => {
|
||||
let arg = self.simplify_operand(arg, location)?;
|
||||
if let Some(value) = self.simplify_unary(op, arg) {
|
||||
return Some(value);
|
||||
}
|
||||
Value::UnaryOp(op, arg)
|
||||
Rvalue::UnaryOp(op, ref mut arg_op) => {
|
||||
return self.simplify_unary(op, arg_op, location);
|
||||
}
|
||||
Rvalue::Discriminant(ref mut place) => {
|
||||
let place = self.simplify_place_value(place, location)?;
|
||||
|
@ -971,8 +967,71 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn simplify_unary(&mut self, op: UnOp, value: VnIndex) -> Option<VnIndex> {
|
||||
let value = match (op, self.get(value)) {
|
||||
fn simplify_unary(
|
||||
&mut self,
|
||||
op: UnOp,
|
||||
arg_op: &mut Operand<'tcx>,
|
||||
location: Location,
|
||||
) -> Option<VnIndex> {
|
||||
let mut arg_index = self.simplify_operand(arg_op, location)?;
|
||||
|
||||
// PtrMetadata doesn't care about *const vs *mut vs & vs &mut,
|
||||
// so start by removing those distinctions so we can update the `Operand`
|
||||
if op == UnOp::PtrMetadata {
|
||||
let mut was_updated = false;
|
||||
loop {
|
||||
match self.get(arg_index) {
|
||||
// Pointer casts that preserve metadata, such as
|
||||
// `*const [i32]` <-> `*mut [i32]` <-> `*mut [f32]`.
|
||||
// It's critical that this not eliminate cases like
|
||||
// `*const [T]` -> `*const T` which remove metadata.
|
||||
// We run on potentially-generic MIR, though, so unlike codegen
|
||||
// we can't always know exactly what the metadata are.
|
||||
// Thankfully, equality on `ptr_metadata_ty_or_tail` gives us
|
||||
// what we need: `Ok(meta_ty)` if the metadata is known, or
|
||||
// `Err(tail_ty)` if not. Matching metadata is ok, but if
|
||||
// that's not known, then matching tail types is also ok,
|
||||
// allowing things like `*mut (?A, ?T)` <-> `*mut (?B, ?T)`.
|
||||
// FIXME: Would it be worth trying to normalize, rather than
|
||||
// passing the identity closure? Or are the types in the
|
||||
// Cast realistically about as normalized as we can get anyway?
|
||||
Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to }
|
||||
if from
|
||||
.builtin_deref(true)
|
||||
.unwrap()
|
||||
.ptr_metadata_ty_or_tail(self.tcx, |t| t)
|
||||
== to
|
||||
.builtin_deref(true)
|
||||
.unwrap()
|
||||
.ptr_metadata_ty_or_tail(self.tcx, |t| t) =>
|
||||
{
|
||||
arg_index = *inner;
|
||||
was_updated = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// `&mut *p`, `&raw *p`, etc don't change metadata.
|
||||
Value::Address { place, kind: _, provenance: _ }
|
||||
if let PlaceRef { local, projection: [PlaceElem::Deref] } =
|
||||
place.as_ref()
|
||||
&& let Some(local_index) = self.locals[local] =>
|
||||
{
|
||||
arg_index = local_index;
|
||||
was_updated = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
_ => {
|
||||
if was_updated && let Some(op) = self.try_as_operand(arg_index, location) {
|
||||
*arg_op = op;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let value = match (op, self.get(arg_index)) {
|
||||
(UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(*inner),
|
||||
(UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(*inner),
|
||||
(UnOp::Not, Value::BinaryOp(BinOp::Eq, lhs, rhs)) => {
|
||||
|
@ -984,9 +1043,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
(UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => {
|
||||
return Some(fields[1]);
|
||||
}
|
||||
_ => return None,
|
||||
// We have an unsizing cast, which assigns the length to fat pointer metadata.
|
||||
(
|
||||
UnOp::PtrMetadata,
|
||||
Value::Cast {
|
||||
kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize),
|
||||
from,
|
||||
to,
|
||||
..
|
||||
},
|
||||
) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind()
|
||||
&& let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() =>
|
||||
{
|
||||
return self.insert_constant(Const::from_ty_const(
|
||||
*len,
|
||||
self.tcx.types.usize,
|
||||
self.tcx,
|
||||
));
|
||||
}
|
||||
_ => Value::UnaryOp(op, arg_index),
|
||||
};
|
||||
|
||||
Some(self.insert(value))
|
||||
}
|
||||
|
||||
|
|
|
@ -1116,12 +1116,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
UnOp::PtrMetadata => {
|
||||
if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
|
||||
// It would probably be fine to support this in earlier phases,
|
||||
// but at the time of writing it's only ever introduced from intrinsic lowering,
|
||||
// but at the time of writing it's only ever introduced from intrinsic lowering
|
||||
// or other runtime-phase optimization passes,
|
||||
// so earlier things can just `bug!` on it.
|
||||
self.fail(location, "PtrMetadata should be in runtime MIR only");
|
||||
}
|
||||
|
||||
check_kinds!(a, "Cannot PtrMetadata non-pointer type {:?}", ty::RawPtr(..));
|
||||
check_kinds!(
|
||||
a,
|
||||
"Cannot PtrMetadata non-pointer non-reference type {:?}",
|
||||
ty::RawPtr(..) | ty::Ref(..)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue