diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index def6b16ee8ae3..b4027a096c532 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -35,13 +35,25 @@ pub enum InstructionSetAttr {
     ArmT32,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)]
 pub enum OptimizeAttr {
-    None,
+    /// No `#[optimize(..)]` attribute
+    #[default]
+    Default,
+    /// `#[optimize(none)]`
+    DoNotOptimize,
+    /// `#[optimize(speed)]`
     Speed,
+    /// `#[optimize(size)]`
     Size,
 }
 
+impl OptimizeAttr {
+    pub fn do_not_optimize(&self) -> bool {
+        matches!(self, Self::DoNotOptimize)
+    }
+}
+
 #[derive(Clone, Debug, Encodable, Decodable)]
 pub enum DiagnosticAttribute {
     // tidy-alphabetical-start
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 95e0481b035e2..3d7afa17bdf3d 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -333,9 +333,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
     let mut to_add = SmallVec::<[_; 16]>::new();
 
     match codegen_fn_attrs.optimize {
-        OptimizeAttr::None => {
+        OptimizeAttr::Default => {
             to_add.extend(default_optimisation_attrs(cx));
         }
+        OptimizeAttr::DoNotOptimize => {
+            to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx));
+        }
         OptimizeAttr::Size => {
             to_add.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx));
             to_add.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx));
@@ -343,12 +346,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
         OptimizeAttr::Speed => {}
     }
 
