1 pub use super::*; 2 3 use crate::storage::AlwaysLiveLocals; 4 use crate::{GenKill, Results, ResultsRefCursor}; 5 use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; 6 use rustc_middle::mir::*; 7 use std::cell::RefCell; 8 9 #[derive(Clone)] 10 pub struct MaybeStorageLive { 11 always_live_locals: AlwaysLiveLocals, 12 } 13 14 impl MaybeStorageLive { new(always_live_locals: AlwaysLiveLocals) -> Self15 pub fn new(always_live_locals: AlwaysLiveLocals) -> Self { 16 MaybeStorageLive { always_live_locals } 17 } 18 } 19 20 impl crate::AnalysisDomain<'tcx> for MaybeStorageLive { 21 type Domain = BitSet<Local>; 22 23 const NAME: &'static str = "maybe_storage_live"; 24 bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain25 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { 26 // bottom = dead 27 BitSet::new_empty(body.local_decls.len()) 28 } 29 initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain)30 fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) { 31 assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size()); 32 for local in self.always_live_locals.iter() { 33 on_entry.insert(local); 34 } 35 36 for arg in body.args_iter() { 37 on_entry.insert(arg); 38 } 39 } 40 } 41 42 impl crate::GenKillAnalysis<'tcx> for MaybeStorageLive { 43 type Idx = Local; 44 statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: Location, )45 fn statement_effect( 46 &self, 47 trans: &mut impl GenKill<Self::Idx>, 48 stmt: &mir::Statement<'tcx>, 49 _: Location, 50 ) { 51 match stmt.kind { 52 StatementKind::StorageLive(l) => trans.gen(l), 53 StatementKind::StorageDead(l) => trans.kill(l), 54 _ => (), 55 } 56 } 57 terminator_effect( &self, _trans: &mut impl GenKill<Self::Idx>, _: &mir::Terminator<'tcx>, _: Location, )58 fn terminator_effect( 59 &self, 60 _trans: &mut impl GenKill<Self::Idx>, 61 _: &mir::Terminator<'tcx>, 62 _: Location, 63 ) { 64 // Terminators have no effect 65 } 66 call_return_effect( &self, _trans: &mut impl GenKill<Self::Idx>, _block: BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], _return_place: mir::Place<'tcx>, )67 fn call_return_effect( 68 &self, 69 _trans: &mut impl GenKill<Self::Idx>, 70 _block: BasicBlock, 71 _func: &mir::Operand<'tcx>, 72 _args: &[mir::Operand<'tcx>], 73 _return_place: mir::Place<'tcx>, 74 ) { 75 // Nothing to do when a call returns successfully 76 } 77 } 78 79 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>; 80 81 /// Dataflow analysis that determines whether each local requires storage at a 82 /// given location; i.e. whether its storage can go away without being observed. 83 pub struct MaybeRequiresStorage<'mir, 'tcx> { 84 body: &'mir Body<'tcx>, 85 borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>, 86 } 87 88 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { new( body: &'mir Body<'tcx>, borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>, ) -> Self89 pub fn new( 90 body: &'mir Body<'tcx>, 91 borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>, 92 ) -> Self { 93 MaybeRequiresStorage { 94 body, 95 borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)), 96 } 97 } 98 } 99 100 impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { 101 type Domain = BitSet<Local>; 102 103 const NAME: &'static str = "requires_storage"; 104 bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain105 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { 106 // bottom = dead 107 BitSet::new_empty(body.local_decls.len()) 108 } 109 initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain)110 fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) { 111 // The resume argument is live on function entry (we don't care about 112 // the `self` argument) 113 for arg in body.args_iter().skip(1) { 114 on_entry.insert(arg); 115 } 116 } 117 } 118 119 impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { 120 type Idx = Local; 121 before_statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, loc: Location, )122 fn before_statement_effect( 123 &self, 124 trans: &mut impl GenKill<Self::Idx>, 125 stmt: &mir::Statement<'tcx>, 126 loc: Location, 127 ) { 128 // If a place is borrowed in a statement, it needs storage for that statement. 129 self.borrowed_locals.borrow().analysis().statement_effect(trans, stmt, loc); 130 131 match &stmt.kind { 132 StatementKind::StorageDead(l) => trans.kill(*l), 133 134 // If a place is assigned to in a statement, it needs storage for that statement. 135 StatementKind::Assign(box (place, _)) 136 | StatementKind::SetDiscriminant { box place, .. } => { 137 trans.gen(place.local); 138 } 139 StatementKind::LlvmInlineAsm(asm) => { 140 for place in &*asm.outputs { 141 trans.gen(place.local); 142 } 143 } 144 145 // Nothing to do for these. Match exhaustively so this fails to compile when new 146 // variants are added. 147 StatementKind::AscribeUserType(..) 148 | StatementKind::Coverage(..) 149 | StatementKind::FakeRead(..) 150 | StatementKind::Nop 151 | StatementKind::Retag(..) 152 | StatementKind::CopyNonOverlapping(..) 153 | StatementKind::StorageLive(..) => {} 154 } 155 } 156 statement_effect( &self, trans: &mut impl GenKill<Self::Idx>, _: &mir::Statement<'tcx>, loc: Location, )157 fn statement_effect( 158 &self, 159 trans: &mut impl GenKill<Self::Idx>, 160 _: &mir::Statement<'tcx>, 161 loc: Location, 162 ) { 163 // If we move from a place then only stops needing storage *after* 164 // that statement. 165 self.check_for_move(trans, loc); 166 } 167 before_terminator_effect( &self, trans: &mut impl GenKill<Self::Idx>, terminator: &mir::Terminator<'tcx>, loc: Location, )168 fn before_terminator_effect( 169 &self, 170 trans: &mut impl GenKill<Self::Idx>, 171 terminator: &mir::Terminator<'tcx>, 172 loc: Location, 173 ) { 174 // If a place is borrowed in a terminator, it needs storage for that terminator. 175 self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc); 176 177 match &terminator.kind { 178 TerminatorKind::Call { destination: Some((place, _)), .. } => { 179 trans.gen(place.local); 180 } 181 182 // Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for 183 // that is that a `yield` will return from the function, and `resume_arg` is written 184 // only when the generator is later resumed. Unlike `Call`, this doesn't require the 185 // place to have storage *before* the yield, only after. 186 TerminatorKind::Yield { .. } => {} 187 188 TerminatorKind::InlineAsm { operands, .. } => { 189 for op in operands { 190 match op { 191 InlineAsmOperand::Out { place, .. } 192 | InlineAsmOperand::InOut { out_place: place, .. } => { 193 if let Some(place) = place { 194 trans.gen(place.local); 195 } 196 } 197 InlineAsmOperand::In { .. } 198 | InlineAsmOperand::Const { .. } 199 | InlineAsmOperand::SymFn { .. } 200 | InlineAsmOperand::SymStatic { .. } => {} 201 } 202 } 203 } 204 205 // Nothing to do for these. Match exhaustively so this fails to compile when new 206 // variants are added. 207 TerminatorKind::Call { destination: None, .. } 208 | TerminatorKind::Abort 209 | TerminatorKind::Assert { .. } 210 | TerminatorKind::Drop { .. } 211 | TerminatorKind::DropAndReplace { .. } 212 | TerminatorKind::FalseEdge { .. } 213 | TerminatorKind::FalseUnwind { .. } 214 | TerminatorKind::GeneratorDrop 215 | TerminatorKind::Goto { .. } 216 | TerminatorKind::Resume 217 | TerminatorKind::Return 218 | TerminatorKind::SwitchInt { .. } 219 | TerminatorKind::Unreachable => {} 220 } 221 } 222 terminator_effect( &self, trans: &mut impl GenKill<Self::Idx>, terminator: &mir::Terminator<'tcx>, loc: Location, )223 fn terminator_effect( 224 &self, 225 trans: &mut impl GenKill<Self::Idx>, 226 terminator: &mir::Terminator<'tcx>, 227 loc: Location, 228 ) { 229 match &terminator.kind { 230 // For call terminators the destination requires storage for the call 231 // and after the call returns successfully, but not after a panic. 232 // Since `propagate_call_unwind` doesn't exist, we have to kill the 233 // destination here, and then gen it again in `call_return_effect`. 234 TerminatorKind::Call { destination: Some((place, _)), .. } => { 235 trans.kill(place.local); 236 } 237 238 // Nothing to do for these. Match exhaustively so this fails to compile when new 239 // variants are added. 240 TerminatorKind::Call { destination: None, .. } 241 | TerminatorKind::Yield { .. } 242 | TerminatorKind::Abort 243 | TerminatorKind::Assert { .. } 244 | TerminatorKind::Drop { .. } 245 | TerminatorKind::DropAndReplace { .. } 246 | TerminatorKind::FalseEdge { .. } 247 | TerminatorKind::FalseUnwind { .. } 248 | TerminatorKind::GeneratorDrop 249 | TerminatorKind::Goto { .. } 250 | TerminatorKind::InlineAsm { .. } 251 | TerminatorKind::Resume 252 | TerminatorKind::Return 253 | TerminatorKind::SwitchInt { .. } 254 | TerminatorKind::Unreachable => {} 255 } 256 257 self.check_for_move(trans, loc); 258 } 259 call_return_effect( &self, trans: &mut impl GenKill<Self::Idx>, _block: BasicBlock, _func: &mir::Operand<'tcx>, _args: &[mir::Operand<'tcx>], return_place: mir::Place<'tcx>, )260 fn call_return_effect( 261 &self, 262 trans: &mut impl GenKill<Self::Idx>, 263 _block: BasicBlock, 264 _func: &mir::Operand<'tcx>, 265 _args: &[mir::Operand<'tcx>], 266 return_place: mir::Place<'tcx>, 267 ) { 268 trans.gen(return_place.local); 269 } 270 yield_resume_effect( &self, trans: &mut impl GenKill<Self::Idx>, _resume_block: BasicBlock, resume_place: mir::Place<'tcx>, )271 fn yield_resume_effect( 272 &self, 273 trans: &mut impl GenKill<Self::Idx>, 274 _resume_block: BasicBlock, 275 resume_place: mir::Place<'tcx>, 276 ) { 277 trans.gen(resume_place.local); 278 } 279 } 280 281 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { 282 /// Kill locals that are fully moved and have not been borrowed. check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location)283 fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) { 284 let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals }; 285 visitor.visit_location(&self.body, loc); 286 } 287 } 288 289 struct MoveVisitor<'a, 'mir, 'tcx, T> { 290 borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>, 291 trans: &'a mut T, 292 } 293 294 impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T> 295 where 296 T: GenKill<Local>, 297 { visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location)298 fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) { 299 if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context { 300 let mut borrowed_locals = self.borrowed_locals.borrow_mut(); 301 borrowed_locals.seek_before_primary_effect(loc); 302 if !borrowed_locals.contains(*local) { 303 self.trans.kill(*local); 304 } 305 } 306 } 307 } 308