#[cfg(not(feature = "std"))]
use alloc::collections::VecDeque;
#[cfg(feature = "std")]
use std::collections::VecDeque;
use core::hash::Hash;
#[cfg(feature = "std")]
use std::collections::{HashMap, HashSet};
#[cfg(not(feature = "std"))]
use hashbrown::{HashMap, HashSet};
use crate::dataflow::analysis::Analysis;
use crate::dataflow::cfg::Cfg;
use crate::dataflow::direction::Direction;
use crate::dataflow::lattice::Lattice;
pub struct DataFlowResult<N, V> {
pub before: HashMap<N, V>,
pub after: HashMap<N, V>,
}
impl<N: Eq + Hash, V> DataFlowResult<N, V> {
pub fn before(&self, node: &N) -> Option<&V> {
self.before.get(node)
}
pub fn after(&self, node: &N) -> Option<&V> {
self.after.get(node)
}
}
pub fn solve<A>(analysis: &A, cfg: &Cfg<A::Node>) -> DataFlowResult<A::Node, A::Value>
where
A: Analysis,
A::Node: Eq + Hash + Clone,
A::Dir: Direction,
{
let boundary = A::Dir::boundary_node(cfg);
let mut state: HashMap<A::Node, A::Value> = cfg
.nodes()
.iter()
.map(|n| {
let v = if n == boundary {
analysis.boundary_value()
} else {
A::Value::top()
};
(n.clone(), v)
})
.collect();
let mut worklist: VecDeque<A::Node> = cfg.nodes().iter().cloned().collect();
let mut in_worklist: HashSet<A::Node> = worklist.iter().cloned().collect();
while let Some(node) = worklist.pop_front() {
let _ = in_worklist.remove(&node);
let input = if &node == A::Dir::boundary_node(cfg) {
analysis.boundary_value()
} else {
A::Dir::input_edges(cfg, &node)
.iter()
.map(|p| state.get(p).cloned().unwrap_or_else(A::Value::top))
.fold(A::Value::top(), |acc, v| acc.meet(&v))
};
let new_out = analysis.transfer(&node, &input);
if &new_out != state.get(&node).unwrap_or(&A::Value::top()) {
let _ = state.insert(node.clone(), new_out);
for next in A::Dir::output_edges(cfg, &node) {
if in_worklist.insert(next.clone()) {
worklist.push_back(next.clone());
}
}
}
}
let boundary_val = analysis.boundary_value();
let mut before: HashMap<A::Node, A::Value> = HashMap::new();
let mut after: HashMap<A::Node, A::Value> = HashMap::new();
for node in cfg.nodes() {
let out = state.get(node).cloned().unwrap_or_else(A::Value::top);
let input = if node == A::Dir::boundary_node(cfg) {
boundary_val.clone()
} else {
A::Dir::input_edges(cfg, node)
.iter()
.map(|p| state.get(p).cloned().unwrap_or_else(A::Value::top))
.fold(A::Value::top(), |acc, v| acc.meet(&v))
};
let is_forward = A::Dir::boundary_node(cfg) == cfg.entry();
if is_forward {
let _ = before.insert(node.clone(), input);
let _ = after.insert(node.clone(), out);
} else {
let _ = before.insert(node.clone(), out);
let _ = after.insert(node.clone(), input);
}
}
DataFlowResult { before, after }
}