rustc_mir_dataflow/impls/
storage_liveness.rs1use std::borrow::Cow;
2use std::cell::RefCell;
3
4use rustc_index::bit_set::DenseBitSet;
5use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
6use rustc_middle::mir::*;
7
8use super::MaybeBorrowedLocals;
9use crate::{Analysis, GenKill, ResultsCursor};
10
11pub fn always_storage_live_locals(body: &Body<'_>) -> DenseBitSet<Local> {
15 let mut always_live_locals = DenseBitSet::new_filled(body.local_decls.len());
16
17 for block in &*body.basic_blocks {
18 for statement in &block.statements {
19 if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = statement.kind {
20 always_live_locals.remove(l);
21 }
22 }
23 }
24
25 always_live_locals
26}
27
28pub struct MaybeStorageLive<'a> {
29 always_live_locals: Cow<'a, DenseBitSet<Local>>,
30}
31
32impl<'a> MaybeStorageLive<'a> {
33 pub fn new(always_live_locals: Cow<'a, DenseBitSet<Local>>) -> Self {
34 MaybeStorageLive { always_live_locals }
35 }
36}
37
38impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> {
39 type Domain = DenseBitSet<Local>;
40
41 const NAME: &'static str = "maybe_storage_live";
42
43 fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
44 DenseBitSet::new_empty(body.local_decls.len())
46 }
47
48 fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
49 state.union(&*self.always_live_locals);
50
51 for arg in body.args_iter() {
52 state.insert(arg);
53 }
54 }
55
56 fn apply_primary_statement_effect(
57 &self,
58 state: &mut Self::Domain,
59 stmt: &Statement<'tcx>,
60 _: Location,
61 ) {
62 match stmt.kind {
63 StatementKind::StorageLive(l) => state.gen_(l),
64 StatementKind::StorageDead(l) => state.kill(l),
65 _ => (),
66 }
67 }
68}
69
70pub struct MaybeStorageDead<'a> {
71 always_live_locals: Cow<'a, DenseBitSet<Local>>,
72}
73
74impl<'a> MaybeStorageDead<'a> {
75 pub fn new(always_live_locals: Cow<'a, DenseBitSet<Local>>) -> Self {
76 MaybeStorageDead { always_live_locals }
77 }
78}
79
80impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> {
81 type Domain = DenseBitSet<Local>;
82
83 const NAME: &'static str = "maybe_storage_dead";
84
85 fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
86 DenseBitSet::new_empty(body.local_decls.len())
88 }
89
90 fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
91 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());
92 for local in body.vars_and_temps_iter() {
94 if !self.always_live_locals.contains(local) {
95 state.insert(local);
96 }
97 }
98 }
99
100 fn apply_primary_statement_effect(
101 &self,
102 state: &mut Self::Domain,
103 stmt: &Statement<'tcx>,
104 _: Location,
105 ) {
106 match stmt.kind {
107 StatementKind::StorageLive(l) => state.kill(l),
108 StatementKind::StorageDead(l) => state.gen_(l),
109 _ => (),
110 }
111 }
112}
113
114type BorrowedLocalsResults<'mir, 'tcx> = ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals>;
115
116pub struct MaybeRequiresStorage<'mir, 'tcx> {
119 borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
120}
121
122impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
123 pub fn new(borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>) -> Self {
124 MaybeRequiresStorage { borrowed_locals: RefCell::new(borrowed_locals) }
125 }
126}
127
128impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
129 type Domain = DenseBitSet<Local>;
130
131 const NAME: &'static str = "requires_storage";
132
133 fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain {
134 DenseBitSet::new_empty(body.local_decls.len())
136 }
137
138 fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
139 for arg in body.args_iter().skip(1) {
142 state.insert(arg);
143 }
144 }
145
146 fn apply_early_statement_effect(
147 &self,
148 state: &mut Self::Domain,
149 stmt: &Statement<'tcx>,
150 loc: Location,
151 ) {
152 MaybeBorrowedLocals::transfer_function(state).visit_statement(stmt, loc);
154
155 match &stmt.kind {
156 StatementKind::StorageDead(l) => state.kill(*l),
157
158 StatementKind::Assign((place, _)) => {
160 state.gen_(place.local);
161 }
162 StatementKind::SetDiscriminant { place, .. } => {
163 state.gen_(place.local);
164 }
165
166 StatementKind::AscribeUserType(..)
169 | StatementKind::PlaceMention(..)
170 | StatementKind::Coverage(..)
171 | StatementKind::FakeRead(..)
172 | StatementKind::ConstEvalCounter
173 | StatementKind::Nop
174 | StatementKind::Intrinsic(..)
175 | StatementKind::BackwardIncompatibleDropHint { .. }
176 | StatementKind::StorageLive(..) => {}
177 }
178 }
179
180 fn apply_primary_statement_effect(
181 &self,
182 state: &mut Self::Domain,
183 _: &Statement<'tcx>,
184 loc: Location,
185 ) {
186 self.check_for_move(state, loc);
189 }
190
191 fn apply_early_terminator_effect(
192 &self,
193 state: &mut Self::Domain,
194 terminator: &Terminator<'tcx>,
195 loc: Location,
196 ) {
197 MaybeBorrowedLocals::transfer_function(state).visit_terminator(terminator, loc);
199
200 match &terminator.kind {
201 TerminatorKind::Call { destination, .. } => {
202 state.gen_(destination.local);
203 }
204
205 TerminatorKind::Yield { .. } => {}
210
211 TerminatorKind::InlineAsm { operands, .. } => {
212 for op in operands {
213 match op {
214 InlineAsmOperand::Out { place, .. }
215 | InlineAsmOperand::InOut { out_place: place, .. } => {
216 if let Some(place) = place {
217 state.gen_(place.local);
218 }
219 }
220 InlineAsmOperand::In { .. }
221 | InlineAsmOperand::Const { .. }
222 | InlineAsmOperand::SymFn { .. }
223 | InlineAsmOperand::SymStatic { .. }
224 | InlineAsmOperand::Label { .. } => {}
225 }
226 }
227 }
228
229 TerminatorKind::UnwindTerminate(_)
232 | TerminatorKind::Assert { .. }
233 | TerminatorKind::Drop { .. }
234 | TerminatorKind::FalseEdge { .. }
235 | TerminatorKind::FalseUnwind { .. }
236 | TerminatorKind::CoroutineDrop
237 | TerminatorKind::Goto { .. }
238 | TerminatorKind::UnwindResume
239 | TerminatorKind::Return
240 | TerminatorKind::TailCall { .. }
241 | TerminatorKind::SwitchInt { .. }
242 | TerminatorKind::Unreachable => {}
243 }
244 }
245
246 fn apply_primary_terminator_effect<'t>(
247 &self,
248 state: &mut Self::Domain,
249 terminator: &'t Terminator<'tcx>,
250 loc: Location,
251 ) -> TerminatorEdges<'t, 'tcx> {
252 match terminator.kind {
253 TerminatorKind::Call { destination, .. } => {
258 state.kill(destination.local);
259 }
260
261 TerminatorKind::InlineAsm { ref operands, .. } => {
263 CallReturnPlaces::InlineAsm(operands).for_each(|place| state.kill(place.local));
264 }
265
266 TerminatorKind::Yield { .. }
269 | TerminatorKind::UnwindTerminate(_)
270 | TerminatorKind::Assert { .. }
271 | TerminatorKind::Drop { .. }
272 | TerminatorKind::FalseEdge { .. }
273 | TerminatorKind::FalseUnwind { .. }
274 | TerminatorKind::CoroutineDrop
275 | TerminatorKind::Goto { .. }
276 | TerminatorKind::UnwindResume
277 | TerminatorKind::Return
278 | TerminatorKind::TailCall { .. }
279 | TerminatorKind::SwitchInt { .. }
280 | TerminatorKind::Unreachable => {}
281 }
282
283 self.check_for_move(state, loc);
284 terminator.edges()
285 }
286
287 fn apply_call_return_effect(
288 &self,
289 state: &mut Self::Domain,
290 _block: BasicBlock,
291 return_places: CallReturnPlaces<'_, 'tcx>,
292 ) {
293 return_places.for_each(|place| state.gen_(place.local));
294 }
295}
296
297impl<'tcx> MaybeRequiresStorage<'_, 'tcx> {
298 fn check_for_move(&self, state: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) {
300 let mut borrowed_locals = self.borrowed_locals.borrow_mut();
301 let body = borrowed_locals.body();
302 let mut visitor = MoveVisitor { state, borrowed_locals: &mut borrowed_locals };
303 visitor.visit_location(body, loc);
304 }
305}
306
307struct MoveVisitor<'a, 'mir, 'tcx> {
308 borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>,
309 state: &'a mut DenseBitSet<Local>,
310}
311
312impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> {
313 fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
314 if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
315 self.borrowed_locals.seek_before_primary_effect(loc);
316 if !self.borrowed_locals.get().contains(local) {
317 self.state.kill(local);
318 }
319 }
320 }
321}