|
7 | 7 | //!
|
8 | 8 | //! The `impls` module contains several examples of dataflow analyses.
|
9 | 9 | //!
|
10 |
| -//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait, |
11 |
| -//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the |
12 |
| -//! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use |
13 |
| -//! `visit_results`. The following example uses the `ResultsCursor` approach. |
| 10 | +//! Then call `iterate_to_fixpoint` on your type that impls `Analysis` to get a `Results`. From |
| 11 | +//! there, you can use a `ResultsCursor` to inspect the fixpoint solution to your dataflow problem, |
| 12 | +//! or implement the `ResultsVisitor` interface and use `visit_results`. The following example uses |
| 13 | +//! the `ResultsCursor` approach. |
14 | 14 | //!
|
15 | 15 | //! ```ignore (cross-crate-imports)
|
16 |
| -//! use rustc_const_eval::dataflow::Analysis; // Makes `into_engine` available. |
| 16 | +//! use rustc_const_eval::dataflow::Analysis; // Makes `iterate_to_fixpoint` available. |
17 | 17 | //!
|
18 | 18 | //! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) {
|
19 | 19 | //! let analysis = MyAnalysis::new()
|
20 |
| -//! .into_engine(tcx, body) |
21 |
| -//! .iterate_to_fixpoint() |
| 20 | +//! .iterate_to_fixpoint(tcx, body, None) |
22 | 21 | //! .into_results_cursor(body);
|
23 | 22 | //!
|
24 | 23 | //! // Print the dataflow state *after* each statement in the start block.
|
|
34 | 33 |
|
35 | 34 | use std::cmp::Ordering;
|
36 | 35 |
|
37 |
| -use rustc_index::Idx; |
| 36 | +use rustc_data_structures::work_queue::WorkQueue; |
38 | 37 | use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet};
|
39 |
| -use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges}; |
| 38 | +use rustc_index::{Idx, IndexVec}; |
| 39 | +use rustc_middle::bug; |
| 40 | +use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges, traversal}; |
40 | 41 | use rustc_middle::ty::TyCtxt;
|
| 42 | +use tracing::error; |
| 43 | + |
| 44 | +use self::results::write_graphviz_results; |
| 45 | +use super::fmt::DebugWithContext; |
41 | 46 |
|
42 | 47 | mod cursor;
|
43 | 48 | mod direction;
|
44 |
| -mod engine; |
45 | 49 | pub mod fmt;
|
46 | 50 | pub mod graphviz;
|
47 | 51 | pub mod lattice;
|
| 52 | +mod results; |
48 | 53 | mod visitor;
|
49 | 54 |
|
50 | 55 | pub use self::cursor::ResultsCursor;
|
51 | 56 | pub use self::direction::{Backward, Direction, Forward};
|
52 |
| -pub use self::engine::{Engine, Results}; |
53 | 57 | pub use self::lattice::{JoinSemiLattice, MaybeReachable};
|
| 58 | +pub use self::results::Results; |
54 | 59 | pub use self::visitor::{ResultsVisitable, ResultsVisitor, visit_results};
|
55 | 60 |
|
56 | 61 | /// Analysis domains are all bitsets of various kinds. This trait holds
|
@@ -223,26 +228,92 @@ pub trait Analysis<'tcx> {
|
223 | 228 |
|
224 | 229 | /* Extension methods */
|
225 | 230 |
|
226 |
| - /// Creates an `Engine` to find the fixpoint for this dataflow problem. |
| 231 | + /// Finds the fixpoint for this dataflow problem. |
227 | 232 | ///
|
228 | 233 | /// You shouldn't need to override this. Its purpose is to enable method chaining like so:
|
229 | 234 | ///
|
230 | 235 | /// ```ignore (cross-crate-imports)
|
231 | 236 | /// let results = MyAnalysis::new(tcx, body)
|
232 |
| - /// .into_engine(tcx, body, def_id) |
233 |
| - /// .iterate_to_fixpoint() |
| 237 | + /// .iterate_to_fixpoint(tcx, body, None) |
234 | 238 | /// .into_results_cursor(body);
|
235 | 239 | /// ```
|
236 |
| - #[inline] |
237 |
| - fn into_engine<'mir>( |
238 |
| - self, |
| 240 | + /// You can optionally add a `pass_name` to the graphviz output for this particular run of a |
| 241 | + /// dataflow analysis. Some analyses are run multiple times in the compilation pipeline. |
| 242 | + /// Without a `pass_name` to differentiates them, only the results for the latest run will be |
| 243 | + /// saved. |
| 244 | + fn iterate_to_fixpoint<'mir>( |
| 245 | + mut self, |
239 | 246 | tcx: TyCtxt<'tcx>,
|
240 | 247 | body: &'mir mir::Body<'tcx>,
|
241 |
| - ) -> Engine<'mir, 'tcx, Self> |
| 248 | + pass_name: Option<&'static str>, |
| 249 | + ) -> Results<'tcx, Self> |
242 | 250 | where
|
243 | 251 | Self: Sized,
|
| 252 | + Self::Domain: DebugWithContext<Self>, |
244 | 253 | {
|
245 |
| - Engine::new(tcx, body, self) |
| 254 | + let mut entry_sets = |
| 255 | + IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len()); |
| 256 | + self.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]); |
| 257 | + |
| 258 | + if Self::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != self.bottom_value(body) { |
| 259 | + bug!("`initialize_start_block` is not yet supported for backward dataflow analyses"); |
| 260 | + } |
| 261 | + |
| 262 | + let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len()); |
| 263 | + |
| 264 | + if Self::Direction::IS_FORWARD { |
| 265 | + for (bb, _) in traversal::reverse_postorder(body) { |
| 266 | + dirty_queue.insert(bb); |
| 267 | + } |
| 268 | + } else { |
| 269 | + // Reverse post-order on the reverse CFG may generate a better iteration order for |
| 270 | + // backward dataflow analyses, but probably not enough to matter. |
| 271 | + for (bb, _) in traversal::postorder(body) { |
| 272 | + dirty_queue.insert(bb); |
| 273 | + } |
| 274 | + } |
| 275 | + |
| 276 | + // `state` is not actually used between iterations; |
| 277 | + // this is just an optimization to avoid reallocating |
| 278 | + // every iteration. |
| 279 | + let mut state = self.bottom_value(body); |
| 280 | + while let Some(bb) = dirty_queue.pop() { |
| 281 | + let bb_data = &body[bb]; |
| 282 | + |
| 283 | + // Set the state to the entry state of the block. |
| 284 | + // This is equivalent to `state = entry_sets[bb].clone()`, |
| 285 | + // but it saves an allocation, thus improving compile times. |
| 286 | + state.clone_from(&entry_sets[bb]); |
| 287 | + |
| 288 | + // Apply the block transfer function, using the cached one if it exists. |
| 289 | + let edges = Self::Direction::apply_effects_in_block(&mut self, &mut state, bb, bb_data); |
| 290 | + |
| 291 | + Self::Direction::join_state_into_successors_of( |
| 292 | + &mut self, |
| 293 | + body, |
| 294 | + &mut state, |
| 295 | + bb, |
| 296 | + edges, |
| 297 | + |target: BasicBlock, state: &Self::Domain| { |
| 298 | + let set_changed = entry_sets[target].join(state); |
| 299 | + if set_changed { |
| 300 | + dirty_queue.insert(target); |
| 301 | + } |
| 302 | + }, |
| 303 | + ); |
| 304 | + } |
| 305 | + |
| 306 | + let results = Results { analysis: self, entry_sets }; |
| 307 | + |
| 308 | + if tcx.sess.opts.unstable_opts.dump_mir_dataflow { |
| 309 | + let (res, results) = write_graphviz_results(tcx, body, results, pass_name); |
| 310 | + if let Err(e) = res { |
| 311 | + error!("Failed to write graphviz dataflow results: {}", e); |
| 312 | + } |
| 313 | + results |
| 314 | + } else { |
| 315 | + results |
| 316 | + } |
246 | 317 | }
|
247 | 318 | }
|
248 | 319 |
|
|
0 commit comments