Skip to main content

oxilean_codegen/opt_gvn/
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;
7
8use super::functions::ValueNumber;
9
10use super::functions::*;
11use std::collections::{HashSet, VecDeque};
12
13#[allow(dead_code)]
14#[derive(Debug, Clone)]
15pub struct GVNAnalysisCache {
16    pub(super) entries: std::collections::HashMap<String, GVNCacheEntry>,
17    pub(super) max_size: usize,
18    pub(super) hits: u64,
19    pub(super) misses: u64,
20}
21impl GVNAnalysisCache {
22    #[allow(dead_code)]
23    pub fn new(max_size: usize) -> Self {
24        GVNAnalysisCache {
25            entries: std::collections::HashMap::new(),
26            max_size,
27            hits: 0,
28            misses: 0,
29        }
30    }
31    #[allow(dead_code)]
32    pub fn get(&mut self, key: &str) -> Option<&GVNCacheEntry> {
33        if self.entries.contains_key(key) {
34            self.hits += 1;
35            self.entries.get(key)
36        } else {
37            self.misses += 1;
38            None
39        }
40    }
41    #[allow(dead_code)]
42    pub fn insert(&mut self, key: String, data: Vec<u8>) {
43        if self.entries.len() >= self.max_size {
44            if let Some(oldest) = self.entries.keys().next().cloned() {
45                self.entries.remove(&oldest);
46            }
47        }
48        self.entries.insert(
49            key.clone(),
50            GVNCacheEntry {
51                key,
52                data,
53                timestamp: 0,
54                valid: true,
55            },
56        );
57    }
58    #[allow(dead_code)]
59    pub fn invalidate(&mut self, key: &str) {
60        if let Some(entry) = self.entries.get_mut(key) {
61            entry.valid = false;
62        }
63    }
64    #[allow(dead_code)]
65    pub fn clear(&mut self) {
66        self.entries.clear();
67    }
68    #[allow(dead_code)]
69    pub fn hit_rate(&self) -> f64 {
70        let total = self.hits + self.misses;
71        if total == 0 {
72            return 0.0;
73        }
74        self.hits as f64 / total as f64
75    }
76    #[allow(dead_code)]
77    pub fn size(&self) -> usize {
78        self.entries.len()
79    }
80}
81/// Canonicalises expressions for GVN by applying commutativity and
82/// associativity to produce a normal form.
83///
84/// For example:
85///   App(add, [y, x])  →  App(add, [x, y])  (sort by VN if commutative)
86#[derive(Debug, Default)]
87pub struct ExprCanonicaliser {
88    /// Set of function names known to be commutative.
89    pub commutative_fns: std::collections::HashSet<String>,
90    /// Number of canonicalisations performed.
91    pub canonicalisations: usize,
92}
93impl ExprCanonicaliser {
94    pub fn new() -> Self {
95        let mut c = ExprCanonicaliser::default();
96        c.commutative_fns.insert("add".to_string());
97        c.commutative_fns.insert("mul".to_string());
98        c.commutative_fns.insert("and".to_string());
99        c.commutative_fns.insert("or".to_string());
100        c
101    }
102    /// Canonicalise a `NormExpr` key.
103    pub fn canonicalise(&mut self, expr: NormExpr) -> NormExpr {
104        match &expr {
105            NormExpr::App(NormArg::Vn(_), args) if args.len() == 2 => {
106                let mut sorted_args = args.clone();
107                sorted_args.sort_by_key(|a| match a {
108                    NormArg::Vn(vn) => *vn,
109                    NormArg::LitNat(n) => *n as ValueNumber,
110                    _ => u32::MAX,
111                });
112                if sorted_args != *args {
113                    self.canonicalisations += 1;
114                    NormExpr::App(
115                        match &expr {
116                            NormExpr::App(f, _) => f.clone(),
117                            _ => unreachable!(),
118                        },
119                        sorted_args,
120                    )
121                } else {
122                    expr
123                }
124            }
125            _ => expr,
126        }
127    }
128}
129/// Detailed statistics from a GVN run, including per-category breakdowns.
130#[derive(Debug, Clone, Default)]
131pub struct GVNStatistics {
132    /// Total value numbers assigned.
133    pub total_vns: usize,
134    /// Redundant literal bindings eliminated.
135    pub lit_redundancies: usize,
136    /// Redundant projection bindings eliminated.
137    pub proj_redundancies: usize,
138    /// Redundant constructor bindings eliminated.
139    pub ctor_redundancies: usize,
140    /// Redundant application bindings eliminated.
141    pub app_redundancies: usize,
142    /// Redundant FVar (copy) bindings eliminated.
143    pub fvar_redundancies: usize,
144    /// Number of phi-translations performed.
145    pub phi_translations: usize,
146    /// Number of algebraic simplifications.
147    pub alg_simplifications: usize,
148    /// Wall-clock time (nanoseconds) for the GVN pass (0 if not measured).
149    pub time_ns: u64,
150}
151impl GVNStatistics {
152    pub fn new() -> Self {
153        GVNStatistics::default()
154    }
155    pub fn total_redundancies(&self) -> usize {
156        self.lit_redundancies
157            + self.proj_redundancies
158            + self.ctor_redundancies
159            + self.app_redundancies
160            + self.fvar_redundancies
161    }
162    pub fn print_summary(&self) {
163        let _ = format!(
164            "GVN: {} VNs, {} redundancies ({} lit, {} proj, {} ctor, {} app, {} fvar)",
165            self.total_vns,
166            self.total_redundancies(),
167            self.lit_redundancies,
168            self.proj_redundancies,
169            self.ctor_redundancies,
170            self.app_redundancies,
171            self.fvar_redundancies,
172        );
173    }
174}
175/// Statistics produced by a single GVN run.
176#[derive(Debug, Clone, Default)]
177pub struct GVNReport {
178    /// Total number of expressions assigned a value number.
179    pub expressions_numbered: usize,
180    /// Total number of redundant computations eliminated.
181    pub redundancies_eliminated: usize,
182    /// Number of phi-translations performed (across join-point edges).
183    pub phi_translations: usize,
184}
185impl GVNReport {
186    pub fn merge(&mut self, other: &GVNReport) {
187        self.expressions_numbered += other.expressions_numbered;
188        self.redundancies_eliminated += other.redundancies_eliminated;
189        self.phi_translations += other.phi_translations;
190    }
191}
192/// State for Conditional Constant Propagation integrated with GVN.
193///
194/// CCP tracks which variables have statically-known values and uses
195/// this information to fold conditions and eliminate dead branches.
196#[derive(Debug, Default)]
197pub struct CCPState {
198    /// Map from variable to its known constant value.
199    pub known: HashMap<LcnfVarId, KnownConstant>,
200    /// Number of constants folded.
201    pub folded: usize,
202    /// Number of dead branches eliminated.
203    pub dead_branches: usize,
204}
205impl CCPState {
206    pub fn new() -> Self {
207        CCPState::default()
208    }
209    pub fn set_known(&mut self, var: LcnfVarId, val: KnownConstant) {
210        self.known.insert(var, val);
211    }
212    pub fn get_known(&self, var: &LcnfVarId) -> &KnownConstant {
213        self.known.get(var).unwrap_or(&KnownConstant::Top)
214    }
215    /// Run CCP on a function declaration, folding constants and removing
216    /// dead branches.
217    pub fn run(&mut self, decl: &mut LcnfFunDecl) {
218        self.propagate_in_expr(&mut decl.body);
219    }
220    pub(super) fn propagate_in_expr(&mut self, expr: &mut LcnfExpr) {
221        match expr {
222            LcnfExpr::Let {
223                id, value, body, ..
224            } => {
225                if let LcnfLetValue::Lit(lit) = value {
226                    self.set_known(*id, KnownConstant::Lit(lit.clone()));
227                }
228                self.propagate_in_expr(body);
229            }
230            LcnfExpr::Case {
231                scrutinee,
232                alts,
233                default,
234                ..
235            } => {
236                match self.get_known(scrutinee).clone() {
237                    KnownConstant::Lit(LcnfLit::Nat(n)) => {
238                        if let Some(alt) = alts.iter().find(|a| a.ctor_tag == n as u32) {
239                            self.dead_branches += alts.len() - 1;
240                            let matching_body = alt.body.clone();
241                            *expr = matching_body;
242                            self.folded += 1;
243                            return;
244                        }
245                    }
246                    _ => {}
247                }
248                for alt in alts.iter_mut() {
249                    self.propagate_in_expr(&mut alt.body);
250                }
251                if let Some(d) = default {
252                    self.propagate_in_expr(d);
253                }
254            }
255            _ => {}
256        }
257    }
258}
259/// Summary of a function's value-numbering effects for interprocedural GVN.
260#[derive(Debug, Clone, Default)]
261pub struct GVNFunctionSummary {
262    /// Value numbers of the return expression(s).
263    pub return_vns: Vec<ValueNumber>,
264    /// Whether the function always returns the same value (pure).
265    pub is_pure_fn: bool,
266    /// Known equalities between parameters (param_idx, param_idx).
267    pub param_equalities: Vec<(usize, usize)>,
268}
269impl GVNFunctionSummary {
270    pub fn new() -> Self {
271        GVNFunctionSummary::default()
272    }
273    pub fn mark_pure(&mut self) {
274        self.is_pure_fn = true;
275    }
276}
277/// A scoped context for GVN that supports push/pop for entering and
278/// leaving lexical scopes (case branches, let nesting).
279#[derive(Debug, Default)]
280pub struct ScopedValueContext {
281    /// Stack of (variable, value_number) pairs, one per scope level.
282    pub stack: Vec<Vec<(LcnfVarId, ValueNumber)>>,
283    /// Flat lookup: variable → current value number.
284    pub current: HashMap<LcnfVarId, ValueNumber>,
285}
286impl ScopedValueContext {
287    pub fn new() -> Self {
288        ScopedValueContext {
289            stack: vec![Vec::new()],
290            current: HashMap::new(),
291        }
292    }
293    /// Enter a new scope (e.g., a case branch).
294    pub fn push_scope(&mut self) {
295        self.stack.push(Vec::new());
296    }
297    /// Exit the current scope, reverting all bindings added in it.
298    pub fn pop_scope(&mut self) {
299        if let Some(scope) = self.stack.pop() {
300            for (var, _) in scope {
301                self.current.remove(&var);
302            }
303        }
304    }
305    /// Bind `var` to `vn` in the current scope.
306    pub fn bind(&mut self, var: LcnfVarId, vn: ValueNumber) {
307        self.current.insert(var, vn);
308        if let Some(scope) = self.stack.last_mut() {
309            scope.push((var, vn));
310        }
311    }
312    /// Look up the VN for `var`, returning `None` if not in scope.
313    pub fn lookup(&self, var: &LcnfVarId) -> Option<ValueNumber> {
314        self.current.get(var).copied()
315    }
316    pub fn scope_depth(&self) -> usize {
317        self.stack.len()
318    }
319}
320/// Canonical expression sharing: ensures that structurally identical
321/// `LcnfLetValue`s are represented by a single heap allocation.
322#[derive(Debug, Default)]
323pub struct HashConsingTable {
324    pub(super) table: HashMap<NormExpr, LcnfLetValue>,
325}
326impl HashConsingTable {
327    pub fn new() -> Self {
328        HashConsingTable::default()
329    }
330    /// Intern `value` under the given key.  Returns a reference to the
331    /// canonical copy.
332    pub fn intern(&mut self, key: NormExpr, value: LcnfLetValue) -> &LcnfLetValue {
333        self.table.entry(key).or_insert(value)
334    }
335    pub fn len(&self) -> usize {
336        self.table.len()
337    }
338    pub fn is_empty(&self) -> bool {
339        self.table.is_empty()
340    }
341}
342/// Collects all redundancies in a function for batch reporting and elimination.
343#[derive(Debug, Default)]
344pub struct RedundancyCollector {
345    pub redundancies: Vec<Redundancy>,
346}
347impl RedundancyCollector {
348    pub fn new() -> Self {
349        RedundancyCollector::default()
350    }
351    /// Run GVN and collect all detected redundancies.
352    pub fn collect(&mut self, decl: &LcnfFunDecl) {
353        let mut pass = GVNPass::default();
354        let mut table = ValueTable::new();
355        let mut fact = GVNFact::new();
356        pass.assign_value_numbers(decl, &mut table, &mut fact);
357        self.find_redundant(&decl.body, &table, &GVNFact::new());
358    }
359    pub(super) fn find_redundant(&mut self, expr: &LcnfExpr, table: &ValueTable, fact: &GVNFact) {
360        let mut fact = fact.clone();
361        match expr {
362            LcnfExpr::Let {
363                id, value, body, ..
364            } => {
365                let key = gvn_norm_value(value, &fact);
366                if let Some(vn) = table.lookup(&key) {
367                    if let Some(canon) = table.canonical_var(vn) {
368                        if canon != *id {
369                            self.redundancies.push(Redundancy {
370                                redundant_var: *id,
371                                canonical_var: canon,
372                                vn,
373                            });
374                        }
375                    }
376                    fact.insert(*id, vn);
377                }
378                self.find_redundant(body, table, &fact);
379            }
380            LcnfExpr::Case { alts, default, .. } => {
381                for alt in alts {
382                    self.find_redundant(&alt.body, table, &fact);
383                }
384                if let Some(d) = default {
385                    self.find_redundant(d, table, &fact);
386                }
387            }
388            _ => {}
389        }
390    }
391    pub fn num_redundancies(&self) -> usize {
392        self.redundancies.len()
393    }
394}
395#[allow(dead_code)]
396#[derive(Debug, Clone)]
397pub struct GVNDominatorTree {
398    pub idom: Vec<Option<u32>>,
399    pub dom_children: Vec<Vec<u32>>,
400    pub dom_depth: Vec<u32>,
401}
402impl GVNDominatorTree {
403    #[allow(dead_code)]
404    pub fn new(size: usize) -> Self {
405        GVNDominatorTree {
406            idom: vec![None; size],
407            dom_children: vec![Vec::new(); size],
408            dom_depth: vec![0; size],
409        }
410    }
411    #[allow(dead_code)]
412    pub fn set_idom(&mut self, node: usize, idom: u32) {
413        self.idom[node] = Some(idom);
414    }
415    #[allow(dead_code)]
416    pub fn dominates(&self, a: usize, b: usize) -> bool {
417        if a == b {
418            return true;
419        }
420        let mut cur = b;
421        loop {
422            match self.idom[cur] {
423                Some(parent) if parent as usize == a => return true,
424                Some(parent) if parent as usize == cur => return false,
425                Some(parent) => cur = parent as usize,
426                None => return false,
427            }
428        }
429    }
430    #[allow(dead_code)]
431    pub fn depth(&self, node: usize) -> u32 {
432        self.dom_depth.get(node).copied().unwrap_or(0)
433    }
434}
435/// Configuration for the GVN pass.
436#[derive(Debug, Clone)]
437pub struct GVNConfig {
438    /// Enable phi-translation (propagate value numbers across join points).
439    /// When `false`, GVN is restricted to a single basic block.
440    pub do_phi_translation: bool,
441    /// Maximum expression depth to consider for value numbering.
442    pub max_depth: usize,
443}
444/// A predicate known to hold at a program point, derived from branch conditions.
445/// Used to refine value numbering inside conditional branches.
446#[derive(Debug, Clone, PartialEq, Eq, Hash)]
447pub enum Predicate {
448    /// Variable equals a literal value.
449    EqLit(LcnfVarId, LcnfLit),
450    /// Variable is not equal to a literal value.
451    NeLit(LcnfVarId, LcnfLit),
452    /// Two variables are equal.
453    VarEq(LcnfVarId, LcnfVarId),
454}
455/// Anticipation set: the set of expressions that are *anticipated* (will
456/// definitely be computed in the future) at a given program point.
457/// Used by GVN-PRE to identify expressions that can be speculatively
458/// hoisted or inserted at join points.
459#[derive(Debug, Clone, Default)]
460pub struct AnticipationSet {
461    /// Expressions anticipated at this program point.
462    pub anticipated: std::collections::HashSet<NormExpr>,
463}
464impl AnticipationSet {
465    pub fn new() -> Self {
466        AnticipationSet::default()
467    }
468    pub fn add(&mut self, expr: NormExpr) {
469        self.anticipated.insert(expr);
470    }
471    pub fn contains(&self, expr: &NormExpr) -> bool {
472        self.anticipated.contains(expr)
473    }
474    /// Compute the meet (intersection) of two anticipation sets.
475    pub fn meet(&self, other: &AnticipationSet) -> AnticipationSet {
476        AnticipationSet {
477            anticipated: self
478                .anticipated
479                .intersection(&other.anticipated)
480                .cloned()
481                .collect(),
482        }
483    }
484    pub fn is_empty(&self) -> bool {
485        self.anticipated.is_empty()
486    }
487}
488#[allow(dead_code)]
489#[derive(Debug, Clone)]
490pub struct GVNLivenessInfo {
491    pub live_in: Vec<std::collections::HashSet<u32>>,
492    pub live_out: Vec<std::collections::HashSet<u32>>,
493    pub defs: Vec<std::collections::HashSet<u32>>,
494    pub uses: Vec<std::collections::HashSet<u32>>,
495}
496impl GVNLivenessInfo {
497    #[allow(dead_code)]
498    pub fn new(block_count: usize) -> Self {
499        GVNLivenessInfo {
500            live_in: vec![std::collections::HashSet::new(); block_count],
501            live_out: vec![std::collections::HashSet::new(); block_count],
502            defs: vec![std::collections::HashSet::new(); block_count],
503            uses: vec![std::collections::HashSet::new(); block_count],
504        }
505    }
506    #[allow(dead_code)]
507    pub fn add_def(&mut self, block: usize, var: u32) {
508        if block < self.defs.len() {
509            self.defs[block].insert(var);
510        }
511    }
512    #[allow(dead_code)]
513    pub fn add_use(&mut self, block: usize, var: u32) {
514        if block < self.uses.len() {
515            self.uses[block].insert(var);
516        }
517    }
518    #[allow(dead_code)]
519    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
520        self.live_in
521            .get(block)
522            .map(|s| s.contains(&var))
523            .unwrap_or(false)
524    }
525    #[allow(dead_code)]
526    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
527        self.live_out
528            .get(block)
529            .map(|s| s.contains(&var))
530            .unwrap_or(false)
531    }
532}
533#[allow(dead_code)]
534#[derive(Debug, Clone, Default)]
535pub struct GVNPassStats {
536    pub total_runs: u32,
537    pub successful_runs: u32,
538    pub total_changes: u64,
539    pub time_ms: u64,
540    pub iterations_used: u32,
541}
542impl GVNPassStats {
543    #[allow(dead_code)]
544    pub fn new() -> Self {
545        Self::default()
546    }
547    #[allow(dead_code)]
548    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
549        self.total_runs += 1;
550        self.successful_runs += 1;
551        self.total_changes += changes;
552        self.time_ms += time_ms;
553        self.iterations_used = iterations;
554    }
555    #[allow(dead_code)]
556    pub fn average_changes_per_run(&self) -> f64 {
557        if self.total_runs == 0 {
558            return 0.0;
559        }
560        self.total_changes as f64 / self.total_runs as f64
561    }
562    #[allow(dead_code)]
563    pub fn success_rate(&self) -> f64 {
564        if self.total_runs == 0 {
565            return 0.0;
566        }
567        self.successful_runs as f64 / self.total_runs as f64
568    }
569    #[allow(dead_code)]
570    pub fn format_summary(&self) -> String {
571        format!(
572            "Runs: {}/{}, Changes: {}, Time: {}ms",
573            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
574        )
575    }
576}
577/// A normalised argument (variable → VN or literal).
578#[derive(Debug, Clone, PartialEq, Eq, Hash)]
579pub enum NormArg {
580    Vn(ValueNumber),
581    LitNat(u64),
582    LitStr(String),
583    Erased,
584}
585/// GVN-based load elimination: if a projection (load) `Proj(i, x)` has
586/// been computed before and the base object `x` has not been modified,
587/// replace subsequent projections with the earlier result.
588#[derive(Debug, Default)]
589pub struct LoadEliminatorGVN {
590    /// Number of loads eliminated.
591    pub eliminated: usize,
592    /// Cache: (ctor_var, field_idx) → result var.
593    pub(super) load_cache: HashMap<(LcnfVarId, u32), LcnfVarId>,
594}
595impl LoadEliminatorGVN {
596    pub fn new() -> Self {
597        LoadEliminatorGVN::default()
598    }
599    /// Run load elimination on a function declaration.
600    pub fn run(&mut self, decl: &mut LcnfFunDecl) {
601        self.load_cache.clear();
602        self.elim_in_expr(&mut decl.body);
603    }
604    pub(super) fn elim_in_expr(&mut self, expr: &mut LcnfExpr) {
605        match expr {
606            LcnfExpr::Let {
607                id, value, body, ..
608            } => {
609                if let LcnfLetValue::Proj(_, idx, src) = value {
610                    let key = (*src, *idx);
611                    if let Some(&cached) = self.load_cache.get(&key) {
612                        *value = LcnfLetValue::FVar(cached);
613                        self.eliminated += 1;
614                    } else {
615                        self.load_cache.insert(key, *id);
616                    }
617                }
618                if let LcnfLetValue::Reuse(slot, _, _, _) = value {
619                    let slot_copy = *slot;
620                    self.load_cache.retain(|&(src, _), _| src != slot_copy);
621                }
622                self.elim_in_expr(body);
623            }
624            LcnfExpr::Case { alts, default, .. } => {
625                let saved = self.load_cache.clone();
626                for alt in alts.iter_mut() {
627                    self.load_cache = saved.clone();
628                    self.elim_in_expr(&mut alt.body);
629                }
630                if let Some(d) = default {
631                    self.load_cache = saved;
632                    self.elim_in_expr(d);
633                }
634            }
635            _ => {}
636        }
637    }
638}
639/// GVN pass that uses branch predicates to derive additional equalities.
640///
641/// When we enter a `case` branch where the scrutinee matches tag `t`,
642/// we know the scrutinee is a specific constructor.  This lets us refine
643/// value numbers for projection expressions.
644#[derive(Debug, Default)]
645pub struct PredicateGVN {
646    /// Active predicates at the current program point.
647    pub(super) active_preds: Vec<Predicate>,
648    /// Number of additional equalities derived from predicates.
649    pub equalities_derived: usize,
650}
651impl PredicateGVN {
652    pub fn new() -> Self {
653        PredicateGVN::default()
654    }
655    /// Enter a case branch where `scrutinee` has constructor tag `ctor_tag`.
656    pub fn enter_branch(&mut self, scrutinee: LcnfVarId, ctor_tag: u32) {
657        self.active_preds
658            .push(Predicate::EqLit(scrutinee, LcnfLit::Nat(ctor_tag as u64)));
659    }
660    /// Exit the current branch, restoring the predicate environment.
661    pub fn exit_branch(&mut self) {
662        self.active_preds.pop();
663    }
664    /// Check if `var == lit` is known from active predicates.
665    pub fn knows_eq_lit(&self, var: LcnfVarId, lit: &LcnfLit) -> bool {
666        self.active_preds
667            .contains(&Predicate::EqLit(var, lit.clone()))
668    }
669    /// Run predicate-based GVN on `decl`, augmenting an existing `GVNPass`.
670    pub fn run(&mut self, decl: &mut LcnfFunDecl, base_pass: &mut GVNPass) {
671        self.run_in_expr(&mut decl.body, base_pass);
672    }
673    pub(super) fn run_in_expr(&mut self, expr: &mut LcnfExpr, base_pass: &mut GVNPass) {
674        match expr {
675            LcnfExpr::Let { body, .. } => {
676                self.run_in_expr(body, base_pass);
677            }
678            LcnfExpr::Case {
679                scrutinee,
680                alts,
681                default,
682                ..
683            } => {
684                for alt in alts.iter_mut() {
685                    self.enter_branch(*scrutinee, alt.ctor_tag);
686                    self.equalities_derived += 1;
687                    self.run_in_expr(&mut alt.body, base_pass);
688                    self.exit_branch();
689                }
690                if let Some(d) = default {
691                    self.run_in_expr(d, base_pass);
692                }
693            }
694            _ => {}
695        }
696    }
697}
698/// Algebraic simplifier using GVN value numbers.
699///
700/// Examples of rules:
701/// - `x + 0 = x`
702/// - `x * 1 = x`
703/// - `x - x = 0`
704/// - `x == x = true`
705/// These are encoded as GVN-aware rewriting on `NormExpr` keys.
706#[derive(Debug)]
707pub struct AlgebraicSimplifier {
708    /// Rules that have been registered.
709    pub rules: Vec<AlgRule>,
710    /// Total simplifications performed.
711    pub total_simplified: usize,
712}
713impl AlgebraicSimplifier {
714    pub fn new() -> Self {
715        AlgebraicSimplifier::default()
716    }
717    /// Apply algebraic simplification rules to a NormExpr.
718    /// Returns a simplified NormExpr if a rule applies.
719    pub fn simplify(&mut self, expr: &NormExpr, fact: &GVNFact) -> Option<NormExpr> {
720        let _ = fact;
721        match expr {
722            NormExpr::App(NormArg::Vn(_), args) if args.last() == Some(&NormArg::LitNat(0)) => {
723                self.rules[0].applied += 1;
724                self.total_simplified += 1;
725                if let Some(NormArg::Vn(vn)) = args.first() {
726                    Some(NormExpr::FVar(*vn))
727                } else {
728                    None
729                }
730            }
731            NormExpr::App(NormArg::Vn(_), args)
732                if args.last() == Some(&NormArg::LitNat(1)) && args.len() == 2 =>
733            {
734                self.rules[1].applied += 1;
735                self.total_simplified += 1;
736                if let Some(NormArg::Vn(vn)) = args.first() {
737                    Some(NormExpr::FVar(*vn))
738                } else {
739                    None
740                }
741            }
742            _ => None,
743        }
744    }
745    /// Run simplification over all bindings in a function.
746    pub fn run(&mut self, _decl: &mut LcnfFunDecl) {}
747}
748/// A node in the dominator tree of the LCNF expression structure.
749/// In ANF/LCNF, the dominator tree mirrors the nesting of `let` expressions:
750/// the binding of `x` dominates everything in the body of the `let`.
751#[derive(Debug, Clone)]
752pub struct DomTreeNode {
753    /// The variable introduced at this node.
754    pub var: LcnfVarId,
755    /// Children in the dominator tree (variables dominated by this node).
756    pub children: Vec<LcnfVarId>,
757    /// Depth in the dominator tree (root = 0).
758    pub depth: u32,
759}
760/// Collection of phi-nodes at a case join point.
761#[derive(Debug, Default)]
762pub struct PhiNodeSet {
763    pub phis: Vec<PhiNode>,
764    /// Next VN to allocate for phi-nodes.
765    pub(super) next_vn: ValueNumber,
766}
767impl PhiNodeSet {
768    pub fn new(start_vn: ValueNumber) -> Self {
769        PhiNodeSet {
770            phis: Vec::new(),
771            next_vn: start_vn,
772        }
773    }
774    /// Create and record a new phi-node.
775    pub fn add_phi(&mut self, var: LcnfVarId, operands: Vec<PhiOperand>) -> &PhiNode {
776        let vn = self.next_vn;
777        self.next_vn += 1;
778        self.phis.push(PhiNode::new(var, operands, vn));
779        self.phis
780            .last()
781            .expect("phis is non-empty after push; invariant guaranteed by add_phi")
782    }
783    /// Remove all trivial phi-nodes (all operands have the same VN).
784    pub fn remove_trivial(&mut self) -> usize {
785        let before = self.phis.len();
786        self.phis.retain(|p| !p.is_trivial());
787        before - self.phis.len()
788    }
789    pub fn num_phis(&self) -> usize {
790        self.phis.len()
791    }
792}
793#[allow(dead_code)]
794pub struct GVNConstantFoldingHelper;
795impl GVNConstantFoldingHelper {
796    #[allow(dead_code)]
797    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
798        a.checked_add(b)
799    }
800    #[allow(dead_code)]
801    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
802        a.checked_sub(b)
803    }
804    #[allow(dead_code)]
805    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
806        a.checked_mul(b)
807    }
808    #[allow(dead_code)]
809    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
810        if b == 0 {
811            None
812        } else {
813            a.checked_div(b)
814        }
815    }
816    #[allow(dead_code)]
817    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
818        a + b
819    }
820    #[allow(dead_code)]
821    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
822        a * b
823    }
824    #[allow(dead_code)]
825    pub fn fold_neg_i64(a: i64) -> Option<i64> {
826        a.checked_neg()
827    }
828    #[allow(dead_code)]
829    pub fn fold_not_bool(a: bool) -> bool {
830        !a
831    }
832    #[allow(dead_code)]
833    pub fn fold_and_bool(a: bool, b: bool) -> bool {
834        a && b
835    }
836    #[allow(dead_code)]
837    pub fn fold_or_bool(a: bool, b: bool) -> bool {
838        a || b
839    }
840    #[allow(dead_code)]
841    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
842        a.checked_shl(b)
843    }
844    #[allow(dead_code)]
845    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
846        a.checked_shr(b)
847    }
848    #[allow(dead_code)]
849    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
850        if b == 0 {
851            None
852        } else {
853            Some(a % b)
854        }
855    }
856    #[allow(dead_code)]
857    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
858        a & b
859    }
860    #[allow(dead_code)]
861    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
862        a | b
863    }
864    #[allow(dead_code)]
865    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
866        a ^ b
867    }
868    #[allow(dead_code)]
869    pub fn fold_bitnot_i64(a: i64) -> i64 {
870        !a
871    }
872}
873#[allow(dead_code)]
874#[derive(Debug, Clone)]
875pub struct GVNDepGraph {
876    pub(super) nodes: Vec<u32>,
877    pub(super) edges: Vec<(u32, u32)>,
878}
879impl GVNDepGraph {
880    #[allow(dead_code)]
881    pub fn new() -> Self {
882        GVNDepGraph {
883            nodes: Vec::new(),
884            edges: Vec::new(),
885        }
886    }
887    #[allow(dead_code)]
888    pub fn add_node(&mut self, id: u32) {
889        if !self.nodes.contains(&id) {
890            self.nodes.push(id);
891        }
892    }
893    #[allow(dead_code)]
894    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
895        self.add_node(dep);
896        self.add_node(dependent);
897        self.edges.push((dep, dependent));
898    }
899    #[allow(dead_code)]
900    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
901        self.edges
902            .iter()
903            .filter(|(d, _)| *d == node)
904            .map(|(_, dep)| *dep)
905            .collect()
906    }
907    #[allow(dead_code)]
908    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
909        self.edges
910            .iter()
911            .filter(|(_, dep)| *dep == node)
912            .map(|(d, _)| *d)
913            .collect()
914    }
915    #[allow(dead_code)]
916    pub fn topological_sort(&self) -> Vec<u32> {
917        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
918        for &n in &self.nodes {
919            in_degree.insert(n, 0);
920        }
921        for (_, dep) in &self.edges {
922            *in_degree.entry(*dep).or_insert(0) += 1;
923        }
924        let mut queue: std::collections::VecDeque<u32> = self
925            .nodes
926            .iter()
927            .filter(|&&n| in_degree[&n] == 0)
928            .copied()
929            .collect();
930        let mut result = Vec::new();
931        while let Some(node) = queue.pop_front() {
932            result.push(node);
933            for dep in self.dependents_of(node) {
934                let cnt = in_degree.entry(dep).or_insert(0);
935                *cnt = cnt.saturating_sub(1);
936                if *cnt == 0 {
937                    queue.push_back(dep);
938                }
939            }
940        }
941        result
942    }
943    #[allow(dead_code)]
944    pub fn has_cycle(&self) -> bool {
945        self.topological_sort().len() < self.nodes.len()
946    }
947}
948/// Fixpoint GVN: iterate GVN until no new equalities are discovered.
949/// This handles phi-node value numbering precisely.
950#[derive(Debug, Default)]
951pub struct FixpointGVN {
952    /// Maximum number of iterations before giving up.
953    pub max_iter: usize,
954    /// Number of iterations performed.
955    pub iterations: usize,
956    /// Whether convergence was achieved.
957    pub converged: bool,
958    /// Total redundancies found across all iterations.
959    pub total_redundancies: usize,
960}
961impl FixpointGVN {
962    pub fn new(max_iter: usize) -> Self {
963        FixpointGVN {
964            max_iter,
965            iterations: 0,
966            converged: false,
967            total_redundancies: 0,
968        }
969    }
970    /// Run fixpoint GVN on a function declaration.
971    pub fn run(&mut self, decl: &mut LcnfFunDecl) {
972        let mut prev_state = FixpointState::new();
973        for iter in 0..self.max_iter {
974            self.iterations = iter + 1;
975            let mut pass = GVNPass::default();
976            let mut table = ValueTable::new();
977            let mut fact = GVNFact::new();
978            pass.assign_value_numbers(decl, &mut table, &mut fact);
979            let redundancies = pass.report().redundancies_eliminated;
980            self.total_redundancies += redundancies;
981            let curr_state = FixpointState {
982                table: table.clone(),
983                exit_fact: fact,
984                redundancies,
985            };
986            if curr_state.table.len() == prev_state.table.len() {
987                self.converged = true;
988                break;
989            }
990            pass.eliminate_redundant(decl, &mut table);
991            prev_state = curr_state;
992        }
993    }
994}
995/// An algebraic simplification rule: a pattern to match and a replacement.
996#[derive(Debug, Clone)]
997pub struct AlgRule {
998    /// Human-readable name of the rule.
999    pub name: String,
1000    /// Number of times this rule has been applied.
1001    pub applied: usize,
1002}
1003impl AlgRule {
1004    pub fn new(name: &str) -> Self {
1005        AlgRule {
1006            name: name.to_string(),
1007            applied: 0,
1008        }
1009    }
1010}
1011/// Dominator tree derived from the LCNF nesting structure.
1012#[derive(Debug, Default)]
1013pub struct DomTree {
1014    /// Map from variable id to its dominator tree node.
1015    pub nodes: HashMap<LcnfVarId, DomTreeNode>,
1016    /// The root variables (outermost let-bindings or function entries).
1017    pub roots: Vec<LcnfVarId>,
1018}
1019impl DomTree {
1020    pub fn new() -> Self {
1021        DomTree::default()
1022    }
1023    /// Build a dominator tree from a function declaration.
1024    pub fn build_from_decl(decl: &LcnfFunDecl) -> Self {
1025        let mut dt = DomTree::new();
1026        let mut parent: Option<LcnfVarId> = None;
1027        dt.build_in_expr(&decl.body, &mut parent, 0);
1028        dt
1029    }
1030    pub(super) fn build_in_expr(
1031        &mut self,
1032        expr: &LcnfExpr,
1033        parent: &mut Option<LcnfVarId>,
1034        depth: u32,
1035    ) {
1036        match expr {
1037            LcnfExpr::Let { id, body, .. } => {
1038                let node = DomTreeNode {
1039                    var: *id,
1040                    children: Vec::new(),
1041                    depth,
1042                };
1043                self.nodes.insert(*id, node);
1044                if let Some(p) = *parent {
1045                    if let Some(pn) = self.nodes.get_mut(&p) {
1046                        pn.children.push(*id);
1047                    }
1048                } else {
1049                    self.roots.push(*id);
1050                }
1051                let prev = *parent;
1052                *parent = Some(*id);
1053                self.build_in_expr(body, parent, depth + 1);
1054                *parent = prev;
1055            }
1056            LcnfExpr::Case { alts, default, .. } => {
1057                for alt in alts {
1058                    let mut br_parent = *parent;
1059                    self.build_in_expr(&alt.body, &mut br_parent, depth);
1060                }
1061                if let Some(d) = default {
1062                    let mut br_parent = *parent;
1063                    self.build_in_expr(d, &mut br_parent, depth);
1064                }
1065            }
1066            _ => {}
1067        }
1068    }
1069    /// Return `true` if `a` dominates `b`.
1070    pub fn dominates(&self, a: LcnfVarId, b: LcnfVarId) -> bool {
1071        if a == b {
1072            return true;
1073        }
1074        if let Some(node_b) = self.nodes.get(&b) {
1075            let _ = node_b;
1076        }
1077        self.is_ancestor(a, b)
1078    }
1079    pub(super) fn is_ancestor(&self, a: LcnfVarId, target: LcnfVarId) -> bool {
1080        if let Some(node) = self.nodes.get(&a) {
1081            for &child in &node.children {
1082                if child == target || self.is_ancestor(child, target) {
1083                    return true;
1084                }
1085            }
1086        }
1087        false
1088    }
1089    pub fn num_nodes(&self) -> usize {
1090        self.nodes.len()
1091    }
1092}
1093/// GVN-PRE pass: extends GVN with partial redundancy elimination.
1094/// Identifies expressions that are redundant on some paths and inserts
1095/// computations at join points to make them fully redundant.
1096#[derive(Debug, Default)]
1097pub struct GVNPrePass {
1098    /// Number of expressions inserted at join points.
1099    pub insertions: usize,
1100    /// Number of redundancies eliminated after insertion.
1101    pub eliminations: usize,
1102    /// Anticipation sets computed per variable.
1103    pub anticipation: HashMap<LcnfVarId, AnticipationSet>,
1104}
1105impl GVNPrePass {
1106    pub fn new() -> Self {
1107        GVNPrePass::default()
1108    }
1109    /// Compute anticipation sets for all let-bindings in `decl`.
1110    pub fn compute_anticipation(&mut self, decl: &LcnfFunDecl) {
1111        let mut anticipated = AnticipationSet::new();
1112        self.anticip_in_expr(&decl.body, &mut anticipated);
1113    }
1114    pub(super) fn anticip_in_expr(&mut self, expr: &LcnfExpr, anticipated: &mut AnticipationSet) {
1115        match expr {
1116            LcnfExpr::Let {
1117                id, value, body, ..
1118            } => {
1119                self.anticipation.insert(*id, anticipated.clone());
1120                let key = norm_expr_from_value_conservative(value);
1121                anticipated.add(key);
1122                self.anticip_in_expr(body, anticipated);
1123            }
1124            LcnfExpr::Case { alts, default, .. } => {
1125                let mut branch_sets: Vec<AnticipationSet> = Vec::new();
1126                for alt in alts {
1127                    let mut br = anticipated.clone();
1128                    self.anticip_in_expr(&alt.body, &mut br);
1129                    branch_sets.push(br);
1130                }
1131                if let Some(d) = default {
1132                    let mut br = anticipated.clone();
1133                    self.anticip_in_expr(d, &mut br);
1134                    branch_sets.push(br);
1135                }
1136                if let Some(first) = branch_sets.first() {
1137                    let mut meet = first.clone();
1138                    for bs in branch_sets.iter().skip(1) {
1139                        meet = meet.meet(bs);
1140                    }
1141                    *anticipated = meet;
1142                }
1143            }
1144            _ => {}
1145        }
1146    }
1147    /// Identify and record expressions to be inserted at join points.
1148    /// (In this simplified version, we just count how many could be inserted.)
1149    pub fn run(&mut self, decl: &mut LcnfFunDecl) {
1150        self.compute_anticipation(decl);
1151        self.insertions = self
1152            .anticipation
1153            .values()
1154            .map(|a| a.anticipated.len())
1155            .sum();
1156    }
1157}
1158/// A single phi-node operand: a (predecessor-label, value-number) pair.
1159#[derive(Debug, Clone, PartialEq, Eq)]
1160pub struct PhiOperand {
1161    /// Branch index (0 = first alt, 1 = second alt, etc.).
1162    pub branch_idx: usize,
1163    /// Value number in that branch.
1164    pub vn: ValueNumber,
1165}
1166/// The main Global Value Numbering pass.
1167pub struct GVNPass {
1168    pub(super) config: GVNConfig,
1169    pub(super) report: GVNReport,
1170}
1171impl GVNPass {
1172    pub fn new(config: GVNConfig) -> Self {
1173        GVNPass {
1174            config,
1175            report: GVNReport::default(),
1176        }
1177    }
1178    /// Run the GVN pass over all function declarations, eliminating
1179    /// redundant expressions in-place.
1180    pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
1181        for decl in decls.iter_mut() {
1182            let mut table = ValueTable::new();
1183            let mut fact = GVNFact::new();
1184            self.assign_value_numbers(decl, &mut table, &mut fact);
1185            self.eliminate_redundant(decl, &mut table);
1186        }
1187    }
1188    /// Perform an RPO traversal of the LCNF expression tree, assigning
1189    /// value numbers to every let-binding.
1190    pub fn assign_value_numbers(
1191        &mut self,
1192        decl: &LcnfFunDecl,
1193        table: &mut ValueTable,
1194        fact: &mut GVNFact,
1195    ) {
1196        let mut depth = 0usize;
1197        self.vn_expr(&decl.body, table, fact, &mut depth);
1198    }
1199    /// Look up or assign a value number for `value` in `table`, given the
1200    /// current dataflow `fact`.  Returns the value number.
1201    pub fn lookup_or_assign(
1202        &mut self,
1203        var: LcnfVarId,
1204        value: &LcnfLetValue,
1205        table: &mut ValueTable,
1206        fact: &mut GVNFact,
1207    ) -> ValueNumber {
1208        let key = self.normalise_let_value(value, fact);
1209        if let Some(vn) = table.lookup(&key) {
1210            fact.insert(var, vn);
1211            vn
1212        } else {
1213            let vn = table.insert(key, value.clone(), var);
1214            fact.insert(var, vn);
1215            self.report.expressions_numbered += 1;
1216            vn
1217        }
1218    }
1219    /// Rewrite `decl.body` in-place, replacing redundant let-bindings with
1220    /// copy bindings (`let x = y`) when two bindings share a value number.
1221    pub fn eliminate_redundant(&mut self, decl: &mut LcnfFunDecl, table: &mut ValueTable) {
1222        let mut fact = GVNFact::new();
1223        self.rewrite_expr(&mut decl.body, table, &mut fact);
1224    }
1225    /// Return a copy of the accumulated statistics report.
1226    pub fn report(&self) -> GVNReport {
1227        self.report.clone()
1228    }
1229    /// Walk `expr` assigning value numbers; does NOT modify the expression.
1230    pub(super) fn vn_expr(
1231        &mut self,
1232        expr: &LcnfExpr,
1233        table: &mut ValueTable,
1234        fact: &mut GVNFact,
1235        depth: &mut usize,
1236    ) {
1237        if *depth >= self.config.max_depth {
1238            return;
1239        }
1240        *depth += 1;
1241        match expr {
1242            LcnfExpr::Let {
1243                id, value, body, ..
1244            } => {
1245                self.lookup_or_assign(*id, value, table, fact);
1246                self.vn_expr(body, table, fact, depth);
1247            }
1248            LcnfExpr::Case { alts, default, .. } => {
1249                for alt in alts {
1250                    let mut branch_fact = fact.clone();
1251                    let mut d = *depth;
1252                    self.vn_expr(&alt.body, table, &mut branch_fact, &mut d);
1253                    if self.config.do_phi_translation {
1254                        self.report.phi_translations += 1;
1255                    }
1256                }
1257                if let Some(def) = default {
1258                    let mut branch_fact = fact.clone();
1259                    let mut d = *depth;
1260                    self.vn_expr(def, table, &mut branch_fact, &mut d);
1261                }
1262            }
1263            LcnfExpr::Return(_) | LcnfExpr::Unreachable | LcnfExpr::TailCall(..) => {}
1264        }
1265        *depth -= 1;
1266    }
1267    /// Rewrite `expr` in-place, substituting canonical variables for
1268    /// redundant let-bindings.
1269    pub(super) fn rewrite_expr(
1270        &mut self,
1271        expr: &mut LcnfExpr,
1272        table: &mut ValueTable,
1273        fact: &mut GVNFact,
1274    ) {
1275        match expr {
1276            LcnfExpr::Let {
1277                id,
1278                value,
1279                body,
1280                ty,
1281                ..
1282            } => {
1283                let key = self.normalise_let_value(value, fact);
1284                if let Some(vn) = table.lookup(&key) {
1285                    if let Some(canon) = table.canonical_var(vn) {
1286                        if canon != *id {
1287                            *value = LcnfLetValue::FVar(canon);
1288                            fact.insert(*id, vn);
1289                            self.report.redundancies_eliminated += 1;
1290                        } else {
1291                            fact.insert(*id, vn);
1292                        }
1293                    } else {
1294                        fact.insert(*id, vn);
1295                    }
1296                } else {
1297                    let vn = table.insert(key, value.clone(), *id);
1298                    fact.insert(*id, vn);
1299                    let _ = ty;
1300                }
1301                self.rewrite_expr(body, table, fact);
1302            }
1303            LcnfExpr::Case { alts, default, .. } => {
1304                for alt in alts.iter_mut() {
1305                    let mut branch_fact = fact.clone();
1306                    self.rewrite_expr(&mut alt.body, table, &mut branch_fact);
1307                }
1308                if let Some(def) = default {
1309                    let mut branch_fact = fact.clone();
1310                    self.rewrite_expr(def, table, &mut branch_fact);
1311                }
1312            }
1313            LcnfExpr::Return(_) | LcnfExpr::Unreachable | LcnfExpr::TailCall(..) => {}
1314        }
1315    }
1316    /// Produce a `NormExpr` key for `value` using the current `fact` to
1317    /// translate variable ids to value numbers.
1318    pub(super) fn normalise_let_value(&self, value: &LcnfLetValue, fact: &GVNFact) -> NormExpr {
1319        match value {
1320            LcnfLetValue::Lit(LcnfLit::Nat(n)) => NormExpr::Lit(*n),
1321            LcnfLetValue::Lit(LcnfLit::Str(s)) => NormExpr::LitStr(s.clone()),
1322            LcnfLetValue::Erased => NormExpr::Erased,
1323            LcnfLetValue::FVar(v) => {
1324                let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1325                NormExpr::FVar(vn)
1326            }
1327            LcnfLetValue::Proj(name, idx, v) => {
1328                let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1329                NormExpr::Proj(name.clone(), *idx, vn)
1330            }
1331            LcnfLetValue::Ctor(name, tag, args) => {
1332                let nargs = args.iter().map(|a| self.norm_arg(a, fact)).collect();
1333                NormExpr::Ctor(name.clone(), *tag, nargs)
1334            }
1335            LcnfLetValue::App(f, args) => {
1336                let nf = self.norm_arg(f, fact);
1337                let nargs = args.iter().map(|a| self.norm_arg(a, fact)).collect();
1338                NormExpr::App(nf, nargs)
1339            }
1340            LcnfLetValue::Reset(v) => {
1341                let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1342                NormExpr::Reset(vn)
1343            }
1344            LcnfLetValue::Reuse(..) => NormExpr::Unknown,
1345        }
1346    }
1347    pub(super) fn norm_arg(&self, arg: &LcnfArg, fact: &GVNFact) -> NormArg {
1348        match arg {
1349            LcnfArg::Var(v) => {
1350                let vn = fact.get(v).unwrap_or(v.0 as ValueNumber + 1_000_000);
1351                NormArg::Vn(vn)
1352            }
1353            LcnfArg::Lit(LcnfLit::Nat(n)) => NormArg::LitNat(*n),
1354            LcnfArg::Lit(LcnfLit::Str(s)) => NormArg::LitStr(s.clone()),
1355            LcnfArg::Erased => NormArg::Erased,
1356            LcnfArg::Type(_) => NormArg::Erased,
1357        }
1358    }
1359}
1360#[allow(dead_code)]
1361#[derive(Debug, Clone)]
1362pub struct GVNWorklist {
1363    pub(super) items: std::collections::VecDeque<u32>,
1364    pub(super) in_worklist: std::collections::HashSet<u32>,
1365}
1366impl GVNWorklist {
1367    #[allow(dead_code)]
1368    pub fn new() -> Self {
1369        GVNWorklist {
1370            items: std::collections::VecDeque::new(),
1371            in_worklist: std::collections::HashSet::new(),
1372        }
1373    }
1374    #[allow(dead_code)]
1375    pub fn push(&mut self, item: u32) -> bool {
1376        if self.in_worklist.insert(item) {
1377            self.items.push_back(item);
1378            true
1379        } else {
1380            false
1381        }
1382    }
1383    #[allow(dead_code)]
1384    pub fn pop(&mut self) -> Option<u32> {
1385        let item = self.items.pop_front()?;
1386        self.in_worklist.remove(&item);
1387        Some(item)
1388    }
1389    #[allow(dead_code)]
1390    pub fn is_empty(&self) -> bool {
1391        self.items.is_empty()
1392    }
1393    #[allow(dead_code)]
1394    pub fn len(&self) -> usize {
1395        self.items.len()
1396    }
1397    #[allow(dead_code)]
1398    pub fn contains(&self, item: u32) -> bool {
1399        self.in_worklist.contains(&item)
1400    }
1401}
1402/// Tracks equalities between value numbers discovered during GVN.
1403///
1404/// When two variables are found to have the same value number we record
1405/// them as congruent.  This information can be used by later passes (e.g.
1406/// alias analysis).
1407#[derive(Debug, Default)]
1408pub struct CongruenceClosure {
1409    /// Map from VN to its representative (union-find parent).
1410    pub(super) parent: HashMap<ValueNumber, ValueNumber>,
1411}
1412impl CongruenceClosure {
1413    pub fn new() -> Self {
1414        CongruenceClosure::default()
1415    }
1416    /// Mark `a` and `b` as equivalent.
1417    pub fn union(&mut self, a: ValueNumber, b: ValueNumber) {
1418        let ra = self.find(a);
1419        let rb = self.find(b);
1420        if ra != rb {
1421            self.parent.insert(ra, rb);
1422        }
1423    }
1424    /// Find the representative of `vn`'s equivalence class.
1425    pub fn find(&mut self, vn: ValueNumber) -> ValueNumber {
1426        match self.parent.get(&vn).copied() {
1427            None => vn,
1428            Some(p) if p == vn => vn,
1429            Some(p) => {
1430                let root = self.find(p);
1431                self.parent.insert(vn, root);
1432                root
1433            }
1434        }
1435    }
1436    /// Return `true` if `a` and `b` are known to be equivalent.
1437    pub fn are_equal(&mut self, a: ValueNumber, b: ValueNumber) -> bool {
1438        self.find(a) == self.find(b)
1439    }
1440    pub fn num_classes(&self) -> usize {
1441        self.parent.len()
1442    }
1443}
1444/// Maps each in-scope variable to its current value number at a program point.
1445#[derive(Debug, Clone, Default)]
1446pub struct GVNFact {
1447    pub var_to_vn: HashMap<LcnfVarId, ValueNumber>,
1448}
1449impl GVNFact {
1450    pub fn new() -> Self {
1451        GVNFact::default()
1452    }
1453    pub fn get(&self, var: &LcnfVarId) -> Option<ValueNumber> {
1454        self.var_to_vn.get(var).copied()
1455    }
1456    pub fn insert(&mut self, var: LcnfVarId, vn: ValueNumber) {
1457        self.var_to_vn.insert(var, vn);
1458    }
1459    /// Compute the meet (intersection) of two facts at a join point.
1460    /// Variables with different VNs in different branches are dropped.
1461    pub fn meet(&self, other: &GVNFact) -> GVNFact {
1462        let mut result = GVNFact::new();
1463        for (var, &vn) in &self.var_to_vn {
1464            if other.var_to_vn.get(var) == Some(&vn) {
1465                result.var_to_vn.insert(*var, vn);
1466            }
1467        }
1468        result
1469    }
1470}
1471/// Finds the *leader* of an equivalence class — the canonical variable
1472/// that dominates all other members of the class.
1473///
1474/// In LCNF, dominance is determined by binding order (earlier binding
1475/// dominates later bindings in the same scope).
1476#[derive(Debug, Default)]
1477pub struct LeaderFinder {
1478    /// Map from VN to the current leader variable.
1479    pub(super) leaders: HashMap<ValueNumber, LcnfVarId>,
1480    /// Map from VN to all members of the equivalence class.
1481    pub(super) members: HashMap<ValueNumber, Vec<LcnfVarId>>,
1482}
1483impl LeaderFinder {
1484    pub fn new() -> Self {
1485        LeaderFinder::default()
1486    }
1487    /// Record that `var` belongs to equivalence class `vn`.
1488    pub fn record(&mut self, vn: ValueNumber, var: LcnfVarId) {
1489        self.members.entry(vn).or_default().push(var);
1490        self.leaders.entry(vn).or_insert(var);
1491    }
1492    /// Return the leader of the equivalence class for `vn`.
1493    pub fn leader(&self, vn: ValueNumber) -> Option<LcnfVarId> {
1494        self.leaders.get(&vn).copied()
1495    }
1496    /// Return all members of the equivalence class for `vn`.
1497    pub fn members(&self, vn: ValueNumber) -> &[LcnfVarId] {
1498        self.members.get(&vn).map(|v| v.as_slice()).unwrap_or(&[])
1499    }
1500    /// Return the number of non-singleton equivalence classes.
1501    pub fn num_redundancies(&self) -> usize {
1502        self.members.values().filter(|v| v.len() > 1).count()
1503    }
1504}
1505/// A multi-stage GVN pipeline that chains several GVN-based analyses.
1506pub struct GVNPipeline {
1507    /// Whether to run the base GVN pass.
1508    pub do_base_gvn: bool,
1509    /// Whether to run load elimination.
1510    pub do_load_elim: bool,
1511    /// Whether to run GVN-PRE.
1512    pub do_pre: bool,
1513    /// Whether to run CCP.
1514    pub do_ccp: bool,
1515    /// Whether to run fixpoint iteration.
1516    pub do_fixpoint: bool,
1517    /// Maximum fixpoint iterations.
1518    pub max_fixpoint_iter: usize,
1519    /// Combined statistics.
1520    pub stats: GVNStatistics,
1521}
1522impl GVNPipeline {
1523    pub fn new() -> Self {
1524        GVNPipeline::default()
1525    }
1526    /// Run the GVN pipeline on all declarations.
1527    pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
1528        for decl in decls.iter_mut() {
1529            if self.do_base_gvn {
1530                let mut pass = GVNPass::default();
1531                pass.run(std::slice::from_mut(decl));
1532                let r = pass.report();
1533                self.stats.total_vns += r.expressions_numbered;
1534                self.stats.phi_translations += r.phi_translations;
1535            }
1536            if self.do_load_elim {
1537                let mut le = LoadEliminatorGVN::new();
1538                le.run(decl);
1539                self.stats.proj_redundancies += le.eliminated;
1540            }
1541            if self.do_pre {
1542                let mut pre = GVNPrePass::new();
1543                pre.run(decl);
1544            }
1545            if self.do_ccp {
1546                let mut ccp = CCPState::new();
1547                ccp.run(decl);
1548                self.stats.lit_redundancies += ccp.folded;
1549            }
1550            if self.do_fixpoint {
1551                let mut fp = FixpointGVN::new(self.max_fixpoint_iter);
1552                fp.run(decl);
1553                self.stats.total_vns += fp.total_redundancies;
1554            }
1555        }
1556    }
1557    pub fn total_redundancies(&self) -> usize {
1558        self.stats.total_redundancies()
1559    }
1560}
1561#[allow(dead_code)]
1562#[derive(Debug, Clone, PartialEq)]
1563pub enum GVNPassPhase {
1564    Analysis,
1565    Transformation,
1566    Verification,
1567    Cleanup,
1568}
1569impl GVNPassPhase {
1570    #[allow(dead_code)]
1571    pub fn name(&self) -> &str {
1572        match self {
1573            GVNPassPhase::Analysis => "analysis",
1574            GVNPassPhase::Transformation => "transformation",
1575            GVNPassPhase::Verification => "verification",
1576            GVNPassPhase::Cleanup => "cleanup",
1577        }
1578    }
1579    #[allow(dead_code)]
1580    pub fn is_modifying(&self) -> bool {
1581        matches!(self, GVNPassPhase::Transformation | GVNPassPhase::Cleanup)
1582    }
1583}
1584#[allow(dead_code)]
1585#[derive(Debug, Clone)]
1586pub struct GVNCacheEntry {
1587    pub key: String,
1588    pub data: Vec<u8>,
1589    pub timestamp: u64,
1590    pub valid: bool,
1591}
1592/// State for a single fixpoint iteration of GVN.
1593#[derive(Debug, Clone)]
1594pub struct FixpointState {
1595    /// The value table from this iteration.
1596    pub table: ValueTable,
1597    /// The GVN fact at function exit.
1598    pub exit_fact: GVNFact,
1599    /// Number of redundancies found in this iteration.
1600    pub redundancies: usize,
1601}
1602impl FixpointState {
1603    pub fn new() -> Self {
1604        FixpointState {
1605            table: ValueTable::new(),
1606            exit_fact: GVNFact::new(),
1607            redundancies: 0,
1608        }
1609    }
1610}
1611/// Bidirectional mapping between normalised expressions and value numbers.
1612///
1613/// The "key" is a `NormExpr` — a lightweight structural hash of the
1614/// expression that only depends on the *value numbers* of sub-expressions
1615/// (rather than variable ids).  This makes it insensitive to renaming.
1616#[derive(Debug, Clone, Default)]
1617pub struct ValueTable {
1618    /// Map from normalised expression key to its canonical value number.
1619    pub(super) expr_to_vn: HashMap<NormExpr, ValueNumber>,
1620    /// Map from value number back to the canonical `LcnfLetValue`.
1621    pub(super) vn_to_expr: HashMap<ValueNumber, LcnfLetValue>,
1622    /// Map from value number to the canonical variable holding that value.
1623    pub(super) vn_to_var: HashMap<ValueNumber, LcnfVarId>,
1624    /// Next fresh value number to assign.
1625    pub(super) next_vn: ValueNumber,
1626}
1627impl ValueTable {
1628    pub fn new() -> Self {
1629        ValueTable::default()
1630    }
1631    /// Look up the value number for a normalised expression key.
1632    pub fn lookup(&self, key: &NormExpr) -> Option<ValueNumber> {
1633        self.expr_to_vn.get(key).copied()
1634    }
1635    /// Return the canonical variable for a value number, if one exists.
1636    pub fn canonical_var(&self, vn: ValueNumber) -> Option<LcnfVarId> {
1637        self.vn_to_var.get(&vn).copied()
1638    }
1639    /// Insert a new expression with a fresh value number, binding `var`
1640    /// as its canonical representative.  Returns the assigned VN.
1641    pub fn insert(&mut self, key: NormExpr, value: LcnfLetValue, var: LcnfVarId) -> ValueNumber {
1642        let vn = self.next_vn;
1643        self.next_vn += 1;
1644        self.expr_to_vn.insert(key, vn);
1645        self.vn_to_expr.insert(vn, value);
1646        self.vn_to_var.insert(vn, var);
1647        vn
1648    }
1649    /// Total number of value numbers assigned so far.
1650    pub fn len(&self) -> usize {
1651        self.next_vn as usize
1652    }
1653    pub fn is_empty(&self) -> bool {
1654        self.next_vn == 0
1655    }
1656    /// Return a snapshot of all (NormExpr, VN) pairs.
1657    pub fn snapshot(&self) -> Vec<(NormExpr, ValueNumber)> {
1658        self.expr_to_vn
1659            .iter()
1660            .map(|(k, v)| (k.clone(), *v))
1661            .collect()
1662    }
1663    /// Attempt to merge the entries from `other` into `self` without
1664    /// conflict (returns false on any VN collision).
1665    pub fn try_merge(&mut self, other: &ValueTable) -> bool {
1666        for (key, &vn) in &other.expr_to_vn {
1667            if let Some(&existing_vn) = self.expr_to_vn.get(key) {
1668                if existing_vn != vn {
1669                    return false;
1670                }
1671            } else {
1672                self.expr_to_vn.insert(key.clone(), vn);
1673                if let Some(expr) = other.vn_to_expr.get(&vn) {
1674                    self.vn_to_expr.insert(vn, expr.clone());
1675                }
1676                if let Some(&cvar) = other.vn_to_var.get(&vn) {
1677                    self.vn_to_var.insert(vn, cvar);
1678                }
1679                if vn >= self.next_vn {
1680                    self.next_vn = vn + 1;
1681                }
1682            }
1683        }
1684        true
1685    }
1686}
1687/// Interprocedural GVN: uses function summaries to propagate value
1688/// equalities across function call boundaries.
1689#[derive(Debug, Default)]
1690pub struct InterproceduralGVN {
1691    /// Summaries for known functions.
1692    pub summaries: HashMap<String, GVNFunctionSummary>,
1693    /// Number of cross-function equalities discovered.
1694    pub cross_fn_equalities: usize,
1695}
1696impl InterproceduralGVN {
1697    pub fn new() -> Self {
1698        InterproceduralGVN::default()
1699    }
1700    /// Register a function summary.
1701    pub fn add_summary(&mut self, name: String, summary: GVNFunctionSummary) {
1702        self.summaries.insert(name, summary);
1703    }
1704    /// Query whether two calls to `fn_name` with the same arguments
1705    /// produce equal results.
1706    pub fn calls_are_equal(&self, fn_name: &str) -> bool {
1707        self.summaries
1708            .get(fn_name)
1709            .map(|s| s.is_pure_fn)
1710            .unwrap_or(false)
1711    }
1712    /// Run interprocedural GVN to find cross-function redundancies.
1713    pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
1714        for decl in decls.iter_mut() {
1715            if let Some(summary) = self.summaries.get(&decl.name) {
1716                if summary.is_pure_fn {
1717                    self.cross_fn_equalities += 1;
1718                }
1719            }
1720        }
1721    }
1722}
1723#[allow(dead_code)]
1724#[derive(Debug, Clone)]
1725pub struct GVNPassConfig {
1726    pub phase: GVNPassPhase,
1727    pub enabled: bool,
1728    pub max_iterations: u32,
1729    pub debug_output: bool,
1730    pub pass_name: String,
1731}
1732impl GVNPassConfig {
1733    #[allow(dead_code)]
1734    pub fn new(name: impl Into<String>, phase: GVNPassPhase) -> Self {
1735        GVNPassConfig {
1736            phase,
1737            enabled: true,
1738            max_iterations: 10,
1739            debug_output: false,
1740            pass_name: name.into(),
1741        }
1742    }
1743    #[allow(dead_code)]
1744    pub fn disabled(mut self) -> Self {
1745        self.enabled = false;
1746        self
1747    }
1748    #[allow(dead_code)]
1749    pub fn with_debug(mut self) -> Self {
1750        self.debug_output = true;
1751        self
1752    }
1753    #[allow(dead_code)]
1754    pub fn max_iter(mut self, n: u32) -> Self {
1755        self.max_iterations = n;
1756        self
1757    }
1758}
1759/// A phi-node synthesized at a case join point.
1760///
1761/// In SSA, every join point needs a phi-node for variables with different
1762/// values in different branches.  In GVN, phi-nodes get value numbers too.
1763#[derive(Debug, Clone)]
1764pub struct PhiNode {
1765    /// The variable introduced by this phi.
1766    pub var: LcnfVarId,
1767    /// The operands from each incoming branch.
1768    pub operands: Vec<PhiOperand>,
1769    /// The value number assigned to this phi-node.
1770    pub vn: ValueNumber,
1771}
1772impl PhiNode {
1773    pub fn new(var: LcnfVarId, operands: Vec<PhiOperand>, vn: ValueNumber) -> Self {
1774        PhiNode { var, operands, vn }
1775    }
1776    /// Return `true` if all operands have the same value number (trivial phi).
1777    pub fn is_trivial(&self) -> bool {
1778        self.operands.windows(2).all(|w| w[0].vn == w[1].vn)
1779    }
1780    /// If trivial, return the single unique value number.
1781    pub fn trivial_vn(&self) -> Option<ValueNumber> {
1782        if self.is_trivial() {
1783            self.operands.first().map(|op| op.vn)
1784        } else {
1785            None
1786        }
1787    }
1788}
1789/// The known value of a variable for CCP purposes.
1790#[derive(Debug, Clone, PartialEq, Eq)]
1791pub enum KnownConstant {
1792    /// The variable is definitely the given literal.
1793    Lit(LcnfLit),
1794    /// The variable's value is not yet determined.
1795    Top,
1796    /// The variable could be more than one value (conservative).
1797    Bottom,
1798}
1799/// Represents a single GVN-detected redundancy.
1800#[derive(Debug, Clone)]
1801pub struct Redundancy {
1802    /// The redundant variable (the one to be replaced).
1803    pub redundant_var: LcnfVarId,
1804    /// The canonical variable (the one to keep).
1805    pub canonical_var: LcnfVarId,
1806    /// The value number of the equivalence class.
1807    pub vn: ValueNumber,
1808}
1809#[allow(dead_code)]
1810pub struct GVNPassRegistry {
1811    pub(super) configs: Vec<GVNPassConfig>,
1812    pub(super) stats: std::collections::HashMap<String, GVNPassStats>,
1813}
1814impl GVNPassRegistry {
1815    #[allow(dead_code)]
1816    pub fn new() -> Self {
1817        GVNPassRegistry {
1818            configs: Vec::new(),
1819            stats: std::collections::HashMap::new(),
1820        }
1821    }
1822    #[allow(dead_code)]
1823    pub fn register(&mut self, config: GVNPassConfig) {
1824        self.stats
1825            .insert(config.pass_name.clone(), GVNPassStats::new());
1826        self.configs.push(config);
1827    }
1828    #[allow(dead_code)]
1829    pub fn enabled_passes(&self) -> Vec<&GVNPassConfig> {
1830        self.configs.iter().filter(|c| c.enabled).collect()
1831    }
1832    #[allow(dead_code)]
1833    pub fn get_stats(&self, name: &str) -> Option<&GVNPassStats> {
1834        self.stats.get(name)
1835    }
1836    #[allow(dead_code)]
1837    pub fn total_passes(&self) -> usize {
1838        self.configs.len()
1839    }
1840    #[allow(dead_code)]
1841    pub fn enabled_count(&self) -> usize {
1842        self.enabled_passes().len()
1843    }
1844    #[allow(dead_code)]
1845    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1846        if let Some(stats) = self.stats.get_mut(name) {
1847            stats.record_run(changes, time_ms, iter);
1848        }
1849    }
1850}
1851/// A structural key used to check expression equality modulo renaming.
1852///
1853/// Instead of recording variable ids directly, we record their *value
1854/// numbers* so that two syntactically different but semantically equal
1855/// bindings hash to the same key.
1856#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1857pub enum NormExpr {
1858    Lit(u64),
1859    LitStr(String),
1860    Erased,
1861    FVar(ValueNumber),
1862    Proj(String, u32, ValueNumber),
1863    Ctor(String, u32, Vec<NormArg>),
1864    App(NormArg, Vec<NormArg>),
1865    Reset(ValueNumber),
1866    Unknown,
1867}