Skip to main content

oxilean_codegen/closure_convert/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use 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
18/// Compute the set of free variables in an LCNF expression.
19///
20/// A variable is "free" if it is referenced but not bound within the expression.
21pub 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}
26/// Recursive helper for free variable collection.
27pub(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}
73/// Collect free variables from a let-value.
74pub(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}
107/// Collect free variables from an argument.
108pub(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}