use rustc::mir::Mir;
use rustc::infer::InferCtxt;
-use util::liveness::LivenessResult;
+use super::LivenessResults;
use super::ToRegionIndex;
use super::region_infer::RegionInferenceContext;
-pub fn generate_constraints<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
- regioncx: &mut RegionInferenceContext,
- mir: &Mir<'tcx>,
- liveness: &LivenessResult)
-{
- ConstraintGeneration { infcx, regioncx, mir, liveness }.add_constraints();
+pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
+ infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+ regioncx: &mut RegionInferenceContext,
+ mir: &Mir<'tcx>,
+ liveness: &LivenessResults,
+) {
+ ConstraintGeneration {
+ infcx,
+ regioncx,
+ mir,
+ liveness,
+ }.add_constraints();
}
struct ConstraintGeneration<'constrain, 'gcx: 'tcx, 'tcx: 'constrain> {
infcx: &'constrain InferCtxt<'constrain, 'gcx, 'tcx>,
regioncx: &'constrain mut RegionInferenceContext,
mir: &'constrain Mir<'tcx>,
- liveness: &'constrain LivenessResult,
+ liveness: &'constrain LivenessResults,
}
impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> {
for bb in self.mir.basic_blocks().indices() {
debug!("add_liveness_constraints: bb={:?}", bb);
- self.liveness.simulate_block(self.mir, bb, |location, live_locals| {
- debug!("add_liveness_constraints: location={:?} live_locals={:?}",
- location, live_locals);
+ self.liveness
+ .regular
+ .simulate_block(self.mir, bb, |location, live_locals| {
+ debug!(
+ "add_liveness_constraints: location={:?} live_locals={:?}",
+ location,
+ live_locals
+ );
- for live_local in live_locals.iter() {
- let live_local_ty = self.mir.local_decls[live_local].ty;
- tcx.for_each_free_region(&live_local_ty, |live_region| {
- let vid = live_region.to_region_index();
- self.regioncx.add_live_point(vid, location);
- })
- }
- });
+ for live_local in live_locals.iter() {
+ let live_local_ty = self.mir.local_decls[live_local].ty;
+ tcx.for_each_free_region(&live_local_ty, |live_region| {
+ let vid = live_region.to_region_index();
+ self.regioncx.add_live_point(vid, location);
+ })
+ }
+ });
}
}
}
use rustc_data_structures::indexed_vec::Idx;
use std::collections::BTreeSet;
use std::fmt;
-use util::liveness::{self, LivenessResult};
+use util::liveness::{self, LivenessResult, LivenessMode};
use util as mir_util;
use self::mir_util::PassWhere;
let num_region_variables = renumber::renumber_mir(infcx, mir);
// Compute what is live where.
- let liveness = &liveness::liveness_of_locals(mir);
+ let liveness = &LivenessResults {
+ regular: liveness::liveness_of_locals(mir, LivenessMode {
+ include_regular_use: true,
+ include_drops: false,
+ }),
+
+ drop: liveness::liveness_of_locals(mir, LivenessMode {
+ include_regular_use: false,
+ include_drops: true,
+ })
+ };
// Create the region inference context, generate the constraints,
// and then solve them.
}
}
+struct LivenessResults {
+ regular: LivenessResult,
+ drop: LivenessResult,
+}
+
fn dump_mir_results<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
- liveness: &LivenessResult,
+ liveness: &LivenessResults,
source: MirSource,
regioncx: &RegionInferenceContext,
mir: &Mir<'tcx>,
return;
}
- let liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
+ let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
+ .indices()
+ .flat_map(|bb| {
+ let mut results = vec![];
+ liveness.regular.simulate_block(&mir, bb, |location, local_set| {
+ results.push((location, local_set.clone()));
+ });
+ results
+ })
+ .collect();
+
+ let drop_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
.indices()
.flat_map(|bb| {
let mut results = vec![];
- liveness.simulate_block(&mir, bb, |location, local_set| {
+ liveness.drop.simulate_block(&mir, bb, |location, local_set| {
results.push((location, local_set.clone()));
});
results
// Before each basic block, dump out the values
// that are live on entry to the basic block.
PassWhere::BeforeBlock(bb) => {
- let local_set = &liveness.ins[bb];
- writeln!(out, " | Variables live on entry to the block {:?}:", bb)?;
- for local in local_set.iter() {
- writeln!(out, " | - {:?}", local)?;
- }
+ writeln!(out, " | Variables regular-live on entry to the block {:?}: {:?}",
+ bb, liveness.regular.ins[bb])?;
+ writeln!(out, " | Variables drop-live on entry to the block {:?}: {:?}",
+ bb, liveness.drop.ins[bb])?;
}
PassWhere::InCFG(location) => {
- let local_set = &liveness_per_location[&location];
- writeln!(out, " | Live variables here: {:?}", local_set)?;
+ let local_set = ®ular_liveness_per_location[&location];
+ writeln!(out, " | Regular-Live variables here: {:?}", local_set)?;
+ let local_set = &drop_liveness_per_location[&location];
+ writeln!(out, " | Drop-Live variables here: {:?}", local_set)?;
}
PassWhere::AfterCFG => {}
use rustc::mir::*;
use rustc::mir::visit::{LvalueContext, Visitor};
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::indexed_set::IdxSetBuf;
-use util::pretty::{write_basic_block, dump_enabled, write_mir_intro};
+use util::pretty::{dump_enabled, write_basic_block, write_mir_intro};
use rustc::mir::transform::MirSource;
use rustc::ty::item_path;
-use std::path::{PathBuf, Path};
+use std::path::{Path, PathBuf};
use std::fs;
use rustc::ty::TyCtxt;
use std::io::{self, Write};
pub type LocalSet = IdxSetBuf<Local>;
-// This gives the result of the liveness analysis at the boundary of basic blocks
+/// This gives the result of the liveness analysis at the boundary of
+/// basic blocks. You can use `simulate_block` to obtain the
+/// intra-block results.
pub struct LivenessResult {
+ /// Liveness mode in use when these results were computed.
+ pub mode: LivenessMode,
+
+ /// Live variables on entry to each basic block.
pub ins: IndexVec<BasicBlock, LocalSet>,
+
+ /// Live variables on exit to each basic block. This is equal to
+ /// the union of the `ins` for each successor.
pub outs: IndexVec<BasicBlock, LocalSet>,
}
-pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
+#[derive(Copy, Clone, Debug)]
+pub struct LivenessMode {
+ /// If true, then we will consider "regular uses" of a variable to be live.
+ /// For example, if the user writes `foo(x)`, then this is a regular use of
+ /// the variable `x`.
+ pub include_regular_use: bool,
+
+ /// If true, then we will consider (implicit) drops of a variable
+ /// to be live. For example, if the user writes `{ let x =
+ /// vec![...]; .. }`, then the drop at the end of the block is an
+ /// implicit drop.
+ ///
+ /// NB. Despite its name, a call like `::std::mem::drop(x)` is
+ /// **not** considered a drop for this purposes, but rather a
+ /// regular use.
+ pub include_drops: bool,
+}
+
+/// Compute which local variables are live within the given function
+/// `mir`. The liveness mode `mode` determines what sorts of uses are
+/// considered to make a variable live (e.g., do drops count?).
+pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> LivenessResult {
let locals = mir.local_decls.len();
- let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| {
- block(b, locals)
- }).collect();
+ let def_use: IndexVec<_, _> = mir.basic_blocks()
+ .iter()
+ .map(|b| block(mode, b, locals))
+ .collect();
let mut ins: IndexVec<_, _> = mir.basic_blocks()
.indices()
}
}
- LivenessResult {
- ins,
- outs,
- }
+ LivenessResult { mode, ins, outs }
}
impl LivenessResult {
/// basic block `block`. At each point within `block`, invokes
/// the callback `op` with the current location and the set of
/// variables that are live on entry to that location.
- pub fn simulate_block<'tcx, OP>(&self,
- mir: &Mir<'tcx>,
- block: BasicBlock,
- mut callback: OP)
- where OP: FnMut(Location, &LocalSet)
+ pub fn simulate_block<'tcx, OP>(&self, mir: &Mir<'tcx>, block: BasicBlock, mut callback: OP)
+ where
+ OP: FnMut(Location, &LocalSet),
{
let data = &mir[block];
let mut statement_index = data.statements.len();
// Compute liveness right before terminator and invoke callback.
- let terminator_location = Location { block, statement_index };
+ let terminator_location = Location {
+ block,
+ statement_index,
+ };
let terminator_defs_uses = self.defs_uses(mir, terminator_location, &data.terminator);
terminator_defs_uses.apply(&mut bits);
callback(terminator_location, &bits);
// Compute liveness before each statement (in rev order) and invoke callback.
for statement in data.statements.iter().rev() {
statement_index -= 1;
- let statement_location = Location { block, statement_index };
+ let statement_location = Location {
+ block,
+ statement_index,
+ };
let statement_defs_uses = self.defs_uses(mir, statement_location, statement);
statement_defs_uses.apply(&mut bits);
callback(statement_location, &bits);
assert_eq!(bits, self.ins[block]);
}
- fn defs_uses<'tcx, V>(&self,
- mir: &Mir<'tcx>,
- location: Location,
- thing: &V)
- -> DefsUses
- where V: MirVisitable<'tcx>,
+ fn defs_uses<'tcx, V>(&self, mir: &Mir<'tcx>, location: Location, thing: &V) -> DefsUses
+ where
+ V: MirVisitable<'tcx>,
{
let locals = mir.local_decls.len();
- let mut visitor = DefsUses {
- defs: LocalSet::new_empty(locals),
- uses: LocalSet::new_empty(locals),
+ let mut visitor = DefsUsesVisitor {
+ mode: self.mode,
+ defs_uses: DefsUses {
+ defs: LocalSet::new_empty(locals),
+ uses: LocalSet::new_empty(locals),
+ },
};
// Visit the various parts of the basic block in reverse. If we go
// forward, the logic in `add_def` and `add_use` would be wrong.
thing.apply(location, &mut visitor);
- visitor
+ visitor.defs_uses
}
}
+struct DefsUsesVisitor {
+ mode: LivenessMode,
+ defs_uses: DefsUses,
+}
+
#[derive(Eq, PartialEq, Clone)]
struct DefsUses {
defs: LocalSet,
}
}
-impl<'tcx> Visitor<'tcx> for DefsUses {
- fn visit_local(&mut self,
- &local: &Local,
- context: LvalueContext<'tcx>,
- _: Location) {
+impl<'tcx> Visitor<'tcx> for DefsUsesVisitor {
+ fn visit_local(&mut self, &local: &Local, context: LvalueContext<'tcx>, _: Location) {
match context {
///////////////////////////////////////////////////////////////////////////
// DEFS
LvalueContext::Store |
- // We let Call defined the result in both the success and
+ // We let Call define the result in both the success and
// unwind cases. This is not really correct, however it
// does not seem to be observable due to the way that we
// generate MIR. See the test case
// values that come before them.
LvalueContext::StorageLive |
LvalueContext::StorageDead => {
- self.add_def(local);
+ self.defs_uses.add_def(local);
}
///////////////////////////////////////////////////////////////////////////
- // USES
+ // REGULAR USES
+ //
+ // These are uses that occur *outside* of a drop. For the
+ // purposes of NLL, these are special in that **all** the
+ // lifetimes appearing in the variable must be live for each regular use.
LvalueContext::Projection(..) |
LvalueContext::Inspect |
LvalueContext::Consume |
- LvalueContext::Validate |
+ LvalueContext::Validate => {
+ if self.mode.include_regular_use {
+ self.defs_uses.add_use(local);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // DROP USES
+ //
+ // These are uses that occur in a DROP (a MIR drop, not a
+ // call to `std::mem::drop()`). For the purposes of NLL,
+ // uses in drop are special because `#[may_dangle]`
+ // attributes can affect whether lifetimes must be live.
- // We consider drops to always be uses of locals.
- // Drop eloboration should be run before this analysis otherwise
- // the results might be too pessimistic.
LvalueContext::Drop => {
- self.add_use(local);
+ if self.mode.include_drops {
+ self.defs_uses.add_use(local);
+ }
}
}
}
}
-fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses {
- let mut visitor = DefsUses {
- defs: LocalSet::new_empty(locals),
- uses: LocalSet::new_empty(locals),
+fn block<'tcx>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses {
+ let mut visitor = DefsUsesVisitor {
+ mode,
+ defs_uses: DefsUses {
+ defs: LocalSet::new_empty(locals),
+ uses: LocalSet::new_empty(locals),
+ },
};
- let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 };
+ let dummy_location = Location {
+ block: BasicBlock::new(0),
+ statement_index: 0,
+ };
// Visit the various parts of the basic block in reverse. If we go
// forward, the logic in `add_def` and `add_use` would be wrong.
visitor.visit_statement(BasicBlock::new(0), statement, dummy_location);
}
- visitor
+ visitor.defs_uses
}
trait MirVisitable<'tcx> {
fn apply<V>(&self, location: Location, visitor: &mut V)
- where V: Visitor<'tcx>;
+ where
+ V: Visitor<'tcx>;
}
impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> {
fn apply<V>(&self, location: Location, visitor: &mut V)
- where V: Visitor<'tcx>
+ where
+ V: Visitor<'tcx>,
{
- visitor.visit_statement(location.block,
- self,
- location)
+ visitor.visit_statement(location.block, self, location)
}
}
impl<'tcx> MirVisitable<'tcx> for Option<Terminator<'tcx>> {
fn apply<V>(&self, location: Location, visitor: &mut V)
- where V: Visitor<'tcx>
+ where
+ V: Visitor<'tcx>,
{
- visitor.visit_terminator(location.block,
- self.as_ref().unwrap(),
- location)
+ visitor.visit_terminator(location.block, self.as_ref().unwrap(), location)
}
}
-pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- pass_name: &str,
- source: MirSource,
- mir: &Mir<'tcx>,
- result: &LivenessResult) {
+pub fn dump_mir<'a, 'tcx>(
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ pass_name: &str,
+ source: MirSource,
+ mir: &Mir<'tcx>,
+ result: &LivenessResult,
+) {
if !dump_enabled(tcx, pass_name, source) {
return;
}
- let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
+ let node_path = item_path::with_forced_impl_filename_line(|| {
+ // see notes on #41697 below
tcx.item_path_str(tcx.hir.local_def_id(source.item_id()))
});
- dump_matched_mir_node(tcx, pass_name, &node_path,
- source, mir, result);
+ dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, result);
}
-fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- pass_name: &str,
- node_path: &str,
- source: MirSource,
- mir: &Mir<'tcx>,
- result: &LivenessResult) {
+fn dump_matched_mir_node<'a, 'tcx>(
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ pass_name: &str,
+ node_path: &str,
+ source: MirSource,
+ mir: &Mir<'tcx>,
+ result: &LivenessResult,
+) {
let mut file_path = PathBuf::new();
if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
let p = Path::new(file_dir);
file_path.push(p);
};
- let file_name = format!("rustc.node{}{}-liveness.mir",
- source.item_id(), pass_name);
+ let file_name = format!("rustc.node{}{}-liveness.mir", source.item_id(), pass_name);
file_path.push(&file_name);
let _ = fs::File::create(&file_path).and_then(|mut file| {
writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
});
}
-pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- src: MirSource,
- mir: &Mir<'tcx>,
- w: &mut Write,
- result: &LivenessResult)
- -> io::Result<()> {
+pub fn write_mir_fn<'a, 'tcx>(
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ src: MirSource,
+ mir: &Mir<'tcx>,
+ w: &mut Write,
+ result: &LivenessResult,
+) -> io::Result<()> {
write_mir_intro(tcx, src, mir, w)?;
for block in mir.basic_blocks().indices() {
let print = |w: &mut Write, prefix, result: &IndexVec<BasicBlock, LocalSet>| {
- let live: Vec<String> = mir.local_decls.indices()
+ let live: Vec<String> = mir.local_decls
+ .indices()
.filter(|i| result[block].contains(i))
.map(|i| format!("{:?}", i))
.collect();
writeln!(w, "}}")?;
Ok(())
}
-