Skip to main content

oxilean_codegen/llvm_backend/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use 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}