-    let inline =
-        if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) {
-            InlineAttr::Hint
-        } else {
-            codegen_fn_attrs.inline
-        };
+    // `optnone` requires `noinline`
+    let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) {
+        (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never,
+        (InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint,
+        (inline, _) => inline,
+    };
     to_add.extend(inline_attr(cx, inline));
 
     // The `uwtable` attribute according to LLVM is:
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 83724af604d14..6099dec2bb2ac 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -1053,10 +1053,7 @@ pub(crate) fn provide(providers: &mut Providers) {
 
         let any_for_speed = defids.items().any(|id| {
             let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id);
-            match optimize {
-                attr::OptimizeAttr::None | attr::OptimizeAttr::Size => false,
-                attr::OptimizeAttr::Speed => true,
-            }
+            matches!(optimize, attr::OptimizeAttr::Speed)
         });
 
         if any_for_speed {
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 1daa17fbaf347..a0bc2d4ea48f1 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -575,7 +575,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         codegen_fn_attrs.inline = InlineAttr::Never;
     }
 
-    codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| {
+    codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
         if !attr.has_name(sym::optimize) {
             return ia;
         }
@@ -587,17 +587,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             inline_span = Some(attr.span);
             if items.len() != 1 {
                 err(attr.span, "expected one argument");
-                OptimizeAttr::None
+                OptimizeAttr::Default
             } else if list_contains_name(items, sym::size) {
                 OptimizeAttr::Size
             } else if list_contains_name(items, sym::speed) {
                 OptimizeAttr::Speed
+            } else if list_contains_name(items, sym::none) {
+                OptimizeAttr::DoNotOptimize
             } else {
                 err(items[0].span(), "invalid argument");
-                OptimizeAttr::None
+                OptimizeAttr::Default
             }
         } else {
-            OptimizeAttr::None
+            OptimizeAttr::Default
         }
     });
 
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 68e0191f45e7b..966f7730918de 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -532,7 +532,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     // RFC 2412
     gated!(
-        optimize, Normal, template!(List: "size|speed"), ErrorPreceding,
+        optimize, Normal, template!(List: "none|size|speed"), ErrorPreceding,
         EncodeCrossCrate::No, optimize_attribute, experimental!(optimize)
     ),
 
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index e05f42af6fd24..cc980f6e62aee 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -147,7 +147,7 @@ impl CodegenFnAttrs {
         CodegenFnAttrs {
             flags: CodegenFnAttrFlags::empty(),
             inline: InlineAttr::None,
-            optimize: OptimizeAttr::None,
+            optimize: OptimizeAttr::Default,
             export_name: None,
             link_name: None,
             link_ordinal: None,
diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
index 03f11885d58ae..5bd6fdcf48577 100644
--- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
+++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs
@@ -116,4 +116,8 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls {
         // We may have invalidated some `cleanup` blocks so clean those up now.
         super::simplify::remove_dead_blocks(body);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs
index 24c955c0c78c6..55694cacd92e0 100644
--- a/compiler/rustc_mir_transform/src/add_call_guards.rs
+++ b/compiler/rustc_mir_transform/src/add_call_guards.rs
@@ -75,4 +75,8 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards {
 
         body.basic_blocks_mut().extend(new_blocks);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
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 12a2fe23b149e..87ae2b7165434 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
@@ -68,6 +68,10 @@ impl<'tcx> crate::MirPass<'tcx> for AddMovesForPackedDrops {
 
         patch.apply(body);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 fn add_move_for_packed_drop<'tcx>(
diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs
index 53176eec9bcb9..1fc788a2dadeb 100644
--- a/compiler/rustc_mir_transform/src/add_retag.rs
+++ b/compiler/rustc_mir_transform/src/add_retag.rs
@@ -176,4 +176,8 @@ impl<'tcx> crate::MirPass<'tcx> for AddRetag {
             }
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs
index e585e338613d8..e41f47db545b8 100644
--- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs
+++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs
@@ -61,4 +61,8 @@ impl<'tcx> crate::MirPass<'tcx> for Subtyper {
         }
         checker.patcher.apply(body);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs
index 1b7c89fd25183..d7e22c123942c 100644
--- a/compiler/rustc_mir_transform/src/check_alignment.rs
+++ b/compiler/rustc_mir_transform/src/check_alignment.rs
@@ -60,6 +60,10 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
             }
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 struct PointerFinder<'a, 'tcx> {
diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
index 6a22a58470c95..cb84401985735 100644
--- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
+++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs
@@ -72,4 +72,8 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck {
             decl.user_ty = None;
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs
index f149bf97cde65..27af5818982d0 100644
--- a/compiler/rustc_mir_transform/src/copy_prop.rs
+++ b/compiler/rustc_mir_transform/src/copy_prop.rs
@@ -56,6 +56,10 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
             crate::simplify::remove_unused_definitions(body);
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 /// `SsaLocals` computed equivalence classes between locals considering copy/move assignments.
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index a3715b5d48556..3b75be58e437f 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -1688,6 +1688,10 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
         // Run derefer to fix Derefs that are not in the first place
         deref_finder(tcx, body);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 /// Looks for any assignments between locals (e.g., `_4 = _5`) that will both be converted to fields
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 19568735df76e..d4523782f7d37 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -61,6 +61,10 @@ impl<'tcx> crate::MirPass<'tcx> for InstrumentCoverage {
 
         instrument_function_for_coverage(tcx, mir_body);
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 fn instrument_function_for_coverage<'tcx>(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 8fce856687cb8..4f45d9588a890 100644
--- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs
+++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
@@ -69,7 +69,7 @@ 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)
+    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());
     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 bd58b1b668997..d0b313e149aca 100644
--- a/compiler/rustc_mir_transform/src/ctfe_limit.rs
+++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs
@@ -36,6 +36,10 @@ impl<'tcx> crate::MirPass<'tcx> for CtfeLimit {
             );
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 fn has_back_edge(
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 51af77778af82..8879e029346da 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -71,6 +71,10 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp {
         let mut patch = visitor.patch;
         debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body));
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 // Note: Currently, places that have their reference taken cannot be tracked. Although this would
diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
index 434e921d43929..eea2b0990d730 100644
--- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs
+++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs
@@ -147,4 +147,8 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         eliminate(tcx, body);
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
index 26b28c8c4870e..63257df66fb9b 100644
--- a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
+++ b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
@@ -31,6 +31,10 @@ impl<'tcx> crate::MirPass<'tcx> for DeduplicateBlocks {
             simplify_cfg(body);
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 struct OptApplier<'tcx> {
diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs
index ad7ccef4976aa..a54bdaa407593 100644
--- a/compiler/rustc_mir_transform/src/deref_separator.rs
+++ b/compiler/rustc_mir_transform/src/deref_separator.rs
@@ -81,4 +81,8 @@ impl<'tcx> crate::MirPass<'tcx> for Derefer {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         deref_finder(tcx, body);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index b4f9f1f08ef82..41de1b58b91bb 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -240,6 +240,10 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
 
         trace!(round_count);
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 #[derive(Debug, Default)]
diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs
index 5dd84975b88ed..e4fcbaa483d04 100644
--- a/compiler/rustc_mir_transform/src/dump_mir.rs
+++ b/compiler/rustc_mir_transform/src/dump_mir.rs
@@ -15,6 +15,10 @@ impl<'tcx> crate::MirPass<'tcx> for Marker {
     }
 
     fn run_pass(&self, _tcx: TyCtxt<'tcx>, _body: &mut Body<'tcx>) {}
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> {
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index 91e1395e76415..eb335a1e4a707 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -227,6 +227,10 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
             simplify_cfg(body);
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 #[derive(Debug)]
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index d6ecadbfe29c1..6d3b5d9851b33 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -150,4 +150,8 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
             }
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index 988f1a25561e9..cb869160c8054 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -88,6 +88,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops {
         elaborate_patch.apply(body);
         deref_finder(tcx, body);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 /// Records unwind edges which are known to be unreachable, because they are in `drop` terminators
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index cb03b422d9e38..dfeb83a0887d3 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -163,6 +163,10 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
         // statements.
         StorageRemover { tcx, reused_locals: state.reused_locals }.visit_body_preserves_cfg(body);
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 newtype_index! {
diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs
index ba8389bbe2ffb..86e2bf6cb3c60 100644
--- a/compiler/rustc_mir_transform/src/impossible_predicates.rs
+++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs
@@ -53,4 +53,8 @@ impl<'tcx> MirPass<'tcx> for ImpossiblePredicates {
             body.local_decls.raw.truncate(body.arg_count + 1);
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 2052e28325c90..95aeccfdda66b 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -66,6 +66,10 @@ impl<'tcx> crate::MirPass<'tcx> for Inline {
             deref_finder(tcx, body);
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 pub struct ForceInline;
@@ -85,6 +89,10 @@ impl<'tcx> crate::MirPass<'tcx> for ForceInline {
         false
     }
 
+    fn is_required(&self) -> bool {
+        true
+    }
+
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         let span = trace_span!("force_inline", body = %tcx.def_path_str(body.source.def_id()));
         let _guard = span.enter();
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index 5a36519e6a3bb..090dcee426141 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -62,6 +62,10 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
             simplify_duplicate_switch_targets(block.terminator.as_mut().unwrap());
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 struct InstSimplifyContext<'a, 'tcx> {
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index c73a03489c57f..17084eca6e388 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -105,6 +105,10 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
         }
         OpportunitySet::new(body, opportunities).apply(body);
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 #[derive(Debug)]
diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs
index 490e7dd8f7e07..e201763468b31 100644
--- a/compiler/rustc_mir_transform/src/large_enums.rs
+++ b/compiler/rustc_mir_transform/src/large_enums.rs
@@ -200,6 +200,10 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt {
             });
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 impl EnumSizeOpt {
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index d1bacf1f5985a..2dc55e3614e6c 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -388,6 +388,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
             &Lint(sanity_check::SanityCheck),
         ],
         None,
+        pm::Optimizations::Allowed,
     );
     tcx.alloc_steal_mir(body)
 }
@@ -440,6 +441,7 @@ fn mir_promoted(
         &mut body,
         &[&promote_pass, &simplify::SimplifyCfg::PromoteConsts, &coverage::InstrumentCoverage],
         Some(MirPhase::Analysis(AnalysisPhase::Initial)),
+        pm::Optimizations::Allowed,
     );
 
     lint_tail_expr_drop_order::run_lint(tcx, def, &body);
@@ -473,7 +475,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
     };
 
     let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const);
-    pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None);
+    pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None, pm::Optimizations::Allowed);
 
     body
 }
@@ -493,7 +495,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)
+        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_with_value().mir_inliner_callees(ty::InstanceKind::Item(def.to_def_id()));
@@ -533,6 +535,7 @@ pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'
                 &Lint(post_drop_elaboration::CheckLiveDrops),
             ],
             None,
+            pm::Optimizations::Allowed,
         );
     }
 
