Skip to content

Commit 24ff327

Browse files
committed
Add fn clear_bit method on BitSlice trait for setting a bit to zero.
1 parent 5757e65 commit 24ff327

File tree

2 files changed

+111
-82
lines changed

2 files changed

+111
-82
lines changed

src/librustc_borrowck/bitslice.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,26 @@ use std::mem;
1313
/// `BitSlice` provides helper methods for treating a `[usize]`
1414
/// as a bitvector.
1515
pub trait BitSlice {
16+
fn clear_bit(&mut self, idx: usize) -> bool;
1617
fn set_bit(&mut self, idx: usize) -> bool;
1718
fn get_bit(&self, idx: usize) -> bool;
1819
}
1920

2021
impl BitSlice for [usize] {
22+
/// Clears bit at `idx` to 0; returns true iff this changed `self.`
23+
fn clear_bit(&mut self, idx: usize) -> bool {
24+
let words = self;
25+
debug!("clear_bit: words={} idx={}",
26+
bits_to_string(words, words.len() * mem::size_of::<usize>()), bit_str(idx));
27+
let BitLookup { word, bit_in_word, bit_mask } = bit_lookup(idx);
28+
debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, bit_mask);
29+
let oldv = words[word];
30+
let newv = oldv & !bit_mask;
31+
words[word] = newv;
32+
oldv != newv
33+
}
34+
35+
/// Sets bit at `idx` to 1; returns true iff this changed `self.`
2136
fn set_bit(&mut self, idx: usize) -> bool {
2237
let words = self;
2338
debug!("set_bit: words={} idx={}",
@@ -30,14 +45,22 @@ impl BitSlice for [usize] {
3045
oldv != newv
3146
}
3247

48+
/// Extracts value of bit at `idx` in `self`.
3349
fn get_bit(&self, idx: usize) -> bool {
3450
let words = self;
3551
let BitLookup { word, bit_mask, .. } = bit_lookup(idx);
3652
(words[word] & bit_mask) != 0
3753
}
3854
}
3955

40-
struct BitLookup { word: usize, bit_in_word: usize, bit_mask: usize }
56+
struct BitLookup {
57+
/// An index of the word holding the bit in original `[usize]` of query.
58+
word: usize,
59+
/// Index of the particular bit within the word holding the bit.
60+
bit_in_word: usize,
61+
/// Word with single 1-bit set corresponding to where the bit is located.
62+
bit_mask: usize,
63+
}
4164

4265
#[inline]
4366
fn bit_lookup(bit: usize) -> BitLookup {

src/librustc_borrowck/borrowck/mir/dataflow.rs

Lines changed: 87 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::mem;
1818
use std::usize;
1919

2020
use super::MirBorrowckCtxt;
21-
use super::gather_moves::{Location, MoveData, MovePathData, MovePathIndex, PathMap};
21+
use super::gather_moves::{Location, MoveData, MovePathData, MovePathIndex, MoveOutIndex, PathMap};
2222
use super::graphviz;
2323
use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
2424

@@ -35,15 +35,31 @@ impl<'b, 'a: 'b, 'tcx: 'a> Dataflow for MirBorrowckCtxt<'b, 'a, 'tcx> {
3535
}
3636
}
3737

38-
struct PropagationContext<'c, 'b: 'c, 'a: 'b, 'tcx: 'a> {
38+
struct PropagationContext<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn>
39+
where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue)
40+
{
3941
mbcx: &'c mut MirBorrowckCtxt<'b, 'a, 'tcx>,
4042
changed: bool,
43+
on_return: OnReturn
4144
}
4245

