Skip to main content

oxilean_codegen/julia_backend/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::HashMap;
6
7use super::types::{
8    JulAnalysisCache, JulConstantFoldingHelper, JulDepGraph, JulDominatorTree, JulLivenessInfo,
9    JulPassConfig, JulPassPhase, JulPassRegistry, JulPassStats, JulWorklist, JuliaBackend,
10    JuliaExpr, JuliaExprDisplay, JuliaExtCache, JuliaExtConstFolder, JuliaExtDepGraph,
11    JuliaExtDomTree, JuliaExtLiveness, JuliaExtPassConfig, JuliaExtPassPhase, JuliaExtPassRegistry,
12    JuliaExtPassStats, JuliaExtWorklist, JuliaFunction, JuliaModule, JuliaParam, JuliaStmt,
13    JuliaStmtDisplay, JuliaStringPart, JuliaStruct, JuliaType,
14};
15use std::fmt;
16
17pub(super) fn emit_expr(f: &mut fmt::Formatter<'_>, expr: &JuliaExpr) -> fmt::Result {
18    match expr {
19        JuliaExpr::IntLit(n) => write!(f, "{}", n),
20        JuliaExpr::UIntLit(n) => write!(f, "0x{:x}", n),
21        JuliaExpr::FloatLit(v) => {
22            if v.fract() == 0.0 {
23                write!(f, "{:.1}", v)
24            } else {
25                write!(f, "{}", v)
26            }
27        }
28        JuliaExpr::BoolLit(b) => write!(f, "{}", b),
29        JuliaExpr::StringLit(s) => write!(f, "\"{}\"", s.replace('"', "\\\"")),
30        JuliaExpr::CharLit(c) => write!(f, "'{}'", c),
31        JuliaExpr::Nothing => write!(f, "nothing"),
32        JuliaExpr::Var(name) => write!(f, "{}", name),
33        JuliaExpr::Field(obj, field) => write!(f, "{}.{}", JuliaExprDisplay(obj), field),
34        JuliaExpr::Index(arr, idxs) => {
35            emit_expr(f, arr)?;
36            write!(f, "[")?;
37            for (i, idx) in idxs.iter().enumerate() {
38                if i > 0 {
39                    write!(f, ", ")?;
40                }
41                emit_expr(f, idx)?;
42            }
43            write!(f, "]")
44        }
45        JuliaExpr::Slice(arr, lo, hi) => {
46            emit_expr(f, arr)?;
47            write!(f, "[")?;
48            if let Some(ref lo) = lo {
49                emit_expr(f, lo)?;
50            } else {
51                write!(f, "begin")?;
52            }
53            write!(f, ":")?;
54            if let Some(ref hi) = hi {
55                emit_expr(f, hi)?;
56            } else {
57                write!(f, "end")?;
58            }
59            write!(f, "]")
60        }
61        JuliaExpr::Call(func, args) => {
62            emit_expr(f, func)?;
63            write!(f, "(")?;
64            for (i, a) in args.iter().enumerate() {
65                if i > 0 {
66                    write!(f, ", ")?;
67                }
68                emit_expr(f, a)?;
69            }
70            write!(f, ")")
71        }
72        JuliaExpr::CallKw(func, args, kwargs) => {
73            emit_expr(f, func)?;
74            write!(f, "(")?;
75            for (i, a) in args.iter().enumerate() {
76                if i > 0 {
77                    write!(f, ", ")?;
78                }
79                emit_expr(f, a)?;
80            }
81            if !kwargs.is_empty() {
82                if !args.is_empty() {
83                    write!(f, "; ")?;
84                } else {
85                    write!(f, ";")?;
86                }
87                for (i, (k, v)) in kwargs.iter().enumerate() {
88                    if i > 0 {
89                        write!(f, ", ")?;
90                    }
91                    write!(f, "{}=", k)?;
92                    emit_expr(f, v)?;
93                }
94            }
95            write!(f, ")")
96        }
97        JuliaExpr::Broadcast(func, args) => {
98            emit_expr(f, func)?;
99            write!(f, ".(")?;
100            for (i, a) in args.iter().enumerate() {
101                if i > 0 {
102                    write!(f, ", ")?;
103                }
104                emit_expr(f, a)?;
105            }
106            write!(f, ")")
107        }
108        JuliaExpr::BinOp(op, lhs, rhs) => {
109            write!(f, "(")?;
110            emit_expr(f, lhs)?;
111            write!(f, " {} ", op)?;
112            emit_expr(f, rhs)?;
113            write!(f, ")")
114        }
115        JuliaExpr::UnOp(op, operand) => {
116            write!(f, "{}", op)?;
117            emit_expr(f, operand)
118        }
119        JuliaExpr::CompareChain(exprs, ops) => {
120            write!(f, "(")?;
121            for (i, expr) in exprs.iter().enumerate() {
122                if i > 0 {
123                    write!(f, " {} ", ops[i - 1])?;
124                }
125                emit_expr(f, expr)?;
126            }
127            write!(f, ")")
128        }
129        JuliaExpr::ArrayLit(elems) => {
130            write!(f, "[")?;
131            for (i, e) in elems.iter().enumerate() {
132                if i > 0 {
133                    write!(f, ", ")?;
134                }
135                emit_expr(f, e)?;
136            }
137            write!(f, "]")
138        }
139        JuliaExpr::MatrixLit(rows) => {
140            write!(f, "[")?;
141            for (i, row) in rows.iter().enumerate() {
142                if i > 0 {
143                    write!(f, "; ")?;
144                }
145                for (j, e) in row.iter().enumerate() {
146                    if j > 0 {
147                        write!(f, " ")?;
148                    }
149                    emit_expr(f, e)?;
150                }
151            }
152            write!(f, "]")
153        }
154        JuliaExpr::Range(lo, step, hi) => {
155            emit_expr(f, lo)?;
156            if let Some(ref s) = step {
157                write!(f, ":")?;
158                emit_expr(f, s)?;
159            }
160            write!(f, ":")?;
161            emit_expr(f, hi)
162        }
163        JuliaExpr::TupleLit(elems) => {
164            write!(f, "(")?;
165            for (i, e) in elems.iter().enumerate() {
166                if i > 0 {
167                    write!(f, ", ")?;
168                }
169                emit_expr(f, e)?;
170            }
171            if elems.len() == 1 {
172                write!(f, ",")?;
173            }
174            write!(f, ")")
175        }
176        JuliaExpr::ArrayComp(body, clauses, guard) => {
177            write!(f, "[")?;
178            emit_expr(f, body)?;
179            for (var, iter) in clauses {
180                write!(f, " for {} in ", var)?;
181                emit_expr(f, iter)?;
182            }
183            if let Some(ref g) = guard {
184                write!(f, " if ")?;
185                emit_expr(f, g)?;
186            }
187            write!(f, "]")
188        }
189        JuliaExpr::Generator(body, clauses, guard) => {
190            write!(f, "(")?;
191            emit_expr(f, body)?;
192            for (var, iter) in clauses {
193                write!(f, " for {} in ", var)?;
194                emit_expr(f, iter)?;
195            }
196            if let Some(ref g) = guard {
197                write!(f, " if ")?;
198                emit_expr(f, g)?;
199            }
200            write!(f, ")")
201        }
202        JuliaExpr::DictComp(k, v, clauses) => {
203            write!(f, "Dict(")?;
204            emit_expr(f, k)?;
205            write!(f, " => ")?;
206            emit_expr(f, v)?;
207            for (var, iter) in clauses {
208                write!(f, " for {} in ", var)?;
209                emit_expr(f, iter)?;
210            }
211            write!(f, ")")
212        }
213        JuliaExpr::Lambda(params, body) => {
214            if params.len() == 1 {
215                write!(f, "{}", params[0])?;
216            } else {
217                write!(f, "(")?;
218                for (i, p) in params.iter().enumerate() {
219                    if i > 0 {
220                        write!(f, ", ")?;
221                    }
222                    write!(f, "{}", p)?;
223                }
224                write!(f, ")")?;
225            }
226            write!(f, " -> ")?;
227            emit_expr(f, body)
228        }
229        JuliaExpr::DoBlock(func, params, body) => {
230            emit_expr(f, func)?;
231            write!(f, " do")?;
232            if !params.is_empty() {
233                write!(f, " {}", params.join(", "))?;
234            }
235            writeln!(f)?;
236            for stmt in body {
237                writeln!(f, "    {}", JuliaStmtDisplay(stmt))?;
238            }
239            write!(f, "end")
240        }
241        JuliaExpr::Ternary(cond, then_, else_) => {
242            emit_expr(f, cond)?;
243            write!(f, " ? ")?;
244            emit_expr(f, then_)?;
245            write!(f, " : ")?;
246            emit_expr(f, else_)
247        }
248        JuliaExpr::TypeAssert(expr, ty) => {
249            write!(f, "(")?;
250            emit_expr(f, expr)?;
251            write!(f, ")::{}", ty)
252        }
253        JuliaExpr::Convert(ty, expr) => {
254            write!(f, "convert({}, ", ty)?;
255            emit_expr(f, expr)?;
256            write!(f, ")")
257        }
258        JuliaExpr::IsA(expr, ty) => {
259            emit_expr(f, expr)?;
260            write!(f, " isa {}", ty)
261        }
262        JuliaExpr::TypeOf(expr) => {
263            write!(f, "typeof(")?;
264            emit_expr(f, expr)?;
265            write!(f, ")")
266        }
267        JuliaExpr::Macro(name, args) => {
268            write!(f, "@{}", name)?;
269            for a in args {
270                write!(f, " ")?;
271                emit_expr(f, a)?;
272            }
273            Ok(())
274        }
275        JuliaExpr::Interpolated(parts) => {
276            write!(f, "\"")?;
277            for part in parts {
278                match part {
279                    JuliaStringPart::Text(s) => write!(f, "{}", s)?,
280                    JuliaStringPart::Expr(e) => {
281                        write!(f, "$(")?;
282                        emit_expr(f, e)?;
283                        write!(f, ")")?;
284                    }
285                }
286            }
287            write!(f, "\"")
288        }
289        JuliaExpr::Splat(expr) => {
290            emit_expr(f, expr)?;
291            write!(f, "...")
292        }
293        JuliaExpr::NamedArg(name, val) => {
294            write!(f, "{}=", name)?;
295            emit_expr(f, val)
296        }
297        JuliaExpr::Pair(k, v) => {
298            emit_expr(f, k)?;
299            write!(f, " => ")?;
300            emit_expr(f, v)
301        }
302        JuliaExpr::Block(stmts) => {
303            writeln!(f, "begin")?;
304            for s in stmts {
305                writeln!(f, "    {}", JuliaStmtDisplay(s))?;
306            }
307            write!(f, "end")
308        }
309    }
310}
311pub(super) fn emit_stmt_inline(f: &mut fmt::Formatter<'_>, stmt: &JuliaStmt) -> fmt::Result {
312    match stmt {
313        JuliaStmt::Expr(e) => emit_expr(f, e),
314        JuliaStmt::Assign(lhs, rhs) => {
315            emit_expr(f, lhs)?;
316            write!(f, " = ")?;
317            emit_expr(f, rhs)
318        }
319        JuliaStmt::Return(Some(e)) => {
320            write!(f, "return ")?;
321            emit_expr(f, e)
322        }
323        JuliaStmt::Return(None) => write!(f, "return"),
324        JuliaStmt::Break => write!(f, "break"),
325        JuliaStmt::Continue => write!(f, "continue"),
326        JuliaStmt::Comment(s) => write!(f, "# {}", s),
327        JuliaStmt::Blank => write!(f, ""),
328        _ => write!(f, "# (complex stmt)"),
329    }
330}
331#[cfg(test)]
332mod tests {
333    use super::*;
334    #[test]
335    pub(super) fn test_julia_type_display() {
336        assert_eq!(JuliaType::Int64.to_string(), "Int64");
337        assert_eq!(JuliaType::Float64.to_string(), "Float64");
338        assert_eq!(JuliaType::Bool.to_string(), "Bool");
339        assert_eq!(JuliaType::String.to_string(), "String");
340        assert_eq!(JuliaType::Nothing.to_string(), "Nothing");
341        assert_eq!(JuliaType::Any.to_string(), "Any");
342        assert_eq!(
343            JuliaType::Vector(Box::new(JuliaType::Float64)).to_string(),
344            "Vector{Float64}"
345        );
346        assert_eq!(
347            JuliaType::Matrix(Box::new(JuliaType::Int32)).to_string(),
348            "Matrix{Int32}"
349        );
350    }
351    #[test]
352    pub(super) fn test_julia_parametric_type_display() {
353        let ty = JuliaType::Parametric(
354            "Dict".to_string(),
355            vec![JuliaType::String, JuliaType::Int64],
356        );
357        assert_eq!(ty.to_string(), "Dict{String, Int64}");
358        let union_ty = JuliaType::Union(vec![JuliaType::Int64, JuliaType::Nothing]);
359        assert_eq!(union_ty.to_string(), "Union{Int64, Nothing}");
360        let tuple_ty = JuliaType::Tuple(vec![JuliaType::Int64, JuliaType::Float64]);
361        assert_eq!(tuple_ty.to_string(), "Tuple{Int64, Float64}");
362    }
363    #[test]
364    pub(super) fn test_julia_expr_literals() {
365        let mut backend = JuliaBackend::new();
366        assert_eq!(backend.emit_expr(&JuliaExpr::IntLit(42)), "42");
367        assert_eq!(backend.emit_expr(&JuliaExpr::FloatLit(3.14)), "3.14");
368        assert_eq!(backend.emit_expr(&JuliaExpr::BoolLit(true)), "true");
369        assert_eq!(
370            backend.emit_expr(&JuliaExpr::StringLit("hello".to_string())),
371            "\"hello\""
372        );
373        assert_eq!(backend.emit_expr(&JuliaExpr::Nothing), "nothing");
374    }
375    #[test]
376    pub(super) fn test_julia_binop_and_unop() {
377        let mut backend = JuliaBackend::new();
378        let binop = JuliaExpr::BinOp(
379            "+".to_string(),
380            Box::new(JuliaExpr::Var("x".to_string())),
381            Box::new(JuliaExpr::IntLit(1)),
382        );
383        assert_eq!(backend.emit_expr(&binop), "(x + 1)");
384        let unop = JuliaExpr::UnOp("-".to_string(), Box::new(JuliaExpr::Var("y".to_string())));
385        assert_eq!(backend.emit_expr(&unop), "-y");
386    }
387    #[test]
388    pub(super) fn test_julia_function_emit() {
389        let mut backend = JuliaBackend::new();
390        let func = JuliaFunction::new("add")
391            .with_type_param_bound("T", "Number")
392            .with_param(JuliaParam::typed("a", JuliaType::TypeVar("T".to_string())))
393            .with_param(JuliaParam::typed("b", JuliaType::TypeVar("T".to_string())))
394            .with_return_type(JuliaType::TypeVar("T".to_string()))
395            .with_body(vec![JuliaStmt::Return(Some(JuliaExpr::BinOp(
396                "+".to_string(),
397                Box::new(JuliaExpr::Var("a".to_string())),
398                Box::new(JuliaExpr::Var("b".to_string())),
399            )))]);
400        backend.emit_function(&func);
401        let out = backend.take_output();
402        assert!(
403            out.contains("function add{T <: Number}"),
404            "missing signature: {}",
405            out
406        );
407        assert!(
408            out.contains("::T"),
409            "missing return type annotation: {}",
410            out
411        );
412        assert!(out.contains("return"), "missing return: {}", out);
413        assert!(out.contains("end"), "missing end: {}", out);
414    }
415    #[test]
416    pub(super) fn test_julia_struct_emit() {
417        let mut backend = JuliaBackend::new();
418        let s = JuliaStruct::new("Point")
419            .with_type_param("T")
420            .with_supertype("AbstractPoint")
421            .with_field("x", JuliaType::TypeVar("T".to_string()))
422            .with_field("y", JuliaType::TypeVar("T".to_string()));
423        backend.emit_struct(&s);
424        let out = backend.take_output();
425        assert!(out.contains("struct Point{T}"), "missing header: {}", out);
426        assert!(
427            out.contains("<: AbstractPoint"),
428            "missing supertype: {}",
429            out
430        );
431        assert!(out.contains("x::T"), "missing field x: {}", out);
432        assert!(out.contains("y::T"), "missing field y: {}", out);
433        assert!(out.contains("end"), "missing end: {}", out);
434    }
435    #[test]
436    pub(super) fn test_julia_multiple_dispatch() {
437        let mut backend = JuliaBackend::new();
438        let m1 = JuliaFunction::new("add")
439            .with_param(JuliaParam::typed("a", JuliaType::Int64))
440            .with_param(JuliaParam::typed("b", JuliaType::Int64))
441            .with_return_type(JuliaType::Int64)
442            .with_body(vec![JuliaStmt::Return(Some(JuliaExpr::BinOp(
443                "+".to_string(),
444                Box::new(JuliaExpr::Var("a".to_string())),
445                Box::new(JuliaExpr::Var("b".to_string())),
446            )))]);
447        let m2 = JuliaFunction::new("add")
448            .with_param(JuliaParam::typed("a", JuliaType::Float64))
449            .with_param(JuliaParam::typed("b", JuliaType::Float64))
450            .with_return_type(JuliaType::Float64)
451            .with_body(vec![JuliaStmt::Return(Some(JuliaExpr::BinOp(
452                "+".to_string(),
453                Box::new(JuliaExpr::Var("a".to_string())),
454                Box::new(JuliaExpr::Var("b".to_string())),
455            )))]);
456        backend.register_method(m1);
457        backend.register_method(m2);
458        let table = backend
459            .dispatch_tables
460            .get("add")
461            .expect("table should be present in map");
462        assert_eq!(table.num_methods(), 2);
463        let found = table.find_method(&[JuliaType::Int64, JuliaType::Int64]);
464        assert!(found.is_some());
465        assert_eq!(
466            found.expect("value should be Some/Ok").return_type,
467            Some(JuliaType::Int64)
468        );
469        let found2 = table.find_method(&[JuliaType::Float64, JuliaType::Float64]);
470        assert!(found2.is_some());
471        assert_eq!(
472            found2.expect("value should be Some/Ok").return_type,
473            Some(JuliaType::Float64)
474        );
475    }
476    #[test]
477    pub(super) fn test_julia_array_comprehension() {
478        let mut backend = JuliaBackend::new();
479        let comp = JuliaExpr::ArrayComp(
480            Box::new(JuliaExpr::BinOp(
481                "*".to_string(),
482                Box::new(JuliaExpr::Var("x".to_string())),
483                Box::new(JuliaExpr::Var("x".to_string())),
484            )),
485            vec![(
486                "x".to_string(),
487                JuliaExpr::Range(
488                    Box::new(JuliaExpr::IntLit(1)),
489                    None,
490                    Box::new(JuliaExpr::IntLit(10)),
491                ),
492            )],
493            Some(Box::new(JuliaExpr::BinOp(
494                ">".to_string(),
495                Box::new(JuliaExpr::Var("x".to_string())),
496                Box::new(JuliaExpr::IntLit(3)),
497            ))),
498        );
499        let s = backend.emit_expr(&comp);
500        assert!(s.contains("for x in"), "missing for clause: {}", s);
501        assert!(s.contains("if"), "missing guard: {}", s);
502    }
503    #[test]
504    pub(super) fn test_julia_module_emit() {
505        let mut backend = JuliaBackend::new();
506        let m = JuliaModule::new("MyMath")
507            .using(vec!["LinearAlgebra".to_string()])
508            .export("dot_product")
509            .push(JuliaStmt::Comment("Module body".to_string()));
510        backend.emit_module(&m);
511        let out = backend.take_output();
512        assert!(
513            out.contains("module MyMath"),
514            "missing module header: {}",
515            out
516        );
517        assert!(
518            out.contains("using LinearAlgebra"),
519            "missing using: {}",
520            out
521        );
522        assert!(
523            out.contains("export dot_product"),
524            "missing export: {}",
525            out
526        );
527        assert!(out.contains("end"), "missing end: {}", out);
528    }
529    #[test]
530    pub(super) fn test_julia_if_for_while_stmts() {
531        let mut backend = JuliaBackend::new();
532        let if_stmt = JuliaStmt::If {
533            cond: JuliaExpr::BoolLit(true),
534            then_body: vec![JuliaStmt::Return(Some(JuliaExpr::IntLit(1)))],
535            elseif_branches: vec![],
536            else_body: Some(vec![JuliaStmt::Return(Some(JuliaExpr::IntLit(0)))]),
537        };
538        backend.emit_stmt(&if_stmt);
539        let out1 = backend.take_output();
540        assert!(out1.contains("if true"), "missing if: {}", out1);
541        assert!(out1.contains("else"), "missing else: {}", out1);
542        let for_stmt = JuliaStmt::For {
543            vars: vec!["i".to_string()],
544            iter: JuliaExpr::Range(
545                Box::new(JuliaExpr::IntLit(1)),
546                None,
547                Box::new(JuliaExpr::IntLit(10)),
548            ),
549            body: vec![JuliaStmt::Continue],
550        };
551        backend.emit_stmt(&for_stmt);
552        let out2 = backend.take_output();
553        assert!(out2.contains("for i in"), "missing for: {}", out2);
554        let while_stmt = JuliaStmt::While {
555            cond: JuliaExpr::BoolLit(true),
556            body: vec![JuliaStmt::Break],
557        };
558        backend.emit_stmt(&while_stmt);
559        let out3 = backend.take_output();
560        assert!(out3.contains("while true"), "missing while: {}", out3);
561    }
562}
563#[cfg(test)]
564mod Jul_infra_tests {
565    use super::*;
566    #[test]
567    pub(super) fn test_pass_config() {
568        let config = JulPassConfig::new("test_pass", JulPassPhase::Transformation);
569        assert!(config.enabled);
570        assert!(config.phase.is_modifying());
571        assert_eq!(config.phase.name(), "transformation");
572    }
573    #[test]
574    pub(super) fn test_pass_stats() {
575        let mut stats = JulPassStats::new();
576        stats.record_run(10, 100, 3);
577        stats.record_run(20, 200, 5);
578        assert_eq!(stats.total_runs, 2);
579        assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
580        assert!((stats.success_rate() - 1.0).abs() < 0.01);
581        let s = stats.format_summary();
582        assert!(s.contains("Runs: 2/2"));
583    }
584    #[test]
585    pub(super) fn test_pass_registry() {
586        let mut reg = JulPassRegistry::new();
587        reg.register(JulPassConfig::new("pass_a", JulPassPhase::Analysis));
588        reg.register(JulPassConfig::new("pass_b", JulPassPhase::Transformation).disabled());
589        assert_eq!(reg.total_passes(), 2);
590        assert_eq!(reg.enabled_count(), 1);
591        reg.update_stats("pass_a", 5, 50, 2);
592        let stats = reg.get_stats("pass_a").expect("stats should exist");
593        assert_eq!(stats.total_changes, 5);
594    }
595    #[test]
596    pub(super) fn test_analysis_cache() {
597        let mut cache = JulAnalysisCache::new(10);
598        cache.insert("key1".to_string(), vec![1, 2, 3]);
599        assert!(cache.get("key1").is_some());
600        assert!(cache.get("key2").is_none());
601        assert!((cache.hit_rate() - 0.5).abs() < 0.01);
602        cache.invalidate("key1");
603        assert!(!cache.entries["key1"].valid);
604        assert_eq!(cache.size(), 1);
605    }
606    #[test]
607    pub(super) fn test_worklist() {
608        let mut wl = JulWorklist::new();
609        assert!(wl.push(1));
610        assert!(wl.push(2));
611        assert!(!wl.push(1));
612        assert_eq!(wl.len(), 2);
613        assert_eq!(wl.pop(), Some(1));
614        assert!(!wl.contains(1));
615        assert!(wl.contains(2));
616    }
617    #[test]
618    pub(super) fn test_dominator_tree() {
619        let mut dt = JulDominatorTree::new(5);
620        dt.set_idom(1, 0);
621        dt.set_idom(2, 0);
622        dt.set_idom(3, 1);
623        assert!(dt.dominates(0, 3));
624        assert!(dt.dominates(1, 3));
625        assert!(!dt.dominates(2, 3));
626        assert!(dt.dominates(3, 3));
627    }
628    #[test]
629    pub(super) fn test_liveness() {
630        let mut liveness = JulLivenessInfo::new(3);
631        liveness.add_def(0, 1);
632        liveness.add_use(1, 1);
633        assert!(liveness.defs[0].contains(&1));
634        assert!(liveness.uses[1].contains(&1));
635    }
636    #[test]
637    pub(super) fn test_constant_folding() {
638        assert_eq!(JulConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
639        assert_eq!(JulConstantFoldingHelper::fold_div_i64(10, 0), None);
640        assert_eq!(JulConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
641        assert_eq!(
642            JulConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
643            0b1000
644        );
645        assert_eq!(JulConstantFoldingHelper::fold_bitnot_i64(0), -1);
646    }
647    #[test]
648    pub(super) fn test_dep_graph() {
649        let mut g = JulDepGraph::new();
650        g.add_dep(1, 2);
651        g.add_dep(2, 3);
652        g.add_dep(1, 3);
653        assert_eq!(g.dependencies_of(2), vec![1]);
654        let topo = g.topological_sort();
655        assert_eq!(topo.len(), 3);
656        assert!(!g.has_cycle());
657        let pos: std::collections::HashMap<u32, usize> =
658            topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
659        assert!(pos[&1] < pos[&2]);
660        assert!(pos[&1] < pos[&3]);
661        assert!(pos[&2] < pos[&3]);
662    }
663}
664#[cfg(test)]
665mod juliaext_pass_tests {
666    use super::*;
667    #[test]
668    pub(super) fn test_juliaext_phase_order() {
669        assert_eq!(JuliaExtPassPhase::Early.order(), 0);
670        assert_eq!(JuliaExtPassPhase::Middle.order(), 1);
671        assert_eq!(JuliaExtPassPhase::Late.order(), 2);
672        assert_eq!(JuliaExtPassPhase::Finalize.order(), 3);
673        assert!(JuliaExtPassPhase::Early.is_early());
674        assert!(!JuliaExtPassPhase::Early.is_late());
675    }
676    #[test]
677    pub(super) fn test_juliaext_config_builder() {
678        let c = JuliaExtPassConfig::new("p")
679            .with_phase(JuliaExtPassPhase::Late)
680            .with_max_iter(50)
681            .with_debug(1);
682        assert_eq!(c.name, "p");
683        assert_eq!(c.max_iterations, 50);
684        assert!(c.is_debug_enabled());
685        assert!(c.enabled);
686        let c2 = c.disabled();
687        assert!(!c2.enabled);
688    }
689    #[test]
690    pub(super) fn test_juliaext_stats() {
691        let mut s = JuliaExtPassStats::new();
692        s.visit();
693        s.visit();
694        s.modify();
695        s.iterate();
696        assert_eq!(s.nodes_visited, 2);
697        assert_eq!(s.nodes_modified, 1);
698        assert!(s.changed);
699        assert_eq!(s.iterations, 1);
700        let e = s.efficiency();
701        assert!((e - 0.5).abs() < 1e-9);
702    }
703    #[test]
704    pub(super) fn test_juliaext_registry() {
705        let mut r = JuliaExtPassRegistry::new();
706        r.register(JuliaExtPassConfig::new("a").with_phase(JuliaExtPassPhase::Early));
707        r.register(JuliaExtPassConfig::new("b").disabled());
708        assert_eq!(r.len(), 2);
709        assert_eq!(r.enabled_passes().len(), 1);
710        assert_eq!(r.passes_in_phase(&JuliaExtPassPhase::Early).len(), 1);
711    }
712    #[test]
713    pub(super) fn test_juliaext_cache() {
714        let mut c = JuliaExtCache::new(4);
715        assert!(c.get(99).is_none());
716        c.put(99, vec![1, 2, 3]);
717        let v = c.get(99).expect("v should be present in map");
718        assert_eq!(v, &[1u8, 2, 3]);
719        assert!(c.hit_rate() > 0.0);
720        assert_eq!(c.live_count(), 1);
721    }
722    #[test]
723    pub(super) fn test_juliaext_worklist() {
724        let mut w = JuliaExtWorklist::new(10);
725        w.push(5);
726        w.push(3);
727        w.push(5);
728        assert_eq!(w.len(), 2);
729        assert!(w.contains(5));
730        let first = w.pop().expect("first should be available to pop");
731        assert!(!w.contains(first));
732    }
733    #[test]
734    pub(super) fn test_juliaext_dom_tree() {
735        let mut dt = JuliaExtDomTree::new(5);
736        dt.set_idom(1, 0);
737        dt.set_idom(2, 0);
738        dt.set_idom(3, 1);
739        dt.set_idom(4, 1);
740        assert!(dt.dominates(0, 3));
741        assert!(dt.dominates(1, 4));
742        assert!(!dt.dominates(2, 3));
743        assert_eq!(dt.depth_of(3), 2);
744    }
745    #[test]
746    pub(super) fn test_juliaext_liveness() {
747        let mut lv = JuliaExtLiveness::new(3);
748        lv.add_def(0, 1);
749        lv.add_use(1, 1);
750        assert!(lv.var_is_def_in_block(0, 1));
751        assert!(lv.var_is_used_in_block(1, 1));
752        assert!(!lv.var_is_def_in_block(1, 1));
753    }
754    #[test]
755    pub(super) fn test_juliaext_const_folder() {
756        let mut cf = JuliaExtConstFolder::new();
757        assert_eq!(cf.add_i64(3, 4), Some(7));
758        assert_eq!(cf.div_i64(10, 0), None);
759        assert_eq!(cf.mul_i64(6, 7), Some(42));
760        assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
761        assert_eq!(cf.fold_count(), 3);
762        assert_eq!(cf.failure_count(), 1);
763    }
764    #[test]
765    pub(super) fn test_juliaext_dep_graph() {
766        let mut g = JuliaExtDepGraph::new(4);
767        g.add_edge(0, 1);
768        g.add_edge(1, 2);
769        g.add_edge(2, 3);
770        assert!(!g.has_cycle());
771        assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
772        assert_eq!(g.reachable(0).len(), 4);
773        let sccs = g.scc();
774        assert_eq!(sccs.len(), 4);
775    }
776}