rustc_mir_dataflow/impls/
storage_liveness.rs1use std::borrow::Cow;
2
3use rustc_data_structures::fx::FxHashMap;
4use rustc_index::bit_set::DenseBitSet;
5use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
6use rustc_middle::mir::*;
7use smallvec::SmallVec;
8
9use super::MaybeBorrowedLocals;
10use crate::{Analysis, GenKill, Results, ResultsCursor};
11
12pub fn always_storage_live_locals(body: &Body<'_>) -> DenseBitSet<Local> {
16 let mut always_live_locals = DenseBitSet::new_filled(body.local_decls.len());
17
18 for block in &*body.basic_blocks {
19 for statement in &block.statements {
20 if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = statement.kind {
21 always_live_locals.remove(l);
22 }
23 }
24 }
25
26 always_live_locals
27}
28
29pub struct MaybeStorageLive<'a> {
30 always_live_locals: Cow<'a, DenseBitSet<Local>>,
31}
32
33impl<'a> MaybeStorageLive<'a> {
34 pub fn new(always_live_locals: Cow<'a, DenseBitSet<Local>>) -> Self {
35 MaybeStorageLive { always_live_locals }
36 }
37}
38
39impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> {
40 type Domain = DenseBitSet<Local>;
41
42 const NAME: &'static str = "maybe_storage_live";
43
44 fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
45 DenseBitSet::new_empty(body.local_decls.len())
47 }
48
49 fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
50 state.union(&*self.always_live_locals);
51
52 for arg in body.args_iter() {
53 state.insert(arg);
54 }
55 }
56
57 fn apply_primary_statement_effect(
58 &self,
59 state: &mut Self::Domain,
60 stmt: &Statement<'tcx>,
61 _: Location,
62 ) {
63 match stmt.kind {
64 StatementKind::StorageLive(l) => state.gen_(l),
65 StatementKind::StorageDead(l) => state.kill(l),
66 _ => (),
67 }
68 }
69}
70
71pub struct MaybeStorageDead<'a> {
72 always_live_locals: Cow<'a, DenseBitSet<Local>>,
73}
74
75impl<'a> MaybeStorageDead<'a> {
76 pub fn new(always_live_locals: Cow<'a, DenseBitSet<Local>>) -> Self {
77 MaybeStorageDead { always_live_locals }
78 }
79}
80
81impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> {
82 type Domain = DenseBitSet<Local>;
83
84 const NAME: &'static str = "maybe_storage_dead";
85
86 fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
87 DenseBitSet::new_empty(body.local_decls.len())
89 }
90
91 fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
92 {
match (&body.local_decls.len(), &self.always_live_locals.domain_size()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
93 for local in body.vars_and_temps_iter() {
95 if !self.always_live_locals.contains(local) {
96 state.insert(local);
97 }
98 }
99 }
100
101 fn apply_primary_statement_effect(
102 &self,
103 state: &mut Self::Domain,
104 stmt: &Statement<'tcx>,
105 _: Location,
106 ) {
107 match stmt.kind {
108 StatementKind::StorageLive(l) => state.kill(l),
109 StatementKind::StorageDead(l) => state.gen_(l),
110 _ => (),
111 }
112 }
113}
114
115type KillableLocals = FxHashMap<Location, SmallVec<[Local; 4]>>;
117
118pub struct MaybeRequiresStorage {
121 killable_locals: KillableLocals,
123}
124
125impl MaybeRequiresStorage {
126 pub fn new<'tcx>(
127 body: &Body<'tcx>,
128 borrowed_locals: &Results<'tcx, MaybeBorrowedLocals>,
129 ) -> Self {
130 struct KillableLocalsVisitor<'mir, 'tcx> {
131 borrowed_locals_cursor: ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals>,
132 killable_locals: KillableLocals,
133 }
134
135 impl<'tcx> Visitor<'tcx> for KillableLocalsVisitor<'_, 'tcx> {
136 fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
137 if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
138 self.borrowed_locals_cursor.seek_before_primary_effect(loc);
139 if !self.borrowed_locals_cursor.get().contains(local) {
140 self.killable_locals.entry(loc).or_default().push(local);
141 }
142 }
143 }
144 }
145
146 let mut visitor = KillableLocalsVisitor {
147 borrowed_locals_cursor: ResultsCursor::new_borrowing(body, borrowed_locals),
148 killable_locals: Default::default(),
149 };
150
151 for (bb, data) in traversal::reachable(body) {
152 visitor.visit_basic_block_data(bb, data);
153 }
154
155 MaybeRequiresStorage { killable_locals: visitor.killable_locals }
156 }
157}
158
159impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage {
160 type Domain = DenseBitSet<Local>;
161
162 const NAME: &'static str = "requires_storage";
163
164 fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
165 DenseBitSet::new_empty(body.local_decls.len())
167 }
168
169 fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
170 for arg in body.args_iter().skip(1) {
173 state.insert(arg);
174 }
175 }
176
177 fn apply_early_statement_effect(
178 &self,
179 state: &mut Self::Domain,
180 stmt: &Statement<'tcx>,
181 _loc: Location,
182 ) {
183 MaybeBorrowedLocals::gen_statement(state, stmt);
185
186 match &stmt.kind {
187 StatementKind::StorageDead(l) => state.kill(*l),
188
189 StatementKind::Assign((place, _)) => {
190 state.gen_(place.local);
191 }
192 StatementKind::SetDiscriminant { place, .. } => {
193 state.gen_(place.local);
194 }
195
196 StatementKind::AscribeUserType(..)
199 | StatementKind::PlaceMention(..)
200 | StatementKind::Coverage(..)
201 | StatementKind::FakeRead(..)
202 | StatementKind::ConstEvalCounter
203 | StatementKind::Nop
204 | StatementKind::Intrinsic(..)
205 | StatementKind::BackwardIncompatibleDropHint { .. }
206 | StatementKind::StorageLive(..) => {}
207 }
208 }
209
210 fn apply_primary_statement_effect(
211 &self,
212 state: &mut Self::Domain,
213 stmt: &Statement<'tcx>,
214 loc: Location,
215 ) {
216 self.check_for_move(state, loc);
218
219 match &stmt.kind {
220 StatementKind::Assign((place, _)) => {
224 state.gen_(place.local);
225 }
226 StatementKind::SetDiscriminant { place, .. } => {
227 state.gen_(place.local);
228 }
229
230 StatementKind::StorageDead(_)
231 | StatementKind::AscribeUserType(..)
232 | StatementKind::PlaceMention(..)
233 | StatementKind::Coverage(..)
234 | StatementKind::FakeRead(..)
235 | StatementKind::ConstEvalCounter
236 | StatementKind::Nop
237 | StatementKind::Intrinsic(..)
238 | StatementKind::BackwardIncompatibleDropHint { .. }
239 | StatementKind::StorageLive(..) => {}
240 }
241 }
242
243 fn apply_early_terminator_effect(
244 &self,
245 state: &mut Self::Domain,
246 terminator: &Terminator<'tcx>,
247 _loc: Location,
248 ) {
249 MaybeBorrowedLocals::gen_terminator(state, terminator);
251
252 match &terminator.kind {
253 TerminatorKind::Call { destination, .. } => {
254 state.gen_(destination.local);
255 }
256
257 TerminatorKind::Yield { .. } => {}
262
263 TerminatorKind::InlineAsm { operands, .. } => {
264 for op in operands {
265 match op {
266 InlineAsmOperand::Out { place, .. }
267 | InlineAsmOperand::InOut { out_place: place, .. } => {
268 if let Some(place) = place {
269 state.gen_(place.local);
270 }
271 }
272 InlineAsmOperand::In { .. }
273 | InlineAsmOperand::Const { .. }
274 | InlineAsmOperand::SymFn { .. }
275 | InlineAsmOperand::SymStatic { .. }
276 | InlineAsmOperand::Label { .. } => {}
277 }
278 }
279 }
280
281 TerminatorKind::UnwindTerminate(_)
284 | TerminatorKind::Assert { .. }
285 | TerminatorKind::Drop { .. }
286 | TerminatorKind::FalseEdge { .. }
287 | TerminatorKind::FalseUnwind { .. }
288 | TerminatorKind::CoroutineDrop
289 | TerminatorKind::Goto { .. }
290 | TerminatorKind::UnwindResume
291 | TerminatorKind::Return
292 | TerminatorKind::TailCall { .. }
293 | TerminatorKind::SwitchInt { .. }
294 | TerminatorKind::Unreachable => {}
295 }
296 }
297
298 fn apply_primary_terminator_effect(
299 &self,
300 state: &mut Self::Domain,
301 terminator: &Terminator<'tcx>,
302 loc: Location,
303 ) {
304 match terminator.kind {
305 TerminatorKind::Call { destination, .. } => {
310 state.kill(destination.local);
311 }
312
313 TerminatorKind::InlineAsm { ref operands, .. } => {
315 CallReturnPlaces::InlineAsm(operands).for_each(|place| state.kill(place.local));
316 }
317
318 TerminatorKind::Yield { .. }
321 | TerminatorKind::UnwindTerminate(_)
322 | TerminatorKind::Assert { .. }
323 | TerminatorKind::Drop { .. }
324 | TerminatorKind::FalseEdge { .. }
325 | TerminatorKind::FalseUnwind { .. }
326 | TerminatorKind::CoroutineDrop
327 | TerminatorKind::Goto { .. }
328 | TerminatorKind::UnwindResume
329 | TerminatorKind::Return
330 | TerminatorKind::TailCall { .. }
331 | TerminatorKind::SwitchInt { .. }
332 | TerminatorKind::Unreachable => {}
333 }
334
335 self.check_for_move(state, loc);
336 }
337
338 fn apply_call_return_effect(
339 &self,
340 state: &mut Self::Domain,
341 _block: BasicBlock,
342 return_places: CallReturnPlaces<'_, 'tcx>,
343 ) {
344 return_places.for_each(|place| state.gen_(place.local));
345 }
346}
347
348impl MaybeRequiresStorage {
349 fn check_for_move(&self, state: &mut <Self as Analysis<'_>>::Domain, loc: Location) {
351 if let Some(locals) = self.killable_locals.get(&loc) {
352 for &l in locals {
353 state.kill(l);
354 }
355 }
356 }
357}