4346
impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
4447
fn propagate(&mut self) {
4548
let mut temp = vec![0; self.flow_state.sets.words_per_block];
46-
let mut propcx = PropagationContext { mbcx: &mut *self, changed: true, };
49+
let mut propcx = PropagationContext {
50+
mbcx: &mut *self,
51+
changed: true,
52+
on_return: |move_data, in_out, dest_lval| {
53+
let move_path_index = move_data.rev_lookup.find(dest_lval);
54+
on_all_children_bits(in_out,
55+
&move_data.path_map,
56+
&move_data.move_paths,
57+
move_path_index,
58+
&|in_out, mpi| {
59+
in_out.clear_bit(mpi.idx().unwrap());
60+
});
61+
},
62+
};
4763
while propcx.changed {
4864
propcx.changed = false;
4965
propcx.reset(&mut temp);
@@ -79,19 +95,22 @@ impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
7995
// Every path deinitialized by a *particular move*
8096
// has corresponding bit, "gen'ed" (i.e. set)
8197
// here, in dataflow vector
82-
let retval = sets.gen_set.set_bit(move_index.idx().unwrap());
83-
assert!(retval);
98+
zero_to_one(&mut sets.gen_set, *move_index);
8499
}
85100
match stmt.kind {
86101
repr::StatementKind::Assign(ref lvalue, _) => {
87102
// assigning into this `lvalue` kills all
88103
// MoveOuts from it, and *also* all MoveOuts
89104
// for children and associated fragment sets.
90105
let move_path_index = rev_lookup.find(lvalue);
91-
set_children_kill_bits(sets.kill_set,
92-
move_path_index,
93-
path_map,
94-
move_paths);
106+
107+
on_all_children_bits(sets.kill_set,
108+
path_map,
109+
move_paths,
110+
move_path_index,
111+
&|kill_set, mpi| {
112+
kill_set.set_bit(mpi.idx().unwrap());
113+
});
95114
}
96115
}
97116
}
@@ -100,79 +119,47 @@ impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
100119
debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
101120
terminator, loc, &loc_map[loc]);
102121
for move_index in &loc_map[loc] {
103-
let retval = sets.gen_set.set_bit(move_index.idx().unwrap());
104-
assert!(retval);
122+
zero_to_one(&mut sets.gen_set, *move_index);
105123
}
124+
}
106125

107-
// Note: while below as originally authored could be
108-
// written as an `if let`, it is more future-proof (to MIR
109-
// changes) to use an explicit `match` here.
110-
match *terminator {
111-
None => {}
112-
Some(repr::Terminator::Goto { target: _ }) => {}
113-
Some(repr::Terminator::If { cond: _, targets: _ }) => {}
114-
Some(repr::Terminator::Switch { discr: _, adt_def: _, targets: _ }) => {}
115-
Some(repr::Terminator::SwitchInt { discr: _, switch_ty: _, values: _, targets: _ }) => {}
116-
Some(repr::Terminator::Resume) => {}
117-
Some(repr::Terminator::Return) => {}
118-
Some(repr::Terminator::Drop { value: _, target: _, unwind: _ }) => {
119-
// either kind of Drop completely invalidates the
120-
// state of the referenced memory, effectively
121-
// acting like a MoveOut. Such gen-set additions
122-
// were added by the loop above over the loc_map.
123-
}
124-
Some(repr::Terminator::Call { func: _, args: _, cleanup: _,
125-
ref destination }) => {
126-
// Note: a followup commit refines this to reflect
127-
// that the destination will be initialized if the
128-
// call succeeds (thus killling any MoveOuts for
129-
// that destination).
130-
//
131-
// That is, this code just does the kills
132-
// unconditionally (which I believe this matches
133-
// the behavior of the old borrowck dataflow
134-
// analysis), but this code also is also removed
135-
// and replaced with something flow-dependent in a
136-
// followup commit.
137-
138-
if let Some((ref destination, _)) = *destination {
139-
let move_path_index = rev_lookup.find(destination);
140-
set_children_kill_bits(sets.kill_set,
141-
move_path_index,
142-
path_map,
143-
move_paths);
144-
}
145-
}
146-
}
126+
fn zero_to_one(gen_set: &mut [usize], move_index: MoveOutIndex) {
127+
let retval = gen_set.set_bit(move_index.idx().unwrap());
128+
assert!(retval);
147129
}
130+
}
131+
}
148132

149-
fn set_children_kill_bits(kill_set: &mut [usize],
150-
move_path_index: MovePathIndex,
151-
path_map: &PathMap,
152-
move_paths: &MovePathData) {
153-
assert!(move_path_index.idx().is_some());
133+
fn on_all_children_bits<Each>(set: &mut [usize],
134+
path_map: &PathMap,
135+
move_paths: &MovePathData,
136+
move_path_index: MovePathIndex,
137+
each_child: &Each)
138+
where Each: Fn(&mut [usize], MoveOutIndex)
139+
{
140+
assert!(move_path_index.idx().is_some());
154141

155-
// 1. set kill bits for all moves that directly
156-
// influence path for `move_path_index`
157-
for move_index in &path_map[move_path_index] {
158-
kill_set.set_bit(move_index.idx().unwrap());
159-
}
142+
// 1. invoke `each_child` callback for all moves that directly
143+
// influence path for `move_path_index`
144+
for move_index in &path_map[move_path_index] {
145+
each_child(set, *move_index);
146+
}
160147

161-
// 2. for each child of the path (that is named in this
162-
// function), recur.
163-
//
164-
// (Unnamed children are irrelevant to dataflow; by
165-
// definition they have no associated moves.)
166-
let mut child_index = move_paths[move_path_index].first_child;
167-
while let Some(_) = child_index.idx() {
168-
set_children_kill_bits(kill_set, child_index, path_map, move_paths);
169-
child_index = move_paths[child_index].next_sibling;
170-
}
171-
}
148+
// 2. for each child of the path (that is named in this
149+
// function), recur.
150+
//
151+
// (Unnamed children are irrelevant to dataflow; by
152+
// definition they have no associated moves.)
153+
let mut child_index = move_paths[move_path_index].first_child;
154+
while let Some(_) = child_index.idx() {
155+
on_all_children_bits(set, path_map, move_paths, child_index, each_child);
156+
child_index = move_paths[child_index].next_sibling;
172157
}
173158
}
174159

