use alloc::{collections::BTreeMap, vec::Vec};
use miden_crypto::{hash::blake::Blake3Digest, utils::collections::KvMap};
use crate::mast::{
DecoratorId, MastForest, MastForestError, MastNode, MastNodeFingerprint, MastNodeId,
MultiMastForestIteratorItem, MultiMastForestNodeIter,
};
#[cfg(test)]
mod tests;
pub(crate) struct MastForestMerger {
mast_forest: MastForest,
node_id_by_hash: BTreeMap<MastNodeFingerprint, MastNodeId>,
hash_by_node_id: BTreeMap<MastNodeId, MastNodeFingerprint>,
decorators_by_hash: BTreeMap<Blake3Digest<32>, DecoratorId>,
decorator_id_mappings: Vec<DecoratorIdMap>,
node_id_mappings: Vec<MastForestNodeIdMap>,
}
impl MastForestMerger {
pub(crate) fn merge<'forest>(
forests: impl IntoIterator<Item = &'forest MastForest>,
) -> Result<(MastForest, MastForestRootMap), MastForestError> {
let forests = forests.into_iter().collect::<Vec<_>>();
let decorator_id_mappings = Vec::with_capacity(forests.len());
let node_id_mappings = vec![MastForestNodeIdMap::new(); forests.len()];
let mut merger = Self {
node_id_by_hash: BTreeMap::new(),
hash_by_node_id: BTreeMap::new(),
decorators_by_hash: BTreeMap::new(),
mast_forest: MastForest::new(),
decorator_id_mappings,
node_id_mappings,
};
merger.merge_inner(forests.clone())?;
let Self { mast_forest, node_id_mappings, .. } = merger;
let root_maps = MastForestRootMap::from_node_id_map(node_id_mappings, forests);
Ok((mast_forest, root_maps))
}
fn merge_inner(&mut self, forests: Vec<&MastForest>) -> Result<(), MastForestError> {
for other_forest in forests.iter() {
self.merge_advice_map(other_forest)?;
}
for other_forest in forests.iter() {
self.merge_decorators(other_forest)?;
}
for other_forest in forests.iter() {
self.merge_error_codes(other_forest)?;
}
let iterator = MultiMastForestNodeIter::new(forests.clone());
for item in iterator {
match item {
MultiMastForestIteratorItem::Node { forest_idx, node_id } => {
let node = &forests[forest_idx][node_id];
self.merge_node(forest_idx, node_id, node)?;
},
MultiMastForestIteratorItem::ExternalNodeReplacement {
replacement_forest_idx,
replacement_mast_node_id,
replaced_forest_idx,
replaced_mast_node_id,
} => {
let mapped_replacement = self.node_id_mappings[replacement_forest_idx]
.get(&replacement_mast_node_id)
.copied()
.expect("every merged node id should be mapped");
self.node_id_mappings[replaced_forest_idx]
.insert(replaced_mast_node_id, mapped_replacement);
},
}
}
for (forest_idx, forest) in forests.iter().enumerate() {
self.merge_roots(forest_idx, forest)?;
}
Ok(())
}
fn merge_decorators(&mut self, other_forest: &MastForest) -> Result<(), MastForestError> {
let mut decorator_id_remapping = DecoratorIdMap::new(other_forest.decorators.len());
for (merging_id, merging_decorator) in other_forest.decorators.iter().enumerate() {
let merging_decorator_hash = merging_decorator.fingerprint();
let new_decorator_id = if let Some(existing_decorator) =
self.decorators_by_hash.get(&merging_decorator_hash)
{
*existing_decorator
} else {
let new_decorator_id = self.mast_forest.add_decorator(merging_decorator.clone())?;
self.decorators_by_hash.insert(merging_decorator_hash, new_decorator_id);
new_decorator_id
};
decorator_id_remapping
.insert(DecoratorId::new_unchecked(merging_id as u32), new_decorator_id);
}
self.decorator_id_mappings.push(decorator_id_remapping);
Ok(())
}
fn merge_advice_map(&mut self, other_forest: &MastForest) -> Result<(), MastForestError> {
for (digest, values) in other_forest.advice_map.iter() {
if let Some(stored_values) = self.mast_forest.advice_map().get(digest) {
if stored_values != values {
return Err(MastForestError::AdviceMapKeyCollisionOnMerge(*digest));
}
} else {
self.mast_forest.advice_map_mut().insert(*digest, values.clone());
}
}
Ok(())
}
fn merge_error_codes(&mut self, other_forest: &MastForest) -> Result<(), MastForestError> {
self.mast_forest.error_codes.extend(other_forest.error_codes.clone());
Ok(())
}
fn merge_node(
&mut self,
forest_idx: usize,
merging_id: MastNodeId,
node: &MastNode,
) -> Result<(), MastForestError> {
let remapped_node = self.remap_node(forest_idx, node)?;
let node_fingerprint = MastNodeFingerprint::from_mast_node(
&self.mast_forest,
&self.hash_by_node_id,
&remapped_node,
)
.expect(
"hash_by_node_id should contain the fingerprints of all children of `remapped_node`",
);
match self.lookup_node_by_fingerprint(&node_fingerprint) {
Some(matching_node_id) => {
self.node_id_mappings[forest_idx].insert(merging_id, matching_node_id);
},
None => {
let new_node_id = self.mast_forest.add_node(remapped_node)?;
self.node_id_mappings[forest_idx].insert(merging_id, new_node_id);
self.node_id_by_hash.insert(node_fingerprint, new_node_id);
self.hash_by_node_id.insert(new_node_id, node_fingerprint);
},
}
Ok(())
}
fn merge_roots(
&mut self,
forest_idx: usize,
other_forest: &MastForest,
) -> Result<(), MastForestError> {
for root_id in other_forest.roots.iter() {
let new_root = self.node_id_mappings[forest_idx]
.get(root_id)
.expect("all node ids should have an entry");
self.mast_forest.make_root(*new_root);
}
Ok(())
}
fn remap_node(&self, forest_idx: usize, node: &MastNode) -> Result<MastNode, MastForestError> {
let map_decorator_id = |decorator_id: &DecoratorId| {
self.decorator_id_mappings[forest_idx].get(decorator_id).ok_or_else(|| {
MastForestError::DecoratorIdOverflow(
*decorator_id,
self.decorator_id_mappings[forest_idx].len(),
)
})
};
let map_decorators = |decorators: &[DecoratorId]| -> Result<Vec<_>, MastForestError> {
decorators.iter().map(map_decorator_id).collect()
};
let map_node_id = |node_id: MastNodeId| {
self.node_id_mappings[forest_idx]
.get(&node_id)
.copied()
.expect("every node id should have an entry")
};
let mut mapped_node = match node {
MastNode::Join(join_node) => {
let first = map_node_id(join_node.first());
let second = map_node_id(join_node.second());
MastNode::new_join(first, second, &self.mast_forest)
.expect("JoinNode children should have been mapped to a lower index")
},
MastNode::Split(split_node) => {
let if_branch = map_node_id(split_node.on_true());
let else_branch = map_node_id(split_node.on_false());
MastNode::new_split(if_branch, else_branch, &self.mast_forest)
.expect("SplitNode children should have been mapped to a lower index")
},
MastNode::Loop(loop_node) => {
let body = map_node_id(loop_node.body());
MastNode::new_loop(body, &self.mast_forest)
.expect("LoopNode children should have been mapped to a lower index")
},
MastNode::Call(call_node) => {
let callee = map_node_id(call_node.callee());
MastNode::new_call(callee, &self.mast_forest)
.expect("CallNode children should have been mapped to a lower index")
},
MastNode::Block(basic_block_node) => {
MastNode::new_basic_block(
basic_block_node.operations().copied().collect(),
Some(
basic_block_node
.decorators()
.iter()
.map(|(idx, decorator_id)| match map_decorator_id(decorator_id) {
Ok(mapped_decorator) => Ok((*idx, mapped_decorator)),
Err(err) => Err(err),
})
.collect::<Result<Vec<_>, _>>()?,
),
)
.expect("previously valid BasicBlockNode should still be valid")
},
MastNode::Dyn(_) => MastNode::new_dyn(),
MastNode::External(external_node) => MastNode::new_external(external_node.digest()),
};
if !mapped_node.is_basic_block() {
mapped_node.append_before_enter(&map_decorators(node.before_enter())?);
mapped_node.append_after_exit(&map_decorators(node.after_exit())?);
}
Ok(mapped_node)
}
fn lookup_node_by_fingerprint(&self, fingerprint: &MastNodeFingerprint) -> Option<MastNodeId> {
self.node_id_by_hash.get(fingerprint).copied()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MastForestRootMap {
root_maps: Vec<BTreeMap<MastNodeId, MastNodeId>>,
}
impl MastForestRootMap {
fn from_node_id_map(id_map: Vec<MastForestNodeIdMap>, forests: Vec<&MastForest>) -> Self {
let mut root_maps = vec![BTreeMap::new(); forests.len()];
for (forest_idx, forest) in forests.into_iter().enumerate() {
for root in forest.procedure_roots() {
let new_id = id_map[forest_idx]
.get(root)
.copied()
.expect("every node id should be mapped to its new id");
root_maps[forest_idx].insert(*root, new_id);
}
}
Self { root_maps }
}
pub fn map_root(&self, forest_index: usize, root: &MastNodeId) -> Option<MastNodeId> {
self.root_maps.get(forest_index).and_then(|map| map.get(root)).copied()
}
}
struct DecoratorIdMap {
inner: Vec<Option<DecoratorId>>,
}
impl DecoratorIdMap {
fn new(num_ids: usize) -> Self {
Self { inner: vec![None; num_ids] }
}
fn insert(&mut self, key: DecoratorId, value: DecoratorId) {
self.inner[key.as_usize()] = Some(value);
}
fn get(&self, key: &DecoratorId) -> Option<DecoratorId> {
self.inner
.get(key.as_usize())
.map(|id| id.expect("every id should have a Some entry in the map when calling get"))
}
fn len(&self) -> usize {
self.inner.len()
}
}
type MastForestNodeIdMap = BTreeMap<MastNodeId, MastNodeId>;