1use std::borrow::Cow;
88
89use either::Either;
90use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx};
91use rustc_const_eval::const_eval::DummyMachine;
92use rustc_const_eval::interpret::{
93 ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar,
94 intern_const_alloc_for_constprop,
95};
96use rustc_data_structures::fx::{FxIndexSet, MutableValues};
97use rustc_data_structures::graph::dominators::Dominators;
98use rustc_hir::def::DefKind;
99use rustc_index::bit_set::DenseBitSet;
100use rustc_index::{IndexVec, newtype_index};
101use rustc_middle::bug;
102use rustc_middle::mir::interpret::GlobalAlloc;
103use rustc_middle::mir::visit::*;
104use rustc_middle::mir::*;
105use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
106use rustc_middle::ty::{self, Ty, TyCtxt};
107use rustc_span::DUMMY_SP;
108use rustc_span::def_id::DefId;
109use smallvec::SmallVec;
110use tracing::{debug, instrument, trace};
111
112use crate::ssa::SsaLocals;
113
114pub(super) struct GVN;
115
116impl<'tcx> crate::MirPass<'tcx> for GVN {
117 fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
118 sess.mir_opt_level() >= 2
119 }
120
121 #[instrument(level = "trace", skip(self, tcx, body))]
122 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
123 debug!(def_id = ?body.source.def_id());
124
125 let typing_env = body.typing_env(tcx);
126 let ssa = SsaLocals::new(tcx, body, typing_env);
127 let dominators = body.basic_blocks.dominators().clone();
129
130 let mut state = VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls);
131
132 for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
133 let opaque = state.new_opaque();
134 state.assign(local, opaque);
135 }
136
137 let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
138 for bb in reverse_postorder {
139 let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb];
140 state.visit_basic_block_data(bb, data);
141 }
142
143 StorageRemover { tcx, reused_locals: state.reused_locals }.visit_body_preserves_cfg(body);
147 }
148
149 fn is_required(&self) -> bool {
150 false
151 }
152}
153
154newtype_index! {
155 struct VnIndex {}
156}
157
158#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
161enum AggregateTy<'tcx> {
162 Array,
164 Tuple,
165 Def(DefId, ty::GenericArgsRef<'tcx>),
166 RawPtr {
167 data_pointer_ty: Ty<'tcx>,
169 output_pointer_ty: Ty<'tcx>,
171 },
172}
173
174#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
175enum AddressKind {
176 Ref(BorrowKind),
177 Address(RawPtrKind),
178}
179
180#[derive(Debug, PartialEq, Eq, Hash)]
181enum Value<'tcx> {
182 Opaque(usize),
186 Constant {
188 value: Const<'tcx>,
189 disambiguator: usize,
193 },
194 Aggregate(AggregateTy<'tcx>, VariantIdx, Vec<VnIndex>),
197 Repeat(VnIndex, ty::Const<'tcx>),
199 Address {
201 place: Place<'tcx>,
202 kind: AddressKind,
203 provenance: usize,
205 },
206
207 Projection(VnIndex, ProjectionElem<VnIndex, Ty<'tcx>>),
210 Discriminant(VnIndex),
212 Len(VnIndex),
214
215 NullaryOp(NullOp<'tcx>, Ty<'tcx>),
217 UnaryOp(UnOp, VnIndex),
218 BinaryOp(BinOp, VnIndex, VnIndex),
219 Cast {
220 kind: CastKind,
221 value: VnIndex,
222 from: Ty<'tcx>,
223 to: Ty<'tcx>,
224 },
225}
226
227struct VnState<'body, 'tcx> {
228 tcx: TyCtxt<'tcx>,
229 ecx: InterpCx<'tcx, DummyMachine>,
230 local_decls: &'body LocalDecls<'tcx>,
231 locals: IndexVec<Local, Option<VnIndex>>,
233 rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>,
236 values: FxIndexSet<Value<'tcx>>,
237 evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>,
239 next_opaque: usize,
241 derefs: Vec<VnIndex>,
243 feature_unsized_locals: bool,
245 ssa: &'body SsaLocals,
246 dominators: Dominators<BasicBlock>,
247 reused_locals: DenseBitSet<Local>,
248}
249
250impl<'body, 'tcx> VnState<'body, 'tcx> {
251 fn new(
252 tcx: TyCtxt<'tcx>,
253 body: &Body<'tcx>,
254 typing_env: ty::TypingEnv<'tcx>,
255 ssa: &'body SsaLocals,
256 dominators: Dominators<BasicBlock>,
257 local_decls: &'body LocalDecls<'tcx>,
258 ) -> Self {
259 let num_values =
264 2 * body.basic_blocks.iter().map(|bbdata| bbdata.statements.len()).sum::<usize>()
265 + 4 * body.basic_blocks.len();
266 VnState {
267 tcx,
268 ecx: InterpCx::new(tcx, DUMMY_SP, typing_env, DummyMachine),
269 local_decls,
270 locals: IndexVec::from_elem(None, local_decls),
271 rev_locals: IndexVec::with_capacity(num_values),
272 values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
273 evaluated: IndexVec::with_capacity(num_values),
274 next_opaque: 1,
275 derefs: Vec::new(),
276 feature_unsized_locals: tcx.features().unsized_locals(),
277 ssa,
278 dominators,
279 reused_locals: DenseBitSet::new_empty(local_decls.len()),
280 }
281 }
282
283 fn typing_env(&self) -> ty::TypingEnv<'tcx> {
284 self.ecx.typing_env()
285 }
286
287 #[instrument(level = "trace", skip(self), ret)]
288 fn insert(&mut self, value: Value<'tcx>) -> VnIndex {
289 let (index, new) = self.values.insert_full(value);
290 let index = VnIndex::from_usize(index);
291 if new {
292 let evaluated = self.eval_to_const(index);
294 let _index = self.evaluated.push(evaluated);
295 debug_assert_eq!(index, _index);
296 let _index = self.rev_locals.push(SmallVec::new());
297 debug_assert_eq!(index, _index);
298 }
299 index
300 }
301
302 fn next_opaque(&mut self) -> usize {
303 let next_opaque = self.next_opaque;
304 self.next_opaque += 1;
305 next_opaque
306 }
307
308 #[instrument(level = "trace", skip(self), ret)]
311 fn new_opaque(&mut self) -> VnIndex {
312 let value = Value::Opaque(self.next_opaque());
313 self.insert(value)
314 }
315
316 #[instrument(level = "trace", skip(self), ret)]
318 fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex {
319 let value = Value::Address { place, kind, provenance: self.next_opaque() };
320 self.insert(value)
321 }
322
323 fn get(&self, index: VnIndex) -> &Value<'tcx> {
324 self.values.get_index(index.as_usize()).unwrap()
325 }
326
327 #[instrument(level = "trace", skip(self))]
329 fn assign(&mut self, local: Local, value: VnIndex) {
330 debug_assert!(self.ssa.is_ssa(local));
331 self.locals[local] = Some(value);
332
333 let is_sized = !self.feature_unsized_locals
335 || self.local_decls[local].ty.is_sized(self.tcx, self.typing_env());
336 if is_sized {
337 self.rev_locals[value].push(local);
338 }
339 }
340
341 fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
342 let disambiguator = if value.is_deterministic() {
343 0
345 } else {
346 let disambiguator = self.next_opaque();
349 debug_assert_ne!(disambiguator, 0);
351 disambiguator
352 };
353 self.insert(Value::Constant { value, disambiguator })
354 }
355
356 fn insert_bool(&mut self, flag: bool) -> VnIndex {
357 let value = Const::from_bool(self.tcx, flag);
359 debug_assert!(value.is_deterministic());
360 self.insert(Value::Constant { value, disambiguator: 0 })
361 }
362
363 fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex {
364 let value = Const::from_scalar(self.tcx, scalar, ty);
366 debug_assert!(value.is_deterministic());
367 self.insert(Value::Constant { value, disambiguator: 0 })
368 }
369
370 fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
371 self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values))
372 }
373
374 fn insert_deref(&mut self, value: VnIndex) -> VnIndex {
375 let value = self.insert(Value::Projection(value, ProjectionElem::Deref));
376 self.derefs.push(value);
377 value
378 }
379
380 fn invalidate_derefs(&mut self) {
381 for deref in std::mem::take(&mut self.derefs) {
382 let opaque = self.next_opaque();
383 *self.values.get_index_mut2(deref.index()).unwrap() = Value::Opaque(opaque);
384 }
385 }
386
387 #[instrument(level = "trace", skip(self), ret)]
388 fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
389 use Value::*;
390 let op = match *self.get(value) {
391 Opaque(_) => return None,
392 Repeat(..) => return None,
394
395 Constant { ref value, disambiguator: _ } => {
396 self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()?
397 }
398 Aggregate(kind, variant, ref fields) => {
399 let fields = fields
400 .iter()
401 .map(|&f| self.evaluated[f].as_ref())
402 .collect::<Option<Vec<_>>>()?;
403 let ty = match kind {
404 AggregateTy::Array => {
405 assert!(fields.len() > 0);
406 Ty::new_array(self.tcx, fields[0].layout.ty, fields.len() as u64)
407 }
408 AggregateTy::Tuple => {
409 Ty::new_tup_from_iter(self.tcx, fields.iter().map(|f| f.layout.ty))
410 }
411 AggregateTy::Def(def_id, args) => {
412 self.tcx.type_of(def_id).instantiate(self.tcx, args)
413 }
414 AggregateTy::RawPtr { output_pointer_ty, .. } => output_pointer_ty,
415 };
416 let variant = if ty.is_enum() { Some(variant) } else { None };
417 let ty = self.ecx.layout_of(ty).ok()?;
418 if ty.is_zst() {
419 ImmTy::uninit(ty).into()
420 } else if matches!(kind, AggregateTy::RawPtr { .. }) {
421 let data = self.ecx.read_pointer(fields[0]).discard_err()?;
423 let meta = if fields[1].layout.is_zst() {
424 MemPlaceMeta::None
425 } else {
426 MemPlaceMeta::Meta(self.ecx.read_scalar(fields[1]).discard_err()?)
427 };
428 let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx);
429 ImmTy::from_immediate(ptr_imm, ty).into()
430 } else if matches!(
431 ty.backend_repr,
432 BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)
433 ) {
434 let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
435 let variant_dest = if let Some(variant) = variant {
436 self.ecx.project_downcast(&dest, variant).discard_err()?
437 } else {
438 dest.clone()
439 };
440 for (field_index, op) in fields.into_iter().enumerate() {
441 let field_dest =
442 self.ecx.project_field(&variant_dest, field_index).discard_err()?;
443 self.ecx.copy_op(op, &field_dest).discard_err()?;
444 }
445 self.ecx
446 .write_discriminant(variant.unwrap_or(FIRST_VARIANT), &dest)
447 .discard_err()?;
448 self.ecx
449 .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id())
450 .discard_err()?;
451 dest.into()
452 } else {
453 return None;
454 }
455 }
456
457 Projection(base, elem) => {
458 let value = self.evaluated[base].as_ref()?;
459 let elem = match elem {
460 ProjectionElem::Deref => ProjectionElem::Deref,
461 ProjectionElem::Downcast(name, read_variant) => {
462 ProjectionElem::Downcast(name, read_variant)
463 }
464 ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty),
465 ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
466 ProjectionElem::ConstantIndex { offset, min_length, from_end }
467 }
468 ProjectionElem::Subslice { from, to, from_end } => {
469 ProjectionElem::Subslice { from, to, from_end }
470 }
471 ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
472 ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty),
473 ProjectionElem::UnwrapUnsafeBinder(ty) => {
474 ProjectionElem::UnwrapUnsafeBinder(ty)
475 }
476 ProjectionElem::Index(_) => return None,
478 };
479 self.ecx.project(value, elem).discard_err()?
480 }
481 Address { place, kind, provenance: _ } => {
482 if !place.is_indirect_first_projection() {
483 return None;
484 }
485 let local = self.locals[place.local]?;
486 let pointer = self.evaluated[local].as_ref()?;
487 let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?;
488 for proj in place.projection.iter().skip(1) {
489 if matches!(proj, ProjectionElem::Index(_)) {
492 return None;
493 }
494 mplace = self.ecx.project(&mplace, proj).discard_err()?;
495 }
496 let pointer = mplace.to_ref(&self.ecx);
497 let ty = match kind {
498 AddressKind::Ref(bk) => Ty::new_ref(
499 self.tcx,
500 self.tcx.lifetimes.re_erased,
501 mplace.layout.ty,
502 bk.to_mutbl_lossy(),
503 ),
504 AddressKind::Address(mutbl) => {
505 Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy())
506 }
507 };
508 let layout = self.ecx.layout_of(ty).ok()?;
509 ImmTy::from_immediate(pointer, layout).into()
510 }
511
512 Discriminant(base) => {
513 let base = self.evaluated[base].as_ref()?;
514 let variant = self.ecx.read_discriminant(base).discard_err()?;
515 let discr_value =
516 self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?;
517 discr_value.into()
518 }
519 Len(slice) => {
520 let slice = self.evaluated[slice].as_ref()?;
521 let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
522 let len = slice.len(&self.ecx).discard_err()?;
523 let imm = ImmTy::from_uint(len, usize_layout);
524 imm.into()
525 }
526 NullaryOp(null_op, ty) => {
527 let layout = self.ecx.layout_of(ty).ok()?;
528 if let NullOp::SizeOf | NullOp::AlignOf = null_op
529 && layout.is_unsized()
530 {
531 return None;
532 }
533 let val = match null_op {
534 NullOp::SizeOf => layout.size.bytes(),
535 NullOp::AlignOf => layout.align.abi.bytes(),
536 NullOp::OffsetOf(fields) => self
537 .ecx
538 .tcx
539 .offset_of_subfield(self.typing_env(), layout, fields.iter())
540 .bytes(),
541 NullOp::UbChecks => return None,
542 NullOp::ContractChecks => return None,
543 };
544 let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
545 let imm = ImmTy::from_uint(val, usize_layout);
546 imm.into()
547 }
548 UnaryOp(un_op, operand) => {
549 let operand = self.evaluated[operand].as_ref()?;
550 let operand = self.ecx.read_immediate(operand).discard_err()?;
551 let val = self.ecx.unary_op(un_op, &operand).discard_err()?;
552 val.into()
553 }
554 BinaryOp(bin_op, lhs, rhs) => {
555 let lhs = self.evaluated[lhs].as_ref()?;
556 let lhs = self.ecx.read_immediate(lhs).discard_err()?;
557 let rhs = self.evaluated[rhs].as_ref()?;
558 let rhs = self.ecx.read_immediate(rhs).discard_err()?;
559 let val = self.ecx.binary_op(bin_op, &lhs, &rhs).discard_err()?;
560 val.into()
561 }
562 Cast { kind, value, from: _, to } => match kind {
563 CastKind::IntToInt | CastKind::IntToFloat => {
564 let value = self.evaluated[value].as_ref()?;
565 let value = self.ecx.read_immediate(value).discard_err()?;
566 let to = self.ecx.layout_of(to).ok()?;
567 let res = self.ecx.int_to_int_or_float(&value, to).discard_err()?;
568 res.into()
569 }
570 CastKind::FloatToFloat | CastKind::FloatToInt => {
571 let value = self.evaluated[value].as_ref()?;
572 let value = self.ecx.read_immediate(value).discard_err()?;
573 let to = self.ecx.layout_of(to).ok()?;
574 let res = self.ecx.float_to_float_or_int(&value, to).discard_err()?;
575 res.into()
576 }
577 CastKind::Transmute => {
578 let value = self.evaluated[value].as_ref()?;
579 let to = self.ecx.layout_of(to).ok()?;
580 if value.as_mplace_or_imm().is_right() {
585 let can_transmute = match (value.layout.backend_repr, to.backend_repr) {
586 (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => {
587 s1.size(&self.ecx) == s2.size(&self.ecx)
588 && !matches!(s1.primitive(), Primitive::Pointer(..))
589 }
590 (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
591 a1.size(&self.ecx) == a2.size(&self.ecx) &&
592 b1.size(&self.ecx) == b2.size(&self.ecx) &&
593 b1.align(&self.ecx) == b2.align(&self.ecx) &&
595 !matches!(a1.primitive(), Primitive::Pointer(..))
597 && !matches!(b1.primitive(), Primitive::Pointer(..))
598 }
599 _ => false,
600 };
601 if !can_transmute {
602 return None;
603 }
604 }
605 value.offset(Size::ZERO, to, &self.ecx).discard_err()?
606 }
607 CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) => {
608 let src = self.evaluated[value].as_ref()?;
609 let to = self.ecx.layout_of(to).ok()?;
610 let dest = self.ecx.allocate(to, MemoryKind::Stack).discard_err()?;
611 self.ecx.unsize_into(src, to, &dest.clone().into()).discard_err()?;
612 self.ecx
613 .alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id())
614 .discard_err()?;
615 dest.into()
616 }
617 CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
618 let src = self.evaluated[value].as_ref()?;
619 let src = self.ecx.read_immediate(src).discard_err()?;
620 let to = self.ecx.layout_of(to).ok()?;
621 let ret = self.ecx.ptr_to_ptr(&src, to).discard_err()?;
622 ret.into()
623 }
624 CastKind::PointerCoercion(ty::adjustment::PointerCoercion::UnsafeFnPointer, _) => {
625 let src = self.evaluated[value].as_ref()?;
626 let src = self.ecx.read_immediate(src).discard_err()?;
627 let to = self.ecx.layout_of(to).ok()?;
628 ImmTy::from_immediate(*src, to).into()
629 }
630 _ => return None,
631 },
632 };
633 Some(op)
634 }
635
636 fn project(
637 &mut self,
638 place: PlaceRef<'tcx>,
639 value: VnIndex,
640 proj: PlaceElem<'tcx>,
641 ) -> Option<VnIndex> {
642 let proj = match proj {
643 ProjectionElem::Deref => {
644 let ty = place.ty(self.local_decls, self.tcx).ty;
645 if let Some(Mutability::Not) = ty.ref_mutability()
646 && let Some(pointee_ty) = ty.builtin_deref(true)
647 && pointee_ty.is_freeze(self.tcx, self.typing_env())
648 {
649 return Some(self.insert_deref(value));
652 } else {
653 return None;
654 }
655 }
656 ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index),
657 ProjectionElem::Field(f, ty) => {
658 if let Value::Aggregate(_, _, fields) = self.get(value) {
659 return Some(fields[f.as_usize()]);
660 } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value)
661 && let Value::Aggregate(_, written_variant, fields) = self.get(*outer_value)
662 && written_variant == read_variant
678 {
679 return Some(fields[f.as_usize()]);
680 }
681 ProjectionElem::Field(f, ty)
682 }
683 ProjectionElem::Index(idx) => {
684 if let Value::Repeat(inner, _) = self.get(value) {
685 return Some(*inner);
686 }
687 let idx = self.locals[idx]?;
688 ProjectionElem::Index(idx)
689 }
690 ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
691 match self.get(value) {
692 Value::Repeat(inner, _) => {
693 return Some(*inner);
694 }
695 Value::Aggregate(AggregateTy::Array, _, operands) => {
696 let offset = if from_end {
697 operands.len() - offset as usize
698 } else {
699 offset as usize
700 };
701 return operands.get(offset).copied();
702 }
703 _ => {}
704 };
705 ProjectionElem::ConstantIndex { offset, min_length, from_end }
706 }
707 ProjectionElem::Subslice { from, to, from_end } => {
708 ProjectionElem::Subslice { from, to, from_end }
709 }
710 ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
711 ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty),
712 ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty),
713 };
714
715 Some(self.insert(Value::Projection(value, proj)))
716 }
717
718 #[instrument(level = "trace", skip(self))]
720 fn simplify_place_projection(&mut self, place: &mut Place<'tcx>, location: Location) {
721 if place.is_indirect_first_projection()
724 && let Some(base) = self.locals[place.local]
725 && let Some(new_local) = self.try_as_local(base, location)
726 && place.local != new_local
727 {
728 place.local = new_local;
729 self.reused_locals.insert(new_local);
730 }
731
732 let mut projection = Cow::Borrowed(&place.projection[..]);
733
734 for i in 0..projection.len() {
735 let elem = projection[i];
736 if let ProjectionElem::Index(idx_local) = elem
737 && let Some(idx) = self.locals[idx_local]
738 {
739 if let Some(offset) = self.evaluated[idx].as_ref()
740 && let Some(offset) = self.ecx.read_target_usize(offset).discard_err()
741 && let Some(min_length) = offset.checked_add(1)
742 {
743 projection.to_mut()[i] =
744 ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
745 } else if let Some(new_idx_local) = self.try_as_local(idx, location)
746 && idx_local != new_idx_local
747 {
748 projection.to_mut()[i] = ProjectionElem::Index(new_idx_local);
749 self.reused_locals.insert(new_idx_local);
750 }
751 }
752 }
753
754 if projection.is_owned() {
755 place.projection = self.tcx.mk_place_elems(&projection);
756 }
757
758 trace!(?place);
759 }
760
761 #[instrument(level = "trace", skip(self), ret)]
764 fn simplify_place_value(
765 &mut self,
766 place: &mut Place<'tcx>,
767 location: Location,
768 ) -> Option<VnIndex> {
769 self.simplify_place_projection(place, location);
770
771 let mut place_ref = place.as_ref();
774
775 let mut value = self.locals[place.local]?;
777 for (index, proj) in place.projection.iter().enumerate() {
778 if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
779 && let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer)
780 && let AddressKind::Ref(BorrowKind::Shared) = kind
781 && let Some(v) = self.simplify_place_value(&mut pointee, location)
782 {
783 value = v;
784 place_ref = pointee.project_deeper(&place.projection[index..], self.tcx).as_ref();
785 }
786 if let Some(local) = self.try_as_local(value, location) {
787 place_ref = PlaceRef { local, projection: &place.projection[index..] };
791 }
792
793 let base = PlaceRef { local: place.local, projection: &place.projection[..index] };
794 value = self.project(base, value, proj)?;
795 }
796
797 if let Value::Projection(pointer, ProjectionElem::Deref) = *self.get(value)
798 && let Value::Address { place: mut pointee, kind, .. } = *self.get(pointer)
799 && let AddressKind::Ref(BorrowKind::Shared) = kind
800 && let Some(v) = self.simplify_place_value(&mut pointee, location)
801 {
802 value = v;
803 place_ref = pointee.project_deeper(&[], self.tcx).as_ref();
804 }
805 if let Some(new_local) = self.try_as_local(value, location) {
806 place_ref = PlaceRef { local: new_local, projection: &[] };
807 }
808
809 if place_ref.local != place.local || place_ref.projection.len() < place.projection.len() {
810 *place = place_ref.project_deeper(&[], self.tcx);
812 self.reused_locals.insert(place_ref.local);
813 }
814
815 Some(value)
816 }
817
818 #[instrument(level = "trace", skip(self), ret)]
819 fn simplify_operand(
820 &mut self,
821 operand: &mut Operand<'tcx>,
822 location: Location,
823 ) -> Option<VnIndex> {
824 match *operand {
825 Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)),
826 Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
827 let value = self.simplify_place_value(place, location)?;
828 if let Some(const_) = self.try_as_constant(value) {
829 *operand = Operand::Constant(Box::new(const_));
830 }
831 Some(value)
832 }
833 }
834 }
835
836 #[instrument(level = "trace", skip(self), ret)]
837 fn simplify_rvalue(
838 &mut self,
839 rvalue: &mut Rvalue<'tcx>,
840 location: Location,
841 ) -> Option<VnIndex> {
842 let value = match *rvalue {
843 Rvalue::Use(ref mut operand) => return self.simplify_operand(operand, location),
845 Rvalue::CopyForDeref(place) => {
846 let mut operand = Operand::Copy(place);
847 let val = self.simplify_operand(&mut operand, location);
848 *rvalue = Rvalue::Use(operand);
849 return val;
850 }
851
852 Rvalue::Repeat(ref mut op, amount) => {
854 let op = self.simplify_operand(op, location)?;
855 Value::Repeat(op, amount)
856 }
857 Rvalue::NullaryOp(op, ty) => Value::NullaryOp(op, ty),
858 Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location),
859 Rvalue::Ref(_, borrow_kind, ref mut place) => {
860 self.simplify_place_projection(place, location);
861 return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind)));
862 }
863 Rvalue::RawPtr(mutbl, ref mut place) => {
864 self.simplify_place_projection(place, location);
865 return Some(self.new_pointer(*place, AddressKind::Address(mutbl)));
866 }
867 Rvalue::WrapUnsafeBinder(ref mut op, ty) => {
868 let value = self.simplify_operand(op, location)?;
869 Value::Cast {
870 kind: CastKind::Transmute,
871 value,
872 from: op.ty(self.local_decls, self.tcx),
873 to: ty,
874 }
875 }
876
877 Rvalue::Len(ref mut place) => return self.simplify_len(place, location),
879 Rvalue::Cast(ref mut kind, ref mut value, to) => {
880 return self.simplify_cast(kind, value, to, location);
881 }
882 Rvalue::BinaryOp(op, box (ref mut lhs, ref mut rhs)) => {
883 return self.simplify_binary(op, lhs, rhs, location);
884 }
885 Rvalue::UnaryOp(op, ref mut arg_op) => {
886 return self.simplify_unary(op, arg_op, location);
887 }
888 Rvalue::Discriminant(ref mut place) => {
889 let place = self.simplify_place_value(place, location)?;
890 if let Some(discr) = self.simplify_discriminant(place) {
891 return Some(discr);
892 }
893 Value::Discriminant(place)
894 }
895
896 Rvalue::ThreadLocalRef(..) | Rvalue::ShallowInitBox(..) => return None,
898 };
899 debug!(?value);
900 Some(self.insert(value))
901 }
902
903 fn simplify_discriminant(&mut self, place: VnIndex) -> Option<VnIndex> {
904 if let Value::Aggregate(enum_ty, variant, _) = *self.get(place)
905 && let AggregateTy::Def(enum_did, enum_args) = enum_ty
906 && let DefKind::Enum = self.tcx.def_kind(enum_did)
907 {
908 let enum_ty = self.tcx.type_of(enum_did).instantiate(self.tcx, enum_args);
909 let discr = self.ecx.discriminant_for_variant(enum_ty, variant).discard_err()?;
910 return Some(self.insert_scalar(discr.to_scalar(), discr.layout.ty));
911 }
912
913 None
914 }
915
916 fn try_as_place_elem(
917 &mut self,
918 proj: ProjectionElem<VnIndex, Ty<'tcx>>,
919 loc: Location,
920 ) -> Option<PlaceElem<'tcx>> {
921 Some(match proj {
922 ProjectionElem::Deref => ProjectionElem::Deref,
923 ProjectionElem::Field(idx, ty) => ProjectionElem::Field(idx, ty),
924 ProjectionElem::Index(idx) => {
925 let Some(local) = self.try_as_local(idx, loc) else {
926 return None;
927 };
928 self.reused_locals.insert(local);
929 ProjectionElem::Index(local)
930 }
931 ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
932 ProjectionElem::ConstantIndex { offset, min_length, from_end }
933 }
934 ProjectionElem::Subslice { from, to, from_end } => {
935 ProjectionElem::Subslice { from, to, from_end }
936 }
937 ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
938 ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx),
939 ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx),
940 ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty),
941 })
942 }
943
944 fn simplify_aggregate_to_copy(
945 &mut self,
946 rvalue: &mut Rvalue<'tcx>,
947 location: Location,
948 fields: &[VnIndex],
949 variant_index: VariantIdx,
950 ) -> Option<VnIndex> {
951 let Some(&first_field) = fields.first() else {
952 return None;
953 };
954 let Value::Projection(copy_from_value, _) = *self.get(first_field) else {
955 return None;
956 };
957 if fields.iter().enumerate().any(|(index, &v)| {
959 if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = *self.get(v)
960 && copy_from_value == pointer
961 && from_index.index() == index
962 {
963 return false;
964 }
965 true
966 }) {
967 return None;
968 }
969
970 let mut copy_from_local_value = copy_from_value;
971 if let Value::Projection(pointer, proj) = *self.get(copy_from_value)
972 && let ProjectionElem::Downcast(_, read_variant) = proj
973 {
974 if variant_index == read_variant {
975 copy_from_local_value = pointer;
977 } else {
978 return None;
980 }
981 }
982
983 if let Some(place) = self.try_as_place(copy_from_local_value, location, true) {
986 if rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty {
987 self.reused_locals.insert(place.local);
988 *rvalue = Rvalue::Use(Operand::Copy(place));
989 return Some(copy_from_local_value);
990 }
991 }
992
993 None
994 }
995
996 fn simplify_aggregate(
997 &mut self,
998 rvalue: &mut Rvalue<'tcx>,
999 location: Location,
1000 ) -> Option<VnIndex> {
1001 let Rvalue::Aggregate(box ref kind, ref mut field_ops) = *rvalue else { bug!() };
1002
1003 let tcx = self.tcx;
1004 if field_ops.is_empty() {
1005 let is_zst = match *kind {
1006 AggregateKind::Array(..)
1007 | AggregateKind::Tuple
1008 | AggregateKind::Closure(..)
1009 | AggregateKind::CoroutineClosure(..) => true,
1010 AggregateKind::Adt(did, ..) => tcx.def_kind(did) != DefKind::Enum,
1012 AggregateKind::Coroutine(..) => false,
1014 AggregateKind::RawPtr(..) => bug!("MIR for RawPtr aggregate must have 2 fields"),
1015 };
1016
1017 if is_zst {
1018 let ty = rvalue.ty(self.local_decls, tcx);
1019 return Some(self.insert_constant(Const::zero_sized(ty)));
1020 }
1021 }
1022
1023 let (mut ty, variant_index) = match *kind {
1024 AggregateKind::Array(..) => {
1025 assert!(!field_ops.is_empty());
1026 (AggregateTy::Array, FIRST_VARIANT)
1027 }
1028 AggregateKind::Tuple => {
1029 assert!(!field_ops.is_empty());
1030 (AggregateTy::Tuple, FIRST_VARIANT)
1031 }
1032 AggregateKind::Closure(did, args)
1033 | AggregateKind::CoroutineClosure(did, args)
1034 | AggregateKind::Coroutine(did, args) => (AggregateTy::Def(did, args), FIRST_VARIANT),
1035 AggregateKind::Adt(did, variant_index, args, _, None) => {
1036 (AggregateTy::Def(did, args), variant_index)
1037 }
1038 AggregateKind::Adt(_, _, _, _, Some(_)) => return None,
1040 AggregateKind::RawPtr(pointee_ty, mtbl) => {
1041 assert_eq!(field_ops.len(), 2);
1042 let data_pointer_ty = field_ops[FieldIdx::ZERO].ty(self.local_decls, self.tcx);
1043 let output_pointer_ty = Ty::new_ptr(self.tcx, pointee_ty, mtbl);
1044 (AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty }, FIRST_VARIANT)
1045 }
1046 };
1047
1048 let mut fields: Vec<_> = field_ops
1049 .iter_mut()
1050 .map(|op| self.simplify_operand(op, location).unwrap_or_else(|| self.new_opaque()))
1051 .collect();
1052
1053 if let AggregateTy::RawPtr { data_pointer_ty, output_pointer_ty } = &mut ty {
1054 let mut was_updated = false;
1055
1056 while let Value::Cast {
1058 kind: CastKind::PtrToPtr,
1059 value: cast_value,
1060 from: cast_from,
1061 to: _,
1062 } = self.get(fields[0])
1063 && let ty::RawPtr(from_pointee_ty, from_mtbl) = cast_from.kind()
1064 && let ty::RawPtr(_, output_mtbl) = output_pointer_ty.kind()
1065 && from_mtbl == output_mtbl
1066 && from_pointee_ty.is_sized(self.tcx, self.typing_env())
1067 {
1068 fields[0] = *cast_value;
1069 *data_pointer_ty = *cast_from;
1070 was_updated = true;
1071 }
1072
1073 if was_updated && let Some(op) = self.try_as_operand(fields[0], location) {
1074 field_ops[FieldIdx::ZERO] = op;
1075 }
1076 }
1077
1078 if let AggregateTy::Array = ty
1079 && fields.len() > 4
1080 {
1081 let first = fields[0];
1082 if fields.iter().all(|&v| v == first) {
1083 let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap());
1084 if let Some(op) = self.try_as_operand(first, location) {
1085 *rvalue = Rvalue::Repeat(op, len);
1086 }
1087 return Some(self.insert(Value::Repeat(first, len)));
1088 }
1089 }
1090
1091 if let AggregateTy::Def(_, _) = ty
1092 && let Some(value) =
1093 self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index)
1094 {
1095 return Some(value);
1096 }
1097
1098 Some(self.insert(Value::Aggregate(ty, variant_index, fields)))
1099 }
1100
1101 #[instrument(level = "trace", skip(self), ret)]
1102 fn simplify_unary(
1103 &mut self,
1104 op: UnOp,
1105 arg_op: &mut Operand<'tcx>,
1106 location: Location,
1107 ) -> Option<VnIndex> {
1108 let mut arg_index = self.simplify_operand(arg_op, location)?;
1109
1110 if op == UnOp::PtrMetadata {
1113 let mut was_updated = false;
1114 loop {
1115 match self.get(arg_index) {
1116 Value::Cast { kind: CastKind::PtrToPtr, value: inner, from, to }
1125 if self.pointers_have_same_metadata(*from, *to) =>
1126 {
1127 arg_index = *inner;
1128 was_updated = true;
1129 continue;
1130 }
1131
1132 Value::Address { place, kind: _, provenance: _ }
1134 if let PlaceRef { local, projection: [PlaceElem::Deref] } =
1135 place.as_ref()
1136 && let Some(local_index) = self.locals[local] =>
1137 {
1138 arg_index = local_index;
1139 was_updated = true;
1140 continue;
1141 }
1142
1143 _ => {
1144 if was_updated && let Some(op) = self.try_as_operand(arg_index, location) {
1145 *arg_op = op;
1146 }
1147 break;
1148 }
1149 }
1150 }
1151 }
1152
1153 let value = match (op, self.get(arg_index)) {
1154 (UnOp::Not, Value::UnaryOp(UnOp::Not, inner)) => return Some(*inner),
1155 (UnOp::Neg, Value::UnaryOp(UnOp::Neg, inner)) => return Some(*inner),
1156 (UnOp::Not, Value::BinaryOp(BinOp::Eq, lhs, rhs)) => {
1157 Value::BinaryOp(BinOp::Ne, *lhs, *rhs)
1158 }
1159 (UnOp::Not, Value::BinaryOp(BinOp::Ne, lhs, rhs)) => {
1160 Value::BinaryOp(BinOp::Eq, *lhs, *rhs)
1161 }
1162 (UnOp::PtrMetadata, Value::Aggregate(AggregateTy::RawPtr { .. }, _, fields)) => {
1163 return Some(fields[1]);
1164 }
1165 (
1167 UnOp::PtrMetadata,
1168 Value::Cast {
1169 kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _),
1170 from,
1171 to,
1172 ..
1173 },
1174 ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind()
1175 && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() =>
1176 {
1177 return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
1178 }
1179 _ => Value::UnaryOp(op, arg_index),
1180 };
1181 Some(self.insert(value))
1182 }
1183
1184 #[instrument(level = "trace", skip(self), ret)]
1185 fn simplify_binary(
1186 &mut self,
1187 op: BinOp,
1188 lhs_operand: &mut Operand<'tcx>,
1189 rhs_operand: &mut Operand<'tcx>,
1190 location: Location,
1191 ) -> Option<VnIndex> {
1192 let lhs = self.simplify_operand(lhs_operand, location);
1193 let rhs = self.simplify_operand(rhs_operand, location);
1194 let mut lhs = lhs?;
1197 let mut rhs = rhs?;
1198
1199 let lhs_ty = lhs_operand.ty(self.local_decls, self.tcx);
1200
1201 if let BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge = op
1204 && lhs_ty.is_any_ptr()
1205 && let Value::Cast {
1206 kind: CastKind::PtrToPtr, value: lhs_value, from: lhs_from, ..
1207 } = self.get(lhs)
1208 && let Value::Cast {
1209 kind: CastKind::PtrToPtr, value: rhs_value, from: rhs_from, ..
1210 } = self.get(rhs)
1211 && lhs_from == rhs_from
1212 && self.pointers_have_same_metadata(*lhs_from, lhs_ty)
1213 {
1214 lhs = *lhs_value;
1215 rhs = *rhs_value;
1216 if let Some(lhs_op) = self.try_as_operand(lhs, location)
1217 && let Some(rhs_op) = self.try_as_operand(rhs, location)
1218 {
1219 *lhs_operand = lhs_op;
1220 *rhs_operand = rhs_op;
1221 }
1222 }
1223
1224 if let Some(value) = self.simplify_binary_inner(op, lhs_ty, lhs, rhs) {
1225 return Some(value);
1226 }
1227 let value = Value::BinaryOp(op, lhs, rhs);
1228 Some(self.insert(value))
1229 }
1230
1231 fn simplify_binary_inner(
1232 &mut self,
1233 op: BinOp,
1234 lhs_ty: Ty<'tcx>,
1235 lhs: VnIndex,
1236 rhs: VnIndex,
1237 ) -> Option<VnIndex> {
1238 let reasonable_ty =
1240 lhs_ty.is_integral() || lhs_ty.is_bool() || lhs_ty.is_char() || lhs_ty.is_any_ptr();
1241 if !reasonable_ty {
1242 return None;
1243 }
1244
1245 let layout = self.ecx.layout_of(lhs_ty).ok()?;
1246
1247 let as_bits = |value: VnIndex| {
1248 let constant = self.evaluated[value].as_ref()?;
1249 if layout.backend_repr.is_scalar() {
1250 let scalar = self.ecx.read_scalar(constant).discard_err()?;
1251 scalar.to_bits(constant.layout.size).discard_err()
1252 } else {
1253 None
1255 }
1256 };
1257
1258 use Either::{Left, Right};
1260 let a = as_bits(lhs).map_or(Right(lhs), Left);
1261 let b = as_bits(rhs).map_or(Right(rhs), Left);
1262
1263 let result = match (op, a, b) {
1264 (
1266 BinOp::Add
1267 | BinOp::AddWithOverflow
1268 | BinOp::AddUnchecked
1269 | BinOp::BitOr
1270 | BinOp::BitXor,
1271 Left(0),
1272 Right(p),
1273 )
1274 | (
1275 BinOp::Add
1276 | BinOp::AddWithOverflow
1277 | BinOp::AddUnchecked
1278 | BinOp::BitOr
1279 | BinOp::BitXor
1280 | BinOp::Sub
1281 | BinOp::SubWithOverflow
1282 | BinOp::SubUnchecked
1283 | BinOp::Offset
1284 | BinOp::Shl
1285 | BinOp::Shr,
1286 Right(p),
1287 Left(0),
1288 )
1289 | (BinOp::Mul | BinOp::MulWithOverflow | BinOp::MulUnchecked, Left(1), Right(p))
1290 | (
1291 BinOp::Mul | BinOp::MulWithOverflow | BinOp::MulUnchecked | BinOp::Div,
1292 Right(p),
1293 Left(1),
1294 ) => p,
1295 (BinOp::BitAnd, Right(p), Left(ones)) | (BinOp::BitAnd, Left(ones), Right(p))
1297 if ones == layout.size.truncate(u128::MAX)
1298 || (layout.ty.is_bool() && ones == 1) =>
1299 {
1300 p
1301 }
1302 (
1304 BinOp::Mul | BinOp::MulWithOverflow | BinOp::MulUnchecked | BinOp::BitAnd,
1305 _,
1306 Left(0),
1307 )
1308 | (BinOp::Rem, _, Left(1))
1309 | (
1310 BinOp::Mul
1311 | BinOp::MulWithOverflow
1312 | BinOp::MulUnchecked
1313 | BinOp::Div
1314 | BinOp::Rem
1315 | BinOp::BitAnd
1316 | BinOp::Shl
1317 | BinOp::Shr,
1318 Left(0),
1319 _,
1320 ) => self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty),
1321 (BinOp::BitOr, _, Left(ones)) | (BinOp::BitOr, Left(ones), _)
1323 if ones == layout.size.truncate(u128::MAX)
1324 || (layout.ty.is_bool() && ones == 1) =>
1325 {
1326 self.insert_scalar(Scalar::from_uint(ones, layout.size), lhs_ty)
1327 }
1328 (BinOp::Sub | BinOp::SubWithOverflow | BinOp::SubUnchecked | BinOp::BitXor, a, b)
1330 if a == b =>
1331 {
1332 self.insert_scalar(Scalar::from_uint(0u128, layout.size), lhs_ty)
1333 }
1334 (BinOp::Eq, Left(a), Left(b)) => self.insert_bool(a == b),
1339 (BinOp::Eq, a, b) if a == b => self.insert_bool(true),
1340 (BinOp::Ne, Left(a), Left(b)) => self.insert_bool(a != b),
1341 (BinOp::Ne, a, b) if a == b => self.insert_bool(false),
1342 _ => return None,
1343 };
1344
1345 if op.is_overflowing() {
1346 let false_val = self.insert_bool(false);
1347 Some(self.insert_tuple(vec![result, false_val]))
1348 } else {
1349 Some(result)
1350 }
1351 }
1352
1353 fn simplify_cast(
1354 &mut self,
1355 initial_kind: &mut CastKind,
1356 initial_operand: &mut Operand<'tcx>,
1357 to: Ty<'tcx>,
1358 location: Location,
1359 ) -> Option<VnIndex> {
1360 use CastKind::*;
1361 use rustc_middle::ty::adjustment::PointerCoercion::*;
1362
1363 let mut from = initial_operand.ty(self.local_decls, self.tcx);
1364 let mut kind = *initial_kind;
1365 let mut value = self.simplify_operand(initial_operand, location)?;
1366 if from == to {
1367 return Some(value);
1368 }
1369
1370 if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind {
1371 return Some(self.new_opaque());
1374 }
1375
1376 let mut was_ever_updated = false;
1377 loop {
1378 let mut was_updated_this_iteration = false;
1379
1380 if let Transmute = kind
1385 && from.is_raw_ptr()
1386 && to.is_raw_ptr()
1387 && self.pointers_have_same_metadata(from, to)
1388 {
1389 kind = PtrToPtr;
1390 was_updated_this_iteration = true;
1391 }
1392
1393 if let PtrToPtr = kind
1396 && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) =
1397 self.get(value)
1398 && let ty::RawPtr(to_pointee, _) = to.kind()
1399 && to_pointee.is_sized(self.tcx, self.typing_env())
1400 {
1401 from = *data_pointer_ty;
1402 value = fields[0];
1403 was_updated_this_iteration = true;
1404 if *data_pointer_ty == to {
1405 return Some(fields[0]);
1406 }
1407 }
1408
1409 if let Transmute = kind
1412 && let Value::Aggregate(_aggregate_ty, variant_idx, field_values) = self.get(value)
1413 && let Some((field_idx, field_ty)) =
1414 self.value_is_all_in_one_field(from, *variant_idx)
1415 {
1416 from = field_ty;
1417 value = field_values[field_idx.as_usize()];
1418 was_updated_this_iteration = true;
1419 if field_ty == to {
1420 return Some(value);
1421 }
1422 }
1423
1424 if let Value::Cast {
1426 kind: inner_kind,
1427 value: inner_value,
1428 from: inner_from,
1429 to: inner_to,
1430 } = *self.get(value)
1431 {
1432 let new_kind = match (inner_kind, kind) {
1433 (PtrToPtr, PtrToPtr) => Some(PtrToPtr),
1437 (PtrToPtr, Transmute)
1441 if self.pointers_have_same_metadata(inner_from, inner_to) =>
1442 {
1443 Some(Transmute)
1444 }
1445 (Transmute, PtrToPtr) if self.pointers_have_same_metadata(from, to) => {
1448 Some(Transmute)
1449 }
1450 (Transmute, Transmute)
1453 if !self.type_may_have_niche_of_interest_to_backend(inner_to) =>
1454 {
1455 Some(Transmute)
1456 }
1457 _ => None,
1458 };
1459 if let Some(new_kind) = new_kind {
1460 kind = new_kind;
1461 from = inner_from;
1462 value = inner_value;
1463 was_updated_this_iteration = true;
1464 if inner_from == to {
1465 return Some(inner_value);
1466 }
1467 }
1468 }
1469
1470 if was_updated_this_iteration {
1471 was_ever_updated = true;
1472 } else {
1473 break;
1474 }
1475 }
1476
1477 if was_ever_updated && let Some(op) = self.try_as_operand(value, location) {
1478 *initial_operand = op;
1479 *initial_kind = kind;
1480 }
1481
1482 Some(self.insert(Value::Cast { kind, value, from, to }))
1483 }
1484
1485 fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> {
1486 let place_ty = place.ty(self.local_decls, self.tcx).ty;
1488 if let ty::Array(_, len) = place_ty.kind() {
1489 return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
1490 }
1491
1492 let mut inner = self.simplify_place_value(place, location)?;
1493
1494 while let Value::Address { place: borrowed, .. } = self.get(inner)
1497 && let [PlaceElem::Deref] = borrowed.projection[..]
1498 && let Some(borrowed) = self.locals[borrowed.local]
1499 {
1500 inner = borrowed;
1501 }
1502
1503 if let Value::Cast { kind, from, to, .. } = self.get(inner)
1505 && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind
1506 && let Some(from) = from.builtin_deref(true)
1507 && let ty::Array(_, len) = from.kind()
1508 && let Some(to) = to.builtin_deref(true)
1509 && let ty::Slice(..) = to.kind()
1510 {
1511 return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len)));
1512 }
1513
1514 Some(self.insert(Value::Len(inner)))
1516 }
1517
1518 fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool {
1519 let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
1520 let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx);
1521 if left_meta_ty == right_meta_ty {
1522 true
1523 } else if let Ok(left) =
1524 self.tcx.try_normalize_erasing_regions(self.typing_env(), left_meta_ty)
1525 && let Ok(right) =
1526 self.tcx.try_normalize_erasing_regions(self.typing_env(), right_meta_ty)
1527 {
1528 left == right
1529 } else {
1530 false
1531 }
1532 }
1533
1534 fn type_may_have_niche_of_interest_to_backend(&self, ty: Ty<'tcx>) -> bool {
1541 let Ok(layout) = self.ecx.layout_of(ty) else {
1542 return true;
1544 };
1545
1546 if layout.uninhabited {
1547 return true;
1548 }
1549
1550 match layout.backend_repr {
1551 BackendRepr::Scalar(a) => !a.is_always_valid(&self.ecx),
1552 BackendRepr::ScalarPair(a, b) => {
1553 !a.is_always_valid(&self.ecx) || !b.is_always_valid(&self.ecx)
1554 }
1555 BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => false,
1556 }
1557 }
1558
1559 fn value_is_all_in_one_field(
1560 &self,
1561 ty: Ty<'tcx>,
1562 variant: VariantIdx,
1563 ) -> Option<(FieldIdx, Ty<'tcx>)> {
1564 if let Ok(layout) = self.ecx.layout_of(ty)
1565 && let abi::Variants::Single { index } = layout.variants
1566 && index == variant
1567 && let Some((field_idx, field_layout)) = layout.non_1zst_field(&self.ecx)
1568 && layout.size == field_layout.size
1569 {
1570 Some((FieldIdx::from_usize(field_idx), field_layout.ty))
1574 } else if let ty::Adt(adt, args) = ty.kind()
1575 && adt.is_struct()
1576 && adt.repr().transparent()
1577 && let [single_field] = adt.non_enum_variant().fields.raw.as_slice()
1578 {
1579 Some((FieldIdx::ZERO, single_field.ty(self.tcx, args)))
1580 } else {
1581 None
1582 }
1583 }
1584}
1585
1586fn op_to_prop_const<'tcx>(
1587 ecx: &mut InterpCx<'tcx, DummyMachine>,
1588 op: &OpTy<'tcx>,
1589) -> Option<ConstValue<'tcx>> {
1590 if op.layout.is_unsized() {
1592 return None;
1593 }
1594
1595 if op.layout.is_zst() {
1597 return Some(ConstValue::ZeroSized);
1598 }
1599
1600 if !matches!(op.layout.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) {
1603 return None;
1604 }
1605
1606 if let BackendRepr::Scalar(abi::Scalar::Initialized { .. }) = op.layout.backend_repr
1608 && let Some(scalar) = ecx.read_scalar(op).discard_err()
1609 {
1610 if !scalar.try_to_scalar_int().is_ok() {
1611 return None;
1615 }
1616 return Some(ConstValue::Scalar(scalar));
1617 }
1618
1619 if let Either::Left(mplace) = op.as_mplace_or_imm() {
1622 let (size, _align) = ecx.size_and_align_of_mplace(&mplace).discard_err()??;
1623
1624 let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).discard_err()??;
1628 if alloc_ref.has_provenance() {
1629 return None;
1630 }
1631
1632 let pointer = mplace.ptr().into_pointer_or_addr().ok()?;
1633 let (prov, offset) = pointer.into_parts();
1634 let alloc_id = prov.alloc_id();
1635 intern_const_alloc_for_constprop(ecx, alloc_id).discard_err()?;
1636
1637 if let GlobalAlloc::Memory(alloc) = ecx.tcx.global_alloc(alloc_id)
1641 && alloc.inner().align >= op.layout.align.abi
1644 {
1645 return Some(ConstValue::Indirect { alloc_id, offset });
1646 }
1647 }
1648
1649 let alloc_id =
1651 ecx.intern_with_temp_alloc(op.layout, |ecx, dest| ecx.copy_op(op, dest)).discard_err()?;
1652 let value = ConstValue::Indirect { alloc_id, offset: Size::ZERO };
1653
1654 if ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() {
1658 return Some(value);
1659 }
1660
1661 None
1662}
1663
1664impl<'tcx> VnState<'_, 'tcx> {
1665 fn try_as_operand(&mut self, index: VnIndex, location: Location) -> Option<Operand<'tcx>> {
1668 if let Some(const_) = self.try_as_constant(index) {
1669 Some(Operand::Constant(Box::new(const_)))
1670 } else if let Some(place) = self.try_as_place(index, location, false) {
1671 self.reused_locals.insert(place.local);
1672 Some(Operand::Copy(place))
1673 } else {
1674 None
1675 }
1676 }
1677
1678 fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
1680 if let Value::Constant { value, disambiguator: 0 } = *self.get(index) {
1684 debug_assert!(value.is_deterministic());
1685 return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
1686 }
1687
1688 let op = self.evaluated[index].as_ref()?;
1689 if op.layout.is_unsized() {
1690 return None;
1692 }
1693
1694 let value = op_to_prop_const(&mut self.ecx, op)?;
1695
1696 assert!(!value.may_have_provenance(self.tcx, op.layout.size));
1700
1701 let const_ = Const::Val(value, op.layout.ty);
1702 Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ })
1703 }
1704
1705 #[instrument(level = "trace", skip(self), ret)]
1709 fn try_as_place(
1710 &mut self,
1711 mut index: VnIndex,
1712 loc: Location,
1713 allow_complex_projection: bool,
1714 ) -> Option<Place<'tcx>> {
1715 let mut projection = SmallVec::<[PlaceElem<'tcx>; 1]>::new();
1716 loop {
1717 if let Some(local) = self.try_as_local(index, loc) {
1718 projection.reverse();
1719 let place =
1720 Place { local, projection: self.tcx.mk_place_elems(projection.as_slice()) };
1721 return Some(place);
1722 } else if let Value::Projection(pointer, proj) = *self.get(index)
1723 && (allow_complex_projection || proj.is_stable_offset())
1724 && let Some(proj) = self.try_as_place_elem(proj, loc)
1725 {
1726 projection.push(proj);
1727 index = pointer;
1728 } else {
1729 return None;
1730 }
1731 }
1732 }
1733
1734 fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option<Local> {
1737 let other = self.rev_locals.get(index)?;
1738 other
1739 .iter()
1740 .find(|&&other| self.ssa.assignment_dominates(&self.dominators, other, loc))
1741 .copied()
1742 }
1743}
1744
1745impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
1746 fn tcx(&self) -> TyCtxt<'tcx> {
1747 self.tcx
1748 }
1749
1750 fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
1751 self.simplify_place_projection(place, location);
1752 if context.is_mutating_use() && !place.projection.is_empty() {
1753 self.invalidate_derefs();
1755 }
1756 self.super_place(place, context, location);
1757 }
1758
1759 fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
1760 self.simplify_operand(operand, location);
1761 self.super_operand(operand, location);
1762 }
1763
1764 fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) {
1765 if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
1766 self.simplify_place_projection(lhs, location);
1767
1768 let value = self.simplify_rvalue(rvalue, location);
1769 let value = if let Some(local) = lhs.as_local()
1770 && self.ssa.is_ssa(local)
1771 && self.local_decls[local].ty == rvalue.ty(self.local_decls, self.tcx)
1774 {
1775 let value = value.unwrap_or_else(|| self.new_opaque());
1776 self.assign(local, value);
1777 Some(value)
1778 } else {
1779 value
1780 };
1781 if let Some(value) = value {
1782 if let Some(const_) = self.try_as_constant(value) {
1783 *rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
1784 } else if let Some(place) = self.try_as_place(value, location, false)
1785 && *rvalue != Rvalue::Use(Operand::Move(place))
1786 && *rvalue != Rvalue::Use(Operand::Copy(place))
1787 {
1788 *rvalue = Rvalue::Use(Operand::Copy(place));
1789 self.reused_locals.insert(place.local);
1790 }
1791 }
1792 }
1793 self.super_statement(stmt, location);
1794 }
1795
1796 fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
1797 if let Terminator { kind: TerminatorKind::Call { destination, .. }, .. } = terminator {
1798 if let Some(local) = destination.as_local()
1799 && self.ssa.is_ssa(local)
1800 {
1801 let opaque = self.new_opaque();
1802 self.assign(local, opaque);
1803 }
1804 }
1805 let safe_to_preserve_derefs = matches!(
1808 terminator.kind,
1809 TerminatorKind::SwitchInt { .. } | TerminatorKind::Goto { .. }
1810 );
1811 if !safe_to_preserve_derefs {
1812 self.invalidate_derefs();
1813 }
1814 self.super_terminator(terminator, location);
1815 }
1816}
1817
1818struct StorageRemover<'tcx> {
1819 tcx: TyCtxt<'tcx>,
1820 reused_locals: DenseBitSet<Local>,
1821}
1822
1823impl<'tcx> MutVisitor<'tcx> for StorageRemover<'tcx> {
1824 fn tcx(&self) -> TyCtxt<'tcx> {
1825 self.tcx
1826 }
1827
1828 fn visit_operand(&mut self, operand: &mut Operand<'tcx>, _: Location) {
1829 if let Operand::Move(place) = *operand
1830 && !place.is_indirect_first_projection()
1831 && self.reused_locals.contains(place.local)
1832 {
1833 *operand = Operand::Copy(place);
1834 }
1835 }
1836
1837 fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, loc: Location) {
1838 match stmt.kind {
1839 StatementKind::StorageLive(l) | StatementKind::StorageDead(l)
1841 if self.reused_locals.contains(l) =>
1842 {
1843 stmt.make_nop()
1844 }
1845 _ => self.super_statement(stmt, loc),
1846 }
1847 }
1848}