Skip to content

Commit 4f75a62

Browse files
Revert "Auto merge of rust-lang#71956 - ecstatic-morse:remove-requires-storage-analysis, r=tmandry"
This reverts commit 458a3e7, reversing changes made to d9417b3.
1 parent 9da7d07 commit 4f75a62

File tree

7 files changed

+372
-254
lines changed

7 files changed

+372
-254
lines changed

src/librustc_mir/dataflow/impls/borrowed_locals.rs

-3
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,6 @@ impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K>
9999
where
100100
K: BorrowAnalysisKind<'tcx>,
101101
{
102-
// The generator transform relies on the fact that this analysis does **not** use "before"
103-
// effects.
104-
105102
fn statement_effect(
106103
&self,
107104
trans: &mut impl GenKill<Self::Idx>,

src/librustc_mir/dataflow/impls/init_locals.rs

-118
This file was deleted.

src/librustc_mir/dataflow/impls/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,13 @@ use crate::dataflow::drop_flag_effects;
2222

2323
mod borrowed_locals;
2424
pub(super) mod borrows;
25-
mod init_locals;
2625
mod liveness;
2726
mod storage_liveness;
2827

2928
pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals};
3029
pub use self::borrows::Borrows;
31-
pub use self::init_locals::MaybeInitializedLocals;
3230
pub use self::liveness::MaybeLiveLocals;
33-
pub use self::storage_liveness::MaybeStorageLive;
31+
pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive};
3432

3533
/// `MaybeInitializedPlaces` tracks all places that might be
3634
/// initialized upon reaching a particular point in the control flow

src/librustc_mir/dataflow/impls/storage_liveness.rs

+233-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
pub use super::*;
22

33
use crate::dataflow::BottomValue;
4-
use crate::dataflow::{self, GenKill};
4+
use crate::dataflow::{self, GenKill, Results, ResultsRefCursor};
55
use crate::util::storage::AlwaysLiveLocals;
6+
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
67
use rustc_middle::mir::*;
8+
use std::cell::RefCell;
79

810
#[derive(Clone)]
911
pub struct MaybeStorageLive {
@@ -76,3 +78,233 @@ impl BottomValue for MaybeStorageLive {
7678
/// bottom = dead
7779
const BOTTOM_VALUE: bool = false;
7880
}
81+
82+
type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
83+
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.
86+
pub struct MaybeRequiresStorage<'mir, 'tcx> {
87+
body: &'mir Body<'tcx>,
88+
borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
89+
}
90+
91+
impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
92+
pub fn new(
93+
body: &'mir Body<'tcx>,
94+
borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
95+
) -> Self {
96+
MaybeRequiresStorage {
97+
body,
98+
borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)),
99+
}
100+
}
101+
}
102+
103+
impl<'mir, 'tcx> dataflow::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
104+
type Idx = Local;
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()
110+
}
111+
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+
}
118+
}
119+
}
120+
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);
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::FakeRead(..)
149+
| StatementKind::Nop
150+
| StatementKind::Retag(..)
151+
| StatementKind::StorageLive(..) => {}
152+
}
153+
}
154+
155+
fn statement_effect(
156+
&self,
157+
trans: &mut impl GenKill<Self::Idx>,
158+
_: &mir::Statement<'tcx>,
159+
loc: Location,
160+
) {
161+
// If we move from a place then only stops needing storage *after*
162+
// that statement.
163+
self.check_for_move(trans, loc);
164+
}
165+
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);
174+
175+
match &terminator.kind {
176+
TerminatorKind::Call { destination: Some((place, _)), .. } => {
177+
trans.gen(place.local);
178+
}
179+
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+
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+
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 { .. }
210+
| TerminatorKind::FalseEdges { .. }
211+
| TerminatorKind::FalseUnwind { .. }
212+
| TerminatorKind::GeneratorDrop
213+
| TerminatorKind::Goto { .. }
214+
| TerminatorKind::Resume
215+
| TerminatorKind::Return
216+
| TerminatorKind::SwitchInt { .. }
217+
| TerminatorKind::Unreachable => {}
218+
}
219+
}
220+
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);
234+
}
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 { .. }
244+
| TerminatorKind::FalseEdges { .. }
245+
| TerminatorKind::FalseUnwind { .. }
246+
| TerminatorKind::GeneratorDrop
247+
| TerminatorKind::Goto { .. }
248+
| TerminatorKind::InlineAsm { .. }
249+
| TerminatorKind::Resume
250+
| TerminatorKind::Return
251+
| TerminatorKind::SwitchInt { .. }
252+
| TerminatorKind::Unreachable => {}
253+
}
254+
255+
self.check_for_move(trans, loc);
256+
}
257+
258+
fn call_return_effect(
259+
&self,
260+
trans: &mut impl GenKill<Self::Idx>,
261+
_block: BasicBlock,
262+
_func: &mir::Operand<'tcx>,
263+
_args: &[mir::Operand<'tcx>],
264+
return_place: mir::Place<'tcx>,
265+
) {
266+
trans.gen(return_place.local);
267+
}
268+
269+
fn yield_resume_effect(
270+
&self,
271+
trans: &mut impl GenKill<Self::Idx>,
272+
_resume_block: BasicBlock,
273+
resume_place: mir::Place<'tcx>,
274+
) {
275+
trans.gen(resume_place.local);
276+
}
277+
}
278+
279+
impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
280+
/// Kill locals that are fully moved and have not been borrowed.
281+
fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) {
282+
let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals };
283+
visitor.visit_location(&self.body, loc);
284+
}
285+
}
286+
287+
impl<'mir, 'tcx> BottomValue for MaybeRequiresStorage<'mir, 'tcx> {
288+
/// bottom = dead
289+
const BOTTOM_VALUE: bool = false;
290+
}
291+
292+
struct MoveVisitor<'a, 'mir, 'tcx, T> {
293+
borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
294+
trans: &'a mut T,
295+
}
296+
297+
impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
298+
where
299+
T: GenKill<Local>,
300+
{
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();
304+
borrowed_locals.seek_before_primary_effect(loc);
305+
if !borrowed_locals.contains(*local) {
306+
self.trans.kill(*local);
307+
}
308+
}
309+
}
310+
}

0 commit comments

Comments
 (0)