1use crate::lcnf::*;
6use std::collections::{HashMap, HashSet};
7
8use super::types::{
9 BindingInfo, BindingKind, DominanceFrontier, M2RAnalysisCache, M2RConstantFoldingHelper,
10 M2RDepGraph, M2RDominatorTree, M2RExtCache, M2RExtConstFolder, M2RExtDepGraph, M2RExtDomTree,
11 M2RExtLiveness, M2RExtPassConfig, M2RExtPassPhase, M2RExtPassRegistry, M2RExtPassStats,
12 M2RExtWorklist, M2RLivenessInfo, M2RPassConfig, M2RPassPhase, M2RPassRegistry, M2RPassStats,
13 M2RWorklist, M2RX2Cache, M2RX2ConstFolder, M2RX2DepGraph, M2RX2DomTree, M2RX2Liveness,
14 M2RX2PassConfig, M2RX2PassPhase, M2RX2PassRegistry, M2RX2PassStats, M2RX2Worklist, Mem2Reg,
15 Mem2RegConfig,
16};
17
18pub(super) fn count_uses(expr: &LcnfExpr, counts: &mut HashMap<LcnfVarId, usize>) {
20 match expr {
21 LcnfExpr::Let {
22 id, value, body, ..
23 } => {
24 count_uses_in_value(value, counts);
25 count_uses(body, counts);
26 let _ = id;
27 }
28 LcnfExpr::Case {
29 scrutinee,
30 alts,
31 default,
32 ..
33 } => {
34 *counts.entry(*scrutinee).or_insert(0) += 1;
35 for alt in alts {
36 count_uses(&alt.body, counts);
37 }
38 if let Some(def) = default {
39 count_uses(def, counts);
40 }
41 }
42 LcnfExpr::Return(arg) => count_uses_in_arg(arg, counts),
43 LcnfExpr::TailCall(func, args) => {
44 count_uses_in_arg(func, counts);
45 for a in args {
46 count_uses_in_arg(a, counts);
47 }
48 }
49 LcnfExpr::Unreachable => {}
50 }
51}
52pub(super) fn count_uses_in_value(value: &LcnfLetValue, counts: &mut HashMap<LcnfVarId, usize>) {
53 match value {
54 LcnfLetValue::App(func, args) => {
55 count_uses_in_arg(func, counts);
56 for a in args {
57 count_uses_in_arg(a, counts);
58 }
59 }
60 LcnfLetValue::Proj(_, _, var) => {
61 *counts.entry(*var).or_insert(0) += 1;
62 }
63 LcnfLetValue::Ctor(_, _, args) => {
64 for a in args {
65 count_uses_in_arg(a, counts);
66 }
67 }
68 LcnfLetValue::Lit(_) => {}
69 LcnfLetValue::Erased => {}
70 LcnfLetValue::FVar(var) => {
71 *counts.entry(*var).or_insert(0) += 1;
72 }
73 LcnfLetValue::Reset(var) => {
74 *counts.entry(*var).or_insert(0) += 1;
75 }
76 LcnfLetValue::Reuse(slot, _, _, args) => {
77 *counts.entry(*slot).or_insert(0) += 1;
78 for a in args {
79 count_uses_in_arg(a, counts);
80 }
81 }
82 }
83}
84pub(super) fn count_uses_in_arg(arg: &LcnfArg, counts: &mut HashMap<LcnfVarId, usize>) {
85 if let LcnfArg::Var(id) = arg {
86 *counts.entry(*id).or_insert(0) += 1;
87 }
88}
89pub(super) fn collect_defined(expr: &LcnfExpr, defined: &mut HashSet<LcnfVarId>) {
91 match expr {
92 LcnfExpr::Let {
93 id, value, body, ..
94 } => {
95 defined.insert(*id);
96 collect_defined_in_value(value, defined);
97 collect_defined(body, defined);
98 }
99 LcnfExpr::Case { alts, default, .. } => {
100 for alt in alts {
101 for p in &alt.params {
102 defined.insert(p.id);
103 }
104 collect_defined(&alt.body, defined);
105 }
106 if let Some(def) = default {
107 collect_defined(def, defined);
108 }
109 }
110 LcnfExpr::Return(_) | LcnfExpr::Unreachable | LcnfExpr::TailCall(_, _) => {}
111 }
112}
113pub(super) fn collect_defined_in_value(value: &LcnfLetValue, defined: &mut HashSet<LcnfVarId>) {
114 let _ = (value, defined);
115}
116pub(super) fn compute_dominance_frontier(expr: &LcnfExpr) -> DominanceFrontier {
119 let mut frontier = DominanceFrontier::default();
120 compute_frontier_rec(expr, &mut frontier);
121 frontier
122}
123pub(super) fn compute_frontier_rec(expr: &LcnfExpr, frontier: &mut DominanceFrontier) {
124 match expr {
125 LcnfExpr::Let { value: _, body, .. } => {
126 compute_frontier_rec(body, frontier);
127 }
128 LcnfExpr::Case { alts, default, .. } => {
129 let mut branch_defs: Vec<HashSet<LcnfVarId>> = Vec::new();
130 for alt in alts {
131 let mut defs = HashSet::new();
132 for p in &alt.params {
133 defs.insert(p.id);
134 }
135 collect_defined(&alt.body, &mut defs);
136 compute_frontier_rec(&alt.body, frontier);
137 branch_defs.push(defs);
138 }
139 if let Some(def) = default {
140 let mut defs = HashSet::new();
141 collect_defined(def, &mut defs);
142 compute_frontier_rec(def, frontier);
143 branch_defs.push(defs);
144 }
145 if branch_defs.len() > 1 {
146 let all_defs: HashSet<LcnfVarId> = branch_defs.iter().flatten().cloned().collect();
147 for var in all_defs {
148 let defined_in_all = branch_defs.iter().all(|d| d.contains(&var));
149 if !defined_in_all {
150 frontier.join_vars.insert(var);
151 }
152 }
153 }
154 }
155 LcnfExpr::Return(_) | LcnfExpr::Unreachable | LcnfExpr::TailCall(_, _) => {}
156 }
157}
158pub(super) fn substitute_var(expr: LcnfExpr, from: LcnfVarId, to: LcnfArg) -> LcnfExpr {
160 match expr {
161 LcnfExpr::Let {
162 id,
163 name,
164 ty,
165 value,
166 body,
167 } => {
168 let value2 = subst_in_value(value, from, &to);
169 let body2 = substitute_var(*body, from, to);
170 LcnfExpr::Let {
171 id,
172 name,
173 ty,
174 value: value2,
175 body: Box::new(body2),
176 }
177 }
178 LcnfExpr::Case {
179 scrutinee,
180 scrutinee_ty,
181 alts,
182 default,
183 } => {
184 let scrutinee2 = if scrutinee == from {
185 match &to {
186 LcnfArg::Var(v) => *v,
187 _ => scrutinee,
188 }
189 } else {
190 scrutinee
191 };
192 let alts2 = alts
193 .into_iter()
194 .map(|alt| LcnfAlt {
195 ctor_name: alt.ctor_name,
196 ctor_tag: alt.ctor_tag,
197 params: alt.params,
198 body: substitute_var(alt.body, from, to.clone()),
199 })
200 .collect();
201 let default2 = default.map(|d| Box::new(substitute_var(*d, from, to)));
202 LcnfExpr::Case {
203 scrutinee: scrutinee2,
204 scrutinee_ty,
205 alts: alts2,
206 default: default2,
207 }
208 }
209 LcnfExpr::Return(arg) => LcnfExpr::Return(subst_in_arg(arg, from, &to)),
210 LcnfExpr::TailCall(func, args) => {
211 let func2 = subst_in_arg(func, from, &to);
212 let args2 = args
213 .into_iter()
214 .map(|a| subst_in_arg(a, from, &to))
215 .collect();
216 LcnfExpr::TailCall(func2, args2)
217 }
218 LcnfExpr::Unreachable => LcnfExpr::Unreachable,
219 }
220}
221pub(super) fn subst_in_arg(arg: LcnfArg, from: LcnfVarId, to: &LcnfArg) -> LcnfArg {
222 match &arg {
223 LcnfArg::Var(id) if *id == from => to.clone(),
224 _ => arg,
225 }
226}
227pub(super) fn subst_in_value(value: LcnfLetValue, from: LcnfVarId, to: &LcnfArg) -> LcnfLetValue {
228 match value {
229 LcnfLetValue::App(func, args) => {
230 let func2 = subst_in_arg(func, from, to);
231 let args2 = args
232 .into_iter()
233 .map(|a| subst_in_arg(a, from, to))
234 .collect();
235 LcnfLetValue::App(func2, args2)
236 }
237 LcnfLetValue::Proj(name, idx, var) => {
238 let var2 = if var == from {
239 match to {
240 LcnfArg::Var(v) => *v,
241 _ => var,
242 }
243 } else {
244 var
245 };
246 LcnfLetValue::Proj(name, idx, var2)
247 }
248 LcnfLetValue::Ctor(name, tag, args) => {
249 let args2 = args
250 .into_iter()
251 .map(|a| subst_in_arg(a, from, to))
252 .collect();
253 LcnfLetValue::Ctor(name, tag, args2)
254 }
255 LcnfLetValue::FVar(var) => {
256 if var == from {
257 match to {
258 LcnfArg::Var(v) => LcnfLetValue::FVar(*v),
259 _ => LcnfLetValue::FVar(var),
260 }
261 } else {
262 LcnfLetValue::FVar(var)
263 }
264 }
265 LcnfLetValue::Reset(var) => {
266 let var2 = if var == from {
267 match to {
268 LcnfArg::Var(v) => *v,
269 _ => var,
270 }
271 } else {
272 var
273 };
274 LcnfLetValue::Reset(var2)
275 }
276 LcnfLetValue::Reuse(slot, name, tag, args) => {
277 let slot2 = if slot == from {
278 match to {
279 LcnfArg::Var(v) => *v,
280 _ => slot,
281 }
282 } else {
283 slot
284 };
285 let args2 = args
286 .into_iter()
287 .map(|a| subst_in_arg(a, from, to))
288 .collect();
289 LcnfLetValue::Reuse(slot2, name, tag, args2)
290 }
291 other => other,
292 }
293}
294pub(super) fn is_promotable(value: &LcnfLetValue) -> bool {
296 match value {
297 LcnfLetValue::Lit(_) => true,
298 LcnfLetValue::FVar(_) => true,
299 LcnfLetValue::Erased => true,
300 LcnfLetValue::Ctor(_, _, _) => true,
301 LcnfLetValue::Proj(_, _, _) => true,
302 LcnfLetValue::App(_, _) => false,
303 LcnfLetValue::Reset(_) => false,
304 LcnfLetValue::Reuse(_, _, _, _) => false,
305 }
306}
307pub(super) fn is_trivial(value: &LcnfLetValue) -> bool {
310 matches!(
311 value,
312 LcnfLetValue::Lit(_) | LcnfLetValue::FVar(_) | LcnfLetValue::Erased
313 )
314}
315pub(super) fn let_value_to_arg(value: &LcnfLetValue) -> Option<LcnfArg> {
318 match value {
319 LcnfLetValue::Lit(l) => Some(LcnfArg::Lit(l.clone())),
320 LcnfLetValue::FVar(v) => Some(LcnfArg::Var(*v)),
321 LcnfLetValue::Erased => Some(LcnfArg::Erased),
322 _ => None,
323 }
324}
325pub(super) fn collect_binding_info(
327 expr: &LcnfExpr,
328 bindings: &mut HashMap<LcnfVarId, BindingInfo>,
329 use_counts: &HashMap<LcnfVarId, usize>,
330 frontier: &DominanceFrontier,
331 depth: usize,
332) {
333 match expr {
334 LcnfExpr::Let {
335 id,
336 ty,
337 value,
338 body,
339 ..
340 } => {
341 let kind = if frontier.join_vars.contains(id) {
342 BindingKind::MayJoin
343 } else {
344 match value {
345 LcnfLetValue::Reset(_) | LcnfLetValue::Reuse(_, _, _, _) => {
346 BindingKind::MemoryOp
347 }
348 _ => BindingKind::Immutable,
349 }
350 };
351 let use_count = *use_counts.get(id).unwrap_or(&0);
352 bindings.insert(
353 *id,
354 BindingInfo {
355 kind,
356 value: value.clone(),
357 ty: ty.clone(),
358 use_count,
359 depth,
360 },
361 );
362 collect_binding_info(body, bindings, use_counts, frontier, depth + 1);
363 }
364 LcnfExpr::Case { alts, default, .. } => {
365 for alt in alts {
366 collect_binding_info(&alt.body, bindings, use_counts, frontier, depth + 1);
367 }
368 if let Some(def) = default {
369 collect_binding_info(def, bindings, use_counts, frontier, depth + 1);
370 }
371 }
372 LcnfExpr::Return(_) | LcnfExpr::Unreachable | LcnfExpr::TailCall(_, _) => {}
373 }
374}
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use crate::lcnf::{
379 LcnfExpr, LcnfFunDecl, LcnfLetValue, LcnfLit, LcnfParam, LcnfType, LcnfVarId,
380 };
381 pub(super) fn make_decl(body: LcnfExpr) -> LcnfFunDecl {
382 LcnfFunDecl {
383 name: "test_fn".to_string(),
384 original_name: None,
385 params: vec![],
386 ret_type: LcnfType::Nat,
387 body,
388 is_recursive: false,
389 is_lifted: false,
390 inline_cost: 1,
391 }
392 }
393 #[test]
395 pub(super) fn test_promote_literal_binding() {
396 let body = LcnfExpr::Let {
397 id: LcnfVarId(0),
398 name: "x".to_string(),
399 ty: LcnfType::Nat,
400 value: LcnfLetValue::Lit(LcnfLit::Nat(42)),
401 body: Box::new(LcnfExpr::Return(LcnfArg::Var(LcnfVarId(0)))),
402 };
403 let mut decl = make_decl(body);
404 let mut pass = Mem2Reg::new(Mem2RegConfig::default());
405 pass.run(&mut decl);
406 assert_eq!(decl.body, LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(42))));
407 assert_eq!(pass.report().bindings_promoted, 1);
408 }
409 #[test]
411 pub(super) fn test_promote_fvar_binding() {
412 let body = LcnfExpr::Let {
413 id: LcnfVarId(1),
414 name: "x".to_string(),
415 ty: LcnfType::Nat,
416 value: LcnfLetValue::FVar(LcnfVarId(0)),
417 body: Box::new(LcnfExpr::Return(LcnfArg::Var(LcnfVarId(1)))),
418 };
419 let mut decl = make_decl(body);
420 let mut pass = Mem2Reg::new(Mem2RegConfig::default());
421 pass.run(&mut decl);
422 assert_eq!(decl.body, LcnfExpr::Return(LcnfArg::Var(LcnfVarId(0))));
423 assert_eq!(pass.report().bindings_promoted, 1);
424 }
425 #[test]
427 pub(super) fn test_no_promote_memory_op() {
428 let body = LcnfExpr::Let {
429 id: LcnfVarId(1),
430 name: "slot".to_string(),
431 ty: LcnfType::Object,
432 value: LcnfLetValue::Reset(LcnfVarId(0)),
433 body: Box::new(LcnfExpr::Return(LcnfArg::Var(LcnfVarId(1)))),
434 };
435 let mut decl = make_decl(body);
436 let mut pass = Mem2Reg::new(Mem2RegConfig::default());
437 pass.run(&mut decl);
438 assert!(matches!(decl.body, LcnfExpr::Let { .. }));
439 assert_eq!(pass.report().bindings_promoted, 0);
440 }
441 #[test]
443 pub(super) fn test_no_promote_app() {
444 let body = LcnfExpr::Let {
445 id: LcnfVarId(1),
446 name: "r".to_string(),
447 ty: LcnfType::Nat,
448 value: LcnfLetValue::App(LcnfArg::Var(LcnfVarId(0)), vec![]),
449 body: Box::new(LcnfExpr::Return(LcnfArg::Var(LcnfVarId(1)))),
450 };
451 let mut decl = make_decl(body);
452 let mut pass = Mem2Reg::new(Mem2RegConfig::default());
453 pass.run(&mut decl);
454 assert!(matches!(decl.body, LcnfExpr::Let { .. }));
455 assert_eq!(pass.report().bindings_promoted, 0);
456 }
457 #[test]
459 pub(super) fn test_conservative_promotes_trivial() {
460 let body = LcnfExpr::Let {
461 id: LcnfVarId(0),
462 name: "e".to_string(),
463 ty: LcnfType::Erased,
464 value: LcnfLetValue::Erased,
465 body: Box::new(LcnfExpr::Return(LcnfArg::Var(LcnfVarId(0)))),
466 };
467 let mut decl = make_decl(body);
468 let mut pass = Mem2Reg::new(Mem2RegConfig {
469 conservative: true,
470 ..Default::default()
471 });
472 pass.run(&mut decl);
473 assert_eq!(decl.body, LcnfExpr::Return(LcnfArg::Erased));
474 assert_eq!(pass.report().bindings_promoted, 1);
475 }
476 #[test]
478 pub(super) fn test_promote_chain() {
479 let body = LcnfExpr::Let {
480 id: LcnfVarId(0),
481 name: "x".to_string(),
482 ty: LcnfType::Nat,
483 value: LcnfLetValue::Lit(LcnfLit::Nat(1)),
484 body: Box::new(LcnfExpr::Let {
485 id: LcnfVarId(1),
486 name: "y".to_string(),
487 ty: LcnfType::Nat,
488 value: LcnfLetValue::FVar(LcnfVarId(0)),
489 body: Box::new(LcnfExpr::Return(LcnfArg::Var(LcnfVarId(1)))),
490 }),
491 };
492 let mut decl = make_decl(body);
493 let mut pass = Mem2Reg::new(Mem2RegConfig::default());
494 pass.run(&mut decl);
495 assert_eq!(decl.body, LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(1))));
496 assert_eq!(pass.report().bindings_promoted, 2);
497 }
498 #[test]
501 pub(super) fn test_dominance_frontier() {
502 let case_expr = LcnfExpr::Case {
503 scrutinee: LcnfVarId(0),
504 scrutinee_ty: LcnfType::Object,
505 alts: vec![LcnfAlt {
506 ctor_name: "A".to_string(),
507 ctor_tag: 0,
508 params: vec![LcnfParam {
509 id: LcnfVarId(1),
510 name: "p".to_string(),
511 ty: LcnfType::Nat,
512 erased: false,
513 borrowed: false,
514 }],
515 body: LcnfExpr::Return(LcnfArg::Var(LcnfVarId(1))),
516 }],
517 default: Some(Box::new(LcnfExpr::Return(LcnfArg::Var(LcnfVarId(0))))),
518 };
519 let frontier = compute_dominance_frontier(&case_expr);
520 assert!(frontier.join_vars.contains(&LcnfVarId(1)));
521 }
522 #[test]
524 pub(super) fn test_default_pass_smoke() {
525 let body = LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(0)));
526 let mut decl = make_decl(body);
527 let mut pass = Mem2Reg::default_pass();
528 pass.run(&mut decl);
529 assert_eq!(pass.report().bindings_promoted, 0);
530 }
531}
532#[cfg(test)]
533mod M2R_infra_tests {
534 use super::*;
535 #[test]
536 pub(super) fn test_pass_config() {
537 let config = M2RPassConfig::new("test_pass", M2RPassPhase::Transformation);
538 assert!(config.enabled);
539 assert!(config.phase.is_modifying());
540 assert_eq!(config.phase.name(), "transformation");
541 }
542 #[test]
543 pub(super) fn test_pass_stats() {
544 let mut stats = M2RPassStats::new();
545 stats.record_run(10, 100, 3);
546 stats.record_run(20, 200, 5);
547 assert_eq!(stats.total_runs, 2);
548 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
549 assert!((stats.success_rate() - 1.0).abs() < 0.01);
550 let s = stats.format_summary();
551 assert!(s.contains("Runs: 2/2"));
552 }
553 #[test]
554 pub(super) fn test_pass_registry() {
555 let mut reg = M2RPassRegistry::new();
556 reg.register(M2RPassConfig::new("pass_a", M2RPassPhase::Analysis));
557 reg.register(M2RPassConfig::new("pass_b", M2RPassPhase::Transformation).disabled());
558 assert_eq!(reg.total_passes(), 2);
559 assert_eq!(reg.enabled_count(), 1);
560 reg.update_stats("pass_a", 5, 50, 2);
561 let stats = reg.get_stats("pass_a").expect("stats should exist");
562 assert_eq!(stats.total_changes, 5);
563 }
564 #[test]
565 pub(super) fn test_analysis_cache() {
566 let mut cache = M2RAnalysisCache::new(10);
567 cache.insert("key1".to_string(), vec![1, 2, 3]);
568 assert!(cache.get("key1").is_some());
569 assert!(cache.get("key2").is_none());
570 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
571 cache.invalidate("key1");
572 assert!(!cache.entries["key1"].valid);
573 assert_eq!(cache.size(), 1);
574 }
575 #[test]
576 pub(super) fn test_worklist() {
577 let mut wl = M2RWorklist::new();
578 assert!(wl.push(1));
579 assert!(wl.push(2));
580 assert!(!wl.push(1));
581 assert_eq!(wl.len(), 2);
582 assert_eq!(wl.pop(), Some(1));
583 assert!(!wl.contains(1));
584 assert!(wl.contains(2));
585 }
586 #[test]
587 pub(super) fn test_dominator_tree() {
588 let mut dt = M2RDominatorTree::new(5);
589 dt.set_idom(1, 0);
590 dt.set_idom(2, 0);
591 dt.set_idom(3, 1);
592 assert!(dt.dominates(0, 3));
593 assert!(dt.dominates(1, 3));
594 assert!(!dt.dominates(2, 3));
595 assert!(dt.dominates(3, 3));
596 }
597 #[test]
598 pub(super) fn test_liveness() {
599 let mut liveness = M2RLivenessInfo::new(3);
600 liveness.add_def(0, 1);
601 liveness.add_use(1, 1);
602 assert!(liveness.defs[0].contains(&1));
603 assert!(liveness.uses[1].contains(&1));
604 }
605 #[test]
606 pub(super) fn test_constant_folding() {
607 assert_eq!(M2RConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
608 assert_eq!(M2RConstantFoldingHelper::fold_div_i64(10, 0), None);
609 assert_eq!(M2RConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
610 assert_eq!(
611 M2RConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
612 0b1000
613 );
614 assert_eq!(M2RConstantFoldingHelper::fold_bitnot_i64(0), -1);
615 }
616 #[test]
617 pub(super) fn test_dep_graph() {
618 let mut g = M2RDepGraph::new();
619 g.add_dep(1, 2);
620 g.add_dep(2, 3);
621 g.add_dep(1, 3);
622 assert_eq!(g.dependencies_of(2), vec![1]);
623 let topo = g.topological_sort();
624 assert_eq!(topo.len(), 3);
625 assert!(!g.has_cycle());
626 let pos: std::collections::HashMap<u32, usize> =
627 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
628 assert!(pos[&1] < pos[&2]);
629 assert!(pos[&1] < pos[&3]);
630 assert!(pos[&2] < pos[&3]);
631 }
632}
633#[cfg(test)]
634mod m2rext_pass_tests {
635 use super::*;
636 #[test]
637 pub(super) fn test_m2rext_phase_order() {
638 assert_eq!(M2RExtPassPhase::Early.order(), 0);
639 assert_eq!(M2RExtPassPhase::Middle.order(), 1);
640 assert_eq!(M2RExtPassPhase::Late.order(), 2);
641 assert_eq!(M2RExtPassPhase::Finalize.order(), 3);
642 assert!(M2RExtPassPhase::Early.is_early());
643 assert!(!M2RExtPassPhase::Early.is_late());
644 }
645 #[test]
646 pub(super) fn test_m2rext_config_builder() {
647 let c = M2RExtPassConfig::new("p")
648 .with_phase(M2RExtPassPhase::Late)
649 .with_max_iter(50)
650 .with_debug(1);
651 assert_eq!(c.name, "p");
652 assert_eq!(c.max_iterations, 50);
653 assert!(c.is_debug_enabled());
654 assert!(c.enabled);
655 let c2 = c.disabled();
656 assert!(!c2.enabled);
657 }
658 #[test]
659 pub(super) fn test_m2rext_stats() {
660 let mut s = M2RExtPassStats::new();
661 s.visit();
662 s.visit();
663 s.modify();
664 s.iterate();
665 assert_eq!(s.nodes_visited, 2);
666 assert_eq!(s.nodes_modified, 1);
667 assert!(s.changed);
668 assert_eq!(s.iterations, 1);
669 let e = s.efficiency();
670 assert!((e - 0.5).abs() < 1e-9);
671 }
672 #[test]
673 pub(super) fn test_m2rext_registry() {
674 let mut r = M2RExtPassRegistry::new();
675 r.register(M2RExtPassConfig::new("a").with_phase(M2RExtPassPhase::Early));
676 r.register(M2RExtPassConfig::new("b").disabled());
677 assert_eq!(r.len(), 2);
678 assert_eq!(r.enabled_passes().len(), 1);
679 assert_eq!(r.passes_in_phase(&M2RExtPassPhase::Early).len(), 1);
680 }
681 #[test]
682 pub(super) fn test_m2rext_cache() {
683 let mut c = M2RExtCache::new(4);
684 assert!(c.get(99).is_none());
685 c.put(99, vec![1, 2, 3]);
686 let v = c.get(99).expect("v should be present in map");
687 assert_eq!(v, &[1u8, 2, 3]);
688 assert!(c.hit_rate() > 0.0);
689 assert_eq!(c.live_count(), 1);
690 }
691 #[test]
692 pub(super) fn test_m2rext_worklist() {
693 let mut w = M2RExtWorklist::new(10);
694 w.push(5);
695 w.push(3);
696 w.push(5);
697 assert_eq!(w.len(), 2);
698 assert!(w.contains(5));
699 let first = w.pop().expect("first should be available to pop");
700 assert!(!w.contains(first));
701 }
702 #[test]
703 pub(super) fn test_m2rext_dom_tree() {
704 let mut dt = M2RExtDomTree::new(5);
705 dt.set_idom(1, 0);
706 dt.set_idom(2, 0);
707 dt.set_idom(3, 1);
708 dt.set_idom(4, 1);
709 assert!(dt.dominates(0, 3));
710 assert!(dt.dominates(1, 4));
711 assert!(!dt.dominates(2, 3));
712 assert_eq!(dt.depth_of(3), 2);
713 }
714 #[test]
715 pub(super) fn test_m2rext_liveness() {
716 let mut lv = M2RExtLiveness::new(3);
717 lv.add_def(0, 1);
718 lv.add_use(1, 1);
719 assert!(lv.var_is_def_in_block(0, 1));
720 assert!(lv.var_is_used_in_block(1, 1));
721 assert!(!lv.var_is_def_in_block(1, 1));
722 }
723 #[test]
724 pub(super) fn test_m2rext_const_folder() {
725 let mut cf = M2RExtConstFolder::new();
726 assert_eq!(cf.add_i64(3, 4), Some(7));
727 assert_eq!(cf.div_i64(10, 0), None);
728 assert_eq!(cf.mul_i64(6, 7), Some(42));
729 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
730 assert_eq!(cf.fold_count(), 3);
731 assert_eq!(cf.failure_count(), 1);
732 }
733 #[test]
734 pub(super) fn test_m2rext_dep_graph() {
735 let mut g = M2RExtDepGraph::new(4);
736 g.add_edge(0, 1);
737 g.add_edge(1, 2);
738 g.add_edge(2, 3);
739 assert!(!g.has_cycle());
740 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
741 assert_eq!(g.reachable(0).len(), 4);
742 let sccs = g.scc();
743 assert_eq!(sccs.len(), 4);
744 }
745}
746#[cfg(test)]
747mod m2rx2_pass_tests {
748 use super::*;
749 #[test]
750 pub(super) fn test_m2rx2_phase_order() {
751 assert_eq!(M2RX2PassPhase::Early.order(), 0);
752 assert_eq!(M2RX2PassPhase::Middle.order(), 1);
753 assert_eq!(M2RX2PassPhase::Late.order(), 2);
754 assert_eq!(M2RX2PassPhase::Finalize.order(), 3);
755 assert!(M2RX2PassPhase::Early.is_early());
756 assert!(!M2RX2PassPhase::Early.is_late());
757 }
758 #[test]
759 pub(super) fn test_m2rx2_config_builder() {
760 let c = M2RX2PassConfig::new("p")
761 .with_phase(M2RX2PassPhase::Late)
762 .with_max_iter(50)
763 .with_debug(1);
764 assert_eq!(c.name, "p");
765 assert_eq!(c.max_iterations, 50);
766 assert!(c.is_debug_enabled());
767 assert!(c.enabled);
768 let c2 = c.disabled();
769 assert!(!c2.enabled);
770 }
771 #[test]
772 pub(super) fn test_m2rx2_stats() {
773 let mut s = M2RX2PassStats::new();
774 s.visit();
775 s.visit();
776 s.modify();
777 s.iterate();
778 assert_eq!(s.nodes_visited, 2);
779 assert_eq!(s.nodes_modified, 1);
780 assert!(s.changed);
781 assert_eq!(s.iterations, 1);
782 let e = s.efficiency();
783 assert!((e - 0.5).abs() < 1e-9);
784 }
785 #[test]
786 pub(super) fn test_m2rx2_registry() {
787 let mut r = M2RX2PassRegistry::new();
788 r.register(M2RX2PassConfig::new("a").with_phase(M2RX2PassPhase::Early));
789 r.register(M2RX2PassConfig::new("b").disabled());
790 assert_eq!(r.len(), 2);
791 assert_eq!(r.enabled_passes().len(), 1);
792 assert_eq!(r.passes_in_phase(&M2RX2PassPhase::Early).len(), 1);
793 }
794 #[test]
795 pub(super) fn test_m2rx2_cache() {
796 let mut c = M2RX2Cache::new(4);
797 assert!(c.get(99).is_none());
798 c.put(99, vec![1, 2, 3]);
799 let v = c.get(99).expect("v should be present in map");
800 assert_eq!(v, &[1u8, 2, 3]);
801 assert!(c.hit_rate() > 0.0);
802 assert_eq!(c.live_count(), 1);
803 }
804 #[test]
805 pub(super) fn test_m2rx2_worklist() {
806 let mut w = M2RX2Worklist::new(10);
807 w.push(5);
808 w.push(3);
809 w.push(5);
810 assert_eq!(w.len(), 2);
811 assert!(w.contains(5));
812 let first = w.pop().expect("first should be available to pop");
813 assert!(!w.contains(first));
814 }
815 #[test]
816 pub(super) fn test_m2rx2_dom_tree() {
817 let mut dt = M2RX2DomTree::new(5);
818 dt.set_idom(1, 0);
819 dt.set_idom(2, 0);
820 dt.set_idom(3, 1);
821 dt.set_idom(4, 1);
822 assert!(dt.dominates(0, 3));
823 assert!(dt.dominates(1, 4));
824 assert!(!dt.dominates(2, 3));
825 assert_eq!(dt.depth_of(3), 2);
826 }
827 #[test]
828 pub(super) fn test_m2rx2_liveness() {
829 let mut lv = M2RX2Liveness::new(3);
830 lv.add_def(0, 1);
831 lv.add_use(1, 1);
832 assert!(lv.var_is_def_in_block(0, 1));
833 assert!(lv.var_is_used_in_block(1, 1));
834 assert!(!lv.var_is_def_in_block(1, 1));
835 }
836 #[test]
837 pub(super) fn test_m2rx2_const_folder() {
838 let mut cf = M2RX2ConstFolder::new();
839 assert_eq!(cf.add_i64(3, 4), Some(7));
840 assert_eq!(cf.div_i64(10, 0), None);
841 assert_eq!(cf.mul_i64(6, 7), Some(42));
842 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
843 assert_eq!(cf.fold_count(), 3);
844 assert_eq!(cf.failure_count(), 1);
845 }
846 #[test]
847 pub(super) fn test_m2rx2_dep_graph() {
848 let mut g = M2RX2DepGraph::new(4);
849 g.add_edge(0, 1);
850 g.add_edge(1, 2);
851 g.add_edge(2, 3);
852 assert!(!g.has_cycle());
853 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
854 assert_eq!(g.reachable(0).len(), 4);
855 let sccs = g.scc();
856 assert_eq!(sccs.len(), 4);
857 }
858}