Skip to content

Commit

Permalink
Validate the MIR of all optimizations in the mir-opt directory
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Aug 18, 2020
1 parent 4717cf2 commit de7c836
Show file tree
Hide file tree
Showing 20 changed files with 267 additions and 81 deletions.
26 changes: 23 additions & 3 deletions src/librustc_middle/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,35 @@ impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> {

/// The various "big phases" that MIR goes through.
///
/// 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 uphelpd *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,
// FIXME: 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,
Validated = 2,
DropElab = 3,
Optimized = 4,
/// 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`,
/// * `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,
Optimized = 5,
}

impl MirPhase {
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_middle/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ rustc_queries! {
desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.did.to_def_id()) }
}

query mir_validated(key: ty::WithOptConstParam<LocalDefId>) ->
query mir_promoted(key: ty::WithOptConstParam<LocalDefId>) ->
(
&'tcx Steal<mir::Body<'tcx>>,
&'tcx Steal<IndexVec<mir::Promoted, mir::Body<'tcx>>>
Expand Down Expand Up @@ -281,6 +281,8 @@ rustc_queries! {
cache_on_disk_if { key.is_local() }
}

/// The `DefId` is the `DefId` of the containing MIR body. Promoteds to not have their own
/// `DefId`.
query promoted_mir(key: DefId) -> &'tcx IndexVec<mir::Promoted, mir::Body<'tcx>> {
desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_middle/ty/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ rustc_query_append! { [define_queries!][<'tcx>] }
/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have
/// everything we need to re-run the query.
///
/// Take the `mir_validated` query as an example. Like many other queries, it
/// Take the `mir_promoted` query as an example. Like many other queries, it
/// just has a single parameter: the `DefId` of the item it will compute the
/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode`
/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode`
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ fn mir_borrowck<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
) -> &'tcx BorrowCheckResult<'tcx> {
let (input_body, promoted) = tcx.mir_validated(def);
let (input_body, promoted) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id()));

let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
Expand Down
25 changes: 24 additions & 1 deletion src/librustc_mir/interpret/intern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use super::validity::RefTracking;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_middle::mir::interpret::InterpResult;
use rustc_middle::ty::{self, query::TyCtxtAt, Ty};
use rustc_middle::ty::{self, layout::TyAndLayout, query::TyCtxtAt, Ty};
use rustc_target::abi::Size;

use rustc_ast::Mutability;

Expand Down Expand Up @@ -430,3 +431,25 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
}
}
}

impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// A helper function that allocates memory for the layout given and gives you access to mutate
/// it. Once your own mutation code is done, the backing `Allocation` is removed from the
/// current `Memory` and returned.
pub(crate) fn with_temp_alloc(
&mut self,
layout: TyAndLayout<'tcx>,
f: impl FnOnce(
&mut InterpCx<'mir, 'tcx, M>,
MPlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, ()>,
) -> InterpResult<'tcx, &'tcx Allocation> {
let dest = self.allocate(layout, MemoryKind::Stack);
f(self, dest)?;
let ptr = dest.ptr.assert_ptr();
assert_eq!(ptr.offset, Size::ZERO);
let mut alloc = self.memory.alloc_map.remove(&ptr.alloc_id).unwrap().1;
alloc.mutability = Mutability::Not;
Ok(self.tcx.intern_const_alloc(alloc))
}
}
59 changes: 35 additions & 24 deletions src/librustc_mir/transform/const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ use rustc_middle::mir::visit::{
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
};
use rustc_middle::mir::{
AggregateKind, AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local,
LocalDecl, LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope,
SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local, LocalDecl, LocalKind,
Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
Expand All @@ -28,9 +28,9 @@ use rustc_trait_selection::traits;

use crate::const_eval::ConstEvalErr;
use crate::interpret::{
self, compile_time_machine, truncate, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx,
LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy,
Pointer, ScalarMaybeUninit, StackPopCleanup,
self, compile_time_machine, truncate, AllocId, Allocation, ConstValue, Frame, ImmTy, Immediate,
InterpCx, LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand as InterpOperand,
PlaceTy, Pointer, ScalarMaybeUninit, StackPopCleanup,
};
use crate::transform::{MirPass, MirSource};

Expand Down Expand Up @@ -824,44 +824,55 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
));
}
Immediate::ScalarPair(
ScalarMaybeUninit::Scalar(one),
ScalarMaybeUninit::Scalar(two),
ScalarMaybeUninit::Scalar(_),
ScalarMaybeUninit::Scalar(_),
) => {
// Found a value represented as a pair. For now only do cont-prop if type of
// Rvalue is also a pair with two scalars. The more general case is more
// complicated to implement so we'll do it later.
// FIXME: implement the general case stated above ^.
let ty = &value.layout.ty.kind;
// Found a value represented as a pair. For now only do const-prop if the type
// of `rvalue` is also a tuple with two scalars.
// FIXME: enable the general case stated above ^.
let ty = &value.layout.ty;
// Only do it for tuples
if let ty::Tuple(substs) = ty {
if let ty::Tuple(substs) = ty.kind {
// Only do it if tuple is also a pair with two scalars
if substs.len() == 2 {
let opt_ty1_ty2 = self.use_ecx(|this| {
let alloc = self.use_ecx(|this| {
let ty1 = substs[0].expect_ty();
let ty2 = substs[1].expect_ty();
let ty_is_scalar = |ty| {
this.ecx.layout_of(ty).ok().map(|layout| layout.abi.is_scalar())
== Some(true)
};
if ty_is_scalar(ty1) && ty_is_scalar(ty2) {
Ok(Some((ty1, ty2)))
let alloc = this
.ecx
.with_temp_alloc(value.layout, |ecx, dest| {
ecx.write_immediate_to_mplace(*imm, dest)
})
.unwrap();
Ok(Some(alloc))
} else {
Ok(None)
}
});

if let Some(Some((ty1, ty2))) = opt_ty1_ty2 {
*rval = Rvalue::Aggregate(
Box::new(AggregateKind::Tuple),
vec![
self.operand_from_scalar(one, ty1, source_info.span),
self.operand_from_scalar(two, ty2, source_info.span),
],
);
if let Some(Some(alloc)) = alloc {
*rval = Rvalue::Use(Operand::Constant(Box::new(Constant {
span: source_info.span,
user_ty: None,
literal: self.ecx.tcx.mk_const(ty::Const {
ty,
val: ty::ConstKind::Value(ConstValue::ByRef {
alloc,
offset: Size::ZERO,
}),
}),
})));
}
}
}
}
// Scalars or scalar pairs that contain undef values are assumed to not have
// successfully evaluated and are thus not propagated.
_ => {}
}
}
Expand Down
37 changes: 25 additions & 12 deletions src/librustc_mir/transform/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ use crate::transform::no_landing_pads::no_landing_pads;
use crate::transform::simplify;
use crate::transform::{MirPass, MirSource};
use crate::util::dump_mir;
use crate::util::expand_aggregate;
use crate::util::storage;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
Expand All @@ -66,7 +67,7 @@ use rustc_index::bit_set::{BitMatrix, BitSet};
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::GeneratorSubsts;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_target::abi::VariantIdx;
Expand Down Expand Up @@ -236,10 +237,28 @@ struct TransformVisitor<'tcx> {
}

impl TransformVisitor<'tcx> {
// Make a GeneratorState rvalue
fn make_state(&self, idx: VariantIdx, val: Operand<'tcx>) -> Rvalue<'tcx> {
let adt = AggregateKind::Adt(self.state_adt_ref, idx, self.state_substs, None, None);
Rvalue::Aggregate(box adt, vec![val])
// Make a GeneratorState variant assignment. `core::ops::GeneratorState` only has single
// element tuple variants, so we can just write to the downcasted first field and then set the
// discriminant to the appropriate variant.
fn make_state(
&self,
idx: VariantIdx,
val: Operand<'tcx>,
source_info: SourceInfo,
) -> impl Iterator<Item = Statement<'tcx>> {
let kind = AggregateKind::Adt(self.state_adt_ref, idx, self.state_substs, None, None);
assert_eq!(self.state_adt_ref.variants[idx].fields.len(), 1);
let ty = self
.tcx
.type_of(self.state_adt_ref.variants[idx].fields[0].did)
.subst(self.tcx, self.state_substs);
expand_aggregate(
Place::return_place(),
std::iter::once((val, ty)),
kind,
source_info,
self.tcx,
)
}

// Create a Place referencing a generator struct field
Expand Down Expand Up @@ -325,13 +344,7 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> {
if let Some((state_idx, resume, v, drop)) = ret_val {
let source_info = data.terminator().source_info;
// We must assign the value first in case it gets declared dead below
data.statements.push(Statement {
source_info,
kind: StatementKind::Assign(box (
Place::return_place(),
self.make_state(state_idx, v),
)),
});
data.statements.extend(self.make_state(state_idx, v, source_info));
let state = if let Some((resume, resume_arg)) = resume {
// Yield
let state = 3 + self.suspension_points.len();
Expand Down
Loading

0 comments on commit de7c836

Please sign in to comment.