Store a full Ty with each Value.

This commit is contained in:
Camille GILLOT 2025-06-30 19:27:27 +00:00
parent ac70dc85e7
commit be41333604
7 changed files with 122 additions and 136 deletions

View file

@ -130,7 +130,7 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls);
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
let opaque = state.new_opaque();
let opaque = state.new_opaque(body.local_decls[local].ty);
state.assign(local, opaque);
}
@ -238,7 +238,7 @@ struct VnState<'body, 'tcx> {
/// Locals that are assigned that value.
// This vector does not hold all the values of `VnIndex` that we create.
rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
values: FxIndexSet<Value<'tcx>>,
values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>,
/// Values evaluated as constants if possible.
evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
/// Counter to generate different values.
@ -287,8 +287,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
#[instrument(level = "trace", skip(self), ret)]
fn insert(&mut self, value: Value<'tcx>) -> VnIndex {
let (index, new) = self.values.insert_full(value);
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex {
let (index, new) = self.values.insert_full((value, ty));
let index = VnIndex::from_usize(index);
if new {
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
@ -310,20 +310,33 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
/// Create a new `Value` for which we have no information at all, except that it is distinct
/// from all the others.
#[instrument(level = "trace", skip(self), ret)]
fn new_opaque(&mut self) -> VnIndex {
fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex {
let value = Value::Opaque(self.next_opaque());
self.insert(value)
self.insert(ty, value)
}
/// 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) -> VnIndex {
let pty = place.ty(self.local_decls, self.tcx).ty;
let ty = match kind {
AddressKind::Ref(bk) => {
Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, pty, bk.to_mutbl_lossy())
}
AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()),
};
let value = Value::Address { place, kind, provenance: self.next_opaque() };
self.insert(value)
self.insert(ty, value)
}
#[inline]
fn get(&self, index: VnIndex) -> &Value<'tcx> {
self.values.get_index(index.as_usize()).unwrap()
&self.values.get_index(index.as_usize()).unwrap().0
}
#[inline]
fn ty(&self, index: VnIndex) -> Ty<'tcx> {
self.values.get_index(index.as_usize()).unwrap().1
}
/// Record that `local` is assigned `value`. `local` must be SSA.
@ -346,29 +359,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
debug_assert_ne!(disambiguator, 0);
disambiguator
};
self.insert(Value::Constant { value, disambiguator })
self.insert(value.ty(), Value::Constant { value, disambiguator })
}
fn insert_bool(&mut self, flag: bool) -> VnIndex {
// Booleans are deterministic.
let value = Const::from_bool(self.tcx, flag);
debug_assert!(value.is_deterministic());
self.insert(Value::Constant { value, disambiguator: 0 })
self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 })
}
fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex {
fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex {
// Scalars are deterministic.
let value = Const::from_scalar(self.tcx, scalar, ty);
debug_assert!(value.is_deterministic());
self.insert(Value::Constant { value, disambiguator: 0 })
self.insert(ty, Value::Constant { value, disambiguator: 0 })
}
fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values))
fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec<VnIndex>) -> VnIndex {
self.insert(ty, Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values))
}
fn insert_deref(&mut self, value: VnIndex) -> VnIndex {
let value = self.insert(Value::Projection(value, ProjectionElem::Deref));
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
}
@ -376,14 +389,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
fn invalidate_derefs(&mut self) {
for deref in std::mem::take(&mut self.derefs) {
let opaque = self.next_opaque();
*self.values.get_index_mut2(deref.index()).unwrap() = Value::Opaque(opaque);
self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque);
}
}
#[instrument(level = "trace", skip(self), ret)]
fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
use Value::*;
let ty = self.ty(value);
let ty = self.ecx.layout_of(ty).ok()?;
let op = match *self.get(value) {
_ if ty.is_zst() => ImmTy::uninit(ty).into(),
Opaque(_) => return None,
// Do not bother evaluating repeat expressions. This would uselessly consume memory.
Repeat(..) => return None,
@ -391,31 +408,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
Constant { ref value, disambiguator: _ } => {
self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()?
}
Aggregate(kind, variant, ref fields) => {
Aggregate(_, variant, ref fields) => {
let fields = fields
.iter()
.map(|&f| self.evaluated[f].as_ref())
.collect::<Option<Vec<_>>>()?;
let ty = match kind {
AggregateTy::Array => {
assert!(fields.len() > 0);
Ty::new_array(self.tcx, fields[0].layout.ty, fields.len() as u64)
}
AggregateTy::Tuple => {
Ty::new_tup_from_iter(self.tcx, fields.iter().map(|f| f.layout.ty))
}
AggregateTy::Def(def_id, args) => {
self.tcx.type_of(def_id).instantiate(self.tcx, args)
}
};
let variant = if ty.is_enum() { Some(variant) } else { None };
let ty = self.ecx.layout_of(ty).ok()?;
if ty.is_zst() {
ImmTy::uninit(ty).into()
} else if matches!(
ty.backend_repr,
BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)
) {
let variant = if ty.ty.is_enum() { Some(variant) } else { None };
if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..))
{
let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
let variant_dest = if let Some(variant) = variant {
self.ecx.project_downcast(&dest, variant).discard_err()?
@ -440,11 +440,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
return None;
}
}
RawPtr { pointer, metadata, output_pointer_ty, data_pointer_ty: _ } => {
RawPtr { pointer, metadata, output_pointer_ty: _, data_pointer_ty: _ } => {
let pointer = self.evaluated[pointer].as_ref()?;
let metadata = self.evaluated[metadata].as_ref()?;
let output_pointer_ty = self.ecx.layout_of(output_pointer_ty).ok()?;
debug_assert!(!output_pointer_ty.is_zst());
// Pointers don't have fields, so don't `project_field` them.
let data = self.ecx.read_pointer(pointer).discard_err()?;
@ -454,7 +452,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
MemPlaceMeta::Meta(self.ecx.read_scalar(metadata).discard_err()?)
};
let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx);
ImmTy::from_immediate(ptr_imm, output_pointer_ty).into()
ImmTy::from_immediate(ptr_imm, ty).into()
}
Projection(base, elem) => {
@ -481,7 +479,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
};
self.ecx.project(value, elem).discard_err()?
}
Address { place, kind, provenance: _ } => {
Address { place, kind: _, provenance: _ } => {
if !place.is_indirect_first_projection() {
return None;
}
@ -497,19 +495,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
mplace = self.ecx.project(&mplace, proj).discard_err()?;
}
let pointer = mplace.to_ref(&self.ecx);
let ty = match kind {
AddressKind::Ref(bk) => Ty::new_ref(
self.tcx,
self.tcx.lifetimes.re_erased,
mplace.layout.ty,
bk.to_mutbl_lossy(),
),
AddressKind::Address(mutbl) => {
Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy())
}
};
let layout = self.ecx.layout_of(ty).ok()?;
ImmTy::from_immediate(pointer, layout).into()
ImmTy::from_immediate(pointer, ty).into()
}
Discriminant(base) => {
@ -521,32 +507,28 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
Len(slice) => {
let slice = self.evaluated[slice].as_ref()?;
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
let len = slice.len(&self.ecx).discard_err()?;
let imm = ImmTy::from_uint(len, usize_layout);
imm.into()
ImmTy::from_uint(len, ty).into()
}
NullaryOp(null_op, ty) => {
let layout = self.ecx.layout_of(ty).ok()?;
NullaryOp(null_op, arg_ty) => {
let arg_layout = self.ecx.layout_of(arg_ty).ok()?;
if let NullOp::SizeOf | NullOp::AlignOf = null_op
&& layout.is_unsized()
&& arg_layout.is_unsized()
{
return None;
}
let val = match null_op {
NullOp::SizeOf => layout.size.bytes(),
NullOp::AlignOf => layout.align.abi.bytes(),
NullOp::SizeOf => arg_layout.size.bytes(),
NullOp::AlignOf => arg_layout.align.abi.bytes(),
NullOp::OffsetOf(fields) => self
.ecx
.tcx
.offset_of_subfield(self.typing_env(), layout, fields.iter())
.offset_of_subfield(self.typing_env(), arg_layout, fields.iter())
.bytes(),
NullOp::UbChecks => return None,
NullOp::ContractChecks => return None,
};
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
let imm = ImmTy::from_uint(val, usize_layout);
imm.into()
ImmTy::from_uint(val, ty).into()
}
UnaryOp(un_op, operand) => {
let operand = self.evaluated[operand].as_ref()?;
@ -562,30 +544,27 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?;
val.into()
}
Cast { kind, value, from: _, to } => match kind {
Cast { kind, value, from: _, to: _ } => match kind {
CastKind::IntToInt | CastKind::IntToFloat => {
let value = self.evaluated[value].as_ref()?;
let value = self.ecx.read_immediate(value).discard_err()?;
let to = self.ecx.layout_of(to).ok()?;
let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?;
let res = self.ecx.int_to_int_or_float(&value, ty).discard_err()?;
res.into()
}
CastKind::FloatToFloat | CastKind::FloatToInt => {
let value = self.evaluated[value].as_ref()?;
let value = self.ecx.read_immediate(value).discard_err()?;
let to = self.ecx.layout_of(to).ok()?;
let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?;
let res = self.ecx.float_to_float_or_int(&value, ty).discard_err()?;
res.into()
}
CastKind::Transmute => {
let value = self.evaluated[value].as_ref()?;
let to = self.ecx.layout_of(to).ok()?;
// `offset` for immediates generally only supports projections that match the
// type of the immediate. However, as a HACK, we exploit that it can also do
// limited transmutes: it only works between types with the same layout, and
// cannot transmute pointers to integers.
if value.as_mplace_or_imm().is_right() {
let can_transmute = match (value.layout.backend_repr, to.backend_repr) {
let can_transmute = match (value.layout.backend_repr, ty.backend_repr) {
(BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => {
s1.size(&self.ecx) == s2.size(&self.ecx)
&& !matches!(s1.primitive(), Primitive::Pointer(..))
@ -605,13 +584,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
return None;
}
}
value.offset(Size::ZERO, to, &self.ecx).discard_err()?
value.offset(Size::ZERO, ty, &self.ecx).discard_err()?
}
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => {
let src = self.evaluated[value].as_ref()?;
let to = self.ecx.layout_of(to).ok()?;
let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_err()?;
self.ecx.unsize_into(src, to, &dest.clone().into()).discard_err()?;
let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
self.ecx.unsize_into(src, ty, &dest.clone().into()).discard_err()?;
self.ecx
.alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id())
.discard_err()?;
@ -620,15 +598,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
let src = self.evaluated[value].as_ref()?;
let src = self.ecx.read_immediate(src).discard_err()?;
let to = self.ecx.layout_of(to).ok()?;
let ret = self.ecx.ptr_to_ptr(&src, to).discard_err()?;
let ret = self.ecx.ptr_to_ptr(&src, ty).discard_err()?;
ret.into()
}
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => {
let src = self.evaluated[value].as_ref()?;
let src = self.ecx.read_immediate(src).discard_err()?;
let to = self.ecx.layout_of(to).ok()?;
ImmTy::from_immediate(*src, to).into()
ImmTy::from_immediate(*src, ty).into()
}
_ => return None,
},
@ -638,21 +614,20 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
fn project(
&mut self,
place: PlaceRef<'tcx>,
place_ty: PlaceTy<'tcx>,
value: VnIndex,
proj: PlaceElem<'tcx>,
from_non_ssa_index: &mut bool,
) -> Option<VnIndex> {
) -> Option<(PlaceTy<'tcx>, VnIndex)> {
let projection_ty = place_ty.projection_ty(self.tcx, proj);
let proj = match proj {
ProjectionElem::Deref => {
let ty = place.ty(self.local_decls, self.tcx).ty;
if let Some(Mutability::Not) = ty.ref_mutability()
&& let Some(pointee_ty) = ty.builtin_deref(true)
&& pointee_ty.is_freeze(self.tcx, self.typing_env())
if let Some(Mutability::Not) = place_ty.ty.ref_mutability()
&& projection_ty.ty.is_freeze(self.tcx, self.typing_env())
{
// 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(self.insert_deref(value));
return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
} else {
return None;
}
@ -660,7 +635,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index),
ProjectionElem::Field(f, ty) => {
if let Value::Aggregate(_, _, fields) = self.get(value) {
return Some(fields[f.as_usize()]);
return Some((projection_ty, fields[f.as_usize()]));
} else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value)
&& let Value::Aggregate(_, written_variant, fields) = self.get(*outer_value)
// This pass is not aware of control-flow, so we do not know whether the
@ -680,14 +655,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
// a downcast to an inactive variant.
&& written_variant == read_variant
{
return Some(fields[f.as_usize()]);
return Some((projection_ty, fields[f.as_usize()]));
}
ProjectionElem::Field(f, ty)
}
ProjectionElem::Index(idx) => {
if let Value::Repeat(inner, _) = self.get(value) {
*from_non_ssa_index |= self.locals[idx].is_none();
return Some(*inner);
return Some((projection_ty, *inner));
}
let idx = self.locals[idx]?;
ProjectionElem::Index(idx)
@ -695,7 +670,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
match self.get(value) {
Value::Repeat(inner, _) => {
return Some(*inner);
return Some((projection_ty, *inner));
}
Value::Aggregate(AggregateTy::Array, _, operands) => {
let offset = if from_end {
@ -703,7 +678,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
} else {
offset as usize
};
return operands.get(offset).copied();
let value = operands.get(offset).copied()?;
return Some((projection_ty, value));
}
_ => {}
};
@ -717,7 +693,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty),
};
Some(self.insert(Value::Projection(value, proj)))
let value = self.insert(projection_ty.ty, Value::Projection(value, proj));
Some((projection_ty, value))
}
/// Simplify the projection chain if we know better.
@ -779,6 +756,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
// Invariant: `value` holds the value up-to the `index`th projection excluded.
let mut value = self.locals[place.local]?;
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
let mut from_non_ssa_index = false;
for (index, proj) in place.projection.iter().enumerate() {
if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
@ -796,8 +775,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
place_ref = PlaceRef { local, projection: &place.projection[index..] };
}
let base = PlaceRef { local: place.local, projection: &place.projection[..index] };
value = self.project(base, value, proj, &mut from_non_ssa_index)?;
(place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?;
}
if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
@ -906,8 +884,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
// Unsupported values.
Rvalue::ThreadLocalRef(..) | Rvalue::ShallowInitBox(..) => return None,
};
debug!(?value);
Some(self.insert(value))
let ty = rvalue.ty(self.local_decls, self.tcx);
Some(self.insert(ty, value))
}
fn simplify_discriminant(&mut self, place: VnIndex) -> Option<VnIndex> {
@ -917,7 +895,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
{
let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args);
let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?;
return Some(self.insert_scalar(discr.to_scalar(), discr.layout.ty));
return Some(self.insert_scalar(discr.layout.ty, discr.to_scalar()));
}
None
@ -1014,9 +992,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
rvalue: &mut Rvalue<'tcx>,
location: Location,
) -> Option<VnIndex> {
let tcx = self.tcx;
let 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(..)
@ -1031,17 +1011,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
};
if is_zst {
let ty = rvalue.ty(self.local_decls, tcx);
return Some(self.insert_constant(Const::zero_sized(ty)));
}
}
let fields: Vec<_> = field_ops
.iter_mut()
.map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque()))
.map(|op| {
self.simplify_operand(op, location)
.unwrap_or_else(|| self.new_opaque(op.ty(self.local_decls, self.tcx)))
})
.collect();
let (ty, variant_index) = match *kind {
let (aty, variant_index) = match *kind {
AggregateKind::Array(..) => {
assert!(!field_ops.is_empty());
(AggregateTy::Array, FIRST_VARIANT)
@ -1058,10 +1040,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
// Do not track unions.
AggregateKind::Adt(_, _, _, _, Some(_)) => return None,
AggregateKind::RawPtr(pointee_ty, mtbl) => {
AggregateKind::RawPtr(..) => {
assert_eq!(field_ops.len(), 2);
let mut data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx);
let output_pointer_ty = Ty::new_ptr(self.tcx, pointee_ty, mtbl);
let [mut pointer, metadata] = fields.try_into().unwrap();
@ -1074,7 +1055,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
to: _,
} = self.get(pointer)
&& let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind()
&& let ty::RawPtr(_, output_mtbl) = output_pointer_ty.kind()
&& let ty::RawPtr(_, output_mtbl) = ty.kind()
&& from_mtbl == output_mtbl
&& from_pointee_ty.is_sized(self.tcx, self.typing_env())
{
@ -1087,16 +1068,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
field_ops[FieldIdx::ZERO] = op;
}
return Some(self.insert(Value::RawPtr {
output_pointer_ty,
data_pointer_ty,
pointer,
metadata,
}));
return Some(self.insert(
ty,
Value::RawPtr { output_pointer_ty: ty, data_pointer_ty, pointer, metadata },
));
}
};
if let AggregateTy::Array = ty
if let AggregateTy::Array = aty
&& fields.len() > 4
{
let first = fields[0];
@ -1105,18 +1084,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
if let Some(op) = self.try_as_operand(first, location) {
*rvalue = Rvalue::Repeat(op, len);
}
return Some(self.insert(Value::Repeat(first, len)));
return Some(self.insert(ty, Value::Repeat(first, len)));
}
}
if let AggregateTy::Def(_, _) = ty
if let AggregateTy::Def(_, _) = aty
&& let Some(value) =
self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index)
{
return Some(value);
}
Some(self.insert(Value::Aggregate(ty, variant_index, fields)))
Some(self.insert(ty, Value::Aggregate(aty, variant_index, fields)))
}
#[instrument(level = "trace", skip(self), ret)]
@ -1197,7 +1176,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
_ => Value::UnaryOp(op, arg_index),
};
Some(self.insert(value))
let ty = op.ty(self.tcx, self.ty(arg_index));
Some(self.insert(ty, value))
}
#[instrument(level = "trace", skip(self), ret)]
@ -1243,8 +1223,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) {
return Some(value);
}
let ty = op.ty(self.tcx, self.ty(lhs), self.ty(rhs));
let value = Value::BinaryOp(op, lhs, rhs);
Some(self.insert(value))
Some(self.insert(ty, value))
}
fn simplify_binary_inner(
@ -1336,19 +1317,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
| BinOp::Shr,
Left(0),
_,
) => self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty),
) => self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size)),
// Attempt to simplify `x | ALL_ONES` to `ALL_ONES`.
(BinOp::BitOr, _, Left(ones)) | (BinOp::BitOr, Left(ones), _)
if ones == layout.size.truncate(u128::MAX)
|| (layout.ty.is_bool() && ones == 1) =>
{
self.insert_scalar(Scalar::from_uint(ones, layout.size), lhs_ty)
self.insert_scalar(lhs_ty, Scalar::from_uint(ones, layout.size))
}
// Sub/Xor with itself.
(BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked | BinOp::BitXor, a, b)
if a == b =>
{
self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty)
self.insert_scalar(lhs_ty, Scalar::from_uint(0u128, layout.size))
}
// Comparison:
// - if both operands can be computed as bits, just compare the bits;
@ -1362,8 +1343,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
};
if op.is_overflowing() {
let ty = Ty::new_tup(self.tcx, &[self.ty(result), self.tcx.types.bool]);
let false_val = self.insert_bool(false);
Some(self.insert_tuple(vec![result, false_val]))
Some(self.insert_tuple(ty, vec![result, false_val]))
} else {
Some(result)
}
@ -1389,7 +1371,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind {
// Each reification of a generic fn may get a different pointer.
// Do not try to merge them.
return Some(self.new_opaque());
return Some(self.new_opaque(to));
}
let mut was_ever_updated = false;
@ -1497,7 +1479,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
*initial_kind = kind;
}
Some(self.insert(Value::Cast { kind, value, from, to }))
Some(self.insert(to, Value::Cast { kind, value, from, to }))
}
fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> {
@ -1530,7 +1512,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
// Fallback: a symbolic `Len`.
Some(self.insert(Value::Len(inner)))
Some(self.insert(self.tcx.types.usize, Value::Len(inner)))
}
fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool {
@ -1807,11 +1789,12 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
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(self.local_decls, self.tcx)
&& self.local_decls[local].ty == rvalue_ty
{
let value = value.unwrap_or_else(|| self.new_opaque());
let value = value.unwrap_or_else(|| self.new_opaque(rvalue_ty));
self.assign(local, value);
}
}
@ -1821,7 +1804,8 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
if let Some(local) = destination.as_local()
&& self.ssa.is_ssa(local)
{
let opaque = self.new_opaque();
let ty = self.local_decls[local].ty;
let opaque = self.new_opaque(ty);
self.assign(local, opaque);
}
}

