rustc_mir: don't pass on_entry when building transfer functions.

This commit makes `sets.on_entry` inaccessible in
`{before_,}{statement,terminator}_effect`. This field was meant to allow
implementors of `BitDenotation` to access the initial state for each
block (optionally with the effect of all previous statements applied via
`accumulates_intrablock_state`) while defining transfer functions.
However, the ability to set the initial value for the entry set of each
basic block (except for START_BLOCK) no longer exists. As a result, this
functionality is mostly useless, and when it *was* used it was used
erroneously (see #62007).

Since `on_entry` is now useless, we can also remove `BlockSets`, which
held the `gen`, `kill`, and `on_entry` bitvectors and replace it with a
`GenKill` struct. Variables of this type are called `trans` since they
represent a transfer function. `GenKill`s are stored contiguously in
`AllSets`, which reduces the number of bounds checks and may improve
cache performance: one is almost never accessed without the other.

Replacing `BlockSets` with `GenKill` allows us to define some new helper
functions which streamline dataflow iteration and the
dataflow-at-location APIs. Notably, `state_for_location` used a subtle
side-effect of the `kill`/`kill_all` setters to apply the transfer
function, and could be incorrect if a transfer function depended on
effects of previous statements in the block on `gen_set`.
This commit is contained in:
Dylan MacKenzie 2019-06-12 15:00:43 -07:00
parent d4d5d67c1c
commit c054186ec7
9 changed files with 234 additions and 301 deletions

View File

@ -4,7 +4,7 @@
use rustc::mir::{BasicBlock, Location};
use rustc_data_structures::bit_set::{BitIter, BitSet, HybridBitSet};
use crate::dataflow::{BitDenotation, BlockSets, DataflowResults};
use crate::dataflow::{BitDenotation, DataflowResults, GenKillSet};
use crate::dataflow::move_paths::{HasMoveData, MovePathIndex};
use std::iter;
@ -66,8 +66,7 @@ pub struct FlowAtLocation<'tcx, BD>
{
base_results: DataflowResults<'tcx, BD>,
curr_state: BitSet<BD::Idx>,
stmt_gen: HybridBitSet<BD::Idx>,
stmt_kill: HybridBitSet<BD::Idx>,
stmt_trans: GenKillSet<BD::Idx>,
}
impl<'tcx, BD> FlowAtLocation<'tcx, BD>
@ -89,19 +88,17 @@ pub fn each_gen_bit<F>(&self, f: F)
where
F: FnMut(BD::Idx),
{
self.stmt_gen.iter().for_each(f)
self.stmt_trans.gen_set.iter().for_each(f)
}
pub fn new(results: DataflowResults<'tcx, BD>) -> Self {
let bits_per_block = results.sets().bits_per_block();
let curr_state = BitSet::new_empty(bits_per_block);
let stmt_gen = HybridBitSet::new_empty(bits_per_block);
let stmt_kill = HybridBitSet::new_empty(bits_per_block);
let stmt_trans = GenKillSet::from_elem(HybridBitSet::new_empty(bits_per_block));
FlowAtLocation {
base_results: results,
curr_state: curr_state,
stmt_gen: stmt_gen,
stmt_kill: stmt_kill,
curr_state,
stmt_trans,
}
}
@ -127,8 +124,7 @@ pub fn with_iter_outgoing<F>(&self, f: F)
F: FnOnce(BitIter<'_, BD::Idx>),
{
let mut curr_state = self.curr_state.clone();
curr_state.union(&self.stmt_gen);
curr_state.subtract(&self.stmt_kill);
self.stmt_trans.apply(&mut curr_state);
f(curr_state.iter());
}
@ -142,68 +138,41 @@ impl<'tcx, BD> FlowsAtLocation for FlowAtLocation<'tcx, BD>
where BD: BitDenotation<'tcx>
{
fn reset_to_entry_of(&mut self, bb: BasicBlock) {
self.curr_state.overwrite(self.base_results.sets().on_entry_set_for(bb.index()));
self.curr_state.overwrite(self.base_results.sets().entry_set_for(bb.index()));
}
fn reset_to_exit_of(&mut self, bb: BasicBlock) {
self.reset_to_entry_of(bb);
self.curr_state.union(self.base_results.sets().gen_set_for(bb.index()));
self.curr_state.subtract(self.base_results.sets().kill_set_for(bb.index()));
let trans = self.base_results.sets().trans_for(bb.index());
trans.apply(&mut self.curr_state)
}
fn reconstruct_statement_effect(&mut self, loc: Location) {
self.stmt_gen.clear();
self.stmt_kill.clear();
{
let mut sets = BlockSets {
on_entry: &mut self.curr_state,
gen_set: &mut self.stmt_gen,
kill_set: &mut self.stmt_kill,
};
self.base_results
.operator()
.before_statement_effect(&mut sets, loc);
}
self.apply_local_effect(loc);
let mut sets = BlockSets {
on_entry: &mut self.curr_state,
gen_set: &mut self.stmt_gen,
kill_set: &mut self.stmt_kill,
};
self.stmt_trans.clear();
self.base_results
.operator()
.statement_effect(&mut sets, loc);
.before_statement_effect(&mut self.stmt_trans, loc);
self.stmt_trans.apply(&mut self.curr_state);
self.base_results
.operator()
.statement_effect(&mut self.stmt_trans, loc);
}
fn reconstruct_terminator_effect(&mut self, loc: Location) {
self.stmt_gen.clear();
self.stmt_kill.clear();
{
let mut sets = BlockSets {
on_entry: &mut self.curr_state,
gen_set: &mut self.stmt_gen,
kill_set: &mut self.stmt_kill,
};
self.base_results
.operator()
.before_terminator_effect(&mut sets, loc);
}
self.apply_local_effect(loc);
let mut sets = BlockSets {
on_entry: &mut self.curr_state,
gen_set: &mut self.stmt_gen,
kill_set: &mut self.stmt_kill,
};
self.stmt_trans.clear();
self.base_results
.operator()
.terminator_effect(&mut sets, loc);
.before_terminator_effect(&mut self.stmt_trans, loc);
self.stmt_trans.apply(&mut self.curr_state);
self.base_results
.operator()
.terminator_effect(&mut self.stmt_trans, loc);
}
fn apply_local_effect(&mut self, _loc: Location) {
self.curr_state.union(&self.stmt_gen);
self.curr_state.subtract(&self.stmt_kill);
self.stmt_trans.apply(&mut self.curr_state)
}
}

View File

@ -170,7 +170,7 @@ macro_rules! dump_set_for {
write!(w, "<tr>")?;
// Entry
dump_set_for!(on_entry_set_for, interpret_set);
dump_set_for!(entry_set_for, interpret_set);
// MIR statements
write!(w, "<td>")?;
@ -208,7 +208,7 @@ fn node_label_final_row<W: io::Write>(&self,
write!(w, "<tr>")?;
// Entry
let set = flow.sets.on_entry_set_for(i);
let set = flow.sets.entry_set_for(i);
write!(w, "<td>{:?}</td>", dot::escape_html(&set.to_string()))?;
// Terminator
@ -221,13 +221,10 @@ fn node_label_final_row<W: io::Write>(&self,
}
write!(w, "</td>")?;
// Gen
let set = flow.sets.gen_set_for(i);
write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", set)))?;
// Kill
let set = flow.sets.kill_set_for(i);
write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", set)))?;
// Gen/Kill
let trans = flow.sets.trans_for(i);
write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", trans.gen_set)))?;
write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", trans.kill_set)))?;
write!(w, "</tr>")?;

View File

@ -2,7 +2,7 @@
use rustc::mir::*;
use rustc::mir::visit::Visitor;
use crate::dataflow::BitDenotation;
use crate::dataflow::{BitDenotation, GenKillSet};
/// This calculates if any part of a MIR local could have previously been borrowed.
/// This means that once a local has been borrowed, its bit will be set
@ -33,39 +33,39 @@ fn bits_per_block(&self) -> usize {
self.body.local_decls.len()
}
fn start_block_effect(&self, _sets: &mut BitSet<Local>) {
fn start_block_effect(&self, _on_entry: &mut BitSet<Local>) {
// Nothing is borrowed on function entry
}
fn statement_effect(&self,
sets: &mut BlockSets<'_, Local>,
trans: &mut GenKillSet<Local>,
loc: Location) {
let stmt = &self.body[loc.block].statements[loc.statement_index];
BorrowedLocalsVisitor {
sets,
trans,
}.visit_statement(stmt, loc);
// StorageDead invalidates all borrows and raw pointers to a local
match stmt.kind {
StatementKind::StorageDead(l) => sets.kill(l),
StatementKind::StorageDead(l) => trans.kill(l),
_ => (),
}
}
fn terminator_effect(&self,
sets: &mut BlockSets<'_, Local>,
trans: &mut GenKillSet<Local>,
loc: Location) {
let terminator = self.body[loc.block].terminator();
BorrowedLocalsVisitor {
sets,
trans,
}.visit_terminator(terminator, loc);
match &terminator.kind {
// Drop terminators borrows the location
TerminatorKind::Drop { location, .. } |
TerminatorKind::DropAndReplace { location, .. } => {
if let Some(local) = find_local(location) {
sets.gen(local);
trans.gen(local);
}
}
_ => (),
@ -97,8 +97,8 @@ fn bottom_value() -> bool {
}
}
struct BorrowedLocalsVisitor<'b, 'c> {
sets: &'b mut BlockSets<'c, Local>,
struct BorrowedLocalsVisitor<'gk> {
trans: &'gk mut GenKillSet<Local>,
}
fn find_local<'tcx>(place: &Place<'tcx>) -> Option<Local> {
@ -117,13 +117,13 @@ fn find_local<'tcx>(place: &Place<'tcx>) -> Option<Local> {
})
}
impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> {
impl<'tcx> Visitor<'tcx> for BorrowedLocalsVisitor<'_> {
fn visit_rvalue(&mut self,
rvalue: &Rvalue<'tcx>,
location: Location) {
if let Rvalue::Ref(_, _, ref place) = *rvalue {
if let Some(local) = find_local(place) {
self.sets.gen(local);
self.trans.gen(local);
}
}

View File

@ -9,7 +9,7 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use crate::dataflow::{BitDenotation, BlockSets, InitialFlow};
use crate::dataflow::{BitDenotation, InitialFlow, GenKillSet};
use crate::borrow_check::nll::region_infer::RegionInferenceContext;
use crate::borrow_check::nll::ToRegionVid;
use crate::borrow_check::places_conflict;
@ -168,7 +168,7 @@ pub fn location(&self, idx: BorrowIndex) -> &Location {
/// Add all borrows to the kill set, if those borrows are out of scope at `location`.
/// That means they went out of a nonlexical scope
fn kill_loans_out_of_scope_at_location(&self,
sets: &mut BlockSets<'_, BorrowIndex>,
trans: &mut GenKillSet<BorrowIndex>,
location: Location) {
// NOTE: The state associated with a given `location`
// reflects the dataflow on entry to the statement.
@ -182,14 +182,14 @@ fn kill_loans_out_of_scope_at_location(&self,
// region, then setting that gen-bit will override any
// potential kill introduced here.
if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
sets.kill_all(indices);
trans.kill_all(indices);
}
}
/// Kill any borrows that conflict with `place`.
fn kill_borrows_on_place(
&self,
sets: &mut BlockSets<'_, BorrowIndex>,
trans: &mut GenKillSet<BorrowIndex>,
place: &Place<'tcx>
) {
debug!("kill_borrows_on_place: place={:?}", place);
@ -206,7 +206,7 @@ fn kill_borrows_on_place(
// local must conflict. This is purely an optimization so we don't have to call
// `places_conflict` for every borrow.
if let Place::Base(PlaceBase::Local(_)) = place {
sets.kill_all(other_borrows_of_local);
trans.kill_all(other_borrows_of_local);
return;
}
@ -224,7 +224,7 @@ fn kill_borrows_on_place(
places_conflict::PlaceConflictBias::NoOverlap)
});
sets.kill_all(definitely_conflicting_borrows);
trans.kill_all(definitely_conflicting_borrows);
}
}
}
@ -236,21 +236,24 @@ fn bits_per_block(&self) -> usize {
self.borrow_set.borrows.len() * 2
}
fn start_block_effect(&self, _entry_set: &mut BitSet<BorrowIndex>) {
fn start_block_effect(&self, _entry_set: &mut BitSet<Self::Idx>) {
// no borrows of code region_scopes have been taken prior to
// function execution, so this method has no effect on
// `_sets`.
// function execution, so this method has no effect.
}
fn before_statement_effect(&self,
sets: &mut BlockSets<'_, BorrowIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location) {
debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
self.kill_loans_out_of_scope_at_location(sets, location);
debug!("Borrows::before_statement_effect trans: {:?} location: {:?}",
trans, location);
self.kill_loans_out_of_scope_at_location(trans, location);
}
fn statement_effect(&self, sets: &mut BlockSets<'_, BorrowIndex>, location: Location) {
debug!("Borrows::statement_effect: sets={:?} location={:?}", sets, location);
fn statement_effect(&self,
trans: &mut GenKillSet<Self::Idx>,
location: Location) {
debug!("Borrows::statement_effect: trans={:?} location={:?}",
trans, location);
let block = &self.body.basic_blocks().get(location.block).unwrap_or_else(|| {
panic!("could not find block at location {:?}", location);
@ -264,7 +267,7 @@ fn statement_effect(&self, sets: &mut BlockSets<'_, BorrowIndex>, location: Loca
mir::StatementKind::Assign(ref lhs, ref rhs) => {
// Make sure there are no remaining borrows for variables
// that are assigned over.
self.kill_borrows_on_place(sets, lhs);
self.kill_borrows_on_place(trans, lhs);
if let mir::Rvalue::Ref(_, _, ref place) = **rhs {
if place.ignore_borrow(
@ -278,20 +281,20 @@ fn statement_effect(&self, sets: &mut BlockSets<'_, BorrowIndex>, location: Loca
panic!("could not find BorrowIndex for location {:?}", location);
});
sets.gen(*index);
trans.gen(*index);
}
}
mir::StatementKind::StorageDead(local) => {
// Make sure there are no remaining borrows for locals that
// are gone out of scope.
self.kill_borrows_on_place(sets, &Place::Base(PlaceBase::Local(local)));
self.kill_borrows_on_place(trans, &Place::Base(PlaceBase::Local(local)));
}
mir::StatementKind::InlineAsm(ref asm) => {
for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) {
if !kind.is_indirect && !kind.is_rw {
self.kill_borrows_on_place(sets, output);
self.kill_borrows_on_place(trans, output);
}
}
}
@ -307,13 +310,16 @@ fn statement_effect(&self, sets: &mut BlockSets<'_, BorrowIndex>, location: Loca
}
fn before_terminator_effect(&self,
sets: &mut BlockSets<'_, BorrowIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location) {
debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
self.kill_loans_out_of_scope_at_location(sets, location);
debug!("Borrows::before_terminator_effect: trans={:?} location={:?}",
trans, location);
self.kill_loans_out_of_scope_at_location(trans, location);
}
fn terminator_effect(&self, _: &mut BlockSets<'_, BorrowIndex>, _: Location) {}
fn terminator_effect(&self,
_: &mut GenKillSet<Self::Idx>,
_: Location) {}
fn propagate_call_return(
&self,

View File

@ -12,7 +12,7 @@
use crate::util::elaborate_drops::DropFlagState;
use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex, InitKind};
use super::{BitDenotation, BlockSets, InitialFlow};
use super::{BitDenotation, InitialFlow, GenKillSet};
use super::drop_flag_effects_for_function_entry;
use super::drop_flag_effects_for_location;
@ -226,34 +226,37 @@ fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
}
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
fn update_bits(trans: &mut GenKillSet<MovePathIndex>,
path: MovePathIndex,
state: DropFlagState)
{
match state {
DropFlagState::Absent => sets.kill(path),
DropFlagState::Present => sets.gen(path),
DropFlagState::Absent => trans.kill(path),
DropFlagState::Present => trans.gen(path),
}
}
}
impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
fn update_bits(trans: &mut GenKillSet<MovePathIndex>,
path: MovePathIndex,
state: DropFlagState)
{
match state {
DropFlagState::Absent => sets.gen(path),
DropFlagState::Present => sets.kill(path),
DropFlagState::Absent => trans.gen(path),
DropFlagState::Present => trans.kill(path),
}
}
}
impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
fn update_bits(trans: &mut GenKillSet<MovePathIndex>,
path: MovePathIndex,
state: DropFlagState)
{
match state {
DropFlagState::Absent => sets.kill(path),
DropFlagState::Present => sets.gen(path),
DropFlagState::Absent => trans.kill(path),
DropFlagState::Present => trans.gen(path),
}
}
}
@ -275,24 +278,24 @@ fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
}
fn statement_effect(&self,
sets: &mut BlockSets<'_, MovePathIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.body, self.mdpe,
location,
|path, s| Self::update_bits(sets, path, s)
|path, s| Self::update_bits(trans, path, s)
)
}
fn terminator_effect(&self,
sets: &mut BlockSets<'_, MovePathIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.body, self.mdpe,
location,
|path, s| Self::update_bits(sets, path, s)
|path, s| Self::update_bits(trans, path, s)
)
}
@ -333,24 +336,24 @@ fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
}
fn statement_effect(&self,
sets: &mut BlockSets<'_, MovePathIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.body, self.mdpe,
location,
|path, s| Self::update_bits(sets, path, s)
|path, s| Self::update_bits(trans, path, s)
)
}
fn terminator_effect(&self,
sets: &mut BlockSets<'_, MovePathIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.body, self.mdpe,
location,
|path, s| Self::update_bits(sets, path, s)
|path, s| Self::update_bits(trans, path, s)
)
}
@ -389,24 +392,24 @@ fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
}
fn statement_effect(&self,
sets: &mut BlockSets<'_, MovePathIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.body, self.mdpe,
location,
|path, s| Self::update_bits(sets, path, s)
|path, s| Self::update_bits(trans, path, s)
)
}
fn terminator_effect(&self,
sets: &mut BlockSets<'_, MovePathIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location)
{
drop_flag_effects_for_location(
self.tcx, self.body, self.mdpe,
location,
|path, s| Self::update_bits(sets, path, s)
|path, s| Self::update_bits(trans, path, s)
)
}
@ -439,7 +442,7 @@ fn start_block_effect(&self, entry_set: &mut BitSet<InitIndex>) {
}
fn statement_effect(&self,
sets: &mut BlockSets<'_, InitIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location) {
let (_, body, move_data) = (self.tcx, self.body, self.move_data());
let stmt = &body[location.block].statements[location.statement_index];
@ -449,7 +452,7 @@ fn statement_effect(&self,
debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
stmt, location, &init_loc_map[location]);
sets.gen_all(&init_loc_map[location]);
trans.gen_all(&init_loc_map[location]);
match stmt.kind {
mir::StatementKind::StorageDead(local) => {
@ -458,14 +461,14 @@ fn statement_effect(&self,
let move_path_index = rev_lookup.find_local(local);
debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
stmt, location, &init_path_map[move_path_index]);
sets.kill_all(&init_path_map[move_path_index]);
trans.kill_all(&init_path_map[move_path_index]);
}
_ => {}
}
}
fn terminator_effect(&self,
sets: &mut BlockSets<'_, InitIndex>,
trans: &mut GenKillSet<Self::Idx>,
location: Location)
{
let (body, move_data) = (self.body, self.move_data());
@ -473,7 +476,7 @@ fn terminator_effect(&self,
let init_loc_map = &move_data.init_loc_map;
debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
term, location, &init_loc_map[location]);
sets.gen_all(
trans.gen_all(
init_loc_map[location].iter().filter(|init_index| {
move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
})

View File

@ -26,24 +26,24 @@ fn bits_per_block(&self) -> usize {
self.body.local_decls.len()
}
fn start_block_effect(&self, _sets: &mut BitSet<Local>) {
fn start_block_effect(&self, _on_entry: &mut BitSet<Local>) {
// Nothing is live on function entry
}
fn statement_effect(&self,
sets: &mut BlockSets<'_, Local>,
trans: &mut GenKillSet<Local>,
loc: Location) {
let stmt = &self.body[loc.block].statements[loc.statement_index];
match stmt.kind {
StatementKind::StorageLive(l) => sets.gen(l),
StatementKind::StorageDead(l) => sets.kill(l),
StatementKind::StorageLive(l) => trans.gen(l),
StatementKind::StorageDead(l) => trans.kill(l),
_ => (),
}
}
fn terminator_effect(&self,
_sets: &mut BlockSets<'_, Local>,
_trans: &mut GenKillSet<Local>,
_loc: Location) {
// Terminators have no effect
}

View File

@ -131,7 +131,7 @@ pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(
p: P,
) -> DataflowResults<'tcx, BD>
where
BD: BitDenotation<'tcx> + InitialFlow,
BD: BitDenotation<'tcx>,
P: Fn(&BD, BD::Idx) -> DebugFormatted,
{
let flow_state = DataflowAnalysis::new(body, dead_unwinds, bd);
@ -199,42 +199,27 @@ fn propagate(&mut self) {
}
fn build_sets(&mut self) {
// First we need to build the entry-, gen- and kill-sets.
{
let sets = &mut self.flow_state.sets.for_block(mir::START_BLOCK.index());
self.flow_state.operator.start_block_effect(&mut sets.on_entry);
}
// Build the transfer function for each block.
for (bb, data) in self.body.basic_blocks().iter_enumerated() {
let &mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = data;
let mut interim_state;
let sets = &mut self.flow_state.sets.for_block(bb.index());
let track_intrablock = BD::accumulates_intrablock_state();
if track_intrablock {
debug!("swapping in mutable on_entry, initially {:?}", sets.on_entry);
interim_state = sets.on_entry.to_owned();
sets.on_entry = &mut interim_state;
}
let trans = self.flow_state.sets.trans_mut_for(bb.index());
for j_stmt in 0..statements.len() {
let location = Location { block: bb, statement_index: j_stmt };
self.flow_state.operator.before_statement_effect(sets, location);
self.flow_state.operator.statement_effect(sets, location);
if track_intrablock {
sets.apply_local_effect();
}
self.flow_state.operator.before_statement_effect(trans, location);
self.flow_state.operator.statement_effect(trans, location);
}
if terminator.is_some() {
let location = Location { block: bb, statement_index: statements.len() };
self.flow_state.operator.before_terminator_effect(sets, location);
self.flow_state.operator.terminator_effect(sets, location);
if track_intrablock {
sets.apply_local_effect();
}
self.flow_state.operator.before_terminator_effect(trans, location);
self.flow_state.operator.terminator_effect(trans, location);
}
}
// Initialize the flow state at entry to the start block.
let on_entry = self.flow_state.sets.entry_set_mut_for(mir::START_BLOCK.index());
self.flow_state.operator.start_block_effect(on_entry);
}
}
@ -247,14 +232,12 @@ fn walk_cfg(&mut self, in_out: &mut BitSet<BD::Idx>) {
WorkQueue::with_all(self.builder.body.basic_blocks().len());
let body = self.builder.body;
while let Some(bb) = dirty_queue.pop() {
let (on_entry, trans) = self.builder.flow_state.sets.get_mut(bb.index());
debug_assert!(in_out.words().len() == on_entry.words().len());
in_out.overwrite(on_entry);
trans.apply(in_out);
let bb_data = &body[bb];
{
let sets = self.builder.flow_state.sets.for_block(bb.index());
debug_assert!(in_out.words().len() == sets.on_entry.words().len());
in_out.overwrite(sets.on_entry);
in_out.union(sets.gen_set);
in_out.subtract(sets.kill_set);
}
self.builder.propagate_bits_into_graph_successors_of(
in_out, (bb, bb_data), &mut dirty_queue);
}
@ -366,33 +349,27 @@ pub fn state_for_location<'tcx, T: BitDenotation<'tcx>>(loc: Location,
result: &DataflowResults<'tcx, T>,
body: &Body<'tcx>)
-> BitSet<T::Idx> {
let mut on_entry = result.sets().on_entry_set_for(loc.block.index()).to_owned();
let mut kill_set = on_entry.to_hybrid();
let mut gen_set = kill_set.clone();
let mut trans = GenKill::from_elem(HybridBitSet::new_empty(analysis.bits_per_block()));
{
let mut sets = BlockSets {
on_entry: &mut on_entry,
kill_set: &mut kill_set,
gen_set: &mut gen_set,
};
for stmt in 0..loc.statement_index {
let mut stmt_loc = loc;
stmt_loc.statement_index = stmt;
analysis.before_statement_effect(&mut sets, stmt_loc);
analysis.statement_effect(&mut sets, stmt_loc);
}
// Apply the pre-statement effect of the statement we're evaluating.
if loc.statement_index == body[loc.block].statements.len() {
analysis.before_terminator_effect(&mut sets, loc);
} else {
analysis.before_statement_effect(&mut sets, loc);
}
for stmt in 0..loc.statement_index {
let mut stmt_loc = loc;
stmt_loc.statement_index = stmt;
analysis.before_statement_effect(&mut trans, stmt_loc);
analysis.statement_effect(&mut trans, stmt_loc);
}
gen_set.to_dense()
// Apply the pre-statement effect of the statement we're evaluating.
if loc.statement_index == body[loc.block].statements.len() {
analysis.before_terminator_effect(&mut trans, loc);
} else {
analysis.before_statement_effect(&mut trans, loc);
}
// Apply the transfer function for all preceding statements to the fixpoint
// at the start of the block.
let mut state = result.sets().entry_set_for(loc.block.index()).to_owned();
trans.apply(&mut state);
state
}
pub struct DataflowAnalysis<'a, 'tcx, O>
@ -462,36 +439,8 @@ pub(crate) fn interpret_hybrid_set<'c, P>(&self,
}
}
#[derive(Debug)]
pub struct AllSets<E: Idx> {
/// Analysis bitwidth for each block.
bits_per_block: usize,
/// For each block, bits valid on entry to the block.
on_entry_sets: Vec<BitSet<E>>,
/// For each block, bits generated by executing the statements +
/// terminator in the block -- with one caveat. In particular, for
/// *call terminators*, the effect of storing the destination is
/// not included, since that only takes effect on the **success**
/// edge (and not the unwind edge).
gen_sets: Vec<HybridBitSet<E>>,
/// For each block, bits killed by executing the statements +
/// terminator in the block -- with one caveat. In particular, for
/// *call terminators*, the effect of storing the destination is
/// not included, since that only takes effect on the **success**
/// edge (and not the unwind edge).
kill_sets: Vec<HybridBitSet<E>>,
}
/// Triple of sets associated with a given block.
///
/// Generally, one sets up `on_entry`, `gen_set`, and `kill_set` for
/// each block individually, and then runs the dataflow analysis which
/// iteratively modifies the various `on_entry` sets (but leaves the
/// other two sets unchanged, since they represent the effect of the
/// block, which should be invariant over the course of the analysis).
/// A 2-tuple representing the "gen" and "kill" bitsets during
/// dataflow analysis.
///
/// It is best to ensure that the intersection of `gen_set` and
/// `kill_set` is empty; otherwise the results of the dataflow will
@ -499,21 +448,32 @@ pub struct AllSets<E: Idx> {
/// killed during the iteration. (This is such a good idea that the
/// `fn gen` and `fn kill` methods that set their state enforce this
/// for you.)
#[derive(Debug)]
pub struct BlockSets<'a, E: Idx> {
/// Dataflow state immediately before control flow enters the given block.
pub(crate) on_entry: &'a mut BitSet<E>,
/// Bits that are set to 1 by the time we exit the given block. Hybrid
/// because it usually contains only 0 or 1 elements.
pub(crate) gen_set: &'a mut HybridBitSet<E>,
/// Bits that are set to 0 by the time we exit the given block. Hybrid
/// because it usually contains only 0 or 1 elements.
pub(crate) kill_set: &'a mut HybridBitSet<E>,
#[derive(Debug, Clone, Copy)]
pub struct GenKill<T> {
pub(crate) gen_set: T,
pub(crate) kill_set: T,
}
impl<'a, E:Idx> BlockSets<'a, E> {
type GenKillSet<T> = GenKill<HybridBitSet<T>>;
impl<T> GenKill<T> {
/// Creates a new tuple where `gen_set == kill_set == elem`.
pub(crate) fn from_elem(elem: T) -> Self
where T: Clone
{
GenKill {
gen_set: elem.clone(),
kill_set: elem,
}
}
}
impl<E:Idx> GenKillSet<E> {
pub(crate) fn clear(&mut self) {
self.gen_set.clear();
self.kill_set.clear();
}
fn gen(&mut self, e: E) {
self.gen_set.insert(e);
self.kill_set.remove(e);
@ -541,30 +501,53 @@ fn kill_all<I>(&mut self, i: I)
}
}
fn apply_local_effect(&mut self) {
self.on_entry.union(self.gen_set);
self.on_entry.subtract(self.kill_set);
/// Computes `(set gen) - kill` and assigns the result to `set`.
pub(crate) fn apply(&self, set: &mut BitSet<E>) {
set.union(&self.gen_set);
set.subtract(&self.kill_set);
}
}
#[derive(Debug)]
pub struct AllSets<E: Idx> {
/// Analysis bitwidth for each block.
bits_per_block: usize,
/// For each block, bits valid on entry to the block.
on_entry: Vec<BitSet<E>>,
/// The transfer function of each block expressed as the set of bits
/// generated and killed by executing the statements + terminator in the
/// block -- with one caveat. In particular, for *call terminators*, the
/// effect of storing the destination is not included, since that only takes
/// effect on the **success** edge (and not the unwind edge).
trans: Vec<GenKillSet<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<'_, E> {
BlockSets {
on_entry: &mut self.on_entry_sets[block_idx],
gen_set: &mut self.gen_sets[block_idx],
kill_set: &mut self.kill_sets[block_idx],
}
pub fn get_mut(&mut self, block_idx: usize) -> (&mut BitSet<E>, &mut GenKillSet<E>) {
(&mut self.on_entry[block_idx], &mut self.trans[block_idx])
}
pub fn on_entry_set_for(&self, block_idx: usize) -> &BitSet<E> {
&self.on_entry_sets[block_idx]
pub fn trans_for(&self, block_idx: usize) -> &GenKillSet<E> {
&self.trans[block_idx]
}
pub fn trans_mut_for(&mut self, block_idx: usize) -> &mut GenKillSet<E> {
&mut self.trans[block_idx]
}
pub fn entry_set_for(&self, block_idx: usize) -> &BitSet<E> {
&self.on_entry[block_idx]
}
pub fn entry_set_mut_for(&mut self, block_idx: usize) -> &mut BitSet<E> {
&mut self.on_entry[block_idx]
}
pub fn gen_set_for(&self, block_idx: usize) -> &HybridBitSet<E> {
&self.gen_sets[block_idx]
&self.trans_for(block_idx).gen_set
}
pub fn kill_set_for(&self, block_idx: usize) -> &HybridBitSet<E> {
&self.kill_sets[block_idx]
&self.trans_for(block_idx).kill_set
}
}
@ -579,35 +562,18 @@ pub trait InitialFlow {
fn bottom_value() -> bool;
}
pub trait BitDenotation<'tcx>: BitSetOperator {
/// A specific flavor of dataflow analysis.
///
/// To run a dataflow analysis, one sets up an initial state for the
/// `START_BLOCK` via `start_block_effect` and a transfer function (`trans`)
/// for each block individually. The entry set for all other basic blocks is
/// initialized to `InitialFlow::bottom_value`. The dataflow analysis then
/// iteratively modifies the various entry sets (but leaves the the transfer
/// function unchanged).
pub trait BitDenotation<'tcx>: BitSetOperator + InitialFlow {
/// Specifies what index type is used to access the bitvector.
type Idx: Idx;
/// Some analyses want to accumulate knowledge within a block when
/// analyzing its statements for building the gen/kill sets. Override
/// this method to return true in such cases.
///
/// When this returns true, the statement-effect (re)construction
/// will clone the `on_entry` state and pass along a reference via
/// `sets.on_entry` to that local clone into `statement_effect` and
/// `terminator_effect`).
///
/// When it's false, no local clone is constructed; instead a
/// reference directly into `on_entry` is passed along via
/// `sets.on_entry` instead, which represents the flow state at
/// the block's start, not necessarily the state immediately prior
/// to the statement/terminator under analysis.
///
/// In either case, the passed reference is mutable, but this is a
/// wart from using the `BlockSets` type in the API; the intention
/// is that the `statement_effect` and `terminator_effect` methods
/// mutate only the gen/kill sets.
//
// FIXME: we should consider enforcing the intention described in
// the previous paragraph by passing the three sets in separate
// parameters to encode their distinct mutabilities.
fn accumulates_intrablock_state() -> bool { false }
/// A name describing the dataflow analysis that this
/// `BitDenotation` is supporting. The name should be something
/// suitable for plugging in as part of a filename (i.e., avoid
@ -640,7 +606,7 @@ fn accumulates_intrablock_state() -> bool { false }
/// applied, in that order, before moving for the next
/// statement.
fn before_statement_effect(&self,
_sets: &mut BlockSets<'_, Self::Idx>,
_trans: &mut GenKillSet<Self::Idx>,
_location: Location) {}
/// Mutates the block-sets (the flow sets for the given
@ -654,7 +620,7 @@ fn before_statement_effect(&self,
/// `bb_data` is the sequence of statements identified by `bb` in
/// the MIR.
fn statement_effect(&self,
sets: &mut BlockSets<'_, Self::Idx>,
trans: &mut GenKillSet<Self::Idx>,
location: Location);
/// Similar to `terminator_effect`, except it applies
@ -669,7 +635,7 @@ fn statement_effect(&self,
/// applied, in that order, before moving for the next
/// terminator.
fn before_terminator_effect(&self,
_sets: &mut BlockSets<'_, Self::Idx>,
_trans: &mut GenKillSet<Self::Idx>,
_location: Location) {}
/// Mutates the block-sets (the flow sets for the given
@ -683,7 +649,7 @@ fn before_terminator_effect(&self,
/// The effects applied here cannot depend on which branch the
/// terminator took.
fn terminator_effect(&self,
sets: &mut BlockSets<'_, Self::Idx>,
trans: &mut GenKillSet<Self::Idx>,
location: Location);
/// Mutates the block-sets according to the (flow-dependent)
@ -718,17 +684,16 @@ impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation<'tcx>
{
pub fn new(body: &'a Body<'tcx>,
dead_unwinds: &'a BitSet<mir::BasicBlock>,
denotation: D) -> Self where D: InitialFlow {
denotation: D) -> Self {
let bits_per_block = denotation.bits_per_block();
let num_blocks = body.basic_blocks().len();
let on_entry_sets = if D::bottom_value() {
let on_entry = if D::bottom_value() {
vec![BitSet::new_filled(bits_per_block); num_blocks]
} else {
vec![BitSet::new_empty(bits_per_block); num_blocks]
};
let gen_sets = vec![HybridBitSet::new_empty(bits_per_block); num_blocks];
let kill_sets = gen_sets.clone();
let nop = GenKill::from_elem(HybridBitSet::new_empty(bits_per_block));
DataflowAnalysis {
body,
@ -736,9 +701,8 @@ pub fn new(body: &'a Body<'tcx>,
flow_state: DataflowState {
sets: AllSets {
bits_per_block,
on_entry_sets,
gen_sets,
kill_sets,
on_entry,
trans: vec![nop; num_blocks],
},
operator: denotation,
}
@ -836,7 +800,7 @@ fn propagate_bits_into_entry_set_for(&mut self,
in_out: &BitSet<D::Idx>,
bb: mir::BasicBlock,
dirty_queue: &mut WorkQueue<mir::BasicBlock>) {
let entry_set = &mut self.flow_state.sets.for_block(bb.index()).on_entry;
let entry_set = self.flow_state.sets.entry_set_mut_for(bb.index());
let set_changed = self.flow_state.operator.join(entry_set, &in_out);
if set_changed {
dirty_queue.insert(bb);

View File

@ -95,7 +95,7 @@ fn find_dead_unwinds<'tcx>(
};
let mut init_data = InitializationData {
live: flow_inits.sets().on_entry_set_for(bb.index()).to_owned(),
live: flow_inits.sets().entry_set_for(bb.index()).to_owned(),
dead: BitSet::new_empty(env.move_data.move_paths.len()),
};
debug!("find_dead_unwinds @ {:?}: {:?}; init_data={:?}",
@ -304,9 +304,9 @@ fn param_env(&self) -> ty::ParamEnv<'tcx> {
fn initialization_data_at(&self, loc: Location) -> InitializationData {
let mut data = InitializationData {
live: self.flow_inits.sets().on_entry_set_for(loc.block.index())
live: self.flow_inits.sets().entry_set_for(loc.block.index())
.to_owned(),
dead: self.flow_uninits.sets().on_entry_set_for(loc.block.index())
dead: self.flow_uninits.sets().entry_set_for(loc.block.index())
.to_owned(),
};
for stmt in 0..loc.statement_index {

View File

@ -18,7 +18,6 @@
};
use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
use crate::dataflow::move_paths::{HasMoveData, MoveData};
use crate::dataflow;
use crate::dataflow::has_rustc_mir_with;
@ -133,9 +132,8 @@ fn each_block<'tcx, O>(
}
};
let mut on_entry = results.0.sets.on_entry_set_for(bb.index()).to_owned();
let mut gen_set = results.0.sets.gen_set_for(bb.index()).clone();
let mut kill_set = results.0.sets.kill_set_for(bb.index()).clone();
let mut on_entry = results.0.sets.entry_set_for(bb.index()).to_owned();
let mut trans = results.0.sets.trans_for(bb.index()).clone();
// Emulate effect of all statements in the block up to (but not
// including) the borrow within `peek_arg_place`. Do *not* include
@ -143,10 +141,6 @@ fn each_block<'tcx, O>(
// of the argument at time immediate preceding Call to
// `rustc_peek`).
let mut sets = dataflow::BlockSets { on_entry: &mut on_entry,
gen_set: &mut gen_set,
kill_set: &mut kill_set };
for (j, stmt) in statements.iter().enumerate() {
debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
let (place, rvalue) = match stmt.kind {
@ -170,7 +164,7 @@ fn each_block<'tcx, O>(
// Okay, our search is over.
match move_data.rev_lookup.find(peeking_at_place) {
LookupResult::Exact(peek_mpi) => {
let bit_state = sets.on_entry.contains(peek_mpi);
let bit_state = on_entry.contains(peek_mpi);
debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
place, peeking_at_place, bit_state);
if !bit_state {
@ -197,18 +191,18 @@ fn each_block<'tcx, O>(
debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
place, lhs_mpi, stmt);
// reset GEN and KILL sets before emulating their effect.
sets.gen_set.clear();
sets.kill_set.clear();
trans.clear();
results.0.operator.before_statement_effect(
&mut sets, Location { block: bb, statement_index: j });
&mut trans,
Location { block: bb, statement_index: j });
results.0.operator.statement_effect(
&mut sets, Location { block: bb, statement_index: j });
sets.on_entry.union(sets.gen_set);
sets.on_entry.subtract(sets.kill_set);
&mut trans,
Location { block: bb, statement_index: j });
trans.apply(&mut on_entry);
}
results.0.operator.before_terminator_effect(
&mut sets,
&mut trans,
Location { block: bb, statement_index: statements.len() });
tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \