Auto merge of #99102 - JakobDegen:reorder-generators, r=oli-obk

Rework definition of MIR phases to more closely reflect semantic concerns

Implements most of rust-lang/compiler-team#522 .

I tried my best to restrict this PR to the "core" parts of the MCP. In other words, this includes just enough changes to make the new definition of `MirPhase` make sense. That means there are a couple of FIXMEs lying around. Depending on what reviewers prefer, I can either fix them in this PR or send follow up PRs. There are also a couple other refactorings of the `rustc_mir_transform/src/lib.rs` file that I want to do in follow ups that I didn't leave explicit FIXMEs for.
This commit is contained in:
bors 2022-08-30 23:43:33 +00:00
commit f07d6e8c0a
17 changed files with 307 additions and 214 deletions

View file

@ -6,10 +6,6 @@ use rustc_middle::ty::TyCtxt;
pub struct Deaggregator;
impl<'tcx> MirPass<'tcx> for Deaggregator {
fn phase_change(&self) -> Option<MirPhase> {
Some(MirPhase::Deaggregated)
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
for bb in basic_blocks {

View file

@ -82,6 +82,5 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
impl<'tcx> MirPass<'tcx> for Derefer {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
deref_finder(tcx, body);
body.phase = MirPhase::Derefered;
}
}

View file

@ -21,10 +21,6 @@ use std::fmt;
pub struct ElaborateDrops;
impl<'tcx> MirPass<'tcx> for ElaborateDrops {
fn phase_change(&self) -> Option<MirPhase> {
Some(MirPhase::DropsLowered)
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
debug!("elaborate_drops({:?} @ {:?})", body.source, body.span);

View file

@ -1240,10 +1240,6 @@ fn create_cases<'tcx>(
}
impl<'tcx> MirPass<'tcx> for StateTransform {
fn phase_change(&self) -> Option<MirPhase> {
Some(MirPhase::GeneratorsLowered)
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let Some(yield_ty) = body.yield_ty() else {
// This only applies to generators

View file

@ -26,7 +26,9 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_index::vec::IndexVec;
use rustc_middle::mir::visit::Visitor as _;
use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPass, MirPhase, Promoted};
use rustc_middle::mir::{
traversal, AnalysisPhase, Body, ConstQualifs, MirPass, MirPhase, Promoted, RuntimePhase,
};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
@ -200,6 +202,8 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) ->
}
/// Make MIR ready for const evaluation. This is run on all MIR, not just on consts!
/// FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query).
/// We used to have this for pre-miri MIR based const eval.
fn mir_const<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
@ -235,7 +239,6 @@ fn mir_const<'tcx>(
// What we need to do constant evaluation.
&simplify::SimplifyCfg::new("initial"),
&rustc_peek::SanityCheck, // Just a lint
&marker::PhaseChange(MirPhase::Const),
],
);
tcx.alloc_steal_mir(body)
@ -341,7 +344,10 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -
pm::run_passes(
tcx,
&mut body,
&[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)],
&[
&const_prop::ConstProp,
&marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)),
],
);
}
}
@ -381,38 +387,61 @@ fn mir_drops_elaborated_and_const_checked<'tcx>(
body.tainted_by_errors = Some(error_reported);
}
// IMPORTANT
pm::run_passes(tcx, &mut body, &[&remove_false_edges::RemoveFalseEdges]);
run_analysis_to_runtime_passes(tcx, &mut body);
tcx.alloc_steal_mir(body)
}
fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
assert!(body.phase == MirPhase::Analysis(AnalysisPhase::Initial));
let did = body.source.def_id();
debug!("analysis_mir_cleanup({:?})", did);
run_analysis_cleanup_passes(tcx, body);
assert!(body.phase == MirPhase::Analysis(AnalysisPhase::PostCleanup));
// Do a little drop elaboration before const-checking if `const_precise_live_drops` is enabled.
if check_consts::post_drop_elaboration::checking_enabled(&ConstCx::new(tcx, &body)) {
pm::run_passes(
tcx,
&mut body,
body,
&[
&simplify::SimplifyCfg::new("remove-false-edges"),
&remove_uninit_drops::RemoveUninitDrops,
&simplify::SimplifyCfg::new("remove-false-edges"),
],
);
check_consts::post_drop_elaboration::check_live_drops(tcx, &body); // FIXME: make this a MIR lint
}
run_post_borrowck_cleanup_passes(tcx, &mut body);
assert!(body.phase == MirPhase::Deaggregated);
tcx.alloc_steal_mir(body)
debug!("runtime_mir_lowering({:?})", did);
run_runtime_lowering_passes(tcx, body);
assert!(body.phase == MirPhase::Runtime(RuntimePhase::Initial));
debug!("runtime_mir_cleanup({:?})", did);
run_runtime_cleanup_passes(tcx, body);
assert!(body.phase == MirPhase::Runtime(RuntimePhase::PostCleanup));
}
/// After this series of passes, no lifetime analysis based on borrowing can be done.
fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
debug!("post_borrowck_cleanup({:?})", body.source.def_id());
// FIXME(JakobDegen): Can we make these lists of passes consts?
let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[
// Remove all things only needed by analysis
/// After this series of passes, no lifetime analysis based on borrowing can be done.
fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let passes: &[&dyn MirPass<'tcx>] = &[
&remove_false_edges::RemoveFalseEdges,
&simplify_branches::SimplifyConstCondition::new("initial"),
&remove_noop_landing_pads::RemoveNoopLandingPads,
&cleanup_post_borrowck::CleanupNonCodegenStatements,
&simplify::SimplifyCfg::new("early-opt"),
&deref_separator::Derefer,
&marker::PhaseChange(MirPhase::Analysis(AnalysisPhase::PostCleanup)),
];
pm::run_passes(tcx, body, passes);
}
/// Returns the sequence of passes that lowers analysis to runtime MIR.
fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let passes: &[&dyn MirPass<'tcx>] = &[
// These next passes must be executed together
&add_call_guards::CriticalCallEdges,
&elaborate_drops::ElaborateDrops,
@ -426,16 +455,27 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc
// `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
// but before optimizations begin.
&elaborate_box_derefs::ElaborateBoxDerefs,
&generator::StateTransform,
&add_retag::AddRetag,
&lower_intrinsics::LowerIntrinsics,
&simplify::SimplifyCfg::new("elaborate-drops"),
// `Deaggregator` is conceptually part of MIR building, some backends rely on it happening
// and it can help optimizations.
// Deaggregator is necessary for const prop. We may want to consider implementing
// CTFE support for aggregates.
&deaggregator::Deaggregator,
&Lint(const_prop_lint::ConstProp),
&marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Initial)),
];
pm::run_passes_no_validate(tcx, body, passes);
}
/// Returns the sequence of passes that do the initial cleanup of runtime MIR.
fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let passes: &[&dyn MirPass<'tcx>] = &[
&elaborate_box_derefs::ElaborateBoxDerefs,
&lower_intrinsics::LowerIntrinsics,
&simplify::SimplifyCfg::new("elaborate-drops"),
&marker::PhaseChange(MirPhase::Runtime(RuntimePhase::PostCleanup)),
];
pm::run_passes(tcx, body, post_borrowck_cleanup);
pm::run_passes(tcx, body, passes);
}
fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
@ -443,9 +483,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
WithMinOptLevel(1, x)
}
// Lowering generator control-flow and variables has to happen before we do anything else
// to them. We run some optimizations before that, because they may be harder to do on the state
// machine than on MIR with async primitives.
// The main optimizations that we do on MIR.
pm::run_passes(
tcx,
body,
@ -457,17 +495,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&uninhabited_enum_branching::UninhabitedEnumBranching,
&o1(simplify::SimplifyCfg::new("after-uninhabited-enum-branching")),
&inline::Inline,
&generator::StateTransform,
],
);
assert!(body.phase == MirPhase::GeneratorsLowered);
// The main optimizations that we do on MIR.
pm::run_passes(
tcx,
body,
&[
&remove_storage_markers::RemoveStorageMarkers,
&remove_zsts::RemoveZsts,
&const_goto::ConstGoto,
@ -499,7 +526,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&deduplicate_blocks::DeduplicateBlocks,
// Some cleanup necessary at least for LLVM and potentially other codegen backends.
&add_call_guards::CriticalCallEdges,
&marker::PhaseChange(MirPhase::Optimized),
&marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)),
// Dump the end result for testing and debugging purposes.
&dump_mir::Marker("PreCodegen"),
],
@ -558,7 +585,7 @@ fn promoted_mir<'tcx>(
if let Some(error_reported) = tainted_by_errors {
body.tainted_by_errors = Some(error_reported);
}
run_post_borrowck_cleanup_passes(tcx, body);
run_analysis_to_runtime_passes(tcx, body);
}
debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR");

