]>
Commit | Line | Data |
---|---|---|
ea8adc8c XL |
1 | pub use super::*; |
2 | ||
74b04a01 | 3 | use crate::dataflow::BottomValue; |
ba9703b0 XL |
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::*; | |
dfeec247 | 8 | use std::cell::RefCell; |
ea8adc8c | 9 | |
ba9703b0 XL |
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 | } | |
ea8adc8c | 20 | |
74b04a01 XL |
21 | impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive { |
22 | type Idx = Local; | |
ea8adc8c | 23 | |
74b04a01 | 24 | const NAME: &'static str = "maybe_storage_live"; |
ea8adc8c | 25 | |
74b04a01 XL |
26 | fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { |
27 | body.local_decls.len() | |
ea8adc8c XL |
28 | } |
29 | ||
74b04a01 | 30 | fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) { |
ba9703b0 XL |
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() { | |
74b04a01 XL |
37 | on_entry.insert(arg); |
38 | } | |
ea8adc8c | 39 | } |
74b04a01 | 40 | } |
ea8adc8c | 41 | |
74b04a01 XL |
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 | ) { | |
ea8adc8c | 49 | match stmt.kind { |
dc9dc135 XL |
50 | StatementKind::StorageLive(l) => trans.gen(l), |
51 | StatementKind::StorageDead(l) => trans.kill(l), | |
ea8adc8c XL |
52 | _ => (), |
53 | } | |
54 | } | |
55 | ||
74b04a01 XL |
56 | fn terminator_effect( |
57 | &self, | |
58 | _trans: &mut impl GenKill<Self::Idx>, | |
59 | _: &mir::Terminator<'tcx>, | |
60 | _: Location, | |
61 | ) { | |
ea8adc8c XL |
62 | // Terminators have no effect |
63 | } | |
64 | ||
74b04a01 | 65 | fn call_return_effect( |
0731742a | 66 | &self, |
74b04a01 XL |
67 | _trans: &mut impl GenKill<Self::Idx>, |
68 | _block: BasicBlock, | |
69 | _func: &mir::Operand<'tcx>, | |
70 | _args: &[mir::Operand<'tcx>], | |
ba9703b0 | 71 | _return_place: mir::Place<'tcx>, |
0731742a | 72 | ) { |
ea8adc8c XL |
73 | // Nothing to do when a call returns successfully |
74 | } | |
75 | } | |
76 | ||
74b04a01 | 77 | impl BottomValue for MaybeStorageLive { |
dc9dc135 XL |
78 | /// bottom = dead |
79 | const BOTTOM_VALUE: bool = false; | |
80 | } | |
81 | ||
74b04a01 XL |
82 | type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>; |
83 | ||
dc9dc135 XL |
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. | |
74b04a01 | 86 | pub struct MaybeRequiresStorage<'mir, 'tcx> { |
f9f354fc | 87 | body: &'mir Body<'tcx>, |
74b04a01 | 88 | borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>, |
dc9dc135 XL |
89 | } |
90 | ||
74b04a01 | 91 | impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { |
dc9dc135 | 92 | pub fn new( |
f9f354fc | 93 | body: &'mir Body<'tcx>, |
74b04a01 | 94 | borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>, |
dc9dc135 | 95 | ) -> Self { |
74b04a01 | 96 | MaybeRequiresStorage { |
dc9dc135 | 97 | body, |
ba9703b0 | 98 | borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)), |
dc9dc135 XL |
99 | } |
100 | } | |
dc9dc135 XL |
101 | } |
102 | ||
74b04a01 | 103 | impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { |
dc9dc135 | 104 | type Idx = Local; |
74b04a01 XL |
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() | |
dc9dc135 XL |
110 | } |
111 | ||
74b04a01 XL |
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 | } | |
dc9dc135 | 118 | } |
74b04a01 | 119 | } |
dc9dc135 | 120 | |
74b04a01 XL |
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); | |
dc9dc135 | 130 | |
74b04a01 XL |
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); | |
dc9dc135 | 138 | } |
ba9703b0 | 139 | StatementKind::LlvmInlineAsm(asm) => { |
74b04a01 XL |
140 | for place in &*asm.outputs { |
141 | trans.gen(place.local); | |
dc9dc135 XL |
142 | } |
143 | } | |
74b04a01 XL |
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(..) => {} | |
dc9dc135 XL |
152 | } |
153 | } | |
154 | ||
74b04a01 XL |
155 | fn statement_effect( |
156 | &self, | |
157 | trans: &mut impl GenKill<Self::Idx>, | |
158 | _: &mir::Statement<'tcx>, | |
159 | loc: Location, | |
160 | ) { | |
e74abb32 XL |
161 | // If we move from a place then only stops needing storage *after* |
162 | // that statement. | |
74b04a01 | 163 | self.check_for_move(trans, loc); |
e74abb32 XL |
164 | } |
165 | ||
74b04a01 XL |
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); | |
e74abb32 | 174 | |
74b04a01 | 175 | match &terminator.kind { |
ba9703b0 | 176 | TerminatorKind::Call { destination: Some((place, _)), .. } => { |
74b04a01 XL |
177 | trans.gen(place.local); |
178 | } | |
179 | ||
ba9703b0 XL |
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 | ||
f9f354fc XL |
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 | ||
74b04a01 XL |
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 { .. } | |
f035d41b | 210 | | TerminatorKind::FalseEdge { .. } |
74b04a01 XL |
211 | | TerminatorKind::FalseUnwind { .. } |
212 | | TerminatorKind::GeneratorDrop | |
213 | | TerminatorKind::Goto { .. } | |
214 | | TerminatorKind::Resume | |
215 | | TerminatorKind::Return | |
216 | | TerminatorKind::SwitchInt { .. } | |
217 | | TerminatorKind::Unreachable => {} | |
e74abb32 XL |
218 | } |
219 | } | |
220 | ||
74b04a01 XL |
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); | |
e74abb32 | 234 | } |
74b04a01 XL |
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 { .. } | |
f035d41b | 244 | | TerminatorKind::FalseEdge { .. } |
74b04a01 XL |
245 | | TerminatorKind::FalseUnwind { .. } |
246 | | TerminatorKind::GeneratorDrop | |
247 | | TerminatorKind::Goto { .. } | |
f9f354fc | 248 | | TerminatorKind::InlineAsm { .. } |
74b04a01 XL |
249 | | TerminatorKind::Resume |
250 | | TerminatorKind::Return | |
251 | | TerminatorKind::SwitchInt { .. } | |
252 | | TerminatorKind::Unreachable => {} | |
e74abb32 | 253 | } |
74b04a01 XL |
254 | |
255 | self.check_for_move(trans, loc); | |
dc9dc135 XL |
256 | } |
257 | ||
74b04a01 | 258 | fn call_return_effect( |
dc9dc135 | 259 | &self, |
74b04a01 XL |
260 | trans: &mut impl GenKill<Self::Idx>, |
261 | _block: BasicBlock, | |
262 | _func: &mir::Operand<'tcx>, | |
263 | _args: &[mir::Operand<'tcx>], | |
ba9703b0 | 264 | return_place: mir::Place<'tcx>, |
dc9dc135 | 265 | ) { |
74b04a01 | 266 | trans.gen(return_place.local); |
dc9dc135 | 267 | } |
ba9703b0 XL |
268 | |
269 | fn yield_resume_effect( | |
270 | &self, | |
f9f354fc | 271 | trans: &mut impl GenKill<Self::Idx>, |
ba9703b0 XL |
272 | _resume_block: BasicBlock, |
273 | resume_place: mir::Place<'tcx>, | |
274 | ) { | |
275 | trans.gen(resume_place.local); | |
276 | } | |
dc9dc135 XL |
277 | } |
278 | ||
74b04a01 | 279 | impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { |
dc9dc135 | 280 | /// Kill locals that are fully moved and have not been borrowed. |
74b04a01 XL |
281 | fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) { |
282 | let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals }; | |
ba9703b0 | 283 | visitor.visit_location(&self.body, loc); |
dc9dc135 | 284 | } |
ea8adc8c XL |
285 | } |
286 | ||
74b04a01 | 287 | impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> { |
dc9dc135 XL |
288 | /// bottom = dead |
289 | const BOTTOM_VALUE: bool = false; | |
290 | } | |
291 | ||
74b04a01 XL |
292 | struct MoveVisitor<'a, 'mir, 'tcx, T> { |
293 | borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>, | |
294 | trans: &'a mut T, | |
dc9dc135 XL |
295 | } |
296 | ||
74b04a01 XL |
297 | impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T> |
298 | where | |
299 | T: GenKill<Local>, | |
300 | { | |
dc9dc135 XL |
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(); | |
f9f354fc | 304 | borrowed_locals.seek_before_primary_effect(loc); |
dc9dc135 | 305 | if !borrowed_locals.contains(*local) { |
74b04a01 | 306 | self.trans.kill(*local); |
dc9dc135 XL |
307 | } |
308 | } | |
ea8adc8c XL |
309 | } |
310 | } |