| // tidy-alphabetical-start | |
| #![feature(const_type_name)] | |
| #![feature(cow_is_borrowed)] | |
| #![feature(deref_patterns)] | |
| #![feature(impl_trait_in_assoc_type)] | |
| #![feature(iterator_try_collect)] | |
| #![feature(option_into_flat_iter)] | |
| #![feature(try_blocks)] | |
| #![feature(yeet_expr)] | |
| // tidy-alphabetical-end | |
| use hir::ConstContext; | |
| use required_consts::RequiredConstsVisitor; | |
| use rustc_const_eval::check_consts::{self, ConstCx}; | |
| use rustc_const_eval::util; | |
| use rustc_data_structures::fx::FxIndexSet; | |
| use rustc_data_structures::steal::Steal; | |
| use rustc_hir as hir; | |
| use rustc_hir::def::{CtorKind, DefKind}; | |
| use rustc_hir::def_id::LocalDefId; | |
| use rustc_index::IndexVec; | |
| use rustc_middle::mir::{ | |
| AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl, | |
| MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, START_BLOCK, | |
| SourceInfo, Statement, StatementKind, TerminatorKind, WithRetag, | |
| }; | |
| use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; | |
| use rustc_middle::util::Providers; | |
| use rustc_middle::{bug, query, span_bug}; | |
| use rustc_span::{DUMMY_SP, Spanned, sym}; | |
| use tracing::debug; | |
| #[macro_use] | |
| mod pass_manager; | |
| use std::sync::LazyLock; | |
| use pass_manager::{self as pm, Lint, MirLint, MirPass, PassPolicy, WithMinOptLevel}; | |
| mod check_pointers; | |
| mod cost_checker; | |
| mod cross_crate_inline; | |
| mod deduce_param_attrs; | |
| mod diagnostics; | |
| mod elaborate_drop; | |
| mod ffi_unwind_calls; | |
| mod lint; | |
| mod lint_tail_expr_drop_order; | |
| mod liveness; | |
| mod patch; | |
| mod shim; | |
| mod ssa; | |
| mod trivial_const; | |
| /// Exposed for rustc drivers. | |
| pub use shim::build_drop_shim; | |
| /// We import passes via this macro so that we can have a static list of pass names | |
| /// (used to verify CLI arguments). It takes a list of modules, followed by the passes | |
| /// declared within them. | |
| /// ```ignore,macro-test | |
| /// declare_passes! { | |
| /// // Declare a single pass from the module `abort_unwinding_calls` | |
| /// mod abort_unwinding_calls : AbortUnwindingCalls; | |
| /// // When passes are grouped together as an enum, declare the two constituent passes | |
| /// mod add_call_guards : AddCallGuards { | |
| /// AllCallEdges, | |
| /// CriticalCallEdges | |
| /// }; | |
| /// // Declares multiple pass groups, each containing their own constituent passes | |
| /// mod simplify : SimplifyCfg { | |
| /// Initial, | |
| /// /* omitted */ | |
| /// }, SimplifyLocals { | |
| /// BeforeConstProp, | |
| /// /* omitted */ | |
| /// }; | |
| /// } | |
| /// ``` | |
| macro_rules! declare_passes { | |
| ( | |
| $( | |
| $vis:vis mod $mod_name:ident : $($pass_name:ident $( { $($ident:ident),* } )?),+ $(,)?; | |
| )* | |
| ) => { | |
| $( | |
| $vis mod $mod_name; | |
| $( | |
| // Make sure the type name is correct | |
| #[allow(unused_imports)] | |
| use $mod_name::$pass_name as _; | |
| )+ | |
| )* | |
| static PASS_NAMES: LazyLock<FxIndexSet<&str>> = LazyLock::new(|| { | |
| let mut set = FxIndexSet::default(); | |
| $( | |
| $( | |
| set.extend(pass_names!($mod_name : $pass_name $( { $($ident),* } )? )); | |
| )+ | |
| )* | |
| set | |
| }); | |
| }; | |
| } | |
| macro_rules! pass_names { | |
| // pass groups: only pass names inside are considered pass_names | |
| ($mod_name:ident : $pass_group:ident { $($pass_name:ident),* $(,)? }) => { | |
| [ | |
| $( | |
| $mod_name::$pass_group::$pass_name.name(), | |
| )* | |
| ] | |
| }; | |
| // lone pass names: stringify the struct or enum name | |
| ($mod_name:ident : $pass_name:ident) => { | |
| [stringify!($pass_name)] | |
| }; | |
| } | |
| declare_passes! { | |
| mod abort_unwinding_calls : AbortUnwindingCalls; | |
| mod add_call_guards : AddCallGuards { AllCallEdges, CriticalCallEdges }; | |
| mod add_moves_for_packed_drops : AddMovesForPackedDrops; | |
| mod add_subtyping_projections : Subtyper; | |
| mod check_inline : CheckForceInline; | |
| mod check_call_recursion : CheckCallRecursion, CheckDropRecursion; | |
| mod check_alignment : CheckAlignment; | |
| mod check_enums : CheckEnums; | |
| mod check_const_item_mutation : CheckConstItemMutation; | |
| mod check_null : CheckNull; | |
| mod check_packed_ref : CheckPackedRef; | |
| mod check_mut_restriction : CheckMutRestriction; | |
| // This pass is public to allow external drivers to perform MIR cleanup | |
| pub mod cleanup_post_borrowck : CleanupPostBorrowck; | |
| mod copy_prop : CopyProp; | |
| mod coroutine : StateTransform; | |
| mod coverage : InstrumentCoverage; | |
| mod ctfe_limit : CtfeLimit; | |
| mod dataflow_const_prop : DataflowConstProp; | |
| mod dead_store_elimination : DeadStoreElimination { | |
| Initial, | |
| Final | |
| }; | |
| mod deref_separator : Derefer; | |
| mod dest_prop : DestinationPropagation; | |
| mod early_otherwise_branch : EarlyOtherwiseBranch; | |
| mod erase_deref_temps : EraseDerefTemps; | |
| mod elaborate_box_derefs : ElaborateBoxDerefs; | |
| mod elaborate_drops : ElaborateDrops; | |
| mod function_item_references : FunctionItemReferences; | |
| mod gvn : GVN; | |
| // Made public so that `mir_drops_elaborated_and_const_checked` can be overridden | |
| // by custom rustc drivers, running all the steps by themselves. See #114628. | |
| pub mod inline : Inline, ForceInline; | |
| mod impossible_clauses : ImpossibleClauses; | |
| mod instsimplify : InstSimplify { BeforeInline, AfterSimplifyCfg }; | |
| mod jump_threading : JumpThreading; | |
| mod known_panics_lint : KnownPanicsLint; | |
| mod large_enums : EnumSizeOpt; | |
| mod lint_and_remove_uninhabited : LintAndRemoveUninhabited; | |
| mod lower_intrinsics : LowerIntrinsics; | |
| mod lower_slice_len : LowerSliceLenCalls; | |
| mod match_branches : MatchBranchSimplification; | |
| mod mentioned_items : MentionedItems; | |
| mod multiple_return_terminators : MultipleReturnTerminators; | |
| mod post_drop_elaboration : CheckLiveDrops; | |
| mod prettify : ReorderBasicBlocks, ReorderLocals; | |
| mod promote_consts : PromoteTemps; | |
| mod ref_prop : ReferencePropagation; | |
| pub mod remove_noop_landing_pads : RemoveNoopLandingPads; | |
| mod remove_place_mention : RemovePlaceMention; | |
| mod remove_storage_markers : RemoveStorageMarkers; | |
| mod remove_uninit_drops : RemoveUninitDrops; | |
| mod remove_unneeded_drops : RemoveUnneededDrops; | |
| mod remove_zsts : RemoveZsts; | |
| mod required_consts : RequiredConstsVisitor; | |
| mod post_analysis_normalize : PostAnalysisNormalize; | |
| mod sanity_check : SanityCheck; | |
| // This pass is public to allow external drivers to perform MIR cleanup | |
| pub mod simplify : | |
| SimplifyCfg { | |
| Initial, | |
| PromoteConsts, | |
| RemoveFalseEdges, | |
| PostAnalysis, | |
| PreOptimizations, | |
| Final, | |
| MakeShim, | |
| AfterUnreachableEnumBranching | |
| }, | |
| SimplifyLocals { | |
| BeforeConstProp, | |
| AfterGVN, | |
| Final | |
| }; | |
| mod simplify_branches : SimplifyConstCondition { | |
| AfterInstSimplify, | |
| AfterConstProp, | |
| Final | |
| }; | |
| mod simplify_comparison_integral : SimplifyComparisonIntegral; | |
| mod single_use_consts : SingleUseConsts; | |
| mod sroa : ScalarReplacementOfAggregates; | |
| mod strip_debuginfo : StripDebugInfo; | |
| mod ssa_range_prop: SsaRangePropagation; | |
| mod unreachable_enum_branching : UnreachableEnumBranching; | |
| mod unreachable_prop : UnreachablePropagation; | |
| mod validate : Validator; | |
| } | |
| pub fn provide(providers: &mut Providers) { | |
| coverage::query::provide(providers); | |
| ffi_unwind_calls::provide(&mut providers.queries); | |
| shim::provide(&mut providers.queries); | |
| cross_crate_inline::provide(&mut providers.queries); | |
| providers.queries = query::Providers { | |
| mir_keys, | |
| mir_built, | |
| mir_const_qualif, | |
| mir_promoted, | |
| mir_drops_elaborated_and_const_checked, | |
| mir_for_ctfe, | |
| mir_coroutine_witnesses: coroutine::mir_coroutine_witnesses, | |
| optimized_mir, | |
| check_liveness: liveness::check_liveness, | |
| is_mir_available, | |
| mir_callgraph_cyclic: inline::cycle::mir_callgraph_cyclic, | |
| mir_inliner_callees: inline::cycle::mir_inliner_callees, | |
| promoted_mir, | |
| deduced_param_attrs: deduce_param_attrs::deduced_param_attrs, | |
| coroutine_by_move_body_def_id: coroutine::coroutine_by_move_body_def_id, | |
| trivial_const: trivial_const::trivial_const_provider, | |
| ..providers.queries | |
| }; | |
| } | |
| fn remap_mir_for_const_eval_select<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| mut body: Body<'tcx>, | |
| context: hir::Constness, | |
| ) -> Body<'tcx> { | |
| for bb in body.basic_blocks.as_mut().iter_mut() { | |
| let terminator = bb.terminator.as_mut().expect("invalid terminator"); | |
| match terminator.kind { | |
| TerminatorKind::Call { | |
| func: Operand::Constant(ConstOperand { ref const_, .. }), | |
| ref mut args, | |
| destination, | |
| target, | |
| unwind, | |
| fn_span, | |
| .. | |
| } if let ty::FnDef(def_id, _) = *const_.ty().kind() | |
| && tcx.is_intrinsic(def_id, sym::const_eval_select) => | |
| { | |
| let Ok([tupled_args, called_in_const, called_at_rt]) = take_array(args) else { | |
| unreachable!() | |
| }; | |
| let ty = tupled_args.node.ty(&body.local_decls, tcx); | |
| let fields = ty.tuple_fields(); | |
| let num_args = fields.len(); | |
| let func = match context { | |
| // Using `const_eval_select` in always-const code is useful when used in macros | |
| // that you don't know whether they are going to be used in `const fn` or in `const` items. | |
| hir::Constness::Const { .. } => called_in_const, | |
| hir::Constness::NotConst => called_at_rt, | |
| }; | |
| let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) = | |
| match tupled_args.node { | |
| Operand::Constant(_) | Operand::RuntimeChecks(_) => { | |
| // There is no good way of extracting a tuple arg from a constant | |
| // (const generic stuff) so we just create a temporary and deconstruct | |
| // that. | |
| let local = body.local_decls.push(LocalDecl::new(ty, fn_span)); | |
| bb.statements.push(Statement::new( | |
| SourceInfo::outermost(fn_span), | |
| StatementKind::Assign(Box::new(( | |
| local.into(), | |
| Rvalue::Use(tupled_args.node.clone(), WithRetag::Yes), | |
| ))), | |
| )); | |
| (Operand::Move, local.into()) | |
| } | |
| Operand::Move(place) => (Operand::Move, place), | |
| Operand::Copy(place) => (Operand::Copy, place), | |
| }; | |
| let place_elems = place.projection; | |
| let arguments = (0..num_args) | |
| .map(|x| { | |
| let mut place_elems = place_elems.to_vec(); | |
| place_elems.push(ProjectionElem::Field(x.into(), fields[x])); | |
| let projection = tcx.mk_place_elems(&place_elems); | |
| let place = Place { local: place.local, projection }; | |
| Spanned { node: method(place), span: DUMMY_SP } | |
| }) | |
| .collect(); | |
| terminator.kind = TerminatorKind::Call { | |
| func: func.node, | |
| args: arguments, | |
| destination, | |
| target, | |
| unwind, | |
| call_source: CallSource::Misc, | |
| fn_span, | |
| }; | |
| } | |
| _ => {} | |
| } | |
| } | |
| body | |
| } | |
| fn take_array<T, const N: usize>(b: &mut Box<[T]>) -> Result<[T; N], Box<[T]>> { | |
| let b: Box<[T; N]> = std::mem::take(b).try_into()?; | |
| Ok(*b) | |
| } | |
| fn is_mir_available(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { | |
| tcx.mir_keys(()).contains(&def_id) | |
| } | |
| /// Finds the full set of `DefId`s within the current crate that have | |
| /// MIR associated with them. | |
| fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> { | |
| // All body-owners have MIR associated with them. | |
| let mut set: FxIndexSet<_> = tcx.hir_body_owners().collect(); | |
| // Remove the fake bodies for `global_asm!`, since they're not useful | |
| // to be emitted (`--emit=mir`) or encoded (in metadata). | |
| set.retain(|&def_id| !matches!(tcx.def_kind(def_id), DefKind::GlobalAsm)); | |
| // Coroutine-closures (e.g. async closures) have an additional by-move MIR | |
| // body that isn't in the HIR. | |
| for body_owner in tcx.hir_body_owners() { | |
| if let DefKind::Closure = tcx.def_kind(body_owner) | |
| && tcx.needs_coroutine_by_move_body_def_id(body_owner.to_def_id()) | |
| { | |
| set.insert(tcx.coroutine_by_move_body_def_id(body_owner).expect_local()); | |
| } | |
| } | |
| // tuple struct/variant constructors have MIR, but they don't have a BodyId, | |
| // so we need to build them separately. | |
| for item in tcx.hir_crate_items(()).free_items() { | |
| if let DefKind::Struct | DefKind::Enum = tcx.def_kind(item.owner_id) { | |
| for variant in tcx.adt_def(item.owner_id).variants() { | |
| if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor { | |
| set.insert(ctor_def_id.expect_local()); | |
| } | |
| } | |
| } | |
| } | |
| set | |
| } | |
| fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { | |
| // N.B., this `borrow()` is guaranteed to be valid (i.e., the value | |
| // cannot yet be stolen), because `mir_promoted()`, which steals | |
| // from `mir_built()`, forces this query to execute before | |
| // performing the steal. | |
| let body = &tcx.mir_built(def).borrow(); | |
| let ccx = check_consts::ConstCx::new(tcx, body); | |
| // No need to const-check a non-const `fn`. | |
| match ccx.const_kind { | |
| Some(ConstContext::Const { .. } | ConstContext::Static(_) | ConstContext::ConstFn) => {} | |
| None => span_bug!( | |
| tcx.def_span(def), | |
| "`mir_const_qualif` should only be called on const fns and const items" | |
| ), | |
| } | |
| if body.return_ty().references_error() { | |
| // It's possible to reach here without an error being emitted (#121103). | |
| tcx.dcx().span_delayed_bug(body.span, "mir_const_qualif: MIR had errors"); | |
| return Default::default(); | |
| } | |
| let mut validator = check_consts::check::Checker::new(&ccx); | |
| validator.check_body(); | |
| // We return the qualifs in the return place for every MIR body, even though it is only used | |
| // when deciding to promote a reference to a `const` for now. | |
| validator.qualifs_in_return_place() | |
| } | |
| /// Implementation of the `mir_built` query. | |
| fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> { | |
| // Delegate to the main MIR building code in the `rustc_mir_build` crate. | |
| // This is the one place that is allowed to call `build_mir_inner_impl`. | |
| let mut body = tcx.build_mir_inner_impl(def); | |
| // Identifying trivial consts based on their mir_built is easy, but a little wasteful. | |
| // Trying to push this logic earlier in the compiler and never even produce the Body would | |
| // probably improve compile time. | |
| if trivial_const::trivial_const(tcx, def, || &body).is_some() { | |
| // Skip all the passes below for trivial consts. | |
| let body = tcx.alloc_steal_mir(body); | |
| pass_manager::dump_mir_for_phase_change(tcx, &body.borrow()); | |
| return body; | |
| } | |
| pass_manager::dump_mir_for_phase_change(tcx, &body); | |
| pm::run_passes( | |
| tcx, | |
| &mut body, | |
| &[ | |
| // This used to be part of MIR building, | |
| // now done separately to separate concerns. | |
| &lint_and_remove_uninhabited::LintAndRemoveUninhabited, | |
| // MIR-level lints. | |
| &Lint(check_inline::CheckForceInline), | |
| &Lint(check_call_recursion::CheckCallRecursion), | |
| &Lint(check_packed_ref::CheckPackedRef), | |
| &Lint(check_const_item_mutation::CheckConstItemMutation), | |
| &Lint(check_mut_restriction::CheckMutRestriction), | |
| &Lint(function_item_references::FunctionItemReferences), | |
| // What we need to do constant evaluation. | |
| &simplify::SimplifyCfg::Initial, | |
| &Lint(sanity_check::SanityCheck), | |
| ], | |
| None, | |
| ); | |
| tcx.alloc_steal_mir(body) | |
| } | |
| /// Compute the main MIR body and the list of MIR bodies of the promoteds. | |
| fn mir_promoted( | |
| tcx: TyCtxt<'_>, | |
| def: LocalDefId, | |
| ) -> (&Steal<Body<'_>>, &Steal<IndexVec<Promoted, Body<'_>>>) { | |
| debug_assert!(!tcx.is_trivial_const(def), "Tried to get mir_promoted of a trivial const"); | |
| debug_assert!(!tcx.is_constructor(def.to_def_id())); | |
| // Ensure that we compute the `mir_const_qualif` for constants at | |
| // this point, before we steal the mir-const result. | |
| // Also this means promotion can rely on all const checks having been done. | |
| let const_qualifs = match tcx.def_kind(def) { | |
| DefKind::Fn | DefKind::AssocFn | DefKind::Closure | |
| if matches!(tcx.constness(def), hir::Constness::Const { .. }) => | |
| { | |
| tcx.mir_const_qualif(def) | |
| } | |
| DefKind::AssocConst { .. } | |
| | DefKind::Const { .. } | |
| | DefKind::Static { .. } | |
| | DefKind::AnonConst => tcx.mir_const_qualif(def), | |
| _ => ConstQualifs::default(), | |
| }; | |
| // the `has_ffi_unwind_calls` query uses the raw mir, so make sure it is run. | |
| tcx.ensure_done().has_ffi_unwind_calls(def); | |
| // the `by_move_body` query uses the raw mir, so make sure it is run. | |
| if tcx.needs_coroutine_by_move_body_def_id(def.to_def_id()) { | |
| tcx.ensure_done().coroutine_by_move_body_def_id(def); | |
| } | |
| // the `trivial_const` query uses mir_built, so make sure it is run. | |
| tcx.ensure_done().trivial_const(def); | |
| let mut body = tcx.mir_built(def).steal(); | |
| if let Some(error_reported) = const_qualifs.tainted_by_errors { | |
| body.tainted_by_errors = Some(error_reported); | |
| } | |
| // Collect `required_consts` *before* promotion, so if there are any consts being promoted | |
| // we still add them to the list in the outer MIR body. | |
| RequiredConstsVisitor::compute_required_consts(&mut body); | |
| // What we need to run borrowck etc. | |
| let promote_pass = promote_consts::PromoteTemps::default(); | |
| pm::run_passes( | |
| tcx, | |
| &mut body, | |
| &[&promote_pass, &simplify::SimplifyCfg::PromoteConsts, &coverage::InstrumentCoverage], | |
| Some(MirPhase::Analysis(AnalysisPhase::Initial)), | |
| ); | |
| lint_tail_expr_drop_order::run_lint(tcx, def, &body); | |
| let promoted = promote_pass.promoted_fragments.into_inner(); | |
| (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted)) | |
| } | |
| /// Compute the MIR that is used during CTFE (and thus has no optimizations run on it) | |
| fn mir_for_ctfe(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &Body<'_> { | |
| debug_assert!(!tcx.is_trivial_const(def_id), "Tried to get mir_for_ctfe of a trivial const"); | |
| tcx.arena.alloc(inner_mir_for_ctfe(tcx, def_id)) | |
| } | |
| fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> { | |
| if tcx.is_constructor(def.to_def_id()) { | |
| // There's no reason to run all of the MIR passes on constructors when | |
| // we can just output the MIR we want directly. This also saves const | |
| // qualification and borrow checking the trouble of special casing | |
| // constructors. | |
| return shim::build_adt_ctor(tcx, def.to_def_id()); | |
| } | |
| let body = tcx.mir_drops_elaborated_and_const_checked(def); | |
| let (body, always) = match tcx.hir_body_const_context(def) { | |
| // consts and statics do not have `optimized_mir`, so we can steal the body instead of | |
| // cloning it. | |
| Some(hir::ConstContext::Const { .. } | hir::ConstContext::Static(_)) => { | |
| (body.steal(), true) | |
| } | |
| Some(hir::ConstContext::ConstFn) => (body.borrow().clone(), false), | |
| None => bug!("`mir_for_ctfe` called on non-const {def:?}"), | |
| }; | |
| let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const { always }); | |
| // FIXME(reflection): probably need to look at this for comptime closures | |
| let passes: &[&dyn MirPass<'_>] = if matches!(tcx.def_kind(def), DefKind::Fn | DefKind::AssocFn) | |
| && matches!(tcx.constness(def), hir::Constness::Const { always: true }) | |
| { | |
| // Need to generate mentioned items, as all functions are expected to have them, but for const | |
| // fns we just look at the optimized MIR, which generates it. For comptime fns, there is no | |
| // optimized MIR. | |
| &[&ctfe_limit::CtfeLimit, &mentioned_items::MentionedItems] | |
| } else { | |
| &[&ctfe_limit::CtfeLimit] | |
| }; | |
| pm::run_passes(tcx, &mut body, passes, None); | |
| body | |
| } | |
| /// Obtain just the main MIR (no promoteds) and run some cleanups on it. This also runs | |
| /// mir borrowck *before* doing so in order to ensure that borrowck can be run and doesn't | |
| /// end up missing the source MIR due to stealing happening. | |
| fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> { | |
| if tcx.is_coroutine(def.to_def_id()) { | |
| tcx.ensure_done().mir_coroutine_witnesses(def); | |
| } | |
| // We only need to borrowck non-synthetic MIR. | |
| let tainted_by_errors = if !tcx.is_synthetic_mir(def) { | |
| tcx.mir_borrowck(tcx.typeck_root_def_id_local(def)).err() | |
| } else { | |
| None | |
| }; | |
| let is_fn_like = tcx.def_kind(def).is_fn_like(); | |
| if is_fn_like { | |
| // Do not compute the mir call graph without said call graph actually being used. | |
| if pm::should_run_pass(tcx, &inline::Inline, pm::Optimizations::Allowed) | |
| || inline::ForceInline::should_run_pass_for_callee(tcx, def.to_def_id()) | |
| { | |
| tcx.ensure_done().mir_inliner_callees(ty::InstanceKind::Item(def.to_def_id())); | |
| } | |
| } | |
| tcx.ensure_done().check_liveness(def); | |
| let (body, _) = tcx.mir_promoted(def); | |
| let mut body = body.steal(); | |
| if let Some(error_reported) = tainted_by_errors { | |
| body.tainted_by_errors = Some(error_reported); | |
| } | |
| let root = tcx.typeck_root_def_id_local(def); | |
| if let Err(e) = tcx.check_transmutes(root) { | |
| body.tainted_by_errors = Some(e); | |
| } | |
| // Also taint the body if it's within a top-level item that is not well formed. | |
| // | |
| // We do this check here and not during `mir_promoted` because that may result | |
| // in borrowck cycles if WF requires looking into an opaque hidden type. | |
| match tcx.def_kind(root) { | |
| DefKind::Fn | |
| | DefKind::AssocFn | |
| | DefKind::Static { .. } | |
| | DefKind::Const { .. } | |
| | DefKind::AssocConst { .. } => { | |
| if let Err(guar) = tcx.ensure_result().check_well_formed(root) { | |
| body.tainted_by_errors = Some(guar); | |
| } | |
| } | |
| _ => {} | |
| } | |
| run_analysis_to_runtime_passes(tcx, &mut body); | |
| tcx.alloc_steal_mir(body) | |
| } | |
| // Made public so that `mir_drops_elaborated_and_const_checked` can be overridden | |
| // by custom rustc drivers, running all the steps by themselves. See #114628. | |
| pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | |
| assert!(body.phase == MirPhase::Analysis(AnalysisPhase::Initial)); | |
| let did = body.source.def_id(); | |
| debug!("analysis_mir_cleanup({:?})", did); | |
| run_analysis_cleanup_passes(tcx, body); | |
| assert!(body.phase == MirPhase::Analysis(AnalysisPhase::PostCleanup)); | |
| // Do a little drop elaboration before const-checking if `const_precise_live_drops` is enabled. | |
| if check_consts::post_drop_elaboration::checking_enabled(&ConstCx::new(tcx, body)) { | |
| pm::run_passes( | |
| tcx, | |
| body, | |
| &[ | |
| &remove_uninit_drops::RemoveUninitDrops, | |
| &simplify::SimplifyCfg::RemoveFalseEdges, | |
| &Lint(post_drop_elaboration::CheckLiveDrops), | |
| ], | |
| None, | |
| ); | |
| } | |
| debug!("runtime_mir_lowering({:?})", did); | |
| run_runtime_lowering_passes(tcx, body); | |
| assert!(body.phase == MirPhase::Runtime(RuntimePhase::Initial)); | |
| debug!("runtime_mir_cleanup({:?})", did); | |
| run_runtime_cleanup_passes(tcx, body); | |
| assert!(body.phase == MirPhase::Runtime(RuntimePhase::PostCleanup)); | |
| } | |
| // FIXME(JakobDegen): Can we make these lists of passes consts? | |
| /// After this series of passes, no lifetime analysis based on borrowing can be done. | |
| fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | |
| let passes: &[&dyn MirPass<'tcx>] = &[ | |
| &impossible_clauses::ImpossibleClauses, | |
| &cleanup_post_borrowck::CleanupPostBorrowck, | |
| &remove_noop_landing_pads::RemoveNoopLandingPads, | |
| &simplify::SimplifyCfg::PostAnalysis, | |
| &deref_separator::Derefer, | |
| ]; | |
| pm::run_passes(tcx, body, passes, Some(MirPhase::Analysis(AnalysisPhase::PostCleanup))); | |
| } | |
| /// Returns the sequence of passes that lowers analysis to runtime MIR. | |
| fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | |
| let passes: &[&dyn MirPass<'tcx>] = &[ | |
| // These next passes must be executed together. | |
| &add_call_guards::CriticalCallEdges, | |
| // Must be done before drop elaboration because we need to drop opaque types, too. | |
| &post_analysis_normalize::PostAnalysisNormalize, | |
| // Calling this after `PostAnalysisNormalize` ensures that we don't deal with opaque types. | |
| &add_subtyping_projections::Subtyper, | |
| &elaborate_drops::ElaborateDrops, | |
| // Needs to happen after drop elaboration. | |
| &Lint(check_call_recursion::CheckDropRecursion), | |
| // This will remove extraneous landing pads which are no longer | |
| // necessary as well as forcing any call in a non-unwinding | |
| // function calling a possibly-unwinding function to abort the process. | |
| &abort_unwinding_calls::AbortUnwindingCalls, | |
| // AddMovesForPackedDrops needs to run after drop | |
| // elaboration. | |
| &add_moves_for_packed_drops::AddMovesForPackedDrops, | |
| &erase_deref_temps::EraseDerefTemps, | |
| &elaborate_box_derefs::ElaborateBoxDerefs, | |
| &coroutine::StateTransform, | |
| &Lint(known_panics_lint::KnownPanicsLint), | |
| ]; | |
| pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial))); | |
| } | |
| /// Returns the sequence of passes that do the initial cleanup of runtime MIR. | |
| fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | |
| let passes: &[&dyn MirPass<'tcx>] = &[ | |
| &lower_intrinsics::LowerIntrinsics, | |
| &remove_place_mention::RemovePlaceMention, | |
| &simplify::SimplifyCfg::PreOptimizations, | |
| ]; | |
| pm::run_passes(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::PostCleanup))); | |
| // Clear this by anticipation. Optimizations and runtime MIR have no reason to look | |
| // into this information, which is meant for borrowck diagnostics. | |
| for decl in &mut body.local_decls { | |
| decl.local_info = ClearCrossCrate::Clear; | |
| } | |
| } | |
| pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | |
| fn o1<T>(x: T) -> WithMinOptLevel<T> { | |
| WithMinOptLevel(1, x) | |
| } | |
| // The main optimizations that we do on MIR. | |
| pm::run_passes( | |
| tcx, | |
| body, | |
| &[ | |
| // Add some UB checks before any UB gets optimized away. | |
| &check_alignment::CheckAlignment, | |
| &check_null::CheckNull, | |
| &check_enums::CheckEnums, | |
| // Before inlining: trim down MIR with passes to reduce inlining work. | |
| // Has to be done before inlining, otherwise actual call will be almost always inlined. | |
| // Also simple, so can just do first. | |
| &lower_slice_len::LowerSliceLenCalls, | |
| // Perform instsimplify before inline to eliminate some trivial calls (like clone | |
| // shims). | |
| &instsimplify::InstSimplify::BeforeInline, | |
| // Perform inlining of `#[rustc_force_inline]`-annotated callees. | |
| &inline::ForceInline, | |
| // Perform inlining, which may add a lot of code. | |
| &inline::Inline, | |
| // Inlining may have introduced a lot of redundant code and a large move pattern. | |
| // Now, we need to shrink the generated MIR. | |
| // Code from other crates may have storage markers, so this needs to happen after | |
| // inlining. | |
| &remove_storage_markers::RemoveStorageMarkers, | |
| // Inlining and instantiation may introduce ZST and useless drops. | |
| &remove_zsts::RemoveZsts, | |
| &remove_unneeded_drops::RemoveUnneededDrops, | |
| // Type instantiation may create uninhabited enums. | |
| // Also eliminates some unreachable branches based on variants of enums. | |
| &unreachable_enum_branching::UnreachableEnumBranching, | |
| &unreachable_prop::UnreachablePropagation, | |
| &o1(simplify::SimplifyCfg::AfterUnreachableEnumBranching), | |
| &multiple_return_terminators::MultipleReturnTerminators, | |
| // After simplifycfg, it allows us to discover new opportunities for peephole | |
| // optimizations. This invalidates CFG caches, so avoid putting between | |
| // `ReferencePropagation` and `GVN` which both use the dominator tree. | |
| &instsimplify::InstSimplify::AfterSimplifyCfg, | |
| // After `InstSimplify-after-simplifycfg` with `-Zub_checks=false`, simplify | |
| // ``` | |
| // _13 = const false; | |
| // assume(copy _13); | |
| // Call(precondition_check); | |
| // ``` | |
| // to unreachable to eliminate the call to help later passes. | |
| // This invalidates CFG caches also. | |
| &o1(simplify_branches::SimplifyConstCondition::AfterInstSimplify), | |
| &ref_prop::ReferencePropagation, | |
| &sroa::ScalarReplacementOfAggregates, | |
| &simplify::SimplifyLocals::BeforeConstProp, | |
| &dead_store_elimination::DeadStoreElimination::Initial, | |
| &gvn::GVN, | |
| &simplify::SimplifyLocals::AfterGVN, | |
| // This pass does attempt to track assignments. | |
| // Keep it close to GVN which merges identical values into the same local. | |
| &ssa_range_prop::SsaRangePropagation, | |
| &match_branches::MatchBranchSimplification, | |
| &dataflow_const_prop::DataflowConstProp, | |
| &single_use_consts::SingleUseConsts, | |
| &o1(simplify_branches::SimplifyConstCondition::AfterConstProp), | |
| &jump_threading::JumpThreading, | |
| &early_otherwise_branch::EarlyOtherwiseBranch, | |
| &simplify_comparison_integral::SimplifyComparisonIntegral, | |
| &o1(simplify_branches::SimplifyConstCondition::Final), | |
| &o1(remove_noop_landing_pads::RemoveNoopLandingPads), | |
| &o1(simplify::SimplifyCfg::Final), | |
| // After the last SimplifyCfg, because this wants one-block functions. | |
| &strip_debuginfo::StripDebugInfo, | |
| ©_prop::CopyProp, | |
| &dead_store_elimination::DeadStoreElimination::Final, | |
| &dest_prop::DestinationPropagation, | |
| &simplify::SimplifyLocals::Final, | |
| &multiple_return_terminators::MultipleReturnTerminators, | |
| &large_enums::EnumSizeOpt { discrepancy: 128 }, | |
| // Some cleanup necessary at least for LLVM and potentially other codegen backends. | |
| &add_call_guards::CriticalCallEdges, | |
| // Cleanup for human readability, off by default. | |
| &prettify::ReorderBasicBlocks, | |
| &prettify::ReorderLocals, | |
| ], | |
| Some(MirPhase::Runtime(RuntimePhase::Optimized)), | |
| ); | |
| } | |
| /// Optimize the MIR and prepare it for codegen. | |
| fn optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> &Body<'_> { | |
| if tcx.is_constructor(did.to_def_id()) { | |
| // There's no reason to run all of the MIR passes on constructors when | |
| // we can just output the MIR we want directly. This also saves const | |
| // qualification and borrow checking the trouble of special casing | |
| // constructors. | |
| return tcx.mir_for_ctfe(did); | |
| } | |
| tcx.arena.alloc(inner_optimized_mir(tcx, did)) | |
| } | |
| fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { | |
| match tcx.hir_body_const_context(did) { | |
| // Run the `mir_for_ctfe` query, which depends on `mir_drops_elaborated_and_const_checked` | |
| // which we are going to steal below. Thus we need to run `mir_for_ctfe` first, so it | |
| // computes and caches its result. | |
| Some(hir::ConstContext::ConstFn) => tcx.ensure_done().mir_for_ctfe(did), | |
| None => {} | |
| Some(other) => panic!("do not use `optimized_mir` for constants: {other:?}"), | |
| } | |
| debug!("about to call mir_drops_elaborated..."); | |
| let body = tcx.mir_drops_elaborated_and_const_checked(did).steal(); | |
| let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst); | |
| if body.tainted_by_errors.is_some() { | |
| return body; | |
| } | |
| // Before doing anything, remember which items are being mentioned so that the set of items | |
| // visited does not depend on the optimization level. | |
| // We do not use `run_passes` for this as that might skip the pass if `injection_phase` is set. | |
| mentioned_items::MentionedItems.run_pass(tcx, &mut body); | |
| // If `mir_drops_elaborated_and_const_checked` found that the current body has unsatisfiable | |
| // predicates, it will shrink the MIR to a single `unreachable` terminator. | |
| // More generally, if MIR is a lone `unreachable`, there is nothing to optimize. | |
| if let TerminatorKind::Unreachable = body.basic_blocks[START_BLOCK].terminator().kind | |
| && body.basic_blocks[START_BLOCK].statements.is_empty() | |
| { | |
| return body; | |
| } | |
| run_optimization_passes(tcx, &mut body); | |
| body | |
| } | |
| /// Fetch all the promoteds of an item and prepare their MIR bodies to be ready for | |
| /// constant evaluation once all generic parameters become known. | |
| fn promoted_mir(tcx: TyCtxt<'_>, def: LocalDefId) -> &IndexVec<Promoted, Body<'_>> { | |
| if tcx.is_constructor(def.to_def_id()) { | |
| return tcx.arena.alloc(IndexVec::new()); | |
| } | |
| if !tcx.is_synthetic_mir(def) { | |
| tcx.ensure_done().mir_borrowck(tcx.typeck_root_def_id_local(def)); | |
| } | |
| let mut promoted = tcx.mir_promoted(def).1.steal(); | |
| for body in &mut promoted { | |
| run_analysis_to_runtime_passes(tcx, body); | |
| } | |
| tcx.arena.alloc(promoted) | |
| } |