1 //! A framework that can express both [gen-kill] and generic dataflow problems.
3 //! There is another interface for dataflow in the compiler in `librustc_mir/dataflow/mod.rs`. The
4 //! interface in this module will eventually [replace that one][design-meeting].
6 //! To actually use this framework, you must implement either the `Analysis` or the
7 //! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill
8 //! operations, prefer `GenKillAnalysis` as it will perform better. Create an `Engine` using the
9 //! appropriate constructor and call `iterate_to_fixpoint`. You can use a `ResultsCursor` to
10 //! inspect the fixpoint solution to your dataflow problem.
12 //! ```ignore(cross-crate-imports)
13 //! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) {
14 //! let analysis = MyAnalysis::new();
16 //! // If `MyAnalysis` implements `GenKillAnalysis`.
17 //! let results = Engine::new_gen_kill(tcx, body, did, analysis).iterate_to_fixpoint();
19 //! // If `MyAnalysis` implements `Analysis`.
20 //! // let results = Engine::new_generic(tcx, body, did, analysis).iterate_to_fixpoint();
22 //! let mut cursor = ResultsCursor::new(body, results);
24 //! for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() {
25 //! cursor.seek_after(Location { block: START_BLOCK, statement_index });
26 //! let state = cursor.get();
27 //! println!("{:?}", state);
32 //! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
33 //! [design-meeting]https://github.com/rust-lang/compiler-team/issues/202
37 use rustc::mir::{self, BasicBlock, Location};
38 use rustc_index::bit_set::{BitSet, HybridBitSet};
39 use rustc_index::vec::{Idx, IndexVec};
41 use crate::dataflow::BottomValue;
47 pub use self::cursor::{ResultsCursor, ResultsRefCursor};
48 pub use self::engine::Engine;
50 /// A dataflow analysis that has converged to fixpoint.
51 pub struct Results<'tcx, A>
56 entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
59 impl<A> Results<'tcx, A>
63 pub fn into_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
64 ResultsCursor::new(body, self)
67 pub fn on_block_entry(&self, block: BasicBlock) -> &BitSet<A::Idx> {
68 &self.entry_sets[block]
72 /// Define the domain of a dataflow problem.
74 /// This trait specifies the lattice on which this analysis operates. For now, this must be a
75 /// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet`
76 /// and referred to as the state vector.
78 /// This trait also defines the initial value for the dataflow state upon entry to the
79 /// `START_BLOCK`, as well as some names used to refer to this analysis when debugging.
80 pub trait AnalysisDomain<'tcx>: BottomValue {
81 /// The type of the elements in the state vector.
84 /// A descriptive name for this analysis. Used only for debugging.
86 /// This name should be brief and contain no spaces, periods or other characters that are not
87 /// suitable as part of a filename.
88 const NAME: &'static str;
90 /// The size of the state vector.
91 fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
93 /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow
95 fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>);
97 /// Prints an element in the state vector for debugging.
98 fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
99 write!(w, "{:?}", idx)
103 /// A dataflow problem with an arbitrarily complex transfer function.
104 pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
105 /// Updates the current dataflow state with the effect of evaluating a statement.
106 fn apply_statement_effect(
108 state: &mut BitSet<Self::Idx>,
109 statement: &mir::Statement<'tcx>,
113 /// Updates the current dataflow state with an effect that occurs immediately *before* the
116 /// This method is useful if the consumer of the results of this analysis needs only to observe
117 /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
118 /// analyses should not implement this without implementing `apply_statement_effect`.
119 fn apply_before_statement_effect(
121 _state: &mut BitSet<Self::Idx>,
122 _statement: &mir::Statement<'tcx>,
127 /// Updates the current dataflow state with the effect of evaluating a terminator.
129 /// The effect of a successful return from a `Call` terminator should **not** be accounted for
130 /// in this function. That should go in `apply_call_return_effect`. For example, in the
131 /// `InitializedPlaces` analyses, the return place for a function call is not marked as
132 /// initialized here.
133 fn apply_terminator_effect(
135 state: &mut BitSet<Self::Idx>,
136 terminator: &mir::Terminator<'tcx>,
140 /// Updates the current dataflow state with an effect that occurs immediately *before* the
141 /// given terminator.
143 /// This method is useful if the consumer of the results of this analysis needs only to observe
144 /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
145 /// analyses should not implement this without implementing `apply_terminator_effect`.
146 fn apply_before_terminator_effect(
148 _state: &mut BitSet<Self::Idx>,
149 _terminator: &mir::Terminator<'tcx>,
154 /// Updates the current dataflow state with the effect of a successful return from a `Call`
157 /// This is separate from `apply_terminator_effect` to properly track state across unwind
159 fn apply_call_return_effect(
161 state: &mut BitSet<Self::Idx>,
163 func: &mir::Operand<'tcx>,
164 args: &[mir::Operand<'tcx>],
165 return_place: &mir::Place<'tcx>,
169 /// A gen/kill dataflow problem.
171 /// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
172 /// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer
173 /// functions for each statement in this way, the transfer function for an entire basic block can
174 /// be computed efficiently.
176 /// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`.
177 pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
178 /// See `Analysis::apply_statement_effect`.
181 trans: &mut impl GenKill<Self::Idx>,
182 statement: &mir::Statement<'tcx>,
186 /// See `Analysis::apply_before_statement_effect`.
187 fn before_statement_effect(
189 _trans: &mut impl GenKill<Self::Idx>,
190 _statement: &mir::Statement<'tcx>,
195 /// See `Analysis::apply_terminator_effect`.
196 fn terminator_effect(
198 trans: &mut impl GenKill<Self::Idx>,
199 terminator: &mir::Terminator<'tcx>,
203 /// See `Analysis::apply_before_terminator_effect`.
204 fn before_terminator_effect(
206 _trans: &mut impl GenKill<Self::Idx>,
207 _terminator: &mir::Terminator<'tcx>,
212 /// See `Analysis::apply_call_return_effect`.
213 fn call_return_effect(
215 trans: &mut impl GenKill<Self::Idx>,
217 func: &mir::Operand<'tcx>,
218 args: &[mir::Operand<'tcx>],
219 return_place: &mir::Place<'tcx>,
223 impl<A> Analysis<'tcx> for A
225 A: GenKillAnalysis<'tcx>,
227 fn apply_statement_effect(
229 state: &mut BitSet<Self::Idx>,
230 statement: &mir::Statement<'tcx>,
233 self.statement_effect(state, statement, location);
236 fn apply_before_statement_effect(
238 state: &mut BitSet<Self::Idx>,
239 statement: &mir::Statement<'tcx>,
242 self.before_statement_effect(state, statement, location);
245 fn apply_terminator_effect(
247 state: &mut BitSet<Self::Idx>,
248 terminator: &mir::Terminator<'tcx>,
251 self.terminator_effect(state, terminator, location);
254 fn apply_before_terminator_effect(
256 state: &mut BitSet<Self::Idx>,
257 terminator: &mir::Terminator<'tcx>,
260 self.before_terminator_effect(state, terminator, location);
263 fn apply_call_return_effect(
265 state: &mut BitSet<Self::Idx>,
267 func: &mir::Operand<'tcx>,
268 args: &[mir::Operand<'tcx>],
269 return_place: &mir::Place<'tcx>,
271 self.call_return_effect(state, block, func, args, return_place);
275 /// The legal operations for a transfer function in a gen/kill problem.
276 pub trait GenKill<T>: Sized {
277 /// Inserts `elem` into the `gen` set, removing it the `kill` set if present.
278 fn gen(&mut self, elem: T);
280 /// Inserts `elem` into the `kill` set, removing it the `gen` set if present.
281 fn kill(&mut self, elem: T);
283 /// Inserts the given elements into the `gen` set, removing them from the `kill` set if present.
284 fn gen_all(&mut self, elems: impl IntoIterator<Item = T>) {
290 /// Inserts the given elements into the `kill` set, removing them from the `gen` set if present.
291 fn kill_all(&mut self, elems: impl IntoIterator<Item = T>) {
298 /// Stores a transfer function for a gen/kill problem.
300 pub struct GenKillSet<T: Idx> {
301 gen: HybridBitSet<T>,
302 kill: HybridBitSet<T>,
305 impl<T: Idx> GenKillSet<T> {
306 /// Creates a new transfer function that will leave the dataflow state unchanged.
307 pub fn identity(universe: usize) -> Self {
309 gen: HybridBitSet::new_empty(universe),
310 kill: HybridBitSet::new_empty(universe),
314 /// Applies this transfer function to the given bitset.
315 pub fn apply(&self, state: &mut BitSet<T>) {
316 state.union(&self.gen);
317 state.subtract(&self.kill);
321 impl<T: Idx> GenKill<T> for GenKillSet<T> {
322 fn gen(&mut self, elem: T) {
323 self.gen.insert(elem);
324 self.kill.remove(elem);
327 fn kill(&mut self, elem: T) {
328 self.kill.insert(elem);
329 self.gen.remove(elem);
333 impl<T: Idx> GenKill<T> for BitSet<T> {
334 fn gen(&mut self, elem: T) {
338 fn kill(&mut self, elem: T) {