1//! Random access inspection of the results of a dataflow analysis.
23use std::cmp::Ordering;
4use std::ops::Deref;
56#[cfg(debug_assertions)]
7use rustc_index::bit_set::DenseBitSet;
8use rustc_middle::mir::{self, BasicBlock, Location};
910use super::{Analysis, Direction, Effect, EffectIndex, Results};
1112/// This is like `Cow`, but it lacks the `T: ToOwned` bound and doesn't support
13/// `to_owned`/`into_owned`.
14enum SimpleCow<'a, T> {
15 Borrowed(&'a T),
16 Owned(T),
17}
1819impl<T> Dereffor SimpleCow<'_, T> {
20type Target = T;
2122fn deref(&self) -> &T {
23match self {
24 SimpleCow::Borrowed(borrowed) => borrowed,
25 SimpleCow::Owned(owned) => owned,
26 }
27 }
28}
2930/// Allows random access inspection of the results of a dataflow analysis. Use this when you want
31/// to inspect domain values only in certain locations; use `ResultsVisitor` if you want to inspect
32/// domain values in many or all locations.
33///
34/// Because `Results` only has domain values for the entry of each basic block, these inspections
35/// involve some amount of domain value recomputations. This cursor only has linear performance
36/// within a basic block when its statements are visited in the same order as the `DIRECTION` of
37/// the analysis. In the worst case—when statements are visited in *reverse* order—performance will
38/// be quadratic in the number of statements in the block. The order in which basic blocks are
39/// inspected has no impact on performance.
40pub struct ResultsCursor<'mir, 'tcx, A>
41where
42A: Analysis<'tcx>,
43{
44 body: &'mir mir::Body<'tcx>,
45 results: SimpleCow<'mir, Results<'tcx, A>>,
46 state: A::Domain,
4748 pos: CursorPosition,
4950/// Indicates that `state` has been modified with a custom effect.
51 ///
52 /// When this flag is set, we need to reset to an entry set before doing a seek.
53state_needs_reset: bool,
5455#[cfg(debug_assertions)]
56reachable_blocks: DenseBitSet<BasicBlock>,
57}
5859impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
60where
61A: Analysis<'tcx>,
62{
63/// Returns the dataflow state at the current location.
64pub fn get(&self) -> &A::Domain {
65&self.state
66 }
6768/// Returns the body this analysis was run on.
69pub fn body(&self) -> &'mir mir::Body<'tcx> {
70self.body
71 }
7273fn new(body: &'mir mir::Body<'tcx>, results: SimpleCow<'mir, Results<'tcx, A>>) -> Self {
74let bottom_value = results.analysis.bottom_value(body);
75ResultsCursor {
76body,
77results,
7879// Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
80 // it needs to reset to block entry before the first seek. The cursor position is
81 // immaterial.
82state_needs_reset: true,
83 state: bottom_value,
84 pos: CursorPosition::block_entry(mir::START_BLOCK),
8586#[cfg(debug_assertions)]
87reachable_blocks: mir::traversal::reachable_as_bitset(body),
88 }
89 }
9091/// Returns a new cursor that takes ownership of and inspects analysis results.
92pub fn new_owning(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self {
93Self::new(body, SimpleCow::Owned(results))
94 }
9596/// Returns a new cursor that borrows and inspects analysis results.
97pub fn new_borrowing(body: &'mir mir::Body<'tcx>, results: &'mir Results<'tcx, A>) -> Self {
98Self::new(body, SimpleCow::Borrowed(results))
99 }
100101/// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled.
102#[cfg(test)]
103pub(crate) fn allow_unreachable(&mut self) {
104#[cfg(debug_assertions)]
105self.reachable_blocks.insert_all()
106 }
107108/// Returns the `Analysis` used to generate the underlying `Results`.
109pub fn analysis(&self) -> &A {
110&self.results.analysis
111 }
112113/// Resets the cursor to hold the entry set for the given basic block.
114 ///
115 /// For forward dataflow analyses, this is the dataflow state prior to the first statement.
116 ///
117 /// For backward dataflow analyses, this is the dataflow state after the terminator.
118pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) {
119#[cfg(debug_assertions)]
120if !self.reachable_blocks.contains(block) {
::core::panicking::panic("assertion failed: self.reachable_blocks.contains(block)")
};assert!(self.reachable_blocks.contains(block));
121122self.state.clone_from(&self.results.entry_states[block]);
123self.pos = CursorPosition::block_entry(block);
124self.state_needs_reset = false;
125 }
126127/// Resets the cursor to hold the state prior to the first statement in a basic block.
128 ///
129 /// For forward analyses, this is the entry set for the given block.
130 ///
131 /// For backward analyses, this is the state that will be propagated to its
132 /// predecessors (ignoring edge-specific effects).
133pub fn seek_to_block_start(&mut self, block: BasicBlock) {
134if A::Direction::IS_FORWARD {
135self.seek_to_block_entry(block)
136 } else {
137self.seek_after(Location { block, statement_index: 0 }, Effect::Primary)
138 }
139 }
140141/// Resets the cursor to hold the state after the terminator in a basic block.
142 ///
143 /// For backward analyses, this is the entry set for the given block.
144 ///
145 /// For forward analyses, this is the state that will be propagated to its
146 /// successors (ignoring edge-specific effects).
147pub fn seek_to_block_end(&mut self, block: BasicBlock) {
148if A::Direction::IS_BACKWARD {
149self.seek_to_block_entry(block)
150 } else {
151self.seek_after(self.body.terminator_loc(block), Effect::Primary)
152 }
153 }
154155/// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is
156 /// applied.
157 ///
158 /// The "early" effect at the target location *will be* applied.
159pub fn seek_before_primary_effect(&mut self, target: Location) {
160self.seek_after(target, Effect::Early)
161 }
162163/// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is
164 /// applied.
165 ///
166 /// The "early" effect at the target location will be applied as well.
167pub fn seek_after_primary_effect(&mut self, target: Location) {
168self.seek_after(target, Effect::Primary)
169 }
170171fn seek_after(&mut self, target: Location, effect: Effect) {
172if !(target <= self.body.terminator_loc(target.block)) {
::core::panicking::panic("assertion failed: target <= self.body.terminator_loc(target.block)")
};assert!(target <= self.body.terminator_loc(target.block));
173174// Reset to the entry of the target block if any of the following are true:
175 // - A custom effect has been applied to the cursor state.
176 // - We are in a different block than the target.
177 // - We are in the same block but have advanced past the target effect.
178if self.state_needs_reset || self.pos.block != target.block {
179self.seek_to_block_entry(target.block);
180 } else if let Some(curr_effect) = self.pos.curr_effect_index {
181let mut ord = curr_effect.statement_index.cmp(&target.statement_index);
182if A::Direction::IS_BACKWARD {
183ord = ord.reverse()
184 }
185186match ord.then_with(|| curr_effect.effect.cmp(&effect)) {
187 Ordering::Equal => return,
188 Ordering::Greater => self.seek_to_block_entry(target.block),
189 Ordering::Less => {}
190 }
191 }
192193// At this point, the cursor is in the same block as the target location at an earlier
194 // statement.
195if true {
match (&target.block, &self.pos.block) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(target.block, self.pos.block);
196197let block_data = &self.body[target.block];
198#[rustfmt::skip]
199let next_effect = if A::Direction::IS_FORWARD {
200self.pos.curr_effect_index.map_or_else(
201 || Effect::Early.at_index(0),
202EffectIndex::next_in_forward_order,
203 )
204 } else {
205self.pos.curr_effect_index.map_or_else(
206 || Effect::Early.at_index(block_data.statements.len()),
207EffectIndex::next_in_backward_order,
208 )
209 };
210211let target_effect_index = effect.at_index(target.statement_index);
212213 A::Direction::apply_effects_in_range(
214&self.results.analysis,
215&mut self.state,
216target.block,
217block_data,
218next_effect..=target_effect_index,
219 );
220221self.pos =
222CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) };
223 }
224225/// Applies `f` to the cursor's internal state.
226 ///
227 /// This can be used, e.g., to apply the call return effect directly to the cursor without
228 /// creating an extra copy of the dataflow state.
229pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) {
230f(&self.results.analysis, &mut self.state);
231self.state_needs_reset = true;
232 }
233}
234235#[derive(#[automatically_derived]
impl ::core::clone::Clone for CursorPosition {
#[inline]
fn clone(&self) -> CursorPosition {
let _: ::core::clone::AssertParamIsClone<BasicBlock>;
let _: ::core::clone::AssertParamIsClone<Option<EffectIndex>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CursorPosition { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for CursorPosition {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"CursorPosition", "block", &self.block, "curr_effect_index",
&&self.curr_effect_index)
}
}Debug)]
236struct CursorPosition {
237 block: BasicBlock,
238 curr_effect_index: Option<EffectIndex>,
239}
240241impl CursorPosition {
242fn block_entry(block: BasicBlock) -> CursorPosition {
243CursorPosition { block, curr_effect_index: None }
244 }
245}