use alloc::{collections::BTreeSet, vec::Vec};
use miden_core::utils::IndexVec;
use super::{MastNodeRef, PendingMastNode};
pub(super) struct FinalForestLayout {
pub(super) procedure_root_refs: Vec<MastNodeRef>,
pub(super) live_node_refs: Vec<MastNodeRef>,
}
impl FinalForestLayout {
pub(super) fn plan(
procedure_root_refs: Vec<MastNodeRef>,
nodes: &IndexVec<MastNodeRef, PendingMastNode>,
) -> Self {
let node_refs_to_remove = Self::unreachable_node_refs(&procedure_root_refs, nodes);
let live_node_refs = Self::live_node_refs_in_final_order(nodes, &node_refs_to_remove);
Self { procedure_root_refs, live_node_refs }
}
fn unreachable_node_refs(
procedure_root_refs: &[MastNodeRef],
nodes: &IndexVec<MastNodeRef, PendingMastNode>,
) -> BTreeSet<MastNodeRef> {
let mut reachable_node_refs = BTreeSet::new();
let mut worklist = procedure_root_refs.to_vec();
while let Some(node_ref) = worklist.pop() {
if reachable_node_refs.insert(node_ref) {
worklist.extend(nodes[node_ref].child_refs.iter().copied());
}
}
(0..nodes.len())
.map(|index| MastNodeRef::from(index as u32))
.filter(|node_ref| !reachable_node_refs.contains(node_ref))
.collect()
}
fn live_node_refs_in_final_order(
nodes: &IndexVec<MastNodeRef, PendingMastNode>,
node_refs_to_remove: &BTreeSet<MastNodeRef>,
) -> Vec<MastNodeRef> {
let mut live_node_refs = (0..nodes.len())
.map(|index| MastNodeRef::from(index as u32))
.filter(|node_ref| !node_refs_to_remove.contains(node_ref))
.collect::<Vec<_>>();
let live_node_ref_set = live_node_refs.iter().copied().collect::<BTreeSet<_>>();
let mut external_node_refs = Vec::new();
live_node_refs.retain(|node_ref| {
if nodes[*node_ref].kind.is_external() {
external_node_refs.push(*node_ref);
false
} else {
true
}
});
external_node_refs.sort_by_key(|node_ref| (nodes[*node_ref].key, *node_ref));
let mut final_order = external_node_refs;
let mut emitted_node_refs = final_order.iter().copied().collect::<BTreeSet<_>>();
let mut remaining_node_refs = live_node_refs.into_iter().collect::<BTreeSet<_>>();
while !remaining_node_refs.is_empty() {
let mut ready_node_refs = remaining_node_refs
.iter()
.copied()
.filter(|node_ref| {
nodes[*node_ref].child_refs.iter().all(|child_ref| {
!live_node_ref_set.contains(child_ref)
|| emitted_node_refs.contains(child_ref)
})
})
.collect::<Vec<_>>();
assert!(
!ready_node_refs.is_empty(),
"pending MAST nodes must form an acyclic dependency graph"
);
ready_node_refs.sort();
for node_ref in ready_node_refs {
remaining_node_refs.remove(&node_ref);
emitted_node_refs.insert(node_ref);
final_order.push(node_ref);
}
}
final_order
}
}