From 464c02e336f15c6fedb7235e93ec6f8f69411b57 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 9 Mar 2016 11:04:26 -0500 Subject: [PATCH] integrate scopes into MIR --- src/librustc/mir/repr.rs | 56 +++++++++++++++++++++++- src/librustc_mir/build/block.rs | 2 +- src/librustc_mir/build/cfg.rs | 7 ++- src/librustc_mir/build/mod.rs | 76 +++++++++++++++++++++++++++++---- src/librustc_mir/build/scope.rs | 36 +++++++++++++--- src/librustc_mir/mir_map.rs | 12 ++++-- 6 files changed, 168 insertions(+), 21 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index ff3e292f458..6723fc72a40 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: ScopeDataVec, + /// Return type of the function. pub return_ty: FnOutput<'tcx>, @@ -613,13 +617,61 @@ impl<'tcx> Debug for Lvalue<'tcx> { } } +/////////////////////////////////////////////////////////////////////////// +// Scopes + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct ScopeDataVec { + pub vec: Vec +} + +impl ScopeDataVec { + pub fn new() -> Self { + ScopeDataVec { vec: Vec::new() } + } +} + +impl Index for ScopeDataVec { + type Output = ScopeData; + + #[inline] + fn index(&self, index: ScopeId) -> &ScopeData { + &self.vec[index.index()] + } +} + +impl IndexMut for ScopeDataVec { + #[inline] + fn index_mut(&mut self, index: ScopeId) -> &mut ScopeData { + &mut self.vec[index.index()] + } +} + +#[derive(Copy, Clone, Debug, 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/build/block.rs b/src/librustc_mir/build/block.rs index 4c80eab102f..ca23be7dc06 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -51,7 +51,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { })); } StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => { - this.push_scope(remainder_scope); + this.push_scope(remainder_scope, block); let_extent_stack.push(remainder_scope); unpack!(block = this.in_scope(init_scope, block, move |this| { // FIXME #30046 ^~~~ diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index c7147d111aa..d804fc8635a 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,6 +43,11 @@ 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, span: Span, diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index b40775f939f..41259f8a281 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -21,8 +21,26 @@ use syntax::codemap::Span; pub struct Builder<'a, 'tcx: 'a> { hir: Cx<'a, 'tcx>, cfg: CFG<'tcx>, + + // 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: Vec, + + // 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_data_vec: ScopeDataVec, + var_decls: Vec>, var_indices: FnvHashMap, temp_decls: Vec>, @@ -33,6 +51,42 @@ 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, +} + +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 MirPlusPlus<'tcx> { + pub mir: Mir<'tcx>, + pub scope_auxiliary: Vec, +} + /////////////////////////////////////////////////////////////////////////// /// The `BlockAnd` "monad" packages up the new basic block along with a /// produced value (sometimes just unit, of course). The `unpack!` @@ -86,13 +140,15 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, argument_extent: CodeExtent, return_ty: FnOutput<'tcx>, ast_block: &'tcx hir::Block) - -> Mir<'tcx> { + -> MirPlusPlus<'tcx> { let cfg = CFG { basic_blocks: vec![] }; let mut builder = Builder { hir: hir, cfg: cfg, scopes: vec![], + scope_data_vec: ScopeDataVec::new(), + scope_auxiliary: vec![], loop_scopes: vec![], temp_decls: vec![], var_decls: vec![], @@ -113,13 +169,17 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>, 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 + MirPlusPlus { + mir: Mir { + basic_blocks: builder.cfg.basic_blocks, + scopes: builder.scope_data_vec, + var_decls: builder.var_decls, + arg_decls: arg_decls, + temp_decls: builder.temp_decls, + return_ty: return_ty, + span: span + }, + scope_auxiliary: builder.scope_auxiliary, } } diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 6d411b9c07b..6a734e1816a 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -86,7 +86,7 @@ should go to. */ -use build::{BlockAnd, BlockAndExtension, Builder, CFG}; +use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary}; use rustc::middle::region::CodeExtent; use rustc::middle::lang_items; use rustc::middle::subst::{Substs, Subst, VecPerParamSpace}; @@ -98,8 +98,11 @@ use rustc::middle::const_eval::ConstVal; use rustc_const_eval::ConstInt; pub struct Scope<'tcx> { + // the scope-id within the scope_data_vec + id: ScopeId, extent: CodeExtent, 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. @@ -208,7 +211,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd { debug!("in_scope(extent={:?}, block={:?})", extent, block); - self.push_scope(extent); + self.push_scope(extent, block); let rv = unpack!(block = f(self)); unpack!(block = self.pop_scope(extent, block)); debug!("in_scope: exiting extent={:?} block={:?}", extent, block); @@ -219,26 +222,44 @@ 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) { debug!("push_scope({:?})", extent); + let parent_id = self.scopes.last().map(|s| s.id); + let id = ScopeId::new(self.scope_data_vec.vec.len()); + self.scope_data_vec.vec.push(ScopeData { + parent_scope: parent_id, + }); self.scopes.push(Scope { - extent: extent.clone(), + id: id, + extent: extent, drops: vec![], free: None }); + self.scope_auxiliary.push(ScopeAuxiliary { + extent: extent, + dom: self.cfg.current_location(entry), + postdoms: vec![] + }); } /// 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.index()] + .postdoms + .push(self.cfg.current_location(block)); + block.and(()) } @@ -269,6 +290,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.terminate(block, free); block = next; } + self.scope_auxiliary[scope.id.index()] + .postdoms + .push(self.cfg.current_location(block)); } self.cfg.terminate(block, Terminator::Goto { target: target }); } diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 05dbd63ef1a..71037d1f080 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -19,7 +19,7 @@ extern crate syntax; extern crate rustc_front; -use build; +use build::{self, MirPlusPlus}; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use hair::cx::Cx; @@ -182,8 +182,14 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, 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 MirPlusPlus { mut mir, scope_auxiliary: _ } = + build::construct(cx, + span, + implicit_arg_tys, + arguments, + parameter_scope, + fn_sig.output, + body); match cx.tcx().node_id_to_type(fn_id).sty { ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {