2014-01-25 01:37:51 -06:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2013-03-15 14:24:24 -05:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* A module for propagating forward dataflow information. The analysis
|
|
|
|
* assumes that the items to be propagated can be represented as bits
|
|
|
|
* and thus uses bitvectors. Your job is simply to specify the so-called
|
|
|
|
* GEN and KILL bits for each expression.
|
|
|
|
*/
|
|
|
|
|
2013-05-17 17:28:44 -05:00
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
use middle::cfg;
|
|
|
|
use middle::cfg::CFGIndex;
|
2014-05-14 14:31:30 -05:00
|
|
|
use middle::ty;
|
2013-11-11 00:46:32 -06:00
|
|
|
use std::io;
|
2014-04-02 18:54:22 -05:00
|
|
|
use std::uint;
|
2013-03-15 14:24:24 -05:00
|
|
|
use syntax::ast;
|
2014-01-09 07:05:33 -06:00
|
|
|
use syntax::ast_util::IdRange;
|
2014-05-21 07:49:16 -05:00
|
|
|
use syntax::visit;
|
2013-03-15 14:24:24 -05:00
|
|
|
use syntax::print::{pp, pprust};
|
2014-02-28 16:34:26 -06:00
|
|
|
use util::nodemap::NodeMap;
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-07-02 10:50:18 -05:00
|
|
|
#[deriving(Show)]
|
|
|
|
pub enum EntryOrExit { Entry, Exit }
|
|
|
|
|
2013-07-02 14:47:32 -05:00
|
|
|
#[deriving(Clone)]
|
2014-04-22 07:56:37 -05:00
|
|
|
pub struct DataFlowContext<'a, 'tcx: 'a, O> {
|
|
|
|
tcx: &'a ty::ctxt<'tcx>,
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
/// a name for the analysis using this dataflow instance
|
|
|
|
analysis_name: &'static str,
|
|
|
|
|
2013-03-15 14:24:24 -05:00
|
|
|
/// the data flow operator
|
2014-03-28 12:05:27 -05:00
|
|
|
oper: O,
|
2013-03-15 14:24:24 -05:00
|
|
|
|
|
|
|
/// number of bits to propagate per id
|
2014-03-28 12:05:27 -05:00
|
|
|
bits_per_id: uint,
|
2013-03-15 14:24:24 -05:00
|
|
|
|
|
|
|
/// number of words we will use to store bits_per_id.
|
2014-01-25 01:37:51 -06:00
|
|
|
/// equal to bits_per_id/uint::BITS rounded up.
|
2014-03-28 12:05:27 -05:00
|
|
|
words_per_id: uint,
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
// mapping from node to cfg node index
|
|
|
|
// FIXME (#6298): Shouldn't this go with CFG?
|
|
|
|
nodeid_to_index: NodeMap<CFGIndex>,
|
2013-06-13 12:07:34 -05:00
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
// Bit sets per cfg node. The following three fields (`gens`, `kills`,
|
2013-03-15 14:24:24 -05:00
|
|
|
// and `on_entry`) all have the same structure. For each id in
|
|
|
|
// `id_range`, there is a range of words equal to `words_per_id`.
|
|
|
|
// So, to access the bits for any given id, you take a slice of
|
|
|
|
// the full vector (see the method `compute_id_range()`).
|
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
/// bits generated as we exit the cfg node. Updated by `add_gen()`.
|
2014-03-28 12:05:27 -05:00
|
|
|
gens: Vec<uint>,
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
/// bits killed as we exit the cfg node. Updated by `add_kill()`.
|
2014-03-28 12:05:27 -05:00
|
|
|
kills: Vec<uint>,
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
/// bits that are valid on entry to the cfg node. Updated by
|
2013-03-15 14:24:24 -05:00
|
|
|
/// `propagate()`.
|
2014-03-28 12:05:27 -05:00
|
|
|
on_entry: Vec<uint>,
|
|
|
|
}
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
pub trait BitwiseOperator {
|
|
|
|
/// Joins two predecessor bits together, typically either `|` or `&`
|
|
|
|
fn join(&self, succ: uint, pred: uint) -> uint;
|
|
|
|
}
|
|
|
|
|
2013-03-15 14:24:24 -05:00
|
|
|
/// Parameterization for the precise form of data flow that is used.
|
2014-05-21 07:49:16 -05:00
|
|
|
pub trait DataFlowOperator : BitwiseOperator {
|
2013-03-15 14:24:24 -05:00
|
|
|
/// Specifies the initial value for each bit in the `on_entry` set
|
|
|
|
fn initial_value(&self) -> bool;
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
struct PropagationContext<'a, 'b: 'a, 'tcx: 'b, O: 'a> {
|
|
|
|
dfcx: &'a mut DataFlowContext<'b, 'tcx, O>,
|
2014-08-27 20:46:52 -05:00
|
|
|
changed: bool
|
|
|
|
}
|
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
fn to_cfgidx_or_die(id: ast::NodeId, index: &NodeMap<CFGIndex>) -> CFGIndex {
|
|
|
|
let opt_cfgindex = index.find(&id).map(|&i|i);
|
|
|
|
opt_cfgindex.unwrap_or_else(|| {
|
|
|
|
fail!("nodeid_to_index does not have entry for NodeId {}", id);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
|
2014-07-02 10:50:18 -05:00
|
|
|
fn has_bitset_for_nodeid(&self, n: ast::NodeId) -> bool {
|
2014-05-21 07:49:16 -05:00
|
|
|
assert!(n != ast::DUMMY_NODE_ID);
|
2014-07-17 07:28:03 -05:00
|
|
|
self.nodeid_to_index.contains_key(&n)
|
2014-05-21 07:49:16 -05:00
|
|
|
}
|
2014-03-08 14:36:22 -06:00
|
|
|
}
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx, O:DataFlowOperator> pprust::PpAnn for DataFlowContext<'a, 'tcx, O> {
|
2014-03-16 13:58:11 -05:00
|
|
|
fn pre(&self,
|
2014-03-18 00:27:37 -05:00
|
|
|
ps: &mut pprust::State,
|
2014-03-16 13:58:11 -05:00
|
|
|
node: pprust::AnnNode) -> io::IoResult<()> {
|
|
|
|
let id = match node {
|
2014-08-11 07:01:37 -05:00
|
|
|
pprust::NodeIdent(_) | pprust::NodeName(_) => 0,
|
2014-03-16 13:58:11 -05:00
|
|
|
pprust::NodeExpr(expr) => expr.id,
|
|
|
|
pprust::NodeBlock(blk) => blk.id,
|
|
|
|
pprust::NodeItem(_) => 0,
|
|
|
|
pprust::NodePat(pat) => pat.id
|
2013-08-29 17:24:33 -05:00
|
|
|
};
|
|
|
|
|
2014-07-02 10:50:18 -05:00
|
|
|
if self.has_bitset_for_nodeid(id) {
|
2014-07-17 08:36:38 -05:00
|
|
|
assert!(self.bits_per_id > 0);
|
2014-05-21 07:49:16 -05:00
|
|
|
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
|
2014-07-17 08:36:38 -05:00
|
|
|
let (start, end) = self.compute_id_range(cfgidx);
|
2013-08-29 17:24:33 -05:00
|
|
|
let on_entry = self.on_entry.slice(start, end);
|
2014-06-21 05:39:03 -05:00
|
|
|
let entry_str = bits_to_string(on_entry);
|
2013-08-29 17:24:33 -05:00
|
|
|
|
|
|
|
let gens = self.gens.slice(start, end);
|
|
|
|
let gens_str = if gens.iter().any(|&u| u != 0) {
|
2014-06-21 05:39:03 -05:00
|
|
|
format!(" gen: {}", bits_to_string(gens))
|
2013-08-29 17:24:33 -05:00
|
|
|
} else {
|
2014-05-25 05:17:19 -05:00
|
|
|
"".to_string()
|
2013-08-29 17:24:33 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
let kills = self.kills.slice(start, end);
|
|
|
|
let kills_str = if kills.iter().any(|&u| u != 0) {
|
2014-06-21 05:39:03 -05:00
|
|
|
format!(" kill: {}", bits_to_string(kills))
|
2013-08-29 17:24:33 -05:00
|
|
|
} else {
|
2014-05-25 05:17:19 -05:00
|
|
|
"".to_string()
|
2013-08-29 17:24:33 -05:00
|
|
|
};
|
|
|
|
|
2014-05-27 22:44:58 -05:00
|
|
|
try!(ps.synth_comment(format!("id {}: {}{}{}", id, entry_str,
|
|
|
|
gens_str, kills_str)));
|
2014-02-19 12:07:49 -06:00
|
|
|
try!(pp::space(&mut ps.s));
|
2013-08-29 17:24:33 -05:00
|
|
|
}
|
2014-01-29 20:42:19 -06:00
|
|
|
Ok(())
|
2013-08-29 17:24:33 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
fn build_nodeid_to_index(decl: Option<&ast::FnDecl>,
|
|
|
|
cfg: &cfg::CFG) -> NodeMap<CFGIndex> {
|
|
|
|
let mut index = NodeMap::new();
|
|
|
|
|
|
|
|
// FIXME (#6298): Would it be better to fold formals from decl
|
|
|
|
// into cfg itself? i.e. introduce a fn-based flow-graph in
|
|
|
|
// addition to the current block-based flow-graph, rather than
|
|
|
|
// have to put traversals like this here?
|
|
|
|
match decl {
|
|
|
|
None => {}
|
|
|
|
Some(decl) => add_entries_from_fn_decl(&mut index, decl, cfg.entry)
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg.graph.each_node(|node_idx, node| {
|
|
|
|
if node.data.id != ast::DUMMY_NODE_ID {
|
|
|
|
index.insert(node.data.id, node_idx);
|
|
|
|
}
|
|
|
|
true
|
|
|
|
});
|
|
|
|
|
|
|
|
return index;
|
|
|
|
|
|
|
|
fn add_entries_from_fn_decl(index: &mut NodeMap<CFGIndex>,
|
|
|
|
decl: &ast::FnDecl,
|
|
|
|
entry: CFGIndex) {
|
|
|
|
//! add mappings from the ast nodes for the formal bindings to
|
|
|
|
//! the entry-node in the graph.
|
|
|
|
struct Formals<'a> {
|
|
|
|
entry: CFGIndex,
|
|
|
|
index: &'a mut NodeMap<CFGIndex>,
|
|
|
|
}
|
|
|
|
let mut formals = Formals { entry: entry, index: index };
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_fn_decl(&mut formals, decl);
|
2014-09-09 17:54:36 -05:00
|
|
|
impl<'a, 'v> visit::Visitor<'v> for Formals<'a> {
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_pat(&mut self, p: &ast::Pat) {
|
2014-05-21 07:49:16 -05:00
|
|
|
self.index.insert(p.id, self.entry);
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_pat(self, p)
|
2014-05-21 07:49:16 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx, O:DataFlowOperator> DataFlowContext<'a, 'tcx, O> {
|
|
|
|
pub fn new(tcx: &'a ty::ctxt<'tcx>,
|
2014-05-21 07:49:16 -05:00
|
|
|
analysis_name: &'static str,
|
|
|
|
decl: Option<&ast::FnDecl>,
|
|
|
|
cfg: &cfg::CFG,
|
2013-03-15 14:24:24 -05:00
|
|
|
oper: O,
|
2014-01-09 07:05:33 -06:00
|
|
|
id_range: IdRange,
|
2014-04-22 07:56:37 -05:00
|
|
|
bits_per_id: uint) -> DataFlowContext<'a, 'tcx, O> {
|
2014-01-25 01:37:51 -06:00
|
|
|
let words_per_id = (bits_per_id + uint::BITS - 1) / uint::BITS;
|
2014-07-17 07:28:03 -05:00
|
|
|
let num_nodes = cfg.graph.all_nodes().len();
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
debug!("DataFlowContext::new(analysis_name: {:s}, id_range={:?}, \
|
2014-07-17 07:28:03 -05:00
|
|
|
bits_per_id={:?}, words_per_id={:?}) \
|
|
|
|
num_nodes: {}",
|
|
|
|
analysis_name, id_range, bits_per_id, words_per_id,
|
|
|
|
num_nodes);
|
|
|
|
|
|
|
|
let entry = if oper.initial_value() { uint::MAX } else {0};
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-07-17 07:28:03 -05:00
|
|
|
let gens = Vec::from_elem(num_nodes * words_per_id, 0);
|
|
|
|
let kills = Vec::from_elem(num_nodes * words_per_id, 0);
|
|
|
|
let on_entry = Vec::from_elem(num_nodes * words_per_id, entry);
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
let nodeid_to_index = build_nodeid_to_index(decl, cfg);
|
|
|
|
|
2013-03-15 14:24:24 -05:00
|
|
|
DataFlowContext {
|
|
|
|
tcx: tcx,
|
2014-05-21 07:49:16 -05:00
|
|
|
analysis_name: analysis_name,
|
2013-03-15 14:24:24 -05:00
|
|
|
words_per_id: words_per_id,
|
2014-05-21 07:49:16 -05:00
|
|
|
nodeid_to_index: nodeid_to_index,
|
2013-03-15 14:24:24 -05:00
|
|
|
bits_per_id: bits_per_id,
|
|
|
|
oper: oper,
|
|
|
|
gens: gens,
|
|
|
|
kills: kills,
|
|
|
|
on_entry: on_entry
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-27 03:25:59 -05:00
|
|
|
pub fn add_gen(&mut self, id: ast::NodeId, bit: uint) {
|
2013-03-15 14:24:24 -05:00
|
|
|
//! Indicates that `id` generates `bit`
|
Ensure dataflow of a proc never looks at blocks from closed-over context.
Details: in a program like:
```
type T = proc(int) -> int; /* 4 */
pub fn outer(captured /* pat 16 */: T) -> T {
(proc(x /* pat 23 */) {
((captured /* 29 */).foo((x /* 30 */)) /* 28 */)
} /* block 27 */ /* 20 */)
} /* block 19 */ /* 12 */
```
the `captured` arg is moved from the outer fn into the inner proc (id=20).
The old dataflow analysis for flowed_move_data_moves, when looking at
the inner proc, would attempt to add a kill bit for `captured` at the
end of its scope; the problem is that it thought the end of the
`captured` arg's scope was the outer fn (id=12), even though at that
point in the analysis, the `captured` arg's scope should now be
restricted to the proc itself (id=20).
This patch fixes handling of upvars so that dataflow of a fn/proc
should never attempts to add a gen or kill bit to any NodeId outside
of the current fn/proc. It accomplishes this by adding an `LpUpvar`
variant to `borrowck::LoanPath`, so for cases like `captured` above
will carry both their original `var_id`, as before, as well as the
`NodeId` for the closure that is capturing them.
As a drive-by fix to another occurrence of a similar bug that
nikomatsakis pointed out to me earlier, this also fixes
`gather_loans::compute_kill_scope` so that it computes the kill scope
of the `captured` arg to be block 27; that is, the block for the proc
itself (id=20).
(This is an updated version that generalizes the new loan path variant
to cover all upvars, and thus renamed the variant from `LpCopiedUpvar`
to just `LpUpvar`.)
2014-06-13 10:19:29 -05:00
|
|
|
debug!("{:s} add_gen(id={:?}, bit={:?})",
|
|
|
|
self.analysis_name, id, bit);
|
|
|
|
assert!(self.nodeid_to_index.contains_key(&id));
|
2014-07-17 08:36:38 -05:00
|
|
|
assert!(self.bits_per_id > 0);
|
|
|
|
|
Ensure dataflow of a proc never looks at blocks from closed-over context.
Details: in a program like:
```
type T = proc(int) -> int; /* 4 */
pub fn outer(captured /* pat 16 */: T) -> T {
(proc(x /* pat 23 */) {
((captured /* 29 */).foo((x /* 30 */)) /* 28 */)
} /* block 27 */ /* 20 */)
} /* block 19 */ /* 12 */
```
the `captured` arg is moved from the outer fn into the inner proc (id=20).
The old dataflow analysis for flowed_move_data_moves, when looking at
the inner proc, would attempt to add a kill bit for `captured` at the
end of its scope; the problem is that it thought the end of the
`captured` arg's scope was the outer fn (id=12), even though at that
point in the analysis, the `captured` arg's scope should now be
restricted to the proc itself (id=20).
This patch fixes handling of upvars so that dataflow of a fn/proc
should never attempts to add a gen or kill bit to any NodeId outside
of the current fn/proc. It accomplishes this by adding an `LpUpvar`
variant to `borrowck::LoanPath`, so for cases like `captured` above
will carry both their original `var_id`, as before, as well as the
`NodeId` for the closure that is capturing them.
As a drive-by fix to another occurrence of a similar bug that
nikomatsakis pointed out to me earlier, this also fixes
`gather_loans::compute_kill_scope` so that it computes the kill scope
of the `captured` arg to be block 27; that is, the block for the proc
itself (id=20).
(This is an updated version that generalizes the new loan path variant
to cover all upvars, and thus renamed the variant from `LpCopiedUpvar`
to just `LpUpvar`.)
2014-06-13 10:19:29 -05:00
|
|
|
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
|
|
|
|
let (start, end) = self.compute_id_range(cfgidx);
|
2014-09-14 22:27:36 -05:00
|
|
|
let gens = self.gens.slice_mut(start, end);
|
Ensure dataflow of a proc never looks at blocks from closed-over context.
Details: in a program like:
```
type T = proc(int) -> int; /* 4 */
pub fn outer(captured /* pat 16 */: T) -> T {
(proc(x /* pat 23 */) {
((captured /* 29 */).foo((x /* 30 */)) /* 28 */)
} /* block 27 */ /* 20 */)
} /* block 19 */ /* 12 */
```
the `captured` arg is moved from the outer fn into the inner proc (id=20).
The old dataflow analysis for flowed_move_data_moves, when looking at
the inner proc, would attempt to add a kill bit for `captured` at the
end of its scope; the problem is that it thought the end of the
`captured` arg's scope was the outer fn (id=12), even though at that
point in the analysis, the `captured` arg's scope should now be
restricted to the proc itself (id=20).
This patch fixes handling of upvars so that dataflow of a fn/proc
should never attempts to add a gen or kill bit to any NodeId outside
of the current fn/proc. It accomplishes this by adding an `LpUpvar`
variant to `borrowck::LoanPath`, so for cases like `captured` above
will carry both their original `var_id`, as before, as well as the
`NodeId` for the closure that is capturing them.
As a drive-by fix to another occurrence of a similar bug that
nikomatsakis pointed out to me earlier, this also fixes
`gather_loans::compute_kill_scope` so that it computes the kill scope
of the `captured` arg to be block 27; that is, the block for the proc
itself (id=20).
(This is an updated version that generalizes the new loan path variant
to cover all upvars, and thus renamed the variant from `LpCopiedUpvar`
to just `LpUpvar`.)
2014-06-13 10:19:29 -05:00
|
|
|
set_bit(gens, bit);
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
2013-07-27 03:25:59 -05:00
|
|
|
pub fn add_kill(&mut self, id: ast::NodeId, bit: uint) {
|
2013-03-15 14:24:24 -05:00
|
|
|
//! Indicates that `id` kills `bit`
|
Ensure dataflow of a proc never looks at blocks from closed-over context.
Details: in a program like:
```
type T = proc(int) -> int; /* 4 */
pub fn outer(captured /* pat 16 */: T) -> T {
(proc(x /* pat 23 */) {
((captured /* 29 */).foo((x /* 30 */)) /* 28 */)
} /* block 27 */ /* 20 */)
} /* block 19 */ /* 12 */
```
the `captured` arg is moved from the outer fn into the inner proc (id=20).
The old dataflow analysis for flowed_move_data_moves, when looking at
the inner proc, would attempt to add a kill bit for `captured` at the
end of its scope; the problem is that it thought the end of the
`captured` arg's scope was the outer fn (id=12), even though at that
point in the analysis, the `captured` arg's scope should now be
restricted to the proc itself (id=20).
This patch fixes handling of upvars so that dataflow of a fn/proc
should never attempts to add a gen or kill bit to any NodeId outside
of the current fn/proc. It accomplishes this by adding an `LpUpvar`
variant to `borrowck::LoanPath`, so for cases like `captured` above
will carry both their original `var_id`, as before, as well as the
`NodeId` for the closure that is capturing them.
As a drive-by fix to another occurrence of a similar bug that
nikomatsakis pointed out to me earlier, this also fixes
`gather_loans::compute_kill_scope` so that it computes the kill scope
of the `captured` arg to be block 27; that is, the block for the proc
itself (id=20).
(This is an updated version that generalizes the new loan path variant
to cover all upvars, and thus renamed the variant from `LpCopiedUpvar`
to just `LpUpvar`.)
2014-06-13 10:19:29 -05:00
|
|
|
debug!("{:s} add_kill(id={:?}, bit={:?})",
|
|
|
|
self.analysis_name, id, bit);
|
|
|
|
assert!(self.nodeid_to_index.contains_key(&id));
|
2014-07-17 08:36:38 -05:00
|
|
|
assert!(self.bits_per_id > 0);
|
|
|
|
|
Ensure dataflow of a proc never looks at blocks from closed-over context.
Details: in a program like:
```
type T = proc(int) -> int; /* 4 */
pub fn outer(captured /* pat 16 */: T) -> T {
(proc(x /* pat 23 */) {
((captured /* 29 */).foo((x /* 30 */)) /* 28 */)
} /* block 27 */ /* 20 */)
} /* block 19 */ /* 12 */
```
the `captured` arg is moved from the outer fn into the inner proc (id=20).
The old dataflow analysis for flowed_move_data_moves, when looking at
the inner proc, would attempt to add a kill bit for `captured` at the
end of its scope; the problem is that it thought the end of the
`captured` arg's scope was the outer fn (id=12), even though at that
point in the analysis, the `captured` arg's scope should now be
restricted to the proc itself (id=20).
This patch fixes handling of upvars so that dataflow of a fn/proc
should never attempts to add a gen or kill bit to any NodeId outside
of the current fn/proc. It accomplishes this by adding an `LpUpvar`
variant to `borrowck::LoanPath`, so for cases like `captured` above
will carry both their original `var_id`, as before, as well as the
`NodeId` for the closure that is capturing them.
As a drive-by fix to another occurrence of a similar bug that
nikomatsakis pointed out to me earlier, this also fixes
`gather_loans::compute_kill_scope` so that it computes the kill scope
of the `captured` arg to be block 27; that is, the block for the proc
itself (id=20).
(This is an updated version that generalizes the new loan path variant
to cover all upvars, and thus renamed the variant from `LpCopiedUpvar`
to just `LpUpvar`.)
2014-06-13 10:19:29 -05:00
|
|
|
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
|
|
|
|
let (start, end) = self.compute_id_range(cfgidx);
|
2014-09-14 22:27:36 -05:00
|
|
|
let kills = self.kills.slice_mut(start, end);
|
Ensure dataflow of a proc never looks at blocks from closed-over context.
Details: in a program like:
```
type T = proc(int) -> int; /* 4 */
pub fn outer(captured /* pat 16 */: T) -> T {
(proc(x /* pat 23 */) {
((captured /* 29 */).foo((x /* 30 */)) /* 28 */)
} /* block 27 */ /* 20 */)
} /* block 19 */ /* 12 */
```
the `captured` arg is moved from the outer fn into the inner proc (id=20).
The old dataflow analysis for flowed_move_data_moves, when looking at
the inner proc, would attempt to add a kill bit for `captured` at the
end of its scope; the problem is that it thought the end of the
`captured` arg's scope was the outer fn (id=12), even though at that
point in the analysis, the `captured` arg's scope should now be
restricted to the proc itself (id=20).
This patch fixes handling of upvars so that dataflow of a fn/proc
should never attempts to add a gen or kill bit to any NodeId outside
of the current fn/proc. It accomplishes this by adding an `LpUpvar`
variant to `borrowck::LoanPath`, so for cases like `captured` above
will carry both their original `var_id`, as before, as well as the
`NodeId` for the closure that is capturing them.
As a drive-by fix to another occurrence of a similar bug that
nikomatsakis pointed out to me earlier, this also fixes
`gather_loans::compute_kill_scope` so that it computes the kill scope
of the `captured` arg to be block 27; that is, the block for the proc
itself (id=20).
(This is an updated version that generalizes the new loan path variant
to cover all upvars, and thus renamed the variant from `LpCopiedUpvar`
to just `LpUpvar`.)
2014-06-13 10:19:29 -05:00
|
|
|
set_bit(kills, bit);
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
2014-07-17 08:36:38 -05:00
|
|
|
fn apply_gen_kill(&self, cfgidx: CFGIndex, bits: &mut [uint]) {
|
2014-07-02 10:50:18 -05:00
|
|
|
//! Applies the gen and kill sets for `cfgidx` to `bits`
|
2014-05-21 07:49:16 -05:00
|
|
|
debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [before]",
|
2014-06-21 05:39:03 -05:00
|
|
|
self.analysis_name, cfgidx, mut_bits_to_string(bits));
|
2014-07-17 08:36:38 -05:00
|
|
|
assert!(self.bits_per_id > 0);
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-07-17 08:36:38 -05:00
|
|
|
let (start, end) = self.compute_id_range(cfgidx);
|
2014-07-02 10:50:18 -05:00
|
|
|
let gens = self.gens.slice(start, end);
|
|
|
|
bitwise(bits, gens, &Union);
|
|
|
|
let kills = self.kills.slice(start, end);
|
|
|
|
bitwise(bits, kills, &Subtract);
|
|
|
|
|
|
|
|
debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [after]",
|
|
|
|
self.analysis_name, cfgidx, mut_bits_to_string(bits));
|
|
|
|
}
|
|
|
|
|
2014-07-17 08:36:38 -05:00
|
|
|
fn compute_id_range(&self, cfgidx: CFGIndex) -> (uint, uint) {
|
|
|
|
let n = cfgidx.node_id();
|
2014-05-21 07:49:16 -05:00
|
|
|
let start = n * self.words_per_id;
|
2013-03-15 14:24:24 -05:00
|
|
|
let end = start + self.words_per_id;
|
2013-06-13 12:07:34 -05:00
|
|
|
|
|
|
|
assert!(start < self.gens.len());
|
|
|
|
assert!(end <= self.gens.len());
|
|
|
|
assert!(self.gens.len() == self.kills.len());
|
|
|
|
assert!(self.gens.len() == self.on_entry.len());
|
|
|
|
|
2013-03-15 14:24:24 -05:00
|
|
|
(start, end)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-17 08:36:38 -05:00
|
|
|
pub fn each_bit_on_entry(&self,
|
|
|
|
id: ast::NodeId,
|
|
|
|
f: |uint| -> bool)
|
|
|
|
-> bool {
|
2013-06-13 12:07:34 -05:00
|
|
|
//! Iterates through each bit that is set on entry to `id`.
|
|
|
|
//! Only useful after `propagate()` has been called.
|
2014-07-02 10:50:18 -05:00
|
|
|
if !self.has_bitset_for_nodeid(id) {
|
2013-06-13 12:07:34 -05:00
|
|
|
return true;
|
|
|
|
}
|
2014-05-21 07:49:16 -05:00
|
|
|
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
|
2014-07-02 10:50:18 -05:00
|
|
|
self.each_bit_for_node(Entry, cfgidx, f)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn each_bit_for_node(&self,
|
|
|
|
e: EntryOrExit,
|
|
|
|
cfgidx: CFGIndex,
|
|
|
|
f: |uint| -> bool)
|
|
|
|
-> bool {
|
|
|
|
//! Iterates through each bit that is set on entry/exit to `cfgidx`.
|
|
|
|
//! Only useful after `propagate()` has been called.
|
2014-07-17 08:36:38 -05:00
|
|
|
|
|
|
|
if self.bits_per_id == 0 {
|
|
|
|
// Skip the surprisingly common degenerate case. (Note
|
|
|
|
// compute_id_range requires self.words_per_id > 0.)
|
2014-07-02 10:50:18 -05:00
|
|
|
return true;
|
|
|
|
}
|
2014-07-17 08:36:38 -05:00
|
|
|
|
|
|
|
let (start, end) = self.compute_id_range(cfgidx);
|
2013-06-27 04:48:50 -05:00
|
|
|
let on_entry = self.on_entry.slice(start, end);
|
2014-07-02 10:50:18 -05:00
|
|
|
let temp_bits;
|
|
|
|
let slice = match e {
|
|
|
|
Entry => on_entry,
|
|
|
|
Exit => {
|
2014-07-16 15:37:28 -05:00
|
|
|
let mut t = on_entry.to_vec();
|
2014-07-17 08:36:38 -05:00
|
|
|
self.apply_gen_kill(cfgidx, t.as_mut_slice());
|
2014-07-02 10:50:18 -05:00
|
|
|
temp_bits = t;
|
|
|
|
temp_bits.as_slice()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
debug!("{:s} each_bit_for_node({}, cfgidx={}) bits={}",
|
|
|
|
self.analysis_name, e, cfgidx, bits_to_string(slice));
|
|
|
|
self.each_bit(slice, f)
|
2013-06-13 12:07:34 -05:00
|
|
|
}
|
|
|
|
|
2014-07-17 08:36:38 -05:00
|
|
|
pub fn each_gen_bit(&self, id: ast::NodeId, f: |uint| -> bool)
|
|
|
|
-> bool {
|
2013-06-13 12:07:34 -05:00
|
|
|
//! Iterates through each bit in the gen set for `id`.
|
2014-07-02 10:50:18 -05:00
|
|
|
if !self.has_bitset_for_nodeid(id) {
|
2013-06-13 12:07:34 -05:00
|
|
|
return true;
|
|
|
|
}
|
2014-07-17 08:36:38 -05:00
|
|
|
|
|
|
|
if self.bits_per_id == 0 {
|
|
|
|
// Skip the surprisingly common degenerate case. (Note
|
|
|
|
// compute_id_range requires self.words_per_id > 0.)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
|
2014-07-17 08:36:38 -05:00
|
|
|
let (start, end) = self.compute_id_range(cfgidx);
|
2013-06-27 04:48:50 -05:00
|
|
|
let gens = self.gens.slice(start, end);
|
2014-05-21 07:49:16 -05:00
|
|
|
debug!("{:s} each_gen_bit(id={:?}, gens={})",
|
2014-06-21 05:39:03 -05:00
|
|
|
self.analysis_name, id, bits_to_string(gens));
|
2013-06-13 12:07:34 -05:00
|
|
|
self.each_bit(gens, f)
|
|
|
|
}
|
|
|
|
|
2013-11-19 15:22:03 -06:00
|
|
|
fn each_bit(&self, words: &[uint], f: |uint| -> bool) -> bool {
|
2013-05-03 12:08:08 -05:00
|
|
|
//! Helper for iterating over the bits in a bit set.
|
2014-07-17 08:36:38 -05:00
|
|
|
//! Returns false on the first call to `f` that returns false;
|
|
|
|
//! if all calls to `f` return true, then returns true.
|
2013-05-03 12:08:08 -05:00
|
|
|
|
2013-08-03 11:45:23 -05:00
|
|
|
for (word_index, &word) in words.iter().enumerate() {
|
2013-05-03 12:08:08 -05:00
|
|
|
if word != 0 {
|
2014-01-25 01:37:51 -06:00
|
|
|
let base_index = word_index * uint::BITS;
|
|
|
|
for offset in range(0u, uint::BITS) {
|
2013-05-03 12:08:08 -05:00
|
|
|
let bit = 1 << offset;
|
|
|
|
if (word & bit) != 0 {
|
|
|
|
// NB: we round up the total number of bits
|
|
|
|
// that we store in any given bit set so that
|
2014-01-25 01:37:51 -06:00
|
|
|
// it is an even multiple of uint::BITS. This
|
2013-05-03 12:08:08 -05:00
|
|
|
// means that there may be some stray bits at
|
|
|
|
// the end that do not correspond to any
|
|
|
|
// actual value. So before we callback, check
|
|
|
|
// whether the bit_index is greater than the
|
|
|
|
// actual value the user specified and stop
|
|
|
|
// iterating if so.
|
|
|
|
let bit_index = base_index + offset;
|
|
|
|
if bit_index >= self.bits_per_id {
|
|
|
|
return true;
|
|
|
|
} else if !f(bit_index) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2014-05-21 07:49:16 -05:00
|
|
|
|
|
|
|
pub fn add_kills_from_flow_exits(&mut self, cfg: &cfg::CFG) {
|
|
|
|
//! Whenever you have a `break` or `continue` statement, flow
|
|
|
|
//! exits through any number of enclosing scopes on its way to
|
|
|
|
//! the new destination. This function infers the kill bits of
|
|
|
|
//! those control operators based on the kill bits associated
|
|
|
|
//! with those scopes.
|
|
|
|
//!
|
|
|
|
//! This is usually called (if it is called at all), after
|
|
|
|
//! all add_gen and add_kill calls, but before propagate.
|
|
|
|
|
|
|
|
debug!("{:s} add_kills_from_flow_exits", self.analysis_name);
|
|
|
|
if self.bits_per_id == 0 {
|
|
|
|
// Skip the surprisingly common degenerate case. (Note
|
|
|
|
// compute_id_range requires self.words_per_id > 0.)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cfg.graph.each_edge(|_edge_index, edge| {
|
|
|
|
let flow_exit = edge.source();
|
|
|
|
let (start, end) = self.compute_id_range(flow_exit);
|
2014-07-16 15:37:28 -05:00
|
|
|
let mut orig_kills = self.kills.slice(start, end).to_vec();
|
2014-05-21 07:49:16 -05:00
|
|
|
|
|
|
|
let mut changed = false;
|
|
|
|
for &node_id in edge.data.exiting_scopes.iter() {
|
|
|
|
let opt_cfg_idx = self.nodeid_to_index.find(&node_id).map(|&i|i);
|
|
|
|
match opt_cfg_idx {
|
|
|
|
Some(cfg_idx) => {
|
|
|
|
let (start, end) = self.compute_id_range(cfg_idx);
|
|
|
|
let kills = self.kills.slice(start, end);
|
|
|
|
if bitwise(orig_kills.as_mut_slice(), kills, &Union) {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
debug!("{:s} add_kills_from_flow_exits flow_exit={} \
|
|
|
|
no cfg_idx for exiting_scope={:?}",
|
|
|
|
self.analysis_name, flow_exit, node_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if changed {
|
2014-09-14 22:27:36 -05:00
|
|
|
let bits = self.kills.slice_mut(start, end);
|
2014-05-21 07:49:16 -05:00
|
|
|
debug!("{:s} add_kills_from_flow_exits flow_exit={} bits={} [before]",
|
2014-06-21 05:39:03 -05:00
|
|
|
self.analysis_name, flow_exit, mut_bits_to_string(bits));
|
2014-05-21 07:49:16 -05:00
|
|
|
bits.copy_from(orig_kills.as_slice());
|
|
|
|
debug!("{:s} add_kills_from_flow_exits flow_exit={} bits={} [after]",
|
2014-06-21 05:39:03 -05:00
|
|
|
self.analysis_name, flow_exit, mut_bits_to_string(bits));
|
2014-05-21 07:49:16 -05:00
|
|
|
}
|
|
|
|
true
|
|
|
|
});
|
|
|
|
}
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx, O:DataFlowOperator+Clone+'static> DataFlowContext<'a, 'tcx, O> {
|
|
|
|
// ^^^^^^^^^^^^^ only needed for pretty printing
|
2014-05-21 07:49:16 -05:00
|
|
|
pub fn propagate(&mut self, cfg: &cfg::CFG, blk: &ast::Block) {
|
2013-03-15 14:24:24 -05:00
|
|
|
//! Performs the data flow analysis.
|
|
|
|
|
|
|
|
if self.bits_per_id == 0 {
|
|
|
|
// Optimize the surprisingly common degenerate case.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-07-02 14:47:32 -05:00
|
|
|
{
|
2014-06-13 22:48:09 -05:00
|
|
|
let words_per_id = self.words_per_id;
|
2013-07-02 14:47:32 -05:00
|
|
|
let mut propcx = PropagationContext {
|
2014-03-05 21:07:47 -06:00
|
|
|
dfcx: &mut *self,
|
2013-07-02 14:47:32 -05:00
|
|
|
changed: true
|
|
|
|
};
|
2013-03-15 14:24:24 -05:00
|
|
|
|
2014-06-13 22:48:09 -05:00
|
|
|
let mut temp = Vec::from_elem(words_per_id, 0u);
|
2013-07-02 14:47:32 -05:00
|
|
|
while propcx.changed {
|
|
|
|
propcx.changed = false;
|
2014-04-17 17:59:07 -05:00
|
|
|
propcx.reset(temp.as_mut_slice());
|
2014-05-21 07:49:16 -05:00
|
|
|
propcx.walk_cfg(cfg, temp.as_mut_slice());
|
2013-07-02 14:47:32 -05:00
|
|
|
}
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
debug!("Dataflow result for {:s}:", self.analysis_name);
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("{}", {
|
2014-04-25 03:08:02 -05:00
|
|
|
self.pretty_print_to(box io::stderr(), blk).unwrap();
|
2013-03-15 14:24:24 -05:00
|
|
|
""
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-08-27 20:46:52 -05:00
|
|
|
fn pretty_print_to(&self, wr: Box<io::Writer+'static>,
|
2014-01-29 20:42:19 -06:00
|
|
|
blk: &ast::Block) -> io::IoResult<()> {
|
2014-02-13 23:07:09 -06:00
|
|
|
let mut ps = pprust::rust_printer_annotated(wr, self);
|
2014-03-16 13:58:11 -05:00
|
|
|
try!(ps.cbox(pprust::indent_unit));
|
|
|
|
try!(ps.ibox(0u));
|
|
|
|
try!(ps.print_block(blk));
|
|
|
|
pp::eof(&mut ps.s)
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'b, 'tcx, O:DataFlowOperator> PropagationContext<'a, 'b, 'tcx, O> {
|
2014-05-21 07:49:16 -05:00
|
|
|
fn walk_cfg(&mut self,
|
|
|
|
cfg: &cfg::CFG,
|
|
|
|
in_out: &mut [uint]) {
|
|
|
|
debug!("DataFlowContext::walk_cfg(in_out={}) {:s}",
|
2014-06-21 05:39:03 -05:00
|
|
|
bits_to_string(in_out), self.dfcx.analysis_name);
|
2014-07-17 08:36:38 -05:00
|
|
|
assert!(self.dfcx.bits_per_id > 0);
|
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
cfg.graph.each_node(|node_index, node| {
|
|
|
|
debug!("DataFlowContext::walk_cfg idx={} id={} begin in_out={}",
|
2014-06-21 05:39:03 -05:00
|
|
|
node_index, node.data.id, bits_to_string(in_out));
|
2014-05-21 07:49:16 -05:00
|
|
|
|
|
|
|
let (start, end) = self.dfcx.compute_id_range(node_index);
|
|
|
|
|
|
|
|
// Initialize local bitvector with state on-entry.
|
|
|
|
in_out.copy_from(self.dfcx.on_entry.slice(start, end));
|
|
|
|
|
|
|
|
// Compute state on-exit by applying transfer function to
|
|
|
|
// state on-entry.
|
|
|
|
self.dfcx.apply_gen_kill(node_index, in_out);
|
|
|
|
|
|
|
|
// Propagate state on-exit from node into its successors.
|
|
|
|
self.propagate_bits_into_graph_successors_of(in_out, cfg, node_index);
|
|
|
|
true // continue to next node
|
2013-11-21 17:42:55 -06:00
|
|
|
});
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn reset(&mut self, bits: &mut [uint]) {
|
2014-01-25 01:37:51 -06:00
|
|
|
let e = if self.dfcx.oper.initial_value() {uint::MAX} else {0};
|
2014-09-14 22:27:36 -05:00
|
|
|
for b in bits.iter_mut() {
|
2014-05-16 12:45:16 -05:00
|
|
|
*b = e;
|
|
|
|
}
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
fn propagate_bits_into_graph_successors_of(&mut self,
|
|
|
|
pred_bits: &[uint],
|
|
|
|
cfg: &cfg::CFG,
|
|
|
|
cfgidx: CFGIndex) {
|
|
|
|
cfg.graph.each_outgoing_edge(cfgidx, |_e_idx, edge| {
|
|
|
|
self.propagate_bits_into_entry_set_for(pred_bits, edge);
|
|
|
|
true
|
|
|
|
});
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
fn propagate_bits_into_entry_set_for(&mut self,
|
|
|
|
pred_bits: &[uint],
|
|
|
|
edge: &cfg::CFGEdge) {
|
|
|
|
let source = edge.source();
|
|
|
|
let cfgidx = edge.target();
|
|
|
|
debug!("{:s} propagate_bits_into_entry_set_for(pred_bits={}, {} to {})",
|
2014-06-21 05:39:03 -05:00
|
|
|
self.dfcx.analysis_name, bits_to_string(pred_bits), source, cfgidx);
|
2014-07-17 08:36:38 -05:00
|
|
|
assert!(self.dfcx.bits_per_id > 0);
|
|
|
|
|
2014-05-21 07:49:16 -05:00
|
|
|
let (start, end) = self.dfcx.compute_id_range(cfgidx);
|
|
|
|
let changed = {
|
|
|
|
// (scoping mutable borrow of self.dfcx.on_entry)
|
2014-09-14 22:27:36 -05:00
|
|
|
let on_entry = self.dfcx.on_entry.slice_mut(start, end);
|
2014-05-21 07:49:16 -05:00
|
|
|
bitwise(on_entry, pred_bits, &self.dfcx.oper)
|
2013-03-15 14:24:24 -05:00
|
|
|
};
|
|
|
|
if changed {
|
2014-05-21 07:49:16 -05:00
|
|
|
debug!("{:s} changed entry set for {:?} to {}",
|
|
|
|
self.dfcx.analysis_name, cfgidx,
|
2014-06-21 05:39:03 -05:00
|
|
|
bits_to_string(self.dfcx.on_entry.slice(start, end)));
|
2013-03-15 14:24:24 -05:00
|
|
|
self.changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
fn mut_bits_to_string(words: &mut [uint]) -> String {
|
|
|
|
bits_to_string(words)
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
2014-06-21 05:39:03 -05:00
|
|
|
fn bits_to_string(words: &[uint]) -> String {
|
2014-05-22 18:57:53 -05:00
|
|
|
let mut result = String::new();
|
2013-03-15 14:24:24 -05:00
|
|
|
let mut sep = '[';
|
|
|
|
|
|
|
|
// Note: this is a little endian printout of bytes.
|
|
|
|
|
2013-08-03 11:45:23 -05:00
|
|
|
for &word in words.iter() {
|
2013-03-15 14:24:24 -05:00
|
|
|
let mut v = word;
|
2014-01-25 01:37:51 -06:00
|
|
|
for _ in range(0u, uint::BYTES) {
|
2013-06-10 02:42:24 -05:00
|
|
|
result.push_char(sep);
|
2014-05-16 12:45:16 -05:00
|
|
|
result.push_str(format!("{:02x}", v & 0xFF).as_slice());
|
2013-03-15 14:24:24 -05:00
|
|
|
v >>= 8;
|
|
|
|
sep = '-';
|
|
|
|
}
|
|
|
|
}
|
2013-06-10 02:42:24 -05:00
|
|
|
result.push_char(']');
|
2014-05-09 20:45:36 -05:00
|
|
|
return result
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
2013-06-18 16:45:18 -05:00
|
|
|
#[inline]
|
2014-05-21 07:49:16 -05:00
|
|
|
fn bitwise<Op:BitwiseOperator>(out_vec: &mut [uint],
|
|
|
|
in_vec: &[uint],
|
|
|
|
op: &Op) -> bool {
|
2013-03-15 14:24:24 -05:00
|
|
|
assert_eq!(out_vec.len(), in_vec.len());
|
|
|
|
let mut changed = false;
|
2014-09-14 22:27:36 -05:00
|
|
|
for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec.iter()) {
|
2013-08-07 13:19:15 -05:00
|
|
|
let old_val = *out_elt;
|
2014-05-21 07:49:16 -05:00
|
|
|
let new_val = op.join(old_val, *in_elt);
|
2013-08-07 13:19:15 -05:00
|
|
|
*out_elt = new_val;
|
2014-02-18 06:40:25 -06:00
|
|
|
changed |= old_val != new_val;
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
2013-08-01 17:35:46 -05:00
|
|
|
changed
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn set_bit(words: &mut [uint], bit: uint) -> bool {
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("set_bit: words={} bit={}",
|
2014-06-21 05:39:03 -05:00
|
|
|
mut_bits_to_string(words), bit_str(bit));
|
2014-01-25 01:37:51 -06:00
|
|
|
let word = bit / uint::BITS;
|
|
|
|
let bit_in_word = bit % uint::BITS;
|
2013-03-15 14:24:24 -05:00
|
|
|
let bit_mask = 1 << bit_in_word;
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, word);
|
2013-03-15 14:24:24 -05:00
|
|
|
let oldv = words[word];
|
|
|
|
let newv = oldv | bit_mask;
|
|
|
|
words[word] = newv;
|
|
|
|
oldv != newv
|
|
|
|
}
|
|
|
|
|
2014-05-22 18:57:53 -05:00
|
|
|
fn bit_str(bit: uint) -> String {
|
2013-03-15 14:24:24 -05:00
|
|
|
let byte = bit >> 8;
|
2014-04-21 16:58:52 -05:00
|
|
|
let lobits = 1u << (bit & 0xFF);
|
2014-05-27 22:44:58 -05:00
|
|
|
format!("[{}:{}-{:02x}]", bit, byte, lobits)
|
2013-03-15 14:24:24 -05:00
|
|
|
}
|
2014-05-21 07:49:16 -05:00
|
|
|
|
|
|
|
struct Union;
|
|
|
|
impl BitwiseOperator for Union {
|
|
|
|
fn join(&self, a: uint, b: uint) -> uint { a | b }
|
|
|
|
}
|
|
|
|
struct Subtract;
|
|
|
|
impl BitwiseOperator for Subtract {
|
|
|
|
fn join(&self, a: uint, b: uint) -> uint { a & !b }
|
|
|
|
}
|