1use super::types::{
6 AgdaAnalysisCache, AgdaClause, AgdaConstantFoldingHelper, AgdaConstructor, AgdaData, AgdaDecl,
7 AgdaDepGraph, AgdaDominatorTree, AgdaExpr, AgdaExtCache, AgdaExtConstFolder, AgdaExtDepGraph,
8 AgdaExtDomTree, AgdaExtLiveness, AgdaExtPassConfig, AgdaExtPassPhase, AgdaExtPassRegistry,
9 AgdaExtPassStats, AgdaExtWorklist, AgdaField, AgdaLivenessInfo, AgdaModule, AgdaPassConfig,
10 AgdaPassPhase, AgdaPassRegistry, AgdaPassStats, AgdaPattern, AgdaRecord, AgdaWorklist,
11 AgdaX2Cache, AgdaX2ConstFolder, AgdaX2DepGraph, AgdaX2DomTree, AgdaX2Liveness,
12 AgdaX2PassConfig, AgdaX2PassPhase, AgdaX2PassRegistry, AgdaX2PassStats, AgdaX2Worklist,
13};
14
15pub(super) fn escape_agda_string(s: &str) -> String {
17 let mut out = String::with_capacity(s.len());
18 for c in s.chars() {
19 match c {
20 '"' => out.push_str("\\\""),
21 '\\' => out.push_str("\\\\"),
22 '\n' => out.push_str("\\n"),
23 '\t' => out.push_str("\\t"),
24 c => out.push(c),
25 }
26 }
27 out
28}
29pub(super) fn emit_agda_params(params: &[(String, AgdaExpr)], indent: usize) -> String {
31 params
32 .iter()
33 .map(|(x, ty)| format!("({} : {})", x, ty.emit(indent)))
34 .collect::<Vec<_>>()
35 .join(" ")
36}
37pub fn var(name: impl Into<String>) -> AgdaExpr {
39 AgdaExpr::Var(name.into())
40}
41pub fn app(func: AgdaExpr, args: Vec<AgdaExpr>) -> AgdaExpr {
43 args.into_iter()
44 .fold(func, |acc, a| AgdaExpr::App(Box::new(acc), Box::new(a)))
45}
46pub fn app1(func: AgdaExpr, arg: AgdaExpr) -> AgdaExpr {
48 AgdaExpr::App(Box::new(func), Box::new(arg))
49}
50pub fn arrow(dom: AgdaExpr, cod: AgdaExpr) -> AgdaExpr {
52 AgdaExpr::Pi(None, Box::new(dom), Box::new(cod))
53}
54pub fn pi(x: impl Into<String>, dom: AgdaExpr, cod: AgdaExpr) -> AgdaExpr {
56 AgdaExpr::Pi(Some(x.into()), Box::new(dom), Box::new(cod))
57}
58pub fn lam(x: impl Into<String>, body: AgdaExpr) -> AgdaExpr {
60 AgdaExpr::Lambda(x.into(), Box::new(body))
61}
62#[cfg(test)]
63mod tests {
64 use super::*;
65 pub(super) fn nat() -> AgdaExpr {
66 var("ℕ")
67 }
68 pub(super) fn set0() -> AgdaExpr {
69 AgdaExpr::Set(None)
70 }
71 pub(super) fn bool_t() -> AgdaExpr {
72 var("Bool")
73 }
74 #[test]
75 pub(super) fn test_pattern_var() {
76 assert_eq!(AgdaPattern::Var("n".into()).to_string(), "n");
77 }
78 #[test]
79 pub(super) fn test_pattern_wildcard() {
80 assert_eq!(AgdaPattern::Wildcard.to_string(), "_");
81 }
82 #[test]
83 pub(super) fn test_pattern_con_nullary() {
84 assert_eq!(AgdaPattern::Con("zero".into(), vec![]).to_string(), "zero");
85 }
86 #[test]
87 pub(super) fn test_pattern_con_unary() {
88 let p = AgdaPattern::Con("suc".into(), vec![AgdaPattern::Var("n".into())]);
89 assert_eq!(p.to_string(), "(suc n)");
90 }
91 #[test]
92 pub(super) fn test_pattern_absurd() {
93 assert_eq!(AgdaPattern::Absurd.to_string(), "()");
94 }
95 #[test]
96 pub(super) fn test_pattern_implicit() {
97 let p = AgdaPattern::Implicit(Box::new(AgdaPattern::Var("A".into())));
98 assert_eq!(p.to_string(), "{A}");
99 }
100 #[test]
101 pub(super) fn test_pattern_as() {
102 let p = AgdaPattern::As(
103 "xs".into(),
104 Box::new(AgdaPattern::Con(
105 "_∷_".into(),
106 vec![AgdaPattern::Var("x".into()), AgdaPattern::Var("xt".into())],
107 )),
108 );
109 assert_eq!(p.to_string(), "xs@(_∷_ x xt)");
110 }
111 #[test]
112 pub(super) fn test_expr_var() {
113 assert_eq!(var("ℕ").emit(0), "ℕ");
114 }
115 #[test]
116 pub(super) fn test_expr_num() {
117 assert_eq!(AgdaExpr::Num(42).emit(0), "42");
118 }
119 #[test]
120 pub(super) fn test_expr_str() {
121 assert_eq!(AgdaExpr::Str("hello".into()).emit(0), "\"hello\"");
122 }
123 #[test]
124 pub(super) fn test_expr_set() {
125 assert_eq!(AgdaExpr::Set(None).emit(0), "Set");
126 assert_eq!(AgdaExpr::Set(Some(0)).emit(0), "Set");
127 assert_eq!(AgdaExpr::Set(Some(1)).emit(0), "Set₁");
128 }
129 #[test]
130 pub(super) fn test_expr_prop() {
131 assert_eq!(AgdaExpr::Prop.emit(0), "Prop");
132 }
133 #[test]
134 pub(super) fn test_expr_hole() {
135 assert_eq!(AgdaExpr::Hole.emit(0), "{! !}");
136 }
137 #[test]
138 pub(super) fn test_expr_app_simple() {
139 let t = app1(var("suc"), var("n"));
140 assert_eq!(t.emit(0), "suc n");
141 }
142 #[test]
143 pub(super) fn test_expr_app_chain() {
144 let t = app(var("_+_"), vec![var("m"), var("n")]);
145 assert_eq!(t.emit(0), "_+_ m n");
146 }
147 #[test]
148 pub(super) fn test_expr_app_nested_parens() {
149 let inner = app1(var("suc"), var("n"));
150 let outer = app1(var("suc"), inner);
151 assert_eq!(outer.emit(0), "suc (suc n)");
152 }
153 #[test]
154 pub(super) fn test_expr_lambda() {
155 let t = lam("n", app1(var("suc"), var("n")));
156 assert_eq!(t.emit(0), "λ n → suc n");
157 }
158 #[test]
159 pub(super) fn test_expr_arrow() {
160 let t = arrow(nat(), nat());
161 assert_eq!(t.emit(0), "ℕ → ℕ");
162 }
163 #[test]
164 pub(super) fn test_expr_pi_dependent() {
165 let t = pi("n", nat(), app1(var("Vec"), var("n")));
166 assert_eq!(t.emit(0), "(n : ℕ) → Vec n");
167 }
168 #[test]
169 pub(super) fn test_expr_implicit() {
170 let t = AgdaExpr::Implicit(Box::new(var("A")));
171 assert_eq!(t.emit(0), "{A}");
172 }
173 #[test]
174 pub(super) fn test_expr_tuple() {
175 let t = AgdaExpr::Tuple(vec![AgdaExpr::Num(1), AgdaExpr::Num(2)]);
176 assert_eq!(t.emit(0), "(1 , 2)");
177 }
178 #[test]
179 pub(super) fn test_expr_record_construction() {
180 let t = AgdaExpr::Record(vec![
181 ("fst".into(), AgdaExpr::Num(1)),
182 ("snd".into(), AgdaExpr::Num(2)),
183 ]);
184 assert_eq!(t.emit(0), "record { fst = 1 ; snd = 2 }");
185 }
186 #[test]
187 pub(super) fn test_expr_ascription() {
188 let t = AgdaExpr::Ascription(Box::new(AgdaExpr::Num(0)), Box::new(nat()));
189 assert_eq!(t.emit(0), "(0 : ℕ)");
190 }
191 #[test]
192 pub(super) fn test_expr_if_then_else() {
193 let t = AgdaExpr::IfThenElse(
194 Box::new(var("b")),
195 Box::new(AgdaExpr::Num(1)),
196 Box::new(AgdaExpr::Num(0)),
197 );
198 assert_eq!(t.emit(0), "if b then 1 else 0");
199 }
200 #[test]
201 pub(super) fn test_clause_base_case() {
202 let c = AgdaClause {
203 patterns: vec![AgdaPattern::Con("zero".into(), vec![])],
204 rhs: Some(AgdaExpr::Num(0)),
205 where_decls: vec![],
206 };
207 let s = c.emit_clause("factorial", 0);
208 assert_eq!(s, "factorial zero = 0");
209 }
210 #[test]
211 pub(super) fn test_clause_recursive() {
212 let c = AgdaClause {
213 patterns: vec![AgdaPattern::Con(
214 "suc".into(),
215 vec![AgdaPattern::Var("n".into())],
216 )],
217 rhs: Some(app(
218 var("_*_"),
219 vec![app1(var("suc"), var("n")), app1(var("factorial"), var("n"))],
220 )),
221 where_decls: vec![],
222 };
223 let s = c.emit_clause("factorial", 0);
224 assert!(s.starts_with("factorial (suc n) ="));
225 }
226 #[test]
227 pub(super) fn test_decl_func_type() {
228 let d = AgdaDecl::FuncType {
229 name: "double".into(),
230 ty: arrow(nat(), nat()),
231 };
232 assert_eq!(d.emit(0), "double : ℕ → ℕ");
233 }
234 #[test]
235 pub(super) fn test_decl_func_def_two_clauses() {
236 let d = AgdaDecl::FuncDef {
237 name: "_+_".into(),
238 clauses: vec![
239 AgdaClause {
240 patterns: vec![
241 AgdaPattern::Con("zero".into(), vec![]),
242 AgdaPattern::Var("m".into()),
243 ],
244 rhs: Some(var("m")),
245 where_decls: vec![],
246 },
247 AgdaClause {
248 patterns: vec![
249 AgdaPattern::Con("suc".into(), vec![AgdaPattern::Var("n".into())]),
250 AgdaPattern::Var("m".into()),
251 ],
252 rhs: Some(app1(var("suc"), app(var("_+_"), vec![var("n"), var("m")]))),
253 where_decls: vec![],
254 },
255 ],
256 };
257 let s = d.emit(0);
258 assert!(s.contains("_+_ zero m = m"));
259 assert!(s.contains("_+_ (suc n) m = suc (_+_ n m)"));
260 }
261 #[test]
262 pub(super) fn test_decl_data_nat() {
263 let d = AgdaDecl::DataDecl(AgdaData {
264 name: "ℕ".into(),
265 params: vec![],
266 indices: set0(),
267 constructors: vec![
268 AgdaConstructor {
269 name: "zero".into(),
270 ty: var("ℕ"),
271 },
272 AgdaConstructor {
273 name: "suc".into(),
274 ty: arrow(var("ℕ"), var("ℕ")),
275 },
276 ],
277 });
278 let s = d.emit(0);
279 assert!(s.contains("data ℕ : Set where"));
280 assert!(s.contains("zero : ℕ"));
281 assert!(s.contains("suc : ℕ → ℕ"));
282 }
283 #[test]
284 pub(super) fn test_decl_data_list() {
285 let d = AgdaDecl::DataDecl(AgdaData {
286 name: "List".into(),
287 params: vec![("A".into(), set0())],
288 indices: set0(),
289 constructors: vec![
290 AgdaConstructor {
291 name: "[]".into(),
292 ty: app1(var("List"), var("A")),
293 },
294 AgdaConstructor {
295 name: "_∷_".into(),
296 ty: arrow(
297 var("A"),
298 arrow(app1(var("List"), var("A")), app1(var("List"), var("A"))),
299 ),
300 },
301 ],
302 });
303 let s = d.emit(0);
304 assert!(s.contains("data List (A : Set) : Set where"));
305 assert!(s.contains("[] : List A"));
306 assert!(s.contains("_∷_ : A → List A → List A"));
307 }
308 #[test]
309 pub(super) fn test_decl_record_sigma() {
310 let d = AgdaDecl::RecordDecl(AgdaRecord {
311 name: "Σ".into(),
312 params: vec![("A".into(), set0()), ("B".into(), arrow(var("A"), set0()))],
313 universe: set0(),
314 constructor: Some("_,_".into()),
315 fields: vec![
316 AgdaField {
317 name: "fst".into(),
318 ty: var("A"),
319 },
320 AgdaField {
321 name: "snd".into(),
322 ty: app1(var("B"), var("fst")),
323 },
324 ],
325 copattern_defs: vec![],
326 });
327 let s = d.emit(0);
328 assert!(s.contains("record Σ (A : Set) (B : A → Set) : Set where"));
329 assert!(s.contains("constructor _,_"));
330 assert!(s.contains("fst : A"));
331 assert!(s.contains("snd : B fst"));
332 }
333 #[test]
334 pub(super) fn test_decl_postulate() {
335 let d = AgdaDecl::Postulate(vec![(
336 "LEM".into(),
337 pi(
338 "P",
339 var("Prop"),
340 app(var("_⊎_"), vec![var("P"), app1(var("¬"), var("P"))]),
341 ),
342 )]);
343 let s = d.emit(0);
344 assert!(s.starts_with("postulate"));
345 assert!(s.contains("LEM : (P : Prop) → _⊎_ P (¬ P)"));
346 }
347 #[test]
348 pub(super) fn test_decl_import() {
349 let d = AgdaDecl::Import("Data.Nat".into());
350 assert_eq!(d.emit(0), "import Data.Nat");
351 }
352 #[test]
353 pub(super) fn test_decl_open() {
354 let d = AgdaDecl::Open("Data.Nat".into());
355 assert_eq!(d.emit(0), "open Data.Nat");
356 }
357 #[test]
358 pub(super) fn test_decl_variable() {
359 let d = AgdaDecl::Variable(vec![("A".into(), set0()), ("B".into(), set0())]);
360 let s = d.emit(0);
361 assert!(s.contains("variable"));
362 assert!(s.contains("{A : Set}"));
363 assert!(s.contains("{B : Set}"));
364 }
365 #[test]
366 pub(super) fn test_decl_module() {
367 let d = AgdaDecl::ModuleDecl {
368 name: "Inner".into(),
369 params: vec![],
370 body: vec![AgdaDecl::FuncType {
371 name: "id".into(),
372 ty: arrow(bool_t(), bool_t()),
373 }],
374 };
375 let s = d.emit(0);
376 assert!(s.contains("module Inner where"));
377 assert!(s.contains("id : Bool → Bool"));
378 }
379 #[test]
380 pub(super) fn test_decl_pragma() {
381 let d = AgdaDecl::Pragma("BUILTIN NATURAL ℕ".into());
382 assert_eq!(d.emit(0), "{-# BUILTIN NATURAL ℕ #-}");
383 }
384 #[test]
385 pub(super) fn test_decl_comment() {
386 let d = AgdaDecl::Comment("Identity function".into());
387 assert_eq!(d.emit(0), "-- Identity function");
388 }
389 #[test]
390 pub(super) fn test_module_emit_basic() {
391 let mut m = AgdaModule::new("MyModule");
392 m.import("Data.Nat");
393 m.open("Data.Nat");
394 m.add(AgdaDecl::Comment("a function".into()));
395 m.add(AgdaDecl::FuncType {
396 name: "f".into(),
397 ty: arrow(nat(), nat()),
398 });
399 m.add(AgdaDecl::FuncDef {
400 name: "f".into(),
401 clauses: vec![AgdaClause {
402 patterns: vec![AgdaPattern::Var("n".into())],
403 rhs: Some(var("n")),
404 where_decls: vec![],
405 }],
406 });
407 let s = m.emit();
408 assert!(s.contains("module MyModule where"));
409 assert!(s.contains("import Data.Nat"));
410 assert!(s.contains("open Data.Nat"));
411 assert!(s.contains("-- a function"));
412 assert!(s.contains("f : ℕ → ℕ"));
413 assert!(s.contains("f n = n"));
414 }
415 #[test]
416 pub(super) fn test_module_parameterised() {
417 let mut m = AgdaModule::new("Container");
418 m.params.push(("A".into(), set0()));
419 let s = m.emit();
420 assert!(s.contains("module Container (A : Set) where"));
421 }
422 #[test]
423 pub(super) fn test_module_full_identity_proof() {
424 let mut m = AgdaModule::new("IdentityProof");
425 m.import("Relation.Binary.PropositionalEquality");
426 m.open("Relation.Binary.PropositionalEquality");
427 m.add(AgdaDecl::FuncType {
428 name: "refl-is-refl".into(),
429 ty: pi(
430 "A",
431 set0(),
432 pi("x", var("A"), app(var("_≡_"), vec![var("x"), var("x")])),
433 ),
434 });
435 m.add(AgdaDecl::FuncDef {
436 name: "refl-is-refl".into(),
437 clauses: vec![AgdaClause {
438 patterns: vec![AgdaPattern::Wildcard, AgdaPattern::Wildcard],
439 rhs: Some(var("refl")),
440 where_decls: vec![],
441 }],
442 });
443 let s = m.emit();
444 assert!(s.contains("refl-is-refl : (A : Set) → (x : A) → _≡_ x x"));
445 assert!(s.contains("refl-is-refl _ _ = refl"));
446 }
447}
448#[cfg(test)]
449mod Agda_infra_tests {
450 use super::*;
451 #[test]
452 pub(super) fn test_pass_config() {
453 let config = AgdaPassConfig::new("test_pass", AgdaPassPhase::Transformation);
454 assert!(config.enabled);
455 assert!(config.phase.is_modifying());
456 assert_eq!(config.phase.name(), "transformation");
457 }
458 #[test]
459 pub(super) fn test_pass_stats() {
460 let mut stats = AgdaPassStats::new();
461 stats.record_run(10, 100, 3);
462 stats.record_run(20, 200, 5);
463 assert_eq!(stats.total_runs, 2);
464 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
465 assert!((stats.success_rate() - 1.0).abs() < 0.01);
466 let s = stats.format_summary();
467 assert!(s.contains("Runs: 2/2"));
468 }
469 #[test]
470 pub(super) fn test_pass_registry() {
471 let mut reg = AgdaPassRegistry::new();
472 reg.register(AgdaPassConfig::new("pass_a", AgdaPassPhase::Analysis));
473 reg.register(AgdaPassConfig::new("pass_b", AgdaPassPhase::Transformation).disabled());
474 assert_eq!(reg.total_passes(), 2);
475 assert_eq!(reg.enabled_count(), 1);
476 reg.update_stats("pass_a", 5, 50, 2);
477 let stats = reg.get_stats("pass_a").expect("stats should exist");
478 assert_eq!(stats.total_changes, 5);
479 }
480 #[test]
481 pub(super) fn test_analysis_cache() {
482 let mut cache = AgdaAnalysisCache::new(10);
483 cache.insert("key1".to_string(), vec![1, 2, 3]);
484 assert!(cache.get("key1").is_some());
485 assert!(cache.get("key2").is_none());
486 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
487 cache.invalidate("key1");
488 assert!(!cache.entries["key1"].valid);
489 assert_eq!(cache.size(), 1);
490 }
491 #[test]
492 pub(super) fn test_worklist() {
493 let mut wl = AgdaWorklist::new();
494 assert!(wl.push(1));
495 assert!(wl.push(2));
496 assert!(!wl.push(1));
497 assert_eq!(wl.len(), 2);
498 assert_eq!(wl.pop(), Some(1));
499 assert!(!wl.contains(1));
500 assert!(wl.contains(2));
501 }
502 #[test]
503 pub(super) fn test_dominator_tree() {
504 let mut dt = AgdaDominatorTree::new(5);
505 dt.set_idom(1, 0);
506 dt.set_idom(2, 0);
507 dt.set_idom(3, 1);
508 assert!(dt.dominates(0, 3));
509 assert!(dt.dominates(1, 3));
510 assert!(!dt.dominates(2, 3));
511 assert!(dt.dominates(3, 3));
512 }
513 #[test]
514 pub(super) fn test_liveness() {
515 let mut liveness = AgdaLivenessInfo::new(3);
516 liveness.add_def(0, 1);
517 liveness.add_use(1, 1);
518 assert!(liveness.defs[0].contains(&1));
519 assert!(liveness.uses[1].contains(&1));
520 }
521 #[test]
522 pub(super) fn test_constant_folding() {
523 assert_eq!(AgdaConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
524 assert_eq!(AgdaConstantFoldingHelper::fold_div_i64(10, 0), None);
525 assert_eq!(AgdaConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
526 assert_eq!(
527 AgdaConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
528 0b1000
529 );
530 assert_eq!(AgdaConstantFoldingHelper::fold_bitnot_i64(0), -1);
531 }
532 #[test]
533 pub(super) fn test_dep_graph() {
534 let mut g = AgdaDepGraph::new();
535 g.add_dep(1, 2);
536 g.add_dep(2, 3);
537 g.add_dep(1, 3);
538 assert_eq!(g.dependencies_of(2), vec![1]);
539 let topo = g.topological_sort();
540 assert_eq!(topo.len(), 3);
541 assert!(!g.has_cycle());
542 let pos: std::collections::HashMap<u32, usize> =
543 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
544 assert!(pos[&1] < pos[&2]);
545 assert!(pos[&1] < pos[&3]);
546 assert!(pos[&2] < pos[&3]);
547 }
548}
549#[cfg(test)]
550mod agdaext_pass_tests {
551 use super::*;
552 #[test]
553 pub(super) fn test_agdaext_phase_order() {
554 assert_eq!(AgdaExtPassPhase::Early.order(), 0);
555 assert_eq!(AgdaExtPassPhase::Middle.order(), 1);
556 assert_eq!(AgdaExtPassPhase::Late.order(), 2);
557 assert_eq!(AgdaExtPassPhase::Finalize.order(), 3);
558 assert!(AgdaExtPassPhase::Early.is_early());
559 assert!(!AgdaExtPassPhase::Early.is_late());
560 }
561 #[test]
562 pub(super) fn test_agdaext_config_builder() {
563 let c = AgdaExtPassConfig::new("p")
564 .with_phase(AgdaExtPassPhase::Late)
565 .with_max_iter(50)
566 .with_debug(1);
567 assert_eq!(c.name, "p");
568 assert_eq!(c.max_iterations, 50);
569 assert!(c.is_debug_enabled());
570 assert!(c.enabled);
571 let c2 = c.disabled();
572 assert!(!c2.enabled);
573 }
574 #[test]
575 pub(super) fn test_agdaext_stats() {
576 let mut s = AgdaExtPassStats::new();
577 s.visit();
578 s.visit();
579 s.modify();
580 s.iterate();
581 assert_eq!(s.nodes_visited, 2);
582 assert_eq!(s.nodes_modified, 1);
583 assert!(s.changed);
584 assert_eq!(s.iterations, 1);
585 let e = s.efficiency();
586 assert!((e - 0.5).abs() < 1e-9);
587 }
588 #[test]
589 pub(super) fn test_agdaext_registry() {
590 let mut r = AgdaExtPassRegistry::new();
591 r.register(AgdaExtPassConfig::new("a").with_phase(AgdaExtPassPhase::Early));
592 r.register(AgdaExtPassConfig::new("b").disabled());
593 assert_eq!(r.len(), 2);
594 assert_eq!(r.enabled_passes().len(), 1);
595 assert_eq!(r.passes_in_phase(&AgdaExtPassPhase::Early).len(), 1);
596 }
597 #[test]
598 pub(super) fn test_agdaext_cache() {
599 let mut c = AgdaExtCache::new(4);
600 assert!(c.get(99).is_none());
601 c.put(99, vec![1, 2, 3]);
602 let v = c.get(99).expect("v should be present in map");
603 assert_eq!(v, &[1u8, 2, 3]);
604 assert!(c.hit_rate() > 0.0);
605 assert_eq!(c.live_count(), 1);
606 }
607 #[test]
608 pub(super) fn test_agdaext_worklist() {
609 let mut w = AgdaExtWorklist::new(10);
610 w.push(5);
611 w.push(3);
612 w.push(5);
613 assert_eq!(w.len(), 2);
614 assert!(w.contains(5));
615 let first = w.pop().expect("first should be available to pop");
616 assert!(!w.contains(first));
617 }
618 #[test]
619 pub(super) fn test_agdaext_dom_tree() {
620 let mut dt = AgdaExtDomTree::new(5);
621 dt.set_idom(1, 0);
622 dt.set_idom(2, 0);
623 dt.set_idom(3, 1);
624 dt.set_idom(4, 1);
625 assert!(dt.dominates(0, 3));
626 assert!(dt.dominates(1, 4));
627 assert!(!dt.dominates(2, 3));
628 assert_eq!(dt.depth_of(3), 2);
629 }
630 #[test]
631 pub(super) fn test_agdaext_liveness() {
632 let mut lv = AgdaExtLiveness::new(3);
633 lv.add_def(0, 1);
634 lv.add_use(1, 1);
635 assert!(lv.var_is_def_in_block(0, 1));
636 assert!(lv.var_is_used_in_block(1, 1));
637 assert!(!lv.var_is_def_in_block(1, 1));
638 }
639 #[test]
640 pub(super) fn test_agdaext_const_folder() {
641 let mut cf = AgdaExtConstFolder::new();
642 assert_eq!(cf.add_i64(3, 4), Some(7));
643 assert_eq!(cf.div_i64(10, 0), None);
644 assert_eq!(cf.mul_i64(6, 7), Some(42));
645 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
646 assert_eq!(cf.fold_count(), 3);
647 assert_eq!(cf.failure_count(), 1);
648 }
649 #[test]
650 pub(super) fn test_agdaext_dep_graph() {
651 let mut g = AgdaExtDepGraph::new(4);
652 g.add_edge(0, 1);
653 g.add_edge(1, 2);
654 g.add_edge(2, 3);
655 assert!(!g.has_cycle());
656 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
657 assert_eq!(g.reachable(0).len(), 4);
658 let sccs = g.scc();
659 assert_eq!(sccs.len(), 4);
660 }
661}
662#[cfg(test)]
663mod agdax2_pass_tests {
664 use super::*;
665 #[test]
666 pub(super) fn test_agdax2_phase_order() {
667 assert_eq!(AgdaX2PassPhase::Early.order(), 0);
668 assert_eq!(AgdaX2PassPhase::Middle.order(), 1);
669 assert_eq!(AgdaX2PassPhase::Late.order(), 2);
670 assert_eq!(AgdaX2PassPhase::Finalize.order(), 3);
671 assert!(AgdaX2PassPhase::Early.is_early());
672 assert!(!AgdaX2PassPhase::Early.is_late());
673 }
674 #[test]
675 pub(super) fn test_agdax2_config_builder() {
676 let c = AgdaX2PassConfig::new("p")
677 .with_phase(AgdaX2PassPhase::Late)
678 .with_max_iter(50)
679 .with_debug(1);
680 assert_eq!(c.name, "p");
681 assert_eq!(c.max_iterations, 50);
682 assert!(c.is_debug_enabled());
683 assert!(c.enabled);
684 let c2 = c.disabled();
685 assert!(!c2.enabled);
686 }
687 #[test]
688 pub(super) fn test_agdax2_stats() {
689 let mut s = AgdaX2PassStats::new();
690 s.visit();
691 s.visit();
692 s.modify();
693 s.iterate();
694 assert_eq!(s.nodes_visited, 2);
695 assert_eq!(s.nodes_modified, 1);
696 assert!(s.changed);
697 assert_eq!(s.iterations, 1);
698 let e = s.efficiency();
699 assert!((e - 0.5).abs() < 1e-9);
700 }
701 #[test]
702 pub(super) fn test_agdax2_registry() {
703 let mut r = AgdaX2PassRegistry::new();
704 r.register(AgdaX2PassConfig::new("a").with_phase(AgdaX2PassPhase::Early));
705 r.register(AgdaX2PassConfig::new("b").disabled());
706 assert_eq!(r.len(), 2);
707 assert_eq!(r.enabled_passes().len(), 1);
708 assert_eq!(r.passes_in_phase(&AgdaX2PassPhase::Early).len(), 1);
709 }
710 #[test]
711 pub(super) fn test_agdax2_cache() {
712 let mut c = AgdaX2Cache::new(4);
713 assert!(c.get(99).is_none());
714 c.put(99, vec![1, 2, 3]);
715 let v = c.get(99).expect("v should be present in map");
716 assert_eq!(v, &[1u8, 2, 3]);
717 assert!(c.hit_rate() > 0.0);
718 assert_eq!(c.live_count(), 1);
719 }
720 #[test]
721 pub(super) fn test_agdax2_worklist() {
722 let mut w = AgdaX2Worklist::new(10);
723 w.push(5);
724 w.push(3);
725 w.push(5);
726 assert_eq!(w.len(), 2);
727 assert!(w.contains(5));
728 let first = w.pop().expect("first should be available to pop");
729 assert!(!w.contains(first));
730 }
731 #[test]
732 pub(super) fn test_agdax2_dom_tree() {
733 let mut dt = AgdaX2DomTree::new(5);
734 dt.set_idom(1, 0);
735 dt.set_idom(2, 0);
736 dt.set_idom(3, 1);
737 dt.set_idom(4, 1);
738 assert!(dt.dominates(0, 3));
739 assert!(dt.dominates(1, 4));
740 assert!(!dt.dominates(2, 3));
741 assert_eq!(dt.depth_of(3), 2);
742 }
743 #[test]
744 pub(super) fn test_agdax2_liveness() {
745 let mut lv = AgdaX2Liveness::new(3);
746 lv.add_def(0, 1);
747 lv.add_use(1, 1);
748 assert!(lv.var_is_def_in_block(0, 1));
749 assert!(lv.var_is_used_in_block(1, 1));
750 assert!(!lv.var_is_def_in_block(1, 1));
751 }
752 #[test]
753 pub(super) fn test_agdax2_const_folder() {
754 let mut cf = AgdaX2ConstFolder::new();
755 assert_eq!(cf.add_i64(3, 4), Some(7));
756 assert_eq!(cf.div_i64(10, 0), None);
757 assert_eq!(cf.mul_i64(6, 7), Some(42));
758 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
759 assert_eq!(cf.fold_count(), 3);
760 assert_eq!(cf.failure_count(), 1);
761 }
762 #[test]
763 pub(super) fn test_agdax2_dep_graph() {
764 let mut g = AgdaX2DepGraph::new(4);
765 g.add_edge(0, 1);
766 g.add_edge(1, 2);
767 g.add_edge(2, 3);
768 assert!(!g.has_cycle());
769 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
770 assert_eq!(g.reachable(0).len(), 4);
771 let sccs = g.scc();
772 assert_eq!(sccs.len(), 4);
773 }
774}