3 use crate::dataflow::generic::{self as dataflow, GenKill, Results, ResultsRefCursor};
4 use crate::dataflow::BottomValue;
5 use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
7 use std::cell::RefCell;
10 pub struct MaybeStorageLive;
12 impl dataflow::AnalysisDomain<'tcx> for MaybeStorageLive {
15 const NAME: &'static str = "maybe_storage_live";
17 fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
18 body.local_decls.len()
21 fn initialize_start_block(&self, body: &mir::Body<'tcx>, on_entry: &mut BitSet<Self::Idx>) {
22 // The resume argument is live on function entry (we don't care about
23 // the `self` argument)
24 for arg in body.args_iter().skip(1) {
30 impl dataflow::GenKillAnalysis<'tcx> for MaybeStorageLive {
33 trans: &mut impl GenKill<Self::Idx>,
34 stmt: &mir::Statement<'tcx>,
38 StatementKind::StorageLive(l) => trans.gen(l),
39 StatementKind::StorageDead(l) => trans.kill(l),
46 _trans: &mut impl GenKill<Self::Idx>,
47 _: &mir::Terminator<'tcx>,
50 // Terminators have no effect
53 fn call_return_effect(
55 _trans: &mut impl GenKill<Self::Idx>,
57 _func: &mir::Operand<'tcx>,
58 _args: &[mir::Operand<'tcx>],
59 _return_place: &mir::Place<'tcx>,
61 // Nothing to do when a call returns successfully
65 impl BottomValue for MaybeStorageLive {
67 const BOTTOM_VALUE: bool = false;
70 type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
72 /// Dataflow analysis that determines whether each local requires storage at a
73 /// given location; i.e. whether its storage can go away without being observed.
74 pub struct RequiresStorage<'mir, 'tcx> {
75 body: ReadOnlyBodyAndCache<'mir, 'tcx>,
76 borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
79 impl<'mir, 'tcx: 'mir> RequiresStorage<'mir, 'tcx> {
81 body: ReadOnlyBodyAndCache<'mir, 'tcx>,
82 borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
86 borrowed_locals: RefCell::new(ResultsRefCursor::new(*body, borrowed_locals)),
90 pub fn body(&self) -> &Body<'tcx> {
95 impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
97 fn name() -> &'static str {
100 fn bits_per_block(&self) -> usize {
101 self.body.local_decls.len()
104 fn start_block_effect(&self, on_entry: &mut BitSet<Local>) {
105 // The resume argument is live on function entry (we don't care about
106 // the `self` argument)
107 for arg in self.body.args_iter().skip(1) {
108 on_entry.insert(arg);
112 fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {
113 let stmt = &self.body[loc.block].statements[loc.statement_index];
115 // If a place is borrowed in a statement, it needs storage for that statement.
116 self.borrowed_locals.borrow().analysis().statement_effect(sets, stmt, loc);
118 // If a place is assigned to in a statement, it needs storage for that statement.
120 StatementKind::StorageDead(l) => sets.kill(*l),
121 StatementKind::Assign(box (place, _))
122 | StatementKind::SetDiscriminant { box place, .. } => {
123 sets.gen(place.local);
125 StatementKind::InlineAsm(box InlineAsm { outputs, .. }) => {
126 for place in &**outputs {
127 sets.gen(place.local);
131 // Nothing to do for these. Match exhaustively so this fails to compile when new
132 // variants are added.
133 StatementKind::AscribeUserType(..)
134 | StatementKind::FakeRead(..)
136 | StatementKind::Retag(..)
137 | StatementKind::StorageLive(..) => {}
141 fn statement_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
142 // If we move from a place then only stops needing storage *after*
144 self.check_for_move(sets, loc);
147 fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
148 let terminator = self.body[loc.block].terminator();
150 // If a place is borrowed in a terminator, it needs storage for that terminator.
151 self.borrowed_locals.borrow().analysis().terminator_effect(sets, terminator, loc);
153 match &terminator.kind {
154 TerminatorKind::Call { destination: Some((Place { local, .. }, _)), .. }
155 | TerminatorKind::Yield { resume_arg: Place { local, .. }, .. } => {
159 // Nothing to do for these. Match exhaustively so this fails to compile when new
160 // variants are added.
161 TerminatorKind::Call { destination: None, .. }
162 | TerminatorKind::Abort
163 | TerminatorKind::Assert { .. }
164 | TerminatorKind::Drop { .. }
165 | TerminatorKind::DropAndReplace { .. }
166 | TerminatorKind::FalseEdges { .. }
167 | TerminatorKind::FalseUnwind { .. }
168 | TerminatorKind::GeneratorDrop
169 | TerminatorKind::Goto { .. }
170 | TerminatorKind::Resume
171 | TerminatorKind::Return
172 | TerminatorKind::SwitchInt { .. }
173 | TerminatorKind::Unreachable => {}
177 fn terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
178 match &self.body[loc.block].terminator().kind {
179 // For call terminators the destination requires storage for the call
180 // and after the call returns successfully, but not after a panic.
181 // Since `propagate_call_unwind` doesn't exist, we have to kill the
182 // destination here, and then gen it again in `propagate_call_return`.
183 TerminatorKind::Call { destination: Some((Place { local, .. }, _)), .. } => {
187 // Nothing to do for these. Match exhaustively so this fails to compile when new
188 // variants are added.
189 TerminatorKind::Call { destination: None, .. }
190 | TerminatorKind::Yield { .. }
191 | TerminatorKind::Abort
192 | TerminatorKind::Assert { .. }
193 | TerminatorKind::Drop { .. }
194 | TerminatorKind::DropAndReplace { .. }
195 | TerminatorKind::FalseEdges { .. }
196 | TerminatorKind::FalseUnwind { .. }
197 | TerminatorKind::GeneratorDrop
198 | TerminatorKind::Goto { .. }
199 | TerminatorKind::Resume
200 | TerminatorKind::Return
201 | TerminatorKind::SwitchInt { .. }
202 | TerminatorKind::Unreachable => {}
205 self.check_for_move(sets, loc);
208 fn propagate_call_return(
210 in_out: &mut BitSet<Local>,
211 _call_bb: mir::BasicBlock,
212 _dest_bb: mir::BasicBlock,
213 dest_place: &mir::Place<'tcx>,
215 in_out.insert(dest_place.local);
219 impl<'mir, 'tcx> RequiresStorage<'mir, 'tcx> {
220 /// Kill locals that are fully moved and have not been borrowed.
221 fn check_for_move(&self, sets: &mut GenKillSet<Local>, loc: Location) {
222 let mut visitor = MoveVisitor { sets, borrowed_locals: &self.borrowed_locals };
223 visitor.visit_location(self.body, loc);
227 impl<'mir, 'tcx> BottomValue for RequiresStorage<'mir, 'tcx> {
229 const BOTTOM_VALUE: bool = false;
232 struct MoveVisitor<'a, 'mir, 'tcx> {
233 borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
234 sets: &'a mut GenKillSet<Local>,
237 impl<'a, 'mir: 'a, 'tcx> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx> {
238 fn visit_local(&mut self, local: &Local, context: PlaceContext, loc: Location) {
239 if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
240 let mut borrowed_locals = self.borrowed_locals.borrow_mut();
241 borrowed_locals.seek_before(loc);
242 if !borrowed_locals.contains(*local) {
243 self.sets.kill(*local);