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