Skip to main content

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