]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/generic/cursor.rs
Use trailing underscore for helper methods
[rust.git] / src / librustc_mir / dataflow / generic / cursor.rs
1 //! Random access inspection of the results of a dataflow analysis.
2
3 use std::borrow::Borrow;
4
5 use rustc::mir::{self, BasicBlock, Location};
6 use rustc_index::bit_set::BitSet;
7
8 use super::{Analysis, Results};
9
10 /// A `ResultsCursor` that borrows the underlying `Results`.
11 pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;
12
13 /// Allows random access inspection of the results of a dataflow analysis.
14 ///
15 /// This cursor only has linear performance within a basic block when its statements are visited in
16 /// order. In the worst case—when statements are visited in *reverse* order—performance will be
17 /// quadratic in the number of statements in the block. The order in which basic blocks are
18 /// inspected has no impact on performance.
19 ///
20 /// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
21 /// type of ownership is determined by `R` (see `ResultsRefCursor` above).
22 pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
23 where
24     A: Analysis<'tcx>,
25 {
26     body: &'mir mir::Body<'tcx>,
27     results: R,
28     state: BitSet<A::Idx>,
29
30     pos: CursorPosition,
31
32     /// When this flag is set, the cursor is pointing at a `Call` terminator whose call return
33     /// effect has been applied to `state`.
34     ///
35     /// This flag helps to ensure that multiple calls to `seek_after_assume_call_returns` with the
36     /// same target will result in exactly one invocation of `apply_call_return_effect`. It is
37     /// sufficient to clear this only in `seek_to_block_start`, since seeking away from a
38     /// terminator will always require a cursor reset.
39     call_return_effect_applied: bool,
40 }
41
42 impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
43 where
44     A: Analysis<'tcx>,
45     R: Borrow<Results<'tcx, A>>,
46 {
47     /// Returns a new cursor for `results` that points to the start of the `START_BLOCK`.
48     pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
49         ResultsCursor {
50             body,
51             pos: CursorPosition::BlockStart(mir::START_BLOCK),
52             state: results.borrow().entry_sets[mir::START_BLOCK].clone(),
53             call_return_effect_applied: false,
54             results,
55         }
56     }
57
58     /// Returns the `Analysis` used to generate the underlying results.
59     pub fn analysis(&self) -> &A {
60         &self.results.borrow().analysis
61     }
62
63     /// Returns the dataflow state at the current location.
64     pub fn get(&self) -> &BitSet<A::Idx> {
65         &self.state
66     }
67
68     /// Resets the cursor to the start of the given basic block.
69     pub fn seek_to_block_start(&mut self, block: BasicBlock) {
70         self.state.overwrite(&self.results.borrow().entry_sets[block]);
71         self.pos = CursorPosition::BlockStart(block);
72         self.call_return_effect_applied = false;
73     }
74
75     /// Advances the cursor to hold all effects up to and including to the "before" effect of the
76     /// statement (or terminator) at the given location.
77     ///
78     /// If you wish to observe the full effect of a statement or terminator, not just the "before"
79     /// effect, use `seek_after` or `seek_after_assume_call_returns`.
80     pub fn seek_before(&mut self, target: Location) {
81         assert!(target <= self.body.terminator_loc(target.block));
82         self.seek_(target, false);
83     }
84
85     /// Advances the cursor to hold the full effect of all statements (and possibly closing
86     /// terminators) up to and including the `target`.
87     ///
88     /// If the `target` is a `Call` terminator, any call return effect for that terminator will
89     /// **not** be observed. Use `seek_after_assume_call_returns` if you wish to observe the call
90     /// return effect.
91     pub fn seek_after(&mut self, target: Location) {
92         assert!(target <= self.body.terminator_loc(target.block));
93
94         // If we have already applied the call return effect, we are currently pointing at a `Call`
95         // terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo"
96         // the call return effect.
97         if self.call_return_effect_applied {
98             self.seek_to_block_start(target.block);
99         }
100
101         self.seek_(target, true);
102     }
103
104     /// Advances the cursor to hold all effects up to and including of the statement (or
105     /// terminator) at the given location.
106     ///
107     /// If the `target` is a `Call` terminator, any call return effect for that terminator will
108     /// be observed. Use `seek_after` if you do **not** wish to observe the call return effect.
109     pub fn seek_after_assume_call_returns(&mut self, target: Location) {
110         let terminator_loc = self.body.terminator_loc(target.block);
111         assert!(target.statement_index <= terminator_loc.statement_index);
112
113         self.seek_(target, true);
114
115         if target != terminator_loc {
116             return;
117         }
118
119         let terminator = self.body.basic_blocks()[target.block].terminator();
120         if let mir::TerminatorKind::Call {
121             destination: Some((return_place, _)), func, args, ..
122         } = &terminator.kind
123         {
124             if !self.call_return_effect_applied {
125                 self.call_return_effect_applied = true;
126                 self.results.borrow().analysis.apply_call_return_effect(
127                     &mut self.state,
128                     target.block,
129                     func,
130                     args,
131                     return_place,
132                 );
133             }
134         }
135     }
136
137     fn seek_(&mut self, target: Location, apply_after_effect_at_target: bool) {
138         use CursorPosition::*;
139
140         match self.pos {
141             // Return early if we are already at the target location.
142             Before(curr) if curr == target && !apply_after_effect_at_target => return,
143             After(curr) if curr == target && apply_after_effect_at_target => return,
144
145             // Otherwise, we must reset to the start of the target block if...
146
147             // we are in a different block entirely.
148             BlockStart(block) | Before(Location { block, .. }) | After(Location { block, .. })
149                 if block != target.block =>
150             {
151                 self.seek_to_block_start(target.block)
152             }
153
154             // we are in the same block but have advanced past the target statement.
155             Before(curr) | After(curr) if curr.statement_index > target.statement_index => {
156                 self.seek_to_block_start(target.block)
157             }
158
159             // we have already applied the entire effect of a statement but only wish to observe
160             // its "before" effect.
161             After(curr)
162                 if curr.statement_index == target.statement_index
163                     && !apply_after_effect_at_target =>
164             {
165                 self.seek_to_block_start(target.block)
166             }
167
168             // N.B., `call_return_effect_applied` is checked in `seek_after`, not here.
169             _ => (),
170         }
171
172         let analysis = &self.results.borrow().analysis;
173         let block_data = &self.body.basic_blocks()[target.block];
174
175         // At this point, the cursor is in the same block as the target location at an earlier
176         // statement.
177         debug_assert_eq!(target.block, self.pos.block());
178
179         // Find the first statement whose transfer function has not yet been applied.
180         let first_unapplied_statement = match self.pos {
181             BlockStart(_) => 0,
182             After(Location { statement_index, .. }) => statement_index + 1,
183
184             // If we have only applied the "before" effect for the current statement, apply the
185             // remainder before continuing.
186             Before(curr) => {
187                 if curr.statement_index == block_data.statements.len() {
188                     let terminator = block_data.terminator();
189                     analysis.apply_terminator_effect(&mut self.state, terminator, curr);
190                 } else {
191                     let statement = &block_data.statements[curr.statement_index];
192                     analysis.apply_statement_effect(&mut self.state, statement, curr);
193                 }
194
195                 // If all we needed to do was go from `Before` to `After` in the same statement,
196                 // we are now done.
197                 if curr.statement_index == target.statement_index {
198                     debug_assert!(apply_after_effect_at_target);
199                     self.pos = After(target);
200                     return;
201                 }
202
203                 curr.statement_index + 1
204             }
205         };
206
207         // We have now applied all effects prior to `first_unapplied_statement`.
208
209         // Apply the effects of all statements before `target`.
210         let mut location = Location { block: target.block, statement_index: 0 };
211         for statement_index in first_unapplied_statement..target.statement_index {
212             location.statement_index = statement_index;
213             let statement = &block_data.statements[statement_index];
214             analysis.apply_before_statement_effect(&mut self.state, statement, location);
215             analysis.apply_statement_effect(&mut self.state, statement, location);
216         }
217
218         // Apply the effect of the statement (or terminator) at `target`.
219         location.statement_index = target.statement_index;
220         if target.statement_index == block_data.statements.len() {
221             let terminator = &block_data.terminator();
222             analysis.apply_before_terminator_effect(&mut self.state, terminator, location);
223
224             if apply_after_effect_at_target {
225                 analysis.apply_terminator_effect(&mut self.state, terminator, location);
226                 self.pos = After(target);
227             } else {
228                 self.pos = Before(target);
229             }
230         } else {
231             let statement = &block_data.statements[target.statement_index];
232             analysis.apply_before_statement_effect(&mut self.state, statement, location);
233
234             if apply_after_effect_at_target {
235                 analysis.apply_statement_effect(&mut self.state, statement, location);
236                 self.pos = After(target)
237             } else {
238                 self.pos = Before(target);
239             }
240         }
241     }
242 }
243
244 #[derive(Clone, Copy, Debug)]
245 enum CursorPosition {
246     /// No effects within this block have been applied.
247     BlockStart(BasicBlock),
248
249     /// Only the "before" effect of the statement (or terminator) at this location has been
250     /// applied (along with the effects of all previous statements).
251     Before(Location),
252
253     /// The effects of all statements up to and including the one at this location have been
254     /// applied.
255     After(Location),
256 }
257
258 impl CursorPosition {
259     fn block(&self) -> BasicBlock {
260         match *self {
261             Self::BlockStart(block) => block,
262             Self::Before(loc) | Self::After(loc) => loc.block,
263         }
264     }
265 }