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