rustc_mir_dataflow/framework/
cursor.rs

1//! Random access inspection of the results of a dataflow analysis.
2
3use std::borrow::Cow;
4use std::cmp::Ordering;
5use std::ops::{Deref, DerefMut};
6
7#[cfg(debug_assertions)]
8use rustc_index::bit_set::DenseBitSet;
9use rustc_middle::mir::{self, BasicBlock, Location};
10
11use super::{Analysis, Direction, Effect, EffectIndex, Results};
12
13/// Some `ResultsCursor`s want to own an `Analysis`, and some want to borrow an `Analysis`, either
14/// mutable or immutably. This type allows all of the above. It's similar to `Cow`, but `Cow`
15/// doesn't allow mutable borrowing.
16enum CowMut<'a, T> {
17    BorrowedMut(&'a mut T),
18    Owned(T),
19}
20
21impl<T> Deref for CowMut<'_, T> {
22    type Target = T;
23
24    fn deref(&self) -> &T {
25        match self {
26            CowMut::BorrowedMut(borrowed) => borrowed,
27            CowMut::Owned(owned) => owned,
28        }
29    }
30}
31
32impl<T> DerefMut for CowMut<'_, T> {
33    fn deref_mut(&mut self) -> &mut T {
34        match self {
35            CowMut::BorrowedMut(borrowed) => borrowed,
36            CowMut::Owned(owned) => owned,
37        }
38    }
39}
40
41/// Allows random access inspection of the results of a dataflow analysis. Use this when you want
42/// to inspect domain values only in certain locations; use `ResultsVisitor` if you want to inspect
43/// domain values in many or all locations.
44///
45/// Because `Results` only has domain values for the entry of each basic block, these inspections
46/// involve some amount of domain value recomputations. This cursor only has linear performance
47/// within a basic block when its statements are visited in the same order as the `DIRECTION` of
48/// the analysis. In the worst case—when statements are visited in *reverse* order—performance will
49/// be quadratic in the number of statements in the block. The order in which basic blocks are
50/// inspected has no impact on performance.
51pub struct ResultsCursor<'mir, 'tcx, A>
52where
53    A: Analysis<'tcx>,
54{
55    body: &'mir mir::Body<'tcx>,
56    analysis: CowMut<'mir, A>,
57    results: Cow<'mir, Results<A::Domain>>,
58    state: A::Domain,
59
60    pos: CursorPosition,
61
62    /// Indicates that `state` has been modified with a custom effect.
63    ///
64    /// When this flag is set, we need to reset to an entry set before doing a seek.
65    state_needs_reset: bool,
66
67    #[cfg(debug_assertions)]
68    reachable_blocks: DenseBitSet<BasicBlock>,
69}
70
71impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
72where
73    A: Analysis<'tcx>,
74{
75    /// Returns the dataflow state at the current location.
76    pub fn get(&self) -> &A::Domain {
77        &self.state
78    }
79
80    /// Returns the body this analysis was run on.
81    pub fn body(&self) -> &'mir mir::Body<'tcx> {
82        self.body
83    }
84
85    fn new(
86        body: &'mir mir::Body<'tcx>,
87        analysis: CowMut<'mir, A>,
88        results: Cow<'mir, Results<A::Domain>>,
89    ) -> Self {
90        let bottom_value = analysis.bottom_value(body);
91        ResultsCursor {
92            body,
93            analysis,
94            results,
95
96            // Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
97            // it needs to reset to block entry before the first seek. The cursor position is
98            // immaterial.
99            state_needs_reset: true,
100            state: bottom_value,
101            pos: CursorPosition::block_entry(mir::START_BLOCK),
102
103            #[cfg(debug_assertions)]
104            reachable_blocks: mir::traversal::reachable_as_bitset(body),
105        }
106    }
107
108    /// Returns a new cursor that takes ownership of and inspects analysis results.
109    pub fn new_owning(
110        body: &'mir mir::Body<'tcx>,
111        analysis: A,
112        results: Results<A::Domain>,
113    ) -> Self {
114        Self::new(body, CowMut::Owned(analysis), Cow::Owned(results))
115    }
116
117    /// Returns a new cursor that borrows and inspects analysis results.
118    pub fn new_borrowing(
119        body: &'mir mir::Body<'tcx>,
120        analysis: &'mir mut A,
121        results: &'mir Results<A::Domain>,
122    ) -> Self {
123        Self::new(body, CowMut::BorrowedMut(analysis), Cow::Borrowed(results))
124    }
125
126    /// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled.
127    #[cfg(test)]
128    pub(crate) fn allow_unreachable(&mut self) {
129        #[cfg(debug_assertions)]
130        self.reachable_blocks.insert_all()
131    }
132
133    /// Returns the `Analysis` used to generate the underlying `Results`.
134    pub fn analysis(&self) -> &A {
135        &self.analysis
136    }
137
138    /// Resets the cursor to hold the entry set for the given basic block.
139    ///
140    /// For forward dataflow analyses, this is the dataflow state prior to the first statement.
141    ///
142    /// For backward dataflow analyses, this is the dataflow state after the terminator.
143    pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) {
144        #[cfg(debug_assertions)]
145        assert!(self.reachable_blocks.contains(block));
146
147        self.state.clone_from(&self.results[block]);
148        self.pos = CursorPosition::block_entry(block);
149        self.state_needs_reset = false;
150    }
151
152    /// Resets the cursor to hold the state prior to the first statement in a basic block.
153    ///
154    /// For forward analyses, this is the entry set for the given block.
155    ///
156    /// For backward analyses, this is the state that will be propagated to its
157    /// predecessors (ignoring edge-specific effects).
158    pub fn seek_to_block_start(&mut self, block: BasicBlock) {
159        if A::Direction::IS_FORWARD {
160            self.seek_to_block_entry(block)
161        } else {
162            self.seek_after(Location { block, statement_index: 0 }, Effect::Primary)
163        }
164    }
165
166    /// Resets the cursor to hold the state after the terminator in a basic block.
167    ///
168    /// For backward analyses, this is the entry set for the given block.
169    ///
170    /// For forward analyses, this is the state that will be propagated to its
171    /// successors (ignoring edge-specific effects).
172    pub fn seek_to_block_end(&mut self, block: BasicBlock) {
173        if A::Direction::IS_BACKWARD {
174            self.seek_to_block_entry(block)
175        } else {
176            self.seek_after(self.body.terminator_loc(block), Effect::Primary)
177        }
178    }
179
180    /// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is
181    /// applied.
182    ///
183    /// The "early" effect at the target location *will be* applied.
184    pub fn seek_before_primary_effect(&mut self, target: Location) {
185        self.seek_after(target, Effect::Early)
186    }
187
188    /// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is
189    /// applied.
190    ///
191    /// The "early" effect at the target location will be applied as well.
192    pub fn seek_after_primary_effect(&mut self, target: Location) {
193        self.seek_after(target, Effect::Primary)
194    }
195
196    fn seek_after(&mut self, target: Location, effect: Effect) {
197        assert!(target <= self.body.terminator_loc(target.block));
198
199        // Reset to the entry of the target block if any of the following are true:
200        //   - A custom effect has been applied to the cursor state.
201        //   - We are in a different block than the target.
202        //   - We are in the same block but have advanced past the target effect.
203        if self.state_needs_reset || self.pos.block != target.block {
204            self.seek_to_block_entry(target.block);
205        } else if let Some(curr_effect) = self.pos.curr_effect_index {
206            let mut ord = curr_effect.statement_index.cmp(&target.statement_index);
207            if A::Direction::IS_BACKWARD {
208                ord = ord.reverse()
209            }
210
211            match ord.then_with(|| curr_effect.effect.cmp(&effect)) {
212                Ordering::Equal => return,
213                Ordering::Greater => self.seek_to_block_entry(target.block),
214                Ordering::Less => {}
215            }
216        }
217
218        // At this point, the cursor is in the same block as the target location at an earlier
219        // statement.
220        debug_assert_eq!(target.block, self.pos.block);
221
222        let block_data = &self.body[target.block];
223        #[rustfmt::skip]
224        let next_effect = if A::Direction::IS_FORWARD {
225            self.pos.curr_effect_index.map_or_else(
226                || Effect::Early.at_index(0),
227                EffectIndex::next_in_forward_order,
228            )
229        } else {
230            self.pos.curr_effect_index.map_or_else(
231                || Effect::Early.at_index(block_data.statements.len()),
232                EffectIndex::next_in_backward_order,
233            )
234        };
235
236        let target_effect_index = effect.at_index(target.statement_index);
237
238        A::Direction::apply_effects_in_range(
239            &mut *self.analysis,
240            &mut self.state,
241            target.block,
242            block_data,
243            next_effect..=target_effect_index,
244        );
245
246        self.pos =
247            CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) };
248    }
249
250    /// Applies `f` to the cursor's internal state.
251    ///
252    /// This can be used, e.g., to apply the call return effect directly to the cursor without
253    /// creating an extra copy of the dataflow state.
254    pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) {
255        f(&mut self.analysis, &mut self.state);
256        self.state_needs_reset = true;
257    }
258}
259
260#[derive(Clone, Copy, Debug)]
261struct CursorPosition {
262    block: BasicBlock,
263    curr_effect_index: Option<EffectIndex>,
264}
265
266impl CursorPosition {
267    fn block_entry(block: BasicBlock) -> CursorPosition {
268        CursorPosition { block, curr_effect_index: None }
269    }
270}
OSZAR »