175-
impl<'c, 'b: 'c, 'a: 'b, 'tcx: 'a> PropagationContext<'c, 'b, 'a, 'tcx> {
160+
impl<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn> PropagationContext<'c, 'b, 'a, 'tcx, OnReturn>
161+
where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue)
162+
{
176163
fn reset(&mut self, bits: &mut [usize]) {
177164
let e = if self.mbcx.flow_state.operator.initial_value() {usize::MAX} else {0};
178165
for b in bits {
@@ -190,7 +177,10 @@ impl<'c, 'b: 'c, 'a: 'b, 'tcx: 'a> PropagationContext<'c, 'b, 'a, 'tcx> {
190177
bitwise(in_out, sets.gen_set, &Union);
191178
bitwise(in_out, sets.kill_set, &Subtract);
192179
}
193-
flow_state.propagate_bits_into_graph_successors_of(in_out, &mut self.changed, bb);
180+
flow_state.propagate_bits_into_graph_successors_of(in_out,
181+
&mut self.changed,
182+
bb,
183+
&self.on_return);
194184
}
195185
}
196186
}
@@ -405,10 +395,23 @@ impl<D: BitDenotation> DataflowState<D> {
405395
}
406396

407397
impl<D: BitDenotation> DataflowState<D> {
408-
fn propagate_bits_into_graph_successors_of(&mut self,
409-
in_out: &mut [usize],
410-
changed: &mut bool,
411-
bb: &repr::BasicBlockData) {
398+
/// Propagates the bits of `in_out` into all the successors of `bb`,
399+
/// using bitwise operator denoted by `self.operator`.
400+
///
401+
/// For most blocks, this is entirely uniform. However, for blocks
402+
/// that end with a call terminator, the effect of the call on the
403+
/// dataflow state may depend on whether the call returned
404+
/// successfully or unwound. To reflect this, the `on_return`
405+
/// callback mutates `in_out` when propagating `in_out` via a call
406+
/// terminator; such mutation is performed *last*, to ensure its
407+
/// side-effects do not leak elsewhere (e.g. into unwind target).
408+
fn propagate_bits_into_graph_successors_of<OnReturn>(
409+
&mut self,
410+
in_out: &mut [usize],
411+
changed: &mut bool,
412+
bb: &repr::BasicBlockData,
413+
on_return: OnReturn) where OnReturn: Fn(&D, &mut [usize], &repr::Lvalue)
414+
{
412415
let term = if let Some(ref term) = bb.terminator { term } else { return };
413416
match *term {
414417
repr::Terminator::Return |
@@ -435,15 +438,18 @@ impl<D: BitDenotation> DataflowState<D> {
435438
if let Some(ref unwind) = *cleanup {
436439
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
437440
}
438-
if let Some((_, ref destination)) = *destination {
439-
self.propagate_bits_into_entry_set_for(in_out, changed, destination);
441+
if let Some((ref dest_lval, ref dest_bb)) = *destination {
442+
// N.B.: This must be done *last*, after all other
443+
// propagation, as documented in comment above.
444+
on_return(&self.operator, in_out, dest_lval);
445+
self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb);
440446
}
441447
}
442448
}
443449
}
444450

445451
fn propagate_bits_into_entry_set_for(&mut self,
446-
in_out: &mut [usize],
452+
in_out: &[usize],
447453
changed: &mut bool,
448454
bb: &repr::BasicBlock) {
449455
let entry_set = self.sets.for_block(bb.index()).on_entry;

0 commit comments

Comments
 (0)