1use crate::op::*;
4use crate::program::*;
5use indexmap::IndexMap;
6use lex_ast as a;
7
8mod constpool;
9mod free_vars;
10mod liveness;
11mod lowering;
12mod peephole;
13
14use constpool::ConstPool;
15use free_vars::free_vars;
16use liveness::apply_last_load_takes;
17use lowering::{apply_arena_lowering, apply_escape_lowering};
18use peephole::{
19 apply_peephole, apply_peephole_slice2, apply_peephole_slice3, apply_peephole_slice4,
20 apply_peephole_slice5, apply_peephole_slice6, apply_peephole_slice7, apply_peephole_slice9,
21};
22
23pub fn compile_program(stages: &[a::Stage]) -> Program {
24 let mut p = Program {
25 constants: Vec::new(),
26 functions: Vec::new(),
27 function_names: IndexMap::new(),
28 module_aliases: IndexMap::new(),
29 entry: None,
30 record_shapes: Vec::new(),
31 };
32
33 for s in stages {
36 if let a::Stage::Import(i) = s {
37 let module = i.reference.strip_prefix("std.").unwrap_or(&i.reference).to_string();
38 p.module_aliases.insert(i.alias.clone(), module);
39 }
40 }
41
42 for s in stages {
43 if let a::Stage::FnDecl(fd) = s {
44 let idx = p.functions.len() as u32;
45 p.function_names.insert(fd.name.clone(), idx);
46 p.functions.push(Function {
47 name: fd.name.clone(),
48 arity: fd.params.len() as u16,
49 locals_count: 0,
50 code: Vec::new(),
51 effects: fd.effects.iter().map(|e| DeclaredEffect {
52 kind: e.name.clone(),
53 arg: e.arg.as_ref().map(|a| match a {
54 a::EffectArg::Str { value } => EffectArg::Str(value.clone()),
55 a::EffectArg::Int { value } => EffectArg::Int(*value),
56 a::EffectArg::Ident { value } => EffectArg::Ident(value.clone()),
57 }),
58 }).collect(),
59 body_hash: crate::program::ZERO_BODY_HASH,
62 refinements: fd.params.iter().map(|p| match &p.ty {
66 a::TypeExpr::Refined { binding, predicate, .. } =>
67 Some(crate::program::Refinement {
68 binding: binding.clone(),
69 predicate: (**predicate).clone(),
70 }),
71 _ => None,
72 }).collect(),
73 field_ic_sites: 0,
75 });
76 }
77 }
78
79 let mut pool = ConstPool::default();
80 let function_names = p.function_names.clone();
81 let module_aliases = p.module_aliases.clone();
82 let mut pending_lambdas: Vec<PendingLambda> = Vec::new();
83 let mut type_aliases: IndexMap<String, a::TypeExpr> = IndexMap::new();
89 for s in stages {
90 if let a::Stage::TypeDecl(td) = s {
91 if td.params.is_empty() {
95 type_aliases.insert(td.name.clone(), td.definition.clone());
96 }
97 }
98 }
99
100 for s in stages {
101 if let a::Stage::FnDecl(_) = s {
102 let id_map = lex_ast::expr_ids(s);
105 let fd = match s { a::Stage::FnDecl(fd) => fd, _ => unreachable!() };
106 let mut fc = FnCompiler {
107 code: Vec::new(),
108 locals: IndexMap::new(),
109 next_local: 0,
110 peak_local: 0,
111 local_types: IndexMap::new(),
112 local_record_field_types: IndexMap::new(),
113 field_get_sites: 0,
114 pool: &mut pool,
115 function_names: &function_names,
116 module_aliases: &module_aliases,
117 id_map: &id_map,
118 pending_lambdas: &mut pending_lambdas,
119 next_fn_id: &mut p.functions,
120 };
121 for param in &fd.params {
122 let i = fc.next_local;
123 fc.locals.insert(param.name.clone(), i);
124 fc.local_types.insert(param.name.clone(), classify_type_expr(¶m.ty));
125 if let Some(ftypes) = record_field_types(¶m.ty, &type_aliases) {
130 fc.local_record_field_types.insert(param.name.clone(), ftypes);
131 }
132 fc.next_local += 1;
133 fc.peak_local = fc.next_local;
134 }
135 fc.compile_expr(&fd.body, true);
136 fc.code.push(Op::Return);
137 let code = std::mem::take(&mut fc.code);
138 let peak = fc.peak_local;
139 let field_sites = fc.field_get_sites as u16;
140 drop(fc);
141 let idx = function_names[&fd.name];
142 p.functions[idx as usize].code = code;
143 p.functions[idx as usize].field_ic_sites = field_sites;
144 p.functions[idx as usize].locals_count = peak;
145 }
146 }
147
148 while let Some(pl) = pending_lambdas.pop() {
151 let id_map = std::collections::HashMap::new();
152 let mut fc = FnCompiler {
153 code: Vec::new(),
154 locals: IndexMap::new(),
155 next_local: 0,
156 peak_local: 0,
157 local_types: IndexMap::new(),
158 local_record_field_types: IndexMap::new(),
159 field_get_sites: 0,
160 pool: &mut pool,
161 function_names: &function_names,
162 module_aliases: &module_aliases,
163 id_map: &id_map,
164 pending_lambdas: &mut pending_lambdas,
165 next_fn_id: &mut p.functions,
166 };
167 for name in &pl.capture_names {
168 let i = fc.next_local;
169 fc.locals.insert(name.clone(), i);
170 fc.local_types.insert(name.clone(), NumTy::Unknown);
175 fc.next_local += 1;
176 fc.peak_local = fc.next_local;
177 }
178 for p in &pl.params {
179 let i = fc.next_local;
180 fc.locals.insert(p.name.clone(), i);
181 fc.local_types.insert(p.name.clone(), classify_type_expr(&p.ty));
182 fc.next_local += 1;
183 fc.peak_local = fc.next_local;
184 }
185 fc.compile_expr(&pl.body, true);
186 fc.code.push(Op::Return);
187 let code = std::mem::take(&mut fc.code);
188 let peak = fc.peak_local;
189 let field_sites = fc.field_get_sites as u16;
190 drop(fc);
191 p.functions[pl.fn_id as usize].code = code;
192 p.functions[pl.fn_id as usize].field_ic_sites = field_sites;
193 p.functions[pl.fn_id as usize].locals_count = peak;
194 }
195
196 if std::env::var_os("LEX_NO_STACK_RECORDS").is_none() {
214 let escape_index = crate::escape::build_escape_index(&p.functions);
215 for f in p.functions.iter_mut() {
216 apply_escape_lowering(&mut f.code, &f.name, &escape_index);
217 }
218 }
219
220 if std::env::var_os("LEX_NO_ARENA_RECORDS").is_none() {
246 let arena_index = crate::arena::build_arena_index(&p.functions);
247 for f in p.functions.iter_mut() {
248 apply_arena_lowering(&mut f.code, &f.name, &arena_index);
249 }
250 }
251
252 for f in p.functions.iter_mut() {
268 apply_peephole(&mut f.code, &pool.pool);
269 apply_peephole_slice2(&mut f.code);
270 apply_peephole_slice3(&mut f.code);
271 apply_peephole_slice4(&mut f.code);
272 apply_peephole_slice5(&mut f.code, &pool.pool);
283 apply_peephole_slice6(&mut f.code);
288 apply_peephole_slice7(&mut f.code);
293 apply_peephole_slice9(&mut f.code);
303 }
304
305 if std::env::var_os("LEX_NO_TAKE_LOCALS").is_none() {
310 for f in p.functions.iter_mut() {
311 apply_last_load_takes(&mut f.code);
312 }
313 }
314
315 for f in p.functions.iter_mut() {
320 if f.body_hash == crate::program::ZERO_BODY_HASH {
321 f.body_hash = crate::program::compute_body_hash(
322 f.arity, f.locals_count, &f.code, &pool.record_shapes);
323 }
324 }
325
326 p.constants = pool.pool;
327 p.record_shapes = pool.record_shapes;
328 p
329}
330
331#[derive(Debug, Clone)]
332struct PendingLambda {
333 fn_id: u32,
334 capture_names: Vec<String>,
336 params: Vec<a::Param>,
337 body: a::CExpr,
338}
339
340struct FnCompiler<'a> {
341 code: Vec<Op>,
342 locals: IndexMap<String, u16>,
343 next_local: u16,
344 peak_local: u16,
346 local_types: IndexMap<String, NumTy>,
361 local_record_field_types: IndexMap<String, IndexMap<String, NumTy>>,
376 field_get_sites: u32,
383 pool: &'a mut ConstPool,
384 function_names: &'a IndexMap<String, u32>,
385 module_aliases: &'a IndexMap<String, String>,
386 id_map: &'a std::collections::HashMap<*const a::CExpr, lex_ast::NodeId>,
388 pending_lambdas: &'a mut Vec<PendingLambda>,
391 next_fn_id: &'a mut Vec<Function>,
394}
395
396#[derive(Debug, Clone, Copy, PartialEq, Eq)]
402enum NumTy { Int, Float, Unknown }
403
404fn record_field_types(
409 ty: &a::TypeExpr,
410 type_aliases: &IndexMap<String, a::TypeExpr>,
411) -> Option<IndexMap<String, NumTy>> {
412 match ty {
413 a::TypeExpr::Record { fields } => {
414 let mut m = IndexMap::new();
415 for f in fields {
416 m.insert(f.name.clone(), classify_type_expr(&f.ty));
417 }
418 Some(m)
419 }
420 a::TypeExpr::Refined { base, .. } => record_field_types(base, type_aliases),
421 a::TypeExpr::Named { name, args } if args.is_empty() => {
422 type_aliases.get(name).and_then(|t| record_field_types(t, type_aliases))
426 }
427 _ => None,
428 }
429}
430
431fn classify_type_expr(ty: &a::TypeExpr) -> NumTy {
432 match ty {
433 a::TypeExpr::Named { name, args } if args.is_empty() => match name.as_str() {
434 "Int" => NumTy::Int,
435 "Float" => NumTy::Float,
436 _ => NumTy::Unknown,
437 },
438 a::TypeExpr::Refined { base, .. } => classify_type_expr(base),
441 _ => NumTy::Unknown,
442 }
443}
444
445impl<'a> FnCompiler<'a> {
446 fn alloc_local(&mut self, name: &str) -> u16 {
447 let i = self.next_local;
448 self.locals.insert(name.into(), i);
449 self.next_local += 1;
450 if self.next_local > self.peak_local { self.peak_local = self.next_local; }
451 i
452 }
453 fn emit(&mut self, op: Op) { self.code.push(op); }
454
455 fn compile_expr(&mut self, e: &a::CExpr, tail: bool) {
456 match e {
457 a::CExpr::Literal { value } => self.compile_lit(value),
458 a::CExpr::Var { name } => {
459 if let Some(slot) = self.locals.get(name) {
460 self.emit(Op::LoadLocal(*slot));
461 } else if let Some(&fn_id) = self.function_names.get(name) {
462 self.emit(Op::MakeClosure { fn_id, capture_count: 0 });
468 } else {
469 panic!("unknown var in compiler: {name}");
473 }
474 }
475 a::CExpr::Let { name, ty, value, body } => {
476 let nty = match ty {
481 Some(t) => classify_type_expr(t),
482 None => self.classify_expr(value),
483 };
484 if let a::CExpr::RecordLit { fields } = value.as_ref() {
490 let mut ftypes = IndexMap::new();
491 for f in fields {
492 let fty = self.classify_expr(&f.value);
493 ftypes.insert(f.name.clone(), fty);
494 }
495 self.local_record_field_types.insert(name.clone(), ftypes);
496 }
497 self.compile_expr(value, false);
498 let slot = self.alloc_local(name);
499 self.local_types.insert(name.clone(), nty);
500 self.emit(Op::StoreLocal(slot));
501 self.compile_expr(body, tail);
502 }
503 a::CExpr::Block { statements, result } => {
504 for s in statements {
505 self.compile_expr(s, false);
506 self.emit(Op::Pop);
507 }
508 self.compile_expr(result, tail);
509 }
510 a::CExpr::Call { callee, args } => self.compile_call(e, callee, args, tail),
511 a::CExpr::Constructor { name, args } => {
512 for a in args { self.compile_expr(a, false); }
513 let name_idx = self.pool.variant(name);
514 self.emit(Op::MakeVariant { name_idx, arity: args.len() as u16 });
515 }
516 a::CExpr::Match { scrutinee, arms } => self.compile_match(scrutinee, arms, tail),
517 a::CExpr::RecordLit { fields } => {
518 let mut idxs = Vec::with_capacity(fields.len());
519 for f in fields {
520 self.compile_expr(&f.value, false);
521 idxs.push(self.pool.field(&f.name));
522 }
523 let field_count = idxs.len() as u16;
524 let shape_idx = self.pool.record_shape(idxs);
525 self.emit(Op::MakeRecord { shape_idx, field_count });
526 }
527 a::CExpr::TupleLit { items } => {
528 for it in items { self.compile_expr(it, false); }
529 self.emit(Op::MakeTuple(items.len() as u16));
530 }
531 a::CExpr::ListLit { items } => {
532 for it in items { self.compile_expr(it, false); }
533 self.emit(Op::MakeList(items.len() as u32));
534 }
535 a::CExpr::FieldAccess { value, field } => {
536 self.compile_expr(value, false);
537 let name_idx = self.pool.field(field);
538 let site_idx = self.field_get_sites;
539 self.field_get_sites += 1;
540 self.emit(Op::GetField { name_idx, site_idx });
541 }
542 a::CExpr::BinOp { op, lhs, rhs } => self.compile_binop(op, lhs, rhs),
543 a::CExpr::UnaryOp { op, expr } => {
544 self.compile_expr(expr, false);
545 match op.as_str() {
546 "-" => self.emit(Op::NumNeg),
547 "not" => self.emit(Op::BoolNot),
548 other => panic!("unknown unary: {other}"),
549 }
550 }
551 a::CExpr::Lambda { params, body, .. } => self.compile_lambda(params, body),
552 a::CExpr::Return { value } => {
553 self.compile_expr(value, true);
554 self.emit(Op::Return);
555 }
556 }
557 }
558
559 fn compile_lit(&mut self, l: &a::CLit) {
560 let i = match l {
561 a::CLit::Int { value } => self.pool.int(*value),
562 a::CLit::Bool { value } => self.pool.bool(*value),
563 a::CLit::Float { value } => {
564 let f: f64 = value.parse().unwrap_or(0.0);
565 self.pool.float(f)
566 }
567 a::CLit::Str { value } => self.pool.str(value),
568 a::CLit::Bytes { value: _ } => {
569 let i = self.pool.pool.len() as u32;
571 self.pool.pool.push(Const::Bytes(Vec::new()));
572 i
573 }
574 a::CLit::Unit => self.pool.unit(),
575 };
576 self.emit(Op::PushConst(i));
577 }
578
579 fn compile_call(&mut self, call_expr: &a::CExpr, callee: &a::CExpr, args: &[a::CExpr], tail: bool) {
580 let node_id = self
581 .id_map
582 .get(&(call_expr as *const a::CExpr))
583 .map(|n| n.as_str().to_string())
584 .unwrap_or_else(|| "n_?".into());
585 let node_id_idx = self.pool.node_id(&node_id);
586
587 if let a::CExpr::FieldAccess { value, field } = callee {
592 if let a::CExpr::Var { name } = value.as_ref() {
593 if let Some(module) = self.module_aliases.get(name) {
594 if self.try_emit_higher_order(module, field, args, node_id_idx) {
595 let _ = tail;
596 return;
597 }
598 for a in args { self.compile_expr(a, false); }
599 let kind_idx = self.pool.str(module);
600 let op_idx = self.pool.str(field);
601 self.emit(Op::EffectCall {
602 kind_idx,
603 op_idx,
604 arity: args.len() as u16,
605 node_id_idx,
606 });
607 let _ = tail;
608 return;
609 }
610 }
611 }
612 match callee {
613 a::CExpr::Var { name } if self.function_names.contains_key(name) => {
614 for a in args { self.compile_expr(a, false); }
615 let fn_id = self.function_names[name];
616 if tail {
617 self.emit(Op::TailCall { fn_id, arity: args.len() as u16, node_id_idx });
618 } else {
619 self.emit(Op::Call { fn_id, arity: args.len() as u16, node_id_idx });
620 }
621 }
622 a::CExpr::Var { name } if self.locals.contains_key(name) => {
623 let slot = self.locals[name];
626 self.emit(Op::LoadLocal(slot));
627 for a in args { self.compile_expr(a, false); }
628 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
629 }
630 other => {
632 self.compile_expr(other, false);
633 for a in args { self.compile_expr(a, false); }
634 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
635 }
636 }
637 }
638
639 fn compile_binop(&mut self, op: &str, lhs: &a::CExpr, rhs: &a::CExpr) {
640 let lhs_ty = self.classify_expr(lhs);
651 let rhs_ty = self.classify_expr(rhs);
652 let typed = match (lhs_ty, rhs_ty) {
653 (NumTy::Int, NumTy::Int) => NumTy::Int,
654 (NumTy::Float, NumTy::Float) => NumTy::Float,
655 _ => NumTy::Unknown,
656 };
657 self.compile_expr(lhs, false);
658 self.compile_expr(rhs, false);
659 match (op, typed) {
660 ("+", NumTy::Int) => self.emit(Op::IntAdd),
661 ("+", NumTy::Float) => self.emit(Op::FloatAdd),
662 ("+", NumTy::Unknown) => self.emit(Op::NumAdd),
663 ("-", NumTy::Int) => self.emit(Op::IntSub),
664 ("-", NumTy::Float) => self.emit(Op::FloatSub),
665 ("-", NumTy::Unknown) => self.emit(Op::NumSub),
666 ("*", NumTy::Int) => self.emit(Op::IntMul),
667 ("*", NumTy::Float) => self.emit(Op::FloatMul),
668 ("*", NumTy::Unknown) => self.emit(Op::NumMul),
669 ("/", NumTy::Int) => self.emit(Op::IntDiv),
670 ("/", NumTy::Float) => self.emit(Op::FloatDiv),
671 ("/", NumTy::Unknown) => self.emit(Op::NumDiv),
672 ("%", NumTy::Int) => self.emit(Op::IntMod),
674 ("%", _) => self.emit(Op::NumMod),
675 ("==", NumTy::Int) => self.emit(Op::IntEq),
676 ("==", NumTy::Float) => self.emit(Op::FloatEq),
677 ("==", NumTy::Unknown) => self.emit(Op::NumEq),
678 ("!=", NumTy::Int) => { self.emit(Op::IntEq); self.emit(Op::BoolNot); }
679 ("!=", NumTy::Float) => { self.emit(Op::FloatEq); self.emit(Op::BoolNot); }
680 ("!=", NumTy::Unknown) => { self.emit(Op::NumEq); self.emit(Op::BoolNot); }
681 ("<", NumTy::Int) => self.emit(Op::IntLt),
682 ("<", NumTy::Float) => self.emit(Op::FloatLt),
683 ("<", NumTy::Unknown) => self.emit(Op::NumLt),
684 ("<=", NumTy::Int) => self.emit(Op::IntLe),
685 ("<=", NumTy::Float) => self.emit(Op::FloatLe),
686 ("<=", NumTy::Unknown) => self.emit(Op::NumLe),
687 (">", NumTy::Int) => { self.emit_swap_top2(); self.emit(Op::IntLt); }
688 (">", NumTy::Float) => { self.emit_swap_top2(); self.emit(Op::FloatLt); }
689 (">", NumTy::Unknown) => { self.emit_swap_top2(); self.emit(Op::NumLt); }
690 (">=", NumTy::Int) => { self.emit_swap_top2(); self.emit(Op::IntLe); }
691 (">=", NumTy::Float) => { self.emit_swap_top2(); self.emit(Op::FloatLe); }
692 (">=", NumTy::Unknown) => { self.emit_swap_top2(); self.emit(Op::NumLe); }
693 ("and", _) => self.emit(Op::BoolAnd),
694 ("or", _) => self.emit(Op::BoolOr),
695 (other, _) => panic!("unknown binop: {other:?}"),
696 }
697 }
698
699 fn classify_expr(&self, e: &a::CExpr) -> NumTy {
707 match e {
708 a::CExpr::Literal { value: a::CLit::Int { .. } } => NumTy::Int,
709 a::CExpr::Literal { value: a::CLit::Float { .. } } => NumTy::Float,
710 a::CExpr::Var { name } =>
711 self.local_types.get(name).copied().unwrap_or(NumTy::Unknown),
712 a::CExpr::BinOp { op, lhs, rhs } => {
713 let is_numeric = matches!(op.as_str(), "+" | "-" | "*" | "/" | "%");
717 if !is_numeric { return NumTy::Unknown; }
718 match (self.classify_expr(lhs), self.classify_expr(rhs)) {
719 (NumTy::Int, NumTy::Int) => NumTy::Int,
720 (NumTy::Float, NumTy::Float) => NumTy::Float,
721 _ => NumTy::Unknown,
722 }
723 }
724 a::CExpr::UnaryOp { op, expr } if op == "-" => self.classify_expr(expr),
725 a::CExpr::FieldAccess { value, field } => {
732 if let a::CExpr::Var { name } = value.as_ref() {
733 if let Some(ftypes) = self.local_record_field_types.get(name) {
734 return ftypes.get(field).copied().unwrap_or(NumTy::Unknown);
735 }
736 }
737 NumTy::Unknown
738 }
739 _ => NumTy::Unknown,
743 }
744 }
745
746 fn emit_swap_top2(&mut self) {
747 let a = self.alloc_local("__swap_a");
748 let b = self.alloc_local("__swap_b");
749 self.emit(Op::StoreLocal(b));
750 self.emit(Op::StoreLocal(a));
751 self.emit(Op::LoadLocal(b));
752 self.emit(Op::LoadLocal(a));
753 }
754
755 fn compile_match(&mut self, scrutinee: &a::CExpr, arms: &[a::Arm], tail: bool) {
756 self.compile_expr(scrutinee, false);
757 let scrut_slot = self.alloc_local("__scrut");
758 self.emit(Op::StoreLocal(scrut_slot));
759
760 let mut end_jumps: Vec<usize> = Vec::new();
761 for arm in arms {
762 let arm_start_locals = self.next_local;
763 let arm_start_locals_map = self.locals.clone();
764
765 self.emit(Op::LoadLocal(scrut_slot));
766 let mut bindings: Vec<(String, u16)> = Vec::new();
767 let fail_jumps: Vec<usize> = self.compile_pattern_test(&arm.pattern, &mut bindings);
768
769 self.compile_expr(&arm.body, tail);
770 let j_end = self.code.len();
771 self.emit(Op::Jump(0));
772 end_jumps.push(j_end);
773
774 let fail_target = self.code.len() as i32;
775 for j in fail_jumps {
776 match &mut self.code[j] {
782 Op::JumpIfNot(off) => *off = fail_target - (j as i32 + 1),
783 Op::Jump(off) => *off = fail_target - (j as i32 + 1),
784 _ => {}
785 }
786 }
787 self.next_local = arm_start_locals;
788 self.locals = arm_start_locals_map;
789 }
790 let panic_msg_idx = self.pool.str("non-exhaustive match");
791 self.emit(Op::Panic(panic_msg_idx));
792
793 let end_target = self.code.len() as i32;
794 for j in end_jumps {
795 if let Op::Jump(off) = &mut self.code[j] {
796 *off = end_target - (j as i32 + 1);
797 }
798 }
799 }
800
801 fn compile_pattern_test(&mut self, p: &a::Pattern, bindings: &mut Vec<(String, u16)>) -> Vec<usize> {
802 let mut fails = Vec::new();
803 match p {
804 a::Pattern::PWild => { self.emit(Op::Pop); }
805 a::Pattern::PVar { name } => {
806 let slot = self.alloc_local(name);
807 self.emit(Op::StoreLocal(slot));
808 bindings.push((name.clone(), slot));
809 }
810 a::Pattern::PLiteral { value } => {
811 self.compile_lit(value);
812 match value {
813 a::CLit::Str { .. } => self.emit(Op::StrEq),
814 a::CLit::Bytes { .. } => self.emit(Op::BytesEq),
815 a::CLit::Int { .. } => self.emit(Op::IntEq),
826 a::CLit::Float { .. } => self.emit(Op::FloatEq),
827 _ => self.emit(Op::NumEq),
828 }
829 let j = self.code.len();
830 self.emit(Op::JumpIfNot(0));
831 fails.push(j);
832 }
833 a::Pattern::PConstructor { name, args } => {
834 let name_idx = self.pool.variant(name);
835 self.emit(Op::Dup); self.emit(Op::TestVariant(name_idx)); let j_success = self.code.len();
852 self.emit(Op::JumpIf(0)); self.emit(Op::Pop); let j_fail = self.code.len();
855 self.emit(Op::Jump(0)); fails.push(j_fail);
857 let success_target = self.code.len() as i32;
858 if let Op::JumpIf(off) = &mut self.code[j_success] {
859 *off = success_target - (j_success as i32 + 1);
860 }
861 if args.is_empty() {
862 self.emit(Op::Pop);
863 } else if args.len() == 1 {
864 self.emit(Op::GetVariantArg(0));
865 let sub_fails = self.compile_pattern_test(&args[0], bindings);
866 fails.extend(sub_fails);
867 } else {
868 let slot = self.alloc_local("__variant");
869 self.emit(Op::StoreLocal(slot));
870 for (i, arg) in args.iter().enumerate() {
871 self.emit(Op::LoadLocal(slot));
872 self.emit(Op::GetVariantArg(i as u16));
873 let sub_fails = self.compile_pattern_test(arg, bindings);
874 fails.extend(sub_fails);
875 }
876 }
877 }
878 a::Pattern::PRecord { fields } => {
879 let slot = self.alloc_local("__record");
880 self.emit(Op::StoreLocal(slot));
881 for f in fields {
882 self.emit(Op::LoadLocal(slot));
883 let name_idx = self.pool.field(&f.name);
884 let site_idx = self.field_get_sites;
885 self.field_get_sites += 1;
886 self.emit(Op::GetField { name_idx, site_idx });
887 let sub_fails = self.compile_pattern_test(&f.pattern, bindings);
888 fails.extend(sub_fails);
889 }
890 }
891 a::Pattern::PTuple { items } => {
892 let slot = self.alloc_local("__tuple");
893 self.emit(Op::StoreLocal(slot));
894 for (i, item) in items.iter().enumerate() {
895 self.emit(Op::LoadLocal(slot));
896 self.emit(Op::GetElem(i as u16));
897 let sub_fails = self.compile_pattern_test(item, bindings);
898 fails.extend(sub_fails);
899 }
900 }
901 }
902 fails
903 }
904
905 fn compile_lambda(&mut self, params: &[a::Param], body: &a::CExpr) {
909 let mut bound: std::collections::HashSet<String> = params.iter().map(|p| p.name.clone()).collect();
911 let mut frees: Vec<String> = Vec::new();
912 free_vars(body, &mut bound, &mut frees);
913
914 let captures: Vec<String> = frees.into_iter()
923 .filter(|n| self.locals.contains_key(n))
924 .collect();
925
926 let fn_id = self.next_fn_id.len() as u32;
928 self.next_fn_id.push(Function {
929 name: format!("__lambda_{fn_id}"),
930 arity: (captures.len() + params.len()) as u16,
931 locals_count: 0,
932 code: Vec::new(),
933 effects: Vec::new(),
934 body_hash: crate::program::ZERO_BODY_HASH,
936 refinements: Vec::new(),
941 field_ic_sites: 0,
944 });
945
946 for c in &captures {
948 let slot = *self.locals.get(c).expect("free var must be in scope");
949 self.emit(Op::LoadLocal(slot));
950 }
951 self.emit(Op::MakeClosure { fn_id, capture_count: captures.len() as u16 });
952
953 self.pending_lambdas.push(PendingLambda {
955 fn_id,
956 capture_names: captures,
957 params: params.to_vec(),
958 body: body.clone(),
959 });
960 }
961
962 fn try_emit_higher_order(
966 &mut self,
967 module: &str,
968 op: &str,
969 args: &[a::CExpr],
970 node_id_idx: u32,
971 ) -> bool {
972 match (module, op) {
973 ("result", "map") => self.emit_variant_map(args, "Ok", true),
974 ("result", "and_then") => self.emit_variant_map(args, "Ok", false),
975 ("result", "map_err") => self.emit_variant_map(args, "Err", true),
976 ("result", "or_else") => self.emit_variant_or_else(args, "Err", 1),
977 ("option", "map") => self.emit_variant_map(args, "Some", true),
978 ("option", "and_then") => self.emit_variant_map(args, "Some", false),
979 ("option", "or_else") => self.emit_variant_or_else(args, "None", 0),
980 ("option", "unwrap_or_else") => self.emit_option_unwrap_or_else(args),
981 ("result", "unwrap_or_else") => self.emit_result_unwrap_or_else(args),
982 ("list", "map") => self.emit_list_map(args),
983 ("list", "par_map") => self.emit_list_par_map(args),
984 ("list", "sort_by") => self.emit_list_sort_by(args),
985 ("list", "filter") => self.emit_list_filter(args),
986 ("list", "fold") => self.emit_list_fold(args),
987 ("iter", "from_list") => self.emit_iter_from_list(args),
988 ("iter", "unfold") => self.emit_iter_unfold(args),
989 ("iter", "next") => self.emit_iter_next(args),
990 ("iter", "is_empty") => self.emit_iter_is_empty(args),
991 ("iter", "count") => self.emit_iter_count(args),
992 ("iter", "take") => self.emit_iter_take(args),
993 ("iter", "skip") => self.emit_iter_skip(args),
994 ("iter", "to_list") => self.emit_iter_to_list(args),
995 ("iter", "collect") => self.emit_iter_to_list(args),
996 ("iter", "map") => self.emit_iter_map(args),
997 ("iter", "filter") => self.emit_iter_filter(args),
998 ("iter", "fold") => self.emit_iter_fold(args),
999 ("map", "fold") => self.emit_map_fold(args, node_id_idx),
1000 ("flow", "sequential") => self.emit_flow_sequential(args),
1001 ("flow", "branch") => self.emit_flow_branch(args),
1002 ("flow", "retry") => self.emit_flow_retry(args),
1003 ("flow", "retry_with_backoff") => self.emit_flow_retry_with_backoff(args),
1004 ("flow", "parallel") => self.emit_flow_parallel(args),
1005 ("flow", "parallel_list") => self.emit_flow_parallel_list(args),
1006 _ => return false,
1007 }
1008 true
1009 }
1010
1011 fn emit_list_map(&mut self, args: &[a::CExpr]) {
1017 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_map");
1020 self.emit(Op::ListMap { node_id_idx: nid });
1021 }
1022
1023 fn emit_list_par_map(&mut self, args: &[a::CExpr]) {
1029 self.compile_expr(&args[0], false);
1030 self.compile_expr(&args[1], false);
1031 let nid = self.pool.node_id("n_list_par_map");
1032 self.emit(Op::ParallelMap { node_id_idx: nid });
1033 }
1034
1035 fn emit_list_sort_by(&mut self, args: &[a::CExpr]) {
1043 self.compile_expr(&args[0], false);
1044 self.compile_expr(&args[1], false);
1045 let nid = self.pool.node_id("n_list_sort_by");
1046 self.emit(Op::SortByKey { node_id_idx: nid });
1047 }
1048
1049 fn emit_list_filter(&mut self, args: &[a::CExpr]) {
1052 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_filter");
1055 self.emit(Op::ListFilter { node_id_idx: nid });
1056 }
1057
1058 fn emit_list_fold(&mut self, args: &[a::CExpr]) {
1061 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); self.compile_expr(&args[2], false); let nid = self.pool.node_id("n_list_fold");
1065 self.emit(Op::ListFold { node_id_idx: nid });
1066 }
1067
1068 fn emit_iter_from_list(&mut self, args: &[a::CExpr]) {
1080 self.compile_expr(&args[0], false);
1081 let zero = self.pool.int(0);
1082 self.emit(Op::PushConst(zero));
1083 let v = self.pool.variant("__IterEager");
1084 self.emit(Op::MakeVariant { name_idx: v, arity: 2 });
1085 }
1086
1087 fn emit_iter_next(&mut self, args: &[a::CExpr]) {
1099 self.compile_expr(&args[0], false);
1100 let it = self.alloc_local("__in_it");
1101 self.emit(Op::StoreLocal(it));
1102
1103 self.emit(Op::LoadLocal(it));
1105 self.emit(Op::Dup);
1106 let lazy_name = self.pool.variant("__IterLazy");
1107 self.emit(Op::TestVariant(lazy_name));
1108 let j_to_check_cursor = self.code.len();
1109 self.emit(Op::JumpIfNot(0));
1110
1111 self.emit(Op::LoadLocal(it));
1115 self.emit(Op::GetVariantArg(0)); let seed = self.alloc_local("__in_seed");
1117 self.emit(Op::StoreLocal(seed));
1118
1119 self.emit(Op::LoadLocal(it));
1120 self.emit(Op::GetVariantArg(1)); let step = self.alloc_local("__in_step");
1122 self.emit(Op::StoreLocal(step));
1123
1124 let nid_lazy = self.pool.node_id("n_iter_next_lazy");
1126 self.emit(Op::LoadLocal(step));
1127 self.emit(Op::LoadLocal(seed));
1128 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
1129 let opt = self.alloc_local("__in_opt");
1130 self.emit(Op::StoreLocal(opt));
1131
1132 self.emit(Op::LoadLocal(opt));
1134 let some_name = self.pool.variant("Some");
1135 self.emit(Op::TestVariant(some_name));
1136 let j_lazy_none = self.code.len();
1137 self.emit(Op::JumpIfNot(0));
1138
1139 self.emit(Op::LoadLocal(opt));
1142 self.emit(Op::GetVariantArg(0)); let pair = self.alloc_local("__in_pair");
1144 self.emit(Op::StoreLocal(pair));
1145
1146 self.emit(Op::LoadLocal(pair));
1147 self.emit(Op::GetElem(0)); self.emit(Op::LoadLocal(pair));
1149 self.emit(Op::GetElem(1)); self.emit(Op::LoadLocal(step)); let lazy_v = self.pool.variant("__IterLazy");
1152 self.emit(Op::MakeVariant { name_idx: lazy_v, arity: 2 }); self.emit(Op::MakeTuple(2)); let some_v = self.pool.variant("Some");
1155 self.emit(Op::MakeVariant { name_idx: some_v, arity: 1 });
1156 let j_after_lazy = self.code.len();
1157 self.emit(Op::Jump(0));
1158
1159 let none_t = self.code.len() as i32;
1161 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_none] {
1162 *off = none_t - (j_lazy_none as i32 + 1);
1163 }
1164 let none_v = self.pool.variant("None");
1165 self.emit(Op::MakeVariant { name_idx: none_v, arity: 0 });
1166 let j_after_lazy_none = self.code.len();
1167 self.emit(Op::Jump(0));
1168
1169 let cursor_check_t = self.code.len() as i32;
1171 if let Op::JumpIfNot(off) = &mut self.code[j_to_check_cursor] {
1172 *off = cursor_check_t - (j_to_check_cursor as i32 + 1);
1173 }
1174
1175 self.emit(Op::LoadLocal(it));
1176 self.emit(Op::Dup);
1177 let cursor_name = self.pool.variant("__IterCursor");
1178 self.emit(Op::TestVariant(cursor_name));
1179 let j_to_eager = self.code.len();
1180 self.emit(Op::JumpIfNot(0));
1181
1182 self.emit(Op::LoadLocal(it));
1186 self.emit(Op::GetVariantArg(0)); let handle = self.alloc_local("__in_handle");
1188 self.emit(Op::StoreLocal(handle));
1189
1190 let kind_idx = self.pool.str("sql");
1191 let op_idx = self.pool.str("cursor_next");
1192 let nid_cursor = self.pool.node_id("n_iter_next_cursor");
1193 self.emit(Op::LoadLocal(handle));
1194 self.emit(Op::EffectCall {
1195 kind_idx,
1196 op_idx,
1197 arity: 1,
1198 node_id_idx: nid_cursor,
1199 });
1200 let cur_opt = self.alloc_local("__in_cur_opt");
1201 self.emit(Op::StoreLocal(cur_opt));
1202
1203 self.emit(Op::LoadLocal(cur_opt));
1204 let some_c = self.pool.variant("Some");
1205 self.emit(Op::TestVariant(some_c));
1206 let j_cursor_none = self.code.len();
1207 self.emit(Op::JumpIfNot(0));
1208
1209 self.emit(Op::LoadLocal(cur_opt));
1211 self.emit(Op::GetVariantArg(0)); self.emit(Op::LoadLocal(handle));
1213 let cursor_v = self.pool.variant("__IterCursor");
1214 self.emit(Op::MakeVariant { name_idx: cursor_v, arity: 1 });
1215 self.emit(Op::MakeTuple(2)); let some_c2 = self.pool.variant("Some");
1217 self.emit(Op::MakeVariant { name_idx: some_c2, arity: 1 });
1218 let j_after_cursor = self.code.len();
1219 self.emit(Op::Jump(0));
1220
1221 let cursor_none_t = self.code.len() as i32;
1223 if let Op::JumpIfNot(off) = &mut self.code[j_cursor_none] {
1224 *off = cursor_none_t - (j_cursor_none as i32 + 1);
1225 }
1226 let none_c = self.pool.variant("None");
1227 self.emit(Op::MakeVariant { name_idx: none_c, arity: 0 });
1228 let j_after_cursor_none = self.code.len();
1229 self.emit(Op::Jump(0));
1230
1231 let eager_t = self.code.len() as i32;
1233 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
1234 *off = eager_t - (j_to_eager as i32 + 1);
1235 }
1236
1237 self.emit(Op::LoadLocal(it));
1238 self.emit(Op::GetVariantArg(0));
1239 let list = self.alloc_local("__in_list");
1240 self.emit(Op::StoreLocal(list));
1241
1242 self.emit(Op::LoadLocal(it));
1243 self.emit(Op::GetVariantArg(1));
1244 let idx = self.alloc_local("__in_idx");
1245 self.emit(Op::StoreLocal(idx));
1246
1247 self.emit(Op::LoadLocal(idx));
1249 self.emit(Op::LoadLocal(list));
1250 self.emit(Op::GetListLen);
1251 self.emit(Op::IntLt);
1252 let j_eager_else = self.code.len();
1253 self.emit(Op::JumpIfNot(0));
1254
1255 self.emit(Op::LoadLocal(list));
1257 self.emit(Op::LoadLocal(idx));
1258 self.emit(Op::GetListElemDyn);
1259
1260 self.emit(Op::LoadLocal(list));
1261 self.emit(Op::LoadLocal(idx));
1262 let one = self.pool.int(1);
1263 self.emit(Op::PushConst(one));
1264 self.emit(Op::IntAdd);
1265 let eager_v = self.pool.variant("__IterEager");
1266 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1267 self.emit(Op::MakeTuple(2));
1268 let some_e = self.pool.variant("Some");
1269 self.emit(Op::MakeVariant { name_idx: some_e, arity: 1 });
1270 let j_after_eager = self.code.len();
1271 self.emit(Op::Jump(0));
1272
1273 let eager_none_t = self.code.len() as i32;
1275 if let Op::JumpIfNot(off) = &mut self.code[j_eager_else] {
1276 *off = eager_none_t - (j_eager_else as i32 + 1);
1277 }
1278 let none_e = self.pool.variant("None");
1279 self.emit(Op::MakeVariant { name_idx: none_e, arity: 0 });
1280
1281 let end = self.code.len() as i32;
1283 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
1284 *off = end - (j_after_lazy as i32 + 1);
1285 }
1286 if let Op::Jump(off) = &mut self.code[j_after_lazy_none] {
1287 *off = end - (j_after_lazy_none as i32 + 1);
1288 }
1289 if let Op::Jump(off) = &mut self.code[j_after_cursor] {
1290 *off = end - (j_after_cursor as i32 + 1);
1291 }
1292 if let Op::Jump(off) = &mut self.code[j_after_cursor_none] {
1293 *off = end - (j_after_cursor_none as i32 + 1);
1294 }
1295 if let Op::Jump(off) = &mut self.code[j_after_eager] {
1296 *off = end - (j_after_eager as i32 + 1);
1297 }
1298 }
1299
1300 fn emit_iter_unfold(&mut self, args: &[a::CExpr]) {
1305 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let lazy = self.pool.variant("__IterLazy");
1308 self.emit(Op::MakeVariant { name_idx: lazy, arity: 2 });
1309 }
1310
1311 fn emit_iter_is_empty(&mut self, args: &[a::CExpr]) {
1317 self.compile_expr(&args[0], false);
1318 let it = self.alloc_local("__ie_it");
1319 self.emit(Op::StoreLocal(it));
1320
1321 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1)); self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0)); self.emit(Op::GetListLen); self.emit(Op::IntLt); self.emit(Op::BoolNot); }
1327
1328 fn emit_iter_count(&mut self, args: &[a::CExpr]) {
1330 self.compile_expr(&args[0], false);
1331 let it = self.alloc_local("__ic_it");
1332 self.emit(Op::StoreLocal(it));
1333
1334 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1335 self.emit(Op::GetListLen); self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1)); self.emit(Op::IntSub); }
1339
1340 fn emit_iter_take(&mut self, args: &[a::CExpr]) {
1342 self.compile_expr(&args[0], false);
1343 let it = self.alloc_local("__itk_it");
1344 self.emit(Op::StoreLocal(it));
1345
1346 self.compile_expr(&args[1], false);
1347 let n = self.alloc_local("__itk_n");
1348 self.emit(Op::StoreLocal(n));
1349
1350 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1351 let list = self.alloc_local("__itk_list");
1352 self.emit(Op::StoreLocal(list));
1353
1354 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1355 let i = self.alloc_local("__itk_i");
1356 self.emit(Op::StoreLocal(i));
1357
1358 self.emit(Op::MakeList(0));
1359 let out = self.alloc_local("__itk_out");
1360 self.emit(Op::StoreLocal(out));
1361
1362 let zero = self.pool.int(0);
1363 self.emit(Op::PushConst(zero));
1364 let cnt = self.alloc_local("__itk_cnt");
1365 self.emit(Op::StoreLocal(cnt));
1366
1367 let loop_top = self.code.len();
1368
1369 self.emit(Op::LoadLocal(cnt));
1371 self.emit(Op::LoadLocal(n));
1372 self.emit(Op::IntLt);
1373 let j_exit_n = self.code.len();
1374 self.emit(Op::JumpIfNot(0));
1375
1376 self.emit(Op::LoadLocal(i));
1378 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1379 self.emit(Op::IntLt);
1380 let j_exit_l = self.code.len();
1381 self.emit(Op::JumpIfNot(0));
1382
1383 self.emit(Op::LoadLocal(out));
1385 self.emit(Op::LoadLocal(list));
1386 self.emit(Op::LoadLocal(i));
1387 self.emit(Op::GetListElemDyn);
1388 self.emit(Op::ListAppend);
1389 self.emit(Op::StoreLocal(out));
1390
1391 let one = self.pool.int(1);
1392 self.emit(Op::LoadLocal(i));
1394 self.emit(Op::PushConst(one));
1395 self.emit(Op::IntAdd);
1396 self.emit(Op::StoreLocal(i));
1397 self.emit(Op::LoadLocal(cnt));
1399 self.emit(Op::PushConst(one));
1400 self.emit(Op::IntAdd);
1401 self.emit(Op::StoreLocal(cnt));
1402
1403 let jback = self.code.len();
1404 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1405
1406 let exit_t = self.code.len() as i32;
1407 if let Op::JumpIfNot(off) = &mut self.code[j_exit_n] { *off = exit_t - (j_exit_n as i32 + 1); }
1408 if let Op::JumpIfNot(off) = &mut self.code[j_exit_l] { *off = exit_t - (j_exit_l as i32 + 1); }
1409
1410 self.emit(Op::LoadLocal(out));
1412 self.emit(Op::PushConst(zero));
1413 let eager_v = self.pool.variant("__IterEager");
1414 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1415 }
1416
1417 fn emit_iter_skip(&mut self, args: &[a::CExpr]) {
1419 self.compile_expr(&args[0], false);
1420 let it = self.alloc_local("__isk_it");
1421 self.emit(Op::StoreLocal(it));
1422
1423 self.compile_expr(&args[1], false);
1424 let n = self.alloc_local("__isk_n");
1425 self.emit(Op::StoreLocal(n));
1426
1427 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1428 let list = self.alloc_local("__isk_list");
1429 self.emit(Op::StoreLocal(list));
1430
1431 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1432 let idx = self.alloc_local("__isk_idx");
1433 self.emit(Op::StoreLocal(idx));
1434
1435 self.emit(Op::LoadLocal(idx));
1437 self.emit(Op::LoadLocal(n));
1438 self.emit(Op::IntAdd);
1439 let raw = self.alloc_local("__isk_raw");
1440 self.emit(Op::StoreLocal(raw));
1441
1442 self.emit(Op::LoadLocal(raw));
1444 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1445 self.emit(Op::IntLt);
1446 let j_use_raw = self.code.len();
1447 self.emit(Op::JumpIf(0));
1448
1449 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1451 let j_end = self.code.len();
1452 self.emit(Op::Jump(0));
1453
1454 let raw_t = self.code.len() as i32;
1456 if let Op::JumpIf(off) = &mut self.code[j_use_raw] { *off = raw_t - (j_use_raw as i32 + 1); }
1457 self.emit(Op::LoadLocal(raw));
1458
1459 let end_t = self.code.len() as i32;
1460 if let Op::Jump(off) = &mut self.code[j_end] { *off = end_t - (j_end as i32 + 1); }
1461
1462 let new_idx = self.alloc_local("__isk_ni");
1464 self.emit(Op::StoreLocal(new_idx));
1465 self.emit(Op::LoadLocal(list));
1466 self.emit(Op::LoadLocal(new_idx));
1467 let eager_v = self.pool.variant("__IterEager");
1468 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1469 }
1470
1471 fn emit_iter_to_list(&mut self, args: &[a::CExpr]) {
1480 self.compile_expr(&args[0], false);
1481 let it = self.alloc_local("__itl_it");
1482 self.emit(Op::StoreLocal(it));
1483
1484 self.emit(Op::MakeList(0));
1486 let out = self.alloc_local("__itl_out");
1487 self.emit(Op::StoreLocal(out));
1488
1489 self.emit(Op::LoadLocal(it));
1491 let lazy_name = self.pool.variant("__IterLazy");
1492 self.emit(Op::TestVariant(lazy_name));
1493 let j_to_eager = self.code.len();
1494 self.emit(Op::JumpIfNot(0));
1495
1496 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1499 let seed = self.alloc_local("__itl_seed");
1500 self.emit(Op::StoreLocal(seed));
1501
1502 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1503 let step = self.alloc_local("__itl_step");
1504 self.emit(Op::StoreLocal(step));
1505
1506 let lazy_loop = self.code.len();
1507 let nid_lazy = self.pool.node_id("n_iter_to_list_lazy");
1508 self.emit(Op::LoadLocal(step));
1509 self.emit(Op::LoadLocal(seed));
1510 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
1511 let opt = self.alloc_local("__itl_opt");
1512 self.emit(Op::StoreLocal(opt));
1513
1514 self.emit(Op::LoadLocal(opt));
1516 let some_name = self.pool.variant("Some");
1517 self.emit(Op::TestVariant(some_name));
1518 let j_lazy_exit = self.code.len();
1519 self.emit(Op::JumpIfNot(0));
1520
1521 self.emit(Op::LoadLocal(opt));
1523 self.emit(Op::GetVariantArg(0));
1524 let pair = self.alloc_local("__itl_pair");
1525 self.emit(Op::StoreLocal(pair));
1526
1527 self.emit(Op::LoadLocal(out));
1528 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(0));
1529 self.emit(Op::ListAppend);
1530 self.emit(Op::StoreLocal(out));
1531
1532 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(1));
1533 self.emit(Op::StoreLocal(seed));
1534
1535 let jback_lazy = self.code.len();
1536 self.emit(Op::Jump((lazy_loop as i32) - (jback_lazy as i32 + 1)));
1537
1538 let lazy_exit_t = self.code.len() as i32;
1539 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_exit] {
1540 *off = lazy_exit_t - (j_lazy_exit as i32 + 1);
1541 }
1542 let j_after_lazy = self.code.len();
1543 self.emit(Op::Jump(0));
1544
1545 let eager_t = self.code.len() as i32;
1547 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
1548 *off = eager_t - (j_to_eager as i32 + 1);
1549 }
1550
1551 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1552 let list = self.alloc_local("__itl_list");
1553 self.emit(Op::StoreLocal(list));
1554
1555 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1556 let i = self.alloc_local("__itl_i");
1557 self.emit(Op::StoreLocal(i));
1558
1559 let loop_top = self.code.len();
1560 self.emit(Op::LoadLocal(i));
1561 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1562 self.emit(Op::IntLt);
1563 let j_exit = self.code.len();
1564 self.emit(Op::JumpIfNot(0));
1565
1566 self.emit(Op::LoadLocal(out));
1567 self.emit(Op::LoadLocal(list));
1568 self.emit(Op::LoadLocal(i));
1569 self.emit(Op::GetListElemDyn);
1570 self.emit(Op::ListAppend);
1571 self.emit(Op::StoreLocal(out));
1572
1573 self.emit(Op::LoadLocal(i));
1574 let one = self.pool.int(1);
1575 self.emit(Op::PushConst(one));
1576 self.emit(Op::IntAdd);
1577 self.emit(Op::StoreLocal(i));
1578
1579 let jback = self.code.len();
1580 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1581
1582 let exit_t = self.code.len() as i32;
1583 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
1584 *off = exit_t - (j_exit as i32 + 1);
1585 }
1586
1587 let converge = self.code.len() as i32;
1589 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
1590 *off = converge - (j_after_lazy as i32 + 1);
1591 }
1592 self.emit(Op::LoadLocal(out));
1593 }
1594
1595 fn emit_iter_map(&mut self, args: &[a::CExpr]) {
1597 self.compile_expr(&args[0], false);
1598 let it = self.alloc_local("__im_it");
1599 self.emit(Op::StoreLocal(it));
1600
1601 self.compile_expr(&args[1], false);
1602 let f = self.alloc_local("__im_f");
1603 self.emit(Op::StoreLocal(f));
1604
1605 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1606 let list = self.alloc_local("__im_list");
1607 self.emit(Op::StoreLocal(list));
1608
1609 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1610 let i = self.alloc_local("__im_i");
1611 self.emit(Op::StoreLocal(i));
1612
1613 self.emit(Op::MakeList(0));
1614 let out = self.alloc_local("__im_out");
1615 self.emit(Op::StoreLocal(out));
1616
1617 let loop_top = self.code.len();
1618 self.emit(Op::LoadLocal(i));
1619 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1620 self.emit(Op::IntLt);
1621 let j_exit = self.code.len();
1622 self.emit(Op::JumpIfNot(0));
1623
1624 let nid = self.pool.node_id("n_iter_map");
1625 self.emit(Op::LoadLocal(out));
1626 self.emit(Op::LoadLocal(f));
1627 self.emit(Op::LoadLocal(list));
1628 self.emit(Op::LoadLocal(i));
1629 self.emit(Op::GetListElemDyn);
1630 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
1631 self.emit(Op::ListAppend);
1632 self.emit(Op::StoreLocal(out));
1633
1634 self.emit(Op::LoadLocal(i));
1635 let one = self.pool.int(1);
1636 self.emit(Op::PushConst(one));
1637 self.emit(Op::IntAdd);
1638 self.emit(Op::StoreLocal(i));
1639
1640 let jback = self.code.len();
1641 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1642
1643 let exit_t = self.code.len() as i32;
1644 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
1645
1646 let zero = self.pool.int(0);
1647 self.emit(Op::LoadLocal(out));
1648 self.emit(Op::PushConst(zero));
1649 let eager_v = self.pool.variant("__IterEager");
1650 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1651 }
1652
1653 fn emit_iter_filter(&mut self, args: &[a::CExpr]) {
1655 self.compile_expr(&args[0], false);
1656 let it = self.alloc_local("__if_it");
1657 self.emit(Op::StoreLocal(it));
1658
1659 self.compile_expr(&args[1], false);
1660 let f = self.alloc_local("__if_f");
1661 self.emit(Op::StoreLocal(f));
1662
1663 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1664 let list = self.alloc_local("__if_list");
1665 self.emit(Op::StoreLocal(list));
1666
1667 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1668 let i = self.alloc_local("__if_i");
1669 self.emit(Op::StoreLocal(i));
1670
1671 self.emit(Op::MakeList(0));
1672 let out = self.alloc_local("__if_out");
1673 self.emit(Op::StoreLocal(out));
1674
1675 let loop_top = self.code.len();
1676 self.emit(Op::LoadLocal(i));
1677 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1678 self.emit(Op::IntLt);
1679 let j_exit = self.code.len();
1680 self.emit(Op::JumpIfNot(0));
1681
1682 self.emit(Op::LoadLocal(list));
1684 self.emit(Op::LoadLocal(i));
1685 self.emit(Op::GetListElemDyn);
1686 let x = self.alloc_local("__if_x");
1687 self.emit(Op::StoreLocal(x));
1688
1689 let nid = self.pool.node_id("n_iter_filter");
1690 self.emit(Op::LoadLocal(f));
1691 self.emit(Op::LoadLocal(x));
1692 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
1693 let j_skip = self.code.len();
1694 self.emit(Op::JumpIfNot(0));
1695
1696 self.emit(Op::LoadLocal(out));
1697 self.emit(Op::LoadLocal(x));
1698 self.emit(Op::ListAppend);
1699 self.emit(Op::StoreLocal(out));
1700
1701 let skip_t = self.code.len() as i32;
1702 if let Op::JumpIfNot(off) = &mut self.code[j_skip] { *off = skip_t - (j_skip as i32 + 1); }
1703
1704 self.emit(Op::LoadLocal(i));
1705 let one = self.pool.int(1);
1706 self.emit(Op::PushConst(one));
1707 self.emit(Op::IntAdd);
1708 self.emit(Op::StoreLocal(i));
1709
1710 let jback = self.code.len();
1711 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1712
1713 let exit_t = self.code.len() as i32;
1714 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
1715
1716 let zero = self.pool.int(0);
1717 self.emit(Op::LoadLocal(out));
1718 self.emit(Op::PushConst(zero));
1719 let eager_v = self.pool.variant("__IterEager");
1720 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1721 }
1722
1723 fn emit_iter_fold(&mut self, args: &[a::CExpr]) {
1725 self.compile_expr(&args[0], false);
1726 let it = self.alloc_local("__ifo_it");
1727 self.emit(Op::StoreLocal(it));
1728
1729 self.compile_expr(&args[1], false);
1730 let acc = self.alloc_local("__ifo_acc");
1731 self.emit(Op::StoreLocal(acc));
1732
1733 self.compile_expr(&args[2], false);
1734 let f = self.alloc_local("__ifo_f");
1735 self.emit(Op::StoreLocal(f));
1736
1737 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1738 let list = self.alloc_local("__ifo_list");
1739 self.emit(Op::StoreLocal(list));
1740
1741 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1742 let i = self.alloc_local("__ifo_i");
1743 self.emit(Op::StoreLocal(i));
1744
1745 let loop_top = self.code.len();
1746 self.emit(Op::LoadLocal(i));
1747 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1748 self.emit(Op::IntLt);
1749 let j_exit = self.code.len();
1750 self.emit(Op::JumpIfNot(0));
1751
1752 let nid = self.pool.node_id("n_iter_fold");
1753 self.emit(Op::LoadLocal(f));
1754 self.emit(Op::LoadLocal(acc));
1755 self.emit(Op::LoadLocal(list));
1756 self.emit(Op::LoadLocal(i));
1757 self.emit(Op::GetListElemDyn);
1758 self.emit(Op::CallClosure { arity: 2, node_id_idx: nid });
1759 self.emit(Op::StoreLocal(acc));
1760
1761 self.emit(Op::LoadLocal(i));
1762 let one = self.pool.int(1);
1763 self.emit(Op::PushConst(one));
1764 self.emit(Op::IntAdd);
1765 self.emit(Op::StoreLocal(i));
1766
1767 let jback = self.code.len();
1768 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1769
1770 let exit_t = self.code.len() as i32;
1771 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
1772 self.emit(Op::LoadLocal(acc));
1773 }
1774
1775 fn emit_map_fold(&mut self, args: &[a::CExpr], node_id_idx: u32) {
1781 self.compile_expr(&args[0], false);
1783 let map_kind = self.pool.str("map");
1784 let entries_op = self.pool.str("entries");
1785 self.emit(Op::EffectCall {
1786 kind_idx: map_kind,
1787 op_idx: entries_op,
1788 arity: 1,
1789 node_id_idx,
1790 });
1791 let xs = self.alloc_local("__mf_xs");
1792 self.emit(Op::StoreLocal(xs));
1793
1794 self.compile_expr(&args[1], false);
1796 let acc = self.alloc_local("__mf_acc");
1797 self.emit(Op::StoreLocal(acc));
1798
1799 self.compile_expr(&args[2], false);
1801 let f = self.alloc_local("__mf_f");
1802 self.emit(Op::StoreLocal(f));
1803
1804 let zero = self.pool.int(0);
1806 self.emit(Op::PushConst(zero));
1807 let i = self.alloc_local("__mf_i");
1808 self.emit(Op::StoreLocal(i));
1809
1810 let loop_top = self.code.len();
1812 self.emit(Op::LoadLocal(i));
1813 self.emit(Op::LoadLocal(xs));
1814 self.emit(Op::GetListLen);
1815 self.emit(Op::IntLt);
1816 let j_exit = self.code.len();
1817 self.emit(Op::JumpIfNot(0));
1818
1819 self.emit(Op::LoadLocal(xs));
1821 self.emit(Op::LoadLocal(i));
1822 self.emit(Op::GetListElemDyn);
1823 let pair = self.alloc_local("__mf_pair");
1824 self.emit(Op::StoreLocal(pair));
1825
1826 let nid = self.pool.node_id("n_map_fold");
1828 self.emit(Op::LoadLocal(f));
1829 self.emit(Op::LoadLocal(acc));
1830 self.emit(Op::LoadLocal(pair));
1831 self.emit(Op::GetElem(0));
1832 self.emit(Op::LoadLocal(pair));
1833 self.emit(Op::GetElem(1));
1834 self.emit(Op::CallClosure { arity: 3, node_id_idx: nid });
1835 self.emit(Op::StoreLocal(acc));
1836
1837 self.emit(Op::LoadLocal(i));
1839 let one = self.pool.int(1);
1840 self.emit(Op::PushConst(one));
1841 self.emit(Op::IntAdd);
1842 self.emit(Op::StoreLocal(i));
1843
1844 let jump_back = self.code.len();
1845 let back = (loop_top as i32) - (jump_back as i32 + 1);
1846 self.emit(Op::Jump(back));
1847
1848 let exit_target = self.code.len() as i32;
1849 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
1850 *off = exit_target - (j_exit as i32 + 1);
1851 }
1852 self.emit(Op::LoadLocal(acc));
1853 }
1854
1855 fn emit_variant_map(
1861 &mut self,
1862 args: &[a::CExpr],
1863 wrap_with: &str,
1864 wrap_result: bool,
1865 ) {
1866 let wrap_idx = self.pool.variant(wrap_with);
1868
1869 self.compile_expr(&args[0], false);
1871 let val_slot = self.alloc_local("__hov");
1872 self.emit(Op::StoreLocal(val_slot));
1873
1874 self.compile_expr(&args[1], false);
1875 let f_slot = self.alloc_local("__hof");
1876 self.emit(Op::StoreLocal(f_slot));
1877
1878 self.emit(Op::LoadLocal(val_slot));
1885 self.emit(Op::Dup);
1886 self.emit(Op::TestVariant(wrap_idx));
1887 let j_skip = self.code.len();
1888 self.emit(Op::JumpIfNot(0));
1889
1890 self.emit(Op::GetVariantArg(0));
1892 let arg_slot = self.alloc_local("__hov_arg");
1893 self.emit(Op::StoreLocal(arg_slot));
1894 self.emit(Op::LoadLocal(f_slot));
1895 self.emit(Op::LoadLocal(arg_slot));
1896 let nid = self.pool.node_id("n_hov");
1897 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
1898 if wrap_result {
1899 self.emit(Op::MakeVariant { name_idx: wrap_idx, arity: 1 });
1900 }
1901 let j_end = self.code.len();
1902 self.emit(Op::Jump(0));
1903
1904 let skip_target = self.code.len() as i32;
1906 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
1907 *off = skip_target - (j_skip as i32 + 1);
1908 }
1909
1910 let end_target = self.code.len() as i32;
1911 if let Op::Jump(off) = &mut self.code[j_end] {
1912 *off = end_target - (j_end as i32 + 1);
1913 }
1914 }
1915
1916 fn emit_variant_or_else(
1925 &mut self,
1926 args: &[a::CExpr],
1927 match_on: &str,
1928 closure_arity: u16,
1929 ) {
1930 let match_idx = self.pool.variant(match_on);
1931
1932 self.compile_expr(&args[0], false);
1933 let val_slot = self.alloc_local("__hoe");
1934 self.emit(Op::StoreLocal(val_slot));
1935
1936 self.compile_expr(&args[1], false);
1937 let f_slot = self.alloc_local("__hoe_f");
1938 self.emit(Op::StoreLocal(f_slot));
1939
1940 self.emit(Op::LoadLocal(val_slot));
1948 self.emit(Op::Dup);
1949 self.emit(Op::TestVariant(match_idx));
1950 let j_skip = self.code.len();
1951 self.emit(Op::JumpIfNot(0));
1952
1953 self.emit(Op::Pop);
1956 self.emit(Op::LoadLocal(f_slot));
1957 if closure_arity == 1 {
1958 self.emit(Op::LoadLocal(val_slot));
1959 self.emit(Op::GetVariantArg(0));
1960 }
1961 let nid = self.pool.node_id("n_hoe");
1962 self.emit(Op::CallClosure { arity: closure_arity, node_id_idx: nid });
1963
1964 let j_end = self.code.len();
1965 self.emit(Op::Jump(0));
1966
1967 let skip_target = self.code.len() as i32;
1969 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
1970 *off = skip_target - (j_skip as i32 + 1);
1971 }
1972
1973 let end_target = self.code.len() as i32;
1974 if let Op::Jump(off) = &mut self.code[j_end] {
1975 *off = end_target - (j_end as i32 + 1);
1976 }
1977 }
1978
1979 fn emit_option_unwrap_or_else(&mut self, args: &[a::CExpr]) {
1983 let some_idx = self.pool.variant("Some");
1984
1985 self.compile_expr(&args[0], false);
1987 let val_slot = self.alloc_local("__uoe_val");
1988 self.emit(Op::StoreLocal(val_slot));
1989
1990 self.compile_expr(&args[1], false);
1991 let f_slot = self.alloc_local("__uoe_f");
1992 self.emit(Op::StoreLocal(f_slot));
1993
1994 self.emit(Op::LoadLocal(val_slot));
2000 self.emit(Op::Dup);
2001 self.emit(Op::TestVariant(some_idx));
2002 let j_none = self.code.len();
2003 self.emit(Op::JumpIfNot(0));
2004
2005 self.emit(Op::GetVariantArg(0));
2007 let j_end = self.code.len();
2008 self.emit(Op::Jump(0));
2009
2010 let none_target = self.code.len() as i32;
2012 if let Op::JumpIfNot(off) = &mut self.code[j_none] {
2013 *off = none_target - (j_none as i32 + 1);
2014 }
2015 self.emit(Op::Pop);
2016 self.emit(Op::LoadLocal(f_slot));
2017 let nid = self.pool.node_id("n_uoe");
2018 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2019
2020 let end_target = self.code.len() as i32;
2022 if let Op::Jump(off) = &mut self.code[j_end] {
2023 *off = end_target - (j_end as i32 + 1);
2024 }
2025 }
2026
2027 fn emit_result_unwrap_or_else(&mut self, args: &[a::CExpr]) {
2033 let ok_idx = self.pool.variant("Ok");
2034
2035 self.compile_expr(&args[0], false);
2036 let val_slot = self.alloc_local("__ruoe_val");
2037 self.emit(Op::StoreLocal(val_slot));
2038
2039 self.compile_expr(&args[1], false);
2040 let f_slot = self.alloc_local("__ruoe_f");
2041 self.emit(Op::StoreLocal(f_slot));
2042
2043 self.emit(Op::LoadLocal(val_slot));
2049 self.emit(Op::Dup);
2050 self.emit(Op::TestVariant(ok_idx));
2051 let j_err = self.code.len();
2052 self.emit(Op::JumpIfNot(0));
2053
2054 self.emit(Op::GetVariantArg(0));
2056 let j_end = self.code.len();
2057 self.emit(Op::Jump(0));
2058
2059 let err_target = self.code.len() as i32;
2061 if let Op::JumpIfNot(off) = &mut self.code[j_err] {
2062 *off = err_target - (j_err as i32 + 1);
2063 }
2064 self.emit(Op::Pop);
2065 self.emit(Op::LoadLocal(f_slot));
2066 self.emit(Op::LoadLocal(val_slot));
2067 self.emit(Op::GetVariantArg(0));
2068 let nid = self.pool.node_id("n_ruoe");
2069 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2070
2071 let end_target = self.code.len() as i32;
2073 if let Op::Jump(off) = &mut self.code[j_end] {
2074 *off = end_target - (j_end as i32 + 1);
2075 }
2076 }
2077
2078 fn install_trampoline(&mut self, name: &str, arity: u16, locals_count: u16, code: Vec<Op>) -> u32 {
2095 let fn_id = self.next_fn_id.len() as u32;
2096 let body_hash = crate::program::compute_body_hash(
2097 arity, locals_count, &code, &self.pool.record_shapes);
2098 self.next_fn_id.push(Function {
2099 name: name.into(),
2100 arity,
2101 locals_count,
2102 code,
2103 effects: Vec::new(),
2104 body_hash,
2105 refinements: Vec::new(),
2108 field_ic_sites: 0,
2112 });
2113 fn_id
2114 }
2115
2116 fn emit_flow_sequential(&mut self, args: &[a::CExpr]) {
2118 self.compile_expr(&args[0], false);
2120 self.compile_expr(&args[1], false);
2121 let nid = self.pool.node_id("n_flow_sequential");
2122 let code = vec![
2123 Op::LoadLocal(0), Op::LoadLocal(2), Op::CallClosure { arity: 1, node_id_idx: nid }, Op::StoreLocal(3), Op::LoadLocal(1), Op::LoadLocal(3), Op::CallClosure { arity: 1, node_id_idx: nid }, Op::Return,
2133 ];
2134 let fn_id = self.install_trampoline("__flow_sequential", 3, 4, code);
2135 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2136 }
2137
2138 fn emit_flow_parallel(&mut self, args: &[a::CExpr]) {
2146 self.compile_expr(&args[0], false);
2148 self.compile_expr(&args[1], false);
2149 let nid = self.pool.node_id("n_flow_parallel");
2150 let code = vec![
2151 Op::LoadLocal(0), Op::CallClosure { arity: 0, node_id_idx: nid }, Op::LoadLocal(1), Op::CallClosure { arity: 0, node_id_idx: nid }, Op::MakeTuple(2), Op::Return,
2158 ];
2159 let fn_id = self.install_trampoline("__flow_parallel", 2, 2, code);
2160 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2161 }
2162
2163 fn emit_flow_parallel_list(&mut self, args: &[a::CExpr]) {
2170 self.compile_expr(&args[0], false);
2172 let xs = self.alloc_local("__fpl_xs");
2173 self.emit(Op::StoreLocal(xs));
2174
2175 self.emit(Op::MakeList(0));
2177 let out = self.alloc_local("__fpl_out");
2178 self.emit(Op::StoreLocal(out));
2179
2180 let zero = self.pool.int(0);
2182 self.emit(Op::PushConst(zero));
2183 let i = self.alloc_local("__fpl_i");
2184 self.emit(Op::StoreLocal(i));
2185
2186 let loop_top = self.code.len();
2188 self.emit(Op::LoadLocal(i));
2189 self.emit(Op::LoadLocal(xs));
2190 self.emit(Op::GetListLen);
2191 self.emit(Op::IntLt);
2192 let j_exit = self.code.len();
2193 self.emit(Op::JumpIfNot(0));
2194
2195 let nid = self.pool.node_id("n_flow_parallel_list");
2197 self.emit(Op::LoadLocal(out));
2198 self.emit(Op::LoadLocal(xs));
2199 self.emit(Op::LoadLocal(i));
2200 self.emit(Op::GetListElemDyn);
2201 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2202 self.emit(Op::ListAppend);
2203 self.emit(Op::StoreLocal(out));
2204
2205 self.emit(Op::LoadLocal(i));
2207 let one = self.pool.int(1);
2208 self.emit(Op::PushConst(one));
2209 self.emit(Op::IntAdd);
2210 self.emit(Op::StoreLocal(i));
2211
2212 let jump_back = self.code.len();
2214 let back = (loop_top as i32) - (jump_back as i32 + 1);
2215 self.emit(Op::Jump(back));
2216
2217 let exit_target = self.code.len() as i32;
2219 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2220 *off = exit_target - (j_exit as i32 + 1);
2221 }
2222 self.emit(Op::LoadLocal(out));
2223 }
2224
2225 fn emit_flow_branch(&mut self, args: &[a::CExpr]) {
2227 self.compile_expr(&args[0], false);
2228 self.compile_expr(&args[1], false);
2229 self.compile_expr(&args[2], false);
2230 let nid = self.pool.node_id("n_flow_branch");
2231 let mut code = vec![
2232 Op::LoadLocal(0), Op::LoadLocal(3), Op::CallClosure { arity: 1, node_id_idx: nid }, ];
2237 let j_false = code.len();
2238 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(1));
2241 code.push(Op::LoadLocal(3));
2242 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2243 code.push(Op::Return);
2244 let false_target = code.len() as i32;
2246 if let Op::JumpIfNot(off) = &mut code[j_false] {
2247 *off = false_target - (j_false as i32 + 1);
2248 }
2249 code.push(Op::LoadLocal(2));
2250 code.push(Op::LoadLocal(3));
2251 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2252 code.push(Op::Return);
2253
2254 let fn_id = self.install_trampoline("__flow_branch", 4, 4, code);
2255 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2256 }
2257
2258 fn emit_flow_retry(&mut self, args: &[a::CExpr]) {
2262 self.compile_expr(&args[0], false);
2263 self.compile_expr(&args[1], false);
2264 let call_nid = self.pool.node_id("n_flow_retry");
2265 let ok_idx = self.pool.variant("Ok");
2266 let zero_const = self.pool.int(0);
2267 let one_const = self.pool.int(1);
2268 let mut code = vec![
2270 Op::PushConst(zero_const),
2272 Op::StoreLocal(3),
2273 ];
2274 let loop_top = code.len() as i32;
2276 code.push(Op::LoadLocal(3));
2277 code.push(Op::LoadLocal(1));
2278 code.push(Op::IntLt);
2279 let j_done = code.len();
2280 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(0));
2284 code.push(Op::LoadLocal(2));
2285 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2286 code.push(Op::StoreLocal(4));
2287
2288 code.push(Op::LoadLocal(4));
2290 code.push(Op::TestVariant(ok_idx));
2291 let j_was_err = code.len();
2292 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(4));
2294 code.push(Op::Return);
2295
2296 let was_err_target = code.len() as i32;
2298 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2299 *off = was_err_target - (j_was_err as i32 + 1);
2300 }
2301 code.push(Op::LoadLocal(3));
2302 code.push(Op::PushConst(one_const));
2303 code.push(Op::IntAdd);
2304 code.push(Op::StoreLocal(3));
2305 let pc_after_jump = code.len() as i32 + 1;
2306 code.push(Op::Jump(loop_top - pc_after_jump));
2307
2308 let done_target = code.len() as i32;
2310 if let Op::JumpIfNot(off) = &mut code[j_done] {
2311 *off = done_target - (j_done as i32 + 1);
2312 }
2313 code.push(Op::LoadLocal(4));
2314 code.push(Op::Return);
2315
2316 let fn_id = self.install_trampoline("__flow_retry", 3, 5, code);
2317 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2318 }
2319
2320 fn emit_flow_retry_with_backoff(&mut self, args: &[a::CExpr]) {
2328 self.compile_expr(&args[0], false);
2331 self.compile_expr(&args[1], false);
2332 self.compile_expr(&args[2], false);
2333 let call_nid = self.pool.node_id("n_flow_retry_backoff");
2334 let sleep_nid = self.pool.node_id("n_flow_retry_backoff_sleep");
2335 let kind_idx = self.pool.str("time");
2336 let op_idx = self.pool.str("sleep_ms");
2337 let ok_idx = self.pool.variant("Ok");
2338 let zero_const = self.pool.int(0);
2339 let one_const = self.pool.int(1);
2340 let two_const = self.pool.int(2);
2341 let mut code = vec![
2346 Op::LoadLocal(2),
2348 Op::StoreLocal(6),
2349 Op::PushConst(zero_const),
2351 Op::StoreLocal(4),
2352 ];
2353
2354 let loop_top = code.len() as i32;
2355 code.push(Op::LoadLocal(4));
2357 code.push(Op::LoadLocal(1));
2358 code.push(Op::IntLt);
2359 let j_done = code.len();
2360 code.push(Op::JumpIfNot(0)); code.push(Op::PushConst(zero_const));
2364 code.push(Op::LoadLocal(4));
2365 code.push(Op::IntLt); let j_no_sleep = code.len();
2367 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(6)); code.push(Op::EffectCall {
2371 kind_idx, op_idx, arity: 1, node_id_idx: sleep_nid,
2372 });
2373 code.push(Op::Pop); code.push(Op::LoadLocal(6));
2376 code.push(Op::PushConst(two_const));
2377 code.push(Op::NumMul);
2378 code.push(Op::StoreLocal(6));
2379 let after_sleep = code.len() as i32;
2381 if let Op::JumpIfNot(off) = &mut code[j_no_sleep] {
2382 *off = after_sleep - (j_no_sleep as i32 + 1);
2383 }
2384
2385 code.push(Op::LoadLocal(0));
2387 code.push(Op::LoadLocal(3));
2388 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2389 code.push(Op::StoreLocal(5));
2390
2391 code.push(Op::LoadLocal(5));
2393 code.push(Op::TestVariant(ok_idx));
2394 let j_was_err = code.len();
2395 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(5));
2397 code.push(Op::Return);
2398
2399 let was_err_target = code.len() as i32;
2401 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2402 *off = was_err_target - (j_was_err as i32 + 1);
2403 }
2404 code.push(Op::LoadLocal(4));
2405 code.push(Op::PushConst(one_const));
2406 code.push(Op::IntAdd);
2407 code.push(Op::StoreLocal(4));
2408 let pc_after_jump = code.len() as i32 + 1;
2409 code.push(Op::Jump(loop_top - pc_after_jump));
2410
2411 let done_target = code.len() as i32;
2413 if let Op::JumpIfNot(off) = &mut code[j_done] {
2414 *off = done_target - (j_done as i32 + 1);
2415 }
2416 code.push(Op::LoadLocal(5));
2417 code.push(Op::Return);
2418
2419 let fn_id = self.install_trampoline("__flow_retry_backoff", 4, 7, code);
2420 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2421 }
2422}