3 use crate::dataflow::generic::{self as dataflow, GenKill, Results, ResultsRefCursor};
4 use crate::dataflow::BottomValue;
5 use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
7 use std::cell::RefCell;
10 pub struct MaybeStorageLive;
12 impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive {
15 const NAME: &'static str = "maybe_storage_live";
17 fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
18 body.local_decls.len()
21 fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) {
22 // The resume argument is live on function entry (we don't care about
23 // the `self` argument)
24 for arg in body.args_iter().skip(1) {
30 impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive {
33 trans: &mut impl GenKill<Self::Idx>,
34 stmt: &mir::Statement<'tcx>,
38 StatementKind::StorageLive(l) => trans.gen(l),
39 StatementKind::StorageDead(l) => trans.kill(l),
46 _trans: &mut impl GenKill<Self::Idx>,
47 _: &mir::Terminator<'tcx>,
50 // Terminators have no effect
53 fn call_return_effect(
55 _trans: &mut impl GenKill<Self::Idx>,
57 _func: &mir::Operand<'tcx>,
58 _args: &[mir::Operand<'tcx>],
59 _return_place: &mir::Place<'tcx>,
61 // Nothing to do when a call returns successfully
65 impl BottomValue for MaybeStorageLive {
67 const BOTTOM_VALUE: bool = false;
70 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
72 /// Dataflow analysis that determines whether each local requires storage at a
73 /// given location; i.e. whether its storage can go away without being observed.
74 pub struct MaybeRequiresStorage<'mir, 'tcx> {
75 body: ReadOnlyBodyAndCache<'mir, 'tcx>,
76 borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
79 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
81 body: ReadOnlyBodyAndCache<'mir, 'tcx>,
82 borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
84 MaybeRequiresStorage {
86 borrowed_locals: RefCell::new(ResultsRefCursor::new(*body, borrowed_locals)),
91 impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
94 const NAME: &'static str = "requires_storage";
96 fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
97 body.local_decls.len()
100 fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) {
101 // The resume argument is live on function entry (we don't care about
102 // the `self` argument)
103 for arg in body.args_iter().skip(1) {
104 on_entry.insert(arg);
109 impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
110 fn before_statement_effect(
112 trans: &mut impl GenKill<Self::Idx>,
113 stmt: &mir::Statement<'tcx>,
116 // If a place is borrowed in a statement, it needs storage for that statement.
117 self.borrowed_locals.borrow().analysis().statement_effect(trans, stmt, loc);
120 StatementKind::StorageDead(l) => trans.kill(*l),
122 // If a place is assigned to in a statement, it needs storage for that statement.
123 StatementKind::Assign(box (place, _))
124 | StatementKind::SetDiscriminant { box place, .. } => {
125 trans.gen(place.local);
127 StatementKind::InlineAsm(asm) => {
128 for place in &*asm.outputs {
129 trans.gen(place.local);
133 // Nothing to do for these. Match exhaustively so this fails to compile when new
134 // variants are added.
135 StatementKind::AscribeUserType(..)
136 | StatementKind::FakeRead(..)
138 | StatementKind::Retag(..)
139 | StatementKind::StorageLive(..) => {}
145 trans: &mut impl GenKill<Self::Idx>,
146 _: &mir::Statement<'tcx>,
149 // If we move from a place then only stops needing storage *after*
151 self.check_for_move(trans, loc);
154 fn before_terminator_effect(
156 trans: &mut impl GenKill<Self::Idx>,
157 terminator: &mir::Terminator<'tcx>,
160 // If a place is borrowed in a terminator, it needs storage for that terminator.
161 self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc);
163 match &terminator.kind {
164 TerminatorKind::Call { destination: Some((place, _)), .. } => {
165 trans.gen(place.local);
168 // Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for
169 // that is that a `yield` will return from the function, and `resume_arg` is written
170 // only when the generator is later resumed. Unlike `Call`, this doesn't require the
171 // place to have storage *before* the yield, only after.
172 TerminatorKind::Yield { .. } => {}
174 // Nothing to do for these. Match exhaustively so this fails to compile when new
175 // variants are added.
176 TerminatorKind::Call { destination: None, .. }
177 | TerminatorKind::Abort
178 | TerminatorKind::Assert { .. }
179 | TerminatorKind::Drop { .. }
180 | TerminatorKind::DropAndReplace { .. }
181 | TerminatorKind::FalseEdges { .. }
182 | TerminatorKind::FalseUnwind { .. }
183 | TerminatorKind::GeneratorDrop
184 | TerminatorKind::Goto { .. }
185 | TerminatorKind::Resume
186 | TerminatorKind::Return
187 | TerminatorKind::SwitchInt { .. }
188 | TerminatorKind::Unreachable => {}
192 fn terminator_effect(
194 trans: &mut impl GenKill<Self::Idx>,
195 terminator: &mir::Terminator<'tcx>,
198 match &terminator.kind {
199 // For call terminators the destination requires storage for the call
200 // and after the call returns successfully, but not after a panic.
201 // Since `propagate_call_unwind` doesn't exist, we have to kill the
202 // destination here, and then gen it again in `call_return_effect`.
203 TerminatorKind::Call { destination: Some((place, _)), .. } => {
204 trans.kill(place.local);
207 // Nothing to do for these. Match exhaustively so this fails to compile when new
208 // variants are added.
209 TerminatorKind::Call { destination: None, .. }
210 | TerminatorKind::Yield { .. }
211 | TerminatorKind::Abort
212 | TerminatorKind::Assert { .. }
213 | TerminatorKind::Drop { .. }
214 | TerminatorKind::DropAndReplace { .. }
215 | TerminatorKind::FalseEdges { .. }
216 | TerminatorKind::FalseUnwind { .. }
217 | TerminatorKind::GeneratorDrop
218 | TerminatorKind::Goto { .. }
219 | TerminatorKind::Resume
220 | TerminatorKind::Return
221 | TerminatorKind::SwitchInt { .. }
222 | TerminatorKind::Unreachable => {}
225 self.check_for_move(trans, loc);
228 fn call_return_effect(
230 trans: &mut impl GenKill<Self::Idx>,
232 _func: &mir::Operand<'tcx>,
233 _args: &[mir::Operand<'tcx>],
234 return_place: &mir::Place<'tcx>,
236 trans.gen(return_place.local);
239 fn yield_resume_effect(
241 trans: &mut BitSet<Self::Idx>,
242 _resume_block: BasicBlock,
243 resume_place: &mir::Place<'tcx>,
245 trans.gen(resume_place.local);
249 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
250 /// Kill locals that are fully moved and have not been borrowed.
251 fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) {
252 let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals };
253 visitor.visit_location(self.body, loc);
257 impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> {
259 const BOTTOM_VALUE: bool = false;
262 struct MoveVisitor<'a, 'mir, 'tcx, T> {
263 borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
267 impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
271 fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) {
272 if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
273 let mut borrowed_locals = self.borrowed_locals.borrow_mut();
274 borrowed_locals.seek_before(loc);
275 if !borrowed_locals.contains(*local) {
276 self.trans.kill(*local);