]> git.lizzy.rs Git - rust.git/commitdiff
make liveness generic over set of local variables
authorNiko Matsakis <niko@alum.mit.edu>
Sun, 15 Jul 2018 04:28:26 +0000 (00:28 -0400)
committerdylan_DPC <dylan.dpc@gmail.com>
Thu, 19 Jul 2018 17:45:26 +0000 (23:15 +0530)
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
src/librustc_mir/borrow_check/nll/type_check/liveness.rs
src/librustc_mir/transform/generator.rs
src/librustc_mir/util/liveness.rs

index 72d7b473204f822ceedf09710d97a96c30df4bc3..15ebb7a7a29f14b64a661d99aebf4682a1a4522d 100644 (file)
@@ -22,7 +22,6 @@
 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::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
index 97f3b1a1c966ce1631877733ec5946246212bbb6..0eb88b7bcbfd390d2ca8009f1fd5353c23bb43fd 100644 (file)
@@ -20,9 +20,8 @@
 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 @@ struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
     'flow: 'gen,
     'tcx: 'typeck + 'flow,
     'gcx: 'tcx,
-    V: 'gen,
 {
     cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
     mir: &'gen Mir<'tcx>,
@@ -69,6 +68,7 @@ struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
     flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
     move_data: &'gen MoveData<'tcx>,
     drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>,
+    map: &'gen IdentityMap<'gen, 'tcx>,
 }
 
 struct DropData<'tcx> {
@@ -76,7 +76,7 @@ struct DropData<'tcx> {
     region_constraint_data: Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>,
 }
 
-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 @@ fn add_liveness_constraints(&mut self, bb: BasicBlock) {
 
         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<V::LiveVar>)> = vec![];