View file

@ -56,7 +56,7 @@ pub unsafe fn undef_union_as_integer() -> u32 {
pub unsafe fn unreachable_direct() -> ! {
// CHECK-LABEL: fn unreachable_direct(
// CHECK: = const ();
// CHECK: = const () as Never (Transmute);
// CHECK: = const ZeroSized: Never;
let x: Never = unsafe { transmute(()) };
match x {}
}

View file

@ -15,7 +15,7 @@
- _2 = ();
- _1 = move _2 as Never (Transmute);
+ _2 = const ();
+ _1 = const () as Never (Transmute);
+ _1 = const ZeroSized: Never;
unreachable;
}
}

View file

@ -15,7 +15,7 @@
- _2 = ();
- _1 = move _2 as Never (Transmute);
+ _2 = const ();
+ _1 = const () as Never (Transmute);
+ _1 = const ZeroSized: Never;
unreachable;
}
}

View file

@ -18,7 +18,8 @@
bb0: {
_4 = copy _1 as *const T (PtrToPtr);
_5 = PtrMetadata(copy _4);
- _5 = PtrMetadata(copy _4);
+ _5 = const ();
_6 = copy _1 as *const (&A, [T]) (PtrToPtr);
- _7 = PtrMetadata(copy _6);
+ _7 = PtrMetadata(copy _1);

View file

@ -18,7 +18,8 @@
bb0: {
_4 = copy _1 as *const T (PtrToPtr);
_5 = PtrMetadata(copy _4);
- _5 = PtrMetadata(copy _4);
+ _5 = const ();
_6 = copy _1 as *const (&A, [T]) (PtrToPtr);
- _7 = PtrMetadata(copy _6);
+ _7 = PtrMetadata(copy _1);

View file

@ -869,7 +869,7 @@ fn generic_cast_metadata<T, A: ?Sized, B: ?Sized>(ps: *const [T], pa: *const A,
// Metadata usize -> (), do not optimize.
// CHECK: [[T:_.+]] = copy _1 as
// CHECK-NEXT: PtrMetadata(copy [[T]])
// CHECK-NEXT: const ();
let t1 = CastPtrToPtr::<_, *const T>(ps);
let m1 = PtrMetadata(t1);