Skip to content

Commit f07d6e8

Browse files
committed
Auto merge of rust-lang#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.
2 parents 02654a0 + d56751c commit f07d6e8

17 files changed

+301
-208
lines changed

compiler/rustc_const_eval/src/transform/promote_consts.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub struct PromoteTemps<'tcx> {
4141

4242
impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
4343
fn phase_change(&self) -> Option<MirPhase> {
44-
Some(MirPhase::ConstsPromoted)
44+
Some(MirPhase::Analysis(AnalysisPhase::Initial))
4545
}
4646

4747
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
@@ -964,7 +964,7 @@ pub fn promote_candidates<'tcx>(
964964
let mut scope = body.source_scopes[body.source_info(candidate.location).scope].clone();
965965
scope.parent_scope = None;
966966

967-
let promoted = Body::new(
967+
let mut promoted = Body::new(
968968
body.source, // `promoted` gets filled in below
969969
IndexVec::new(),
970970
IndexVec::from_elem_n(scope, 1),
@@ -976,6 +976,7 @@ pub fn promote_candidates<'tcx>(
976976
body.generator_kind(),
977977
body.tainted_by_errors,
978978
);
979+
promoted.phase = MirPhase::Analysis(AnalysisPhase::Initial);
979980

980981
let promoter = Promoter {
981982
promoted,

compiler/rustc_const_eval/src/transform/validate.rs

+20-18
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
88
use rustc_middle::mir::visit::{PlaceContext, Visitor};
99
use rustc_middle::mir::{
1010
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, Local, Location,
11-
MirPass, MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope,
12-
Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
11+
MirPass, MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, RuntimePhase, Rvalue,
12+
SourceScope, Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
1313
};
1414
use rustc_middle::ty::fold::BottomUpFolder;
1515
use rustc_middle::ty::subst::Subst;
@@ -221,7 +221,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
221221

222222
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
223223
// This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
224-
if self.tcx.sess.opts.unstable_opts.validate_mir && self.mir_phase < MirPhase::DropsLowered
224+
if self.tcx.sess.opts.unstable_opts.validate_mir
225+
&& self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial)
225226
{
226227
// `Operand::Copy` is only supposed to be used with `Copy` types.
227228
if let Operand::Copy(place) = operand {
@@ -252,7 +253,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
252253
self.fail(location, format!("bad index ({:?} != usize)", index_ty))
253254
}
254255
}
255-
ProjectionElem::Deref if self.mir_phase >= MirPhase::GeneratorsLowered => {
256+
ProjectionElem::Deref
257+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) =>
258+
{
256259
let base_ty = Place::ty_from(local, proj_base, &self.body.local_decls, self.tcx).ty;
257260

258261
if base_ty.is_box() {
@@ -360,7 +363,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
360363
// Set off any `bug!`s in the type computation code
361364
let _ = place.ty(&self.body.local_decls, self.tcx);
362365

363-
if self.mir_phase >= MirPhase::Derefered
366+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial)
364367
&& place.projection.len() > 1
365368
&& cntxt != PlaceContext::NonUse(VarDebugInfo)
366369
&& place.projection[1..].contains(&ProjectionElem::Deref)
@@ -384,8 +387,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
384387
Rvalue::Aggregate(agg_kind, _) => {
385388
let disallowed = match **agg_kind {
386389
AggregateKind::Array(..) => false,
387-
AggregateKind::Generator(..) => self.mir_phase >= MirPhase::GeneratorsLowered,
388-
_ => self.mir_phase >= MirPhase::Deaggregated,
390+
_ => self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup),
389391
};
390392
if disallowed {
391393
self.fail(
@@ -395,10 +397,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
395397
}
396398
}
397399
Rvalue::Ref(_, BorrowKind::Shallow, _) => {
398-
if self.mir_phase >= MirPhase::DropsLowered {
400+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
399401
self.fail(
400402
location,
401-
"`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
403+
"`Assign` statement with a `Shallow` borrow should have been removed in runtime MIR",
402404
);
403405
}
404406
}
@@ -612,15 +614,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
612614
}
613615
}
614616
StatementKind::AscribeUserType(..) => {
615-
if self.mir_phase >= MirPhase::DropsLowered {
617+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
616618
self.fail(
617619
location,
618620
"`AscribeUserType` should have been removed after drop lowering phase",
619621
);
620622
}
621623
}
622624
StatementKind::FakeRead(..) => {
623-
if self.mir_phase >= MirPhase::DropsLowered {
625+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
624626
self.fail(
625627
location,
626628
"`FakeRead` should have been removed after drop lowering phase",
@@ -664,7 +666,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
664666
}
665667
}
666668
StatementKind::SetDiscriminant { place, .. } => {
667-
if self.mir_phase < MirPhase::Deaggregated {
669+
if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
668670
self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
669671
}
670672
let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind();
@@ -679,7 +681,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
679681
}
680682
}
681683
StatementKind::Deinit(..) => {
682-
if self.mir_phase < MirPhase::Deaggregated {
684+
if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
683685
self.fail(location, "`Deinit`is not allowed until deaggregation");
684686
}
685687
}
@@ -759,7 +761,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
759761
}
760762
}
761763
TerminatorKind::DropAndReplace { target, unwind, .. } => {
762-
if self.mir_phase >= MirPhase::DropsLowered {
764+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
763765
self.fail(
764766
location,
765767
"`DropAndReplace` should have been removed during drop elaboration",
@@ -830,7 +832,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
830832
if self.body.generator.is_none() {
831833
self.fail(location, "`Yield` cannot appear outside generator bodies");
832834
}
833-
if self.mir_phase >= MirPhase::GeneratorsLowered {
835+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
834836
self.fail(location, "`Yield` should have been replaced by generator lowering");
835837
}
836838
self.check_edge(location, *resume, EdgeKind::Normal);
@@ -839,7 +841,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
839841
}
840842
}
841843
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
842-
if self.mir_phase >= MirPhase::DropsLowered {
844+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
843845
self.fail(
844846
location,
845847
"`FalseEdge` should have been removed after drop elaboration",
@@ -849,7 +851,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
849851
self.check_edge(location, *imaginary_target, EdgeKind::Normal);
850852
}
851853
TerminatorKind::FalseUnwind { real_target, unwind } => {
852-
if self.mir_phase >= MirPhase::DropsLowered {
854+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
853855
self.fail(
854856
location,
855857
"`FalseUnwind` should have been removed after drop elaboration",
@@ -872,7 +874,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
872874
if self.body.generator.is_none() {
873875
self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
874876
}
875-
if self.mir_phase >= MirPhase::GeneratorsLowered {
877+
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
876878
self.fail(
877879
location,
878880
"`GeneratorDrop` should have been replaced by generator lowering",

compiler/rustc_middle/src/mir/mod.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,20 @@ pub trait MirPass<'tcx> {
128128

129129
impl MirPhase {
130130
/// Gets the index of the current MirPhase within the set of all `MirPhase`s.
131+
///
132+
/// FIXME(JakobDegen): Return a `(usize, usize)` instead.
131133
pub fn phase_index(&self) -> usize {
132-
*self as usize
134+
const BUILT_PHASE_COUNT: usize = 1;
135+
const ANALYSIS_PHASE_COUNT: usize = 2;
136+
match self {
137+
MirPhase::Built => 1,
138+
MirPhase::Analysis(analysis_phase) => {
139+
1 + BUILT_PHASE_COUNT + (*analysis_phase as usize)
140+
}
141+
MirPhase::Runtime(runtime_phase) => {
142+
1 + BUILT_PHASE_COUNT + ANALYSIS_PHASE_COUNT + (*runtime_phase as usize)
143+
}
144+
}
133145
}
134146
}
135147

compiler/rustc_middle/src/mir/syntax.rs

+89-54
Original file line numberDiff line numberDiff line change
@@ -23,75 +23,110 @@ use rustc_span::symbol::Symbol;
2323
use rustc_span::Span;
2424
use rustc_target::asm::InlineAsmRegOrRegClass;
2525

26-
/// The various "big phases" that MIR goes through.
26+
/// Represents the "flavors" of MIR.
2727
///
28-
/// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the
29-
/// dialects forbid certain variants or values in certain phases. The sections below summarize the
30-
/// changes, but do not document them thoroughly. The full documentation is found in the appropriate
31-
/// documentation for the thing the change is affecting.
28+
/// All flavors of MIR use the same data structure, but there are some important differences. These
29+
/// differences come in two forms: Dialects and phases.
3230
///
33-
/// Warning: ordering of variants is significant.
31+
/// Dialects represent a stronger distinction than phases. This is because the transitions between
32+
/// dialects are semantic changes, and therefore technically *lowerings* between distinct IRs. In
33+
/// other words, the same [`Body`](crate::mir::Body) might be well-formed for multiple dialects, but
34+
/// have different semantic meaning and different behavior at runtime.
35+
///
36+
/// Each dialect additionally has a number of phases. However, phase changes never involve semantic
37+
/// changes. If some MIR is well-formed both before and after a phase change, it is also guaranteed
38+
/// that it has the same semantic meaning. In this sense, phase changes can only add additional
39+
/// restrictions on what MIR is well-formed.
40+
///
41+
/// When adding phases, remember to update [`MirPhase::phase_index`].
3442
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
3543
#[derive(HashStable)]
3644
pub enum MirPhase {
37-
/// The dialect of MIR used during all phases before `DropsLowered` is the same. This is also
38-
/// the MIR that analysis such as borrowck uses.
39-
///
40-
/// One important thing to remember about the behavior of this section of MIR is that drop terminators
41-
/// (including drop and replace) are *conditional*. The elaborate drops pass will then replace each
42-
/// instance of a drop terminator with a nop, an unconditional drop, or a drop conditioned on a drop
43-
/// flag. Of course, this means that it is important that the drop elaboration can accurately recognize
44-
/// when things are initialized and when things are de-initialized. That means any code running on this
45-
/// version of MIR must be sure to produce output that drop elaboration can reason about. See the
46-
/// section on the drop terminatorss for more details.
47-
Built = 0,
48-
// FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query).
49-
// We used to have this for pre-miri MIR based const eval.
50-
Const = 1,
51-
/// This phase checks the MIR for promotable elements and takes them out of the main MIR body
52-
/// by creating a new MIR body per promoted element. After this phase (and thus the termination
53-
/// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir`
54-
/// query.
55-
ConstsPromoted = 2,
56-
/// After this projections may only contain deref projections as the first element.
57-
Derefered = 3,
58-
/// Beginning with this phase, the following variants are disallowed:
59-
/// * [`TerminatorKind::DropAndReplace`]
45+
/// The MIR that is generated by MIR building.
46+
///
47+
/// The only things that operate on this dialect are unsafeck, the various MIR lints, and const
48+
/// qualifs.
49+
///
50+
/// This has no distinct phases.
51+
Built,
52+
/// The MIR used for most analysis.
53+
///
54+
/// The only semantic change between analysis and built MIR is constant promotion. In built MIR,
55+
/// sequences of statements that would generally be subject to constant promotion are
56+
/// semantically constants, while in analysis MIR all constants are explicit.
57+
///
58+
/// The result of const promotion is available from the `mir_promoted` and `promoted_mir` queries.
59+
///
60+
/// This is the version of MIR used by borrowck and friends.
61+
Analysis(AnalysisPhase),
62+
/// The MIR used for CTFE, optimizations, and codegen.
63+
///
64+
/// The semantic changes that occur in the lowering from analysis to runtime MIR are as follows:
65+
///
66+
/// - Drops: In analysis MIR, `Drop` terminators represent *conditional* drops; roughly speaking,
67+
/// if dataflow analysis determines that the place being dropped is uninitialized, the drop will
68+
/// not be executed. The exact semantics of this aren't written down anywhere, which means they
69+
/// are essentially "what drop elaboration does." In runtime MIR, the drops are unconditional;
70+
/// when a `Drop` terminator is reached, if the type has drop glue that drop glue is always
71+
/// executed. This may be UB if the underlying place is not initialized.
72+
/// - Packed drops: Places might in general be misaligned - in most cases this is UB, the exception
73+
/// is fields of packed structs. In analysis MIR, `Drop(P)` for a `P` that might be misaligned
74+
/// for this reason implicitly moves `P` to a temporary before dropping. Runtime MIR has no such
75+
/// rules, and dropping a misaligned place is simply UB.
76+
/// - Unwinding: in analysis MIR, unwinding from a function which may not unwind aborts. In runtime
77+
/// MIR, this is UB.
78+
/// - Retags: If `-Zmir-emit-retag` is enabled, analysis MIR has "implicit" retags in the same way
79+
/// that Rust itself has them. Where exactly these are is generally subject to change, and so we
80+
/// don't document this here. Runtime MIR has all retags explicit.
81+
/// - Generator bodies: In analysis MIR, locals may actually be behind a pointer that user code has
82+
/// access to. This occurs in generator bodies. Such locals do not behave like other locals,
83+
/// because they eg may be aliased in surprising ways. Runtime MIR has no such special locals -
84+
/// all generator bodies are lowered and so all places that look like locals really are locals.
85+
/// - Const prop lints: The lint pass which reports eg `200_u8 + 200_u8` as an error is run as a
86+
/// part of analysis to runtime MIR lowering. This means that transformations which may supress
87+
/// such errors may not run on analysis MIR.
88+
Runtime(RuntimePhase),
89+
}
90+
91+
/// See [`MirPhase::Analysis`].
92+
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
93+
#[derive(HashStable)]
94+
pub enum AnalysisPhase {
95+
Initial = 0,
96+
/// Beginning in this phase, the following variants are disallowed:
6097
/// * [`TerminatorKind::FalseUnwind`]
6198
/// * [`TerminatorKind::FalseEdge`]
6299
/// * [`StatementKind::FakeRead`]
63100
/// * [`StatementKind::AscribeUserType`]
64101
/// * [`Rvalue::Ref`] with `BorrowKind::Shallow`
65102
///
66-
/// And the following variant is allowed:
67-
/// * [`StatementKind::Retag`]
68-
///
69-
/// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop`
70-
/// terminator means that the auto-generated drop glue will be invoked. Also, `Copy` operands
71-
/// are allowed for non-`Copy` types.
72-
DropsLowered = 4,
73-
/// Beginning with this phase, the following variant is disallowed:
103+
/// Furthermore, `Deref` projections must be the first projection within any place (if they
104+
/// appear at all)
105+
PostCleanup = 1,
106+
}
107+
108+
/// See [`MirPhase::Runtime`].
109+
#[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
110+
#[derive(HashStable)]
111+
pub enum RuntimePhase {
112+
/// In addition to the semantic changes, beginning with this phase, the following variants are
113+
/// disallowed:
114+
/// * [`TerminatorKind::DropAndReplace`]
115+
/// * [`TerminatorKind::Yield`]
116+
/// * [`TerminatorKind::GeneratorDrop`]
74117
/// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
75118
///
76-
/// And the following variant is allowed:
119+
/// And the following variants are allowed:
120+
/// * [`StatementKind::Retag`]
77121
/// * [`StatementKind::SetDiscriminant`]
78-
Deaggregated = 5,
79-
/// Before this phase, generators are in the "source code" form, featuring `yield` statements
80-
/// and such. With this phase change, they are transformed into a proper state machine. Running
81-
/// optimizations before this change can be potentially dangerous because the source code is to
82-
/// some extent a "lie." In particular, `yield` terminators effectively make the value of all
83-
/// locals visible to the caller. This means that dead store elimination before them, or code
84-
/// motion across them, is not correct in general. This is also exasperated by type checking
85-
/// having pre-computed a list of the types that it thinks are ok to be live across a yield
86-
/// point - this is necessary to decide eg whether autotraits are implemented. Introducing new
87-
/// types across a yield point will lead to ICEs becaues of this.
88-
///
89-
/// Beginning with this phase, the following variants are disallowed:
90-
/// * [`TerminatorKind::Yield`]
91-
/// * [`TerminatorKind::GeneratorDrop`]
122+
/// * [`StatementKind::Deinit`]
123+
///
124+
/// Furthermore, `Copy` operands are allowed for non-`Copy` types.
125+
Initial = 0,
126+
/// Beginning with this phase, the following variant is disallowed:
92127
/// * [`ProjectionElem::Deref`] of `Box`
93-
GeneratorsLowered = 6,
94-
Optimized = 7,
128+
PostCleanup = 1,
129+
Optimized = 2,
95130
}
96131

97132
///////////////////////////////////////////////////////////////////////////

compiler/rustc_mir_transform/src/deaggregator.rs

-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ use rustc_middle::ty::TyCtxt;
66
pub struct Deaggregator;
77

88
impl<'tcx> MirPass<'tcx> for Deaggregator {
9-
fn phase_change(&self) -> Option<MirPhase> {
10-
Some(MirPhase::Deaggregated)
11-
}
12-
139
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
1410
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
1511
for bb in basic_blocks {

compiler/rustc_mir_transform/src/deref_separator.rs

-1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,5 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
8282
impl<'tcx> MirPass<'tcx> for Derefer {
8383
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
8484
deref_finder(tcx, body);
85-
body.phase = MirPhase::Derefered;
8685
}
8786
}

0 commit comments

Comments
 (0)