+        let mut all_live_locals: Vec<(Location, Vec<Local>)> = 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!(
index f1091e40c77a5f54776187d2250da6c48dfb5216..7d4d561072e7a851d5e69366179c79c407874923 100644 (file)
@@ -66,7 +66,7 @@
 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 @@ fn visit_statement(&mut self,
 
 struct BorrowedLocals(liveness::LocalSet<Local>);
 
-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();
 
index 4e78be76b6137a44fcb38142ba9b95b16b171a3a..ea8a180e3b618f87f4f6d4abc289187256978d23 100644 (file)
 //! generator yield points, all pre-existing references are invalidated, so this
 //! doesn't matter).
 
-use rustc::mir::*;
+use rustc::mir::visit::MirVisitable;
 use rustc::mir::visit::{PlaceContext, Visitor};
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+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 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::ty::TyCtxt;
 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<V: LiveVariableMap> = IdxSetBuf<V::LiveVar>;
+pub type LocalSet<V> = IdxSetBuf<V>;
 
 /// 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<V: LiveVariableMap> {
+///
+/// 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<V: Idx> {
     /// 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<BasicBlock, LocalSet<V::LiveVar>>,
+    pub outs: IndexVec<BasicBlock, LocalSet<V>>,
 }
 
-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<Self::LiveVar>;
     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<Self::LiveVar> {
@@ -82,6 +99,10 @@ fn from_local(&self, local: Local) -> Option<Self::LiveVar> {
     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<V> {
+pub struct LivenessResults<V: Idx> {
     /// Liveness results where a regular use makes a variable X live,
     /// but not a drop.
     pub regular: LivenessResult<V>,
@@ -113,8 +134,11 @@ pub struct LivenessResults<V> {
     pub drop: LivenessResult<V>,
 }
 
-impl<V, M: LiveVariableMap<LiveVar = V>> LivenessResults<V> {
-    pub fn compute<'tcx>(mir: &Mir<'tcx>, map: &M) -> LivenessResults<V> {
+impl<V: Idx> LivenessResults<V> {
+    pub fn compute<'tcx>(
+        mir: &Mir<'tcx>,
+        map: &impl LiveVariableMap<LiveVar = V>,
+    ) -> LivenessResults<V> {
         LivenessResults {
             regular: liveness_of_locals(
                 &mir,
@@ -122,6 +146,7 @@ pub fn compute<'tcx>(mir: &Mir<'tcx>, map: &M) -> LivenessResults<V> {
                     include_regular_use: true,
                     include_drops: false,
                 },
+                map,
             ),
 
             drop: liveness_of_locals(
@@ -130,6 +155,7 @@ pub fn compute<'tcx>(mir: &Mir<'tcx>, map: &M) -> LivenessResults<V> {
                     include_regular_use: false,
                     include_drops: true,
                 },
+                map,
             ),
         }
     }
@@ -138,14 +164,21 @@ pub fn compute<'tcx>(mir: &Mir<'tcx>, map: &M) -> LivenessResults<V> {
 /// 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<V> {
-    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<LiveVar = V>,
+) -> LivenessResult<V> {
+    let locals = map.num_variables();
+
+    let def_use: IndexVec<_, DefsUses<V>> = 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<V>> = 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<V: LiveVariableMap> LivenessResult<V>
-{
+impl<V: Idx> LivenessResult<V> {
     /// 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<LiveVar = V>,
+        mut callback: OP,
+    ) where
         OP: FnMut(Location, &LocalSet<V>),
     {
         let data = &mir[block];
@@ -206,16 +243,20 @@ pub fn simulate_block<'tcx, OP>(&self, mir: &Mir<'tcx>, block: BasicBlock, mut c
         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 @@ pub fn simulate_block<'tcx, OP>(&self, mir: &Mir<'tcx>, block: BasicBlock, mut c
                 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<LiveVar = V> + 'lv,
+{
     mode: LivenessMode,
-    defs_uses: DefsUses<'lv>,
+    map: &'lv M,
+    defs_uses: DefsUses<V>,
 }
 
 #[derive(Eq, PartialEq, Clone)]
-struct DefsUses<'lv>
-{
-    defs: LocalSet<Local>,
-    uses: LocalSet<Local>,
-    map: &'lv dyn LiveVariableMap<LiveVar=Local>,
+struct DefsUses<V: Idx> {
+    defs: LocalSet<V>,
+    uses: LocalSet<V>,
 }
 
-impl<'lv> DefsUses<'lv> {
+impl<V: Idx> DefsUses<V> {
     fn clear(&mut self) {
         self.uses.clear();
         self.defs.clear();
     }
 
-    fn apply(&self, bits: &mut LocalSet<Local>) -> bool {
+    fn apply(&self, bits: &mut LocalSet<V>) -> 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 @@ fn add_def(&mut self, index: Local) {
         //     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 @@ fn add_use(&mut self, index: Local) {
         //     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<LiveVar = V>,
 {
     /// 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<Local>,
-                                             callback: &mut OP)
-    where
-        OP: FnMut(Location, &LocalSet<Local>),
+    fn update_bits_and_do_callback<'tcx, OP>(
+        &mut self,
+        location: Location,
+        value: &impl MirVisitable<'tcx>,
+        bits: &mut LocalSet<V>,
+        callback: &mut OP,
+    ) where
+        OP: FnMut(Location, &LocalSet<V>),
     {
         value.apply(location, self);
         self.defs_uses.apply(bits);
@@ -380,29 +430,34 @@ fn update_bits_and_do_callback<'tcx, OP>(&mut self, location: Location,
     }
 }
 
-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<LiveVar = V>,
+{
     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<LiveVar = V>,
+    b: &BasicBlockData<'tcx>,
+    locals: usize,
+) -> DefsUses<V> {
     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<Local>,
-    map: &impl LiveVariableMap<LiveVar = V>
+    map: &impl LiveVariableMap<LiveVar = V>,
+    result: &LivenessResult<V>,
 ) {
     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<V::LiveVar>,
+    map: &dyn LiveVariableMap<LiveVar = V>,
+    result: &LivenessResult<V>,
 ) {
     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<LiveVar = V>,
     w: &mut dyn Write,
-    result: &LivenessResult<V::LiveVar>,
+    result: &LivenessResult<V>,
 ) -> 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<BasicBlock, LocalSet<Local>>| {
-            let live: Vec<String> = mir.local_decls
-                .indices()
-                .filter(|i| result[block].contains(i))
-                .map(|i| format!("{:?}", i))
+        let print = |w: &mut dyn Write, prefix, result: &IndexVec<BasicBlock, LocalSet<V>>| {
+            let live: Vec<String> = result[block].iter()
+                .map(|v| map.from_live_var(v))
+                .map(|local| format!("{:?}", local))
                 .collect();
             writeln!(w, "{} {{{}}}", prefix, live.join(", "))
         };