use rustc_data_structures::bitslice::bits_to_string;
use rustc_data_structures::indexed_set::{IdxSet};
use rustc_data_structures::indexed_vec::Idx;
+use rustc_mir::util as mir_util;
use dot;
use dot::IntoCow;
}
Ok(())
}
- ::rustc_mir::graphviz::write_node_label(
+ mir_util::write_graphviz_node_label(
*n, self.mbcx.mir(), &mut v, 4,
|w| {
let flow = self.mbcx.flow_state();
use rustc_data_structures::bitslice::{BitwiseOperator};
use rustc_data_structures::indexed_set::{IdxSet};
use rustc_data_structures::indexed_vec::Idx;
+use rustc_mir::util::elaborate_drops::DropFlagState;
use super::super::gather_moves::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex};
use super::super::MoveDataParamEnv;
-use super::super::DropFlagState;
use super::super::drop_flag_effects_for_function_entry;
use super::super::drop_flag_effects_for_location;
use super::super::on_lookup_result_bits;
use super::dataflow::{DataflowResults};
use super::{drop_flag_effects_for_location, on_all_children_bits};
use super::on_lookup_result_bits;
-use super::{DropFlagState, MoveDataParamEnv};
-use super::patch::MirPatch;
-use rustc::ty::{self, Ty, TyCtxt};
-use rustc::ty::subst::{Kind, Subst, Substs};
-use rustc::ty::util::IntTypeExt;
+use super::MoveDataParamEnv;
+use rustc::ty::{self, TyCtxt};
use rustc::mir::*;
use rustc::mir::transform::{Pass, MirPass, MirSource};
use rustc::middle::const_val::ConstVal;
-use rustc::middle::lang_items;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_set::IdxSetBuf;
use rustc_data_structures::indexed_vec::Idx;
+use rustc_mir::util::patch::MirPatch;
+use rustc_mir::util::elaborate_drops::{DropFlagState, elaborate_drop};
+use rustc_mir::util::elaborate_drops::{DropElaborator, DropStyle, DropFlagMode};
use syntax_pos::Span;
use std::fmt;
-use std::iter;
use std::u32;
pub struct ElaborateDrops;
}
}
-impl fmt::Debug for InitializationData {
+struct Elaborator<'a, 'b: 'a, 'tcx: 'b> {
+ init_data: &'a InitializationData,
+ ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>,
+}
+
+impl<'a, 'b, 'tcx> fmt::Debug for Elaborator<'a, 'b, 'tcx> {
fn fmt(&self, _f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
Ok(())
}
}
+impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> {
+ type Path = MovePathIndex;
+
+ fn patch(&mut self) -> &mut MirPatch<'tcx> {
+ &mut self.ctxt.patch
+ }
+
+ fn mir(&self) -> &'a Mir<'tcx> {
+ self.ctxt.mir
+ }
+
+ fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> {
+ self.ctxt.tcx
+ }
+
+ fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx> {
+ self.ctxt.param_env()
+ }
+
+ fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle {
+ let ((maybe_live, maybe_dead), multipart) = match mode {
+ DropFlagMode::Shallow => (self.init_data.state(path), false),
+ DropFlagMode::Deep => {
+ let mut some_live = false;
+ let mut some_dead = false;
+ let mut children_count = 0;
+ on_all_children_bits(
+ self.tcx(), self.mir(), self.ctxt.move_data(),
+ path, |child| {
+ if self.ctxt.path_needs_drop(child) {
+ let (live, dead) = self.init_data.state(child);
+ debug!("elaborate_drop: state({:?}) = {:?}",
+ child, (live, dead));
+ some_live |= live;
+ some_dead |= dead;
+ children_count += 1;
+ }
+ });
+ ((some_live, some_dead), children_count != 1)
+ }
+ };
+ match (maybe_live, maybe_dead, multipart) {
+ (false, _, _) => DropStyle::Dead,
+ (true, false, _) => DropStyle::Static,
+ (true, true, false) => DropStyle::Conditional,
+ (true, true, true) => DropStyle::Open,
+ }
+ }
+
+ fn clear_drop_flag(&mut self, loc: Location, path: Self::Path, mode: DropFlagMode) {
+ match mode {
+ DropFlagMode::Shallow => {
+ self.ctxt.set_drop_flag(loc, path, DropFlagState::Absent);
+ }
+ DropFlagMode::Deep => {
+ on_all_children_bits(
+ self.tcx(), self.mir(), self.ctxt.move_data(), path,
+ |child| self.ctxt.set_drop_flag(loc, child, DropFlagState::Absent)
+ );
+ }
+ }
+ }
+
+ fn field_subpath(&self, path: Self::Path, field: Field) -> Option<Self::Path> {
+ super::move_path_children_matching(self.ctxt.move_data(), path, |p| {
+ match p {
+ &Projection {
+ elem: ProjectionElem::Field(idx, _), ..
+ } => idx == field,
+ _ => false
+ }
+ })
+ }
+
+ fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path> {
+ super::move_path_children_matching(self.ctxt.move_data(), path, |p| {
+ match p {
+ &Projection { elem: ProjectionElem::Deref, .. } => true,
+ _ => false
+ }
+ })
+ }
+
+ fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option<Self::Path> {
+ super::move_path_children_matching(self.ctxt.move_data(), path, |p| {
+ match p {
+ &Projection {
+ elem: ProjectionElem::Downcast(_, idx), ..
+ } => idx == variant,
+ _ => false
+ }
+ })
+ }
+
+ fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>> {
+ self.ctxt.drop_flag(path).map(Operand::Consume)
+ }
+}
+
struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &'a Mir<'tcx>,
patch: MirPatch<'tcx>,
}
-#[derive(Copy, Clone, Debug)]
-struct DropCtxt<'a, 'tcx: 'a> {
- source_info: SourceInfo,
- is_cleanup: bool,
-
- init_data: &'a InitializationData,
-
- lvalue: &'a Lvalue<'tcx>,
- path: MovePathIndex,
- succ: BasicBlock,
- unwind: Option<BasicBlock>
-}
-
impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
fn move_data(&self) -> &'b MoveData<'tcx> { &self.env.move_data }
fn param_env(&self) -> &'b ty::ParameterEnvironment<'tcx> {
let init_data = self.initialization_data_at(loc);
match self.move_data().rev_lookup.find(location) {
LookupResult::Exact(path) => {
- self.elaborate_drop(&DropCtxt {
- source_info: terminator.source_info,
- is_cleanup: data.is_cleanup,
- init_data: &init_data,
- lvalue: location,
- path: path,
- succ: target,
- unwind: if data.is_cleanup {
+ elaborate_drop(
+ &mut Elaborator {
+ init_data: &init_data,
+ ctxt: self
+ },
+ terminator.source_info,
+ data.is_cleanup,
+ location,
+ path,
+ target,
+ if data.is_cleanup {
None
} else {
Some(Option::unwrap_or(unwind, resume_block))
- }
- }, bb);
+ },
+ bb)
}
LookupResult::Parent(..) => {
span_bug!(terminator.source_info.span,
debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path);
let init_data = self.initialization_data_at(loc);
- self.elaborate_drop(&DropCtxt {
- source_info: terminator.source_info,
- is_cleanup: data.is_cleanup,
- init_data: &init_data,
- lvalue: location,
- path: path,
- succ: target,
- unwind: Some(unwind)
- }, bb);
+ elaborate_drop(
+ &mut Elaborator {
+ init_data: &init_data,
+ ctxt: self
+ },
+ terminator.source_info,
+ data.is_cleanup,
+ location,
+ path,
+ target,
+ Some(unwind),
+ bb);
on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| {
self.set_drop_flag(Location { block: target, statement_index: 0 },
child, DropFlagState::Present);
}
}
- /// This elaborates a single drop instruction, located at `bb`, and
- /// patches over it.
- ///
- /// The elaborated drop checks the drop flags to only drop what
- /// is initialized.
- ///
- /// In addition, the relevant drop flags also need to be cleared
- /// to avoid double-drops. However, in the middle of a complex
- /// drop, one must avoid clearing some of the flags before they
- /// are read, as that would cause a memory leak.
- ///
- /// In particular, when dropping an ADT, multiple fields may be
- /// joined together under the `rest` subpath. They are all controlled
- /// by the primary drop flag, but only the last rest-field dropped
- /// should clear it (and it must also not clear anything else).
- ///
- /// FIXME: I think we should just control the flags externally
- /// and then we do not need this machinery.
- fn elaborate_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, bb: BasicBlock) {
- debug!("elaborate_drop({:?})", c);
-
- let mut some_live = false;
- let mut some_dead = false;
- let mut children_count = 0;
- on_all_children_bits(
- self.tcx, self.mir, self.move_data(),
- c.path, |child| {
- if self.path_needs_drop(child) {
- let (live, dead) = c.init_data.state(child);
- debug!("elaborate_drop: state({:?}) = {:?}",
- child, (live, dead));
- some_live |= live;
- some_dead |= dead;
- children_count += 1;
- }
- });
-
- debug!("elaborate_drop({:?}): live - {:?}", c,
- (some_live, some_dead));
- match (some_live, some_dead) {
- (false, false) | (false, true) => {
- // dead drop - patch it out
- self.patch.patch_terminator(bb, TerminatorKind::Goto {
- target: c.succ
- });
- }
- (true, false) => {
- // static drop - just set the flag
- self.patch.patch_terminator(bb, TerminatorKind::Drop {
- location: c.lvalue.clone(),
- target: c.succ,
- unwind: c.unwind
- });
- self.drop_flags_for_drop(c, bb);
- }
- (true, true) => {
- // dynamic drop
- let drop_bb = if children_count == 1 || self.must_complete_drop(c) {
- self.conditional_drop(c)
- } else {
- self.open_drop(c)
- };
- self.patch.patch_terminator(bb, TerminatorKind::Goto {
- target: drop_bb
- });
- }
- }
- }
-
- /// Return the lvalue and move path for each field of `variant`,
- /// (the move path is `None` if the field is a rest field).
- fn move_paths_for_fields(&self,
- base_lv: &Lvalue<'tcx>,
- variant_path: MovePathIndex,
- variant: &'tcx ty::VariantDef,
- substs: &'tcx Substs<'tcx>)
- -> Vec<(Lvalue<'tcx>, Option<MovePathIndex>)>
- {
- variant.fields.iter().enumerate().map(|(i, f)| {
- let subpath =
- super::move_path_children_matching(self.move_data(), variant_path, |p| {
- match p {
- &Projection {
- elem: ProjectionElem::Field(idx, _), ..
- } => idx.index() == i,
- _ => false
- }
- });
-
- let field_ty =
- self.tcx.normalize_associated_type_in_env(
- &f.ty(self.tcx, substs),
- self.param_env()
- );
- (base_lv.clone().field(Field::new(i), field_ty), subpath)
- }).collect()
- }
-
- /// Create one-half of the drop ladder for a list of fields, and return
- /// the list of steps in it in reverse order.
- ///
- /// `unwind_ladder` is such a list of steps in reverse order,
- /// which is called instead of the next step if the drop unwinds
- /// (the first field is never reached). If it is `None`, all
- /// unwind targets are left blank.
- fn drop_halfladder<'a>(&mut self,
- c: &DropCtxt<'a, 'tcx>,
- unwind_ladder: Option<Vec<BasicBlock>>,
- succ: BasicBlock,
- fields: &[(Lvalue<'tcx>, Option<MovePathIndex>)],
- is_cleanup: bool)
- -> Vec<BasicBlock>
- {
- let mut unwind_succ = if is_cleanup {
- None
- } else {
- c.unwind
- };
-
- let mut succ = self.new_block(
- c, c.is_cleanup, TerminatorKind::Goto { target: succ }
- );
-
- // Always clear the "master" drop flag at the bottom of the
- // ladder. This is needed because the "master" drop flag
- // protects the ADT's discriminant, which is invalidated
- // after the ADT is dropped.
- self.set_drop_flag(
- Location { block: succ, statement_index: 0 },
- c.path,
- DropFlagState::Absent
- );
-
- fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| {
- succ = if let Some(path) = path {
- debug!("drop_ladder: for std field {} ({:?})", i, lv);
-
- self.elaborated_drop_block(&DropCtxt {
- source_info: c.source_info,
- is_cleanup: is_cleanup,
- init_data: c.init_data,
- lvalue: lv,
- path: path,
- succ: succ,
- unwind: unwind_succ,
- })
- } else {
- debug!("drop_ladder: for rest field {} ({:?})", i, lv);
-
- self.complete_drop(&DropCtxt {
- source_info: c.source_info,
- is_cleanup: is_cleanup,
- init_data: c.init_data,
- lvalue: lv,
- path: c.path,
- succ: succ,
- unwind: unwind_succ,
- }, false)
- };
-
- unwind_succ = unwind_ladder.as_ref().map(|p| p[i]);
- succ
- }).collect()
- }
-
- /// Create a full drop ladder, consisting of 2 connected half-drop-ladders
- ///
- /// For example, with 3 fields, the drop ladder is
- ///
- /// .d0:
- /// ELAB(drop location.0 [target=.d1, unwind=.c1])
- /// .d1:
- /// ELAB(drop location.1 [target=.d2, unwind=.c2])
- /// .d2:
- /// ELAB(drop location.2 [target=`c.succ`, unwind=`c.unwind`])
- /// .c1:
- /// ELAB(drop location.1 [target=.c2])
- /// .c2:
- /// ELAB(drop location.2 [target=`c.unwind])
- fn drop_ladder<'a>(&mut self,
- c: &DropCtxt<'a, 'tcx>,
- fields: Vec<(Lvalue<'tcx>, Option<MovePathIndex>)>)
- -> BasicBlock
- {
- debug!("drop_ladder({:?}, {:?})", c, fields);
-
- let mut fields = fields;
- fields.retain(|&(ref lvalue, _)| {
- let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
- self.tcx.type_needs_drop_given_env(ty, self.param_env())
- });
-
- debug!("drop_ladder - fields needing drop: {:?}", fields);
-
- let unwind_ladder = if c.is_cleanup {
- None
- } else {
- Some(self.drop_halfladder(c, None, c.unwind.unwrap(), &fields, true))
- };
-
- self.drop_halfladder(c, unwind_ladder, c.succ, &fields, c.is_cleanup)
- .last().cloned().unwrap_or(c.succ)
- }
-
- fn open_drop_for_tuple<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, tys: &[Ty<'tcx>])
- -> BasicBlock
- {
- debug!("open_drop_for_tuple({:?}, {:?})", c, tys);
-
- let fields = tys.iter().enumerate().map(|(i, &ty)| {
- (c.lvalue.clone().field(Field::new(i), ty),
- super::move_path_children_matching(
- self.move_data(), c.path, |proj| match proj {
- &Projection {
- elem: ProjectionElem::Field(f, _), ..
- } => f.index() == i,
- _ => false
- }
- ))
- }).collect();
-
- self.drop_ladder(c, fields)
- }
-
- fn open_drop_for_box<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, ty: Ty<'tcx>)
- -> BasicBlock
- {
- debug!("open_drop_for_box({:?}, {:?})", c, ty);
-
- let interior_path = super::move_path_children_matching(
- self.move_data(), c.path, |proj| match proj {
- &Projection { elem: ProjectionElem::Deref, .. } => true,
- _ => false
- }).unwrap();
-
- let interior = c.lvalue.clone().deref();
- let inner_c = DropCtxt {
- lvalue: &interior,
- unwind: c.unwind.map(|u| {
- self.box_free_block(c, ty, u, true)
- }),
- succ: self.box_free_block(c, ty, c.succ, c.is_cleanup),
- path: interior_path,
- ..*c
- };
-
- self.elaborated_drop_block(&inner_c)
- }
-
- fn open_drop_for_adt<'a>(&mut self, c: &DropCtxt<'a, 'tcx>,
- adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>)
- -> BasicBlock {
- debug!("open_drop_for_adt({:?}, {:?}, {:?})", c, adt, substs);
-
- match adt.variants.len() {
- 1 => {
- let fields = self.move_paths_for_fields(
- c.lvalue,
- c.path,
- &adt.variants[0],
- substs
- );
- self.drop_ladder(c, fields)
- }
- _ => {
- let mut values = Vec::with_capacity(adt.variants.len());
- let mut blocks = Vec::with_capacity(adt.variants.len());
- let mut otherwise = None;
- for (variant_index, discr) in adt.discriminants(self.tcx).enumerate() {
- let subpath = super::move_path_children_matching(
- self.move_data(), c.path, |proj| match proj {
- &Projection {
- elem: ProjectionElem::Downcast(_, idx), ..
- } => idx == variant_index,
- _ => false
- });
- if let Some(variant_path) = subpath {
- let base_lv = c.lvalue.clone().elem(
- ProjectionElem::Downcast(adt, variant_index)
- );
- let fields = self.move_paths_for_fields(
- &base_lv,
- variant_path,
- &adt.variants[variant_index],
- substs);
- values.push(discr);
- blocks.push(self.drop_ladder(c, fields));
- } else {
- // variant not found - drop the entire enum
- if let None = otherwise {
- otherwise = Some(self.complete_drop(c, true));
- }
- }
- }
- if let Some(block) = otherwise {
- blocks.push(block);
- } else {
- values.pop();
- }
- // If there are multiple variants, then if something
- // is present within the enum the discriminant, tracked
- // by the rest path, must be initialized.
- //
- // Additionally, we do not want to switch on the
- // discriminant after it is free-ed, because that
- // way lies only trouble.
- let discr_ty = adt.repr.discr_type().to_ty(self.tcx);
- let discr = Lvalue::Local(self.patch.new_temp(discr_ty));
- let switch_block = self.patch.new_block(BasicBlockData {
- statements: vec![
- Statement {
- source_info: c.source_info,
- kind: StatementKind::Assign(discr.clone(),
- Rvalue::Discriminant(c.lvalue.clone()))
- }
- ],
- terminator: Some(Terminator {
- source_info: c.source_info,
- kind: TerminatorKind::SwitchInt {
- discr: Operand::Consume(discr),
- switch_ty: discr_ty,
- values: From::from(values),
- targets: blocks,
- }
- }),
- is_cleanup: c.is_cleanup,
- });
- self.drop_flag_test_block(c, switch_block)
- }
- }
- }
-
- /// The slow-path - create an "open", elaborated drop for a type
- /// which is moved-out-of only partially, and patch `bb` to a jump
- /// to it. This must not be called on ADTs with a destructor,
- /// as these can't be moved-out-of, except for `Box<T>`, which is
- /// special-cased.
- ///
- /// This creates a "drop ladder" that drops the needed fields of the
- /// ADT, both in the success case or if one of the destructors fail.
- fn open_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
- let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
- match ty.sty {
- ty::TyClosure(def_id, substs) => {
- let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx).collect();
- self.open_drop_for_tuple(c, &tys)
- }
- ty::TyTuple(tys, _) => {
- self.open_drop_for_tuple(c, tys)
- }
- ty::TyAdt(def, _) if def.is_box() => {
- self.open_drop_for_box(c, ty.boxed_ty())
- }
- ty::TyAdt(def, substs) => {
- self.open_drop_for_adt(c, def, substs)
- }
- _ => bug!("open drop from non-ADT `{:?}`", ty)
- }
- }
-
- /// Return a basic block that drop an lvalue using the context
- /// and path in `c`. If `update_drop_flag` is true, also
- /// clear `c`.
- ///
- /// if FLAG(c.path)
- /// if(update_drop_flag) FLAG(c.path) = false
- /// drop(c.lv)
- fn complete_drop<'a>(
- &mut self,
- c: &DropCtxt<'a, 'tcx>,
- update_drop_flag: bool)
- -> BasicBlock
- {
- debug!("complete_drop({:?},{:?})", c, update_drop_flag);
-
- let drop_block = self.drop_block(c);
- if update_drop_flag {
- self.set_drop_flag(
- Location { block: drop_block, statement_index: 0 },
- c.path,
- DropFlagState::Absent
- );
- }
-
- self.drop_flag_test_block(c, drop_block)
- }
-
- /// Create a simple conditional drop.
- ///
- /// if FLAG(c.lv)
- /// FLAGS(c.lv) = false
- /// drop(c.lv)
- fn conditional_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>)
- -> BasicBlock
- {
- debug!("conditional_drop({:?})", c);
- let drop_bb = self.drop_block(c);
- self.drop_flags_for_drop(c, drop_bb);
-
- self.drop_flag_test_block(c, drop_bb)
- }
-
- fn new_block<'a>(&mut self,
- c: &DropCtxt<'a, 'tcx>,
- is_cleanup: bool,
- k: TerminatorKind<'tcx>)
- -> BasicBlock
- {
- self.patch.new_block(BasicBlockData {
- statements: vec![],
- terminator: Some(Terminator {
- source_info: c.source_info, kind: k
- }),
- is_cleanup: is_cleanup
- })
- }
-
- fn elaborated_drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
- debug!("elaborated_drop_block({:?})", c);
- let blk = self.drop_block(c);
- self.elaborate_drop(c, blk);
- blk
- }
-
- fn drop_flag_test_block<'a>(&mut self,
- c: &DropCtxt<'a, 'tcx>,
- on_set: BasicBlock)
- -> BasicBlock {
- self.drop_flag_test_block_with_succ(c, c.is_cleanup, on_set, c.succ)
- }
-
- fn drop_flag_test_block_with_succ<'a>(&mut self,
- c: &DropCtxt<'a, 'tcx>,
- is_cleanup: bool,
- on_set: BasicBlock,
- on_unset: BasicBlock)
- -> BasicBlock
- {
- let (maybe_live, maybe_dead) = c.init_data.state(c.path);
- debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}",
- c, is_cleanup, on_set, (maybe_live, maybe_dead));
-
- match (maybe_live, maybe_dead) {
- (false, _) => on_unset,
- (true, false) => on_set,
- (true, true) => {
- let flag = self.drop_flag(c.path).unwrap();
- let term = TerminatorKind::if_(self.tcx, Operand::Consume(flag), on_set, on_unset);
- self.new_block(c, is_cleanup, term)
- }
- }
- }
-
- fn drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
- self.new_block(c, c.is_cleanup, TerminatorKind::Drop {
- location: c.lvalue.clone(),
- target: c.succ,
- unwind: c.unwind
- })
- }
-
- fn box_free_block<'a>(
- &mut self,
- c: &DropCtxt<'a, 'tcx>,
- ty: Ty<'tcx>,
- target: BasicBlock,
- is_cleanup: bool
- ) -> BasicBlock {
- let block = self.unelaborated_free_block(c, ty, target, is_cleanup);
- self.drop_flag_test_block_with_succ(c, is_cleanup, block, target)
- }
-
- fn unelaborated_free_block<'a>(
- &mut self,
- c: &DropCtxt<'a, 'tcx>,
- ty: Ty<'tcx>,
- target: BasicBlock,
- is_cleanup: bool
- ) -> BasicBlock {
- let mut statements = vec![];
- if let Some(&flag) = self.drop_flags.get(&c.path) {
- statements.push(Statement {
- source_info: c.source_info,
- kind: StatementKind::Assign(
- Lvalue::Local(flag),
- self.constant_bool(c.source_info.span, false)
- )
- });
- }
-
- let tcx = self.tcx;
- let unit_temp = Lvalue::Local(self.patch.new_temp(tcx.mk_nil()));
- let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem);
- let substs = tcx.mk_substs(iter::once(Kind::from(ty)));
- let fty = tcx.item_type(free_func).subst(tcx, substs);
-
- self.patch.new_block(BasicBlockData {
- statements: statements,
- terminator: Some(Terminator {
- source_info: c.source_info, kind: TerminatorKind::Call {
- func: Operand::Constant(Constant {
- span: c.source_info.span,
- ty: fty,
- literal: Literal::Item {
- def_id: free_func,
- substs: substs
- }
- }),
- args: vec![Operand::Consume(c.lvalue.clone())],
- destination: Some((unit_temp, target)),
- cleanup: None
- }
- }),
- is_cleanup: is_cleanup
- })
- }
-
- fn must_complete_drop<'a>(&self, c: &DropCtxt<'a, 'tcx>) -> bool {
- // if we have a destuctor, we must *not* split the drop.
-
- // dataflow can create unneeded children in some cases
- // - be sure to ignore them.
-
- let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
-
- match ty.sty {
- ty::TyAdt(def, _) => {
- if def.has_dtor(self.tcx) && !def.is_box() {
- self.tcx.sess.span_warn(
- c.source_info.span,
- &format!("dataflow bug??? moving out of type with dtor {:?}",
- c));
- true
- } else {
- false
- }
- }
- _ => false
- }
- }
-
fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> {
Rvalue::Use(Operand::Constant(Constant {
span: span,
}
}
}
-
- fn drop_flags_for_drop<'a>(&mut self,
- c: &DropCtxt<'a, 'tcx>,
- bb: BasicBlock)
- {
- let loc = self.patch.terminator_loc(self.mir, bb);
- on_all_children_bits(
- self.tcx, self.mir, self.move_data(), c.path,
- |child| self.set_drop_flag(loc, child, DropFlagState::Absent)
- );
- }
}
use rustc::mir::{self, BasicBlock, BasicBlockData, Mir, Statement, Terminator, Location};
use rustc::session::Session;
use rustc::ty::{self, TyCtxt};
+use rustc_mir::util::elaborate_drops::DropFlagState;
mod abs_domain;
pub mod elaborate_drops;
mod dataflow;
mod gather_moves;
-mod patch;
// mod graphviz;
use self::dataflow::{BitDenotation};
}
}
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-enum DropFlagState {
- Present, // i.e. initialized
- Absent, // i.e. deinitialized or "moved"
-}
-
-impl DropFlagState {
- fn value(self) -> bool {
- match self {
- DropFlagState::Present => true,
- DropFlagState::Absent => false
- }
- }
-}
-
fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
path: MovePathIndex,
mut cond: F)
+++ /dev/null
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use rustc::ty::Ty;
-use rustc::mir::*;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
-
-/// This struct represents a patch to MIR, which can add
-/// new statements and basic blocks and patch over block
-/// terminators.
-pub struct MirPatch<'tcx> {
- patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>,
- new_blocks: Vec<BasicBlockData<'tcx>>,
- new_statements: Vec<(Location, StatementKind<'tcx>)>,
- new_locals: Vec<LocalDecl<'tcx>>,
- resume_block: BasicBlock,
- next_local: usize,
-}
-
-impl<'tcx> MirPatch<'tcx> {
- pub fn new(mir: &Mir<'tcx>) -> Self {
- let mut result = MirPatch {
- patch_map: IndexVec::from_elem(None, mir.basic_blocks()),
- new_blocks: vec![],
- new_statements: vec![],
- new_locals: vec![],
- next_local: mir.local_decls.len(),
- resume_block: START_BLOCK
- };
-
- // make sure the MIR we create has a resume block. It is
- // completely legal to convert jumps to the resume block
- // to jumps to None, but we occasionally have to add
- // instructions just before that.
-
- let mut resume_block = None;
- let mut resume_stmt_block = None;
- for (bb, block) in mir.basic_blocks().iter_enumerated() {
- if let TerminatorKind::Resume = block.terminator().kind {
- if block.statements.len() > 0 {
- resume_stmt_block = Some(bb);
- } else {
- resume_block = Some(bb);
- }
- break
- }
- }
- let resume_block = resume_block.unwrap_or_else(|| {
- result.new_block(BasicBlockData {
- statements: vec![],
- terminator: Some(Terminator {
- source_info: SourceInfo {
- span: mir.span,
- scope: ARGUMENT_VISIBILITY_SCOPE
- },
- kind: TerminatorKind::Resume
- }),
- is_cleanup: true
- })});
- result.resume_block = resume_block;
- if let Some(resume_stmt_block) = resume_stmt_block {
- result.patch_terminator(resume_stmt_block, TerminatorKind::Goto {
- target: resume_block
- });
- }
- result
- }
-
- pub fn resume_block(&self) -> BasicBlock {
- self.resume_block
- }
-
- pub fn is_patched(&self, bb: BasicBlock) -> bool {
- self.patch_map[bb].is_some()
- }
-
- pub fn terminator_loc(&self, mir: &Mir<'tcx>, bb: BasicBlock) -> Location {
- let offset = match bb.index().checked_sub(mir.basic_blocks().len()) {
- Some(index) => self.new_blocks[index].statements.len(),
- None => mir[bb].statements.len()
- };
- Location {
- block: bb,
- statement_index: offset
- }
- }
-
- pub fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
- let index = self.next_local;
- self.next_local += 1;
- self.new_locals.push(LocalDecl::new_temp(ty));
- Local::new(index as usize)
- }
-
- pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
- let block = BasicBlock::new(self.patch_map.len());
- debug!("MirPatch: new_block: {:?}: {:?}", block, data);
- self.new_blocks.push(data);
- self.patch_map.push(None);
- block
- }
-
- pub fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) {
- assert!(self.patch_map[block].is_none());
- debug!("MirPatch: patch_terminator({:?}, {:?})", block, new);
- self.patch_map[block] = Some(new);
- }
-
- pub fn add_statement(&mut self, loc: Location, stmt: StatementKind<'tcx>) {
- debug!("MirPatch: add_statement({:?}, {:?})", loc, stmt);
- self.new_statements.push((loc, stmt));
- }
-
- pub fn add_assign(&mut self, loc: Location, lv: Lvalue<'tcx>, rv: Rvalue<'tcx>) {
- self.add_statement(loc, StatementKind::Assign(lv, rv));
- }
-
- pub fn apply(self, mir: &mut Mir<'tcx>) {
- debug!("MirPatch: {:?} new temps, starting from index {}: {:?}",
- self.new_locals.len(), mir.local_decls.len(), self.new_locals);
- debug!("MirPatch: {} new blocks, starting from index {}",
- self.new_blocks.len(), mir.basic_blocks().len());
- mir.basic_blocks_mut().extend(self.new_blocks);
- mir.local_decls.extend(self.new_locals);
- for (src, patch) in self.patch_map.into_iter_enumerated() {
- if let Some(patch) = patch {
- debug!("MirPatch: patching block {:?}", src);
- mir[src].terminator_mut().kind = patch;
- }
- }
-
- let mut new_statements = self.new_statements;
- new_statements.sort_by(|u,v| u.0.cmp(&v.0));
-
- let mut delta = 0;
- let mut last_bb = START_BLOCK;
- for (mut loc, stmt) in new_statements {
- if loc.block != last_bb {
- delta = 0;
- last_bb = loc.block;
- }
- debug!("MirPatch: adding statement {:?} at loc {:?}+{}",
- stmt, loc, delta);
- loc.statement_index += delta;
- let source_info = Self::source_info_for_index(
- &mir[loc.block], loc
- );
- mir[loc.block].statements.insert(
- loc.statement_index, Statement {
- source_info: source_info,
- kind: stmt
- });
- delta += 1;
- }
- }
-
- pub fn source_info_for_index(data: &BasicBlockData, loc: Location) -> SourceInfo {
- match data.statements.get(loc.statement_index) {
- Some(stmt) => stmt.source_info,
- None => data.terminator().source_info
- }
- }
-
- pub fn source_info_for_location(&self, mir: &Mir, loc: Location) -> SourceInfo {
- let data = match loc.block.index().checked_sub(mir.basic_blocks().len()) {
- Some(new) => &self.new_blocks[new],
- None => &mir[loc.block]
- };
- Self::source_info_for_index(data, loc)
- }
-}
use rustc_borrowck as borrowck;
use rustc_borrowck::graphviz as borrowck_dot;
-use rustc_mir::pretty::write_mir_pretty;
-use rustc_mir::graphviz::write_mir_graphviz;
+use rustc_mir::util::{write_mir_pretty, write_mir_graphviz};
use syntax::ast::{self, BlockCheckMode};
use syntax::fold::{self, Folder};
+++ /dev/null
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Def-use analysis.
-
-use rustc::mir::{Local, Location, Lvalue, Mir};
-use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
-use rustc_data_structures::indexed_vec::IndexVec;
-use std::marker::PhantomData;
-use std::mem;
-
-pub struct DefUseAnalysis<'tcx> {
- info: IndexVec<Local, Info<'tcx>>,
-}
-
-#[derive(Clone)]
-pub struct Info<'tcx> {
- pub defs_and_uses: Vec<Use<'tcx>>,
-}
-
-#[derive(Clone)]
-pub struct Use<'tcx> {
- pub context: LvalueContext<'tcx>,
- pub location: Location,
-}
-
-impl<'tcx> DefUseAnalysis<'tcx> {
- pub fn new(mir: &Mir<'tcx>) -> DefUseAnalysis<'tcx> {
- DefUseAnalysis {
- info: IndexVec::from_elem_n(Info::new(), mir.local_decls.len()),
- }
- }
-
- pub fn analyze(&mut self, mir: &Mir<'tcx>) {
- let mut finder = DefUseFinder {
- info: mem::replace(&mut self.info, IndexVec::new()),
- };
- finder.visit_mir(mir);
- self.info = finder.info
- }
-
- pub fn local_info(&self, local: Local) -> &Info<'tcx> {
- &self.info[local]
- }
-
- pub fn local_info_mut(&mut self, local: Local) -> &mut Info<'tcx> {
- &mut self.info[local]
- }
-
- fn mutate_defs_and_uses<F>(&self, local: Local, mir: &mut Mir<'tcx>, mut callback: F)
- where F: for<'a> FnMut(&'a mut Lvalue<'tcx>,
- LvalueContext<'tcx>,
- Location) {
- for lvalue_use in &self.info[local].defs_and_uses {
- MutateUseVisitor::new(local,
- &mut callback,
- mir).visit_location(mir, lvalue_use.location)
- }
- }
-
- /// FIXME(pcwalton): This should update the def-use chains.
- pub fn replace_all_defs_and_uses_with(&self,
- local: Local,
- mir: &mut Mir<'tcx>,
- new_lvalue: Lvalue<'tcx>) {
- self.mutate_defs_and_uses(local, mir, |lvalue, _, _| *lvalue = new_lvalue.clone())
- }
-}
-
-struct DefUseFinder<'tcx> {
- info: IndexVec<Local, Info<'tcx>>,
-}
-
-impl<'tcx> DefUseFinder<'tcx> {
- fn lvalue_mut_info(&mut self, lvalue: &Lvalue<'tcx>) -> Option<&mut Info<'tcx>> {
- let info = &mut self.info;
-
- if let Lvalue::Local(local) = *lvalue {
- Some(&mut info[local])
- } else {
- None
- }
- }
-}
-
-impl<'tcx> Visitor<'tcx> for DefUseFinder<'tcx> {
- fn visit_lvalue(&mut self,
- lvalue: &Lvalue<'tcx>,
- context: LvalueContext<'tcx>,
- location: Location) {
- if let Some(ref mut info) = self.lvalue_mut_info(lvalue) {
- info.defs_and_uses.push(Use {
- context: context,
- location: location,
- })
- }
- self.super_lvalue(lvalue, context, location)
- }
-}
-
-impl<'tcx> Info<'tcx> {
- fn new() -> Info<'tcx> {
- Info {
- defs_and_uses: vec![],
- }
- }
-
- pub fn def_count(&self) -> usize {
- self.defs_and_uses.iter().filter(|lvalue_use| lvalue_use.context.is_mutating_use()).count()
- }
-
- pub fn def_count_not_including_drop(&self) -> usize {
- self.defs_and_uses.iter().filter(|lvalue_use| {
- lvalue_use.context.is_mutating_use() && !lvalue_use.context.is_drop()
- }).count()
- }
-
- pub fn use_count(&self) -> usize {
- self.defs_and_uses.iter().filter(|lvalue_use| {
- lvalue_use.context.is_nonmutating_use()
- }).count()
- }
-}
-
-struct MutateUseVisitor<'tcx, F> {
- query: Local,
- callback: F,
- phantom: PhantomData<&'tcx ()>,
-}
-
-impl<'tcx, F> MutateUseVisitor<'tcx, F> {
- fn new(query: Local, callback: F, _: &Mir<'tcx>)
- -> MutateUseVisitor<'tcx, F>
- where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
- MutateUseVisitor {
- query: query,
- callback: callback,
- phantom: PhantomData,
- }
- }
-}
-
-impl<'tcx, F> MutVisitor<'tcx> for MutateUseVisitor<'tcx, F>
- where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
- fn visit_lvalue(&mut self,
- lvalue: &mut Lvalue<'tcx>,
- context: LvalueContext<'tcx>,
- location: Location) {
- if let Lvalue::Local(local) = *lvalue {
- if local == self.query {
- (self.callback)(lvalue, context, location)
- }
- }
- self.super_lvalue(lvalue, context, location)
- }
-}
+++ /dev/null
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use dot;
-use rustc::hir::def_id::DefId;
-use rustc::mir::*;
-use rustc::ty::TyCtxt;
-use std::fmt::Debug;
-use std::io::{self, Write};
-use syntax::ast::NodeId;
-
-use rustc_data_structures::indexed_vec::Idx;
-
-/// Write a graphviz DOT graph of a list of MIRs.
-pub fn write_mir_graphviz<'a, 'b, 'tcx, W, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
- iter: I,
- w: &mut W)
- -> io::Result<()>
- where W: Write, I: Iterator<Item=DefId>
-{
- for def_id in iter {
- let nodeid = tcx.hir.as_local_node_id(def_id).unwrap();
- let mir = &tcx.item_mir(def_id);
-
- writeln!(w, "digraph Mir_{} {{", nodeid)?;
-
- // Global graph properties
- writeln!(w, r#" graph [fontname="monospace"];"#)?;
- writeln!(w, r#" node [fontname="monospace"];"#)?;
- writeln!(w, r#" edge [fontname="monospace"];"#)?;
-
- // Graph label
- write_graph_label(tcx, nodeid, mir, w)?;
-
- // Nodes
- for (block, _) in mir.basic_blocks().iter_enumerated() {
- write_node(block, mir, w)?;
- }
-
- // Edges
- for (source, _) in mir.basic_blocks().iter_enumerated() {
- write_edges(source, mir, w)?;
- }
- writeln!(w, "}}")?
- }
- Ok(())
-}
-
-/// Write a graphviz HTML-styled label for the given basic block, with
-/// all necessary escaping already performed. (This is suitable for
-/// emitting directly, as is done in this module, or for use with the
-/// LabelText::HtmlStr from libgraphviz.)
-///
-/// `init` and `fini` are callbacks for emitting additional rows of
-/// data (using HTML enclosed with `<tr>` in the emitted text).
-pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
- mir: &Mir,
- w: &mut W,
- num_cols: u32,
- init: INIT,
- fini: FINI) -> io::Result<()>
- where INIT: Fn(&mut W) -> io::Result<()>,
- FINI: Fn(&mut W) -> io::Result<()>
-{
- let data = &mir[block];
-
- write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
-
- // Basic block number at the top.
- write!(w, r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
- attrs=r#"bgcolor="gray" align="center""#,
- colspan=num_cols,
- blk=block.index())?;
-
- init(w)?;
-
- // List of statements in the middle.
- if !data.statements.is_empty() {
- write!(w, r#"<tr><td align="left" balign="left">"#)?;
- for statement in &data.statements {
- write!(w, "{}<br/>", escape(statement))?;
- }
- write!(w, "</td></tr>")?;
- }
-
- // Terminator head at the bottom, not including the list of successor blocks. Those will be
- // displayed as labels on the edges between blocks.
- let mut terminator_head = String::new();
- data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
- write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
-
- fini(w)?;
-
- // Close the table
- writeln!(w, "</table>")
-}
-
-/// Write a graphviz DOT node for the given basic block.
-fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
- // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
- write!(w, r#" {} [shape="none", label=<"#, node(block))?;
- write_node_label(block, mir, w, 1, |_| Ok(()), |_| Ok(()))?;
- // Close the node label and the node itself.
- writeln!(w, ">];")
-}
-
-/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
-fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
- let terminator = mir[source].terminator();
- let labels = terminator.kind.fmt_successor_labels();
-
- for (&target, label) in terminator.successors().iter().zip(labels) {
- writeln!(w, r#" {} -> {} [label="{}"];"#, node(source), node(target), label)?;
- }
-
- Ok(())
-}
-
-/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
-/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
-/// all the variables and temporaries.
-fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- nid: NodeId,
- mir: &Mir,
- w: &mut W)
- -> io::Result<()> {
- write!(w, " label=<fn {}(", dot::escape_html(&tcx.node_path_str(nid)))?;
-
- // fn argument types.
- for (i, arg) in mir.args_iter().enumerate() {
- if i > 0 {
- write!(w, ", ")?;
- }
- write!(w, "{:?}: {}", Lvalue::Local(arg), escape(&mir.local_decls[arg].ty))?;
- }
-
- write!(w, ") -> {}", escape(mir.return_ty))?;
- write!(w, r#"<br align="left"/>"#)?;
-
- for local in mir.vars_and_temps_iter() {
- let decl = &mir.local_decls[local];
-
- write!(w, "let ")?;
- if decl.mutability == Mutability::Mut {
- write!(w, "mut ")?;
- }
-
- if let Some(name) = decl.name {
- write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
- Lvalue::Local(local), escape(&decl.ty), name)?;
- } else {
- write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
- Lvalue::Local(local), escape(&decl.ty))?;
- }
- }
-
- writeln!(w, ">;")
-}
-
-fn node(block: BasicBlock) -> String {
- format!("bb{}", block.index())
-}
-
-fn escape<T: Debug>(t: &T) -> String {
- dot::escape_html(&format!("{:?}", t))
-}
pub mod build;
pub mod callgraph;
-pub mod def_use;
-pub mod graphviz;
mod hair;
mod shim;
pub mod mir_map;
-pub mod pretty;
pub mod transform;
+pub mod util;
use rustc::ty::maps::Providers;
use rustc::mir::transform::MirSource;
use rustc::mir::visit::MutVisitor;
use shim;
-use pretty;
use hair::cx::Cx;
+use util as mir_util;
use rustc::traits::Reveal;
use rustc::ty::{self, Ty, TyCtxt};
mem::transmute::<Mir, Mir<'tcx>>(mir)
};
- pretty::dump_mir(tcx, "mir_map", &0, src, &mir);
+ mir_util::dump_mir(tcx, "mir_map", &0, src, &mir);
tcx.alloc_mir(mir)
})
mem::transmute::<Mir, Mir<'tcx>>(mir)
};
- pretty::dump_mir(tcx, "mir_map", &0, src, &mir);
+ mir_util::dump_mir(tcx, "mir_map", &0, src, &mir);
tcx.alloc_mir(mir)
})
+++ /dev/null
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use rustc::hir;
-use rustc::hir::def_id::DefId;
-use rustc::mir::*;
-use rustc::mir::transform::MirSource;
-use rustc::ty::TyCtxt;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::{Idx};
-use std::fmt::Display;
-use std::fs;
-use std::io::{self, Write};
-use std::path::{PathBuf, Path};
-
-const INDENT: &'static str = " ";
-/// Alignment for lining up comments following MIR statements
-const ALIGN: usize = 40;
-
-/// If the session is properly configured, dumps a human-readable
-/// representation of the mir into:
-///
-/// ```text
-/// rustc.node<node_id>.<pass_name>.<disambiguator>
-/// ```
-///
-/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
-/// where `<filter>` takes the following forms:
-///
-/// - `all` -- dump MIR for all fns, all passes, all everything
-/// - `substring1&substring2,...` -- `&`-separated list of substrings
-/// that can appear in the pass-name or the `item_path_str` for the given
-/// node-id. If any one of the substrings match, the data is dumped out.
-pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- pass_name: &str,
- disambiguator: &Display,
- src: MirSource,
- mir: &Mir<'tcx>) {
- let filters = match tcx.sess.opts.debugging_opts.dump_mir {
- None => return,
- Some(ref filters) => filters,
- };
- let node_id = src.item_id();
- let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id));
- let is_matched =
- filters.split("&")
- .any(|filter| {
- filter == "all" ||
- pass_name.contains(filter) ||
- node_path.contains(filter)
- });
- if !is_matched {
- return;
- }
-
- let promotion_id = match src {
- MirSource::Promoted(_, id) => format!("-{:?}", id),
- _ => String::new()
- };
-
- let mut file_path = PathBuf::new();
- if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
- let p = Path::new(file_dir);
- file_path.push(p);
- };
- let file_name = format!("rustc.node{}{}.{}.{}.mir",
- node_id, promotion_id, pass_name, disambiguator);
- file_path.push(&file_name);
- let _ = fs::File::create(&file_path).and_then(|mut file| {
- writeln!(file, "// MIR for `{}`", node_path)?;
- writeln!(file, "// node_id = {}", node_id)?;
- writeln!(file, "// pass_name = {}", pass_name)?;
- writeln!(file, "// disambiguator = {}", disambiguator)?;
- writeln!(file, "")?;
- write_mir_fn(tcx, src, mir, &mut file)?;
- Ok(())
- });
-}
-
-/// Write out a human-readable textual representation for the given MIR.
-pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
- iter: I,
- w: &mut Write)
- -> io::Result<()>
- where I: Iterator<Item=DefId>, 'tcx: 'a
-{
- let mut first = true;
- for def_id in iter.filter(DefId::is_local) {
- let mir = &tcx.item_mir(def_id);
-
- if first {
- first = false;
- } else {
- // Put empty lines between all items
- writeln!(w, "")?;
- }
-
- let id = tcx.hir.as_local_node_id(def_id).unwrap();
- let src = MirSource::from_node(tcx, id);
- write_mir_fn(tcx, src, mir, w)?;
-
- for (i, mir) in mir.promoted.iter_enumerated() {
- writeln!(w, "")?;
- write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?;
- }
- }
- Ok(())
-}
-
-pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- src: MirSource,
- mir: &Mir<'tcx>,
- w: &mut Write)
- -> io::Result<()> {
- write_mir_intro(tcx, src, mir, w)?;
- for block in mir.basic_blocks().indices() {
- write_basic_block(tcx, block, mir, w)?;
- if block.index() + 1 != mir.basic_blocks().len() {
- writeln!(w, "")?;
- }
- }
-
- writeln!(w, "}}")?;
- Ok(())
-}
-
-/// Write out a human-readable textual representation for the given basic block.
-fn write_basic_block(tcx: TyCtxt,
- block: BasicBlock,
- mir: &Mir,
- w: &mut Write)
- -> io::Result<()> {
- let data = &mir[block];
-
- // Basic block label at the top.
- writeln!(w, "{}{:?}: {{", INDENT, block)?;
-
- // List of statements in the middle.
- let mut current_location = Location { block: block, statement_index: 0 };
- for statement in &data.statements {
- let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
- writeln!(w, "{0:1$} // {2}",
- indented_mir,
- ALIGN,
- comment(tcx, statement.source_info))?;
-
- current_location.statement_index += 1;
- }
-
- // Terminator at the bottom.
- let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
- writeln!(w, "{0:1$} // {2}",
- indented_terminator,
- ALIGN,
- comment(tcx, data.terminator().source_info))?;
-
- writeln!(w, "{}}}", INDENT)
-}
-
-fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
- format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
-}
-
-/// Prints user-defined variables in a scope tree.
-///
-/// Returns the total number of variables printed.
-fn write_scope_tree(tcx: TyCtxt,
- mir: &Mir,
- scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
- w: &mut Write,
- parent: VisibilityScope,
- depth: usize)
- -> io::Result<()> {
- let indent = depth * INDENT.len();
-
- let children = match scope_tree.get(&parent) {
- Some(childs) => childs,
- None => return Ok(()),
- };
-
- for &child in children {
- let data = &mir.visibility_scopes[child];
- assert_eq!(data.parent_scope, Some(parent));
- writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
-
- // User variable types (including the user's name in a comment).
- for local in mir.vars_iter() {
- let var = &mir.local_decls[local];
- let (name, source_info) = if var.source_info.unwrap().scope == child {
- (var.name.unwrap(), var.source_info.unwrap())
- } else {
- // Not a variable or not declared in this scope.
- continue;
- };
-
- let mut_str = if var.mutability == Mutability::Mut {
- "mut "
- } else {
- ""
- };
-
- let indent = indent + INDENT.len();
- let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
- INDENT,
- indent,
- mut_str,
- local,
- var.ty);
- writeln!(w, "{0:1$} // \"{2}\" in {3}",
- indented_var,
- ALIGN,
- name,
- comment(tcx, source_info))?;
- }
-
- write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
-
- writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
- }
-
- Ok(())
-}
-
-/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
-/// local variables (both user-defined bindings and compiler temporaries).
-fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- src: MirSource,
- mir: &Mir,
- w: &mut Write)
- -> io::Result<()> {
- write_mir_sig(tcx, src, mir, w)?;
- writeln!(w, " {{")?;
-
- // construct a scope tree and write it out
- let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
- for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
- if let Some(parent) = scope_data.parent_scope {
- scope_tree.entry(parent)
- .or_insert(vec![])
- .push(VisibilityScope::new(index));
- } else {
- // Only the argument scope has no parent, because it's the root.
- assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
- }
- }
-
- // Print return pointer
- let indented_retptr = format!("{}let mut {:?}: {};",
- INDENT,
- RETURN_POINTER,
- mir.return_ty);
- writeln!(w, "{0:1$} // return pointer",
- indented_retptr,
- ALIGN)?;
-
- write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
-
- write_temp_decls(mir, w)?;
-
- // Add an empty line before the first block is printed.
- writeln!(w, "")?;
-
- Ok(())
-}
-
-fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
- -> io::Result<()>
-{
- match src {
- MirSource::Fn(_) => write!(w, "fn")?,
- MirSource::Const(_) => write!(w, "const")?,
- MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
- MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
- MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
- }
-
- write!(w, " {}", tcx.node_path_str(src.item_id()))?;
-
- if let MirSource::Fn(_) = src {
- write!(w, "(")?;
-
- // fn argument types.
- for (i, arg) in mir.args_iter().enumerate() {
- if i != 0 {
- write!(w, ", ")?;
- }
- write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
- }
-
- write!(w, ") -> {}", mir.return_ty)
- } else {
- assert_eq!(mir.arg_count, 0);
- write!(w, ": {} =", mir.return_ty)
- }
-}
-
-fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
- // Compiler-introduced temporary types.
- for temp in mir.temps_iter() {
- writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
- }
-
- Ok(())
-}
//! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
//! future.
-use def_use::DefUseAnalysis;
use rustc::mir::{Constant, Local, LocalKind, Location, Lvalue, Mir, Operand, Rvalue, StatementKind};
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc::mir::visit::MutVisitor;
use rustc::ty::TyCtxt;
+use util::def_use::DefUseAnalysis;
use transform::qualify_consts;
pub struct CopyPropagation;
use rustc::ty::TyCtxt;
use rustc::mir::*;
use rustc::mir::transform::{Pass, MirPass, MirPassHook, MirSource};
-use pretty;
+use util as mir_util;
pub struct Marker<'a>(pub &'a str);
pass: &Pass,
is_after: bool)
{
- pretty::dump_mir(
+ mir_util::dump_mir(
tcx,
&*pass.name(),
&Disambiguator {
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Def-use analysis.
+
+use rustc::mir::{Local, Location, Lvalue, Mir};
+use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
+use rustc_data_structures::indexed_vec::IndexVec;
+use std::marker::PhantomData;
+use std::mem;
+
+pub struct DefUseAnalysis<'tcx> {
+ info: IndexVec<Local, Info<'tcx>>,
+}
+
+#[derive(Clone)]
+pub struct Info<'tcx> {
+ pub defs_and_uses: Vec<Use<'tcx>>,
+}
+
+#[derive(Clone)]
+pub struct Use<'tcx> {
+ pub context: LvalueContext<'tcx>,
+ pub location: Location,
+}
+
+impl<'tcx> DefUseAnalysis<'tcx> {
+ pub fn new(mir: &Mir<'tcx>) -> DefUseAnalysis<'tcx> {
+ DefUseAnalysis {
+ info: IndexVec::from_elem_n(Info::new(), mir.local_decls.len()),
+ }
+ }
+
+ pub fn analyze(&mut self, mir: &Mir<'tcx>) {
+ let mut finder = DefUseFinder {
+ info: mem::replace(&mut self.info, IndexVec::new()),
+ };
+ finder.visit_mir(mir);
+ self.info = finder.info
+ }
+
+ pub fn local_info(&self, local: Local) -> &Info<'tcx> {
+ &self.info[local]
+ }
+
+ pub fn local_info_mut(&mut self, local: Local) -> &mut Info<'tcx> {
+ &mut self.info[local]
+ }
+
+ fn mutate_defs_and_uses<F>(&self, local: Local, mir: &mut Mir<'tcx>, mut callback: F)
+ where F: for<'a> FnMut(&'a mut Lvalue<'tcx>,
+ LvalueContext<'tcx>,
+ Location) {
+ for lvalue_use in &self.info[local].defs_and_uses {
+ MutateUseVisitor::new(local,
+ &mut callback,
+ mir).visit_location(mir, lvalue_use.location)
+ }
+ }
+
+ /// FIXME(pcwalton): This should update the def-use chains.
+ pub fn replace_all_defs_and_uses_with(&self,
+ local: Local,
+ mir: &mut Mir<'tcx>,
+ new_lvalue: Lvalue<'tcx>) {
+ self.mutate_defs_and_uses(local, mir, |lvalue, _, _| *lvalue = new_lvalue.clone())
+ }
+}
+
+struct DefUseFinder<'tcx> {
+ info: IndexVec<Local, Info<'tcx>>,
+}
+
+impl<'tcx> DefUseFinder<'tcx> {
+ fn lvalue_mut_info(&mut self, lvalue: &Lvalue<'tcx>) -> Option<&mut Info<'tcx>> {
+ let info = &mut self.info;
+
+ if let Lvalue::Local(local) = *lvalue {
+ Some(&mut info[local])
+ } else {
+ None
+ }
+ }
+}
+
+impl<'tcx> Visitor<'tcx> for DefUseFinder<'tcx> {
+ fn visit_lvalue(&mut self,
+ lvalue: &Lvalue<'tcx>,
+ context: LvalueContext<'tcx>,
+ location: Location) {
+ if let Some(ref mut info) = self.lvalue_mut_info(lvalue) {
+ info.defs_and_uses.push(Use {
+ context: context,
+ location: location,
+ })
+ }
+ self.super_lvalue(lvalue, context, location)
+ }
+}
+
+impl<'tcx> Info<'tcx> {
+ fn new() -> Info<'tcx> {
+ Info {
+ defs_and_uses: vec![],
+ }
+ }
+
+ pub fn def_count(&self) -> usize {
+ self.defs_and_uses.iter().filter(|lvalue_use| lvalue_use.context.is_mutating_use()).count()
+ }
+
+ pub fn def_count_not_including_drop(&self) -> usize {
+ self.defs_and_uses.iter().filter(|lvalue_use| {
+ lvalue_use.context.is_mutating_use() && !lvalue_use.context.is_drop()
+ }).count()
+ }
+
+ pub fn use_count(&self) -> usize {
+ self.defs_and_uses.iter().filter(|lvalue_use| {
+ lvalue_use.context.is_nonmutating_use()
+ }).count()
+ }
+}
+
+struct MutateUseVisitor<'tcx, F> {
+ query: Local,
+ callback: F,
+ phantom: PhantomData<&'tcx ()>,
+}
+
+impl<'tcx, F> MutateUseVisitor<'tcx, F> {
+ fn new(query: Local, callback: F, _: &Mir<'tcx>)
+ -> MutateUseVisitor<'tcx, F>
+ where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
+ MutateUseVisitor {
+ query: query,
+ callback: callback,
+ phantom: PhantomData,
+ }
+ }
+}
+
+impl<'tcx, F> MutVisitor<'tcx> for MutateUseVisitor<'tcx, F>
+ where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
+ fn visit_lvalue(&mut self,
+ lvalue: &mut Lvalue<'tcx>,
+ context: LvalueContext<'tcx>,
+ location: Location) {
+ if let Lvalue::Local(local) = *lvalue {
+ if local == self.query {
+ (self.callback)(lvalue, context, location)
+ }
+ }
+ self.super_lvalue(lvalue, context, location)
+ }
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fmt;
+use rustc::mir::*;
+use rustc::middle::lang_items;
+use rustc::ty::{self, Ty};
+use rustc::ty::subst::{Kind, Subst, Substs};
+use rustc::ty::util::IntTypeExt;
+use rustc_data_structures::indexed_vec::Idx;
+use util::patch::MirPatch;
+
+use std::iter;
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub enum DropFlagState {
+ Present, // i.e. initialized
+ Absent, // i.e. deinitialized or "moved"
+}
+
+impl DropFlagState {
+ pub fn value(self) -> bool {
+ match self {
+ DropFlagState::Present => true,
+ DropFlagState::Absent => false
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum DropStyle {
+ Dead,
+ Static,
+ Conditional,
+ Open,
+}
+
+#[derive(Debug)]
+pub enum DropFlagMode {
+ Shallow,
+ Deep
+}
+
+pub trait DropElaborator<'a, 'tcx: 'a> : fmt::Debug {
+ type Path : Copy + fmt::Debug;
+
+ fn patch(&mut self) -> &mut MirPatch<'tcx>;
+ fn mir(&self) -> &'a Mir<'tcx>;
+ fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx>;
+ fn param_env(&self) -> &'a ty::ParameterEnvironment<'tcx>;
+
+ fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle;
+ fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>>;
+ fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode);
+
+
+ fn field_subpath(&self, path: Self::Path, field: Field) -> Option<Self::Path>;
+ fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path>;
+ fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option<Self::Path>;
+}
+
+#[derive(Debug)]
+struct DropCtxt<'l, 'b: 'l, 'tcx: 'b, D>
+ where D : DropElaborator<'b, 'tcx> + 'l
+{
+ elaborator: &'l mut D,
+
+ source_info: SourceInfo,
+ is_cleanup: bool,
+
+ lvalue: &'l Lvalue<'tcx>,
+ path: D::Path,
+ succ: BasicBlock,
+ unwind: Option<BasicBlock>,
+}
+
+pub fn elaborate_drop<'b, 'tcx, D>(
+ elaborator: &mut D,
+ source_info: SourceInfo,
+ is_cleanup: bool,
+ lvalue: &Lvalue<'tcx>,
+ path: D::Path,
+ succ: BasicBlock,
+ unwind: Option<BasicBlock>,
+ bb: BasicBlock)
+ where D: DropElaborator<'b, 'tcx>
+{
+ DropCtxt {
+ elaborator, source_info, is_cleanup, lvalue, path, succ, unwind
+ }.elaborate_drop(bb)
+}
+
+impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
+ where D: DropElaborator<'b, 'tcx>
+{
+ fn lvalue_ty(&self, lvalue: &Lvalue<'tcx>) -> Ty<'tcx> {
+ lvalue.ty(self.elaborator.mir(), self.tcx()).to_ty(self.tcx())
+ }
+
+ fn tcx(&self) -> ty::TyCtxt<'b, 'tcx, 'tcx> {
+ self.elaborator.tcx()
+ }
+
+ /// This elaborates a single drop instruction, located at `bb`, and
+ /// patches over it.
+ ///
+ /// The elaborated drop checks the drop flags to only drop what
+ /// is initialized.
+ ///
+ /// In addition, the relevant drop flags also need to be cleared
+ /// to avoid double-drops. However, in the middle of a complex
+ /// drop, one must avoid clearing some of the flags before they
+ /// are read, as that would cause a memory leak.
+ ///
+ /// In particular, when dropping an ADT, multiple fields may be
+ /// joined together under the `rest` subpath. They are all controlled
+ /// by the primary drop flag, but only the last rest-field dropped
+ /// should clear it (and it must also not clear anything else).
+ ///
+ /// FIXME: I think we should just control the flags externally
+ /// and then we do not need this machinery.
+ pub fn elaborate_drop<'a>(&mut self, bb: BasicBlock) {
+ debug!("elaborate_drop({:?})", self);
+ let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep);
+ debug!("elaborate_drop({:?}): live - {:?}", self, style);
+ match style {
+ DropStyle::Dead => {
+ self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto {
+ target: self.succ
+ });
+ }
+ DropStyle::Static => {
+ let loc = self.terminator_loc(bb);
+ self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep);
+ self.elaborator.patch().patch_terminator(bb, TerminatorKind::Drop {
+ location: self.lvalue.clone(),
+ target: self.succ,
+ unwind: self.unwind
+ });
+ }
+ DropStyle::Conditional => {
+ let drop_bb = self.complete_drop(Some(DropFlagMode::Deep));
+ self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto {
+ target: drop_bb
+ });
+ }
+ DropStyle::Open => {
+ let drop_bb = self.open_drop();
+ self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto {
+ target: drop_bb
+ });
+ }
+ }
+ }
+
+ /// Return the lvalue and move path for each field of `variant`,
+ /// (the move path is `None` if the field is a rest field).
+ fn move_paths_for_fields(&self,
+ base_lv: &Lvalue<'tcx>,
+ variant_path: D::Path,
+ variant: &'tcx ty::VariantDef,
+ substs: &'tcx Substs<'tcx>)
+ -> Vec<(Lvalue<'tcx>, Option<D::Path>)>
+ {
+ variant.fields.iter().enumerate().map(|(i, f)| {
+ let field = Field::new(i);
+ let subpath = self.elaborator.field_subpath(variant_path, field);
+
+ let field_ty =
+ self.tcx().normalize_associated_type_in_env(
+ &f.ty(self.tcx(), substs),
+ self.elaborator.param_env()
+ );
+ (base_lv.clone().field(field, field_ty), subpath)
+ }).collect()
+ }
+
+ fn drop_subpath(&mut self,
+ is_cleanup: bool,
+ lvalue: &Lvalue<'tcx>,
+ path: Option<D::Path>,
+ succ: BasicBlock,
+ unwind: Option<BasicBlock>)
+ -> BasicBlock
+ {
+ if let Some(path) = path {
+ debug!("drop_subpath: for std field {:?}", lvalue);
+
+ DropCtxt {
+ elaborator: self.elaborator,
+ source_info: self.source_info,
+ path, lvalue, succ, unwind, is_cleanup
+ }.elaborated_drop_block()
+ } else {
+ debug!("drop_subpath: for rest field {:?}", lvalue);
+
+ DropCtxt {
+ elaborator: self.elaborator,
+ source_info: self.source_info,
+ lvalue, succ, unwind, is_cleanup,
+ // Using `self.path` here to condition the drop on
+ // our own drop flag.
+ path: self.path
+ }.complete_drop(None)
+ }
+ }
+
+ /// Create one-half of the drop ladder for a list of fields, and return
+ /// the list of steps in it in reverse order.
+ ///
+ /// `unwind_ladder` is such a list of steps in reverse order,
+ /// which is called instead of the next step if the drop unwinds
+ /// (the first field is never reached). If it is `None`, all
+ /// unwind targets are left blank.
+ fn drop_halfladder<'a>(&mut self,
+ unwind_ladder: Option<Vec<BasicBlock>>,
+ succ: BasicBlock,
+ fields: &[(Lvalue<'tcx>, Option<D::Path>)],
+ is_cleanup: bool)
+ -> Vec<BasicBlock>
+ {
+ let mut unwind_succ = if is_cleanup {
+ None
+ } else {
+ self.unwind
+ };
+
+ let goto = TerminatorKind::Goto { target: succ };
+ let mut succ = self.new_block(is_cleanup, goto);
+
+ // Always clear the "master" drop flag at the bottom of the
+ // ladder. This is needed because the "master" drop flag
+ // protects the ADT's discriminant, which is invalidated
+ // after the ADT is dropped.
+ let succ_loc = Location { block: succ, statement_index: 0 };
+ self.elaborator.clear_drop_flag(succ_loc, self.path, DropFlagMode::Shallow);
+
+ fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| {
+ succ = self.drop_subpath(is_cleanup, lv, path, succ, unwind_succ);
+ unwind_succ = unwind_ladder.as_ref().map(|p| p[i]);
+ succ
+ }).collect()
+ }
+
+ /// Create a full drop ladder, consisting of 2 connected half-drop-ladders
+ ///
+ /// For example, with 3 fields, the drop ladder is
+ ///
+ /// .d0:
+ /// ELAB(drop location.0 [target=.d1, unwind=.c1])
+ /// .d1:
+ /// ELAB(drop location.1 [target=.d2, unwind=.c2])
+ /// .d2:
+ /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`])
+ /// .c1:
+ /// ELAB(drop location.1 [target=.c2])
+ /// .c2:
+ /// ELAB(drop location.2 [target=`self.unwind])
+ fn drop_ladder<'a>(&mut self,
+ fields: Vec<(Lvalue<'tcx>, Option<D::Path>)>)
+ -> BasicBlock
+ {
+ debug!("drop_ladder({:?}, {:?})", self, fields);
+
+ let mut fields = fields;
+ fields.retain(|&(ref lvalue, _)| {
+ self.tcx().type_needs_drop_given_env(
+ self.lvalue_ty(lvalue), self.elaborator.param_env())
+ });
+
+ debug!("drop_ladder - fields needing drop: {:?}", fields);
+
+ let unwind_ladder = if self.is_cleanup {
+ None
+ } else {
+ let unwind = self.unwind.unwrap(); // FIXME(#6393)
+ Some(self.drop_halfladder(None, unwind, &fields, true))
+ };
+
+ let succ = self.succ; // FIXME(#6393)
+ let is_cleanup = self.is_cleanup;
+ self.drop_halfladder(unwind_ladder, succ, &fields, is_cleanup)
+ .last().cloned().unwrap_or(succ)
+ }
+
+ fn open_drop_for_tuple<'a>(&mut self, tys: &[Ty<'tcx>])
+ -> BasicBlock
+ {
+ debug!("open_drop_for_tuple({:?}, {:?})", self, tys);
+
+ let fields = tys.iter().enumerate().map(|(i, &ty)| {
+ (self.lvalue.clone().field(Field::new(i), ty),
+ self.elaborator.field_subpath(self.path, Field::new(i)))
+ }).collect();
+
+ self.drop_ladder(fields)
+ }
+
+ fn open_drop_for_box<'a>(&mut self, ty: Ty<'tcx>) -> BasicBlock
+ {
+ debug!("open_drop_for_box({:?}, {:?})", self, ty);
+
+ let interior = self.lvalue.clone().deref();
+ let interior_path = self.elaborator.deref_subpath(self.path);
+
+ let succ = self.succ; // FIXME(#6393)
+ let is_cleanup = self.is_cleanup;
+ let succ = self.box_free_block(ty, succ, is_cleanup);
+ let unwind_succ = self.unwind.map(|u| {
+ self.box_free_block(ty, u, true)
+ });
+
+ self.drop_subpath(is_cleanup, &interior, interior_path, succ, unwind_succ)
+ }
+
+ fn open_drop_for_adt<'a>(&mut self, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>)
+ -> BasicBlock {
+ debug!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt, substs);
+
+ match adt.variants.len() {
+ 1 => {
+ let fields = self.move_paths_for_fields(
+ self.lvalue,
+ self.path,
+ &adt.variants[0],
+ substs
+ );
+ self.drop_ladder(fields)
+ }
+ _ => {
+ let mut values = Vec::with_capacity(adt.variants.len());
+ let mut blocks = Vec::with_capacity(adt.variants.len());
+ let mut otherwise = None;
+ for (variant_index, discr) in adt.discriminants(self.tcx()).enumerate() {
+ let subpath = self.elaborator.downcast_subpath(
+ self.path, variant_index);
+ if let Some(variant_path) = subpath {
+ let base_lv = self.lvalue.clone().elem(
+ ProjectionElem::Downcast(adt, variant_index)
+ );
+ let fields = self.move_paths_for_fields(
+ &base_lv,
+ variant_path,
+ &adt.variants[variant_index],
+ substs);
+ values.push(discr);
+ blocks.push(self.drop_ladder(fields));
+ } else {
+ // variant not found - drop the entire enum
+ if let None = otherwise {
+ otherwise =
+ Some(self.complete_drop(Some(DropFlagMode::Shallow)));
+ }
+ }
+ }
+ if let Some(block) = otherwise {
+ blocks.push(block);
+ } else {
+ values.pop();
+ }
+ // If there are multiple variants, then if something
+ // is present within the enum the discriminant, tracked
+ // by the rest path, must be initialized.
+ //
+ // Additionally, we do not want to switch on the
+ // discriminant after it is free-ed, because that
+ // way lies only trouble.
+ let discr_ty = adt.repr.discr_type().to_ty(self.tcx());
+ let discr = Lvalue::Local(self.new_temp(discr_ty));
+ let discr_rv = Rvalue::Discriminant(self.lvalue.clone());
+ let switch_block = self.elaborator.patch().new_block(BasicBlockData {
+ statements: vec![
+ Statement {
+ source_info: self.source_info,
+ kind: StatementKind::Assign(discr.clone(), discr_rv),
+ }
+ ],
+ terminator: Some(Terminator {
+ source_info: self.source_info,
+ kind: TerminatorKind::SwitchInt {
+ discr: Operand::Consume(discr),
+ switch_ty: discr_ty,
+ values: From::from(values),
+ targets: blocks,
+ }
+ }),
+ is_cleanup: self.is_cleanup,
+ });
+ self.drop_flag_test_block(switch_block)
+ }
+ }
+ }
+
+ /// The slow-path - create an "open", elaborated drop for a type
+ /// which is moved-out-of only partially, and patch `bb` to a jump
+ /// to it. This must not be called on ADTs with a destructor,
+ /// as these can't be moved-out-of, except for `Box<T>`, which is
+ /// special-cased.
+ ///
+ /// This creates a "drop ladder" that drops the needed fields of the
+ /// ADT, both in the success case or if one of the destructors fail.
+ fn open_drop<'a>(&mut self) -> BasicBlock {
+ let ty = self.lvalue_ty(self.lvalue);
+ match ty.sty {
+ ty::TyClosure(def_id, substs) => {
+ let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
+ self.open_drop_for_tuple(&tys)
+ }
+ ty::TyTuple(tys, _) => {
+ self.open_drop_for_tuple(tys)
+ }
+ ty::TyAdt(def, _) if def.is_box() => {
+ self.open_drop_for_box(ty.boxed_ty())
+ }
+ ty::TyAdt(def, substs) => {
+ self.open_drop_for_adt(def, substs)
+ }
+ _ => bug!("open drop from non-ADT `{:?}`", ty)
+ }
+ }
+
+ /// Return a basic block that drop an lvalue using the context
+ /// and path in `c`. If `mode` is something, also clear `c`
+ /// according to it.
+ ///
+ /// if FLAG(self.path)
+ /// if let Some(mode) = mode: FLAG(self.path)[mode] = false
+ /// drop(self.lv)
+ fn complete_drop<'a>(&mut self, drop_mode: Option<DropFlagMode>) -> BasicBlock
+ {
+ debug!("complete_drop({:?},{:?})", self, drop_mode);
+
+ let drop_block = self.drop_block();
+ if let Some(mode) = drop_mode {
+ let block_start = Location { block: drop_block, statement_index: 0 };
+ self.elaborator.clear_drop_flag(block_start, self.path, mode);
+ }
+
+ self.drop_flag_test_block(drop_block)
+ }
+
+ fn elaborated_drop_block<'a>(&mut self) -> BasicBlock {
+ debug!("elaborated_drop_block({:?})", self);
+ let blk = self.drop_block();
+ self.elaborate_drop(blk);
+ blk
+ }
+
+ fn box_free_block<'a>(
+ &mut self,
+ ty: Ty<'tcx>,
+ target: BasicBlock,
+ is_cleanup: bool
+ ) -> BasicBlock {
+ let block = self.unelaborated_free_block(ty, target, is_cleanup);
+ self.drop_flag_test_block_with_succ(is_cleanup, block, target)
+ }
+
+ fn unelaborated_free_block<'a>(
+ &mut self,
+ ty: Ty<'tcx>,
+ target: BasicBlock,
+ is_cleanup: bool
+ ) -> BasicBlock {
+ let tcx = self.tcx();
+ let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil()));
+ let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem);
+ let substs = tcx.mk_substs(iter::once(Kind::from(ty)));
+ let fty = tcx.item_type(free_func).subst(tcx, substs);
+
+ let free_block = self.elaborator.patch().new_block(BasicBlockData {
+ statements: vec![],
+ terminator: Some(Terminator {
+ source_info: self.source_info, kind: TerminatorKind::Call {
+ func: Operand::Constant(Constant {
+ span: self.source_info.span,
+ ty: fty,
+ literal: Literal::Item {
+ def_id: free_func,
+ substs: substs
+ }
+ }),
+ args: vec![Operand::Consume(self.lvalue.clone())],
+ destination: Some((unit_temp, target)),
+ cleanup: None
+ }
+ }),
+ is_cleanup: is_cleanup
+ });
+ let block_start = Location { block: free_block, statement_index: 0 };
+ self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
+ free_block
+ }
+
+ fn drop_block<'a>(&mut self) -> BasicBlock {
+ let block = TerminatorKind::Drop {
+ location: self.lvalue.clone(),
+ target: self.succ,
+ unwind: self.unwind
+ };
+ let is_cleanup = self.is_cleanup; // FIXME(#6393)
+ self.new_block(is_cleanup, block)
+ }
+
+ fn drop_flag_test_block<'a>(&mut self, on_set: BasicBlock) -> BasicBlock {
+ let is_cleanup = self.is_cleanup;
+ let succ = self.succ; // FIXME(#6393)
+ self.drop_flag_test_block_with_succ(is_cleanup, on_set, succ)
+ }
+
+ fn drop_flag_test_block_with_succ<'a>(&mut self,
+ is_cleanup: bool,
+ on_set: BasicBlock,
+ on_unset: BasicBlock)
+ -> BasicBlock
+ {
+ let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow);
+ debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}",
+ self, is_cleanup, on_set, style);
+
+ match style {
+ DropStyle::Dead => on_unset,
+ DropStyle::Static => on_set,
+ DropStyle::Conditional | DropStyle::Open => {
+ let flag = self.elaborator.get_drop_flag(self.path).unwrap();
+ let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset);
+ self.new_block(is_cleanup, term)
+ }
+ }
+ }
+
+ fn new_block<'a>(&mut self,
+ is_cleanup: bool,
+ k: TerminatorKind<'tcx>)
+ -> BasicBlock
+ {
+ self.elaborator.patch().new_block(BasicBlockData {
+ statements: vec![],
+ terminator: Some(Terminator {
+ source_info: self.source_info, kind: k
+ }),
+ is_cleanup: is_cleanup
+ })
+ }
+
+ fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
+ self.elaborator.patch().new_temp(ty)
+ }
+
+ fn terminator_loc(&mut self, bb: BasicBlock) -> Location {
+ let mir = self.elaborator.mir();
+ self.elaborator.patch().terminator_loc(mir, bb)
+ }
+}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use dot;
+use rustc::hir::def_id::DefId;
+use rustc::mir::*;
+use rustc::ty::TyCtxt;
+use std::fmt::Debug;
+use std::io::{self, Write};
+use syntax::ast::NodeId;
+
+use rustc_data_structures::indexed_vec::Idx;
+
+/// Write a graphviz DOT graph of a list of MIRs.
+pub fn write_mir_graphviz<'a, 'b, 'tcx, W, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
+ iter: I,
+ w: &mut W)
+ -> io::Result<()>
+ where W: Write, I: Iterator<Item=DefId>
+{
+ for def_id in iter {
+ let nodeid = tcx.hir.as_local_node_id(def_id).unwrap();
+ let mir = &tcx.item_mir(def_id);
+
+ writeln!(w, "digraph Mir_{} {{", nodeid)?;
+
+ // Global graph properties
+ writeln!(w, r#" graph [fontname="monospace"];"#)?;
+ writeln!(w, r#" node [fontname="monospace"];"#)?;
+ writeln!(w, r#" edge [fontname="monospace"];"#)?;
+
+ // Graph label
+ write_graph_label(tcx, nodeid, mir, w)?;
+
+ // Nodes
+ for (block, _) in mir.basic_blocks().iter_enumerated() {
+ write_node(block, mir, w)?;
+ }
+
+ // Edges
+ for (source, _) in mir.basic_blocks().iter_enumerated() {
+ write_edges(source, mir, w)?;
+ }
+ writeln!(w, "}}")?
+ }
+ Ok(())
+}
+
+/// Write a graphviz HTML-styled label for the given basic block, with
+/// all necessary escaping already performed. (This is suitable for
+/// emitting directly, as is done in this module, or for use with the
+/// LabelText::HtmlStr from libgraphviz.)
+///
+/// `init` and `fini` are callbacks for emitting additional rows of
+/// data (using HTML enclosed with `<tr>` in the emitted text).
+pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
+ mir: &Mir,
+ w: &mut W,
+ num_cols: u32,
+ init: INIT,
+ fini: FINI) -> io::Result<()>
+ where INIT: Fn(&mut W) -> io::Result<()>,
+ FINI: Fn(&mut W) -> io::Result<()>
+{
+ let data = &mir[block];
+
+ write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
+
+ // Basic block number at the top.
+ write!(w, r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
+ attrs=r#"bgcolor="gray" align="center""#,
+ colspan=num_cols,
+ blk=block.index())?;
+
+ init(w)?;
+
+ // List of statements in the middle.
+ if !data.statements.is_empty() {
+ write!(w, r#"<tr><td align="left" balign="left">"#)?;
+ for statement in &data.statements {
+ write!(w, "{}<br/>", escape(statement))?;
+ }
+ write!(w, "</td></tr>")?;
+ }
+
+ // Terminator head at the bottom, not including the list of successor blocks. Those will be
+ // displayed as labels on the edges between blocks.
+ let mut terminator_head = String::new();
+ data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
+ write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
+
+ fini(w)?;
+
+ // Close the table
+ writeln!(w, "</table>")
+}
+
+/// Write a graphviz DOT node for the given basic block.
+fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
+ // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
+ write!(w, r#" {} [shape="none", label=<"#, node(block))?;
+ write_node_label(block, mir, w, 1, |_| Ok(()), |_| Ok(()))?;
+ // Close the node label and the node itself.
+ writeln!(w, ">];")
+}
+
+/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
+fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
+ let terminator = mir[source].terminator();
+ let labels = terminator.kind.fmt_successor_labels();
+
+ for (&target, label) in terminator.successors().iter().zip(labels) {
+ writeln!(w, r#" {} -> {} [label="{}"];"#, node(source), node(target), label)?;
+ }
+
+ Ok(())
+}
+
+/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
+/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
+/// all the variables and temporaries.
+fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ nid: NodeId,
+ mir: &Mir,
+ w: &mut W)
+ -> io::Result<()> {
+ write!(w, " label=<fn {}(", dot::escape_html(&tcx.node_path_str(nid)))?;
+
+ // fn argument types.
+ for (i, arg) in mir.args_iter().enumerate() {
+ if i > 0 {
+ write!(w, ", ")?;
+ }
+ write!(w, "{:?}: {}", Lvalue::Local(arg), escape(&mir.local_decls[arg].ty))?;
+ }
+
+ write!(w, ") -> {}", escape(mir.return_ty))?;
+ write!(w, r#"<br align="left"/>"#)?;
+
+ for local in mir.vars_and_temps_iter() {
+ let decl = &mir.local_decls[local];
+
+ write!(w, "let ")?;
+ if decl.mutability == Mutability::Mut {
+ write!(w, "mut ")?;
+ }
+
+ if let Some(name) = decl.name {
+ write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
+ Lvalue::Local(local), escape(&decl.ty), name)?;
+ } else {
+ write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
+ Lvalue::Local(local), escape(&decl.ty))?;
+ }
+ }
+
+ writeln!(w, ">;")
+}
+
+fn node(block: BasicBlock) -> String {
+ format!("bb{}", block.index())
+}
+
+fn escape<T: Debug>(t: &T) -> String {
+ dot::escape_html(&format!("{:?}", t))
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub mod elaborate_drops;
+pub mod def_use;
+pub mod patch;
+
+mod graphviz;
+mod pretty;
+
+pub use self::pretty::{dump_mir, write_mir_pretty};
+pub use self::graphviz::{write_mir_graphviz};
+pub use self::graphviz::write_node_label as write_graphviz_node_label;
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::ty::Ty;
+use rustc::mir::*;
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+
+/// This struct represents a patch to MIR, which can add
+/// new statements and basic blocks and patch over block
+/// terminators.
+pub struct MirPatch<'tcx> {
+ patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>,
+ new_blocks: Vec<BasicBlockData<'tcx>>,
+ new_statements: Vec<(Location, StatementKind<'tcx>)>,
+ new_locals: Vec<LocalDecl<'tcx>>,
+ resume_block: BasicBlock,
+ next_local: usize,
+}
+
+impl<'tcx> MirPatch<'tcx> {
+ pub fn new(mir: &Mir<'tcx>) -> Self {
+ let mut result = MirPatch {
+ patch_map: IndexVec::from_elem(None, mir.basic_blocks()),
+ new_blocks: vec![],
+ new_statements: vec![],
+ new_locals: vec![],
+ next_local: mir.local_decls.len(),
+ resume_block: START_BLOCK
+ };
+
+ // make sure the MIR we create has a resume block. It is
+ // completely legal to convert jumps to the resume block
+ // to jumps to None, but we occasionally have to add
+ // instructions just before that.
+
+ let mut resume_block = None;
+ let mut resume_stmt_block = None;
+ for (bb, block) in mir.basic_blocks().iter_enumerated() {
+ if let TerminatorKind::Resume = block.terminator().kind {
+ if block.statements.len() > 0 {
+ resume_stmt_block = Some(bb);
+ } else {
+ resume_block = Some(bb);
+ }
+ break
+ }
+ }
+ let resume_block = resume_block.unwrap_or_else(|| {
+ result.new_block(BasicBlockData {
+ statements: vec![],
+ terminator: Some(Terminator {
+ source_info: SourceInfo {
+ span: mir.span,
+ scope: ARGUMENT_VISIBILITY_SCOPE
+ },
+ kind: TerminatorKind::Resume
+ }),
+ is_cleanup: true
+ })});
+ result.resume_block = resume_block;
+ if let Some(resume_stmt_block) = resume_stmt_block {
+ result.patch_terminator(resume_stmt_block, TerminatorKind::Goto {
+ target: resume_block
+ });
+ }
+ result
+ }
+
+ pub fn resume_block(&self) -> BasicBlock {
+ self.resume_block
+ }
+
+ pub fn is_patched(&self, bb: BasicBlock) -> bool {
+ self.patch_map[bb].is_some()
+ }
+
+ pub fn terminator_loc(&self, mir: &Mir<'tcx>, bb: BasicBlock) -> Location {
+ let offset = match bb.index().checked_sub(mir.basic_blocks().len()) {
+ Some(index) => self.new_blocks[index].statements.len(),
+ None => mir[bb].statements.len()
+ };
+ Location {
+ block: bb,
+ statement_index: offset
+ }
+ }
+
+ pub fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
+ let index = self.next_local;
+ self.next_local += 1;
+ self.new_locals.push(LocalDecl::new_temp(ty));
+ Local::new(index as usize)
+ }
+
+ pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
+ let block = BasicBlock::new(self.patch_map.len());
+ debug!("MirPatch: new_block: {:?}: {:?}", block, data);
+ self.new_blocks.push(data);
+ self.patch_map.push(None);
+ block
+ }
+
+ pub fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) {
+ assert!(self.patch_map[block].is_none());
+ debug!("MirPatch: patch_terminator({:?}, {:?})", block, new);
+ self.patch_map[block] = Some(new);
+ }
+
+ pub fn add_statement(&mut self, loc: Location, stmt: StatementKind<'tcx>) {
+ debug!("MirPatch: add_statement({:?}, {:?})", loc, stmt);
+ self.new_statements.push((loc, stmt));
+ }
+
+ pub fn add_assign(&mut self, loc: Location, lv: Lvalue<'tcx>, rv: Rvalue<'tcx>) {
+ self.add_statement(loc, StatementKind::Assign(lv, rv));
+ }
+
+ pub fn apply(self, mir: &mut Mir<'tcx>) {
+ debug!("MirPatch: {:?} new temps, starting from index {}: {:?}",
+ self.new_locals.len(), mir.local_decls.len(), self.new_locals);
+ debug!("MirPatch: {} new blocks, starting from index {}",
+ self.new_blocks.len(), mir.basic_blocks().len());
+ mir.basic_blocks_mut().extend(self.new_blocks);
+ mir.local_decls.extend(self.new_locals);
+ for (src, patch) in self.patch_map.into_iter_enumerated() {
+ if let Some(patch) = patch {
+ debug!("MirPatch: patching block {:?}", src);
+ mir[src].terminator_mut().kind = patch;
+ }
+ }
+
+ let mut new_statements = self.new_statements;
+ new_statements.sort_by(|u,v| u.0.cmp(&v.0));
+
+ let mut delta = 0;
+ let mut last_bb = START_BLOCK;
+ for (mut loc, stmt) in new_statements {
+ if loc.block != last_bb {
+ delta = 0;
+ last_bb = loc.block;
+ }
+ debug!("MirPatch: adding statement {:?} at loc {:?}+{}",
+ stmt, loc, delta);
+ loc.statement_index += delta;
+ let source_info = Self::source_info_for_index(
+ &mir[loc.block], loc
+ );
+ mir[loc.block].statements.insert(
+ loc.statement_index, Statement {
+ source_info: source_info,
+ kind: stmt
+ });
+ delta += 1;
+ }
+ }
+
+ pub fn source_info_for_index(data: &BasicBlockData, loc: Location) -> SourceInfo {
+ match data.statements.get(loc.statement_index) {
+ Some(stmt) => stmt.source_info,
+ None => data.terminator().source_info
+ }
+ }
+
+ pub fn source_info_for_location(&self, mir: &Mir, loc: Location) -> SourceInfo {
+ let data = match loc.block.index().checked_sub(mir.basic_blocks().len()) {
+ Some(new) => &self.new_blocks[new],
+ None => &mir[loc.block]
+ };
+ Self::source_info_for_index(data, loc)
+ }
+}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::hir;
+use rustc::hir::def_id::DefId;
+use rustc::mir::*;
+use rustc::mir::transform::MirSource;
+use rustc::ty::TyCtxt;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::indexed_vec::{Idx};
+use std::fmt::Display;
+use std::fs;
+use std::io::{self, Write};
+use std::path::{PathBuf, Path};
+
+const INDENT: &'static str = " ";
+/// Alignment for lining up comments following MIR statements
+const ALIGN: usize = 40;
+
+/// If the session is properly configured, dumps a human-readable
+/// representation of the mir into:
+///
+/// ```text
+/// rustc.node<node_id>.<pass_name>.<disambiguator>
+/// ```
+///
+/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
+/// where `<filter>` takes the following forms:
+///
+/// - `all` -- dump MIR for all fns, all passes, all everything
+/// - `substring1&substring2,...` -- `&`-separated list of substrings
+/// that can appear in the pass-name or the `item_path_str` for the given
+/// node-id. If any one of the substrings match, the data is dumped out.
+pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ pass_name: &str,
+ disambiguator: &Display,
+ src: MirSource,
+ mir: &Mir<'tcx>) {
+ let filters = match tcx.sess.opts.debugging_opts.dump_mir {
+ None => return,
+ Some(ref filters) => filters,
+ };
+ let node_id = src.item_id();
+ let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id));
+ let is_matched =
+ filters.split("&")
+ .any(|filter| {
+ filter == "all" ||
+ pass_name.contains(filter) ||
+ node_path.contains(filter)
+ });
+ if !is_matched {
+ return;
+ }
+
+ let promotion_id = match src {
+ MirSource::Promoted(_, id) => format!("-{:?}", id),
+ _ => String::new()
+ };
+
+ let mut file_path = PathBuf::new();
+ if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
+ let p = Path::new(file_dir);
+ file_path.push(p);
+ };
+ let file_name = format!("rustc.node{}{}.{}.{}.mir",
+ node_id, promotion_id, pass_name, disambiguator);
+ file_path.push(&file_name);
+ let _ = fs::File::create(&file_path).and_then(|mut file| {
+ writeln!(file, "// MIR for `{}`", node_path)?;
+ writeln!(file, "// node_id = {}", node_id)?;
+ writeln!(file, "// pass_name = {}", pass_name)?;
+ writeln!(file, "// disambiguator = {}", disambiguator)?;
+ writeln!(file, "")?;
+ write_mir_fn(tcx, src, mir, &mut file)?;
+ Ok(())
+ });
+}
+
+/// Write out a human-readable textual representation for the given MIR.
+pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
+ iter: I,
+ w: &mut Write)
+ -> io::Result<()>
+ where I: Iterator<Item=DefId>, 'tcx: 'a
+{
+ let mut first = true;
+ for def_id in iter.filter(DefId::is_local) {
+ let mir = &tcx.item_mir(def_id);
+
+ if first {
+ first = false;
+ } else {
+ // Put empty lines between all items
+ writeln!(w, "")?;
+ }
+
+ let id = tcx.hir.as_local_node_id(def_id).unwrap();
+ let src = MirSource::from_node(tcx, id);
+ write_mir_fn(tcx, src, mir, w)?;
+
+ for (i, mir) in mir.promoted.iter_enumerated() {
+ writeln!(w, "")?;
+ write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?;
+ }
+ }
+ Ok(())
+}
+
+pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ src: MirSource,
+ mir: &Mir<'tcx>,
+ w: &mut Write)
+ -> io::Result<()> {
+ write_mir_intro(tcx, src, mir, w)?;
+ for block in mir.basic_blocks().indices() {
+ write_basic_block(tcx, block, mir, w)?;
+ if block.index() + 1 != mir.basic_blocks().len() {
+ writeln!(w, "")?;
+ }
+ }
+
+ writeln!(w, "}}")?;
+ Ok(())
+}
+
+/// Write out a human-readable textual representation for the given basic block.
+fn write_basic_block(tcx: TyCtxt,
+ block: BasicBlock,
+ mir: &Mir,
+ w: &mut Write)
+ -> io::Result<()> {
+ let data = &mir[block];
+
+ // Basic block label at the top.
+ writeln!(w, "{}{:?}: {{", INDENT, block)?;
+
+ // List of statements in the middle.
+ let mut current_location = Location { block: block, statement_index: 0 };
+ for statement in &data.statements {
+ let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
+ writeln!(w, "{0:1$} // {2}",
+ indented_mir,
+ ALIGN,
+ comment(tcx, statement.source_info))?;
+
+ current_location.statement_index += 1;
+ }
+
+ // Terminator at the bottom.
+ let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
+ writeln!(w, "{0:1$} // {2}",
+ indented_terminator,
+ ALIGN,
+ comment(tcx, data.terminator().source_info))?;
+
+ writeln!(w, "{}}}", INDENT)
+}
+
+fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
+ format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
+}
+
+/// Prints user-defined variables in a scope tree.
+///
+/// Returns the total number of variables printed.
+fn write_scope_tree(tcx: TyCtxt,
+ mir: &Mir,
+ scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
+ w: &mut Write,
+ parent: VisibilityScope,
+ depth: usize)
+ -> io::Result<()> {
+ let indent = depth * INDENT.len();
+
+ let children = match scope_tree.get(&parent) {
+ Some(childs) => childs,
+ None => return Ok(()),
+ };
+
+ for &child in children {
+ let data = &mir.visibility_scopes[child];
+ assert_eq!(data.parent_scope, Some(parent));
+ writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
+
+ // User variable types (including the user's name in a comment).
+ for local in mir.vars_iter() {
+ let var = &mir.local_decls[local];
+ let (name, source_info) = if var.source_info.unwrap().scope == child {
+ (var.name.unwrap(), var.source_info.unwrap())
+ } else {
+ // Not a variable or not declared in this scope.
+ continue;
+ };
+
+ let mut_str = if var.mutability == Mutability::Mut {
+ "mut "
+ } else {
+ ""
+ };
+
+ let indent = indent + INDENT.len();
+ let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
+ INDENT,
+ indent,
+ mut_str,
+ local,
+ var.ty);
+ writeln!(w, "{0:1$} // \"{2}\" in {3}",
+ indented_var,
+ ALIGN,
+ name,
+ comment(tcx, source_info))?;
+ }
+
+ write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
+
+ writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
+ }
+
+ Ok(())
+}
+
+/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
+/// local variables (both user-defined bindings and compiler temporaries).
+fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ src: MirSource,
+ mir: &Mir,
+ w: &mut Write)
+ -> io::Result<()> {
+ write_mir_sig(tcx, src, mir, w)?;
+ writeln!(w, " {{")?;
+
+ // construct a scope tree and write it out
+ let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
+ for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
+ if let Some(parent) = scope_data.parent_scope {
+ scope_tree.entry(parent)
+ .or_insert(vec![])
+ .push(VisibilityScope::new(index));
+ } else {
+ // Only the argument scope has no parent, because it's the root.
+ assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
+ }
+ }
+
+ // Print return pointer
+ let indented_retptr = format!("{}let mut {:?}: {};",
+ INDENT,
+ RETURN_POINTER,
+ mir.return_ty);
+ writeln!(w, "{0:1$} // return pointer",
+ indented_retptr,
+ ALIGN)?;
+
+ write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
+
+ write_temp_decls(mir, w)?;
+
+ // Add an empty line before the first block is printed.
+ writeln!(w, "")?;
+
+ Ok(())
+}
+
+fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
+ -> io::Result<()>
+{
+ match src {
+ MirSource::Fn(_) => write!(w, "fn")?,
+ MirSource::Const(_) => write!(w, "const")?,
+ MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
+ MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
+ MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
+ }
+
+ write!(w, " {}", tcx.node_path_str(src.item_id()))?;
+
+ if let MirSource::Fn(_) = src {
+ write!(w, "(")?;
+
+ // fn argument types.
+ for (i, arg) in mir.args_iter().enumerate() {
+ if i != 0 {
+ write!(w, ", ")?;
+ }
+ write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
+ }
+
+ write!(w, ") -> {}", mir.return_ty)
+ } else {
+ assert_eq!(mir.arg_count, 0);
+ write!(w, ": {} =", mir.return_ty)
+ }
+}
+
+fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
+ // Compiler-introduced temporary types.
+ for temp in mir.temps_iter() {
+ writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
+ }
+
+ Ok(())
+}