@@ -557,7 +560,13 @@ fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         &deref_separator::Derefer,
     ];
 
-    pm::run_passes(tcx, body, passes, Some(MirPhase::Analysis(AnalysisPhase::PostCleanup)));
+    pm::run_passes(
+        tcx,
+        body,
+        passes,
+        Some(MirPhase::Analysis(AnalysisPhase::PostCleanup)),
+        pm::Optimizations::Allowed,
+    );
 }
 
 /// Returns the sequence of passes that lowers analysis to runtime MIR.
@@ -597,7 +606,13 @@ fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         &simplify::SimplifyCfg::PreOptimizations,
     ];
 
-    pm::run_passes(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::PostCleanup)));
+    pm::run_passes(
+        tcx,
+        body,
+        passes,
+        Some(MirPhase::Runtime(RuntimePhase::PostCleanup)),
+        pm::Optimizations::Allowed,
+    );
 
     // Clear this by anticipation. Optimizations and runtime MIR have no reason to look
     // into this information, which is meant for borrowck diagnostics.
@@ -611,6 +626,15 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         WithMinOptLevel(1, x)
     }
 
+    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()
+    {
+        pm::Optimizations::Suppressed
+    } else {
+        pm::Optimizations::Allowed
+    };
+
     // The main optimizations that we do on MIR.
     pm::run_passes(
         tcx,
@@ -683,6 +707,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
             &dump_mir::Marker("PreCodegen"),
         ],
         Some(MirPhase::Runtime(RuntimePhase::Optimized)),
+        optimizations,
     );
 }
 
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index 942c6144ea69c..9a9f66ed4fd7a 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -325,4 +325,8 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
             }
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs
index 420661f29c8cf..aca80e36e339d 100644
--- a/compiler/rustc_mir_transform/src/lower_slice_len.rs
+++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs
@@ -26,6 +26,10 @@ impl<'tcx> crate::MirPass<'tcx> for LowerSliceLenCalls {
             lower_slice_len_call(block, slice_len_fn_item_def_id);
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 fn lower_slice_len_call<'tcx>(block: &mut BasicBlockData<'tcx>, slice_len_fn_item_def_id: DefId) {
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 534ba99178017..500116580d5d7 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -49,6 +49,10 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification {
             simplify_cfg(body);
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 trait SimplifyMatch<'tcx> {
diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs
index cf5c5f85a9ff6..f5c57418467a1 100644
--- a/compiler/rustc_mir_transform/src/mentioned_items.rs
+++ b/compiler/rustc_mir_transform/src/mentioned_items.rs
@@ -27,6 +27,10 @@ impl<'tcx> crate::MirPass<'tcx> for MentionedItems {
         visitor.visit_body(body);
         body.set_mentioned_items(visitor.mentioned_items);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 // This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are
diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
index 6dfa14d6b527d..c63bfdcee8559 100644
--- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
+++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs
@@ -36,4 +36,8 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators {
 
         simplify::remove_dead_blocks(body)
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs
index 35872de385245..965002aae04c7 100644
--- a/compiler/rustc_mir_transform/src/nrvo.rs
+++ b/compiler/rustc_mir_transform/src/nrvo.rs
@@ -71,6 +71,10 @@ impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace {
         // The return place is always mutable.
         ret_decl.mutability = Mutability::Mut;
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 /// MIR that is eligible for the NRVO must fulfill two conditions:
diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs
index c3f0a989ce105..7a8d3ba1ff1fa 100644
--- a/compiler/rustc_mir_transform/src/pass_manager.rs
+++ b/compiler/rustc_mir_transform/src/pass_manager.rs
@@ -90,6 +90,11 @@ pub(super) trait MirPass<'tcx> {
     fn is_mir_dump_enabled(&self) -> bool {
         true
     }
+
+    /// Returns `true` if this pass must be run (i.e. it is required for soundness).
+    /// For passes which are strictly optimizations, this should return `false`.
+    /// If this is `false`, `#[optimize(none)]` will disable the pass.
+    fn is_required(&self) -> bool;
 }
 
 /// Just like `MirPass`, except it cannot mutate `Body`, and MIR dumping is
@@ -134,6 +139,10 @@ where
     fn is_mir_dump_enabled(&self) -> bool {
         false
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 pub(super) struct WithMinOptLevel<T>(pub u32, pub T);
@@ -153,6 +162,19 @@ where
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         self.1.run_pass(tcx, body)
     }
+
+    fn is_required(&self) -> bool {
+        self.1.is_required()
+    }
+}
+
+/// Whether to allow non-[required] optimizations
+///
+/// [required]: MirPass::is_required
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(crate) enum Optimizations {
+    Suppressed,
+    Allowed,
 }
 
 /// Run the sequence of passes without validating the MIR after each pass. The MIR is still
@@ -163,7 +185,7 @@ pub(super) fn run_passes_no_validate<'tcx>(
     passes: &[&dyn MirPass<'tcx>],
     phase_change: Option<MirPhase>,
 ) {
-    run_passes_inner(tcx, body, passes, phase_change, false);
+    run_passes_inner(tcx, body, passes, phase_change, false, Optimizations::Allowed);
 }
 
 /// The optional `phase_change` is applied after executing all the passes, if present
@@ -172,11 +194,16 @@ pub(super) fn run_passes<'tcx>(
     body: &mut Body<'tcx>,
     passes: &[&dyn MirPass<'tcx>],
     phase_change: Option<MirPhase>,
+    optimizations: Optimizations,
 ) {
-    run_passes_inner(tcx, body, passes, phase_change, true);
+    run_passes_inner(tcx, body, passes, phase_change, true, optimizations);
 }
 
-pub(super) fn should_run_pass<'tcx, P>(tcx: TyCtxt<'tcx>, pass: &P) -> bool
+pub(super) fn should_run_pass<'tcx, P>(
+    tcx: TyCtxt<'tcx>,
+    pass: &P,
+    optimizations: Optimizations,
+) -> bool
 where
     P: MirPass<'tcx> + ?Sized,
 {
@@ -196,7 +223,8 @@ where
             );
             *polarity
         });
