]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/impls/storage_liveness.rs
bbc4942030ef796902ca3f31789052de634ea919
[rust.git] / src / librustc_mir / dataflow / impls / storage_liveness.rs
1 pub use super::*;
2
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;
9
10 #[derive(Clone)]
11 pub struct MaybeStorageLive {
12     always_live_locals: AlwaysLiveLocals,
13 }
14
15 impl MaybeStorageLive {
16     pub fn new(always_live_locals: AlwaysLiveLocals) -> Self {
17         MaybeStorageLive { always_live_locals }
18     }
19 }
20
21 impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive {
22     type Idx = Local;
23
24     const NAME: &'static str = "maybe_storage_live";
25
26     fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
27         body.local_decls.len()
28     }
29
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);
34         }
35
36         for arg in body.args_iter() {
37             on_entry.insert(arg);
38         }
39     }
40 }
41
42 impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive {
43     fn statement_effect(
44         &self,
45         trans: &mut impl GenKill<Self::Idx>,
46         stmt: &mir::Statement<'tcx>,
47         _: Location,
48     ) {
49         match stmt.kind {
50             StatementKind::StorageLive(l) => trans.gen(l),
51             StatementKind::StorageDead(l) => trans.kill(l),
52             _ => (),
53         }
54     }
55
56     fn terminator_effect(
57         &self,
58         _trans: &mut impl GenKill<Self::Idx>,
59         _: &mir::Terminator<'tcx>,
60         _: Location,
61     ) {
62         // Terminators have no effect
63     }
64
65     fn call_return_effect(
66         &self,
67         _trans: &mut impl GenKill<Self::Idx>,
68         _block: BasicBlock,
69         _func: &mir::Operand<'tcx>,
70         _args: &[mir::Operand<'tcx>],
71         _return_place: mir::Place<'tcx>,
72     ) {
73         // Nothing to do when a call returns successfully
74     }
75 }
76
77 impl BottomValue for MaybeStorageLive {
78     /// bottom = dead
79     const BOTTOM_VALUE: bool = false;
80 }
81
82 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
83
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>>,
89 }
90
91 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
92     pub fn new(
93         body: &'mir Body<'tcx>,
94         borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
95     ) -> Self {
96         MaybeRequiresStorage {
97             body,
98             borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)),
99         }
100     }
101 }
102
103 impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
104     type Idx = Local;
105
106     const NAME: &'static str = "requires_storage";
107
108     fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
109         body.local_decls.len()
110     }
111
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);
117         }
118     }
119 }
120
121 impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
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::FakeRead(..)
149             | StatementKind::Nop
150             | StatementKind::Retag(..)
151             | StatementKind::StorageLive(..) => {}
152         }
153     }
154
155     fn statement_effect(
156         &self,
157         trans: &mut impl GenKill<Self::Idx>,
158         _: &mir::Statement<'tcx>,
159         loc: Location,
160     ) {
161         // If we move from a place then only stops needing storage *after*
162         // that statement.
163         self.check_for_move(trans, loc);
164     }
165
166     fn before_terminator_effect(
167         &self,
168         trans: &mut impl GenKill<Self::Idx>,
169         terminator: &mir::Terminator<'tcx>,
170         loc: Location,
171     ) {
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);
174
175         match &terminator.kind {
176             TerminatorKind::Call { destination: Some((place, _)), .. } => {
177                 trans.gen(place.local);
178             }
179
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 { .. } => {}
185
186             TerminatorKind::InlineAsm { operands, .. } => {
187                 for op in operands {
188                     match op {
189                         InlineAsmOperand::Out { place, .. }
190                         | InlineAsmOperand::InOut { out_place: place, .. } => {
191                             if let Some(place) = place {
192                                 trans.gen(place.local);
193                             }
194                         }
195                         InlineAsmOperand::In { .. }
196                         | InlineAsmOperand::Const { .. }
197                         | InlineAsmOperand::SymFn { .. }
198                         | InlineAsmOperand::SymStatic { .. } => {}
199                     }
200                 }
201             }
202
203             // Nothing to do for these. Match exhaustively so this fails to compile when new
204             // variants are added.
205             TerminatorKind::Call { destination: None, .. }
206             | TerminatorKind::Abort
207             | TerminatorKind::Assert { .. }
208             | TerminatorKind::Drop { .. }
209             | TerminatorKind::DropAndReplace { .. }
210             | TerminatorKind::FalseEdges { .. }
211             | TerminatorKind::FalseUnwind { .. }
212             | TerminatorKind::GeneratorDrop
213             | TerminatorKind::Goto { .. }
214             | TerminatorKind::Resume
215             | TerminatorKind::Return
216             | TerminatorKind::SwitchInt { .. }
217             | TerminatorKind::Unreachable => {}
218         }
219     }
220
221     fn terminator_effect(
222         &self,
223         trans: &mut impl GenKill<Self::Idx>,
224         terminator: &mir::Terminator<'tcx>,
225         loc: Location,
226     ) {
227         match &terminator.kind {
228             // For call terminators the destination requires storage for the call
229             // and after the call returns successfully, but not after a panic.
230             // Since `propagate_call_unwind` doesn't exist, we have to kill the
231             // destination here, and then gen it again in `call_return_effect`.
232             TerminatorKind::Call { destination: Some((place, _)), .. } => {
233                 trans.kill(place.local);
234             }
235
236             // Nothing to do for these. Match exhaustively so this fails to compile when new
237             // variants are added.
238             TerminatorKind::Call { destination: None, .. }
239             | TerminatorKind::Yield { .. }
240             | TerminatorKind::Abort
241             | TerminatorKind::Assert { .. }
242             | TerminatorKind::Drop { .. }
243             | TerminatorKind::DropAndReplace { .. }
244             | TerminatorKind::FalseEdges { .. }
245             | TerminatorKind::FalseUnwind { .. }
246             | TerminatorKind::GeneratorDrop
247             | TerminatorKind::Goto { .. }
248             | TerminatorKind::InlineAsm { .. }
249             | TerminatorKind::Resume
250             | TerminatorKind::Return
251             | TerminatorKind::SwitchInt { .. }
252             | TerminatorKind::Unreachable => {}
253         }
254
255         self.check_for_move(trans, loc);
256     }
257
258     fn call_return_effect(
259         &self,
260         trans: &mut impl GenKill<Self::Idx>,
261         _block: BasicBlock,
262         _func: &mir::Operand<'tcx>,
263         _args: &[mir::Operand<'tcx>],
264         return_place: mir::Place<'tcx>,
265     ) {
266         trans.gen(return_place.local);
267     }
268
269     fn yield_resume_effect(
270         &self,
271         trans: &mut impl GenKill<Self::Idx>,
272         _resume_block: BasicBlock,
273         resume_place: mir::Place<'tcx>,
274     ) {
275         trans.gen(resume_place.local);
276     }
277 }
278
279 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
280     /// Kill locals that are fully moved and have not been borrowed.
281     fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) {
282         let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals };
283         visitor.visit_location(&self.body, loc);
284     }
285 }
286
287 impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> {
288     /// bottom = dead
289     const BOTTOM_VALUE: bool = false;
290 }
291
292 struct MoveVisitor<'a, 'mir, 'tcx, T> {
293     borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
294     trans: &'a mut T,
295 }
296
297 impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
298 where
299     T: GenKill<Local>,
300 {
301     fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) {
302         if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
303             let mut borrowed_locals = self.borrowed_locals.borrow_mut();
304             borrowed_locals.seek_before_primary_effect(loc);
305             if !borrowed_locals.contains(*local) {
306                 self.trans.kill(*local);
307             }
308         }
309     }
310 }