]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
Auto merge of #99028 - tmiasko:inline, r=estebank
[rust.git] / compiler / rustc_mir_dataflow / src / impls / storage_liveness.rs
1 pub use super::*;
2
3 use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor};
4 use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
5 use rustc_middle::mir::*;
6 use std::cell::RefCell;
7
8 #[derive(Clone)]
9 pub struct MaybeStorageLive {
10     always_live_locals: BitSet<Local>,
11 }
12
13 impl MaybeStorageLive {
14     pub fn new(always_live_locals: BitSet<Local>) -> Self {
15         MaybeStorageLive { always_live_locals }
16     }
17 }
18
19 impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageLive {
20     type Domain = BitSet<Local>;
21
22     const NAME: &'static str = "maybe_storage_live";
23
24     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
25         // bottom = dead
26         BitSet::new_empty(body.local_decls.len())
27     }
28
29     fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
30         assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
31         for local in self.always_live_locals.iter() {
32             on_entry.insert(local);
33         }
34
35         for arg in body.args_iter() {
36             on_entry.insert(arg);
37         }
38     }
39 }
40
41 impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageLive {
42     type Idx = Local;
43
44     fn statement_effect(
45         &self,
46         trans: &mut impl GenKill<Self::Idx>,
47         stmt: &mir::Statement<'tcx>,
48         _: Location,
49     ) {
50         match stmt.kind {
51             StatementKind::StorageLive(l) => trans.gen(l),
52             StatementKind::StorageDead(l) => trans.kill(l),
53             _ => (),
54         }
55     }
56
57     fn terminator_effect(
58         &self,
59         _trans: &mut impl GenKill<Self::Idx>,
60         _: &mir::Terminator<'tcx>,
61         _: Location,
62     ) {
63         // Terminators have no effect
64     }
65
66     fn call_return_effect(
67         &self,
68         _trans: &mut impl GenKill<Self::Idx>,
69         _block: BasicBlock,
70         _return_places: CallReturnPlaces<'_, 'tcx>,
71     ) {
72         // Nothing to do when a call returns successfully
73     }
74 }
75
76 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
77
78 /// Dataflow analysis that determines whether each local requires storage at a
79 /// given location; i.e. whether its storage can go away without being observed.
80 pub struct MaybeRequiresStorage<'mir, 'tcx> {
81     body: &'mir Body<'tcx>,
82     borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
83 }
84
85 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
86     pub fn new(
87         body: &'mir Body<'tcx>,
88         borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
89     ) -> Self {
90         MaybeRequiresStorage {
91             body,
92             borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)),
93         }
94     }
95 }
96
97 impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
98     type Domain = BitSet<Local>;
99
100     const NAME: &'static str = "requires_storage";
101
102     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
103         // bottom = dead
104         BitSet::new_empty(body.local_decls.len())
105     }
106
107     fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut Self::Domain) {
108         // The resume argument is live on function entry (we don't care about
109         // the `self` argument)
110         for arg in body.args_iter().skip(1) {
111             on_entry.insert(arg);
112         }
113     }
114 }
115
116 impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
117     type Idx = Local;
118
119     fn before_statement_effect(
120         &self,
121         trans: &mut impl GenKill<Self::Idx>,
122         stmt: &mir::Statement<'tcx>,
123         loc: Location,
124     ) {
125         // If a place is borrowed in a statement, it needs storage for that statement.
126         self.borrowed_locals.borrow().analysis().statement_effect(trans, stmt, loc);
127
128         match &stmt.kind {
129             StatementKind::StorageDead(l) => trans.kill(*l),
130
131             // If a place is assigned to in a statement, it needs storage for that statement.
132             StatementKind::Assign(box (place, _))
133             | StatementKind::SetDiscriminant { box place, .. }
134             | StatementKind::Deinit(box place) => {
135                 trans.gen(place.local);
136             }
137
138             // Nothing to do for these. Match exhaustively so this fails to compile when new
139             // variants are added.
140             StatementKind::AscribeUserType(..)
141             | StatementKind::Coverage(..)
142             | StatementKind::FakeRead(..)
143             | StatementKind::Nop
144             | StatementKind::Retag(..)
145             | StatementKind::CopyNonOverlapping(..)
146             | StatementKind::StorageLive(..) => {}
147         }
148     }
149
150     fn statement_effect(
151         &self,
152         trans: &mut impl GenKill<Self::Idx>,
153         _: &mir::Statement<'tcx>,
154         loc: Location,
155     ) {
156         // If we move from a place then it only stops needing storage *after*
157         // that statement.
158         self.check_for_move(trans, loc);
159     }
160
161     fn before_terminator_effect(
162         &self,
163         trans: &mut impl GenKill<Self::Idx>,
164         terminator: &mir::Terminator<'tcx>,
165         loc: Location,
166     ) {
167         // If a place is borrowed in a terminator, it needs storage for that terminator.
168         self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc);
169
170         match &terminator.kind {
171             TerminatorKind::Call { destination, .. } => {
172                 trans.gen(destination.local);
173             }
174
175             // Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for
176             // that is that a `yield` will return from the function, and `resume_arg` is written
177             // only when the generator is later resumed. Unlike `Call`, this doesn't require the
178             // place to have storage *before* the yield, only after.
179             TerminatorKind::Yield { .. } => {}
180
181             TerminatorKind::InlineAsm { operands, .. } => {
182                 for op in operands {
183                     match op {
184                         InlineAsmOperand::Out { place, .. }
185                         | InlineAsmOperand::InOut { out_place: place, .. } => {
186                             if let Some(place) = place {
187                                 trans.gen(place.local);
188                             }
189                         }
190                         InlineAsmOperand::In { .. }
191                         | InlineAsmOperand::Const { .. }
192                         | InlineAsmOperand::SymFn { .. }
193                         | InlineAsmOperand::SymStatic { .. } => {}
194                     }
195                 }
196             }
197
198             // Nothing to do for these. Match exhaustively so this fails to compile when new
199             // variants are added.
200             TerminatorKind::Abort
201             | TerminatorKind::Assert { .. }
202             | TerminatorKind::Drop { .. }
203             | TerminatorKind::DropAndReplace { .. }
204             | TerminatorKind::FalseEdge { .. }
205             | TerminatorKind::FalseUnwind { .. }
206             | TerminatorKind::GeneratorDrop
207             | TerminatorKind::Goto { .. }
208             | TerminatorKind::Resume
209             | TerminatorKind::Return
210             | TerminatorKind::SwitchInt { .. }
211             | TerminatorKind::Unreachable => {}
212         }
213     }
214
215     fn terminator_effect(
216         &self,
217         trans: &mut impl GenKill<Self::Idx>,
218         terminator: &mir::Terminator<'tcx>,
219         loc: Location,
220     ) {
221         match terminator.kind {
222             // For call terminators the destination requires storage for the call
223             // and after the call returns successfully, but not after a panic.
224             // Since `propagate_call_unwind` doesn't exist, we have to kill the
225             // destination here, and then gen it again in `call_return_effect`.
226             TerminatorKind::Call { destination, .. } => {
227                 trans.kill(destination.local);
228             }
229
230             // The same applies to InlineAsm outputs.
231             TerminatorKind::InlineAsm { ref operands, .. } => {
232                 CallReturnPlaces::InlineAsm(operands).for_each(|place| trans.kill(place.local));
233             }
234
235             // Nothing to do for these. Match exhaustively so this fails to compile when new
236             // variants are added.
237             TerminatorKind::Yield { .. }
238             | TerminatorKind::Abort
239             | TerminatorKind::Assert { .. }
240             | TerminatorKind::Drop { .. }
241             | TerminatorKind::DropAndReplace { .. }
242             | TerminatorKind::FalseEdge { .. }
243             | TerminatorKind::FalseUnwind { .. }
244             | TerminatorKind::GeneratorDrop
245             | TerminatorKind::Goto { .. }
246             | TerminatorKind::Resume
247             | TerminatorKind::Return
248             | TerminatorKind::SwitchInt { .. }
249             | TerminatorKind::Unreachable => {}
250         }
251
252         self.check_for_move(trans, loc);
253     }
254
255     fn call_return_effect(
256         &self,
257         trans: &mut impl GenKill<Self::Idx>,
258         _block: BasicBlock,
259         return_places: CallReturnPlaces<'_, 'tcx>,
260     ) {
261         return_places.for_each(|place| trans.gen(place.local));
262     }
263
264     fn yield_resume_effect(
265         &self,
266         trans: &mut impl GenKill<Self::Idx>,
267         _resume_block: BasicBlock,
268         resume_place: mir::Place<'tcx>,
269     ) {
270         trans.gen(resume_place.local);
271     }
272 }
273
274 impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
275     /// Kill locals that are fully moved and have not been borrowed.
276     fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) {
277         let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals };
278         visitor.visit_location(&self.body, loc);
279     }
280 }
281
282 struct MoveVisitor<'a, 'mir, 'tcx, T> {
283     borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
284     trans: &'a mut T,
285 }
286
287 impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
288 where
289     T: GenKill<Local>,
290 {
291     fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
292         if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
293             let mut borrowed_locals = self.borrowed_locals.borrow_mut();
294             borrowed_locals.seek_before_primary_effect(loc);
295             if !borrowed_locals.contains(local) {
296                 self.trans.kill(local);
297             }
298         }
299     }
300 }