1use std::collections::HashMap;
6
7use super::types::{
8 ElixirBackend, ElixirExpr, ElixirExtCache, ElixirExtConstFolder, ElixirExtDepGraph,
9 ElixirExtDomTree, ElixirExtLiveness, ElixirExtPassConfig, ElixirExtPassPhase,
10 ElixirExtPassRegistry, ElixirExtPassStats, ElixirExtWorklist, ElixirFunction, ElixirModule,
11 ElixirX2Cache, ElixirX2ConstFolder, ElixirX2DepGraph, ElixirX2DomTree, ElixirX2Liveness,
12 ElixirX2PassConfig, ElixirX2PassPhase, ElixirX2PassRegistry, ElixirX2PassStats,
13 ElixirX2Worklist, ElxAnalysisCache, ElxConstantFoldingHelper, ElxDepGraph, ElxDominatorTree,
14 ElxLivenessInfo, ElxPassConfig, ElxPassPhase, ElxPassRegistry, ElxPassStats, ElxWorklist,
15};
16
17pub(super) fn to_snake_case(s: &str) -> String {
19 let mut out = String::new();
20 let mut prev_upper = false;
21 for (i, c) in s.char_indices() {
22 if c.is_uppercase() {
23 if i > 0 && !prev_upper {
24 out.push('_');
25 }
26 out.extend(c.to_lowercase());
27 prev_upper = true;
28 } else {
29 out.push(c);
30 prev_upper = false;
31 }
32 }
33 out
34}
35pub(super) fn is_elixir_reserved(name: &str) -> bool {
37 matches!(
38 name,
39 "do" | "end"
40 | "fn"
41 | "in"
42 | "nil"
43 | "true"
44 | "false"
45 | "when"
46 | "and"
47 | "or"
48 | "not"
49 | "if"
50 | "else"
51 | "cond"
52 | "case"
53 | "receive"
54 | "after"
55 | "for"
56 | "try"
57 | "catch"
58 | "rescue"
59 | "with"
60 | "import"
61 | "use"
62 | "require"
63 | "alias"
64 | "defmodule"
65 | "def"
66 | "defp"
67 | "defmacro"
68 | "defmacrop"
69 | "defstruct"
70 | "defprotocol"
71 | "defimpl"
72 | "defdelegate"
73 )
74}
75pub(super) fn escape_elixir_string(s: &str) -> String {
77 s.replace('\\', "\\\\")
78 .replace('"', "\\\"")
79 .replace('\n', "\\n")
80 .replace('\r', "\\r")
81 .replace('\t', "\\t")
82}
83pub const ELIXIR_RUNTIME: &str = r#"defmodule OxiLean.Runtime do
84 @moduledoc """
85 OxiLean runtime support for Elixir-compiled code.
86
87 Provides:
88 - Algebraic-data-type helpers (tagged tuples)
89 - Basic numeric utilities
90 - Functional combinators
91 """
92
93 # ---- ADT constructors -------------------------------------------------
94
95 @doc "Wrap a value in a tagged tuple for algebraic data types."
96 def adt(tag, fields) when is_atom(tag) do
97 List.to_tuple([tag | fields])
98 end
99
100 @doc "Extract the tag from an ADT tagged tuple."
101 def adt_tag(t) when is_tuple(t), do: elem(t, 0)
102
103 # ---- Numeric utilities ------------------------------------------------
104
105 @doc "Integer power: base^exp (exp >= 0)."
106 def ipow(_base, 0), do: 1
107 def ipow(base, exp) when rem(exp, 2) == 0 do
108 half = ipow(base, div(exp, 2))
109 half * half
110 end
111 def ipow(base, exp), do: base * ipow(base, exp - 1)
112
113 @doc "Clamp a value to [lo, hi]."
114 def clamp(v, lo, hi), do: max(lo, min(hi, v))
115
116 # ---- Functional combinators -------------------------------------------
117
118 @doc "Function identity."
119 def id(x), do: x
120
121 @doc "Constant function: always returns `a`."
122 def const(a, _b), do: a
123
124 @doc "Flip argument order of a two-argument function."
125 def flip(f, a, b), do: f.(b, a)
126
127 @doc "Compose two functions: `compose(f, g).(x) == f.(g.(x))`."
128 def compose(f, g), do: fn x -> f.(g.(x)) end
129
130 # ---- Option/Maybe helpers ---------------------------------------------
131
132 @doc "Wrap in `{:some, v}` or return `:none`."
133 def some(v), do: {:some, v}
134 def none, do: :none
135
136 @doc "Unwrap an option, returning `default` on `:none`."
137 def option_get({:some, v}, _default), do: v
138 def option_get(:none, default), do: default
139
140 # ---- List helpers -----------------------------------------------------
141
142 @doc "Safe head: returns `{:some, head}` or `:none`."
143 def list_head([h | _]), do: {:some, h}
144 def list_head([]), do: :none
145
146 @doc "Safe tail: returns `{:some, tail}` or `:none`."
147 def list_tail([_ | t]), do: {:some, t}
148 def list_tail([]), do: :none
149
150 @doc "Zip two lists into a list of 2-tuples."
151 def zip([], _), do: []
152 def zip(_, []), do: []
153 def zip([h1 | t1], [h2 | t2]), do: [{h1, h2} | zip(t1, t2)]
154end
155"#;
156#[cfg(test)]
157mod tests {
158 use super::*;
159 pub(super) fn backend() -> ElixirBackend {
160 ElixirBackend::new()
161 }
162 #[test]
163 pub(super) fn test_emit_atom() {
164 assert_eq!(
165 backend().emit_expr(&ElixirExpr::Atom("ok".to_string())),
166 ":ok"
167 );
168 }
169 #[test]
170 pub(super) fn test_emit_integer() {
171 assert_eq!(backend().emit_expr(&ElixirExpr::Integer(42)), "42");
172 assert_eq!(backend().emit_expr(&ElixirExpr::Integer(-7)), "-7");
173 }
174 #[test]
175 pub(super) fn test_emit_float() {
176 let s = backend().emit_expr(&ElixirExpr::Float(3.14));
177 assert!(s.contains("3.14"), "got: {}", s);
178 }
179 #[test]
180 pub(super) fn test_emit_binary() {
181 assert_eq!(
182 backend().emit_expr(&ElixirExpr::Binary("hello".to_string())),
183 "\"hello\""
184 );
185 }
186 #[test]
187 pub(super) fn test_emit_bool() {
188 assert_eq!(backend().emit_expr(&ElixirExpr::Bool(true)), "true");
189 assert_eq!(backend().emit_expr(&ElixirExpr::Bool(false)), "false");
190 }
191 #[test]
192 pub(super) fn test_emit_nil() {
193 assert_eq!(backend().emit_expr(&ElixirExpr::Nil), "nil");
194 }
195 #[test]
196 pub(super) fn test_emit_list() {
197 let e = ElixirExpr::List(vec![
198 ElixirExpr::Integer(1),
199 ElixirExpr::Integer(2),
200 ElixirExpr::Integer(3),
201 ]);
202 assert_eq!(backend().emit_expr(&e), "[1, 2, 3]");
203 let empty = ElixirExpr::List(vec![]);
204 assert_eq!(backend().emit_expr(&empty), "[]");
205 }
206 #[test]
207 pub(super) fn test_emit_tuple() {
208 let e = ElixirExpr::Tuple(vec![
209 ElixirExpr::Atom("ok".to_string()),
210 ElixirExpr::Integer(0),
211 ]);
212 assert_eq!(backend().emit_expr(&e), "{:ok, 0}");
213 }
214 #[test]
215 pub(super) fn test_emit_map() {
216 let e = ElixirExpr::Map(vec![(
217 ElixirExpr::Atom("key".to_string()),
218 ElixirExpr::Integer(1),
219 )]);
220 assert_eq!(backend().emit_expr(&e), "%{:key => 1}");
221 let empty = ElixirExpr::Map(vec![]);
222 assert_eq!(backend().emit_expr(&empty), "%{}");
223 }
224 #[test]
225 pub(super) fn test_emit_func_call() {
226 let e = ElixirExpr::FuncCall(
227 "Enum.map".to_string(),
228 vec![
229 ElixirExpr::Var("list".to_string()),
230 ElixirExpr::Var("f".to_string()),
231 ],
232 );
233 assert_eq!(backend().emit_expr(&e), "Enum.map(list, f)");
234 }
235 #[test]
236 pub(super) fn test_emit_binop() {
237 let e = ElixirExpr::BinOp(
238 "+".to_string(),
239 Box::new(ElixirExpr::Integer(1)),
240 Box::new(ElixirExpr::Integer(2)),
241 );
242 assert_eq!(backend().emit_expr(&e), "1 + 2");
243 }
244 #[test]
245 pub(super) fn test_emit_pipe() {
246 let e = ElixirExpr::Pipe(
247 Box::new(ElixirExpr::Var("list".to_string())),
248 Box::new(ElixirExpr::FuncCall("Enum.sort".to_string(), vec![])),
249 );
250 let s = backend().emit_expr(&e);
251 assert!(s.contains("|>"), "pipe missing: {}", s);
252 assert!(s.contains("list"), "lhs missing: {}", s);
253 }
254 #[test]
255 pub(super) fn test_mangle_name() {
256 let b = backend();
257 assert_eq!(b.mangle_name("fooBar"), "foo_bar");
258 assert_eq!(b.mangle_name("do"), "do_");
259 assert_eq!(b.mangle_name("myFunc"), "my_func");
260 }
261 #[test]
262 pub(super) fn test_emit_function() {
263 let func = ElixirFunction {
264 name: "add".to_string(),
265 arity: 2,
266 clauses: vec![(
267 vec![
268 ElixirExpr::Var("a".to_string()),
269 ElixirExpr::Var("b".to_string()),
270 ],
271 None,
272 ElixirExpr::BinOp(
273 "+".to_string(),
274 Box::new(ElixirExpr::Var("a".to_string())),
275 Box::new(ElixirExpr::Var("b".to_string())),
276 ),
277 )],
278 is_private: false,
279 doc: None,
280 };
281 let s = backend().emit_function(&func);
282 assert!(s.contains("def add(a, b) do"), "got: {}", s);
283 assert!(s.contains("a + b"), "got: {}", s);
284 }
285 #[test]
286 pub(super) fn test_emit_module() {
287 let module = ElixirModule {
288 name: "MyApp.Math".to_string(),
289 functions: vec![ElixirFunction {
290 name: "square".to_string(),
291 arity: 1,
292 clauses: vec![(
293 vec![ElixirExpr::Var("x".to_string())],
294 None,
295 ElixirExpr::BinOp(
296 "*".to_string(),
297 Box::new(ElixirExpr::Var("x".to_string())),
298 Box::new(ElixirExpr::Var("x".to_string())),
299 ),
300 )],
301 is_private: false,
302 doc: None,
303 }],
304 use_modules: vec![],
305 imports: vec![],
306 attributes: HashMap::new(),
307 };
308 let s = backend().emit_module(&module);
309 assert!(s.contains("defmodule MyApp.Math do"), "got: {}", s);
310 assert!(s.contains("def square(x) do"), "got: {}", s);
311 assert!(s.contains("x * x"), "got: {}", s);
312 assert!(s.ends_with("end\n"), "got: {}", s);
313 }
314 #[test]
315 pub(super) fn test_runtime_content() {
316 let rt = backend().emit_runtime();
317 assert!(rt.contains("OxiLean.Runtime"));
318 assert!(rt.contains("defmodule"));
319 assert!(rt.contains("def adt(tag, fields)"));
320 assert!(rt.contains("def ipow("));
321 assert!(rt.contains("def compose(f, g)"));
322 }
323 #[test]
324 pub(super) fn test_emit_case() {
325 let e = ElixirExpr::Case(
326 Box::new(ElixirExpr::Var("x".to_string())),
327 vec![
328 (ElixirExpr::Integer(0), ElixirExpr::Atom("zero".to_string())),
329 (
330 ElixirExpr::Var("_".to_string()),
331 ElixirExpr::Atom("nonzero".to_string()),
332 ),
333 ],
334 );
335 let s = backend().emit_expr(&e);
336 assert!(s.contains("case x do"), "got: {}", s);
337 assert!(s.contains(":zero"), "got: {}", s);
338 assert!(s.contains(":nonzero"), "got: {}", s);
339 }
340 #[test]
341 pub(super) fn test_emit_lambda() {
342 let e = ElixirExpr::Lambda(
343 vec!["x".to_string(), "y".to_string()],
344 Box::new(ElixirExpr::BinOp(
345 "+".to_string(),
346 Box::new(ElixirExpr::Var("x".to_string())),
347 Box::new(ElixirExpr::Var("y".to_string())),
348 )),
349 );
350 let s = backend().emit_expr(&e);
351 assert!(s.contains("fn x, y ->"), "got: {}", s);
352 assert!(s.contains("x + y"), "got: {}", s);
353 }
354 #[test]
355 pub(super) fn test_escape_string() {
356 assert_eq!(
357 backend().emit_expr(&ElixirExpr::Binary("say \"hi\"".to_string())),
358 r#""say \"hi\"""#
359 );
360 }
361 #[test]
362 pub(super) fn test_private_function() {
363 let func = ElixirFunction {
364 name: "helper".to_string(),
365 arity: 0,
366 clauses: vec![(vec![], None, ElixirExpr::Nil)],
367 is_private: true,
368 doc: None,
369 };
370 let s = backend().emit_function(&func);
371 assert!(s.contains("defp helper()"), "got: {}", s);
372 }
373 #[test]
374 pub(super) fn test_emit_if() {
375 let e = ElixirExpr::If(
376 Box::new(ElixirExpr::Bool(true)),
377 Box::new(ElixirExpr::Integer(1)),
378 Box::new(ElixirExpr::Integer(0)),
379 );
380 let s = backend().emit_expr(&e);
381 assert!(s.contains("if true do"), "got: {}", s);
382 assert!(s.contains("else"), "got: {}", s);
383 }
384}
385#[cfg(test)]
386mod Elx_infra_tests {
387 use super::*;
388 #[test]
389 pub(super) fn test_pass_config() {
390 let config = ElxPassConfig::new("test_pass", ElxPassPhase::Transformation);
391 assert!(config.enabled);
392 assert!(config.phase.is_modifying());
393 assert_eq!(config.phase.name(), "transformation");
394 }
395 #[test]
396 pub(super) fn test_pass_stats() {
397 let mut stats = ElxPassStats::new();
398 stats.record_run(10, 100, 3);
399 stats.record_run(20, 200, 5);
400 assert_eq!(stats.total_runs, 2);
401 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
402 assert!((stats.success_rate() - 1.0).abs() < 0.01);
403 let s = stats.format_summary();
404 assert!(s.contains("Runs: 2/2"));
405 }
406 #[test]
407 pub(super) fn test_pass_registry() {
408 let mut reg = ElxPassRegistry::new();
409 reg.register(ElxPassConfig::new("pass_a", ElxPassPhase::Analysis));
410 reg.register(ElxPassConfig::new("pass_b", ElxPassPhase::Transformation).disabled());
411 assert_eq!(reg.total_passes(), 2);
412 assert_eq!(reg.enabled_count(), 1);
413 reg.update_stats("pass_a", 5, 50, 2);
414 let stats = reg.get_stats("pass_a").expect("stats should exist");
415 assert_eq!(stats.total_changes, 5);
416 }
417 #[test]
418 pub(super) fn test_analysis_cache() {
419 let mut cache = ElxAnalysisCache::new(10);
420 cache.insert("key1".to_string(), vec![1, 2, 3]);
421 assert!(cache.get("key1").is_some());
422 assert!(cache.get("key2").is_none());
423 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
424 cache.invalidate("key1");
425 assert!(!cache.entries["key1"].valid);
426 assert_eq!(cache.size(), 1);
427 }
428 #[test]
429 pub(super) fn test_worklist() {
430 let mut wl = ElxWorklist::new();
431 assert!(wl.push(1));
432 assert!(wl.push(2));
433 assert!(!wl.push(1));
434 assert_eq!(wl.len(), 2);
435 assert_eq!(wl.pop(), Some(1));
436 assert!(!wl.contains(1));
437 assert!(wl.contains(2));
438 }
439 #[test]
440 pub(super) fn test_dominator_tree() {
441 let mut dt = ElxDominatorTree::new(5);
442 dt.set_idom(1, 0);
443 dt.set_idom(2, 0);
444 dt.set_idom(3, 1);
445 assert!(dt.dominates(0, 3));
446 assert!(dt.dominates(1, 3));
447 assert!(!dt.dominates(2, 3));
448 assert!(dt.dominates(3, 3));
449 }
450 #[test]
451 pub(super) fn test_liveness() {
452 let mut liveness = ElxLivenessInfo::new(3);
453 liveness.add_def(0, 1);
454 liveness.add_use(1, 1);
455 assert!(liveness.defs[0].contains(&1));
456 assert!(liveness.uses[1].contains(&1));
457 }
458 #[test]
459 pub(super) fn test_constant_folding() {
460 assert_eq!(ElxConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
461 assert_eq!(ElxConstantFoldingHelper::fold_div_i64(10, 0), None);
462 assert_eq!(ElxConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
463 assert_eq!(
464 ElxConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
465 0b1000
466 );
467 assert_eq!(ElxConstantFoldingHelper::fold_bitnot_i64(0), -1);
468 }
469 #[test]
470 pub(super) fn test_dep_graph() {
471 let mut g = ElxDepGraph::new();
472 g.add_dep(1, 2);
473 g.add_dep(2, 3);
474 g.add_dep(1, 3);
475 assert_eq!(g.dependencies_of(2), vec![1]);
476 let topo = g.topological_sort();
477 assert_eq!(topo.len(), 3);
478 assert!(!g.has_cycle());
479 let pos: std::collections::HashMap<u32, usize> =
480 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
481 assert!(pos[&1] < pos[&2]);
482 assert!(pos[&1] < pos[&3]);
483 assert!(pos[&2] < pos[&3]);
484 }
485}
486#[cfg(test)]
487mod elixirext_pass_tests {
488 use super::*;
489 #[test]
490 pub(super) fn test_elixirext_phase_order() {
491 assert_eq!(ElixirExtPassPhase::Early.order(), 0);
492 assert_eq!(ElixirExtPassPhase::Middle.order(), 1);
493 assert_eq!(ElixirExtPassPhase::Late.order(), 2);
494 assert_eq!(ElixirExtPassPhase::Finalize.order(), 3);
495 assert!(ElixirExtPassPhase::Early.is_early());
496 assert!(!ElixirExtPassPhase::Early.is_late());
497 }
498 #[test]
499 pub(super) fn test_elixirext_config_builder() {
500 let c = ElixirExtPassConfig::new("p")
501 .with_phase(ElixirExtPassPhase::Late)
502 .with_max_iter(50)
503 .with_debug(1);
504 assert_eq!(c.name, "p");
505 assert_eq!(c.max_iterations, 50);
506 assert!(c.is_debug_enabled());
507 assert!(c.enabled);
508 let c2 = c.disabled();
509 assert!(!c2.enabled);
510 }
511 #[test]
512 pub(super) fn test_elixirext_stats() {
513 let mut s = ElixirExtPassStats::new();
514 s.visit();
515 s.visit();
516 s.modify();
517 s.iterate();
518 assert_eq!(s.nodes_visited, 2);
519 assert_eq!(s.nodes_modified, 1);
520 assert!(s.changed);
521 assert_eq!(s.iterations, 1);
522 let e = s.efficiency();
523 assert!((e - 0.5).abs() < 1e-9);
524 }
525 #[test]
526 pub(super) fn test_elixirext_registry() {
527 let mut r = ElixirExtPassRegistry::new();
528 r.register(ElixirExtPassConfig::new("a").with_phase(ElixirExtPassPhase::Early));
529 r.register(ElixirExtPassConfig::new("b").disabled());
530 assert_eq!(r.len(), 2);
531 assert_eq!(r.enabled_passes().len(), 1);
532 assert_eq!(r.passes_in_phase(&ElixirExtPassPhase::Early).len(), 1);
533 }
534 #[test]
535 pub(super) fn test_elixirext_cache() {
536 let mut c = ElixirExtCache::new(4);
537 assert!(c.get(99).is_none());
538 c.put(99, vec![1, 2, 3]);
539 let v = c.get(99).expect("v should be present in map");
540 assert_eq!(v, &[1u8, 2, 3]);
541 assert!(c.hit_rate() > 0.0);
542 assert_eq!(c.live_count(), 1);
543 }
544 #[test]
545 pub(super) fn test_elixirext_worklist() {
546 let mut w = ElixirExtWorklist::new(10);
547 w.push(5);
548 w.push(3);
549 w.push(5);
550 assert_eq!(w.len(), 2);
551 assert!(w.contains(5));
552 let first = w.pop().expect("first should be available to pop");
553 assert!(!w.contains(first));
554 }
555 #[test]
556 pub(super) fn test_elixirext_dom_tree() {
557 let mut dt = ElixirExtDomTree::new(5);
558 dt.set_idom(1, 0);
559 dt.set_idom(2, 0);
560 dt.set_idom(3, 1);
561 dt.set_idom(4, 1);
562 assert!(dt.dominates(0, 3));
563 assert!(dt.dominates(1, 4));
564 assert!(!dt.dominates(2, 3));
565 assert_eq!(dt.depth_of(3), 2);
566 }
567 #[test]
568 pub(super) fn test_elixirext_liveness() {
569 let mut lv = ElixirExtLiveness::new(3);
570 lv.add_def(0, 1);
571 lv.add_use(1, 1);
572 assert!(lv.var_is_def_in_block(0, 1));
573 assert!(lv.var_is_used_in_block(1, 1));
574 assert!(!lv.var_is_def_in_block(1, 1));
575 }
576 #[test]
577 pub(super) fn test_elixirext_const_folder() {
578 let mut cf = ElixirExtConstFolder::new();
579 assert_eq!(cf.add_i64(3, 4), Some(7));
580 assert_eq!(cf.div_i64(10, 0), None);
581 assert_eq!(cf.mul_i64(6, 7), Some(42));
582 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
583 assert_eq!(cf.fold_count(), 3);
584 assert_eq!(cf.failure_count(), 1);
585 }
586 #[test]
587 pub(super) fn test_elixirext_dep_graph() {
588 let mut g = ElixirExtDepGraph::new(4);
589 g.add_edge(0, 1);
590 g.add_edge(1, 2);
591 g.add_edge(2, 3);
592 assert!(!g.has_cycle());
593 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
594 assert_eq!(g.reachable(0).len(), 4);
595 let sccs = g.scc();
596 assert_eq!(sccs.len(), 4);
597 }
598}
599#[cfg(test)]
600mod elixirx2_pass_tests {
601 use super::*;
602 #[test]
603 pub(super) fn test_elixirx2_phase_order() {
604 assert_eq!(ElixirX2PassPhase::Early.order(), 0);
605 assert_eq!(ElixirX2PassPhase::Middle.order(), 1);
606 assert_eq!(ElixirX2PassPhase::Late.order(), 2);
607 assert_eq!(ElixirX2PassPhase::Finalize.order(), 3);
608 assert!(ElixirX2PassPhase::Early.is_early());
609 assert!(!ElixirX2PassPhase::Early.is_late());
610 }
611 #[test]
612 pub(super) fn test_elixirx2_config_builder() {
613 let c = ElixirX2PassConfig::new("p")
614 .with_phase(ElixirX2PassPhase::Late)
615 .with_max_iter(50)
616 .with_debug(1);
617 assert_eq!(c.name, "p");
618 assert_eq!(c.max_iterations, 50);
619 assert!(c.is_debug_enabled());
620 assert!(c.enabled);
621 let c2 = c.disabled();
622 assert!(!c2.enabled);
623 }
624 #[test]
625 pub(super) fn test_elixirx2_stats() {
626 let mut s = ElixirX2PassStats::new();
627 s.visit();
628 s.visit();
629 s.modify();
630 s.iterate();
631 assert_eq!(s.nodes_visited, 2);
632 assert_eq!(s.nodes_modified, 1);
633 assert!(s.changed);
634 assert_eq!(s.iterations, 1);
635 let e = s.efficiency();
636 assert!((e - 0.5).abs() < 1e-9);
637 }
638 #[test]
639 pub(super) fn test_elixirx2_registry() {
640 let mut r = ElixirX2PassRegistry::new();
641 r.register(ElixirX2PassConfig::new("a").with_phase(ElixirX2PassPhase::Early));
642 r.register(ElixirX2PassConfig::new("b").disabled());
643 assert_eq!(r.len(), 2);
644 assert_eq!(r.enabled_passes().len(), 1);
645 assert_eq!(r.passes_in_phase(&ElixirX2PassPhase::Early).len(), 1);
646 }
647 #[test]
648 pub(super) fn test_elixirx2_cache() {
649 let mut c = ElixirX2Cache::new(4);
650 assert!(c.get(99).is_none());
651 c.put(99, vec![1, 2, 3]);
652 let v = c.get(99).expect("v should be present in map");
653 assert_eq!(v, &[1u8, 2, 3]);
654 assert!(c.hit_rate() > 0.0);
655 assert_eq!(c.live_count(), 1);
656 }
657 #[test]
658 pub(super) fn test_elixirx2_worklist() {
659 let mut w = ElixirX2Worklist::new(10);
660 w.push(5);
661 w.push(3);
662 w.push(5);
663 assert_eq!(w.len(), 2);
664 assert!(w.contains(5));
665 let first = w.pop().expect("first should be available to pop");
666 assert!(!w.contains(first));
667 }
668 #[test]
669 pub(super) fn test_elixirx2_dom_tree() {
670 let mut dt = ElixirX2DomTree::new(5);
671 dt.set_idom(1, 0);
672 dt.set_idom(2, 0);
673 dt.set_idom(3, 1);
674 dt.set_idom(4, 1);
675 assert!(dt.dominates(0, 3));
676 assert!(dt.dominates(1, 4));
677 assert!(!dt.dominates(2, 3));
678 assert_eq!(dt.depth_of(3), 2);
679 }
680 #[test]
681 pub(super) fn test_elixirx2_liveness() {
682 let mut lv = ElixirX2Liveness::new(3);
683 lv.add_def(0, 1);
684 lv.add_use(1, 1);
685 assert!(lv.var_is_def_in_block(0, 1));
686 assert!(lv.var_is_used_in_block(1, 1));
687 assert!(!lv.var_is_def_in_block(1, 1));
688 }
689 #[test]
690 pub(super) fn test_elixirx2_const_folder() {
691 let mut cf = ElixirX2ConstFolder::new();
692 assert_eq!(cf.add_i64(3, 4), Some(7));
693 assert_eq!(cf.div_i64(10, 0), None);
694 assert_eq!(cf.mul_i64(6, 7), Some(42));
695 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
696 assert_eq!(cf.fold_count(), 3);
697 assert_eq!(cf.failure_count(), 1);
698 }
699 #[test]
700 pub(super) fn test_elixirx2_dep_graph() {
701 let mut g = ElixirX2DepGraph::new(4);
702 g.add_edge(0, 1);
703 g.add_edge(1, 2);
704 g.add_edge(2, 3);
705 assert!(!g.has_cycle());
706 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
707 assert_eq!(g.reachable(0).len(), 4);
708 let sccs = g.scc();
709 assert_eq!(sccs.len(), 4);
710 }
711}