| //! Performs various peephole optimizations. | 
 |  | 
 | use rustc_abi::ExternAbi; | 
 | use rustc_ast::attr; | 
 | use rustc_hir::LangItem; | 
 | use rustc_middle::bug; | 
 | use rustc_middle::mir::*; | 
 | use rustc_middle::ty::layout::ValidityRequirement; | 
 | use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, layout}; | 
 | use rustc_span::{DUMMY_SP, Symbol, sym}; | 
 |  | 
 | use crate::simplify::simplify_duplicate_switch_targets; | 
 |  | 
 | pub(super) enum InstSimplify { | 
 |     BeforeInline, | 
 |     AfterSimplifyCfg, | 
 | } | 
 |  | 
 | impl<'tcx> crate::MirPass<'tcx> for InstSimplify { | 
 |     fn name(&self) -> &'static str { | 
 |         match self { | 
 |             InstSimplify::BeforeInline => "InstSimplify-before-inline", | 
 |             InstSimplify::AfterSimplifyCfg => "InstSimplify-after-simplifycfg", | 
 |         } | 
 |     } | 
 |  | 
 |     fn is_enabled(&self, sess: &rustc_session::Session) -> bool { | 
 |         sess.mir_opt_level() > 0 | 
 |     } | 
 |  | 
 |     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | 
 |         let ctx = InstSimplifyContext { | 
 |             tcx, | 
 |             local_decls: &body.local_decls, | 
 |             typing_env: body.typing_env(tcx), | 
 |         }; | 
 |         let preserve_ub_checks = | 
 |             attr::contains_name(tcx.hir_krate_attrs(), sym::rustc_preserve_ub_checks); | 
 |         for block in body.basic_blocks.as_mut() { | 
 |             for statement in block.statements.iter_mut() { | 
 |                 let StatementKind::Assign(box (.., rvalue)) = &mut statement.kind else { | 
 |                     continue; | 
 |                 }; | 
 |  | 
 |                 if !preserve_ub_checks { | 
 |                     ctx.simplify_ub_check(rvalue); | 
 |                 } | 
 |                 ctx.simplify_bool_cmp(rvalue); | 
 |                 ctx.simplify_ref_deref(rvalue); | 
 |                 ctx.simplify_ptr_aggregate(rvalue); | 
 |                 ctx.simplify_cast(rvalue); | 
 |                 ctx.simplify_repeated_aggregate(rvalue); | 
 |                 ctx.simplify_repeat_once(rvalue); | 
 |             } | 
 |  | 
 |             let terminator = block.terminator.as_mut().unwrap(); | 
 |             ctx.simplify_primitive_clone(terminator, &mut block.statements); | 
 |             ctx.simplify_align_of_slice_val(terminator, &mut block.statements); | 
 |             ctx.simplify_intrinsic_assert(terminator); | 
 |             ctx.simplify_nounwind_call(terminator); | 
 |             simplify_duplicate_switch_targets(terminator); | 
 |         } | 
 |     } | 
 |  | 
 |     fn is_required(&self) -> bool { | 
 |         false | 
 |     } | 
 | } | 
 |  | 
 | struct InstSimplifyContext<'a, 'tcx> { | 
 |     tcx: TyCtxt<'tcx>, | 
 |     local_decls: &'a LocalDecls<'tcx>, | 
 |     typing_env: ty::TypingEnv<'tcx>, | 
 | } | 
 |  | 
 | impl<'tcx> InstSimplifyContext<'_, 'tcx> { | 
 |     /// Transform aggregates like [0, 0, 0, 0, 0] into [0; 5]. | 
 |     /// GVN can also do this optimization, but GVN is only run at mir-opt-level 2 so having this in | 
 |     /// InstSimplify helps unoptimized builds. | 
 |     fn simplify_repeated_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { | 
 |         let Rvalue::Aggregate(box AggregateKind::Array(_), fields) = &*rvalue else { | 
 |             return; | 
 |         }; | 
 |         if fields.len() < 5 { | 
 |             return; | 
 |         } | 
 |         let (first, rest) = fields[..].split_first().unwrap(); | 
 |         let Operand::Constant(first) = first else { | 
 |             return; | 
 |         }; | 
 |         let Ok(first_val) = first.const_.eval(self.tcx, self.typing_env, first.span) else { | 
 |             return; | 
 |         }; | 
 |         if rest.iter().all(|field| { | 
 |             let Operand::Constant(field) = field else { | 
 |                 return false; | 
 |             }; | 
 |             let field = field.const_.eval(self.tcx, self.typing_env, field.span); | 
 |             field == Ok(first_val) | 
 |         }) { | 
 |             let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap()); | 
 |             *rvalue = Rvalue::Repeat(Operand::Constant(first.clone()), len); | 
 |         } | 
 |     } | 
 |  | 
 |     /// Transform boolean comparisons into logical operations. | 
 |     fn simplify_bool_cmp(&self, rvalue: &mut Rvalue<'tcx>) { | 
 |         let Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), box (a, b)) = &*rvalue else { return }; | 
 |         *rvalue = match (op, self.try_eval_bool(a), self.try_eval_bool(b)) { | 
 |             // Transform "Eq(a, true)" ==> "a" | 
 |             (BinOp::Eq, _, Some(true)) => Rvalue::Use(a.clone()), | 
 |  | 
 |             // Transform "Ne(a, false)" ==> "a" | 
 |             (BinOp::Ne, _, Some(false)) => Rvalue::Use(a.clone()), | 
 |  | 
 |             // Transform "Eq(true, b)" ==> "b" | 
 |             (BinOp::Eq, Some(true), _) => Rvalue::Use(b.clone()), | 
 |  | 
 |             // Transform "Ne(false, b)" ==> "b" | 
 |             (BinOp::Ne, Some(false), _) => Rvalue::Use(b.clone()), | 
 |  | 
 |             // Transform "Eq(false, b)" ==> "Not(b)" | 
 |             (BinOp::Eq, Some(false), _) => Rvalue::UnaryOp(UnOp::Not, b.clone()), | 
 |  | 
 |             // Transform "Ne(true, b)" ==> "Not(b)" | 
 |             (BinOp::Ne, Some(true), _) => Rvalue::UnaryOp(UnOp::Not, b.clone()), | 
 |  | 
 |             // Transform "Eq(a, false)" ==> "Not(a)" | 
 |             (BinOp::Eq, _, Some(false)) => Rvalue::UnaryOp(UnOp::Not, a.clone()), | 
 |  | 
 |             // Transform "Ne(a, true)" ==> "Not(a)" | 
 |             (BinOp::Ne, _, Some(true)) => Rvalue::UnaryOp(UnOp::Not, a.clone()), | 
 |  | 
 |             _ => return, | 
 |         }; | 
 |     } | 
 |  | 
 |     fn try_eval_bool(&self, a: &Operand<'_>) -> Option<bool> { | 
 |         let a = a.constant()?; | 
 |         if a.const_.ty().is_bool() { a.const_.try_to_bool() } else { None } | 
 |     } | 
 |  | 
 |     /// Transform `&(*a)` ==> `a`. | 
 |     fn simplify_ref_deref(&self, rvalue: &mut Rvalue<'tcx>) { | 
 |         if let Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) = rvalue | 
 |             && let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() | 
 |             && rvalue.ty(self.local_decls, self.tcx) == base.ty(self.local_decls, self.tcx).ty | 
 |         { | 
 |             *rvalue = Rvalue::Use(Operand::Copy(Place { | 
 |                 local: base.local, | 
 |                 projection: self.tcx.mk_place_elems(base.projection), | 
 |             })); | 
 |         } | 
 |     } | 
 |  | 
 |     /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`. | 
 |     fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { | 
 |         if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue | 
 |             && let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx) | 
 |             && meta_ty.is_unit() | 
 |         { | 
 |             // The mutable borrows we're holding prevent printing `rvalue` here | 
 |             let mut fields = std::mem::take(fields); | 
 |             let _meta = fields.pop().unwrap(); | 
 |             let data = fields.pop().unwrap(); | 
 |             let ptr_ty = Ty::new_ptr(self.tcx, *pointee_ty, *mutability); | 
 |             *rvalue = Rvalue::Cast(CastKind::PtrToPtr, data, ptr_ty); | 
 |         } | 
 |     } | 
 |  | 
 |     fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>) { | 
 |         let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue else { return }; | 
 |  | 
 |         let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks()); | 
 |         let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None }; | 
 |         *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); | 
 |     } | 
 |  | 
 |     fn simplify_cast(&self, rvalue: &mut Rvalue<'tcx>) { | 
 |         let Rvalue::Cast(kind, operand, cast_ty) = rvalue else { return }; | 
 |  | 
 |         let operand_ty = operand.ty(self.local_decls, self.tcx); | 
 |         if operand_ty == *cast_ty { | 
 |             *rvalue = Rvalue::Use(operand.clone()); | 
 |         } else if *kind == CastKind::Transmute | 
 |             // Transmuting an integer to another integer is just a signedness cast | 
 |             && let (ty::Int(int), ty::Uint(uint)) | (ty::Uint(uint), ty::Int(int)) = | 
 |                 (operand_ty.kind(), cast_ty.kind()) | 
 |             && int.bit_width() == uint.bit_width() | 
 |         { | 
 |             // The width check isn't strictly necessary, as different widths | 
 |             // are UB and thus we'd be allowed to turn it into a cast anyway. | 
 |             // But let's keep the UB around for codegen to exploit later. | 
 |             // (If `CastKind::Transmute` ever becomes *not* UB for mismatched sizes, | 
 |             // then the width check is necessary for big-endian correctness.) | 
 |             *kind = CastKind::IntToInt; | 
 |         } | 
 |     } | 
 |  | 
 |     /// Simplify `[x; 1]` to just `[x]`. | 
 |     fn simplify_repeat_once(&self, rvalue: &mut Rvalue<'tcx>) { | 
 |         if let Rvalue::Repeat(operand, count) = rvalue | 
 |             && let Some(1) = count.try_to_target_usize(self.tcx) | 
 |         { | 
 |             *rvalue = Rvalue::Aggregate( | 
 |                 Box::new(AggregateKind::Array(operand.ty(self.local_decls, self.tcx))), | 
 |                 [operand.clone()].into(), | 
 |             ); | 
 |         } | 
 |     } | 
 |  | 
 |     fn simplify_primitive_clone( | 
 |         &self, | 
 |         terminator: &mut Terminator<'tcx>, | 
 |         statements: &mut Vec<Statement<'tcx>>, | 
 |     ) { | 
 |         let TerminatorKind::Call { | 
 |             func, args, destination, target: Some(destination_block), .. | 
 |         } = &terminator.kind | 
 |         else { | 
 |             return; | 
 |         }; | 
 |  | 
 |         // It's definitely not a clone if there are multiple arguments | 
 |         let [arg] = &args[..] else { return }; | 
 |  | 
 |         // Only bother looking more if it's easy to know what we're calling | 
 |         let Some((fn_def_id, ..)) = func.const_fn_def() else { return }; | 
 |  | 
 |         // These types are easily available from locals, so check that before | 
 |         // doing DefId lookups to figure out what we're actually calling. | 
 |         let arg_ty = arg.node.ty(self.local_decls, self.tcx); | 
 |  | 
 |         let ty::Ref(_region, inner_ty, Mutability::Not) = *arg_ty.kind() else { return }; | 
 |  | 
 |         if !self.tcx.is_lang_item(fn_def_id, LangItem::CloneFn) | 
 |             || !inner_ty.is_trivially_pure_clone_copy() | 
 |         { | 
 |             return; | 
 |         } | 
 |  | 
 |         let Some(arg_place) = arg.node.place() else { return }; | 
 |  | 
 |         statements.push(Statement::new( | 
 |             terminator.source_info, | 
 |             StatementKind::Assign(Box::new(( | 
 |                 *destination, | 
 |                 Rvalue::Use(Operand::Copy( | 
 |                     arg_place.project_deeper(&[ProjectionElem::Deref], self.tcx), | 
 |                 )), | 
 |             ))), | 
 |         )); | 
 |         terminator.kind = TerminatorKind::Goto { target: *destination_block }; | 
 |     } | 
 |  | 
 |     // Convert `align_of_val::<[T]>(ptr)` to `align_of::<T>()`, since the | 
 |     // alignment of a slice doesn't actually depend on metadata at all | 
 |     // and the element type is always `Sized`. | 
 |     // | 
 |     // This is here so it can run after inlining, where it's more useful. | 
 |     // (LowerIntrinsics is done in cleanup, before the optimization passes.) | 
 |     fn simplify_align_of_slice_val( | 
 |         &self, | 
 |         terminator: &mut Terminator<'tcx>, | 
 |         statements: &mut Vec<Statement<'tcx>>, | 
 |     ) { | 
 |         let source_info = terminator.source_info; | 
 |         if let TerminatorKind::Call { | 
 |             func, args, destination, target: Some(destination_block), .. | 
 |         } = &terminator.kind | 
 |             && args.len() == 1 | 
 |             && let Some((fn_def_id, generics)) = func.const_fn_def() | 
 |             && self.tcx.is_intrinsic(fn_def_id, sym::align_of_val) | 
 |             && let ty::Slice(elem_ty) = *generics.type_at(0).kind() | 
 |         { | 
 |             let align_def_id = self.tcx.require_lang_item(LangItem::AlignOf, source_info.span); | 
 |             let align_const = Operand::unevaluated_constant( | 
 |                 self.tcx, | 
 |                 align_def_id, | 
 |                 &[elem_ty.into()], | 
 |                 source_info.span, | 
 |             ); | 
 |             statements.push(Statement::new( | 
 |                 source_info, | 
 |                 StatementKind::Assign(Box::new((*destination, Rvalue::Use(align_const)))), | 
 |             )); | 
 |             terminator.kind = TerminatorKind::Goto { target: *destination_block }; | 
 |         } | 
 |     } | 
 |  | 
 |     fn simplify_nounwind_call(&self, terminator: &mut Terminator<'tcx>) { | 
 |         let TerminatorKind::Call { ref func, ref mut unwind, .. } = terminator.kind else { | 
 |             return; | 
 |         }; | 
 |  | 
 |         let Some((def_id, _)) = func.const_fn_def() else { | 
 |             return; | 
 |         }; | 
 |  | 
 |         let body_ty = self.tcx.type_of(def_id).skip_binder(); | 
 |         let body_abi = match body_ty.kind() { | 
 |             ty::FnDef(..) => body_ty.fn_sig(self.tcx).abi(), | 
 |             ty::Closure(..) => ExternAbi::RustCall, | 
 |             ty::Coroutine(..) => ExternAbi::Rust, | 
 |             _ => bug!("unexpected body ty: {body_ty:?}"), | 
 |         }; | 
 |  | 
 |         if !layout::fn_can_unwind(self.tcx, Some(def_id), body_abi) { | 
 |             *unwind = UnwindAction::Unreachable; | 
 |         } | 
 |     } | 
 |  | 
 |     fn simplify_intrinsic_assert(&self, terminator: &mut Terminator<'tcx>) { | 
 |         let TerminatorKind::Call { ref func, target: ref mut target @ Some(target_block), .. } = | 
 |             terminator.kind | 
 |         else { | 
 |             return; | 
 |         }; | 
 |         let func_ty = func.ty(self.local_decls, self.tcx); | 
 |         let Some((intrinsic_name, args)) = resolve_rust_intrinsic(self.tcx, func_ty) else { | 
 |             return; | 
 |         }; | 
 |         // The intrinsics we are interested in have one generic parameter | 
 |         let [arg, ..] = args[..] else { return }; | 
 |  | 
 |         let known_is_valid = | 
 |             intrinsic_assert_panics(self.tcx, self.typing_env, arg, intrinsic_name); | 
 |         match known_is_valid { | 
 |             // We don't know the layout or it's not validity assertion at all, don't touch it | 
 |             None => {} | 
 |             Some(true) => { | 
 |                 // If we know the assert panics, indicate to later opts that the call diverges | 
 |                 *target = None; | 
 |             } | 
 |             Some(false) => { | 
 |                 // If we know the assert does not panic, turn the call into a Goto | 
 |                 terminator.kind = TerminatorKind::Goto { target: target_block }; | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | fn intrinsic_assert_panics<'tcx>( | 
 |     tcx: TyCtxt<'tcx>, | 
 |     typing_env: ty::TypingEnv<'tcx>, | 
 |     arg: ty::GenericArg<'tcx>, | 
 |     intrinsic_name: Symbol, | 
 | ) -> Option<bool> { | 
 |     let requirement = ValidityRequirement::from_intrinsic(intrinsic_name)?; | 
 |     let ty = arg.expect_ty(); | 
 |     Some(!tcx.check_validity_requirement((requirement, typing_env.as_query_input(ty))).ok()?) | 
 | } | 
 |  | 
 | fn resolve_rust_intrinsic<'tcx>( | 
 |     tcx: TyCtxt<'tcx>, | 
 |     func_ty: Ty<'tcx>, | 
 | ) -> Option<(Symbol, GenericArgsRef<'tcx>)> { | 
 |     let ty::FnDef(def_id, args) = *func_ty.kind() else { return None }; | 
 |     let intrinsic = tcx.intrinsic(def_id)?; | 
 |     Some((intrinsic.name, args)) | 
 | } |