Skip to main content

oxilean_codegen/elixir_backend/
types.rs

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