Skip to main content

oxilean_codegen/runtime_codegen/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::lcnf::*;
6use crate::native_backend::*;
7
8use super::types::{
9    AllocStrategy, AllocatorCodegen, ArrayCodegen, BigNatCodegen, ClosureCodegen, ClosureLayout,
10    ClosureRepr, ExternalObjectCodegen, LayoutCache, LayoutComputer, ObjectLayout, ObjectTag,
11    RcCodegen, RcStrategy, RcUseAnalysis, RuntimeConfig, RuntimeModuleBuilder, StringCodegen,
12    StringLayout, ThunkCodegen, TypeInfo,
13};
14
15/// Align a value up to the given alignment.
16pub(super) fn align_up(value: usize, align: usize) -> usize {
17    (value + align - 1) & !(align - 1)
18}
19/// Whether an LCNF type requires boxing (heap allocation).
20pub(super) fn is_boxed_type(ty: &LcnfType) -> bool {
21    matches!(
22        ty,
23        LcnfType::Object
24            | LcnfType::Var(_)
25            | LcnfType::Fun(_, _)
26            | LcnfType::Ctor(_, _)
27            | LcnfType::LcnfString
28    )
29}
30/// Size of a scalar (unboxed) LCNF type in bytes.
31pub(super) fn scalar_type_size(ty: &LcnfType) -> usize {
32    match ty {
33        LcnfType::Nat => 8,
34        LcnfType::Unit | LcnfType::Erased | LcnfType::Irrelevant => 0,
35        _ => 8,
36    }
37}
38#[cfg(test)]
39mod tests {
40    use super::*;
41    #[test]
42    pub(super) fn test_object_tag_roundtrip() {
43        for tag in [
44            ObjectTag::Scalar,
45            ObjectTag::Closure,
46            ObjectTag::Array,
47            ObjectTag::Struct,
48            ObjectTag::External,
49            ObjectTag::String,
50            ObjectTag::BigNat,
51            ObjectTag::Thunk,
52        ] {
53            let n = tag.to_u8();
54            let back = ObjectTag::from_u8(n).expect("back should be valid");
55            assert_eq!(tag, back);
56        }
57        assert!(ObjectTag::from_u8(255).is_none());
58    }
59    #[test]
60    pub(super) fn test_object_tag_display() {
61        assert_eq!(ObjectTag::Scalar.to_string(), "scalar");
62        assert_eq!(ObjectTag::Closure.to_string(), "closure");
63        assert_eq!(ObjectTag::Struct.to_string(), "struct");
64    }
65    #[test]
66    pub(super) fn test_ctor_layout() {
67        let layout = ObjectLayout::for_ctor(0, 2, 0);
68        assert_eq!(layout.num_obj_fields, 2);
69        assert_eq!(layout.scalar_size, 0);
70        assert!(layout.total_size >= ObjectLayout::HEADER_SIZE + 16);
71        assert_eq!(layout.alignment, 8);
72        assert_eq!(layout.obj_field_offset(0), ObjectLayout::HEADER_SIZE);
73        assert_eq!(layout.obj_field_offset(1), ObjectLayout::HEADER_SIZE + 8);
74    }
75    #[test]
76    pub(super) fn test_closure_layout() {
77        let layout = ObjectLayout::for_closure(2, 3);
78        assert_eq!(layout.tag, ObjectTag::Closure);
79        assert!(layout.total_size > ObjectLayout::HEADER_SIZE);
80        assert_eq!(layout.num_obj_fields, 3);
81    }
82    #[test]
83    pub(super) fn test_array_layout() {
84        let layout = ObjectLayout::for_array(10);
85        assert_eq!(layout.tag, ObjectTag::Array);
86        assert_eq!(layout.num_obj_fields, 10);
87    }
88    #[test]
89    pub(super) fn test_external_layout() {
90        let layout = ObjectLayout::for_external();
91        assert_eq!(layout.tag, ObjectTag::External);
92        assert_eq!(layout.num_obj_fields, 0);
93    }
94    #[test]
95    pub(super) fn test_layout_display() {
96        let layout = ObjectLayout::for_ctor(0, 2, 8);
97        let s = layout.to_string();
98        assert!(s.contains("obj_fields=2"));
99        assert!(s.contains("scalar=8"));
100    }
101    #[test]
102    pub(super) fn test_align_up() {
103        assert_eq!(align_up(0, 8), 0);
104        assert_eq!(align_up(1, 8), 8);
105        assert_eq!(align_up(8, 8), 8);
106        assert_eq!(align_up(9, 8), 16);
107        assert_eq!(align_up(16, 8), 16);
108    }
109    #[test]
110    pub(super) fn test_layout_computer_ctor() {
111        let mut computer = LayoutComputer::new();
112        let layout = computer.compute_ctor_layout("Pair", 0, &[LcnfType::Nat, LcnfType::Object]);
113        assert_eq!(layout.num_obj_fields, 1);
114        assert!(layout.scalar_size > 0);
115    }
116    #[test]
117    pub(super) fn test_layout_computer_cache() {
118        let mut computer = LayoutComputer::new();
119        let layout1 = computer.compute_ctor_layout("Nil", 0, &[]);
120        let layout2 = computer.compute_ctor_layout("Nil", 0, &[]);
121        assert_eq!(layout1, layout2);
122    }
123    #[test]
124    pub(super) fn test_layout_computer_register_type() {
125        let mut computer = LayoutComputer::new();
126        computer.register_type(TypeInfo {
127            name: "Bool".to_string(),
128            constructors: vec![
129                ("False".to_string(), 0, vec![]),
130                ("True".to_string(), 1, vec![]),
131            ],
132            is_recursive: false,
133        });
134        let layouts = computer.compute_layout("Bool");
135        assert_eq!(layouts.len(), 2);
136    }
137    #[test]
138    pub(super) fn test_rc_codegen_inc() {
139        let mut rc = RcCodegen::new(true);
140        let insts = rc.emit_rc_inc(Register::virt(5));
141        assert!(insts.len() >= 2);
142        let has_call = insts.iter().any(|i| matches!(i, NativeInst::Call { .. }));
143        assert!(has_call);
144    }
145    #[test]
146    pub(super) fn test_rc_codegen_dec() {
147        let mut rc = RcCodegen::new(true);
148        let insts = rc.emit_rc_dec(Register::virt(5));
149        assert!(insts.len() >= 2);
150    }
151    #[test]
152    pub(super) fn test_rc_codegen_is_unique() {
153        let mut rc = RcCodegen::new(true);
154        let inst = rc.emit_rc_is_unique(Register::virt(5));
155        assert!(matches!(inst, NativeInst::Call { .. }));
156    }
157    #[test]
158    pub(super) fn test_rc_codegen_disabled() {
159        let mut rc = RcCodegen::new(false);
160        let insts = rc.emit_rc_inc(Register::virt(0));
161        assert!(insts.len() == 1);
162        assert!(matches!(insts[0], NativeInst::Comment(_)));
163    }
164    #[test]
165    pub(super) fn test_rc_codegen_inc_n() {
166        let mut rc = RcCodegen::new(true);
167        let insts = rc.emit_rc_inc_n(Register::virt(5), 3);
168        assert!(!insts.is_empty());
169    }
170    #[test]
171    pub(super) fn test_allocator_codegen_system() {
172        let mut alloc = AllocatorCodegen::new(AllocStrategy::System);
173        let insts = alloc.emit_alloc(64, 8);
174        assert!(insts.len() >= 2);
175    }
176    #[test]
177    pub(super) fn test_allocator_codegen_free() {
178        let mut alloc = AllocatorCodegen::new(AllocStrategy::System);
179        let insts = alloc.emit_free(Register::virt(5));
180        assert!(insts.len() >= 2);
181    }
182    #[test]
183    pub(super) fn test_allocator_codegen_bump_no_free() {
184        let mut alloc = AllocatorCodegen::new(AllocStrategy::Bump);
185        let insts = alloc.emit_free(Register::virt(5));
186        assert!(insts.len() == 1);
187        assert!(matches!(insts[0], NativeInst::Comment(_)));
188    }
189    #[test]
190    pub(super) fn test_allocator_alloc_ctor() {
191        let mut alloc = AllocatorCodegen::new(AllocStrategy::LeanRuntime);
192        let insts = alloc.emit_alloc_ctor(0, 2, 0);
193        assert!(!insts.is_empty());
194    }
195    #[test]
196    pub(super) fn test_allocator_alloc_closure() {
197        let mut alloc = AllocatorCodegen::new(AllocStrategy::LeanRuntime);
198        let insts = alloc.emit_alloc_closure("my_func", 2, 1);
199        assert!(!insts.is_empty());
200    }
201    #[test]
202    pub(super) fn test_closure_layout_new() {
203        let layout = ClosureLayout::new(2, 3);
204        assert_eq!(layout.arity, 2);
205        assert_eq!(layout.num_captured, 3);
206        assert!(layout.env_offset > ObjectLayout::HEADER_SIZE);
207        assert_eq!(layout.env_size, 24);
208    }
209    #[test]
210    pub(super) fn test_closure_layout_offsets() {
211        let layout = ClosureLayout::new(1, 2);
212        let off0 = layout.captured_var_offset(0);
213        let off1 = layout.captured_var_offset(1);
214        assert_eq!(off1 - off0, 8);
215    }
216    #[test]
217    pub(super) fn test_closure_layout_display() {
218        let layout = ClosureLayout::new(2, 3);
219        let s = layout.to_string();
220        assert!(s.contains("arity=2"));
221        assert!(s.contains("captured=3"));
222    }
223    #[test]
224    pub(super) fn test_closure_codegen_create() {
225        let mut codegen = ClosureCodegen::new();
226        let insts = codegen.emit_closure_create("add", 2, &[Register::virt(0), Register::virt(1)]);
227        assert!(!insts.is_empty());
228        let call_count = insts
229            .iter()
230            .filter(|i| matches!(i, NativeInst::Call { .. }))
231            .count();
232        assert!(call_count >= 3);
233    }
234    #[test]
235    pub(super) fn test_closure_codegen_apply() {
236        let mut codegen = ClosureCodegen::new();
237        let insts = codegen.emit_closure_apply(Register::virt(0), &[Register::virt(1)]);
238        assert!(!insts.is_empty());
239    }
240    #[test]
241    pub(super) fn test_closure_codegen_partial_apply() {
242        let mut codegen = ClosureCodegen::new();
243        let insts = codegen.emit_partial_apply(Register::virt(0), &[Register::virt(1)]);
244        assert!(!insts.is_empty());
245    }
246    #[test]
247    pub(super) fn test_closure_codegen_partial_apply_empty() {
248        let mut codegen = ClosureCodegen::new();
249        let insts = codegen.emit_partial_apply(Register::virt(0), &[]);
250        assert!(insts.len() == 1);
251        assert!(matches!(insts[0], NativeInst::Comment(_)));
252    }
253    #[test]
254    pub(super) fn test_runtime_config_default() {
255        let cfg = RuntimeConfig::default();
256        assert_eq!(cfg.rc_strategy, RcStrategy::Standard);
257        assert_eq!(cfg.alloc_strategy, AllocStrategy::LeanRuntime);
258        assert_eq!(cfg.closure_repr, ClosureRepr::Standard);
259        assert!(!cfg.debug_checks);
260    }
261    #[test]
262    pub(super) fn test_runtime_config_display() {
263        let cfg = RuntimeConfig::default();
264        let s = cfg.to_string();
265        assert!(s.contains("Standard"));
266        assert!(s.contains("LeanRuntime"));
267    }
268    #[test]
269    pub(super) fn test_is_boxed_type() {
270        assert!(is_boxed_type(&LcnfType::Object));
271        assert!(is_boxed_type(&LcnfType::LcnfString));
272        assert!(is_boxed_type(&LcnfType::Ctor("List".into(), vec![])));
273        assert!(!is_boxed_type(&LcnfType::Nat));
274        assert!(!is_boxed_type(&LcnfType::Unit));
275    }
276    #[test]
277    pub(super) fn test_scalar_type_size() {
278        assert_eq!(scalar_type_size(&LcnfType::Nat), 8);
279        assert_eq!(scalar_type_size(&LcnfType::Unit), 0);
280        assert_eq!(scalar_type_size(&LcnfType::Erased), 0);
281    }
282    #[test]
283    pub(super) fn test_conditional_reset() {
284        let mut rc = RcCodegen::new(true);
285        let insts = rc.emit_conditional_reset(Register::virt(0), 2, 0);
286        assert!(insts.len() >= 3);
287    }
288}
289#[cfg(test)]
290mod runtime_extended_tests {
291    use super::*;
292    pub(super) fn vid(n: u64) -> LcnfVarId {
293        LcnfVarId(n)
294    }
295    pub(super) fn mk_fun_decl(name: &str, body: LcnfExpr) -> LcnfFunDecl {
296        LcnfFunDecl {
297            name: name.to_string(),
298            original_name: None,
299            params: vec![],
300            ret_type: LcnfType::Nat,
301            body,
302            is_recursive: false,
303            is_lifted: false,
304            inline_cost: 1,
305        }
306    }
307    pub(super) fn mk_module(decls: Vec<LcnfFunDecl>) -> LcnfModule {
308        LcnfModule {
309            fun_decls: decls,
310            extern_decls: vec![],
311            name: "test".to_string(),
312            metadata: LcnfModuleMetadata::default(),
313        }
314    }
315    #[test]
316    pub(super) fn test_string_layout_standard() {
317        let layout = StringLayout::standard();
318        assert!(!layout.is_sso);
319        assert_eq!(layout.len_offset, ObjectLayout::HEADER_SIZE);
320        assert!(layout.data_offset > layout.len_offset);
321    }
322    #[test]
323    pub(super) fn test_string_layout_sso() {
324        let layout = StringLayout::sso(15);
325        assert!(layout.is_sso);
326        assert_eq!(layout.sso_max_len, 15);
327        assert!(layout.sso_total_size > 0);
328    }
329    #[test]
330    pub(super) fn test_string_layout_alloc_size_standard() {
331        let layout = StringLayout::standard();
332        let size = layout.alloc_size(100);
333        assert!(size >= layout.data_offset + 100);
334    }
335    #[test]
336    pub(super) fn test_string_layout_alloc_size_sso_short() {
337        let layout = StringLayout::sso(15);
338        let size = layout.alloc_size(10);
339        assert_eq!(size, layout.sso_total_size);
340    }
341    #[test]
342    pub(super) fn test_string_layout_alloc_size_sso_long() {
343        let layout = StringLayout::sso(15);
344        let size_long = layout.alloc_size(100);
345        assert!(size_long > layout.sso_total_size);
346    }
347    #[test]
348    pub(super) fn test_string_layout_display() {
349        let layout = StringLayout::sso(8);
350        let s = layout.to_string();
351        assert!(s.contains("sso=true"));
352        assert!(s.contains("max_len=8"));
353    }
354    #[test]
355    pub(super) fn test_array_codegen_alloc() {
356        let mut codegen = ArrayCodegen::default();
357        let insts = codegen.emit_alloc_array(16);
358        assert!(!insts.is_empty());
359        assert!(insts.iter().any(|i| matches!(i, NativeInst::Call { .. })));
360    }
361    #[test]
362    pub(super) fn test_array_codegen_get() {
363        let mut codegen = ArrayCodegen::default();
364        let insts = codegen.emit_array_get(Register::virt(0), Register::virt(1));
365        assert!(!insts.is_empty());
366    }
367    #[test]
368    pub(super) fn test_array_codegen_set() {
369        let mut codegen = ArrayCodegen::default();
370        let insts = codegen.emit_array_set(Register::virt(0), Register::virt(1), Register::virt(2));
371        assert!(!insts.is_empty());
372    }
373    #[test]
374    pub(super) fn test_array_codegen_push() {
375        let mut codegen = ArrayCodegen::default();
376        let insts = codegen.emit_array_push(Register::virt(0), Register::virt(1));
377        assert!(!insts.is_empty());
378    }
379    #[test]
380    pub(super) fn test_array_codegen_size() {
381        let mut codegen = ArrayCodegen::default();
382        let insts = codegen.emit_array_size(Register::virt(0));
383        assert!(!insts.is_empty());
384    }
385    #[test]
386    pub(super) fn test_string_codegen_lit() {
387        let mut codegen = StringCodegen::default();
388        let insts = codegen.emit_string_lit("hello");
389        assert!(!insts.is_empty());
390    }
391    #[test]
392    pub(super) fn test_string_codegen_append() {
393        let mut codegen = StringCodegen::default();
394        let insts = codegen.emit_string_append(Register::virt(0), Register::virt(1));
395        assert!(!insts.is_empty());
396    }
397    #[test]
398    pub(super) fn test_string_codegen_length() {
399        let mut codegen = StringCodegen::default();
400        let insts = codegen.emit_string_length(Register::virt(0));
401        assert!(!insts.is_empty());
402        assert!(insts.iter().any(|i| matches!(i, NativeInst::Load { .. })));
403    }
404    #[test]
405    pub(super) fn test_string_codegen_eq() {
406        let mut codegen = StringCodegen::default();
407        let insts = codegen.emit_string_eq(Register::virt(0), Register::virt(1));
408        assert!(!insts.is_empty());
409    }
410    #[test]
411    pub(super) fn test_thunk_codegen_alloc() {
412        let mut codegen = ThunkCodegen::new();
413        let insts = codegen.emit_alloc_thunk("my_lazy_fn");
414        assert!(!insts.is_empty());
415    }
416    #[test]
417    pub(super) fn test_thunk_codegen_force() {
418        let mut codegen = ThunkCodegen::new();
419        let insts = codegen.emit_force_thunk(Register::virt(5));
420        assert!(!insts.is_empty());
421    }
422    #[test]
423    pub(super) fn test_thunk_codegen_is_evaluated() {
424        let mut codegen = ThunkCodegen::new();
425        let insts = codegen.emit_is_evaluated(Register::virt(3));
426        assert!(!insts.is_empty());
427    }
428    #[test]
429    pub(super) fn test_bignat_codegen_add() {
430        let mut codegen = BigNatCodegen::new();
431        let insts = codegen.emit_add(Register::virt(0), Register::virt(1));
432        assert!(!insts.is_empty());
433    }
434    #[test]
435    pub(super) fn test_bignat_codegen_mul() {
436        let mut codegen = BigNatCodegen::new();
437        let insts = codegen.emit_mul(Register::virt(0), Register::virt(1));
438        assert!(!insts.is_empty());
439    }
440    #[test]
441    pub(super) fn test_bignat_codegen_sub() {
442        let mut codegen = BigNatCodegen::new();
443        let insts = codegen.emit_sub(Register::virt(0), Register::virt(1));
444        assert!(!insts.is_empty());
445    }
446    #[test]
447    pub(super) fn test_bignat_codegen_div() {
448        let mut codegen = BigNatCodegen::new();
449        let insts = codegen.emit_div(Register::virt(0), Register::virt(1));
450        assert!(!insts.is_empty());
451    }
452    #[test]
453    pub(super) fn test_bignat_codegen_cmp() {
454        let mut codegen = BigNatCodegen::new();
455        let insts = codegen.emit_cmp(Register::virt(0), Register::virt(1));
456        assert!(!insts.is_empty());
457    }
458    #[test]
459    pub(super) fn test_bignat_codegen_of_u64() {
460        let mut codegen = BigNatCodegen::new();
461        let insts = codegen.emit_of_u64(42);
462        assert!(!insts.is_empty());
463    }
464    #[test]
465    pub(super) fn test_external_codegen_alloc() {
466        let mut codegen = ExternalObjectCodegen::new();
467        let insts = codegen.emit_alloc_external(Register::virt(0), "my_finalizer");
468        assert!(!insts.is_empty());
469    }
470    #[test]
471    pub(super) fn test_external_codegen_get_data() {
472        let mut codegen = ExternalObjectCodegen::new();
473        let insts = codegen.emit_get_external_data(Register::virt(0));
474        assert!(!insts.is_empty());
475        assert!(insts.iter().any(|i| matches!(i, NativeInst::Load { .. })));
476    }
477    #[test]
478    pub(super) fn test_runtime_module_builder_new() {
479        let config = RuntimeConfig::default();
480        let builder = RuntimeModuleBuilder::new(config);
481        assert_eq!(builder.instruction_count(), 0);
482    }
483    #[test]
484    pub(super) fn test_runtime_module_builder_emit_ctor() {
485        let mut builder = RuntimeModuleBuilder::new(RuntimeConfig::default());
486        builder.emit_ctor(0, 2, 0);
487        assert!(builder.instruction_count() > 0);
488    }
489    #[test]
490    pub(super) fn test_runtime_module_builder_emit_closure() {
491        let mut builder = RuntimeModuleBuilder::new(RuntimeConfig::default());
492        let env = vec![Register::virt(0), Register::virt(1)];
493        builder.emit_closure("my_fn", 2, &env);
494        assert!(builder.call_count() > 0);
495    }
496    #[test]
497    pub(super) fn test_runtime_module_builder_emit_inc_dec() {
498        let mut builder = RuntimeModuleBuilder::new(RuntimeConfig::default());
499        builder.emit_inc(Register::virt(5));
500        builder.emit_dec(Register::virt(5));
501        assert!(builder.instruction_count() > 0);
502    }
503    #[test]
504    pub(super) fn test_runtime_module_builder_emit_nat_add() {
505        let mut builder = RuntimeModuleBuilder::new(RuntimeConfig::default());
506        builder.emit_nat_add(Register::virt(0), Register::virt(1));
507        assert!(!builder.instructions().is_empty());
508    }
509    #[test]
510    pub(super) fn test_runtime_module_builder_emit_str_append() {
511        let mut builder = RuntimeModuleBuilder::new(RuntimeConfig::default());
512        builder.emit_str_append(Register::virt(0), Register::virt(1));
513        assert!(!builder.instructions().is_empty());
514    }
515    #[test]
516    pub(super) fn test_runtime_module_builder_comment_count() {
517        let mut builder = RuntimeModuleBuilder::new(RuntimeConfig::default());
518        builder.emit_ctor(0, 1, 0);
519        assert!(builder.comment_count() > 0);
520    }
521    #[test]
522    pub(super) fn test_rc_use_analysis_new() {
523        let analysis = RcUseAnalysis::new();
524        assert_eq!(analysis.use_count(vid(0)), 0);
525    }
526    #[test]
527    pub(super) fn test_rc_use_analysis_single_use() {
528        let mut analysis = RcUseAnalysis::new();
529        let module = mk_module(vec![mk_fun_decl(
530            "f",
531            LcnfExpr::Return(LcnfArg::Var(vid(5))),
532        )]);
533        analysis.analyze_module(&module);
534        assert_eq!(analysis.use_count(vid(5)), 1);
535    }
536    #[test]
537    pub(super) fn test_rc_use_analysis_multi_use() {
538        let mut analysis = RcUseAnalysis::new();
539        let module = mk_module(vec![mk_fun_decl(
540            "f",
541            LcnfExpr::TailCall(
542                LcnfArg::Var(vid(1)),
543                vec![LcnfArg::Var(vid(1)), LcnfArg::Var(vid(2))],
544            ),
545        )]);
546        analysis.analyze_module(&module);
547        assert_eq!(analysis.use_count(vid(1)), 2);
548        assert_eq!(analysis.use_count(vid(2)), 1);
549    }
550    #[test]
551    pub(super) fn test_rc_use_analysis_multi_use_vars() {
552        let mut analysis = RcUseAnalysis::new();
553        analysis.analyze_module(&mk_module(vec![mk_fun_decl(
554            "f",
555            LcnfExpr::TailCall(
556                LcnfArg::Var(vid(3)),
557                vec![LcnfArg::Var(vid(3)), LcnfArg::Var(vid(3))],
558            ),
559        )]));
560        let multi = analysis.multi_use_vars();
561        assert!(multi.iter().any(|(v, _)| *v == vid(3)));
562    }
563    #[test]
564    pub(super) fn test_layout_cache_new() {
565        let cache = LayoutCache::new();
566        assert_eq!(cache.ctor_count(), 0);
567        assert_eq!(cache.closure_count(), 0);
568    }
569    #[test]
570    pub(super) fn test_layout_cache_get_ctor() {
571        let mut cache = LayoutCache::new();
572        let layout = cache.get_ctor("Pair", 0, 2, 0);
573        assert_eq!(layout.num_obj_fields, 2);
574        assert_eq!(cache.ctor_count(), 1);
575        let layout2 = cache.get_ctor("Pair", 0, 2, 0);
576        assert_eq!(layout2.num_obj_fields, 2);
577        assert_eq!(cache.ctor_count(), 1);
578    }
579    #[test]
580    pub(super) fn test_layout_cache_get_closure() {
581        let mut cache = LayoutCache::new();
582        let layout = cache.get_closure(2, 3);
583        assert_eq!(layout.arity, 2);
584        assert_eq!(layout.num_captured, 3);
585        assert_eq!(cache.closure_count(), 1);
586        let layout2 = cache.get_closure(2, 3);
587        assert_eq!(layout2.arity, 2);
588        assert_eq!(cache.closure_count(), 1);
589    }
590    #[test]
591    pub(super) fn test_layout_cache_clear() {
592        let mut cache = LayoutCache::new();
593        cache.get_ctor("Nil", 0, 0, 0);
594        cache.get_closure(1, 1);
595        cache.clear();
596        assert_eq!(cache.ctor_count(), 0);
597        assert_eq!(cache.closure_count(), 0);
598    }
599    #[test]
600    pub(super) fn test_object_tag_invalid_from_u8() {
601        assert!(ObjectTag::from_u8(8).is_none());
602        assert!(ObjectTag::from_u8(100).is_none());
603    }
604    #[test]
605    pub(super) fn test_object_tag_all_values() {
606        let tags: Vec<ObjectTag> = (0u8..8).filter_map(ObjectTag::from_u8).collect();
607        assert_eq!(tags.len(), 8);
608    }
609    #[test]
610    pub(super) fn test_object_layout_scalar_offset() {
611        let layout = ObjectLayout::for_ctor(0, 3, 16);
612        let scalar_off = layout.scalar_offset();
613        assert_eq!(scalar_off, ObjectLayout::HEADER_SIZE + 3 * 8);
614    }
615    #[test]
616    pub(super) fn test_object_layout_for_external_size() {
617        let layout = ObjectLayout::for_external();
618        assert!(layout.total_size >= ObjectLayout::HEADER_SIZE + 24);
619    }
620    #[test]
621    pub(super) fn test_closure_codegen_apply_5_args() {
622        let mut codegen = ClosureCodegen::new();
623        let args: Vec<Register> = (1..=5).map(Register::virt).collect();
624        let insts = codegen.emit_closure_apply(Register::virt(0), &args);
625        assert!(!insts.is_empty());
626        let has_apply_n = insts.iter().any(|i| {
627            if let NativeInst::Call {
628                func: NativeValue::FRef(name),
629                ..
630            } = i
631            {
632                name.contains("lean_apply_n")
633            } else {
634                false
635            }
636        });
637        assert!(has_apply_n);
638    }
639    #[test]
640    pub(super) fn test_alloc_strategy_pool() {
641        let mut alloc = AllocatorCodegen::new(AllocStrategy::Pool);
642        let insts = alloc.emit_free(Register::virt(0));
643        assert!(insts.iter().any(|i| matches!(i, NativeInst::Call { .. })));
644    }
645    #[test]
646    pub(super) fn test_alloc_strategy_bump_alloc() {
647        let mut alloc = AllocatorCodegen::new(AllocStrategy::Bump);
648        let insts = alloc.emit_alloc(64, 8);
649        assert!(insts.iter().any(|i| {
650            if let NativeInst::Call {
651                func: NativeValue::FRef(name),
652                ..
653            } = i
654            {
655                name == "bump_alloc"
656            } else {
657                false
658            }
659        }));
660    }
661}