1use crate::lcnf::*;
6
7use super::types::{
8 FcmpPred, IcmpPred, LLVMAnalysisCache, LLVMConstantFoldingHelper, LLVMDepGraph,
9 LLVMDominatorTree, LLVMExtCache, LLVMExtConstFolder, LLVMExtDepGraph, LLVMExtDomTree,
10 LLVMExtLiveness, LLVMExtPassConfig, LLVMExtPassPhase, LLVMExtPassRegistry, LLVMExtPassStats,
11 LLVMExtWorklist, LLVMLivenessInfo, LLVMPassConfig, LLVMPassPhase, LLVMPassRegistry,
12 LLVMPassStats, LLVMWorklist, LlvmAttr, LlvmBackend, LlvmFunc, LlvmInstr, LlvmLinkage,
13 LlvmModule, LlvmType, LlvmValue,
14};
15
16#[cfg(test)]
17mod tests {
18 use super::*;
19 #[test]
20 pub(super) fn test_llvm_type_display() {
21 assert_eq!(LlvmType::I32.to_string(), "i32");
22 assert_eq!(LlvmType::Ptr.to_string(), "ptr");
23 assert_eq!(
24 LlvmType::Array(4, Box::new(LlvmType::I32)).to_string(),
25 "[4 x i32]"
26 );
27 assert_eq!(
28 LlvmType::Vector(8, Box::new(LlvmType::F32)).to_string(),
29 "<8 x float>"
30 );
31 assert_eq!(
32 LlvmType::Struct(vec![LlvmType::I64, LlvmType::Ptr]).to_string(),
33 "{ i64, ptr }"
34 );
35 assert_eq!(
36 LlvmType::FuncType {
37 ret: Box::new(LlvmType::I32),
38 params: vec![LlvmType::I64, LlvmType::Ptr],
39 variadic: false,
40 }
41 .to_string(),
42 "i32 (i64, ptr)"
43 );
44 assert_eq!(
45 LlvmType::FuncType {
46 ret: Box::new(LlvmType::I32),
47 params: vec![LlvmType::I8],
48 variadic: true,
49 }
50 .to_string(),
51 "i32 (i8, ...)"
52 );
53 assert_eq!(
54 LlvmType::Named("MyStruct".to_string()).to_string(),
55 "%MyStruct"
56 );
57 }
58 #[test]
59 pub(super) fn test_llvm_value_display() {
60 assert_eq!(LlvmValue::Const(42).to_string(), "42");
61 assert_eq!(LlvmValue::Undef.to_string(), "undef");
62 assert_eq!(LlvmValue::Null.to_string(), "null");
63 assert_eq!(LlvmValue::True_.to_string(), "true");
64 assert_eq!(LlvmValue::False_.to_string(), "false");
65 assert_eq!(
66 LlvmValue::GlobalRef("printf".to_string()).to_string(),
67 "@printf"
68 );
69 assert_eq!(LlvmValue::LocalRef("x0".to_string()).to_string(), "%x0");
70 assert_eq!(LlvmValue::ZeroInitializer.to_string(), "zeroinitializer");
71 assert_eq!(
72 LlvmValue::ConstArray(
73 LlvmType::I32,
74 vec![LlvmValue::Const(1), LlvmValue::Const(2)]
75 )
76 .to_string(),
77 "[i32 1, i32 2]"
78 );
79 }
80 #[test]
81 pub(super) fn test_icmp_pred_display() {
82 assert_eq!(IcmpPred::Eq.to_string(), "eq");
83 assert_eq!(IcmpPred::Ne.to_string(), "ne");
84 assert_eq!(IcmpPred::Slt.to_string(), "slt");
85 assert_eq!(IcmpPred::Sgt.to_string(), "sgt");
86 assert_eq!(IcmpPred::Sle.to_string(), "sle");
87 assert_eq!(IcmpPred::Sge.to_string(), "sge");
88 assert_eq!(IcmpPred::Ult.to_string(), "ult");
89 assert_eq!(IcmpPred::Ugt.to_string(), "ugt");
90 assert_eq!(IcmpPred::Ule.to_string(), "ule");
91 assert_eq!(IcmpPred::Uge.to_string(), "uge");
92 assert_eq!(FcmpPred::Oeq.to_string(), "oeq");
93 assert_eq!(FcmpPred::Uno.to_string(), "uno");
94 assert_eq!(FcmpPred::True_.to_string(), "true");
95 assert_eq!(FcmpPred::False_.to_string(), "false");
96 }
97 #[test]
98 pub(super) fn test_llvm_instr_add_display() {
99 let instr = LlvmInstr::Add {
100 result: "r0".to_string(),
101 lhs: LlvmValue::LocalRef("x0".to_string()),
102 rhs: LlvmValue::LocalRef("x1".to_string()),
103 };
104 assert_eq!(instr.to_string(), " %r0 = add i64 %x0, %x1");
105 let instr2 = LlvmInstr::ICmp {
106 result: "cmp".to_string(),
107 pred: IcmpPred::Slt,
108 lhs: LlvmValue::LocalRef("a".to_string()),
109 rhs: LlvmValue::Const(0),
110 };
111 assert_eq!(instr2.to_string(), " %cmp = icmp slt i64 %a, 0");
112 let instr3 = LlvmInstr::Ret(Some((LlvmType::I64, LlvmValue::Const(0))));
113 assert_eq!(instr3.to_string(), " ret i64 0");
114 let instr4 = LlvmInstr::Ret(None);
115 assert_eq!(instr4.to_string(), " ret void");
116 }
117 #[test]
118 pub(super) fn test_llvm_func_display() {
119 let func = LlvmFunc {
120 name: "add_two".to_string(),
121 ret_ty: LlvmType::I64,
122 params: vec![
123 (LlvmType::I64, "a".to_string()),
124 (LlvmType::I64, "b".to_string()),
125 ],
126 body: vec![
127 LlvmInstr::Add {
128 result: "result".to_string(),
129 lhs: LlvmValue::LocalRef("a".to_string()),
130 rhs: LlvmValue::LocalRef("b".to_string()),
131 },
132 LlvmInstr::Ret(Some((
133 LlvmType::I64,
134 LlvmValue::LocalRef("result".to_string()),
135 ))),
136 ],
137 linkage: LlvmLinkage::External,
138 attrs: vec![LlvmAttr::NoUnwind],
139 is_declare: false,
140 };
141 let text = func.to_string();
142 assert!(text.contains("define"));
143 assert!(text.contains("@add_two"));
144 assert!(text.contains("i64 %a"));
145 assert!(text.contains("i64 %b"));
146 assert!(text.contains("%result = add i64 %a, %b"));
147 assert!(text.contains("ret i64 %result"));
148 assert!(text.contains("nounwind"));
149 }
150 #[test]
151 pub(super) fn test_llvm_module_emit() {
152 let func = LlvmFunc {
153 name: "main_func".to_string(),
154 ret_ty: LlvmType::I64,
155 params: vec![],
156 body: vec![LlvmInstr::Ret(Some((LlvmType::I64, LlvmValue::Const(42))))],
157 linkage: LlvmLinkage::External,
158 attrs: vec![],
159 is_declare: false,
160 };
161 let module = LlvmModule {
162 source_filename: "test.ll".to_string(),
163 target_triple: "x86_64-unknown-linux-gnu".to_string(),
164 data_layout: String::new(),
165 type_aliases: vec![],
166 globals: vec![],
167 functions: vec![func],
168 metadata: vec![],
169 };
170 let text = module.emit();
171 assert!(text.contains("source_filename = \"test.ll\""));
172 assert!(text.contains("target triple = \"x86_64-unknown-linux-gnu\""));
173 assert!(text.contains("define i64 @main_func()"));
174 assert!(text.contains("ret i64 42"));
175 }
176 #[test]
177 pub(super) fn test_mangle_name() {
178 assert_eq!(LlvmBackend::mangle_name("Nat.add"), "Nat_add");
179 assert_eq!(LlvmBackend::mangle_name("foo bar"), "foo_bar");
180 assert_eq!(LlvmBackend::mangle_name("hello_world"), "hello_world");
181 assert_eq!(LlvmBackend::mangle_name("a.b.c"), "a_b_c");
182 assert_eq!(LlvmBackend::mangle_name("my-func"), "my_func");
183 }
184 #[test]
185 pub(super) fn test_llvm_type_for_lcnf() {
186 assert_eq!(LlvmBackend::llvm_type_for(&LcnfType::Nat), LlvmType::I64);
187 assert_eq!(LlvmBackend::llvm_type_for(&LcnfType::Object), LlvmType::Ptr);
188 assert_eq!(
189 LlvmBackend::llvm_type_for(&LcnfType::LcnfString),
190 LlvmType::Ptr
191 );
192 assert_eq!(LlvmBackend::llvm_type_for(&LcnfType::Erased), LlvmType::I64);
193 assert_eq!(LlvmBackend::llvm_type_for(&LcnfType::Unit), LlvmType::I64);
194 assert_eq!(
195 LlvmBackend::llvm_type_for(&LcnfType::Fun(
196 vec![LcnfType::Nat],
197 Box::new(LcnfType::Nat)
198 )),
199 LlvmType::Ptr
200 );
201 }
202 #[test]
203 pub(super) fn test_compile_decl_simple() {
204 let mut backend = LlvmBackend::new();
205 let decl = LcnfFunDecl {
206 name: "answer".to_string(),
207 original_name: None,
208 params: vec![],
209 ret_type: LcnfType::Nat,
210 body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(42))),
211 is_recursive: false,
212 is_lifted: false,
213 inline_cost: 1,
214 };
215 let func = backend.compile_decl(&decl);
216 assert_eq!(func.name, "answer");
217 assert_eq!(func.ret_ty, LlvmType::I64);
218 assert!(func.params.is_empty());
219 assert!(!func.body.is_empty());
220 let text = func.to_string();
221 assert!(text.contains("ret i64 42"));
222 }
223 #[test]
224 pub(super) fn test_llvm_backend_emit_module() {
225 let mut backend = LlvmBackend::new();
226 let decl = LcnfFunDecl {
227 name: "Nat.succ".to_string(),
228 original_name: None,
229 params: vec![LcnfParam {
230 id: LcnfVarId(0),
231 name: "n".to_string(),
232 ty: LcnfType::Nat,
233 erased: false,
234 borrowed: false,
235 }],
236 ret_type: LcnfType::Nat,
237 body: LcnfExpr::Return(LcnfArg::Var(LcnfVarId(0))),
238 is_recursive: false,
239 is_lifted: false,
240 inline_cost: 1,
241 };
242 let result = backend.emit_module(&[decl]);
243 assert!(result.is_ok());
244 let text = result.expect("text should be Some/Ok");
245 assert!(text.contains("define"));
246 assert!(text.contains("Nat_succ"));
247 assert!(text.contains("i64"));
248 assert!(text.contains("ret"));
249 }
250}
251#[cfg(test)]
252mod LLVM_infra_tests {
253 use super::*;
254 #[test]
255 pub(super) fn test_pass_config() {
256 let config = LLVMPassConfig::new("test_pass", LLVMPassPhase::Transformation);
257 assert!(config.enabled);
258 assert!(config.phase.is_modifying());
259 assert_eq!(config.phase.name(), "transformation");
260 }
261 #[test]
262 pub(super) fn test_pass_stats() {
263 let mut stats = LLVMPassStats::new();
264 stats.record_run(10, 100, 3);
265 stats.record_run(20, 200, 5);
266 assert_eq!(stats.total_runs, 2);
267 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
268 assert!((stats.success_rate() - 1.0).abs() < 0.01);
269 let s = stats.format_summary();
270 assert!(s.contains("Runs: 2/2"));
271 }
272 #[test]
273 pub(super) fn test_pass_registry() {
274 let mut reg = LLVMPassRegistry::new();
275 reg.register(LLVMPassConfig::new("pass_a", LLVMPassPhase::Analysis));
276 reg.register(LLVMPassConfig::new("pass_b", LLVMPassPhase::Transformation).disabled());
277 assert_eq!(reg.total_passes(), 2);
278 assert_eq!(reg.enabled_count(), 1);
279 reg.update_stats("pass_a", 5, 50, 2);
280 let stats = reg.get_stats("pass_a").expect("stats should exist");
281 assert_eq!(stats.total_changes, 5);
282 }
283 #[test]
284 pub(super) fn test_analysis_cache() {
285 let mut cache = LLVMAnalysisCache::new(10);
286 cache.insert("key1".to_string(), vec![1, 2, 3]);
287 assert!(cache.get("key1").is_some());
288 assert!(cache.get("key2").is_none());
289 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
290 cache.invalidate("key1");
291 assert!(!cache.entries["key1"].valid);
292 assert_eq!(cache.size(), 1);
293 }
294 #[test]
295 pub(super) fn test_worklist() {
296 let mut wl = LLVMWorklist::new();
297 assert!(wl.push(1));
298 assert!(wl.push(2));
299 assert!(!wl.push(1));
300 assert_eq!(wl.len(), 2);
301 assert_eq!(wl.pop(), Some(1));
302 assert!(!wl.contains(1));
303 assert!(wl.contains(2));
304 }
305 #[test]
306 pub(super) fn test_dominator_tree() {
307 let mut dt = LLVMDominatorTree::new(5);
308 dt.set_idom(1, 0);
309 dt.set_idom(2, 0);
310 dt.set_idom(3, 1);
311 assert!(dt.dominates(0, 3));
312 assert!(dt.dominates(1, 3));
313 assert!(!dt.dominates(2, 3));
314 assert!(dt.dominates(3, 3));
315 }
316 #[test]
317 pub(super) fn test_liveness() {
318 let mut liveness = LLVMLivenessInfo::new(3);
319 liveness.add_def(0, 1);
320 liveness.add_use(1, 1);
321 assert!(liveness.defs[0].contains(&1));
322 assert!(liveness.uses[1].contains(&1));
323 }
324 #[test]
325 pub(super) fn test_constant_folding() {
326 assert_eq!(LLVMConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
327 assert_eq!(LLVMConstantFoldingHelper::fold_div_i64(10, 0), None);
328 assert_eq!(LLVMConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
329 assert_eq!(
330 LLVMConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
331 0b1000
332 );
333 assert_eq!(LLVMConstantFoldingHelper::fold_bitnot_i64(0), -1);
334 }
335 #[test]
336 pub(super) fn test_dep_graph() {
337 let mut g = LLVMDepGraph::new();
338 g.add_dep(1, 2);
339 g.add_dep(2, 3);
340 g.add_dep(1, 3);
341 assert_eq!(g.dependencies_of(2), vec![1]);
342 let topo = g.topological_sort();
343 assert_eq!(topo.len(), 3);
344 assert!(!g.has_cycle());
345 let pos: std::collections::HashMap<u32, usize> =
346 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
347 assert!(pos[&1] < pos[&2]);
348 assert!(pos[&1] < pos[&3]);
349 assert!(pos[&2] < pos[&3]);
350 }
351}
352#[cfg(test)]
353mod llvmext_pass_tests {
354 use super::*;
355 #[test]
356 pub(super) fn test_llvmext_phase_order() {
357 assert_eq!(LLVMExtPassPhase::Early.order(), 0);
358 assert_eq!(LLVMExtPassPhase::Middle.order(), 1);
359 assert_eq!(LLVMExtPassPhase::Late.order(), 2);
360 assert_eq!(LLVMExtPassPhase::Finalize.order(), 3);
361 assert!(LLVMExtPassPhase::Early.is_early());
362 assert!(!LLVMExtPassPhase::Early.is_late());
363 }
364 #[test]
365 pub(super) fn test_llvmext_config_builder() {
366 let c = LLVMExtPassConfig::new("p")
367 .with_phase(LLVMExtPassPhase::Late)
368 .with_max_iter(50)
369 .with_debug(1);
370 assert_eq!(c.name, "p");
371 assert_eq!(c.max_iterations, 50);
372 assert!(c.is_debug_enabled());
373 assert!(c.enabled);
374 let c2 = c.disabled();
375 assert!(!c2.enabled);
376 }
377 #[test]
378 pub(super) fn test_llvmext_stats() {
379 let mut s = LLVMExtPassStats::new();
380 s.visit();
381 s.visit();
382 s.modify();
383 s.iterate();
384 assert_eq!(s.nodes_visited, 2);
385 assert_eq!(s.nodes_modified, 1);
386 assert!(s.changed);
387 assert_eq!(s.iterations, 1);
388 let e = s.efficiency();
389 assert!((e - 0.5).abs() < 1e-9);
390 }
391 #[test]
392 pub(super) fn test_llvmext_registry() {
393 let mut r = LLVMExtPassRegistry::new();
394 r.register(LLVMExtPassConfig::new("a").with_phase(LLVMExtPassPhase::Early));
395 r.register(LLVMExtPassConfig::new("b").disabled());
396 assert_eq!(r.len(), 2);
397 assert_eq!(r.enabled_passes().len(), 1);
398 assert_eq!(r.passes_in_phase(&LLVMExtPassPhase::Early).len(), 1);
399 }
400 #[test]
401 pub(super) fn test_llvmext_cache() {
402 let mut c = LLVMExtCache::new(4);
403 assert!(c.get(99).is_none());
404 c.put(99, vec![1, 2, 3]);
405 let v = c.get(99).expect("v should be present in map");
406 assert_eq!(v, &[1u8, 2, 3]);
407 assert!(c.hit_rate() > 0.0);
408 assert_eq!(c.live_count(), 1);
409 }
410 #[test]
411 pub(super) fn test_llvmext_worklist() {
412 let mut w = LLVMExtWorklist::new(10);
413 w.push(5);
414 w.push(3);
415 w.push(5);
416 assert_eq!(w.len(), 2);
417 assert!(w.contains(5));
418 let first = w.pop().expect("first should be available to pop");
419 assert!(!w.contains(first));
420 }
421 #[test]
422 pub(super) fn test_llvmext_dom_tree() {
423 let mut dt = LLVMExtDomTree::new(5);
424 dt.set_idom(1, 0);
425 dt.set_idom(2, 0);
426 dt.set_idom(3, 1);
427 dt.set_idom(4, 1);
428 assert!(dt.dominates(0, 3));
429 assert!(dt.dominates(1, 4));
430 assert!(!dt.dominates(2, 3));
431 assert_eq!(dt.depth_of(3), 2);
432 }
433 #[test]
434 pub(super) fn test_llvmext_liveness() {
435 let mut lv = LLVMExtLiveness::new(3);
436 lv.add_def(0, 1);
437 lv.add_use(1, 1);
438 assert!(lv.var_is_def_in_block(0, 1));
439 assert!(lv.var_is_used_in_block(1, 1));
440 assert!(!lv.var_is_def_in_block(1, 1));
441 }
442 #[test]
443 pub(super) fn test_llvmext_const_folder() {
444 let mut cf = LLVMExtConstFolder::new();
445 assert_eq!(cf.add_i64(3, 4), Some(7));
446 assert_eq!(cf.div_i64(10, 0), None);
447 assert_eq!(cf.mul_i64(6, 7), Some(42));
448 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
449 assert_eq!(cf.fold_count(), 3);
450 assert_eq!(cf.failure_count(), 1);
451 }
452 #[test]
453 pub(super) fn test_llvmext_dep_graph() {
454 let mut g = LLVMExtDepGraph::new(4);
455 g.add_edge(0, 1);
456 g.add_edge(1, 2);
457 g.add_edge(2, 3);
458 assert!(!g.has_cycle());
459 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
460 assert_eq!(g.reachable(0).len(), 4);
461 let sccs = g.scc();
462 assert_eq!(sccs.len(), 4);
463 }
464}