Skip to main content

oxilean_codegen/closure_convert/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::lcnf::*;
6use std::collections::{HashMap, HashSet};
7
8use super::functions::*;
9use std::collections::VecDeque;
10
11/// The escape status of a closure or variable.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub enum EscapeInfo {
14    /// The value does not escape its defining scope. It can be
15    /// stack-allocated.
16    NoEscape,
17    /// The value escapes within the current function (e.g. stored in
18    /// a local data structure) but does not escape the function itself.
19    LocalEscape,
20    /// The value escapes the function (e.g. returned, stored in a global,
21    /// or passed to an unknown function). Must be heap-allocated.
22    GlobalEscape,
23}
24impl EscapeInfo {
25    /// Merge two escape infos, taking the more conservative (higher) one.
26    pub fn merge(self, other: EscapeInfo) -> EscapeInfo {
27        match (self, other) {
28            (EscapeInfo::GlobalEscape, _) | (_, EscapeInfo::GlobalEscape) => {
29                EscapeInfo::GlobalEscape
30            }
31            (EscapeInfo::LocalEscape, _) | (_, EscapeInfo::LocalEscape) => EscapeInfo::LocalEscape,
32            _ => EscapeInfo::NoEscape,
33        }
34    }
35    /// Whether heap allocation is required.
36    pub fn requires_heap(self) -> bool {
37        matches!(self, EscapeInfo::GlobalEscape)
38    }
39}
40#[allow(dead_code)]
41#[derive(Debug, Clone)]
42pub struct CCDominatorTree {
43    pub idom: Vec<Option<u32>>,
44    pub dom_children: Vec<Vec<u32>>,
45    pub dom_depth: Vec<u32>,
46}
47impl CCDominatorTree {
48    #[allow(dead_code)]
49    pub fn new(size: usize) -> Self {
50        CCDominatorTree {
51            idom: vec![None; size],
52            dom_children: vec![Vec::new(); size],
53            dom_depth: vec![0; size],
54        }
55    }
56    #[allow(dead_code)]
57    pub fn set_idom(&mut self, node: usize, idom: u32) {
58        self.idom[node] = Some(idom);
59    }
60    #[allow(dead_code)]
61    pub fn dominates(&self, a: usize, b: usize) -> bool {
62        if a == b {
63            return true;
64        }
65        let mut cur = b;
66        loop {
67            match self.idom[cur] {
68                Some(parent) if parent as usize == a => return true,
69                Some(parent) if parent as usize == cur => return false,
70                Some(parent) => cur = parent as usize,
71                None => return false,
72            }
73        }
74    }
75    #[allow(dead_code)]
76    pub fn depth(&self, node: usize) -> u32 {
77        self.dom_depth.get(node).copied().unwrap_or(0)
78    }
79}
80#[allow(dead_code)]
81#[derive(Debug, Clone)]
82pub struct CCAnalysisCache {
83    pub(super) entries: std::collections::HashMap<String, CCCacheEntry>,
84    pub(super) max_size: usize,
85    pub(super) hits: u64,
86    pub(super) misses: u64,
87}
88impl CCAnalysisCache {
89    #[allow(dead_code)]
90    pub fn new(max_size: usize) -> Self {
91        CCAnalysisCache {
92            entries: std::collections::HashMap::new(),
93            max_size,
94            hits: 0,
95            misses: 0,
96        }
97    }
98    #[allow(dead_code)]
99    pub fn get(&mut self, key: &str) -> Option<&CCCacheEntry> {
100        if self.entries.contains_key(key) {
101            self.hits += 1;
102            self.entries.get(key)
103        } else {
104            self.misses += 1;
105            None
106        }
107    }
108    #[allow(dead_code)]
109    pub fn insert(&mut self, key: String, data: Vec<u8>) {
110        if self.entries.len() >= self.max_size {
111            if let Some(oldest) = self.entries.keys().next().cloned() {
112                self.entries.remove(&oldest);
113            }
114        }
115        self.entries.insert(
116            key.clone(),
117            CCCacheEntry {
118                key,
119                data,
120                timestamp: 0,
121                valid: true,
122            },
123        );
124    }
125    #[allow(dead_code)]
126    pub fn invalidate(&mut self, key: &str) {
127        if let Some(entry) = self.entries.get_mut(key) {
128            entry.valid = false;
129        }
130    }
131    #[allow(dead_code)]
132    pub fn clear(&mut self) {
133        self.entries.clear();
134    }
135    #[allow(dead_code)]
136    pub fn hit_rate(&self) -> f64 {
137        let total = self.hits + self.misses;
138        if total == 0 {
139            return 0.0;
140        }
141        self.hits as f64 / total as f64
142    }
143    #[allow(dead_code)]
144    pub fn size(&self) -> usize {
145        self.entries.len()
146    }
147}
148/// Dominator tree for CCExt.
149#[allow(dead_code)]
150#[derive(Debug, Clone)]
151pub struct CCExtDomTree {
152    pub(super) idom: Vec<Option<usize>>,
153    pub(super) children: Vec<Vec<usize>>,
154    pub(super) depth: Vec<usize>,
155}
156impl CCExtDomTree {
157    #[allow(dead_code)]
158    pub fn new(n: usize) -> Self {
159        Self {
160            idom: vec![None; n],
161            children: vec![Vec::new(); n],
162            depth: vec![0; n],
163        }
164    }
165    #[allow(dead_code)]
166    pub fn set_idom(&mut self, node: usize, dom: usize) {
167        if node < self.idom.len() {
168            self.idom[node] = Some(dom);
169            if dom < self.children.len() {
170                self.children[dom].push(node);
171            }
172            self.depth[node] = if dom < self.depth.len() {
173                self.depth[dom] + 1
174            } else {
175                1
176            };
177        }
178    }
179    #[allow(dead_code)]
180    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
181        if a == b {
182            return true;
183        }
184        let n = self.idom.len();
185        for _ in 0..n {
186            match self.idom.get(b).copied().flatten() {
187                None => return false,
188                Some(p) if p == a => return true,
189                Some(p) if p == b => return false,
190                Some(p) => b = p,
191            }
192        }
193        false
194    }
195    #[allow(dead_code)]
196    pub fn children_of(&self, n: usize) -> &[usize] {
197        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
198    }
199    #[allow(dead_code)]
200    pub fn depth_of(&self, n: usize) -> usize {
201        self.depth.get(n).copied().unwrap_or(0)
202    }
203    #[allow(dead_code)]
204    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
205        let n = self.idom.len();
206        for _ in 0..(2 * n) {
207            if a == b {
208                return a;
209            }
210            if self.depth_of(a) > self.depth_of(b) {
211                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
212            } else {
213                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
214            }
215        }
216        0
217    }
218}
219/// Performs escape analysis on an LCNF module.
220pub struct EscapeAnalysis {
221    /// Escape status for each variable.
222    pub(super) var_escape: HashMap<LcnfVarId, EscapeInfo>,
223    /// Escape status for each function.
224    pub(super) fn_escape: HashMap<String, EscapeInfo>,
225    /// Which variables are returned from functions.
226    pub(super) returned_vars: HashSet<LcnfVarId>,
227    /// Which variables are passed to unknown functions.
228    pub(super) externally_passed: HashSet<LcnfVarId>,
229    /// Which variables are stored in constructors.
230    pub(super) stored_in_ctor: HashSet<LcnfVarId>,
231}
232impl EscapeAnalysis {
233    /// Create a new escape analysis.
234    pub fn new() -> Self {
235        EscapeAnalysis {
236            var_escape: HashMap::new(),
237            fn_escape: HashMap::new(),
238            returned_vars: HashSet::new(),
239            externally_passed: HashSet::new(),
240            stored_in_ctor: HashSet::new(),
241        }
242    }
243    /// Analyze an entire module.
244    pub fn analyze(&mut self, module: &LcnfModule) -> HashMap<String, EscapeInfo> {
245        for decl in &module.fun_decls {
246            self.analyze_function(decl);
247        }
248        self.fn_escape.clone()
249    }
250    /// Analyze a single function declaration.
251    pub(super) fn analyze_function(&mut self, decl: &LcnfFunDecl) {
252        for param in &decl.params {
253            self.var_escape.insert(param.id, EscapeInfo::LocalEscape);
254        }
255        self.analyze_expr(&decl.body, false);
256        let fn_escape = if self.returned_vars.iter().any(|v| {
257            self.var_escape
258                .get(v)
259                .copied()
260                .unwrap_or(EscapeInfo::NoEscape)
261                == EscapeInfo::GlobalEscape
262        }) {
263            EscapeInfo::GlobalEscape
264        } else {
265            EscapeInfo::NoEscape
266        };
267        self.fn_escape.insert(decl.name.clone(), fn_escape);
268    }
269    /// Analyze an expression for escape behavior.
270    pub(super) fn analyze_expr(&mut self, expr: &LcnfExpr, _is_tail_position: bool) {
271        match expr {
272            LcnfExpr::Let {
273                id, value, body, ..
274            } => {
275                let escape = self.analyze_let_value(value);
276                self.var_escape.insert(*id, escape);
277                self.analyze_expr(body, _is_tail_position);
278            }
279            LcnfExpr::Case {
280                scrutinee,
281                alts,
282                default,
283                ..
284            } => {
285                self.mark_read(*scrutinee);
286                for alt in alts {
287                    for param in &alt.params {
288                        self.var_escape.insert(param.id, EscapeInfo::LocalEscape);
289                    }
290                    self.analyze_expr(&alt.body, _is_tail_position);
291                }
292                if let Some(def) = default {
293                    self.analyze_expr(def, _is_tail_position);
294                }
295            }
296            LcnfExpr::Return(arg) => {
297                if let LcnfArg::Var(v) = arg {
298                    self.returned_vars.insert(*v);
299                    self.mark_escape(*v, EscapeInfo::GlobalEscape);
300                }
301            }
302            LcnfExpr::TailCall(func, args) => {
303                if let LcnfArg::Var(v) = func {
304                    self.mark_escape(*v, EscapeInfo::GlobalEscape);
305                }
306                for arg in args {
307                    if let LcnfArg::Var(v) = arg {
308                        self.externally_passed.insert(*v);
309                        self.mark_escape(*v, EscapeInfo::GlobalEscape);
310                    }
311                }
312            }
313            LcnfExpr::Unreachable => {}
314        }
315    }
316    /// Analyze a let-value and return its escape status.
317    pub(super) fn analyze_let_value(&mut self, value: &LcnfLetValue) -> EscapeInfo {
318        match value {
319            LcnfLetValue::App(func, args) => {
320                if let LcnfArg::Var(v) = func {
321                    self.mark_read(*v);
322                }
323                for arg in args {
324                    if let LcnfArg::Var(v) = arg {
325                        self.externally_passed.insert(*v);
326                        self.mark_escape(*v, EscapeInfo::GlobalEscape);
327                    }
328                }
329                EscapeInfo::LocalEscape
330            }
331            LcnfLetValue::Ctor(_, _, args) => {
332                for arg in args {
333                    if let LcnfArg::Var(v) = arg {
334                        self.stored_in_ctor.insert(*v);
335                        self.mark_escape(*v, EscapeInfo::GlobalEscape);
336                    }
337                }
338                EscapeInfo::LocalEscape
339            }
340            LcnfLetValue::Proj(_, _, var) => {
341                self.mark_read(*var);
342                EscapeInfo::LocalEscape
343            }
344            LcnfLetValue::FVar(var) => {
345                self.mark_read(*var);
346                self.var_escape
347                    .get(var)
348                    .copied()
349                    .unwrap_or(EscapeInfo::NoEscape)
350            }
351            LcnfLetValue::Lit(_)
352            | LcnfLetValue::Erased
353            | LcnfLetValue::Reset(_)
354            | LcnfLetValue::Reuse(_, _, _, _) => EscapeInfo::NoEscape,
355        }
356    }
357    /// Mark a variable as read (used but not escaped).
358    pub(super) fn mark_read(&mut self, var: LcnfVarId) {
359        self.var_escape.entry(var).or_insert(EscapeInfo::NoEscape);
360    }
361    /// Mark a variable with a specific escape level, merging with existing.
362    pub(super) fn mark_escape(&mut self, var: LcnfVarId, escape: EscapeInfo) {
363        let current = self
364            .var_escape
365            .get(&var)
366            .copied()
367            .unwrap_or(EscapeInfo::NoEscape);
368        self.var_escape.insert(var, current.merge(escape));
369    }
370    /// Get the escape status of a variable.
371    pub fn get_var_escape(&self, var: LcnfVarId) -> EscapeInfo {
372        self.var_escape
373            .get(&var)
374            .copied()
375            .unwrap_or(EscapeInfo::NoEscape)
376    }
377    /// Get the escape status of a function.
378    pub fn get_fn_escape(&self, name: &str) -> EscapeInfo {
379        self.fn_escape
380            .get(name)
381            .copied()
382            .unwrap_or(EscapeInfo::NoEscape)
383    }
384}
385/// Constant folding helper for CCExt.
386#[allow(dead_code)]
387#[derive(Debug, Clone, Default)]
388pub struct CCExtConstFolder {
389    pub(super) folds: usize,
390    pub(super) failures: usize,
391    pub(super) enabled: bool,
392}
393impl CCExtConstFolder {
394    #[allow(dead_code)]
395    pub fn new() -> Self {
396        Self {
397            folds: 0,
398            failures: 0,
399            enabled: true,
400        }
401    }
402    #[allow(dead_code)]
403    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
404        self.folds += 1;
405        a.checked_add(b)
406    }
407    #[allow(dead_code)]
408    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
409        self.folds += 1;
410        a.checked_sub(b)
411    }
412    #[allow(dead_code)]
413    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
414        self.folds += 1;
415        a.checked_mul(b)
416    }
417    #[allow(dead_code)]
418    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
419        if b == 0 {
420            self.failures += 1;
421            None
422        } else {
423            self.folds += 1;
424            a.checked_div(b)
425        }
426    }
427    #[allow(dead_code)]
428    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
429        if b == 0 {
430            self.failures += 1;
431            None
432        } else {
433            self.folds += 1;
434            a.checked_rem(b)
435        }
436    }
437    #[allow(dead_code)]
438    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
439        self.folds += 1;
440        a.checked_neg()
441    }
442    #[allow(dead_code)]
443    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
444        if s >= 64 {
445            self.failures += 1;
446            None
447        } else {
448            self.folds += 1;
449            a.checked_shl(s)
450        }
451    }
452    #[allow(dead_code)]
453    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
454        if s >= 64 {
455            self.failures += 1;
456            None
457        } else {
458            self.folds += 1;
459            a.checked_shr(s)
460        }
461    }
462    #[allow(dead_code)]
463    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
464        self.folds += 1;
465        a & b
466    }
467    #[allow(dead_code)]
468    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
469        self.folds += 1;
470        a | b
471    }
472    #[allow(dead_code)]
473    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
474        self.folds += 1;
475        a ^ b
476    }
477    #[allow(dead_code)]
478    pub fn not_i64(&mut self, a: i64) -> i64 {
479        self.folds += 1;
480        !a
481    }
482    #[allow(dead_code)]
483    pub fn fold_count(&self) -> usize {
484        self.folds
485    }
486    #[allow(dead_code)]
487    pub fn failure_count(&self) -> usize {
488        self.failures
489    }
490    #[allow(dead_code)]
491    pub fn enable(&mut self) {
492        self.enabled = true;
493    }
494    #[allow(dead_code)]
495    pub fn disable(&mut self) {
496        self.enabled = false;
497    }
498    #[allow(dead_code)]
499    pub fn is_enabled(&self) -> bool {
500        self.enabled
501    }
502}
503/// Constant folding helper for CCX2.
504#[allow(dead_code)]
505#[derive(Debug, Clone, Default)]
506pub struct CCX2ConstFolder {
507    pub(super) folds: usize,
508    pub(super) failures: usize,
509    pub(super) enabled: bool,
510}
511impl CCX2ConstFolder {
512    #[allow(dead_code)]
513    pub fn new() -> Self {
514        Self {
515            folds: 0,
516            failures: 0,
517            enabled: true,
518        }
519    }
520    #[allow(dead_code)]
521    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
522        self.folds += 1;
523        a.checked_add(b)
524    }
525    #[allow(dead_code)]
526    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
527        self.folds += 1;
528        a.checked_sub(b)
529    }
530    #[allow(dead_code)]
531    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
532        self.folds += 1;
533        a.checked_mul(b)
534    }
535    #[allow(dead_code)]
536    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
537        if b == 0 {
538            self.failures += 1;
539            None
540        } else {
541            self.folds += 1;
542            a.checked_div(b)
543        }
544    }
545    #[allow(dead_code)]
546    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
547        if b == 0 {
548            self.failures += 1;
549            None
550        } else {
551            self.folds += 1;
552            a.checked_rem(b)
553        }
554    }
555    #[allow(dead_code)]
556    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
557        self.folds += 1;
558        a.checked_neg()
559    }
560    #[allow(dead_code)]
561    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
562        if s >= 64 {
563            self.failures += 1;
564            None
565        } else {
566            self.folds += 1;
567            a.checked_shl(s)
568        }
569    }
570    #[allow(dead_code)]
571    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
572        if s >= 64 {
573            self.failures += 1;
574            None
575        } else {
576            self.folds += 1;
577            a.checked_shr(s)
578        }
579    }
580    #[allow(dead_code)]
581    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
582        self.folds += 1;
583        a & b
584    }
585    #[allow(dead_code)]
586    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
587        self.folds += 1;
588        a | b
589    }
590    #[allow(dead_code)]
591    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
592        self.folds += 1;
593        a ^ b
594    }
595    #[allow(dead_code)]
596    pub fn not_i64(&mut self, a: i64) -> i64 {
597        self.folds += 1;
598        !a
599    }
600    #[allow(dead_code)]
601    pub fn fold_count(&self) -> usize {
602        self.folds
603    }
604    #[allow(dead_code)]
605    pub fn failure_count(&self) -> usize {
606        self.failures
607    }
608    #[allow(dead_code)]
609    pub fn enable(&mut self) {
610        self.enabled = true;
611    }
612    #[allow(dead_code)]
613    pub fn disable(&mut self) {
614        self.enabled = false;
615    }
616    #[allow(dead_code)]
617    pub fn is_enabled(&self) -> bool {
618        self.enabled
619    }
620}
621/// Statistics for CCExt passes.
622#[allow(dead_code)]
623#[derive(Debug, Clone, Default)]
624pub struct CCExtPassStats {
625    pub iterations: usize,
626    pub changed: bool,
627    pub nodes_visited: usize,
628    pub nodes_modified: usize,
629    pub time_ms: u64,
630    pub memory_bytes: usize,
631    pub errors: usize,
632}
633impl CCExtPassStats {
634    #[allow(dead_code)]
635    pub fn new() -> Self {
636        Self::default()
637    }
638    #[allow(dead_code)]
639    pub fn visit(&mut self) {
640        self.nodes_visited += 1;
641    }
642    #[allow(dead_code)]
643    pub fn modify(&mut self) {
644        self.nodes_modified += 1;
645        self.changed = true;
646    }
647    #[allow(dead_code)]
648    pub fn iterate(&mut self) {
649        self.iterations += 1;
650    }
651    #[allow(dead_code)]
652    pub fn error(&mut self) {
653        self.errors += 1;
654    }
655    #[allow(dead_code)]
656    pub fn efficiency(&self) -> f64 {
657        if self.nodes_visited == 0 {
658            0.0
659        } else {
660            self.nodes_modified as f64 / self.nodes_visited as f64
661        }
662    }
663    #[allow(dead_code)]
664    pub fn merge(&mut self, o: &CCExtPassStats) {
665        self.iterations += o.iterations;
666        self.changed |= o.changed;
667        self.nodes_visited += o.nodes_visited;
668        self.nodes_modified += o.nodes_modified;
669        self.time_ms += o.time_ms;
670        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
671        self.errors += o.errors;
672    }
673}
674/// Dependency graph for CCExt.
675#[allow(dead_code)]
676#[derive(Debug, Clone)]
677pub struct CCExtDepGraph {
678    pub(super) n: usize,
679    pub(super) adj: Vec<Vec<usize>>,
680    pub(super) rev: Vec<Vec<usize>>,
681    pub(super) edge_count: usize,
682}
683impl CCExtDepGraph {
684    #[allow(dead_code)]
685    pub fn new(n: usize) -> Self {
686        Self {
687            n,
688            adj: vec![Vec::new(); n],
689            rev: vec![Vec::new(); n],
690            edge_count: 0,
691        }
692    }
693    #[allow(dead_code)]
694    pub fn add_edge(&mut self, from: usize, to: usize) {
695        if from < self.n && to < self.n {
696            if !self.adj[from].contains(&to) {
697                self.adj[from].push(to);
698                self.rev[to].push(from);
699                self.edge_count += 1;
700            }
701        }
702    }
703    #[allow(dead_code)]
704    pub fn succs(&self, n: usize) -> &[usize] {
705        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
706    }
707    #[allow(dead_code)]
708    pub fn preds(&self, n: usize) -> &[usize] {
709        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
710    }
711    #[allow(dead_code)]
712    pub fn topo_sort(&self) -> Option<Vec<usize>> {
713        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
714        let mut q: std::collections::VecDeque<usize> =
715            (0..self.n).filter(|&i| deg[i] == 0).collect();
716        let mut out = Vec::with_capacity(self.n);
717        while let Some(u) = q.pop_front() {
718            out.push(u);
719            for &v in &self.adj[u] {
720                deg[v] -= 1;
721                if deg[v] == 0 {
722                    q.push_back(v);
723                }
724            }
725        }
726        if out.len() == self.n {
727            Some(out)
728        } else {
729            None
730        }
731    }
732    #[allow(dead_code)]
733    pub fn has_cycle(&self) -> bool {
734        self.topo_sort().is_none()
735    }
736    #[allow(dead_code)]
737    pub fn reachable(&self, start: usize) -> Vec<usize> {
738        let mut vis = vec![false; self.n];
739        let mut stk = vec![start];
740        let mut out = Vec::new();
741        while let Some(u) = stk.pop() {
742            if u < self.n && !vis[u] {
743                vis[u] = true;
744                out.push(u);
745                for &v in &self.adj[u] {
746                    if !vis[v] {
747                        stk.push(v);
748                    }
749                }
750            }
751        }
752        out
753    }
754    #[allow(dead_code)]
755    pub fn scc(&self) -> Vec<Vec<usize>> {
756        let mut visited = vec![false; self.n];
757        let mut order = Vec::new();
758        for i in 0..self.n {
759            if !visited[i] {
760                let mut stk = vec![(i, 0usize)];
761                while let Some((u, idx)) = stk.last_mut() {
762                    if !visited[*u] {
763                        visited[*u] = true;
764                    }
765                    if *idx < self.adj[*u].len() {
766                        let v = self.adj[*u][*idx];
767                        *idx += 1;
768                        if !visited[v] {
769                            stk.push((v, 0));
770                        }
771                    } else {
772                        order.push(*u);
773                        stk.pop();
774                    }
775                }
776            }
777        }
778        let mut comp = vec![usize::MAX; self.n];
779        let mut components: Vec<Vec<usize>> = Vec::new();
780        for &start in order.iter().rev() {
781            if comp[start] == usize::MAX {
782                let cid = components.len();
783                let mut component = Vec::new();
784                let mut stk = vec![start];
785                while let Some(u) = stk.pop() {
786                    if comp[u] == usize::MAX {
787                        comp[u] = cid;
788                        component.push(u);
789                        for &v in &self.rev[u] {
790                            if comp[v] == usize::MAX {
791                                stk.push(v);
792                            }
793                        }
794                    }
795                }
796                components.push(component);
797            }
798        }
799        components
800    }
801    #[allow(dead_code)]
802    pub fn node_count(&self) -> usize {
803        self.n
804    }
805    #[allow(dead_code)]
806    pub fn edge_count(&self) -> usize {
807        self.edge_count
808    }
809}
810/// Analysis cache for CCExt.
811#[allow(dead_code)]
812#[derive(Debug)]
813pub struct CCExtCache {
814    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
815    pub(super) cap: usize,
816    pub(super) total_hits: u64,
817    pub(super) total_misses: u64,
818}
819impl CCExtCache {
820    #[allow(dead_code)]
821    pub fn new(cap: usize) -> Self {
822        Self {
823            entries: Vec::new(),
824            cap,
825            total_hits: 0,
826            total_misses: 0,
827        }
828    }
829    #[allow(dead_code)]
830    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
831        for e in self.entries.iter_mut() {
832            if e.0 == key && e.2 {
833                e.3 += 1;
834                self.total_hits += 1;
835                return Some(&e.1);
836            }
837        }
838        self.total_misses += 1;
839        None
840    }
841    #[allow(dead_code)]
842    pub fn put(&mut self, key: u64, data: Vec<u8>) {
843        if self.entries.len() >= self.cap {
844            self.entries.retain(|e| e.2);
845            if self.entries.len() >= self.cap {
846                self.entries.remove(0);
847            }
848        }
849        self.entries.push((key, data, true, 0));
850    }
851    #[allow(dead_code)]
852    pub fn invalidate(&mut self) {
853        for e in self.entries.iter_mut() {
854            e.2 = false;
855        }
856    }
857    #[allow(dead_code)]
858    pub fn hit_rate(&self) -> f64 {
859        let t = self.total_hits + self.total_misses;
860        if t == 0 {
861            0.0
862        } else {
863            self.total_hits as f64 / t as f64
864        }
865    }
866    #[allow(dead_code)]
867    pub fn live_count(&self) -> usize {
868        self.entries.iter().filter(|e| e.2).count()
869    }
870}
871/// Pass execution phase for CCX2.
872#[allow(dead_code)]
873#[derive(Debug, Clone, PartialEq, Eq, Hash)]
874pub enum CCX2PassPhase {
875    Early,
876    Middle,
877    Late,
878    Finalize,
879}
880impl CCX2PassPhase {
881    #[allow(dead_code)]
882    pub fn is_early(&self) -> bool {
883        matches!(self, Self::Early)
884    }
885    #[allow(dead_code)]
886    pub fn is_middle(&self) -> bool {
887        matches!(self, Self::Middle)
888    }
889    #[allow(dead_code)]
890    pub fn is_late(&self) -> bool {
891        matches!(self, Self::Late)
892    }
893    #[allow(dead_code)]
894    pub fn is_finalize(&self) -> bool {
895        matches!(self, Self::Finalize)
896    }
897    #[allow(dead_code)]
898    pub fn order(&self) -> u32 {
899        match self {
900            Self::Early => 0,
901            Self::Middle => 1,
902            Self::Late => 2,
903            Self::Finalize => 3,
904        }
905    }
906    #[allow(dead_code)]
907    pub fn from_order(n: u32) -> Option<Self> {
908        match n {
909            0 => Some(Self::Early),
910            1 => Some(Self::Middle),
911            2 => Some(Self::Late),
912            3 => Some(Self::Finalize),
913            _ => None,
914        }
915    }
916}
917#[allow(dead_code)]
918pub struct CCConstantFoldingHelper;
919impl CCConstantFoldingHelper {
920    #[allow(dead_code)]
921    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
922        a.checked_add(b)
923    }
924    #[allow(dead_code)]
925    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
926        a.checked_sub(b)
927    }
928    #[allow(dead_code)]
929    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
930        a.checked_mul(b)
931    }
932    #[allow(dead_code)]
933    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
934        if b == 0 {
935            None
936        } else {
937            a.checked_div(b)
938        }
939    }
940    #[allow(dead_code)]
941    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
942        a + b
943    }
944    #[allow(dead_code)]
945    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
946        a * b
947    }
948    #[allow(dead_code)]
949    pub fn fold_neg_i64(a: i64) -> Option<i64> {
950        a.checked_neg()
951    }
952    #[allow(dead_code)]
953    pub fn fold_not_bool(a: bool) -> bool {
954        !a
955    }
956    #[allow(dead_code)]
957    pub fn fold_and_bool(a: bool, b: bool) -> bool {
958        a && b
959    }
960    #[allow(dead_code)]
961    pub fn fold_or_bool(a: bool, b: bool) -> bool {
962        a || b
963    }
964    #[allow(dead_code)]
965    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
966        a.checked_shl(b)
967    }
968    #[allow(dead_code)]
969    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
970        a.checked_shr(b)
971    }
972    #[allow(dead_code)]
973    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
974        if b == 0 {
975            None
976        } else {
977            Some(a % b)
978        }
979    }
980    #[allow(dead_code)]
981    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
982        a & b
983    }
984    #[allow(dead_code)]
985    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
986        a | b
987    }
988    #[allow(dead_code)]
989    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
990        a ^ b
991    }
992    #[allow(dead_code)]
993    pub fn fold_bitnot_i64(a: i64) -> i64 {
994        !a
995    }
996}
997/// Pass execution phase for CCExt.
998#[allow(dead_code)]
999#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1000pub enum CCExtPassPhase {
1001    Early,
1002    Middle,
1003    Late,
1004    Finalize,
1005}
1006impl CCExtPassPhase {
1007    #[allow(dead_code)]
1008    pub fn is_early(&self) -> bool {
1009        matches!(self, Self::Early)
1010    }
1011    #[allow(dead_code)]
1012    pub fn is_middle(&self) -> bool {
1013        matches!(self, Self::Middle)
1014    }
1015    #[allow(dead_code)]
1016    pub fn is_late(&self) -> bool {
1017        matches!(self, Self::Late)
1018    }
1019    #[allow(dead_code)]
1020    pub fn is_finalize(&self) -> bool {
1021        matches!(self, Self::Finalize)
1022    }
1023    #[allow(dead_code)]
1024    pub fn order(&self) -> u32 {
1025        match self {
1026            Self::Early => 0,
1027            Self::Middle => 1,
1028            Self::Late => 2,
1029            Self::Finalize => 3,
1030        }
1031    }
1032    #[allow(dead_code)]
1033    pub fn from_order(n: u32) -> Option<Self> {
1034        match n {
1035            0 => Some(Self::Early),
1036            1 => Some(Self::Middle),
1037            2 => Some(Self::Late),
1038            3 => Some(Self::Finalize),
1039            _ => None,
1040        }
1041    }
1042}
1043/// Configuration for CCExt passes.
1044#[allow(dead_code)]
1045#[derive(Debug, Clone)]
1046pub struct CCExtPassConfig {
1047    pub name: String,
1048    pub phase: CCExtPassPhase,
1049    pub enabled: bool,
1050    pub max_iterations: usize,
1051    pub debug: u32,
1052    pub timeout_ms: Option<u64>,
1053}
1054impl CCExtPassConfig {
1055    #[allow(dead_code)]
1056    pub fn new(name: impl Into<String>) -> Self {
1057        Self {
1058            name: name.into(),
1059            phase: CCExtPassPhase::Middle,
1060            enabled: true,
1061            max_iterations: 100,
1062            debug: 0,
1063            timeout_ms: None,
1064        }
1065    }
1066    #[allow(dead_code)]
1067    pub fn with_phase(mut self, phase: CCExtPassPhase) -> Self {
1068        self.phase = phase;
1069        self
1070    }
1071    #[allow(dead_code)]
1072    pub fn with_max_iter(mut self, n: usize) -> Self {
1073        self.max_iterations = n;
1074        self
1075    }
1076    #[allow(dead_code)]
1077    pub fn with_debug(mut self, d: u32) -> Self {
1078        self.debug = d;
1079        self
1080    }
1081    #[allow(dead_code)]
1082    pub fn disabled(mut self) -> Self {
1083        self.enabled = false;
1084        self
1085    }
1086    #[allow(dead_code)]
1087    pub fn with_timeout(mut self, ms: u64) -> Self {
1088        self.timeout_ms = Some(ms);
1089        self
1090    }
1091    #[allow(dead_code)]
1092    pub fn is_debug_enabled(&self) -> bool {
1093        self.debug > 0
1094    }
1095}
1096/// Pass registry for CCX2.
1097#[allow(dead_code)]
1098#[derive(Debug, Default)]
1099pub struct CCX2PassRegistry {
1100    pub(super) configs: Vec<CCX2PassConfig>,
1101    pub(super) stats: Vec<CCX2PassStats>,
1102}
1103impl CCX2PassRegistry {
1104    #[allow(dead_code)]
1105    pub fn new() -> Self {
1106        Self::default()
1107    }
1108    #[allow(dead_code)]
1109    pub fn register(&mut self, c: CCX2PassConfig) {
1110        self.stats.push(CCX2PassStats::new());
1111        self.configs.push(c);
1112    }
1113    #[allow(dead_code)]
1114    pub fn len(&self) -> usize {
1115        self.configs.len()
1116    }
1117    #[allow(dead_code)]
1118    pub fn is_empty(&self) -> bool {
1119        self.configs.is_empty()
1120    }
1121    #[allow(dead_code)]
1122    pub fn get(&self, i: usize) -> Option<&CCX2PassConfig> {
1123        self.configs.get(i)
1124    }
1125    #[allow(dead_code)]
1126    pub fn get_stats(&self, i: usize) -> Option<&CCX2PassStats> {
1127        self.stats.get(i)
1128    }
1129    #[allow(dead_code)]
1130    pub fn enabled_passes(&self) -> Vec<&CCX2PassConfig> {
1131        self.configs.iter().filter(|c| c.enabled).collect()
1132    }
1133    #[allow(dead_code)]
1134    pub fn passes_in_phase(&self, ph: &CCX2PassPhase) -> Vec<&CCX2PassConfig> {
1135        self.configs
1136            .iter()
1137            .filter(|c| c.enabled && &c.phase == ph)
1138            .collect()
1139    }
1140    #[allow(dead_code)]
1141    pub fn total_nodes_visited(&self) -> usize {
1142        self.stats.iter().map(|s| s.nodes_visited).sum()
1143    }
1144    #[allow(dead_code)]
1145    pub fn any_changed(&self) -> bool {
1146        self.stats.iter().any(|s| s.changed)
1147    }
1148}
1149#[allow(dead_code)]
1150#[derive(Debug, Clone, PartialEq)]
1151pub enum CCPassPhase {
1152    Analysis,
1153    Transformation,
1154    Verification,
1155    Cleanup,
1156}
1157impl CCPassPhase {
1158    #[allow(dead_code)]
1159    pub fn name(&self) -> &str {
1160        match self {
1161            CCPassPhase::Analysis => "analysis",
1162            CCPassPhase::Transformation => "transformation",
1163            CCPassPhase::Verification => "verification",
1164            CCPassPhase::Cleanup => "cleanup",
1165        }
1166    }
1167    #[allow(dead_code)]
1168    pub fn is_modifying(&self) -> bool {
1169        matches!(self, CCPassPhase::Transformation | CCPassPhase::Cleanup)
1170    }
1171}
1172#[allow(dead_code)]
1173#[derive(Debug, Clone)]
1174pub struct CCPassConfig {
1175    pub phase: CCPassPhase,
1176    pub enabled: bool,
1177    pub max_iterations: u32,
1178    pub debug_output: bool,
1179    pub pass_name: String,
1180}
1181impl CCPassConfig {
1182    #[allow(dead_code)]
1183    pub fn new(name: impl Into<String>, phase: CCPassPhase) -> Self {
1184        CCPassConfig {
1185            phase,
1186            enabled: true,
1187            max_iterations: 10,
1188            debug_output: false,
1189            pass_name: name.into(),
1190        }
1191    }
1192    #[allow(dead_code)]
1193    pub fn disabled(mut self) -> Self {
1194        self.enabled = false;
1195        self
1196    }
1197    #[allow(dead_code)]
1198    pub fn with_debug(mut self) -> Self {
1199        self.debug_output = true;
1200        self
1201    }
1202    #[allow(dead_code)]
1203    pub fn max_iter(mut self, n: u32) -> Self {
1204        self.max_iterations = n;
1205        self
1206    }
1207}
1208#[allow(dead_code)]
1209#[derive(Debug, Clone)]
1210pub struct CCDepGraph {
1211    pub(super) nodes: Vec<u32>,
1212    pub(super) edges: Vec<(u32, u32)>,
1213}
1214impl CCDepGraph {
1215    #[allow(dead_code)]
1216    pub fn new() -> Self {
1217        CCDepGraph {
1218            nodes: Vec::new(),
1219            edges: Vec::new(),
1220        }
1221    }
1222    #[allow(dead_code)]
1223    pub fn add_node(&mut self, id: u32) {
1224        if !self.nodes.contains(&id) {
1225            self.nodes.push(id);
1226        }
1227    }
1228    #[allow(dead_code)]
1229    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1230        self.add_node(dep);
1231        self.add_node(dependent);
1232        self.edges.push((dep, dependent));
1233    }
1234    #[allow(dead_code)]
1235    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1236        self.edges
1237            .iter()
1238            .filter(|(d, _)| *d == node)
1239            .map(|(_, dep)| *dep)
1240            .collect()
1241    }
1242    #[allow(dead_code)]
1243    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1244        self.edges
1245            .iter()
1246            .filter(|(_, dep)| *dep == node)
1247            .map(|(d, _)| *d)
1248            .collect()
1249    }
1250    #[allow(dead_code)]
1251    pub fn topological_sort(&self) -> Vec<u32> {
1252        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1253        for &n in &self.nodes {
1254            in_degree.insert(n, 0);
1255        }
1256        for (_, dep) in &self.edges {
1257            *in_degree.entry(*dep).or_insert(0) += 1;
1258        }
1259        let mut queue: std::collections::VecDeque<u32> = self
1260            .nodes
1261            .iter()
1262            .filter(|&&n| in_degree[&n] == 0)
1263            .copied()
1264            .collect();
1265        let mut result = Vec::new();
1266        while let Some(node) = queue.pop_front() {
1267            result.push(node);
1268            for dep in self.dependents_of(node) {
1269                let cnt = in_degree.entry(dep).or_insert(0);
1270                *cnt = cnt.saturating_sub(1);
1271                if *cnt == 0 {
1272                    queue.push_back(dep);
1273                }
1274            }
1275        }
1276        result
1277    }
1278    #[allow(dead_code)]
1279    pub fn has_cycle(&self) -> bool {
1280        self.topological_sort().len() < self.nodes.len()
1281    }
1282}
1283#[allow(dead_code)]
1284pub struct CCPassRegistry {
1285    pub(super) configs: Vec<CCPassConfig>,
1286    pub(super) stats: std::collections::HashMap<String, CCPassStats>,
1287}
1288impl CCPassRegistry {
1289    #[allow(dead_code)]
1290    pub fn new() -> Self {
1291        CCPassRegistry {
1292            configs: Vec::new(),
1293            stats: std::collections::HashMap::new(),
1294        }
1295    }
1296    #[allow(dead_code)]
1297    pub fn register(&mut self, config: CCPassConfig) {
1298        self.stats
1299            .insert(config.pass_name.clone(), CCPassStats::new());
1300        self.configs.push(config);
1301    }
1302    #[allow(dead_code)]
1303    pub fn enabled_passes(&self) -> Vec<&CCPassConfig> {
1304        self.configs.iter().filter(|c| c.enabled).collect()
1305    }
1306    #[allow(dead_code)]
1307    pub fn get_stats(&self, name: &str) -> Option<&CCPassStats> {
1308        self.stats.get(name)
1309    }
1310    #[allow(dead_code)]
1311    pub fn total_passes(&self) -> usize {
1312        self.configs.len()
1313    }
1314    #[allow(dead_code)]
1315    pub fn enabled_count(&self) -> usize {
1316        self.enabled_passes().len()
1317    }
1318    #[allow(dead_code)]
1319    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1320        if let Some(stats) = self.stats.get_mut(name) {
1321            stats.record_run(changes, time_ms, iter);
1322        }
1323    }
1324}
1325#[allow(dead_code)]
1326#[derive(Debug, Clone)]
1327pub struct CCCacheEntry {
1328    pub key: String,
1329    pub data: Vec<u8>,
1330    pub timestamp: u64,
1331    pub valid: bool,
1332}
1333/// Statistics from the closure conversion pass.
1334#[derive(Debug, Clone, Default)]
1335pub struct ClosureConvertStats {
1336    /// Number of closures converted.
1337    pub closures_converted: usize,
1338    /// Number of helper functions lifted.
1339    pub helpers_lifted: usize,
1340    /// Number of closures defunctionalized.
1341    pub defunctionalized: usize,
1342    /// Number of closures stack-allocated.
1343    pub stack_allocated: usize,
1344    /// Number of closures that had to be heap-allocated.
1345    pub heap_allocated: usize,
1346    /// Number of closures merged.
1347    pub closures_merged: usize,
1348}
1349/// The closure conversion pass.
1350///
1351/// Transforms an LCNF module by making all closures explicit:
1352/// lambda expressions become struct allocations with captured variables.
1353pub struct ClosureConverter {
1354    pub(super) config: ClosureConvertConfig,
1355    pub(super) stats: ClosureConvertStats,
1356    /// Escape analysis results.
1357    pub(super) escape_info: HashMap<LcnfVarId, EscapeInfo>,
1358    /// Collected closure infos (closure var -> info).
1359    pub(super) closure_infos: HashMap<LcnfVarId, ClosureInfo>,
1360    /// Helper functions generated during conversion.
1361    pub(super) lifted_helpers: Vec<LcnfFunDecl>,
1362    /// Fresh variable counter.
1363    pub(super) next_var: u64,
1364    /// Fresh name counter.
1365    pub(super) name_counter: usize,
1366    /// Set of known function names (for defunctionalization).
1367    pub(super) known_functions: HashSet<String>,
1368}
1369impl ClosureConverter {
1370    /// Create a new closure converter.
1371    pub fn new(config: ClosureConvertConfig) -> Self {
1372        ClosureConverter {
1373            config,
1374            stats: ClosureConvertStats::default(),
1375            escape_info: HashMap::new(),
1376            closure_infos: HashMap::new(),
1377            lifted_helpers: Vec::new(),
1378            next_var: 10000,
1379            name_counter: 0,
1380            known_functions: HashSet::new(),
1381        }
1382    }
1383    /// Create with default configuration.
1384    pub fn default_converter() -> Self {
1385        Self::new(ClosureConvertConfig::default())
1386    }
1387    /// Allocate a fresh variable ID.
1388    pub(super) fn fresh_var(&mut self) -> LcnfVarId {
1389        let id = LcnfVarId(self.next_var);
1390        self.next_var += 1;
1391        id
1392    }
1393    /// Generate a fresh helper function name.
1394    pub(super) fn fresh_name(&mut self, base: &str) -> String {
1395        let name = format!("{}_closure_{}", base, self.name_counter);
1396        self.name_counter += 1;
1397        name
1398    }
1399    /// Convert all closures in a module.
1400    pub fn convert_module(&mut self, module: &mut LcnfModule) {
1401        let mut escape = EscapeAnalysis::new();
1402        escape.analyze(module);
1403        self.escape_info = escape.var_escape.clone();
1404        for decl in &module.fun_decls {
1405            self.known_functions.insert(decl.name.clone());
1406        }
1407        let mut new_decls = Vec::new();
1408        for decl in &module.fun_decls {
1409            let (converted, helpers) = self.convert_fun_decl(decl);
1410            new_decls.push(converted);
1411            new_decls.extend(helpers);
1412        }
1413        new_decls.append(&mut self.lifted_helpers);
1414        module.fun_decls = new_decls;
1415    }
1416    /// Convert a single function declaration.
1417    ///
1418    /// Returns the converted declaration and any helper functions
1419    /// that were lifted out.
1420    pub fn convert_fun_decl(&mut self, decl: &LcnfFunDecl) -> (LcnfFunDecl, Vec<LcnfFunDecl>) {
1421        let mut helpers = Vec::new();
1422        let bound: HashSet<LcnfVarId> = decl.params.iter().map(|p| p.id).collect();
1423        let _body_free = compute_free_vars(&decl.body, &bound);
1424        let new_body = self.convert_expr(&decl.body, &decl.name, &mut helpers);
1425        let converted = LcnfFunDecl {
1426            name: decl.name.clone(),
1427            original_name: decl.original_name.clone(),
1428            params: decl.params.clone(),
1429            ret_type: decl.ret_type.clone(),
1430            body: new_body,
1431            is_recursive: decl.is_recursive,
1432            is_lifted: decl.is_lifted,
1433            inline_cost: decl.inline_cost,
1434        };
1435        self.stats.closures_converted += helpers.len();
1436        (converted, helpers)
1437    }
1438    /// Convert an expression, possibly lifting closures.
1439    pub(super) fn convert_expr(
1440        &mut self,
1441        expr: &LcnfExpr,
1442        parent_fn: &str,
1443        helpers: &mut Vec<LcnfFunDecl>,
1444    ) -> LcnfExpr {
1445        match expr {
1446            LcnfExpr::Let {
1447                id,
1448                name,
1449                ty,
1450                value,
1451                body,
1452            } => {
1453                let new_value = self.convert_let_value(value, parent_fn, helpers);
1454                let new_body = self.convert_expr(body, parent_fn, helpers);
1455                LcnfExpr::Let {
1456                    id: *id,
1457                    name: name.clone(),
1458                    ty: ty.clone(),
1459                    value: new_value,
1460                    body: Box::new(new_body),
1461                }
1462            }
1463            LcnfExpr::Case {
1464                scrutinee,
1465                scrutinee_ty,
1466                alts,
1467                default,
1468            } => {
1469                let new_alts: Vec<LcnfAlt> = alts
1470                    .iter()
1471                    .map(|alt| LcnfAlt {
1472                        ctor_name: alt.ctor_name.clone(),
1473                        ctor_tag: alt.ctor_tag,
1474                        params: alt.params.clone(),
1475                        body: self.convert_expr(&alt.body, parent_fn, helpers),
1476                    })
1477                    .collect();
1478                let new_default = default
1479                    .as_ref()
1480                    .map(|d| Box::new(self.convert_expr(d, parent_fn, helpers)));
1481                LcnfExpr::Case {
1482                    scrutinee: *scrutinee,
1483                    scrutinee_ty: scrutinee_ty.clone(),
1484                    alts: new_alts,
1485                    default: new_default,
1486                }
1487            }
1488            other => other.clone(),
1489        }
1490    }
1491    /// Convert a let-value, possibly lifting lambda bodies.
1492    pub(super) fn convert_let_value(
1493        &mut self,
1494        value: &LcnfLetValue,
1495        _parent_fn: &str,
1496        _helpers: &mut Vec<LcnfFunDecl>,
1497    ) -> LcnfLetValue {
1498        match value {
1499            LcnfLetValue::App(func, _args) => {
1500                if self.config.defunctionalize {
1501                    if let LcnfArg::Var(_fvar) = func {}
1502                }
1503                value.clone()
1504            }
1505            LcnfLetValue::Ctor(_name, _tag, _args) => value.clone(),
1506            _ => value.clone(),
1507        }
1508    }
1509    /// Attempt to defunctionalize a set of closures at a call site.
1510    ///
1511    /// When the set of possible callees is statically known, replace
1512    /// the closure call with a switch on the closure's tag.
1513    pub fn defunctionalize(
1514        &mut self,
1515        call_site_var: LcnfVarId,
1516        possible_callees: &[String],
1517    ) -> Option<LcnfExpr> {
1518        if possible_callees.is_empty() || !self.config.defunctionalize {
1519            return None;
1520        }
1521        self.stats.defunctionalized += 1;
1522        let alts: Vec<LcnfAlt> = possible_callees
1523            .iter()
1524            .enumerate()
1525            .map(|(tag, callee_name)| LcnfAlt {
1526                ctor_name: callee_name.clone(),
1527                ctor_tag: tag as u32,
1528                params: vec![],
1529                body: LcnfExpr::Return(LcnfArg::Var(call_site_var)),
1530            })
1531            .collect();
1532        Some(LcnfExpr::Case {
1533            scrutinee: call_site_var,
1534            scrutinee_ty: LcnfType::Object,
1535            alts,
1536            default: Some(Box::new(LcnfExpr::Unreachable)),
1537        })
1538    }
1539    /// Determine whether a closure should be stack-allocated.
1540    pub fn stack_allocate_closure(&self, info: &ClosureInfo) -> bool {
1541        self.config.stack_alloc_non_escaping
1542            && !info.is_escaping
1543            && info.free_vars.len() <= self.config.max_inline_captures
1544    }
1545    /// Get the conversion statistics.
1546    pub fn stats(&self) -> &ClosureConvertStats {
1547        &self.stats
1548    }
1549    /// Get closure info for a variable, if available.
1550    pub fn get_closure_info(&self, var: LcnfVarId) -> Option<&ClosureInfo> {
1551        self.closure_infos.get(&var)
1552    }
1553}
1554/// Liveness analysis for CCExt.
1555#[allow(dead_code)]
1556#[derive(Debug, Clone, Default)]
1557pub struct CCExtLiveness {
1558    pub live_in: Vec<Vec<usize>>,
1559    pub live_out: Vec<Vec<usize>>,
1560    pub defs: Vec<Vec<usize>>,
1561    pub uses: Vec<Vec<usize>>,
1562}
1563impl CCExtLiveness {
1564    #[allow(dead_code)]
1565    pub fn new(n: usize) -> Self {
1566        Self {
1567            live_in: vec![Vec::new(); n],
1568            live_out: vec![Vec::new(); n],
1569            defs: vec![Vec::new(); n],
1570            uses: vec![Vec::new(); n],
1571        }
1572    }
1573    #[allow(dead_code)]
1574    pub fn live_in(&self, b: usize, v: usize) -> bool {
1575        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1576    }
1577    #[allow(dead_code)]
1578    pub fn live_out(&self, b: usize, v: usize) -> bool {
1579        self.live_out
1580            .get(b)
1581            .map(|s| s.contains(&v))
1582            .unwrap_or(false)
1583    }
1584    #[allow(dead_code)]
1585    pub fn add_def(&mut self, b: usize, v: usize) {
1586        if let Some(s) = self.defs.get_mut(b) {
1587            if !s.contains(&v) {
1588                s.push(v);
1589            }
1590        }
1591    }
1592    #[allow(dead_code)]
1593    pub fn add_use(&mut self, b: usize, v: usize) {
1594        if let Some(s) = self.uses.get_mut(b) {
1595            if !s.contains(&v) {
1596                s.push(v);
1597            }
1598        }
1599    }
1600    #[allow(dead_code)]
1601    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1602        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1603    }
1604    #[allow(dead_code)]
1605    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1606        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1607    }
1608}
1609/// Information about a closure discovered during analysis.
1610#[derive(Debug, Clone)]
1611pub struct ClosureInfo {
1612    /// Free variables captured by the closure.
1613    pub free_vars: Vec<LcnfVarId>,
1614    /// Types of the captured variables.
1615    pub captured_types: Vec<LcnfType>,
1616    /// Arity (number of explicit parameters).
1617    pub arity: usize,
1618    /// Whether the closure escapes its defining scope.
1619    pub is_escaping: bool,
1620    /// Whether the closure body has side effects.
1621    pub has_side_effects: bool,
1622    /// The original function name (if this closure was lifted).
1623    pub original_name: Option<String>,
1624}
1625impl ClosureInfo {
1626    /// Whether this closure can be stack-allocated.
1627    pub fn can_stack_allocate(&self) -> bool {
1628        !self.is_escaping
1629    }
1630    /// Total number of fields in the closure struct (fn_ptr + captured).
1631    pub fn total_fields(&self) -> usize {
1632        1 + self.free_vars.len()
1633    }
1634}
1635/// Configuration for the closure conversion pass.
1636#[derive(Debug, Clone)]
1637pub struct ClosureConvertConfig {
1638    /// Whether to attempt defunctionalization.
1639    pub defunctionalize: bool,
1640    /// Whether to stack-allocate non-escaping closures.
1641    pub stack_alloc_non_escaping: bool,
1642    /// Maximum number of captured variables for inlining the closure.
1643    pub max_inline_captures: usize,
1644    /// Whether to merge identical closures.
1645    pub merge_closures: bool,
1646}
1647#[allow(dead_code)]
1648#[derive(Debug, Clone, Default)]
1649pub struct CCPassStats {
1650    pub total_runs: u32,
1651    pub successful_runs: u32,
1652    pub total_changes: u64,
1653    pub time_ms: u64,
1654    pub iterations_used: u32,
1655}
1656impl CCPassStats {
1657    #[allow(dead_code)]
1658    pub fn new() -> Self {
1659        Self::default()
1660    }
1661    #[allow(dead_code)]
1662    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1663        self.total_runs += 1;
1664        self.successful_runs += 1;
1665        self.total_changes += changes;
1666        self.time_ms += time_ms;
1667        self.iterations_used = iterations;
1668    }
1669    #[allow(dead_code)]
1670    pub fn average_changes_per_run(&self) -> f64 {
1671        if self.total_runs == 0 {
1672            return 0.0;
1673        }
1674        self.total_changes as f64 / self.total_runs as f64
1675    }
1676    #[allow(dead_code)]
1677    pub fn success_rate(&self) -> f64 {
1678        if self.total_runs == 0 {
1679            return 0.0;
1680        }
1681        self.successful_runs as f64 / self.total_runs as f64
1682    }
1683    #[allow(dead_code)]
1684    pub fn format_summary(&self) -> String {
1685        format!(
1686            "Runs: {}/{}, Changes: {}, Time: {}ms",
1687            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1688        )
1689    }
1690}
1691/// Configuration for CCX2 passes.
1692#[allow(dead_code)]
1693#[derive(Debug, Clone)]
1694pub struct CCX2PassConfig {
1695    pub name: String,
1696    pub phase: CCX2PassPhase,
1697    pub enabled: bool,
1698    pub max_iterations: usize,
1699    pub debug: u32,
1700    pub timeout_ms: Option<u64>,
1701}
1702impl CCX2PassConfig {
1703    #[allow(dead_code)]
1704    pub fn new(name: impl Into<String>) -> Self {
1705        Self {
1706            name: name.into(),
1707            phase: CCX2PassPhase::Middle,
1708            enabled: true,
1709            max_iterations: 100,
1710            debug: 0,
1711            timeout_ms: None,
1712        }
1713    }
1714    #[allow(dead_code)]
1715    pub fn with_phase(mut self, phase: CCX2PassPhase) -> Self {
1716        self.phase = phase;
1717        self
1718    }
1719    #[allow(dead_code)]
1720    pub fn with_max_iter(mut self, n: usize) -> Self {
1721        self.max_iterations = n;
1722        self
1723    }
1724    #[allow(dead_code)]
1725    pub fn with_debug(mut self, d: u32) -> Self {
1726        self.debug = d;
1727        self
1728    }
1729    #[allow(dead_code)]
1730    pub fn disabled(mut self) -> Self {
1731        self.enabled = false;
1732        self
1733    }
1734    #[allow(dead_code)]
1735    pub fn with_timeout(mut self, ms: u64) -> Self {
1736        self.timeout_ms = Some(ms);
1737        self
1738    }
1739    #[allow(dead_code)]
1740    pub fn is_debug_enabled(&self) -> bool {
1741        self.debug > 0
1742    }
1743}
1744#[allow(dead_code)]
1745#[derive(Debug, Clone)]
1746pub struct CCLivenessInfo {
1747    pub live_in: Vec<std::collections::HashSet<u32>>,
1748    pub live_out: Vec<std::collections::HashSet<u32>>,
1749    pub defs: Vec<std::collections::HashSet<u32>>,
1750    pub uses: Vec<std::collections::HashSet<u32>>,
1751}
1752impl CCLivenessInfo {
1753    #[allow(dead_code)]
1754    pub fn new(block_count: usize) -> Self {
1755        CCLivenessInfo {
1756            live_in: vec![std::collections::HashSet::new(); block_count],
1757            live_out: vec![std::collections::HashSet::new(); block_count],
1758            defs: vec![std::collections::HashSet::new(); block_count],
1759            uses: vec![std::collections::HashSet::new(); block_count],
1760        }
1761    }
1762    #[allow(dead_code)]
1763    pub fn add_def(&mut self, block: usize, var: u32) {
1764        if block < self.defs.len() {
1765            self.defs[block].insert(var);
1766        }
1767    }
1768    #[allow(dead_code)]
1769    pub fn add_use(&mut self, block: usize, var: u32) {
1770        if block < self.uses.len() {
1771            self.uses[block].insert(var);
1772        }
1773    }
1774    #[allow(dead_code)]
1775    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1776        self.live_in
1777            .get(block)
1778            .map(|s| s.contains(&var))
1779            .unwrap_or(false)
1780    }
1781    #[allow(dead_code)]
1782    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1783        self.live_out
1784            .get(block)
1785            .map(|s| s.contains(&var))
1786            .unwrap_or(false)
1787    }
1788}
1789/// Worklist for CCExt.
1790#[allow(dead_code)]
1791#[derive(Debug, Clone)]
1792pub struct CCExtWorklist {
1793    pub(super) items: std::collections::VecDeque<usize>,
1794    pub(super) present: Vec<bool>,
1795}
1796impl CCExtWorklist {
1797    #[allow(dead_code)]
1798    pub fn new(capacity: usize) -> Self {
1799        Self {
1800            items: std::collections::VecDeque::new(),
1801            present: vec![false; capacity],
1802        }
1803    }
1804    #[allow(dead_code)]
1805    pub fn push(&mut self, id: usize) {
1806        if id < self.present.len() && !self.present[id] {
1807            self.present[id] = true;
1808            self.items.push_back(id);
1809        }
1810    }
1811    #[allow(dead_code)]
1812    pub fn push_front(&mut self, id: usize) {
1813        if id < self.present.len() && !self.present[id] {
1814            self.present[id] = true;
1815            self.items.push_front(id);
1816        }
1817    }
1818    #[allow(dead_code)]
1819    pub fn pop(&mut self) -> Option<usize> {
1820        let id = self.items.pop_front()?;
1821        if id < self.present.len() {
1822            self.present[id] = false;
1823        }
1824        Some(id)
1825    }
1826    #[allow(dead_code)]
1827    pub fn is_empty(&self) -> bool {
1828        self.items.is_empty()
1829    }
1830    #[allow(dead_code)]
1831    pub fn len(&self) -> usize {
1832        self.items.len()
1833    }
1834    #[allow(dead_code)]
1835    pub fn contains(&self, id: usize) -> bool {
1836        id < self.present.len() && self.present[id]
1837    }
1838    #[allow(dead_code)]
1839    pub fn drain_all(&mut self) -> Vec<usize> {
1840        let v: Vec<usize> = self.items.drain(..).collect();
1841        for &id in &v {
1842            if id < self.present.len() {
1843                self.present[id] = false;
1844            }
1845        }
1846        v
1847    }
1848}
1849/// Worklist for CCX2.
1850#[allow(dead_code)]
1851#[derive(Debug, Clone)]
1852pub struct CCX2Worklist {
1853    pub(super) items: std::collections::VecDeque<usize>,
1854    pub(super) present: Vec<bool>,
1855}
1856impl CCX2Worklist {
1857    #[allow(dead_code)]
1858    pub fn new(capacity: usize) -> Self {
1859        Self {
1860            items: std::collections::VecDeque::new(),
1861            present: vec![false; capacity],
1862        }
1863    }
1864    #[allow(dead_code)]
1865    pub fn push(&mut self, id: usize) {
1866        if id < self.present.len() && !self.present[id] {
1867            self.present[id] = true;
1868            self.items.push_back(id);
1869        }
1870    }
1871    #[allow(dead_code)]
1872    pub fn push_front(&mut self, id: usize) {
1873        if id < self.present.len() && !self.present[id] {
1874            self.present[id] = true;
1875            self.items.push_front(id);
1876        }
1877    }
1878    #[allow(dead_code)]
1879    pub fn pop(&mut self) -> Option<usize> {
1880        let id = self.items.pop_front()?;
1881        if id < self.present.len() {
1882            self.present[id] = false;
1883        }
1884        Some(id)
1885    }
1886    #[allow(dead_code)]
1887    pub fn is_empty(&self) -> bool {
1888        self.items.is_empty()
1889    }
1890    #[allow(dead_code)]
1891    pub fn len(&self) -> usize {
1892        self.items.len()
1893    }
1894    #[allow(dead_code)]
1895    pub fn contains(&self, id: usize) -> bool {
1896        id < self.present.len() && self.present[id]
1897    }
1898    #[allow(dead_code)]
1899    pub fn drain_all(&mut self) -> Vec<usize> {
1900        let v: Vec<usize> = self.items.drain(..).collect();
1901        for &id in &v {
1902            if id < self.present.len() {
1903                self.present[id] = false;
1904            }
1905        }
1906        v
1907    }
1908}
1909/// Dominator tree for CCX2.
1910#[allow(dead_code)]
1911#[derive(Debug, Clone)]
1912pub struct CCX2DomTree {
1913    pub(super) idom: Vec<Option<usize>>,
1914    pub(super) children: Vec<Vec<usize>>,
1915    pub(super) depth: Vec<usize>,
1916}
1917impl CCX2DomTree {
1918    #[allow(dead_code)]
1919    pub fn new(n: usize) -> Self {
1920        Self {
1921            idom: vec![None; n],
1922            children: vec![Vec::new(); n],
1923            depth: vec![0; n],
1924        }
1925    }
1926    #[allow(dead_code)]
1927    pub fn set_idom(&mut self, node: usize, dom: usize) {
1928        if node < self.idom.len() {
1929            self.idom[node] = Some(dom);
1930            if dom < self.children.len() {
1931                self.children[dom].push(node);
1932            }
1933            self.depth[node] = if dom < self.depth.len() {
1934                self.depth[dom] + 1
1935            } else {
1936                1
1937            };
1938        }
1939    }
1940    #[allow(dead_code)]
1941    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1942        if a == b {
1943            return true;
1944        }
1945        let n = self.idom.len();
1946        for _ in 0..n {
1947            match self.idom.get(b).copied().flatten() {
1948                None => return false,
1949                Some(p) if p == a => return true,
1950                Some(p) if p == b => return false,
1951                Some(p) => b = p,
1952            }
1953        }
1954        false
1955    }
1956    #[allow(dead_code)]
1957    pub fn children_of(&self, n: usize) -> &[usize] {
1958        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1959    }
1960    #[allow(dead_code)]
1961    pub fn depth_of(&self, n: usize) -> usize {
1962        self.depth.get(n).copied().unwrap_or(0)
1963    }
1964    #[allow(dead_code)]
1965    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1966        let n = self.idom.len();
1967        for _ in 0..(2 * n) {
1968            if a == b {
1969                return a;
1970            }
1971            if self.depth_of(a) > self.depth_of(b) {
1972                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1973            } else {
1974                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1975            }
1976        }
1977        0
1978    }
1979}
1980/// Analysis cache for CCX2.
1981#[allow(dead_code)]
1982#[derive(Debug)]
1983pub struct CCX2Cache {
1984    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
1985    pub(super) cap: usize,
1986    pub(super) total_hits: u64,
1987    pub(super) total_misses: u64,
1988}
1989impl CCX2Cache {
1990    #[allow(dead_code)]
1991    pub fn new(cap: usize) -> Self {
1992        Self {
1993            entries: Vec::new(),
1994            cap,
1995            total_hits: 0,
1996            total_misses: 0,
1997        }
1998    }
1999    #[allow(dead_code)]
2000    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
2001        for e in self.entries.iter_mut() {
2002            if e.0 == key && e.2 {
2003                e.3 += 1;
2004                self.total_hits += 1;
2005                return Some(&e.1);
2006            }
2007        }
2008        self.total_misses += 1;
2009        None
2010    }
2011    #[allow(dead_code)]
2012    pub fn put(&mut self, key: u64, data: Vec<u8>) {
2013        if self.entries.len() >= self.cap {
2014            self.entries.retain(|e| e.2);
2015            if self.entries.len() >= self.cap {
2016                self.entries.remove(0);
2017            }
2018        }
2019        self.entries.push((key, data, true, 0));
2020    }
2021    #[allow(dead_code)]
2022    pub fn invalidate(&mut self) {
2023        for e in self.entries.iter_mut() {
2024            e.2 = false;
2025        }
2026    }
2027    #[allow(dead_code)]
2028    pub fn hit_rate(&self) -> f64 {
2029        let t = self.total_hits + self.total_misses;
2030        if t == 0 {
2031            0.0
2032        } else {
2033            self.total_hits as f64 / t as f64
2034        }
2035    }
2036    #[allow(dead_code)]
2037    pub fn live_count(&self) -> usize {
2038        self.entries.iter().filter(|e| e.2).count()
2039    }
2040}
2041#[allow(dead_code)]
2042#[derive(Debug, Clone)]
2043pub struct CCWorklist {
2044    pub(super) items: std::collections::VecDeque<u32>,
2045    pub(super) in_worklist: std::collections::HashSet<u32>,
2046}
2047impl CCWorklist {
2048    #[allow(dead_code)]
2049    pub fn new() -> Self {
2050        CCWorklist {
2051            items: std::collections::VecDeque::new(),
2052            in_worklist: std::collections::HashSet::new(),
2053        }
2054    }
2055    #[allow(dead_code)]
2056    pub fn push(&mut self, item: u32) -> bool {
2057        if self.in_worklist.insert(item) {
2058            self.items.push_back(item);
2059            true
2060        } else {
2061            false
2062        }
2063    }
2064    #[allow(dead_code)]
2065    pub fn pop(&mut self) -> Option<u32> {
2066        let item = self.items.pop_front()?;
2067        self.in_worklist.remove(&item);
2068        Some(item)
2069    }
2070    #[allow(dead_code)]
2071    pub fn is_empty(&self) -> bool {
2072        self.items.is_empty()
2073    }
2074    #[allow(dead_code)]
2075    pub fn len(&self) -> usize {
2076        self.items.len()
2077    }
2078    #[allow(dead_code)]
2079    pub fn contains(&self, item: u32) -> bool {
2080        self.in_worklist.contains(&item)
2081    }
2082}
2083/// Liveness analysis for CCX2.
2084#[allow(dead_code)]
2085#[derive(Debug, Clone, Default)]
2086pub struct CCX2Liveness {
2087    pub live_in: Vec<Vec<usize>>,
2088    pub live_out: Vec<Vec<usize>>,
2089    pub defs: Vec<Vec<usize>>,
2090    pub uses: Vec<Vec<usize>>,
2091}
2092impl CCX2Liveness {
2093    #[allow(dead_code)]
2094    pub fn new(n: usize) -> Self {
2095        Self {
2096            live_in: vec![Vec::new(); n],
2097            live_out: vec![Vec::new(); n],
2098            defs: vec![Vec::new(); n],
2099            uses: vec![Vec::new(); n],
2100        }
2101    }
2102    #[allow(dead_code)]
2103    pub fn live_in(&self, b: usize, v: usize) -> bool {
2104        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2105    }
2106    #[allow(dead_code)]
2107    pub fn live_out(&self, b: usize, v: usize) -> bool {
2108        self.live_out
2109            .get(b)
2110            .map(|s| s.contains(&v))
2111            .unwrap_or(false)
2112    }
2113    #[allow(dead_code)]
2114    pub fn add_def(&mut self, b: usize, v: usize) {
2115        if let Some(s) = self.defs.get_mut(b) {
2116            if !s.contains(&v) {
2117                s.push(v);
2118            }
2119        }
2120    }
2121    #[allow(dead_code)]
2122    pub fn add_use(&mut self, b: usize, v: usize) {
2123        if let Some(s) = self.uses.get_mut(b) {
2124            if !s.contains(&v) {
2125                s.push(v);
2126            }
2127        }
2128    }
2129    #[allow(dead_code)]
2130    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
2131        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2132    }
2133    #[allow(dead_code)]
2134    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
2135        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2136    }
2137}
2138/// Pass registry for CCExt.
2139#[allow(dead_code)]
2140#[derive(Debug, Default)]
2141pub struct CCExtPassRegistry {
2142    pub(super) configs: Vec<CCExtPassConfig>,
2143    pub(super) stats: Vec<CCExtPassStats>,
2144}
2145impl CCExtPassRegistry {
2146    #[allow(dead_code)]
2147    pub fn new() -> Self {
2148        Self::default()
2149    }
2150    #[allow(dead_code)]
2151    pub fn register(&mut self, c: CCExtPassConfig) {
2152        self.stats.push(CCExtPassStats::new());
2153        self.configs.push(c);
2154    }
2155    #[allow(dead_code)]
2156    pub fn len(&self) -> usize {
2157        self.configs.len()
2158    }
2159    #[allow(dead_code)]
2160    pub fn is_empty(&self) -> bool {
2161        self.configs.is_empty()
2162    }
2163    #[allow(dead_code)]
2164    pub fn get(&self, i: usize) -> Option<&CCExtPassConfig> {
2165        self.configs.get(i)
2166    }
2167    #[allow(dead_code)]
2168    pub fn get_stats(&self, i: usize) -> Option<&CCExtPassStats> {
2169        self.stats.get(i)
2170    }
2171    #[allow(dead_code)]
2172    pub fn enabled_passes(&self) -> Vec<&CCExtPassConfig> {
2173        self.configs.iter().filter(|c| c.enabled).collect()
2174    }
2175    #[allow(dead_code)]
2176    pub fn passes_in_phase(&self, ph: &CCExtPassPhase) -> Vec<&CCExtPassConfig> {
2177        self.configs
2178            .iter()
2179            .filter(|c| c.enabled && &c.phase == ph)
2180            .collect()
2181    }
2182    #[allow(dead_code)]
2183    pub fn total_nodes_visited(&self) -> usize {
2184        self.stats.iter().map(|s| s.nodes_visited).sum()
2185    }
2186    #[allow(dead_code)]
2187    pub fn any_changed(&self) -> bool {
2188        self.stats.iter().any(|s| s.changed)
2189    }
2190}
2191/// Statistics for CCX2 passes.
2192#[allow(dead_code)]
2193#[derive(Debug, Clone, Default)]
2194pub struct CCX2PassStats {
2195    pub iterations: usize,
2196    pub changed: bool,
2197    pub nodes_visited: usize,
2198    pub nodes_modified: usize,
2199    pub time_ms: u64,
2200    pub memory_bytes: usize,
2201    pub errors: usize,
2202}
2203impl CCX2PassStats {
2204    #[allow(dead_code)]
2205    pub fn new() -> Self {
2206        Self::default()
2207    }
2208    #[allow(dead_code)]
2209    pub fn visit(&mut self) {
2210        self.nodes_visited += 1;
2211    }
2212    #[allow(dead_code)]
2213    pub fn modify(&mut self) {
2214        self.nodes_modified += 1;
2215        self.changed = true;
2216    }
2217    #[allow(dead_code)]
2218    pub fn iterate(&mut self) {
2219        self.iterations += 1;
2220    }
2221    #[allow(dead_code)]
2222    pub fn error(&mut self) {
2223        self.errors += 1;
2224    }
2225    #[allow(dead_code)]
2226    pub fn efficiency(&self) -> f64 {
2227        if self.nodes_visited == 0 {
2228            0.0
2229        } else {
2230            self.nodes_modified as f64 / self.nodes_visited as f64
2231        }
2232    }
2233    #[allow(dead_code)]
2234    pub fn merge(&mut self, o: &CCX2PassStats) {
2235        self.iterations += o.iterations;
2236        self.changed |= o.changed;
2237        self.nodes_visited += o.nodes_visited;
2238        self.nodes_modified += o.nodes_modified;
2239        self.time_ms += o.time_ms;
2240        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
2241        self.errors += o.errors;
2242    }
2243}
2244/// Dependency graph for CCX2.
2245#[allow(dead_code)]
2246#[derive(Debug, Clone)]
2247pub struct CCX2DepGraph {
2248    pub(super) n: usize,
2249    pub(super) adj: Vec<Vec<usize>>,
2250    pub(super) rev: Vec<Vec<usize>>,
2251    pub(super) edge_count: usize,
2252}
2253impl CCX2DepGraph {
2254    #[allow(dead_code)]
2255    pub fn new(n: usize) -> Self {
2256        Self {
2257            n,
2258            adj: vec![Vec::new(); n],
2259            rev: vec![Vec::new(); n],
2260            edge_count: 0,
2261        }
2262    }
2263    #[allow(dead_code)]
2264    pub fn add_edge(&mut self, from: usize, to: usize) {
2265        if from < self.n && to < self.n {
2266            if !self.adj[from].contains(&to) {
2267                self.adj[from].push(to);
2268                self.rev[to].push(from);
2269                self.edge_count += 1;
2270            }
2271        }
2272    }
2273    #[allow(dead_code)]
2274    pub fn succs(&self, n: usize) -> &[usize] {
2275        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2276    }
2277    #[allow(dead_code)]
2278    pub fn preds(&self, n: usize) -> &[usize] {
2279        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2280    }
2281    #[allow(dead_code)]
2282    pub fn topo_sort(&self) -> Option<Vec<usize>> {
2283        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
2284        let mut q: std::collections::VecDeque<usize> =
2285            (0..self.n).filter(|&i| deg[i] == 0).collect();
2286        let mut out = Vec::with_capacity(self.n);
2287        while let Some(u) = q.pop_front() {
2288            out.push(u);
2289            for &v in &self.adj[u] {
2290                deg[v] -= 1;
2291                if deg[v] == 0 {
2292                    q.push_back(v);
2293                }
2294            }
2295        }
2296        if out.len() == self.n {
2297            Some(out)
2298        } else {
2299            None
2300        }
2301    }
2302    #[allow(dead_code)]
2303    pub fn has_cycle(&self) -> bool {
2304        self.topo_sort().is_none()
2305    }
2306    #[allow(dead_code)]
2307    pub fn reachable(&self, start: usize) -> Vec<usize> {
2308        let mut vis = vec![false; self.n];
2309        let mut stk = vec![start];
2310        let mut out = Vec::new();
2311        while let Some(u) = stk.pop() {
2312            if u < self.n && !vis[u] {
2313                vis[u] = true;
2314                out.push(u);
2315                for &v in &self.adj[u] {
2316                    if !vis[v] {
2317                        stk.push(v);
2318                    }
2319                }
2320            }
2321        }
2322        out
2323    }
2324    #[allow(dead_code)]
2325    pub fn scc(&self) -> Vec<Vec<usize>> {
2326        let mut visited = vec![false; self.n];
2327        let mut order = Vec::new();
2328        for i in 0..self.n {
2329            if !visited[i] {
2330                let mut stk = vec![(i, 0usize)];
2331                while let Some((u, idx)) = stk.last_mut() {
2332                    if !visited[*u] {
2333                        visited[*u] = true;
2334                    }
2335                    if *idx < self.adj[*u].len() {
2336                        let v = self.adj[*u][*idx];
2337                        *idx += 1;
2338                        if !visited[v] {
2339                            stk.push((v, 0));
2340                        }
2341                    } else {
2342                        order.push(*u);
2343                        stk.pop();
2344                    }
2345                }
2346            }
2347        }
2348        let mut comp = vec![usize::MAX; self.n];
2349        let mut components: Vec<Vec<usize>> = Vec::new();
2350        for &start in order.iter().rev() {
2351            if comp[start] == usize::MAX {
2352                let cid = components.len();
2353                let mut component = Vec::new();
2354                let mut stk = vec![start];
2355                while let Some(u) = stk.pop() {
2356                    if comp[u] == usize::MAX {
2357                        comp[u] = cid;
2358                        component.push(u);
2359                        for &v in &self.rev[u] {
2360                            if comp[v] == usize::MAX {
2361                                stk.push(v);
2362                            }
2363                        }
2364                    }
2365                }
2366                components.push(component);
2367            }
2368        }
2369        components
2370    }
2371    #[allow(dead_code)]
2372    pub fn node_count(&self) -> usize {
2373        self.n
2374    }
2375    #[allow(dead_code)]
2376    pub fn edge_count(&self) -> usize {
2377        self.edge_count
2378    }
2379}