]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/impls/storage_liveness.rs
Rollup merge of #70038 - DutchGhost:const-forget-tests, r=RalfJung
[rust.git] / src / librustc_mir / dataflow / impls / storage_liveness.rs
1 pub use super::*;
2
3 use crate::dataflow::generic::{self as dataflow, GenKill, Results, ResultsRefCursor};
4 use crate::dataflow::BottomValue;
5 use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
6 use rustc::mir::*;
7 use std::cell::RefCell;
8
9 #[derive(Copy, Clone)]
10 pub struct MaybeStorageLive;
11
12 impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive {
13     type Idx = Local;
14
15     const NAME: &'static str = "maybe_storage_live";
16
17     fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
18         body.local_decls.len()
19     }
20
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) {
25             on_entry.insert(arg);
26         }
27     }
28 }
29
30 impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive {
31     fn statement_effect(
32         &self,
33         trans: &mut impl GenKill<Self::Idx>,
34         stmt: &mir::Statement<'tcx>,
35         _: Location,
36     ) {
37         match stmt.kind {
38             StatementKind::StorageLive(l) => trans.gen(l),
39             StatementKind::StorageDead(l) => trans.kill(l),
40             _ => (),
41         }
42     }
43
44     fn terminator_effect(
45         &self,
46         _trans: &mut impl GenKill<Self::Idx>,
47         _: &mir::Terminator<'tcx>,
48         _: Location,
49     ) {
50         // Terminators have no effect
51     }
52
53     fn call_return_effect(
54         &self,
55         _trans: &mut impl GenKill<Self::Idx>,
56         _block: BasicBlock,
57         _func: &mir::Operand<'tcx>,
58         _args: &[mir::Operand<'tcx>],
59         _return_place: &mir::Place<'tcx>,
60     ) {
61         // Nothing to do when a call returns successfully
62     }
63 }
64
65 impl BottomValue for MaybeStorageLive {
66     /// bottom = dead
67     const BOTTOM_VALUE: bool = false;
68 }
69
70 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
71
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>>,
77 }
78
79 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
80     pub fn new(
81         body: ReadOnlyBodyAndCache<'mir, 'tcx>,
82         borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
83     ) -> Self {
84         MaybeRequiresStorage {
85             body,
86             borrowed_locals: RefCell::new(ResultsRefCursor::new(*body, borrowed_locals)),
87         }
88     }
89 }
90
91 impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
92     type Idx = Local;
93
94     const NAME: &'static str = "requires_storage";
95
96     fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
97         body.local_decls.len()
98     }
99
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);
105         }
106     }
107 }
108
109 impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
110     fn before_statement_effect(
111         &self,
112         trans: &mut impl GenKill<Self::Idx>,
113         stmt: &mir::Statement<'tcx>,
114         loc: Location,
115     ) {
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);
118
119         match &stmt.kind {
120             StatementKind::StorageDead(l) => trans.kill(*l),
121
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);
126             }
127             StatementKind::InlineAsm(asm) => {
128                 for place in &*asm.outputs {
129                     trans.gen(place.local);
130                 }
131             }
132
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(..)
137             | StatementKind::Nop
138             | StatementKind::Retag(..)
139             | StatementKind::StorageLive(..) => {}
140         }
141     }
142
143     fn statement_effect(
144         &self,
145         trans: &mut impl GenKill<Self::Idx>,
146         _: &mir::Statement<'tcx>,
147         loc: Location,
148     ) {
149         // If we move from a place then only stops needing storage *after*
150         // that statement.
151         self.check_for_move(trans, loc);
152     }
153
154     fn before_terminator_effect(
155         &self,
156         trans: &mut impl GenKill<Self::Idx>,
157         terminator: &mir::Terminator<'tcx>,
158         loc: Location,
159     ) {
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);
162
163         match &terminator.kind {
164             TerminatorKind::Call { destination: Some((place, _)), .. } => {
165                 trans.gen(place.local);
166             }
167
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 { .. } => {}
173
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 => {}
189         }
190     }
191
192     fn terminator_effect(
193         &self,
194         trans: &mut impl GenKill<Self::Idx>,
195         terminator: &mir::Terminator<'tcx>,
196         loc: Location,
197     ) {
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);
205             }
206
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 => {}
223         }
224
225         self.check_for_move(trans, loc);
226     }
227
228     fn call_return_effect(
229         &self,
230         trans: &mut impl GenKill<Self::Idx>,
231         _block: BasicBlock,
232         _func: &mir::Operand<'tcx>,
233         _args: &[mir::Operand<'tcx>],
234         return_place: &mir::Place<'tcx>,
235     ) {
236         trans.gen(return_place.local);
237     }
238
239     fn yield_resume_effect(
240         &self,
241         trans: &mut BitSet<Self::Idx>,
242         _resume_block: BasicBlock,
243         resume_place: &mir::Place<'tcx>,
244     ) {
245         trans.gen(resume_place.local);
246     }
247 }
248
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);
254     }
255 }
256
257 impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> {
258     /// bottom = dead
259     const BOTTOM_VALUE: bool = false;
260 }
261
262 struct MoveVisitor<'a, 'mir, 'tcx, T> {
263     borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
264     trans: &'a mut T,
265 }
266
267 impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
268 where
269     T: GenKill<Local>,
270 {
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);
277             }
278         }
279     }
280 }