1use crate::lcnf::*;
6use std::collections::{HashMap, HashSet};
7
8use super::types::{
9 BasicBlock, BlockId, CondCode, NatAnalysisCache, NatConstantFoldingHelper, NatDepGraph,
10 NatDominatorTree, NatLivenessInfo, NatPassConfig, NatPassPhase, NatPassRegistry, NatPassStats,
11 NatWorklist, NativeBackend, NativeEmitConfig, NativeEmitStats, NativeInst, NativeModule,
12 NativeType, NativeValue, Register, RegisterAllocator,
13};
14
15pub(super) fn lcnf_type_to_native(ty: &LcnfType) -> NativeType {
17 match ty {
18 LcnfType::Nat => NativeType::I64,
19 LcnfType::Int => NativeType::I64,
20 LcnfType::LcnfString => NativeType::Ptr,
21 LcnfType::Object => NativeType::Ptr,
22 LcnfType::Var(_) => NativeType::Ptr,
23 LcnfType::Fun(_, _) => NativeType::Ptr,
24 LcnfType::Ctor(_, _) => NativeType::Ptr,
25 LcnfType::Erased | LcnfType::Irrelevant | LcnfType::Unit => NativeType::Void,
26 }
27}
28pub fn compile_to_native(module: &LcnfModule) -> NativeModule {
30 let mut backend = NativeBackend::default_backend();
31 backend.compile_module(module)
32}
33pub fn compile_and_regalloc(
35 module: &LcnfModule,
36 num_regs: usize,
37) -> (NativeModule, Vec<HashMap<Register, Register>>) {
38 let mut backend = NativeBackend::default_backend();
39 let native_module = backend.compile_module(module);
40 let mut allocations = Vec::new();
41 for func in &native_module.functions {
42 let mut allocator = RegisterAllocator::new(num_regs);
43 let alloc = allocator.allocate(func);
44 allocations.push(alloc);
45 }
46 (native_module, allocations)
47}
48#[cfg(test)]
49mod tests {
50 use super::*;
51 pub(super) fn vid(n: u64) -> LcnfVarId {
52 LcnfVarId(n)
53 }
54 pub(super) fn mk_param(n: u64, name: &str) -> LcnfParam {
55 LcnfParam {
56 id: vid(n),
57 name: name.to_string(),
58 ty: LcnfType::Nat,
59 erased: false,
60 borrowed: false,
61 }
62 }
63 pub(super) fn mk_fun_decl(name: &str, body: LcnfExpr) -> LcnfFunDecl {
64 LcnfFunDecl {
65 name: name.to_string(),
66 original_name: None,
67 params: vec![mk_param(0, "n")],
68 ret_type: LcnfType::Nat,
69 body,
70 is_recursive: false,
71 is_lifted: false,
72 inline_cost: 1,
73 }
74 }
75 pub(super) fn mk_module(decls: Vec<LcnfFunDecl>) -> LcnfModule {
76 LcnfModule {
77 fun_decls: decls,
78 extern_decls: vec![],
79 name: "test_mod".to_string(),
80 metadata: LcnfModuleMetadata::default(),
81 }
82 }
83 #[test]
84 pub(super) fn test_native_type_size() {
85 assert_eq!(NativeType::I8.size_bytes(), 1);
86 assert_eq!(NativeType::I16.size_bytes(), 2);
87 assert_eq!(NativeType::I32.size_bytes(), 4);
88 assert_eq!(NativeType::I64.size_bytes(), 8);
89 assert_eq!(NativeType::Ptr.size_bytes(), 8);
90 assert_eq!(NativeType::Void.size_bytes(), 0);
91 }
92 #[test]
93 pub(super) fn test_native_type_display() {
94 assert_eq!(NativeType::I64.to_string(), "i64");
95 assert_eq!(NativeType::Ptr.to_string(), "ptr");
96 assert_eq!(NativeType::Void.to_string(), "void");
97 }
98 #[test]
99 pub(super) fn test_native_type_properties() {
100 assert!(NativeType::I32.is_integer());
101 assert!(!NativeType::I32.is_float());
102 assert!(NativeType::F64.is_float());
103 assert!(NativeType::Ptr.is_pointer());
104 }
105 #[test]
106 pub(super) fn test_register_virtual_physical() {
107 let vr = Register::virt(5);
108 assert!(vr.is_virtual());
109 assert!(!vr.is_physical());
110 let pr = Register::phys(3);
111 assert!(!pr.is_virtual());
112 assert!(pr.is_physical());
113 }
114 #[test]
115 pub(super) fn test_register_display() {
116 assert_eq!(Register::virt(5).to_string(), "v5");
117 assert_eq!(Register::phys(3).to_string(), "r3");
118 }
119 #[test]
120 pub(super) fn test_native_value_display() {
121 assert_eq!(NativeValue::Reg(Register::virt(0)).to_string(), "v0");
122 assert_eq!(NativeValue::Imm(42).to_string(), "#42");
123 assert_eq!(NativeValue::FRef("foo".to_string()).to_string(), "@foo");
124 assert_eq!(NativeValue::StackSlot(3).to_string(), "ss3");
125 }
126 #[test]
127 pub(super) fn test_block_id_display() {
128 assert_eq!(BlockId(0).to_string(), "bb0");
129 assert_eq!(BlockId(5).to_string(), "bb5");
130 }
131 #[test]
132 pub(super) fn test_basic_block_successors() {
133 let mut block = BasicBlock::new(BlockId(0));
134 block.push_inst(NativeInst::Br { target: BlockId(1) });
135 assert_eq!(block.successors(), vec![BlockId(1)]);
136 let mut block2 = BasicBlock::new(BlockId(1));
137 block2.push_inst(NativeInst::CondBr {
138 cond: NativeValue::Reg(Register::virt(0)),
139 then_target: BlockId(2),
140 else_target: BlockId(3),
141 });
142 assert_eq!(block2.successors(), vec![BlockId(2), BlockId(3)]);
143 }
144 #[test]
145 pub(super) fn test_inst_is_terminator() {
146 assert!(NativeInst::Br { target: BlockId(0) }.is_terminator());
147 assert!(NativeInst::Ret { value: None }.is_terminator());
148 assert!(!NativeInst::Nop.is_terminator());
149 assert!(!NativeInst::LoadImm {
150 dst: Register::virt(0),
151 ty: NativeType::I64,
152 value: 0
153 }
154 .is_terminator());
155 }
156 #[test]
157 pub(super) fn test_inst_dst_reg() {
158 let inst = NativeInst::Add {
159 dst: Register::virt(5),
160 ty: NativeType::I64,
161 lhs: NativeValue::Reg(Register::virt(0)),
162 rhs: NativeValue::Imm(1),
163 };
164 assert_eq!(inst.dst_reg(), Some(Register::virt(5)));
165 let inst2 = NativeInst::Ret { value: None };
166 assert_eq!(inst2.dst_reg(), None);
167 }
168 #[test]
169 pub(super) fn test_inst_src_regs() {
170 let inst = NativeInst::Add {
171 dst: Register::virt(5),
172 ty: NativeType::I64,
173 lhs: NativeValue::Reg(Register::virt(1)),
174 rhs: NativeValue::Reg(Register::virt(2)),
175 };
176 let srcs = inst.src_regs();
177 assert_eq!(srcs.len(), 2);
178 assert!(srcs.contains(&Register::virt(1)));
179 assert!(srcs.contains(&Register::virt(2)));
180 }
181 #[test]
182 pub(super) fn test_compile_simple_function() {
183 let body = LcnfExpr::Return(LcnfArg::Var(vid(0)));
184 let decl = mk_fun_decl("identity", body);
185 let module = mk_module(vec![decl]);
186 let mut backend = NativeBackend::default_backend();
187 let native_module = backend.compile_module(&module);
188 assert_eq!(native_module.functions.len(), 1);
189 let func = &native_module.functions[0];
190 assert_eq!(func.name, "identity");
191 assert!(!func.blocks.is_empty());
192 }
193 #[test]
194 pub(super) fn test_compile_case_expression() {
195 let body = LcnfExpr::Case {
196 scrutinee: vid(0),
197 scrutinee_ty: LcnfType::Ctor("Bool".into(), vec![]),
198 alts: vec![
199 LcnfAlt {
200 ctor_name: "False".into(),
201 ctor_tag: 0,
202 params: vec![],
203 body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(0))),
204 },
205 LcnfAlt {
206 ctor_name: "True".into(),
207 ctor_tag: 1,
208 params: vec![],
209 body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(1))),
210 },
211 ],
212 default: Some(Box::new(LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(99))))),
213 };
214 let decl = mk_fun_decl("to_nat", body);
215 let module = mk_module(vec![decl]);
216 let mut backend = NativeBackend::default_backend();
217 let native_module = backend.compile_module(&module);
218 let func = &native_module.functions[0];
219 assert!(func.blocks.len() > 1);
220 }
221 #[test]
222 pub(super) fn test_register_allocation() {
223 let body = LcnfExpr::Let {
224 id: vid(1),
225 name: "a".to_string(),
226 ty: LcnfType::Nat,
227 value: LcnfLetValue::Lit(LcnfLit::Nat(42)),
228 body: Box::new(LcnfExpr::Let {
229 id: vid(2),
230 name: "b".to_string(),
231 ty: LcnfType::Nat,
232 value: LcnfLetValue::Lit(LcnfLit::Nat(10)),
233 body: Box::new(LcnfExpr::Return(LcnfArg::Var(vid(1)))),
234 }),
235 };
236 let decl = mk_fun_decl("test_alloc", body);
237 let module = mk_module(vec![decl]);
238 let (native_module, allocations) = compile_and_regalloc(&module, 8);
239 assert_eq!(native_module.functions.len(), 1);
240 assert_eq!(allocations.len(), 1);
241 let alloc = &allocations[0];
242 for (vreg, phys) in alloc {
243 assert!(vreg.is_virtual());
244 assert!(phys.is_physical());
245 }
246 }
247 #[test]
248 pub(super) fn test_native_module_display() {
249 let module = NativeModule::new("test");
250 let s = module.to_string();
251 assert!(s.contains("module: test"));
252 }
253 #[test]
254 pub(super) fn test_native_emit_config_default() {
255 let cfg = NativeEmitConfig::default();
256 assert_eq!(cfg.opt_level, 1);
257 assert!(!cfg.debug_info);
258 assert_eq!(cfg.target_arch, "x86_64");
259 }
260 #[test]
261 pub(super) fn test_native_emit_stats_display() {
262 let stats = NativeEmitStats {
263 functions_compiled: 3,
264 blocks_generated: 10,
265 ..Default::default()
266 };
267 let s = stats.to_string();
268 assert!(s.contains("fns=3"));
269 assert!(s.contains("blocks=10"));
270 }
271 #[test]
272 pub(super) fn test_lcnf_type_to_native() {
273 assert_eq!(lcnf_type_to_native(&LcnfType::Nat), NativeType::I64);
274 assert_eq!(lcnf_type_to_native(&LcnfType::Object), NativeType::Ptr);
275 assert_eq!(lcnf_type_to_native(&LcnfType::Unit), NativeType::Void);
276 }
277 #[test]
278 pub(super) fn test_compile_let_chain() {
279 let body = LcnfExpr::Let {
280 id: vid(1),
281 name: "a".to_string(),
282 ty: LcnfType::Nat,
283 value: LcnfLetValue::Lit(LcnfLit::Nat(42)),
284 body: Box::new(LcnfExpr::Let {
285 id: vid(2),
286 name: "b".to_string(),
287 ty: LcnfType::Nat,
288 value: LcnfLetValue::App(LcnfArg::Var(vid(99)), vec![LcnfArg::Var(vid(1))]),
289 body: Box::new(LcnfExpr::Return(LcnfArg::Var(vid(2)))),
290 }),
291 };
292 let decl = mk_fun_decl("chain", body);
293 let mut backend = NativeBackend::default_backend();
294 let func = backend.compile_fun_decl(&decl);
295 assert!(!func.blocks.is_empty());
296 assert!(func.instruction_count() > 0);
297 }
298 #[test]
299 pub(super) fn test_virtual_registers() {
300 let body = LcnfExpr::Let {
301 id: vid(1),
302 name: "a".to_string(),
303 ty: LcnfType::Nat,
304 value: LcnfLetValue::Lit(LcnfLit::Nat(1)),
305 body: Box::new(LcnfExpr::Return(LcnfArg::Var(vid(1)))),
306 };
307 let decl = mk_fun_decl("test", body);
308 let mut backend = NativeBackend::default_backend();
309 let func = backend.compile_fun_decl(&decl);
310 let vregs = func.virtual_registers();
311 assert!(!vregs.is_empty());
312 }
313 #[test]
314 pub(super) fn test_cond_code_display() {
315 assert_eq!(CondCode::Eq.to_string(), "eq");
316 assert_eq!(CondCode::Lt.to_string(), "lt");
317 assert_eq!(CondCode::Uge.to_string(), "uge");
318 }
319 #[test]
320 pub(super) fn test_native_func_display() {
321 let body = LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(0)));
322 let decl = mk_fun_decl("display_test", body);
323 let mut backend = NativeBackend::default_backend();
324 let func = backend.compile_fun_decl(&decl);
325 let s = func.to_string();
326 assert!(s.contains("func @display_test"));
327 }
328}
329#[cfg(test)]
330mod Nat_infra_tests {
331 use super::*;
332 #[test]
333 pub(super) fn test_pass_config() {
334 let config = NatPassConfig::new("test_pass", NatPassPhase::Transformation);
335 assert!(config.enabled);
336 assert!(config.phase.is_modifying());
337 assert_eq!(config.phase.name(), "transformation");
338 }
339 #[test]
340 pub(super) fn test_pass_stats() {
341 let mut stats = NatPassStats::new();
342 stats.record_run(10, 100, 3);
343 stats.record_run(20, 200, 5);
344 assert_eq!(stats.total_runs, 2);
345 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
346 assert!((stats.success_rate() - 1.0).abs() < 0.01);
347 let s = stats.format_summary();
348 assert!(s.contains("Runs: 2/2"));
349 }
350 #[test]
351 pub(super) fn test_pass_registry() {
352 let mut reg = NatPassRegistry::new();
353 reg.register(NatPassConfig::new("pass_a", NatPassPhase::Analysis));
354 reg.register(NatPassConfig::new("pass_b", NatPassPhase::Transformation).disabled());
355 assert_eq!(reg.total_passes(), 2);
356 assert_eq!(reg.enabled_count(), 1);
357 reg.update_stats("pass_a", 5, 50, 2);
358 let stats = reg.get_stats("pass_a").expect("stats should exist");
359 assert_eq!(stats.total_changes, 5);
360 }
361 #[test]
362 pub(super) fn test_analysis_cache() {
363 let mut cache = NatAnalysisCache::new(10);
364 cache.insert("key1".to_string(), vec![1, 2, 3]);
365 assert!(cache.get("key1").is_some());
366 assert!(cache.get("key2").is_none());
367 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
368 cache.invalidate("key1");
369 assert!(!cache.entries["key1"].valid);
370 assert_eq!(cache.size(), 1);
371 }
372 #[test]
373 pub(super) fn test_worklist() {
374 let mut wl = NatWorklist::new();
375 assert!(wl.push(1));
376 assert!(wl.push(2));
377 assert!(!wl.push(1));
378 assert_eq!(wl.len(), 2);
379 assert_eq!(wl.pop(), Some(1));
380 assert!(!wl.contains(1));
381 assert!(wl.contains(2));
382 }
383 #[test]
384 pub(super) fn test_dominator_tree() {
385 let mut dt = NatDominatorTree::new(5);
386 dt.set_idom(1, 0);
387 dt.set_idom(2, 0);
388 dt.set_idom(3, 1);
389 assert!(dt.dominates(0, 3));
390 assert!(dt.dominates(1, 3));
391 assert!(!dt.dominates(2, 3));
392 assert!(dt.dominates(3, 3));
393 }
394 #[test]
395 pub(super) fn test_liveness() {
396 let mut liveness = NatLivenessInfo::new(3);
397 liveness.add_def(0, 1);
398 liveness.add_use(1, 1);
399 assert!(liveness.defs[0].contains(&1));
400 assert!(liveness.uses[1].contains(&1));
401 }
402 #[test]
403 pub(super) fn test_constant_folding() {
404 assert_eq!(NatConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
405 assert_eq!(NatConstantFoldingHelper::fold_div_i64(10, 0), None);
406 assert_eq!(NatConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
407 assert_eq!(
408 NatConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
409 0b1000
410 );
411 assert_eq!(NatConstantFoldingHelper::fold_bitnot_i64(0), -1);
412 }
413 #[test]
414 pub(super) fn test_dep_graph() {
415 let mut g = NatDepGraph::new();
416 g.add_dep(1, 2);
417 g.add_dep(2, 3);
418 g.add_dep(1, 3);
419 assert_eq!(g.dependencies_of(2), vec![1]);
420 let topo = g.topological_sort();
421 assert_eq!(topo.len(), 3);
422 assert!(!g.has_cycle());
423 let pos: std::collections::HashMap<u32, usize> =
424 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
425 assert!(pos[&1] < pos[&2]);
426 assert!(pos[&1] < pos[&3]);
427 assert!(pos[&2] < pos[&3]);
428 }
429}