integrate scopes into MIR

This commit is contained in:
Niko Matsakis 2016-03-09 11:04:26 -05:00
parent b76f818cad
commit 464c02e336
6 changed files with 168 additions and 21 deletions

View File

@ -32,6 +32,10 @@ pub struct Mir<'tcx> {
/// that indexes into this vector.
pub basic_blocks: Vec<BasicBlockData<'tcx>>,
/// 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<ScopeData>
}
impl ScopeDataVec {
pub fn new() -> Self {
ScopeDataVec { vec: Vec::new() }
}
}
impl Index<ScopeId> for ScopeDataVec {
type Output = ScopeData;
#[inline]
fn index(&self, index: ScopeId) -> &ScopeData {
&self.vec[index.index()]
}
}
impl IndexMut<ScopeId> 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<ScopeId>,
}
///////////////////////////////////////////////////////////////////////////
// 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>),

View File

@ -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 ^~~~

View File

@ -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,

View File

@ -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<scope::Scope<'tcx>>,
// 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<ScopeAuxiliary>,
// the current set of loops; see the `scope` module for more
// details
loop_scopes: Vec<scope::LoopScope>,
// the vector of all scopes that we have created thus far;
// we track this for debuginfo later
scope_data_vec: ScopeDataVec,
var_decls: Vec<VarDecl<'tcx>>,
var_indices: FnvHashMap<ast::NodeId, u32>,
temp_decls: Vec<TempDecl<'tcx>>,
@ -33,6 +51,42 @@ struct CFG<'tcx> {
basic_blocks: Vec<BasicBlockData<'tcx>>,
}
/// 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<Location>,
}
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<ScopeAuxiliary>,
}
///////////////////////////////////////////////////////////////////////////
/// 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,
}
}

View File

@ -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<DropData<'tcx>>,
// 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<R>
{
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 });
}

View File

@ -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 => {