]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dataflow/impls/storage_liveness.rs
Auto merge of #70416 - mzohreva:mz/sgx-test, r=nikomatsakis
[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             // 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 => {}
201         }
202     }
203
204     fn terminator_effect(
205         &self,
206         trans: &mut impl GenKill<Self::Idx>,
207         terminator: &mir::Terminator<'tcx>,
208         loc: Location,
209     ) {
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);
217             }
218
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 => {}
235         }
236
237         self.check_for_move(trans, loc);
238     }
239
240     fn call_return_effect(
241         &self,
242         trans: &mut impl GenKill<Self::Idx>,
243         _block: BasicBlock,
244         _func: &mir::Operand<'tcx>,
245         _args: &[mir::Operand<'tcx>],
246         return_place: mir::Place<'tcx>,
247     ) {
248         trans.gen(return_place.local);
249     }
250
251     fn yield_resume_effect(
252         &self,
253         trans: &mut impl GenKill<Self::Idx>,
254         _resume_block: BasicBlock,
255         resume_place: mir::Place<'tcx>,
256     ) {
257         trans.gen(resume_place.local);
258     }
259 }
260
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);
266     }
267 }
268
269 impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> {
270     /// bottom = dead
271     const BOTTOM_VALUE: bool = false;
272 }
273
274 struct MoveVisitor<'a, 'mir, 'tcx, T> {
275     borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
276     trans: &'a mut T,
277 }
278
279 impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
280 where
281     T: GenKill<Local>,
282 {
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_primary_effect(loc);
287             if !borrowed_locals.contains(*local) {
288                 self.trans.kill(*local);
289             }
290         }
291     }
292 }