1use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::types::{
9 AvailableSet, CSEAnalysisCache, CSEConstantFoldingHelper, CSEDepGraph, CSEDominatorTree,
10 CSEExtCache, CSEExtConstFolder, CSEExtDepGraph, CSEExtDomTree, CSEExtLiveness,
11 CSEExtPassConfig, CSEExtPassPhase, CSEExtPassRegistry, CSEExtPassStats, CSEExtWorklist,
12 CSELivenessInfo, CSEPass, CSEPassConfig, CSEPassPhase, CSEPassRegistry, CSEPassStats,
13 CSEWorklist, CSEX2Cache, CSEX2ConstFolder, CSEX2DepGraph, CSEX2DomTree, CSEX2Liveness,
14 CSEX2PassConfig, CSEX2PassPhase, CSEX2PassRegistry, CSEX2PassStats, CSEX2Worklist, CseConfig,
15 CseReport, DominatorTree, ExprKey, GvnTable,
16};
17
18pub fn let_value_to_key(value: &LcnfLetValue, pure_fns: &[String]) -> Option<ExprKey> {
21 match value {
22 LcnfLetValue::Lit(l) => Some(ExprKey::Lit(l.clone())),
23 LcnfLetValue::FVar(v) => Some(ExprKey::Var(*v)),
24 LcnfLetValue::Proj(name, idx, var) => Some(ExprKey::Proj(name.clone(), *idx, *var)),
25 LcnfLetValue::Ctor(name, tag, args) => {
26 Some(ExprKey::Ctor(name.clone(), *tag, args.clone()))
27 }
28 LcnfLetValue::App(func, args) => {
29 let is_pure = match func {
30 LcnfArg::Var(_) => false,
31 LcnfArg::Lit(_) => false,
32 LcnfArg::Erased => false,
33 LcnfArg::Type(_) => false,
34 };
35 let is_named_pure = pure_fns
36 .iter()
37 .any(|name| matches!(func, LcnfArg::Lit(LcnfLit::Str(s)) if s == name));
38 if is_pure || is_named_pure {
39 Some(ExprKey::App(func.clone(), args.clone()))
40 } else {
41 None
42 }
43 }
44 LcnfLetValue::Erased => None,
45 LcnfLetValue::Reset(_) => None,
46 LcnfLetValue::Reuse(_, _, _, _) => None,
47 }
48}
49pub fn optimize_cse(decls: &mut [LcnfFunDecl]) {
51 CSEPass::default().run(decls);
52}
53#[cfg(test)]
54mod tests {
55 use super::*;
56 pub(super) fn make_var(id: u64) -> LcnfVarId {
57 LcnfVarId(id)
58 }
59 pub(super) fn make_lit_nat(n: u64) -> LcnfLetValue {
60 LcnfLetValue::Lit(LcnfLit::Nat(n))
61 }
62 pub(super) fn make_fvar(id: u64) -> LcnfLetValue {
63 LcnfLetValue::FVar(make_var(id))
64 }
65 pub(super) fn make_proj(field: &str, idx: u32, var: u64) -> LcnfLetValue {
66 LcnfLetValue::Proj(field.to_string(), idx, make_var(var))
67 }
68 pub(super) fn make_ctor(name: &str, tag: u32, args: Vec<LcnfArg>) -> LcnfLetValue {
69 LcnfLetValue::Ctor(name.to_string(), tag, args)
70 }
71 pub(super) fn ret(v: u64) -> LcnfExpr {
72 LcnfExpr::Return(LcnfArg::Var(make_var(v)))
73 }
74 pub(super) fn let_binding(id: u64, value: LcnfLetValue, body: LcnfExpr) -> LcnfExpr {
75 LcnfExpr::Let {
76 id: make_var(id),
77 name: format!("x{}", id),
78 ty: LcnfType::Nat,
79 value,
80 body: Box::new(body),
81 }
82 }
83 #[test]
84 pub(super) fn test_key_lit() {
85 let v = make_lit_nat(42);
86 let key = let_value_to_key(&v, &[]);
87 assert_eq!(key, Some(ExprKey::Lit(LcnfLit::Nat(42))));
88 }
89 #[test]
90 pub(super) fn test_key_fvar() {
91 let v = make_fvar(7);
92 let key = let_value_to_key(&v, &[]);
93 assert_eq!(key, Some(ExprKey::Var(make_var(7))));
94 }
95 #[test]
96 pub(super) fn test_key_proj() {
97 let v = make_proj("foo", 1, 3);
98 let key = let_value_to_key(&v, &[]);
99 assert_eq!(key, Some(ExprKey::Proj("foo".into(), 1, make_var(3))));
100 }
101 #[test]
102 pub(super) fn test_key_ctor() {
103 let v = make_ctor("Pair", 0, vec![LcnfArg::Var(make_var(1))]);
104 let key = let_value_to_key(&v, &[]);
105 assert_eq!(
106 key,
107 Some(ExprKey::Ctor(
108 "Pair".into(),
109 0,
110 vec![LcnfArg::Var(make_var(1))]
111 ))
112 );
113 }
114 #[test]
115 pub(super) fn test_key_reset_is_none() {
116 let v = LcnfLetValue::Reset(make_var(5));
117 assert_eq!(let_value_to_key(&v, &[]), None);
118 }
119 #[test]
120 pub(super) fn test_key_erased_is_none() {
121 let v = LcnfLetValue::Erased;
122 assert_eq!(let_value_to_key(&v, &[]), None);
123 }
124 #[test]
125 pub(super) fn test_avail_insert_and_find() {
126 let mut avail = AvailableSet::new();
127 let key = ExprKey::Lit(LcnfLit::Nat(1));
128 avail.insert(key.clone(), make_var(10), "x10".into(), 0);
129 assert_eq!(avail.find(&key), Some(make_var(10)));
130 }
131 #[test]
132 pub(super) fn test_avail_miss() {
133 let avail = AvailableSet::new();
134 let key = ExprKey::Lit(LcnfLit::Nat(99));
135 assert_eq!(avail.find(&key), None);
136 }
137 #[test]
138 pub(super) fn test_avail_kill_above_depth() {
139 let mut avail = AvailableSet::new();
140 let k0 = ExprKey::Lit(LcnfLit::Nat(0));
141 let k1 = ExprKey::Lit(LcnfLit::Nat(1));
142 avail.insert(k0.clone(), make_var(1), "a".into(), 0);
143 avail.insert(k1.clone(), make_var(2), "b".into(), 3);
144 avail.kill_above_depth(2);
145 assert_eq!(avail.find(&k0), Some(make_var(1)));
146 assert_eq!(avail.find(&k1), None);
147 }
148 #[test]
149 pub(super) fn test_avail_len() {
150 let mut avail = AvailableSet::new();
151 assert_eq!(avail.len(), 0);
152 avail.insert(ExprKey::Lit(LcnfLit::Nat(1)), make_var(1), "a".into(), 0);
153 assert_eq!(avail.len(), 1);
154 }
155 #[test]
156 pub(super) fn test_gvn_insert_and_lookup() {
157 let mut gvn = GvnTable::new();
158 let key = ExprKey::Lit(LcnfLit::Nat(7));
159 let rep = gvn.insert(key.clone(), make_var(42));
160 assert_eq!(rep, make_var(42));
161 assert_eq!(gvn.lookup(&key), Some(make_var(42)));
162 }
163 #[test]
164 pub(super) fn test_gvn_duplicate_keeps_first() {
165 let mut gvn = GvnTable::new();
166 let key = ExprKey::Lit(LcnfLit::Nat(7));
167 gvn.insert(key.clone(), make_var(1));
168 let rep2 = gvn.insert(key.clone(), make_var(2));
169 assert_eq!(rep2, make_var(1));
170 }
171 #[test]
172 pub(super) fn test_dominator_tree() {
173 let mut dom = DominatorTree::new();
174 dom.insert(make_var(1), 0);
175 dom.insert(make_var(2), 2);
176 assert!(dom.dominates(make_var(1), make_var(2)));
177 assert!(!dom.dominates(make_var(2), make_var(1)));
178 }
179 #[test]
180 pub(super) fn test_local_cse_eliminates_duplicate_lit() {
181 let expr = let_binding(
182 1,
183 make_lit_nat(42),
184 let_binding(2, make_lit_nat(42), ret(2)),
185 );
186 let mut pass = CSEPass::default();
187 let result = pass.local_cse(&expr);
188 if let LcnfExpr::Let { body, .. } = &result {
189 if let LcnfExpr::Let { value, .. } = body.as_ref() {
190 assert_eq!(*value, LcnfLetValue::FVar(make_var(1)));
191 } else {
192 panic!("expected inner Let");
193 }
194 } else {
195 panic!("expected outer Let");
196 }
197 assert_eq!(pass.report().expressions_eliminated, 1);
198 }
199 #[test]
200 pub(super) fn test_local_cse_no_false_positive_different_lit() {
201 let expr = let_binding(1, make_lit_nat(1), let_binding(2, make_lit_nat(2), ret(2)));
202 let mut pass = CSEPass::default();
203 let result = pass.local_cse(&expr);
204 if let LcnfExpr::Let { body, .. } = &result {
205 if let LcnfExpr::Let { value, .. } = body.as_ref() {
206 assert_eq!(*value, make_lit_nat(2));
207 } else {
208 panic!("expected inner Let");
209 }
210 }
211 assert_eq!(pass.report().expressions_eliminated, 0);
212 }
213 #[test]
214 pub(super) fn test_local_cse_duplicate_proj() {
215 let proj = make_proj("record", 0, 5);
216 let expr = let_binding(10, proj.clone(), let_binding(11, proj.clone(), ret(11)));
217 let mut pass = CSEPass::default();
218 let result = pass.local_cse(&expr);
219 if let LcnfExpr::Let { body, .. } = &result {
220 if let LcnfExpr::Let { value, .. } = body.as_ref() {
221 assert_eq!(*value, LcnfLetValue::FVar(make_var(10)));
222 } else {
223 panic!("expected inner Let");
224 }
225 }
226 assert_eq!(pass.report().expressions_eliminated, 1);
227 }
228 #[test]
229 pub(super) fn test_local_cse_duplicate_ctor() {
230 let ctor = make_ctor("Some", 1, vec![LcnfArg::Var(make_var(3))]);
231 let expr = let_binding(20, ctor.clone(), let_binding(21, ctor.clone(), ret(21)));
232 let mut pass = CSEPass::default();
233 pass.local_cse(&expr);
234 assert_eq!(pass.report().expressions_eliminated, 1);
235 }
236 #[test]
237 pub(super) fn test_cse_return_unchanged() {
238 let expr = LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(0)));
239 let mut pass = CSEPass::default();
240 let result = pass.local_cse(&expr);
241 assert_eq!(result, expr);
242 }
243 #[test]
244 pub(super) fn test_cse_unreachable_unchanged() {
245 let expr = LcnfExpr::Unreachable;
246 let mut pass = CSEPass::default();
247 let result = pass.local_cse(&expr);
248 assert_eq!(result, expr);
249 }
250 #[test]
251 pub(super) fn test_cse_report_display() {
252 let r = CseReport {
253 expressions_found: 5,
254 expressions_eliminated: 3,
255 lets_hoisted: 1,
256 };
257 let s = format!("{}", r);
258 assert!(s.contains("found=5"));
259 assert!(s.contains("eliminated=3"));
260 }
261 #[test]
262 pub(super) fn test_cse_config_display() {
263 let c = CseConfig::default();
264 let s = format!("{}", c);
265 assert!(s.contains("max_expr_size=20"));
266 }
267 #[test]
268 pub(super) fn test_global_cse_run() {
269 let mut decls: Vec<LcnfFunDecl> = vec![];
270 CSEPass::default().run(&mut decls);
271 }
272 #[test]
273 pub(super) fn test_cse_triple_dup() {
274 let expr = let_binding(
275 1,
276 make_lit_nat(7),
277 let_binding(2, make_lit_nat(7), let_binding(3, make_lit_nat(7), ret(3))),
278 );
279 let mut pass = CSEPass::default();
280 pass.local_cse(&expr);
281 assert_eq!(pass.report().expressions_eliminated, 2);
282 }
283 #[test]
284 pub(super) fn test_cse_case_branches_independent() {
285 let inner_lit = make_lit_nat(99);
286 let alt_body = let_binding(
287 10,
288 inner_lit.clone(),
289 let_binding(11, inner_lit.clone(), ret(11)),
290 );
291 let case_expr = LcnfExpr::Case {
292 scrutinee: make_var(0),
293 scrutinee_ty: LcnfType::Nat,
294 alts: vec![LcnfAlt {
295 ctor_name: "Zero".into(),
296 ctor_tag: 0,
297 params: vec![],
298 body: alt_body,
299 }],
300 default: None,
301 };
302 let mut pass = CSEPass::default();
303 pass.local_cse(&case_expr);
304 assert_eq!(pass.report().expressions_eliminated, 1);
305 }
306 #[test]
307 pub(super) fn test_find_available() {
308 let pass = CSEPass::default();
309 let mut avail = AvailableSet::new();
310 let proj = make_proj("hd", 0, 1);
311 let key = let_value_to_key(&proj, &[]).expect("key key extraction should succeed");
312 avail.insert(key, make_var(5), "hd".into(), 0);
313 let result = pass.find_available(&avail, &proj);
314 assert_eq!(result, Some(make_var(5)));
315 }
316 #[test]
317 pub(super) fn test_hash_expr_is_commutative() {
318 let pass = CSEPass::default();
319 let va = LcnfArg::Lit(LcnfLit::Nat(1));
320 let vb = LcnfArg::Lit(LcnfLit::Nat(2));
321 let v1 = LcnfLetValue::App(
322 LcnfArg::Lit(LcnfLit::Str("add".into())),
323 vec![va.clone(), vb.clone()],
324 );
325 let v2 = LcnfLetValue::App(
326 LcnfArg::Lit(LcnfLit::Str("add".into())),
327 vec![vb.clone(), va.clone()],
328 );
329 let k1 = pass.hash_expr(&v1);
330 let k2 = pass.hash_expr(&v2);
331 assert_eq!(k1, k2);
332 }
333 #[test]
334 pub(super) fn test_optimize_cse_convenience() {
335 let mut decls: Vec<LcnfFunDecl> = vec![];
336 optimize_cse(&mut decls);
337 }
338}
339#[cfg(test)]
340mod CSE_infra_tests {
341 use super::*;
342 #[test]
343 pub(super) fn test_pass_config() {
344 let config = CSEPassConfig::new("test_pass", CSEPassPhase::Transformation);
345 assert!(config.enabled);
346 assert!(config.phase.is_modifying());
347 assert_eq!(config.phase.name(), "transformation");
348 }
349 #[test]
350 pub(super) fn test_pass_stats() {
351 let mut stats = CSEPassStats::new();
352 stats.record_run(10, 100, 3);
353 stats.record_run(20, 200, 5);
354 assert_eq!(stats.total_runs, 2);
355 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
356 assert!((stats.success_rate() - 1.0).abs() < 0.01);
357 let s = stats.format_summary();
358 assert!(s.contains("Runs: 2/2"));
359 }
360 #[test]
361 pub(super) fn test_pass_registry() {
362 let mut reg = CSEPassRegistry::new();
363 reg.register(CSEPassConfig::new("pass_a", CSEPassPhase::Analysis));
364 reg.register(CSEPassConfig::new("pass_b", CSEPassPhase::Transformation).disabled());
365 assert_eq!(reg.total_passes(), 2);
366 assert_eq!(reg.enabled_count(), 1);
367 reg.update_stats("pass_a", 5, 50, 2);
368 let stats = reg.get_stats("pass_a").expect("stats should exist");
369 assert_eq!(stats.total_changes, 5);
370 }
371 #[test]
372 pub(super) fn test_analysis_cache() {
373 let mut cache = CSEAnalysisCache::new(10);
374 cache.insert("key1".to_string(), vec![1, 2, 3]);
375 assert!(cache.get("key1").is_some());
376 assert!(cache.get("key2").is_none());
377 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
378 cache.invalidate("key1");
379 assert!(!cache.entries["key1"].valid);
380 assert_eq!(cache.size(), 1);
381 }
382 #[test]
383 pub(super) fn test_worklist() {
384 let mut wl = CSEWorklist::new();
385 assert!(wl.push(1));
386 assert!(wl.push(2));
387 assert!(!wl.push(1));
388 assert_eq!(wl.len(), 2);
389 assert_eq!(wl.pop(), Some(1));
390 assert!(!wl.contains(1));
391 assert!(wl.contains(2));
392 }
393 #[test]
394 pub(super) fn test_dominator_tree() {
395 let mut dt = CSEDominatorTree::new(5);
396 dt.set_idom(1, 0);
397 dt.set_idom(2, 0);
398 dt.set_idom(3, 1);
399 assert!(dt.dominates(0, 3));
400 assert!(dt.dominates(1, 3));
401 assert!(!dt.dominates(2, 3));
402 assert!(dt.dominates(3, 3));
403 }
404 #[test]
405 pub(super) fn test_liveness() {
406 let mut liveness = CSELivenessInfo::new(3);
407 liveness.add_def(0, 1);
408 liveness.add_use(1, 1);
409 assert!(liveness.defs[0].contains(&1));
410 assert!(liveness.uses[1].contains(&1));
411 }
412 #[test]
413 pub(super) fn test_constant_folding() {
414 assert_eq!(CSEConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
415 assert_eq!(CSEConstantFoldingHelper::fold_div_i64(10, 0), None);
416 assert_eq!(CSEConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
417 assert_eq!(
418 CSEConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
419 0b1000
420 );
421 assert_eq!(CSEConstantFoldingHelper::fold_bitnot_i64(0), -1);
422 }
423 #[test]
424 pub(super) fn test_dep_graph() {
425 let mut g = CSEDepGraph::new();
426 g.add_dep(1, 2);
427 g.add_dep(2, 3);
428 g.add_dep(1, 3);
429 assert_eq!(g.dependencies_of(2), vec![1]);
430 let topo = g.topological_sort();
431 assert_eq!(topo.len(), 3);
432 assert!(!g.has_cycle());
433 let pos: std::collections::HashMap<u32, usize> =
434 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
435 assert!(pos[&1] < pos[&2]);
436 assert!(pos[&1] < pos[&3]);
437 assert!(pos[&2] < pos[&3]);
438 }
439}
440#[cfg(test)]
441mod cseext_pass_tests {
442 use super::*;
443 #[test]
444 pub(super) fn test_cseext_phase_order() {
445 assert_eq!(CSEExtPassPhase::Early.order(), 0);
446 assert_eq!(CSEExtPassPhase::Middle.order(), 1);
447 assert_eq!(CSEExtPassPhase::Late.order(), 2);
448 assert_eq!(CSEExtPassPhase::Finalize.order(), 3);
449 assert!(CSEExtPassPhase::Early.is_early());
450 assert!(!CSEExtPassPhase::Early.is_late());
451 }
452 #[test]
453 pub(super) fn test_cseext_config_builder() {
454 let c = CSEExtPassConfig::new("p")
455 .with_phase(CSEExtPassPhase::Late)
456 .with_max_iter(50)
457 .with_debug(1);
458 assert_eq!(c.name, "p");
459 assert_eq!(c.max_iterations, 50);
460 assert!(c.is_debug_enabled());
461 assert!(c.enabled);
462 let c2 = c.disabled();
463 assert!(!c2.enabled);
464 }
465 #[test]
466 pub(super) fn test_cseext_stats() {
467 let mut s = CSEExtPassStats::new();
468 s.visit();
469 s.visit();
470 s.modify();
471 s.iterate();
472 assert_eq!(s.nodes_visited, 2);
473 assert_eq!(s.nodes_modified, 1);
474 assert!(s.changed);
475 assert_eq!(s.iterations, 1);
476 let e = s.efficiency();
477 assert!((e - 0.5).abs() < 1e-9);
478 }
479 #[test]
480 pub(super) fn test_cseext_registry() {
481 let mut r = CSEExtPassRegistry::new();
482 r.register(CSEExtPassConfig::new("a").with_phase(CSEExtPassPhase::Early));
483 r.register(CSEExtPassConfig::new("b").disabled());
484 assert_eq!(r.len(), 2);
485 assert_eq!(r.enabled_passes().len(), 1);
486 assert_eq!(r.passes_in_phase(&CSEExtPassPhase::Early).len(), 1);
487 }
488 #[test]
489 pub(super) fn test_cseext_cache() {
490 let mut c = CSEExtCache::new(4);
491 assert!(c.get(99).is_none());
492 c.put(99, vec![1, 2, 3]);
493 let v = c.get(99).expect("v should be present in map");
494 assert_eq!(v, &[1u8, 2, 3]);
495 assert!(c.hit_rate() > 0.0);
496 assert_eq!(c.live_count(), 1);
497 }
498 #[test]
499 pub(super) fn test_cseext_worklist() {
500 let mut w = CSEExtWorklist::new(10);
501 w.push(5);
502 w.push(3);
503 w.push(5);
504 assert_eq!(w.len(), 2);
505 assert!(w.contains(5));
506 let first = w.pop().expect("first should be available to pop");
507 assert!(!w.contains(first));
508 }
509 #[test]
510 pub(super) fn test_cseext_dom_tree() {
511 let mut dt = CSEExtDomTree::new(5);
512 dt.set_idom(1, 0);
513 dt.set_idom(2, 0);
514 dt.set_idom(3, 1);
515 dt.set_idom(4, 1);
516 assert!(dt.dominates(0, 3));
517 assert!(dt.dominates(1, 4));
518 assert!(!dt.dominates(2, 3));
519 assert_eq!(dt.depth_of(3), 2);
520 }
521 #[test]
522 pub(super) fn test_cseext_liveness() {
523 let mut lv = CSEExtLiveness::new(3);
524 lv.add_def(0, 1);
525 lv.add_use(1, 1);
526 assert!(lv.var_is_def_in_block(0, 1));
527 assert!(lv.var_is_used_in_block(1, 1));
528 assert!(!lv.var_is_def_in_block(1, 1));
529 }
530 #[test]
531 pub(super) fn test_cseext_const_folder() {
532 let mut cf = CSEExtConstFolder::new();
533 assert_eq!(cf.add_i64(3, 4), Some(7));
534 assert_eq!(cf.div_i64(10, 0), None);
535 assert_eq!(cf.mul_i64(6, 7), Some(42));
536 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
537 assert_eq!(cf.fold_count(), 3);
538 assert_eq!(cf.failure_count(), 1);
539 }
540 #[test]
541 pub(super) fn test_cseext_dep_graph() {
542 let mut g = CSEExtDepGraph::new(4);
543 g.add_edge(0, 1);
544 g.add_edge(1, 2);
545 g.add_edge(2, 3);
546 assert!(!g.has_cycle());
547 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
548 assert_eq!(g.reachable(0).len(), 4);
549 let sccs = g.scc();
550 assert_eq!(sccs.len(), 4);
551 }
552}
553#[cfg(test)]
554mod csex2_pass_tests {
555 use super::*;
556 #[test]
557 pub(super) fn test_csex2_phase_order() {
558 assert_eq!(CSEX2PassPhase::Early.order(), 0);
559 assert_eq!(CSEX2PassPhase::Middle.order(), 1);
560 assert_eq!(CSEX2PassPhase::Late.order(), 2);
561 assert_eq!(CSEX2PassPhase::Finalize.order(), 3);
562 assert!(CSEX2PassPhase::Early.is_early());
563 assert!(!CSEX2PassPhase::Early.is_late());
564 }
565 #[test]
566 pub(super) fn test_csex2_config_builder() {
567 let c = CSEX2PassConfig::new("p")
568 .with_phase(CSEX2PassPhase::Late)
569 .with_max_iter(50)
570 .with_debug(1);
571 assert_eq!(c.name, "p");
572 assert_eq!(c.max_iterations, 50);
573 assert!(c.is_debug_enabled());
574 assert!(c.enabled);
575 let c2 = c.disabled();
576 assert!(!c2.enabled);
577 }
578 #[test]
579 pub(super) fn test_csex2_stats() {
580 let mut s = CSEX2PassStats::new();
581 s.visit();
582 s.visit();
583 s.modify();
584 s.iterate();
585 assert_eq!(s.nodes_visited, 2);
586 assert_eq!(s.nodes_modified, 1);
587 assert!(s.changed);
588 assert_eq!(s.iterations, 1);
589 let e = s.efficiency();
590 assert!((e - 0.5).abs() < 1e-9);
591 }
592 #[test]
593 pub(super) fn test_csex2_registry() {
594 let mut r = CSEX2PassRegistry::new();
595 r.register(CSEX2PassConfig::new("a").with_phase(CSEX2PassPhase::Early));
596 r.register(CSEX2PassConfig::new("b").disabled());
597 assert_eq!(r.len(), 2);
598 assert_eq!(r.enabled_passes().len(), 1);
599 assert_eq!(r.passes_in_phase(&CSEX2PassPhase::Early).len(), 1);
600 }
601 #[test]
602 pub(super) fn test_csex2_cache() {
603 let mut c = CSEX2Cache::new(4);
604 assert!(c.get(99).is_none());
605 c.put(99, vec![1, 2, 3]);
606 let v = c.get(99).expect("v should be present in map");
607 assert_eq!(v, &[1u8, 2, 3]);
608 assert!(c.hit_rate() > 0.0);
609 assert_eq!(c.live_count(), 1);
610 }
611 #[test]
612 pub(super) fn test_csex2_worklist() {
613 let mut w = CSEX2Worklist::new(10);
614 w.push(5);
615 w.push(3);
616 w.push(5);
617 assert_eq!(w.len(), 2);
618 assert!(w.contains(5));
619 let first = w.pop().expect("first should be available to pop");
620 assert!(!w.contains(first));
621 }
622 #[test]
623 pub(super) fn test_csex2_dom_tree() {
624 let mut dt = CSEX2DomTree::new(5);
625 dt.set_idom(1, 0);
626 dt.set_idom(2, 0);
627 dt.set_idom(3, 1);
628 dt.set_idom(4, 1);
629 assert!(dt.dominates(0, 3));
630 assert!(dt.dominates(1, 4));
631 assert!(!dt.dominates(2, 3));
632 assert_eq!(dt.depth_of(3), 2);
633 }
634 #[test]
635 pub(super) fn test_csex2_liveness() {
636 let mut lv = CSEX2Liveness::new(3);
637 lv.add_def(0, 1);
638 lv.add_use(1, 1);
639 assert!(lv.var_is_def_in_block(0, 1));
640 assert!(lv.var_is_used_in_block(1, 1));
641 assert!(!lv.var_is_def_in_block(1, 1));
642 }
643 #[test]
644 pub(super) fn test_csex2_const_folder() {
645 let mut cf = CSEX2ConstFolder::new();
646 assert_eq!(cf.add_i64(3, 4), Some(7));
647 assert_eq!(cf.div_i64(10, 0), None);
648 assert_eq!(cf.mul_i64(6, 7), Some(42));
649 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
650 assert_eq!(cf.fold_count(), 3);
651 assert_eq!(cf.failure_count(), 1);
652 }
653 #[test]
654 pub(super) fn test_csex2_dep_graph() {
655 let mut g = CSEX2DepGraph::new(4);
656 g.add_edge(0, 1);
657 g.add_edge(1, 2);
658 g.add_edge(2, 3);
659 assert!(!g.has_cycle());
660 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
661 assert_eq!(g.reachable(0).len(), 4);
662 let sccs = g.scc();
663 assert_eq!(sccs.len(), 4);
664 }
665}