1use crate::lcnf::*;
6use std::collections::{HashMap, HashSet};
7
8use super::types::{
9 CCAnalysisCache, CCConstantFoldingHelper, CCDepGraph, CCDominatorTree, CCExtCache,
10 CCExtConstFolder, CCExtDepGraph, CCExtDomTree, CCExtLiveness, CCExtPassConfig, CCExtPassPhase,
11 CCExtPassRegistry, CCExtPassStats, CCExtWorklist, CCLivenessInfo, CCPassConfig, CCPassPhase,
12 CCPassRegistry, CCPassStats, CCWorklist, CCX2Cache, CCX2ConstFolder, CCX2DepGraph, CCX2DomTree,
13 CCX2Liveness, CCX2PassConfig, CCX2PassPhase, CCX2PassRegistry, CCX2PassStats, CCX2Worklist,
14 ClosureConvertConfig, ClosureConvertStats, ClosureConverter, ClosureInfo, EscapeAnalysis,
15 EscapeInfo,
16};
17
18pub fn compute_free_vars(expr: &LcnfExpr, bound: &HashSet<LcnfVarId>) -> HashSet<LcnfVarId> {
22 let mut free = HashSet::new();
23 collect_free_vars(expr, bound, &mut free);
24 free
25}
26pub(super) fn collect_free_vars(
28 expr: &LcnfExpr,
29 bound: &HashSet<LcnfVarId>,
30 free: &mut HashSet<LcnfVarId>,
31) {
32 match expr {
33 LcnfExpr::Let {
34 id, value, body, ..
35 } => {
36 collect_free_vars_value(value, bound, free);
37 let mut new_bound = bound.clone();
38 new_bound.insert(*id);
39 collect_free_vars(body, &new_bound, free);
40 }
41 LcnfExpr::Case {
42 scrutinee,
43 alts,
44 default,
45 ..
46 } => {
47 if !bound.contains(scrutinee) {
48 free.insert(*scrutinee);
49 }
50 for alt in alts {
51 let mut alt_bound = bound.clone();
52 for param in &alt.params {
53 alt_bound.insert(param.id);
54 }
55 collect_free_vars(&alt.body, &alt_bound, free);
56 }
57 if let Some(def) = default {
58 collect_free_vars(def, bound, free);
59 }
60 }
61 LcnfExpr::Return(arg) => {
62 collect_free_vars_arg(arg, bound, free);
63 }
64 LcnfExpr::TailCall(func, args) => {
65 collect_free_vars_arg(func, bound, free);
66 for arg in args {
67 collect_free_vars_arg(arg, bound, free);
68 }
69 }
70 LcnfExpr::Unreachable => {}
71 }
72}
73pub(super) fn collect_free_vars_value(
75 value: &LcnfLetValue,
76 bound: &HashSet<LcnfVarId>,
77 free: &mut HashSet<LcnfVarId>,
78) {
79 match value {
80 LcnfLetValue::App(func, args) => {
81 collect_free_vars_arg(func, bound, free);
82 for arg in args {
83 collect_free_vars_arg(arg, bound, free);
84 }
85 }
86 LcnfLetValue::Ctor(_, _, args) => {
87 for arg in args {
88 collect_free_vars_arg(arg, bound, free);
89 }
90 }
91 LcnfLetValue::Proj(_, _, var) => {
92 if !bound.contains(var) {
93 free.insert(*var);
94 }
95 }
96 LcnfLetValue::FVar(var) => {
97 if !bound.contains(var) {
98 free.insert(*var);
99 }
100 }
101 LcnfLetValue::Lit(_)
102 | LcnfLetValue::Erased
103 | LcnfLetValue::Reset(_)
104 | LcnfLetValue::Reuse(_, _, _, _) => {}
105 }
106}
107pub(super) fn collect_free_vars_arg(
109 arg: &LcnfArg,
110 bound: &HashSet<LcnfVarId>,
111 free: &mut HashSet<LcnfVarId>,
112) {
113 if let LcnfArg::Var(v) = arg {
114 if !bound.contains(v) {
115 free.insert(*v);
116 }
117 }
118}
119#[cfg(test)]
120mod tests {
121 use super::*;
122 pub(super) fn vid(n: u64) -> LcnfVarId {
123 LcnfVarId(n)
124 }
125 pub(super) fn mk_param(n: u64, name: &str) -> LcnfParam {
126 LcnfParam {
127 id: vid(n),
128 name: name.to_string(),
129 ty: LcnfType::Nat,
130 erased: false,
131 borrowed: false,
132 }
133 }
134 pub(super) fn mk_fun_decl(name: &str, body: LcnfExpr) -> LcnfFunDecl {
135 LcnfFunDecl {
136 name: name.to_string(),
137 original_name: None,
138 params: vec![mk_param(0, "x")],
139 ret_type: LcnfType::Nat,
140 body,
141 is_recursive: false,
142 is_lifted: false,
143 inline_cost: 1,
144 }
145 }
146 pub(super) fn mk_let(id: u64, value: LcnfLetValue, body: LcnfExpr) -> LcnfExpr {
147 LcnfExpr::Let {
148 id: vid(id),
149 name: format!("x{}", id),
150 ty: LcnfType::Nat,
151 value,
152 body: Box::new(body),
153 }
154 }
155 pub(super) fn mk_module(decls: Vec<LcnfFunDecl>) -> LcnfModule {
156 LcnfModule {
157 fun_decls: decls,
158 extern_decls: vec![],
159 name: "test_mod".to_string(),
160 metadata: LcnfModuleMetadata::default(),
161 }
162 }
163 #[test]
164 pub(super) fn test_escape_info_merge() {
165 assert_eq!(
166 EscapeInfo::NoEscape.merge(EscapeInfo::NoEscape),
167 EscapeInfo::NoEscape
168 );
169 assert_eq!(
170 EscapeInfo::NoEscape.merge(EscapeInfo::LocalEscape),
171 EscapeInfo::LocalEscape
172 );
173 assert_eq!(
174 EscapeInfo::LocalEscape.merge(EscapeInfo::GlobalEscape),
175 EscapeInfo::GlobalEscape
176 );
177 assert_eq!(
178 EscapeInfo::GlobalEscape.merge(EscapeInfo::NoEscape),
179 EscapeInfo::GlobalEscape
180 );
181 }
182 #[test]
183 pub(super) fn test_escape_info_requires_heap() {
184 assert!(!EscapeInfo::NoEscape.requires_heap());
185 assert!(!EscapeInfo::LocalEscape.requires_heap());
186 assert!(EscapeInfo::GlobalEscape.requires_heap());
187 }
188 #[test]
189 pub(super) fn test_escape_info_display() {
190 assert_eq!(EscapeInfo::NoEscape.to_string(), "no-escape");
191 assert_eq!(EscapeInfo::LocalEscape.to_string(), "local-escape");
192 assert_eq!(EscapeInfo::GlobalEscape.to_string(), "global-escape");
193 }
194 #[test]
195 pub(super) fn test_closure_info_can_stack_allocate() {
196 let info = ClosureInfo {
197 free_vars: vec![vid(1), vid(2)],
198 captured_types: vec![LcnfType::Nat, LcnfType::Nat],
199 arity: 1,
200 is_escaping: false,
201 has_side_effects: false,
202 original_name: None,
203 };
204 assert!(info.can_stack_allocate());
205 let escaping = ClosureInfo {
206 is_escaping: true,
207 ..info.clone()
208 };
209 assert!(!escaping.can_stack_allocate());
210 }
211 #[test]
212 pub(super) fn test_closure_info_total_fields() {
213 let info = ClosureInfo {
214 free_vars: vec![vid(1), vid(2), vid(3)],
215 captured_types: vec![],
216 arity: 2,
217 is_escaping: false,
218 has_side_effects: false,
219 original_name: None,
220 };
221 assert_eq!(info.total_fields(), 4);
222 }
223 #[test]
224 pub(super) fn test_closure_info_display() {
225 let info = ClosureInfo {
226 free_vars: vec![vid(1)],
227 captured_types: vec![LcnfType::Nat],
228 arity: 2,
229 is_escaping: false,
230 has_side_effects: true,
231 original_name: None,
232 };
233 let s = info.to_string();
234 assert!(s.contains("arity=2"));
235 assert!(s.contains("captured=1"));
236 assert!(s.contains("side_effects=true"));
237 }
238 #[test]
239 pub(super) fn test_escape_analysis_simple_return() {
240 let body = LcnfExpr::Return(LcnfArg::Var(vid(0)));
241 let decl = mk_fun_decl("f", body);
242 let module = mk_module(vec![decl]);
243 let mut analysis = EscapeAnalysis::new();
244 analysis.analyze(&module);
245 assert_eq!(analysis.get_var_escape(vid(0)), EscapeInfo::GlobalEscape);
246 }
247 #[test]
248 pub(super) fn test_escape_analysis_let_no_escape() {
249 let body = mk_let(
250 1,
251 LcnfLetValue::Lit(LcnfLit::Nat(42)),
252 LcnfExpr::Return(LcnfArg::Var(vid(0))),
253 );
254 let decl = mk_fun_decl("f", body);
255 let module = mk_module(vec![decl]);
256 let mut analysis = EscapeAnalysis::new();
257 analysis.analyze(&module);
258 assert_eq!(analysis.get_var_escape(vid(1)), EscapeInfo::NoEscape);
259 }
260 #[test]
261 pub(super) fn test_escape_analysis_ctor_escape() {
262 let body = mk_let(
263 1,
264 LcnfLetValue::Ctor(
265 "Pair".into(),
266 0,
267 vec![LcnfArg::Var(vid(0)), LcnfArg::Var(vid(0))],
268 ),
269 LcnfExpr::Return(LcnfArg::Var(vid(1))),
270 );
271 let decl = mk_fun_decl("f", body);
272 let module = mk_module(vec![decl]);
273 let mut analysis = EscapeAnalysis::new();
274 analysis.analyze(&module);
275 assert_eq!(analysis.get_var_escape(vid(0)), EscapeInfo::GlobalEscape);
276 }
277 #[test]
278 pub(super) fn test_free_vars_return() {
279 let expr = LcnfExpr::Return(LcnfArg::Var(vid(5)));
280 let bound = HashSet::new();
281 let free = compute_free_vars(&expr, &bound);
282 assert!(free.contains(&vid(5)));
283 }
284 #[test]
285 pub(super) fn test_free_vars_let_binds() {
286 let expr = mk_let(
287 1,
288 LcnfLetValue::Lit(LcnfLit::Nat(42)),
289 LcnfExpr::Return(LcnfArg::Var(vid(1))),
290 );
291 let bound = HashSet::new();
292 let free = compute_free_vars(&expr, &bound);
293 assert!(!free.contains(&vid(1)));
294 }
295 #[test]
296 pub(super) fn test_free_vars_with_bound() {
297 let expr = LcnfExpr::Return(LcnfArg::Var(vid(0)));
298 let mut bound = HashSet::new();
299 bound.insert(vid(0));
300 let free = compute_free_vars(&expr, &bound);
301 assert!(!free.contains(&vid(0)));
302 }
303 #[test]
304 pub(super) fn test_free_vars_case() {
305 let expr = LcnfExpr::Case {
306 scrutinee: vid(0),
307 scrutinee_ty: LcnfType::Nat,
308 alts: vec![
309 LcnfAlt {
310 ctor_name: "A".into(),
311 ctor_tag: 0,
312 params: vec![mk_param(1, "a")],
313 body: LcnfExpr::Return(LcnfArg::Var(vid(1))),
314 },
315 LcnfAlt {
316 ctor_name: "B".into(),
317 ctor_tag: 1,
318 params: vec![],
319 body: LcnfExpr::Return(LcnfArg::Var(vid(5))),
320 },
321 ],
322 default: None,
323 };
324 let bound = HashSet::new();
325 let free = compute_free_vars(&expr, &bound);
326 assert!(free.contains(&vid(0)));
327 assert!(!free.contains(&vid(1)));
328 assert!(free.contains(&vid(5)));
329 }
330 #[test]
331 pub(super) fn test_free_vars_tail_call() {
332 let expr = LcnfExpr::TailCall(
333 LcnfArg::Var(vid(10)),
334 vec![LcnfArg::Var(vid(0)), LcnfArg::Var(vid(1))],
335 );
336 let bound = HashSet::new();
337 let free = compute_free_vars(&expr, &bound);
338 assert!(free.contains(&vid(10)));
339 assert!(free.contains(&vid(0)));
340 assert!(free.contains(&vid(1)));
341 }
342 #[test]
343 pub(super) fn test_closure_convert_identity() {
344 let body = LcnfExpr::Return(LcnfArg::Var(vid(0)));
345 let decl = mk_fun_decl("identity", body.clone());
346 let mut module = mk_module(vec![decl]);
347 let mut converter = ClosureConverter::default_converter();
348 converter.convert_module(&mut module);
349 assert_eq!(module.fun_decls.len(), 1);
350 assert_eq!(module.fun_decls[0].name, "identity");
351 }
352 #[test]
353 pub(super) fn test_closure_convert_let_chain() {
354 let body = mk_let(
355 1,
356 LcnfLetValue::Lit(LcnfLit::Nat(42)),
357 mk_let(
358 2,
359 LcnfLetValue::FVar(vid(1)),
360 LcnfExpr::Return(LcnfArg::Var(vid(2))),
361 ),
362 );
363 let decl = mk_fun_decl("chain", body);
364 let mut module = mk_module(vec![decl]);
365 let mut converter = ClosureConverter::default_converter();
366 converter.convert_module(&mut module);
367 assert!(!module.fun_decls.is_empty());
368 }
369 #[test]
370 pub(super) fn test_defunctionalize() {
371 let mut converter = ClosureConverter::new(ClosureConvertConfig {
372 defunctionalize: true,
373 ..Default::default()
374 });
375 let result = converter.defunctionalize(vid(5), &["add".to_string(), "sub".to_string()]);
376 assert!(result.is_some());
377 if let Some(LcnfExpr::Case { alts, .. }) = &result {
378 assert_eq!(alts.len(), 2);
379 assert_eq!(alts[0].ctor_name, "add");
380 assert_eq!(alts[1].ctor_name, "sub");
381 } else {
382 panic!("expected Case");
383 }
384 }
385 #[test]
386 pub(super) fn test_defunctionalize_disabled() {
387 let mut converter = ClosureConverter::new(ClosureConvertConfig {
388 defunctionalize: false,
389 ..Default::default()
390 });
391 let result = converter.defunctionalize(vid(5), &["f".to_string()]);
392 assert!(result.is_none());
393 }
394 #[test]
395 pub(super) fn test_stack_allocate_closure() {
396 let converter = ClosureConverter::default_converter();
397 let non_escaping = ClosureInfo {
398 free_vars: vec![vid(1), vid(2)],
399 captured_types: vec![],
400 arity: 1,
401 is_escaping: false,
402 has_side_effects: false,
403 original_name: None,
404 };
405 assert!(converter.stack_allocate_closure(&non_escaping));
406 let escaping = ClosureInfo {
407 is_escaping: true,
408 ..non_escaping.clone()
409 };
410 assert!(!converter.stack_allocate_closure(&escaping));
411 let too_many = ClosureInfo {
412 free_vars: vec![vid(1), vid(2), vid(3), vid(4), vid(5)],
413 ..non_escaping.clone()
414 };
415 assert!(!converter.stack_allocate_closure(&too_many));
416 }
417 #[test]
418 pub(super) fn test_closure_convert_stats_display() {
419 let stats = ClosureConvertStats {
420 closures_converted: 3,
421 helpers_lifted: 2,
422 defunctionalized: 1,
423 stack_allocated: 2,
424 heap_allocated: 1,
425 closures_merged: 0,
426 };
427 let s = stats.to_string();
428 assert!(s.contains("converted=3"));
429 assert!(s.contains("lifted=2"));
430 }
431 #[test]
432 pub(super) fn test_closure_convert_config_default() {
433 let cfg = ClosureConvertConfig::default();
434 assert!(cfg.defunctionalize);
435 assert!(cfg.stack_alloc_non_escaping);
436 assert_eq!(cfg.max_inline_captures, 4);
437 }
438 #[test]
439 pub(super) fn test_escape_analysis_fn_escape() {
440 let body = LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(0)));
441 let decl = mk_fun_decl("pure_fn", body);
442 let module = mk_module(vec![decl]);
443 let mut analysis = EscapeAnalysis::new();
444 let fn_info = analysis.analyze(&module);
445 assert!(fn_info.contains_key("pure_fn"));
446 }
447}
448#[cfg(test)]
449mod CC_infra_tests {
450 use super::*;
451 #[test]
452 pub(super) fn test_pass_config() {
453 let config = CCPassConfig::new("test_pass", CCPassPhase::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 = CCPassStats::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 = CCPassRegistry::new();
472 reg.register(CCPassConfig::new("pass_a", CCPassPhase::Analysis));
473 reg.register(CCPassConfig::new("pass_b", CCPassPhase::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 = CCAnalysisCache::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 = CCWorklist::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 = CCDominatorTree::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 = CCLivenessInfo::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!(CCConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
524 assert_eq!(CCConstantFoldingHelper::fold_div_i64(10, 0), None);
525 assert_eq!(CCConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
526 assert_eq!(
527 CCConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
528 0b1000
529 );
530 assert_eq!(CCConstantFoldingHelper::fold_bitnot_i64(0), -1);
531 }
532 #[test]
533 pub(super) fn test_dep_graph() {
534 let mut g = CCDepGraph::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 ccext_pass_tests {
551 use super::*;
552 #[test]
553 pub(super) fn test_ccext_phase_order() {
554 assert_eq!(CCExtPassPhase::Early.order(), 0);
555 assert_eq!(CCExtPassPhase::Middle.order(), 1);
556 assert_eq!(CCExtPassPhase::Late.order(), 2);
557 assert_eq!(CCExtPassPhase::Finalize.order(), 3);
558 assert!(CCExtPassPhase::Early.is_early());
559 assert!(!CCExtPassPhase::Early.is_late());
560 }
561 #[test]
562 pub(super) fn test_ccext_config_builder() {
563 let c = CCExtPassConfig::new("p")
564 .with_phase(CCExtPassPhase::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_ccext_stats() {
576 let mut s = CCExtPassStats::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_ccext_registry() {
590 let mut r = CCExtPassRegistry::new();
591 r.register(CCExtPassConfig::new("a").with_phase(CCExtPassPhase::Early));
592 r.register(CCExtPassConfig::new("b").disabled());
593 assert_eq!(r.len(), 2);
594 assert_eq!(r.enabled_passes().len(), 1);
595 assert_eq!(r.passes_in_phase(&CCExtPassPhase::Early).len(), 1);
596 }
597 #[test]
598 pub(super) fn test_ccext_cache() {
599 let mut c = CCExtCache::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_ccext_worklist() {
609 let mut w = CCExtWorklist::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_ccext_dom_tree() {
620 let mut dt = CCExtDomTree::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_ccext_liveness() {
632 let mut lv = CCExtLiveness::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_ccext_const_folder() {
641 let mut cf = CCExtConstFolder::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_ccext_dep_graph() {
651 let mut g = CCExtDepGraph::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 ccx2_pass_tests {
664 use super::*;
665 #[test]
666 pub(super) fn test_ccx2_phase_order() {
667 assert_eq!(CCX2PassPhase::Early.order(), 0);
668 assert_eq!(CCX2PassPhase::Middle.order(), 1);
669 assert_eq!(CCX2PassPhase::Late.order(), 2);
670 assert_eq!(CCX2PassPhase::Finalize.order(), 3);
671 assert!(CCX2PassPhase::Early.is_early());
672 assert!(!CCX2PassPhase::Early.is_late());
673 }
674 #[test]
675 pub(super) fn test_ccx2_config_builder() {
676 let c = CCX2PassConfig::new("p")
677 .with_phase(CCX2PassPhase::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_ccx2_stats() {
689 let mut s = CCX2PassStats::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_ccx2_registry() {
703 let mut r = CCX2PassRegistry::new();
704 r.register(CCX2PassConfig::new("a").with_phase(CCX2PassPhase::Early));
705 r.register(CCX2PassConfig::new("b").disabled());
706 assert_eq!(r.len(), 2);
707 assert_eq!(r.enabled_passes().len(), 1);
708 assert_eq!(r.passes_in_phase(&CCX2PassPhase::Early).len(), 1);
709 }
710 #[test]
711 pub(super) fn test_ccx2_cache() {
712 let mut c = CCX2Cache::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_ccx2_worklist() {
722 let mut w = CCX2Worklist::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_ccx2_dom_tree() {
733 let mut dt = CCX2DomTree::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_ccx2_liveness() {
745 let mut lv = CCX2Liveness::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_ccx2_const_folder() {
754 let mut cf = CCX2ConstFolder::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_ccx2_dep_graph() {
764 let mut g = CCX2DepGraph::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}