Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify which kinds of MIR are allowed during which phases. #94655

Merged
merged 2 commits into from
Mar 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/transform/promote_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub struct PromoteTemps<'tcx> {

impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
fn phase_change(&self) -> Option<MirPhase> {
Some(MirPhase::ConstPromotion)
Some(MirPhase::ConstsPromoted)
}

fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
Expand Down
74 changes: 47 additions & 27 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,30 +266,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
);
}
}
// The deaggregator currently does not deaggreagate arrays.
// So for now, we ignore them here.
Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {}
// All other aggregates must be gone after some phases.
Rvalue::Aggregate(box kind, _) => {
if self.mir_phase > MirPhase::DropLowering
&& !matches!(kind, AggregateKind::Generator(..))
{
// Generators persist until the state machine transformation, but all
// 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.
Rvalue::Aggregate(agg_kind, _) => {
let disallowed = match **agg_kind {
AggregateKind::Array(..) => false,
AggregateKind::Generator(..) => {
self.mir_phase >= MirPhase::GeneratorsLowered
}
_ => self.mir_phase >= MirPhase::Deaggregated,
};
if disallowed {
self.fail(
location,
format!("{:?} have been lowered to field assignments", rvalue),
)
}
}
Rvalue::Ref(_, BorrowKind::Shallow, _) => {
if self.mir_phase > MirPhase::DropLowering {
if self.mir_phase >= MirPhase::DropsLowered {
self.fail(
location,
"`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
Expand All @@ -300,15 +293,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
StatementKind::AscribeUserType(..) => {
if self.mir_phase > MirPhase::DropLowering {
if self.mir_phase >= MirPhase::DropsLowered {
self.fail(
location,
"`AscribeUserType` should have been removed after drop lowering phase",
);
}
}
StatementKind::FakeRead(..) => {
if self.mir_phase > MirPhase::DropLowering {
if self.mir_phase >= MirPhase::DropsLowered {
self.fail(
location,
"`FakeRead` should have been removed after drop lowering phase",
Expand Down Expand Up @@ -351,10 +344,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty))
}
}
StatementKind::SetDiscriminant { .. }
| StatementKind::StorageLive(..)
StatementKind::SetDiscriminant { .. } => {
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::Retag(_, _)
| StatementKind::Coverage(_)
| StatementKind::Nop => {}
}
Expand Down Expand Up @@ -424,10 +425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
TerminatorKind::DropAndReplace { target, unwind, .. } => {
if self.mir_phase > MirPhase::DropLowering {
if self.mir_phase >= MirPhase::DropsLowered {
self.fail(
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);
Expand Down Expand Up @@ -494,7 +495,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
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.check_edge(location, *resume, EdgeKind::Normal);
Expand All @@ -503,10 +504,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
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, *imaginary_target, EdgeKind::Normal);
}
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);
if let Some(unwind) = unwind {
self.check_edge(location, *unwind, EdgeKind::Unwind);
Expand All @@ -520,12 +533,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
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.
TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::GeneratorDrop => {}
| TerminatorKind::Unreachable => {}
}

self.super_terminator(terminator, location);
Expand Down
71 changes: 54 additions & 17 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,32 +127,44 @@ pub trait MirPass<'tcx> {
/// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the
/// 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.
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(HashStable)]
pub enum MirPhase {
Build = 0,
Built = 0,
// 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.
Const = 1,
/// This phase checks the MIR for promotable elements and takes them out of the main MIR body
/// 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`
/// query.
ConstPromotion = 2,
/// After this phase
/// * the only `AggregateKind`s allowed are `Array` and `Generator`,
JakobDegen marked this conversation as resolved.
Show resolved Hide resolved
/// * `DropAndReplace` is gone for good
/// * `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.
DropLowering = 3,
/// After this phase, generators are explicit state machines (no more `Yield`).
/// `AggregateKind::Generator` is gone for good.
GeneratorLowering = 4,
Optimization = 5,
ConstsPromoted = 2,
/// Beginning with this phase, the following variants are disallowed:
/// * [`TerminatorKind::DropAndReplace`](terminator::TerminatorKind::DropAndReplace)
/// * [`TerminatorKind::FalseUnwind`](terminator::TerminatorKind::FalseUnwind)
/// * [`TerminatorKind::FalseEdge`](terminator::TerminatorKind::FalseEdge)
/// * [`StatementKind::FakeRead`]
/// * [`StatementKind::AscribeUserType`]
/// * [`Rvalue::Ref`] with `BorrowKind::Shallow`
///
/// And the following variant is allowed:
/// * [`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 {
Expand Down Expand Up @@ -311,7 +323,7 @@ impl<'tcx> Body<'tcx> {
);

let mut body = Body {
phase: MirPhase::Build,
phase: MirPhase::Built,
source,
basic_blocks,
source_scopes,
Expand Down Expand Up @@ -346,7 +358,7 @@ impl<'tcx> Body<'tcx> {
/// crate.
pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
let mut body = Body {
phase: MirPhase::Build,
phase: MirPhase::Built,
source: MirSource::item(DefId::local(CRATE_DEF_INDEX)),
basic_blocks,
source_scopes: IndexVec::new(),
Expand Down Expand Up @@ -1541,9 +1553,16 @@ 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)]
pub enum StatementKind<'tcx> {
/// Write the RHS Rvalue to the LHS Place.
///
/// The LHS place may not overlap with any memory accessed on the RHS.
Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>),

/// This represents all the reading that a pattern match may do
Expand Down Expand Up @@ -1761,6 +1780,19 @@ static_assert_size!(Place<'_>, 16);
pub enum ProjectionElem<V, T> {
Deref,
Field(Field, T),
/// Index into a slice/array.
///
/// Note that this does not also dereference, and so it does not exactly correspond to slice
/// indexing in Rust. In other words, in the below Rust code:
///
/// ```rust
/// let x = &[1, 2, 3, 4];
/// let i = 2;
/// x[i];
/// ```
///
/// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same
/// thing is true of the `ConstantIndex` and `Subslice` projections below.
Index(V),

/// These indices are generated by slice patterns. Easiest to explain
Expand Down Expand Up @@ -2223,6 +2255,11 @@ impl<'tcx> Operand<'tcx> {
/// Rvalues

#[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> {
/// x (either a move or copy, depending on type of x)
Use(Operand<'tcx>),
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_mir_transform/src/deaggregator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ 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, local_decls) = body.basic_blocks_and_local_decls_mut();
let local_decls = &*local_decls;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/elaborate_drops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub struct ElaborateDrops;

impl<'tcx> MirPass<'tcx> for ElaborateDrops {
fn phase_change(&self) -> Option<MirPhase> {
Some(MirPhase::DropLowering)
Some(MirPhase::DropsLowered)
}

fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,7 @@ fn create_cases<'tcx>(

impl<'tcx> MirPass<'tcx> for StateTransform {
fn phase_change(&self) -> Option<MirPhase> {
Some(MirPhase::GeneratorLowering)
Some(MirPhase::GeneratorsLowered)
}

fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -
pm::run_passes(
tcx,
&mut body,
&[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimization)],
&[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)],
);
}
}
Expand Down Expand Up @@ -398,7 +398,7 @@ fn mir_drops_elaborated_and_const_checked<'tcx>(
}

run_post_borrowck_cleanup_passes(tcx, &mut body);
assert!(body.phase == MirPhase::DropLowering);
assert!(body.phase == MirPhase::Deaggregated);
tcx.alloc_steal_mir(body)
}

Expand Down Expand Up @@ -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.
pm::run_passes(
Expand Down Expand Up @@ -495,7 +495,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::Optimization),
&marker::PhaseChange(MirPhase::Optimized),
// Dump the end result for testing and debugging purposes.
&dump_mir::Marker("PreCodegen"),
],
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/pass_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
Expand Down