Skip to main content

oxilean_codegen/julia_backend/
types.rs

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