3 use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor};
4 use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
5 use rustc_middle::mir::*;
7 use std::cell::RefCell;
10 pub struct MaybeStorageLive<'a> {
11 always_live_locals: Cow<'a, BitSet<Local>>,
14 impl<'a> MaybeStorageLive<'a> {
15 pub fn new(always_live_locals: Cow<'a, BitSet<Local>>) -> Self {
16 MaybeStorageLive { always_live_locals }
20 impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
21 type Domain = BitSet<Local>;
23 const NAME: &'static str = "maybe_storage_live";
25 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
27 BitSet::new_empty(body.local_decls.len())
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);
36 for arg in body.args_iter() {
42 impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
47 trans: &mut impl GenKill<Self::Idx>,
48 stmt: &mir::Statement<'tcx>,
52 StatementKind::StorageLive(l) => trans.gen(l),
53 StatementKind::StorageDead(l) => trans.kill(l),
60 _trans: &mut impl GenKill<Self::Idx>,
61 _: &mir::Terminator<'tcx>,
64 // Terminators have no effect
67 fn call_return_effect(
69 _trans: &mut impl GenKill<Self::Idx>,
71 _return_places: CallReturnPlaces<'_, 'tcx>,
73 // Nothing to do when a call returns successfully
77 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
79 /// Dataflow analysis that determines whether each local requires storage at a
80 /// given location; i.e. whether its storage can go away without being observed.
81 pub struct MaybeRequiresStorage<'mir, 'tcx> {
82 body: &'mir Body<'tcx>,
83 borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
86 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
88 body: &'mir Body<'tcx>,
89 borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
91 MaybeRequiresStorage {
93 borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)),
98 impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
99 type Domain = BitSet<Local>;
101 const NAME: &'static str = "requires_storage";
103 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
105 BitSet::new_empty(body.local_decls.len())
108 fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
109 // The resume argument is live on function entry (we don't care about
110 // the `self` argument)
111 for arg in body.args_iter().skip(1) {
112 on_entry.insert(arg);
117 impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
120 fn before_statement_effect(
122 trans: &mut impl GenKill<Self::Idx>,
123 stmt: &mir::Statement<'tcx>,
126 // If a place is borrowed in a statement, it needs storage for that statement.
127 self.borrowed_locals.borrow().analysis().statement_effect(trans, stmt, loc);
130 StatementKind::StorageDead(l) => trans.kill(*l),
132 // If a place is assigned to in a statement, it needs storage for that statement.
133 StatementKind::Assign(box (place, _))
134 | StatementKind::SetDiscriminant { box place, .. }
135 | StatementKind::Deinit(box place) => {
136 trans.gen(place.local);
139 // Nothing to do for these. Match exhaustively so this fails to compile when new
140 // variants are added.
141 StatementKind::AscribeUserType(..)
142 | StatementKind::Coverage(..)
143 | StatementKind::FakeRead(..)
145 | StatementKind::Retag(..)
146 | StatementKind::Intrinsic(..)
147 | StatementKind::StorageLive(..) => {}
153 trans: &mut impl GenKill<Self::Idx>,
154 _: &mir::Statement<'tcx>,
157 // If we move from a place then it only stops needing storage *after*
159 self.check_for_move(trans, loc);
162 fn before_terminator_effect(
164 trans: &mut impl GenKill<Self::Idx>,
165 terminator: &mir::Terminator<'tcx>,
168 // If a place is borrowed in a terminator, it needs storage for that terminator.
169 self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc);
171 match &terminator.kind {
172 TerminatorKind::Call { destination, .. } => {
173 trans.gen(destination.local);
176 // Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for
177 // that is that a `yield` will return from the function, and `resume_arg` is written
178 // only when the generator is later resumed. Unlike `Call`, this doesn't require the
179 // place to have storage *before* the yield, only after.
180 TerminatorKind::Yield { .. } => {}
182 TerminatorKind::InlineAsm { operands, .. } => {
185 InlineAsmOperand::Out { place, .. }
186 | InlineAsmOperand::InOut { out_place: place, .. } => {
187 if let Some(place) = place {
188 trans.gen(place.local);
191 InlineAsmOperand::In { .. }
192 | InlineAsmOperand::Const { .. }
193 | InlineAsmOperand::SymFn { .. }
194 | InlineAsmOperand::SymStatic { .. } => {}
199 // Nothing to do for these. Match exhaustively so this fails to compile when new
200 // variants are added.
201 TerminatorKind::Abort
202 | TerminatorKind::Assert { .. }
203 | TerminatorKind::Drop { .. }
204 | TerminatorKind::DropAndReplace { .. }
205 | TerminatorKind::FalseEdge { .. }
206 | TerminatorKind::FalseUnwind { .. }
207 | TerminatorKind::GeneratorDrop
208 | TerminatorKind::Goto { .. }
209 | TerminatorKind::Resume
210 | TerminatorKind::Return
211 | TerminatorKind::SwitchInt { .. }
212 | TerminatorKind::Unreachable => {}
216 fn terminator_effect(
218 trans: &mut impl GenKill<Self::Idx>,
219 terminator: &mir::Terminator<'tcx>,
222 match terminator.kind {
223 // For call terminators the destination requires storage for the call
224 // and after the call returns successfully, but not after a panic.
225 // Since `propagate_call_unwind` doesn't exist, we have to kill the
226 // destination here, and then gen it again in `call_return_effect`.
227 TerminatorKind::Call { destination, .. } => {
228 trans.kill(destination.local);
231 // The same applies to InlineAsm outputs.
232 TerminatorKind::InlineAsm { ref operands, .. } => {
233 CallReturnPlaces::InlineAsm(operands).for_each(|place| trans.kill(place.local));
236 // Nothing to do for these. Match exhaustively so this fails to compile when new
237 // variants are added.
238 TerminatorKind::Yield { .. }
239 | TerminatorKind::Abort
240 | TerminatorKind::Assert { .. }
241 | TerminatorKind::Drop { .. }
242 | TerminatorKind::DropAndReplace { .. }
243 | TerminatorKind::FalseEdge { .. }
244 | TerminatorKind::FalseUnwind { .. }
245 | TerminatorKind::GeneratorDrop
246 | TerminatorKind::Goto { .. }
247 | TerminatorKind::Resume
248 | TerminatorKind::Return
249 | TerminatorKind::SwitchInt { .. }
250 | TerminatorKind::Unreachable => {}
253 self.check_for_move(trans, loc);
256 fn call_return_effect(
258 trans: &mut impl GenKill<Self::Idx>,
260 return_places: CallReturnPlaces<'_, 'tcx>,
262 return_places.for_each(|place| trans.gen(place.local));
265 fn yield_resume_effect(
267 trans: &mut impl GenKill<Self::Idx>,
268 _resume_block: BasicBlock,
269 resume_place: mir::Place<'tcx>,
271 trans.gen(resume_place.local);
275 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
276 /// Kill locals that are fully moved and have not been borrowed.
277 fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) {
278 let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals };
279 visitor.visit_location(&self.body, loc);
283 struct MoveVisitor<'a, 'mir, 'tcx, T> {
284 borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
288 impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
292 fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
293 if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
294 let mut borrowed_locals = self.borrowed_locals.borrow_mut();
295 borrowed_locals.seek_before_primary_effect(loc);
296 if !borrowed_locals.contains(local) {
297 self.trans.kill(local);