1use crate::c_backend;
6use crate::lcnf::*;
7use crate::native_backend;
8use crate::opt_join::{self, JoinPointConfig};
9use crate::opt_reuse::{self, ReuseConfig};
10use crate::opt_specialize::{self, SpecializationConfig};
11use crate::CodegenTarget;
12use oxilean_kernel::expr::Expr;
13use oxilean_kernel::Name;
14
15use super::types::{
16 CompilerPipeline, OptLevel, PassId, PassStats, PipeAnalysisCache, PipeConstantFoldingHelper,
17 PipeDepGraph, PipeDominatorTree, PipeExtCache, PipeExtConstFolder, PipeExtDepGraph,
18 PipeExtDomTree, PipeExtLiveness, PipeExtPassConfig, PipeExtPassPhase, PipeExtPassRegistry,
19 PipeExtPassStats, PipeExtWorklist, PipeLivenessInfo, PipePassConfig, PipePassPhase,
20 PipePassRegistry, PipePassStats, PipeWorklist, PipeX2Cache, PipeX2ConstFolder, PipeX2DepGraph,
21 PipeX2DomTree, PipeX2Liveness, PipeX2PassConfig, PipeX2PassPhase, PipeX2PassRegistry,
22 PipeX2PassStats, PipeX2Worklist, PipelineBuilder, PipelineChangeSummary, PipelineConfig,
23 PipelineResult, PipelineStats,
24};
25
26pub type LcnfDeclInput = (Name, Vec<(Name, Expr)>, Expr);
28pub(super) fn peel_lam_params(expr: &Expr) -> (Vec<(Name, Expr)>, Expr) {
35 let mut params = Vec::new();
36 let mut cur = expr.clone();
37 loop {
38 match cur {
39 Expr::Lam(_, name, ty, body) => {
40 params.push((name, *ty));
41 cur = *body;
42 }
43 other => {
44 return (params, other);
45 }
46 }
47 }
48}
49pub(super) fn run_join_point_pass(module: &LcnfModule) -> LcnfModule {
55 let config = JoinPointConfig::default();
56 let mut result = module.clone();
57 opt_join::optimize_join_points(&mut result, &config);
58 result
59}
60pub(super) fn run_specialize_pass(module: &LcnfModule) -> LcnfModule {
66 let config = SpecializationConfig::default();
67 let mut result = module.clone();
68 opt_specialize::specialize_module(&mut result, &config);
69 result
70}
71pub(super) fn run_reuse_pass(module: &LcnfModule) -> LcnfModule {
78 let config = ReuseConfig::default();
79 let mut result = module.clone();
80 opt_reuse::optimize_reuse(&mut result, &config);
81 result
82}
83pub(super) fn count_module_lets(module: &LcnfModule) -> usize {
85 module
86 .fun_decls
87 .iter()
88 .map(|d| count_expr_lets(&d.body))
89 .sum()
90}
91pub(super) fn count_expr_lets(expr: &LcnfExpr) -> usize {
93 match expr {
94 LcnfExpr::Let { body, .. } => 1 + count_expr_lets(body),
95 LcnfExpr::Case { alts, default, .. } => {
96 let alt_count: usize = alts.iter().map(|a| count_expr_lets(&a.body)).sum();
97 let def_count = default.as_ref().map(|d| count_expr_lets(d)).unwrap_or(0);
98 alt_count + def_count
99 }
100 _ => 0,
101 }
102}
103pub fn compile_module(
105 module: &LcnfModule,
106 opt_level: OptLevel,
107 target: CodegenTarget,
108) -> PipelineResult {
109 let config = PipelineConfig {
110 opt_level,
111 target,
112 ..Default::default()
113 };
114 let pipeline = CompilerPipeline::new(config.clone());
115 let mut stats = PipelineStats {
116 input_decls: module.fun_decls.len(),
117 ..Default::default()
118 };
119 let passes = config.effective_passes();
120 let max_iter = config.effective_max_iterations();
121 let mut optimized = module.clone();
122 if !passes.is_empty() {
123 optimized = pipeline.iterate_to_fixpoint(optimized, &passes, max_iter, &mut stats);
124 }
125 stats.output_decls = optimized.fun_decls.len();
126 let mut result = PipelineResult {
127 c_output: None,
128 native_output: None,
129 lcnf_module: optimized.clone(),
130 stats,
131 };
132 match target {
133 CodegenTarget::C => {
134 let c_output = c_backend::compile_to_c_default(&optimized);
135 result.c_output = Some(c_output);
136 }
137 CodegenTarget::LlvmIr | CodegenTarget::Rust => {
138 let native_module = native_backend::compile_to_native(&optimized);
139 result.native_output = Some(native_module);
140 }
141 CodegenTarget::Interpreter => {}
142 }
143 result
144}
145pub fn compile_module_o0(module: &LcnfModule) -> PipelineResult {
147 compile_module(module, OptLevel::O0, CodegenTarget::C)
148}
149pub fn compile_module_o2(module: &LcnfModule) -> PipelineResult {
151 compile_module(module, OptLevel::O2, CodegenTarget::C)
152}
153#[cfg(test)]
154mod tests {
155 use super::*;
156 pub(super) fn vid(n: u64) -> LcnfVarId {
157 LcnfVarId(n)
158 }
159 pub(super) fn mk_param(n: u64, name: &str) -> LcnfParam {
160 LcnfParam {
161 id: vid(n),
162 name: name.to_string(),
163 ty: LcnfType::Nat,
164 erased: false,
165 borrowed: false,
166 }
167 }
168 pub(super) fn mk_fun_decl(name: &str, body: LcnfExpr) -> LcnfFunDecl {
169 LcnfFunDecl {
170 name: name.to_string(),
171 original_name: None,
172 params: vec![mk_param(0, "x")],
173 ret_type: LcnfType::Nat,
174 body,
175 is_recursive: false,
176 is_lifted: false,
177 inline_cost: 1,
178 }
179 }
180 pub(super) fn mk_let(id: u64, value: LcnfLetValue, body: LcnfExpr) -> LcnfExpr {
181 LcnfExpr::Let {
182 id: vid(id),
183 name: format!("x{}", id),
184 ty: LcnfType::Nat,
185 value,
186 body: Box::new(body),
187 }
188 }
189 pub(super) fn mk_module(decls: Vec<LcnfFunDecl>) -> LcnfModule {
190 LcnfModule {
191 fun_decls: decls,
192 extern_decls: vec![],
193 name: "test_mod".to_string(),
194 metadata: LcnfModuleMetadata::default(),
195 }
196 }
197 #[test]
198 pub(super) fn test_opt_level_display() {
199 assert_eq!(OptLevel::O0.to_string(), "O0");
200 assert_eq!(OptLevel::O1.to_string(), "O1");
201 assert_eq!(OptLevel::O2.to_string(), "O2");
202 assert_eq!(OptLevel::O3.to_string(), "O3");
203 }
204 #[test]
205 pub(super) fn test_opt_level_to_u8() {
206 assert_eq!(OptLevel::O0.to_u8(), 0);
207 assert_eq!(OptLevel::O1.to_u8(), 1);
208 assert_eq!(OptLevel::O2.to_u8(), 2);
209 assert_eq!(OptLevel::O3.to_u8(), 3);
210 }
211 #[test]
212 pub(super) fn test_opt_level_default_passes() {
213 assert!(OptLevel::O0.default_passes().is_empty());
214 assert!(!OptLevel::O1.default_passes().is_empty());
215 assert!(OptLevel::O2.default_passes().len() > OptLevel::O1.default_passes().len());
216 assert!(OptLevel::O3.default_passes().len() >= OptLevel::O2.default_passes().len());
217 }
218 #[test]
219 pub(super) fn test_pass_id_display() {
220 assert_eq!(PassId::Dce.to_string(), "dce");
221 assert_eq!(PassId::JoinPoints.to_string(), "join-points");
222 assert_eq!(PassId::Specialize.to_string(), "specialize");
223 assert_eq!(PassId::Reuse.to_string(), "reuse");
224 assert_eq!(PassId::ClosureConvert.to_string(), "closure-convert");
225 assert_eq!(
226 PassId::Custom("my-pass".to_string()).to_string(),
227 "custom:my-pass"
228 );
229 }
230 #[test]
231 pub(super) fn test_pipeline_config_default() {
232 let cfg = PipelineConfig::default();
233 assert_eq!(cfg.opt_level, OptLevel::O1);
234 assert_eq!(cfg.target, CodegenTarget::C);
235 assert!(!cfg.debug);
236 assert!(!cfg.emit_ir);
237 }
238 #[test]
239 pub(super) fn test_pipeline_config_effective_passes() {
240 let mut cfg = PipelineConfig::default();
241 assert_eq!(cfg.effective_passes(), OptLevel::O1.default_passes());
242 cfg.passes = vec![PassId::Dce, PassId::JoinPoints];
243 assert_eq!(cfg.effective_passes().len(), 2);
244 }
245 #[test]
246 pub(super) fn test_full_pipeline_o0() {
247 let module = mk_module(vec![mk_fun_decl(
248 "test",
249 LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(42))),
250 )]);
251 let result = compile_module_o0(&module);
252 assert!(result.c_output.is_some());
253 assert!(result.native_output.is_none());
254 }
255 #[test]
256 pub(super) fn test_pipeline_with_single_pass() {
257 let body = mk_let(
258 1,
259 LcnfLetValue::Lit(LcnfLit::Nat(42)),
260 mk_let(
261 2,
262 LcnfLetValue::Lit(LcnfLit::Nat(99)),
263 LcnfExpr::Return(LcnfArg::Var(vid(1))),
264 ),
265 );
266 let _module = mk_module(vec![mk_fun_decl("test", body)]);
267 let config = PipelineConfig {
268 opt_level: OptLevel::O0,
269 target: CodegenTarget::C,
270 passes: vec![PassId::Dce],
271 max_iterations: 3,
272 ..Default::default()
273 };
274 let pipeline = CompilerPipeline::new(config.clone());
275 let result = pipeline.run_pipeline(vec![], &config);
276 let _ = result.stats.iterations;
277 }
278 #[test]
279 pub(super) fn test_iterate_to_fixpoint() {
280 let body = mk_let(
281 1,
282 LcnfLetValue::Lit(LcnfLit::Nat(42)),
283 mk_let(
284 2,
285 LcnfLetValue::Lit(LcnfLit::Nat(99)),
286 LcnfExpr::Return(LcnfArg::Var(vid(1))),
287 ),
288 );
289 let module = mk_module(vec![mk_fun_decl("test", body)]);
290 let pipeline = CompilerPipeline::default_pipeline();
291 let mut stats = PipelineStats::default();
292 let result = pipeline.iterate_to_fixpoint(module, &[PassId::Dce], 5, &mut stats);
293 assert_eq!(result.fun_decls.len(), 1);
294 let let_count = count_expr_lets(&result.fun_decls[0].body);
295 assert!(let_count <= 1, "expected at most 1 let, got {}", let_count);
296 }
297 #[test]
298 pub(super) fn test_run_pass_dce() {
299 let body = mk_let(
300 1,
301 LcnfLetValue::Lit(LcnfLit::Nat(42)),
302 LcnfExpr::Return(LcnfArg::Var(vid(0))),
303 );
304 let module = mk_module(vec![mk_fun_decl("test", body)]);
305 let pipeline = CompilerPipeline::default_pipeline();
306 let result = pipeline.run_pass(&module, &PassId::Dce);
307 assert!(result.changed);
308 }
309 #[test]
310 pub(super) fn test_run_pass_custom() {
311 let module = mk_module(vec![mk_fun_decl(
312 "test",
313 LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(0))),
314 )]);
315 let pipeline = CompilerPipeline::default_pipeline();
316 let result = pipeline.run_pass(&module, &PassId::Custom("noop".to_string()));
317 assert!(!result.changed);
318 }
319 #[test]
320 pub(super) fn test_compile_module_c() {
321 let module = mk_module(vec![mk_fun_decl(
322 "main",
323 LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(0))),
324 )]);
325 let result = compile_module(&module, OptLevel::O1, CodegenTarget::C);
326 assert!(result.c_output.is_some());
327 assert!(result.native_output.is_none());
328 }
329 #[test]
330 pub(super) fn test_compile_module_native() {
331 let module = mk_module(vec![mk_fun_decl(
332 "main",
333 LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(0))),
334 )]);
335 let result = compile_module(&module, OptLevel::O0, CodegenTarget::LlvmIr);
336 assert!(result.c_output.is_none());
337 assert!(result.native_output.is_some());
338 }
339 #[test]
340 pub(super) fn test_pipeline_stats_display() {
341 let stats = PipelineStats {
342 total_time_us: 1234,
343 iterations: 3,
344 input_decls: 5,
345 output_decls: 4,
346 per_pass: vec![(
347 PassId::Dce,
348 PassStats {
349 decls_processed: 5,
350 transformations: 2,
351 time_us: 500,
352 },
353 )],
354 };
355 let s = stats.to_string();
356 assert!(s.contains("total_time=1234us"));
357 assert!(s.contains("iterations=3"));
358 assert!(s.contains("dce"));
359 }
360 #[test]
361 pub(super) fn test_pass_stats_display() {
362 let stats = PassStats {
363 decls_processed: 10,
364 transformations: 3,
365 time_us: 100,
366 };
367 let s = stats.to_string();
368 assert!(s.contains("decls=10"));
369 assert!(s.contains("transforms=3"));
370 }
371 #[test]
372 pub(super) fn test_count_module_lets() {
373 let body = mk_let(
374 1,
375 LcnfLetValue::Lit(LcnfLit::Nat(1)),
376 mk_let(
377 2,
378 LcnfLetValue::Lit(LcnfLit::Nat(2)),
379 LcnfExpr::Return(LcnfArg::Var(vid(2))),
380 ),
381 );
382 let module = mk_module(vec![mk_fun_decl("test", body)]);
383 assert_eq!(count_module_lets(&module), 2);
384 }
385 #[test]
386 pub(super) fn test_empty_module_pipeline() {
387 let module = mk_module(vec![]);
388 let result = compile_module_o0(&module);
389 assert_eq!(result.lcnf_module.fun_decls.len(), 0);
390 }
391 #[test]
392 pub(super) fn test_compile_module_o2() {
393 let body = mk_let(
394 1,
395 LcnfLetValue::Lit(LcnfLit::Nat(42)),
396 mk_let(
397 2,
398 LcnfLetValue::FVar(vid(1)),
399 mk_let(
400 3,
401 LcnfLetValue::Lit(LcnfLit::Nat(99)),
402 LcnfExpr::Return(LcnfArg::Var(vid(2))),
403 ),
404 ),
405 );
406 let module = mk_module(vec![mk_fun_decl("opt_test", body)]);
407 let result = compile_module_o2(&module);
408 assert!(result.c_output.is_some());
409 }
410}
411#[cfg(test)]
412mod extra_pipeline_tests {
413 use super::*;
414 pub(super) fn mk_simple_module() -> LcnfModule {
415 LcnfModule {
416 fun_decls: vec![LcnfFunDecl {
417 name: "test".to_string(),
418 original_name: None,
419 params: vec![],
420 ret_type: LcnfType::Nat,
421 body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(0))),
422 is_recursive: false,
423 is_lifted: false,
424 inline_cost: 1,
425 }],
426 extern_decls: vec![],
427 name: "mod".to_string(),
428 metadata: LcnfModuleMetadata::default(),
429 }
430 }
431 #[test]
432 pub(super) fn test_pipeline_builder_defaults() {
433 let cfg = PipelineBuilder::new().build();
434 assert_eq!(cfg.opt_level, OptLevel::O1);
435 assert!(!cfg.debug);
436 }
437 #[test]
438 pub(super) fn test_pipeline_builder_opt_level() {
439 let cfg = PipelineBuilder::new().opt_level(OptLevel::O3).build();
440 assert_eq!(cfg.opt_level, OptLevel::O3);
441 }
442 #[test]
443 pub(super) fn test_pipeline_builder_target() {
444 let cfg = PipelineBuilder::new().target(CodegenTarget::LlvmIr).build();
445 assert_eq!(cfg.target, CodegenTarget::LlvmIr);
446 }
447 #[test]
448 pub(super) fn test_pipeline_builder_debug() {
449 let cfg = PipelineBuilder::new().debug().build();
450 assert!(cfg.debug);
451 }
452 #[test]
453 pub(super) fn test_pipeline_builder_emit_ir() {
454 let cfg = PipelineBuilder::new().emit_ir().build();
455 assert!(cfg.emit_ir);
456 }
457 #[test]
458 pub(super) fn test_pipeline_builder_with_passes() {
459 let cfg = PipelineBuilder::new()
460 .with_passes(vec![PassId::Dce, PassId::JoinPoints])
461 .build();
462 assert_eq!(cfg.passes.len(), 2);
463 }
464 #[test]
465 pub(super) fn test_pipeline_builder_max_iterations() {
466 let cfg = PipelineBuilder::new().max_iterations(7).build();
467 assert_eq!(cfg.max_iterations, 7);
468 }
469 #[test]
470 pub(super) fn test_change_summary_mark_active() {
471 let mut s = PipelineChangeSummary::new();
472 s.mark_active("dce");
473 assert!(s.any_changed());
474 }
475 #[test]
476 pub(super) fn test_change_summary_mark_converged() {
477 let mut s = PipelineChangeSummary::new();
478 s.mark_converged("join-points");
479 assert!(!s.any_changed());
480 assert_eq!(s.converged_passes.len(), 1);
481 }
482 #[test]
483 pub(super) fn test_change_summary_display() {
484 let mut s = PipelineChangeSummary::new();
485 s.mark_active("dce");
486 let text = format!("{}", s);
487 assert!(text.contains("dce"));
488 }
489 #[test]
490 pub(super) fn test_run_pipeline_with_builder() {
491 let cfg = PipelineBuilder::new()
492 .opt_level(OptLevel::O1)
493 .target(CodegenTarget::C)
494 .build();
495 let pipeline = CompilerPipeline::new(cfg.clone());
496 let result = pipeline.run_pipeline(vec![], &cfg);
497 assert!(result.c_output.is_some());
498 }
499 #[test]
500 pub(super) fn test_opt_level_max_iterations_ordering() {
501 assert!(OptLevel::O0.max_iterations() <= OptLevel::O1.max_iterations());
502 assert!(OptLevel::O1.max_iterations() <= OptLevel::O2.max_iterations());
503 assert!(OptLevel::O2.max_iterations() <= OptLevel::O3.max_iterations());
504 }
505}
506#[cfg(test)]
507mod Pipe_infra_tests {
508 use super::*;
509 #[test]
510 pub(super) fn test_pass_config() {
511 let config = PipePassConfig::new("test_pass", PipePassPhase::Transformation);
512 assert!(config.enabled);
513 assert!(config.phase.is_modifying());
514 assert_eq!(config.phase.name(), "transformation");
515 }
516 #[test]
517 pub(super) fn test_pass_stats() {
518 let mut stats = PipePassStats::new();
519 stats.record_run(10, 100, 3);
520 stats.record_run(20, 200, 5);
521 assert_eq!(stats.total_runs, 2);
522 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
523 assert!((stats.success_rate() - 1.0).abs() < 0.01);
524 let s = stats.format_summary();
525 assert!(s.contains("Runs: 2/2"));
526 }
527 #[test]
528 pub(super) fn test_pass_registry() {
529 let mut reg = PipePassRegistry::new();
530 reg.register(PipePassConfig::new("pass_a", PipePassPhase::Analysis));
531 reg.register(PipePassConfig::new("pass_b", PipePassPhase::Transformation).disabled());
532 assert_eq!(reg.total_passes(), 2);
533 assert_eq!(reg.enabled_count(), 1);
534 reg.update_stats("pass_a", 5, 50, 2);
535 let stats = reg.get_stats("pass_a").expect("stats should exist");
536 assert_eq!(stats.total_changes, 5);
537 }
538 #[test]
539 pub(super) fn test_analysis_cache() {
540 let mut cache = PipeAnalysisCache::new(10);
541 cache.insert("key1".to_string(), vec![1, 2, 3]);
542 assert!(cache.get("key1").is_some());
543 assert!(cache.get("key2").is_none());
544 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
545 cache.invalidate("key1");
546 assert!(!cache.entries["key1"].valid);
547 assert_eq!(cache.size(), 1);
548 }
549 #[test]
550 pub(super) fn test_worklist() {
551 let mut wl = PipeWorklist::new();
552 assert!(wl.push(1));
553 assert!(wl.push(2));
554 assert!(!wl.push(1));
555 assert_eq!(wl.len(), 2);
556 assert_eq!(wl.pop(), Some(1));
557 assert!(!wl.contains(1));
558 assert!(wl.contains(2));
559 }
560 #[test]
561 pub(super) fn test_dominator_tree() {
562 let mut dt = PipeDominatorTree::new(5);
563 dt.set_idom(1, 0);
564 dt.set_idom(2, 0);
565 dt.set_idom(3, 1);
566 assert!(dt.dominates(0, 3));
567 assert!(dt.dominates(1, 3));
568 assert!(!dt.dominates(2, 3));
569 assert!(dt.dominates(3, 3));
570 }
571 #[test]
572 pub(super) fn test_liveness() {
573 let mut liveness = PipeLivenessInfo::new(3);
574 liveness.add_def(0, 1);
575 liveness.add_use(1, 1);
576 assert!(liveness.defs[0].contains(&1));
577 assert!(liveness.uses[1].contains(&1));
578 }
579 #[test]
580 pub(super) fn test_constant_folding() {
581 assert_eq!(PipeConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
582 assert_eq!(PipeConstantFoldingHelper::fold_div_i64(10, 0), None);
583 assert_eq!(PipeConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
584 assert_eq!(
585 PipeConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
586 0b1000
587 );
588 assert_eq!(PipeConstantFoldingHelper::fold_bitnot_i64(0), -1);
589 }
590 #[test]
591 pub(super) fn test_dep_graph() {
592 let mut g = PipeDepGraph::new();
593 g.add_dep(1, 2);
594 g.add_dep(2, 3);
595 g.add_dep(1, 3);
596 assert_eq!(g.dependencies_of(2), vec![1]);
597 let topo = g.topological_sort();
598 assert_eq!(topo.len(), 3);
599 assert!(!g.has_cycle());
600 let pos: std::collections::HashMap<u32, usize> =
601 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
602 assert!(pos[&1] < pos[&2]);
603 assert!(pos[&1] < pos[&3]);
604 assert!(pos[&2] < pos[&3]);
605 }
606}
607#[cfg(test)]
608mod pipeext_pass_tests {
609 use super::*;
610 #[test]
611 pub(super) fn test_pipeext_phase_order() {
612 assert_eq!(PipeExtPassPhase::Early.order(), 0);
613 assert_eq!(PipeExtPassPhase::Middle.order(), 1);
614 assert_eq!(PipeExtPassPhase::Late.order(), 2);
615 assert_eq!(PipeExtPassPhase::Finalize.order(), 3);
616 assert!(PipeExtPassPhase::Early.is_early());
617 assert!(!PipeExtPassPhase::Early.is_late());
618 }
619 #[test]
620 pub(super) fn test_pipeext_config_builder() {
621 let c = PipeExtPassConfig::new("p")
622 .with_phase(PipeExtPassPhase::Late)
623 .with_max_iter(50)
624 .with_debug(1);
625 assert_eq!(c.name, "p");
626 assert_eq!(c.max_iterations, 50);
627 assert!(c.is_debug_enabled());
628 assert!(c.enabled);
629 let c2 = c.disabled();
630 assert!(!c2.enabled);
631 }
632 #[test]
633 pub(super) fn test_pipeext_stats() {
634 let mut s = PipeExtPassStats::new();
635 s.visit();
636 s.visit();
637 s.modify();
638 s.iterate();
639 assert_eq!(s.nodes_visited, 2);
640 assert_eq!(s.nodes_modified, 1);
641 assert!(s.changed);
642 assert_eq!(s.iterations, 1);
643 let e = s.efficiency();
644 assert!((e - 0.5).abs() < 1e-9);
645 }
646 #[test]
647 pub(super) fn test_pipeext_registry() {
648 let mut r = PipeExtPassRegistry::new();
649 r.register(PipeExtPassConfig::new("a").with_phase(PipeExtPassPhase::Early));
650 r.register(PipeExtPassConfig::new("b").disabled());
651 assert_eq!(r.len(), 2);
652 assert_eq!(r.enabled_passes().len(), 1);
653 assert_eq!(r.passes_in_phase(&PipeExtPassPhase::Early).len(), 1);
654 }
655 #[test]
656 pub(super) fn test_pipeext_cache() {
657 let mut c = PipeExtCache::new(4);
658 assert!(c.get(99).is_none());
659 c.put(99, vec![1, 2, 3]);
660 let v = c.get(99).expect("v should be present in map");
661 assert_eq!(v, &[1u8, 2, 3]);
662 assert!(c.hit_rate() > 0.0);
663 assert_eq!(c.live_count(), 1);
664 }
665 #[test]
666 pub(super) fn test_pipeext_worklist() {
667 let mut w = PipeExtWorklist::new(10);
668 w.push(5);
669 w.push(3);
670 w.push(5);
671 assert_eq!(w.len(), 2);
672 assert!(w.contains(5));
673 let first = w.pop().expect("first should be available to pop");
674 assert!(!w.contains(first));
675 }
676 #[test]
677 pub(super) fn test_pipeext_dom_tree() {
678 let mut dt = PipeExtDomTree::new(5);
679 dt.set_idom(1, 0);
680 dt.set_idom(2, 0);
681 dt.set_idom(3, 1);
682 dt.set_idom(4, 1);
683 assert!(dt.dominates(0, 3));
684 assert!(dt.dominates(1, 4));
685 assert!(!dt.dominates(2, 3));
686 assert_eq!(dt.depth_of(3), 2);
687 }
688 #[test]
689 pub(super) fn test_pipeext_liveness() {
690 let mut lv = PipeExtLiveness::new(3);
691 lv.add_def(0, 1);
692 lv.add_use(1, 1);
693 assert!(lv.var_is_def_in_block(0, 1));
694 assert!(lv.var_is_used_in_block(1, 1));
695 assert!(!lv.var_is_def_in_block(1, 1));
696 }
697 #[test]
698 pub(super) fn test_pipeext_const_folder() {
699 let mut cf = PipeExtConstFolder::new();
700 assert_eq!(cf.add_i64(3, 4), Some(7));
701 assert_eq!(cf.div_i64(10, 0), None);
702 assert_eq!(cf.mul_i64(6, 7), Some(42));
703 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
704 assert_eq!(cf.fold_count(), 3);
705 assert_eq!(cf.failure_count(), 1);
706 }
707 #[test]
708 pub(super) fn test_pipeext_dep_graph() {
709 let mut g = PipeExtDepGraph::new(4);
710 g.add_edge(0, 1);
711 g.add_edge(1, 2);
712 g.add_edge(2, 3);
713 assert!(!g.has_cycle());
714 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
715 assert_eq!(g.reachable(0).len(), 4);
716 let sccs = g.scc();
717 assert_eq!(sccs.len(), 4);
718 }
719}
720#[cfg(test)]
721mod pipex2_pass_tests {
722 use super::*;
723 #[test]
724 pub(super) fn test_pipex2_phase_order() {
725 assert_eq!(PipeX2PassPhase::Early.order(), 0);
726 assert_eq!(PipeX2PassPhase::Middle.order(), 1);
727 assert_eq!(PipeX2PassPhase::Late.order(), 2);
728 assert_eq!(PipeX2PassPhase::Finalize.order(), 3);
729 assert!(PipeX2PassPhase::Early.is_early());
730 assert!(!PipeX2PassPhase::Early.is_late());
731 }
732 #[test]
733 pub(super) fn test_pipex2_config_builder() {
734 let c = PipeX2PassConfig::new("p")
735 .with_phase(PipeX2PassPhase::Late)
736 .with_max_iter(50)
737 .with_debug(1);
738 assert_eq!(c.name, "p");
739 assert_eq!(c.max_iterations, 50);
740 assert!(c.is_debug_enabled());
741 assert!(c.enabled);
742 let c2 = c.disabled();
743 assert!(!c2.enabled);
744 }
745 #[test]
746 pub(super) fn test_pipex2_stats() {
747 let mut s = PipeX2PassStats::new();
748 s.visit();
749 s.visit();
750 s.modify();
751 s.iterate();
752 assert_eq!(s.nodes_visited, 2);
753 assert_eq!(s.nodes_modified, 1);
754 assert!(s.changed);
755 assert_eq!(s.iterations, 1);
756 let e = s.efficiency();
757 assert!((e - 0.5).abs() < 1e-9);
758 }
759 #[test]
760 pub(super) fn test_pipex2_registry() {
761 let mut r = PipeX2PassRegistry::new();
762 r.register(PipeX2PassConfig::new("a").with_phase(PipeX2PassPhase::Early));
763 r.register(PipeX2PassConfig::new("b").disabled());
764 assert_eq!(r.len(), 2);
765 assert_eq!(r.enabled_passes().len(), 1);
766 assert_eq!(r.passes_in_phase(&PipeX2PassPhase::Early).len(), 1);
767 }
768 #[test]
769 pub(super) fn test_pipex2_cache() {
770 let mut c = PipeX2Cache::new(4);
771 assert!(c.get(99).is_none());
772 c.put(99, vec![1, 2, 3]);
773 let v = c.get(99).expect("v should be present in map");
774 assert_eq!(v, &[1u8, 2, 3]);
775 assert!(c.hit_rate() > 0.0);
776 assert_eq!(c.live_count(), 1);
777 }
778 #[test]
779 pub(super) fn test_pipex2_worklist() {
780 let mut w = PipeX2Worklist::new(10);
781 w.push(5);
782 w.push(3);
783 w.push(5);
784 assert_eq!(w.len(), 2);
785 assert!(w.contains(5));
786 let first = w.pop().expect("first should be available to pop");
787 assert!(!w.contains(first));
788 }
789 #[test]
790 pub(super) fn test_pipex2_dom_tree() {
791 let mut dt = PipeX2DomTree::new(5);
792 dt.set_idom(1, 0);
793 dt.set_idom(2, 0);
794 dt.set_idom(3, 1);
795 dt.set_idom(4, 1);
796 assert!(dt.dominates(0, 3));
797 assert!(dt.dominates(1, 4));
798 assert!(!dt.dominates(2, 3));
799 assert_eq!(dt.depth_of(3), 2);
800 }
801 #[test]
802 pub(super) fn test_pipex2_liveness() {
803 let mut lv = PipeX2Liveness::new(3);
804 lv.add_def(0, 1);
805 lv.add_use(1, 1);
806 assert!(lv.var_is_def_in_block(0, 1));
807 assert!(lv.var_is_used_in_block(1, 1));
808 assert!(!lv.var_is_def_in_block(1, 1));
809 }
810 #[test]
811 pub(super) fn test_pipex2_const_folder() {
812 let mut cf = PipeX2ConstFolder::new();
813 assert_eq!(cf.add_i64(3, 4), Some(7));
814 assert_eq!(cf.div_i64(10, 0), None);
815 assert_eq!(cf.mul_i64(6, 7), Some(42));
816 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
817 assert_eq!(cf.fold_count(), 3);
818 assert_eq!(cf.failure_count(), 1);
819 }
820 #[test]
821 pub(super) fn test_pipex2_dep_graph() {
822 let mut g = PipeX2DepGraph::new(4);
823 g.add_edge(0, 1);
824 g.add_edge(1, 2);
825 g.add_edge(2, 3);
826 assert!(!g.has_cycle());
827 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
828 assert_eq!(g.reachable(0).len(), 4);
829 let sccs = g.scc();
830 assert_eq!(sccs.len(), 4);
831 }
832}