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:
parent
d4d5d67c1c
commit
c054186ec7
9 changed files with 234 additions and 301 deletions
|
@ -4,7 +4,7 @@
|
||||||
use rustc::mir::{BasicBlock, Location};
|
use rustc::mir::{BasicBlock, Location};
|
||||||
use rustc_data_structures::bit_set::{BitIter, BitSet, HybridBitSet};
|
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 crate::dataflow::move_paths::{HasMoveData, MovePathIndex};
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
@ -66,8 +66,7 @@ where
|
||||||
{
|
{
|
||||||
base_results: DataflowResults<'tcx, BD>,
|
base_results: DataflowResults<'tcx, BD>,
|
||||||
curr_state: BitSet<BD::Idx>,
|
curr_state: BitSet<BD::Idx>,
|
||||||
stmt_gen: HybridBitSet<BD::Idx>,
|
stmt_trans: GenKillSet<BD::Idx>,
|
||||||
stmt_kill: HybridBitSet<BD::Idx>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx, BD> FlowAtLocation<'tcx, BD>
|
impl<'tcx, BD> FlowAtLocation<'tcx, BD>
|
||||||
|
@ -89,19 +88,17 @@ where
|
||||||
where
|
where
|
||||||
F: FnMut(BD::Idx),
|
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 {
|
pub fn new(results: DataflowResults<'tcx, BD>) -> Self {
|
||||||
let bits_per_block = results.sets().bits_per_block();
|
let bits_per_block = results.sets().bits_per_block();
|
||||||
let curr_state = BitSet::new_empty(bits_per_block);
|
let curr_state = BitSet::new_empty(bits_per_block);
|
||||||
let stmt_gen = HybridBitSet::new_empty(bits_per_block);
|
let stmt_trans = GenKillSet::from_elem(HybridBitSet::new_empty(bits_per_block));
|
||||||
let stmt_kill = HybridBitSet::new_empty(bits_per_block);
|
|
||||||
FlowAtLocation {
|
FlowAtLocation {
|
||||||
base_results: results,
|
base_results: results,
|
||||||
curr_state: curr_state,
|
curr_state,
|
||||||
stmt_gen: stmt_gen,
|
stmt_trans,
|
||||||
stmt_kill: stmt_kill,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,8 +124,7 @@ where
|
||||||
F: FnOnce(BitIter<'_, BD::Idx>),
|
F: FnOnce(BitIter<'_, BD::Idx>),
|
||||||
{
|
{
|
||||||
let mut curr_state = self.curr_state.clone();
|
let mut curr_state = self.curr_state.clone();
|
||||||
curr_state.union(&self.stmt_gen);
|
self.stmt_trans.apply(&mut curr_state);
|
||||||
curr_state.subtract(&self.stmt_kill);
|
|
||||||
f(curr_state.iter());
|
f(curr_state.iter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,68 +138,41 @@ impl<'tcx, BD> FlowsAtLocation for FlowAtLocation<'tcx, BD>
|
||||||
where BD: BitDenotation<'tcx>
|
where BD: BitDenotation<'tcx>
|
||||||
{
|
{
|
||||||
fn reset_to_entry_of(&mut self, bb: BasicBlock) {
|
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) {
|
fn reset_to_exit_of(&mut self, bb: BasicBlock) {
|
||||||
self.reset_to_entry_of(bb);
|
self.reset_to_entry_of(bb);
|
||||||
self.curr_state.union(self.base_results.sets().gen_set_for(bb.index()));
|
let trans = self.base_results.sets().trans_for(bb.index());
|
||||||
self.curr_state.subtract(self.base_results.sets().kill_set_for(bb.index()));
|
trans.apply(&mut self.curr_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reconstruct_statement_effect(&mut self, loc: Location) {
|
fn reconstruct_statement_effect(&mut self, loc: Location) {
|
||||||
self.stmt_gen.clear();
|
self.stmt_trans.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.base_results
|
self.base_results
|
||||||
.operator()
|
.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) {
|
fn reconstruct_terminator_effect(&mut self, loc: Location) {
|
||||||
self.stmt_gen.clear();
|
self.stmt_trans.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.base_results
|
self.base_results
|
||||||
.operator()
|
.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) {
|
fn apply_local_effect(&mut self, _loc: Location) {
|
||||||
self.curr_state.union(&self.stmt_gen);
|
self.stmt_trans.apply(&mut self.curr_state)
|
||||||
self.curr_state.subtract(&self.stmt_kill);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ where MWF: MirWithFlowState<'tcx>,
|
||||||
|
|
||||||
write!(w, "<tr>")?;
|
write!(w, "<tr>")?;
|
||||||
// Entry
|
// Entry
|
||||||
dump_set_for!(on_entry_set_for, interpret_set);
|
dump_set_for!(entry_set_for, interpret_set);
|
||||||
|
|
||||||
// MIR statements
|
// MIR statements
|
||||||
write!(w, "<td>")?;
|
write!(w, "<td>")?;
|
||||||
|
@ -208,7 +208,7 @@ where MWF: MirWithFlowState<'tcx>,
|
||||||
write!(w, "<tr>")?;
|
write!(w, "<tr>")?;
|
||||||
|
|
||||||
// Entry
|
// 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()))?;
|
write!(w, "<td>{:?}</td>", dot::escape_html(&set.to_string()))?;
|
||||||
|
|
||||||
// Terminator
|
// Terminator
|
||||||
|
@ -221,13 +221,10 @@ where MWF: MirWithFlowState<'tcx>,
|
||||||
}
|
}
|
||||||
write!(w, "</td>")?;
|
write!(w, "</td>")?;
|
||||||
|
|
||||||
// Gen
|
// Gen/Kill
|
||||||
let set = flow.sets.gen_set_for(i);
|
let trans = flow.sets.trans_for(i);
|
||||||
write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", set)))?;
|
write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", trans.gen_set)))?;
|
||||||
|
write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", trans.kill_set)))?;
|
||||||
// Kill
|
|
||||||
let set = flow.sets.kill_set_for(i);
|
|
||||||
write!(w, "<td>{:?}</td>", dot::escape_html(&format!("{:?}", set)))?;
|
|
||||||
|
|
||||||
write!(w, "</tr>")?;
|
write!(w, "</tr>")?;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ pub use super::*;
|
||||||
|
|
||||||
use rustc::mir::*;
|
use rustc::mir::*;
|
||||||
use rustc::mir::visit::Visitor;
|
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 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
|
/// This means that once a local has been borrowed, its bit will be set
|
||||||
|
@ -33,39 +33,39 @@ impl<'a, 'tcx> BitDenotation<'tcx> for HaveBeenBorrowedLocals<'a, 'tcx> {
|
||||||
self.body.local_decls.len()
|
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
|
// Nothing is borrowed on function entry
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement_effect(&self,
|
fn statement_effect(&self,
|
||||||
sets: &mut BlockSets<'_, Local>,
|
trans: &mut GenKillSet<Local>,
|
||||||
loc: Location) {
|
loc: Location) {
|
||||||
let stmt = &self.body[loc.block].statements[loc.statement_index];
|
let stmt = &self.body[loc.block].statements[loc.statement_index];
|
||||||
|
|
||||||
BorrowedLocalsVisitor {
|
BorrowedLocalsVisitor {
|
||||||
sets,
|
trans,
|
||||||
}.visit_statement(stmt, loc);
|
}.visit_statement(stmt, loc);
|
||||||
|
|
||||||
// StorageDead invalidates all borrows and raw pointers to a local
|
// StorageDead invalidates all borrows and raw pointers to a local
|
||||||
match stmt.kind {
|
match stmt.kind {
|
||||||
StatementKind::StorageDead(l) => sets.kill(l),
|
StatementKind::StorageDead(l) => trans.kill(l),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(&self,
|
fn terminator_effect(&self,
|
||||||
sets: &mut BlockSets<'_, Local>,
|
trans: &mut GenKillSet<Local>,
|
||||||
loc: Location) {
|
loc: Location) {
|
||||||
let terminator = self.body[loc.block].terminator();
|
let terminator = self.body[loc.block].terminator();
|
||||||
BorrowedLocalsVisitor {
|
BorrowedLocalsVisitor {
|
||||||
sets,
|
trans,
|
||||||
}.visit_terminator(terminator, loc);
|
}.visit_terminator(terminator, loc);
|
||||||
match &terminator.kind {
|
match &terminator.kind {
|
||||||
// Drop terminators borrows the location
|
// Drop terminators borrows the location
|
||||||
TerminatorKind::Drop { location, .. } |
|
TerminatorKind::Drop { location, .. } |
|
||||||
TerminatorKind::DropAndReplace { location, .. } => {
|
TerminatorKind::DropAndReplace { location, .. } => {
|
||||||
if let Some(local) = find_local(location) {
|
if let Some(local) = find_local(location) {
|
||||||
sets.gen(local);
|
trans.gen(local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -97,8 +97,8 @@ impl<'a, 'tcx> InitialFlow for HaveBeenBorrowedLocals<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BorrowedLocalsVisitor<'b, 'c> {
|
struct BorrowedLocalsVisitor<'gk> {
|
||||||
sets: &'b mut BlockSets<'c, Local>,
|
trans: &'gk mut GenKillSet<Local>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_local<'tcx>(place: &Place<'tcx>) -> Option<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,
|
fn visit_rvalue(&mut self,
|
||||||
rvalue: &Rvalue<'tcx>,
|
rvalue: &Rvalue<'tcx>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
if let Rvalue::Ref(_, _, ref place) = *rvalue {
|
if let Rvalue::Ref(_, _, ref place) = *rvalue {
|
||||||
if let Some(local) = find_local(place) {
|
if let Some(local) = find_local(place) {
|
||||||
self.sets.gen(local);
|
self.trans.gen(local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use rustc_data_structures::bit_set::{BitSet, BitSetOperator};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
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::region_infer::RegionInferenceContext;
|
||||||
use crate::borrow_check::nll::ToRegionVid;
|
use crate::borrow_check::nll::ToRegionVid;
|
||||||
use crate::borrow_check::places_conflict;
|
use crate::borrow_check::places_conflict;
|
||||||
|
@ -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`.
|
/// 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
|
/// That means they went out of a nonlexical scope
|
||||||
fn kill_loans_out_of_scope_at_location(&self,
|
fn kill_loans_out_of_scope_at_location(&self,
|
||||||
sets: &mut BlockSets<'_, BorrowIndex>,
|
trans: &mut GenKillSet<BorrowIndex>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
// NOTE: The state associated with a given `location`
|
// NOTE: The state associated with a given `location`
|
||||||
// reflects the dataflow on entry to the statement.
|
// reflects the dataflow on entry to the statement.
|
||||||
|
@ -182,14 +182,14 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
||||||
// region, then setting that gen-bit will override any
|
// region, then setting that gen-bit will override any
|
||||||
// potential kill introduced here.
|
// potential kill introduced here.
|
||||||
if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
|
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`.
|
/// Kill any borrows that conflict with `place`.
|
||||||
fn kill_borrows_on_place(
|
fn kill_borrows_on_place(
|
||||||
&self,
|
&self,
|
||||||
sets: &mut BlockSets<'_, BorrowIndex>,
|
trans: &mut GenKillSet<BorrowIndex>,
|
||||||
place: &Place<'tcx>
|
place: &Place<'tcx>
|
||||||
) {
|
) {
|
||||||
debug!("kill_borrows_on_place: place={:?}", place);
|
debug!("kill_borrows_on_place: place={:?}", place);
|
||||||
|
@ -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
|
// local must conflict. This is purely an optimization so we don't have to call
|
||||||
// `places_conflict` for every borrow.
|
// `places_conflict` for every borrow.
|
||||||
if let Place::Base(PlaceBase::Local(_)) = place {
|
if let Place::Base(PlaceBase::Local(_)) = place {
|
||||||
sets.kill_all(other_borrows_of_local);
|
trans.kill_all(other_borrows_of_local);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
|
||||||
places_conflict::PlaceConflictBias::NoOverlap)
|
places_conflict::PlaceConflictBias::NoOverlap)
|
||||||
});
|
});
|
||||||
|
|
||||||
sets.kill_all(definitely_conflicting_borrows);
|
trans.kill_all(definitely_conflicting_borrows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,21 +236,24 @@ impl<'a, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'tcx> {
|
||||||
self.borrow_set.borrows.len() * 2
|
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
|
// no borrows of code region_scopes have been taken prior to
|
||||||
// function execution, so this method has no effect on
|
// function execution, so this method has no effect.
|
||||||
// `_sets`.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn before_statement_effect(&self,
|
fn before_statement_effect(&self,
|
||||||
sets: &mut BlockSets<'_, BorrowIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
|
debug!("Borrows::before_statement_effect trans: {:?} location: {:?}",
|
||||||
self.kill_loans_out_of_scope_at_location(sets, location);
|
trans, location);
|
||||||
|
self.kill_loans_out_of_scope_at_location(trans, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement_effect(&self, sets: &mut BlockSets<'_, BorrowIndex>, location: Location) {
|
fn statement_effect(&self,
|
||||||
debug!("Borrows::statement_effect: sets={:?} location={:?}", sets, location);
|
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(|| {
|
let block = &self.body.basic_blocks().get(location.block).unwrap_or_else(|| {
|
||||||
panic!("could not find block at location {:?}", location);
|
panic!("could not find block at location {:?}", location);
|
||||||
|
@ -264,7 +267,7 @@ impl<'a, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'tcx> {
|
||||||
mir::StatementKind::Assign(ref lhs, ref rhs) => {
|
mir::StatementKind::Assign(ref lhs, ref rhs) => {
|
||||||
// Make sure there are no remaining borrows for variables
|
// Make sure there are no remaining borrows for variables
|
||||||
// that are assigned over.
|
// 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 let mir::Rvalue::Ref(_, _, ref place) = **rhs {
|
||||||
if place.ignore_borrow(
|
if place.ignore_borrow(
|
||||||
|
@ -278,20 +281,20 @@ impl<'a, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'tcx> {
|
||||||
panic!("could not find BorrowIndex for location {:?}", location);
|
panic!("could not find BorrowIndex for location {:?}", location);
|
||||||
});
|
});
|
||||||
|
|
||||||
sets.gen(*index);
|
trans.gen(*index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mir::StatementKind::StorageDead(local) => {
|
mir::StatementKind::StorageDead(local) => {
|
||||||
// Make sure there are no remaining borrows for locals that
|
// Make sure there are no remaining borrows for locals that
|
||||||
// are gone out of scope.
|
// 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) => {
|
mir::StatementKind::InlineAsm(ref asm) => {
|
||||||
for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) {
|
for (output, kind) in asm.outputs.iter().zip(&asm.asm.outputs) {
|
||||||
if !kind.is_indirect && !kind.is_rw {
|
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 @@ impl<'a, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn before_terminator_effect(&self,
|
fn before_terminator_effect(&self,
|
||||||
sets: &mut BlockSets<'_, BorrowIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
|
debug!("Borrows::before_terminator_effect: trans={:?} location={:?}",
|
||||||
self.kill_loans_out_of_scope_at_location(sets, 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(
|
fn propagate_call_return(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -12,7 +12,7 @@ use super::MoveDataParamEnv;
|
||||||
use crate::util::elaborate_drops::DropFlagState;
|
use crate::util::elaborate_drops::DropFlagState;
|
||||||
|
|
||||||
use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex, InitKind};
|
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_function_entry;
|
||||||
use super::drop_flag_effects_for_location;
|
use super::drop_flag_effects_for_location;
|
||||||
|
@ -226,34 +226,37 @@ impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
|
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)
|
state: DropFlagState)
|
||||||
{
|
{
|
||||||
match state {
|
match state {
|
||||||
DropFlagState::Absent => sets.kill(path),
|
DropFlagState::Absent => trans.kill(path),
|
||||||
DropFlagState::Present => sets.gen(path),
|
DropFlagState::Present => trans.gen(path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
|
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)
|
state: DropFlagState)
|
||||||
{
|
{
|
||||||
match state {
|
match state {
|
||||||
DropFlagState::Absent => sets.gen(path),
|
DropFlagState::Absent => trans.gen(path),
|
||||||
DropFlagState::Present => sets.kill(path),
|
DropFlagState::Present => trans.kill(path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
|
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)
|
state: DropFlagState)
|
||||||
{
|
{
|
||||||
match state {
|
match state {
|
||||||
DropFlagState::Absent => sets.kill(path),
|
DropFlagState::Absent => trans.kill(path),
|
||||||
DropFlagState::Present => sets.gen(path),
|
DropFlagState::Present => trans.gen(path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,24 +278,24 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement_effect(&self,
|
fn statement_effect(&self,
|
||||||
sets: &mut BlockSets<'_, MovePathIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location)
|
location: Location)
|
||||||
{
|
{
|
||||||
drop_flag_effects_for_location(
|
drop_flag_effects_for_location(
|
||||||
self.tcx, self.body, self.mdpe,
|
self.tcx, self.body, self.mdpe,
|
||||||
location,
|
location,
|
||||||
|path, s| Self::update_bits(sets, path, s)
|
|path, s| Self::update_bits(trans, path, s)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(&self,
|
fn terminator_effect(&self,
|
||||||
sets: &mut BlockSets<'_, MovePathIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location)
|
location: Location)
|
||||||
{
|
{
|
||||||
drop_flag_effects_for_location(
|
drop_flag_effects_for_location(
|
||||||
self.tcx, self.body, self.mdpe,
|
self.tcx, self.body, self.mdpe,
|
||||||
location,
|
location,
|
||||||
|path, s| Self::update_bits(sets, path, s)
|
|path, s| Self::update_bits(trans, path, s)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,24 +336,24 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement_effect(&self,
|
fn statement_effect(&self,
|
||||||
sets: &mut BlockSets<'_, MovePathIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location)
|
location: Location)
|
||||||
{
|
{
|
||||||
drop_flag_effects_for_location(
|
drop_flag_effects_for_location(
|
||||||
self.tcx, self.body, self.mdpe,
|
self.tcx, self.body, self.mdpe,
|
||||||
location,
|
location,
|
||||||
|path, s| Self::update_bits(sets, path, s)
|
|path, s| Self::update_bits(trans, path, s)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(&self,
|
fn terminator_effect(&self,
|
||||||
sets: &mut BlockSets<'_, MovePathIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location)
|
location: Location)
|
||||||
{
|
{
|
||||||
drop_flag_effects_for_location(
|
drop_flag_effects_for_location(
|
||||||
self.tcx, self.body, self.mdpe,
|
self.tcx, self.body, self.mdpe,
|
||||||
location,
|
location,
|
||||||
|path, s| Self::update_bits(sets, path, s)
|
|path, s| Self::update_bits(trans, path, s)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,24 +392,24 @@ impl<'a, 'tcx> BitDenotation<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement_effect(&self,
|
fn statement_effect(&self,
|
||||||
sets: &mut BlockSets<'_, MovePathIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location)
|
location: Location)
|
||||||
{
|
{
|
||||||
drop_flag_effects_for_location(
|
drop_flag_effects_for_location(
|
||||||
self.tcx, self.body, self.mdpe,
|
self.tcx, self.body, self.mdpe,
|
||||||
location,
|
location,
|
||||||
|path, s| Self::update_bits(sets, path, s)
|
|path, s| Self::update_bits(trans, path, s)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(&self,
|
fn terminator_effect(&self,
|
||||||
sets: &mut BlockSets<'_, MovePathIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location)
|
location: Location)
|
||||||
{
|
{
|
||||||
drop_flag_effects_for_location(
|
drop_flag_effects_for_location(
|
||||||
self.tcx, self.body, self.mdpe,
|
self.tcx, self.body, self.mdpe,
|
||||||
location,
|
location,
|
||||||
|path, s| Self::update_bits(sets, path, s)
|
|path, s| Self::update_bits(trans, path, s)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,7 +442,7 @@ impl<'a, 'tcx> BitDenotation<'tcx> for EverInitializedPlaces<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement_effect(&self,
|
fn statement_effect(&self,
|
||||||
sets: &mut BlockSets<'_, InitIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
let (_, body, move_data) = (self.tcx, self.body, self.move_data());
|
let (_, body, move_data) = (self.tcx, self.body, self.move_data());
|
||||||
let stmt = &body[location.block].statements[location.statement_index];
|
let stmt = &body[location.block].statements[location.statement_index];
|
||||||
|
@ -449,7 +452,7 @@ impl<'a, 'tcx> BitDenotation<'tcx> for EverInitializedPlaces<'a, 'tcx> {
|
||||||
|
|
||||||
debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
|
debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
|
||||||
stmt, location, &init_loc_map[location]);
|
stmt, location, &init_loc_map[location]);
|
||||||
sets.gen_all(&init_loc_map[location]);
|
trans.gen_all(&init_loc_map[location]);
|
||||||
|
|
||||||
match stmt.kind {
|
match stmt.kind {
|
||||||
mir::StatementKind::StorageDead(local) => {
|
mir::StatementKind::StorageDead(local) => {
|
||||||
|
@ -458,14 +461,14 @@ impl<'a, 'tcx> BitDenotation<'tcx> for EverInitializedPlaces<'a, 'tcx> {
|
||||||
let move_path_index = rev_lookup.find_local(local);
|
let move_path_index = rev_lookup.find_local(local);
|
||||||
debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
|
debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
|
||||||
stmt, location, &init_path_map[move_path_index]);
|
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,
|
fn terminator_effect(&self,
|
||||||
sets: &mut BlockSets<'_, InitIndex>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location)
|
location: Location)
|
||||||
{
|
{
|
||||||
let (body, move_data) = (self.body, self.move_data());
|
let (body, move_data) = (self.body, self.move_data());
|
||||||
|
@ -473,7 +476,7 @@ impl<'a, 'tcx> BitDenotation<'tcx> for EverInitializedPlaces<'a, 'tcx> {
|
||||||
let init_loc_map = &move_data.init_loc_map;
|
let init_loc_map = &move_data.init_loc_map;
|
||||||
debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
|
debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
|
||||||
term, location, &init_loc_map[location]);
|
term, location, &init_loc_map[location]);
|
||||||
sets.gen_all(
|
trans.gen_all(
|
||||||
init_loc_map[location].iter().filter(|init_index| {
|
init_loc_map[location].iter().filter(|init_index| {
|
||||||
move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
|
move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,24 +26,24 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> {
|
||||||
self.body.local_decls.len()
|
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
|
// Nothing is live on function entry
|
||||||
}
|
}
|
||||||
|
|
||||||
fn statement_effect(&self,
|
fn statement_effect(&self,
|
||||||
sets: &mut BlockSets<'_, Local>,
|
trans: &mut GenKillSet<Local>,
|
||||||
loc: Location) {
|
loc: Location) {
|
||||||
let stmt = &self.body[loc.block].statements[loc.statement_index];
|
let stmt = &self.body[loc.block].statements[loc.statement_index];
|
||||||
|
|
||||||
match stmt.kind {
|
match stmt.kind {
|
||||||
StatementKind::StorageLive(l) => sets.gen(l),
|
StatementKind::StorageLive(l) => trans.gen(l),
|
||||||
StatementKind::StorageDead(l) => sets.kill(l),
|
StatementKind::StorageDead(l) => trans.kill(l),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminator_effect(&self,
|
fn terminator_effect(&self,
|
||||||
_sets: &mut BlockSets<'_, Local>,
|
_trans: &mut GenKillSet<Local>,
|
||||||
_loc: Location) {
|
_loc: Location) {
|
||||||
// Terminators have no effect
|
// Terminators have no effect
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(
|
||||||
p: P,
|
p: P,
|
||||||
) -> DataflowResults<'tcx, BD>
|
) -> DataflowResults<'tcx, BD>
|
||||||
where
|
where
|
||||||
BD: BitDenotation<'tcx> + InitialFlow,
|
BD: BitDenotation<'tcx>,
|
||||||
P: Fn(&BD, BD::Idx) -> DebugFormatted,
|
P: Fn(&BD, BD::Idx) -> DebugFormatted,
|
||||||
{
|
{
|
||||||
let flow_state = DataflowAnalysis::new(body, dead_unwinds, bd);
|
let flow_state = DataflowAnalysis::new(body, dead_unwinds, bd);
|
||||||
|
@ -199,42 +199,27 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_sets(&mut self) {
|
fn build_sets(&mut self) {
|
||||||
// First we need to build the entry-, gen- and kill-sets.
|
// Build the transfer function for each block.
|
||||||
|
|
||||||
{
|
|
||||||
let sets = &mut self.flow_state.sets.for_block(mir::START_BLOCK.index());
|
|
||||||
self.flow_state.operator.start_block_effect(&mut sets.on_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (bb, data) in self.body.basic_blocks().iter_enumerated() {
|
for (bb, data) in self.body.basic_blocks().iter_enumerated() {
|
||||||
let &mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = data;
|
let &mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = data;
|
||||||
|
|
||||||
let mut interim_state;
|
let trans = self.flow_state.sets.trans_mut_for(bb.index());
|
||||||
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;
|
|
||||||
}
|
|
||||||
for j_stmt in 0..statements.len() {
|
for j_stmt in 0..statements.len() {
|
||||||
let location = Location { block: bb, statement_index: j_stmt };
|
let location = Location { block: bb, statement_index: j_stmt };
|
||||||
self.flow_state.operator.before_statement_effect(sets, location);
|
self.flow_state.operator.before_statement_effect(trans, location);
|
||||||
self.flow_state.operator.statement_effect(sets, location);
|
self.flow_state.operator.statement_effect(trans, location);
|
||||||
if track_intrablock {
|
|
||||||
sets.apply_local_effect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if terminator.is_some() {
|
if terminator.is_some() {
|
||||||
let location = Location { block: bb, statement_index: statements.len() };
|
let location = Location { block: bb, statement_index: statements.len() };
|
||||||
self.flow_state.operator.before_terminator_effect(sets, location);
|
self.flow_state.operator.before_terminator_effect(trans, location);
|
||||||
self.flow_state.operator.terminator_effect(sets, location);
|
self.flow_state.operator.terminator_effect(trans, location);
|
||||||
if track_intrablock {
|
|
||||||
sets.apply_local_effect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 @@ where
|
||||||
WorkQueue::with_all(self.builder.body.basic_blocks().len());
|
WorkQueue::with_all(self.builder.body.basic_blocks().len());
|
||||||
let body = self.builder.body;
|
let body = self.builder.body;
|
||||||
while let Some(bb) = dirty_queue.pop() {
|
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 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(
|
self.builder.propagate_bits_into_graph_successors_of(
|
||||||
in_out, (bb, bb_data), &mut dirty_queue);
|
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>,
|
result: &DataflowResults<'tcx, T>,
|
||||||
body: &Body<'tcx>)
|
body: &Body<'tcx>)
|
||||||
-> BitSet<T::Idx> {
|
-> BitSet<T::Idx> {
|
||||||
let mut on_entry = result.sets().on_entry_set_for(loc.block.index()).to_owned();
|
let mut trans = GenKill::from_elem(HybridBitSet::new_empty(analysis.bits_per_block()));
|
||||||
let mut kill_set = on_entry.to_hybrid();
|
|
||||||
let mut gen_set = kill_set.clone();
|
|
||||||
|
|
||||||
{
|
for stmt in 0..loc.statement_index {
|
||||||
let mut sets = BlockSets {
|
let mut stmt_loc = loc;
|
||||||
on_entry: &mut on_entry,
|
stmt_loc.statement_index = stmt;
|
||||||
kill_set: &mut kill_set,
|
analysis.before_statement_effect(&mut trans, stmt_loc);
|
||||||
gen_set: &mut gen_set,
|
analysis.statement_effect(&mut trans, stmt_loc);
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
pub struct DataflowAnalysis<'a, 'tcx, O>
|
||||||
|
@ -462,36 +439,8 @@ impl<'tcx, O: BitDenotation<'tcx>> DataflowState<'tcx, O> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// A 2-tuple representing the "gen" and "kill" bitsets during
|
||||||
pub struct AllSets<E: Idx> {
|
/// dataflow analysis.
|
||||||
/// 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).
|
|
||||||
///
|
///
|
||||||
/// It is best to ensure that the intersection of `gen_set` and
|
/// It is best to ensure that the intersection of `gen_set` and
|
||||||
/// `kill_set` is empty; otherwise the results of the dataflow will
|
/// `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
|
/// killed during the iteration. (This is such a good idea that the
|
||||||
/// `fn gen` and `fn kill` methods that set their state enforce this
|
/// `fn gen` and `fn kill` methods that set their state enforce this
|
||||||
/// for you.)
|
/// for you.)
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct BlockSets<'a, E: Idx> {
|
pub struct GenKill<T> {
|
||||||
/// Dataflow state immediately before control flow enters the given block.
|
pub(crate) gen_set: T,
|
||||||
pub(crate) on_entry: &'a mut BitSet<E>,
|
pub(crate) kill_set: T,
|
||||||
|
|
||||||
/// 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>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
fn gen(&mut self, e: E) {
|
||||||
self.gen_set.insert(e);
|
self.gen_set.insert(e);
|
||||||
self.kill_set.remove(e);
|
self.kill_set.remove(e);
|
||||||
|
@ -541,30 +501,53 @@ impl<'a, E:Idx> BlockSets<'a, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_local_effect(&mut self) {
|
/// Computes `(set ∪ gen) - kill` and assigns the result to `set`.
|
||||||
self.on_entry.union(self.gen_set);
|
pub(crate) fn apply(&self, set: &mut BitSet<E>) {
|
||||||
self.on_entry.subtract(self.kill_set);
|
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> {
|
impl<E:Idx> AllSets<E> {
|
||||||
pub fn bits_per_block(&self) -> usize { self.bits_per_block }
|
pub fn bits_per_block(&self) -> usize { self.bits_per_block }
|
||||||
pub fn for_block(&mut self, block_idx: usize) -> BlockSets<'_, E> {
|
|
||||||
BlockSets {
|
pub fn get_mut(&mut self, block_idx: usize) -> (&mut BitSet<E>, &mut GenKillSet<E>) {
|
||||||
on_entry: &mut self.on_entry_sets[block_idx],
|
(&mut self.on_entry[block_idx], &mut self.trans[block_idx])
|
||||||
gen_set: &mut self.gen_sets[block_idx],
|
|
||||||
kill_set: &mut self.kill_sets[block_idx],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_entry_set_for(&self, block_idx: usize) -> &BitSet<E> {
|
pub fn trans_for(&self, block_idx: usize) -> &GenKillSet<E> {
|
||||||
&self.on_entry_sets[block_idx]
|
&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> {
|
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> {
|
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;
|
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.
|
/// Specifies what index type is used to access the bitvector.
|
||||||
type Idx: Idx;
|
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
|
/// A name describing the dataflow analysis that this
|
||||||
/// `BitDenotation` is supporting. The name should be something
|
/// `BitDenotation` is supporting. The name should be something
|
||||||
/// suitable for plugging in as part of a filename (i.e., avoid
|
/// suitable for plugging in as part of a filename (i.e., avoid
|
||||||
|
@ -640,7 +606,7 @@ pub trait BitDenotation<'tcx>: BitSetOperator {
|
||||||
/// applied, in that order, before moving for the next
|
/// applied, in that order, before moving for the next
|
||||||
/// statement.
|
/// statement.
|
||||||
fn before_statement_effect(&self,
|
fn before_statement_effect(&self,
|
||||||
_sets: &mut BlockSets<'_, Self::Idx>,
|
_trans: &mut GenKillSet<Self::Idx>,
|
||||||
_location: Location) {}
|
_location: Location) {}
|
||||||
|
|
||||||
/// Mutates the block-sets (the flow sets for the given
|
/// Mutates the block-sets (the flow sets for the given
|
||||||
|
@ -654,7 +620,7 @@ pub trait BitDenotation<'tcx>: BitSetOperator {
|
||||||
/// `bb_data` is the sequence of statements identified by `bb` in
|
/// `bb_data` is the sequence of statements identified by `bb` in
|
||||||
/// the MIR.
|
/// the MIR.
|
||||||
fn statement_effect(&self,
|
fn statement_effect(&self,
|
||||||
sets: &mut BlockSets<'_, Self::Idx>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location);
|
location: Location);
|
||||||
|
|
||||||
/// Similar to `terminator_effect`, except it applies
|
/// Similar to `terminator_effect`, except it applies
|
||||||
|
@ -669,7 +635,7 @@ pub trait BitDenotation<'tcx>: BitSetOperator {
|
||||||
/// applied, in that order, before moving for the next
|
/// applied, in that order, before moving for the next
|
||||||
/// terminator.
|
/// terminator.
|
||||||
fn before_terminator_effect(&self,
|
fn before_terminator_effect(&self,
|
||||||
_sets: &mut BlockSets<'_, Self::Idx>,
|
_trans: &mut GenKillSet<Self::Idx>,
|
||||||
_location: Location) {}
|
_location: Location) {}
|
||||||
|
|
||||||
/// Mutates the block-sets (the flow sets for the given
|
/// Mutates the block-sets (the flow sets for the given
|
||||||
|
@ -683,7 +649,7 @@ pub trait BitDenotation<'tcx>: BitSetOperator {
|
||||||
/// The effects applied here cannot depend on which branch the
|
/// The effects applied here cannot depend on which branch the
|
||||||
/// terminator took.
|
/// terminator took.
|
||||||
fn terminator_effect(&self,
|
fn terminator_effect(&self,
|
||||||
sets: &mut BlockSets<'_, Self::Idx>,
|
trans: &mut GenKillSet<Self::Idx>,
|
||||||
location: Location);
|
location: Location);
|
||||||
|
|
||||||
/// Mutates the block-sets according to the (flow-dependent)
|
/// 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>,
|
pub fn new(body: &'a Body<'tcx>,
|
||||||
dead_unwinds: &'a BitSet<mir::BasicBlock>,
|
dead_unwinds: &'a BitSet<mir::BasicBlock>,
|
||||||
denotation: D) -> Self where D: InitialFlow {
|
denotation: D) -> Self {
|
||||||
let bits_per_block = denotation.bits_per_block();
|
let bits_per_block = denotation.bits_per_block();
|
||||||
let num_blocks = body.basic_blocks().len();
|
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]
|
vec![BitSet::new_filled(bits_per_block); num_blocks]
|
||||||
} else {
|
} else {
|
||||||
vec![BitSet::new_empty(bits_per_block); num_blocks]
|
vec![BitSet::new_empty(bits_per_block); num_blocks]
|
||||||
};
|
};
|
||||||
let gen_sets = vec![HybridBitSet::new_empty(bits_per_block); num_blocks];
|
let nop = GenKill::from_elem(HybridBitSet::new_empty(bits_per_block));
|
||||||
let kill_sets = gen_sets.clone();
|
|
||||||
|
|
||||||
DataflowAnalysis {
|
DataflowAnalysis {
|
||||||
body,
|
body,
|
||||||
|
@ -736,9 +701,8 @@ impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation<'tcx>
|
||||||
flow_state: DataflowState {
|
flow_state: DataflowState {
|
||||||
sets: AllSets {
|
sets: AllSets {
|
||||||
bits_per_block,
|
bits_per_block,
|
||||||
on_entry_sets,
|
on_entry,
|
||||||
gen_sets,
|
trans: vec![nop; num_blocks],
|
||||||
kill_sets,
|
|
||||||
},
|
},
|
||||||
operator: denotation,
|
operator: denotation,
|
||||||
}
|
}
|
||||||
|
@ -836,7 +800,7 @@ where
|
||||||
in_out: &BitSet<D::Idx>,
|
in_out: &BitSet<D::Idx>,
|
||||||
bb: mir::BasicBlock,
|
bb: mir::BasicBlock,
|
||||||
dirty_queue: &mut WorkQueue<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);
|
let set_changed = self.flow_state.operator.join(entry_set, &in_out);
|
||||||
if set_changed {
|
if set_changed {
|
||||||
dirty_queue.insert(bb);
|
dirty_queue.insert(bb);
|
||||||
|
|
|
@ -95,7 +95,7 @@ fn find_dead_unwinds<'tcx>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut init_data = InitializationData {
|
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()),
|
dead: BitSet::new_empty(env.move_data.move_paths.len()),
|
||||||
};
|
};
|
||||||
debug!("find_dead_unwinds @ {:?}: {:?}; init_data={:?}",
|
debug!("find_dead_unwinds @ {:?}: {:?}; init_data={:?}",
|
||||||
|
@ -304,9 +304,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
|
||||||
|
|
||||||
fn initialization_data_at(&self, loc: Location) -> InitializationData {
|
fn initialization_data_at(&self, loc: Location) -> InitializationData {
|
||||||
let mut data = 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(),
|
.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(),
|
.to_owned(),
|
||||||
};
|
};
|
||||||
for stmt in 0..loc.statement_index {
|
for stmt in 0..loc.statement_index {
|
||||||
|
|
|
@ -18,7 +18,6 @@ use crate::dataflow::{
|
||||||
};
|
};
|
||||||
use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
|
use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
|
||||||
use crate::dataflow::move_paths::{HasMoveData, MoveData};
|
use crate::dataflow::move_paths::{HasMoveData, MoveData};
|
||||||
use crate::dataflow;
|
|
||||||
|
|
||||||
use crate::dataflow::has_rustc_mir_with;
|
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 on_entry = results.0.sets.entry_set_for(bb.index()).to_owned();
|
||||||
let mut gen_set = results.0.sets.gen_set_for(bb.index()).clone();
|
let mut trans = results.0.sets.trans_for(bb.index()).clone();
|
||||||
let mut kill_set = results.0.sets.kill_set_for(bb.index()).clone();
|
|
||||||
|
|
||||||
// Emulate effect of all statements in the block up to (but not
|
// Emulate effect of all statements in the block up to (but not
|
||||||
// including) the borrow within `peek_arg_place`. Do *not* include
|
// 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
|
// of the argument at time immediate preceding Call to
|
||||||
// `rustc_peek`).
|
// `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() {
|
for (j, stmt) in statements.iter().enumerate() {
|
||||||
debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
|
debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
|
||||||
let (place, rvalue) = match stmt.kind {
|
let (place, rvalue) = match stmt.kind {
|
||||||
|
@ -170,7 +164,7 @@ fn each_block<'tcx, O>(
|
||||||
// Okay, our search is over.
|
// Okay, our search is over.
|
||||||
match move_data.rev_lookup.find(peeking_at_place) {
|
match move_data.rev_lookup.find(peeking_at_place) {
|
||||||
LookupResult::Exact(peek_mpi) => {
|
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: {}",
|
debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
|
||||||
place, peeking_at_place, bit_state);
|
place, peeking_at_place, bit_state);
|
||||||
if !bit_state {
|
if !bit_state {
|
||||||
|
@ -197,18 +191,18 @@ fn each_block<'tcx, O>(
|
||||||
debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
|
debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
|
||||||
place, lhs_mpi, stmt);
|
place, lhs_mpi, stmt);
|
||||||
// reset GEN and KILL sets before emulating their effect.
|
// reset GEN and KILL sets before emulating their effect.
|
||||||
sets.gen_set.clear();
|
trans.clear();
|
||||||
sets.kill_set.clear();
|
|
||||||
results.0.operator.before_statement_effect(
|
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(
|
results.0.operator.statement_effect(
|
||||||
&mut sets, Location { block: bb, statement_index: j });
|
&mut trans,
|
||||||
sets.on_entry.union(sets.gen_set);
|
Location { block: bb, statement_index: j });
|
||||||
sets.on_entry.subtract(sets.kill_set);
|
trans.apply(&mut on_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
results.0.operator.before_terminator_effect(
|
results.0.operator.before_terminator_effect(
|
||||||
&mut sets,
|
&mut trans,
|
||||||
Location { block: bb, statement_index: statements.len() });
|
Location { block: bb, statement_index: statements.len() });
|
||||||
|
|
||||||
tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
|
tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue