1 //! A framework for expressing dataflow problems.
5 use rustc::mir::{self, BasicBlock, Location};
6 use rustc_index::bit_set::{BitSet, HybridBitSet};
7 use rustc_index::vec::{Idx, IndexVec};
9 use crate::dataflow::BottomValue;
15 pub use self::cursor::{ResultsCursor, ResultsRefCursor};
16 pub use self::engine::Engine;
18 /// A dataflow analysis that has converged to fixpoint.
19 pub struct Results<'tcx, A>
24 entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
27 impl<A> Results<'tcx, A>
31 pub fn into_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
32 ResultsCursor::new(body, self)
35 pub fn on_block_entry(&self, block: BasicBlock) -> &BitSet<A::Idx> {
36 &self.entry_sets[block]
40 /// Define the domain of a dataflow problem.
42 /// This trait specifies the lattice on which this analysis operates. For now, this must be a
43 /// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet`
44 /// and referred to as the state vector.
46 /// This trait also defines the initial value for the dataflow state upon entry to the
47 /// `START_BLOCK`, as well as some names used to refer to this analysis when debugging.
48 pub trait AnalysisDomain<'tcx>: BottomValue {
49 /// The type of the elements in the state vector.
52 /// A descriptive name for this analysis. Used only for debugging.
54 /// This name should be brief and contain no spaces, periods or other characters that are not
55 /// suitable as part of a filename.
56 const NAME: &'static str;
58 /// The size of the state vector.
59 fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
61 /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow
63 fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>);
65 /// Prints an element in the state vector for debugging.
66 fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
67 write!(w, "{:?}", idx)
71 /// Define a dataflow problem with an arbitrarily complex transfer function.
72 pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
73 /// Updates the current dataflow state with the effect of evaluating a statement.
74 fn apply_statement_effect(
76 state: &mut BitSet<Self::Idx>,
77 statement: &mir::Statement<'tcx>,
81 /// Updates the current dataflow state with an effect that occurs immediately *before* the
84 /// This method is useful if the consumer of the results of this analysis needs only to observe
85 /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
86 /// analyses should not implement this without implementing `apply_statement_effect`.
87 fn apply_before_statement_effect(
89 _state: &mut BitSet<Self::Idx>,
90 _statement: &mir::Statement<'tcx>,
95 /// Updates the current dataflow state with the effect of evaluating a terminator.
97 /// The effect of a successful return from a `Call` terminator should **not** be accounted for
98 /// in this function. That should go in `apply_call_return_effect`. For example, in the
99 /// `InitializedPlaces` analyses, the return place for a function call is not marked as
100 /// initialized here.
101 fn apply_terminator_effect(
103 state: &mut BitSet<Self::Idx>,
104 terminator: &mir::Terminator<'tcx>,
108 /// Updates the current dataflow state with an effect that occurs immediately *before* the
109 /// given terminator.
111 /// This method is useful if the consumer of the results of this analysis needs only to observe
112 /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
113 /// analyses should not implement this without implementing `apply_terminator_effect`.
114 fn apply_before_terminator_effect(
116 _state: &mut BitSet<Self::Idx>,
117 _terminator: &mir::Terminator<'tcx>,
122 /// Updates the current dataflow state with the effect of a successful return from a `Call`
125 /// This is separate from `apply_terminator_effect` to properly track state across unwind
127 fn apply_call_return_effect(
129 state: &mut BitSet<Self::Idx>,
131 func: &mir::Operand<'tcx>,
132 args: &[mir::Operand<'tcx>],
133 return_place: &mir::Place<'tcx>,
137 /// Define a gen/kill dataflow problem.
139 /// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
140 /// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer
141 /// functions for each statement in this way, the transfer function for an entire basic block can
142 /// be computed efficiently.
144 /// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`.
145 pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
146 /// See `Analysis::apply_statement_effect`.
149 trans: &mut impl GenKill<Self::Idx>,
150 statement: &mir::Statement<'tcx>,
154 /// See `Analysis::apply_before_statement_effect`.
155 fn before_statement_effect(
157 _trans: &mut impl GenKill<Self::Idx>,
158 _statement: &mir::Statement<'tcx>,
163 /// See `Analysis::apply_terminator_effect`.
164 fn terminator_effect(
166 trans: &mut impl GenKill<Self::Idx>,
167 terminator: &mir::Terminator<'tcx>,
171 /// See `Analysis::apply_before_terminator_effect`.
172 fn before_terminator_effect(
174 _trans: &mut impl GenKill<Self::Idx>,
175 _terminator: &mir::Terminator<'tcx>,
180 /// See `Analysis::apply_call_return_effect`.
181 fn call_return_effect(
183 trans: &mut impl GenKill<Self::Idx>,
185 func: &mir::Operand<'tcx>,
186 args: &[mir::Operand<'tcx>],
187 return_place: &mir::Place<'tcx>,
191 impl<A> Analysis<'tcx> for A
193 A: GenKillAnalysis<'tcx>,
195 fn apply_statement_effect(
197 state: &mut BitSet<Self::Idx>,
198 statement: &mir::Statement<'tcx>,
201 self.statement_effect(state, statement, location);
204 fn apply_before_statement_effect(
206 state: &mut BitSet<Self::Idx>,
207 statement: &mir::Statement<'tcx>,
210 self.before_statement_effect(state, statement, location);
213 fn apply_terminator_effect(
215 state: &mut BitSet<Self::Idx>,
216 terminator: &mir::Terminator<'tcx>,
219 self.terminator_effect(state, terminator, location);
222 fn apply_before_terminator_effect(
224 state: &mut BitSet<Self::Idx>,
225 terminator: &mir::Terminator<'tcx>,
228 self.before_terminator_effect(state, terminator, location);
231 fn apply_call_return_effect(
233 state: &mut BitSet<Self::Idx>,
235 func: &mir::Operand<'tcx>,
236 args: &[mir::Operand<'tcx>],
237 return_place: &mir::Place<'tcx>,
239 self.call_return_effect(state, block, func, args, return_place);
243 /// The legal operations for a transfer function in a gen/kill problem.
244 pub trait GenKill<T>: Sized {
245 /// Inserts `elem` into the `gen` set, removing it the `kill` set if present.
246 fn gen(&mut self, elem: T);
248 /// Inserts `elem` into the `kill` set, removing it the `gen` set if present.
249 fn kill(&mut self, elem: T);
251 /// Inserts the given elements into the `gen` set, removing them from the `kill` set if present.
252 fn gen_all(&mut self, elems: impl IntoIterator<Item = T>) {
258 /// Inserts the given elements into the `kill` set, removing them from the `gen` set if present.
259 fn kill_all(&mut self, elems: impl IntoIterator<Item = T>) {
266 /// Stores a transfer function for a gen/kill problem.
268 pub struct GenKillSet<T: Idx> {
269 gen: HybridBitSet<T>,
270 kill: HybridBitSet<T>,
273 impl<T: Idx> GenKillSet<T> {
274 /// Creates a new transfer function that will leave the dataflow state unchanged.
275 pub fn identity(universe: usize) -> Self {
277 gen: HybridBitSet::new_empty(universe),
278 kill: HybridBitSet::new_empty(universe),
282 /// Applies this transfer function to the given bitset.
283 pub fn apply(&self, state: &mut BitSet<T>) {
284 state.union(&self.gen);
285 state.subtract(&self.kill);
289 impl<T: Idx> GenKill<T> for GenKillSet<T> {
290 fn gen(&mut self, elem: T) {
291 self.gen.insert(elem);
292 self.kill.remove(elem);
295 fn kill(&mut self, elem: T) {
296 self.kill.insert(elem);
297 self.gen.remove(elem);
301 impl<T: Idx> GenKill<T> for BitSet<T> {
302 fn gen(&mut self, elem: T) {
306 fn kill(&mut self, elem: T) {