View file

@ -1,6 +1,6 @@
use std::borrow::Cow;
use rustc_middle::mir::{self, Body, MirPhase};
use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
@ -72,48 +72,62 @@ where
}
}
/// Run the sequence of passes without validating the MIR after each pass. The MIR is still
/// validated at the end.
pub fn run_passes_no_validate<'tcx>(
tcx: TyCtxt<'tcx>,
body: &mut Body<'tcx>,
passes: &[&dyn MirPass<'tcx>],
) {
run_passes_inner(tcx, body, passes, false);
}
pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>]) {
run_passes_inner(tcx, body, passes, true);
}
fn run_passes_inner<'tcx>(
tcx: TyCtxt<'tcx>,
body: &mut Body<'tcx>,
passes: &[&dyn MirPass<'tcx>],
validate_each: bool,
) {
let start_phase = body.phase;
let mut cnt = 0;
let validate = tcx.sess.opts.unstable_opts.validate_mir;
let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir;
let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes;
trace!(?overridden_passes);
if validate {
validate_body(tcx, body, format!("start of phase transition from {:?}", start_phase));
}
for pass in passes {
let name = pass.name();
if let Some((_, polarity)) = overridden_passes.iter().rev().find(|(s, _)| s == &*name) {
trace!(
pass = %name,
"{} as requested by flag",
if *polarity { "Running" } else { "Not running" },
);
if !polarity {
continue;
}
} else {
if !pass.is_enabled(&tcx.sess) {
continue;
}
}
let dump_enabled = pass.is_mir_dump_enabled();
// Gather information about what we should be doing for this pass
let overriden =
overridden_passes.iter().rev().find(|(s, _)| s == &*name).map(|(_name, polarity)| {
trace!(
pass = %name,
"{} as requested by flag",
if *polarity { "Running" } else { "Not running" },
);
*polarity
});
let is_enabled = overriden.unwrap_or_else(|| pass.is_enabled(&tcx.sess));
let new_phase = pass.phase_change();
let dump_enabled = (is_enabled && pass.is_mir_dump_enabled()) || new_phase.is_some();
let validate = (validate && is_enabled)
|| new_phase == Some(MirPhase::Runtime(RuntimePhase::Optimized));
if dump_enabled {
dump_mir(tcx, body, start_phase, &name, cnt, false);
}
pass.run_pass(tcx, body);
if is_enabled {
pass.run_pass(tcx, body);
}
if dump_enabled {
dump_mir(tcx, body, start_phase, &name, cnt, true);
cnt += 1;
}
if let Some(new_phase) = pass.phase_change() {
if body.phase >= new_phase {
panic!("Invalid MIR phase transition from {:?} to {:?}", body.phase, new_phase);
@ -121,15 +135,10 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn
body.phase = new_phase;
}
if validate {
validate_body(tcx, body, format!("after pass {}", pass.name()));
validate_body(tcx, body, format!("after pass {}", name));
}
}
if validate || body.phase == MirPhase::Optimized {
validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase));
}
}
pub fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) {
@ -144,7 +153,7 @@ pub fn dump_mir<'tcx>(
cnt: usize,
is_after: bool,
) {
let phase_index = phase as u32;
let phase_index = phase.phase_index();
mir::dump_mir(
tcx,

View file

@ -17,8 +17,8 @@ use std::iter;
use crate::util::expand_aggregate;
use crate::{
abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, marker, pass_manager as pm,
remove_noop_landing_pads, simplify,
abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, marker,
pass_manager as pm, remove_noop_landing_pads, simplify,
};
use rustc_middle::mir::patch::MirPatch;
use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
@ -92,11 +92,12 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
&mut result,
&[
&add_moves_for_packed_drops::AddMovesForPackedDrops,
&deref_separator::Derefer,
&remove_noop_landing_pads::RemoveNoopLandingPads,
&simplify::SimplifyCfg::new("make_shim"),
&add_call_guards::CriticalCallEdges,
&abort_unwinding_calls::AbortUnwindingCalls,
&marker::PhaseChange(MirPhase::Const),
&marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)),
],
);