Skip to main content

oxilean_codegen/pipeline/
types.rs

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