1
Fork 0

Clarify which kinds of MIR are allowed during which phases.

This enhances documentation with these details and extends the validator to check these requirements
more thoroughly. As a part of this, we add a new `Deaggregated` phase, and rename other phases so
that their names more naturally correspond to what they represent.
This commit is contained in:
Jakob Degen 2022-03-05 20:37:04 -05:00
parent 547369d3d8
commit fe40240e4d
8 changed files with 98 additions and 52 deletions

View file

@ -42,7 +42,7 @@ pub struct PromoteTemps<'tcx> {
impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
fn phase_change(&self) -> Option<MirPhase> { fn phase_change(&self) -> Option<MirPhase> {
Some(MirPhase::ConstPromotion) Some(MirPhase::ConstsPromoted)
} }
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {

View file

@ -266,22 +266,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
); );
} }
} }
// The deaggregator currently does not deaggreagate arrays. Rvalue::Aggregate(agg_kind, _) => {
// So for now, we ignore them here. let disallowed = match **agg_kind {
Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {} AggregateKind::Array(..) => false,
// All other aggregates must be gone after some phases. AggregateKind::Generator(..) => {
Rvalue::Aggregate(box kind, _) => { self.mir_phase >= MirPhase::GeneratorsLowered
if self.mir_phase > MirPhase::DropLowering }
&& !matches!(kind, AggregateKind::Generator(..)) _ => self.mir_phase >= MirPhase::Deaggregated,
{ };
// Generators persist until the state machine transformation, but all if disallowed {
// other aggregates must have been lowered.
self.fail(
location,
format!("{:?} have been lowered to field assignments", rvalue),
)
} else if self.mir_phase > MirPhase::GeneratorLowering {
// No more aggregates after drop and generator lowering.
self.fail( self.fail(
location, location,
format!("{:?} have been lowered to field assignments", rvalue), format!("{:?} have been lowered to field assignments", rvalue),
@ -289,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} }
} }
Rvalue::Ref(_, BorrowKind::Shallow, _) => { Rvalue::Ref(_, BorrowKind::Shallow, _) => {
if self.mir_phase > MirPhase::DropLowering { if self.mir_phase >= MirPhase::DropsLowered {
self.fail( self.fail(
location, location,
"`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
@ -300,7 +293,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} }
} }
StatementKind::AscribeUserType(..) => { StatementKind::AscribeUserType(..) => {
if self.mir_phase > MirPhase::DropLowering { if self.mir_phase >= MirPhase::DropsLowered {
self.fail( self.fail(
location, location,
"`AscribeUserType` should have been removed after drop lowering phase", "`AscribeUserType` should have been removed after drop lowering phase",
@ -308,7 +301,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} }
} }
StatementKind::FakeRead(..) => { StatementKind::FakeRead(..) => {
if self.mir_phase > MirPhase::DropLowering { if self.mir_phase >= MirPhase::DropsLowered {
self.fail( self.fail(
location, location,
"`FakeRead` should have been removed after drop lowering phase", "`FakeRead` should have been removed after drop lowering phase",
@ -351,10 +344,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty)) self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty))
} }
} }
StatementKind::SetDiscriminant { .. } StatementKind::SetDiscriminant { .. } => {
| StatementKind::StorageLive(..) if self.mir_phase < MirPhase::DropsLowered {
self.fail(location, "`SetDiscriminant` is not allowed until drop elaboration");
}
}
StatementKind::Retag(_, _) => {
// FIXME(JakobDegen) The validator should check that `self.mir_phase <
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
// seem to fail to set their `MirPhase` correctly.
}
StatementKind::StorageLive(..)
| StatementKind::StorageDead(..) | StatementKind::StorageDead(..)
| StatementKind::Retag(_, _)
| StatementKind::Coverage(_) | StatementKind::Coverage(_)
| StatementKind::Nop => {} | StatementKind::Nop => {}
} }
@ -424,10 +425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} }
} }
TerminatorKind::DropAndReplace { target, unwind, .. } => { TerminatorKind::DropAndReplace { target, unwind, .. } => {
if self.mir_phase > MirPhase::DropLowering { if self.mir_phase >= MirPhase::DropsLowered {
self.fail( self.fail(
location, location,
"`DropAndReplace` is not permitted to exist after drop elaboration", "`DropAndReplace` should have been removed during drop elaboration",
); );
} }
self.check_edge(location, *target, EdgeKind::Normal); self.check_edge(location, *target, EdgeKind::Normal);
@ -494,7 +495,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} }
} }
TerminatorKind::Yield { resume, drop, .. } => { TerminatorKind::Yield { resume, drop, .. } => {
if self.mir_phase > MirPhase::GeneratorLowering { if self.mir_phase >= MirPhase::GeneratorsLowered {
self.fail(location, "`Yield` should have been replaced by generator lowering"); self.fail(location, "`Yield` should have been replaced by generator lowering");
} }
self.check_edge(location, *resume, EdgeKind::Normal); self.check_edge(location, *resume, EdgeKind::Normal);
@ -503,10 +504,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
} }
} }
TerminatorKind::FalseEdge { real_target, imaginary_target } => { TerminatorKind::FalseEdge { real_target, imaginary_target } => {
if self.mir_phase >= MirPhase::DropsLowered {
self.fail(
location,
"`FalseEdge` should have been removed after drop elaboration",
);
}
self.check_edge(location, *real_target, EdgeKind::Normal); self.check_edge(location, *real_target, EdgeKind::Normal);
self.check_edge(location, *imaginary_target, EdgeKind::Normal); self.check_edge(location, *imaginary_target, EdgeKind::Normal);
} }
TerminatorKind::FalseUnwind { real_target, unwind } => { TerminatorKind::FalseUnwind { real_target, unwind } => {
if self.mir_phase >= MirPhase::DropsLowered {
self.fail(
location,
"`FalseUnwind` should have been removed after drop elaboration",
);
}
self.check_edge(location, *real_target, EdgeKind::Normal); self.check_edge(location, *real_target, EdgeKind::Normal);
if let Some(unwind) = unwind { if let Some(unwind) = unwind {
self.check_edge(location, *unwind, EdgeKind::Unwind); self.check_edge(location, *unwind, EdgeKind::Unwind);
@ -520,12 +533,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.check_edge(location, *cleanup, EdgeKind::Unwind); self.check_edge(location, *cleanup, EdgeKind::Unwind);
} }
} }
TerminatorKind::GeneratorDrop => {
if self.mir_phase >= MirPhase::GeneratorsLowered {
self.fail(
location,
"`GeneratorDrop` should have been replaced by generator lowering",
);
}
}
// Nothing to validate for these. // Nothing to validate for these.
TerminatorKind::Resume TerminatorKind::Resume
| TerminatorKind::Abort | TerminatorKind::Abort
| TerminatorKind::Return | TerminatorKind::Return
| TerminatorKind::Unreachable | TerminatorKind::Unreachable => {}
| TerminatorKind::GeneratorDrop => {}
} }
self.super_terminator(terminator, location); self.super_terminator(terminator, location);

