Skip to content

Commit 4c34ec6

Browse files
Live variable analysis
1 parent 20899db commit 4c34ec6

File tree

3 files changed

+141
-2
lines changed

3 files changed

+141
-2
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use rustc_index::bit_set::BitSet;
2+
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
3+
use rustc_middle::mir::{self, Local, Location};
4+
5+
use crate::dataflow::{AnalysisDomain, Backward, BottomValue, GenKill, GenKillAnalysis};
6+
7+
/// A [live-variable dataflow analysis][liveness].
8+
///
9+
/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
10+
pub struct MaybeLiveLocals;
11+
12+
impl MaybeLiveLocals {
13+
fn transfer_function<T>(&self, trans: &'a mut T) -> TransferFunction<'a, T> {
14+
TransferFunction(trans)
15+
}
16+
}
17+
18+
impl BottomValue for MaybeLiveLocals {
19+
// bottom = not live
20+
const BOTTOM_VALUE: bool = false;
21+
}
22+
23+
impl AnalysisDomain<'tcx> for MaybeLiveLocals {
24+
type Idx = Local;
25+
type Direction = Backward;
26+
27+
const NAME: &'static str = "liveness";
28+
29+
fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
30+
body.local_decls.len()
31+
}
32+
33+
fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
34+
// No variables are live until we observe a use
35+
}
36+
}
37+
38+
impl GenKillAnalysis<'tcx> for MaybeLiveLocals {
39+
fn statement_effect(
40+
&self,
41+
trans: &mut impl GenKill<Self::Idx>,
42+
statement: &mir::Statement<'tcx>,
43+
location: Location,
44+
) {
45+
self.transfer_function(trans).visit_statement(statement, location);
46+
}
47+
48+
fn terminator_effect(
49+
&self,
50+
trans: &mut impl GenKill<Self::Idx>,
51+
terminator: &mir::Terminator<'tcx>,
52+
location: Location,
53+
) {
54+
self.transfer_function(trans).visit_terminator(terminator, location);
55+
}
56+
57+
fn call_return_effect(
58+
&self,
59+
trans: &mut impl GenKill<Self::Idx>,
60+
_block: mir::BasicBlock,
61+
_func: &mir::Operand<'tcx>,
62+
_args: &[mir::Operand<'tcx>],
63+
dest_place: mir::Place<'tcx>,
64+
) {
65+
if let Some(local) = dest_place.as_local() {
66+
trans.kill(local);
67+
}
68+
}
69+
70+
fn yield_resume_effect(
71+
&self,
72+
trans: &mut impl GenKill<Self::Idx>,
73+
_resume_block: mir::BasicBlock,
74+
resume_place: mir::Place<'tcx>,
75+
) {
76+
if let Some(local) = resume_place.as_local() {
77+
trans.kill(local);
78+
}
79+
}
80+
}
81+
82+
struct TransferFunction<'a, T>(&'a mut T);
83+
84+
impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T>
85+
where
86+
T: GenKill<Local>,
87+
{
88+
fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
89+
match DefUse::for_place(context) {
90+
Some(DefUse::Def) => self.0.kill(local),
91+
Some(DefUse::Use) => self.0.gen(local),
92+
_ => {}
93+
}
94+
}
95+
}
96+
97+
#[derive(Eq, PartialEq, Clone)]
98+
enum DefUse {
99+
Def,
100+
Use,
101+
}
102+
103+
impl DefUse {
104+
fn for_place(context: PlaceContext) -> Option<DefUse> {
105+
match context {
106+
PlaceContext::NonUse(_) => None,
107+
108+
PlaceContext::MutatingUse(MutatingUseContext::Store) => Some(DefUse::Def),
109+
110+
// `MutatingUseContext::Call` and `MutatingUseContext::Yield` indicate that this is the
111+
// destination place for a `Call` return or `Yield` resume respectively. Since this is
112+
// only a `Def` when the function returns succesfully, we handle this case separately
113+
// in `call_return_effect` above.
114+
PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None,
115+
116+
// All other contexts are uses...
117+
PlaceContext::MutatingUse(
118+
MutatingUseContext::AddressOf
119+
| MutatingUseContext::AsmOutput
120+
| MutatingUseContext::Borrow
121+
| MutatingUseContext::Drop
122+
| MutatingUseContext::Projection
123+
| MutatingUseContext::Retag,
124+
)
125+
| PlaceContext::NonMutatingUse(
126+
NonMutatingUseContext::AddressOf
127+
| NonMutatingUseContext::Copy
128+
| NonMutatingUseContext::Inspect
129+
| NonMutatingUseContext::Move
130+
| NonMutatingUseContext::Projection
131+
| NonMutatingUseContext::ShallowBorrow
132+
| NonMutatingUseContext::SharedBorrow
133+
| NonMutatingUseContext::UniqueBorrow,
134+
) => Some(DefUse::Use),
135+
}
136+
}
137+
}

src/librustc_mir/dataflow/impls/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ use super::on_lookup_result_bits;
2121
use crate::dataflow::drop_flag_effects;
2222

2323
mod borrowed_locals;
24+
mod liveness;
2425
mod storage_liveness;
2526

2627
pub use self::borrowed_locals::*;
28+
pub use self::liveness::MaybeLiveLocals;
2729
pub use self::storage_liveness::*;
2830

2931
pub(super) mod borrows;

src/librustc_mir/dataflow/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ pub use self::framework::{
1010
};
1111
pub use self::impls::{
1212
borrows::Borrows, DefinitelyInitializedPlaces, EverInitializedPlaces, MaybeBorrowedLocals,
13-
MaybeInitializedPlaces, MaybeMutBorrowedLocals, MaybeRequiresStorage, MaybeStorageLive,
14-
MaybeUninitializedPlaces,
13+
MaybeInitializedPlaces, MaybeLiveLocals, MaybeMutBorrowedLocals, MaybeRequiresStorage,
14+
MaybeStorageLive, MaybeUninitializedPlaces,
1515
};
1616

1717
use self::move_paths::MoveData;

0 commit comments

Comments
 (0)