/// Rvalues promoted from this function, such as borrows of constants.
/// Each of them is the Mir of a constant with the fn's type parameters
- /// in scope, but no vars or args and a separate set of temps.
+ /// in scope, but a separate set of locals.
pub promoted: IndexVec<Promoted, Mir<'tcx>>,
/// Return type of the function.
pub return_ty: Ty<'tcx>,
- /// Variables: these are stack slots corresponding to user variables. They may be
- /// assigned many times.
- pub var_decls: IndexVec<Var, VarDecl<'tcx>>,
-
- /// Args: these are stack slots corresponding to the input arguments.
- pub arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
+ /// Declarations of locals.
+ ///
+ /// The first local is the return value pointer, followed by `arg_count`
+ /// locals for the function arguments, followed by any user-declared
+ /// variables and temporaries.
+ pub local_decls: IndexVec<Local, LocalDecl<'tcx>>,
- /// Temp declarations: stack slots that for temporaries created by
- /// the compiler. These are assigned once, but they are not SSA
- /// values in that it is possible to borrow them and mutate them
- /// through the resulting reference.
- pub temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
+ /// Number of arguments this function takes.
+ ///
+ /// Starting at local1, `arg_count` locals will be provided by the caller
+ /// and can be assumed to be initialized.
+ ///
+ /// If this MIR was built for a constant, this will be 0.
+ pub arg_count: usize,
/// Names and capture modes of all the closure upvars, assuming
/// the first argument is either the closure or a reference to it.
visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
promoted: IndexVec<Promoted, Mir<'tcx>>,
return_ty: Ty<'tcx>,
- var_decls: IndexVec<Var, VarDecl<'tcx>>,
- arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
- temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
+ local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+ arg_count: usize,
upvar_decls: Vec<UpvarDecl>,
span: Span) -> Self
{
+ // We need `arg_count` locals, and one for the return pointer
+ assert!(local_decls.len() >= arg_count + 1,
+ "expected at least {} locals, got {}", arg_count + 1, local_decls.len());
+ assert_eq!(local_decls[RETURN_POINTER].ty, return_ty);
+
Mir {
basic_blocks: basic_blocks,
visibility_scopes: visibility_scopes,
promoted: promoted,
return_ty: return_ty,
- var_decls: var_decls,
- arg_decls: arg_decls,
- temp_decls: temp_decls,
+ local_decls: local_decls,
+ arg_count: arg_count,
upvar_decls: upvar_decls,
spread_last_arg: false,
span: span,
dominators(self)
}
- /// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order)
- /// to their index in the whole list of locals. This is useful if you
- /// want to treat all locals the same instead of repeating yourself.
- pub fn local_index(&self, lvalue: &Lvalue<'tcx>) -> Option<Local> {
- let idx = match *lvalue {
- Lvalue::Arg(arg) => arg.index(),
- Lvalue::Var(var) => {
- self.arg_decls.len() +
- var.index()
- }
- Lvalue::Temp(temp) => {
- self.arg_decls.len() +
- self.var_decls.len() +
- temp.index()
+ #[inline]
+ pub fn local_kind(&self, local: Local) -> LocalKind {
+ let index = local.0 as usize;
+ if index == 0 {
+ debug_assert!(self.local_decls[local].mutability == Mutability::Mut,
+ "return pointer should be mutable");
+
+ LocalKind::ReturnPointer
+ } else if index < self.arg_count + 1 {
+ LocalKind::Arg
+ } else if self.local_decls[local].name.is_some() {
+ LocalKind::Var
+ } else {
+ debug_assert!(self.local_decls[local].mutability == Mutability::Mut,
+ "temp should be mutable");
+
+ LocalKind::Temp
+ }
+ }
+
+ /// Returns an iterator over all temporaries.
+ #[inline]
+ pub fn temp_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
+ (self.arg_count+1..self.local_decls.len()).filter_map(move |index| {
+ let local = Local::new(index);
+ if self.local_decls[local].source_info.is_none() {
+ Some(local)
+ } else {
+ None
}
- Lvalue::ReturnPointer => {
- self.arg_decls.len() +
- self.var_decls.len() +
- self.temp_decls.len()
+ })
+ }
+
+ /// Returns an iterator over all user-declared locals.
+ #[inline]
+ pub fn var_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
+ (self.arg_count+1..self.local_decls.len()).filter_map(move |index| {
+ let local = Local::new(index);
+ if self.local_decls[local].source_info.is_none() {
+ None
+ } else {
+ Some(local)
}
- Lvalue::Static(_) |
- Lvalue::Projection(_) => return None
- };
- Some(Local::new(idx))
+ })
}
- /// Counts the number of locals, such that local_index
- /// will always return an index smaller than this count.
- pub fn count_locals(&self) -> usize {
- self.arg_decls.len() +
- self.var_decls.len() +
- self.temp_decls.len() + 1
+ /// Returns an iterator over all function arguments.
+ #[inline]
+ pub fn arg_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
+ (1..self.arg_count+1).map(Local::new)
}
- pub fn format_local(&self, local: Local) -> String {
- let mut index = local.index();
- index = match index.checked_sub(self.arg_decls.len()) {
- None => return format!("{:?}", Arg::new(index)),
- Some(index) => index,
- };
- index = match index.checked_sub(self.var_decls.len()) {
- None => return format!("{:?}", Var::new(index)),
- Some(index) => index,
- };
- index = match index.checked_sub(self.temp_decls.len()) {
- None => return format!("{:?}", Temp::new(index)),
- Some(index) => index,
- };
- debug_assert!(index == 0);
- return "ReturnPointer".to_string()
+ /// Returns an iterator over all user-defined variables and compiler-generated temporaries (all
+ /// locals that are neither arguments nor the return pointer).
+ #[inline]
+ pub fn var_and_temp_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
+ (self.arg_count+1..self.local_decls.len()).map(Local::new)
}
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
///////////////////////////////////////////////////////////////////////////
// Variables and temps
-/// A "variable" is a binding declared by the user as part of the fn
-/// decl, a let, etc.
+newtype_index!(Local, "local");
+
+pub const RETURN_POINTER: Local = Local(0);
+
+/// Classifies locals into categories. See `Mir::local_kind`.
+#[derive(PartialEq, Eq, Debug)]
+pub enum LocalKind {
+ /// User-declared variable binding
+ Var,
+ /// Compiler-introduced temporary
+ Temp,
+ /// Function argument
+ Arg,
+ /// Location of function's return value
+ ReturnPointer,
+}
+
+/// A MIR local.
+///
+/// This can be a binding declared by the user, a temporary inserted by the compiler, a function
+/// argument, or the return pointer.
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
-pub struct VarDecl<'tcx> {
- /// `let mut x` vs `let x`
+pub struct LocalDecl<'tcx> {
+ /// `let mut x` vs `let x`.
+ ///
+ /// Temporaries and the return pointer are always mutable.
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 = ...`)
+ /// Type of this local.
pub ty: Ty<'tcx>,
- /// source information (span, scope, etc.) for the declaration
- pub source_info: SourceInfo,
-}
+ /// Name of the local, used in debuginfo and pretty-printing.
+ ///
+ /// Note that function arguments can also have this set to `Some(_)`
+ /// to generate better debuginfo.
+ pub name: Option<Name>,
-/// A "temp" is a temporary that we place on the stack. They are
-/// anonymous, always mutable, and have only a type.
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
-pub struct TempDecl<'tcx> {
- pub ty: Ty<'tcx>,
+ /// For user-declared variables, stores their source information.
+ ///
+ /// For temporaries, this is `None`.
+ ///
+ /// This is the primary way to differentiate between user-declared
+ /// variables and compiler-generated temporaries.
+ pub source_info: Option<SourceInfo>,
}
-/// A "arg" is one of the function's formal arguments. These are
-/// anonymous and distinct from the bindings that the user declares.
-///
-/// For example, in this function:
-///
-/// ```
-/// fn foo((x, y): (i32, u32)) { ... }
-/// ```
-///
-/// there is only one argument, of type `(i32, u32)`, but two bindings
-/// (`x` and `y`).
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
-pub struct ArgDecl<'tcx> {
- pub ty: Ty<'tcx>,
+impl<'tcx> LocalDecl<'tcx> {
+ /// Create a new `LocalDecl` for a temporary.
+ #[inline]
+ pub fn new_temp(ty: Ty<'tcx>) -> Self {
+ LocalDecl {
+ mutability: Mutability::Mut,
+ ty: ty,
+ name: None,
+ source_info: None,
+ }
+ }
- /// Either keywords::Invalid or the name of a single-binding
- /// pattern associated with this argument. Useful for debuginfo.
- pub debug_name: Name
+ /// Builds a `LocalDecl` for the return pointer.
+ ///
+ /// This must be inserted into the `local_decls` list as the first local.
+ #[inline]
+ pub fn new_return_pointer(return_ty: Ty) -> LocalDecl {
+ LocalDecl {
+ mutability: Mutability::Mut,
+ ty: return_ty,
+ source_info: None,
+ name: None, // FIXME maybe we do want some name here?
+ }
+ }
}
/// A closure capture, with its name and mode.
/// continue. Emitted by build::scope::diverge_cleanup.
Resume,
- /// Indicates a normal return. The ReturnPointer lvalue should
+ /// Indicates a normal return. The return pointer lvalue should
/// have been filled in by now. This should occur at most once.
Return,
///////////////////////////////////////////////////////////////////////////
// Lvalues
-newtype_index!(Var, "var");
-newtype_index!(Temp, "tmp");
-newtype_index!(Arg, "arg");
-newtype_index!(Local, "local");
-
/// A path to a value; something that can be evaluated without
/// changing or disturbing program state.
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum Lvalue<'tcx> {
- /// local variable declared by the user
- Var(Var),
-
- /// temporary introduced during lowering into MIR
- Temp(Temp),
-
- /// formal parameter of the function; note that these are NOT the
- /// bindings that the user declares, which are vars
- Arg(Arg),
+ /// local variable
+ Local(Local),
/// static or static mut variable
Static(DefId),
- /// the return pointer of the fn
- ReturnPointer,
-
/// projection out of an lvalue (access a field, deref a pointer, etc)
Projection(Box<LvalueProjection<'tcx>>),
}
elem: elem,
}))
}
-
- pub fn from_local(mir: &Mir<'tcx>, local: Local) -> Lvalue<'tcx> {
- let mut index = local.index();
- index = match index.checked_sub(mir.arg_decls.len()) {
- None => return Lvalue::Arg(Arg(index as u32)),
- Some(index) => index,
- };
- index = match index.checked_sub(mir.var_decls.len()) {
- None => return Lvalue::Var(Var(index as u32)),
- Some(index) => index,
- };
- index = match index.checked_sub(mir.temp_decls.len()) {
- None => return Lvalue::Temp(Temp(index as u32)),
- Some(index) => index,
- };
- debug_assert!(index == 0);
- Lvalue::ReturnPointer
- }
}
impl<'tcx> Debug for Lvalue<'tcx> {
use self::Lvalue::*;
match *self {
- Var(id) => write!(fmt, "{:?}", id),
- Arg(id) => write!(fmt, "{:?}", id),
- Temp(id) => write!(fmt, "{:?}", id),
+ Local(id) => write!(fmt, "{:?}", id),
Static(def_id) =>
write!(fmt, "{}", ty::tls::with(|tcx| tcx.item_path_str(def_id))),
- ReturnPointer =>
- write!(fmt, "return"),
Projection(ref data) =>
match data.elem {
ProjectionElem::Downcast(ref adt_def, index) =>
-> LvalueTy<'tcx>
{
match *elem {
- ProjectionElem::Deref =>
+ ProjectionElem::Deref => {
+ let ty = self.to_ty(tcx)
+ .builtin_deref(true, ty::LvaluePreference::NoPreference)
+ .unwrap_or_else(|| {
+ bug!("deref projection of non-dereferencable ty {:?}", self)
+ })
+ .ty;
LvalueTy::Ty {
- ty: self.to_ty(tcx).builtin_deref(true, ty::LvaluePreference::NoPreference)
- .unwrap()
- .ty
- },
+ ty: ty,
+ }
+ }
ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } =>
LvalueTy::Ty {
ty: self.to_ty(tcx).builtin_index().unwrap()
impl<'tcx> Lvalue<'tcx> {
pub fn ty<'a, 'gcx>(&self, mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> LvalueTy<'tcx> {
- match self {
- &Lvalue::Var(index) =>
- LvalueTy::Ty { ty: mir.var_decls[index].ty },
- &Lvalue::Temp(index) =>
- LvalueTy::Ty { ty: mir.temp_decls[index].ty },
- &Lvalue::Arg(index) =>
- LvalueTy::Ty { ty: mir.arg_decls[index].ty },
- &Lvalue::Static(def_id) =>
+ match *self {
+ Lvalue::Local(index) =>
+ LvalueTy::Ty { ty: mir.local_decls[index].ty },
+ Lvalue::Static(def_id) =>
LvalueTy::Ty { ty: tcx.lookup_item_type(def_id).ty },
- &Lvalue::ReturnPointer =>
- LvalueTy::Ty { ty: mir.return_ty },
- &Lvalue::Projection(ref proj) =>
+ Lvalue::Projection(ref proj) =>
proj.base.ty(mir, tcx).projection_ty(tcx, &proj.elem),
}
}
self.super_typed_const_val(val, location);
}
- 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_local_decl(&mut self,
+ local_decl: & $($mutability)* LocalDecl<'tcx>) {
+ self.super_local_decl(local_decl);
}
fn visit_visibility_scope(&mut self,
self.visit_ty(&$($mutability)* mir.return_ty);
- for var_decl in &$($mutability)* mir.var_decls {
- self.visit_var_decl(var_decl);
- }
-
- for arg_decl in &$($mutability)* mir.arg_decls {
- self.visit_arg_decl(arg_decl);
- }
-
- for temp_decl in &$($mutability)* mir.temp_decls {
- self.visit_temp_decl(temp_decl);
+ for local_decl in &$($mutability)* mir.local_decls {
+ self.visit_local_decl(local_decl);
}
self.visit_span(&$($mutability)* mir.span);
context: LvalueContext<'tcx>,
location: Location) {
match *lvalue {
- Lvalue::Var(_) |
- Lvalue::Temp(_) |
- Lvalue::Arg(_) |
- Lvalue::ReturnPointer => {
+ Lvalue::Local(_) => {
}
Lvalue::Static(ref $($mutability)* def_id) => {
self.visit_def_id(def_id, location);
}
}
- fn super_var_decl(&mut self,
- var_decl: & $($mutability)* VarDecl<'tcx>) {
- let VarDecl {
+ fn super_local_decl(&mut self,
+ local_decl: & $($mutability)* LocalDecl<'tcx>) {
+ let LocalDecl {
mutability: _,
- name: _,
ref $($mutability)* ty,
+ name: _,
ref $($mutability)* source_info,
- } = *var_decl;
-
- self.visit_ty(ty);
- self.visit_source_info(source_info);
- }
-
- 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,
- debug_name: _
- } = *arg_decl;
+ } = *local_decl;
self.visit_ty(ty);
+ if let Some(ref $($mutability)* info) = *source_info {
+ self.visit_source_info(info);
+ }
}
fn super_visibility_scope(&mut self,
use syntax_pos::Span;
use rustc::ty::{self, TyCtxt};
-use rustc::mir::repr::{self, Mir};
+use rustc::mir::repr::{self, Mir, LocalKind};
use rustc_data_structures::indexed_vec::Idx;
use super::super::gather_moves::{MovePathIndex, LookupResult};
};
assert!(args.len() == 1);
let peek_arg_lval = match args[0] {
- repr::Operand::Consume(ref lval @ repr::Lvalue::Temp(_)) => {
- lval
- }
- repr::Operand::Consume(_) |
+ repr::Operand::Consume(ref lval) => match *lval {
+ repr::Lvalue::Local(local) if mir.local_kind(local) == LocalKind::Temp => {
+ Some(lval)
+ }
+ _ => None
+ },
repr::Operand::Constant(_) => {
+ None
+ }
+ };
+
+ let peek_arg_lval = match peek_arg_lval {
+ Some(arg) => arg,
+ None => {
tcx.sess.diagnostic().span_err(
span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
return;
env: &'a MoveDataParamEnv<'tcx>,
flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx>>,
flow_uninits: DataflowResults<MaybeUninitializedLvals<'a, 'tcx>>,
- drop_flags: FnvHashMap<MovePathIndex, Temp>,
+ drop_flags: FnvHashMap<MovePathIndex, Local>,
patch: MirPatch<'tcx>,
}
}
fn drop_flag(&mut self, index: MovePathIndex) -> Option<Lvalue<'tcx>> {
- self.drop_flags.get(&index).map(|t| Lvalue::Temp(*t))
+ self.drop_flags.get(&index).map(|t| Lvalue::Local(*t))
}
/// create a patch that elaborates all drops in the input
statements.push(Statement {
source_info: c.source_info,
kind: StatementKind::Assign(
- Lvalue::Temp(flag),
+ Lvalue::Local(flag),
self.constant_bool(c.source_info.span, false)
)
});
}
let tcx = self.tcx;
- let unit_temp = Lvalue::Temp(self.patch.new_temp(tcx.mk_nil()));
+ let unit_temp = Lvalue::Local(self.patch.new_temp(tcx.mk_nil()));
let free_func = tcx.lang_items.require(lang_items::BoxFreeFnLangItem)
.unwrap_or_else(|e| tcx.sess.fatal(&e));
let substs = Substs::new(tcx, iter::once(Kind::from(ty)));
if let Some(&flag) = self.drop_flags.get(&path) {
let span = self.patch.source_info_for_location(self.mir, loc).span;
let val = self.constant_bool(span, val.value());
- self.patch.add_assign(loc, Lvalue::Temp(flag), val);
+ self.patch.add_assign(loc, Lvalue::Local(flag), val);
}
}
let span = self.patch.source_info_for_location(self.mir, loc).span;
let false_ = self.constant_bool(span, false);
for flag in self.drop_flags.values() {
- self.patch.add_assign(loc, Lvalue::Temp(*flag), false_.clone());
+ self.patch.add_assign(loc, Lvalue::Local(*flag), false_.clone());
}
}
/// Tables mapping from an l-value to its MovePathIndex.
#[derive(Debug)]
pub struct MovePathLookup<'tcx> {
- vars: IndexVec<Var, MovePathIndex>,
- temps: IndexVec<Temp, MovePathIndex>,
- args: IndexVec<Arg, MovePathIndex>,
-
- /// The move path representing the return value is constructed
- /// lazily when we first encounter it in the input MIR.
- return_ptr: Option<MovePathIndex>,
+ locals: IndexVec<Local, MovePathIndex>,
/// projections are made from a base-lvalue and a projection
/// elem. The base-lvalue will have a unique MovePathIndex; we use
moves: IndexVec::new(),
loc_map: LocationMap::new(mir),
rev_lookup: MovePathLookup {
- vars: mir.var_decls.indices().map(Lvalue::Var).map(|v| {
+ locals: mir.local_decls.indices().map(Lvalue::Local).map(|v| {
Self::new_move_path(&mut move_paths, &mut path_map, None, v)
}).collect(),
- temps: mir.temp_decls.indices().map(Lvalue::Temp).map(|t| {
- Self::new_move_path(&mut move_paths, &mut path_map, None, t)
- }).collect(),
- args: mir.arg_decls.indices().map(Lvalue::Arg).map(|a| {
- Self::new_move_path(&mut move_paths, &mut path_map, None, a)
- }).collect(),
- return_ptr: None,
projections: FnvHashMap(),
},
move_paths: move_paths,
{
debug!("lookup({:?})", lval);
match *lval {
- Lvalue::Var(var) => Ok(self.data.rev_lookup.vars[var]),
- Lvalue::Arg(arg) => Ok(self.data.rev_lookup.args[arg]),
- Lvalue::Temp(temp) => Ok(self.data.rev_lookup.temps[temp]),
+ Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]),
// error: can't move out of a static
Lvalue::Static(..) => Err(MovePathError::IllegalMove),
- Lvalue::ReturnPointer => match self.data.rev_lookup.return_ptr {
- Some(ptr) => Ok(ptr),
- ref mut ptr @ None => {
- let path = Self::new_move_path(
- &mut self.data.move_paths,
- &mut self.data.path_map,
- None,
- lval.clone());
- *ptr = Some(path);
- Ok(path)
- }
- },
Lvalue::Projection(ref proj) => {
self.move_path_for_projection(lval, proj)
}
// parent.
pub fn find(&self, lval: &Lvalue<'tcx>) -> LookupResult {
match *lval {
- Lvalue::Var(var) => LookupResult::Exact(self.vars[var]),
- Lvalue::Temp(temp) => LookupResult::Exact(self.temps[temp]),
- Lvalue::Arg(arg) => LookupResult::Exact(self.args[arg]),
+ Lvalue::Local(local) => LookupResult::Exact(self.locals[local]),
Lvalue::Static(..) => LookupResult::Parent(None),
- Lvalue::ReturnPointer => LookupResult::Exact(self.return_ptr.unwrap()),
Lvalue::Projection(ref proj) => {
match self.find(&proj.base) {
LookupResult::Exact(base_path) => {
TerminatorKind::Unreachable => { }
TerminatorKind::Return => {
- self.gather_move(loc, &Lvalue::ReturnPointer);
+ self.gather_move(loc, &Lvalue::Local(RETURN_POINTER));
}
TerminatorKind::If { .. } |
where F: FnMut(MovePathIndex, DropFlagState)
{
let move_data = &ctxt.move_data;
- for (arg, _) in mir.arg_decls.iter_enumerated() {
- let lvalue = repr::Lvalue::Arg(arg);
+ for arg in mir.arg_iter() {
+ let lvalue = repr::Lvalue::Local(arg);
let lookup_result = move_data.rev_lookup.find(&lvalue);
on_lookup_result_bits(tcx, mir, move_data,
lookup_result,
patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>,
new_blocks: Vec<BasicBlockData<'tcx>>,
new_statements: Vec<(Location, StatementKind<'tcx>)>,
- new_temps: Vec<TempDecl<'tcx>>,
+ new_locals: Vec<LocalDecl<'tcx>>,
resume_block: BasicBlock,
- next_temp: usize,
+ next_local: usize,
}
impl<'tcx> MirPatch<'tcx> {
let mut result = MirPatch {
patch_map: IndexVec::from_elem(None, mir.basic_blocks()),
new_blocks: vec![],
- new_temps: vec![],
new_statements: vec![],
- next_temp: mir.temp_decls.len(),
+ new_locals: vec![],
+ next_local: mir.local_decls.len(),
resume_block: START_BLOCK
};
}
}
- pub fn new_temp(&mut self, ty: Ty<'tcx>) -> Temp {
- let index = self.next_temp;
- self.next_temp += 1;
- self.new_temps.push(TempDecl { ty: ty });
- Temp::new(index as usize)
+ pub fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
+ let index = self.next_local;
+ self.next_local += 1;
+ self.new_locals.push(LocalDecl::new_temp(ty));
+ Local::new(index as usize)
}
pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
pub fn apply(self, mir: &mut Mir<'tcx>) {
debug!("MirPatch: {:?} new temps, starting from index {}: {:?}",
- self.new_temps.len(), mir.temp_decls.len(), self.new_temps);
+ self.new_locals.len(), mir.local_decls.len(), self.new_locals);
debug!("MirPatch: {} new blocks, starting from index {}",
self.new_blocks.len(), mir.basic_blocks().len());
mir.basic_blocks_mut().extend(self.new_blocks);
- mir.temp_decls.extend(self.new_temps);
+ mir.local_decls.extend(self.new_locals);
for (src, patch) in self.patch_map.into_iter_enumerated() {
if let Some(patch) = patch {
debug!("MirPatch: patching block {:?}", src);
success.and(slice.index(idx))
}
ExprKind::SelfRef => {
- block.and(Lvalue::Arg(Arg::new(0)))
+ block.and(Lvalue::Local(Local::new(1)))
}
ExprKind::VarRef { id } => {
let index = this.var_indices[&id];
- block.and(Lvalue::Var(index))
+ block.and(Lvalue::Local(index))
}
ExprKind::StaticRef { id } => {
block.and(Lvalue::Static(id))
}
ExprKind::Return { value } => {
block = match value {
- Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
+ Some(value) => {
+ unpack!(this.into(&Lvalue::Local(RETURN_POINTER), block, value))
+ }
None => {
- this.cfg.push_assign_unit(block, source_info, &Lvalue::ReturnPointer);
+ this.cfg.push_assign_unit(block,
+ source_info,
+ &Lvalue::Local(RETURN_POINTER));
block
}
};
var,
subpattern: None, .. } => {
self.storage_live_for_bindings(block, &irrefutable_pat);
- let lvalue = Lvalue::Var(self.var_indices[&var]);
+ let lvalue = Lvalue::Local(self.var_indices[&var]);
return self.into(&lvalue, block, initializer);
}
_ => {}
pattern: &Pattern<'tcx>) {
match *pattern.kind {
PatternKind::Binding { var, ref subpattern, .. } => {
- let lvalue = Lvalue::Var(self.var_indices[&var]);
+ let lvalue = Lvalue::Local(self.var_indices[&var]);
let source_info = self.source_info(pattern.span);
self.cfg.push(block, Statement {
source_info: source_info,
let source_info = self.source_info(binding.span);
self.cfg.push(block, Statement {
source_info: source_info,
- kind: StatementKind::StorageLive(Lvalue::Var(var_index))
+ kind: StatementKind::StorageLive(Lvalue::Local(var_index))
});
self.cfg.push_assign(block, source_info,
- &Lvalue::Var(var_index), rvalue);
+ &Lvalue::Local(var_index), rvalue);
}
}
name: Name,
var_id: NodeId,
var_ty: Ty<'tcx>)
- -> Var
+ -> Local
{
debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, source_info={:?})",
var_id, name, var_ty, source_info);
- let var = self.var_decls.push(VarDecl::<'tcx> {
- source_info: source_info,
+ let var = self.local_decls.push(LocalDecl::<'tcx> {
mutability: mutability,
- name: name,
ty: var_ty.clone(),
+ name: Some(name),
+ source_info: Some(source_info),
});
let extent = self.extent_of_innermost_scope();
- self.schedule_drop(source_info.span, extent, &Lvalue::Var(var), var_ty);
+ self.schedule_drop(source_info.span, extent, &Lvalue::Local(var), var_ty);
self.var_indices.insert(var_id, var);
debug!("declare_binding: var={:?}", var);
/// NB: **No cleanup is scheduled for this temporary.** You should
/// call `schedule_drop` once the temporary is initialized.
pub fn temp(&mut self, ty: Ty<'tcx>) -> Lvalue<'tcx> {
- let temp = self.temp_decls.push(TempDecl { ty: ty });
- let lvalue = Lvalue::Temp(temp);
+ let temp = self.local_decls.push(LocalDecl::new_temp(ty));
+ let lvalue = Lvalue::Local(temp);
debug!("temp: created temp {:?} with type {:?}",
- lvalue, self.temp_decls[temp].ty);
+ lvalue, self.local_decls[temp].ty);
lvalue
}
cfg: CFG<'tcx>,
fn_span: Span,
+ arg_count: usize,
/// the current set of scopes, updated as we traverse;
/// see the `scope` module for more details
visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
visibility_scope: VisibilityScope,
- var_decls: IndexVec<Var, VarDecl<'tcx>>,
- var_indices: NodeMap<Var>,
- temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
+ /// Maps node ids of variable bindings to the `Local`s created for them.
+ var_indices: NodeMap<Local>,
+ local_decls: IndexVec<Local, LocalDecl<'tcx>>,
unit_temp: Option<Lvalue<'tcx>>,
/// cached block with the RESUME terminator; this is created
-> (Mir<'tcx>, ScopeAuxiliaryVec)
where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
{
+ let arguments: Vec<_> = arguments.collect();
+
let tcx = hir.tcx();
let span = tcx.map.span(fn_id);
- let mut builder = Builder::new(hir, span);
+ let mut builder = Builder::new(hir, span, arguments.len(), return_ty);
let body_id = ast_block.id;
let call_site_extent =
tcx.region_maps.lookup_code_extent(
CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
let mut block = START_BLOCK;
- let arg_decls = unpack!(block = builder.in_scope(call_site_extent, block, |builder| {
- let arg_decls = unpack!(block = builder.in_scope(arg_extent, block, |builder| {
- builder.args_and_body(block, return_ty, arguments, arg_extent, ast_block)
+ unpack!(block = builder.in_scope(call_site_extent, block, |builder| {
+ unpack!(block = builder.in_scope(arg_extent, block, |builder| {
+ builder.args_and_body(block, return_ty, &arguments, arg_extent, ast_block)
}));
let source_info = builder.source_info(span);
TerminatorKind::Goto { target: return_block });
builder.cfg.terminate(return_block, source_info,
TerminatorKind::Return);
- return_block.and(arg_decls)
+ return_block.unit()
}));
assert_eq!(block, builder.return_block());
}).collect()
});
- let (mut mir, aux) = builder.finish(upvar_decls, arg_decls, return_ty);
+ let (mut mir, aux) = builder.finish(upvar_decls, return_ty);
mir.spread_last_arg = spread_last_arg;
(mir, aux)
}
ast_expr: &'tcx hir::Expr)
-> (Mir<'tcx>, ScopeAuxiliaryVec) {
let tcx = hir.tcx();
+ let ty = tcx.expr_ty_adjusted(ast_expr);
let span = tcx.map.span(item_id);
- let mut builder = Builder::new(hir, span);
+ let mut builder = Builder::new(hir, span, 0, ty);
let extent = tcx.region_maps.temporary_scope(ast_expr.id)
.unwrap_or(ROOT_CODE_EXTENT);
let mut block = START_BLOCK;
let _ = builder.in_scope(extent, block, |builder| {
let expr = builder.hir.mirror(ast_expr);
- unpack!(block = builder.into(&Lvalue::ReturnPointer, block, expr));
+ unpack!(block = builder.into(&Lvalue::Local(RETURN_POINTER), block, expr));
let source_info = builder.source_info(span);
let return_block = builder.return_block();
return_block.unit()
});
- let ty = tcx.expr_ty_adjusted(ast_expr);
- builder.finish(vec![], IndexVec::new(), ty)
+ builder.finish(vec![], ty)
}
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
- fn new(hir: Cx<'a, 'gcx, 'tcx>, span: Span) -> Builder<'a, 'gcx, 'tcx> {
+ fn new(hir: Cx<'a, 'gcx, 'tcx>,
+ span: Span,
+ arg_count: usize,
+ return_ty: Ty<'tcx>)
+ -> Builder<'a, 'gcx, 'tcx> {
let mut builder = Builder {
hir: hir,
cfg: CFG { basic_blocks: IndexVec::new() },
fn_span: span,
+ arg_count: arg_count,
scopes: vec![],
visibility_scopes: IndexVec::new(),
visibility_scope: ARGUMENT_VISIBILITY_SCOPE,
scope_auxiliary: IndexVec::new(),
loop_scopes: vec![],
- temp_decls: IndexVec::new(),
- var_decls: IndexVec::new(),
+ local_decls: IndexVec::from_elem_n(LocalDecl::new_return_pointer(return_ty), 1),
var_indices: NodeMap(),
unit_temp: None,
cached_resume_block: None,
fn finish(self,
upvar_decls: Vec<UpvarDecl>,
- arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
return_ty: Ty<'tcx>)
-> (Mir<'tcx>, ScopeAuxiliaryVec) {
for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
self.visibility_scopes,
IndexVec::new(),
return_ty,
- self.var_decls,
- arg_decls,
- self.temp_decls,
+ self.local_decls,
+ self.arg_count,
upvar_decls,
self.fn_span
), self.scope_auxiliary)
}
- fn args_and_body<A>(&mut self,
- mut block: BasicBlock,
- return_ty: Ty<'tcx>,
- arguments: A,
- argument_extent: CodeExtent,
- ast_block: &'gcx hir::Block)
- -> BlockAnd<IndexVec<Arg, ArgDecl<'tcx>>>
- where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
+ fn args_and_body(&mut self,
+ mut block: BasicBlock,
+ return_ty: Ty<'tcx>,
+ arguments: &[(Ty<'gcx>, Option<&'gcx hir::Pat>)],
+ argument_extent: CodeExtent,
+ ast_block: &'gcx hir::Block)
+ -> BlockAnd<()>
{
- // to start, translate the argument patterns and collect the argument types.
+ // Allocate locals for the function arguments
+ for &(ty, pattern) in arguments.iter() {
+ // If this is a simple binding pattern, give the local a nice name for debuginfo.
+ let mut name = None;
+ if let Some(pat) = pattern {
+ if let hir::PatKind::Binding(_, ref ident, _) = pat.node {
+ name = Some(ident.node);
+ }
+ }
+
+ self.local_decls.push(LocalDecl {
+ mutability: Mutability::Not,
+ ty: ty,
+ source_info: None,
+ name: name,
+ });
+ }
+
let mut scope = None;
- let arg_decls = arguments.enumerate().map(|(index, (ty, pattern))| {
- let lvalue = Lvalue::Arg(Arg::new(index));
+ // Bind the argument patterns
+ for (index, &(ty, pattern)) in arguments.iter().enumerate() {
+ // Function arguments always get the first Local indices after the return pointer
+ let lvalue = Lvalue::Local(Local::new(index + 1));
+
if let Some(pattern) = pattern {
let pattern = self.hir.irrefutable_pat(pattern);
scope = self.declare_bindings(scope, ast_block.span, &pattern);
self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
argument_extent, &lvalue, ty);
- let mut name = keywords::Invalid.name();
- if let Some(pat) = pattern {
- if let hir::PatKind::Binding(_, ref ident, _) = pat.node {
- name = ident.node;
- }
- }
-
- ArgDecl {
- ty: ty,
- debug_name: name
- }
- }).collect();
+ }
// Enter the argument pattern bindings visibility scope, if it exists.
if let Some(visibility_scope) = scope {
// FIXME(#32959): temporary hack for the issue at hand
let return_is_unit = return_ty.is_nil();
// start the first basic block and translate the body
- unpack!(block = self.ast_block(&Lvalue::ReturnPointer, return_is_unit, block, ast_block));
+ unpack!(block = self.ast_block(&Lvalue::Local(RETURN_POINTER),
+ return_is_unit, block, ast_block));
- block.and(arg_decls)
+ block.unit()
}
fn get_unit_temp(&mut self) -> Lvalue<'tcx> {
For now, we keep a mapping from each `CodeExtent` to its
corresponding SEME region for later reference (see caveat in next
paragraph). This is because region scopes are tied to
-them. Eventually, when we shift to non-lexical lifetimes, three should
+them. Eventually, when we shift to non-lexical lifetimes, there should
be no need to remember this mapping.
There is one additional wrinkle, actually, that I wanted to hide from
early exit occurs, the method `exit_scope` is called. It is given the
current point in execution where the early exit occurs, as well as the
scope you want to branch to (note that all early exits from to some
-other enclosing scope). `exit_scope` will record thid exit point and
+other enclosing scope). `exit_scope` will record this exit point and
also add all drops.
Panics are handled in a similar fashion, except that a panic always
self.diverge_cleanup();
let scope = self.scopes.pop().unwrap();
assert_eq!(scope.extent, extent);
- unpack!(block = build_scope_drops(&mut self.cfg, &scope, &self.scopes, block));
+ unpack!(block = build_scope_drops(&mut self.cfg,
+ &scope,
+ &self.scopes,
+ block,
+ self.arg_count));
self.scope_auxiliary[scope.id]
.postdoms
.push(self.cfg.current_location(block));
scope.cached_exits.insert((target, extent), b);
b
};
- unpack!(block = build_scope_drops(&mut self.cfg, scope, rest, block));
+ unpack!(block = build_scope_drops(&mut self.cfg,
+ scope,
+ rest,
+ block,
+ self.arg_count));
if let Some(ref free_data) = scope.free {
let next = self.cfg.start_new_block();
let free = build_free(self.hir.tcx(), &tmp, free_data, next);
} else {
// Only temps and vars need their storage dead.
match *lvalue {
- Lvalue::Temp(_) | Lvalue::Var(_) => DropKind::Storage,
+ Lvalue::Local(index) if index.index() > self.arg_count + 1 => DropKind::Storage,
_ => return
}
};
fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
scope: &Scope<'tcx>,
earlier_scopes: &[Scope<'tcx>],
- mut block: BasicBlock)
+ mut block: BasicBlock,
+ arg_count: usize)
-> BlockAnd<()> {
let mut iter = scope.drops.iter().rev().peekable();
while let Some(drop_data) = iter.next() {
DropKind::Storage => {
// Only temps and vars need their storage dead.
match drop_data.location {
- Lvalue::Temp(_) | Lvalue::Var(_) => {}
+ Lvalue::Local(index) if index.index() > arg_count => {}
_ => continue
}
use rustc::mir::repr::{Local, Location, Lvalue, Mir};
use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_data_structures::indexed_vec::IndexVec;
use std::marker::PhantomData;
use std::mem;
pub struct DefUseAnalysis<'tcx> {
info: IndexVec<Local, Info<'tcx>>,
- mir_summary: MirSummary,
}
#[derive(Clone)]
impl<'tcx> DefUseAnalysis<'tcx> {
pub fn new(mir: &Mir<'tcx>) -> DefUseAnalysis<'tcx> {
DefUseAnalysis {
- info: IndexVec::from_elem_n(Info::new(), mir.count_locals()),
- mir_summary: MirSummary::new(mir),
+ info: IndexVec::from_elem_n(Info::new(), mir.local_decls.len()),
}
}
pub fn analyze(&mut self, mir: &Mir<'tcx>) {
let mut finder = DefUseFinder {
info: mem::replace(&mut self.info, IndexVec::new()),
- mir_summary: self.mir_summary,
};
finder.visit_mir(mir);
self.info = finder.info
for lvalue_use in &self.info[local].defs_and_uses {
MutateUseVisitor::new(local,
&mut callback,
- self.mir_summary,
mir).visit_location(mir, lvalue_use.location)
}
}
struct DefUseFinder<'tcx> {
info: IndexVec<Local, Info<'tcx>>,
- mir_summary: MirSummary,
}
impl<'tcx> DefUseFinder<'tcx> {
fn lvalue_mut_info(&mut self, lvalue: &Lvalue<'tcx>) -> Option<&mut Info<'tcx>> {
let info = &mut self.info;
- self.mir_summary.local_index(lvalue).map(move |local| &mut info[local])
+
+ if let Lvalue::Local(local) = *lvalue {
+ Some(&mut info[local])
+ } else {
+ None
+ }
}
}
struct MutateUseVisitor<'tcx, F> {
query: Local,
callback: F,
- mir_summary: MirSummary,
phantom: PhantomData<&'tcx ()>,
}
impl<'tcx, F> MutateUseVisitor<'tcx, F> {
- fn new(query: Local, callback: F, mir_summary: MirSummary, _: &Mir<'tcx>)
+ fn new(query: Local, callback: F, _: &Mir<'tcx>)
-> MutateUseVisitor<'tcx, F>
where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
MutateUseVisitor {
query: query,
callback: callback,
- mir_summary: mir_summary,
phantom: PhantomData,
}
}
lvalue: &mut Lvalue<'tcx>,
context: LvalueContext<'tcx>,
location: Location) {
- if self.mir_summary.local_index(lvalue) == Some(self.query) {
- (self.callback)(lvalue, context, location)
- }
- self.super_lvalue(lvalue, context, location)
- }
-}
-
-/// A small structure that enables various metadata of the MIR to be queried
-/// without a reference to the MIR itself.
-#[derive(Clone, Copy)]
-pub struct MirSummary {
- arg_count: usize,
- var_count: usize,
- temp_count: usize,
-}
-
-impl MirSummary {
- pub fn new(mir: &Mir) -> MirSummary {
- MirSummary {
- arg_count: mir.arg_decls.len(),
- var_count: mir.var_decls.len(),
- temp_count: mir.temp_decls.len(),
- }
- }
-
- pub fn local_index<'tcx>(&self, lvalue: &Lvalue<'tcx>) -> Option<Local> {
- match *lvalue {
- Lvalue::Arg(arg) => Some(Local::new(arg.index())),
- Lvalue::Var(var) => Some(Local::new(var.index() + self.arg_count)),
- Lvalue::Temp(temp) => {
- Some(Local::new(temp.index() + self.arg_count + self.var_count))
+ if let Lvalue::Local(local) = *lvalue {
+ if local == self.query {
+ (self.callback)(lvalue, context, location)
}
- Lvalue::ReturnPointer => {
- Some(Local::new(self.arg_count + self.var_count + self.temp_count))
- }
- _ => None,
}
+ self.super_lvalue(lvalue, context, location)
}
}
-
write!(w, " label=<fn {}(", dot::escape_html(&tcx.node_path_str(nid)))?;
// fn argument types.
- for (i, arg) in mir.arg_decls.iter().enumerate() {
+ for (i, arg) in mir.arg_iter().enumerate() {
if i > 0 {
write!(w, ", ")?;
}
- write!(w, "{:?}: {}", Lvalue::Arg(Arg::new(i)), escape(&arg.ty))?;
+ write!(w, "{:?}: {}", Lvalue::Local(arg), escape(&mir.local_decls[arg].ty))?;
}
write!(w, ") -> {}", escape(mir.return_ty))?;
write!(w, r#"<br align="left"/>"#)?;
- // User variable types (including the user's name in a comment).
- for (i, var) in mir.var_decls.iter().enumerate() {
+ for local in mir.var_and_temp_iter() {
+ let decl = &mir.local_decls[local];
+
write!(w, "let ")?;
- if var.mutability == Mutability::Mut {
+ if decl.mutability == Mutability::Mut {
write!(w, "mut ")?;
}
- write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
- Lvalue::Var(Var::new(i)), escape(&var.ty), var.name)?;
- }
- // Compiler-introduced temporary types.
- for (i, temp) in mir.temp_decls.iter().enumerate() {
- write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
- Lvalue::Temp(Temp::new(i)), escape(&temp.ty))?;
+ if let Some(name) = decl.name {
+ write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
+ Lvalue::Local(local), escape(&decl.ty), name)?;
+ } else {
+ write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
+ Lvalue::Local(local), escape(&decl.ty))?;
+ }
}
writeln!(w, ">;")
fn escape<T: Debug>(t: &T) -> String {
dot::escape_html(&format!("{:?}", t))
}
+
+// TODO manually test
format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
}
+/// Prints user-defined variables in a scope tree.
+///
+/// Returns the total number of variables printed.
fn write_scope_tree(tcx: TyCtxt,
mir: &Mir,
scope_tree: &FnvHashMap<VisibilityScope, Vec<VisibilityScope>>,
writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
// User variable types (including the user's name in a comment).
- for (id, var) in mir.var_decls.iter_enumerated() {
- // Skip if not declared in this scope.
- if var.source_info.scope != child {
+ for local in mir.var_iter() {
+ let var = &mir.local_decls[local];
+ let (name, source_info) = if var.source_info.unwrap().scope == child {
+ (var.name.unwrap(), var.source_info.unwrap())
+ } else {
+ // Not a variable or not declared in this scope.
continue;
- }
+ };
let mut_str = if var.mutability == Mutability::Mut {
"mut "
INDENT,
indent,
mut_str,
- id,
+ local,
var.ty);
writeln!(w, "{0:1$} // \"{2}\" in {3}",
indented_var,
ALIGN,
- var.name,
- comment(tcx, var.source_info))?;
+ name,
+ comment(tcx, source_info))?;
}
write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
}
}
+ // Print return pointer
+ let indented_retptr = format!("{}let mut {:?}: {};",
+ INDENT,
+ RETURN_POINTER,
+ mir.return_ty);
+ writeln!(w, "{0:1$} // return pointer",
+ indented_retptr,
+ ALIGN)?;
+
write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
- write_mir_decls(mir, w)
+ write_temp_decls(mir, w)?;
+
+ // Add an empty line before the first block is printed.
+ writeln!(w, "")?;
+
+ Ok(())
}
fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
write!(w, "(")?;
// fn argument types.
- for (i, arg) in mir.arg_decls.iter_enumerated() {
- if i.index() != 0 {
+ for (i, arg) in mir.arg_iter().enumerate() {
+ if i != 0 {
write!(w, ", ")?;
}
- write!(w, "{:?}: {}", Lvalue::Arg(i), arg.ty)?;
+ write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
}
write!(w, ") -> {}", mir.return_ty)
} else {
- assert!(mir.arg_decls.is_empty());
+ assert_eq!(mir.arg_count, 0);
write!(w, ": {} =", mir.return_ty)
}
}
-fn write_mir_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
+fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
// Compiler-introduced temporary types.
- for (id, temp) in mir.temp_decls.iter_enumerated() {
- writeln!(w, "{}let mut {:?}: {};", INDENT, id, temp.ty)?;
- }
-
- // Wrote any declaration? Add an empty line before the first block is printed.
- if !mir.var_decls.is_empty() || !mir.temp_decls.is_empty() {
- writeln!(w, "")?;
+ for temp in mir.temp_iter() {
+ writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
}
Ok(())
}
+
+// TODO manually test
//! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
//! future.
-use def_use::{DefUseAnalysis, MirSummary};
+use def_use::DefUseAnalysis;
use rustc::mir::repr::{Constant, Local, Location, Lvalue, Mir, Operand, Rvalue, StatementKind};
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc::mir::visit::MutVisitor;
use rustc::ty::TyCtxt;
-use rustc_data_structures::indexed_vec::Idx;
use transform::qualify_consts;
pub struct CopyPropagation;
def_use_analysis.analyze(mir);
let mut changed = false;
- for dest_local_index in 0..mir.count_locals() {
- let dest_local = Local::new(dest_local_index);
- debug!("Considering destination local: {}", mir.format_local(dest_local));
+ for dest_local in mir.local_decls.indices() {
+ debug!("Considering destination local: {:?}", dest_local);
let action;
let location;
let dest_use_info = def_use_analysis.local_info(dest_local);
let dest_def_count = dest_use_info.def_count_not_including_drop();
if dest_def_count == 0 {
- debug!(" Can't copy-propagate local: dest {} undefined",
- mir.format_local(dest_local));
+ debug!(" Can't copy-propagate local: dest {:?} undefined",
+ dest_local);
continue
}
if dest_def_count > 1 {
- debug!(" Can't copy-propagate local: dest {} defined {} times",
- mir.format_local(dest_local),
+ debug!(" Can't copy-propagate local: dest {:?} defined {} times",
+ dest_local,
dest_use_info.def_count());
continue
}
if dest_use_info.use_count() == 0 {
- debug!(" Can't copy-propagate local: dest {} unused",
- mir.format_local(dest_local));
+ debug!(" Can't copy-propagate local: dest {:?} unused",
+ dest_local);
continue
}
let dest_lvalue_def = dest_use_info.defs_and_uses.iter().filter(|lvalue_def| {
// That use of the source must be an assignment.
match statement.kind {
- StatementKind::Assign(ref dest_lvalue, Rvalue::Use(ref operand)) if
- Some(dest_local) == mir.local_index(dest_lvalue) => {
+ StatementKind::Assign(Lvalue::Local(local), Rvalue::Use(ref operand)) if
+ local == dest_local => {
let maybe_action = match *operand {
Operand::Consume(ref src_lvalue) => {
- Action::local_copy(mir, &def_use_analysis, src_lvalue)
+ Action::local_copy(&def_use_analysis, src_lvalue)
}
Operand::Constant(ref src_constant) => {
Action::constant(src_constant)
}
impl<'tcx> Action<'tcx> {
- fn local_copy(mir: &Mir<'tcx>, def_use_analysis: &DefUseAnalysis, src_lvalue: &Lvalue<'tcx>)
+ fn local_copy(def_use_analysis: &DefUseAnalysis, src_lvalue: &Lvalue<'tcx>)
-> Option<Action<'tcx>> {
// The source must be a local.
- let src_local = match mir.local_index(src_lvalue) {
- Some(src_local) => src_local,
- None => {
- debug!(" Can't copy-propagate local: source is not a local");
- return None
- }
+ let src_local = if let Lvalue::Local(local) = *src_lvalue {
+ local
+ } else {
+ debug!(" Can't copy-propagate local: source is not a local");
+ return None;
};
// We're trying to copy propagate a local.
// First, remove all markers.
//
// FIXME(pcwalton): Don't do this. Merge live ranges instead.
- debug!(" Replacing all uses of {} with {} (local)",
- mir.format_local(dest_local),
- mir.format_local(src_local));
+ debug!(" Replacing all uses of {:?} with {:?} (local)",
+ dest_local,
+ src_local);
for lvalue_use in &def_use_analysis.local_info(dest_local).defs_and_uses {
if lvalue_use.context.is_storage_marker() {
mir.make_statement_nop(lvalue_use.location)
}
// Replace all uses of the destination local with the source local.
- let src_lvalue = Lvalue::from_local(mir, src_local);
+ let src_lvalue = Lvalue::Local(src_local);
def_use_analysis.replace_all_defs_and_uses_with(dest_local, mir, src_lvalue);
// Finally, zap the now-useless assignment instruction.
// First, remove all markers.
//
// FIXME(pcwalton): Don't do this. Merge live ranges instead.
- debug!(" Replacing all uses of {} with {:?} (constant)",
- mir.format_local(dest_local),
+ debug!(" Replacing all uses of {:?} with {:?} (constant)",
+ dest_local,
src_constant);
let dest_local_info = def_use_analysis.local_info(dest_local);
for lvalue_use in &dest_local_info.defs_and_uses {
}
// Replace all uses of the destination local with the constant.
- let mut visitor = ConstantPropagationVisitor::new(MirSummary::new(mir),
- dest_local,
+ let mut visitor = ConstantPropagationVisitor::new(dest_local,
src_constant);
for dest_lvalue_use in &dest_local_info.defs_and_uses {
visitor.visit_location(mir, dest_lvalue_use.location)
struct ConstantPropagationVisitor<'tcx> {
dest_local: Local,
constant: Constant<'tcx>,
- mir_summary: MirSummary,
uses_replaced: usize,
}
impl<'tcx> ConstantPropagationVisitor<'tcx> {
- fn new(mir_summary: MirSummary, dest_local: Local, constant: Constant<'tcx>)
+ fn new(dest_local: Local, constant: Constant<'tcx>)
-> ConstantPropagationVisitor<'tcx> {
ConstantPropagationVisitor {
dest_local: dest_local,
constant: constant,
- mir_summary: mir_summary,
uses_replaced: 0,
}
}
self.super_operand(operand, location);
match *operand {
- Operand::Consume(ref lvalue) => {
- if self.mir_summary.local_index(lvalue) != Some(self.dest_local) {
- return
- }
- }
- Operand::Constant(_) => return,
+ Operand::Consume(Lvalue::Local(local)) if local == self.dest_local => {}
+ _ => return,
}
*operand = Operand::Constant(self.constant.clone());
self.uses_replaced += 1
}
}
-
//! Performs various peephole optimizations.
-use rustc::mir::repr::{Location, Lvalue, Mir, Operand, ProjectionElem, Rvalue};
+use rustc::mir::repr::{Location, Lvalue, Mir, Operand, ProjectionElem, Rvalue, Local};
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc::mir::visit::{MutVisitor, Visitor};
use rustc::ty::TyCtxt;
use rustc::util::nodemap::FnvHashSet;
+use rustc_data_structures::indexed_vec::Idx;
use std::mem;
pub struct InstCombine {
debug!("Replacing `&*`: {:?}", rvalue);
let new_lvalue = match *rvalue {
Rvalue::Ref(_, _, Lvalue::Projection(ref mut projection)) => {
- mem::replace(&mut projection.base, Lvalue::ReturnPointer)
+ // Replace with dummy
+ mem::replace(&mut projection.base, Lvalue::Local(Local::new(0)))
}
_ => bug!("Detected `&*` but didn't find `&*`!"),
};
struct OptimizationList {
and_stars: FnvHashSet<Location>,
}
-
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use std::iter;
use std::mem;
use std::usize;
ShuffleIndices(BasicBlock)
}
-struct TempCollector {
- temps: IndexVec<Temp, TempState>,
- span: Span
+struct TempCollector<'tcx> {
+ temps: IndexVec<Local, TempState>,
+ span: Span,
+ mir: &'tcx Mir<'tcx>,
}
-impl<'tcx> Visitor<'tcx> for TempCollector {
- fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext, location: Location) {
+impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> {
+ fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext<'tcx>, location: Location) {
self.super_lvalue(lvalue, context, location);
- if let Lvalue::Temp(index) = *lvalue {
+ if let Lvalue::Local(index) = *lvalue {
+ // We're only interested in temporaries
+ if self.mir.local_kind(index) != LocalKind::Temp {
+ return;
+ }
+
// Ignore drops, if the temp gets promoted,
// then it's constant and thus drop is noop.
// Storage live ranges are also irrelevant.
}
}
-pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Temp, TempState> {
+pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Local, TempState> {
let mut collector = TempCollector {
- temps: IndexVec::from_elem(TempState::Undefined, &mir.temp_decls),
- span: mir.span
+ temps: IndexVec::from_elem(TempState::Undefined, &mir.local_decls),
+ span: mir.span,
+ mir: mir,
};
for (bb, data) in rpo {
collector.visit_basic_block_data(bb, data);
struct Promoter<'a, 'tcx: 'a> {
source: &'a mut Mir<'tcx>,
promoted: Mir<'tcx>,
- temps: &'a mut IndexVec<Temp, TempState>,
+ temps: &'a mut IndexVec<Local, TempState>,
/// If true, all nested temps are also kept in the
/// source MIR, not moved to the promoted MIR.
/// Copy the initialization of this temp to the
/// promoted MIR, recursing through temps.
- fn promote_temp(&mut self, temp: Temp) -> Temp {
+ fn promote_temp(&mut self, temp: Local) -> Local {
let old_keep_original = self.keep_original;
let (bb, stmt_idx) = match self.temps[temp] {
TempState::Defined {
});
}
- let new_temp = self.promoted.temp_decls.push(TempDecl {
- ty: self.source.temp_decls[temp].ty
- });
+ let new_temp = self.promoted.local_decls.push(
+ LocalDecl::new_temp(self.source.local_decls[temp].ty));
// Inject the Rvalue or Call into the promoted MIR.
if stmt_idx < no_stmts {
- self.assign(Lvalue::Temp(new_temp), rvalue.unwrap(), source_info.span);
+ self.assign(Lvalue::Local(new_temp), rvalue.unwrap(), source_info.span);
} else {
let last = self.promoted.basic_blocks().last().unwrap();
let new_target = self.new_block();
let mut call = call.unwrap();
match call {
TerminatorKind::Call { ref mut destination, ..} => {
- *destination = Some((Lvalue::Temp(new_temp), new_target));
+ *destination = Some((Lvalue::Local(new_temp), new_target));
}
_ => bug!()
}
}
}
};
- self.visit_rvalue(&mut rvalue, Location{
+ self.visit_rvalue(&mut rvalue, Location {
block: BasicBlock::new(0),
statement_index: usize::MAX
});
- self.assign(Lvalue::ReturnPointer, rvalue, span);
+
+ self.assign(Lvalue::Local(RETURN_POINTER), rvalue, span);
self.source.promoted.push(self.promoted);
}
}
lvalue: &mut Lvalue<'tcx>,
context: LvalueContext<'tcx>,
location: Location) {
- if let Lvalue::Temp(ref mut temp) = *lvalue {
- *temp = self.promote_temp(*temp);
+ if let Lvalue::Local(ref mut temp) = *lvalue {
+ if self.source.local_kind(*temp) == LocalKind::Temp {
+ *temp = self.promote_temp(*temp);
+ }
}
self.super_lvalue(lvalue, context, location);
}
pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
- mut temps: IndexVec<Temp, TempState>,
+ mut temps: IndexVec<Local, TempState>,
candidates: Vec<Candidate>) {
// Visit candidates in reverse, in case they're nested.
for candidate in candidates.into_iter().rev() {
"expected assignment to promote");
}
};
- if let Lvalue::Temp(index) = *dest {
+ if let Lvalue::Local(index) = *dest {
if temps[index] == TempState::PromotedOut {
// Already promoted.
continue;
}
};
+ // Declare return pointer local
+ let initial_locals = iter::once(LocalDecl::new_return_pointer(ty)).collect();
+
let mut promoter = Promoter {
- source: mir,
promoted: Mir::new(
IndexVec::new(),
Some(VisibilityScopeData {
}).into_iter().collect(),
IndexVec::new(),
ty,
- IndexVec::new(),
- IndexVec::new(),
- IndexVec::new(),
+ initial_locals,
+ 0,
vec![],
span
),
+ source: mir,
temps: &mut temps,
keep_original: false
};
}
// Eliminate assignments to, and drops of promoted temps.
- let promoted = |index: Temp| temps[index] == TempState::PromotedOut;
+ let promoted = |index: Local| temps[index] == TempState::PromotedOut;
for block in mir.basic_blocks_mut() {
block.statements.retain(|statement| {
match statement.kind {
- StatementKind::Assign(Lvalue::Temp(index), _) |
- StatementKind::StorageLive(Lvalue::Temp(index)) |
- StatementKind::StorageDead(Lvalue::Temp(index)) => {
+ StatementKind::Assign(Lvalue::Local(index), _) |
+ StatementKind::StorageLive(Lvalue::Local(index)) |
+ StatementKind::StorageDead(Lvalue::Local(index)) => {
!promoted(index)
}
_ => true
});
let terminator = block.terminator_mut();
match terminator.kind {
- TerminatorKind::Drop { location: Lvalue::Temp(index), target, .. } => {
+ TerminatorKind::Drop { location: Lvalue::Local(index), target, .. } => {
if promoted(index) {
terminator.kind = TerminatorKind::Goto {
target: target
param_env: ty::ParameterEnvironment<'tcx>,
qualif_map: &'a mut DefIdMap<Qualif>,
mir_map: Option<&'a MirMap<'tcx>>,
- temp_qualif: IndexVec<Temp, Option<Qualif>>,
+ temp_qualif: IndexVec<Local, Option<Qualif>>,
return_qualif: Option<Qualif>,
qualif: Qualif,
const_fn_arg_vars: BitVector,
- temp_promotion_state: IndexVec<Temp, TempState>,
+ temp_promotion_state: IndexVec<Local, TempState>,
promotion_candidates: Vec<Candidate>
}
param_env: param_env,
qualif_map: qualif_map,
mir_map: mir_map,
- temp_qualif: IndexVec::from_elem(None, &mir.temp_decls),
+ temp_qualif: IndexVec::from_elem(None, &mir.local_decls),
return_qualif: None,
qualif: Qualif::empty(),
- const_fn_arg_vars: BitVector::new(mir.var_decls.len()),
+ const_fn_arg_vars: BitVector::new(mir.local_decls.len()),
temp_promotion_state: temps,
promotion_candidates: vec![]
}
// Only handle promotable temps in non-const functions.
if self.mode == Mode::Fn {
- if let Lvalue::Temp(index) = *dest {
- if self.temp_promotion_state[index].is_promotable() {
+ if let Lvalue::Local(index) = *dest {
+ if self.mir.local_kind(index) == LocalKind::Temp
+ && self.temp_promotion_state[index].is_promotable() {
+ debug!("store to promotable temp {:?}", index);
store(&mut self.temp_qualif[index]);
}
}
}
match *dest {
- Lvalue::Temp(index) => store(&mut self.temp_qualif[index]),
- Lvalue::ReturnPointer => store(&mut self.return_qualif),
+ Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
+ debug!("store to temp {:?}", index);
+ store(&mut self.temp_qualif[index])
+ }
+ Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
+ debug!("store to return pointer {:?}", index);
+ store(&mut self.return_qualif)
+ }
Lvalue::Projection(box Projection {
- base: Lvalue::Temp(index),
+ base: Lvalue::Local(index),
elem: ProjectionElem::Deref
- }) if self.mir.temp_decls[index].ty.is_unique()
+ }) if self.mir.local_kind(index) == LocalKind::Temp
+ && self.mir.local_decls[index].ty.is_unique()
&& self.temp_qualif[index].map_or(false, |qualif| {
qualif.intersects(Qualif::NOT_CONST)
}) => {
/// Qualify a whole const, static initializer or const fn.
fn qualify_const(&mut self) -> Qualif {
+ debug!("qualifying {} {}", self.mode, self.tcx.item_path_str(self.def_id));
+
let mir = self.mir;
let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
TerminatorKind::Return => {
// Check for unused values. This usually means
// there are extra statements in the AST.
- for temp in mir.temp_decls.indices() {
+ for temp in mir.temp_iter() {
if self.temp_qualif[temp].is_none() {
continue;
}
// Make sure there are no extra unassigned variables.
self.qualif = Qualif::NOT_CONST;
- for index in 0..mir.var_decls.len() {
- if !self.const_fn_arg_vars.contains(index) {
- self.assign(&Lvalue::Var(Var::new(index)), Location {
+ for index in mir.var_iter() {
+ if !self.const_fn_arg_vars.contains(index.index()) {
+ debug!("unassigned variable {:?}", index);
+ self.assign(&Lvalue::Local(index), Location {
block: bb,
statement_index: usize::MAX,
});
context: LvalueContext<'tcx>,
location: Location) {
match *lvalue {
- Lvalue::Arg(_) => {
- self.add(Qualif::FN_ARGUMENT);
- }
- Lvalue::Var(_) => {
- self.add(Qualif::NOT_CONST);
- }
- Lvalue::Temp(index) => {
- if !self.temp_promotion_state[index].is_promotable() {
- self.add(Qualif::NOT_PROMOTABLE);
+ Lvalue::Local(local) => match self.mir.local_kind(local) {
+ LocalKind::ReturnPointer => {
+ self.not_const();
+ }
+ LocalKind::Arg => {
+ self.add(Qualif::FN_ARGUMENT);
+ }
+ LocalKind::Var => {
+ self.add(Qualif::NOT_CONST);
}
+ LocalKind::Temp => {
+ if !self.temp_promotion_state[local].is_promotable() {
+ self.add(Qualif::NOT_PROMOTABLE);
+ }
- if let Some(qualif) = self.temp_qualif[index] {
- self.add(qualif);
- } else {
- self.not_const();
+ if let Some(qualif) = self.temp_qualif[local] {
+ self.add(qualif);
+ } else {
+ self.not_const();
+ }
}
- }
+ },
Lvalue::Static(_) => {
self.add(Qualif::STATIC);
if self.mode == Mode::Const || self.mode == Mode::ConstFn {
a constant instead", self.mode);
}
}
- Lvalue::ReturnPointer => {
- self.not_const();
- }
Lvalue::Projection(ref proj) => {
self.nest(|this| {
this.super_lvalue(lvalue, context, location);
if self.mode == Mode::Fn || self.mode == Mode::ConstFn {
if !self.qualif.intersects(Qualif::NEVER_PROMOTE) {
// We can only promote direct borrows of temps.
- if let Lvalue::Temp(_) = *lvalue {
- self.promotion_candidates.push(candidate);
+ if let Lvalue::Local(local) = *lvalue {
+ if self.mir.local_kind(local) == LocalKind::Temp {
+ self.promotion_candidates.push(candidate);
+ }
}
}
}
self.visit_rvalue(rvalue, location);
// Check the allowed const fn argument forms.
- if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) {
- if self.const_fn_arg_vars.insert(index.index()) {
+ if let (Mode::ConstFn, &Lvalue::Local(index)) = (self.mode, dest) {
+ if self.mir.local_kind(index) == LocalKind::Var &&
+ self.const_fn_arg_vars.insert(index.index()) {
+
// Direct use of an argument is permitted.
- if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue {
- return;
+ if let Rvalue::Use(Operand::Consume(Lvalue::Local(local))) = *rvalue {
+ if self.mir.local_kind(local) == LocalKind::Arg {
+ return;
+ }
}
// Avoid a generic error for other uses of arguments.
if self.qualif.intersects(Qualif::FN_ARGUMENT) {
- let decl = &self.mir.var_decls[index];
- span_err!(self.tcx.sess, decl.source_info.span, E0022,
+ let decl = &self.mir.local_decls[index];
+ span_err!(self.tcx.sess, decl.source_info.unwrap().span, E0022,
"arguments of constant functions can only \
be immutable by-value bindings");
return;
fn visit_mir(&mut self, mir: &Mir<'tcx>) {
self.sanitize_type(&"return type", mir.return_ty);
- for var_decl in &mir.var_decls {
- self.sanitize_type(var_decl, var_decl.ty);
- }
- for (n, arg_decl) in mir.arg_decls.iter().enumerate() {
- self.sanitize_type(&(n, arg_decl), arg_decl.ty);
- }
- for (n, tmp_decl) in mir.temp_decls.iter().enumerate() {
- self.sanitize_type(&(n, tmp_decl), tmp_decl.ty);
+ for local_decl in &mir.local_decls {
+ self.sanitize_type(local_decl, local_decl.ty);
}
if self.errors_reported {
return;
fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>, location: Location) -> LvalueTy<'tcx> {
debug!("sanitize_lvalue: {:?}", lvalue);
match *lvalue {
- Lvalue::Var(index) => LvalueTy::Ty { ty: self.mir.var_decls[index].ty },
- Lvalue::Temp(index) => LvalueTy::Ty { ty: self.mir.temp_decls[index].ty },
- Lvalue::Arg(index) => LvalueTy::Ty { ty: self.mir.arg_decls[index].ty },
+ Lvalue::Local(index) => LvalueTy::Ty { ty: self.mir.local_decls[index].ty },
Lvalue::Static(def_id) =>
LvalueTy::Ty { ty: self.tcx().lookup_item_type(def_id).ty },
- Lvalue::ReturnPointer => {
- LvalueTy::Ty { ty: self.mir.return_ty }
- }
Lvalue::Projection(ref proj) => {
let base_ty = self.sanitize_lvalue(&proj.base, location);
if let LvalueTy::Ty { ty } = base_ty {
StatementKind::StorageLive(ref lv) |
StatementKind::StorageDead(ref lv) => {
match *lv {
- Lvalue::Temp(_) | Lvalue::Var(_) => {}
+ Lvalue::Local(_) => {}
_ => {
- span_mirbug!(self, stmt, "bad lvalue: expected temp or var");
+ span_mirbug!(self, stmt, "bad lvalue: expected local");
}
}
}
impl<'tcx> MirPass<'tcx> for TypeckMir {
fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource, mir: &mut Mir<'tcx>) {
+ debug!("run_pass: {}", tcx.node_path_str(src.item_id()));
+
if tcx.sess.err_count() > 0 {
// compiling a broken program can obviously result in a
// broken MIR, so try not to report duplicate errors.
// Find all the scopes with variables defined in them.
let mut has_variables = BitVector::new(mir.visibility_scopes.len());
- for var in &mir.var_decls {
- has_variables.insert(var.source_info.scope.index());
+ for var in mir.var_iter() {
+ let decl = &mir.local_decls[var];
+ has_variables.insert(decl.source_info.unwrap().scope.index());
}
// Instantiate all scopes.
use rustc::mir::traversal;
use common::{self, Block, BlockAndBuilder};
use glue;
-use std::iter;
use super::rvalue;
pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>,
analyzer.visit_mir(mir);
- let local_types = mir.arg_decls.iter().map(|a| a.ty)
- .chain(mir.var_decls.iter().map(|v| v.ty))
- .chain(mir.temp_decls.iter().map(|t| t.ty))
- .chain(iter::once(mir.return_ty));
- for (index, ty) in local_types.enumerate() {
+ for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() {
let ty = bcx.monomorphize(&ty);
debug!("local {} has type {:?}", index, ty);
if ty.is_scalar() ||
fn new(mir: &'mir mir::Mir<'tcx>,
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>)
-> LocalAnalyzer<'mir, 'bcx, 'tcx> {
- let local_count = mir.count_locals();
LocalAnalyzer {
mir: mir,
bcx: bcx,
- lvalue_locals: BitVector::new(local_count),
- seen_assigned: BitVector::new(local_count)
+ lvalue_locals: BitVector::new(mir.local_decls.len()),
+ seen_assigned: BitVector::new(mir.local_decls.len())
}
}
location: Location) {
debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue);
- if let Some(index) = self.mir.local_index(lvalue) {
+ if let mir::Lvalue::Local(index) = *lvalue {
self.mark_assigned(index);
if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) {
self.mark_as_lvalue(index);
// Allow uses of projections of immediate pair fields.
if let mir::Lvalue::Projection(ref proj) = *lvalue {
- if self.mir.local_index(&proj.base).is_some() {
+ if let mir::Lvalue::Local(_) = proj.base {
let ty = proj.base.ty(self.mir, self.bcx.tcx());
let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx()));
}
}
- if let Some(index) = self.mir.local_index(lvalue) {
+ if let mir::Lvalue::Local(index) = *lvalue {
match context {
LvalueContext::Call => {
self.mark_assigned(index);
}
let llval = if let Some(cast_ty) = ret.cast {
- let index = mir.local_index(&mir::Lvalue::ReturnPointer).unwrap();
- let op = match self.locals[index] {
+ let op = match self.locals[mir::RETURN_POINTER] {
LocalRef::Operand(Some(op)) => op,
LocalRef::Operand(None) => bug!("use of return before def"),
LocalRef::Lvalue(tr_lvalue) => {
}
load
} else {
- let op = self.trans_consume(&bcx, &mir::Lvalue::ReturnPointer);
+ let op = self.trans_consume(&bcx, &mir::Lvalue::Local(mir::RETURN_POINTER));
op.pack_if_pair(&bcx).immediate()
};
bcx.ret(llval);
if fn_ret_ty.is_ignore() {
return ReturnDest::Nothing;
}
- let dest = if let Some(index) = self.mir.local_index(dest) {
+ let dest = if let mir::Lvalue::Local(index) = *dest {
let ret_ty = self.monomorphized_lvalue_ty(dest);
match self.locals[index] {
LocalRef::Lvalue(dest) => dest,
fn new(ccx: &'a CrateContext<'a, 'tcx>,
mir: &'a mir::Mir<'tcx>,
substs: &'tcx Substs<'tcx>,
- args: IndexVec<mir::Arg, Const<'tcx>>)
+ args: IndexVec<mir::Local, Const<'tcx>>)
-> MirConstContext<'a, 'tcx> {
let mut context = MirConstContext {
ccx: ccx,
mir: mir,
substs: substs,
- locals: (0..mir.count_locals()).map(|_| None).collect(),
+ locals: (0..mir.local_decls.len()).map(|_| None).collect(),
};
for (i, arg) in args.into_iter().enumerate() {
- let index = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(i))).unwrap();
+ // Locals after local0 are the function arguments
+ let index = mir::Local::new(i + 1);
context.locals[index] = Some(arg);
}
context
fn trans_def(ccx: &'a CrateContext<'a, 'tcx>,
mut instance: Instance<'tcx>,
- args: IndexVec<mir::Arg, Const<'tcx>>)
+ args: IndexVec<mir::Local, Const<'tcx>>)
-> Result<Const<'tcx>, ConstEvalErr> {
// Try to resolve associated constants.
if let Some(trait_id) = ccx.tcx().trait_of_item(instance.def) {
mir::TerminatorKind::Goto { target } => target,
mir::TerminatorKind::Return => {
failure?;
- let index = self.mir.local_index(&mir::Lvalue::ReturnPointer).unwrap();
- return Ok(self.locals[index].unwrap_or_else(|| {
+ return Ok(self.locals[mir::RETURN_POINTER].unwrap_or_else(|| {
span_bug!(span, "no returned value in constant");
}));
}
}
fn store(&mut self, dest: &mir::Lvalue<'tcx>, value: Const<'tcx>, span: Span) {
- if let Some(index) = self.mir.local_index(dest) {
+ if let mir::Lvalue::Local(index) = *dest {
self.locals[index] = Some(value);
} else {
span_bug!(span, "assignment to {:?} in constant", dest);
-> Result<ConstLvalue<'tcx>, ConstEvalErr> {
let tcx = self.ccx.tcx();
- if let Some(index) = self.mir.local_index(lvalue) {
+ if let mir::Lvalue::Local(index) = *lvalue {
return Ok(self.locals[index].unwrap_or_else(|| {
span_bug!(span, "{:?} not initialized", lvalue)
}).as_lvalue());
}
let lvalue = match *lvalue {
- mir::Lvalue::Var(_) |
- mir::Lvalue::Temp(_) |
- mir::Lvalue::Arg(_) |
- mir::Lvalue::ReturnPointer => bug!(), // handled above
+ mir::Lvalue::Local(_) => bug!(), // handled above
mir::Lvalue::Static(def_id) => {
ConstLvalue {
base: Base::Static(consts::get_static(self.ccx, def_id)),
let ccx = bcx.ccx();
let tcx = bcx.tcx();
- if let Some(index) = self.mir.local_index(lvalue) {
+ if let mir::Lvalue::Local(index) = *lvalue {
match self.locals[index] {
LocalRef::Lvalue(lvalue) => {
return lvalue;
}
let result = match *lvalue {
- mir::Lvalue::Var(_) |
- mir::Lvalue::Temp(_) |
- mir::Lvalue::Arg(_) |
- mir::Lvalue::ReturnPointer => bug!(), // handled above
+ mir::Lvalue::Local(_) => bug!(), // handled above
mir::Lvalue::Static(def_id) => {
let const_ty = self.monomorphized_lvalue_ty(lvalue);
LvalueRef::new_sized(consts::get_static(ccx, def_id),
lvalue: &mir::Lvalue<'tcx>, f: F) -> U
where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U
{
- if let Some(index) = self.mir.local_index(lvalue) {
+ if let mir::Lvalue::Local(index) = *lvalue {
match self.locals[index] {
LocalRef::Lvalue(lvalue) => f(self, lvalue),
LocalRef::Operand(None) => {
// Allocate variable and temp allocas
mircx.locals = {
let args = arg_local_refs(&bcx, &mir, &mircx.scopes, &lvalue_locals);
- let vars = mir.var_decls.iter().enumerate().map(|(i, decl)| {
+
+ let mut allocate_local = |local| {
+ let decl = &mir.local_decls[local];
let ty = bcx.monomorphize(&decl.ty);
- let debug_scope = mircx.scopes[decl.source_info.scope];
- let dbg = debug_scope.is_valid() && bcx.sess().opts.debuginfo == FullDebugInfo;
- let local = mir.local_index(&mir::Lvalue::Var(mir::Var::new(i))).unwrap();
- if !lvalue_locals.contains(local.index()) && !dbg {
- return LocalRef::new_operand(bcx.ccx(), ty);
- }
+ if let Some(name) = decl.name {
+ // User variable
+ let source_info = decl.source_info.unwrap();
+ let debug_scope = mircx.scopes[source_info.scope];
+ let dbg = debug_scope.is_valid() && bcx.sess().opts.debuginfo == FullDebugInfo;
- let lvalue = LvalueRef::alloca(&bcx, ty, &decl.name.as_str());
- if dbg {
- let dbg_loc = mircx.debug_loc(decl.source_info);
- if let DebugLoc::ScopeAt(scope, span) = dbg_loc {
- bcx.with_block(|bcx| {
- declare_local(bcx, decl.name, ty, scope,
- VariableAccess::DirectVariable { alloca: lvalue.llval },
- VariableKind::LocalVariable, span);
- });
- } else {
- panic!("Unexpected");
+ if !lvalue_locals.contains(local.index()) && !dbg {
+ debug!("alloc: {:?} ({}) -> operand", local, name);
+ return LocalRef::new_operand(bcx.ccx(), ty);
}
- }
- LocalRef::Lvalue(lvalue)
- });
-
- let locals = mir.temp_decls.iter().enumerate().map(|(i, decl)| {
- (mir::Lvalue::Temp(mir::Temp::new(i)), decl.ty)
- }).chain(iter::once((mir::Lvalue::ReturnPointer, mir.return_ty)));
-
- args.into_iter().chain(vars).chain(locals.map(|(lvalue, ty)| {
- let ty = bcx.monomorphize(&ty);
- let local = mir.local_index(&lvalue).unwrap();
- if lvalue == mir::Lvalue::ReturnPointer && fcx.fn_ty.ret.is_indirect() {
- let llretptr = llvm::get_param(fcx.llfn, 0);
- LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty)))
- } else if lvalue_locals.contains(local.index()) {
- LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", lvalue)))
+
+ debug!("alloc: {:?} ({}) -> lvalue", local, name);
+ let lvalue = LvalueRef::alloca(&bcx, ty, &name.as_str());
+ if dbg {
+ let dbg_loc = mircx.debug_loc(source_info);
+ if let DebugLoc::ScopeAt(scope, span) = dbg_loc {
+ bcx.with_block(|bcx| {
+ declare_local(bcx, name, ty, scope,
+ VariableAccess::DirectVariable { alloca: lvalue.llval },
+ VariableKind::LocalVariable, span);
+ });
+ } else {
+ panic!("Unexpected");
+ }
+ }
+ LocalRef::Lvalue(lvalue)
} else {
- // If this is an immediate local, we do not create an
- // alloca in advance. Instead we wait until we see the
- // definition and update the operand there.
- LocalRef::new_operand(bcx.ccx(), ty)
+ // Temporary or return pointer
+ if local == mir::RETURN_POINTER && fcx.fn_ty.ret.is_indirect() {
+ debug!("alloc: {:?} (return pointer) -> lvalue", local);
+ let llretptr = llvm::get_param(fcx.llfn, 0);
+ LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty)))
+ } else if lvalue_locals.contains(local.index()) {
+ debug!("alloc: {:?} -> lvalue", local);
+ LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", local)))
+ } else {
+ // If this is an immediate local, we do not create an
+ // alloca in advance. Instead we wait until we see the
+ // definition and update the operand there.
+ debug!("alloc: {:?} -> operand", local);
+ LocalRef::new_operand(bcx.ccx(), ty)
+ }
}
- })).collect()
+ };
+
+ let retptr = allocate_local(mir::RETURN_POINTER);
+ iter::once(retptr)
+ .chain(args.into_iter())
+ .chain(mir.var_and_temp_iter().map(&mut allocate_local))
+ .collect()
};
// Branch to the START block
None
};
- mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| {
+ mir.arg_iter().enumerate().map(|(arg_index, local)| {
+ let arg_decl = &mir.local_decls[local];
let arg_ty = bcx.monomorphize(&arg_decl.ty);
- let local = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(arg_index))).unwrap();
- if mir.spread_last_arg && arg_index == mir.arg_decls.len() - 1 {
+ if mir.spread_last_arg && arg_index == mir.arg_count - 1 {
// This argument (e.g. the last argument in the "rust-call" ABI)
// is a tuple that was spread at the ABI level and now we have
// to reconstruct it into a tuple local variable, from multiple
bcx.with_block(|bcx| arg_scope.map(|scope| {
// Is this a regular argument?
if arg_index > 0 || mir.upvar_decls.is_empty() {
- declare_local(bcx, arg_decl.debug_name, arg_ty, scope,
- VariableAccess::DirectVariable { alloca: llval },
+ declare_local(bcx, arg_decl.name.unwrap_or(keywords::Invalid.name()), arg_ty,
+ scope, VariableAccess::DirectVariable { alloca: llval },
VariableKind::ArgumentVariable(arg_index + 1),
bcx.fcx().span.unwrap_or(DUMMY_SP));
return;
// watch out for locals that do not have an
// alloca; they are handled somewhat differently
- if let Some(index) = self.mir.local_index(lvalue) {
+ if let mir::Lvalue::Local(index) = *lvalue {
match self.locals[index] {
LocalRef::Operand(Some(o)) => {
return o;
// Moves out of pair fields are trivial.
if let &mir::Lvalue::Projection(ref proj) = lvalue {
- if let Some(index) = self.mir.local_index(&proj.base) {
+ if let mir::Lvalue::Local(index) = proj.base {
if let LocalRef::Operand(Some(o)) = self.locals[index] {
match (o.val, &proj.elem) {
(OperandValue::Pair(a, b),
debug_loc.apply(bcx.fcx());
match statement.kind {
mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
- if let Some(index) = self.mir.local_index(lvalue) {
+ if let mir::Lvalue::Local(index) = *lvalue {
match self.locals[index] {
LocalRef::Lvalue(tr_dest) => {
self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
lvalue: &mir::Lvalue<'tcx>,
intrinsic: base::Lifetime)
-> BlockAndBuilder<'bcx, 'tcx> {
- if let Some(index) = self.mir.local_index(lvalue) {
+ if let mir::Lvalue::Local(index) = *lvalue {
if let LocalRef::Lvalue(tr_lval) = self.locals[index] {
intrinsic.call(&bcx, tr_lval.llval);
}
// END RUST SOURCE
// START rustc.node13.Deaggregator.before.mir
// bb0: {
-// var0 = arg0; // scope 0 at main.rs:8:8: 8:9
-// tmp0 = var0; // scope 1 at main.rs:9:14: 9:15
-// return = Baz { x: tmp0, y: const F32(0), z: const false }; // scope ...
+// local2 = local1; // scope 0 at main.rs:8:8: 8:9
+// local3 = local2; // scope 1 at main.rs:9:14: 9:15
+// local0 = Baz { x: local3, y: const F32(0), z: const false }; // scope ...
// goto -> bb1; // scope 1 at main.rs:8:1: 10:2
// }
// END rustc.node13.Deaggregator.before.mir
// START rustc.node13.Deaggregator.after.mir
// bb0: {
-// var0 = arg0; // scope 0 at main.rs:8:8: 8:9
-// tmp0 = var0; // scope 1 at main.rs:9:14: 9:15
-// (return.0: usize) = tmp0; // scope 1 at main.rs:9:5: 9:34
-// (return.1: f32) = const F32(0); // scope 1 at main.rs:9:5: 9:34
-// (return.2: bool) = const false; // scope 1 at main.rs:9:5: 9:34
+// local2 = local1; // scope 0 at main.rs:8:8: 8:9
+// local3 = local2; // scope 1 at main.rs:9:14: 9:15
+// (local0.0: usize) = local3; // scope 1 at main.rs:9:5: 9:34
+// (local0.1: f32) = const F32(0); // scope 1 at main.rs:9:5: 9:34
+// (local0.2: bool) = const false; // scope 1 at main.rs:9:5: 9:34
// goto -> bb1; // scope 1 at main.rs:8:1: 10:2
// }
-// END rustc.node13.Deaggregator.after.mir
\ No newline at end of file
+// END rustc.node13.Deaggregator.after.mir
// END RUST SOURCE
// START rustc.node10.Deaggregator.before.mir
// bb0: {
-// var0 = arg0; // scope 0 at main.rs:7:8: 7:9
-// tmp0 = var0; // scope 1 at main.rs:8:19: 8:20
-// return = Baz::Foo { x: tmp0 }; // scope 1 at main.rs:8:5: 8:21
+// local2 = local1; // scope 0 at main.rs:7:8: 7:9
+// local3 = local2; // scope 1 at main.rs:8:19: 8:20
+// local0 = Baz::Foo { x: local3 }; // scope 1 at main.rs:8:5: 8:21
// goto -> bb1; // scope 1 at main.rs:7:1: 9:2
// }
// END rustc.node10.Deaggregator.before.mir
// START rustc.node10.Deaggregator.after.mir
// bb0: {
-// var0 = arg0; // scope 0 at main.rs:7:8: 7:9
-// tmp0 = var0; // scope 1 at main.rs:8:19: 8:20
-// ((return as Foo).0: usize) = tmp0; // scope 1 at main.rs:8:5: 8:21
-// discriminant(return) = 1; // scope 1 at main.rs:8:5: 8:21
+// local2 = local1; // scope 0 at main.rs:7:8: 7:9
+// local3 = local2; // scope 1 at main.rs:8:19: 8:20
+// ((local0 as Foo).0: usize) = local3; // scope 1 at main.rs:8:5: 8:21
+// discriminant(local0) = 1; // scope 1 at main.rs:8:5: 8:21
// goto -> bb1; // scope 1 at main.rs:7:1: 9:2
// }
-// END rustc.node10.Deaggregator.after.mir
\ No newline at end of file
+// END rustc.node10.Deaggregator.after.mir
let c = 1;
}
+// TODO The StorageDead for local1 (a) after local6's (c) is missing!
+
// END RUST SOURCE
// START rustc.node4.TypeckMir.before.mir
// bb0: {
-// StorageLive(var0); // scope 0 at storage_ranges.rs:14:9: 14:10
-// var0 = const 0i32; // scope 0 at storage_ranges.rs:14:13: 14:14
-// StorageLive(var1); // scope 1 at storage_ranges.rs:16:13: 16:14
-// StorageLive(tmp1); // scope 1 at storage_ranges.rs:16:18: 16:25
-// StorageLive(tmp2); // scope 1 at storage_ranges.rs:16:23: 16:24
-// tmp2 = var0; // scope 1 at storage_ranges.rs:16:23: 16:24
-// tmp1 = std::option::Option<i32>::Some(tmp2,); // scope 1 at storage_ranges.rs:16:18: 16:25
-// var1 = &tmp1; // scope 1 at storage_ranges.rs:16:17: 16:25
-// StorageDead(tmp2); // scope 1 at storage_ranges.rs:16:23: 16:24
-// tmp0 = (); // scope 2 at storage_ranges.rs:15:5: 17:6
-// StorageDead(tmp1); // scope 1 at storage_ranges.rs:16:18: 16:25
-// StorageDead(var1); // scope 1 at storage_ranges.rs:16:13: 16:14
-// StorageLive(var2); // scope 1 at storage_ranges.rs:18:9: 18:10
-// var2 = const 1i32; // scope 1 at storage_ranges.rs:18:13: 18:14
-// return = (); // scope 3 at storage_ranges.rs:13:11: 19:2
-// StorageDead(var2); // scope 1 at storage_ranges.rs:18:9: 18:10
-// StorageDead(var0); // scope 0 at storage_ranges.rs:14:9: 14:10
-// goto -> bb1; // scope 0 at storage_ranges.rs:13:1: 19:2
+// StorageLive(local1); // scope 0 at storage_ranges.rs:12:9: 12:10
+// local1 = const 0i32; // scope 0 at storage_ranges.rs:12:13: 12:14
+// StorageLive(local3); // scope 1 at storage_ranges.rs:14:13: 14:14
+// StorageLive(local4); // scope 1 at storage_ranges.rs:14:18: 14:25
+// StorageLive(local5); // scope 1 at storage_ranges.rs:14:23: 14:24
+// local5 = local1; // scope 1 at storage_ranges.rs:14:23: 14:24
+// local4 = std::option::Option<i32>::Some(local5,); // scope 1 at storage_ranges.rs:14:18: 14:25
+// local3 = &local4; // scope 1 at storage_ranges.rs:14:17: 14:25
+// StorageDead(local5); // scope 1 at storage_ranges.rs:14:23: 14:24
+// local2 = (); // scope 2 at storage_ranges.rs:13:5: 15:6
+// StorageDead(local4); // scope 1 at storage_ranges.rs:14:18: 14:25
+// StorageDead(local3); // scope 1 at storage_ranges.rs:14:13: 14:14
+// StorageLive(local6); // scope 1 at storage_ranges.rs:16:9: 16:10
+// local6 = const 1i32; // scope 1 at storage_ranges.rs:16:13: 16:14
+// local0 = (); // scope 3 at storage_ranges.rs:11:11: 17:2
+// StorageDead(local6); // scope 1 at storage_ranges.rs:16:9: 16:10
+// goto -> bb1; // scope 0 at storage_ranges.rs:11:1: 17:2
// }
//
// bb1: {