-    overridden.unwrap_or_else(|| pass.is_enabled(tcx.sess))
+    let suppressed = !pass.is_required() && matches!(optimizations, Optimizations::Suppressed);
+    overridden.unwrap_or_else(|| !suppressed && pass.is_enabled(tcx.sess))
 }
 
 fn run_passes_inner<'tcx>(
@@ -205,6 +233,7 @@ fn run_passes_inner<'tcx>(
     passes: &[&dyn MirPass<'tcx>],
     phase_change: Option<MirPhase>,
     validate_each: bool,
+    optimizations: Optimizations,
 ) {
     let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes;
     trace!(?overridden_passes);
@@ -243,7 +272,7 @@ fn run_passes_inner<'tcx>(
         for pass in passes {
             let name = pass.name();
 
-            if !should_run_pass(tcx, *pass) {
+            if !should_run_pass(tcx, *pass, optimizations) {
                 continue;
             };
 
diff --git a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs
index 3eecf79a7eae7..76c2f082c0bfc 100644
--- a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs
+++ b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs
@@ -15,6 +15,10 @@ impl<'tcx> crate::MirPass<'tcx> for PostAnalysisNormalize {
         let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id());
         PostAnalysisNormalizeVisitor { tcx, typing_env }.visit_body_preserves_cfg(body);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 struct PostAnalysisNormalizeVisitor<'tcx> {
diff --git a/compiler/rustc_mir_transform/src/prettify.rs b/compiler/rustc_mir_transform/src/prettify.rs
index 51abd4da86eb1..8ccfbe2f194b4 100644
--- a/compiler/rustc_mir_transform/src/prettify.rs
+++ b/compiler/rustc_mir_transform/src/prettify.rs
@@ -35,6 +35,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReorderBasicBlocks {
 
         permute(body.basic_blocks.as_mut(), &updater.map);
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 /// Rearranges the locals into *use* order.
@@ -85,6 +89,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReorderLocals {
 
         permute(&mut body.local_decls, &updater.map);
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 fn permute<I: rustc_index::Idx + Ord, T>(data: &mut IndexVec<I, T>, map: &IndexSlice<I, I>) {
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 6be95b1f0f1e6..6aa3343bb6e3b 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -61,6 +61,10 @@ impl<'tcx> crate::MirPass<'tcx> for PromoteTemps<'tcx> {
         let promoted = promote_candidates(body, tcx, temps, promotable_candidates);
         self.promoted_fragments.set(promoted);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 /// State of a temporary during collection and promotion.
diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs
index 95b05f94270a8..368d5340ac34f 100644
--- a/compiler/rustc_mir_transform/src/ref_prop.rs
+++ b/compiler/rustc_mir_transform/src/ref_prop.rs
@@ -81,6 +81,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReferencePropagation {
         debug!(def_id = ?body.source.def_id());
         while propagate_ssa(tcx, body) {}
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
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 76a3edfe0be4e..9cdd52bec7bb9 100644
--- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
+++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
@@ -74,6 +74,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads {
 
         debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed);
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 impl RemoveNoopLandingPads {
diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs
index 71399eb72f004..15fe77d53195a 100644
--- a/compiler/rustc_mir_transform/src/remove_place_mention.rs
+++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs
@@ -20,4 +20,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention {
             })
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs
index 3ecb4a8994f5f..1ae33c0096875 100644
--- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs
+++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs
@@ -22,4 +22,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers {
             })
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
index e955d8277a458..9044a88295cc4 100644
--- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
@@ -62,6 +62,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops {
             block.terminator_mut().kind = TerminatorKind::Goto { target: *target };
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 fn is_needs_drop_and_init<'tcx>(
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
index e335051d65644..8a8cdafc69070 100644
--- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
@@ -38,4 +38,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops {
             simplify_cfg(body);
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs
index 55e5701bd0af3..78d94a038671d 100644
--- a/compiler/rustc_mir_transform/src/remove_zsts.rs
+++ b/compiler/rustc_mir_transform/src/remove_zsts.rs
@@ -27,6 +27,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveZsts {
             replacer.visit_basic_block_data(bb, data);
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 struct Replacer<'a, 'tcx> {
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 4648ec33c9345..f7ec0f740d300 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -119,6 +119,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<
                         &add_call_guards::CriticalCallEdges,
                     ],
                     Some(MirPhase::Runtime(RuntimePhase::Optimized)),
+                    pm::Optimizations::Allowed,
                 );
 
                 return body;
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index 4f312ed2aaabc..67070f03dedbd 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -83,6 +83,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
         debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source);
         simplify_cfg(body);
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 struct CfgSimplifier<'a, 'tcx> {
@@ -405,6 +409,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyLocals {
             body.local_decls.shrink_to_fit();
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 pub(super) fn remove_unused_definitions<'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 bea3d0d8557a7..12c3503879fb9 100644
--- a/compiler/rustc_mir_transform/src/simplify_branches.rs
+++ b/compiler/rustc_mir_transform/src/simplify_branches.rs
@@ -60,4 +60,8 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition {
             };
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
index b6d8017308662..c0f25c7ecfebc 100644
--- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
+++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs
@@ -140,6 +140,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral {
             body.basic_blocks_mut()[idx].statements.insert(0, stmt);
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 struct OptimizationFinder<'a, 'tcx> {
diff --git a/compiler/rustc_mir_transform/src/single_use_consts.rs b/compiler/rustc_mir_transform/src/single_use_consts.rs
index 10b3c0ae94f24..c5e951eb8b2c2 100644
--- a/compiler/rustc_mir_transform/src/single_use_consts.rs
+++ b/compiler/rustc_mir_transform/src/single_use_consts.rs
@@ -81,6 +81,10 @@ impl<'tcx> crate::MirPass<'tcx> for SingleUseConsts {
             }
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 #[derive(Copy, Clone, Debug)]
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
index d54ea3feab6ee..92e5c8a9ca428 100644
--- a/compiler/rustc_mir_transform/src/sroa.rs
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -48,6 +48,10 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates {
             }
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 /// Identify all locals that are not eligible for SROA.
diff --git a/compiler/rustc_mir_transform/src/strip_debuginfo.rs b/compiler/rustc_mir_transform/src/strip_debuginfo.rs
index 438c75726bb90..9ede8aa79c44a 100644
--- a/compiler/rustc_mir_transform/src/strip_debuginfo.rs
+++ b/compiler/rustc_mir_transform/src/strip_debuginfo.rs
@@ -31,4 +31,8 @@ impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo {
             )
         });
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs
index 55dcad0680a96..1ff7043ed14d9 100644
--- a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs
+++ b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs
@@ -208,4 +208,8 @@ impl<'tcx> crate::MirPass<'tcx> for UnreachableEnumBranching {
 
         patch.apply(body);
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs
index 734703ec78b55..1e5d9469c033e 100644
--- a/compiler/rustc_mir_transform/src/unreachable_prop.rs
+++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs
@@ -52,6 +52,10 @@ impl crate::MirPass<'_> for UnreachablePropagation {
             body.basic_blocks_mut()[bb].statements.clear();
         }
     }
+
+    fn is_required(&self) -> bool {
+        false
+    }
 }
 
 /// Return whether the current terminator is fully unreachable.
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index b62e34ac08d09..026923ad786bb 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -91,6 +91,10 @@ impl<'tcx> crate::MirPass<'tcx> for Validator {
             }
         }
     }
+
+    fn is_required(&self) -> bool {
+        true
+    }
 }
 
 struct CfgChecker<'a, 'tcx> {
diff --git a/tests/codegen/optimize-attr-1.rs b/tests/codegen/optimize-attr-1.rs
index 3aee44791e0e6..db6bdcf9a8b90 100644
--- a/tests/codegen/optimize-attr-1.rs
+++ b/tests/codegen/optimize-attr-1.rs
@@ -37,11 +37,23 @@ pub fn speed() -> i32 {
     4 + 4
 }
 
+// CHECK-LABEL: define{{.*}}i32 @none
+// CHECK-SAME: [[NONE_ATTRS:#[0-9]+]]
+// SIZE-OPT: alloca
+// SPEED-OPT: alloca
+#[no_mangle]
+#[optimize(none)]
+pub fn none() -> i32 {
+    let arr = [0, 1, 2, 3, 4];
+    arr[4]
+}
+
 // NO-OPT-DAG: attributes [[SIZE_ATTRS]] = {{.*}}minsize{{.*}}optsize{{.*}}
 // SPEED-OPT-DAG: attributes [[SIZE_ATTRS]] = {{.*}}minsize{{.*}}optsize{{.*}}
 // SIZE-OPT-DAG: attributes [[NOTHING_ATTRS]] = {{.*}}optsize{{.*}}
 // SIZE-OPT-DAG: attributes [[SIZE_ATTRS]] = {{.*}}minsize{{.*}}optsize{{.*}}
+// CHECK-DAG: attributes [[NONE_ATTRS]] = {{.*}}noinline{{.*}}optnone{{.*}}
 
-// SIZE-OPT: attributes [[SPEED_ATTRS]]
+// SIZE-OPT-DAG: attributes [[SPEED_ATTRS]]
 // SIZE-OPT-NOT: minsize
 // SIZE-OPT-NOT: optsize
diff --git a/tests/mir-opt/optimize_none.rs b/tests/mir-opt/optimize_none.rs
new file mode 100644
index 0000000000000..a5b541bd2b628
--- /dev/null
+++ b/tests/mir-opt/optimize_none.rs
@@ -0,0 +1,32 @@
+//@ revisions: NO-OPT SPEED-OPT
+//@[NO-OPT] compile-flags: -Copt-level=0
+//@[SPEED-OPT] compile-flags: -Copt-level=3 -Coverflow-checks=y
+
+#![feature(optimize_attribute)]
+
+#[optimize(none)]
+pub fn add_noopt() -> i32 {
+    // CHECK-LABEL: fn add_noopt(
+    // CHECK: AddWithOverflow(const 1_i32, const 2_i32);
+    // CHECK-NEXT: assert
+    1 + 2
+}
+
+#[optimize(none)]
+pub fn const_branch() -> i32 {
+    // CHECK-LABEL: fn const_branch(
+    // CHECK: switchInt(const true) -> [0: [[FALSE:bb[0-9]+]], otherwise: [[TRUE:bb[0-9]+]]];
+    // CHECK-NEXT: }
+    // CHECK: [[FALSE]]: {
+    // CHECK-NEXT: _0 = const 0
+    // CHECK-NEXT: goto
+    // CHECK-NEXT: }
+    // CHECK: [[TRUE]]: {
+    // CHECK-NEXT: _0 = const 1
+    // CHECK-NEXT: goto
+    // CHECK-NEXT: }
+
+    if true { 1 } else { 0 }
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
index 7f9cada6a47d5..77cc307c9f453 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
@@ -6,6 +6,9 @@ fn size() {}
 #[optimize(speed)] //~ ERROR the `#[optimize]` attribute is an experimental feature
 fn speed() {}
 
+#[optimize(none)] //~ ERROR the `#[optimize]` attribute is an experimental feature
+fn none() {}
+
 #[optimize(banana)]
 //~^ ERROR the `#[optimize]` attribute is an experimental feature
 //~| ERROR E0722
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
index ca8f4a078f09e..4e6e4ac2703a8 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
@@ -21,6 +21,16 @@ LL | #[optimize(speed)]
 error[E0658]: the `#[optimize]` attribute is an experimental feature
   --> $DIR/feature-gate-optimize_attribute.rs:9:1
    |
+LL | #[optimize(none)]
+   | ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #54882 <https://github.com/rust-lang/rust/issues/54882> for more information
+   = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the `#[optimize]` attribute is an experimental feature
+  --> $DIR/feature-gate-optimize_attribute.rs:12:1
+   |
 LL | #[optimize(banana)]
    | ^^^^^^^^^^^^^^^^^^^
    |
@@ -29,12 +39,12 @@ LL | #[optimize(banana)]
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0722]: invalid argument
-  --> $DIR/feature-gate-optimize_attribute.rs:9:12
+  --> $DIR/feature-gate-optimize_attribute.rs:12:12
    |
 LL | #[optimize(banana)]
    |            ^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
 Some errors have detailed explanations: E0658, E0722.
 For more information about an error, try `rustc --explain E0658`.