mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-20 10:55:14 +00:00
extend liveness to distinguish "drop" and "non-drop" uses
This commit is contained in:
parent
cb56ff5a77
commit
e02937848b
@ -68,7 +68,7 @@ use rustc::mir::visit::{LvalueContext, Visitor, MutVisitor};
|
||||
use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior};
|
||||
use rustc::ty::subst::{Kind, Substs};
|
||||
use util::dump_mir;
|
||||
use util::liveness;
|
||||
use util::liveness::{self, LivenessMode};
|
||||
use rustc_const_math::ConstInt;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc_data_structures::indexed_set::IdxSetBuf;
|
||||
@ -348,7 +348,10 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
ignored.visit_mir(mir);
|
||||
|
||||
let mut set = liveness::LocalSet::new_empty(mir.local_decls.len());
|
||||
let liveness = liveness::liveness_of_locals(mir);
|
||||
let liveness = liveness::liveness_of_locals(mir, LivenessMode {
|
||||
include_regular_use: true,
|
||||
include_drops: true,
|
||||
});
|
||||
liveness::dump_mir(tcx, "generator_liveness", source, mir, &liveness);
|
||||
|
||||
let mut storage_liveness_map = HashMap::new();
|
||||
|
@ -10,24 +10,30 @@
|
||||
|
||||
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> {
|
||||
@ -47,18 +53,23 @@ 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);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use rustc::util::nodemap::FxHashMap;
|
||||
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;
|
||||
@ -50,7 +50,17 @@ impl MirPass for NLL {
|
||||
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.
|
||||
@ -64,9 +74,14 @@ impl MirPass for NLL {
|
||||
}
|
||||
}
|
||||
|
||||
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>,
|
||||
@ -75,11 +90,22 @@ fn dump_mir_results<'a, 'gcx, '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.simulate_block(&mir, bb, |location, local_set| {
|
||||
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.drop.simulate_block(&mir, bb, |location, local_set| {
|
||||
results.push((location, local_set.clone()));
|
||||
});
|
||||
results
|
||||
@ -96,16 +122,17 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
|
||||
// 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 => {}
|
||||
|
@ -35,29 +35,60 @@
|
||||
|
||||
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()
|
||||
@ -89,10 +120,7 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
|
||||
}
|
||||
}
|
||||
|
||||
LivenessResult {
|
||||
ins,
|
||||
outs,
|
||||
}
|
||||
LivenessResult { mode, ins, outs }
|
||||
}
|
||||
|
||||
impl LivenessResult {
|
||||
@ -100,11 +128,9 @@ 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];
|
||||
|
||||
@ -116,7 +142,10 @@ impl LivenessResult {
|
||||
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);
|
||||
@ -124,7 +153,10 @@ impl LivenessResult {
|
||||
// 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);
|
||||
@ -133,27 +165,32 @@ impl LivenessResult {
|
||||
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,
|
||||
@ -195,18 +232,15 @@ impl DefsUses {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@ -220,11 +254,15 @@ impl<'tcx> Visitor<'tcx> for DefsUses {
|
||||
// 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(..) |
|
||||
|
||||
@ -236,25 +274,42 @@ impl<'tcx> Visitor<'tcx> for DefsUses {
|
||||
|
||||
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.
|
||||
@ -263,62 +318,64 @@ fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses {
|
||||
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)?;
|
||||
@ -330,16 +387,18 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
@ -356,4 +415,3 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
writeln!(w, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -26,21 +26,26 @@ fn main() {
|
||||
//
|
||||
// END RUST SOURCE
|
||||
// START rustc.node12.nll.0.mir
|
||||
// | Variables live on entry to the block bb0:
|
||||
// | Variables regular-live on entry to the block bb0: []
|
||||
// | Variables drop-live on entry to the block bb0: []
|
||||
// bb0: {
|
||||
// | Live variables here: []
|
||||
// | Regular-Live variables here: []
|
||||
// | Drop-Live variables here: []
|
||||
// StorageLive(_1);
|
||||
// | Live variables here: []
|
||||
// | Regular-Live variables here: []
|
||||
// | Drop-Live variables here: []
|
||||
// _1 = const <std::boxed::Box<T>>::new(const 22usize) -> bb1;
|
||||
// }
|
||||
// END rustc.node12.nll.0.mir
|
||||
// START rustc.node12.nll.0.mir
|
||||
// | Variables live on entry to the block bb1:
|
||||
// | - _1
|
||||
// | Variables regular-live on entry to the block bb1: []
|
||||
// | Variables drop-live on entry to the block bb1: [_1]
|
||||
// bb1: {
|
||||
// | Live variables here: [_1]
|
||||
// | Regular-Live variables here: []
|
||||
// | Drop-Live variables here: [_1]
|
||||
// StorageLive(_2);
|
||||
// | Live variables here: [_1]
|
||||
// | Regular-Live variables here: []
|
||||
// | Drop-Live variables here: [_1]
|
||||
// _2 = const can_panic() -> [return: bb2, unwind: bb4];
|
||||
// }
|
||||
// END rustc.node12.nll.0.mir
|
||||
|
@ -25,17 +25,23 @@ fn main() {
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.node12.nll.0.mir
|
||||
// | Variables live on entry to the block bb1:
|
||||
// | Variables regular-live on entry to the block bb1: []
|
||||
// | Variables drop-live on entry to the block bb1: []
|
||||
// bb1: {
|
||||
// | Live variables here: []
|
||||
// | Regular-Live variables here: []
|
||||
// | Drop-Live variables here: []
|
||||
// _1 = const 55usize;
|
||||
// | Live variables here: [_1]
|
||||
// | Regular-Live variables here: [_1]
|
||||
// | Drop-Live variables here: []
|
||||
// StorageLive(_3);
|
||||
// | Live variables here: [_1]
|
||||
// | Regular-Live variables here: [_1]
|
||||
// | Drop-Live variables here: []
|
||||
// StorageLive(_4);
|
||||
// | Live variables here: [_1]
|
||||
// | Regular-Live variables here: [_1]
|
||||
// | Drop-Live variables here: []
|
||||
// _4 = _1;
|
||||
// | Live variables here: [_4]
|
||||
// | Regular-Live variables here: [_4]
|
||||
// | Drop-Live variables here: []
|
||||
// _3 = const use_x(_4) -> bb2;
|
||||
// }
|
||||
// END rustc.node12.nll.0.mir
|
||||
|
@ -29,21 +29,26 @@ fn main() {
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.node18.nll.0.mir
|
||||
// | Variables live on entry to the block bb2:
|
||||
// | - _1
|
||||
// | Variables regular-live on entry to the block bb2: [_1]
|
||||
// | Variables drop-live on entry to the block bb2: []
|
||||
// bb2: {
|
||||
// | Live variables here: [_1]
|
||||
// | Regular-Live variables here: [_1]
|
||||
// | Drop-Live variables here: []
|
||||
// StorageLive(_4);
|
||||
// | Live variables here: [_1]
|
||||
// | Regular-Live variables here: [_1]
|
||||
// | Drop-Live variables here: []
|
||||
// _4 = _1;
|
||||
// | Live variables here: [_4]
|
||||
// | Regular-Live variables here: [_4]
|
||||
// | Drop-Live variables here: []
|
||||
// _3 = const make_live(_4) -> bb4;
|
||||
// }
|
||||
// END rustc.node18.nll.0.mir
|
||||
// START rustc.node18.nll.0.mir
|
||||
// | Variables live on entry to the block bb3:
|
||||
// | Variables regular-live on entry to the block bb3: []
|
||||
// | Variables drop-live on entry to the block bb3: []
|
||||
// bb3: {
|
||||
// | Live variables here: []
|
||||
// | Regular-Live variables here: []
|
||||
// | Drop-Live variables here: []
|
||||
// _5 = const make_dead() -> bb5;
|
||||
// }
|
||||
// END rustc.node18.nll.0.mir
|
||||
|
@ -37,19 +37,24 @@ fn main() {
|
||||
// END rustc.node12.nll.0.mir
|
||||
// START rustc.node12.nll.0.mir
|
||||
// bb1: {
|
||||
// | Live variables here: [_1, _3]
|
||||
// | Regular-Live variables here: [_1, _3]
|
||||
// | Drop-Live variables here: []
|
||||
// _2 = &'_#0r _1[_3];
|
||||
// | Live variables here: [_2]
|
||||
// | Regular-Live variables here: [_2]
|
||||
// | Drop-Live variables here: []
|
||||
// switchInt(const true) -> [0u8: bb3, otherwise: bb2];
|
||||
// }
|
||||
// END rustc.node12.nll.0.mir
|
||||
// START rustc.node12.nll.0.mir
|
||||
// bb2: {
|
||||
// | Live variables here: [_2]
|
||||
// | Regular-Live variables here: [_2]
|
||||
// | Drop-Live variables here: []
|
||||
// StorageLive(_7);
|
||||
// | Live variables here: [_2]
|
||||
// | Regular-Live variables here: [_2]
|
||||
// | Drop-Live variables here: []
|
||||
// _7 = (*_2);
|
||||
// | Live variables here: [_7]
|
||||
// | Regular-Live variables here: [_7]
|
||||
// | Drop-Live variables here: []
|
||||
// _6 = const use_x(_7) -> bb4;
|
||||
// }
|
||||
// END rustc.node12.nll.0.mir
|
||||
|
48
src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
Normal file
48
src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Basic test for liveness constraints: the region (`R1`) that appears
|
||||
// in the type of `p` includes the points after `&v[0]` up to (but not
|
||||
// including) the call to `use_x`. The `else` branch is not included.
|
||||
|
||||
// compile-flags:-Znll -Zverbose
|
||||
// ^^^^^^^^^ force compiler to dump more region information
|
||||
|
||||
#![allow(warnings)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
#![feature(generic_param_attrs)]
|
||||
|
||||
fn use_x(_: usize) -> bool { true }
|
||||
|
||||
fn main() {
|
||||
let mut v = [1, 2, 3];
|
||||
let p: Wrap<& /* R4 */ usize> = Wrap { value: &v[0] };
|
||||
if true {
|
||||
use_x(*p.value);
|
||||
} else {
|
||||
use_x(22);
|
||||
}
|
||||
|
||||
// `p` will get dropped here. However, because of the
|
||||
// `#[may_dangle]` attribute, we do not need to consider R4 live.
|
||||
}
|
||||
|
||||
struct Wrap<T> {
|
||||
value: T
|
||||
}
|
||||
|
||||
unsafe impl<#[may_dangle] T> Drop for Wrap<T> {
|
||||
fn drop(&mut self) { }
|
||||
}
|
||||
|
||||
// END RUST SOURCE
|
||||
// START rustc.node12.nll.0.mir
|
||||
// | R4: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]}
|
||||
// END rustc.node12.nll.0.mir
|
Loading…
Reference in New Issue
Block a user