diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index ff3e292f45818..47d923cbce359 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -32,6 +32,10 @@ pub struct Mir<'tcx> { /// that indexes into this vector. pub basic_blocks: Vec>, + /// List of lexical scopes; these are referenced by statements and + /// used (eventually) for debuginfo. Indexed by a `ScopeId`. + pub scopes: Vec, + /// Return type of the function. pub return_ty: FnOutput<'tcx>, @@ -152,9 +156,21 @@ pub enum BorrowKind { /// decl, a let, etc. #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct VarDecl<'tcx> { + /// `let mut x` vs `let x` pub mutability: Mutability, + + /// name that user gave the variable; not that, internally, + /// mir references variables by index pub name: Name, + + /// type inferred for this variable (`let x: ty = ...`) pub ty: Ty<'tcx>, + + /// scope in which variable was declared + pub scope: ScopeId, + + /// span where variable was declared + pub span: Span, } /// A "temp" is a temporary that we place on the stack. They are @@ -191,7 +207,7 @@ pub struct ArgDecl<'tcx> { /// list of the `Mir`. /// /// (We use a `u32` internally just to save memory.) -#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct BasicBlock(u32); impl BasicBlock { @@ -217,13 +233,35 @@ impl Debug for BasicBlock { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct BasicBlockData<'tcx> { + /// List of statements in this block. pub statements: Vec>, + + /// Terminator for this block. + /// + /// NB. This should generally ONLY be `None` during construction. + /// Therefore, you should generally access it via the + /// `terminator()` or `terminator_mut()` methods. The only + /// exception is that certain passes, such as `simplify_cfg`, swap + /// out the terminator temporarily with `None` while they continue + /// to recurse over the set of basic blocks. pub terminator: Option>, + + /// If true, this block lies on an unwind path. This is used + /// during trans where distinct kinds of basic blocks may be + /// generated (particularly for MSVC cleanup). Unwind blocks must + /// only branch to other unwind blocks. pub is_cleanup: bool, } +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct Terminator<'tcx> { + pub span: Span, + pub scope: ScopeId, + pub kind: TerminatorKind<'tcx> +} + #[derive(Clone, RustcEncodable, RustcDecodable)] -pub enum Terminator<'tcx> { +pub enum TerminatorKind<'tcx> { /// block should have one successor in the graph; we jump there Goto { target: BasicBlock, @@ -293,7 +331,17 @@ pub enum Terminator<'tcx> { impl<'tcx> Terminator<'tcx> { pub fn successors(&self) -> Cow<[BasicBlock]> { - use self::Terminator::*; + self.kind.successors() + } + + pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> { + self.kind.successors_mut() + } +} + +impl<'tcx> TerminatorKind<'tcx> { + pub fn successors(&self) -> Cow<[BasicBlock]> { + use self::TerminatorKind::*; match *self { Goto { target: ref b } => slice::ref_slice(b).into_cow(), If { targets: (b1, b2), .. } => vec![b1, b2].into_cow(), @@ -314,7 +362,7 @@ impl<'tcx> Terminator<'tcx> { // FIXME: no mootable cow. I’m honestly not sure what a “cow” between `&mut [BasicBlock]` and // `Vec<&mut BasicBlock>` would look like in the first place. pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> { - use self::Terminator::*; + use self::TerminatorKind::*; match *self { Goto { target: ref mut b } => vec![b], If { targets: (ref mut b1, ref mut b2), .. } => vec![b1, b2], @@ -354,7 +402,7 @@ impl<'tcx> BasicBlockData<'tcx> { } } -impl<'tcx> Debug for Terminator<'tcx> { +impl<'tcx> Debug for TerminatorKind<'tcx> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { self.fmt_head(fmt)?; let successors = self.successors(); @@ -381,12 +429,12 @@ impl<'tcx> Debug for Terminator<'tcx> { } } -impl<'tcx> Terminator<'tcx> { +impl<'tcx> TerminatorKind<'tcx> { /// Write the "head" part of the terminator; that is, its name and the data it uses to pick the /// successor basic block, if any. The only information not inlcuded is the list of possible /// successors, which may be rendered differently between the text and the graphviz format. pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { - use self::Terminator::*; + use self::TerminatorKind::*; match *self { Goto { .. } => write!(fmt, "goto"), If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv), @@ -413,7 +461,7 @@ impl<'tcx> Terminator<'tcx> { /// Return the list of labels for the edges to the successor basic blocks. pub fn fmt_successor_labels(&self) -> Vec> { - use self::Terminator::*; + use self::TerminatorKind::*; match *self { Return | Resume => vec![], Goto { .. } => vec!["".into()], @@ -452,6 +500,7 @@ impl<'tcx> Terminator<'tcx> { #[derive(Clone, RustcEncodable, RustcDecodable)] pub struct Statement<'tcx> { pub span: Span, + pub scope: ScopeId, pub kind: StatementKind<'tcx>, } @@ -468,6 +517,7 @@ impl<'tcx> Debug for Statement<'tcx> { } } } + /////////////////////////////////////////////////////////////////////////// // Lvalues @@ -613,13 +663,50 @@ impl<'tcx> Debug for Lvalue<'tcx> { } } +/////////////////////////////////////////////////////////////////////////// +// Scopes + +impl Index for Vec { + type Output = ScopeData; + + #[inline] + fn index(&self, index: ScopeId) -> &ScopeData { + &self[index.index()] + } +} + +impl IndexMut for Vec { + #[inline] + fn index_mut(&mut self, index: ScopeId) -> &mut ScopeData { + &mut self[index.index()] + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)] +pub struct ScopeId(u32); + +impl ScopeId { + pub fn new(index: usize) -> ScopeId { + assert!(index < (u32::MAX as usize)); + ScopeId(index as u32) + } + + pub fn index(self) -> usize { + self.0 as usize + } +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct ScopeData { + pub parent_scope: Option, +} + /////////////////////////////////////////////////////////////////////////// // Operands -// + /// These are values that can appear inside an rvalue (or an index /// lvalue). They are intentionally limited to prevent rvalues from /// being nested in one another. - #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] pub enum Operand<'tcx> { Consume(Lvalue<'tcx>), diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 36d45f0a51ec2..bc0056b0af02f 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -8,12 +8,79 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use middle::const_eval::ConstVal; use middle::def_id::DefId; -use middle::ty::Region; +use middle::subst::Substs; +use middle::ty::{ClosureSubsts, FnOutput, Region, Ty}; use mir::repr::*; +use rustc_const_eval::ConstUsize; use rustc_data_structures::tuple_slice::TupleSlice; use syntax::codemap::Span; +// # The MIR Visitor +// +// ## Overview +// +// There are two visitors, one for immutable and one for mutable references, +// but both are generated by the following macro. The code is written according +// to the following conventions: +// +// - introduce a `visit_foo` and a `super_foo` method for every MIR type +// - `visit_foo`, by default, calls `super_foo` +// - `super_foo`, by default, destructures the `foo` and calls `visit_foo` +// +// This allows you as a user to override `visit_foo` for types are +// interested in, and invoke (within that method) call +// `self.super_foo` to get the default behavior. Just as in an OO +// language, you should never call `super` methods ordinarily except +// in that circumstance. +// +// For the most part, we do not destructure things external to the +// MIR, e.g. types, spans, etc, but simply visit them and stop. This +// avoids duplication with other visitors like `TypeFoldable`. But +// there is one exception: we do destructure the `FnOutput` to reach +// the type within. Just because. +// +// ## Updating +// +// The code is written in a very deliberate style intended to minimize +// the chance of things being overlooked. You'll notice that we always +// use pattern matching to reference fields and we ensure that all +// matches are exhaustive. +// +// For example, the `super_basic_block_data` method begins like this: +// +// ```rust +// fn super_basic_block_data(&mut self, +// block: BasicBlock, +// data: & $($mutability)* BasicBlockData<'tcx>) { +// let BasicBlockData { +// ref $($mutability)* statements, +// ref $($mutability)* terminator, +// is_cleanup: _ +// } = *data; +// +// for statement in statements { +// self.visit_statement(block, statement); +// } +// +// ... +// } +// ``` +// +// Here we used `let BasicBlockData { } = *data` deliberately, +// rather than writing `data.statements` in the body. This is because if one +// adds a new field to `BasicBlockData`, one will be forced to revise this code, +// and hence one will (hopefully) invoke the correct visit methods (if any). +// +// For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS. +// That means you never write `..` to skip over fields, nor do you write `_` +// to skip over variants in a `match`. +// +// The only place that `_` is acceptable is to match a field (or +// variant argument) that does not require visiting, as in +// `is_cleanup` above. + macro_rules! make_mir_visitor { ($visitor_trait_name:ident, $($mutability:ident)*) => { pub trait $visitor_trait_name<'tcx> { @@ -30,6 +97,11 @@ macro_rules! make_mir_visitor { self.super_basic_block_data(block, data); } + fn visit_scope_data(&mut self, + scope_data: & $($mutability)* ScopeData) { + self.super_scope_data(scope_data); + } + fn visit_statement(&mut self, block: BasicBlock, statement: & $($mutability)* Statement<'tcx>) { @@ -49,6 +121,12 @@ macro_rules! make_mir_visitor { self.super_terminator(block, terminator); } + fn visit_terminator_kind(&mut self, + block: BasicBlock, + kind: & $($mutability)* TerminatorKind<'tcx>) { + self.super_terminator_kind(block, kind); + } + fn visit_rvalue(&mut self, rvalue: & $($mutability)* Rvalue<'tcx>) { self.super_rvalue(rvalue); @@ -65,6 +143,18 @@ macro_rules! make_mir_visitor { self.super_lvalue(lvalue, context); } + fn visit_projection(&mut self, + lvalue: & $($mutability)* LvalueProjection<'tcx>, + context: LvalueContext) { + self.super_projection(lvalue, context); + } + + fn visit_projection_elem(&mut self, + lvalue: & $($mutability)* LvalueElem<'tcx>, + context: LvalueContext) { + self.super_projection_elem(lvalue, context); + } + fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { @@ -91,35 +181,143 @@ macro_rules! make_mir_visitor { self.super_span(span); } + fn visit_fn_output(&mut self, + fn_output: & $($mutability)* FnOutput<'tcx>) { + self.super_fn_output(fn_output); + } + + fn visit_ty(&mut self, + ty: & $($mutability)* Ty<'tcx>) { + self.super_ty(ty); + } + + fn visit_substs(&mut self, + substs: & $($mutability)* &'tcx Substs<'tcx>) { + self.super_substs(substs); + } + + fn visit_closure_substs(&mut self, + substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) { + self.super_closure_substs(substs); + } + + fn visit_const_val(&mut self, + const_val: & $($mutability)* ConstVal) { + self.super_const_val(const_val); + } + + fn visit_const_usize(&mut self, + const_usize: & $($mutability)* ConstUsize) { + self.super_const_usize(const_usize); + } + + fn visit_typed_const_val(&mut self, + val: & $($mutability)* TypedConstVal<'tcx>) { + self.super_typed_const_val(val); + } + + fn visit_var_decl(&mut self, + var_decl: & $($mutability)* VarDecl<'tcx>) { + self.super_var_decl(var_decl); + } + + fn visit_temp_decl(&mut self, + temp_decl: & $($mutability)* TempDecl<'tcx>) { + self.super_temp_decl(temp_decl); + } + + fn visit_arg_decl(&mut self, + arg_decl: & $($mutability)* ArgDecl<'tcx>) { + self.super_arg_decl(arg_decl); + } + + fn visit_scope_id(&mut self, + scope_id: & $($mutability)* ScopeId) { + self.super_scope_id(scope_id); + } + // The `super_xxx` methods comprise the default behavior and are // not meant to be overridden. fn super_mir(&mut self, mir: & $($mutability)* Mir<'tcx>) { - for block in mir.all_basic_blocks() { - let data = & $($mutability)* mir[block]; + let Mir { + ref $($mutability)* basic_blocks, + ref $($mutability)* scopes, + ref $($mutability)* return_ty, + ref $($mutability)* var_decls, + ref $($mutability)* arg_decls, + ref $($mutability)* temp_decls, + ref $($mutability)* span, + } = *mir; + + for (index, data) in basic_blocks.into_iter().enumerate() { + let block = BasicBlock::new(index); self.visit_basic_block_data(block, data); } + + for scope in scopes { + self.visit_scope_data(scope); + } + + self.visit_fn_output(return_ty); + + for var_decl in var_decls { + self.visit_var_decl(var_decl); + } + + for arg_decl in arg_decls { + self.visit_arg_decl(arg_decl); + } + + for temp_decl in temp_decls { + self.visit_temp_decl(temp_decl); + } + + self.visit_span(span); } fn super_basic_block_data(&mut self, block: BasicBlock, data: & $($mutability)* BasicBlockData<'tcx>) { - for statement in & $($mutability)* data.statements { + let BasicBlockData { + ref $($mutability)* statements, + ref $($mutability)* terminator, + is_cleanup: _ + } = *data; + + for statement in statements { self.visit_statement(block, statement); } - if let Some(ref $($mutability)* terminator) = data.terminator { + if let Some(ref $($mutability)* terminator) = *terminator { self.visit_terminator(block, terminator); } } + fn super_scope_data(&mut self, + scope_data: & $($mutability)* ScopeData) { + let ScopeData { + ref $($mutability)* parent_scope, + } = *scope_data; + + if let Some(ref $($mutability)* parent_scope) = *parent_scope { + self.visit_scope_id(parent_scope); + } + } + fn super_statement(&mut self, block: BasicBlock, statement: & $($mutability)* Statement<'tcx>) { - self.visit_span(& $($mutability)* statement.span); - - match statement.kind { + let Statement { + ref $($mutability)* span, + ref $($mutability)* scope, + ref $($mutability)* kind, + } = *statement; + + self.visit_span(span); + self.visit_scope_id(scope); + match *kind { StatementKind::Assign(ref $($mutability)* lvalue, ref $($mutability)* rvalue) => { self.visit_assign(block, lvalue, rvalue); @@ -138,52 +336,72 @@ macro_rules! make_mir_visitor { fn super_terminator(&mut self, block: BasicBlock, terminator: &$($mutability)* Terminator<'tcx>) { - match *terminator { - Terminator::Goto { target } => { + let Terminator { + ref $($mutability)* span, + ref $($mutability)* scope, + ref $($mutability)* kind, + } = *terminator; + + self.visit_span(span); + self.visit_scope_id(scope); + self.visit_terminator_kind(block, kind); + } + + fn super_terminator_kind(&mut self, + block: BasicBlock, + kind: & $($mutability)* TerminatorKind<'tcx>) { + match *kind { + TerminatorKind::Goto { target } => { self.visit_branch(block, target); } - Terminator::If { ref $($mutability)* cond, - ref $($mutability)* targets } => { + TerminatorKind::If { ref $($mutability)* cond, + ref $($mutability)* targets } => { self.visit_operand(cond); for &target in targets.as_slice() { self.visit_branch(block, target); } } - Terminator::Switch { ref $($mutability)* discr, - adt_def: _, - ref targets } => { + TerminatorKind::Switch { ref $($mutability)* discr, + adt_def: _, + ref targets } => { self.visit_lvalue(discr, LvalueContext::Inspect); for &target in targets { self.visit_branch(block, target); } } - Terminator::SwitchInt { ref $($mutability)* discr, - switch_ty: _, - values: _, - ref targets } => { + TerminatorKind::SwitchInt { ref $($mutability)* discr, + ref $($mutability)* switch_ty, + ref $($mutability)* values, + ref targets } => { self.visit_lvalue(discr, LvalueContext::Inspect); + self.visit_ty(switch_ty); + for value in values { + self.visit_const_val(value); + } for &target in targets { self.visit_branch(block, target); } } - Terminator::Resume | - Terminator::Return => { + TerminatorKind::Resume | + TerminatorKind::Return => { } - Terminator::Drop { ref $($mutability)* value, target, unwind } => { + TerminatorKind::Drop { ref $($mutability)* value, + target, + unwind } => { self.visit_lvalue(value, LvalueContext::Drop); self.visit_branch(block, target); unwind.map(|t| self.visit_branch(block, t)); } - Terminator::Call { ref $($mutability)* func, - ref $($mutability)* args, - ref $($mutability)* destination, - cleanup } => { + TerminatorKind::Call { ref $($mutability)* func, + ref $($mutability)* args, + ref $($mutability)* destination, + cleanup } => { self.visit_operand(func); for arg in args { self.visit_operand(arg); @@ -205,8 +423,9 @@ macro_rules! make_mir_visitor { } Rvalue::Repeat(ref $($mutability)* value, - _) => { + ref $($mutability)* typed_const_val) => { self.visit_operand(value); + self.visit_typed_const_val(typed_const_val); } Rvalue::Ref(r, bk, ref $($mutability)* path) => { @@ -220,34 +439,48 @@ macro_rules! make_mir_visitor { self.visit_lvalue(path, LvalueContext::Inspect); } - Rvalue::Cast(_, ref $($mutability)* operand, _) => { + Rvalue::Cast(_cast_kind, + ref $($mutability)* operand, + ref $($mutability)* ty) => { self.visit_operand(operand); + self.visit_ty(ty); } - Rvalue::BinaryOp(_, + Rvalue::BinaryOp(_bin_op, ref $($mutability)* lhs, ref $($mutability)* rhs) => { self.visit_operand(lhs); self.visit_operand(rhs); } - Rvalue::UnaryOp(_, ref $($mutability)* op) => { + Rvalue::UnaryOp(_un_op, ref $($mutability)* op) => { self.visit_operand(op); } - Rvalue::Box(_) => { + Rvalue::Box(ref $($mutability)* ty) => { + self.visit_ty(ty); } Rvalue::Aggregate(ref $($mutability)* kind, ref $($mutability)* operands) => { match *kind { - AggregateKind::Closure(ref $($mutability)* def_id, _) => { + AggregateKind::Vec => { + } + AggregateKind::Tuple => { + } + AggregateKind::Adt(_adt_def, + _variant_index, + ref $($mutability)* substs) => { + self.visit_substs(substs); + } + AggregateKind::Closure(ref $($mutability)* def_id, + ref $($mutability)* closure_substs) => { self.visit_def_id(def_id); + self.visit_closure_substs(closure_substs); } - _ => { /* nothing to do */ } } - for operand in & $($mutability)* operands[..] { + for operand in operands { self.visit_operand(operand); } } @@ -262,7 +495,8 @@ macro_rules! make_mir_visitor { } Rvalue::InlineAsm { ref $($mutability)* outputs, - ref $($mutability)* inputs, .. } => { + ref $($mutability)* inputs, + asm: _ } => { for output in & $($mutability)* outputs[..] { self.visit_lvalue(output, LvalueContext::Store); } @@ -287,7 +521,7 @@ macro_rules! make_mir_visitor { fn super_lvalue(&mut self, lvalue: & $($mutability)* Lvalue<'tcx>, - _context: LvalueContext) { + context: LvalueContext) { match *lvalue { Lvalue::Var(_) | Lvalue::Temp(_) | @@ -298,12 +532,81 @@ macro_rules! make_mir_visitor { self.visit_def_id(def_id); } Lvalue::Projection(ref $($mutability)* proj) => { - self.visit_lvalue(& $($mutability)* proj.base, - LvalueContext::Projection); + self.visit_projection(proj, context); } } } + fn super_projection(&mut self, + proj: & $($mutability)* LvalueProjection<'tcx>, + context: LvalueContext) { + let Projection { + ref $($mutability)* base, + ref $($mutability)* elem, + } = *proj; + self.visit_lvalue(base, LvalueContext::Projection); + self.visit_projection_elem(elem, context); + } + + fn super_projection_elem(&mut self, + proj: & $($mutability)* LvalueElem<'tcx>, + _context: LvalueContext) { + match *proj { + ProjectionElem::Deref => { + } + ProjectionElem::Field(_field, ref $($mutability)* ty) => { + self.visit_ty(ty); + } + ProjectionElem::Index(ref $($mutability)* operand) => { + self.visit_operand(operand); + } + ProjectionElem::ConstantIndex { offset: _, + min_length: _, + from_end: _ } => { + } + ProjectionElem::Downcast(_adt_def, _variant_index) => { + } + } + } + + fn super_var_decl(&mut self, + var_decl: & $($mutability)* VarDecl<'tcx>) { + let VarDecl { + mutability: _, + name: _, + ref $($mutability)* ty, + ref $($mutability)* scope, + ref $($mutability)* span, + } = *var_decl; + + self.visit_ty(ty); + self.visit_scope_id(scope); + self.visit_span(span); + } + + fn super_temp_decl(&mut self, + temp_decl: & $($mutability)* TempDecl<'tcx>) { + let TempDecl { + ref $($mutability)* ty, + } = *temp_decl; + + self.visit_ty(ty); + } + + fn super_arg_decl(&mut self, + arg_decl: & $($mutability)* ArgDecl<'tcx>) { + let ArgDecl { + ref $($mutability)* ty, + spread: _ + } = *arg_decl; + + self.visit_ty(ty); + } + + fn super_scope_id(&mut self, + _scope_id: & $($mutability)* ScopeId) { + } + fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { @@ -312,17 +615,32 @@ macro_rules! make_mir_visitor { fn super_constant(&mut self, constant: & $($mutability)* Constant<'tcx>) { self.visit_span(& $($mutability)* constant.span); + self.visit_ty(& $($mutability)* constant.ty); self.visit_literal(& $($mutability)* constant.literal); } + fn super_typed_const_val(&mut self, + constant: & $($mutability)* TypedConstVal<'tcx>) { + let TypedConstVal { + ref $($mutability)* span, + ref $($mutability)* ty, + ref $($mutability)* value, + } = *constant; + self.visit_span(span); + self.visit_ty(ty); + self.visit_const_usize(value); + } + fn super_literal(&mut self, literal: & $($mutability)* Literal<'tcx>) { match *literal { - Literal::Item { ref $($mutability)* def_id, .. } => { + Literal::Item { ref $($mutability)* def_id, + ref $($mutability)* substs } => { self.visit_def_id(def_id); + self.visit_substs(substs); }, - Literal::Value { .. } => { - // Nothing to do + Literal::Value { ref $($mutability)* value } => { + self.visit_const_val(value); } } } @@ -332,6 +650,32 @@ macro_rules! make_mir_visitor { fn super_span(&mut self, _span: & $($mutability)* Span) { } + + fn super_fn_output(&mut self, fn_output: & $($mutability)* FnOutput<'tcx>) { + match *fn_output { + FnOutput::FnConverging(ref $($mutability)* ty) => { + self.visit_ty(ty); + } + FnOutput::FnDiverging => { + } + } + } + + fn super_ty(&mut self, _ty: & $($mutability)* Ty<'tcx>) { + } + + fn super_substs(&mut self, _substs: & $($mutability)* &'tcx Substs<'tcx>) { + } + + fn super_closure_substs(&mut self, + _substs: & $($mutability)* &'tcx ClosureSubsts<'tcx>) { + } + + fn super_const_val(&mut self, _substs: & $($mutability)* ConstVal) { + } + + fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) { + } } } } diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 17f70b2d8dc66..bf532d9ccf9ed 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -663,6 +663,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print the result of the translation item collection pass"), mir_opt_level: Option = (None, parse_opt_uint, "set the MIR optimization level (0-3)"), + dump_mir: Option = (None, parse_opt_string, + "dump MIR state at various points in translation"), orbit: bool = (false, parse_bool, "get MIR where it belongs - everywhere; most importantly, in orbit"), } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs index 69aaae91c49fa..3c3a60b995ff0 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow.rs @@ -410,29 +410,28 @@ impl DataflowState { bb: &repr::BasicBlockData, on_return: OnReturn) where OnReturn: Fn(&D, &mut [usize], &repr::Lvalue) { - let term = if let Some(ref term) = bb.terminator { term } else { return }; - match *term { - repr::Terminator::Return | - repr::Terminator::Resume => {} - repr::Terminator::Goto { ref target } | - repr::Terminator::Drop { ref target, value: _, unwind: None } => { + match bb.terminator().kind { + repr::TerminatorKind::Return | + repr::TerminatorKind::Resume => {} + repr::TerminatorKind::Goto { ref target } | + repr::TerminatorKind::Drop { ref target, value: _, unwind: None } => { self.propagate_bits_into_entry_set_for(in_out, changed, target); } - repr::Terminator::Drop { ref target, value: _, unwind: Some(ref unwind) } => { + repr::TerminatorKind::Drop { ref target, value: _, unwind: Some(ref unwind) } => { self.propagate_bits_into_entry_set_for(in_out, changed, target); self.propagate_bits_into_entry_set_for(in_out, changed, unwind); } - repr::Terminator::If { ref targets, .. } => { + repr::TerminatorKind::If { ref targets, .. } => { self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0); self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1); } - repr::Terminator::Switch { ref targets, .. } | - repr::Terminator::SwitchInt { ref targets, .. } => { + repr::TerminatorKind::Switch { ref targets, .. } | + repr::TerminatorKind::SwitchInt { ref targets, .. } => { for target in targets { self.propagate_bits_into_entry_set_for(in_out, changed, target); } } - repr::Terminator::Call { ref cleanup, ref destination, func: _, args: _ } => { + repr::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => { if let Some(ref unwind) = *cleanup { self.propagate_bits_into_entry_set_for(in_out, changed, unwind); } diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 826284f1d78c0..46eb3d3ca03e5 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -9,9 +9,8 @@ // except according to those terms. -use rustc::middle::ty; -use rustc::mir::repr::{self, Mir, BasicBlock, Lvalue, Rvalue}; -use rustc::mir::repr::{StatementKind, Terminator}; +use rustc::middle::ty::TyCtxt; +use rustc::mir::repr::*; use rustc::util::nodemap::FnvHashMap; use std::cell::{Cell}; @@ -361,7 +360,7 @@ impl<'tcx> MovePathLookup<'tcx> { } fn lookup_proj(&mut self, - proj: &repr::LvalueProjection<'tcx>, + proj: &LvalueProjection<'tcx>, base: MovePathIndex) -> Lookup { let MovePathLookup { ref mut projections, ref mut next_index, .. } = *self; @@ -484,7 +483,7 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> { } impl<'tcx> MoveData<'tcx> { - pub fn gather_moves(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> Self { + pub fn gather_moves(mir: &Mir<'tcx>, tcx: &TyCtxt<'tcx>) -> Self { gather_moves(mir, tcx) } } @@ -495,7 +494,7 @@ enum StmtKind { Aggregate, Drop, CallFn, CallArg, Return, } -fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> { +fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &TyCtxt<'tcx>) -> MoveData<'tcx> { use self::StmtKind as SK; let bbs = mir.all_basic_blocks(); @@ -554,9 +553,9 @@ fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> Rvalue::Box(ref _ty) => { // this is creating uninitialized // memory that needs to be initialized. - let deref_lval = Lvalue::Projection(Box::new( repr::Projection { + let deref_lval = Lvalue::Projection(Box::new(Projection { base: lval.clone(), - elem: repr::ProjectionElem::Deref, + elem: ProjectionElem::Deref, })); bb_ctxt.on_move_out_lval(SK::Box, &deref_lval, source); } @@ -577,50 +576,48 @@ fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> } } - if let Some(ref term) = bb_data.terminator { - match *term { - Terminator::Goto { target: _ } | Terminator::Resume => { } + match bb_data.terminator().kind { + TerminatorKind::Goto { target: _ } | TerminatorKind::Resume => { } - Terminator::Return => { - let source = Location { block: bb, - index: bb_data.statements.len() }; - let lval = &Lvalue::ReturnPointer.deref(); - bb_ctxt.on_move_out_lval(SK::Return, lval, source); - } + TerminatorKind::Return => { + let source = Location { block: bb, + index: bb_data.statements.len() }; + let lval = &Lvalue::ReturnPointer.deref(); + bb_ctxt.on_move_out_lval(SK::Return, lval, source); + } - Terminator::If { ref cond, targets: _ } => { - // The `cond` is always of (copyable) type `bool`, - // so there will never be anything to move. - let _ = cond; - } + TerminatorKind::If { ref cond, targets: _ } => { + // The `cond` is always of (copyable) type `bool`, + // so there will never be anything to move. + let _ = cond; + } - Terminator::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } | - Terminator::Switch { adt_def: _, targets: _, ref discr } => { - // The `discr` is not consumed; that is instead - // encoded on specific match arms (and for - // SwitchInt`, it is always a copyable integer - // type anyway). - let _ = discr; - } + TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } | + TerminatorKind::Switch { adt_def: _, targets: _, ref discr } => { + // The `discr` is not consumed; that is instead + // encoded on specific match arms (and for + // SwitchInt`, it is always a copyable integer + // type anyway). + let _ = discr; + } - Terminator::Drop { value: ref lval, target: _, unwind: _ } => { - let source = Location { block: bb, - index: bb_data.statements.len() }; - bb_ctxt.on_move_out_lval(SK::Drop, lval, source); - } + TerminatorKind::Drop { value: ref lval, target: _, unwind: _ } => { + let source = Location { block: bb, + index: bb_data.statements.len() }; + bb_ctxt.on_move_out_lval(SK::Drop, lval, source); + } - Terminator::Call { ref func, ref args, ref destination, cleanup: _ } => { - let source = Location { block: bb, - index: bb_data.statements.len() }; - bb_ctxt.on_operand(SK::CallFn, func, source); - for arg in args { - bb_ctxt.on_operand(SK::CallArg, arg, source); - } - if let Some((ref destination, _bb)) = *destination { - // Create MovePath for `destination`, then - // discard returned index. - bb_ctxt.builder.move_path_for(destination); - } + TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { + let source = Location { block: bb, + index: bb_data.statements.len() }; + bb_ctxt.on_operand(SK::CallFn, func, source); + for arg in args { + bb_ctxt.on_operand(SK::CallArg, arg, source); + } + if let Some((ref destination, _bb)) = *destination { + // Create MovePath for `destination`, then + // discard returned index. + bb_ctxt.builder.move_path_for(destination); } } } @@ -670,7 +667,7 @@ fn gather_moves<'tcx>(mir: &Mir<'tcx>, tcx: &ty::TyCtxt<'tcx>) -> MoveData<'tcx> } struct BlockContext<'b, 'a: 'b, 'tcx: 'a> { - tcx: &'b ty::TyCtxt<'tcx>, + tcx: &'b TyCtxt<'tcx>, moves: &'b mut Vec, builder: MovePathDataBuilder<'a, 'tcx>, path_map: &'b mut Vec>, @@ -680,7 +677,7 @@ struct BlockContext<'b, 'a: 'b, 'tcx: 'a> { impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> { fn on_move_out_lval(&mut self, stmt_kind: StmtKind, - lval: &repr::Lvalue<'tcx>, + lval: &Lvalue<'tcx>, source: Location) { let tcx = self.tcx; let lval_ty = self.builder.mir.lvalue_ty(tcx, lval); @@ -726,10 +723,10 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> { self.loc_map_bb[i].push(index); } - fn on_operand(&mut self, stmt_kind: StmtKind, operand: &repr::Operand<'tcx>, source: Location) { + fn on_operand(&mut self, stmt_kind: StmtKind, operand: &Operand<'tcx>, source: Location) { match *operand { - repr::Operand::Constant(..) => {} // not-a-move - repr::Operand::Consume(ref lval) => { // a move + Operand::Constant(..) => {} // not-a-move + Operand::Consume(ref lval) => { // a move self.on_move_out_lval(stmt_kind, lval, source); } } diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 4c80eab102fc5..ef48a408e79a7 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -20,7 +20,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { ast_block: &'tcx hir::Block) -> BlockAnd<()> { let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block); - self.in_scope(extent, block, move |this| { + self.in_scope(extent, block, move |this, _| { // This convoluted structure is to avoid using recursion as we walk down a list // of statements. Basically, the structure we get back is something like: // @@ -42,23 +42,24 @@ impl<'a,'tcx> Builder<'a,'tcx> { let Stmt { span: _, kind } = this.hir.mirror(stmt); match kind { StmtKind::Expr { scope, expr } => { - unpack!(block = this.in_scope(scope, block, |this| { + unpack!(block = this.in_scope(scope, block, |this, _| { let expr = this.hir.mirror(expr); + let expr_span = expr.span; let temp = this.temp(expr.ty.clone()); unpack!(block = this.into(&temp, block, expr)); - unpack!(block = this.build_drop(block, temp)); + unpack!(block = this.build_drop(block, expr_span, temp)); block.unit() })); } StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => { - this.push_scope(remainder_scope); + let remainder_scope_id = this.push_scope(remainder_scope, block); let_extent_stack.push(remainder_scope); - unpack!(block = this.in_scope(init_scope, block, move |this| { + unpack!(block = this.in_scope(init_scope, block, move |this, _| { // FIXME #30046 ^~~~ if let Some(init) = initializer { - this.expr_into_pattern(block, remainder_scope, pattern, init) + this.expr_into_pattern(block, remainder_scope_id, pattern, init) } else { - this.declare_bindings(remainder_scope, &pattern); + this.declare_bindings(remainder_scope_id, &pattern); block.unit() } })); @@ -71,7 +72,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { unpack!(block = this.into(destination, block, expr)); } else { // FIXME(#31472) - this.cfg.push_assign_unit(block, span, destination); + let scope_id = this.innermost_scope_id(); + this.cfg.push_assign_unit(block, scope_id, span, destination); } // Finally, we pop all the let scopes before exiting out from the scope of block // itself. diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index c7147d111aace..4859257f291c9 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -13,7 +13,7 @@ //! Routines for manipulating the control-flow graph. -use build::CFG; +use build::{CFG, Location}; use rustc::mir::repr::*; use syntax::codemap::Span; @@ -43,12 +43,19 @@ impl<'tcx> CFG<'tcx> { self.block_data_mut(block).statements.push(statement); } + pub fn current_location(&mut self, block: BasicBlock) -> Location { + let index = self.block_data(block).statements.len(); + Location { block: block, statement_index: index } + } + pub fn push_assign(&mut self, block: BasicBlock, + scope: ScopeId, span: Span, lvalue: &Lvalue<'tcx>, rvalue: Rvalue<'tcx>) { self.push(block, Statement { + scope: scope, span: span, kind: StatementKind::Assign(lvalue.clone(), rvalue) }); @@ -56,26 +63,35 @@ impl<'tcx> CFG<'tcx> { pub fn push_assign_constant(&mut self, block: BasicBlock, + scope: ScopeId, span: Span, temp: &Lvalue<'tcx>, constant: Constant<'tcx>) { - self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant))); + self.push_assign(block, scope, span, temp, + Rvalue::Use(Operand::Constant(constant))); } pub fn push_assign_unit(&mut self, block: BasicBlock, + scope: ScopeId, span: Span, lvalue: &Lvalue<'tcx>) { - self.push_assign(block, span, lvalue, Rvalue::Aggregate( + self.push_assign(block, scope, span, lvalue, Rvalue::Aggregate( AggregateKind::Tuple, vec![] )); } pub fn terminate(&mut self, block: BasicBlock, - terminator: Terminator<'tcx>) { + scope: ScopeId, + span: Span, + kind: TerminatorKind<'tcx>) { debug_assert!(self.block_data(block).terminator.is_none(), "terminate: block {:?} already has a terminator set", block); - self.block_data_mut(block).terminator = Some(terminator); + self.block_data_mut(block).terminator = Some(Terminator { + span: span, + scope: scope, + kind: kind, + }); } } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index b2c7507ed7b28..0c9323f4af378 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -34,10 +34,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { debug!("expr_as_lvalue(block={:?}, expr={:?})", block, expr); let this = self; + let scope_id = this.innermost_scope_id(); let expr_span = expr.span; match expr.kind { ExprKind::Scope { extent, value } => { - this.in_scope(extent, block, |this| this.as_lvalue(block, value)) + this.in_scope(extent, block, |this, _| this.as_lvalue(block, value)) } ExprKind::Field { lhs, name } => { let lvalue = unpack!(block = this.as_lvalue(block, lhs)); @@ -58,16 +59,18 @@ impl<'a,'tcx> Builder<'a,'tcx> { // bounds check: let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty)); - this.cfg.push_assign(block, expr_span, // len = len(slice) + this.cfg.push_assign(block, scope_id, expr_span, // len = len(slice) &len, Rvalue::Len(slice.clone())); - this.cfg.push_assign(block, expr_span, // lt = idx < len + this.cfg.push_assign(block, scope_id, expr_span, // lt = idx < len <, Rvalue::BinaryOp(BinOp::Lt, idx.clone(), Operand::Consume(len.clone()))); let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block()); this.cfg.terminate(block, - Terminator::If { + scope_id, + expr_span, + TerminatorKind::If { cond: Operand::Consume(lt), targets: (success, failure), }); diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs index 7738ebca26b47..661d01ce989cd 100644 --- a/src/librustc_mir/build/expr/as_operand.rs +++ b/src/librustc_mir/build/expr/as_operand.rs @@ -35,7 +35,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let this = self; if let ExprKind::Scope { extent, value } = expr.kind { - return this.in_scope(extent, block, |this| this.as_operand(block, value)); + return this.in_scope(extent, block, |this, _| this.as_operand(block, value)); } let category = Category::of(&expr.kind).unwrap(); diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 4c0e9b98d9a6e..b340d933e64c3 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -33,11 +33,12 @@ impl<'a,'tcx> Builder<'a,'tcx> { debug!("expr_as_rvalue(block={:?}, expr={:?})", block, expr); let this = self; + let scope_id = this.innermost_scope_id(); let expr_span = expr.span; match expr.kind { ExprKind::Scope { extent, value } => { - this.in_scope(extent, block, |this| this.as_rvalue(block, value)) + this.in_scope(extent, block, |this, _| this.as_rvalue(block, value)) } ExprKind::InlineAsm { asm, outputs, inputs } => { let outputs = outputs.into_iter().map(|output| { @@ -75,8 +76,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { let value = this.hir.mirror(value); let result = this.temp(expr.ty); // to start, malloc some memory of suitable type (thus far, uninitialized): - this.cfg.push_assign(block, expr_span, &result, Rvalue::Box(value.ty)); - this.in_scope(value_extents, block, |this| { + this.cfg.push_assign(block, scope_id, expr_span, &result, Rvalue::Box(value.ty)); + this.in_scope(value_extents, block, |this, _| { // schedule a shallow free of that memory, lest we unwind: this.schedule_box_free(expr_span, value_extents, &result, value.ty); // initialize the box contents: diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index 27c374e1ac2c4..30a42bcd709d2 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -30,7 +30,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let this = self; if let ExprKind::Scope { extent, value } = expr.kind { - return this.in_scope(extent, block, |this| this.as_temp(block, value)); + return this.in_scope(extent, block, |this, _| this.as_temp(block, value)); } let expr_ty = expr.ty.clone(); @@ -55,7 +55,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { let expr_span = expr.span; let lvalue = unpack!(block = this.as_lvalue(block, expr)); let rvalue = Rvalue::Use(Operand::Consume(lvalue)); - this.cfg.push_assign(block, expr_span, &temp, rvalue); + let scope_id = this.innermost_scope_id(); + this.cfg.push_assign(block, scope_id, expr_span, &temp, rvalue); } _ => { unpack!(block = this.into(&temp, block, expr)); diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index a7f4a53b022e1..30c039cdde0c3 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -36,10 +36,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { // just use the name `this` uniformly let this = self; let expr_span = expr.span; + let scope_id = this.innermost_scope_id(); match expr.kind { ExprKind::Scope { extent, value } => { - this.in_scope(extent, block, |this| this.into(destination, block, value)) + this.in_scope(extent, block, |this, _| this.into(destination, block, value)) } ExprKind::Block { body: ast_block } => { this.ast_block(destination, block, ast_block) @@ -52,7 +53,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let mut then_block = this.cfg.start_new_block(); let mut else_block = this.cfg.start_new_block(); - this.cfg.terminate(block, Terminator::If { + this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::If { cond: operand, targets: (then_block, else_block) }); @@ -63,13 +64,20 @@ impl<'a,'tcx> Builder<'a,'tcx> { } else { // Body of the `if` expression without an `else` clause must return `()`, thus // we implicitly generate a `else {}` if it is not specified. - this.cfg.push_assign_unit(else_block, expr_span, destination); + let scope_id = this.innermost_scope_id(); + this.cfg.push_assign_unit(else_block, scope_id, expr_span, destination); else_block }; let join_block = this.cfg.start_new_block(); - this.cfg.terminate(then_block, Terminator::Goto { target: join_block }); - this.cfg.terminate(else_block, Terminator::Goto { target: join_block }); + this.cfg.terminate(then_block, + scope_id, + expr_span, + TerminatorKind::Goto { target: join_block }); + this.cfg.terminate(else_block, + scope_id, + expr_span, + TerminatorKind::Goto { target: join_block }); join_block.unit() } @@ -95,16 +103,19 @@ impl<'a,'tcx> Builder<'a,'tcx> { LogicalOp::And => (else_block, false_block), LogicalOp::Or => (true_block, else_block), }; - this.cfg.terminate(block, Terminator::If { cond: lhs, targets: blocks }); + this.cfg.terminate(block, + scope_id, + expr_span, + TerminatorKind::If { cond: lhs, targets: blocks }); let rhs = unpack!(else_block = this.as_operand(else_block, rhs)); - this.cfg.terminate(else_block, Terminator::If { + this.cfg.terminate(else_block, scope_id, expr_span, TerminatorKind::If { cond: rhs, targets: (true_block, false_block) }); this.cfg.push_assign_constant( - true_block, expr_span, destination, + true_block, scope_id, expr_span, destination, Constant { span: expr_span, ty: this.hir.bool_ty(), @@ -112,15 +123,21 @@ impl<'a,'tcx> Builder<'a,'tcx> { }); this.cfg.push_assign_constant( - false_block, expr_span, destination, + false_block, scope_id, expr_span, destination, Constant { span: expr_span, ty: this.hir.bool_ty(), literal: this.hir.false_literal(), }); - this.cfg.terminate(true_block, Terminator::Goto { target: join_block }); - this.cfg.terminate(false_block, Terminator::Goto { target: join_block }); + this.cfg.terminate(true_block, + scope_id, + expr_span, + TerminatorKind::Goto { target: join_block }); + this.cfg.terminate(false_block, + scope_id, + expr_span, + TerminatorKind::Goto { target: join_block }); join_block.unit() } @@ -144,7 +161,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { let exit_block = this.cfg.start_new_block(); // start the loop - this.cfg.terminate(block, Terminator::Goto { target: loop_block }); + this.cfg.terminate(block, + scope_id, + expr_span, + TerminatorKind::Goto { target: loop_block }); let might_break = this.in_loop_scope(loop_block, exit_block, move |this| { // conduct the test, if necessary @@ -157,7 +177,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr)); body_block = this.cfg.start_new_block(); this.cfg.terminate(loop_block_end, - Terminator::If { + scope_id, + expr_span, + TerminatorKind::If { cond: cond, targets: (body_block, exit_block) }); @@ -173,12 +195,15 @@ impl<'a,'tcx> Builder<'a,'tcx> { let tmp = this.get_unit_temp(); // Execute the body, branching back to the test. let body_block_end = unpack!(this.into(&tmp, body_block, body)); - this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block }); + this.cfg.terminate(body_block_end, + scope_id, + expr_span, + TerminatorKind::Goto { target: loop_block }); }); // If the loop may reach its exit_block, we assign an empty tuple to the // destination to keep the MIR well-formed. if might_break { - this.cfg.push_assign_unit(exit_block, expr_span, destination); + this.cfg.push_assign_unit(exit_block, scope_id, expr_span, destination); } exit_block.unit() } @@ -186,10 +211,12 @@ impl<'a,'tcx> Builder<'a,'tcx> { // Note: we evaluate assignments right-to-left. This // is better for borrowck interaction with overloaded // operators like x[j] = x[i]. + let lhs = this.hir.mirror(lhs); + let lhs_span = lhs.span; let rhs = unpack!(block = this.as_operand(block, rhs)); let lhs = unpack!(block = this.as_lvalue(block, lhs)); - unpack!(block = this.build_drop(block, lhs.clone())); - this.cfg.push_assign(block, expr_span, &lhs, Rvalue::Use(rhs)); + unpack!(block = this.build_drop(block, lhs_span, lhs.clone())); + this.cfg.push_assign(block, scope_id, expr_span, &lhs, Rvalue::Use(rhs)); block.unit() } ExprKind::AssignOp { op, lhs, rhs } => { @@ -208,7 +235,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // we don't have to drop prior contents or anything // because AssignOp is only legal for Copy types // (overloaded ops should be desugared into a call). - this.cfg.push_assign(block, expr_span, &lhs, + this.cfg.push_assign(block, scope_id, expr_span, &lhs, Rvalue::BinaryOp(op, Operand::Consume(lhs.clone()), rhs)); @@ -229,11 +256,12 @@ impl<'a,'tcx> Builder<'a,'tcx> { block = match value { Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)), None => { - this.cfg.push_assign_unit(block, expr_span, &Lvalue::ReturnPointer); + this.cfg.push_assign_unit(block, scope_id, + expr_span, &Lvalue::ReturnPointer); block } }; - let extent = this.extent_of_outermost_scope(); + let extent = this.extent_of_return_scope(); this.exit_scope(expr_span, extent, block, END_BLOCK); this.cfg.start_new_block().unit() } @@ -252,7 +280,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let success = this.cfg.start_new_block(); let cleanup = this.diverge_cleanup(); - this.cfg.terminate(block, Terminator::Call { + this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::Call { func: fun, args: args, cleanup: cleanup, @@ -293,7 +321,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }); let rvalue = unpack!(block = this.as_rvalue(block, expr)); - this.cfg.push_assign(block, expr_span, destination, rvalue); + this.cfg.push_assign(block, scope_id, expr_span, destination, rvalue); block.unit() } } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 673ff9e86c440..b1286e935b684 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -16,7 +16,6 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use rustc_data_structures::fnv::FnvHashMap; use rustc::middle::const_eval::ConstVal; -use rustc::middle::region::CodeExtent; use rustc::middle::ty::{AdtDef, Ty}; use rustc::mir::repr::*; use hair::*; @@ -42,9 +41,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { // suitable extent for all of the bindings in this match. It's // easiest to do this up front because some of these arms may // be unreachable or reachable multiple times. - let var_extent = self.extent_of_innermost_scope(); + let var_scope_id = self.innermost_scope_id(); for arm in &arms { - self.declare_bindings(var_extent, &arm.patterns[0]); + self.declare_bindings(var_scope_id, &arm.patterns[0]); } let mut arm_blocks = ArmBlocks { @@ -72,6 +71,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }) .map(|(arm_index, pattern, guard)| { Candidate { + span: pattern.span, match_pairs: vec![MatchPair::new(discriminant_lvalue.clone(), pattern)], bindings: vec![], guard: guard, @@ -88,7 +88,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // an empty vector to be returned here, but the algorithm is // not entirely precise if !otherwise.is_empty() { - let join_block = self.join_otherwise_blocks(otherwise); + let join_block = self.join_otherwise_blocks(span, otherwise); self.panic(join_block, "something about matches algorithm not being precise", span); } @@ -98,7 +98,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { for (arm_index, arm_body) in arm_bodies.into_iter().enumerate() { let mut arm_block = arm_blocks.blocks[arm_index]; unpack!(arm_block = self.into(destination, arm_block, arm_body)); - self.cfg.terminate(arm_block, Terminator::Goto { target: end_block }); + self.cfg.terminate(arm_block, + var_scope_id, + span, + TerminatorKind::Goto { target: end_block }); } end_block.unit() @@ -106,7 +109,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { pub fn expr_into_pattern(&mut self, mut block: BasicBlock, - var_extent: CodeExtent, // lifetime of vars + var_scope_id: ScopeId, // lifetime of vars irrefutable_pat: Pattern<'tcx>, initializer: ExprRef<'tcx>) -> BlockAnd<()> { @@ -118,7 +121,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { var, ty, subpattern: None } => { - let index = self.declare_binding(var_extent, + let index = self.declare_binding(var_scope_id, mutability, name, var, @@ -131,22 +134,23 @@ impl<'a,'tcx> Builder<'a,'tcx> { } let lvalue = unpack!(block = self.as_lvalue(block, initializer)); self.lvalue_into_pattern(block, - var_extent, + var_scope_id, irrefutable_pat, &lvalue) } pub fn lvalue_into_pattern(&mut self, mut block: BasicBlock, - var_extent: CodeExtent, + var_scope_id: ScopeId, irrefutable_pat: Pattern<'tcx>, initializer: &Lvalue<'tcx>) -> BlockAnd<()> { // first, creating the bindings - self.declare_bindings(var_extent, &irrefutable_pat); + self.declare_bindings(var_scope_id, &irrefutable_pat); // create a dummy candidate let mut candidate = Candidate { + span: irrefutable_pat.span, match_pairs: vec![MatchPair::new(initializer.clone(), &irrefutable_pat)], bindings: vec![], guard: None, @@ -170,29 +174,29 @@ impl<'a,'tcx> Builder<'a,'tcx> { block.unit() } - pub fn declare_bindings(&mut self, var_extent: CodeExtent, pattern: &Pattern<'tcx>) { + pub fn declare_bindings(&mut self, var_scope_id: ScopeId, pattern: &Pattern<'tcx>) { match *pattern.kind { PatternKind::Binding { mutability, name, mode: _, var, ty, ref subpattern } => { - self.declare_binding(var_extent, mutability, name, var, ty, pattern.span); + self.declare_binding(var_scope_id, mutability, name, var, ty, pattern.span); if let Some(subpattern) = subpattern.as_ref() { - self.declare_bindings(var_extent, subpattern); + self.declare_bindings(var_scope_id, subpattern); } } PatternKind::Array { ref prefix, ref slice, ref suffix } | PatternKind::Slice { ref prefix, ref slice, ref suffix } => { for subpattern in prefix.iter().chain(slice).chain(suffix) { - self.declare_bindings(var_extent, subpattern); + self.declare_bindings(var_scope_id, subpattern); } } PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => { } PatternKind::Deref { ref subpattern } => { - self.declare_bindings(var_extent, subpattern); + self.declare_bindings(var_scope_id, subpattern); } PatternKind::Leaf { ref subpatterns } | PatternKind::Variant { ref subpatterns, .. } => { for subpattern in subpatterns { - self.declare_bindings(var_extent, &subpattern.pattern); + self.declare_bindings(var_scope_id, &subpattern.pattern); } } } @@ -207,6 +211,9 @@ struct ArmBlocks { #[derive(Clone, Debug)] pub struct Candidate<'pat, 'tcx:'pat> { + // span of the original pattern that gave rise to this candidate + span: Span, + // all of these must be satisfied... match_pairs: Vec>, @@ -371,20 +378,25 @@ impl<'a,'tcx> Builder<'a,'tcx> { } // Otherwise, let's process those remaining candidates. - let join_block = self.join_otherwise_blocks(otherwise); + let join_block = self.join_otherwise_blocks(span, otherwise); self.match_candidates(span, arm_blocks, untested_candidates, join_block) } fn join_otherwise_blocks(&mut self, + span: Span, otherwise: Vec) -> BasicBlock { + let scope_id = self.innermost_scope_id(); if otherwise.len() == 1 { otherwise[0] } else { let join_block = self.cfg.start_new_block(); for block in otherwise { - self.cfg.terminate(block, Terminator::Goto { target: join_block }); + self.cfg.terminate(block, + scope_id, + span, + TerminatorKind::Goto { target: join_block }); } join_block } @@ -551,16 +563,25 @@ impl<'a,'tcx> Builder<'a,'tcx> { let arm_block = arm_blocks.blocks[candidate.arm_index]; + let scope_id = self.innermost_scope_id(); if let Some(guard) = candidate.guard { // the block to branch to if the guard fails; if there is no // guard, this block is simply unreachable + let guard = self.hir.mirror(guard); + let guard_span = guard.span; let cond = unpack!(block = self.as_operand(block, guard)); let otherwise = self.cfg.start_new_block(); - self.cfg.terminate(block, Terminator::If { cond: cond, - targets: (arm_block, otherwise)}); + self.cfg.terminate(block, + scope_id, + guard_span, + TerminatorKind::If { cond: cond, + targets: (arm_block, otherwise)}); Some(otherwise) } else { - self.cfg.terminate(block, Terminator::Goto { target: arm_block }); + self.cfg.terminate(block, + scope_id, + candidate.span, + TerminatorKind::Goto { target: arm_block }); None } } @@ -585,12 +606,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { Rvalue::Ref(region, borrow_kind, binding.source), }; - self.cfg.push_assign(block, binding.span, &Lvalue::Var(var_index), rvalue); + let scope_id = self.innermost_scope_id(); + self.cfg.push_assign(block, scope_id, binding.span, + &Lvalue::Var(var_index), rvalue); } } fn declare_binding(&mut self, - var_extent: CodeExtent, + var_scope_id: ScopeId, mutability: Mutability, name: Name, var_id: NodeId, @@ -598,17 +621,20 @@ impl<'a,'tcx> Builder<'a,'tcx> { span: Span) -> u32 { - debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_extent={:?}, span={:?})", - var_id, name, var_ty, var_extent, span); + debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_scope_id={:?}, span={:?})", + var_id, name, var_ty, var_scope_id, span); let index = self.var_decls.len(); self.var_decls.push(VarDecl::<'tcx> { + scope: var_scope_id, mutability: mutability, name: name, ty: var_ty.clone(), + span: span, }); let index = index as u32; - self.schedule_drop(span, var_extent, &Lvalue::Var(index), var_ty); + let extent = self.scope_auxiliary[var_scope_id].extent; + self.schedule_drop(span, extent, &Lvalue::Var(index), var_ty); self.var_indices.insert(var_id, index); debug!("declare_binding: index={:?}", index); diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 0efa24f311943..3211e5849a06c 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -146,13 +146,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { lvalue: &Lvalue<'tcx>, test: &Test<'tcx>) -> Vec { + let scope_id = self.innermost_scope_id(); match test.kind { TestKind::Switch { adt_def } => { let num_enum_variants = self.hir.num_variants(adt_def); let target_blocks: Vec<_> = (0..num_enum_variants).map(|_| self.cfg.start_new_block()) .collect(); - self.cfg.terminate(block, Terminator::Switch { + self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Switch { discr: lvalue.clone(), adt_def: adt_def, targets: target_blocks.clone() @@ -167,12 +168,15 @@ impl<'a,'tcx> Builder<'a,'tcx> { .map(|_| self.cfg.start_new_block()) .chain(Some(otherwise)) .collect(); - self.cfg.terminate(block, Terminator::SwitchInt { - discr: lvalue.clone(), - switch_ty: switch_ty, - values: options.clone(), - targets: targets.clone(), - }); + self.cfg.terminate(block, + scope_id, + test.span, + TerminatorKind::SwitchInt { + discr: lvalue.clone(), + switch_ty: switch_ty, + values: options.clone(), + targets: targets.clone(), + }); targets } @@ -189,7 +193,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { if let ty::TyArray(_, _) = mt.ty.sty { ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8)); let val_slice = self.temp(ty); - self.cfg.push_assign(block, test.span, &val_slice, + self.cfg.push_assign(block, scope_id, test.span, &val_slice, Rvalue::Cast(CastKind::Unsize, val, ty)); val = Operand::Consume(val_slice); } @@ -204,7 +208,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { }); let slice = self.temp(ty); - self.cfg.push_assign(block, test.span, &slice, + self.cfg.push_assign(block, scope_id, test.span, &slice, Rvalue::Cast(CastKind::Unsize, array, ty)); Operand::Consume(slice) } else { @@ -225,7 +229,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let eq_result = self.temp(bool_ty); let eq_block = self.cfg.start_new_block(); let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Call { + self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Call { func: Operand::Constant(Constant { span: test.span, ty: mty, @@ -238,7 +242,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // check the result let block = self.cfg.start_new_block(); - self.cfg.terminate(eq_block, Terminator::If { + self.cfg.terminate(eq_block, scope_id, test.span, TerminatorKind::If { cond: Operand::Consume(eq_result), targets: (block, fail), }); @@ -268,13 +272,15 @@ impl<'a,'tcx> Builder<'a,'tcx> { let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty)); // actual = len(lvalue) - self.cfg.push_assign(block, test.span, &actual, Rvalue::Len(lvalue.clone())); + self.cfg.push_assign(block, scope_id, test.span, + &actual, Rvalue::Len(lvalue.clone())); // expected = - let expected = self.push_usize(block, test.span, len); + let expected = self.push_usize(block, scope_id, test.span, len); // result = actual == expected OR result = actual < expected self.cfg.push_assign(block, + scope_id, test.span, &result, Rvalue::BinaryOp(op, @@ -284,7 +290,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // branch based on result let target_blocks: Vec<_> = vec![self.cfg.start_new_block(), self.cfg.start_new_block()]; - self.cfg.terminate(block, Terminator::If { + self.cfg.terminate(block, scope_id, test.span, TerminatorKind::If { cond: Operand::Consume(result), targets: (target_blocks[0], target_blocks[1]) }); @@ -305,11 +311,13 @@ impl<'a,'tcx> Builder<'a,'tcx> { let result = self.temp(bool_ty); // result = op(left, right) - self.cfg.push_assign(block, span, &result, Rvalue::BinaryOp(op, left, right)); + let scope_id = self.innermost_scope_id(); + self.cfg.push_assign(block, scope_id, span, &result, + Rvalue::BinaryOp(op, left, right)); // branch based on result let target_block = self.cfg.start_new_block(); - self.cfg.terminate(block, Terminator::If { + self.cfg.terminate(block, scope_id, span, TerminatorKind::If { cond: Operand::Consume(result), targets: (target_block, fail_block) }); @@ -462,6 +470,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { .map(|(_, mp)| mp.clone()) .collect(); Candidate { + span: candidate.span, match_pairs: other_match_pairs, bindings: candidate.bindings.clone(), guard: candidate.guard.clone(), @@ -503,6 +512,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect(); Candidate { + span: candidate.span, match_pairs: all_match_pairs, bindings: candidate.bindings.clone(), guard: candidate.guard.clone(), diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index b46c3ffb76a1b..101d7594309a9 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -61,7 +61,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { from_end: suffix_len, }; let temp = self.temp(slice.ty.clone()); // no need to schedule drop, temp is always copy - self.cfg.push_assign(block, slice.span, &temp, rvalue); + let scope_id = self.innermost_scope_id(); + self.cfg.push_assign(block, scope_id, slice.span, &temp, rvalue); match_pairs.push(MatchPair::new(temp, slice)); } diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index 13ab26c358d6a..0d1690783301b 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -46,11 +46,16 @@ impl<'a,'tcx> Builder<'a,'tcx> { Operand::Constant(constant) } - pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: u64) -> Lvalue<'tcx> { + pub fn push_usize(&mut self, + block: BasicBlock, + scope_id: ScopeId, + span: Span, + value: u64) + -> Lvalue<'tcx> { let usize_ty = self.hir.usize_ty(); let temp = self.temp(usize_ty); self.cfg.push_assign_constant( - block, span, &temp, + block, scope_id, span, &temp, Constant { span: span, ty: self.hir.usize_ty(), diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index b40775f939f75..b79f492179fb9 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -9,30 +9,106 @@ // except according to those terms. use hair::cx::Cx; -use rustc::middle::region::CodeExtent; +use rustc::middle::region::{CodeExtent, CodeExtentData}; use rustc::middle::ty::{FnOutput, Ty}; use rustc::mir::repr::*; use rustc_data_structures::fnv::FnvHashMap; use rustc_front::hir; - +use std::ops::{Index, IndexMut}; use syntax::ast; use syntax::codemap::Span; pub struct Builder<'a, 'tcx: 'a> { hir: Cx<'a, 'tcx>, cfg: CFG<'tcx>, + + fn_span: Span, + + // the current set of scopes, updated as we traverse; + // see the `scope` module for more details scopes: Vec>, + + // for each scope, a span of blocks that defines it; + // we track these for use in region and borrow checking, + // but these are liable to get out of date once optimization + // begins. They are also hopefully temporary, and will be + // no longer needed when we adopt graph-based regions. + scope_auxiliary: ScopeAuxiliaryVec, + + // the current set of loops; see the `scope` module for more + // details loop_scopes: Vec, + + // the vector of all scopes that we have created thus far; + // we track this for debuginfo later + scope_datas: Vec, + var_decls: Vec>, var_indices: FnvHashMap, temp_decls: Vec>, unit_temp: Option>, + + // cached block with a RESUME terminator; we create this at the + // first panic + cached_resume_block: Option, } struct CFG<'tcx> { basic_blocks: Vec>, } +/// For each scope, we track the extent (from the HIR) and a +/// single-entry-multiple-exit subgraph that contains all the +/// statements/terminators within it. +/// +/// This information is separated out from the main `ScopeData` +/// because it is short-lived. First, the extent contains node-ids, +/// so it cannot be saved and re-loaded. Second, any optimization will mess up +/// the dominator/postdominator information. +/// +/// The intention is basically to use this information to do +/// regionck/borrowck and then throw it away once we are done. +pub struct ScopeAuxiliary { + /// extent of this scope from the MIR. + pub extent: CodeExtent, + + /// "entry point": dominator of all nodes in the scope + pub dom: Location, + + /// "exit points": mutual postdominators of all nodes in the scope + pub postdoms: Vec, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Location { + /// the location is within this block + pub block: BasicBlock, + + /// the location is the start of the this statement; or, if `statement_index` + /// == num-statements, then the start of the terminator. + pub statement_index: usize, +} + +pub struct ScopeAuxiliaryVec { + pub vec: Vec +} + +impl Index for ScopeAuxiliaryVec { + type Output = ScopeAuxiliary; + + #[inline] + fn index(&self, index: ScopeId) -> &ScopeAuxiliary { + &self.vec[index.index()] + } +} + +impl IndexMut for ScopeAuxiliaryVec { + #[inline] + fn index_mut(&mut self, index: ScopeId) -> &mut ScopeAuxiliary { + &mut self.vec[index.index()] + } +} + /////////////////////////////////////////////////////////////////////////// /// The `BlockAnd` "monad" packages up the new basic block along with a /// produced value (sometimes just unit, of course). The `unpack!` @@ -81,46 +157,85 @@ macro_rules! unpack { pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, span: Span, + fn_id: ast::NodeId, + body_id: ast::NodeId, implicit_arguments: Vec>, explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>, - argument_extent: CodeExtent, return_ty: FnOutput<'tcx>, ast_block: &'tcx hir::Block) - -> Mir<'tcx> { + -> (Mir<'tcx>, ScopeAuxiliaryVec) { + let tcx = hir.tcx(); let cfg = CFG { basic_blocks: vec![] }; let mut builder = Builder { hir: hir, cfg: cfg, + fn_span: span, scopes: vec![], + scope_datas: vec![], + scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] }, loop_scopes: vec![], temp_decls: vec![], var_decls: vec![], var_indices: FnvHashMap(), unit_temp: None, + cached_resume_block: None, }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); assert_eq!(builder.cfg.start_new_block(), END_BLOCK); - let mut block = START_BLOCK; - let arg_decls = unpack!(block = builder.args_and_body(block, - implicit_arguments, - explicit_arguments, - argument_extent, - ast_block)); - - builder.cfg.terminate(block, Terminator::Goto { target: END_BLOCK }); - builder.cfg.terminate(END_BLOCK, Terminator::Return); - - Mir { - basic_blocks: builder.cfg.basic_blocks, - var_decls: builder.var_decls, - arg_decls: arg_decls, - temp_decls: builder.temp_decls, - return_ty: return_ty, - span: span - } + + let mut arg_decls = None; // assigned to `Some` in closures below + let call_site_extent = + tcx.region_maps.lookup_code_extent( + CodeExtentData::CallSiteScope { fn_id: fn_id, body_id: body_id }); + let _ = builder.in_scope(call_site_extent, START_BLOCK, |builder, call_site_scope_id| { + let mut block = START_BLOCK; + let arg_extent = + tcx.region_maps.lookup_code_extent( + CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id }); + unpack!(block = builder.in_scope(arg_extent, block, |builder, arg_scope_id| { + arg_decls = Some(unpack!(block = builder.args_and_body(block, + implicit_arguments, + explicit_arguments, + arg_scope_id, + ast_block))); + block.unit() + })); + + builder.cfg.terminate(block, call_site_scope_id, span, + TerminatorKind::Goto { target: END_BLOCK }); + builder.cfg.terminate(END_BLOCK, call_site_scope_id, span, + TerminatorKind::Return); + + END_BLOCK.unit() + }); + + assert!( + builder.cfg.basic_blocks + .iter() + .enumerate() + .all(|(index, block)| { + if block.terminator.is_none() { + panic!("no terminator on block {:?} in fn {:?}", + index, fn_id) + } + true + })); + + ( + Mir { + basic_blocks: builder.cfg.basic_blocks, + scopes: builder.scope_datas, + var_decls: builder.var_decls, + arg_decls: arg_decls.take().expect("args never built?"), + temp_decls: builder.temp_decls, + return_ty: return_ty, + span: span + }, + builder.scope_auxiliary, + ) } impl<'a,'tcx> Builder<'a,'tcx> { @@ -128,39 +243,40 @@ impl<'a,'tcx> Builder<'a,'tcx> { mut block: BasicBlock, implicit_arguments: Vec>, explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>, - argument_extent: CodeExtent, + argument_scope_id: ScopeId, ast_block: &'tcx hir::Block) -> BlockAnd>> { - self.in_scope(argument_extent, block, |this| { - // to start, translate the argument patterns and collect the argument types. - let implicits = implicit_arguments.into_iter().map(|ty| (ty, None)); - let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat))); + // to start, translate the argument patterns and collect the argument types. + let implicits = implicit_arguments.into_iter().map(|ty| (ty, None)); + let explicits = explicit_arguments.into_iter().map(|(ty, pat)| (ty, Some(pat))); let arg_decls = - implicits - .chain(explicits) - .enumerate() - .map(|(index, (ty, pattern))| { - let lvalue = Lvalue::Arg(index as u32); - if let Some(pattern) = pattern { - let pattern = this.hir.irrefutable_pat(pattern); - unpack!(block = this.lvalue_into_pattern(block, - argument_extent, - pattern, - &lvalue)); - } - // Make sure we drop (parts of) the argument even when not matched on. - this.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span), - argument_extent, &lvalue, ty); - ArgDecl { ty: ty, spread: false } - }) - .collect(); - - // start the first basic block and translate the body - unpack!(block = this.ast_block(&Lvalue::ReturnPointer, block, ast_block)); - - block.and(arg_decls) - }) + implicits + .chain(explicits) + .enumerate() + .map(|(index, (ty, pattern))| { + let lvalue = Lvalue::Arg(index as u32); + if let Some(pattern) = pattern { + let pattern = self.hir.irrefutable_pat(pattern); + unpack!(block = self.lvalue_into_pattern(block, + argument_scope_id, + pattern, + &lvalue)); + } + + // Make sure we drop (parts of) the argument even when not matched on. + let argument_extent = self.scope_auxiliary[argument_scope_id].extent; + self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span), + argument_extent, &lvalue, ty); + + ArgDecl { ty: ty, spread: false } + }) + .collect(); + + // start the first basic block and translate the body + unpack!(block = self.ast_block(&Lvalue::ReturnPointer, block, ast_block)); + + block.and(arg_decls) } fn get_unit_temp(&mut self) -> Lvalue<'tcx> { diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 6d411b9c07b31..f5f6f409eaba4 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -86,8 +86,8 @@ should go to. */ -use build::{BlockAnd, BlockAndExtension, Builder, CFG}; -use rustc::middle::region::CodeExtent; +use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary}; +use rustc::middle::region::{CodeExtent, CodeExtentData}; use rustc::middle::lang_items; use rustc::middle::subst::{Substs, Subst, VecPerParamSpace}; use rustc::middle::ty::{self, Ty, TyCtxt}; @@ -98,35 +98,66 @@ use rustc::middle::const_eval::ConstVal; use rustc_const_eval::ConstInt; pub struct Scope<'tcx> { + /// the scope-id within the scope_datas + id: ScopeId, + + /// the extent of this scope within source code; also stored in + /// `ScopeAuxiliary`, but kept here for convenience extent: CodeExtent, + + /// set of lvalues to drop when exiting this scope. This starts + /// out empty but grows as variables are declared during the + /// building process. This is a stack, so we always drop from the + /// end of the vector (top of the stack) first. drops: Vec>, - // A scope may only have one associated free, because: - // 1. We require a `free` to only be scheduled in the scope of `EXPR` in `box EXPR`; - // 2. It only makes sense to have it translated into the diverge-path. - // - // This kind of drop will be run *after* all the regular drops scheduled onto this scope, - // because drops may have dependencies on the allocated memory. - // - // This is expected to go away once `box EXPR` becomes a sugar for placement protocol and gets - // desugared in some earlier stage. + + /// A scope may only have one associated free, because: + /// + /// 1. We require a `free` to only be scheduled in the scope of + /// `EXPR` in `box EXPR`; + /// 2. It only makes sense to have it translated into the diverge-path. + /// + /// This kind of drop will be run *after* all the regular drops + /// scheduled onto this scope, because drops may have dependencies + /// on the allocated memory. + /// + /// This is expected to go away once `box EXPR` becomes a sugar + /// for placement protocol and gets desugared in some earlier + /// stage. free: Option>, + + /// The cached block for the cleanups-on-diverge path. This block + /// contains a block that will just do a RESUME to an appropriate + /// place. This block does not execute any of the drops or free: + /// each of those has their own cached-blocks, which will branch + /// to this point. + cached_block: Option } struct DropData<'tcx> { + /// span where drop obligation was incurred (typically where lvalue was declared) + span: Span, + + /// lvalue to drop value: Lvalue<'tcx>, - // NB: per-drop “cache” is necessary for the build_scope_drops function below. - /// The cached block for the cleanups-on-diverge path. This block contains code to run the - /// current drop and all the preceding drops (i.e. those having lower index in Drop’s - /// Scope drop array) + + /// The cached block for the cleanups-on-diverge path. This block + /// contains code to run the current drop and all the preceding + /// drops (i.e. those having lower index in Drop’s Scope drop + /// array) cached_block: Option } struct FreeData<'tcx> { + /// span where free obligation was incurred span: Span, + /// Lvalue containing the allocated box. value: Lvalue<'tcx>, + /// type of item for which the box was allocated for (i.e. the T in Box). item_ty: Ty<'tcx>, + /// The cached block containing code to run the free. The block will also execute all the drops /// in the scope. cached_block: Option @@ -151,6 +182,7 @@ impl<'tcx> Scope<'tcx> { /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a /// larger extent of code. fn invalidate_cache(&mut self) { + self.cached_block = None; for dropdata in &mut self.drops { dropdata.cached_block = None; } @@ -205,11 +237,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// Convenience wrapper that pushes a scope and then executes `f` /// to build its contents, popping the scope afterwards. pub fn in_scope(&mut self, extent: CodeExtent, mut block: BasicBlock, f: F) -> BlockAnd - where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd + where F: FnOnce(&mut Builder<'a, 'tcx>, ScopeId) -> BlockAnd { debug!("in_scope(extent={:?}, block={:?})", extent, block); - self.push_scope(extent); - let rv = unpack!(block = f(self)); + let id = self.push_scope(extent, block); + let rv = unpack!(block = f(self, id)); unpack!(block = self.pop_scope(extent, block)); debug!("in_scope: exiting extent={:?} block={:?}", extent, block); block.and(rv) @@ -219,26 +251,46 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// scope and call `pop_scope` afterwards. Note that these two /// calls must be paired; using `in_scope` as a convenience /// wrapper maybe preferable. - pub fn push_scope(&mut self, extent: CodeExtent) { + pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) -> ScopeId { debug!("push_scope({:?})", extent); + let parent_id = self.scopes.last().map(|s| s.id); + let id = ScopeId::new(self.scope_datas.len()); + self.scope_datas.push(ScopeData { + parent_scope: parent_id, + }); self.scopes.push(Scope { - extent: extent.clone(), + id: id, + extent: extent, drops: vec![], - free: None + free: None, + cached_block: None, }); + self.scope_auxiliary.vec.push(ScopeAuxiliary { + extent: extent, + dom: self.cfg.current_location(entry), + postdoms: vec![] + }); + id } /// Pops a scope, which should have extent `extent`, adding any /// drops onto the end of `block` that are needed. This must /// match 1-to-1 with `push_scope`. - pub fn pop_scope(&mut self, extent: CodeExtent, block: BasicBlock) -> BlockAnd<()> { + pub fn pop_scope(&mut self, + extent: CodeExtent, + mut block: BasicBlock) + -> BlockAnd<()> { debug!("pop_scope({:?}, {:?})", extent, block); // We need to have `cached_block`s available for all the drops, so we call diverge_cleanup // to make sure all the `cached_block`s are filled in. self.diverge_cleanup(); let scope = self.scopes.pop().unwrap(); assert_eq!(scope.extent, extent); - build_scope_drops(&mut self.cfg, &scope, &self.scopes[..], block) + unpack!(block = build_scope_drops(&mut self.cfg, &scope, &self.scopes, block)); + self.scope_auxiliary[scope.id] + .postdoms + .push(self.cfg.current_location(block)); + block.unit() } @@ -265,12 +317,24 @@ impl<'a,'tcx> Builder<'a,'tcx> { block)); if let Some(ref free_data) = scope.free { let next = self.cfg.start_new_block(); - let free = build_free(self.hir.tcx(), tmp.clone(), free_data, next); - self.cfg.terminate(block, free); + let free = build_free(self.hir.tcx(), &tmp, free_data, next); + self.cfg.terminate(block, scope.id, span, free); block = next; } + self.scope_auxiliary[scope.id] + .postdoms + .push(self.cfg.current_location(block)); } - self.cfg.terminate(block, Terminator::Goto { target: target }); + + assert!(scope_count < self.scopes.len(), + "should never use `exit_scope` to pop *ALL* scopes"); + let scope = self.scopes.iter().rev().skip(scope_count) + .next() + .unwrap(); + self.cfg.terminate(block, + scope.id, + span, + TerminatorKind::Goto { target: target }); } // Finding scopes @@ -297,12 +361,25 @@ impl<'a,'tcx> Builder<'a,'tcx> { }.unwrap_or_else(|| hir.span_bug(span, "no enclosing loop scope found?")) } + pub fn innermost_scope_id(&self) -> ScopeId { + self.scopes.last().map(|scope| scope.id).unwrap() + } + pub fn extent_of_innermost_scope(&self) -> CodeExtent { self.scopes.last().map(|scope| scope.extent).unwrap() } - pub fn extent_of_outermost_scope(&self) -> CodeExtent { - self.scopes.first().map(|scope| scope.extent).unwrap() + /// Returns the extent of the scope which should be exited by a + /// return. + pub fn extent_of_return_scope(&self) -> CodeExtent { + // The outermost scope (`scopes[0]`) will be the `CallSiteScope`. + // We want `scopes[1]`, which is the `ParameterScope`. + assert!(self.scopes.len() >= 2); + assert!(match self.hir.tcx().region_maps.code_extent_data(self.scopes[1].extent) { + CodeExtentData::ParameterScope { .. } => true, + _ => false, + }); + self.scopes[1].extent } // Scheduling drops @@ -322,6 +399,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // No need to invalidate any caches here. The just-scheduled drop will branch into // the drop that comes before it in the vector. scope.drops.push(DropData { + span: span, value: lvalue.clone(), cached_block: None }); @@ -378,36 +456,60 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// See module comment for more details. None indicates there’s no /// cleanup to do at this point. pub fn diverge_cleanup(&mut self) -> Option { - if self.scopes.is_empty() { + if self.scopes.iter().all(|scope| scope.drops.is_empty() && scope.free.is_none()) { return None; } + assert!(!self.scopes.is_empty()); // or `all` above would be true + let unit_temp = self.get_unit_temp(); - let Builder { ref mut hir, ref mut cfg, ref mut scopes, .. } = *self; - let mut next_block = None; - - // Given an array of scopes, we generate these from the outermost scope to the innermost - // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will - // generate B0 <- B1 <- B2 in left-to-right order. Control flow of the generated blocks - // always ends up at a block with the Resume terminator. - for scope in scopes.iter_mut().filter(|s| !s.drops.is_empty() || s.free.is_some()) { - next_block = Some(build_diverge_scope(hir.tcx(), - cfg, - unit_temp.clone(), - scope, - next_block)); + let Builder { ref mut hir, ref mut cfg, ref mut scopes, + ref mut cached_resume_block, .. } = *self; + + // Build up the drops in **reverse** order. The end result will + // look like: + // + // scopes[n] -> scopes[n-1] -> ... -> scopes[0] + // + // However, we build this in **reverse order**. That is, we + // process scopes[0], then scopes[1], etc, pointing each one at + // the result generates from the one before. Along the way, we + // store caches. If everything is cached, we'll just walk right + // to left reading the cached results but never created anything. + + // To start, create the resume terminator. + let mut target = if let Some(target) = *cached_resume_block { + target + } else { + let resumeblk = cfg.start_new_cleanup_block(); + cfg.terminate(resumeblk, scopes[0].id, self.fn_span, TerminatorKind::Resume); + *cached_resume_block = Some(resumeblk); + resumeblk + }; + + for scope in scopes { + target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target); } - scopes.iter().rev().flat_map(|x| x.cached_block()).next() + + Some(target) } /// Utility function for *non*-scope code to build their own drops - pub fn build_drop(&mut self, block: BasicBlock, value: Lvalue<'tcx>) -> BlockAnd<()> { + pub fn build_drop(&mut self, + block: BasicBlock, + span: Span, + value: Lvalue<'tcx>) + -> BlockAnd<()> { + let scope_id = self.innermost_scope_id(); let next_target = self.cfg.start_new_block(); let diverge_target = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Drop { - value: value, - target: next_target, - unwind: diverge_target, - }); + self.cfg.terminate(block, + scope_id, + span, + TerminatorKind::Drop { + value: value, + target: next_target, + unwind: diverge_target, + }); next_target.unit() } @@ -416,10 +518,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { // ========= // FIXME: should be moved into their own module pub fn panic_bounds_check(&mut self, - block: BasicBlock, - index: Operand<'tcx>, - len: Operand<'tcx>, - span: Span) { + block: BasicBlock, + index: Operand<'tcx>, + len: Operand<'tcx>, + span: Span) { // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> ! let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region? let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem); @@ -435,16 +537,17 @@ impl<'a,'tcx> Builder<'a,'tcx> { let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); let (file, line) = self.span_to_fileline_args(span); let elems = vec![Operand::Constant(file), Operand::Constant(line)]; + let scope_id = self.innermost_scope_id(); // FIXME: We should have this as a constant, rather than a stack variable (to not pollute // icache with cold branch code), however to achieve that we either have to rely on rvalue // promotion or have some way, in MIR, to create constants. - self.cfg.push_assign(block, span, &tuple, // tuple = (file_arg, line_arg); + self.cfg.push_assign(block, scope_id, span, &tuple, // tuple = (file_arg, line_arg); Rvalue::Aggregate(AggregateKind::Tuple, elems)); // FIXME: is this region really correct here? - self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple; + self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple; Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Call { + self.cfg.terminate(block, scope_id, span, TerminatorKind::Call { func: Operand::Constant(func), args: vec![Operand::Consume(tuple_ref), index, len], destination: None, @@ -476,16 +579,18 @@ impl<'a,'tcx> Builder<'a,'tcx> { let elems = vec![Operand::Constant(message), Operand::Constant(file), Operand::Constant(line)]; + let scope_id = self.innermost_scope_id(); // FIXME: We should have this as a constant, rather than a stack variable (to not pollute // icache with cold branch code), however to achieve that we either have to rely on rvalue // promotion or have some way, in MIR, to create constants. - self.cfg.push_assign(block, span, &tuple, // tuple = (message_arg, file_arg, line_arg); + self.cfg.push_assign(block, scope_id, span, &tuple, // [1] Rvalue::Aggregate(AggregateKind::Tuple, elems)); + // [1] tuple = (message_arg, file_arg, line_arg); // FIXME: is this region really correct here? - self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple; + self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple; Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Call { + self.cfg.terminate(block, scope_id, span, TerminatorKind::Call { func: Operand::Constant(func), args: vec![Operand::Consume(tuple_ref)], cleanup: cleanup, @@ -544,7 +649,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next() }); let next = cfg.start_new_block(); - cfg.terminate(block, Terminator::Drop { + cfg.terminate(block, scope.id, drop_data.span, TerminatorKind::Drop { value: drop_data.value.clone(), target: next, unwind: on_diverge @@ -556,89 +661,75 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>, cfg: &mut CFG<'tcx>, - unit_temp: Lvalue<'tcx>, + unit_temp: &Lvalue<'tcx>, scope: &mut Scope<'tcx>, - target: Option) - -> BasicBlock { - debug_assert!(!scope.drops.is_empty() || scope.free.is_some()); - - // First, we build the drops, iterating the drops array in reverse. We do that so that as soon - // as we find a `cached_block`, we know that we’re finished and don’t need to do anything else. - let mut previous = None; - let mut last_drop_block = None; - for drop_data in scope.drops.iter_mut().rev() { - if let Some(cached_block) = drop_data.cached_block { - if let Some((previous_block, previous_value)) = previous { - cfg.terminate(previous_block, Terminator::Drop { - value: previous_value, - target: cached_block, - unwind: None - }); - return last_drop_block.unwrap(); - } else { - return cached_block; - } - } else { - let block = cfg.start_new_cleanup_block(); - drop_data.cached_block = Some(block); - if let Some((previous_block, previous_value)) = previous { - cfg.terminate(previous_block, Terminator::Drop { - value: previous_value, - target: block, - unwind: None - }); - } else { - last_drop_block = Some(block); - } - previous = Some((block, drop_data.value.clone())); - } - } - - // Prepare the end target for this chain. - let mut target = target.unwrap_or_else(||{ - let b = cfg.start_new_cleanup_block(); - cfg.terminate(b, Terminator::Resume); - b - }); + mut target: BasicBlock) + -> BasicBlock +{ + // Build up the drops in **reverse** order. The end result will + // look like: + // + // [drops[n]] -...-> [drops[0]] -> [Free] -> [target] + // | | + // +------------------------------------+ + // code for scope + // + // The code in this function reads from right to left. At each + // point, we check for cached blocks representing the + // remainder. If everything is cached, we'll just walk right to + // left reading the cached results but never created anything. - // Then, build the free branching into the prepared target. + // Next, build up any free. if let Some(ref mut free_data) = scope.free { target = if let Some(cached_block) = free_data.cached_block { cached_block } else { let into = cfg.start_new_cleanup_block(); - cfg.terminate(into, build_free(tcx, unit_temp, free_data, target)); + cfg.terminate(into, + scope.id, + free_data.span, + build_free(tcx, unit_temp, free_data, target)); free_data.cached_block = Some(into); into - } - }; - - if let Some((previous_block, previous_value)) = previous { - // Finally, branch into that just-built `target` from the `previous_block`. - cfg.terminate(previous_block, Terminator::Drop { - value: previous_value, - target: target, - unwind: None - }); - last_drop_block.unwrap() - } else { - // If `previous.is_none()`, there were no drops in this scope – we return the - // target. - target + }; + } + + // Next, build up the drops. Here we iterate the vector in + // *forward* order, so that we generate drops[0] first (right to + // left in diagram above). + for drop_data in &mut scope.drops { + target = if let Some(cached_block) = drop_data.cached_block { + cached_block + } else { + let block = cfg.start_new_cleanup_block(); + cfg.terminate(block, + scope.id, + drop_data.span, + TerminatorKind::Drop { + value: drop_data.value.clone(), + target: target, + unwind: None + }); + drop_data.cached_block = Some(block); + block + }; } + + target } fn build_free<'tcx>(tcx: &TyCtxt<'tcx>, - unit_temp: Lvalue<'tcx>, + unit_temp: &Lvalue<'tcx>, data: &FreeData<'tcx>, - target: BasicBlock) -> Terminator<'tcx> { + target: BasicBlock) + -> TerminatorKind<'tcx> { let free_func = tcx.lang_items.require(lang_items::BoxFreeFnLangItem) .unwrap_or_else(|e| tcx.sess.fatal(&e)); let substs = tcx.mk_substs(Substs::new( VecPerParamSpace::new(vec![], vec![], vec![data.item_ty]), VecPerParamSpace::new(vec![], vec![], vec![]) )); - Terminator::Call { + TerminatorKind::Call { func: Operand::Constant(Constant { span: data.span, ty: tcx.lookup_item_type(free_func).ty.subst(tcx, substs), @@ -648,7 +739,7 @@ fn build_free<'tcx>(tcx: &TyCtxt<'tcx>, } }), args: vec![Operand::Consume(data.value.clone())], - destination: Some((unit_temp, target)), + destination: Some((unit_temp.clone(), target)), cleanup: None } } diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs index 953eb724f50ec..a5e749ea6870e 100644 --- a/src/librustc_mir/graphviz.rs +++ b/src/librustc_mir/graphviz.rs @@ -83,7 +83,7 @@ pub fn write_node_label(block: BasicBlock, // 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().fmt_head(&mut terminator_head).unwrap(); + data.terminator().kind.fmt_head(&mut terminator_head).unwrap(); write!(w, r#"{}"#, dot::escape_html(&terminator_head))?; fini(w)?; @@ -104,7 +104,7 @@ fn write_node(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<( /// Write graphviz DOT edges with labels between the given basic block and all of its successors. fn write_edges(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { let terminator = &mir.basic_block_data(source).terminator(); - let labels = terminator.fmt_successor_labels(); + 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)?; diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 05dbd63ef1a42..5a6e2edfdf9e7 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,11 +22,11 @@ extern crate rustc_front; use build; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; +use pretty; use hair::cx::Cx; use rustc::mir::mir_map::MirMap; use rustc::middle::infer; -use rustc::middle::region::CodeExtentData; use rustc::middle::traits::ProjectionMode; use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::util::common::ErrorReported; @@ -179,11 +179,15 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, }) .collect(); - let parameter_scope = - cx.tcx().region_maps.lookup_code_extent( - CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id }); - let mut mir = build::construct(cx, span, implicit_arg_tys, arguments, - parameter_scope, fn_sig.output, body); + let (mut mir, scope_auxiliary) = + build::construct(cx, + span, + fn_id, + body.id, + implicit_arg_tys, + arguments, + fn_sig.output, + body); match cx.tcx().node_id_to_type(fn_id).sty { ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => { @@ -195,6 +199,13 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, _ => {} } + pretty::dump_mir(cx.tcx(), + "mir_map", + &0, + fn_id, + &mir, + Some(&scope_auxiliary)); + Ok(mir) } diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index b8bb1b4ee1157..834897f8eaee0 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -8,50 +8,214 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use build::{Location, ScopeAuxiliaryVec}; use rustc::mir::repr::*; -use rustc::middle::ty; +use rustc::middle::ty::{self, TyCtxt}; +use rustc_data_structures::fnv::FnvHashMap; +use std::fmt::Display; +use std::fs; use std::io::{self, Write}; use syntax::ast::NodeId; +use syntax::codemap::Span; const INDENT: &'static str = " "; +/// If the session is properly configured, dumps a human-readable +/// representation of the mir into: +/// +/// ``` +/// rustc.node.. +/// ``` +/// +/// Output from this function is controlled by passing `-Z dump-mir=`, +/// where `` 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<'tcx>, + pass_name: &str, + disambiguator: &Display, + node_id: NodeId, + mir: &Mir<'tcx>, + auxiliary: Option<&ScopeAuxiliaryVec>) { + let filters = match tcx.sess.opts.debugging_opts.dump_mir { + None => return, + Some(ref filters) => filters, + }; + let node_path = tcx.item_path_str(tcx.map.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 file_name = format!("rustc.node{}.{}.{}.mir", + node_id, pass_name, disambiguator); + let _ = fs::File::create(&file_name).and_then(|mut file| { + try!(writeln!(file, "// MIR for `{}`", node_path)); + try!(writeln!(file, "// node_id = {}", node_id)); + try!(writeln!(file, "// pass_name = {}", pass_name)); + try!(writeln!(file, "// disambiguator = {}", disambiguator)); + try!(writeln!(file, "")); + try!(write_mir_fn(tcx, node_id, mir, &mut file, auxiliary)); + Ok(()) + }); +} + /// Write out a human-readable textual representation for the given MIR. -pub fn write_mir_pretty<'a, 't, W, I>(tcx: &ty::TyCtxt<'t>, iter: I, w: &mut W) -> io::Result<()> -where W: Write, I: Iterator)> { - for (&nodeid, mir) in iter { - write_mir_intro(tcx, nodeid, mir, w)?; - // Nodes - for block in mir.all_basic_blocks() { - write_basic_block(block, mir, w)?; +pub fn write_mir_pretty<'a, 'tcx, I>(tcx: &TyCtxt<'tcx>, + iter: I, + w: &mut Write) + -> io::Result<()> + where I: Iterator)>, 'tcx: 'a +{ + for (&node_id, mir) in iter { + write_mir_fn(tcx, node_id, mir, w, None)?; + } + Ok(()) +} + +enum Annotation { + EnterScope(ScopeId), + ExitScope(ScopeId), +} + +pub fn write_mir_fn<'tcx>(tcx: &TyCtxt<'tcx>, + node_id: NodeId, + mir: &Mir<'tcx>, + w: &mut Write, + auxiliary: Option<&ScopeAuxiliaryVec>) + -> io::Result<()> { + // compute scope/entry exit annotations + let mut annotations = FnvHashMap(); + if let Some(auxiliary) = auxiliary { + for (index, auxiliary) in auxiliary.vec.iter().enumerate() { + let scope_id = ScopeId::new(index); + + annotations.entry(auxiliary.dom) + .or_insert(vec![]) + .push(Annotation::EnterScope(scope_id)); + + for &loc in &auxiliary.postdoms { + annotations.entry(loc) + .or_insert(vec![]) + .push(Annotation::ExitScope(scope_id)); + } } - writeln!(w, "}}")? } + + write_mir_intro(tcx, node_id, mir, w)?; + for block in mir.all_basic_blocks() { + write_basic_block(tcx, block, mir, w, &annotations)?; + } + + // construct a scope tree and write it out + let mut scope_tree: FnvHashMap, Vec> = FnvHashMap(); + for (index, scope_data) in mir.scopes.iter().enumerate() { + scope_tree.entry(scope_data.parent_scope) + .or_insert(vec![]) + .push(ScopeId::new(index)); + } + write_scope_tree(tcx, mir, auxiliary, &scope_tree, w, None, 1)?; + + writeln!(w, "}}")?; Ok(()) } /// Write out a human-readable textual representation for the given basic block. -fn write_basic_block(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { +fn write_basic_block(tcx: &TyCtxt, + block: BasicBlock, + mir: &Mir, + w: &mut Write, + annotations: &FnvHashMap>) + -> io::Result<()> { let data = mir.basic_block_data(block); // Basic block label at the top. writeln!(w, "\n{}{:?}: {{", INDENT, block)?; // List of statements in the middle. + let mut current_location = Location { block: block, statement_index: 0 }; for statement in &data.statements { - writeln!(w, "{0}{0}{1:?};", INDENT, statement)?; + if let Some(ref annotations) = annotations.get(¤t_location) { + for annotation in annotations.iter() { + match *annotation { + Annotation::EnterScope(id) => + writeln!(w, "{0}{0}// Enter Scope({1})", + INDENT, id.index())?, + Annotation::ExitScope(id) => + writeln!(w, "{0}{0}// Exit Scope({1})", + INDENT, id.index())?, + } + } + } + + writeln!(w, "{0}{0}{1:?}; // {2}", + INDENT, + statement, + comment(tcx, statement.scope, statement.span))?; + + current_location.statement_index += 1; } // Terminator at the bottom. - writeln!(w, "{0}{0}{1:?};", INDENT, data.terminator())?; + writeln!(w, "{0}{0}{1:?}; // {2}", + INDENT, + data.terminator().kind, + comment(tcx, data.terminator().scope, data.terminator().span))?; writeln!(w, "{}}}", INDENT) } +fn comment(tcx: &TyCtxt, + scope: ScopeId, + span: Span) + -> String { + format!("Scope({}) at {}", scope.index(), tcx.sess.codemap().span_to_string(span)) +} + +fn write_scope_tree(tcx: &TyCtxt, + mir: &Mir, + auxiliary: Option<&ScopeAuxiliaryVec>, + scope_tree: &FnvHashMap, Vec>, + w: &mut Write, + parent: Option, + depth: usize) + -> io::Result<()> { + for &child in scope_tree.get(&parent).unwrap_or(&vec![]) { + let indent = depth * INDENT.len(); + let data = &mir.scopes[child]; + assert_eq!(data.parent_scope, parent); + writeln!(w, "{0:1$}Scope({2}) {{", "", indent, child.index())?; + + let indent = indent + INDENT.len(); + if let Some(parent) = parent { + writeln!(w, "{0:1$}Parent: Scope({2})", "", indent, parent.index())?; + } + + if let Some(auxiliary) = auxiliary { + let extent = auxiliary[child].extent; + let data = tcx.region_maps.code_extent_data(extent); + writeln!(w, "{0:1$}Extent: {2:?}", "", indent, data)?; + } + + write_scope_tree(tcx, mir, auxiliary, scope_tree, w, + Some(child), depth + 1)?; + } + 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(tcx: &ty::TyCtxt, nid: NodeId, mir: &Mir, w: &mut W) --> io::Result<()> { - +fn write_mir_intro(tcx: &TyCtxt, nid: NodeId, mir: &Mir, w: &mut Write) + -> io::Result<()> { write!(w, "fn {}(", tcx.map.path_to_string(nid))?; // fn argument types. diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 9ac3749589e90..d8aa0d9b72503 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -58,17 +58,17 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { } fn visit_terminator(&mut self, bb: BasicBlock, terminator: &mut Terminator<'tcx>) { - match *terminator { - Terminator::Goto { .. } | - Terminator::Resume | - Terminator::Return | - Terminator::If { .. } | - Terminator::Switch { .. } | - Terminator::Drop { .. } | - Terminator::Call { .. } => { + match terminator.kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::If { .. } | + TerminatorKind::Switch { .. } | + TerminatorKind::Drop { .. } | + TerminatorKind::Call { .. } => { /* nothing to do */ }, - Terminator::SwitchInt { ref mut switch_ty, .. } => { + TerminatorKind::SwitchInt { ref mut switch_ty, .. } => { *switch_ty = self.tcx.erase_regions(switch_ty); }, } diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index 9caee36e44a7d..0e9a18107870e 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -21,19 +21,19 @@ pub struct NoLandingPads; impl<'tcx> MutVisitor<'tcx> for NoLandingPads { fn visit_terminator(&mut self, bb: BasicBlock, terminator: &mut Terminator<'tcx>) { - match *terminator { - Terminator::Goto { .. } | - Terminator::Resume | - Terminator::Return | - Terminator::If { .. } | - Terminator::Switch { .. } | - Terminator::SwitchInt { .. } => { + match terminator.kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::If { .. } | + TerminatorKind::Switch { .. } | + TerminatorKind::SwitchInt { .. } => { /* nothing to do */ }, - Terminator::Drop { ref mut unwind, .. } => { + TerminatorKind::Drop { ref mut unwind, .. } => { unwind.take(); }, - Terminator::Call { ref mut cleanup, .. } => { + TerminatorKind::Call { ref mut cleanup, .. } => { cleanup.take(); }, } diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 84410bdc57c0a..21b1d022fda60 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -12,6 +12,7 @@ use rustc::middle::const_eval::ConstVal; use rustc::middle::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{MirPass, Pass}; +use pretty; use syntax::ast::NodeId; use super::remove_dead_blocks::RemoveDeadBlocks; @@ -30,15 +31,22 @@ impl SimplifyCfg { let mut seen: Vec = Vec::with_capacity(8); while mir.basic_block_data(target).statements.is_empty() { - match mir.basic_block_data(target).terminator { - Some(Terminator::Goto { target: next }) => { - if seen.contains(&next) { - return None; + // NB -- terminator may have been swapped with `None` + // below, in which case we have a cycle and just want + // to stop + if let Some(ref terminator) = mir.basic_block_data(target).terminator { + match terminator.kind { + TerminatorKind::Goto { target: next } => { + if seen.contains(&next) { + return None; + } + seen.push(next); + target = next; } - seen.push(next); - target = next; + _ => break } - _ => break + } else { + break } } @@ -51,6 +59,8 @@ impl SimplifyCfg { let mut terminator = mir.basic_block_data_mut(bb).terminator.take() .expect("invalid terminator state"); + debug!("remove_goto_chains: bb={:?} terminator={:?}", bb, terminator); + for target in terminator.successors_mut() { let new_target = match final_target(mir, *target) { Some(new_target) => new_target, @@ -71,27 +81,27 @@ impl SimplifyCfg { for bb in mir.all_basic_blocks() { let basic_block = mir.basic_block_data_mut(bb); let mut terminator = basic_block.terminator_mut(); - *terminator = match *terminator { - Terminator::If { ref targets, .. } if targets.0 == targets.1 => { + terminator.kind = match terminator.kind { + TerminatorKind::If { ref targets, .. } if targets.0 == targets.1 => { changed = true; - Terminator::Goto { target: targets.0 } + TerminatorKind::Goto { target: targets.0 } } - Terminator::If { ref targets, cond: Operand::Constant(Constant { + TerminatorKind::If { ref targets, cond: Operand::Constant(Constant { literal: Literal::Value { value: ConstVal::Bool(cond) }, .. }) } => { changed = true; if cond { - Terminator::Goto { target: targets.0 } + TerminatorKind::Goto { target: targets.0 } } else { - Terminator::Goto { target: targets.1 } + TerminatorKind::Goto { target: targets.1 } } } - Terminator::SwitchInt { ref targets, .. } if targets.len() == 1 => { - Terminator::Goto { target: targets[0] } + TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => { + TerminatorKind::Goto { target: targets[0] } } _ => continue } @@ -103,8 +113,11 @@ impl SimplifyCfg { impl<'tcx> MirPass<'tcx> for SimplifyCfg { fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>) { + let mut counter = 0; let mut changed = true; while changed { + pretty::dump_mir(tcx, "simplify_cfg", &counter, id, mir, None); + counter += 1; changed = self.simplify_branches(mir); changed |= self.remove_goto_chains(mir); RemoveDeadBlocks.run_pass(tcx, id, mir); diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index d99e6ff4bf55d..c0605ebe6d4a8 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -376,15 +376,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { term: &Terminator<'tcx>) { debug!("check_terminator: {:?}", term); let tcx = self.tcx(); - match *term { - Terminator::Goto { .. } | - Terminator::Resume | - Terminator::Return | - Terminator::Drop { .. } => { + match term.kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::Drop { .. } => { // no checks needed for these } - Terminator::If { ref cond, .. } => { + TerminatorKind::If { ref cond, .. } => { let cond_ty = mir.operand_ty(tcx, cond); match cond_ty.sty { ty::TyBool => {} @@ -393,7 +393,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } } - Terminator::SwitchInt { ref discr, switch_ty, .. } => { + TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx); if let Err(terr) = self.mk_subty(self.last_span, discr_ty, switch_ty) { span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}", @@ -406,7 +406,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // FIXME: check the values } - Terminator::Switch { ref discr, adt_def, ref targets } => { + TerminatorKind::Switch { ref discr, adt_def, ref targets } => { let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx); match discr_ty.sty { ty::TyEnum(def, _) @@ -418,7 +418,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } } - Terminator::Call { ref func, ref args, ref destination, .. } => { + TerminatorKind::Call { ref func, ref args, ref destination, .. } => { let func_ty = mir.operand_ty(tcx, func); debug!("check_terminator: call, func_ty={:?}", func_ty); let func_ty = match func_ty.sty { diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 080547952a5ce..7abaeb44c1c29 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -56,8 +56,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug!("trans_block: terminator: {:?}", data.terminator()); - match *data.terminator() { - mir::Terminator::Resume => { + match data.terminator().kind { + mir::TerminatorKind::Resume => { if let Some(cleanup_pad) = cleanup_pad { bcx.cleanup_ret(cleanup_pad, None); } else { @@ -70,18 +70,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Goto { target } => { + mir::TerminatorKind::Goto { target } => { funclet_br(bcx, self.llblock(target)); } - mir::Terminator::If { ref cond, targets: (true_bb, false_bb) } => { + mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => { let cond = self.trans_operand(&bcx, cond); let lltrue = self.llblock(true_bb); let llfalse = self.llblock(false_bb); bcx.cond_br(cond.immediate(), lltrue, llfalse); } - mir::Terminator::Switch { ref discr, ref adt_def, ref targets } => { + mir::TerminatorKind::Switch { ref discr, ref adt_def, ref targets } => { let discr_lvalue = self.trans_lvalue(&bcx, discr); let ty = discr_lvalue.ty.to_ty(bcx.tcx()); let repr = adt::represent_type(bcx.ccx(), ty); @@ -103,7 +103,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { + mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { let (otherwise, targets) = targets.split_last().unwrap(); let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval); let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty)); @@ -115,13 +115,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Return => { + mir::TerminatorKind::Return => { bcx.with_block(|bcx| { self.fcx.build_return_block(bcx, DebugLoc::None); }) } - mir::Terminator::Drop { ref value, target, unwind } => { + mir::TerminatorKind::Drop { ref value, target, unwind } => { let lvalue = self.trans_lvalue(&bcx, value); let ty = lvalue.ty.to_ty(bcx.tcx()); // Double check for necessity to drop @@ -152,7 +152,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => { + mir::TerminatorKind::Call { ref func, ref args, ref destination, ref cleanup } => { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.trans_operand(&bcx, func);