From 2a9cc8f4d6aee54ecf1882bfaa690165b52d66f2 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 11 Nov 2024 15:32:25 +0000 Subject: [PATCH 1/4] Declare all MIR passes in a list --- compiler/rustc_mir_transform/src/lib.rs | 204 ++++++++++++------ .../rustc_mir_transform/src/pass_manager.rs | 12 +- 2 files changed, 154 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index e18f66ac0a3bb..66ac3d30ca403 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -40,77 +40,159 @@ use tracing::{debug, trace}; #[macro_use] mod pass_manager; +use std::sync::LazyLock; + use pass_manager::{self as pm, Lint, MirLint, MirPass, WithMinOptLevel}; -mod abort_unwinding_calls; -mod add_call_guards; -mod add_moves_for_packed_drops; -mod add_retag; -mod add_subtyping_projections; -mod check_alignment; -mod check_const_item_mutation; -mod check_packed_ref; -mod check_undefined_transmutes; -// This pass is public to allow external drivers to perform MIR cleanup -pub mod cleanup_post_borrowck; -mod copy_prop; -mod coroutine; mod cost_checker; -mod coverage; mod cross_crate_inline; -mod ctfe_limit; -mod dataflow_const_prop; -mod dead_store_elimination; mod deduce_param_attrs; -mod deduplicate_blocks; -mod deref_separator; -mod dest_prop; -pub mod dump_mir; -mod early_otherwise_branch; -mod elaborate_box_derefs; -mod elaborate_drops; mod errors; mod ffi_unwind_calls; -mod function_item_references; -mod gvn; -// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden -// by custom rustc drivers, running all the steps by themselves. See #114628. -pub mod inline; -mod instsimplify; -mod jump_threading; -mod known_panics_lint; -mod large_enums; mod lint; -mod lower_intrinsics; -mod lower_slice_len; -mod match_branches; -mod mentioned_items; -mod multiple_return_terminators; -mod nrvo; -mod post_drop_elaboration; -mod prettify; -mod promote_consts; -mod ref_prop; -mod remove_noop_landing_pads; -mod remove_place_mention; -mod remove_storage_markers; -mod remove_uninit_drops; -mod remove_unneeded_drops; -mod remove_zsts; -mod required_consts; -mod reveal_all; -mod sanity_check; mod shim; mod ssa; -// This pass is public to allow external drivers to perform MIR cleanup -pub mod simplify; -mod simplify_branches; -mod simplify_comparison_integral; -mod single_use_consts; -mod sroa; -mod unreachable_enum_branching; -mod unreachable_prop; -mod validate; + +/// We import passes via this macro so that we can have a static list of pass names +/// (used to verify CLI arguments). It takes a list of modules, followed by the passes +/// declared within them. +/// ```ignore,macro-test +/// declare_passes! { +/// // Declare a single pass from the module `abort_unwinding_calls` +/// mod abort_unwinding_calls : AbortUnwindingCalls; +/// // When passes are grouped together as an enum, declare the two constituent passes +/// mod add_call_guards : AddCallGuards { +/// AllCallEdges, +/// CriticalCallEdges +/// }; +/// // Declares multiple pass groups, each containing their own constituent passes +/// mod simplify : SimplifyCfg { +/// Initial, +/// /* omitted */ +/// }, SimplifyLocals { +/// BeforeConstProp, +/// /* omitted */ +/// }; +/// } +/// ``` +macro_rules! declare_passes { + ( + $( + $vis:vis mod $mod_name:ident : $($pass_name:ident $( { $($ident:ident),* } )?),+ $(,)?; + )* + ) => { + $( + $vis mod $mod_name; + $( + // Make sure the type name is correct + #[allow(unused_imports)] + use $mod_name::$pass_name as _; + )+ + )* + + #[cfg(debug_assertions)] + static PASS_NAMES: LazyLock> = LazyLock::new(|| vec![ + // Fake marker pass + "PreCodegen".to_string(), + $( + $( + stringify!($pass_name).to_string(), + $( + $( + $mod_name::$pass_name::$ident.name().to_string(), + )* + )? + )+ + )* + ]); + }; +} + +declare_passes! { + mod abort_unwinding_calls : AbortUnwindingCalls; + mod add_call_guards : AddCallGuards { AllCallEdges, CriticalCallEdges }; + mod add_moves_for_packed_drops : AddMovesForPackedDrops; + mod add_retag : AddRetag; + mod add_subtyping_projections : Subtyper; + mod check_alignment : CheckAlignment; + mod check_const_item_mutation : CheckConstItemMutation; + mod check_packed_ref : CheckPackedRef; + mod check_undefined_transmutes : CheckUndefinedTransmutes; + // This pass is public to allow external drivers to perform MIR cleanup + pub mod cleanup_post_borrowck : CleanupPostBorrowck; + + mod copy_prop : CopyProp; + mod coroutine : StateTransform; + mod coverage : InstrumentCoverage; + mod ctfe_limit : CtfeLimit; + mod dataflow_const_prop : DataflowConstProp; + mod dead_store_elimination : DeadStoreElimination { + Initial, + Final + }; + mod deduplicate_blocks : DeduplicateBlocks; + mod deref_separator : Derefer; + mod dest_prop : DestinationPropagation; + pub mod dump_mir : Marker; + mod early_otherwise_branch : EarlyOtherwiseBranch; + mod elaborate_box_derefs : ElaborateBoxDerefs; + mod elaborate_drops : ElaborateDrops; + mod function_item_references : FunctionItemReferences; + mod gvn : GVN; + // Made public so that `mir_drops_elaborated_and_const_checked` can be overridden + // by custom rustc drivers, running all the steps by themselves. See #114628. + pub mod inline : Inline; + mod instsimplify : InstSimplify { BeforeInline, AfterSimplifyCfg }; + mod jump_threading : JumpThreading; + mod known_panics_lint : KnownPanicsLint; + mod large_enums : EnumSizeOpt; + mod lower_intrinsics : LowerIntrinsics; + mod lower_slice_len : LowerSliceLenCalls; + mod match_branches : MatchBranchSimplification; + mod mentioned_items : MentionedItems; + mod multiple_return_terminators : MultipleReturnTerminators; + mod nrvo : RenameReturnPlace; + mod post_drop_elaboration : CheckLiveDrops; + mod prettify : ReorderBasicBlocks, ReorderLocals; + mod promote_consts : PromoteTemps; + mod ref_prop : ReferencePropagation; + mod remove_noop_landing_pads : RemoveNoopLandingPads; + mod remove_place_mention : RemovePlaceMention; + mod remove_storage_markers : RemoveStorageMarkers; + mod remove_uninit_drops : RemoveUninitDrops; + mod remove_unneeded_drops : RemoveUnneededDrops; + mod remove_zsts : RemoveZsts; + mod required_consts : RequiredConstsVisitor; + mod reveal_all : RevealAll; + mod sanity_check : SanityCheck; + // This pass is public to allow external drivers to perform MIR cleanup + pub mod simplify : + SimplifyCfg { + Initial, + PromoteConsts, + RemoveFalseEdges, + PostAnalysis, + PreOptimizations, + Final, + MakeShim, + AfterUnreachableEnumBranching + }, + SimplifyLocals { + BeforeConstProp, + AfterGVN, + Final + }; + mod simplify_branches : SimplifyConstCondition { + AfterConstProp, + Final + }; + mod simplify_comparison_integral : SimplifyComparisonIntegral; + mod single_use_consts : SingleUseConsts; + mod sroa : ScalarReplacementOfAggregates; + mod unreachable_enum_branching : UnreachableEnumBranching; + mod unreachable_prop : UnreachablePropagation; + mod validate : Validator; +} rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 29f8b4f6e4ddf..37c9f5b4c2136 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -1,7 +1,7 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -198,6 +198,16 @@ fn run_passes_inner<'tcx>( let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; trace!(?overridden_passes); + #[cfg(debug_assertions)] + { + let used_passes: FxIndexSet<_> = passes.iter().map(|p| p.name()).collect(); + let known_passes: FxIndexSet<_> = crate::PASS_NAMES.iter().map(|p| p.as_str()).collect(); + + for &name in used_passes.difference(&known_passes) { + tcx.dcx().bug(format!("pass `{name}` is not declared in `PASS_NAMES`")); + } + } + let prof_arg = tcx.sess.prof.enabled().then(|| format!("{:?}", body.source.def_id())); if !body.should_skip() { From 94371d5a8cf3c0480a744e4727837e9d7b2281b0 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 11 Nov 2024 16:36:00 +0000 Subject: [PATCH 2/4] Validate and test `-Zmir-enable-passes` --- compiler/rustc_mir_transform/messages.ftl | 2 ++ compiler/rustc_mir_transform/src/errors.rs | 6 +++++ compiler/rustc_mir_transform/src/lib.rs | 1 - .../rustc_mir_transform/src/pass_manager.rs | 24 +++++++++++++++---- ...nable_passes_validation.all_unknown.stderr | 14 +++++++++++ .../mir/enable_passes_validation.empty.stderr | 2 ++ .../mir/enable_passes_validation.mixed.stderr | 8 +++++++ tests/ui/mir/enable_passes_validation.rs | 21 ++++++++++++++++ ...enable_passes_validation.unprefixed.stderr | 2 ++ 9 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 tests/ui/mir/enable_passes_validation.all_unknown.stderr create mode 100644 tests/ui/mir/enable_passes_validation.empty.stderr create mode 100644 tests/ui/mir/enable_passes_validation.mixed.stderr create mode 100644 tests/ui/mir/enable_passes_validation.rs create mode 100644 tests/ui/mir/enable_passes_validation.unprefixed.stderr diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index c8992b8b83430..9bbfae17fd9e8 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -34,3 +34,5 @@ mir_transform_undefined_transmute = pointers cannot be transmuted to integers du .note = at compile-time, pointers do not have an integer value .note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior .help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html + +mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 8b309147c64cd..2d9eeddea2e22 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -38,6 +38,12 @@ pub(crate) struct UnalignedPackedRef { pub span: Span, } +#[derive(Diagnostic)] +#[diag(mir_transform_unknown_pass_name)] +pub(crate) struct UnknownPassName<'a> { + pub(crate) name: &'a str, +} + pub(crate) struct AssertLint

{ pub span: Span, pub assert_kind: AssertKind

, diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 66ac3d30ca403..68904c65252ae 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -90,7 +90,6 @@ macro_rules! declare_passes { )+ )* - #[cfg(debug_assertions)] static PASS_NAMES: LazyLock> = LazyLock::new(|| vec![ // Fake marker pass "PreCodegen".to_string(), diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 37c9f5b4c2136..bc960ae0f56aa 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -8,7 +8,7 @@ use rustc_session::Session; use tracing::trace; use crate::lint::lint_body; -use crate::validate; +use crate::{errors, validate}; thread_local! { static PASS_NAMES: RefCell> = { @@ -198,13 +198,29 @@ fn run_passes_inner<'tcx>( let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; trace!(?overridden_passes); + let named_passes: FxIndexSet<_> = + overridden_passes.iter().map(|(name, _)| name.as_str()).collect(); + let known_passes: FxIndexSet<_> = crate::PASS_NAMES.iter().map(|p| p.as_str()).collect(); + + for &name in named_passes.difference(&known_passes) { + tcx.dcx().emit_warn(errors::UnknownPassName { name }); + } + + // Verify that no passes are missing from the `declare_passes` invocation #[cfg(debug_assertions)] + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] { let used_passes: FxIndexSet<_> = passes.iter().map(|p| p.name()).collect(); - let known_passes: FxIndexSet<_> = crate::PASS_NAMES.iter().map(|p| p.as_str()).collect(); - for &name in used_passes.difference(&known_passes) { - tcx.dcx().bug(format!("pass `{name}` is not declared in `PASS_NAMES`")); + let undeclared = used_passes.difference(&known_passes).collect::>(); + if let Some((name, rest)) = undeclared.split_first() { + let mut err = + tcx.dcx().struct_bug(format!("pass `{name}` is not declared in `PASS_NAMES`")); + for name in rest { + err.note(format!("pass `{name}` is also not declared in `PASS_NAMES`")); + } + err.emit(); } } diff --git a/tests/ui/mir/enable_passes_validation.all_unknown.stderr b/tests/ui/mir/enable_passes_validation.all_unknown.stderr new file mode 100644 index 0000000000000..85a942c00ede7 --- /dev/null +++ b/tests/ui/mir/enable_passes_validation.all_unknown.stderr @@ -0,0 +1,14 @@ +warning: MIR pass `ThisPass` is unknown and will be ignored + +warning: MIR pass `DoesNotExist` is unknown and will be ignored + +warning: MIR pass `ThisPass` is unknown and will be ignored + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: MIR pass `DoesNotExist` is unknown and will be ignored + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: 4 warnings emitted + diff --git a/tests/ui/mir/enable_passes_validation.empty.stderr b/tests/ui/mir/enable_passes_validation.empty.stderr new file mode 100644 index 0000000000000..0e922663acc57 --- /dev/null +++ b/tests/ui/mir/enable_passes_validation.empty.stderr @@ -0,0 +1,2 @@ +error: incorrect value `` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected + diff --git a/tests/ui/mir/enable_passes_validation.mixed.stderr b/tests/ui/mir/enable_passes_validation.mixed.stderr new file mode 100644 index 0000000000000..5aace86abc020 --- /dev/null +++ b/tests/ui/mir/enable_passes_validation.mixed.stderr @@ -0,0 +1,8 @@ +warning: MIR pass `ThisPassDoesNotExist` is unknown and will be ignored + +warning: MIR pass `ThisPassDoesNotExist` is unknown and will be ignored + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: 2 warnings emitted + diff --git a/tests/ui/mir/enable_passes_validation.rs b/tests/ui/mir/enable_passes_validation.rs new file mode 100644 index 0000000000000..957e7d4d96dff --- /dev/null +++ b/tests/ui/mir/enable_passes_validation.rs @@ -0,0 +1,21 @@ +//@ revisions: empty unprefixed all_unknown all_known mixed + +//@[empty] compile-flags: -Zmir-enable-passes= +//@[empty] error-pattern error: incorrect value `` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected + +//@[unprefixed] compile-flags: -Zmir-enable-passes=CheckAlignment +//@[unprefixed] error-pattern error: incorrect value `CheckAlignment` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected + +//@[all_unknown] check-pass +//@[all_unknown] compile-flags: -Zmir-enable-passes=+ThisPass,-DoesNotExist +//@[all_unknown] error-pattern: warning: MIR pass `ThisPass` is unknown and will be ignored +//@[all_unknown] error-pattern: warning: MIR pass `DoesNotExist` is unknown and will be ignored + +//@[all_known] check-pass +//@[all_known] compile-flags: -Zmir-enable-passes=+CheckAlignment,+LowerIntrinsics + +//@[mixed] check-pass +//@[mixed] compile-flags: -Zmir-enable-passes=+ThisPassDoesNotExist,+CheckAlignment +//@[mixed] error-pattern: warning: MIR pass `ThisPassDoesNotExist` is unknown and will be ignored + +fn main() {} diff --git a/tests/ui/mir/enable_passes_validation.unprefixed.stderr b/tests/ui/mir/enable_passes_validation.unprefixed.stderr new file mode 100644 index 0000000000000..697589448f4a0 --- /dev/null +++ b/tests/ui/mir/enable_passes_validation.unprefixed.stderr @@ -0,0 +1,2 @@ +error: incorrect value `CheckAlignment` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected + From 12036830d039134c447a1d7299a59560cd4a39d0 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 11 Nov 2024 16:43:49 +0000 Subject: [PATCH 3/4] Store known passes as an IndexSet --- compiler/rustc_mir_transform/src/lib.rs | 10 +++++----- compiler/rustc_mir_transform/src/pass_manager.rs | 5 ++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 68904c65252ae..d2d5facbbdc83 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -90,20 +90,20 @@ macro_rules! declare_passes { )+ )* - static PASS_NAMES: LazyLock> = LazyLock::new(|| vec![ + static PASS_NAMES: LazyLock> = LazyLock::new(|| [ // Fake marker pass - "PreCodegen".to_string(), + "PreCodegen", $( $( - stringify!($pass_name).to_string(), + stringify!($pass_name), $( $( - $mod_name::$pass_name::$ident.name().to_string(), + $mod_name::$pass_name::$ident.name(), )* )? )+ )* - ]); + ].into_iter().collect()); }; } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index bc960ae0f56aa..e26e0c78b29db 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -200,9 +200,8 @@ fn run_passes_inner<'tcx>( let named_passes: FxIndexSet<_> = overridden_passes.iter().map(|(name, _)| name.as_str()).collect(); - let known_passes: FxIndexSet<_> = crate::PASS_NAMES.iter().map(|p| p.as_str()).collect(); - for &name in named_passes.difference(&known_passes) { + for &name in named_passes.difference(&*crate::PASS_NAMES) { tcx.dcx().emit_warn(errors::UnknownPassName { name }); } @@ -213,7 +212,7 @@ fn run_passes_inner<'tcx>( { let used_passes: FxIndexSet<_> = passes.iter().map(|p| p.name()).collect(); - let undeclared = used_passes.difference(&known_passes).collect::>(); + let undeclared = used_passes.difference(&*crate::PASS_NAMES).collect::>(); if let Some((name, rest)) = undeclared.split_first() { let mut err = tcx.dcx().struct_bug(format!("pass `{name}` is not declared in `PASS_NAMES`")); From 8c64e9d17dad7d6e8a9f00191d8f55bd14b44aef Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 11 Nov 2024 16:51:39 +0000 Subject: [PATCH 4/4] Rename `PASS_NAMES` to disambiguate --- compiler/rustc_mir_transform/src/pass_manager.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index e26e0c78b29db..779e7f22101c2 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -11,14 +11,15 @@ use crate::lint::lint_body; use crate::{errors, validate}; thread_local! { - static PASS_NAMES: RefCell> = { + /// Maps MIR pass names to a snake case form to match profiling naming style + static PASS_TO_PROFILER_NAMES: RefCell> = { RefCell::new(FxHashMap::default()) }; } /// Converts a MIR pass name into a snake case form to match the profiling naming style. fn to_profiler_name(type_name: &'static str) -> &'static str { - PASS_NAMES.with(|names| match names.borrow_mut().entry(type_name) { + PASS_TO_PROFILER_NAMES.with(|names| match names.borrow_mut().entry(type_name) { Entry::Occupied(e) => *e.get(), Entry::Vacant(e) => { let snake_case: String = type_name