use std::collections::{HashMap};
use super::{dep, Dataflow, Node, Exit};
use crate::util::{ArrayMap};
#[derive(Debug, Clone, Default)]
pub struct Frontier(pub HashMap<Node, dep::Value>);
pub struct Fill<'a> {
dataflow: &'a Dataflow,
marks: &'a mut ArrayMap<Node, usize>,
marker: usize,
nodes: Vec<Node>,
frontier: Frontier,
}
impl<'a> Fill<'a> {
pub fn new(dataflow: &'a Dataflow, marks: &'a mut ArrayMap<Node, usize>) -> Self {
Fill {dataflow, marks, marker: 1, nodes: Vec::new(), frontier: Frontier::default()}
}
pub fn nested(&mut self) -> Fill<'_> {
Fill {
dataflow: self.dataflow,
marks: &mut *self.marks,
marker: self.marker + 1,
nodes: Vec::new(),
frontier: Frontier::default(),
}
}
pub fn dataflow(&self) -> &'a Dataflow { self.dataflow }
pub fn mark(&mut self, node: Node) {
self.dataflow.each_input(node, |in_, _| assert_ne!(self[in_], 0));
assert_eq!(self[node], 0);
self.marks[node] = self.marker;
self.nodes.push(node);
}
pub fn visit(&mut self, node: Node) -> bool {
if self[node] == 0 {
self.dataflow.each_input(node, |in_, dep| self.input(in_, dep.0));
self.mark(node);
}
self[node] < self.marker
}
pub fn input(&mut self, in_: Node, dep: dep::Value) {
if self.visit(in_) {
let v = self.frontier.0.entry(in_).or_insert(dep::Value::Unused);
*v = std::cmp::max(*v, dep);
}
}
pub fn exit(&mut self, exit: &Exit) {
self.input(exit.sequence, dep::Value::Unused);
for &node in &*exit.outputs { self.input(node, dep::Value::Normal); }
}
pub fn resume(&mut self, frontier: &Frontier) {
for (&input, &dep) in &frontier.0 { self.input(input, dep); }
}
pub fn nodes(&self) -> impl '_ + Iterator<Item=Node> { self.nodes.iter().copied() }
pub fn drain(&mut self) -> (Vec<Node>, Frontier) {
let mut frontier = Frontier::default();
std::mem::swap(&mut frontier, &mut self.frontier);
let mut nodes = Vec::new();
std::mem::swap(&mut nodes, &mut self.nodes);
for &node in &nodes { self.marks[node] = 0; }
(nodes, frontier)
}
}
impl<'a> std::fmt::Debug for Fill<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Fill")
.field("marker", &self.marker)
.field("nodes", &self.nodes)
.field("frontier", &self.frontier)
.finish()
}
}
impl<'a> std::ops::Index<Node> for Fill<'a> {
type Output = usize;
fn index(&self, index: Node) -> &Self::Output { &self.marks[index] }
}
pub fn with_fill<T>(
dataflow: &Dataflow,
callback: impl FnOnce(Fill) -> T,
) -> T {
let mut marks = dataflow.node_map();
let mut fill = Fill::new(dataflow, &mut marks);
fill.mark(dataflow.undefined());
for &node in dataflow.inputs() { fill.mark(node); }
callback(fill.nested())
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::{code, Op};
use code::{Precision, BinaryOp, Width};
use Precision::*;
#[test]
fn test() {
let mut df = Dataflow::new(1);
let u = df.undefined();
let a = df.inputs()[0];
let guard = df.add_node(Op::Guard, &[u, a]);
let constant = df.add_node(Op::Constant(1), &[]);
let b = constant;
let add = df.add_node(Op::Binary(P64, BinaryOp::Add), &[a, b]);
let c = add;
let exit1 = Exit {sequence: guard, outputs: Box::new([c])};
let store = df.add_node(Op::Store(0, Width::Eight), &[guard, b, a]);
let d = store;
let exit2 = Exit {sequence: store, outputs: Box::new([d])};
let _ = df.add_node(Op::Binary(P64, BinaryOp::Mul), &[b, b]);
let mut marks = df.node_map();
let mut fill = Fill::new(&df, &mut marks);
fill.input(u, dep::Value::Unused);
fill.input(a, dep::Value::Normal);
let mut fill1 = fill.nested();
fill1.exit(&exit1);
let (nodes1, frontier1) = fill1.drain();
assert_eq!(&nodes1, &[guard, constant, add]);
assert_eq!(frontier1.0.len(), 2);
assert_eq!(frontier1.0[&u], dep::Value::Unused);
assert_eq!(frontier1.0[&a], dep::Value::Normal);
for node in nodes1 { fill1.mark(node); }
let mut fill2 = fill1.nested();
fill2.exit(&exit2);
let (nodes2, frontier2) = fill2.drain();
assert_eq!(&nodes2, &[store]);
assert_eq!(frontier2.0.len(), 3);
assert_eq!(frontier2.0[&a], dep::Value::Address);
assert_eq!(frontier2.0[&b], dep::Value::Normal);
assert_eq!(frontier2.0[&guard], dep::Value::Unused);
for node in nodes2 { fill2.mark(node); }
assert_eq!(marks.as_ref(), &[1, 1, 2, 2, 2, 3, 0]);
}
}