• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Random access inspection of the results of a dataflow analysis.
2 
3 use crate::{framework::BitSetExt, CloneAnalysis};
4 
5 use std::borrow::{Borrow, BorrowMut};
6 use std::cmp::Ordering;
7 
8 #[cfg(debug_assertions)]
9 use rustc_index::bit_set::BitSet;
10 use rustc_middle::mir::{self, BasicBlock, Location};
11 
12 use super::{Analysis, Direction, Effect, EffectIndex, EntrySets, Results, ResultsCloned};
13 
14 // `AnalysisResults` is needed as an impl such as the following has an unconstrained type
15 // parameter:
16 // ```
17 // impl<'tcx, A, E, R> ResultsCursor<'_, 'tcx, A, R>
18 // where
19 //     A: Analysis<'tcx>,
20 //     E: Borrow<EntrySets<'tcx, A>>,
21 //     R: Results<'tcx, A, E>,
22 // {}
23 // ```
24 
25 /// A type representing the analysis results consumed by a `ResultsCursor`.
26 pub trait AnalysisResults<'tcx, A>: BorrowMut<Results<'tcx, A, Self::EntrySets>>
27 where
28     A: Analysis<'tcx>,
29 {
30     /// The type containing the entry sets for this `Results` type.
31     ///
32     /// Should be either `EntrySets<'tcx, A>` or `&EntrySets<'tcx, A>`.
33     type EntrySets: Borrow<EntrySets<'tcx, A>>;
34 }
35 impl<'tcx, A, E> AnalysisResults<'tcx, A> for Results<'tcx, A, E>
36 where
37     A: Analysis<'tcx>,
38     E: Borrow<EntrySets<'tcx, A>>,
39 {
40     type EntrySets = E;
41 }
42 impl<'a, 'tcx, A, E> AnalysisResults<'tcx, A> for &'a mut Results<'tcx, A, E>
43 where
44     A: Analysis<'tcx>,
45     E: Borrow<EntrySets<'tcx, A>>,
46 {
47     type EntrySets = E;
48 }
49 
50 /// A `ResultsCursor` that borrows the underlying `Results`.
51 pub type ResultsRefCursor<'res, 'mir, 'tcx, A> =
52     ResultsCursor<'mir, 'tcx, A, &'res mut Results<'tcx, A>>;
53 
54 /// A `ResultsCursor` which uses a cloned `Analysis` while borrowing the underlying `Results`. This
55 /// allows multiple cursors over the same `Results`.
56 pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> =
57     ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>;
58 
59 /// Allows random access inspection of the results of a dataflow analysis.
60 ///
61 /// This cursor only has linear performance within a basic block when its statements are visited in
62 /// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are
63 /// visited in *reverse* order—performance will be quadratic in the number of statements in the
64 /// block. The order in which basic blocks are inspected has no impact on performance.
65 ///
66 /// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
67 /// type of ownership is determined by `R` (see `ResultsRefCursor` above).
68 pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
69 where
70     A: Analysis<'tcx>,
71 {
72     body: &'mir mir::Body<'tcx>,
73     results: R,
74     state: A::Domain,
75 
76     pos: CursorPosition,
77 
78     /// Indicates that `state` has been modified with a custom effect.
79     ///
80     /// When this flag is set, we need to reset to an entry set before doing a seek.
81     state_needs_reset: bool,
82 
83     #[cfg(debug_assertions)]
84     reachable_blocks: BitSet<BasicBlock>,
85 }
86 
87 impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
88 where
89     A: Analysis<'tcx>,
90 {
91     /// Returns the dataflow state at the current location.
get(&self) -> &A::Domain92     pub fn get(&self) -> &A::Domain {
93         &self.state
94     }
95 
96     /// Returns the body this analysis was run on.
body(&self) -> &'mir mir::Body<'tcx>97     pub fn body(&self) -> &'mir mir::Body<'tcx> {
98         self.body
99     }
100 
101     /// Unwraps this cursor, returning the underlying `Results`.
into_results(self) -> R102     pub fn into_results(self) -> R {
103         self.results
104     }
105 }
106 
107 impl<'res, 'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>
108 where
109     A: Analysis<'tcx> + CloneAnalysis,
110 {
111     /// Creates a new cursor over the same `Results`. Note that the cursor's position is *not*
112     /// copied.
new_cursor(&self) -> Self113     pub fn new_cursor(&self) -> Self {
114         Self::new(self.body, self.results.reclone_analysis())
115     }
116 }
117 
118 impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
119 where
120     A: Analysis<'tcx>,
121     R: AnalysisResults<'tcx, A>,
122 {
123     /// Returns a new cursor that can inspect `results`.
new(body: &'mir mir::Body<'tcx>, results: R) -> Self124     pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
125         let bottom_value = results.borrow().analysis.bottom_value(body);
126         ResultsCursor {
127             body,
128             results,
129 
130             // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
131             // it needs to reset to block entry before the first seek. The cursor position is
132             // immaterial.
133             state_needs_reset: true,
134             state: bottom_value,
135             pos: CursorPosition::block_entry(mir::START_BLOCK),
136 
137             #[cfg(debug_assertions)]
138             reachable_blocks: mir::traversal::reachable_as_bitset(body),
139         }
140     }
141 
142     /// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled.
143     #[cfg(test)]
allow_unreachable(&mut self)144     pub(crate) fn allow_unreachable(&mut self) {
145         #[cfg(debug_assertions)]
146         self.reachable_blocks.insert_all()
147     }
148 
149     /// Returns the underlying `Results`.
results(&mut self) -> &Results<'tcx, A, R::EntrySets>150     pub fn results(&mut self) -> &Results<'tcx, A, R::EntrySets> {
151         self.results.borrow()
152     }
153 
154     /// Returns the underlying `Results`.
mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets>155     pub fn mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets> {
156         self.results.borrow_mut()
157     }
158 
159     /// Returns the `Analysis` used to generate the underlying `Results`.
analysis(&self) -> &A160     pub fn analysis(&self) -> &A {
161         &self.results.borrow().analysis
162     }
163 
164     /// Returns the `Analysis` used to generate the underlying `Results`.
mut_analysis(&mut self) -> &mut A165     pub fn mut_analysis(&mut self) -> &mut A {
166         &mut self.results.borrow_mut().analysis
167     }
168 
169     /// Returns both the dataflow state at the current location and the `Analysis`.
get_with_analysis(&mut self) -> (&A::Domain, &mut A)170     pub fn get_with_analysis(&mut self) -> (&A::Domain, &mut A) {
171         (&self.state, &mut self.results.borrow_mut().analysis)
172     }
173 
174     /// Resets the cursor to hold the entry set for the given basic block.
175     ///
176     /// For forward dataflow analyses, this is the dataflow state prior to the first statement.
177     ///
178     /// For backward dataflow analyses, this is the dataflow state after the terminator.
seek_to_block_entry(&mut self, block: BasicBlock)179     pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) {
180         #[cfg(debug_assertions)]
181         assert!(self.reachable_blocks.contains(block));
182 
183         self.state.clone_from(self.results.borrow().entry_set_for_block(block));
184         self.pos = CursorPosition::block_entry(block);
185         self.state_needs_reset = false;
186     }
187 
188     /// Resets the cursor to hold the state prior to the first statement in a basic block.
189     ///
190     /// For forward analyses, this is the entry set for the given block.
191     ///
192     /// For backward analyses, this is the state that will be propagated to its
193     /// predecessors (ignoring edge-specific effects).
seek_to_block_start(&mut self, block: BasicBlock)194     pub fn seek_to_block_start(&mut self, block: BasicBlock) {
195         if A::Direction::IS_FORWARD {
196             self.seek_to_block_entry(block)
197         } else {
198             self.seek_after(Location { block, statement_index: 0 }, Effect::Primary)
199         }
200     }
201 
202     /// Resets the cursor to hold the state after the terminator in a basic block.
203     ///
204     /// For backward analyses, this is the entry set for the given block.
205     ///
206     /// For forward analyses, this is the state that will be propagated to its
207     /// successors (ignoring edge-specific effects).
seek_to_block_end(&mut self, block: BasicBlock)208     pub fn seek_to_block_end(&mut self, block: BasicBlock) {
209         if A::Direction::IS_BACKWARD {
210             self.seek_to_block_entry(block)
211         } else {
212             self.seek_after(self.body.terminator_loc(block), Effect::Primary)
213         }
214     }
215 
216     /// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is
217     /// applied.
218     ///
219     /// The "before" effect at the target location *will be* applied.
seek_before_primary_effect(&mut self, target: Location)220     pub fn seek_before_primary_effect(&mut self, target: Location) {
221         self.seek_after(target, Effect::Before)
222     }
223 
224     /// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is
225     /// applied.
226     ///
227     /// The "before" effect at the target location will be applied as well.
seek_after_primary_effect(&mut self, target: Location)228     pub fn seek_after_primary_effect(&mut self, target: Location) {
229         self.seek_after(target, Effect::Primary)
230     }
231 
seek_after(&mut self, target: Location, effect: Effect)232     fn seek_after(&mut self, target: Location, effect: Effect) {
233         assert!(target <= self.body.terminator_loc(target.block));
234 
235         // Reset to the entry of the target block if any of the following are true:
236         //   - A custom effect has been applied to the cursor state.
237         //   - We are in a different block than the target.
238         //   - We are in the same block but have advanced past the target effect.
239         if self.state_needs_reset || self.pos.block != target.block {
240             self.seek_to_block_entry(target.block);
241         } else if let Some(curr_effect) = self.pos.curr_effect_index {
242             let mut ord = curr_effect.statement_index.cmp(&target.statement_index);
243             if A::Direction::IS_BACKWARD {
244                 ord = ord.reverse()
245             }
246 
247             match ord.then_with(|| curr_effect.effect.cmp(&effect)) {
248                 Ordering::Equal => return,
249                 Ordering::Greater => self.seek_to_block_entry(target.block),
250                 Ordering::Less => {}
251             }
252         }
253 
254         // At this point, the cursor is in the same block as the target location at an earlier
255         // statement.
256         debug_assert_eq!(target.block, self.pos.block);
257 
258         let block_data = &self.body[target.block];
259         let next_effect = if A::Direction::IS_FORWARD {
260             #[rustfmt::skip]
261             self.pos.curr_effect_index.map_or_else(
262                 || Effect::Before.at_index(0),
263                 EffectIndex::next_in_forward_order,
264             )
265         } else {
266             self.pos.curr_effect_index.map_or_else(
267                 || Effect::Before.at_index(block_data.statements.len()),
268                 EffectIndex::next_in_backward_order,
269             )
270         };
271 
272         let analysis = &mut self.results.borrow_mut().analysis;
273         let target_effect_index = effect.at_index(target.statement_index);
274 
275         A::Direction::apply_effects_in_range(
276             analysis,
277             &mut self.state,
278             target.block,
279             block_data,
280             next_effect..=target_effect_index,
281         );
282 
283         self.pos =
284             CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) };
285     }
286 
287     /// Applies `f` to the cursor's internal state.
288     ///
289     /// This can be used, e.g., to apply the call return effect directly to the cursor without
290     /// creating an extra copy of the dataflow state.
apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain))291     pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) {
292         f(&mut self.results.borrow_mut().analysis, &mut self.state);
293         self.state_needs_reset = true;
294     }
295 }
296 
297 impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
298 where
299     A: crate::GenKillAnalysis<'tcx>,
300     A::Domain: BitSetExt<A::Idx>,
301 {
contains(&self, elem: A::Idx) -> bool302     pub fn contains(&self, elem: A::Idx) -> bool {
303         self.get().contains(elem)
304     }
305 }
306 
307 #[derive(Clone, Copy, Debug)]
308 struct CursorPosition {
309     block: BasicBlock,
310     curr_effect_index: Option<EffectIndex>,
311 }
312 
313 impl CursorPosition {
block_entry(block: BasicBlock) -> CursorPosition314     fn block_entry(block: BasicBlock) -> CursorPosition {
315         CursorPosition { block, curr_effect_index: None }
316     }
317 }
318