Skip to main content

lutra_compiler/
bytecoding.rs

1use std::collections::HashMap;
2
3use indexmap::IndexSet;
4use lutra_bin::Encode;
5use lutra_bin::br::*;
6use lutra_bin::bytes::BufMut;
7use lutra_bin::ir;
8
9pub fn compile_program(value: ir::Program) -> Program {
10    let mut b = ByteCoder {
11        externals: Default::default(),
12        include_defs: false,
13
14        defs: &value.defs,
15        def_map: value.defs.iter().map(|def| (&def.name, &def.ty)).collect(),
16        next_wrapper_id: 0xFF00,
17    };
18
19    let program = Program {
20        main: b.compile_expr(value.main),
21        externals: b.externals.into_iter().collect(),
22        defs: if b.include_defs { value.defs } else { vec![] },
23    };
24    tracing::debug!("br:\n{program:#?}");
25    program
26}
27
28struct ByteCoder<'t> {
29    externals: IndexSet<ExternalSymbol>,
30    defs: &'t [ir::TyDef],
31    def_map: HashMap<&'t ir::Path, &'t ir::Ty>,
32
33    // Some externals need defs (read_parquet), but most of the time, skip them.
34    include_defs: bool,
35
36    /// Counter for generating unique wrapper function IDs.
37    next_wrapper_id: u32,
38}
39
40impl<'t> ByteCoder<'t> {
41    fn get_ty_mat<'a: 't>(&self, ty: &'a ir::Ty) -> &'t ir::Ty {
42        let mut ty = ty;
43        while let TyKind::Ident(path) = &ty.kind {
44            ty = self.def_map.get(path).unwrap();
45        }
46        ty
47    }
48    fn get_ty_mat_or_std<'a: 't>(&self, ty: &'a ir::Ty) -> &'t ir::Ty {
49        let mut ty = ty;
50        while let TyKind::Ident(path) = &ty.kind {
51            if ir::TyStd::try_new(path).is_some() {
52                return ty;
53            }
54            ty = self.def_map.get(path).unwrap();
55        }
56        ty
57    }
58
59    fn compile_expr(&mut self, expr: ir::Expr) -> Expr {
60        let kind = match expr.kind {
61            ir::ExprKind::Pointer(v) => self.compile_pointer(v, &expr.ty),
62            ir::ExprKind::Literal(v) => ExprKind::Literal(self.compile_literal(v)),
63            ir::ExprKind::Call(v) => ExprKind::Call(Box::new(self.compile_call(*v))),
64            ir::ExprKind::Function(v) => ExprKind::Function(Box::new(self.compile_function(*v))),
65            ir::ExprKind::Tuple(v) => ExprKind::Tuple(Box::new(self.compile_tuple(v))),
66            ir::ExprKind::Array(v) => ExprKind::Array(Box::new(self.compile_array(expr.ty, v))),
67            ir::ExprKind::EnumVariant(v) => {
68                ExprKind::EnumVariant(Box::new(self.compile_enum_variant(expr.ty, *v)))
69            }
70            ir::ExprKind::EnumTag(v) => self.compile_expr(v.subject).kind,
71            ir::ExprKind::EnumUnwrap(v) => return self.compile_enum_unwrap(*v),
72            ir::ExprKind::TupleLookup(v) => return self.compile_tuple_lookup(*v),
73            ir::ExprKind::Binding(v) => ExprKind::Binding(Box::new(self.compile_binding(*v))),
74            ir::ExprKind::Switch(v) => ExprKind::Switch(self.compile_switch(v)),
75        };
76
77        Expr { kind }
78    }
79
80    fn compile_pointer(&mut self, ptr: ir::Pointer, ty: &ir::Ty) -> ExprKind {
81        match ptr {
82            ir::Pointer::External(e_ptr) => {
83                let ty = self.get_ty_mat(ty);
84                self.compile_external_symbol(e_ptr.id, ty)
85            }
86            #[rustfmt::skip]
87            ir::Pointer::Binding(binding_id) => {
88                ExprKind::Pointer(Sid(binding_id).with_tag(SidKind::Var))
89            },
90            ir::Pointer::Parameter(param_ptr) => {
91                let sid = param_ptr.function_id << 8 | param_ptr.param_position as u32;
92
93                ExprKind::Pointer(Sid(sid).with_tag(SidKind::FunctionScope))
94            }
95        }
96    }
97
98    fn compile_literal(&mut self, value: ir::Literal) -> Vec<u8> {
99        match value {
100            ir::Literal::Prim8(v) => v.encode(),
101            ir::Literal::Prim16(v) => v.encode(),
102            ir::Literal::Prim32(v) => v.encode(),
103            ir::Literal::Prim64(v) => v.encode(),
104            ir::Literal::Text(v) => v.encode(),
105        }
106    }
107
108    fn compile_call(&mut self, value: ir::Call) -> Call {
109        Call {
110            function: self.compile_expr(value.function),
111            args: value
112                .args
113                .into_iter()
114                .map(|x| self.compile_expr(x))
115                .collect(),
116        }
117    }
118
119    fn compile_function(&mut self, value: ir::Function) -> Function {
120        Function {
121            symbol_ns: Sid(value.id << 8).with_tag(SidKind::FunctionScope),
122            body: self.compile_expr(value.body),
123        }
124    }
125
126    fn compile_tuple(&mut self, fields: Vec<ir::TupleField>) -> Tuple {
127        let field_layouts = fields
128            .iter()
129            .flat_map(|f| {
130                if f.unpack {
131                    let ir::TyKind::Tuple(fields) = &self.get_ty_mat(&f.expr.ty).kind else {
132                        panic!();
133                    };
134                    fields.iter().map(|f| &f.ty).collect::<Vec<_>>()
135                } else {
136                    vec![&f.expr.ty]
137                }
138            })
139            .map(|ty| self.compile_ty_layout(ty.layout.clone().unwrap()))
140            .collect();
141
142        let fields = fields
143            .into_iter()
144            .map(|f| {
145                let unpack = if f.unpack {
146                    let ir::TyKind::Tuple(fields) = &self.get_ty_mat(&f.expr.ty).kind else {
147                        panic!();
148                    };
149                    fields.len() as u8
150                } else {
151                    0
152                };
153                let expr = self.compile_expr(f.expr);
154
155                TupleField { expr, unpack }
156            })
157            .collect();
158        Tuple {
159            fields,
160            field_layouts,
161        }
162    }
163
164    fn compile_array(&mut self, ty: ir::Ty, items: Vec<ir::Expr>) -> Array {
165        let ty_item = self.get_ty_mat(&ty).kind.as_array().unwrap();
166        Array {
167            items: items.into_iter().map(|x| self.compile_expr(x)).collect(),
168            item_layout: self.compile_ty_layout(ty_item.layout.clone().unwrap()),
169        }
170    }
171
172    fn compile_enum_variant(&mut self, ty: Ty, v: ir::EnumVariant) -> EnumVariant {
173        let ty_mat = self.get_ty_mat(&ty);
174        let ir::TyKind::Enum(ty_variants) = &ty_mat.kind else {
175            panic!()
176        };
177        let ty_variant = ty_variants.get(v.tag as usize).unwrap();
178        let head_format = lutra_bin::layout::enum_head_format(ty_variants, &ty.variants_recursive);
179        let variant_format = lutra_bin::layout::enum_variant_format(&head_format, &ty_variant.ty);
180
181        EnumVariant {
182            tag: v.tag.to_le_bytes()[0..head_format.tag_bytes as usize].to_vec(),
183            inner_bytes: head_format.inner_bytes as u8,
184            has_ptr: head_format.has_ptr,
185            padding_bytes: variant_format.padding_bytes,
186            inner: self.compile_expr(v.inner),
187        }
188    }
189
190    fn compile_enum_unwrap(&mut self, v: ir::EnumUnwrap) -> Expr {
191        let ty_mat = self.get_ty_mat(&v.subject.ty);
192        let ir::TyKind::Enum(ty_variants) = &ty_mat.kind else {
193            panic!()
194        };
195
196        let head_format =
197            lutra_bin::layout::enum_head_format(ty_variants, &ty_mat.variants_recursive);
198
199        let mut expr = self.compile_expr(v.subject);
200
201        // offset tag
202        expr = Expr {
203            kind: ExprKind::Offset(Box::new(Offset {
204                base: expr,
205                offset: head_format.tag_bytes,
206            })),
207        };
208
209        // dereference pointer (if there is a pointer)
210        if head_format.has_ptr {
211            expr = Expr {
212                kind: ExprKind::Deref(Box::new(Deref { ptr: expr })),
213            };
214        }
215
216        expr
217    }
218
219    fn compile_tuple_lookup(&mut self, value: ir::TupleLookup) -> Expr {
220        let base_ty = self.get_ty_mat(&value.base.ty);
221        let offset = lutra_bin::layout::tuple_field_offset(base_ty, value.position);
222
223        let kind = ExprKind::Offset(Box::new(Offset {
224            base: self.compile_expr(value.base),
225            offset,
226        }));
227        Expr { kind }
228    }
229
230    fn compile_binding(&mut self, value: ir::Binding) -> Binding {
231        Binding {
232            symbol: Sid(value.id).with_tag(SidKind::Var),
233            expr: self.compile_expr(value.expr),
234            main: self.compile_expr(value.main),
235        }
236    }
237
238    fn compile_switch(&mut self, branches: Vec<ir::SwitchBranch>) -> Vec<SwitchBranch> {
239        branches
240            .into_iter()
241            .map(|b| SwitchBranch {
242                condition: self.compile_expr(b.condition),
243                value: self.compile_expr(b.value),
244            })
245            .collect()
246    }
247
248    fn compile_ty_layout(&self, value: ir::TyLayout) -> TyLayout {
249        TyLayout {
250            head_size: value.head_size,
251            body_ptrs: value.body_ptrs,
252        }
253    }
254
255    fn compile_external_symbol(&mut self, id: String, ty_mat: &ir::Ty) -> ExprKind {
256        let layout_args: Vec<u32> = match id.as_str() {
257            "std::ops::add"
258            | "std::ops::sub"
259            | "std::ops::mul"
260            | "std::ops::div"
261            | "std::ops::mod"
262            | "std::ops::neg"
263            | "std::ops::cmp"
264            | "std::ops::eq"
265            | "std::ops::lt"
266            | "std::ops::lte"
267            | "std::convert::to_int8"
268            | "std::convert::to_int16"
269            | "std::convert::to_int32"
270            | "std::convert::to_int64"
271            | "std::convert::to_uint8"
272            | "std::convert::to_uint16"
273            | "std::convert::to_uint32"
274            | "std::convert::to_uint64"
275            | "std::convert::to_float32"
276            | "std::convert::to_float64"
277            | "std::convert::to_text"
278            | "std::math::abs"
279            | "std::math::pow"
280            | "std::array::sequence" => {
281                let param_ty = as_ty_of_param(ty_mat);
282                let ty_name = self.as_std_ty_suffix(param_ty);
283                return self.make_external(format!("{id}_{ty_name}"), vec![]);
284            }
285
286            "std::array::fold" => {
287                let item_layout = as_layout_of_param_array(ty_mat);
288                vec![
289                    item_layout.head_size.div_ceil(8), // item_head_size
290                ]
291            }
292
293            "std::array::min"
294            | "std::array::max"
295            | "std::array::rank"
296            | "std::array::rank_dense"
297            | "std::array::rank_percentile"
298            | "std::array::cume_dist" => {
299                // These take func([T]): ... — inject cmp for item type T
300                let param_ty = as_ty_of_param(ty_mat);
301                let item_ty = self.get_ty_mat(param_ty).kind.as_array().unwrap();
302                let item_layout = item_ty.layout.as_ref().unwrap();
303                let layout_args = vec![item_layout.head_size.div_ceil(8)];
304
305                let n_params = ty_mat.kind.as_function().unwrap().params.len();
306                let cmp_id = format!("std::ops::cmp_{}", self.as_std_ty_suffix(item_ty));
307                let cmp = self.make_external(cmp_id, vec![]);
308                return self.wrap_external_with_extra_args(id, layout_args, n_params, vec![cmp]);
309            }
310
311            "std::array::sort" => {
312                let item_layout = as_layout_of_param_array(ty_mat);
313
314                let mut layout_args = Vec::with_capacity(1 + 1 + item_layout.body_ptrs.len());
315                layout_args.push(item_layout.head_size.div_ceil(8));
316                layout_args.extend(as_len_and_items(&item_layout.body_ptrs));
317
318                let ty_func = ty_mat.kind.as_function().unwrap();
319                let key_extractor_ty = self.get_ty_mat(&ty_func.params[1]);
320                let key_ty = &key_extractor_ty.kind.as_function().unwrap().body;
321
322                let n_params = ty_func.params.len();
323                let cmp_id = format!("std::ops::cmp_{}", self.as_std_ty_suffix(key_ty));
324                let cmp = self.make_external(cmp_id, vec![]);
325                return self.wrap_external_with_extra_args(id, layout_args, n_params, vec![cmp]);
326            }
327
328            "std::array::sum" | "std::array::mean" | "std::array::rolling_mean" => {
329                let param_ty = as_ty_of_param(ty_mat);
330                let item_ty = self.get_ty_mat(param_ty).kind.as_array().unwrap();
331                let item_layout = item_ty.layout.as_ref().unwrap();
332                let layout_args = vec![item_layout.head_size.div_ceil(8)];
333
334                let ty_name = self.as_std_ty_suffix(item_ty);
335                return self.make_external(format!("{id}_{ty_name}"), layout_args);
336            }
337
338            "std::array::index" => {
339                let item_layout = as_layout_of_param_array(ty_mat);
340
341                let ty_func = ty_mat.kind.as_function().unwrap();
342                let ty_out_variants = self.get_ty_mat(&ty_func.body).kind.as_enum().unwrap();
343                let ty_out_format = lutra_bin::layout::enum_format(
344                    ty_out_variants,
345                    &ty_func.body.variants_recursive,
346                );
347                let ty_out_format = ty_out_format.encode();
348
349                let mut r = vec![
350                    item_layout.head_size.div_ceil(8), // item_head_size
351                ];
352
353                pack_bytes_to_u32(ty_out_format, &mut r);
354                r
355            }
356
357            "std::array::filter"
358            | "std::array::slice"
359            | "std::array::append"
360            | "std::array::loop_until_empty" => {
361                let item_layout = as_layout_of_param_array(ty_mat);
362
363                let mut r = Vec::with_capacity(1 + 1 + item_layout.body_ptrs.len());
364                r.push(item_layout.head_size.div_ceil(8)); // item_head_size
365                r.extend(as_len_and_items(&item_layout.body_ptrs)); // item_body_ptrs
366                r
367            }
368
369            "std::array::lag" | "std::array::lead" => {
370                let item_layout = as_layout_of_param_array(ty_mat);
371
372                let mut r = Vec::with_capacity(1 + 1 + item_layout.body_ptrs.len());
373                r.push(item_layout.head_size.div_ceil(8)); // item_head_size
374                r.extend(as_len_and_items(&item_layout.body_ptrs)); // item_body_ptrs
375
376                // also encode default value
377                let ty_func = ty_mat.kind.as_function().unwrap();
378                let ty_item = ty_func.body.kind.as_array().unwrap();
379                let default_val = self.construct_default_for_ty(ty_item);
380                pack_bytes_to_u32(default_val, &mut r);
381
382                r
383            }
384
385            "std::array::map" | "std::array::flat_map" | "std::array::scan" => {
386                let input_layout = as_layout_of_param_array(ty_mat);
387                let output_layout = as_layout_of_return_array(ty_mat);
388
389                let mut r = Vec::with_capacity(2 + 1 + output_layout.body_ptrs.len());
390                r.push(input_layout.head_size.div_ceil(8)); // input_item_head
391                r.push(output_layout.head_size.div_ceil(8)); // output_item_head
392                r.extend(as_len_and_items(&output_layout.body_ptrs)); // output_item_body_ptrs
393                r
394            }
395
396            "std::array::to_columnar" => {
397                let ty_func = ty_mat.kind.as_function().unwrap();
398
399                let input_item = ty_func.params[0].kind.as_array().unwrap();
400                let input_layout = input_item.layout.as_ref().unwrap();
401
402                let mut r = Vec::new();
403                r.push(input_layout.head_size.div_ceil(8)); // item_head_size
404
405                let input_field_offsets = lutra_bin::layout::tuple_field_offsets(input_item);
406                r.extend(as_len_and_items(&input_field_offsets)); // field_offsets
407
408                // fields_head_bytes
409                let fields = input_item.kind.as_tuple().unwrap();
410                r.push(fields.len() as u32);
411                for field in fields {
412                    let field_layout = field.ty.layout.as_ref().unwrap();
413                    r.push(field_layout.head_size.div_ceil(8));
414                }
415
416                // fields_body_ptrs
417                for field in fields {
418                    let field_layout = field.ty.layout.as_ref().unwrap();
419                    r.extend(as_len_and_items(&field_layout.body_ptrs));
420                }
421
422                r
423            }
424            "std::array::from_columnar" => {
425                let ty_func = ty_mat.kind.as_function().unwrap();
426
427                let output_item = ty_func.body.kind.as_array().unwrap();
428                let output_layout = output_item.layout.as_ref().unwrap();
429
430                let mut r = Vec::new();
431                r.push(output_layout.head_size.div_ceil(8)); // output_head_bytes
432
433                r.extend(as_len_and_items(&output_layout.body_ptrs)); // output_body_ptrs
434
435                // fields_item_head_bytes
436                let fields = output_item.kind.as_tuple().unwrap();
437                r.push(fields.len() as u32);
438                for field in fields {
439                    let field_layout = field.ty.layout.as_ref().unwrap();
440                    r.push(field_layout.head_size.div_ceil(8));
441                }
442
443                // fields_body_ptrs
444                for field in fields {
445                    let field_layout = field.ty.layout.as_ref().unwrap();
446                    r.extend(as_len_and_items(&field_layout.body_ptrs));
447                }
448
449                r
450            }
451
452            "std::array::zip" => {
453                let ty_func = ty_mat.kind.as_function().unwrap();
454
455                let a_item = self.get_ty_mat(&ty_func.params[0]).kind.as_array().unwrap();
456                let a_layout = a_item.layout.as_ref().unwrap();
457
458                let b_item = self.get_ty_mat(&ty_func.params[1]).kind.as_array().unwrap();
459                let b_layout = b_item.layout.as_ref().unwrap();
460
461                let mut r = Vec::new();
462                r.push(a_layout.head_size.div_ceil(8));
463                r.extend(as_len_and_items(&a_layout.body_ptrs));
464                r.push(b_layout.head_size.div_ceil(8));
465                r.extend(as_len_and_items(&b_layout.body_ptrs));
466                r
467            }
468
469            "std::array::group" => {
470                let ty_func = ty_mat.kind.as_function().unwrap();
471
472                let input_item = self.get_ty_mat(&ty_func.params[0]).kind.as_array().unwrap();
473                let input_layout = input_item.layout.as_ref().unwrap();
474
475                let output_item = self.get_ty_mat(&ty_func.body).kind.as_array().unwrap();
476                let output_layout = output_item.layout.as_ref().unwrap();
477
478                let key = &self.get_ty_mat(output_item).kind.as_tuple().unwrap()[0].ty;
479                let key_layout = key.layout.as_ref().unwrap();
480
481                let mut r = Vec::new();
482                r.push(input_layout.head_size.div_ceil(8)); // input_head_bytes
483                r.extend(as_len_and_items(&input_layout.body_ptrs)); // input_body_ptrs
484
485                r.push(output_layout.head_size.div_ceil(8)); // output_head_bytes
486                r.extend(as_len_and_items(&output_layout.body_ptrs)); // output_body_ptrs
487
488                // output_field_head_bytes
489                let fields = output_item.kind.as_tuple().unwrap();
490                r.push(fields.len() as u32);
491                for field in fields {
492                    let field_layout = field.ty.layout.as_ref().unwrap();
493                    r.push(field_layout.head_size.div_ceil(8));
494                }
495
496                // output_fields_body_ptrs
497                for field in fields {
498                    let field_layout = field.ty.layout.as_ref().unwrap();
499                    r.extend(as_len_and_items(&field_layout.body_ptrs));
500                }
501
502                r.push(key_layout.head_size.div_ceil(8)); // key_head_bytes
503
504                r
505            }
506
507            "std::fs::read_parquet" => {
508                let ty_func = ty_mat.kind.as_function().unwrap();
509                let ty_data = &ty_func.body;
510
511                self.include_defs = true;
512
513                let mut r = Vec::new();
514                pack_bytes_to_u32(ty_data.encode(), &mut r);
515                r
516            }
517            "std::fs::write_parquet" => {
518                let ty_data = as_ty_of_param(ty_mat);
519
520                self.include_defs = true;
521
522                let mut r = Vec::new();
523                pack_bytes_to_u32(ty_data.encode(), &mut r);
524                r
525            }
526
527            _ => vec![],
528        };
529
530        let (index, _) = self
531            .externals
532            .insert_full(ExternalSymbol { id, layout_args });
533        ExprKind::Pointer(Sid(index as u32).with_tag(SidKind::External))
534    }
535
536    /// Determines the snake_case suffix for a framed std type
537    fn as_std_ty_suffix(&self, ty: &ir::Ty) -> String {
538        match &self.get_ty_mat_or_std(ty).kind {
539            ir::TyKind::Ident(path) => path.0.last().unwrap().to_ascii_lowercase(),
540            ir::TyKind::Primitive(ir::TyPrimitive::Prim8) => "uint8".into(),
541            ir::TyKind::Primitive(ir::TyPrimitive::Prim16) => "uint16".into(),
542            ir::TyKind::Primitive(ir::TyPrimitive::Prim32) => "uint32".into(),
543            ir::TyKind::Primitive(ir::TyPrimitive::Prim64) => "uint64".into(),
544            _ => {
545                panic!("std specialization not supported for {}", ir::print_ty(ty));
546            }
547        }
548    }
549
550    /// Registers an external symbol and returns a `Pointer` ExprKind for it.
551    fn make_external(&mut self, id: String, layout_args: Vec<u32>) -> ExprKind {
552        let (index, _) = self
553            .externals
554            .insert_full(ExternalSymbol { id, layout_args });
555        ExprKind::Pointer(Sid(index as u32).with_tag(SidKind::External))
556    }
557
558    /// Wraps an external function in a `Function` that forwards the original
559    /// params and appends extra args (e.g. an injected comparator).
560    ///
561    /// Produces:
562    /// ```text
563    /// Function(params: [p0, ..., pN-1]) {
564    ///     Call(
565    ///         func: ExternalPointer(id),
566    ///         args: [p0, ..., pN-1, extra0, extra1, ...]
567    ///     )
568    /// }
569    /// ```
570    fn wrap_external_with_extra_args(
571        &mut self,
572        id: String,
573        layout_args: Vec<u32>,
574        n_params: usize,
575        extra_args: Vec<ExprKind>,
576    ) -> ExprKind {
577        // Register the real external symbol
578        let (ext_index, _) = self
579            .externals
580            .insert_full(ExternalSymbol { id, layout_args });
581        let ext_sid = Sid(ext_index as u32).with_tag(SidKind::External);
582
583        // Generate a unique wrapper function ID
584        let wrapper_id = self.next_wrapper_id;
585        self.next_wrapper_id += 1;
586        let wrapper_ns = Sid(wrapper_id << 8).with_tag(SidKind::FunctionScope);
587
588        // Build args: forward original params + append extras
589        let mut args: Vec<Expr> = (0..n_params)
590            .map(|i| {
591                let sid = Sid(wrapper_id << 8 | i as u32).with_tag(SidKind::FunctionScope);
592                Expr {
593                    kind: ExprKind::Pointer(sid),
594                }
595            })
596            .collect();
597        args.extend(extra_args.into_iter().map(|kind| Expr { kind }));
598
599        ExprKind::Function(Box::new(Function {
600            symbol_ns: wrapper_ns,
601            body: Expr {
602                kind: ExprKind::Call(Box::new(Call {
603                    function: Expr {
604                        kind: ExprKind::Pointer(ext_sid),
605                    },
606                    args,
607                })),
608            },
609        }))
610    }
611
612    fn construct_default_for_ty(&self, ty: &ir::Ty) -> Vec<u8> {
613        self.construct_default_for_ty_re(ty)
614            .encode(ty, self.defs)
615            .unwrap()
616    }
617
618    fn construct_default_for_ty_re(&self, ty: &ir::Ty) -> lutra_bin::Value {
619        match &self.get_ty_mat(ty).kind {
620            ir::TyKind::Primitive(prim) => match prim {
621                ir::TyPrimitive::Prim8 => lutra_bin::Value::Prim8(0),
622                ir::TyPrimitive::Prim16 => lutra_bin::Value::Prim16(0),
623                ir::TyPrimitive::Prim32 => lutra_bin::Value::Prim32(0),
624                ir::TyPrimitive::Prim64 => lutra_bin::Value::Prim64(0),
625            },
626            ir::TyKind::Array(_) => lutra_bin::Value::Array(vec![]),
627            ir::TyKind::Tuple(ty_fields) => lutra_bin::Value::Tuple(
628                ty_fields
629                    .iter()
630                    .map(|f| self.construct_default_for_ty_re(&f.ty))
631                    .collect(),
632            ),
633            ir::TyKind::Enum(ty_enum_variants) => {
634                let variant = ty_enum_variants.iter().next().unwrap();
635                lutra_bin::Value::Enum(0, Box::new(self.construct_default_for_ty_re(&variant.ty)))
636            }
637
638            ir::TyKind::Function(_) => panic!(),
639            ir::TyKind::Ident(_) => unreachable!(),
640        }
641    }
642}
643
644fn as_len_and_items(items: &[u32]) -> impl Iterator<Item = u32> + '_ {
645    Some(items.len() as u32)
646        .into_iter()
647        .chain(items.iter().cloned())
648}
649
650fn as_layout_of_param_array(ty: &Ty) -> &ir::TyLayout {
651    let ty_func = ty.kind.as_function().unwrap();
652    let ty_array = ty_func.params[0].kind.as_array().unwrap();
653
654    ty_array.layout.as_ref().unwrap()
655}
656
657fn as_layout_of_return_array(ty: &Ty) -> &ir::TyLayout {
658    let ty_func = ty.kind.as_function().unwrap();
659    let ty_array = ty_func.body.kind.as_array().unwrap();
660
661    ty_array.layout.as_ref().unwrap()
662}
663
664fn as_ty_of_param(ty: &Ty) -> &ir::Ty {
665    let ty_func = ty.kind.as_function().unwrap();
666    &ty_func.params[0]
667}
668
669fn pack_bytes_to_u32(mut input: Vec<u8>, output: &mut Vec<u32>) {
670    let input_len = input.len();
671
672    // pad
673    if !input.len().is_multiple_of(4) {
674        input.put_bytes(0, 4 - input.len() % 4);
675    }
676
677    // cast to Vec<u32> as le bytes
678    output.reserve(2 + input.len() / 4);
679    output.push((input.len() / 4) as u32 + 1);
680    output.push(input_len as u32);
681    for chunk in input.chunks_exact(4) {
682        output.push(u32::from_le_bytes(chunk.try_into().unwrap()));
683    }
684}