Skip to main content

oxilean_codegen/opt_specialize/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::lcnf::*;
6use std::collections::{HashMap, HashSet};
7
8use super::functions::*;
9use std::collections::VecDeque;
10
11/// Statistics for the specialization pass
12#[derive(Debug, Clone, Default)]
13pub struct SpecializationStats {
14    /// Number of type-based specializations created
15    pub type_specializations: usize,
16    /// Number of constant-based specializations created
17    pub const_specializations: usize,
18    /// Number of closure specializations created
19    pub closure_specializations: usize,
20    /// Number of call sites redirected to specializations
21    pub call_sites_redirected: usize,
22    /// Total code growth (in instructions)
23    pub total_code_growth: usize,
24    /// Number of specialization opportunities skipped due to budget
25    pub skipped_budget: usize,
26    /// Number of specialization opportunities skipped due to limits
27    pub skipped_limit: usize,
28    /// Number of functions analyzed
29    pub functions_analyzed: usize,
30}
31#[allow(dead_code)]
32pub struct SpecPassRegistry {
33    pub(super) configs: Vec<SpecPassConfig>,
34    pub(super) stats: std::collections::HashMap<String, SpecPassStats>,
35}
36impl SpecPassRegistry {
37    #[allow(dead_code)]
38    pub fn new() -> Self {
39        SpecPassRegistry {
40            configs: Vec::new(),
41            stats: std::collections::HashMap::new(),
42        }
43    }
44    #[allow(dead_code)]
45    pub fn register(&mut self, config: SpecPassConfig) {
46        self.stats
47            .insert(config.pass_name.clone(), SpecPassStats::new());
48        self.configs.push(config);
49    }
50    #[allow(dead_code)]
51    pub fn enabled_passes(&self) -> Vec<&SpecPassConfig> {
52        self.configs.iter().filter(|c| c.enabled).collect()
53    }
54    #[allow(dead_code)]
55    pub fn get_stats(&self, name: &str) -> Option<&SpecPassStats> {
56        self.stats.get(name)
57    }
58    #[allow(dead_code)]
59    pub fn total_passes(&self) -> usize {
60        self.configs.len()
61    }
62    #[allow(dead_code)]
63    pub fn enabled_count(&self) -> usize {
64        self.enabled_passes().len()
65    }
66    #[allow(dead_code)]
67    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
68        if let Some(stats) = self.stats.get_mut(name) {
69            stats.record_run(changes, time_ms, iter);
70        }
71    }
72}
73/// Configuration for the specialization pass
74#[derive(Debug, Clone)]
75pub struct SpecializationConfig {
76    /// Maximum number of specializations per function
77    pub max_specializations: usize,
78    /// Whether to specialize functions that receive closures
79    pub specialize_closures: bool,
80    /// Whether to specialize numeric operations (Nat -> u64, etc.)
81    pub specialize_numerics: bool,
82    /// Maximum function size to consider for specialization (in instructions)
83    pub size_threshold: usize,
84    /// Maximum total code growth factor (1.0 = no growth, 2.0 = double)
85    pub growth_factor: f64,
86    /// Whether to handle recursive specializations
87    pub allow_recursive: bool,
88    /// Whether to specialize on type parameters
89    pub specialize_type_params: bool,
90    /// Maximum depth for recursive specialization
91    pub max_recursive_depth: usize,
92}
93/// A specialized version of a function
94#[derive(Debug, Clone)]
95pub struct SpecializedDecl {
96    /// The specialization key
97    pub key: SpecializationKey,
98    /// The specialized function declaration
99    pub decl: LcnfFunDecl,
100    /// How much code growth this specialization added (in instructions)
101    pub code_growth: usize,
102    /// Whether this was created from a recursive function
103    pub from_recursive: bool,
104}
105#[allow(dead_code)]
106#[derive(Debug, Clone)]
107pub struct SpecCacheEntry {
108    pub key: String,
109    pub data: Vec<u8>,
110    pub timestamp: u64,
111    pub valid: bool,
112}
113/// A closure argument in a specialization key
114#[derive(Debug, Clone, PartialEq, Eq, Hash)]
115pub struct SpecClosureArg {
116    /// The name of the known function being passed
117    pub known_fn: Option<String>,
118    /// Parameter index
119    pub param_idx: usize,
120}
121/// Main specialization pass
122pub struct SpecializationPass {
123    pub(super) config: SpecializationConfig,
124    pub(super) cache: SpecializationCache,
125    pub(super) stats: SpecializationStats,
126    pub(super) next_id: u64,
127    /// Map from variable IDs to known function names
128    pub(super) var_to_fn: HashMap<LcnfVarId, String>,
129    /// Original sizes of functions for growth budget
130    pub(super) original_sizes: HashMap<String, usize>,
131}
132impl SpecializationPass {
133    /// Create a new specialization pass
134    pub fn new(config: SpecializationConfig) -> Self {
135        SpecializationPass {
136            config,
137            cache: SpecializationCache::new(),
138            stats: SpecializationStats::default(),
139            next_id: 5000,
140            var_to_fn: HashMap::new(),
141            original_sizes: HashMap::new(),
142        }
143    }
144    /// Get the optimization statistics
145    pub fn stats(&self) -> &SpecializationStats {
146        &self.stats
147    }
148    /// Generate a fresh variable ID
149    pub(super) fn fresh_id(&mut self) -> LcnfVarId {
150        let id = self.next_id;
151        self.next_id += 1;
152        LcnfVarId(id)
153    }
154    /// Run the specialization pass on a module
155    pub(super) fn run(&mut self, module: &mut LcnfModule) {
156        for decl in &module.fun_decls {
157            let size = count_instructions(&decl.body);
158            self.original_sizes.insert(decl.name.clone(), size);
159        }
160        let decl_names: HashSet<String> = module.fun_decls.iter().map(|d| d.name.clone()).collect();
161        let mut all_sites: Vec<(String, Vec<SpecCallSite>)> = Vec::new();
162        for decl in &module.fun_decls {
163            self.stats.functions_analyzed += 1;
164            let known_consts: HashMap<LcnfVarId, LcnfLit> = HashMap::new();
165            let sites =
166                find_specialization_sites(&decl.body, &known_consts, &self.var_to_fn, &decl_names);
167            if !sites.is_empty() {
168                all_sites.push((decl.name.clone(), sites));
169            }
170        }
171        let mut new_decls: Vec<LcnfFunDecl> = Vec::new();
172        let total_original_size: usize = self.original_sizes.values().sum();
173        let budget = (total_original_size as f64 * self.config.growth_factor) as usize;
174        for (_caller, sites) in &all_sites {
175            for site in sites {
176                if self.cache.total_growth >= budget {
177                    self.stats.skipped_budget += 1;
178                    continue;
179                }
180                if self.cache.specialization_count(&site.callee) >= self.config.max_specializations
181                {
182                    self.stats.skipped_limit += 1;
183                    continue;
184                }
185                let key = self.build_key(site);
186                if key.is_trivial() {
187                    continue;
188                }
189                if self.cache.lookup(&key).is_some() {
190                    continue;
191                }
192                if let Some(original) = module.fun_decls.iter().find(|d| d.name == site.callee) {
193                    let original_size = count_instructions(&original.body);
194                    if original_size > self.config.size_threshold {
195                        self.stats.skipped_budget += 1;
196                        continue;
197                    }
198                    if let Some(spec_decl) = self.create_specialization(original, &key) {
199                        let growth = count_instructions(&spec_decl.decl.body);
200                        self.cache
201                            .insert(key.clone(), spec_decl.decl.name.clone(), growth);
202                        self.update_stats(&key);
203                        new_decls.push(spec_decl.decl);
204                    }
205                }
206            }
207        }
208        module.fun_decls.extend(new_decls);
209        self.redirect_call_sites(module);
210    }
211    /// Build a specialization key from a call site
212    pub(super) fn build_key(&self, site: &SpecCallSite) -> SpecializationKey {
213        let type_args = if site.type_args.is_empty() {
214            vec![]
215        } else {
216            site.type_args.clone()
217        };
218        SpecializationKey {
219            original: site.callee.clone(),
220            type_args,
221            const_args: site.const_args.clone(),
222            closure_args: site.closure_args.clone(),
223        }
224    }
225    /// Create a specialized version of a function
226    pub(super) fn create_specialization(
227        &mut self,
228        original: &LcnfFunDecl,
229        key: &SpecializationKey,
230    ) -> Option<SpecializedDecl> {
231        if !self.config.allow_recursive && original.is_recursive {
232            return None;
233        }
234        let spec_name = key.mangled_name();
235        let mut spec_body = original.body.clone();
236        let mut spec_params = original.params.clone();
237        for (i, ca) in key.const_args.iter().enumerate() {
238            match ca {
239                SpecConstArg::Nat(n) => {
240                    if i < spec_params.len() {
241                        let param_id = spec_params[i].id;
242                        self.substitute_constant(&mut spec_body, param_id, &LcnfLit::Nat(*n));
243                        spec_params[i].erased = true;
244                    }
245                }
246                SpecConstArg::Str(s) => {
247                    if i < spec_params.len() {
248                        let param_id = spec_params[i].id;
249                        self.substitute_constant(
250                            &mut spec_body,
251                            param_id,
252                            &LcnfLit::Str(s.clone()),
253                        );
254                        spec_params[i].erased = true;
255                    }
256                }
257                SpecConstArg::Unknown => {}
258            }
259        }
260        for (i, ta) in key.type_args.iter().enumerate() {
261            if let SpecTypeArg::Concrete(ty) = ta {
262                if i < spec_params.len() {
263                    spec_params[i].ty = ty.clone();
264                }
265            }
266        }
267        let active_params: Vec<LcnfParam> = spec_params.into_iter().filter(|p| !p.erased).collect();
268        let spec_decl = LcnfFunDecl {
269            name: spec_name.clone(),
270            original_name: original.original_name.clone(),
271            params: active_params,
272            ret_type: original.ret_type.clone(),
273            body: spec_body,
274            is_recursive: original.is_recursive,
275            is_lifted: true,
276            inline_cost: original.inline_cost,
277        };
278        let code_growth = count_instructions(&spec_decl.body);
279        Some(SpecializedDecl {
280            key: key.clone(),
281            decl: spec_decl,
282            code_growth,
283            from_recursive: original.is_recursive,
284        })
285    }
286    /// Substitute a constant value for a variable in an expression
287    pub(super) fn substitute_constant(&self, expr: &mut LcnfExpr, var: LcnfVarId, lit: &LcnfLit) {
288        match expr {
289            LcnfExpr::Let {
290                id, value, body, ..
291            } => {
292                self.substitute_constant_in_value(value, var, lit);
293                if *id != var {
294                    self.substitute_constant(body, var, lit);
295                }
296            }
297            LcnfExpr::Case {
298                scrutinee,
299                alts,
300                default,
301                ..
302            } => {
303                if *scrutinee == var {}
304                for alt in alts.iter_mut() {
305                    let shadows = alt.params.iter().any(|p| p.id == var);
306                    if !shadows {
307                        self.substitute_constant(&mut alt.body, var, lit);
308                    }
309                }
310                if let Some(def) = default {
311                    self.substitute_constant(def, var, lit);
312                }
313            }
314            LcnfExpr::Return(arg) => {
315                self.substitute_arg(arg, var, lit);
316            }
317            LcnfExpr::TailCall(func, args) => {
318                self.substitute_arg(func, var, lit);
319                for a in args.iter_mut() {
320                    self.substitute_arg(a, var, lit);
321                }
322            }
323            LcnfExpr::Unreachable => {}
324        }
325    }
326    /// Substitute in a let-value
327    pub(super) fn substitute_constant_in_value(
328        &self,
329        value: &mut LcnfLetValue,
330        var: LcnfVarId,
331        lit: &LcnfLit,
332    ) {
333        match value {
334            LcnfLetValue::App(func, args) => {
335                self.substitute_arg(func, var, lit);
336                for a in args.iter_mut() {
337                    self.substitute_arg(a, var, lit);
338                }
339            }
340            LcnfLetValue::Proj(_, _, obj) => if *obj == var {},
341            LcnfLetValue::Ctor(_, _, args) => {
342                for a in args.iter_mut() {
343                    self.substitute_arg(a, var, lit);
344                }
345            }
346            LcnfLetValue::FVar(v) => {
347                if *v == var {
348                    *value = LcnfLetValue::Lit(lit.clone());
349                }
350            }
351            LcnfLetValue::Lit(_)
352            | LcnfLetValue::Erased
353            | LcnfLetValue::Reset(_)
354            | LcnfLetValue::Reuse(_, _, _, _) => {}
355        }
356    }
357    /// Substitute a variable reference in an argument
358    pub(super) fn substitute_arg(&self, arg: &mut LcnfArg, var: LcnfVarId, lit: &LcnfLit) {
359        if let LcnfArg::Var(v) = arg {
360            if *v == var {
361                *arg = LcnfArg::Lit(lit.clone());
362            }
363        }
364    }
365    /// Update statistics based on the specialization key
366    pub(super) fn update_stats(&mut self, key: &SpecializationKey) {
367        let has_type = key
368            .type_args
369            .iter()
370            .any(|a| matches!(a, SpecTypeArg::Concrete(_)));
371        let has_const = key
372            .const_args
373            .iter()
374            .any(|a| !matches!(a, SpecConstArg::Unknown));
375        let has_closure = key.closure_args.iter().any(|a| a.known_fn.is_some());
376        if has_type {
377            self.stats.type_specializations += 1;
378        }
379        if has_const {
380            self.stats.const_specializations += 1;
381        }
382        if has_closure {
383            self.stats.closure_specializations += 1;
384        }
385    }
386    /// Redirect call sites to their specialized versions
387    pub(super) fn redirect_call_sites(&mut self, module: &mut LcnfModule) {
388        let redirects: HashMap<SpecializationKey, String> = self.cache.entries.clone();
389        if redirects.is_empty() {
390            return;
391        }
392        for decl in &mut module.fun_decls {
393            let redirected = self.redirect_in_expr(&mut decl.body, &redirects);
394            self.stats.call_sites_redirected += redirected;
395        }
396    }
397    /// Redirect call sites in an expression
398    pub(super) fn redirect_in_expr(
399        &self,
400        expr: &mut LcnfExpr,
401        redirects: &HashMap<SpecializationKey, String>,
402    ) -> usize {
403        let mut count = 0;
404        let mut local_consts: HashMap<LcnfVarId, LcnfLit> = HashMap::new();
405        let mut local_fn_map: HashMap<LcnfVarId, String> = self.var_to_fn.clone();
406        self.redirect_inner(
407            expr,
408            redirects,
409            &mut local_consts,
410            &mut local_fn_map,
411            &mut count,
412        );
413        count
414    }
415    /// Inner recursive helper for call-site redirection.
416    pub(super) fn redirect_inner(
417        &self,
418        expr: &mut LcnfExpr,
419        redirects: &HashMap<SpecializationKey, String>,
420        local_consts: &mut HashMap<LcnfVarId, LcnfLit>,
421        local_fn_map: &mut HashMap<LcnfVarId, String>,
422        count: &mut usize,
423    ) {
424        match expr {
425            LcnfExpr::Let {
426                id, value, body, ..
427            } => {
428                if let LcnfLetValue::App(func, args) = value {
429                    if let LcnfArg::Var(v) = func {
430                        if let Some(fn_name) = local_fn_map.get(v).cloned() {
431                            let const_args = Self::build_const_args(args, local_consts);
432                            let closure_args = Self::build_closure_args(args, local_fn_map);
433                            for (key, spec_name) in redirects {
434                                if key.original == fn_name
435                                    && key.const_args == const_args
436                                    && (key.closure_args.is_empty()
437                                        || key.closure_args == closure_args)
438                                {
439                                    if let Some(spec_var) = local_fn_map
440                                        .iter()
441                                        .find(|(_, name)| *name == spec_name)
442                                        .map(|(var, _)| *var)
443                                    {
444                                        *func = LcnfArg::Var(spec_var);
445                                        *count += 1;
446                                    }
447                                    break;
448                                }
449                            }
450                        }
451                    }
452                }
453                if let LcnfLetValue::Lit(lit) = value {
454                    local_consts.insert(*id, lit.clone());
455                }
456                if let LcnfLetValue::FVar(v) = value {
457                    if let Some(fn_name) = local_fn_map.get(v).cloned() {
458                        local_fn_map.insert(*id, fn_name);
459                    }
460                }
461                self.redirect_inner(body, redirects, local_consts, local_fn_map, count);
462            }
463            LcnfExpr::Case { alts, default, .. } => {
464                for alt in alts.iter_mut() {
465                    let mut alt_consts = local_consts.clone();
466                    let mut alt_fn_map = local_fn_map.clone();
467                    self.redirect_inner(
468                        &mut alt.body,
469                        redirects,
470                        &mut alt_consts,
471                        &mut alt_fn_map,
472                        count,
473                    );
474                }
475                if let Some(def) = default {
476                    let mut def_consts = local_consts.clone();
477                    let mut def_fn_map = local_fn_map.clone();
478                    self.redirect_inner(def, redirects, &mut def_consts, &mut def_fn_map, count);
479                }
480            }
481            LcnfExpr::TailCall(func, args) => {
482                if let LcnfArg::Var(v) = func {
483                    if let Some(fn_name) = local_fn_map.get(v).cloned() {
484                        let const_args = Self::build_const_args(args, local_consts);
485                        let closure_args = Self::build_closure_args(args, local_fn_map);
486                        for (key, spec_name) in redirects {
487                            if key.original == fn_name
488                                && key.const_args == const_args
489                                && (key.closure_args.is_empty() || key.closure_args == closure_args)
490                            {
491                                if let Some(spec_var) = local_fn_map
492                                    .iter()
493                                    .find(|(_, name)| *name == spec_name)
494                                    .map(|(var, _)| *var)
495                                {
496                                    *func = LcnfArg::Var(spec_var);
497                                    *count += 1;
498                                }
499                                break;
500                            }
501                        }
502                    }
503                }
504            }
505            LcnfExpr::Return(_) | LcnfExpr::Unreachable => {}
506        }
507    }
508    /// Build SpecConstArg list from call-site arguments.
509    pub(super) fn build_const_args(
510        args: &[LcnfArg],
511        local_consts: &HashMap<LcnfVarId, LcnfLit>,
512    ) -> Vec<SpecConstArg> {
513        args.iter()
514            .map(|arg| match arg {
515                LcnfArg::Lit(LcnfLit::Nat(n)) => SpecConstArg::Nat(*n),
516                LcnfArg::Lit(LcnfLit::Str(s)) => SpecConstArg::Str(s.clone()),
517                LcnfArg::Var(v) => match local_consts.get(v) {
518                    Some(LcnfLit::Nat(n)) => SpecConstArg::Nat(*n),
519                    Some(LcnfLit::Int(_)) => SpecConstArg::Unknown,
520                    Some(LcnfLit::Str(s)) => SpecConstArg::Str(s.clone()),
521                    None => SpecConstArg::Unknown,
522                },
523                _ => SpecConstArg::Unknown,
524            })
525            .collect()
526    }
527    /// Build SpecClosureArg list from call-site arguments.
528    pub(super) fn build_closure_args(
529        args: &[LcnfArg],
530        local_fn_map: &HashMap<LcnfVarId, String>,
531    ) -> Vec<SpecClosureArg> {
532        args.iter()
533            .enumerate()
534            .map(|(i, arg)| SpecClosureArg {
535                known_fn: match arg {
536                    LcnfArg::Var(v) => local_fn_map.get(v).cloned(),
537                    _ => None,
538                },
539                param_idx: i,
540            })
541            .collect()
542    }
543}
544/// Pass registry for SpecExt.
545#[allow(dead_code)]
546#[derive(Debug, Default)]
547pub struct SpecExtPassRegistry {
548    pub(super) configs: Vec<SpecExtPassConfig>,
549    pub(super) stats: Vec<SpecExtPassStats>,
550}
551impl SpecExtPassRegistry {
552    #[allow(dead_code)]
553    pub fn new() -> Self {
554        Self::default()
555    }
556    #[allow(dead_code)]
557    pub fn register(&mut self, c: SpecExtPassConfig) {
558        self.stats.push(SpecExtPassStats::new());
559        self.configs.push(c);
560    }
561    #[allow(dead_code)]
562    pub fn len(&self) -> usize {
563        self.configs.len()
564    }
565    #[allow(dead_code)]
566    pub fn is_empty(&self) -> bool {
567        self.configs.is_empty()
568    }
569    #[allow(dead_code)]
570    pub fn get(&self, i: usize) -> Option<&SpecExtPassConfig> {
571        self.configs.get(i)
572    }
573    #[allow(dead_code)]
574    pub fn get_stats(&self, i: usize) -> Option<&SpecExtPassStats> {
575        self.stats.get(i)
576    }
577    #[allow(dead_code)]
578    pub fn enabled_passes(&self) -> Vec<&SpecExtPassConfig> {
579        self.configs.iter().filter(|c| c.enabled).collect()
580    }
581    #[allow(dead_code)]
582    pub fn passes_in_phase(&self, ph: &SpecExtPassPhase) -> Vec<&SpecExtPassConfig> {
583        self.configs
584            .iter()
585            .filter(|c| c.enabled && &c.phase == ph)
586            .collect()
587    }
588    #[allow(dead_code)]
589    pub fn total_nodes_visited(&self) -> usize {
590        self.stats.iter().map(|s| s.nodes_visited).sum()
591    }
592    #[allow(dead_code)]
593    pub fn any_changed(&self) -> bool {
594        self.stats.iter().any(|s| s.changed)
595    }
596}
597/// Analysis cache for SpecExt.
598#[allow(dead_code)]
599#[derive(Debug)]
600pub struct SpecExtCache {
601    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
602    pub(super) cap: usize,
603    pub(super) total_hits: u64,
604    pub(super) total_misses: u64,
605}
606impl SpecExtCache {
607    #[allow(dead_code)]
608    pub fn new(cap: usize) -> Self {
609        Self {
610            entries: Vec::new(),
611            cap,
612            total_hits: 0,
613            total_misses: 0,
614        }
615    }
616    #[allow(dead_code)]
617    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
618        for e in self.entries.iter_mut() {
619            if e.0 == key && e.2 {
620                e.3 += 1;
621                self.total_hits += 1;
622                return Some(&e.1);
623            }
624        }
625        self.total_misses += 1;
626        None
627    }
628    #[allow(dead_code)]
629    pub fn put(&mut self, key: u64, data: Vec<u8>) {
630        if self.entries.len() >= self.cap {
631            self.entries.retain(|e| e.2);
632            if self.entries.len() >= self.cap {
633                self.entries.remove(0);
634            }
635        }
636        self.entries.push((key, data, true, 0));
637    }
638    #[allow(dead_code)]
639    pub fn invalidate(&mut self) {
640        for e in self.entries.iter_mut() {
641            e.2 = false;
642        }
643    }
644    #[allow(dead_code)]
645    pub fn hit_rate(&self) -> f64 {
646        let t = self.total_hits + self.total_misses;
647        if t == 0 {
648            0.0
649        } else {
650            self.total_hits as f64 / t as f64
651        }
652    }
653    #[allow(dead_code)]
654    pub fn live_count(&self) -> usize {
655        self.entries.iter().filter(|e| e.2).count()
656    }
657}
658/// Dependency graph for SpecExt.
659#[allow(dead_code)]
660#[derive(Debug, Clone)]
661pub struct SpecExtDepGraph {
662    pub(super) n: usize,
663    pub(super) adj: Vec<Vec<usize>>,
664    pub(super) rev: Vec<Vec<usize>>,
665    pub(super) edge_count: usize,
666}
667impl SpecExtDepGraph {
668    #[allow(dead_code)]
669    pub fn new(n: usize) -> Self {
670        Self {
671            n,
672            adj: vec![Vec::new(); n],
673            rev: vec![Vec::new(); n],
674            edge_count: 0,
675        }
676    }
677    #[allow(dead_code)]
678    pub fn add_edge(&mut self, from: usize, to: usize) {
679        if from < self.n && to < self.n {
680            if !self.adj[from].contains(&to) {
681                self.adj[from].push(to);
682                self.rev[to].push(from);
683                self.edge_count += 1;
684            }
685        }
686    }
687    #[allow(dead_code)]
688    pub fn succs(&self, n: usize) -> &[usize] {
689        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
690    }
691    #[allow(dead_code)]
692    pub fn preds(&self, n: usize) -> &[usize] {
693        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
694    }
695    #[allow(dead_code)]
696    pub fn topo_sort(&self) -> Option<Vec<usize>> {
697        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
698        let mut q: std::collections::VecDeque<usize> =
699            (0..self.n).filter(|&i| deg[i] == 0).collect();
700        let mut out = Vec::with_capacity(self.n);
701        while let Some(u) = q.pop_front() {
702            out.push(u);
703            for &v in &self.adj[u] {
704                deg[v] -= 1;
705                if deg[v] == 0 {
706                    q.push_back(v);
707                }
708            }
709        }
710        if out.len() == self.n {
711            Some(out)
712        } else {
713            None
714        }
715    }
716    #[allow(dead_code)]
717    pub fn has_cycle(&self) -> bool {
718        self.topo_sort().is_none()
719    }
720    #[allow(dead_code)]
721    pub fn reachable(&self, start: usize) -> Vec<usize> {
722        let mut vis = vec![false; self.n];
723        let mut stk = vec![start];
724        let mut out = Vec::new();
725        while let Some(u) = stk.pop() {
726            if u < self.n && !vis[u] {
727                vis[u] = true;
728                out.push(u);
729                for &v in &self.adj[u] {
730                    if !vis[v] {
731                        stk.push(v);
732                    }
733                }
734            }
735        }
736        out
737    }
738    #[allow(dead_code)]
739    pub fn scc(&self) -> Vec<Vec<usize>> {
740        let mut visited = vec![false; self.n];
741        let mut order = Vec::new();
742        for i in 0..self.n {
743            if !visited[i] {
744                let mut stk = vec![(i, 0usize)];
745                while let Some((u, idx)) = stk.last_mut() {
746                    if !visited[*u] {
747                        visited[*u] = true;
748                    }
749                    if *idx < self.adj[*u].len() {
750                        let v = self.adj[*u][*idx];
751                        *idx += 1;
752                        if !visited[v] {
753                            stk.push((v, 0));
754                        }
755                    } else {
756                        order.push(*u);
757                        stk.pop();
758                    }
759                }
760            }
761        }
762        let mut comp = vec![usize::MAX; self.n];
763        let mut components: Vec<Vec<usize>> = Vec::new();
764        for &start in order.iter().rev() {
765            if comp[start] == usize::MAX {
766                let cid = components.len();
767                let mut component = Vec::new();
768                let mut stk = vec![start];
769                while let Some(u) = stk.pop() {
770                    if comp[u] == usize::MAX {
771                        comp[u] = cid;
772                        component.push(u);
773                        for &v in &self.rev[u] {
774                            if comp[v] == usize::MAX {
775                                stk.push(v);
776                            }
777                        }
778                    }
779                }
780                components.push(component);
781            }
782        }
783        components
784    }
785    #[allow(dead_code)]
786    pub fn node_count(&self) -> usize {
787        self.n
788    }
789    #[allow(dead_code)]
790    pub fn edge_count(&self) -> usize {
791        self.edge_count
792    }
793}
794/// Dominator tree for SpecExt.
795#[allow(dead_code)]
796#[derive(Debug, Clone)]
797pub struct SpecExtDomTree {
798    pub(super) idom: Vec<Option<usize>>,
799    pub(super) children: Vec<Vec<usize>>,
800    pub(super) depth: Vec<usize>,
801}
802impl SpecExtDomTree {
803    #[allow(dead_code)]
804    pub fn new(n: usize) -> Self {
805        Self {
806            idom: vec![None; n],
807            children: vec![Vec::new(); n],
808            depth: vec![0; n],
809        }
810    }
811    #[allow(dead_code)]
812    pub fn set_idom(&mut self, node: usize, dom: usize) {
813        if node < self.idom.len() {
814            self.idom[node] = Some(dom);
815            if dom < self.children.len() {
816                self.children[dom].push(node);
817            }
818            self.depth[node] = if dom < self.depth.len() {
819                self.depth[dom] + 1
820            } else {
821                1
822            };
823        }
824    }
825    #[allow(dead_code)]
826    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
827        if a == b {
828            return true;
829        }
830        let n = self.idom.len();
831        for _ in 0..n {
832            match self.idom.get(b).copied().flatten() {
833                None => return false,
834                Some(p) if p == a => return true,
835                Some(p) if p == b => return false,
836                Some(p) => b = p,
837            }
838        }
839        false
840    }
841    #[allow(dead_code)]
842    pub fn children_of(&self, n: usize) -> &[usize] {
843        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
844    }
845    #[allow(dead_code)]
846    pub fn depth_of(&self, n: usize) -> usize {
847        self.depth.get(n).copied().unwrap_or(0)
848    }
849    #[allow(dead_code)]
850    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
851        let n = self.idom.len();
852        for _ in 0..(2 * n) {
853            if a == b {
854                return a;
855            }
856            if self.depth_of(a) > self.depth_of(b) {
857                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
858            } else {
859                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
860            }
861        }
862        0
863    }
864}
865#[allow(dead_code)]
866#[derive(Debug, Clone, PartialEq)]
867pub enum SpecPassPhase {
868    Analysis,
869    Transformation,
870    Verification,
871    Cleanup,
872}
873impl SpecPassPhase {
874    #[allow(dead_code)]
875    pub fn name(&self) -> &str {
876        match self {
877            SpecPassPhase::Analysis => "analysis",
878            SpecPassPhase::Transformation => "transformation",
879            SpecPassPhase::Verification => "verification",
880            SpecPassPhase::Cleanup => "cleanup",
881        }
882    }
883    #[allow(dead_code)]
884    pub fn is_modifying(&self) -> bool {
885        matches!(self, SpecPassPhase::Transformation | SpecPassPhase::Cleanup)
886    }
887}
888/// Pass execution phase for SpecExt.
889#[allow(dead_code)]
890#[derive(Debug, Clone, PartialEq, Eq, Hash)]
891pub enum SpecExtPassPhase {
892    Early,
893    Middle,
894    Late,
895    Finalize,
896}
897impl SpecExtPassPhase {
898    #[allow(dead_code)]
899    pub fn is_early(&self) -> bool {
900        matches!(self, Self::Early)
901    }
902    #[allow(dead_code)]
903    pub fn is_middle(&self) -> bool {
904        matches!(self, Self::Middle)
905    }
906    #[allow(dead_code)]
907    pub fn is_late(&self) -> bool {
908        matches!(self, Self::Late)
909    }
910    #[allow(dead_code)]
911    pub fn is_finalize(&self) -> bool {
912        matches!(self, Self::Finalize)
913    }
914    #[allow(dead_code)]
915    pub fn order(&self) -> u32 {
916        match self {
917            Self::Early => 0,
918            Self::Middle => 1,
919            Self::Late => 2,
920            Self::Finalize => 3,
921        }
922    }
923    #[allow(dead_code)]
924    pub fn from_order(n: u32) -> Option<Self> {
925        match n {
926            0 => Some(Self::Early),
927            1 => Some(Self::Middle),
928            2 => Some(Self::Late),
929            3 => Some(Self::Finalize),
930            _ => None,
931        }
932    }
933}
934/// Constant folding helper for SpecExt.
935#[allow(dead_code)]
936#[derive(Debug, Clone, Default)]
937pub struct SpecExtConstFolder {
938    pub(super) folds: usize,
939    pub(super) failures: usize,
940    pub(super) enabled: bool,
941}
942impl SpecExtConstFolder {
943    #[allow(dead_code)]
944    pub fn new() -> Self {
945        Self {
946            folds: 0,
947            failures: 0,
948            enabled: true,
949        }
950    }
951    #[allow(dead_code)]
952    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
953        self.folds += 1;
954        a.checked_add(b)
955    }
956    #[allow(dead_code)]
957    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
958        self.folds += 1;
959        a.checked_sub(b)
960    }
961    #[allow(dead_code)]
962    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
963        self.folds += 1;
964        a.checked_mul(b)
965    }
966    #[allow(dead_code)]
967    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
968        if b == 0 {
969            self.failures += 1;
970            None
971        } else {
972            self.folds += 1;
973            a.checked_div(b)
974        }
975    }
976    #[allow(dead_code)]
977    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
978        if b == 0 {
979            self.failures += 1;
980            None
981        } else {
982            self.folds += 1;
983            a.checked_rem(b)
984        }
985    }
986    #[allow(dead_code)]
987    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
988        self.folds += 1;
989        a.checked_neg()
990    }
991    #[allow(dead_code)]
992    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
993        if s >= 64 {
994            self.failures += 1;
995            None
996        } else {
997            self.folds += 1;
998            a.checked_shl(s)
999        }
1000    }
1001    #[allow(dead_code)]
1002    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1003        if s >= 64 {
1004            self.failures += 1;
1005            None
1006        } else {
1007            self.folds += 1;
1008            a.checked_shr(s)
1009        }
1010    }
1011    #[allow(dead_code)]
1012    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
1013        self.folds += 1;
1014        a & b
1015    }
1016    #[allow(dead_code)]
1017    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1018        self.folds += 1;
1019        a | b
1020    }
1021    #[allow(dead_code)]
1022    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1023        self.folds += 1;
1024        a ^ b
1025    }
1026    #[allow(dead_code)]
1027    pub fn not_i64(&mut self, a: i64) -> i64 {
1028        self.folds += 1;
1029        !a
1030    }
1031    #[allow(dead_code)]
1032    pub fn fold_count(&self) -> usize {
1033        self.folds
1034    }
1035    #[allow(dead_code)]
1036    pub fn failure_count(&self) -> usize {
1037        self.failures
1038    }
1039    #[allow(dead_code)]
1040    pub fn enable(&mut self) {
1041        self.enabled = true;
1042    }
1043    #[allow(dead_code)]
1044    pub fn disable(&mut self) {
1045        self.enabled = false;
1046    }
1047    #[allow(dead_code)]
1048    pub fn is_enabled(&self) -> bool {
1049        self.enabled
1050    }
1051}
1052#[allow(dead_code)]
1053#[derive(Debug, Clone, Default)]
1054pub struct SpecPassStats {
1055    pub total_runs: u32,
1056    pub successful_runs: u32,
1057    pub total_changes: u64,
1058    pub time_ms: u64,
1059    pub iterations_used: u32,
1060}
1061impl SpecPassStats {
1062    #[allow(dead_code)]
1063    pub fn new() -> Self {
1064        Self::default()
1065    }
1066    #[allow(dead_code)]
1067    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1068        self.total_runs += 1;
1069        self.successful_runs += 1;
1070        self.total_changes += changes;
1071        self.time_ms += time_ms;
1072        self.iterations_used = iterations;
1073    }
1074    #[allow(dead_code)]
1075    pub fn average_changes_per_run(&self) -> f64 {
1076        if self.total_runs == 0 {
1077            return 0.0;
1078        }
1079        self.total_changes as f64 / self.total_runs as f64
1080    }
1081    #[allow(dead_code)]
1082    pub fn success_rate(&self) -> f64 {
1083        if self.total_runs == 0 {
1084            return 0.0;
1085        }
1086        self.successful_runs as f64 / self.total_runs as f64
1087    }
1088    #[allow(dead_code)]
1089    pub fn format_summary(&self) -> String {
1090        format!(
1091            "Runs: {}/{}, Changes: {}, Time: {}ms",
1092            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1093        )
1094    }
1095}
1096#[allow(dead_code)]
1097pub struct SpecConstantFoldingHelper;
1098impl SpecConstantFoldingHelper {
1099    #[allow(dead_code)]
1100    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1101        a.checked_add(b)
1102    }
1103    #[allow(dead_code)]
1104    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1105        a.checked_sub(b)
1106    }
1107    #[allow(dead_code)]
1108    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1109        a.checked_mul(b)
1110    }
1111    #[allow(dead_code)]
1112    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1113        if b == 0 {
1114            None
1115        } else {
1116            a.checked_div(b)
1117        }
1118    }
1119    #[allow(dead_code)]
1120    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1121        a + b
1122    }
1123    #[allow(dead_code)]
1124    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1125        a * b
1126    }
1127    #[allow(dead_code)]
1128    pub fn fold_neg_i64(a: i64) -> Option<i64> {
1129        a.checked_neg()
1130    }
1131    #[allow(dead_code)]
1132    pub fn fold_not_bool(a: bool) -> bool {
1133        !a
1134    }
1135    #[allow(dead_code)]
1136    pub fn fold_and_bool(a: bool, b: bool) -> bool {
1137        a && b
1138    }
1139    #[allow(dead_code)]
1140    pub fn fold_or_bool(a: bool, b: bool) -> bool {
1141        a || b
1142    }
1143    #[allow(dead_code)]
1144    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1145        a.checked_shl(b)
1146    }
1147    #[allow(dead_code)]
1148    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1149        a.checked_shr(b)
1150    }
1151    #[allow(dead_code)]
1152    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1153        if b == 0 {
1154            None
1155        } else {
1156            Some(a % b)
1157        }
1158    }
1159    #[allow(dead_code)]
1160    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1161        a & b
1162    }
1163    #[allow(dead_code)]
1164    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1165        a | b
1166    }
1167    #[allow(dead_code)]
1168    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1169        a ^ b
1170    }
1171    #[allow(dead_code)]
1172    pub fn fold_bitnot_i64(a: i64) -> i64 {
1173        !a
1174    }
1175}
1176/// A constant argument in a specialization key
1177#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1178pub enum SpecConstArg {
1179    /// Known natural number
1180    Nat(u64),
1181    /// Known string
1182    Str(String),
1183    /// Not a known constant
1184    Unknown,
1185}
1186#[allow(dead_code)]
1187#[derive(Debug, Clone)]
1188pub struct SpecDominatorTree {
1189    pub idom: Vec<Option<u32>>,
1190    pub dom_children: Vec<Vec<u32>>,
1191    pub dom_depth: Vec<u32>,
1192}
1193impl SpecDominatorTree {
1194    #[allow(dead_code)]
1195    pub fn new(size: usize) -> Self {
1196        SpecDominatorTree {
1197            idom: vec![None; size],
1198            dom_children: vec![Vec::new(); size],
1199            dom_depth: vec![0; size],
1200        }
1201    }
1202    #[allow(dead_code)]
1203    pub fn set_idom(&mut self, node: usize, idom: u32) {
1204        self.idom[node] = Some(idom);
1205    }
1206    #[allow(dead_code)]
1207    pub fn dominates(&self, a: usize, b: usize) -> bool {
1208        if a == b {
1209            return true;
1210        }
1211        let mut cur = b;
1212        loop {
1213            match self.idom[cur] {
1214                Some(parent) if parent as usize == a => return true,
1215                Some(parent) if parent as usize == cur => return false,
1216                Some(parent) => cur = parent as usize,
1217                None => return false,
1218            }
1219        }
1220    }
1221    #[allow(dead_code)]
1222    pub fn depth(&self, node: usize) -> u32 {
1223        self.dom_depth.get(node).copied().unwrap_or(0)
1224    }
1225}
1226#[allow(dead_code)]
1227#[derive(Debug, Clone)]
1228pub struct SpecPassConfig {
1229    pub phase: SpecPassPhase,
1230    pub enabled: bool,
1231    pub max_iterations: u32,
1232    pub debug_output: bool,
1233    pub pass_name: String,
1234}
1235impl SpecPassConfig {
1236    #[allow(dead_code)]
1237    pub fn new(name: impl Into<String>, phase: SpecPassPhase) -> Self {
1238        SpecPassConfig {
1239            phase,
1240            enabled: true,
1241            max_iterations: 10,
1242            debug_output: false,
1243            pass_name: name.into(),
1244        }
1245    }
1246    #[allow(dead_code)]
1247    pub fn disabled(mut self) -> Self {
1248        self.enabled = false;
1249        self
1250    }
1251    #[allow(dead_code)]
1252    pub fn with_debug(mut self) -> Self {
1253        self.debug_output = true;
1254        self
1255    }
1256    #[allow(dead_code)]
1257    pub fn max_iter(mut self, n: u32) -> Self {
1258        self.max_iterations = n;
1259        self
1260    }
1261}
1262#[allow(dead_code)]
1263#[derive(Debug, Clone)]
1264pub struct SpecLivenessInfo {
1265    pub live_in: Vec<std::collections::HashSet<u32>>,
1266    pub live_out: Vec<std::collections::HashSet<u32>>,
1267    pub defs: Vec<std::collections::HashSet<u32>>,
1268    pub uses: Vec<std::collections::HashSet<u32>>,
1269}
1270impl SpecLivenessInfo {
1271    #[allow(dead_code)]
1272    pub fn new(block_count: usize) -> Self {
1273        SpecLivenessInfo {
1274            live_in: vec![std::collections::HashSet::new(); block_count],
1275            live_out: vec![std::collections::HashSet::new(); block_count],
1276            defs: vec![std::collections::HashSet::new(); block_count],
1277            uses: vec![std::collections::HashSet::new(); block_count],
1278        }
1279    }
1280    #[allow(dead_code)]
1281    pub fn add_def(&mut self, block: usize, var: u32) {
1282        if block < self.defs.len() {
1283            self.defs[block].insert(var);
1284        }
1285    }
1286    #[allow(dead_code)]
1287    pub fn add_use(&mut self, block: usize, var: u32) {
1288        if block < self.uses.len() {
1289            self.uses[block].insert(var);
1290        }
1291    }
1292    #[allow(dead_code)]
1293    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1294        self.live_in
1295            .get(block)
1296            .map(|s| s.contains(&var))
1297            .unwrap_or(false)
1298    }
1299    #[allow(dead_code)]
1300    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1301        self.live_out
1302            .get(block)
1303            .map(|s| s.contains(&var))
1304            .unwrap_or(false)
1305    }
1306}
1307/// Configuration for SpecExt passes.
1308#[allow(dead_code)]
1309#[derive(Debug, Clone)]
1310pub struct SpecExtPassConfig {
1311    pub name: String,
1312    pub phase: SpecExtPassPhase,
1313    pub enabled: bool,
1314    pub max_iterations: usize,
1315    pub debug: u32,
1316    pub timeout_ms: Option<u64>,
1317}
1318impl SpecExtPassConfig {
1319    #[allow(dead_code)]
1320    pub fn new(name: impl Into<String>) -> Self {
1321        Self {
1322            name: name.into(),
1323            phase: SpecExtPassPhase::Middle,
1324            enabled: true,
1325            max_iterations: 100,
1326            debug: 0,
1327            timeout_ms: None,
1328        }
1329    }
1330    #[allow(dead_code)]
1331    pub fn with_phase(mut self, phase: SpecExtPassPhase) -> Self {
1332        self.phase = phase;
1333        self
1334    }
1335    #[allow(dead_code)]
1336    pub fn with_max_iter(mut self, n: usize) -> Self {
1337        self.max_iterations = n;
1338        self
1339    }
1340    #[allow(dead_code)]
1341    pub fn with_debug(mut self, d: u32) -> Self {
1342        self.debug = d;
1343        self
1344    }
1345    #[allow(dead_code)]
1346    pub fn disabled(mut self) -> Self {
1347        self.enabled = false;
1348        self
1349    }
1350    #[allow(dead_code)]
1351    pub fn with_timeout(mut self, ms: u64) -> Self {
1352        self.timeout_ms = Some(ms);
1353        self
1354    }
1355    #[allow(dead_code)]
1356    pub fn is_debug_enabled(&self) -> bool {
1357        self.debug > 0
1358    }
1359}
1360#[allow(dead_code)]
1361#[derive(Debug, Clone)]
1362pub struct SpecWorklist {
1363    pub(super) items: std::collections::VecDeque<u32>,
1364    pub(super) in_worklist: std::collections::HashSet<u32>,
1365}
1366impl SpecWorklist {
1367    #[allow(dead_code)]
1368    pub fn new() -> Self {
1369        SpecWorklist {
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/// Worklist for SpecExt.
1403#[allow(dead_code)]
1404#[derive(Debug, Clone)]
1405pub struct SpecExtWorklist {
1406    pub(super) items: std::collections::VecDeque<usize>,
1407    pub(super) present: Vec<bool>,
1408}
1409impl SpecExtWorklist {
1410    #[allow(dead_code)]
1411    pub fn new(capacity: usize) -> Self {
1412        Self {
1413            items: std::collections::VecDeque::new(),
1414            present: vec![false; capacity],
1415        }
1416    }
1417    #[allow(dead_code)]
1418    pub fn push(&mut self, id: usize) {
1419        if id < self.present.len() && !self.present[id] {
1420            self.present[id] = true;
1421            self.items.push_back(id);
1422        }
1423    }
1424    #[allow(dead_code)]
1425    pub fn push_front(&mut self, id: usize) {
1426        if id < self.present.len() && !self.present[id] {
1427            self.present[id] = true;
1428            self.items.push_front(id);
1429        }
1430    }
1431    #[allow(dead_code)]
1432    pub fn pop(&mut self) -> Option<usize> {
1433        let id = self.items.pop_front()?;
1434        if id < self.present.len() {
1435            self.present[id] = false;
1436        }
1437        Some(id)
1438    }
1439    #[allow(dead_code)]
1440    pub fn is_empty(&self) -> bool {
1441        self.items.is_empty()
1442    }
1443    #[allow(dead_code)]
1444    pub fn len(&self) -> usize {
1445        self.items.len()
1446    }
1447    #[allow(dead_code)]
1448    pub fn contains(&self, id: usize) -> bool {
1449        id < self.present.len() && self.present[id]
1450    }
1451    #[allow(dead_code)]
1452    pub fn drain_all(&mut self) -> Vec<usize> {
1453        let v: Vec<usize> = self.items.drain(..).collect();
1454        for &id in &v {
1455            if id < self.present.len() {
1456                self.present[id] = false;
1457            }
1458        }
1459        v
1460    }
1461}
1462/// Cache for specializations to avoid duplicates
1463#[derive(Debug, Clone, Default)]
1464pub struct SpecializationCache {
1465    /// Map from specialization key to specialized function name
1466    pub(super) entries: HashMap<SpecializationKey, String>,
1467    /// Total code growth from all specializations
1468    pub(super) total_growth: usize,
1469}
1470impl SpecializationCache {
1471    pub(super) fn new() -> Self {
1472        SpecializationCache {
1473            entries: HashMap::new(),
1474            total_growth: 0,
1475        }
1476    }
1477    pub(super) fn lookup(&self, key: &SpecializationKey) -> Option<&str> {
1478        self.entries.get(key).map(|s| s.as_str())
1479    }
1480    pub(super) fn insert(&mut self, key: SpecializationKey, name: String, growth: usize) {
1481        self.entries.insert(key, name);
1482        self.total_growth += growth;
1483    }
1484    pub(super) fn specialization_count(&self, original: &str) -> usize {
1485        self.entries
1486            .keys()
1487            .filter(|k| k.original == original)
1488            .count()
1489    }
1490}
1491/// Track code size budget for specialization
1492#[derive(Debug, Clone)]
1493pub struct SizeBudget {
1494    pub(super) original_total: usize,
1495    pub(super) max_total: usize,
1496    pub(super) current_growth: usize,
1497}
1498impl SizeBudget {
1499    pub(super) fn new(original_total: usize, growth_factor: f64) -> Self {
1500        SizeBudget {
1501            original_total,
1502            max_total: (original_total as f64 * growth_factor) as usize,
1503            current_growth: 0,
1504        }
1505    }
1506    pub(super) fn can_afford(&self, additional: usize) -> bool {
1507        self.original_total + self.current_growth + additional <= self.max_total
1508    }
1509    pub(super) fn spend(&mut self, amount: usize) {
1510        self.current_growth += amount;
1511    }
1512    pub(super) fn remaining(&self) -> usize {
1513        self.max_total
1514            .saturating_sub(self.original_total + self.current_growth)
1515    }
1516}
1517/// A type argument in a specialization key
1518#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1519pub enum SpecTypeArg {
1520    /// Concrete type
1521    Concrete(LcnfType),
1522    /// Still polymorphic
1523    Poly,
1524}
1525#[allow(dead_code)]
1526#[derive(Debug, Clone)]
1527pub struct SpecDepGraph {
1528    pub(super) nodes: Vec<u32>,
1529    pub(super) edges: Vec<(u32, u32)>,
1530}
1531impl SpecDepGraph {
1532    #[allow(dead_code)]
1533    pub fn new() -> Self {
1534        SpecDepGraph {
1535            nodes: Vec::new(),
1536            edges: Vec::new(),
1537        }
1538    }
1539    #[allow(dead_code)]
1540    pub fn add_node(&mut self, id: u32) {
1541        if !self.nodes.contains(&id) {
1542            self.nodes.push(id);
1543        }
1544    }
1545    #[allow(dead_code)]
1546    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1547        self.add_node(dep);
1548        self.add_node(dependent);
1549        self.edges.push((dep, dependent));
1550    }
1551    #[allow(dead_code)]
1552    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1553        self.edges
1554            .iter()
1555            .filter(|(d, _)| *d == node)
1556            .map(|(_, dep)| *dep)
1557            .collect()
1558    }
1559    #[allow(dead_code)]
1560    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1561        self.edges
1562            .iter()
1563            .filter(|(_, dep)| *dep == node)
1564            .map(|(d, _)| *d)
1565            .collect()
1566    }
1567    #[allow(dead_code)]
1568    pub fn topological_sort(&self) -> Vec<u32> {
1569        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1570        for &n in &self.nodes {
1571            in_degree.insert(n, 0);
1572        }
1573        for (_, dep) in &self.edges {
1574            *in_degree.entry(*dep).or_insert(0) += 1;
1575        }
1576        let mut queue: std::collections::VecDeque<u32> = self
1577            .nodes
1578            .iter()
1579            .filter(|&&n| in_degree[&n] == 0)
1580            .copied()
1581            .collect();
1582        let mut result = Vec::new();
1583        while let Some(node) = queue.pop_front() {
1584            result.push(node);
1585            for dep in self.dependents_of(node) {
1586                let cnt = in_degree.entry(dep).or_insert(0);
1587                *cnt = cnt.saturating_sub(1);
1588                if *cnt == 0 {
1589                    queue.push_back(dep);
1590                }
1591            }
1592        }
1593        result
1594    }
1595    #[allow(dead_code)]
1596    pub fn has_cycle(&self) -> bool {
1597        self.topological_sort().len() < self.nodes.len()
1598    }
1599}
1600/// Numeric type specialization: replace polymorphic Nat operations with u64
1601pub struct NumericSpecializer {
1602    /// Known numeric functions that can be specialized
1603    pub(super) numeric_ops: HashSet<String>,
1604}
1605impl NumericSpecializer {
1606    pub(super) fn new() -> Self {
1607        let mut ops = HashSet::new();
1608        ops.insert("Nat.add".to_string());
1609        ops.insert("Nat.sub".to_string());
1610        ops.insert("Nat.mul".to_string());
1611        ops.insert("Nat.div".to_string());
1612        ops.insert("Nat.mod".to_string());
1613        ops.insert("Nat.beq".to_string());
1614        ops.insert("Nat.ble".to_string());
1615        ops.insert("Nat.blt".to_string());
1616        ops.insert("Nat.decEq".to_string());
1617        ops.insert("Nat.zero".to_string());
1618        ops.insert("Nat.succ".to_string());
1619        NumericSpecializer { numeric_ops: ops }
1620    }
1621    pub(super) fn is_numeric_op(&self, name: &str) -> bool {
1622        self.numeric_ops.contains(name)
1623    }
1624    pub(super) fn specialize_nat_to_u64(&self, ty: &LcnfType) -> LcnfType {
1625        match ty {
1626            LcnfType::Nat => LcnfType::Nat,
1627            LcnfType::Fun(params, ret) => {
1628                let new_params: Vec<LcnfType> = params
1629                    .iter()
1630                    .map(|p| self.specialize_nat_to_u64(p))
1631                    .collect();
1632                let new_ret = self.specialize_nat_to_u64(ret);
1633                LcnfType::Fun(new_params, Box::new(new_ret))
1634            }
1635            LcnfType::Ctor(name, args) => {
1636                let new_args: Vec<LcnfType> =
1637                    args.iter().map(|a| self.specialize_nat_to_u64(a)).collect();
1638                LcnfType::Ctor(name.clone(), new_args)
1639            }
1640            other => other.clone(),
1641        }
1642    }
1643}
1644/// What makes a specialization unique
1645#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1646pub struct SpecializationKey {
1647    /// The original function name
1648    pub original: String,
1649    /// Type arguments that are specialized
1650    pub type_args: Vec<SpecTypeArg>,
1651    /// Constant arguments that are specialized
1652    pub const_args: Vec<SpecConstArg>,
1653    /// Closure arguments that are specialized
1654    pub closure_args: Vec<SpecClosureArg>,
1655}
1656impl SpecializationKey {
1657    pub(super) fn is_trivial(&self) -> bool {
1658        self.type_args
1659            .iter()
1660            .all(|a| matches!(a, SpecTypeArg::Poly))
1661            && self
1662                .const_args
1663                .iter()
1664                .all(|a| matches!(a, SpecConstArg::Unknown))
1665            && self.closure_args.iter().all(|a| a.known_fn.is_none())
1666    }
1667    pub(super) fn mangled_name(&self) -> String {
1668        let mut name = self.original.clone();
1669        for (i, ta) in self.type_args.iter().enumerate() {
1670            if let SpecTypeArg::Concrete(ty) = ta {
1671                name.push_str(&format!("_T{}_{}", i, type_suffix(ty)));
1672            }
1673        }
1674        for (i, ca) in self.const_args.iter().enumerate() {
1675            match ca {
1676                SpecConstArg::Nat(n) => name.push_str(&format!("_C{}_N{}", i, n)),
1677                SpecConstArg::Str(s) => {
1678                    let short = if s.len() > 8 { &s[..8] } else { s.as_str() };
1679                    name.push_str(&format!("_C{}_S{}", i, short));
1680                }
1681                SpecConstArg::Unknown => {}
1682            }
1683        }
1684        for ca in &self.closure_args {
1685            if let Some(fn_name) = &ca.known_fn {
1686                name.push_str(&format!("_F{}", fn_name));
1687            }
1688        }
1689        name
1690    }
1691}
1692/// Liveness analysis for SpecExt.
1693#[allow(dead_code)]
1694#[derive(Debug, Clone, Default)]
1695pub struct SpecExtLiveness {
1696    pub live_in: Vec<Vec<usize>>,
1697    pub live_out: Vec<Vec<usize>>,
1698    pub defs: Vec<Vec<usize>>,
1699    pub uses: Vec<Vec<usize>>,
1700}
1701impl SpecExtLiveness {
1702    #[allow(dead_code)]
1703    pub fn new(n: usize) -> Self {
1704        Self {
1705            live_in: vec![Vec::new(); n],
1706            live_out: vec![Vec::new(); n],
1707            defs: vec![Vec::new(); n],
1708            uses: vec![Vec::new(); n],
1709        }
1710    }
1711    #[allow(dead_code)]
1712    pub fn live_in(&self, b: usize, v: usize) -> bool {
1713        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1714    }
1715    #[allow(dead_code)]
1716    pub fn live_out(&self, b: usize, v: usize) -> bool {
1717        self.live_out
1718            .get(b)
1719            .map(|s| s.contains(&v))
1720            .unwrap_or(false)
1721    }
1722    #[allow(dead_code)]
1723    pub fn add_def(&mut self, b: usize, v: usize) {
1724        if let Some(s) = self.defs.get_mut(b) {
1725            if !s.contains(&v) {
1726                s.push(v);
1727            }
1728        }
1729    }
1730    #[allow(dead_code)]
1731    pub fn add_use(&mut self, b: usize, v: usize) {
1732        if let Some(s) = self.uses.get_mut(b) {
1733            if !s.contains(&v) {
1734                s.push(v);
1735            }
1736        }
1737    }
1738    #[allow(dead_code)]
1739    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1740        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1741    }
1742    #[allow(dead_code)]
1743    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1744        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1745    }
1746}
1747/// Statistics for SpecExt passes.
1748#[allow(dead_code)]
1749#[derive(Debug, Clone, Default)]
1750pub struct SpecExtPassStats {
1751    pub iterations: usize,
1752    pub changed: bool,
1753    pub nodes_visited: usize,
1754    pub nodes_modified: usize,
1755    pub time_ms: u64,
1756    pub memory_bytes: usize,
1757    pub errors: usize,
1758}
1759impl SpecExtPassStats {
1760    #[allow(dead_code)]
1761    pub fn new() -> Self {
1762        Self::default()
1763    }
1764    #[allow(dead_code)]
1765    pub fn visit(&mut self) {
1766        self.nodes_visited += 1;
1767    }
1768    #[allow(dead_code)]
1769    pub fn modify(&mut self) {
1770        self.nodes_modified += 1;
1771        self.changed = true;
1772    }
1773    #[allow(dead_code)]
1774    pub fn iterate(&mut self) {
1775        self.iterations += 1;
1776    }
1777    #[allow(dead_code)]
1778    pub fn error(&mut self) {
1779        self.errors += 1;
1780    }
1781    #[allow(dead_code)]
1782    pub fn efficiency(&self) -> f64 {
1783        if self.nodes_visited == 0 {
1784            0.0
1785        } else {
1786            self.nodes_modified as f64 / self.nodes_visited as f64
1787        }
1788    }
1789    #[allow(dead_code)]
1790    pub fn merge(&mut self, o: &SpecExtPassStats) {
1791        self.iterations += o.iterations;
1792        self.changed |= o.changed;
1793        self.nodes_visited += o.nodes_visited;
1794        self.nodes_modified += o.nodes_modified;
1795        self.time_ms += o.time_ms;
1796        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1797        self.errors += o.errors;
1798    }
1799}
1800/// Information about a call site that might benefit from specialization
1801#[derive(Debug, Clone)]
1802pub struct SpecCallSite {
1803    /// The called function name
1804    pub(super) callee: String,
1805    /// Index of this call site in the function
1806    pub(super) call_idx: usize,
1807    /// Type arguments at this call site (if determinable)
1808    pub(super) type_args: Vec<SpecTypeArg>,
1809    /// Constant arguments at this call site
1810    pub(super) const_args: Vec<SpecConstArg>,
1811    /// Closure arguments at this call site
1812    pub(super) closure_args: Vec<SpecClosureArg>,
1813    /// The variable ID used for the call
1814    pub(super) callee_var: Option<LcnfVarId>,
1815}
1816#[allow(dead_code)]
1817#[derive(Debug, Clone)]
1818pub struct SpecAnalysisCache {
1819    pub(super) entries: std::collections::HashMap<String, SpecCacheEntry>,
1820    pub(super) max_size: usize,
1821    pub(super) hits: u64,
1822    pub(super) misses: u64,
1823}
1824impl SpecAnalysisCache {
1825    #[allow(dead_code)]
1826    pub fn new(max_size: usize) -> Self {
1827        SpecAnalysisCache {
1828            entries: std::collections::HashMap::new(),
1829            max_size,
1830            hits: 0,
1831            misses: 0,
1832        }
1833    }
1834    #[allow(dead_code)]
1835    pub fn get(&mut self, key: &str) -> Option<&SpecCacheEntry> {
1836        if self.entries.contains_key(key) {
1837            self.hits += 1;
1838            self.entries.get(key)
1839        } else {
1840            self.misses += 1;
1841            None
1842        }
1843    }
1844    #[allow(dead_code)]
1845    pub fn insert(&mut self, key: String, data: Vec<u8>) {
1846        if self.entries.len() >= self.max_size {
1847            if let Some(oldest) = self.entries.keys().next().cloned() {
1848                self.entries.remove(&oldest);
1849            }
1850        }
1851        self.entries.insert(
1852            key.clone(),
1853            SpecCacheEntry {
1854                key,
1855                data,
1856                timestamp: 0,
1857                valid: true,
1858            },
1859        );
1860    }
1861    #[allow(dead_code)]
1862    pub fn invalidate(&mut self, key: &str) {
1863        if let Some(entry) = self.entries.get_mut(key) {
1864            entry.valid = false;
1865        }
1866    }
1867    #[allow(dead_code)]
1868    pub fn clear(&mut self) {
1869        self.entries.clear();
1870    }
1871    #[allow(dead_code)]
1872    pub fn hit_rate(&self) -> f64 {
1873        let total = self.hits + self.misses;
1874        if total == 0 {
1875            return 0.0;
1876        }
1877        self.hits as f64 / total as f64
1878    }
1879    #[allow(dead_code)]
1880    pub fn size(&self) -> usize {
1881        self.entries.len()
1882    }
1883}