1use 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}