Rewrite expressions to use Share(idx) references for shared subterms. Returns the rewritten expressions and the sharing vector.
( exprs: &[Arc<Expr>], shared_hashes: &IndexSet<blake3::Hash>, ptr_to_hash: &FxHashMap<*const Expr, blake3::Hash>, info_map: &HashMap<blake3::Hash, SubtermInfo>, topo_order: &[blake3::Hash],
| 630 | /// Rewrite expressions to use Share(idx) references for shared subterms. |
| 631 | /// |
| 632 | /// Returns the rewritten expressions and the sharing vector. |
| 633 | pub fn build_sharing_vec( |
| 634 | exprs: &[Arc<Expr>], |
| 635 | shared_hashes: &IndexSet<blake3::Hash>, |
| 636 | ptr_to_hash: &FxHashMap<*const Expr, blake3::Hash>, |
| 637 | info_map: &FxHashMap<blake3::Hash, SubtermInfo>, |
| 638 | topo_order: &[blake3::Hash], |
| 639 | ) -> (Vec<Arc<Expr>>, Vec<Arc<Expr>>) { |
| 640 | // CRITICAL: Re-sort shared_hashes in topological order (leaves first). |
| 641 | // decide_sharing returns hashes sorted by gross benefit (large terms first), |
| 642 | // but we need leaves first so that when serializing sharing[i], all its |
| 643 | // children are already available as Share(j) for j < i. |
| 644 | let shared_in_topo_order: Vec<blake3::Hash> = |
| 645 | topo_order.iter().copied().filter(|h| shared_hashes.contains(h)).collect(); |
| 646 | |
| 647 | // Build sharing vector incrementally to avoid forward references. |
| 648 | // When building sharing[i], only Share(j) for j < i is allowed. |
| 649 | let mut sharing_vec: Vec<Arc<Expr>> = Vec::with_capacity(shared_hashes.len()); |
| 650 | let mut hash_to_idx: FxHashMap<blake3::Hash, u64> = FxHashMap::default(); |
| 651 | let mut cache: FxHashMap<*const Expr, Arc<Expr>> = FxHashMap::default(); |
| 652 | |
| 653 | for h in &shared_in_topo_order { |
| 654 | let info = info_map.get(h).expect("shared hash must be in info_map"); |
| 655 | // No cache.clear() needed: rewrite_expr checks hash_to_idx BEFORE the |
| 656 | // cache, so newly-shareable expressions are always caught even if the |
| 657 | // cache has a stale entry from a prior iteration. Topological order |
| 658 | // guarantees all children of `h` were already added to hash_to_idx, |
| 659 | // so their cached rewrites (containing correct Share references) remain |
| 660 | // valid. |
| 661 | let rewritten = |
| 662 | rewrite_expr(&info.expr, &hash_to_idx, ptr_to_hash, &mut cache); |
| 663 | |
| 664 | let idx = sharing_vec.len() as u64; |
| 665 | sharing_vec.push(rewritten); |
| 666 | // Now add this hash to the map for subsequent entries |
| 667 | hash_to_idx.insert(*h, idx); |
| 668 | } |
| 669 | |
| 670 | // Rewrite the root expressions (can use all Share indices) |
| 671 | let rewritten_exprs: Vec<Arc<Expr>> = exprs |
| 672 | .iter() |
| 673 | .map(|e| rewrite_expr(e, &hash_to_idx, ptr_to_hash, &mut cache)) |
| 674 | .collect(); |
| 675 | |
| 676 | (rewritten_exprs, sharing_vec) |
| 677 | } |
| 678 | |
| 679 | /// Frame for iterative rewrite traversal. |