Skip to main content

oxilean_codegen/opt_mem2reg/
types.rs

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