From fcdb1a50b42d22bb3ef04547b31fc304ca1c55aa Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Tue, 4 Aug 2026 20:54:58 +0100 Subject: [PATCH 1/2] mir-transform: Handle the `optimize` attribute the same as `-C opt-level` --- .../src/abort_unwinding_calls.rs | 2 +- .../src/add_call_guards.rs | 2 +- .../src/add_moves_for_packed_drops.rs | 2 +- .../src/add_subtyping_projections.rs | 2 +- .../src/check_alignment.rs | 5 +- .../rustc_mir_transform/src/check_enums.rs | 5 +- .../rustc_mir_transform/src/check_null.rs | 5 +- .../src/cleanup_post_borrowck.rs | 2 +- compiler/rustc_mir_transform/src/copy_prop.rs | 4 +- .../rustc_mir_transform/src/coroutine/mod.rs | 2 +- .../rustc_mir_transform/src/coverage/mod.rs | 4 +- .../src/cross_crate_inline.rs | 5 +- .../rustc_mir_transform/src/ctfe_limit.rs | 4 +- .../src/dataflow_const_prop.rs | 4 +- .../src/dead_store_elimination.rs | 4 +- .../src/deref_separator.rs | 2 +- compiler/rustc_mir_transform/src/dest_prop.rs | 4 +- .../src/early_otherwise_branch.rs | 4 +- .../src/elaborate_box_derefs.rs | 2 +- .../src/elaborate_drops.rs | 2 +- .../src/erase_deref_temps.rs | 2 +- compiler/rustc_mir_transform/src/gvn.rs | 4 +- .../src/impossible_clauses.rs | 4 +- compiler/rustc_mir_transform/src/inline.rs | 24 +-- .../rustc_mir_transform/src/inline/cycle.rs | 6 +- .../rustc_mir_transform/src/instsimplify.rs | 4 +- .../rustc_mir_transform/src/jump_threading.rs | 19 +-- .../rustc_mir_transform/src/large_enums.rs | 7 +- compiler/rustc_mir_transform/src/lib.rs | 4 +- .../src/lint_and_remove_uninhabited.rs | 2 +- .../src/lower_intrinsics.rs | 2 +- .../src/lower_slice_len.rs | 4 +- .../rustc_mir_transform/src/match_branches.rs | 4 +- .../src/mentioned_items.rs | 3 +- .../src/multiple_return_terminators.rs | 4 +- .../rustc_mir_transform/src/pass_manager.rs | 143 +++++++++--------- .../src/post_analysis_normalize.rs | 2 +- compiler/rustc_mir_transform/src/prettify.rs | 9 +- .../rustc_mir_transform/src/promote_consts.rs | 2 +- compiler/rustc_mir_transform/src/ref_prop.rs | 4 +- .../src/remove_noop_landing_pads.rs | 6 +- .../src/remove_place_mention.rs | 4 +- .../src/remove_storage_markers.rs | 6 +- .../src/remove_uninit_drops.rs | 2 +- .../src/remove_unneeded_drops.rs | 4 +- .../rustc_mir_transform/src/remove_zsts.rs | 4 +- compiler/rustc_mir_transform/src/simplify.rs | 8 +- .../src/simplify_branches.rs | 4 +- .../src/simplify_comparison_integral.rs | 4 +- .../src/single_use_consts.rs | 4 +- compiler/rustc_mir_transform/src/sroa.rs | 4 +- .../rustc_mir_transform/src/ssa_range_prop.rs | 4 +- .../src/strip_debuginfo.rs | 6 +- .../src/unreachable_enum_branching.rs | 4 +- .../src/unreachable_prop.rs | 4 +- compiler/rustc_mir_transform/src/validate.rs | 4 +- compiler/rustc_session/src/config.rs | 11 ++ compiler/rustc_session/src/session.rs | 7 +- tests/mir-opt/optimize_none.rs | 26 ++-- 59 files changed, 210 insertions(+), 225 deletions(-) diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index 6421ba6d7c866..ece5bc634d2bf 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -140,7 +140,7 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { super::simplify::remove_dead_blocks(body); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Implements part of MIR semantics, turning effectively implicit aborts into explicit // ones. PassPolicy::Required diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index 55d8493d55c3c..b73cb453752e1 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -129,7 +129,7 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { basic_blocks.extend(new_blocks); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Breaks critical edges so codegen can place edge-specific actions without affecting // other control-flow edges. PassPolicy::Required diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs index 378a5618f0faa..6b74d53801594 100644 --- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs @@ -70,7 +70,7 @@ impl<'tcx> crate::MirPass<'tcx> for AddMovesForPackedDrops { patch.apply(body); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Implements part of MIR semantics by making implicit packed-drop handling explicit. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index 08c4b0a0dc5e9..cab927d152196 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -66,7 +66,7 @@ impl<'tcx> crate::MirPass<'tcx> for Subtyper { checker.patcher.apply(body); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Later MIR phases expect all subtyping to be explicit. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index eeb96d109545e..56fae176360a1 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -5,7 +5,6 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::PlaceContext; use rustc_middle::mir::*; use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_session::Session; use crate::PassPolicy; use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers}; @@ -13,9 +12,9 @@ use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_poi pub(super) struct CheckAlignment; impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { - fn policy(&self, sess: &Session) -> PassPolicy { + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { // When UB checks are enabled this is part of their semantics, not an optimization. - PassPolicy::optional_non_optimization(sess.ub_checks()) + PassPolicy::optional(ctx.ub_checks()) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/check_enums.rs b/compiler/rustc_mir_transform/src/check_enums.rs index 3233d7022c520..1f5667ebcf116 100644 --- a/compiler/rustc_mir_transform/src/check_enums.rs +++ b/compiler/rustc_mir_transform/src/check_enums.rs @@ -7,7 +7,6 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::layout::PrimitiveExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypingEnv}; -use rustc_session::Session; use tracing::debug; use crate::PassPolicy; @@ -18,9 +17,9 @@ use crate::PassPolicy; pub(super) struct CheckEnums; impl<'tcx> crate::MirPass<'tcx> for CheckEnums { - fn policy(&self, sess: &Session) -> PassPolicy { + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { // When UB checks are enabled this is part of their semantics, not an optimization. - PassPolicy::optional_non_optimization(sess.ub_checks()) + PassPolicy::optional(ctx.ub_checks()) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/check_null.rs b/compiler/rustc_mir_transform/src/check_null.rs index 7b4ccb11c7cf4..ab2c48f0eb1e8 100644 --- a/compiler/rustc_mir_transform/src/check_null.rs +++ b/compiler/rustc_mir_transform/src/check_null.rs @@ -3,7 +3,6 @@ use rustc_index::IndexVec; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext}; use rustc_middle::mir::*; use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_session::Session; use crate::PassPolicy; use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers}; @@ -11,9 +10,9 @@ use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_poi pub(super) struct CheckNull; impl<'tcx> crate::MirPass<'tcx> for CheckNull { - fn policy(&self, sess: &Session) -> PassPolicy { + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { // When UB checks are enabled this is part of their semantics, not an optimization. - PassPolicy::optional_non_optimization(sess.ub_checks()) + PassPolicy::optional(ctx.ub_checks()) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index fa034119fb57c..a27d98c3e31f5 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -87,7 +87,7 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Removes administrative MIR instructions that later passes must never see. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index 6ba520f0431a2..d22a5c5cc4eef 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -22,8 +22,8 @@ use crate::ssa::{MaybeUninitializedLocals, SsaLocals}; pub(super) struct CopyProp; impl<'tcx> crate::MirPass<'tcx> for CopyProp { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() >= 1) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 1) } #[instrument(level = "trace", skip(self, tcx, body))] diff --git a/compiler/rustc_mir_transform/src/coroutine/mod.rs b/compiler/rustc_mir_transform/src/coroutine/mod.rs index c5c65553dae8c..ad91fda4df37d 100644 --- a/compiler/rustc_mir_transform/src/coroutine/mod.rs +++ b/compiler/rustc_mir_transform/src/coroutine/mod.rs @@ -1219,7 +1219,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { create_coroutine_resume_function(tcx, transform, body, can_return, can_unwind); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Implements coroutine semantics by lowering the coroutine body to a state machine. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index a9d9a593a0f26..6d87b54a1f4e8 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -25,8 +25,8 @@ mod tests; pub(super) struct InstrumentCoverage; impl<'tcx> crate::MirPass<'tcx> for InstrumentCoverage { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optional_non_optimization(sess.instrument_coverage()) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.instrument_coverage()) } fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 13b3304fda8f2..f516b91e1ded5 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -83,8 +83,9 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // Don't do any inference if codegen optimizations are disabled and also MIR inlining is not // enabled. This ensures that we do inference even if someone only passes -Zinline-mir, // which is less confusing than having to also enable -Copt-level=1. - let inliner_will_run = pm::should_run_pass(tcx, &inline::Inline, pm::Optimizations::Allowed) - || inline::ForceInline::should_run_pass_for_callee(tcx, def_id.to_def_id()); + let inliner_will_run = + pm::should_run_pass(&inline::Inline, &pm::PassCtx::for_body(tcx, def_id.to_def_id())) + || inline::ForceInline::should_run_pass_for_callee(tcx, def_id.to_def_id()); if matches!(tcx.sess.opts.optimize, OptLevel::No) && !inliner_will_run { return false; } diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index f9334590c1e42..e69b5c4060321 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -39,9 +39,9 @@ impl<'tcx> crate::MirPass<'tcx> for CtfeLimit { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // This is part of CTFE diagnostics rather than an optimization. - PassPolicy::optional_non_optimization(true) + PassPolicy::optional(true) } } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 2af68a9046e5a..9a94b73748f54 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -36,8 +36,8 @@ const PLACE_LIMIT: usize = 100; pub(super) struct DataflowConstProp; impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() >= 3) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 3) } #[instrument(skip_all level = "debug")] diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 879335d15dd98..3faceb6be295d 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -141,8 +141,8 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination { } } - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() >= 2) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 2) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs index ef5f8931ac600..3ca2a670a2f98 100644 --- a/compiler/rustc_mir_transform/src/deref_separator.rs +++ b/compiler/rustc_mir_transform/src/deref_separator.rs @@ -102,7 +102,7 @@ impl<'tcx> crate::MirPass<'tcx> for Derefer { deref_finder(tcx, body, true); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Later MIR stages expect derefs to only appear as the first place projection. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 924125404a07a..7f9bba03ff345 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -154,8 +154,8 @@ use crate::PassPolicy; pub(super) struct DestinationPropagation; impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() >= 2) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 2) } #[tracing::instrument(level = "trace", skip(self, tcx, body))] diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 28c7e7facc578..b13a4fa16e21d 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -95,8 +95,8 @@ use crate::patch::MirPatch; pub(super) struct EarlyOtherwiseBranch; impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() >= 2) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 2) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index 6ce39aac6a0db..fecd3865b3b32 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -150,7 +150,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Implements Box dereference semantics so backends and Miri do not have to handle them. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 84c9c044ae6a6..a19ae86542d25 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -88,7 +88,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { elaborate_patch.apply(body); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Implements MIR drop semantics. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/erase_deref_temps.rs b/compiler/rustc_mir_transform/src/erase_deref_temps.rs index a0c3dd930ccf6..20dde292d1413 100644 --- a/compiler/rustc_mir_transform/src/erase_deref_temps.rs +++ b/compiler/rustc_mir_transform/src/erase_deref_temps.rs @@ -39,7 +39,7 @@ impl<'tcx> crate::MirPass<'tcx> for EraseDerefTemps { EraseDerefTempsVisitor { tcx }.visit_body_preserves_cfg(body); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Later MIR stages assume that CopyForDeref is gone. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 9d751a7cc5bd0..81534b057641f 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -128,8 +128,8 @@ use crate::ssa::{MaybeUninitializedLocals, SsaLocals}; pub(super) struct GVN; impl<'tcx> crate::MirPass<'tcx> for GVN { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() >= 2) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 2) } #[instrument(level = "trace", skip(self, tcx, body))] diff --git a/compiler/rustc_mir_transform/src/impossible_clauses.rs b/compiler/rustc_mir_transform/src/impossible_clauses.rs index bb6644716bf05..f802de87ca11f 100644 --- a/compiler/rustc_mir_transform/src/impossible_clauses.rs +++ b/compiler/rustc_mir_transform/src/impossible_clauses.rs @@ -114,8 +114,8 @@ impl<'tcx> MirPass<'tcx> for ImpossibleClauses { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // This can only replace code proven unreachable with immediate UB, so it cannot remove UB. - PassPolicy::optional_non_optimization(true) + PassPolicy::optional(true) } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index ae20d21ea665f..efe5042c86b14 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -45,18 +45,18 @@ struct CallSite<'tcx> { pub struct Inline; impl<'tcx> crate::MirPass<'tcx> for Inline { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - let enabled_by_default = - sess.opts.unstable_opts.inline_mir.unwrap_or_else(|| match sess.mir_opt_level() { - 0 | 1 => false, - 2 => { - (sess.opts.optimize == OptLevel::More - || sess.opts.optimize == OptLevel::Aggressive) - && sess.opts.incremental == None - } + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + match ctx.opts.unstable_opts.inline_mir { + Some(enabled) => PassPolicy::optional(enabled), + None => PassPolicy::optional(match (ctx.mir_opt_level(), ctx.opts.optimize) { + (0 | 1, _) => false, + // Inlining reduces incremental effectiveness. + (2, OptLevel::More | OptLevel::Aggressive) => ctx.opts.incremental.is_none(), + // Don't inline if the global `-Copt-level` is 1/s/z + (2, _) => false, _ => true, - }); - PassPolicy::optimization(enabled_by_default) + }), + } } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -78,7 +78,7 @@ impl ForceInline { } impl<'tcx> crate::MirPass<'tcx> for ForceInline { - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Forced inlining is part of MIR semantics. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index b974cb656379d..17151661d9d8a 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -51,8 +51,10 @@ fn should_recurse<'tcx>(tcx: TyCtxt<'tcx>, callee: ty::Instance<'tcx>) -> bool { } } - crate::pm::should_run_pass(tcx, &crate::inline::Inline, crate::pm::Optimizations::Allowed) - || crate::inline::ForceInline::should_run_pass_for_callee(tcx, callee.def.def_id()) + crate::pm::should_run_pass( + &crate::inline::Inline, + &crate::pm::PassCtx::for_body(tcx, callee.def_id()), + ) || crate::inline::ForceInline::should_run_pass_for_callee(tcx, callee.def.def_id()) } #[instrument( diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 8f8b4aec7cbbc..71b27ebde8459 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -27,8 +27,8 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { } } - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() > 0) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 1) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index b704c39044ac9..69b91eac31b06 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -76,18 +76,13 @@ pub(super) struct JumpThreading; const MAX_COST: u8 = 100; impl<'tcx> crate::MirPass<'tcx> for JumpThreading { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - let enabled_by_default = if sess.target.is_like_gpu { - // Jump threading can duplicate calls in control-flow. - // This leads to incorrect code when done for so called "convergent" operations on GPU - // targets, similar to how inline assembly cannot be duplicated on all targets. - // Conservatively prevent this by disabling the pass. - // See also issue #137086. - false - } else { - sess.mir_opt_level() >= 2 - }; - PassPolicy::optimization(enabled_by_default) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + // Jump threading can duplicate calls in control-flow. + // This leads to incorrect code when done for so called "convergent" operations on GPU + // targets, similar to how inline assembly cannot be duplicated on all targets. + // Conservatively prevent this by disabling the pass. + // See also issue #137086. + PassPolicy::optional(ctx.mir_opt_level() >= 2 && !ctx.target.is_like_gpu) } #[instrument(skip_all level = "debug")] diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 43cd4198b2621..1fdf827c14044 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -5,7 +5,6 @@ use rustc_middle::mir::interpret::AllocId; use rustc_middle::mir::*; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; -use rustc_session::Session; use crate::PassPolicy; use crate::patch::MirPatch; @@ -32,13 +31,11 @@ pub(super) struct EnumSizeOpt { } impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { - fn policy(&self, sess: &Session) -> PassPolicy { + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { // There are some differences in behavior on wasm and ARM that are not properly // understood, so we conservatively treat this optimization as unsound: // https://github.com/rust-lang/rust/issues/154413 - PassPolicy::optimization( - sess.opts.unstable_opts.unsound_mir_opts && sess.mir_opt_level() >= 3, - ) + PassPolicy::optional(ctx.mir_opt_level() >= 3 && ctx.opts.unstable_opts.unsound_mir_opts) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index d2dd77c986318..6fdccad1505a5 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -35,7 +35,7 @@ mod pass_manager; use std::sync::LazyLock; -use pass_manager::{self as pm, Lint, MirLint, MirPass, PassPolicy, WithMinOptLevel}; +use pass_manager::{self as pm, Lint, MirLint, MirPass, PassCtx, PassPolicy, WithMinOptLevel}; mod check_pointers; mod cost_checker; @@ -549,7 +549,7 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & 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) + if pm::should_run_pass(&inline::Inline, &pm::PassCtx::for_body(tcx, def.to_def_id())) || 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())); diff --git a/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs b/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs index 1a7f94cc859e3..458be599c3fea 100644 --- a/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs +++ b/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs @@ -83,7 +83,7 @@ impl<'tcx> crate::MirPass<'tcx> for LintAndRemoveUninhabited { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Removing visibly uninhabited return edges determines the control flow seen by MIR checks. // Cannot remove UB: removing the return edge would *introduce* UB if the call actually returned. PassPolicy::Required diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 6126560949c79..1d5d02e4d88ee 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -339,7 +339,7 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Implements intrinsic semantics by lowering intrinsic calls to ordinary MIR operations. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index b157cf3d53d40..1dfba7d6c5200 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -10,8 +10,8 @@ use crate::PassPolicy; pub(super) struct LowerSliceLenCalls; impl<'tcx> crate::MirPass<'tcx> for LowerSliceLenCalls { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() > 0) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 1) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 36eed06fcadda..894f209f9b473 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -14,9 +14,9 @@ use crate::unreachable_prop::remove_successors_from_switch; pub(super) struct MatchBranchSimplification; impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { // Enable only under -Zmir-opt-level=2 as this can make programs less debuggable. - PassPolicy::optimization(sess.mir_opt_level() >= 2) + PassPolicy::optional(ctx.mir_opt_level() >= 2) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index 89146f5ce8548..e3cf9f246e669 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -2,7 +2,6 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, Location, MentionedItem}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::Session; use rustc_span::Spanned; use crate::PassPolicy; @@ -16,7 +15,7 @@ struct MentionedItemsVisitor<'a, 'tcx> { } impl<'tcx> crate::MirPass<'tcx> for MentionedItems { - fn policy(&self, _sess: &Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // If this pass is skipped the collector assume that nothing got mentioned! We could // potentially skip it in opt-level 0 if we are sure that opt-level will never *remove* uses // of anything, but that still seems fragile. Furthermore, even debug builds use level 1, so diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs index 8f2709e71dfcf..280a57e474237 100644 --- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs +++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs @@ -10,8 +10,8 @@ use crate::{PassPolicy, simplify}; pub(super) struct MultipleReturnTerminators; impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() >= 4) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 4) } fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 798f69d3cc883..ddf16d3fff4c4 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -3,9 +3,13 @@ use std::collections::hash_map::Entry; use std::sync::atomic::Ordering; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_hir::attrs::OptimizeAttr; +use rustc_hir::def_id::DefId; +use rustc_middle::bug; use rustc_middle::mir::{Body, MirDumper, MirPhase, RuntimePhase}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; +use rustc_session::config::OptLevel; use tracing::trace; use crate::lint::lint_body; @@ -89,39 +93,16 @@ pub(crate) enum PassPolicy { Required, /// An optional pass that may be configured by `-Zmir-enable-passes`. Optional { - /// Whether this pass should be enabled by default in this session in the absence of - /// an explicit `-Zmir-enable-passes` or `#[optimize(none)]`. - generally_enabled: bool, - /// Whether this is an optimization pass. `#[optimize(none)]` only disables optimization - /// passes. - /// A pass may be optional without being an optimization pass, - /// e.g. if it just adds extra debug checks that one can turn off. - optimization: bool, + /// Whether this pass should be enabled in the absence of an explicit + /// `-Zmir-enable-passes` override. + enabled_by_default: bool, }, } impl PassPolicy { - fn and_enabled(self, enabled: bool) -> Self { - match self { - PassPolicy::Required => PassPolicy::Required, - PassPolicy::Optional { generally_enabled: enabled_by_default, optimization } => { - PassPolicy::Optional { - generally_enabled: enabled_by_default && enabled, - optimization, - } - } - } - } - - /// Create a [`PassPolicy::Optional`] that is not an optimization, - /// enabled by default under the given condition. - pub(crate) fn optional_non_optimization(condition: bool) -> Self { - Self::Optional { generally_enabled: condition, optimization: false } - } - - /// Create a [`PassPolicy::Optional`] optimization, enabled by default under the given condition. - pub(crate) fn optimization(condition: bool) -> Self { - Self::Optional { generally_enabled: condition, optimization: true } + /// Create a [`PassPolicy::Optional`] enabled by default under the given condition. + pub(crate) fn optional(enabled_by_default: bool) -> Self { + Self::Optional { enabled_by_default } } } @@ -138,7 +119,7 @@ pub(super) trait MirPass<'tcx> { } /// Describes how this pass is enabled and which mechanisms may disable it. - fn policy(&self, sess: &Session) -> PassPolicy; + fn policy(&self, ctx: &PassCtx<'_>) -> PassPolicy; fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>); @@ -147,6 +128,43 @@ pub(super) trait MirPass<'tcx> { } } +#[derive(Copy, Clone)] +pub(super) struct PassCtx<'sess> { + /// Prefer [`Self::mir_opt_level`] to [`Session::mir_opt_level`] to account for overrides. + session: &'sess Session, + /// The MIR optimization level for this body; may be overridden by `#[optimize]`. + body_mir_opt_level: usize, +} + +impl<'sess> PassCtx<'sess> { + pub(super) fn for_body(tcx: TyCtxt<'sess>, def_id: DefId) -> Self { + let body_mir_opt_level = if !tcx.def_kind(def_id).has_codegen_attrs() { + tcx.sess.mir_opt_level() + } else { + match tcx.codegen_fn_attrs(def_id).optimize { + OptimizeAttr::Default => tcx.sess.mir_opt_level(), + OptimizeAttr::DoNotOptimize => OptLevel::No.mir_opt_level(), + OptimizeAttr::Speed => OptLevel::Aggressive.mir_opt_level(), + OptimizeAttr::Size => OptLevel::Size.mir_opt_level(), + } + }; + Self { session: tcx.sess, body_mir_opt_level } + } + + /// The effective MIR optimization level for this body, including `#[optimize]` overrides. + pub(super) fn mir_opt_level(&self) -> usize { + self.body_mir_opt_level + } +} + +impl std::ops::Deref for PassCtx<'_> { + type Target = Session; + + fn deref(&self) -> &Self::Target { + self.session + } +} + /// Just like `MirPass`, except it cannot mutate `Body`, and MIR dumping is /// disabled (via the `Lint` adapter). pub(super) trait MirLint<'tcx> { @@ -177,12 +195,12 @@ where false } - fn policy(&self, _sess: &Session) -> PassPolicy { - PassPolicy::optional_non_optimization(true) + fn policy(&self, _ctx: &PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(true) } } -pub(super) struct WithMinOptLevel(pub u32, pub T); +pub(super) struct WithMinOptLevel(pub usize, pub T); impl<'tcx, T> MirPass<'tcx> for WithMinOptLevel where @@ -196,22 +214,17 @@ where self.1.run_pass(tcx, body) } - fn policy(&self, sess: &Session) -> PassPolicy { - self.1.policy(sess).and_enabled(sess.mir_opt_level() >= self.0 as usize) + fn policy(&self, ctx: &PassCtx<'_>) -> PassPolicy { + let policy = self.1.policy(ctx); + match policy { + PassPolicy::Required => bug!("required pass cannot be gated by an opt level"), + PassPolicy::Optional { enabled_by_default } => PassPolicy::Optional { + enabled_by_default: enabled_by_default && ctx.mir_opt_level() >= self.0, + }, + } } } -/// Whether to allow [optimization passes]. -/// -/// [optimization passes]: PassPolicy::Optional::optimization -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum Optimizations { - /// The current function has `#[optimize(none)]`. - Suppressed, - /// Normal optimizations may run. - Allowed, -} - /// Run the sequence of passes without validating the MIR after each pass. The MIR is still /// validated at the end. pub(super) fn run_passes_no_validate<'tcx>( @@ -233,18 +246,13 @@ pub(super) fn run_passes<'tcx>( run_passes_inner(tcx, body, passes, phase_change, true); } -pub(super) fn should_run_pass<'tcx, P>( - tcx: TyCtxt<'tcx>, - pass: &P, - optimizations: Optimizations, -) -> bool +pub(super) fn should_run_pass<'tcx, P>(pass: &P, ctx: &PassCtx<'_>) -> bool where P: MirPass<'tcx> + ?Sized, { let name = pass.name(); let pass_override = || { - tcx.sess - .opts + ctx.opts .unstable_opts .mir_enable_passes .iter() @@ -252,9 +260,9 @@ where .find_map(|(name_, polarity)| if name == name_ { Some(*polarity) } else { None }) }; - match pass.policy(tcx.sess) { + match pass.policy(ctx) { PassPolicy::Required => true, - PassPolicy::Optional { generally_enabled: enabled_by_default, optimization } => { + PassPolicy::Optional { enabled_by_default } => { if let Some(o) = pass_override() { trace!( pass = %name, @@ -262,9 +270,6 @@ where if o { "Running" } else { "Not running" } ); o - } else if optimization && optimizations == Optimizations::Suppressed { - trace!(pass = %name, "Not running as requested by `#[optimize(none)]`"); - false } else { enabled_by_default } @@ -319,23 +324,16 @@ fn run_passes_inner<'tcx>( let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir; let lint = tcx.sess.opts.unstable_opts.lint_mir; - let def_id = body.source.def_id(); - let optimizations = if tcx.def_kind(def_id).has_codegen_attrs() - && tcx.codegen_fn_attrs(def_id).optimize.do_not_optimize() - { - Optimizations::Suppressed - } else { - Optimizations::Allowed - }; + let ctx = PassCtx::for_body(tcx, body.source.def_id()); for pass in passes { let pass_name = pass.name(); - if !should_run_pass(tcx, *pass, optimizations) { + if !should_run_pass(*pass, &ctx) { continue; }; - if is_optimization_stage(body, phase_change, optimizations) + if is_optimization_stage(body, phase_change) && let Some(limit) = &tcx.sess.opts.unstable_opts.mir_opt_bisect_limit && limited_by_opt_bisect( tcx, @@ -419,13 +417,8 @@ pub(super) fn dump_mir_for_phase_change<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tc } } -fn is_optimization_stage( - body: &Body<'_>, - phase_change: Option, - optimizations: Optimizations, -) -> bool { - optimizations == Optimizations::Allowed - && body.phase == MirPhase::Runtime(RuntimePhase::PostCleanup) +fn is_optimization_stage(body: &Body<'_>, phase_change: Option) -> bool { + body.phase == MirPhase::Runtime(RuntimePhase::PostCleanup) && phase_change == Some(MirPhase::Runtime(RuntimePhase::Optimized)) } diff --git a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs index 532e1097b5546..78f97c2eb1448 100644 --- a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs +++ b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs @@ -18,7 +18,7 @@ impl<'tcx> crate::MirPass<'tcx> for PostAnalysisNormalize { PostAnalysisNormalizeVisitor { tcx, typing_env }.visit_body_preserves_cfg(body); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Reveals opaque types and normalizes MIR while transitioning to the runtime dialect. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/prettify.rs b/compiler/rustc_mir_transform/src/prettify.rs index ea1988c0b5c5a..f86ad4d49e769 100644 --- a/compiler/rustc_mir_transform/src/prettify.rs +++ b/compiler/rustc_mir_transform/src/prettify.rs @@ -9,7 +9,6 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_session::Session; use crate::PassPolicy; @@ -20,8 +19,8 @@ use crate::PassPolicy; pub(super) struct ReorderBasicBlocks; impl<'tcx> crate::MirPass<'tcx> for ReorderBasicBlocks { - fn policy(&self, _session: &Session) -> PassPolicy { - PassPolicy::optional_non_optimization(false) + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(false) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -48,8 +47,8 @@ impl<'tcx> crate::MirPass<'tcx> for ReorderBasicBlocks { pub(super) struct ReorderLocals; impl<'tcx> crate::MirPass<'tcx> for ReorderLocals { - fn policy(&self, _session: &Session) -> PassPolicy { - PassPolicy::optional_non_optimization(false) + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(false) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index ae2028f1c62ea..ccd9969c3692e 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -64,7 +64,7 @@ impl<'tcx> crate::MirPass<'tcx> for PromoteTemps<'tcx> { self.promoted_fragments.set(promoted); } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Implements promotion by extracting eligible values into separate constant MIR bodies. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 1db9c36bee184..566f1851ff49e 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -73,8 +73,8 @@ use crate::ssa::{SsaLocals, StorageLiveLocals}; pub(super) struct ReferencePropagation; impl<'tcx> crate::MirPass<'tcx> for ReferencePropagation { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() >= 2) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 2) } #[instrument(level = "trace", skip(self, tcx, body))] diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 7d55756a9a694..c25404ed7ab99 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -12,10 +12,10 @@ use crate::patch::MirPatch; pub(super) struct RemoveNoopLandingPads; impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - // FIXME: isn't this an optimization? Or is the LLVM code so terrible we want this even with + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + // FIXME: Should this really run on opt-level 0? Or is the LLVM code so terrible we want this even with // "no" optimizations? - PassPolicy::optional_non_optimization(sess.panic_strategy().unwinds()) + PassPolicy::optional(ctx.panic_strategy().unwinds()) } #[instrument(level = "debug", skip(self, _tcx, body))] diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index bec46896a8d55..5123b19e1c0a1 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -9,8 +9,8 @@ use crate::PassPolicy; pub(super) struct RemovePlaceMention; impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optional_non_optimization(!sess.opts.unstable_opts.mir_preserve_ub) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(!ctx.opts.unstable_opts.mir_preserve_ub) } fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index 47fcbf2420164..833d281366adc 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -9,10 +9,8 @@ use crate::PassPolicy; pub(super) struct RemoveStorageMarkers; impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optional_non_optimization( - sess.mir_opt_level() > 0 && !sess.emit_lifetime_markers(), - ) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 1 && !ctx.emit_lifetime_markers()) } fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index 19ac267b62836..c5f462f714dd8 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -66,7 +66,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { // Const checking relies on uninitialized drops being removed before drop elaboration. PassPolicy::Required } diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index f36423a8c8e73..45a5c5626f98a 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -40,7 +40,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optional_non_optimization(true) + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(true) } } diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 6dddf4838a6c3..09379fc252072 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -9,8 +9,8 @@ use crate::PassPolicy; pub(super) struct RemoveZsts; impl<'tcx> crate::MirPass<'tcx> for RemoveZsts { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optional_non_optimization(sess.mir_opt_level() > 0) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 1) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 47d31f4e0d04b..32504fc72fecd 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -95,8 +95,8 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { self.name() } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(true) + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(true) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -429,8 +429,8 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyLocals { } } - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() > 0) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 1) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs index ceea038444e4d..58a3f1b571e59 100644 --- a/compiler/rustc_mir_transform/src/simplify_branches.rs +++ b/compiler/rustc_mir_transform/src/simplify_branches.rs @@ -23,8 +23,8 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(true) + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(true) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index a0ce3932a0952..68a8920c2d014 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -27,8 +27,8 @@ use crate::ssa::SsaLocals; pub(super) struct SimplifyComparisonIntegral; impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() > 1) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 2) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/single_use_consts.rs b/compiler/rustc_mir_transform/src/single_use_consts.rs index 21ba434acc6ad..6a3d4ae416425 100644 --- a/compiler/rustc_mir_transform/src/single_use_consts.rs +++ b/compiler/rustc_mir_transform/src/single_use_consts.rs @@ -25,8 +25,8 @@ use crate::strip_debuginfo::drop_invalid_debuginfos; pub(super) struct SingleUseConsts; impl<'tcx> crate::MirPass<'tcx> for SingleUseConsts { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() > 0) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 1) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index c115889205878..8ae130753be1d 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -16,8 +16,8 @@ use crate::patch::MirPatch; pub(super) struct ScalarReplacementOfAggregates; impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() >= 2) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 2) } #[instrument(level = "debug", skip(self, tcx, body))] diff --git a/compiler/rustc_mir_transform/src/ssa_range_prop.rs b/compiler/rustc_mir_transform/src/ssa_range_prop.rs index 0492398fd7bcb..79c6868bf87c2 100644 --- a/compiler/rustc_mir_transform/src/ssa_range_prop.rs +++ b/compiler/rustc_mir_transform/src/ssa_range_prop.rs @@ -25,8 +25,8 @@ use crate::ssa::SsaLocals; pub(super) struct SsaRangePropagation; impl<'tcx> crate::MirPass<'tcx> for SsaRangePropagation { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() > 1) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 2) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/strip_debuginfo.rs b/compiler/rustc_mir_transform/src/strip_debuginfo.rs index 7535ab166c757..46c53b53e49c9 100644 --- a/compiler/rustc_mir_transform/src/strip_debuginfo.rs +++ b/compiler/rustc_mir_transform/src/strip_debuginfo.rs @@ -12,10 +12,8 @@ use crate::PassPolicy; pub(super) struct StripDebugInfo; impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optional_non_optimization( - sess.opts.unstable_opts.mir_strip_debuginfo != MirStripDebugInfo::None, - ) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.opts.unstable_opts.mir_strip_debuginfo != MirStripDebugInfo::None) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs index 0bb6d87379caa..a7e35e5c4bf6b 100644 --- a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs @@ -78,8 +78,8 @@ fn variant_discriminants<'tcx>( } impl<'tcx> crate::MirPass<'tcx> for UnreachableEnumBranching { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optimization(sess.mir_opt_level() > 0) + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(ctx.mir_opt_level() >= 1) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index 3c9ae691c885c..84d6c39396248 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -15,9 +15,9 @@ use crate::patch::MirPatch; pub(super) struct UnreachablePropagation; impl crate::MirPass<'_> for UnreachablePropagation { - fn policy(&self, sess: &rustc_session::Session) -> PassPolicy { + fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { // Enable only under -Zmir-opt-level=2 as this can make programs less debuggable. - PassPolicy::optimization(sess.mir_opt_level() >= 2) + PassPolicy::optional(ctx.mir_opt_level() >= 2) } fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index b9c55439f0597..65c6adc5430e4 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -98,8 +98,8 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { } } - fn policy(&self, _sess: &rustc_session::Session) -> PassPolicy { - PassPolicy::optional_non_optimization(true) + fn policy(&self, _ctx: &crate::PassCtx<'_>) -> PassPolicy { + PassPolicy::optional(true) } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 1083d952e1bf9..e4fe955f49e25 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -108,6 +108,17 @@ pub enum OptLevel { SizeMin, } +impl OptLevel { + /// Infers an MIR opt-level (if not otherwise specified) from general opt-level. + /// Produces `1` at opt-level 0, and `2` at all other levels. + pub fn mir_opt_level(&self) -> usize { + match self { + OptLevel::No => 1, + _ => 2, + } + } +} + /// This is what the `LtoCli` values get mapped to after resolving defaults and /// and taking other command line options into account. /// diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 74f533e11e66f..b42ee1b10d818 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -37,7 +37,7 @@ pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, Varian use crate::config::{ self, BranchProtection, Cfg, CheckCfg, CoverageLevel, CoverageOptions, CrateType, DebugInfo, ErrorOutputType, FunctionReturn, Input, InstrumentCoverage, InstrumentMcount, NATIVE_CPU, - OptLevel, OutFileName, OutputType, PAuthKey, PointerAuthOption, SwitchWithOptPath, + OutFileName, OutputType, PAuthKey, PointerAuthOption, SwitchWithOptPath, }; use crate::filesearch::FileSearch; use crate::lint::LintId; @@ -835,10 +835,7 @@ impl Session { } pub fn mir_opt_level(&self) -> usize { - self.opts - .unstable_opts - .mir_opt_level - .unwrap_or_else(|| if self.opts.optimize != OptLevel::No { 2 } else { 1 }) + self.opts.unstable_opts.mir_opt_level.unwrap_or_else(|| self.opts.optimize.mir_opt_level()) } /// Calculates the flavor of LTO to use for this compilation. diff --git a/tests/mir-opt/optimize_none.rs b/tests/mir-opt/optimize_none.rs index 23245f6cc687e..657cfd0675503 100644 --- a/tests/mir-opt/optimize_none.rs +++ b/tests/mir-opt/optimize_none.rs @@ -13,21 +13,19 @@ pub fn add_noopt() -> i32 { } #[optimize(none)] -pub fn const_branch() -> i32 { - // CHECK-LABEL: fn const_branch( - // CHECK: [[BOOL:_[0-9]+]] = const true; - // CHECK: switchInt(move [[BOOL]]) -> [0: [[BB_FALSE_SHIM:bb[0-9]+]], otherwise: [[BB_TRUE:bb[0-9]+]]]; - // CHECK-NEXT: } - // CHECK: [[BB_FALSE_SHIM]]: { - // CHECK-NEXT: goto -> [[BB_FALSE:bb[0-9]+]] - // CHECK: [[BB_FALSE]]: { - // CHECK-NEXT: _0 = const 0 - // CHECK: [[BB_TRUE]]: { - // CHECK-NEXT: _0 = const 1 - // CHECK-NEXT: goto - // CHECK-NEXT: } +#[allow(unused_assignments)] +pub fn dead_store_noopt(input: i32) -> i32 { + // CHECK-LABEL: fn dead_store_noopt( + // CHECK: debug value => [[VALUE:_[0-9]+]]; + // CHECK: [[VALUE]] = copy _1; + // CHECK-NEXT: [[VALUE]] = const 1_i32; + // CHECK-NEXT: [[VALUE]] = const 2_i32; + // CHECK-NEXT: _0 = copy [[VALUE]]; - if true { 1 } else { 0 } + let mut value = input; + value = 1; + value = 2; + value } fn main() {} From c3ddd3dca2452994f72e0a1ad62c67edaff1b549 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Fri, 21 Aug 2026 19:56:23 +0100 Subject: [PATCH 2/2] mir-transform: Record source of opt-level decision for body --- compiler/rustc_mir_transform/src/inline.rs | 23 +++++--- .../rustc_mir_transform/src/pass_manager.rs | 52 ++++++++++++++++--- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index efe5042c86b14..50b6b474a4b11 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -23,6 +23,7 @@ use rustc_span::Spanned; use tracing::{debug, instrument, trace, trace_span}; use crate::cost_checker::{CostChecker, is_call_like}; +use crate::pass_manager::BodyMirOptLevel; use crate::simplify::{UsedInStmtLocals, simplify_cfg}; use crate::validate::validate_types; use crate::{PassPolicy, check_inline, util}; @@ -48,13 +49,21 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy { match ctx.opts.unstable_opts.inline_mir { Some(enabled) => PassPolicy::optional(enabled), - None => PassPolicy::optional(match (ctx.mir_opt_level(), ctx.opts.optimize) { - (0 | 1, _) => false, - // Inlining reduces incremental effectiveness. - (2, OptLevel::More | OptLevel::Aggressive) => ctx.opts.incremental.is_none(), - // Don't inline if the global `-Copt-level` is 1/s/z - (2, _) => false, - _ => true, + None => PassPolicy::optional({ + let source = ctx.mir_opt_level_source(); + match source.level() { + 0 | 1 => false, + 3.. => true, + // If level 2 has been inferred from opt-level=1/s/z, we don't want to enable inlining. + // However, if `optimize(speed)` has been set, we want to inline irrespective of global opt level. + 2 if matches!(ctx.opts.optimize, OptLevel::More | OptLevel::Aggressive) + || matches!(source, BodyMirOptLevel::Overridden(_)) => + { + // Inlining reduces incremental effectiveness. + ctx.opts.incremental.is_none() + } + _ => false, + } }), } } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index ddf16d3fff4c4..f82d666ed40b8 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -133,26 +133,62 @@ pub(super) struct PassCtx<'sess> { /// Prefer [`Self::mir_opt_level`] to [`Session::mir_opt_level`] to account for overrides. session: &'sess Session, /// The MIR optimization level for this body; may be overridden by `#[optimize]`. - body_mir_opt_level: usize, + // FIXME: This information is currently only inspected by inlining and may be unnecessary. + body_mir_opt_level: BodyMirOptLevel, +} + +/// The optimization level for a body and the source for this decision. +#[derive(Copy, Clone, Debug)] +pub(super) enum BodyMirOptLevel { + /// Inferred from the global `-C opt-level`. + GlobalOptLevel(OptLevel), + /// Specified by the global `-Z mir-opt-level`. + GlobalMirOptLevel(usize), + /// Overridden by an `#[optimize]` attribute. + Overridden(OptLevel), +} + +impl BodyMirOptLevel { + pub(super) fn level(&self) -> usize { + match self { + BodyMirOptLevel::GlobalOptLevel(level) | BodyMirOptLevel::Overridden(level) => { + level.mir_opt_level() + } + BodyMirOptLevel::GlobalMirOptLevel(level) => *level, + } + } } impl<'sess> PassCtx<'sess> { pub(super) fn for_body(tcx: TyCtxt<'sess>, def_id: DefId) -> Self { - let body_mir_opt_level = if !tcx.def_kind(def_id).has_codegen_attrs() { - tcx.sess.mir_opt_level() + let body_mir_opt_level = if tcx.def_kind(def_id).has_codegen_attrs() + && let opt_attr = tcx.codegen_fn_attrs(def_id).optimize + && opt_attr != OptimizeAttr::Default + { + BodyMirOptLevel::Overridden(match opt_attr { + OptimizeAttr::DoNotOptimize => OptLevel::No, + OptimizeAttr::Speed => OptLevel::Aggressive, + OptimizeAttr::Size => OptLevel::Size, + OptimizeAttr::Default => unreachable!(), + }) } else { - match tcx.codegen_fn_attrs(def_id).optimize { - OptimizeAttr::Default => tcx.sess.mir_opt_level(), - OptimizeAttr::DoNotOptimize => OptLevel::No.mir_opt_level(), - OptimizeAttr::Speed => OptLevel::Aggressive.mir_opt_level(), - OptimizeAttr::Size => OptLevel::Size.mir_opt_level(), + #[allow(rustc::bad_opt_access, reason = "we want to know the source of the opt-level")] + if let Some(level) = tcx.sess.opts.unstable_opts.mir_opt_level { + BodyMirOptLevel::GlobalMirOptLevel(level) + } else { + BodyMirOptLevel::GlobalOptLevel(tcx.sess.opts.optimize) } }; + Self { session: tcx.sess, body_mir_opt_level } } /// The effective MIR optimization level for this body, including `#[optimize]` overrides. pub(super) fn mir_opt_level(&self) -> usize { + self.body_mir_opt_level.level() + } + + pub(super) fn mir_opt_level_source(&self) -> BodyMirOptLevel { self.body_mir_opt_level } }