From 71af40bb9dd5887ce9797e73f608b48b0f41355b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 23 May 2016 18:26:52 +0200 Subject: [PATCH] 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` --- src/librustc_borrowck/bitslice.rs | 29 ++ .../borrowck/mir/dataflow/graphviz.rs | 11 +- .../borrowck/mir/dataflow/mod.rs | 297 +++++++++--------- .../borrowck/mir/dataflow/sanity_check.rs | 23 +- .../borrowck/mir/gather_moves.rs | 17 +- src/librustc_borrowck/indexed_set.rs | 75 ++++- 6 files changed, 272 insertions(+), 180 deletions(-) diff --git a/src/librustc_borrowck/bitslice.rs b/src/librustc_borrowck/bitslice.rs index 1c01e98cf859..7a203b7f0b71 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(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 bc6ee89fa257..5a8b3ec32062 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> ", 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> ", 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 09abbb6af681..e1459ad2abea 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) { + 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) { 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, +#[derive(Debug)] +struct Bits { + bits: OwnIdxSet, } -impl Bits { - fn new(init_word: usize, num_words: usize) -> Self { - Bits { bits: vec![init_word; num_words] } +impl Clone for Bits { + fn clone(&self) -> Self { Bits { bits: self.bits.clone() } } +} + +impl Bits { + fn new(bits: OwnIdxSet) -> 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(DataflowState); +pub struct DataflowResults(DataflowState) 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 { /// 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, /// operator used to initialize, combine, and interpret bits. operator: O, } #[derive(Debug)] -pub struct AllSets { +pub struct AllSets { /// 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, /// 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, /// For each block, bits valid on entry to the block. - on_entry_sets: Bits, + on_entry_sets: Bits, } -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, + gen_set: &'a mut IdxSet, + kill_set: &'a mut IdxSet, } -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 AllSets { 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 { 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, block_idx: usize) -> &'a IdxSet { 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 { 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 { 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 { self.lookup_set_for(&self.on_entry_sets, block_idx) } } impl DataflowState { - fn each_bit(&self, ctxt: &O::Ctxt, words: &[usize], mut f: F) + fn each_bit(&self, ctxt: &O::Ctxt, words: &IdxSet, 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::() * 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 DataflowState { } } - 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) -> 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 DataflowState { } } -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); /// 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, 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, 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, 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::() * 8; let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits; + + // (now rounded up to multiple of word size) + let bits_per_block = words_per_block * usize_bits; + let num_blocks = mir.basic_blocks.len(); - let num_words = num_blocks * words_per_block; + let num_overall = num_blocks * bits_per_block; - let entry = if D::bottom_value() { usize::MAX } else {0}; + 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) + }); - let zeroes = Bits::new(0, num_words); - let on_entry = Bits::new(entry, num_words); - - 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, + 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, 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, 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) { // 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, 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, 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, _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, 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, 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, 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) { 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, 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, 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, _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) { // 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, 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, 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, _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) { + 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, 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, 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, _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(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 7c8a9a4aeb0f..6cf20def0e0c 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) - where O: BitDenotation>, O::Ctxt: HasMoveData<'tcx> + where O: BitDenotation, 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 519e9398436b..e196de46ee67 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 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); 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 c743e58aa996..c37f8e09e0ac 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 { bits: Vec, } +impl Clone for OwnIdxSet { + fn clone(&self) -> Self { + OwnIdxSet { _pd: PhantomData, bits: self.bits.clone() } + } +} + // pnkfelix wants to have this be `IdxSet([Word]) and then pass // around `&mut IdxSet` or `&IdxSet`. // -// Mmapping a `&OwnIdxSet` to `&IdxSet` (at least today) +// WARNING: Mapping a `&OwnIdxSet` to `&IdxSet` (at least today) // requires a transmute relying on representation guarantees that may // not hold in the future. @@ -65,9 +74,41 @@ impl OwnIdxSet { pub fn new_empty(universe_size: usize) -> Self { Self::new(0, universe_size) } +} + +impl IdxSet { + 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 Deref for OwnIdxSet { + type Target = IdxSet; + fn deref(&self) -> &IdxSet { + unsafe { IdxSet::from_slice(&self.bits[..]) } + } +} + +impl DerefMut for OwnIdxSet { + fn deref_mut(&mut self) -> &mut IdxSet { + unsafe { IdxSet::from_slice_mut(&mut self.bits[..]) } + } +} + +impl IdxSet { + pub fn to_owned(&self) -> OwnIdxSet { + 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 OwnIdxSet { self.bits.set_bit(elem.idx()) } + pub fn range(&self, elems: &Range) -> &Self { + let elems = elems.start.idx()..elems.end.idx(); + unsafe { Self::from_slice(&self.bits[elems]) } + } + + pub fn range_mut(&mut self, elems: &Range) -> &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) { + self.words_mut().clone_from_slice(other.words()); + } + + pub fn union(&mut self, other: &IdxSet) -> bool { + bitwise(self.words_mut(), other.words(), &Union) + } + + pub fn subtract(&mut self, other: &IdxSet) -> bool { + bitwise(self.words_mut(), other.words(), &Subtract) + } }