about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2016-05-23 18:26:52 +0200
committerFelix S. Klock II <pnkfelix@pnkfx.org>2016-05-23 18:26:52 +0200
commit71af40bb9dd5887ce9797e73f608b48b0f41355b (patch)
tree97622f8afe12f44346718a4dd251de9a6daf7794
parent0796ee77baf4a186413c1176878e6705fa9dc377 (diff)
downloadrust-71af40bb9dd5887ce9797e73f608b48b0f41355b.tar.gz
rust-71af40bb9dd5887ce9797e73f608b48b0f41355b.zip
revised mir-dataflow so bitvectors carry a phantom type for their index domain.
As some drive-by's:

 * moved bitwise operators into `mod bitslice`

 * factored out `fn gen` and `fn kill` methods on `BlockSets` type

 * removed outdated comment about `fn propagate_call_return`
-rw-r--r--src/librustc_borrowck/bitslice.rs29
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs11
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/mod.rs297
-rw-r--r--src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs23
-rw-r--r--src/librustc_borrowck/borrowck/mir/gather_moves.rs17
-rw-r--r--src/librustc_borrowck/indexed_set.rs75
6 files changed, 272 insertions, 180 deletions
diff --git a/src/librustc_borrowck/bitslice.rs b/src/librustc_borrowck/bitslice.rs
index 1c01e98cf85..7a203b7f0b7 100644
--- a/src/librustc_borrowck/bitslice.rs
+++ b/src/librustc_borrowck/bitslice.rs
@@ -109,3 +109,32 @@ pub fn bits_to_string(words: &[Word], bits: usize) -> String {
     result.push(']');
     return result
 }
+
+#[inline]
+pub fn bitwise<Op:BitwiseOperator>(out_vec: &mut [usize],
+                                   in_vec: &[usize],
+                                   op: &Op) -> bool {
+    assert_eq!(out_vec.len(), in_vec.len());
+    let mut changed = false;
+    for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) {
+        let old_val = *out_elt;
+        let new_val = op.join(old_val, *in_elt);
+        *out_elt = new_val;
+        changed |= old_val != new_val;
+    }
+    changed
+}
+
+pub trait BitwiseOperator {
+    /// Applies some bit-operation pointwise to each of the bits in the two inputs.
+    fn join(&self, pred1: usize, pred2: usize) -> usize;
+}
+
+pub struct Union;
+impl BitwiseOperator for Union {
+    fn join(&self, a: usize, b: usize) -> usize { a | b }
+}
+pub struct Subtract;
+impl BitwiseOperator for Subtract {
+    fn join(&self, a: usize, b: usize) -> usize { a & !b }
+}
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs
index bc6ee89fa25..5a8b3ec3206 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs
@@ -54,8 +54,9 @@ struct Graph<'a, 'tcx, MWF:'a> where MWF: MirWithFlowState<'tcx>,
 
 pub fn print_borrowck_graph_to<'a, 'tcx, BD>(
     mbcx: &MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>,
-    path: &Path) -> io::Result<()> where BD: BitDenotation,
-                                        BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx>
+    path: &Path)
+    -> io::Result<()>
+    where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx>,
 {
     let g = Graph { mbcx: mbcx, phantom: PhantomData };
     let mut v = Vec::new();
@@ -180,7 +181,7 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF>
                                         <td></td></tr>",
                        bg = BG_FLOWCONTENT,
                        face = FACE_MONOSPACE,
-                       entrybits=bits_to_string(entry, bits_per_block))
+                       entrybits=bits_to_string(entry.words(), bits_per_block))
             },
             |w| {
                 let ctxt = self.mbcx.analysis_ctxt();
@@ -197,7 +198,7 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF>
                                            <td></td></tr>",
                            bg = BG_FLOWCONTENT,
                            face = FACE_MONOSPACE,
-                           genbits=bits_to_string(gen, bits_per_block))?;
+                           genbits=bits_to_string(gen.words(), bits_per_block))?;
                 }
 
                 {
@@ -209,7 +210,7 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF>
                            bg = BG_FLOWCONTENT,
                            align = ALIGN_RIGHT,
                            face = FACE_MONOSPACE,
-                           killbits=bits_to_string(kill, bits_per_block))?;
+                           killbits=bits_to_string(kill.words(), bits_per_block))?;
                 }
 
                 // (chunked_present_right)
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
index 09abbb6af68..e1459ad2abe 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
@@ -24,6 +24,8 @@ use super::gather_moves::{MoveOut, MovePath};
 use super::DropFlagState;
 
 use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
+use bitslice::{bitwise, BitwiseOperator};
+use indexed_set::{Idx, IdxSet, OwnIdxSet};
 
 pub use self::sanity_check::sanity_check_via_rustc_peek;
 
@@ -35,7 +37,9 @@ pub trait Dataflow {
 }
 
 impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
