]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/impls/storage_liveness.rs
fdc34f2204b1db9a7fd504d41e81c6f8cb498da8
[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 RequiresStorage<'mir, 'tcx> {
75     body: ReadOnlyBodyAndCache<'mir, 'tcx>,
76     borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
77 }
78
79 impl<'mir, 'tcx: 'mir> RequiresStorage<'mir, 'tcx> {
80     pub fn new(
81         body: ReadOnlyBodyAndCache<'mir, 'tcx>,
82         borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
83     ) -> Self {
84         RequiresStorage {
85             body,
86             borrowed_locals: RefCell::new(ResultsRefCursor::new(*body, borrowed_locals)),
87         }
88     }
89
90     pub fn body(&self) -> &Body<'tcx> {
91         &self.body
92     }
93 }
94
95 impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
96     type Idx = Local;
97     fn name() -> &'static str {
98         "requires_storage"
99     }
100     fn bits_per_block(&self) -> usize {
101         self.body.local_decls.len()
102     }
103
104     fn start_block_effect(&self, on_entry: &mut BitSet<Local>) {
105         // The resume argument is live on function entry (we don't care about
106         // the `self` argument)
107         for arg in self.body.args_iter().skip(1) {
108             on_entry.insert(arg);
109         }
110     }
111
112     fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {
113         let stmt = &self.body[loc.block].statements[loc.statement_index];
114
115         // If a place is borrowed in a statement, it needs storage for that statement.
116         self.borrowed_locals.borrow().analysis().statement_effect(sets, stmt, loc);
117
118         // If a place is assigned to in a statement, it needs storage for that statement.
119         match &stmt.kind {
120             StatementKind::StorageDead(l) => sets.kill(*l),
121             StatementKind::Assign(box (place, _))
122             | StatementKind::SetDiscriminant { box place, .. } => {
123                 sets.gen(place.local);
124             }
125             StatementKind::InlineAsm(box InlineAsm { outputs, .. }) => {
126                 for place in &**outputs {
127                     sets.gen(place.local);
128                 }
129             }
130
131             // Nothing to do for these. Match exhaustively so this fails to compile when new
132             // variants are added.
133             StatementKind::AscribeUserType(..)
134             | StatementKind::FakeRead(..)
135             | StatementKind::Nop
136             | StatementKind::Retag(..)
137             | StatementKind::StorageLive(..) => {}
138         }
139     }
140
141     fn statement_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
142         // If we move from a place then only stops needing storage *after*
143         // that statement.
144         self.check_for_move(sets, loc);
145     }
146
147     fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
148         let terminator = self.body[loc.block].terminator();
149
150         // If a place is borrowed in a terminator, it needs storage for that terminator.
151         self.borrowed_locals.borrow().analysis().terminator_effect(sets, terminator, loc);
152
153         match &terminator.kind {
154             TerminatorKind::Call { destination: Some((Place { local, .. }, _)), .. }
155             | TerminatorKind::Yield { resume_arg: Place { local, .. }, .. } => {
156                 sets.gen(*local);
157             }
158
159             // Nothing to do for these. Match exhaustively so this fails to compile when new
160             // variants are added.
161             TerminatorKind::Call { destination: None, .. }
162             | TerminatorKind::Abort
163             | TerminatorKind::Assert { .. }
164             | TerminatorKind::Drop { .. }
165             | TerminatorKind::DropAndReplace { .. }
166             | TerminatorKind::FalseEdges { .. }
167             | TerminatorKind::FalseUnwind { .. }
168             | TerminatorKind::GeneratorDrop
169             | TerminatorKind::Goto { .. }
170             | TerminatorKind::Resume
171             | TerminatorKind::Return
172             | TerminatorKind::SwitchInt { .. }
173             | TerminatorKind::Unreachable => {}
174         }
175     }
176
177     fn terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
178         match &self.body[loc.block].terminator().kind {
179             // For call terminators the destination requires storage for the call
180             // and after the call returns successfully, but not after a panic.
181             // Since `propagate_call_unwind` doesn't exist, we have to kill the
182             // destination here, and then gen it again in `propagate_call_return`.
183             TerminatorKind::Call { destination: Some((Place { local, .. }, _)), .. } => {
184                 sets.kill(*local);
185             }
186
187             // Nothing to do for these. Match exhaustively so this fails to compile when new
188             // variants are added.
189             TerminatorKind::Call { destination: None, .. }
190             | TerminatorKind::Yield { .. }
191             | TerminatorKind::Abort
192             | TerminatorKind::Assert { .. }
193             | TerminatorKind::Drop { .. }
194             | TerminatorKind::DropAndReplace { .. }
195             | TerminatorKind::FalseEdges { .. }
196             | TerminatorKind::FalseUnwind { .. }
197             | TerminatorKind::GeneratorDrop
198             | TerminatorKind::Goto { .. }
199             | TerminatorKind::Resume
200             | TerminatorKind::Return
201             | TerminatorKind::SwitchInt { .. }
202             | TerminatorKind::Unreachable => {}
203         }
204
205         self.check_for_move(sets, loc);
206     }
207
208     fn propagate_call_return(
209         &self,
210         in_out: &mut BitSet<Local>,
211         _call_bb: mir::BasicBlock,
212         _dest_bb: mir::BasicBlock,
213         dest_place: &mir::Place<'tcx>,
214     ) {
215         in_out.insert(dest_place.local);
216     }
217 }
218
219 impl<'mir, 'tcx> RequiresStorage<'mir, 'tcx> {
220     /// Kill locals that are fully moved and have not been borrowed.
221     fn check_for_move(&self, sets: &mut GenKillSet<Local>, loc: Location) {
222         let mut visitor = MoveVisitor { sets, borrowed_locals: &self.borrowed_locals };
223         visitor.visit_location(self.body, loc);
224     }
225 }
226
227 impl<'mir, 'tcx> BottomValue for RequiresStorage<'mir, 'tcx> {
228     /// bottom = dead
229     const BOTTOM_VALUE: bool = false;
230 }
231
232 struct MoveVisitor<'a, 'mir, 'tcx> {
233     borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
234     sets: &'a mut GenKillSet<Local>,
235 }
236
237 impl<'a, 'mir: 'a, 'tcx> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx> {
238     fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) {
239         if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
240             let mut borrowed_locals = self.borrowed_locals.borrow_mut();
241             borrowed_locals.seek_before(loc);
242             if !borrowed_locals.contains(*local) {
243                 self.sets.kill(*local);
244             }
245         }
246     }
247 }