Skip to content

Commit

Permalink
rustc_mir: don't pass on_entry when building transfer functions.
Browse files Browse the repository at this point in the history
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 rust-lang#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`.
  • Loading branch information
ecstatic-morse committed Jun 22, 2019
1 parent d4d5d67 commit c054186
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 296 deletions.
77 changes: 23 additions & 54 deletions src/librustc_mir/dataflow/at_location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -66,8 +66,7 @@ where
{
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>
Expand All @@ -89,19 +88,17 @@ where
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,
}
}

Expand All @@ -127,8 +124,7 @@ where
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());
}

Expand All @@ -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);
self.stmt_trans.clear();
self.base_results
.operator()
.before_statement_effect(&mut self.stmt_trans, loc);
self.stmt_trans.apply(&mut self.curr_state);

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()
.statement_effect(&mut sets, loc);
.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);
self.stmt_trans.clear();
self.base_results
.operator()
.before_terminator_effect(&mut self.stmt_trans, loc);
self.stmt_trans.apply(&mut self.curr_state);

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()
.terminator_effect(&mut sets, loc);
.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)
}
}

Expand Down
15 changes: 6 additions & 9 deletions src/librustc_mir/dataflow/graphviz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ where MWF: MirWithFlowState<'tcx>,

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>")?;
Expand Down Expand Up @@ -208,7 +208,7 @@ where MWF: MirWithFlowState<'tcx>,
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
Expand All @@ -221,13 +221,10 @@ where MWF: MirWithFlowState<'tcx>,
}
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>")?;

Expand Down
24 changes: 12 additions & 12 deletions src/librustc_mir/dataflow/impls/borrowed_locals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pub use super::*;

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
Expand Down Expand Up @@ -33,39 +33,39 @@ impl<'a, 'tcx> BitDenotation<'tcx> for HaveBeenBorrowedLocals<'a, 'tcx> {
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);
}
}
_ => (),
Expand Down Expand Up @@ -97,8 +97,8 @@ impl<'a, 'tcx> InitialFlow for HaveBeenBorrowedLocals<'a, 'tcx> {
}
}

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> {
Expand All @@ -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);
}
}

Expand Down
50 changes: 28 additions & 22 deletions src/librustc_mir/dataflow/impls/borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_data_structures::bit_set::{BitSet, BitSetOperator};
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;
Expand Down Expand Up @@ -168,7 +168,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
/// 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.
Expand All @@ -182,14 +182,14 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
// 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);
Expand All @@ -206,7 +206,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
// 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;
}

Expand All @@ -224,7 +224,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
places_conflict::PlaceConflictBias::NoOverlap)
});

sets.kill_all(definitely_conflicting_borrows);
trans.kill_all(definitely_conflicting_borrows);
}
}
}
Expand All @@ -236,21 +236,24 @@ impl<'a, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'tcx> {
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);
Expand All @@ -264,7 +267,7 @@ impl<'a, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'tcx> {
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(
Expand All @@ -278,20 +281,20 @@ impl<'a, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'tcx> {
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);
}
}
}
Expand All @@ -307,13 +310,16 @@ impl<'a, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'tcx> {
}

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,
Expand Down
Loading

0 comments on commit c054186

Please sign in to comment.