Document new dataflow analysis

This commit is contained in:
Dylan MacKenzie 2019-09-17 17:22:05 -07:00
parent ceb72e4ad1
commit 5496a1fbbe

View File

@ -8,15 +8,48 @@
use crate::dataflow::BottomValue;
/// A specific kind of dataflow analysis.
///
/// To run a dataflow analysis, one must set the initial state of the `START_BLOCK` via
/// `initialize_start_block` and define a transfer function for each statement or terminator via
/// the various `effect` methods. The entry set for all other basic blocks is initialized to
/// `Self::BOTTOM_VALUE`. The dataflow `Engine` then iteratively updates the various entry sets for
/// each block with the cumulative effects of the transfer functions of all preceding blocks.
///
/// You should use an `Engine` to actually run an analysis, and a `ResultsCursor` to inspect the
/// results of that analysis like so:
///
/// ```ignore
/// fn do_my_analysis(body: &mir::Body<'tcx>, dead_unwinds: &BitSet<BasicBlock>) {
/// let analysis = MyAnalysis::new();
/// let results = Engine::new(body, dead_unwinds, analysis).iterate_to_fixpoint();
/// let mut cursor = dataflow::ResultsCursor::new(body, results);
///
/// for statement_index in body.block_data[START_BLOCK].statements.iter() {
/// cursor.seek_after(Location { block: START_BLOCK, statement_index });
/// let state = cursor.get();
/// println!("{:?}", state);
/// }
/// }
/// ```
pub trait Analysis<'tcx>: BottomValue {
/// The index type used to access the dataflow state.
type Idx: Idx;
/// A name describing the dataflow analysis being implemented.
///
/// The name should be suitable as part of a filename, so avoid whitespace, slashes or periods
/// and try to keep it short.
fn name() -> &'static str;
/// The size of each bitvector allocated for each block.
fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
/// Mutates the entry set of the `START_BLOCK` to containthe initial state for dataflow
/// analysis.
fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>);
/// Updates the current dataflow state with the effect of evaluating a statement.
fn apply_statement_effect(
&self,
state: &mut BitSet<Self::Idx>,
@ -24,6 +57,11 @@ fn apply_statement_effect(
location: Location,
);
/// Updates the current dataflow state with the effect of evaluating a statement.
///
/// Note that the effect of a successful return from a `Call` terminator should **not** be
/// acounted for in this function. That should go in `apply_call_return_effect`. For example,
/// in the `InitializedPlaces` analyses, the return place is not marked as initialized here.
fn apply_terminator_effect(
&self,
state: &mut BitSet<Self::Idx>,
@ -31,6 +69,11 @@ fn apply_terminator_effect(
location: Location,
);
/// Updates the current dataflow state with the effect of a successful return from a `Call`
/// terminator.
///
/// This is separated from `apply_terminator_effect` to properly track state across
/// unwind edges for `Call`s.
fn apply_call_return_effect(
&self,
state: &mut BitSet<Self::Idx>,
@ -117,6 +160,11 @@ fn block(&self) -> BasicBlock {
}
}
/// Inspect the results of dataflow analysis.
///
/// This cursor has linear performance when visiting statements in a block in order. Visiting
/// statements within a block in reverse order is `O(n^2)`, where `n` is the number of statements
/// in that block.
pub struct ResultsCursor<'mir, 'tcx, A>
where
A: Analysis<'tcx>,
@ -267,6 +315,7 @@ pub fn get(&self) -> &BitSet<A::Idx> {
}
}
/// A completed dataflow analysis.
pub struct Results<'tcx, A>
where
A: Analysis<'tcx>,
@ -275,6 +324,7 @@ pub struct Results<'tcx, A>
entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
}
/// All information required to iterate a dataflow analysis to fixpoint.
pub struct Engine<'a, 'tcx, A>
where
A: Analysis<'tcx>,