From 43b69c27771929e6dfa7bf1771bc8ee5ce7a99c5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 15 Jul 2018 00:28:26 -0400 Subject: [PATCH] make liveness generic over set of local variables We used to hardcode that we wanted the liveness of *all* variables. This can now be configured by selecting an alternative index type V and providing a (partial) map from locals to that new type V. --- src/librustc_mir/borrow_check/nll/mod.rs | 11 +- .../borrow_check/nll/type_check/liveness.rs | 14 +- src/librustc_mir/transform/generator.rs | 25 +- src/librustc_mir/util/liveness.rs | 226 +++++++++++------- 4 files changed, 172 insertions(+), 104 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 72d7b473204f..15ebb7a7a29f 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -22,7 +22,6 @@ use rustc::infer::InferCtxt; use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Mir, Local}; use rustc::ty::{self, RegionKind, RegionVid}; use rustc::util::nodemap::FxHashMap; -use rustc_data_structures::indexed_vec::Idx; use std::collections::BTreeSet; use std::fmt::Debug; use std::env; @@ -31,7 +30,7 @@ use std::path::PathBuf; use std::rc::Rc; use std::str::FromStr; use transform::MirSource; -use util::liveness::{LivenessResults, LocalSet}; +use util::liveness::{IdentityMap, LivenessResults, LocalSet}; use self::mir_util::PassWhere; use polonius_engine::{Algorithm, Output}; @@ -104,7 +103,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( let elements = &Rc::new(RegionValueElements::new(mir, universal_regions.len())); // Run the MIR type-checker. - let liveness = &LivenessResults::compute(mir); + let liveness = &LivenessResults::compute(mir, &IdentityMap::new(mir)); let constraint_sets = type_check::type_check( infcx, param_env, @@ -220,6 +219,8 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( return; } + let map = &IdentityMap::new(mir); + let regular_liveness_per_location: FxHashMap<_, _> = mir .basic_blocks() .indices() @@ -227,7 +228,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( let mut results = vec![]; liveness .regular - .simulate_block(&mir, bb, |location, local_set| { + .simulate_block(&mir, bb, map, |location, local_set| { results.push((location, local_set.clone())); }); results @@ -241,7 +242,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( let mut results = vec![]; liveness .drop - .simulate_block(&mir, bb, |location, local_set| { + .simulate_block(&mir, bb, map, |location, local_set| { results.push((location, local_set.clone())); }); results diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs index 97f3b1a1c966..0eb88b7bcbfd 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -20,9 +20,8 @@ use rustc::traits::query::type_op::outlives::DropckOutlives; use rustc::traits::query::type_op::TypeOp; use rustc::ty::{Ty, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::indexed_vec::Idx; use std::rc::Rc; -use util::liveness::{LivenessResults, LiveVariableMap}; +use util::liveness::{IdentityMap, LivenessResults}; use super::TypeChecker; @@ -48,6 +47,7 @@ pub(super) fn generate<'gcx, 'tcx>( flow_inits, move_data, drop_data: FxHashMap(), + map: &IdentityMap::new(mir), }; for bb in mir.basic_blocks().indices() { @@ -61,7 +61,6 @@ where 'flow: 'gen, 'tcx: 'typeck + 'flow, 'gcx: 'tcx, - V: 'gen, { cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>, mir: &'gen Mir<'tcx>, @@ -69,6 +68,7 @@ where flow_inits: &'gen mut FlowAtLocation>, move_data: &'gen MoveData<'tcx>, drop_data: FxHashMap, DropData<'tcx>>, + map: &'gen IdentityMap<'gen, 'tcx>, } struct DropData<'tcx> { @@ -76,7 +76,7 @@ struct DropData<'tcx> { region_constraint_data: Option>>>, } -impl<'gen, 'typeck, 'flow, 'gcx, 'tcx, V:LiveVariableMap> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx, V> { +impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> { /// Liveness constraints: /// /// > If a variable V is live at point P, then all regions R in the type of V @@ -86,17 +86,17 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx, V:LiveVariableMap> TypeLivenessGenerator< self.liveness .regular - .simulate_block(self.mir, bb, |location, live_locals| { + .simulate_block(self.mir, bb, self.map, |location, live_locals| { for live_local in live_locals.iter() { let live_local_ty = self.mir.local_decls[live_local].ty; Self::push_type_live_constraint(&mut self.cx, live_local_ty, location); } }); - let mut all_live_locals: Vec<(Location, Vec)> = vec![]; + let mut all_live_locals: Vec<(Location, Vec)> = vec![]; self.liveness .drop - .simulate_block(self.mir, bb, |location, live_locals| { + .simulate_block(self.mir, bb, self.map, |location, live_locals| { all_live_locals.push((location, live_locals.iter().collect())); }); debug!( diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index f1091e40c77a..7d4d561072e7 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -66,7 +66,7 @@ use rustc::mir::visit::{PlaceContext, Visitor, MutVisitor}; use rustc::ty::{self, TyCtxt, AdtDef, Ty}; use rustc::ty::subst::Substs; use util::dump_mir; -use util::liveness::{self, LivenessMode}; +use util::liveness::{self, IdentityMap, LivenessMode}; use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_set::IdxSetBuf; use std::collections::HashMap; @@ -334,7 +334,7 @@ impl<'tcx> Visitor<'tcx> for StorageIgnored { struct BorrowedLocals(liveness::LocalSet); -fn mark_as_borrowed<'tcx, V: Idx>(place: &Place<'tcx>, locals: &mut BorrowedLocals) { +fn mark_as_borrowed<'tcx>(place: &Place<'tcx>, locals: &mut BorrowedLocals) { match *place { Place::Local(l) => { locals.0.add(&l); }, Place::Static(..) => (), @@ -397,11 +397,22 @@ fn locals_live_across_suspend_points<'a, 'tcx,>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Calculate the liveness of MIR locals ignoring borrows. let mut set = liveness::LocalSet::new_empty(mir.local_decls.len()); - let mut 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 liveness = liveness::liveness_of_locals( + mir, + LivenessMode { + include_regular_use: true, + include_drops: true, + }, + &IdentityMap::new(mir), + ); + liveness::dump_mir( + tcx, + "generator_liveness", + source, + mir, + &IdentityMap::new(mir), + &liveness, + ); let mut storage_liveness_map = HashMap::new(); diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index 4e78be76b613..ea8a180e3b61 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -33,46 +33,63 @@ //! generator yield points, all pre-existing references are invalidated, so this //! doesn't matter). -use rustc::mir::*; -use rustc::mir::visit::{PlaceContext, Visitor}; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc_data_structures::indexed_set::IdxSetBuf; -use rustc_data_structures::work_queue::WorkQueue; -use util::pretty::{dump_enabled, write_basic_block, write_mir_intro}; -use rustc::ty::item_path; -use rustc::mir::Local; use rustc::mir::visit::MirVisitable; -use std::path::{Path, PathBuf}; -use std::fs; +use rustc::mir::visit::{PlaceContext, Visitor}; +use rustc::mir::Local; +use rustc::mir::*; +use rustc::ty::item_path; use rustc::ty::TyCtxt; +use rustc_data_structures::indexed_set::IdxSetBuf; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::work_queue::WorkQueue; +use std::fs; use std::io::{self, Write}; +use std::path::{Path, PathBuf}; use transform::MirSource; +use util::pretty::{dump_enabled, write_basic_block, write_mir_intro}; -pub type LocalSet = IdxSetBuf; +pub type LocalSet = IdxSetBuf; /// 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 { +/// +/// The `V` type defines the set of variables that we computed +/// liveness for. This is often `Local`, in which case we computed +/// liveness for all variables -- but it can also be some other type, +/// which indicates a subset of the variables within the graph. +pub struct LivenessResult { /// Liveness mode in use when these results were computed. pub mode: LivenessMode, /// Live variables on exit to each basic block. This is equal to /// the union of the `ins` for each successor. - pub outs: IndexVec>, + pub outs: IndexVec>, } -pub(crate) trait LiveVariableMap { +/// Defines the mapping to/from the MIR local variables (`Local`) to +/// the "live variable indices" we are using in a particular +/// computation. +pub trait LiveVariableMap { type LiveVar; fn from_local(&self, local: Local) -> Option; fn from_live_var(&self, local: Self::LiveVar) -> Local; + fn num_variables(&self) -> usize; } -#[derive(Eq, PartialEq, Clone)] -struct IdentityMap; +#[derive(Debug)] +pub struct IdentityMap<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, +} -impl LiveVariableMap for IdentityMap { +impl<'a, 'tcx> IdentityMap<'a, 'tcx> { + pub fn new(mir: &'a Mir<'tcx>) -> Self { + Self { mir } + } +} + +impl<'a, 'tcx> LiveVariableMap for IdentityMap<'a, 'tcx> { type LiveVar = Local; fn from_local(&self, local: Local) -> Option { @@ -82,6 +99,10 @@ impl LiveVariableMap for IdentityMap { fn from_live_var(&self, local: Self::LiveVar) -> Local { local } + + fn num_variables(&self) -> usize { + self.mir.local_decls.len() + } } #[derive(Copy, Clone, Debug)] @@ -103,7 +124,7 @@ pub struct LivenessMode { } /// A combination of liveness results, used in NLL. -pub struct LivenessResults { +pub struct LivenessResults { /// Liveness results where a regular use makes a variable X live, /// but not a drop. pub regular: LivenessResult, @@ -113,8 +134,11 @@ pub struct LivenessResults { pub drop: LivenessResult, } -impl> LivenessResults { - pub fn compute<'tcx>(mir: &Mir<'tcx>, map: &M) -> LivenessResults { +impl LivenessResults { + pub fn compute<'tcx>( + mir: &Mir<'tcx>, + map: &impl LiveVariableMap, + ) -> LivenessResults { LivenessResults { regular: liveness_of_locals( &mir, @@ -122,6 +146,7 @@ impl> LivenessResults { include_regular_use: true, include_drops: false, }, + map, ), drop: liveness_of_locals( @@ -130,6 +155,7 @@ impl> LivenessResults { include_regular_use: false, include_drops: true, }, + map, ), } } @@ -138,14 +164,21 @@ impl> LivenessResults { /// 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, V>(mir: &Mir<'tcx>, mode: LivenessMode) -> LivenessResult { - let locals = mir.local_decls.len(); - let def_use: IndexVec<_, _> = mir.basic_blocks() +pub fn liveness_of_locals<'tcx, V: Idx>( + mir: &Mir<'tcx>, + mode: LivenessMode, + map: &impl LiveVariableMap, +) -> LivenessResult { + let locals = map.num_variables(); + + let def_use: IndexVec<_, DefsUses> = mir + .basic_blocks() .iter() - .map(|b| block(mode, b, locals)) + .map(|b| block(mode, map, b, locals)) .collect(); - let mut outs: IndexVec<_, _> = mir.basic_blocks() + let mut outs: IndexVec<_, LocalSet> = mir + .basic_blocks() .indices() .map(|_| LocalSet::new_empty(locals)) .collect(); @@ -179,14 +212,18 @@ pub fn liveness_of_locals<'tcx, V>(mir: &Mir<'tcx>, mode: LivenessMode) -> Liven LivenessResult { mode, outs } } -impl LivenessResult -{ +impl LivenessResult { /// Walks backwards through the statements/terminator in the given /// 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 + pub fn simulate_block<'tcx, OP>( + &self, + mir: &Mir<'tcx>, + block: BasicBlock, + map: &impl LiveVariableMap, + mut callback: OP, + ) where OP: FnMut(Location, &LocalSet), { let data = &mir[block]; @@ -206,16 +243,20 @@ impl LivenessResult let locals = mir.local_decls.len(); let mut visitor = DefsUsesVisitor { mode: self.mode, + map, defs_uses: DefsUses { defs: LocalSet::new_empty(locals), uses: LocalSet::new_empty(locals), - map: &IdentityMap {}, }, }; // 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.update_bits_and_do_callback(terminator_location, &data.terminator, &mut bits, - &mut callback); + visitor.update_bits_and_do_callback( + terminator_location, + &data.terminator, + &mut bits, + &mut callback, + ); // Compute liveness before each statement (in rev order) and invoke callback. for statement in data.statements.iter().rev() { @@ -225,8 +266,12 @@ impl LivenessResult statement_index, }; visitor.defs_uses.clear(); - visitor.update_bits_and_do_callback(statement_location, statement, &mut bits, - &mut callback); + visitor.update_bits_and_do_callback( + statement_location, + statement, + &mut bits, + &mut callback, + ); } } } @@ -306,30 +351,33 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>, mode: LivenessMode) -> Opti } } -struct DefsUsesVisitor<'lv> { +struct DefsUsesVisitor<'lv, V, M> +where + V: Idx, + M: LiveVariableMap + 'lv, +{ mode: LivenessMode, - defs_uses: DefsUses<'lv>, + map: &'lv M, + defs_uses: DefsUses, } #[derive(Eq, PartialEq, Clone)] -struct DefsUses<'lv> -{ - defs: LocalSet, - uses: LocalSet, - map: &'lv dyn LiveVariableMap, +struct DefsUses { + defs: LocalSet, + uses: LocalSet, } -impl<'lv> DefsUses<'lv> { +impl DefsUses { fn clear(&mut self) { self.uses.clear(); self.defs.clear(); } - fn apply(&self, bits: &mut LocalSet) -> bool { + fn apply(&self, bits: &mut LocalSet) -> bool { bits.subtract(&self.defs) | bits.union(&self.uses) } - fn add_def(&mut self, index: Local) { + fn add_def(&mut self, index: V) { // If it was used already in the block, remove that use // now that we found a definition. // @@ -339,13 +387,11 @@ impl<'lv> DefsUses<'lv> { // X = 5 // // Defs = {}, Uses = {X} // use(X) - if let Some(v_index) = self.map.from_local(index) { - self.uses.remove(&v_index); - self.defs.add(&v_index); - } + self.uses.remove(&index); + self.defs.add(&index); } - fn add_use(&mut self, index: Local) { + fn add_use(&mut self, index: V) { // Inverse of above. // // Example: @@ -356,23 +402,27 @@ impl<'lv> DefsUses<'lv> { // X = 5 // // Defs = {}, Uses = {X} // use(X) - if let Some(v_index) = self.map.from_local(index) { - self.defs.remove(&v_index); - self.uses.add(&v_index); - } + self.defs.remove(&index); + self.uses.add(&index); } } -impl<'lv> DefsUsesVisitor<'lv> +impl<'lv, V, M> DefsUsesVisitor<'lv, V, M> +where + V: Idx, + M: LiveVariableMap, { /// Update `bits` with the effects of `value` and call `callback`. We /// should always visit in reverse order. This method assumes that we have /// not visited anything before; if you have, clear `bits` first. - fn update_bits_and_do_callback<'tcx, OP>(&mut self, location: Location, - value: &impl MirVisitable<'tcx>, bits: &mut LocalSet, - callback: &mut OP) - where - OP: FnMut(Location, &LocalSet), + fn update_bits_and_do_callback<'tcx, OP>( + &mut self, + location: Location, + value: &impl MirVisitable<'tcx>, + bits: &mut LocalSet, + callback: &mut OP, + ) where + OP: FnMut(Location, &LocalSet), { value.apply(location, self); self.defs_uses.apply(bits); @@ -380,29 +430,34 @@ impl<'lv> DefsUsesVisitor<'lv> } } -impl<'tcx, 'lv> Visitor<'tcx> for DefsUsesVisitor<'lv> { +impl<'tcx, 'lv, V, M> Visitor<'tcx> for DefsUsesVisitor<'lv, V, M> +where + V: Idx, + M: LiveVariableMap, +{ fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) { - match categorize(context, self.mode) { - Some(DefUse::Def) => { - self.defs_uses.add_def(local); + if let Some(v_index) = self.map.from_local(local) { + match categorize(context, self.mode) { + Some(DefUse::Def) => self.defs_uses.add_def(v_index), + Some(DefUse::Use) => self.defs_uses.add_use(v_index), + None => (), } - - Some(DefUse::Use) => { - self.defs_uses.add_use(local); - } - - None => {} } } } -fn block<'tcx, 'lv>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses<'lv> { +fn block<'tcx, V: Idx>( + mode: LivenessMode, + map: &impl LiveVariableMap, + b: &BasicBlockData<'tcx>, + locals: usize, +) -> DefsUses { let mut visitor = DefsUsesVisitor { mode, + map, defs_uses: DefsUses { defs: LocalSet::new_empty(locals), uses: LocalSet::new_empty(locals), - map: &IdentityMap {}, }, }; @@ -421,13 +476,13 @@ fn block<'tcx, 'lv>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) visitor.defs_uses } -pub fn dump_mir<'a, 'tcx, V: LiveVariableMap>( +pub fn dump_mir<'a, 'tcx, V: Idx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, pass_name: &str, source: MirSource, mir: &Mir<'tcx>, - result: &LivenessResult, - map: &impl LiveVariableMap + map: &impl LiveVariableMap, + result: &LivenessResult, ) { if !dump_enabled(tcx, pass_name, source) { return; @@ -436,16 +491,17 @@ pub fn dump_mir<'a, 'tcx, V: LiveVariableMap>( // see notes on #41697 below tcx.item_path_str(source.def_id) }); - dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, result); + dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, map, result); } -fn dump_matched_mir_node<'a, 'tcx, V: LiveVariableMap>( +fn dump_matched_mir_node<'a, 'tcx, V: Idx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, pass_name: &str, node_path: &str, source: MirSource, mir: &Mir<'tcx>, - result: &LivenessResult, + map: &dyn LiveVariableMap, + result: &LivenessResult, ) { let mut file_path = PathBuf::new(); file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); @@ -457,25 +513,25 @@ fn dump_matched_mir_node<'a, 'tcx, V: LiveVariableMap>( writeln!(file, "// source = {:?}", source)?; writeln!(file, "// pass_name = {}", pass_name)?; writeln!(file, "")?; - write_mir_fn(tcx, source, mir, &mut file, result)?; + write_mir_fn(tcx, source, mir, map, &mut file, result)?; Ok(()) }); } -pub fn write_mir_fn<'a, 'tcx, V: LiveVariableMap>( +pub fn write_mir_fn<'a, 'tcx, V: Idx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &Mir<'tcx>, + map: &dyn LiveVariableMap, w: &mut dyn Write, - result: &LivenessResult, + result: &LivenessResult, ) -> io::Result<()> { write_mir_intro(tcx, src, mir, w)?; for block in mir.basic_blocks().indices() { - let print = |w: &mut dyn Write, prefix, result: &IndexVec>| { - let live: Vec = mir.local_decls - .indices() - .filter(|i| result[block].contains(i)) - .map(|i| format!("{:?}", i)) + let print = |w: &mut dyn Write, prefix, result: &IndexVec>| { + let live: Vec = result[block].iter() + .map(|v| map.from_live_var(v)) + .map(|local| format!("{:?}", local)) .collect(); writeln!(w, "{} {{{}}}", prefix, live.join(", ")) };