From 4c34ec6848b1e09624a521255d1ce69f9d62555e Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 25 Mar 2020 16:19:14 -0700 Subject: [PATCH] Live variable analysis --- src/librustc_mir/dataflow/impls/liveness.rs | 137 ++++++++++++++++++++ src/librustc_mir/dataflow/impls/mod.rs | 2 + src/librustc_mir/dataflow/mod.rs | 4 +- 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 src/librustc_mir/dataflow/impls/liveness.rs diff --git a/src/librustc_mir/dataflow/impls/liveness.rs b/src/librustc_mir/dataflow/impls/liveness.rs new file mode 100644 index 00000000000..5e9bec89ac0 --- /dev/null +++ b/src/librustc_mir/dataflow/impls/liveness.rs @@ -0,0 +1,137 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{self, Local, Location}; + +use crate::dataflow::{AnalysisDomain, Backward, BottomValue, GenKill, GenKillAnalysis}; + +/// A [live-variable dataflow analysis][liveness]. +/// +/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis +pub struct MaybeLiveLocals; + +impl MaybeLiveLocals { + fn transfer_function(&self, trans: &'a mut T) -> TransferFunction<'a, T> { + TransferFunction(trans) + } +} + +impl BottomValue for MaybeLiveLocals { + // bottom = not live + const BOTTOM_VALUE: bool = false; +} + +impl AnalysisDomain<'tcx> for MaybeLiveLocals { + type Idx = Local; + type Direction = Backward; + + const NAME: &'static str = "liveness"; + + fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { + body.local_decls.len() + } + + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet) { + // No variables are live until we observe a use + } +} + +impl GenKillAnalysis<'tcx> for MaybeLiveLocals { + fn statement_effect( + &self, + trans: &mut impl GenKill, + statement: &mir::Statement<'tcx>, + location: Location, + ) { + self.transfer_function(trans).visit_statement(statement, location); + } + + fn terminator_effect( + &self, + trans: &mut impl GenKill, + terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + self.transfer_function(trans).visit_terminator(terminator, location); + } + + fn call_return_effect( + &self, + trans: &mut impl GenKill, + _block: mir::BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + dest_place: mir::Place<'tcx>, + ) { + if let Some(local) = dest_place.as_local() { + trans.kill(local); + } + } + + fn yield_resume_effect( + &self, + trans: &mut impl GenKill, + _resume_block: mir::BasicBlock, + resume_place: mir::Place<'tcx>, + ) { + if let Some(local) = resume_place.as_local() { + trans.kill(local); + } + } +} + +struct TransferFunction<'a, T>(&'a mut T); + +impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T> +where + T: GenKill, +{ + fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) { + match DefUse::for_place(context) { + Some(DefUse::Def) => self.0.kill(local), + Some(DefUse::Use) => self.0.gen(local), + _ => {} + } + } +} + +#[derive(Eq, PartialEq, Clone)] +enum DefUse { + Def, + Use, +} + +impl DefUse { + fn for_place(context: PlaceContext) -> Option { + match context { + PlaceContext::NonUse(_) => None, + + PlaceContext::MutatingUse(MutatingUseContext::Store) => Some(DefUse::Def), + + // `MutatingUseContext::Call` and `MutatingUseContext::Yield` indicate that this is the + // destination place for a `Call` return or `Yield` resume respectively. Since this is + // only a `Def` when the function returns succesfully, we handle this case separately + // in `call_return_effect` above. + PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None, + + // All other contexts are uses... + PlaceContext::MutatingUse( + MutatingUseContext::AddressOf + | MutatingUseContext::AsmOutput + | MutatingUseContext::Borrow + | MutatingUseContext::Drop + | MutatingUseContext::Projection + | MutatingUseContext::Retag, + ) + | PlaceContext::NonMutatingUse( + NonMutatingUseContext::AddressOf + | NonMutatingUseContext::Copy + | NonMutatingUseContext::Inspect + | NonMutatingUseContext::Move + | NonMutatingUseContext::Projection + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::UniqueBorrow, + ) => Some(DefUse::Use), + } + } +} diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index b5f1a2d4eb9..222ae137d96 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -21,9 +21,11 @@ use super::on_lookup_result_bits; use crate::dataflow::drop_flag_effects; mod borrowed_locals; +mod liveness; mod storage_liveness; pub use self::borrowed_locals::*; +pub use self::liveness::MaybeLiveLocals; pub use self::storage_liveness::*; pub(super) mod borrows; diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index 41b7821707c..a05b4a5385d 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -10,8 +10,8 @@ pub use self::framework::{ }; pub use self::impls::{ borrows::Borrows, DefinitelyInitializedPlaces, EverInitializedPlaces, MaybeBorrowedLocals, - MaybeInitializedPlaces, MaybeMutBorrowedLocals, MaybeRequiresStorage, MaybeStorageLive, - MaybeUninitializedPlaces, + MaybeInitializedPlaces, MaybeLiveLocals, MaybeMutBorrowedLocals, MaybeRequiresStorage, + MaybeStorageLive, MaybeUninitializedPlaces, }; use self::move_paths::MoveData;