3 use crate::dataflow::BottomValue;
4 use crate::dataflow::{self, GenKill, Results, ResultsRefCursor};
5 use crate::util::storage::AlwaysLiveLocals;
6 use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
7 use rustc_middle::mir::*;
8 use std::cell::RefCell;
11 pub struct MaybeStorageLive {
12 always_live_locals: AlwaysLiveLocals,
15 impl MaybeStorageLive {
16 pub fn new(always_live_locals: AlwaysLiveLocals) -> Self {
17 MaybeStorageLive { always_live_locals }
21 impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive {
24 const NAME: &'static str = "maybe_storage_live";
26 fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
27 body.local_decls.len()
30 fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) {
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 dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive {
45 trans: &mut impl GenKill<Self::Idx>,
46 stmt: &mir::Statement<'tcx>,
50 StatementKind::StorageLive(l) => trans.gen(l),
51 StatementKind::StorageDead(l) => trans.kill(l),
58 _trans: &mut impl GenKill<Self::Idx>,
59 _: &mir::Terminator<'tcx>,
62 // Terminators have no effect
65 fn call_return_effect(
67 _trans: &mut impl GenKill<Self::Idx>,
69 _func: &mir::Operand<'tcx>,
70 _args: &[mir::Operand<'tcx>],
71 _return_place: mir::Place<'tcx>,
73 // Nothing to do when a call returns successfully
77 impl BottomValue for MaybeStorageLive {
79 const BOTTOM_VALUE: bool = false;
82 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
84 /// Dataflow analysis that determines whether each local requires storage at a
85 /// given location; i.e. whether its storage can go away without being observed.
86 pub struct MaybeRequiresStorage<'mir, 'tcx> {
87 body: &'mir Body<'tcx>,
88 borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
91 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
93 body: &'mir Body<'tcx>,
94 borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
96 MaybeRequiresStorage {
98 borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)),
103 impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
106 const NAME: &'static str = "requires_storage";
108 fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
109 body.local_decls.len()
112 fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) {
113 // The resume argument is live on function entry (we don't care about
114 // the `self` argument)
115 for arg in body.args_iter().skip(1) {
116 on_entry.insert(arg);
121 impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
122 fn before_statement_effect(
124 trans: &mut impl GenKill<Self::Idx>,
125 stmt: &mir::Statement<'tcx>,
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);
132 StatementKind::StorageDead(l) => trans.kill(*l),
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);
139 StatementKind::LlvmInlineAsm(asm) => {
140 for place in &*asm.outputs {
141 trans.gen(place.local);
145 // Nothing to do for these. Match exhaustively so this fails to compile when new
146 // variants are added.
147 StatementKind::AscribeUserType(..)
148 | StatementKind::FakeRead(..)
150 | StatementKind::Retag(..)
151 | StatementKind::StorageLive(..) => {}
157 trans: &mut impl GenKill<Self::Idx>,
158 _: &mir::Statement<'tcx>,
161 // If we move from a place then only stops needing storage *after*
163 self.check_for_move(trans, loc);
166 fn before_terminator_effect(
168 trans: &mut impl GenKill<Self::Idx>,
169 terminator: &mir::Terminator<'tcx>,
172 // If a place is borrowed in a terminator, it needs storage for that terminator.
173 self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc);
175 match &terminator.kind {
176 TerminatorKind::Call { destination: Some((place, _)), .. } => {
177 trans.gen(place.local);
180 // Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for
181 // that is that a `yield` will return from the function, and `resume_arg` is written
182 // only when the generator is later resumed. Unlike `Call`, this doesn't require the
183 // place to have storage *before* the yield, only after.
184 TerminatorKind::Yield { .. } => {}
186 // Nothing to do for these. Match exhaustively so this fails to compile when new
187 // variants are added.
188 TerminatorKind::Call { destination: None, .. }
189 | TerminatorKind::Abort
190 | TerminatorKind::Assert { .. }
191 | TerminatorKind::Drop { .. }
192 | TerminatorKind::DropAndReplace { .. }
193 | TerminatorKind::FalseEdges { .. }
194 | TerminatorKind::FalseUnwind { .. }
195 | TerminatorKind::GeneratorDrop
196 | TerminatorKind::Goto { .. }
197 | TerminatorKind::Resume
198 | TerminatorKind::Return
199 | TerminatorKind::SwitchInt { .. }
200 | TerminatorKind::Unreachable => {}
204 fn terminator_effect(
206 trans: &mut impl GenKill<Self::Idx>,
207 terminator: &mir::Terminator<'tcx>,
210 match &terminator.kind {
211 // For call terminators the destination requires storage for the call
212 // and after the call returns successfully, but not after a panic.
213 // Since `propagate_call_unwind` doesn't exist, we have to kill the
214 // destination here, and then gen it again in `call_return_effect`.
215 TerminatorKind::Call { destination: Some((place, _)), .. } => {
216 trans.kill(place.local);
219 // Nothing to do for these. Match exhaustively so this fails to compile when new
220 // variants are added.
221 TerminatorKind::Call { destination: None, .. }
222 | TerminatorKind::Yield { .. }
223 | TerminatorKind::Abort
224 | TerminatorKind::Assert { .. }
225 | TerminatorKind::Drop { .. }
226 | TerminatorKind::DropAndReplace { .. }
227 | TerminatorKind::FalseEdges { .. }
228 | TerminatorKind::FalseUnwind { .. }
229 | TerminatorKind::GeneratorDrop
230 | TerminatorKind::Goto { .. }
231 | TerminatorKind::Resume
232 | TerminatorKind::Return
233 | TerminatorKind::SwitchInt { .. }
234 | TerminatorKind::Unreachable => {}
237 self.check_for_move(trans, loc);
240 fn call_return_effect(
242 trans: &mut impl GenKill<Self::Idx>,
244 _func: &mir::Operand<'tcx>,
245 _args: &[mir::Operand<'tcx>],
246 return_place: mir::Place<'tcx>,
248 trans.gen(return_place.local);
251 fn yield_resume_effect(
253 trans: &mut BitSet<Self::Idx>,
254 _resume_block: BasicBlock,
255 resume_place: mir::Place<'tcx>,
257 trans.gen(resume_place.local);
261 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
262 /// Kill locals that are fully moved and have not been borrowed.
263 fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) {
264 let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals };
265 visitor.visit_location(&self.body, loc);
269 impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> {
271 const BOTTOM_VALUE: bool = false;
274 struct MoveVisitor<'a, 'mir, 'tcx, T> {
275 borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
279 impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
283 fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) {
284 if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
285 let mut borrowed_locals = self.borrowed_locals.borrow_mut();
286 borrowed_locals.seek_before(loc);
287 if !borrowed_locals.contains(*local) {
288 self.trans.kill(*local);