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 name == "todo" => {
623 let idx = self.pool.str("todo() reached");
632 self.emit(Op::Panic(idx));
633 }
634 a::CExpr::Var { name } if self.locals.contains_key(name) => {
635 let slot = self.locals[name];
638 self.emit(Op::LoadLocal(slot));
639 for a in args { self.compile_expr(a, false); }
640 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
641 }
642 other => {
644 self.compile_expr(other, false);
645 for a in args { self.compile_expr(a, false); }
646 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
647 }
648 }
649 }
650
651 fn compile_binop(&mut self, op: &str, lhs: &a::CExpr, rhs: &a::CExpr) {
652 let lhs_ty = self.classify_expr(lhs);
663 let rhs_ty = self.classify_expr(rhs);
664 let typed = match (lhs_ty, rhs_ty) {
665 (NumTy::Int, NumTy::Int) => NumTy::Int,
666 (NumTy::Float, NumTy::Float) => NumTy::Float,
667 _ => NumTy::Unknown,
668 };
669 self.compile_expr(lhs, false);
670 self.compile_expr(rhs, false);
671 match (op, typed) {
672 ("+", NumTy::Int) => self.emit(Op::IntAdd),
673 ("+", NumTy::Float) => self.emit(Op::FloatAdd),
674 ("+", NumTy::Unknown) => self.emit(Op::NumAdd),
675 ("-", NumTy::Int) => self.emit(Op::IntSub),
676 ("-", NumTy::Float) => self.emit(Op::FloatSub),
677 ("-", NumTy::Unknown) => self.emit(Op::NumSub),
678 ("*", NumTy::Int) => self.emit(Op::IntMul),
679 ("*", NumTy::Float) => self.emit(Op::FloatMul),
680 ("*", NumTy::Unknown) => self.emit(Op::NumMul),
681 ("/", NumTy::Int) => self.emit(Op::IntDiv),
682 ("/", NumTy::Float) => self.emit(Op::FloatDiv),
683 ("/", NumTy::Unknown) => self.emit(Op::NumDiv),
684 ("%", NumTy::Int) => self.emit(Op::IntMod),
686 ("%", _) => self.emit(Op::NumMod),
687 ("==", NumTy::Int) => self.emit(Op::IntEq),
688 ("==", NumTy::Float) => self.emit(Op::FloatEq),
689 ("==", NumTy::Unknown) => self.emit(Op::NumEq),
690 ("!=", NumTy::Int) => { self.emit(Op::IntEq); self.emit(Op::BoolNot); }
691 ("!=", NumTy::Float) => { self.emit(Op::FloatEq); self.emit(Op::BoolNot); }
692 ("!=", NumTy::Unknown) => { self.emit(Op::NumEq); self.emit(Op::BoolNot); }
693 ("<", NumTy::Int) => self.emit(Op::IntLt),
694 ("<", NumTy::Float) => self.emit(Op::FloatLt),
695 ("<", NumTy::Unknown) => self.emit(Op::NumLt),
696 ("<=", NumTy::Int) => self.emit(Op::IntLe),
697 ("<=", NumTy::Float) => self.emit(Op::FloatLe),
698 ("<=", NumTy::Unknown) => self.emit(Op::NumLe),
699 (">", NumTy::Int) => { self.emit_swap_top2(); self.emit(Op::IntLt); }
700 (">", NumTy::Float) => { self.emit_swap_top2(); self.emit(Op::FloatLt); }
701 (">", NumTy::Unknown) => { self.emit_swap_top2(); self.emit(Op::NumLt); }
702 (">=", NumTy::Int) => { self.emit_swap_top2(); self.emit(Op::IntLe); }
703 (">=", NumTy::Float) => { self.emit_swap_top2(); self.emit(Op::FloatLe); }
704 (">=", NumTy::Unknown) => { self.emit_swap_top2(); self.emit(Op::NumLe); }
705 ("and", _) => self.emit(Op::BoolAnd),
706 ("or", _) => self.emit(Op::BoolOr),
707 (other, _) => panic!("unknown binop: {other:?}"),
708 }
709 }
710
711 fn classify_expr(&self, e: &a::CExpr) -> NumTy {
719 match e {
720 a::CExpr::Literal { value: a::CLit::Int { .. } } => NumTy::Int,
721 a::CExpr::Literal { value: a::CLit::Float { .. } } => NumTy::Float,
722 a::CExpr::Var { name } =>
723 self.local_types.get(name).copied().unwrap_or(NumTy::Unknown),
724 a::CExpr::BinOp { op, lhs, rhs } => {
725 let is_numeric = matches!(op.as_str(), "+" | "-" | "*" | "/" | "%");
729 if !is_numeric { return NumTy::Unknown; }
730 match (self.classify_expr(lhs), self.classify_expr(rhs)) {
731 (NumTy::Int, NumTy::Int) => NumTy::Int,
732 (NumTy::Float, NumTy::Float) => NumTy::Float,
733 _ => NumTy::Unknown,
734 }
735 }
736 a::CExpr::UnaryOp { op, expr } if op == "-" => self.classify_expr(expr),
737 a::CExpr::FieldAccess { value, field } => {
744 if let a::CExpr::Var { name } = value.as_ref() {
745 if let Some(ftypes) = self.local_record_field_types.get(name) {
746 return ftypes.get(field).copied().unwrap_or(NumTy::Unknown);
747 }
748 }
749 NumTy::Unknown
750 }
751 _ => NumTy::Unknown,
755 }
756 }
757
758 fn emit_swap_top2(&mut self) {
759 let a = self.alloc_local("__swap_a");
760 let b = self.alloc_local("__swap_b");
761 self.emit(Op::StoreLocal(b));
762 self.emit(Op::StoreLocal(a));
763 self.emit(Op::LoadLocal(b));
764 self.emit(Op::LoadLocal(a));
765 }
766
767 fn compile_match(&mut self, scrutinee: &a::CExpr, arms: &[a::Arm], tail: bool) {
768 self.compile_expr(scrutinee, false);
769 let scrut_slot = self.alloc_local("__scrut");
770 self.emit(Op::StoreLocal(scrut_slot));
771
772 let mut end_jumps: Vec<usize> = Vec::new();
773 for arm in arms {
774 let arm_start_locals = self.next_local;
775 let arm_start_locals_map = self.locals.clone();
776
777 self.emit(Op::LoadLocal(scrut_slot));
778 let mut bindings: Vec<(String, u16)> = Vec::new();
779 let fail_jumps: Vec<usize> = self.compile_pattern_test(&arm.pattern, &mut bindings);
780
781 self.compile_expr(&arm.body, tail);
782 let j_end = self.code.len();
783 self.emit(Op::Jump(0));
784 end_jumps.push(j_end);
785
786 let fail_target = self.code.len() as i32;
787 for j in fail_jumps {
788 match &mut self.code[j] {
794 Op::JumpIfNot(off) => *off = fail_target - (j as i32 + 1),
795 Op::Jump(off) => *off = fail_target - (j as i32 + 1),
796 _ => {}
797 }
798 }
799 self.next_local = arm_start_locals;
800 self.locals = arm_start_locals_map;
801 }
802 let panic_msg_idx = self.pool.str("non-exhaustive match");
803 self.emit(Op::Panic(panic_msg_idx));
804
805 let end_target = self.code.len() as i32;
806 for j in end_jumps {
807 if let Op::Jump(off) = &mut self.code[j] {
808 *off = end_target - (j as i32 + 1);
809 }
810 }
811 }
812
813 fn compile_pattern_test(&mut self, p: &a::Pattern, bindings: &mut Vec<(String, u16)>) -> Vec<usize> {
814 let mut fails = Vec::new();
815 match p {
816 a::Pattern::PWild => { self.emit(Op::Pop); }
817 a::Pattern::PVar { name } => {
818 let slot = self.alloc_local(name);
819 self.emit(Op::StoreLocal(slot));
820 bindings.push((name.clone(), slot));
821 }
822 a::Pattern::PLiteral { value } => {
823 self.compile_lit(value);
824 match value {
825 a::CLit::Str { .. } => self.emit(Op::StrEq),
826 a::CLit::Bytes { .. } => self.emit(Op::BytesEq),
827 a::CLit::Int { .. } => self.emit(Op::IntEq),
838 a::CLit::Float { .. } => self.emit(Op::FloatEq),
839 _ => self.emit(Op::NumEq),
840 }
841 let j = self.code.len();
842 self.emit(Op::JumpIfNot(0));
843 fails.push(j);
844 }
845 a::Pattern::PConstructor { name, args } => {
846 let name_idx = self.pool.variant(name);
847 self.emit(Op::Dup); self.emit(Op::TestVariant(name_idx)); let j_success = self.code.len();
864 self.emit(Op::JumpIf(0)); self.emit(Op::Pop); let j_fail = self.code.len();
867 self.emit(Op::Jump(0)); fails.push(j_fail);
869 let success_target = self.code.len() as i32;
870 if let Op::JumpIf(off) = &mut self.code[j_success] {
871 *off = success_target - (j_success as i32 + 1);
872 }
873 if args.is_empty() {
874 self.emit(Op::Pop);
875 } else if args.len() == 1 {
876 self.emit(Op::GetVariantArg(0));
877 let sub_fails = self.compile_pattern_test(&args[0], bindings);
878 fails.extend(sub_fails);
879 } else {
880 let slot = self.alloc_local("__variant");
881 self.emit(Op::StoreLocal(slot));
882 for (i, arg) in args.iter().enumerate() {
883 self.emit(Op::LoadLocal(slot));
884 self.emit(Op::GetVariantArg(i as u16));
885 let sub_fails = self.compile_pattern_test(arg, bindings);
886 fails.extend(sub_fails);
887 }
888 }
889 }
890 a::Pattern::PRecord { fields } => {
891 let slot = self.alloc_local("__record");
892 self.emit(Op::StoreLocal(slot));
893 for f in fields {
894 self.emit(Op::LoadLocal(slot));
895 let name_idx = self.pool.field(&f.name);
896 let site_idx = self.field_get_sites;
897 self.field_get_sites += 1;
898 self.emit(Op::GetField { name_idx, site_idx });
899 let sub_fails = self.compile_pattern_test(&f.pattern, bindings);
900 fails.extend(sub_fails);
901 }
902 }
903 a::Pattern::PTuple { items } => {
904 let slot = self.alloc_local("__tuple");
905 self.emit(Op::StoreLocal(slot));
906 for (i, item) in items.iter().enumerate() {
907 self.emit(Op::LoadLocal(slot));
908 self.emit(Op::GetElem(i as u16));
909 let sub_fails = self.compile_pattern_test(item, bindings);
910 fails.extend(sub_fails);
911 }
912 }
913 }
914 fails
915 }
916
917 fn compile_lambda(&mut self, params: &[a::Param], body: &a::CExpr) {
921 let mut bound: std::collections::HashSet<String> = params.iter().map(|p| p.name.clone()).collect();
923 let mut frees: Vec<String> = Vec::new();
924 free_vars(body, &mut bound, &mut frees);
925
926 let captures: Vec<String> = frees.into_iter()
935 .filter(|n| self.locals.contains_key(n))
936 .collect();
937
938 let fn_id = self.next_fn_id.len() as u32;
940 self.next_fn_id.push(Function {
941 name: format!("__lambda_{fn_id}"),
942 arity: (captures.len() + params.len()) as u16,
943 locals_count: 0,
944 code: Vec::new(),
945 effects: Vec::new(),
946 body_hash: crate::program::ZERO_BODY_HASH,
948 refinements: Vec::new(),
953 field_ic_sites: 0,
956 });
957
958 for c in &captures {
960 let slot = *self.locals.get(c).expect("free var must be in scope");
961 self.emit(Op::LoadLocal(slot));
962 }
963 self.emit(Op::MakeClosure { fn_id, capture_count: captures.len() as u16 });
964
965 self.pending_lambdas.push(PendingLambda {
967 fn_id,
968 capture_names: captures,
969 params: params.to_vec(),
970 body: body.clone(),
971 });
972 }
973
974 fn try_emit_higher_order(
978 &mut self,
979 module: &str,
980 op: &str,
981 args: &[a::CExpr],
982 node_id_idx: u32,
983 ) -> bool {
984 match (module, op) {
985 ("result", "map") => self.emit_variant_map(args, "Ok", true),
986 ("result", "and_then") => self.emit_variant_map(args, "Ok", false),
987 ("result", "map_err") => self.emit_variant_map(args, "Err", true),
988 ("result", "or_else") => self.emit_variant_or_else(args, "Err", 1),
989 ("option", "map") => self.emit_variant_map(args, "Some", true),
990 ("option", "and_then") => self.emit_variant_map(args, "Some", false),
991 ("option", "or_else") => self.emit_variant_or_else(args, "None", 0),
992 ("option", "unwrap_or_else") => self.emit_option_unwrap_or_else(args),
993 ("result", "unwrap_or_else") => self.emit_result_unwrap_or_else(args),
994 ("list", "map") => self.emit_list_map(args),
995 ("list", "par_map") => self.emit_list_par_map(args),
996 ("list", "sort_by") => self.emit_list_sort_by(args),
997 ("list", "filter") => self.emit_list_filter(args),
998 ("list", "fold") => self.emit_list_fold(args),
999 ("iter", "from_list") => self.emit_iter_from_list(args),
1000 ("iter", "unfold") => self.emit_iter_unfold(args),
1001 ("iter", "next") => self.emit_iter_next(args),
1002 ("iter", "is_empty") => self.emit_iter_is_empty(args),
1003 ("iter", "count") => self.emit_iter_count(args),
1004 ("iter", "take") => self.emit_iter_take(args),
1005 ("iter", "skip") => self.emit_iter_skip(args),
1006 ("iter", "to_list") => self.emit_iter_to_list(args),
1007 ("iter", "collect") => self.emit_iter_to_list(args),
1008 ("iter", "map") => self.emit_iter_map(args),
1009 ("iter", "filter") => self.emit_iter_filter(args),
1010 ("iter", "fold") => self.emit_iter_fold(args),
1011 ("map", "fold") => self.emit_map_fold(args, node_id_idx),
1012 ("flow", "sequential") => self.emit_flow_sequential(args),
1013 ("flow", "branch") => self.emit_flow_branch(args),
1014 ("flow", "retry") => self.emit_flow_retry(args),
1015 ("flow", "retry_with_backoff") => self.emit_flow_retry_with_backoff(args),
1016 ("flow", "parallel") => self.emit_flow_parallel(args),
1017 ("flow", "parallel_list") => self.emit_flow_parallel_list(args),
1018 _ => return false,
1019 }
1020 true
1021 }
1022
1023 fn emit_list_map(&mut self, args: &[a::CExpr]) {
1029 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_map");
1032 self.emit(Op::ListMap { node_id_idx: nid });
1033 }
1034
1035 fn emit_list_par_map(&mut self, args: &[a::CExpr]) {
1041 self.compile_expr(&args[0], false);
1042 self.compile_expr(&args[1], false);
1043 let nid = self.pool.node_id("n_list_par_map");
1044 self.emit(Op::ParallelMap { node_id_idx: nid });
1045 }
1046
1047 fn emit_list_sort_by(&mut self, args: &[a::CExpr]) {
1055 self.compile_expr(&args[0], false);
1056 self.compile_expr(&args[1], false);
1057 let nid = self.pool.node_id("n_list_sort_by");
1058 self.emit(Op::SortByKey { node_id_idx: nid });
1059 }
1060
1061 fn emit_list_filter(&mut self, args: &[a::CExpr]) {
1064 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_filter");
1067 self.emit(Op::ListFilter { node_id_idx: nid });
1068 }
1069
1070 fn emit_list_fold(&mut self, args: &[a::CExpr]) {
1073 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");
1077 self.emit(Op::ListFold { node_id_idx: nid });
1078 }
1079
1080 fn emit_iter_from_list(&mut self, args: &[a::CExpr]) {
1092 self.compile_expr(&args[0], false);
1093 let zero = self.pool.int(0);
1094 self.emit(Op::PushConst(zero));
1095 let v = self.pool.variant("__IterEager");
1096 self.emit(Op::MakeVariant { name_idx: v, arity: 2 });
1097 }
1098
1099 fn emit_iter_next(&mut self, args: &[a::CExpr]) {
1111 self.compile_expr(&args[0], false);
1112 let it = self.alloc_local("__in_it");
1113 self.emit(Op::StoreLocal(it));
1114
1115 self.emit(Op::LoadLocal(it));
1117 self.emit(Op::Dup);
1118 let lazy_name = self.pool.variant("__IterLazy");
1119 self.emit(Op::TestVariant(lazy_name));
1120 let j_to_check_cursor = self.code.len();
1121 self.emit(Op::JumpIfNot(0));
1122
1123 self.emit(Op::LoadLocal(it));
1127 self.emit(Op::GetVariantArg(0)); let seed = self.alloc_local("__in_seed");
1129 self.emit(Op::StoreLocal(seed));
1130
1131 self.emit(Op::LoadLocal(it));
1132 self.emit(Op::GetVariantArg(1)); let step = self.alloc_local("__in_step");
1134 self.emit(Op::StoreLocal(step));
1135
1136 let nid_lazy = self.pool.node_id("n_iter_next_lazy");
1138 self.emit(Op::LoadLocal(step));
1139 self.emit(Op::LoadLocal(seed));
1140 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
1141 let opt = self.alloc_local("__in_opt");
1142 self.emit(Op::StoreLocal(opt));
1143
1144 self.emit(Op::LoadLocal(opt));
1146 let some_name = self.pool.variant("Some");
1147 self.emit(Op::TestVariant(some_name));
1148 let j_lazy_none = self.code.len();
1149 self.emit(Op::JumpIfNot(0));
1150
1151 self.emit(Op::LoadLocal(opt));
1154 self.emit(Op::GetVariantArg(0)); let pair = self.alloc_local("__in_pair");
1156 self.emit(Op::StoreLocal(pair));
1157
1158 self.emit(Op::LoadLocal(pair));
1159 self.emit(Op::GetElem(0)); self.emit(Op::LoadLocal(pair));
1161 self.emit(Op::GetElem(1)); self.emit(Op::LoadLocal(step)); let lazy_v = self.pool.variant("__IterLazy");
1164 self.emit(Op::MakeVariant { name_idx: lazy_v, arity: 2 }); self.emit(Op::MakeTuple(2)); let some_v = self.pool.variant("Some");
1167 self.emit(Op::MakeVariant { name_idx: some_v, arity: 1 });
1168 let j_after_lazy = self.code.len();
1169 self.emit(Op::Jump(0));
1170
1171 let none_t = self.code.len() as i32;
1173 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_none] {
1174 *off = none_t - (j_lazy_none as i32 + 1);
1175 }
1176 let none_v = self.pool.variant("None");
1177 self.emit(Op::MakeVariant { name_idx: none_v, arity: 0 });
1178 let j_after_lazy_none = self.code.len();
1179 self.emit(Op::Jump(0));
1180
1181 let cursor_check_t = self.code.len() as i32;
1183 if let Op::JumpIfNot(off) = &mut self.code[j_to_check_cursor] {
1184 *off = cursor_check_t - (j_to_check_cursor as i32 + 1);
1185 }
1186
1187 self.emit(Op::LoadLocal(it));
1188 self.emit(Op::Dup);
1189 let cursor_name = self.pool.variant("__IterCursor");
1190 self.emit(Op::TestVariant(cursor_name));
1191 let j_to_eager = self.code.len();
1192 self.emit(Op::JumpIfNot(0));
1193
1194 self.emit(Op::LoadLocal(it));
1198 self.emit(Op::GetVariantArg(0)); let handle = self.alloc_local("__in_handle");
1200 self.emit(Op::StoreLocal(handle));
1201
1202 let kind_idx = self.pool.str("sql");
1203 let op_idx = self.pool.str("cursor_next");
1204 let nid_cursor = self.pool.node_id("n_iter_next_cursor");
1205 self.emit(Op::LoadLocal(handle));
1206 self.emit(Op::EffectCall {
1207 kind_idx,
1208 op_idx,
1209 arity: 1,
1210 node_id_idx: nid_cursor,
1211 });
1212 let cur_opt = self.alloc_local("__in_cur_opt");
1213 self.emit(Op::StoreLocal(cur_opt));
1214
1215 self.emit(Op::LoadLocal(cur_opt));
1216 let some_c = self.pool.variant("Some");
1217 self.emit(Op::TestVariant(some_c));
1218 let j_cursor_none = self.code.len();
1219 self.emit(Op::JumpIfNot(0));
1220
1221 self.emit(Op::LoadLocal(cur_opt));
1223 self.emit(Op::GetVariantArg(0)); self.emit(Op::LoadLocal(handle));
1225 let cursor_v = self.pool.variant("__IterCursor");
1226 self.emit(Op::MakeVariant { name_idx: cursor_v, arity: 1 });
1227 self.emit(Op::MakeTuple(2)); let some_c2 = self.pool.variant("Some");
1229 self.emit(Op::MakeVariant { name_idx: some_c2, arity: 1 });
1230 let j_after_cursor = self.code.len();
1231 self.emit(Op::Jump(0));
1232
1233 let cursor_none_t = self.code.len() as i32;
1235 if let Op::JumpIfNot(off) = &mut self.code[j_cursor_none] {
1236 *off = cursor_none_t - (j_cursor_none as i32 + 1);
1237 }
1238 let none_c = self.pool.variant("None");
1239 self.emit(Op::MakeVariant { name_idx: none_c, arity: 0 });
1240 let j_after_cursor_none = self.code.len();
1241 self.emit(Op::Jump(0));
1242
1243 let eager_t = self.code.len() as i32;
1245 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
1246 *off = eager_t - (j_to_eager as i32 + 1);
1247 }
1248
1249 self.emit(Op::LoadLocal(it));
1250 self.emit(Op::GetVariantArg(0));
1251 let list = self.alloc_local("__in_list");
1252 self.emit(Op::StoreLocal(list));
1253
1254 self.emit(Op::LoadLocal(it));
1255 self.emit(Op::GetVariantArg(1));
1256 let idx = self.alloc_local("__in_idx");
1257 self.emit(Op::StoreLocal(idx));
1258
1259 self.emit(Op::LoadLocal(idx));
1261 self.emit(Op::LoadLocal(list));
1262 self.emit(Op::GetListLen);
1263 self.emit(Op::IntLt);
1264 let j_eager_else = self.code.len();
1265 self.emit(Op::JumpIfNot(0));
1266
1267 self.emit(Op::LoadLocal(list));
1269 self.emit(Op::LoadLocal(idx));
1270 self.emit(Op::GetListElemDyn);
1271
1272 self.emit(Op::LoadLocal(list));
1273 self.emit(Op::LoadLocal(idx));
1274 let one = self.pool.int(1);
1275 self.emit(Op::PushConst(one));
1276 self.emit(Op::IntAdd);
1277 let eager_v = self.pool.variant("__IterEager");
1278 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1279 self.emit(Op::MakeTuple(2));
1280 let some_e = self.pool.variant("Some");
1281 self.emit(Op::MakeVariant { name_idx: some_e, arity: 1 });
1282 let j_after_eager = self.code.len();
1283 self.emit(Op::Jump(0));
1284
1285 let eager_none_t = self.code.len() as i32;
1287 if let Op::JumpIfNot(off) = &mut self.code[j_eager_else] {
1288 *off = eager_none_t - (j_eager_else as i32 + 1);
1289 }
1290 let none_e = self.pool.variant("None");
1291 self.emit(Op::MakeVariant { name_idx: none_e, arity: 0 });
1292
1293 let end = self.code.len() as i32;
1295 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
1296 *off = end - (j_after_lazy as i32 + 1);
1297 }
1298 if let Op::Jump(off) = &mut self.code[j_after_lazy_none] {
1299 *off = end - (j_after_lazy_none as i32 + 1);
1300 }
1301 if let Op::Jump(off) = &mut self.code[j_after_cursor] {
1302 *off = end - (j_after_cursor as i32 + 1);
1303 }
1304 if let Op::Jump(off) = &mut self.code[j_after_cursor_none] {
1305 *off = end - (j_after_cursor_none as i32 + 1);
1306 }
1307 if let Op::Jump(off) = &mut self.code[j_after_eager] {
1308 *off = end - (j_after_eager as i32 + 1);
1309 }
1310 }
1311
1312 fn emit_iter_unfold(&mut self, args: &[a::CExpr]) {
1317 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let lazy = self.pool.variant("__IterLazy");
1320 self.emit(Op::MakeVariant { name_idx: lazy, arity: 2 });
1321 }
1322
1323 fn emit_iter_is_empty(&mut self, args: &[a::CExpr]) {
1329 self.compile_expr(&args[0], false);
1330 let it = self.alloc_local("__ie_it");
1331 self.emit(Op::StoreLocal(it));
1332
1333 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); }
1339
1340 fn emit_iter_count(&mut self, args: &[a::CExpr]) {
1342 self.compile_expr(&args[0], false);
1343 let it = self.alloc_local("__ic_it");
1344 self.emit(Op::StoreLocal(it));
1345
1346 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1347 self.emit(Op::GetListLen); self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1)); self.emit(Op::IntSub); }
1351
1352 fn emit_iter_take(&mut self, args: &[a::CExpr]) {
1354 self.compile_expr(&args[0], false);
1355 let it = self.alloc_local("__itk_it");
1356 self.emit(Op::StoreLocal(it));
1357
1358 self.compile_expr(&args[1], false);
1359 let n = self.alloc_local("__itk_n");
1360 self.emit(Op::StoreLocal(n));
1361
1362 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1363 let list = self.alloc_local("__itk_list");
1364 self.emit(Op::StoreLocal(list));
1365
1366 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1367 let i = self.alloc_local("__itk_i");
1368 self.emit(Op::StoreLocal(i));
1369
1370 self.emit(Op::MakeList(0));
1371 let out = self.alloc_local("__itk_out");
1372 self.emit(Op::StoreLocal(out));
1373
1374 let zero = self.pool.int(0);
1375 self.emit(Op::PushConst(zero));
1376 let cnt = self.alloc_local("__itk_cnt");
1377 self.emit(Op::StoreLocal(cnt));
1378
1379 let loop_top = self.code.len();
1380
1381 self.emit(Op::LoadLocal(cnt));
1383 self.emit(Op::LoadLocal(n));
1384 self.emit(Op::IntLt);
1385 let j_exit_n = self.code.len();
1386 self.emit(Op::JumpIfNot(0));
1387
1388 self.emit(Op::LoadLocal(i));
1390 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1391 self.emit(Op::IntLt);
1392 let j_exit_l = self.code.len();
1393 self.emit(Op::JumpIfNot(0));
1394
1395 self.emit(Op::LoadLocal(out));
1397 self.emit(Op::LoadLocal(list));
1398 self.emit(Op::LoadLocal(i));
1399 self.emit(Op::GetListElemDyn);
1400 self.emit(Op::ListAppend);
1401 self.emit(Op::StoreLocal(out));
1402
1403 let one = self.pool.int(1);
1404 self.emit(Op::LoadLocal(i));
1406 self.emit(Op::PushConst(one));
1407 self.emit(Op::IntAdd);
1408 self.emit(Op::StoreLocal(i));
1409 self.emit(Op::LoadLocal(cnt));
1411 self.emit(Op::PushConst(one));
1412 self.emit(Op::IntAdd);
1413 self.emit(Op::StoreLocal(cnt));
1414
1415 let jback = self.code.len();
1416 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1417
1418 let exit_t = self.code.len() as i32;
1419 if let Op::JumpIfNot(off) = &mut self.code[j_exit_n] { *off = exit_t - (j_exit_n as i32 + 1); }
1420 if let Op::JumpIfNot(off) = &mut self.code[j_exit_l] { *off = exit_t - (j_exit_l as i32 + 1); }
1421
1422 self.emit(Op::LoadLocal(out));
1424 self.emit(Op::PushConst(zero));
1425 let eager_v = self.pool.variant("__IterEager");
1426 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1427 }
1428
1429 fn emit_iter_skip(&mut self, args: &[a::CExpr]) {
1431 self.compile_expr(&args[0], false);
1432 let it = self.alloc_local("__isk_it");
1433 self.emit(Op::StoreLocal(it));
1434
1435 self.compile_expr(&args[1], false);
1436 let n = self.alloc_local("__isk_n");
1437 self.emit(Op::StoreLocal(n));
1438
1439 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1440 let list = self.alloc_local("__isk_list");
1441 self.emit(Op::StoreLocal(list));
1442
1443 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1444 let idx = self.alloc_local("__isk_idx");
1445 self.emit(Op::StoreLocal(idx));
1446
1447 self.emit(Op::LoadLocal(idx));
1449 self.emit(Op::LoadLocal(n));
1450 self.emit(Op::IntAdd);
1451 let raw = self.alloc_local("__isk_raw");
1452 self.emit(Op::StoreLocal(raw));
1453
1454 self.emit(Op::LoadLocal(raw));
1456 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1457 self.emit(Op::IntLt);
1458 let j_use_raw = self.code.len();
1459 self.emit(Op::JumpIf(0));
1460
1461 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1463 let j_end = self.code.len();
1464 self.emit(Op::Jump(0));
1465
1466 let raw_t = self.code.len() as i32;
1468 if let Op::JumpIf(off) = &mut self.code[j_use_raw] { *off = raw_t - (j_use_raw as i32 + 1); }
1469 self.emit(Op::LoadLocal(raw));
1470
1471 let end_t = self.code.len() as i32;
1472 if let Op::Jump(off) = &mut self.code[j_end] { *off = end_t - (j_end as i32 + 1); }
1473
1474 let new_idx = self.alloc_local("__isk_ni");
1476 self.emit(Op::StoreLocal(new_idx));
1477 self.emit(Op::LoadLocal(list));
1478 self.emit(Op::LoadLocal(new_idx));
1479 let eager_v = self.pool.variant("__IterEager");
1480 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1481 }
1482
1483 fn emit_iter_to_list(&mut self, args: &[a::CExpr]) {
1492 self.compile_expr(&args[0], false);
1493 let it = self.alloc_local("__itl_it");
1494 self.emit(Op::StoreLocal(it));
1495
1496 self.emit(Op::MakeList(0));
1498 let out = self.alloc_local("__itl_out");
1499 self.emit(Op::StoreLocal(out));
1500
1501 self.emit(Op::LoadLocal(it));
1503 let lazy_name = self.pool.variant("__IterLazy");
1504 self.emit(Op::TestVariant(lazy_name));
1505 let j_to_eager = self.code.len();
1506 self.emit(Op::JumpIfNot(0));
1507
1508 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1511 let seed = self.alloc_local("__itl_seed");
1512 self.emit(Op::StoreLocal(seed));
1513
1514 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1515 let step = self.alloc_local("__itl_step");
1516 self.emit(Op::StoreLocal(step));
1517
1518 let lazy_loop = self.code.len();
1519 let nid_lazy = self.pool.node_id("n_iter_to_list_lazy");
1520 self.emit(Op::LoadLocal(step));
1521 self.emit(Op::LoadLocal(seed));
1522 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
1523 let opt = self.alloc_local("__itl_opt");
1524 self.emit(Op::StoreLocal(opt));
1525
1526 self.emit(Op::LoadLocal(opt));
1528 let some_name = self.pool.variant("Some");
1529 self.emit(Op::TestVariant(some_name));
1530 let j_lazy_exit = self.code.len();
1531 self.emit(Op::JumpIfNot(0));
1532
1533 self.emit(Op::LoadLocal(opt));
1535 self.emit(Op::GetVariantArg(0));
1536 let pair = self.alloc_local("__itl_pair");
1537 self.emit(Op::StoreLocal(pair));
1538
1539 self.emit(Op::LoadLocal(out));
1540 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(0));
1541 self.emit(Op::ListAppend);
1542 self.emit(Op::StoreLocal(out));
1543
1544 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(1));
1545 self.emit(Op::StoreLocal(seed));
1546
1547 let jback_lazy = self.code.len();
1548 self.emit(Op::Jump((lazy_loop as i32) - (jback_lazy as i32 + 1)));
1549
1550 let lazy_exit_t = self.code.len() as i32;
1551 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_exit] {
1552 *off = lazy_exit_t - (j_lazy_exit as i32 + 1);
1553 }
1554 let j_after_lazy = self.code.len();
1555 self.emit(Op::Jump(0));
1556
1557 let eager_t = self.code.len() as i32;
1559 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
1560 *off = eager_t - (j_to_eager as i32 + 1);
1561 }
1562
1563 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1564 let list = self.alloc_local("__itl_list");
1565 self.emit(Op::StoreLocal(list));
1566
1567 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1568 let i = self.alloc_local("__itl_i");
1569 self.emit(Op::StoreLocal(i));
1570
1571 let loop_top = self.code.len();
1572 self.emit(Op::LoadLocal(i));
1573 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1574 self.emit(Op::IntLt);
1575 let j_exit = self.code.len();
1576 self.emit(Op::JumpIfNot(0));
1577
1578 self.emit(Op::LoadLocal(out));
1579 self.emit(Op::LoadLocal(list));
1580 self.emit(Op::LoadLocal(i));
1581 self.emit(Op::GetListElemDyn);
1582 self.emit(Op::ListAppend);
1583 self.emit(Op::StoreLocal(out));
1584
1585 self.emit(Op::LoadLocal(i));
1586 let one = self.pool.int(1);
1587 self.emit(Op::PushConst(one));
1588 self.emit(Op::IntAdd);
1589 self.emit(Op::StoreLocal(i));
1590
1591 let jback = self.code.len();
1592 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1593
1594 let exit_t = self.code.len() as i32;
1595 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
1596 *off = exit_t - (j_exit as i32 + 1);
1597 }
1598
1599 let converge = self.code.len() as i32;
1601 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
1602 *off = converge - (j_after_lazy as i32 + 1);
1603 }
1604 self.emit(Op::LoadLocal(out));
1605 }
1606
1607 fn emit_iter_map(&mut self, args: &[a::CExpr]) {
1609 self.compile_expr(&args[0], false);
1610 let it = self.alloc_local("__im_it");
1611 self.emit(Op::StoreLocal(it));
1612
1613 self.compile_expr(&args[1], false);
1614 let f = self.alloc_local("__im_f");
1615 self.emit(Op::StoreLocal(f));
1616
1617 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1618 let list = self.alloc_local("__im_list");
1619 self.emit(Op::StoreLocal(list));
1620
1621 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1622 let i = self.alloc_local("__im_i");
1623 self.emit(Op::StoreLocal(i));
1624
1625 self.emit(Op::MakeList(0));
1626 let out = self.alloc_local("__im_out");
1627 self.emit(Op::StoreLocal(out));
1628
1629 let loop_top = self.code.len();
1630 self.emit(Op::LoadLocal(i));
1631 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1632 self.emit(Op::IntLt);
1633 let j_exit = self.code.len();
1634 self.emit(Op::JumpIfNot(0));
1635
1636 let nid = self.pool.node_id("n_iter_map");
1637 self.emit(Op::LoadLocal(out));
1638 self.emit(Op::LoadLocal(f));
1639 self.emit(Op::LoadLocal(list));
1640 self.emit(Op::LoadLocal(i));
1641 self.emit(Op::GetListElemDyn);
1642 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
1643 self.emit(Op::ListAppend);
1644 self.emit(Op::StoreLocal(out));
1645
1646 self.emit(Op::LoadLocal(i));
1647 let one = self.pool.int(1);
1648 self.emit(Op::PushConst(one));
1649 self.emit(Op::IntAdd);
1650 self.emit(Op::StoreLocal(i));
1651
1652 let jback = self.code.len();
1653 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1654
1655 let exit_t = self.code.len() as i32;
1656 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
1657
1658 let zero = self.pool.int(0);
1659 self.emit(Op::LoadLocal(out));
1660 self.emit(Op::PushConst(zero));
1661 let eager_v = self.pool.variant("__IterEager");
1662 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1663 }
1664
1665 fn emit_iter_filter(&mut self, args: &[a::CExpr]) {
1667 self.compile_expr(&args[0], false);
1668 let it = self.alloc_local("__if_it");
1669 self.emit(Op::StoreLocal(it));
1670
1671 self.compile_expr(&args[1], false);
1672 let f = self.alloc_local("__if_f");
1673 self.emit(Op::StoreLocal(f));
1674
1675 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1676 let list = self.alloc_local("__if_list");
1677 self.emit(Op::StoreLocal(list));
1678
1679 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1680 let i = self.alloc_local("__if_i");
1681 self.emit(Op::StoreLocal(i));
1682
1683 self.emit(Op::MakeList(0));
1684 let out = self.alloc_local("__if_out");
1685 self.emit(Op::StoreLocal(out));
1686
1687 let loop_top = self.code.len();
1688 self.emit(Op::LoadLocal(i));
1689 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1690 self.emit(Op::IntLt);
1691 let j_exit = self.code.len();
1692 self.emit(Op::JumpIfNot(0));
1693
1694 self.emit(Op::LoadLocal(list));
1696 self.emit(Op::LoadLocal(i));
1697 self.emit(Op::GetListElemDyn);
1698 let x = self.alloc_local("__if_x");
1699 self.emit(Op::StoreLocal(x));
1700
1701 let nid = self.pool.node_id("n_iter_filter");
1702 self.emit(Op::LoadLocal(f));
1703 self.emit(Op::LoadLocal(x));
1704 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
1705 let j_skip = self.code.len();
1706 self.emit(Op::JumpIfNot(0));
1707
1708 self.emit(Op::LoadLocal(out));
1709 self.emit(Op::LoadLocal(x));
1710 self.emit(Op::ListAppend);
1711 self.emit(Op::StoreLocal(out));
1712
1713 let skip_t = self.code.len() as i32;
1714 if let Op::JumpIfNot(off) = &mut self.code[j_skip] { *off = skip_t - (j_skip as i32 + 1); }
1715
1716 self.emit(Op::LoadLocal(i));
1717 let one = self.pool.int(1);
1718 self.emit(Op::PushConst(one));
1719 self.emit(Op::IntAdd);
1720 self.emit(Op::StoreLocal(i));
1721
1722 let jback = self.code.len();
1723 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1724
1725 let exit_t = self.code.len() as i32;
1726 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
1727
1728 let zero = self.pool.int(0);
1729 self.emit(Op::LoadLocal(out));
1730 self.emit(Op::PushConst(zero));
1731 let eager_v = self.pool.variant("__IterEager");
1732 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1733 }
1734
1735 fn emit_iter_fold(&mut self, args: &[a::CExpr]) {
1737 self.compile_expr(&args[0], false);
1738 let it = self.alloc_local("__ifo_it");
1739 self.emit(Op::StoreLocal(it));
1740
1741 self.compile_expr(&args[1], false);
1742 let acc = self.alloc_local("__ifo_acc");
1743 self.emit(Op::StoreLocal(acc));
1744
1745 self.compile_expr(&args[2], false);
1746 let f = self.alloc_local("__ifo_f");
1747 self.emit(Op::StoreLocal(f));
1748
1749 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1750 let list = self.alloc_local("__ifo_list");
1751 self.emit(Op::StoreLocal(list));
1752
1753 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1754 let i = self.alloc_local("__ifo_i");
1755 self.emit(Op::StoreLocal(i));
1756
1757 let loop_top = self.code.len();
1758 self.emit(Op::LoadLocal(i));
1759 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1760 self.emit(Op::IntLt);
1761 let j_exit = self.code.len();
1762 self.emit(Op::JumpIfNot(0));
1763
1764 let nid = self.pool.node_id("n_iter_fold");
1765 self.emit(Op::LoadLocal(f));
1766 self.emit(Op::LoadLocal(acc));
1767 self.emit(Op::LoadLocal(list));
1768 self.emit(Op::LoadLocal(i));
1769 self.emit(Op::GetListElemDyn);
1770 self.emit(Op::CallClosure { arity: 2, node_id_idx: nid });
1771 self.emit(Op::StoreLocal(acc));
1772
1773 self.emit(Op::LoadLocal(i));
1774 let one = self.pool.int(1);
1775 self.emit(Op::PushConst(one));
1776 self.emit(Op::IntAdd);
1777 self.emit(Op::StoreLocal(i));
1778
1779 let jback = self.code.len();
1780 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1781
1782 let exit_t = self.code.len() as i32;
1783 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
1784 self.emit(Op::LoadLocal(acc));
1785 }
1786
1787 fn emit_map_fold(&mut self, args: &[a::CExpr], node_id_idx: u32) {
1793 self.compile_expr(&args[0], false);
1795 let map_kind = self.pool.str("map");
1796 let entries_op = self.pool.str("entries");
1797 self.emit(Op::EffectCall {
1798 kind_idx: map_kind,
1799 op_idx: entries_op,
1800 arity: 1,
1801 node_id_idx,
1802 });
1803 let xs = self.alloc_local("__mf_xs");
1804 self.emit(Op::StoreLocal(xs));
1805
1806 self.compile_expr(&args[1], false);
1808 let acc = self.alloc_local("__mf_acc");
1809 self.emit(Op::StoreLocal(acc));
1810
1811 self.compile_expr(&args[2], false);
1813 let f = self.alloc_local("__mf_f");
1814 self.emit(Op::StoreLocal(f));
1815
1816 let zero = self.pool.int(0);
1818 self.emit(Op::PushConst(zero));
1819 let i = self.alloc_local("__mf_i");
1820 self.emit(Op::StoreLocal(i));
1821
1822 let loop_top = self.code.len();
1824 self.emit(Op::LoadLocal(i));
1825 self.emit(Op::LoadLocal(xs));
1826 self.emit(Op::GetListLen);
1827 self.emit(Op::IntLt);
1828 let j_exit = self.code.len();
1829 self.emit(Op::JumpIfNot(0));
1830
1831 self.emit(Op::LoadLocal(xs));
1833 self.emit(Op::LoadLocal(i));
1834 self.emit(Op::GetListElemDyn);
1835 let pair = self.alloc_local("__mf_pair");
1836 self.emit(Op::StoreLocal(pair));
1837
1838 let nid = self.pool.node_id("n_map_fold");
1840 self.emit(Op::LoadLocal(f));
1841 self.emit(Op::LoadLocal(acc));
1842 self.emit(Op::LoadLocal(pair));
1843 self.emit(Op::GetElem(0));
1844 self.emit(Op::LoadLocal(pair));
1845 self.emit(Op::GetElem(1));
1846 self.emit(Op::CallClosure { arity: 3, node_id_idx: nid });
1847 self.emit(Op::StoreLocal(acc));
1848
1849 self.emit(Op::LoadLocal(i));
1851 let one = self.pool.int(1);
1852 self.emit(Op::PushConst(one));
1853 self.emit(Op::IntAdd);
1854 self.emit(Op::StoreLocal(i));
1855
1856 let jump_back = self.code.len();
1857 let back = (loop_top as i32) - (jump_back as i32 + 1);
1858 self.emit(Op::Jump(back));
1859
1860 let exit_target = self.code.len() as i32;
1861 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
1862 *off = exit_target - (j_exit as i32 + 1);
1863 }
1864 self.emit(Op::LoadLocal(acc));
1865 }
1866
1867 fn emit_variant_map(
1873 &mut self,
1874 args: &[a::CExpr],
1875 wrap_with: &str,
1876 wrap_result: bool,
1877 ) {
1878 let wrap_idx = self.pool.variant(wrap_with);
1880
1881 self.compile_expr(&args[0], false);
1883 let val_slot = self.alloc_local("__hov");
1884 self.emit(Op::StoreLocal(val_slot));
1885
1886 self.compile_expr(&args[1], false);
1887 let f_slot = self.alloc_local("__hof");
1888 self.emit(Op::StoreLocal(f_slot));
1889
1890 self.emit(Op::LoadLocal(val_slot));
1897 self.emit(Op::Dup);
1898 self.emit(Op::TestVariant(wrap_idx));
1899 let j_skip = self.code.len();
1900 self.emit(Op::JumpIfNot(0));
1901
1902 self.emit(Op::GetVariantArg(0));
1904 let arg_slot = self.alloc_local("__hov_arg");
1905 self.emit(Op::StoreLocal(arg_slot));
1906 self.emit(Op::LoadLocal(f_slot));
1907 self.emit(Op::LoadLocal(arg_slot));
1908 let nid = self.pool.node_id("n_hov");
1909 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
1910 if wrap_result {
1911 self.emit(Op::MakeVariant { name_idx: wrap_idx, arity: 1 });
1912 }
1913 let j_end = self.code.len();
1914 self.emit(Op::Jump(0));
1915
1916 let skip_target = self.code.len() as i32;
1918 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
1919 *off = skip_target - (j_skip as i32 + 1);
1920 }
1921
1922 let end_target = self.code.len() as i32;
1923 if let Op::Jump(off) = &mut self.code[j_end] {
1924 *off = end_target - (j_end as i32 + 1);
1925 }
1926 }
1927
1928 fn emit_variant_or_else(
1937 &mut self,
1938 args: &[a::CExpr],
1939 match_on: &str,
1940 closure_arity: u16,
1941 ) {
1942 let match_idx = self.pool.variant(match_on);
1943
1944 self.compile_expr(&args[0], false);
1945 let val_slot = self.alloc_local("__hoe");
1946 self.emit(Op::StoreLocal(val_slot));
1947
1948 self.compile_expr(&args[1], false);
1949 let f_slot = self.alloc_local("__hoe_f");
1950 self.emit(Op::StoreLocal(f_slot));
1951
1952 self.emit(Op::LoadLocal(val_slot));
1960 self.emit(Op::Dup);
1961 self.emit(Op::TestVariant(match_idx));
1962 let j_skip = self.code.len();
1963 self.emit(Op::JumpIfNot(0));
1964
1965 self.emit(Op::Pop);
1968 self.emit(Op::LoadLocal(f_slot));
1969 if closure_arity == 1 {
1970 self.emit(Op::LoadLocal(val_slot));
1971 self.emit(Op::GetVariantArg(0));
1972 }
1973 let nid = self.pool.node_id("n_hoe");
1974 self.emit(Op::CallClosure { arity: closure_arity, node_id_idx: nid });
1975
1976 let j_end = self.code.len();
1977 self.emit(Op::Jump(0));
1978
1979 let skip_target = self.code.len() as i32;
1981 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
1982 *off = skip_target - (j_skip as i32 + 1);
1983 }
1984
1985 let end_target = self.code.len() as i32;
1986 if let Op::Jump(off) = &mut self.code[j_end] {
1987 *off = end_target - (j_end as i32 + 1);
1988 }
1989 }
1990
1991 fn emit_option_unwrap_or_else(&mut self, args: &[a::CExpr]) {
1995 let some_idx = self.pool.variant("Some");
1996
1997 self.compile_expr(&args[0], false);
1999 let val_slot = self.alloc_local("__uoe_val");
2000 self.emit(Op::StoreLocal(val_slot));
2001
2002 self.compile_expr(&args[1], false);
2003 let f_slot = self.alloc_local("__uoe_f");
2004 self.emit(Op::StoreLocal(f_slot));
2005
2006 self.emit(Op::LoadLocal(val_slot));
2012 self.emit(Op::Dup);
2013 self.emit(Op::TestVariant(some_idx));
2014 let j_none = self.code.len();
2015 self.emit(Op::JumpIfNot(0));
2016
2017 self.emit(Op::GetVariantArg(0));
2019 let j_end = self.code.len();
2020 self.emit(Op::Jump(0));
2021
2022 let none_target = self.code.len() as i32;
2024 if let Op::JumpIfNot(off) = &mut self.code[j_none] {
2025 *off = none_target - (j_none as i32 + 1);
2026 }
2027 self.emit(Op::Pop);
2028 self.emit(Op::LoadLocal(f_slot));
2029 let nid = self.pool.node_id("n_uoe");
2030 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2031
2032 let end_target = self.code.len() as i32;
2034 if let Op::Jump(off) = &mut self.code[j_end] {
2035 *off = end_target - (j_end as i32 + 1);
2036 }
2037 }
2038
2039 fn emit_result_unwrap_or_else(&mut self, args: &[a::CExpr]) {
2045 let ok_idx = self.pool.variant("Ok");
2046
2047 self.compile_expr(&args[0], false);
2048 let val_slot = self.alloc_local("__ruoe_val");
2049 self.emit(Op::StoreLocal(val_slot));
2050
2051 self.compile_expr(&args[1], false);
2052 let f_slot = self.alloc_local("__ruoe_f");
2053 self.emit(Op::StoreLocal(f_slot));
2054
2055 self.emit(Op::LoadLocal(val_slot));
2061 self.emit(Op::Dup);
2062 self.emit(Op::TestVariant(ok_idx));
2063 let j_err = self.code.len();
2064 self.emit(Op::JumpIfNot(0));
2065
2066 self.emit(Op::GetVariantArg(0));
2068 let j_end = self.code.len();
2069 self.emit(Op::Jump(0));
2070
2071 let err_target = self.code.len() as i32;
2073 if let Op::JumpIfNot(off) = &mut self.code[j_err] {
2074 *off = err_target - (j_err as i32 + 1);
2075 }
2076 self.emit(Op::Pop);
2077 self.emit(Op::LoadLocal(f_slot));
2078 self.emit(Op::LoadLocal(val_slot));
2079 self.emit(Op::GetVariantArg(0));
2080 let nid = self.pool.node_id("n_ruoe");
2081 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2082
2083 let end_target = self.code.len() as i32;
2085 if let Op::Jump(off) = &mut self.code[j_end] {
2086 *off = end_target - (j_end as i32 + 1);
2087 }
2088 }
2089
2090 fn install_trampoline(&mut self, name: &str, arity: u16, locals_count: u16, code: Vec<Op>) -> u32 {
2107 let fn_id = self.next_fn_id.len() as u32;
2108 let body_hash = crate::program::compute_body_hash(
2109 arity, locals_count, &code, &self.pool.record_shapes);
2110 self.next_fn_id.push(Function {
2111 name: name.into(),
2112 arity,
2113 locals_count,
2114 code,
2115 effects: Vec::new(),
2116 body_hash,
2117 refinements: Vec::new(),
2120 field_ic_sites: 0,
2124 });
2125 fn_id
2126 }
2127
2128 fn emit_flow_sequential(&mut self, args: &[a::CExpr]) {
2130 self.compile_expr(&args[0], false);
2132 self.compile_expr(&args[1], false);
2133 let nid = self.pool.node_id("n_flow_sequential");
2134 let code = vec![
2135 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,
2145 ];
2146 let fn_id = self.install_trampoline("__flow_sequential", 3, 4, code);
2147 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2148 }
2149
2150 fn emit_flow_parallel(&mut self, args: &[a::CExpr]) {
2158 self.compile_expr(&args[0], false);
2160 self.compile_expr(&args[1], false);
2161 let nid = self.pool.node_id("n_flow_parallel");
2162 let code = vec![
2163 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,
2170 ];
2171 let fn_id = self.install_trampoline("__flow_parallel", 2, 2, code);
2172 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2173 }
2174
2175 fn emit_flow_parallel_list(&mut self, args: &[a::CExpr]) {
2182 self.compile_expr(&args[0], false);
2184 let xs = self.alloc_local("__fpl_xs");
2185 self.emit(Op::StoreLocal(xs));
2186
2187 self.emit(Op::MakeList(0));
2189 let out = self.alloc_local("__fpl_out");
2190 self.emit(Op::StoreLocal(out));
2191
2192 let zero = self.pool.int(0);
2194 self.emit(Op::PushConst(zero));
2195 let i = self.alloc_local("__fpl_i");
2196 self.emit(Op::StoreLocal(i));
2197
2198 let loop_top = self.code.len();
2200 self.emit(Op::LoadLocal(i));
2201 self.emit(Op::LoadLocal(xs));
2202 self.emit(Op::GetListLen);
2203 self.emit(Op::IntLt);
2204 let j_exit = self.code.len();
2205 self.emit(Op::JumpIfNot(0));
2206
2207 let nid = self.pool.node_id("n_flow_parallel_list");
2209 self.emit(Op::LoadLocal(out));
2210 self.emit(Op::LoadLocal(xs));
2211 self.emit(Op::LoadLocal(i));
2212 self.emit(Op::GetListElemDyn);
2213 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2214 self.emit(Op::ListAppend);
2215 self.emit(Op::StoreLocal(out));
2216
2217 self.emit(Op::LoadLocal(i));
2219 let one = self.pool.int(1);
2220 self.emit(Op::PushConst(one));
2221 self.emit(Op::IntAdd);
2222 self.emit(Op::StoreLocal(i));
2223
2224 let jump_back = self.code.len();
2226 let back = (loop_top as i32) - (jump_back as i32 + 1);
2227 self.emit(Op::Jump(back));
2228
2229 let exit_target = self.code.len() as i32;
2231 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2232 *off = exit_target - (j_exit as i32 + 1);
2233 }
2234 self.emit(Op::LoadLocal(out));
2235 }
2236
2237 fn emit_flow_branch(&mut self, args: &[a::CExpr]) {
2239 self.compile_expr(&args[0], false);
2240 self.compile_expr(&args[1], false);
2241 self.compile_expr(&args[2], false);
2242 let nid = self.pool.node_id("n_flow_branch");
2243 let mut code = vec![
2244 Op::LoadLocal(0), Op::LoadLocal(3), Op::CallClosure { arity: 1, node_id_idx: nid }, ];
2249 let j_false = code.len();
2250 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(1));
2253 code.push(Op::LoadLocal(3));
2254 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2255 code.push(Op::Return);
2256 let false_target = code.len() as i32;
2258 if let Op::JumpIfNot(off) = &mut code[j_false] {
2259 *off = false_target - (j_false as i32 + 1);
2260 }
2261 code.push(Op::LoadLocal(2));
2262 code.push(Op::LoadLocal(3));
2263 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2264 code.push(Op::Return);
2265
2266 let fn_id = self.install_trampoline("__flow_branch", 4, 4, code);
2267 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2268 }
2269
2270 fn emit_flow_retry(&mut self, args: &[a::CExpr]) {
2274 self.compile_expr(&args[0], false);
2275 self.compile_expr(&args[1], false);
2276 let call_nid = self.pool.node_id("n_flow_retry");
2277 let ok_idx = self.pool.variant("Ok");
2278 let zero_const = self.pool.int(0);
2279 let one_const = self.pool.int(1);
2280 let mut code = vec![
2282 Op::PushConst(zero_const),
2284 Op::StoreLocal(3),
2285 ];
2286 let loop_top = code.len() as i32;
2288 code.push(Op::LoadLocal(3));
2289 code.push(Op::LoadLocal(1));
2290 code.push(Op::IntLt);
2291 let j_done = code.len();
2292 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(0));
2296 code.push(Op::LoadLocal(2));
2297 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2298 code.push(Op::StoreLocal(4));
2299
2300 code.push(Op::LoadLocal(4));
2302 code.push(Op::TestVariant(ok_idx));
2303 let j_was_err = code.len();
2304 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(4));
2306 code.push(Op::Return);
2307
2308 let was_err_target = code.len() as i32;
2310 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2311 *off = was_err_target - (j_was_err as i32 + 1);
2312 }
2313 code.push(Op::LoadLocal(3));
2314 code.push(Op::PushConst(one_const));
2315 code.push(Op::IntAdd);
2316 code.push(Op::StoreLocal(3));
2317 let pc_after_jump = code.len() as i32 + 1;
2318 code.push(Op::Jump(loop_top - pc_after_jump));
2319
2320 let done_target = code.len() as i32;
2322 if let Op::JumpIfNot(off) = &mut code[j_done] {
2323 *off = done_target - (j_done as i32 + 1);
2324 }
2325 code.push(Op::LoadLocal(4));
2326 code.push(Op::Return);
2327
2328 let fn_id = self.install_trampoline("__flow_retry", 3, 5, code);
2329 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2330 }
2331
2332 fn emit_flow_retry_with_backoff(&mut self, args: &[a::CExpr]) {
2340 self.compile_expr(&args[0], false);
2343 self.compile_expr(&args[1], false);
2344 self.compile_expr(&args[2], false);
2345 let call_nid = self.pool.node_id("n_flow_retry_backoff");
2346 let sleep_nid = self.pool.node_id("n_flow_retry_backoff_sleep");
2347 let kind_idx = self.pool.str("time");
2348 let op_idx = self.pool.str("sleep_ms");
2349 let ok_idx = self.pool.variant("Ok");
2350 let zero_const = self.pool.int(0);
2351 let one_const = self.pool.int(1);
2352 let two_const = self.pool.int(2);
2353 let mut code = vec![
2358 Op::LoadLocal(2),
2360 Op::StoreLocal(6),
2361 Op::PushConst(zero_const),
2363 Op::StoreLocal(4),
2364 ];
2365
2366 let loop_top = code.len() as i32;
2367 code.push(Op::LoadLocal(4));
2369 code.push(Op::LoadLocal(1));
2370 code.push(Op::IntLt);
2371 let j_done = code.len();
2372 code.push(Op::JumpIfNot(0)); code.push(Op::PushConst(zero_const));
2376 code.push(Op::LoadLocal(4));
2377 code.push(Op::IntLt); let j_no_sleep = code.len();
2379 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(6)); code.push(Op::EffectCall {
2383 kind_idx, op_idx, arity: 1, node_id_idx: sleep_nid,
2384 });
2385 code.push(Op::Pop); code.push(Op::LoadLocal(6));
2388 code.push(Op::PushConst(two_const));
2389 code.push(Op::NumMul);
2390 code.push(Op::StoreLocal(6));
2391 let after_sleep = code.len() as i32;
2393 if let Op::JumpIfNot(off) = &mut code[j_no_sleep] {
2394 *off = after_sleep - (j_no_sleep as i32 + 1);
2395 }
2396
2397 code.push(Op::LoadLocal(0));
2399 code.push(Op::LoadLocal(3));
2400 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2401 code.push(Op::StoreLocal(5));
2402
2403 code.push(Op::LoadLocal(5));
2405 code.push(Op::TestVariant(ok_idx));
2406 let j_was_err = code.len();
2407 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(5));
2409 code.push(Op::Return);
2410
2411 let was_err_target = code.len() as i32;
2413 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2414 *off = was_err_target - (j_was_err as i32 + 1);
2415 }
2416 code.push(Op::LoadLocal(4));
2417 code.push(Op::PushConst(one_const));
2418 code.push(Op::IntAdd);
2419 code.push(Op::StoreLocal(4));
2420 let pc_after_jump = code.len() as i32 + 1;
2421 code.push(Op::Jump(loop_top - pc_after_jump));
2422
2423 let done_target = code.len() as i32;
2425 if let Op::JumpIfNot(off) = &mut code[j_done] {
2426 *off = done_target - (j_done as i32 + 1);
2427 }
2428 code.push(Op::LoadLocal(5));
2429 code.push(Op::Return);
2430
2431 let fn_id = self.install_trampoline("__flow_retry_backoff", 4, 7, code);
2432 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2433 }
2434}