1
1
//! A framework that can express both [gen-kill] and generic dataflow problems.
2
2
//!
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].
3
+ //! To actually use this framework, you must implement either the `Analysis` or the
4
+ //! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill
5
+ //! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The
6
+ //! `impls` module contains several examples of gen/kill dataflow analyses.
5
7
//!
6
- //! To actually use this framework, you must implement either the `Analysis` or the `GenKillAnalysis`
7
- //! trait. If your transfer function can be expressed with only gen/kill operations, prefer
8
- //! `GenKillAnalysis` since it will run faster while iterating to fixpoint. Create an `Engine` using
9
- //! the appropriate constructor and call `iterate_to_fixpoint`. You can use a `ResultsCursor` to
10
- //! inspect the fixpoint solution to your dataflow problem.
8
+ //! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait,
9
+ //! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the
10
+ //! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use
11
+ //! `visit_results`. The following example uses the `ResultsCursor` approach.
11
12
//!
12
13
//! ```ignore(cross-crate-imports)
13
- //! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) {
14
- //! let analysis = MyAnalysis::new();
15
- //!
16
- //! // If `MyAnalysis` implements `GenKillAnalysis`.
17
- //! let results = Engine::new_gen_kill(tcx, body, did, analysis).iterate_to_fixpoint();
14
+ //! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available.
18
15
//!
19
- //! // If `MyAnalysis` implements `Analysis`.
20
- //! // let results = Engine::new_generic(tcx, body, did, analysis).iterate_to_fixpoint();
21
- //!
22
- //! let mut cursor = ResultsCursor::new(body, results);
16
+ //! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) {
17
+ //! let analysis = MyAnalysis::new()
18
+ //! .into_engine(tcx, body, did)
19
+ //! .iterate_to_fixpoint()
20
+ //! .into_results_cursor(body);
23
21
//!
22
+ //! // Print the dataflow state *after* each statement in the start block.
24
23
//! for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() {
25
24
//! cursor.seek_after(Location { block: START_BLOCK, statement_index });
26
25
//! let state = cursor.get();
30
29
//! ```
31
30
//!
32
31
//! [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
34
32
35
33
use std:: io;
36
34
@@ -41,8 +39,6 @@ use rustc_hir::def_id::DefId;
41
39
use rustc_index:: bit_set:: { BitSet , HybridBitSet } ;
42
40
use rustc_index:: vec:: { Idx , IndexVec } ;
43
41
44
- use crate :: dataflow:: BottomValue ;
45
-
46
42
mod cursor;
47
43
mod engine;
48
44
mod graphviz;
95
91
}
96
92
}
97
93
94
+ /// Parameterization for the precise form of data flow that is used.
95
+ ///
96
+ /// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
97
+ /// This also determines the semantics of the lattice `join` operator used to merge dataflow
98
+ /// results, since dataflow works by starting at the bottom and moving monotonically to a fixed
99
+ /// point.
100
+ ///
101
+ /// This means, for propagation across the graph, that you either want to start at all-zeroes and
102
+ /// then use Union as your merge when propagating, or you start at all-ones and then use Intersect
103
+ /// as your merge when propagating.
104
+ pub trait BottomValue {
105
+ /// Specifies the initial value for each bit in the entry set for each basic block.
106
+ const BOTTOM_VALUE : bool ;
107
+
108
+ /// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
109
+ ///
110
+ /// It is almost certainly wrong to override this, since it automatically applies
111
+ /// * `inout_set & in_set` if `BOTTOM_VALUE == true`
112
+ /// * `inout_set | in_set` if `BOTTOM_VALUE == false`
113
+ ///
114
+ /// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks.
115
+ /// For clarity, the above statement again from a different perspective:
116
+ /// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is
117
+ /// `!BOTTOM_VALUE`.
118
+ ///
119
+ /// There are situations where you want the opposite behaviour: propagate only if *all*
120
+ /// predecessor blocks's value is `!BOTTOM_VALUE`.
121
+ /// E.g. if you want to know whether a bit is *definitely* set at a specific location. This
122
+ /// means that all code paths leading to the location must have set the bit, instead of any
123
+ /// code path leading there.
124
+ ///
125
+ /// If you want this kind of "definitely set" analysis, you need to
126
+ /// 1. Invert `BOTTOM_VALUE`
127
+ /// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE`
128
+ /// 3. Override `join` to do the opposite from what it's doing now.
129
+ #[ inline]
130
+ fn join < T : Idx > ( & self , inout_set : & mut BitSet < T > , in_set : & BitSet < T > ) -> bool {
131
+ if !Self :: BOTTOM_VALUE { inout_set. union ( in_set) } else { inout_set. intersect ( in_set) }
132
+ }
133
+ }
134
+
98
135
/// Define the domain of a dataflow problem.
99
136
///
100
137
/// This trait specifies the lattice on which this analysis operates. For now, this must be a
0 commit comments