View file

@ -127,14 +127,11 @@ pub trait MirPass<'tcx> {
/// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the /// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the
/// dialects forbid certain variants or values in certain phases. /// dialects forbid certain variants or values in certain phases.
/// ///
/// Note: Each phase's validation checks all invariants of the *previous* phases' dialects. A phase
/// that changes the dialect documents what invariants must be upheld *after* that phase finishes.
///
/// Warning: ordering of variants is significant. /// Warning: ordering of variants is significant.
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(HashStable)] #[derive(HashStable)]
pub enum MirPhase { pub enum MirPhase {
Build = 0, Built = 0,
// FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query). // 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. // We used to have this for pre-miri MIR based const eval.
Const = 1, Const = 1,
@ -142,17 +139,32 @@ pub enum MirPhase {
/// by creating a new MIR body per promoted element. After this phase (and thus the termination /// by creating a new MIR body per promoted element. After this phase (and thus the termination
/// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir` /// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir`
/// query. /// query.
ConstPromotion = 2, ConstsPromoted = 2,
/// After this phase /// Beginning with this phase, the following variants are disallowed:
/// * the only `AggregateKind`s allowed are `Array` and `Generator`, /// * [`TerminatorKind::DropAndReplace`](terminator::TerminatorKind::DropAndReplace)
/// * `DropAndReplace` is gone for good /// * [`TerminatorKind::FalseUnwind`](terminator::TerminatorKind::FalseUnwind)
/// * `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` terminator /// * [`TerminatorKind::FalseEdge`](terminator::TerminatorKind::FalseEdge)
/// means that the auto-generated drop glue will be invoked. /// * [`StatementKind::FakeRead`]
DropLowering = 3, /// * [`StatementKind::AscribeUserType`]
/// After this phase, generators are explicit state machines (no more `Yield`). /// * [`Rvalue::Ref`] with `BorrowKind::Shallow`
/// `AggregateKind::Generator` is gone for good. ///
GeneratorLowering = 4, /// And the following variant is allowed:
Optimization = 5, /// * [`StatementKind::Retag`]
///
/// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop`
/// terminator means that the auto-generated drop glue will be invoked.
DropsLowered = 3,
/// Beginning with this phase, the following variant is disallowed:
/// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
///
/// And the following variant is allowed:
/// * [`StatementKind::SetDiscriminant`]
Deaggregated = 4,
/// Beginning with this phase, the following variants are disallowed:
/// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield)
/// * [`TerminatorKind::GeneratorDrop](terminator::TerminatorKind::GeneratorDrop)
GeneratorsLowered = 5,
Optimized = 6,
} }
impl MirPhase { impl MirPhase {
@ -311,7 +323,7 @@ impl<'tcx> Body<'tcx> {
); );
let mut body = Body { let mut body = Body {
phase: MirPhase::Build, phase: MirPhase::Built,
source, source,
basic_blocks, basic_blocks,
source_scopes, source_scopes,
@ -346,7 +358,7 @@ impl<'tcx> Body<'tcx> {
/// crate. /// crate.
pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self { pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
let mut body = Body { let mut body = Body {
phase: MirPhase::Build, phase: MirPhase::Built,
source: MirSource::item(DefId::local(CRATE_DEF_INDEX)), source: MirSource::item(DefId::local(CRATE_DEF_INDEX)),
basic_blocks, basic_blocks,
source_scopes: IndexVec::new(), source_scopes: IndexVec::new(),
@ -1541,6 +1553,11 @@ impl Statement<'_> {
} }
} }
/// The various kinds of statements that can appear in MIR.
///
/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which
/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions,
/// causing an ICE if they are violated.
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)]
pub enum StatementKind<'tcx> { pub enum StatementKind<'tcx> {
/// Write the RHS Rvalue to the LHS Place. /// Write the RHS Rvalue to the LHS Place.
@ -2223,6 +2240,11 @@ impl<'tcx> Operand<'tcx> {
/// Rvalues /// Rvalues
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
/// The various kinds of rvalues that can appear in MIR.
///
/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which
/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions,
/// causing an ICE if they are violated.
pub enum Rvalue<'tcx> { pub enum Rvalue<'tcx> {
/// x (either a move or copy, depending on type of x) /// x (either a move or copy, depending on type of x)
Use(Operand<'tcx>), Use(Operand<'tcx>),

View file

@ -6,6 +6,10 @@ use rustc_middle::ty::TyCtxt;
pub struct Deaggregator; pub struct Deaggregator;
impl<'tcx> MirPass<'tcx> for 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>) { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
let local_decls = &*local_decls; let local_decls = &*local_decls;

View file

@ -20,7 +20,7 @@ pub struct ElaborateDrops;
impl<'tcx> MirPass<'tcx> for ElaborateDrops { impl<'tcx> MirPass<'tcx> for ElaborateDrops {
fn phase_change(&self) -> Option<MirPhase> { fn phase_change(&self) -> Option<MirPhase> {
Some(MirPhase::DropLowering) Some(MirPhase::DropsLowered)
} }
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {

View file

@ -1235,7 +1235,7 @@ fn create_cases<'tcx>(
impl<'tcx> MirPass<'tcx> for StateTransform { impl<'tcx> MirPass<'tcx> for StateTransform {
fn phase_change(&self) -> Option<MirPhase> { fn phase_change(&self) -> Option<MirPhase> {
Some(MirPhase::GeneratorLowering) Some(MirPhase::GeneratorsLowered)
} }
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {

View file

@ -341,7 +341,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -
pm::run_passes( pm::run_passes(
tcx, tcx,
&mut body, &mut body,
&[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimization)], &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)],
); );
} }
} }
@ -398,7 +398,7 @@ fn mir_drops_elaborated_and_const_checked<'tcx>(
} }
run_post_borrowck_cleanup_passes(tcx, &mut body); run_post_borrowck_cleanup_passes(tcx, &mut body);
assert!(body.phase == MirPhase::DropLowering); assert!(body.phase == MirPhase::Deaggregated);
tcx.alloc_steal_mir(body) tcx.alloc_steal_mir(body)
} }
@ -458,7 +458,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
], ],
); );
assert!(body.phase == MirPhase::GeneratorLowering); assert!(body.phase == MirPhase::GeneratorsLowered);
// The main optimizations that we do on MIR. // The main optimizations that we do on MIR.
pm::run_passes( pm::run_passes(
@ -495,7 +495,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&deduplicate_blocks::DeduplicateBlocks, &deduplicate_blocks::DeduplicateBlocks,
// Some cleanup necessary at least for LLVM and potentially other codegen backends. // Some cleanup necessary at least for LLVM and potentially other codegen backends.
&add_call_guards::CriticalCallEdges, &add_call_guards::CriticalCallEdges,
&marker::PhaseChange(MirPhase::Optimization), &marker::PhaseChange(MirPhase::Optimized),
// Dump the end result for testing and debugging purposes. // Dump the end result for testing and debugging purposes.
&dump_mir::Marker("PreCodegen"), &dump_mir::Marker("PreCodegen"),
], ],

View file

@ -114,7 +114,7 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn
} }
} }
if validate || body.phase == MirPhase::Optimization { if validate || body.phase == MirPhase::Optimized {
validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase)); validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase));
} }
} }