1
Fork 0

[mir-opt] GVN some more transmute cases

We already did `Transmute`-then-`PtrToPtr`; this adds the nearly-identical `PtrToPtr`-then-`Transmute`.

It also adds `transmute(Foo(x))` → `transmute(x)`, when `Foo` is a single-field transparent type.  That's useful for things like `NonNull { pointer: p }.as_ptr()`.

Found these as I was looking at MCP807-related changes.
This commit is contained in:
Scott McMurray 2024-11-21 17:59:43 -08:00
parent e26ff2f908
commit 8dcc676c92
30 changed files with 1045 additions and 659 deletions

View file

@ -1368,7 +1368,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
let mut was_updated = false;
// If that cast just casts away the metadata again,
// Transmuting `*const T` <=> `*mut T` is just a pointer cast,
// which we might be able to merge with other ones later.
if let Transmute = kind
&& let ty::RawPtr(from_pointee, _) = from.kind()
&& let ty::RawPtr(to_pointee, _) = to.kind()
&& from_pointee == to_pointee
{
*kind = PtrToPtr;
was_updated = true;
}
// If a cast just casts away the metadata again, then we can get it by
// casting the original thin pointer passed to `from_raw_parts`
if let PtrToPtr = kind
&& let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) =
self.get(value)
@ -1397,6 +1409,28 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
}
// Aggregate-then-Transmute can just transmute the original field value,
// so long as the type is transparent over only that one single field.
if let Transmute = kind
&& let Value::Aggregate(
AggregateTy::Def(aggregate_did, aggregate_args),
FIRST_VARIANT,
field_values,
) = self.get(value)
&& let [single_field_value] = **field_values
&& let adt = self.tcx.adt_def(aggregate_did)
&& adt.is_struct()
&& adt.repr().transparent()
{
let field_ty = adt.non_enum_variant().single_field().ty(self.tcx, aggregate_args);
from = field_ty;
value = single_field_value;
was_updated = true;
if field_ty == to {
return Some(single_field_value);
}
}
// PtrToPtr-then-Transmute can just transmute the original, so long as the
// PtrToPtr didn't change metadata (and thus the size of the pointer)
if let Transmute = kind
@ -1416,6 +1450,26 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
}
// PtrToPtr-then-Transmute can just transmute the original, so long as the
// PtrToPtr didn't change metadata (and thus the size of the pointer)
if let PtrToPtr = kind
&& let Value::Cast {
kind: Transmute,
value: inner_value,
from: inner_from,
to: _inner_to,
} = *self.get(value)
&& self.pointers_have_same_metadata(from, to)
{
*kind = Transmute;
from = inner_from;
value = inner_value;
was_updated = true;
if inner_from == to {
return Some(inner_value);
}
}
if was_updated && let Some(op) = self.try_as_operand(value, location) {
*operand = op;
}

View file

@ -173,29 +173,6 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
*kind = CastKind::IntToInt;
return;
}
// Transmuting a transparent struct/union to a field's type is a projection
if let ty::Adt(adt_def, args) = operand_ty.kind()
&& adt_def.repr().transparent()
&& (adt_def.is_struct() || adt_def.is_union())
&& let Some(place) = operand.place()
{
let variant = adt_def.non_enum_variant();
for (i, field) in variant.fields.iter_enumerated() {
let field_ty = field.ty(self.tcx, args);
if field_ty == *cast_ty {
let place = place
.project_deeper(&[ProjectionElem::Field(i, *cast_ty)], self.tcx);
let operand = if operand.is_move() {
Operand::Move(place)
} else {
Operand::Copy(place)
};
*rvalue = Rvalue::Use(operand);
return;
}
}
}
}
}
}