-    where BD: BitDenotation + DataflowOperator, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx>
+    where BD: BitDenotation + DataflowOperator,
+          BD::Bit: Debug,
+          BD::Ctxt: HasMoveData<'tcx>
 {
     fn dataflow(&mut self) {
         self.flow_state.build_sets();
@@ -46,7 +50,7 @@ impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
 }
 
 struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O>
-    where O: 'b + BitDenotation, O::Ctxt: HasMoveData<'tcx>,
+    where O: 'b + BitDenotation, O::Ctxt: HasMoveData<'tcx>
 {
     builder: &'b mut DataflowAnalysis<'a, 'tcx, O>,
     changed: bool,
@@ -56,7 +60,7 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD>
     where BD: BitDenotation + DataflowOperator, BD::Ctxt: HasMoveData<'tcx>
 {
     fn propagate(&mut self) {
-        let mut temp = vec![0; self.flow_state.sets.words_per_block];
+        let mut temp = OwnIdxSet::new_empty(self.flow_state.sets.bits_per_block);
         let mut propcx = PropagationContext {
             builder: self,
             changed: true,
@@ -102,23 +106,23 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD>
 impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD>
     where BD: BitDenotation + DataflowOperator, BD::Ctxt: HasMoveData<'tcx>
 {
-    fn reset(&mut self, bits: &mut [usize]) {
-        let e = if BD::bottom_value() {usize::MAX} else {0};
-        for b in bits {
+    fn reset(&mut self, bits: &mut IdxSet<BD::Idx>) {
+        let e = if BD::bottom_value() {!0} else {0};
+        for b in bits.words_mut() {
             *b = e;
         }
     }
 
-    fn walk_cfg(&mut self, in_out: &mut [usize]) {
+    fn walk_cfg(&mut self, in_out: &mut IdxSet<BD::Idx>) {
         let mir = self.builder.mir;
         for (bb_idx, bb_data) in mir.basic_blocks.iter().enumerate() {
             let builder = &mut self.builder;
             {
                 let sets = builder.flow_state.sets.for_block(bb_idx);
-                debug_assert!(in_out.len() == sets.on_entry.len());
-                in_out.clone_from_slice(sets.on_entry);
-                bitwise(in_out, sets.gen_set, &Union);
-                bitwise(in_out, sets.kill_set, &Subtract);
+                debug_assert!(in_out.words().len() == sets.on_entry.words().len());
+                in_out.clone_from(sets.on_entry);
+                in_out.union(sets.gen_set);
+                in_out.subtract(sets.kill_set);
             }
             builder.propagate_bits_into_graph_successors_of(in_out,
                                                             &mut self.changed,
@@ -161,14 +165,18 @@ impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
 }
 
 /// Maps each block to a set of bits
-#[derive(Clone, Debug)]
-struct Bits {
-    bits: Vec<usize>,
+#[derive(Debug)]
+struct Bits<E:Idx> {
+    bits: OwnIdxSet<E>,
+}
+
+impl<E:Idx> Clone for Bits<E> {
+    fn clone(&self) -> Self { Bits { bits: self.bits.clone() } }
 }
 
-impl Bits {
-    fn new(init_word: usize, num_words: usize) -> Self {
-        Bits { bits: vec![init_word; num_words] }
+impl<E:Idx> Bits<E> {
+    fn new(bits: OwnIdxSet<E>) -> Self {
+        Bits { bits: bits }
     }
 }
 
@@ -201,25 +209,23 @@ impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O>
     pub fn mir(&self) -> &'a Mir<'tcx> { self.mir }
 }
 
-#[derive(Debug)]
-pub struct DataflowResults<O: BitDenotation>(DataflowState<O>);
+pub struct DataflowResults<O>(DataflowState<O>) where O: BitDenotation;
 
 // FIXME: This type shouldn't be public, but the graphviz::MirWithFlowState trait
 // references it in a method signature. Look into using `pub(crate)` to address this.
-#[derive(Debug)]
 pub struct DataflowState<O: BitDenotation>
 {
     /// All the sets for the analysis. (Factored into its
     /// own structure so that we can borrow it mutably
     /// on its own separate from other fields.)
-    pub sets: AllSets,
+    pub sets: AllSets<O::Idx>,
 
     /// operator used to initialize, combine, and interpret bits.
     operator: O,
 }
 
 #[derive(Debug)]
-pub struct AllSets {
+pub struct AllSets<E: Idx> {
     /// Analysis bitwidth for each block.
     bits_per_block: usize,
 
@@ -230,59 +236,71 @@ pub struct AllSets {
     /// For each block, bits generated by executing the statements in
     /// the block. (For comparison, the Terminator for each block is
     /// handled in a flow-specific manner during propagation.)
-    gen_sets: Bits,
+    gen_sets: Bits<E>,
 
     /// For each block, bits killed by executing the statements in the
     /// block. (For comparison, the Terminator for each block is
     /// handled in a flow-specific manner during propagation.)
-    kill_sets: Bits,
+    kill_sets: Bits<E>,
 
     /// For each block, bits valid on entry to the block.
-    on_entry_sets: Bits,
+    on_entry_sets: Bits<E>,
 }
 
-pub struct BlockSets<'a> {
-    on_entry: &'a mut [usize],
-    gen_set: &'a mut [usize],
-    kill_set: &'a mut [usize],
+pub struct BlockSets<'a, E: Idx> {
+    on_entry: &'a mut IdxSet<E>,
+    gen_set: &'a mut IdxSet<E>,
+    kill_set: &'a mut IdxSet<E>,
 }
 
-impl AllSets {
+impl<'a, E:Idx> BlockSets<'a, E> {
+    fn gen(&mut self, e: &E) {
+        self.gen_set.add(e);
+        self.kill_set.remove(e);
+    }
+    fn kill(&mut self, e: &E) {
+        self.gen_set.remove(e);
+        self.kill_set.add(e);
+    }
+}
+
+impl<E:Idx> AllSets<E> {
     pub fn bits_per_block(&self) -> usize { self.bits_per_block }
-    pub fn for_block(&mut self, block_idx: usize) -> BlockSets {
+    pub fn for_block(&mut self, block_idx: usize) -> BlockSets<E> {
         let offset = self.words_per_block * block_idx;
-        let range = offset..(offset + self.words_per_block);
+        let range = E::new(offset)..E::new(offset + self.words_per_block);
         BlockSets {
-            on_entry: &mut self.on_entry_sets.bits[range.clone()],
-            gen_set: &mut self.gen_sets.bits[range.clone()],
-            kill_set: &mut self.kill_sets.bits[range],
+            on_entry: self.on_entry_sets.bits.range_mut(&range),
+            gen_set: self.gen_sets.bits.range_mut(&range),
+            kill_set: self.kill_sets.bits.range_mut(&range),
         }
     }
 
-    fn lookup_set_for<'a>(&self, sets: &'a Bits, block_idx: usize) -> &'a [usize] {
+    fn lookup_set_for<'a>(&self, sets: &'a Bits<E>, block_idx: usize) -> &'a IdxSet<E> {
         let offset = self.words_per_block * block_idx;
-        &sets.bits[offset..(offset + self.words_per_block)]
+        let range = E::new(offset)..E::new(offset + self.words_per_block);
+        sets.bits.range(&range)
     }
-    pub fn gen_set_for(&self, block_idx: usize) -> &[usize] {
+    pub fn gen_set_for(&self, block_idx: usize) -> &IdxSet<E> {
         self.lookup_set_for(&self.gen_sets, block_idx)
     }
-    pub fn kill_set_for(&self, block_idx: usize) -> &[usize] {
+    pub fn kill_set_for(&self, block_idx: usize) -> &IdxSet<E> {
         self.lookup_set_for(&self.kill_sets, block_idx)
     }
-    pub fn on_entry_set_for(&self, block_idx: usize) -> &[usize] {
+    pub fn on_entry_set_for(&self, block_idx: usize) -> &IdxSet<E> {
         self.lookup_set_for(&self.on_entry_sets, block_idx)
     }
 }
 
 impl<O: BitDenotation> DataflowState<O> {
-    fn each_bit<F>(&self, ctxt: &O::Ctxt, words: &[usize], mut f: F)
+    fn each_bit<F>(&self, ctxt: &O::Ctxt, words: &IdxSet<O::Idx>, mut f: F)
         where F: FnMut(usize) {
         //! Helper for iterating over the bits in a bitvector.
 
         let bits_per_block = self.operator.bits_per_block(ctxt);
         let usize_bits: usize = mem::size_of::<usize>() * 8;
 
-        for (word_index, &word) in words.iter().enumerate() {
+        for (word_index, &word) in words.words().iter().enumerate() {
             if word != 0 {
                 let base_index = word_index * usize_bits;
                 for offset in 0..usize_bits {
@@ -307,7 +325,7 @@ impl<O: BitDenotation> DataflowState<O> {
         }
     }
 
-    pub fn interpret_set<'c>(&self, ctxt: &'c O::Ctxt, words: &[usize]) -> Vec<&'c O::Bit> {
+    pub fn interpret_set<'c>(&self, ctxt: &'c O::Ctxt, words: &IdxSet<O::Idx>) -> Vec<&'c O::Bit> {
         let mut v = Vec::new();
         self.each_bit(ctxt, words, |i| {
             v.push(self.operator.interpret(ctxt, i));
@@ -316,11 +334,6 @@ impl<O: BitDenotation> DataflowState<O> {
     }
 }
 
-pub trait BitwiseOperator {
-    /// Applies some bit-operation pointwise to each of the bits in the two inputs.
-    fn join(&self, pred1: usize, pred2: usize) -> usize;
-}
-
 /// Parameterization for the precise form of data flow that is used.
 pub trait DataflowOperator: BitwiseOperator {
     /// Specifies the initial value for each bit in the `on_entry` set
@@ -331,6 +344,9 @@ pub trait BitDenotation {
     /// Specifies what is represented by each bit in the dataflow bitvector.
     type Bit;
 
+    /// Specifies what index type is used to access the bitvector.
+    type Idx: Idx;
+
     /// Specifies what, if any, separate context needs to be supplied for methods below.
     type Ctxt;
 
@@ -359,7 +375,7 @@ pub trait BitDenotation {
     /// (Typically this should only modify `sets.on_entry`, since the
     /// gen and kill sets should reflect the effects of *executing*
     /// the start block itself.)
-    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets);
+    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<Self::Idx>);
 
     /// Mutates the block-sets (the flow sets for the given
     /// basic block) according to the effects of evaluating statement.
@@ -373,7 +389,7 @@ pub trait BitDenotation {
     /// in the basic block.
     fn statement_effect(&self,
                         ctxt: &Self::Ctxt,
-                        sets: &mut BlockSets,
+                        sets: &mut BlockSets<Self::Idx>,
                         bb: repr::BasicBlock,
                         idx_stmt: usize);
 
@@ -393,7 +409,7 @@ pub trait BitDenotation {
     /// terminator took.
     fn terminator_effect(&self,
                          ctxt: &Self::Ctxt,
-                         sets: &mut BlockSets,
+                         sets: &mut BlockSets<Self::Idx>,
                          bb: repr::BasicBlock,
                          idx_term: usize);
 
@@ -411,21 +427,17 @@ pub trait BitDenotation {
     /// flow-dependent, the current MIR cannot encode them via just
     /// GEN and KILL sets attached to the block, and so instead we add
     /// this extra machinery to represent the flow-dependent effect.
-    ///
-    /// Note: as a historical artifact, this currently takes as input
-    /// the *entire* packed collection of bitvectors in `in_out`.  We
-    /// might want to look into narrowing that to something more
-    /// specific, just to make the interface more self-documenting.
     fn propagate_call_return(&self,
                              ctxt: &Self::Ctxt,
-                             in_out: &mut [usize],
+                             in_out: &mut IdxSet<Self::Idx>,
                              call_bb: repr::BasicBlock,
                              dest_bb: repr::BasicBlock,
                              dest_lval: &repr::Lvalue);
 }
 
 impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
-    where D: BitDenotation + DataflowOperator, D::Ctxt: HasMoveData<'tcx>
+    where D: BitDenotation + DataflowOperator,
+          D::Ctxt: HasMoveData<'tcx>
 {
     pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>,
                mir: &'a Mir<'tcx>,
@@ -434,33 +446,41 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
         let bits_per_block = denotation.bits_per_block(&ctxt);
         let usize_bits = mem::size_of::<usize>() * 8;
         let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits;
-        let num_blocks = mir.basic_blocks.len();
-        let num_words = num_blocks * words_per_block;
 
-        let entry = if D::bottom_value() { usize::MAX } else {0};
+        // (now rounded up to multiple of word size)
+        let bits_per_block = words_per_block * usize_bits;
 
-        let zeroes = Bits::new(0, num_words);
-        let on_entry = Bits::new(entry, num_words);
+        let num_blocks = mir.basic_blocks.len();
+        let num_overall = num_blocks * bits_per_block;
 
-        DataflowAnalysis { flow_state: DataflowState {
-            sets: AllSets {
-                bits_per_block: bits_per_block,
-                words_per_block: words_per_block,
-                gen_sets: zeroes.clone(),
-                kill_sets: zeroes,
-                on_entry_sets: on_entry,
+        let zeroes = Bits::new(OwnIdxSet::new_empty(num_overall));
+        let on_entry = Bits::new(if D::bottom_value() {
+            OwnIdxSet::new_filled(num_overall)
+        } else {
+            OwnIdxSet::new_empty(num_overall)
+        });
+
+        DataflowAnalysis {
+            ctxt: ctxt,
+            mir: mir,
+            flow_state: DataflowState {
+                sets: AllSets {
+                    bits_per_block: bits_per_block,
+                    words_per_block: words_per_block,
+                    gen_sets: zeroes.clone(),
+                    kill_sets: zeroes,
+                    on_entry_sets: on_entry,
+                },
+                operator: denotation,
             },
-            operator: denotation,
-        },
-                           ctxt: ctxt,
-                           mir: mir,
         }
 
     }
 }
 
 impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
-    where D: BitDenotation + DataflowOperator, D::Ctxt: HasMoveData<'tcx>
+    where D: BitDenotation + DataflowOperator,
+          D::Ctxt: HasMoveData<'tcx>,
 {
     /// Propagates the bits of `in_out` into all the successors of `bb`,
     /// using bitwise operator denoted by `self.operator`.
@@ -477,7 +497,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
     /// unwind target).
     fn propagate_bits_into_graph_successors_of(
         &mut self,
-        in_out: &mut [usize],
+        in_out: &mut IdxSet<D::Idx>,
         changed: &mut bool,
         (bb, bb_data): (repr::BasicBlock, &repr::BasicBlockData))
     {
@@ -518,11 +538,13 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
     }
 
     fn propagate_bits_into_entry_set_for(&mut self,
-                                         in_out: &[usize],
+                                         in_out: &IdxSet<D::Idx>,
                                          changed: &mut bool,
                                          bb: &repr::BasicBlock) {
         let entry_set = self.flow_state.sets.for_block(bb.index()).on_entry;
-        let set_changed = bitwise(entry_set, in_out, &self.flow_state.operator);
+        let set_changed = bitwise(entry_set.words_mut(),
+                                  in_out.words(),
+                                  &self.flow_state.operator);
         if set_changed {
             *changed = true;
         }
@@ -694,6 +716,7 @@ pub struct MovingOutStatements<'a, 'tcx: 'a> {
 }
 
 impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
+    type Idx = MoveOutIndex;
     type Bit = MoveOut;
     type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>);
     fn name() -> &'static str { "moving_out" }
@@ -703,13 +726,13 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
     fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit {
         &ctxt.2.moves[idx]
     }
-    fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets) {
+    fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets<MoveOutIndex>) {
         // no move-statements have been executed prior to function
         // execution, so this method has no effect on `_sets`.
     }
     fn statement_effect(&self,
                         ctxt: &Self::Ctxt,
-                        sets: &mut BlockSets,
+                        sets: &mut BlockSets<MoveOutIndex>,
                         bb: repr::BasicBlock,
                         idx: usize) {
         let &(tcx, mir, ref move_data) = ctxt;
@@ -725,7 +748,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
             // Every path deinitialized by a *particular move*
             // has corresponding bit, "gen'ed" (i.e. set)
             // here, in dataflow vector
-            zero_to_one(&mut sets.gen_set, *move_index);
+            zero_to_one(sets.gen_set.words_mut(), *move_index);
         }
         let bits_per_block = self.bits_per_block(ctxt);
         match stmt.kind {
@@ -740,7 +763,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
                                             move_path_index,
                                             |mpi| for moi in &path_map[mpi] {
                                                 assert!(moi.idx() < bits_per_block);
-                                                sets.kill_set.set_bit(moi.idx());
+                                                sets.kill_set.add(&moi);
                                             });
             }
         }
@@ -748,7 +771,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
 
     fn terminator_effect(&self,
                          ctxt: &Self::Ctxt,
-                         sets: &mut BlockSets,
+                         sets: &mut BlockSets<MoveOutIndex>,
                          bb: repr::BasicBlock,
                          statements_len: usize)
     {
@@ -761,13 +784,13 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
         let bits_per_block = self.bits_per_block(ctxt);
         for move_index in &loc_map[loc] {
             assert!(move_index.idx() < bits_per_block);
-            zero_to_one(&mut sets.gen_set, *move_index);
+            zero_to_one(sets.gen_set.words_mut(), *move_index);
         }
     }
 
     fn propagate_call_return(&self,
                              ctxt: &Self::Ctxt,
-                             in_out: &mut [usize],
+                             in_out: &mut IdxSet<MoveOutIndex>,
                              _call_bb: repr::BasicBlock,
                              _dest_bb: repr::BasicBlock,
                              dest_lval: &repr::Lvalue) {
@@ -782,63 +805,46 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
                                     move_path_index,
                                     |mpi| for moi in &path_map[mpi] {
                                         assert!(moi.idx() < bits_per_block);
-                                        in_out.clear_bit(moi.idx());
+                                        in_out.remove(&moi);
                                     });
     }
 }
 
 impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
-    fn update_bits(sets: &mut BlockSets, path: MovePathIndex,
+    fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
                    state: super::DropFlagState)
     {
         match state {
-            DropFlagState::Absent => {
-                sets.gen_set.clear_bit(path.idx());
-                sets.kill_set.set_bit(path.idx());
-            }
-            DropFlagState::Present => {
-                sets.gen_set.set_bit(path.idx());
-                sets.kill_set.clear_bit(path.idx());
-            }
+            DropFlagState::Absent => sets.kill(&path),
+            DropFlagState::Present => sets.gen(&path),
         }
     }
 }
 
 impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
-    fn update_bits(sets: &mut BlockSets, path: MovePathIndex,
+    fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
                    state: super::DropFlagState)
     {
         match state {
-            DropFlagState::Absent => {
-                sets.gen_set.set_bit(path.idx());
-                sets.kill_set.clear_bit(path.idx());
-            }
-            DropFlagState::Present => {
-                sets.gen_set.clear_bit(path.idx());
-                sets.kill_set.set_bit(path.idx());
-            }
+            DropFlagState::Absent => sets.gen(&path),
+            DropFlagState::Present => sets.kill(&path),
         }
     }
 }
 
 impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
-    fn update_bits(sets: &mut BlockSets, path: MovePathIndex,
+    fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
                    state: super::DropFlagState)
     {
         match state {
-            DropFlagState::Absent => {
-                sets.gen_set.clear_bit(path.idx());
-                sets.kill_set.set_bit(path.idx());
-            }
-            DropFlagState::Present => {
-                sets.gen_set.set_bit(path.idx());
-                sets.kill_set.clear_bit(path.idx());
-            }
+            DropFlagState::Absent => sets.kill(&path),
+            DropFlagState::Present => sets.gen(&path),
         }
     }
 }
 
 impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
+    type Idx = MovePathIndex;
     type Bit = MovePath<'tcx>;
     type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>);
     fn name() -> &'static str { "maybe_init" }
@@ -848,19 +854,19 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
     fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit {
         &ctxt.2.move_paths[MovePathIndex::new(idx)]
     }
-    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets)
+    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>)
     {
         super::drop_flag_effects_for_function_entry(
             ctxt.0, ctxt.1, &ctxt.2,
             |path, s| {
                 assert!(s == DropFlagState::Present);
-                sets.on_entry.set_bit(path.idx());
+                sets.on_entry.add(&path);
             });
     }
 
     fn statement_effect(&self,
                         ctxt: &Self::Ctxt,
-                        sets: &mut BlockSets,
+                        sets: &mut BlockSets<MovePathIndex>,
                         bb: repr::BasicBlock,
                         idx: usize)
     {
@@ -873,7 +879,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
 
     fn terminator_effect(&self,
                          ctxt: &Self::Ctxt,
-                         sets: &mut BlockSets,
+                         sets: &mut BlockSets<MovePathIndex>,
                          bb: repr::BasicBlock,
                          statements_len: usize)
     {
@@ -886,7 +892,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
 
     fn propagate_call_return(&self,
                              ctxt: &Self::Ctxt,
-                             in_out: &mut [usize],
+                             in_out: &mut IdxSet<MovePathIndex>,
                              _call_bb: repr::BasicBlock,
                              _dest_bb: repr::BasicBlock,
                              dest_lval: &repr::Lvalue) {
@@ -897,12 +903,13 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
         super::on_all_children_bits(
             ctxt.0, ctxt.1, &ctxt.2,
             move_path_index,
-            |mpi| { in_out.set_bit(mpi.idx()); }
+            |mpi| { in_out.add(&mpi); }
         );
     }
 }
 
 impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
+    type Idx = MovePathIndex;
     type Bit = MovePath<'tcx>;
     type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>);
     fn name() -> &'static str { "maybe_uninit" }
@@ -914,21 +921,21 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
     }
 
     // sets on_entry bits for Arg lvalues
-    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) {
+    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
         // set all bits to 1 (uninit) before gathering counterevidence
-        for e in &mut sets.on_entry[..] { *e = !0; }
+        for e in sets.on_entry.words_mut() { *e = !0; }
 
         super::drop_flag_effects_for_function_entry(
             ctxt.0, ctxt.1, &ctxt.2,
             |path, s| {
                 assert!(s == DropFlagState::Present);
-                sets.on_entry.clear_bit(path.idx());
+                sets.on_entry.remove(&path);
             });
     }
 
     fn statement_effect(&self,
                         ctxt: &Self::Ctxt,
-                        sets: &mut BlockSets,
+                        sets: &mut BlockSets<MovePathIndex>,
                         bb: repr::BasicBlock,
                         idx: usize)
     {
@@ -941,7 +948,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
 
     fn terminator_effect(&self,
                          ctxt: &Self::Ctxt,
-                         sets: &mut BlockSets,
+                         sets: &mut BlockSets<MovePathIndex>,
                          bb: repr::BasicBlock,
                          statements_len: usize)
     {
@@ -954,7 +961,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
 
     fn propagate_call_return(&self,
                              ctxt: &Self::Ctxt,
-                             in_out: &mut [usize],
+                             in_out: &mut IdxSet<MovePathIndex>,
                              _call_bb: repr::BasicBlock,
                              _dest_bb: repr::BasicBlock,
                              dest_lval: &repr::Lvalue) {
@@ -964,12 +971,13 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
         super::on_all_children_bits(
             ctxt.0, ctxt.1, &ctxt.2,
             move_path_index,
-            |mpi| { in_out.clear_bit(mpi.idx()); }
+            |mpi| { in_out.remove(&mpi); }
         );
     }
 }
 
 impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
+    type Idx = MovePathIndex;
     type Bit = MovePath<'tcx>;
     type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>);
     fn name() -> &'static str { "definite_init" }
@@ -981,20 +989,20 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
     }
 
     // sets on_entry bits for Arg lvalues
-    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) {
-        for e in &mut sets.on_entry[..] { *e = 0; }
+    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
+        for e in sets.on_entry.words_mut() { *e = 0; }
 
         super::drop_flag_effects_for_function_entry(
             ctxt.0, ctxt.1, &ctxt.2,
             |path, s| {
                 assert!(s == DropFlagState::Present);
-                sets.on_entry.set_bit(path.idx());
+                sets.on_entry.add(&path);
             });
     }
 
     fn statement_effect(&self,
                         ctxt: &Self::Ctxt,
-                        sets: &mut BlockSets,
+                        sets: &mut BlockSets<MovePathIndex>,
                         bb: repr::BasicBlock,
                         idx: usize)
     {
@@ -1007,7 +1015,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
 
     fn terminator_effect(&self,
                          ctxt: &Self::Ctxt,
-                         sets: &mut BlockSets,
+                         sets: &mut BlockSets<MovePathIndex>,
                          bb: repr::BasicBlock,
                          statements_len: usize)
     {
@@ -1020,7 +1028,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
 
     fn propagate_call_return(&self,
                              ctxt: &Self::Ctxt,
-                             in_out: &mut [usize],
+                             in_out: &mut IdxSet<MovePathIndex>,
                              _call_bb: repr::BasicBlock,
                              _dest_bb: repr::BasicBlock,
                              dest_lval: &repr::Lvalue) {
@@ -1030,7 +1038,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
         super::on_all_children_bits(
             ctxt.0, ctxt.1, &ctxt.2,
             move_path_index,
-            |mpi| { in_out.set_bit(mpi.idx()); }
+            |mpi| { in_out.add(&mpi); }
         );
     }
 }
@@ -1106,26 +1114,3 @@ impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
     }
 }
 
-#[inline]
-fn bitwise<Op:BitwiseOperator>(out_vec: &mut [usize],
-                               in_vec: &[usize],
-                               op: &Op) -> bool {
-    assert_eq!(out_vec.len(), in_vec.len());
-    let mut changed = false;
-    for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) {
-        let old_val = *out_elt;
-        let new_val = op.join(old_val, *in_elt);
-        *out_elt = new_val;
-        changed |= old_val != new_val;
-    }
-    changed
-}
-
-struct Union;
-impl BitwiseOperator for Union {
-    fn join(&self, a: usize, b: usize) -> usize { a | b }
-}
-struct Subtract;
-impl BitwiseOperator for Subtract {
-    fn join(&self, a: usize, b: usize) -> usize { a & !b }
-}
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
index 7c8a9a4aeb0..6cf20def0e0 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
@@ -15,10 +15,7 @@ use syntax::codemap::Span;
 use rustc::ty::{self, TyCtxt};
 use rustc::mir::repr::{self, Mir};
 
-use bitslice::BitSlice;
-
-use super::super::gather_moves::MovePath;
-use super::{bitwise, Union, Subtract};
+use super::super::gather_moves::{MovePath, MovePathIndex};
 use super::BitDenotation;
 use super::DataflowResults;
 use super::HasMoveData;
@@ -45,7 +42,7 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                                 _attributes: &[ast::Attribute],
                                                 flow_ctxt: &O::Ctxt,
                                                 results: &DataflowResults<O>)
-    where O: BitDenotation<Bit=MovePath<'tcx>>, O::Ctxt: HasMoveData<'tcx>
+    where O: BitDenotation<Bit=MovePath<'tcx>, Idx=MovePathIndex>, O::Ctxt: HasMoveData<'tcx>
 {
     debug!("sanity_check_via_rustc_peek id: {:?}", id);
     // FIXME: this is not DRY. Figure out way to abstract this and
@@ -87,9 +84,9 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         // include terminator (since we are peeking the state of the
         // argument at time immediate preceding Call to `rustc_peek`).
 
-        let mut sets = super::BlockSets { on_entry: &mut entry[..],
-                                          gen_set: &mut gen[..],
-                                          kill_set: &mut kill[..] };
+        let mut sets = super::BlockSets { on_entry: &mut entry,
+                                          gen_set: &mut gen,
+                                          kill_set: &mut kill };
 
         for (j, stmt) in statements.iter().enumerate() {
             debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
@@ -105,7 +102,7 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                          ref peeking_at_lval) = *rvalue {
                     // Okay, our search is over.
                     let peek_mpi = move_data.rev_lookup.find(peeking_at_lval);
-                    let bit_state = sets.on_entry.get_bit(peek_mpi.idx());
+                    let bit_state = sets.on_entry.contains(&peek_mpi);
                     debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
                            lvalue, peeking_at_lval, bit_state);
                     if !bit_state {
@@ -126,11 +123,11 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}",
                    lvalue, lhs_mpi, stmt);
             // reset GEN and KILL sets before emulating their effect.
-            for e in &mut sets.gen_set[..] { *e = 0; }
-            for e in &mut sets.kill_set[..] { *e = 0; }
+            for e in sets.gen_set.words_mut() { *e = 0; }
+            for e in sets.kill_set.words_mut() { *e = 0; }
             results.0.operator.statement_effect(flow_ctxt, &mut sets, bb, j);
-            bitwise(sets.on_entry, sets.gen_set, &Union);
-            bitwise(sets.on_entry, sets.kill_set, &Subtract);
+            sets.on_entry.union(sets.gen_set);
+            sets.on_entry.subtract(sets.kill_set);
         }
 
         tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
index 519e9398436..e196de46ee6 100644
--- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
@@ -20,6 +20,7 @@ use std::iter;
 use std::ops::Index;
 
 use super::abs_domain::{AbstractElem, Lift};
+use indexed_set::{Idx, Indexed};
 
 // This submodule holds some newtype'd Index wrappers that are using
 // NonZero to ensure that Option<Index> occupies only a single word.
@@ -28,6 +29,7 @@ use super::abs_domain::{AbstractElem, Lift};
 // (which is likely to yield a subtle off-by-one error).
 mod indexes {
     use core::nonzero::NonZero;
+    use indexed_set::Idx;
 
     macro_rules! new_index {
         ($Index:ident) => {
@@ -35,10 +37,13 @@ mod indexes {
             pub struct $Index(NonZero<usize>);
 
             impl $Index {
-                pub fn new(idx: usize) -> Self {
+            }
+
+            impl Idx for $Index {
+                fn new(idx: usize) -> Self {
                     unsafe { $Index(NonZero::new(idx + 1)) }
                 }
-                pub fn idx(&self) -> usize {
+                fn idx(&self) -> usize {
                     *self.0 - 1
                 }
             }
@@ -55,6 +60,14 @@ mod indexes {
 pub use self::indexes::MovePathIndex;
 pub use self::indexes::MoveOutIndex;
 
+impl<'tcx> Indexed for MovePath<'tcx> {
+    type Idx = MovePathIndex;
+}
+
+impl Indexed for MoveOut {
+    type Idx = MoveOutIndex;
+}
+
 impl self::indexes::MoveOutIndex {
     pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex {
         move_data.moves[self.idx()].path
diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs
index c743e58aa99..c37f8e09e0a 100644
--- a/src/librustc_borrowck/indexed_set.rs
+++ b/src/librustc_borrowck/indexed_set.rs
@@ -11,13 +11,16 @@
 use std::fmt;
 use std::marker::PhantomData;
 use std::mem;
+use std::ops::{Deref, DerefMut, Range};
 use bitslice::{BitSlice, Word};
+use bitslice::{bitwise, Union, Subtract};
 
 pub trait Indexed {
     type Idx: Idx;
 }
 
-pub trait Idx {
+pub trait Idx: 'static {
+    fn new(usize) -> Self;
     fn idx(&self) -> usize;
 }
 
@@ -26,10 +29,16 @@ pub struct OwnIdxSet<T: Idx> {
     bits: Vec<Word>,
 }
 
+impl<T: Idx> Clone for OwnIdxSet<T> {
+    fn clone(&self) -> Self {
+        OwnIdxSet { _pd: PhantomData, bits: self.bits.clone() }
+    }
+}
+
 // pnkfelix wants to have this be `IdxSet<T>([Word]) and then pass
 // around `&mut IdxSet<T>` or `&IdxSet<T>`.
 //
-// Mmapping a `&OwnIdxSet<T>` to `&IdxSet<T>` (at least today)
+// WARNING: Mapping a `&OwnIdxSet<T>` to `&IdxSet<T>` (at least today)
 // requires a transmute relying on representation guarantees that may
 // not hold in the future.
 
@@ -65,9 +74,41 @@ impl<T: Idx> OwnIdxSet<T> {
     pub fn new_empty(universe_size: usize) -> Self {
         Self::new(0, universe_size)
     }
+}
+
+impl<T: Idx> IdxSet<T> {
+    unsafe fn from_slice(s: &[Word]) -> &Self {
+        mem::transmute(s) // (see above WARNING)
+    }
+
+    unsafe fn from_slice_mut(s: &mut [Word]) -> &mut Self {
+        mem::transmute(s) // (see above WARNING)
+    }
+}
+
+impl<T: Idx> Deref for OwnIdxSet<T> {
+    type Target = IdxSet<T>;
+    fn deref(&self) -> &IdxSet<T> {
+        unsafe { IdxSet::from_slice(&self.bits[..]) }
+    }
+}
+
+impl<T: Idx> DerefMut for OwnIdxSet<T> {
+    fn deref_mut(&mut self) -> &mut IdxSet<T> {
+        unsafe { IdxSet::from_slice_mut(&mut self.bits[..]) }
+    }
+}
+
+impl<T: Idx> IdxSet<T> {
+    pub fn to_owned(&self) -> OwnIdxSet<T> {
+        OwnIdxSet {
+            _pd: Default::default(),
+            bits: self.bits.to_owned(),
+        }
+    }
 
     /// Removes `elem` from the set `self`; returns true iff this changed `self`.
-    pub fn clear(&mut self, elem: &T) -> bool {
+    pub fn remove(&mut self, elem: &T) -> bool {
         self.bits.clear_bit(elem.idx())
     }
 
@@ -76,12 +117,38 @@ impl<T: Idx> OwnIdxSet<T> {
         self.bits.set_bit(elem.idx())
     }
 
+    pub fn range(&self, elems: &Range<T>) -> &Self {
+        let elems = elems.start.idx()..elems.end.idx();
+        unsafe { Self::from_slice(&self.bits[elems]) }
+    }
+
+    pub fn range_mut(&mut self, elems: &Range<T>) -> &mut Self {
+        let elems = elems.start.idx()..elems.end.idx();
+        unsafe { Self::from_slice_mut(&mut self.bits[elems]) }
+    }
+
     /// Returns true iff set `self` contains `elem`.
     pub fn contains(&self, elem: &T) -> bool {
         self.bits.get_bit(elem.idx())
     }
 
-    pub fn bits(&self) -> &[Word] {
+    pub fn words(&self) -> &[Word] {
         &self.bits[..]
     }
+
+    pub fn words_mut(&mut self) -> &mut [Word] {
+        &mut self.bits[..]
+    }
+
+    pub fn clone_from(&mut self, other: &IdxSet<T>) {
+        self.words_mut().clone_from_slice(other.words());
+    }
+
+    pub fn union(&mut self, other: &IdxSet<T>) -> bool {
+        bitwise(self.words_mut(), other.words(), &Union)
+    }
+
+    pub fn subtract(&mut self, other: &IdxSet<T>) -> bool {
+        bitwise(self.words_mut(), other.words(), &Subtract)
+    }
 }