use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use jingle_sleigh::PcodeOperation;
use crate::analysis::cfg::CfgState;
use crate::analysis::cpa::lattice::JoinSemiLattice;
use crate::analysis::cpa::state::{
AbstractState, LocationState, MergeOutcome, PcodeLocation, Successor,
};
use crate::analysis::linkage::PcodeReverseLinkage;
use crate::analysis::liveness::state::LivenessState;
use crate::analysis::pcode_store::{PcodeOpRef, PcodeStore};
pub struct LivenessCpaState<N: CfgState, L: PcodeReverseLinkage<N>> {
pub(crate) location: N,
pub(crate) live: LivenessState,
pub(crate) linkage: Arc<L>,
}
impl<N: CfgState, L: PcodeReverseLinkage<N>> PartialEq for LivenessCpaState<N, L> {
fn eq(&self, other: &Self) -> bool {
self.location == other.location
}
}
impl<N: CfgState, L: PcodeReverseLinkage<N>> Eq for LivenessCpaState<N, L> {}
impl<N: CfgState, L: PcodeReverseLinkage<N>> Hash for LivenessCpaState<N, L> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.location.hash(state);
}
}
impl<N: CfgState + PartialOrd, L: PcodeReverseLinkage<N>> PartialOrd for LivenessCpaState<N, L> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.location != other.location {
return None;
}
self.live.partial_cmp(&other.live)
}
}
impl<N: CfgState, L: PcodeReverseLinkage<N>> Clone for LivenessCpaState<N, L> {
fn clone(&self) -> Self {
Self {
location: self.location.clone(),
live: self.live.clone(),
linkage: Arc::clone(&self.linkage),
}
}
}
impl<N: CfgState, L: PcodeReverseLinkage<N>> Debug for LivenessCpaState<N, L> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LivenessCpaState")
.field("location", &self.location)
.field("live", &self.live)
.finish()
}
}
impl<N: CfgState + Display, L: PcodeReverseLinkage<N>> Display for LivenessCpaState<N, L> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Liveness({}, {})", self.location, self.live)
}
}
impl<N: CfgState + PartialOrd, L: PcodeReverseLinkage<N>> JoinSemiLattice
for LivenessCpaState<N, L>
{
fn join(&mut self, other: &Self) {
self.live.join(&other.live);
}
}
impl<N, L> AbstractState for LivenessCpaState<N, L>
where
N: CfgState + JoinSemiLattice + Display + PartialOrd + 'static,
L: PcodeReverseLinkage<N> + 'static,
{
fn merge(&mut self, other: &Self) -> MergeOutcome {
if self.location != other.location {
return MergeOutcome::NoOp;
}
let old_live = self.live.clone();
self.live.join(&other.live);
if self.live == old_live {
MergeOutcome::NoOp
} else {
MergeOutcome::Merged
}
}
fn stop<'a, T: Iterator<Item = &'a Self>>(&'a self, states: T) -> bool {
self.stop_sep(states)
}
fn transfer<'a, B: Borrow<PcodeOperation>>(&'a self, op: B) -> Successor<'a, Self> {
let new_live = self.live.apply_transfer(op.borrow());
let preds = self.linkage.predecessors_of(&self.location);
let linkage = Arc::clone(&self.linkage);
preds
.into_iter()
.map(move |pred| LivenessCpaState {
location: pred,
live: new_live.clone(),
linkage: Arc::clone(&linkage),
})
.into()
}
}
impl<N: CfgState, L: PcodeReverseLinkage<N>> PcodeLocation for LivenessCpaState<N, L> {
fn location(&self) -> crate::analysis::cpa::lattice::pcode::PcodeAddressLattice {
self.location.location()
}
}
impl<N, L> LocationState for LivenessCpaState<N, L>
where
N: CfgState + JoinSemiLattice + Display + PartialOrd + 'static,
L: PcodeReverseLinkage<N> + 'static,
{
fn get_transitions<'op, T: PcodeStore<'op> + ?Sized>(
&self,
store: &'op T,
) -> Vec<(PcodeOpRef<'op>, Self)> {
let live_in_current = &self.live;
let linkage = Arc::clone(&self.linkage);
self.linkage
.predecessors_of(&self.location)
.into_iter()
.filter_map(|pred| {
let addr = pred.concrete_location()?;
let op = store.get_pcode_op_at(addr)?;
let live_in_pred = live_in_current.apply_transfer(op.as_ref());
Some((
op,
LivenessCpaState {
location: pred,
live: live_in_pred,
linkage: Arc::clone(&linkage),
},
))
})
.collect()
}
}