1use crate::op::*;
4use crate::program::*;
5use indexmap::IndexMap;
6use lex_ast as a;
7
8#[derive(Default)]
9struct ConstPool {
10 pool: Vec<Const>,
11 fields: IndexMap<String, u32>,
12 variants: IndexMap<String, u32>,
13 node_ids: IndexMap<String, u32>,
14 ints: IndexMap<i64, u32>,
15 bools: IndexMap<u8, u32>,
16 strs: IndexMap<String, u32>,
17 record_shapes: Vec<Vec<u32>>,
22 record_shape_dedup: IndexMap<Vec<u32>, u32>,
23}
24
25impl ConstPool {
26 fn field(&mut self, name: &str) -> u32 {
27 if let Some(i) = self.fields.get(name) { return *i; }
28 let i = self.pool.len() as u32;
29 self.pool.push(Const::FieldName(name.into()));
30 self.fields.insert(name.into(), i);
31 i
32 }
33 fn variant(&mut self, name: &str) -> u32 {
34 if let Some(i) = self.variants.get(name) { return *i; }
35 let i = self.pool.len() as u32;
36 self.pool.push(Const::VariantName(name.into()));
37 self.variants.insert(name.into(), i);
38 i
39 }
40 fn node_id(&mut self, name: &str) -> u32 {
41 if let Some(i) = self.node_ids.get(name) { return *i; }
42 let i = self.pool.len() as u32;
43 self.pool.push(Const::NodeId(name.into()));
44 self.node_ids.insert(name.into(), i);
45 i
46 }
47 fn int(&mut self, n: i64) -> u32 {
48 if let Some(i) = self.ints.get(&n) { return *i; }
49 let i = self.pool.len() as u32;
50 self.pool.push(Const::Int(n));
51 self.ints.insert(n, i);
52 i
53 }
54 fn bool(&mut self, b: bool) -> u32 {
55 let key = b as u8;
56 if let Some(i) = self.bools.get(&key) { return *i; }
57 let i = self.pool.len() as u32;
58 self.pool.push(Const::Bool(b));
59 self.bools.insert(key, i);
60 i
61 }
62 fn str(&mut self, s: &str) -> u32 {
63 if let Some(i) = self.strs.get(s) { return *i; }
64 let i = self.pool.len() as u32;
65 self.pool.push(Const::Str(s.into()));
66 self.strs.insert(s.into(), i);
67 i
68 }
69 fn float(&mut self, f: f64) -> u32 {
70 let i = self.pool.len() as u32;
72 self.pool.push(Const::Float(f));
73 i
74 }
75 fn unit(&mut self) -> u32 {
76 let i = self.pool.len() as u32;
77 self.pool.push(Const::Unit);
78 i
79 }
80
81 fn record_shape(&mut self, idxs: Vec<u32>) -> u32 {
85 if let Some(i) = self.record_shape_dedup.get(&idxs) {
86 return *i;
87 }
88 let i = self.record_shapes.len() as u32;
89 self.record_shape_dedup.insert(idxs.clone(), i);
90 self.record_shapes.push(idxs);
91 i
92 }
93}
94
95pub fn compile_program(stages: &[a::Stage]) -> Program {
96 let mut p = Program {
97 constants: Vec::new(),
98 functions: Vec::new(),
99 function_names: IndexMap::new(),
100 module_aliases: IndexMap::new(),
101 entry: None,
102 record_shapes: Vec::new(),
103 };
104
105 for s in stages {
108 if let a::Stage::Import(i) = s {
109 let module = i.reference.strip_prefix("std.").unwrap_or(&i.reference).to_string();
110 p.module_aliases.insert(i.alias.clone(), module);
111 }
112 }
113
114 for s in stages {
115 if let a::Stage::FnDecl(fd) = s {
116 let idx = p.functions.len() as u32;
117 p.function_names.insert(fd.name.clone(), idx);
118 p.functions.push(Function {
119 name: fd.name.clone(),
120 arity: fd.params.len() as u16,
121 locals_count: 0,
122 code: Vec::new(),
123 effects: fd.effects.iter().map(|e| DeclaredEffect {
124 kind: e.name.clone(),
125 arg: e.arg.as_ref().map(|a| match a {
126 a::EffectArg::Str { value } => EffectArg::Str(value.clone()),
127 a::EffectArg::Int { value } => EffectArg::Int(*value),
128 a::EffectArg::Ident { value } => EffectArg::Ident(value.clone()),
129 }),
130 }).collect(),
131 body_hash: crate::program::ZERO_BODY_HASH,
134 refinements: fd.params.iter().map(|p| match &p.ty {
138 a::TypeExpr::Refined { binding, predicate, .. } =>
139 Some(crate::program::Refinement {
140 binding: binding.clone(),
141 predicate: (**predicate).clone(),
142 }),
143 _ => None,
144 }).collect(),
145 field_ic_sites: 0,
147 });
148 }
149 }
150
151 let mut pool = ConstPool::default();
152 let function_names = p.function_names.clone();
153 let module_aliases = p.module_aliases.clone();
154 let mut pending_lambdas: Vec<PendingLambda> = Vec::new();
155 let mut type_aliases: IndexMap<String, a::TypeExpr> = IndexMap::new();
161 for s in stages {
162 if let a::Stage::TypeDecl(td) = s {
163 if td.params.is_empty() {
167 type_aliases.insert(td.name.clone(), td.definition.clone());
168 }
169 }
170 }
171
172 for s in stages {
173 if let a::Stage::FnDecl(_) = s {
174 let id_map = lex_ast::expr_ids(s);
177 let fd = match s { a::Stage::FnDecl(fd) => fd, _ => unreachable!() };
178 let mut fc = FnCompiler {
179 code: Vec::new(),
180 locals: IndexMap::new(),
181 next_local: 0,
182 peak_local: 0,
183 local_types: IndexMap::new(),
184 local_record_field_types: IndexMap::new(),
185 field_get_sites: 0,
186 pool: &mut pool,
187 function_names: &function_names,
188 module_aliases: &module_aliases,
189 id_map: &id_map,
190 pending_lambdas: &mut pending_lambdas,
191 next_fn_id: &mut p.functions,
192 };
193 for param in &fd.params {
194 let i = fc.next_local;
195 fc.locals.insert(param.name.clone(), i);
196 fc.local_types.insert(param.name.clone(), classify_type_expr(¶m.ty));
197 if let Some(ftypes) = record_field_types(¶m.ty, &type_aliases) {
202 fc.local_record_field_types.insert(param.name.clone(), ftypes);
203 }
204 fc.next_local += 1;
205 fc.peak_local = fc.next_local;
206 }
207 fc.compile_expr(&fd.body, true);
208 fc.code.push(Op::Return);
209 let code = std::mem::take(&mut fc.code);
210 let peak = fc.peak_local;
211 let field_sites = fc.field_get_sites as u16;
212 drop(fc);
213 let idx = function_names[&fd.name];
214 p.functions[idx as usize].code = code;
215 p.functions[idx as usize].field_ic_sites = field_sites;
216 p.functions[idx as usize].locals_count = peak;
217 }
218 }
219
220 while let Some(pl) = pending_lambdas.pop() {
223 let id_map = std::collections::HashMap::new();
224 let mut fc = FnCompiler {
225 code: Vec::new(),
226 locals: IndexMap::new(),
227 next_local: 0,
228 peak_local: 0,
229 local_types: IndexMap::new(),
230 local_record_field_types: IndexMap::new(),
231 field_get_sites: 0,
232 pool: &mut pool,
233 function_names: &function_names,
234 module_aliases: &module_aliases,
235 id_map: &id_map,
236 pending_lambdas: &mut pending_lambdas,
237 next_fn_id: &mut p.functions,
238 };
239 for name in &pl.capture_names {
240 let i = fc.next_local;
241 fc.locals.insert(name.clone(), i);
242 fc.local_types.insert(name.clone(), NumTy::Unknown);
247 fc.next_local += 1;
248 fc.peak_local = fc.next_local;
249 }
250 for p in &pl.params {
251 let i = fc.next_local;
252 fc.locals.insert(p.name.clone(), i);
253 fc.local_types.insert(p.name.clone(), classify_type_expr(&p.ty));
254 fc.next_local += 1;
255 fc.peak_local = fc.next_local;
256 }
257 fc.compile_expr(&pl.body, true);
258 fc.code.push(Op::Return);
259 let code = std::mem::take(&mut fc.code);
260 let peak = fc.peak_local;
261 let field_sites = fc.field_get_sites as u16;
262 drop(fc);
263 p.functions[pl.fn_id as usize].code = code;
264 p.functions[pl.fn_id as usize].field_ic_sites = field_sites;
265 p.functions[pl.fn_id as usize].locals_count = peak;
266 }
267
268 if std::env::var_os("LEX_NO_STACK_RECORDS").is_none() {
286 let escape_index = crate::escape::build_escape_index(&p.functions);
287 for f in p.functions.iter_mut() {
288 apply_escape_lowering(&mut f.code, &f.name, &escape_index);
289 }
290 }
291
292 if std::env::var_os("LEX_NO_ARENA_RECORDS").is_none() {
318 let arena_index = crate::arena::build_arena_index(&p.functions);
319 for f in p.functions.iter_mut() {
320 apply_arena_lowering(&mut f.code, &f.name, &arena_index);
321 }
322 }
323
324 for f in p.functions.iter_mut() {
340 apply_peephole(&mut f.code, &pool.pool);
341 apply_peephole_slice2(&mut f.code);
342 apply_peephole_slice3(&mut f.code);
343 apply_peephole_slice4(&mut f.code);
344 apply_peephole_slice5(&mut f.code, &pool.pool);
355 apply_peephole_slice6(&mut f.code);
360 apply_peephole_slice7(&mut f.code);
365 apply_peephole_slice9(&mut f.code);
375 }
376
377 for f in p.functions.iter_mut() {
382 if f.body_hash == crate::program::ZERO_BODY_HASH {
383 f.body_hash = crate::program::compute_body_hash(
384 f.arity, f.locals_count, &f.code, &pool.record_shapes);
385 }
386 }
387
388 p.constants = pool.pool;
389 p.record_shapes = pool.record_shapes;
390 p
391}
392
393fn apply_escape_lowering(
421 code: &mut [Op],
422 fn_name: &str,
423 escape_index: &std::collections::HashMap<(String, u32), bool>,
424) {
425 for (pc, op) in code.iter_mut().enumerate() {
426 let key = (fn_name.to_string(), pc as u32);
433 if !matches!(escape_index.get(&key), Some(false)) {
434 continue;
435 }
436 match *op {
437 Op::MakeRecord { shape_idx, field_count } => {
438 *op = Op::AllocStackRecord { shape_idx, field_count };
439 }
440 Op::MakeTuple(arity) => {
442 *op = Op::AllocStackTuple { arity };
443 }
444 _ => {}
445 }
446 }
447}
448
449fn apply_arena_lowering(
467 code: &mut [Op],
468 fn_name: &str,
469 arena_index: &std::collections::HashMap<(String, u32), bool>,
470) {
471 for (pc, op) in code.iter_mut().enumerate() {
472 let key = (fn_name.to_string(), pc as u32);
476 if !matches!(arena_index.get(&key), Some(true)) {
477 continue;
478 }
479 match *op {
480 Op::MakeRecord { shape_idx, field_count } => {
481 *op = Op::AllocArenaRecord { shape_idx, field_count };
482 }
483 Op::MakeTuple(arity) => {
484 *op = Op::AllocArenaTuple { arity };
485 }
486 _ => {}
487 }
488 }
489}
490
491fn apply_peephole(code: &mut [Op], constants: &[Const]) {
492 if code.len() < 3 { return; }
493 let jump_targets = collect_jump_targets(code);
494
495 let n = code.len();
496 let mut k = 0;
497 while k + 2 < n {
498 if let (Op::LoadLocal(local_idx), Op::PushConst(imm_const_idx), Op::IntAdd)
499 = (code[k], code[k + 1], code[k + 2])
500 {
501 let imm_is_int = matches!(
502 constants.get(imm_const_idx as usize),
503 Some(Const::Int(_))
504 );
505 let safe = imm_is_int
509 && !jump_targets.contains(&(k + 1))
510 && !jump_targets.contains(&(k + 2));
511 if safe {
512 code[k] = Op::LoadLocalAddIntConst { local_idx, imm_const_idx };
513 k += 3;
514 continue;
515 }
516 }
517 k += 1;
518 }
519}
520
521fn apply_peephole_slice2(code: &mut [Op]) {
528 if code.len() < 4 { return; }
529 let jump_targets = collect_jump_targets(code);
530
531 let n = code.len();
532 let mut k = 0;
533 while k + 3 < n {
534 if let (
535 Op::LoadLocalAddIntConst { local_idx: src, imm_const_idx },
536 _,
537 _,
538 Op::StoreLocal(dest),
539 ) = (code[k], code[k + 1], code[k + 2], code[k + 3])
540 {
541 let safe = !jump_targets.contains(&(k + 1))
551 && !jump_targets.contains(&(k + 2))
552 && !jump_targets.contains(&(k + 3));
553 if safe {
554 code[k] = Op::LoadLocalAddIntConstStoreLocal {
555 src,
556 imm_const_idx,
557 dest,
558 };
559 k += 4;
560 continue;
561 }
562 }
563 k += 1;
564 }
565}
566
567fn apply_peephole_slice3(code: &mut [Op]) {
585 if code.len() < 3 { return; }
586 let jump_targets = collect_jump_targets(code);
587
588 let n = code.len();
589 let mut k = 0;
590 while k + 2 < n {
591 if let (Op::LoadLocal(lhs_idx), Op::LoadLocal(rhs_idx), Op::IntAdd)
592 = (code[k], code[k + 1], code[k + 2])
593 {
594 let safe = !jump_targets.contains(&(k + 1))
595 && !jump_targets.contains(&(k + 2));
596 if safe {
597 code[k] = Op::LoadLocalAddLocal { lhs_idx, rhs_idx };
598 k += 3;
599 continue;
600 }
601 }
602 k += 1;
603 }
604}
605
606fn apply_peephole_slice4(code: &mut [Op]) {
618 if code.len() < 3 { return; }
619 let jump_targets = collect_jump_targets(code);
620
621 let n = code.len();
622 let mut k = 0;
623 while k + 2 < n {
624 if let (Op::LoadLocal(lhs_idx), Op::LoadLocal(rhs_idx), terminator)
625 = (code[k], code[k + 1], code[k + 2])
626 {
627 let fused = match terminator {
628 Op::IntSub => Some(Op::LoadLocalSubLocal { lhs_idx, rhs_idx }),
629 Op::IntMul => Some(Op::LoadLocalMulLocal { lhs_idx, rhs_idx }),
630 _ => None,
631 };
632 if let Some(fused_op) = fused {
633 let safe = !jump_targets.contains(&(k + 1))
634 && !jump_targets.contains(&(k + 2));
635 if safe {
636 code[k] = fused_op;
637 k += 3;
638 continue;
639 }
640 }
641 }
642 k += 1;
643 }
644}
645
646fn apply_peephole_slice5(code: &mut [Op], constants: &[Const]) {
666 if code.len() < 4 { return; }
667 let jump_targets = collect_jump_targets(code);
668
669 let n = code.len();
670 let mut k = 0;
671 while k + 3 < n {
672 let lhs_idx = match code[k] {
674 Op::LoadLocal(i) => i,
675 _ => { k += 1; continue; }
676 };
677 let fused = match (code[k + 1], code[k + 2], code[k + 3]) {
680 (Op::PushConst(imm_const_idx), Op::IntEq, Op::JumpIfNot(jump_offset))
681 if matches!(constants.get(imm_const_idx as usize), Some(Const::Int(_))) =>
682 Some(Op::LoadLocalEqIntConstJumpIfNot {
683 local_idx: lhs_idx, imm_const_idx, jump_offset,
684 }),
685 _ => None,
686 };
687 if let Some(fused_op) = fused {
688 let safe = !jump_targets.contains(&(k + 1))
689 && !jump_targets.contains(&(k + 2))
690 && !jump_targets.contains(&(k + 3));
691 if safe {
692 code[k] = fused_op;
693 k += 4;
694 continue;
695 }
696 }
697 k += 1;
698 }
699}
700
701fn apply_peephole_slice6(code: &mut [Op]) {
724 if code.len() < 3 { return; }
725 let jump_targets = collect_jump_targets(code);
726
727 let n = code.len();
728 let mut k = 0;
729 while k + 2 < n {
730 if let (
731 Op::LoadLocal(src),
732 Op::StoreLocal(dst),
733 Op::LoadLocalEqIntConstJumpIfNot { local_idx, imm_const_idx, jump_offset },
734 ) = (code[k], code[k + 1], code[k + 2]) {
735 if local_idx == dst {
739 let safe = !jump_targets.contains(&(k + 1))
740 && !jump_targets.contains(&(k + 2));
741 if safe {
742 code[k] = Op::LoadLocalStoreEqIntConstJumpIfNot {
743 src, dst, imm_const_idx, jump_offset,
744 };
745 k += 3;
750 continue;
751 }
752 }
753 }
754 k += 1;
755 }
756}
757
758fn apply_peephole_slice7(code: &mut [Op]) {
784 if code.len() < 3 { return; }
785 let jump_targets = collect_jump_targets(code);
786
787 let n = code.len();
788 let mut k = 0;
789 while k + 2 < n {
790 if let (Op::LoadLocal(local_idx), Op::GetField { name_idx, site_idx })
791 = (code[k], code[k + 1])
792 {
793 let fused = match code[k + 2] {
794 Op::IntAdd => Some(Op::LoadLocalGetFieldAdd { local_idx, name_idx, site_idx }),
795 Op::IntSub => Some(Op::LoadLocalGetFieldSub { local_idx, name_idx, site_idx }),
796 Op::IntMul => Some(Op::LoadLocalGetFieldMul { local_idx, name_idx, site_idx }),
797 _ => None,
798 };
799 if let Some(op) = fused {
800 let safe = !jump_targets.contains(&(k + 1))
801 && !jump_targets.contains(&(k + 2));
802 if safe {
803 code[k] = op;
804 k += 3;
805 continue;
806 }
807 }
808 }
809 k += 1;
810 }
811}
812
813fn apply_peephole_slice9(code: &mut [Op]) {
837 if code.len() < 2 { return; }
838 let jump_targets = collect_jump_targets(code);
839
840 let n = code.len();
841 let mut k = 0;
842 while k + 1 < n {
843 if let (Op::LoadLocal(local_idx), Op::GetField { name_idx, site_idx })
844 = (code[k], code[k + 1])
845 {
846 if !jump_targets.contains(&(k + 1)) {
847 code[k] = Op::LoadLocalGetField { local_idx, name_idx, site_idx };
848 k += 2;
849 continue;
850 }
851 }
852 k += 1;
853 }
854}
855
856fn collect_jump_targets(code: &[Op]) -> std::collections::HashSet<usize> {
857 let mut targets = std::collections::HashSet::new();
858 for (pc, op) in code.iter().enumerate() {
859 let off = match op {
860 Op::Jump(off) | Op::JumpIf(off) | Op::JumpIfNot(off) => Some(*off),
861 _ => None,
862 };
863 if let Some(off) = off {
864 let target = (pc as i32 + 1 + off) as usize;
865 targets.insert(target);
866 }
867 }
868 targets
869}
870
871#[derive(Debug, Clone)]
872struct PendingLambda {
873 fn_id: u32,
874 capture_names: Vec<String>,
876 params: Vec<a::Param>,
877 body: a::CExpr,
878}
879
880struct FnCompiler<'a> {
881 code: Vec<Op>,
882 locals: IndexMap<String, u16>,
883 next_local: u16,
884 peak_local: u16,
886 local_types: IndexMap<String, NumTy>,
901 local_record_field_types: IndexMap<String, IndexMap<String, NumTy>>,
916 field_get_sites: u32,
923 pool: &'a mut ConstPool,
924 function_names: &'a IndexMap<String, u32>,
925 module_aliases: &'a IndexMap<String, String>,
926 id_map: &'a std::collections::HashMap<*const a::CExpr, lex_ast::NodeId>,
928 pending_lambdas: &'a mut Vec<PendingLambda>,
931 next_fn_id: &'a mut Vec<Function>,
934}
935
936#[derive(Debug, Clone, Copy, PartialEq, Eq)]
942enum NumTy { Int, Float, Unknown }
943
944fn record_field_types(
949 ty: &a::TypeExpr,
950 type_aliases: &IndexMap<String, a::TypeExpr>,
951) -> Option<IndexMap<String, NumTy>> {
952 match ty {
953 a::TypeExpr::Record { fields } => {
954 let mut m = IndexMap::new();
955 for f in fields {
956 m.insert(f.name.clone(), classify_type_expr(&f.ty));
957 }
958 Some(m)
959 }
960 a::TypeExpr::Refined { base, .. } => record_field_types(base, type_aliases),
961 a::TypeExpr::Named { name, args } if args.is_empty() => {
962 type_aliases.get(name).and_then(|t| record_field_types(t, type_aliases))
966 }
967 _ => None,
968 }
969}
970
971fn classify_type_expr(ty: &a::TypeExpr) -> NumTy {
972 match ty {
973 a::TypeExpr::Named { name, args } if args.is_empty() => match name.as_str() {
974 "Int" => NumTy::Int,
975 "Float" => NumTy::Float,
976 _ => NumTy::Unknown,
977 },
978 a::TypeExpr::Refined { base, .. } => classify_type_expr(base),
981 _ => NumTy::Unknown,
982 }
983}
984
985impl<'a> FnCompiler<'a> {
986 fn alloc_local(&mut self, name: &str) -> u16 {
987 let i = self.next_local;
988 self.locals.insert(name.into(), i);
989 self.next_local += 1;
990 if self.next_local > self.peak_local { self.peak_local = self.next_local; }
991 i
992 }
993 fn emit(&mut self, op: Op) { self.code.push(op); }
994
995 fn compile_expr(&mut self, e: &a::CExpr, tail: bool) {
996 match e {
997 a::CExpr::Literal { value } => self.compile_lit(value),
998 a::CExpr::Var { name } => {
999 if let Some(slot) = self.locals.get(name) {
1000 self.emit(Op::LoadLocal(*slot));
1001 } else if let Some(&fn_id) = self.function_names.get(name) {
1002 self.emit(Op::MakeClosure { fn_id, capture_count: 0 });
1008 } else {
1009 panic!("unknown var in compiler: {name}");
1013 }
1014 }
1015 a::CExpr::Let { name, ty, value, body } => {
1016 let nty = match ty {
1021 Some(t) => classify_type_expr(t),
1022 None => self.classify_expr(value),
1023 };
1024 if let a::CExpr::RecordLit { fields } = value.as_ref() {
1030 let mut ftypes = IndexMap::new();
1031 for f in fields {
1032 let fty = self.classify_expr(&f.value);
1033 ftypes.insert(f.name.clone(), fty);
1034 }
1035 self.local_record_field_types.insert(name.clone(), ftypes);
1036 }
1037 self.compile_expr(value, false);
1038 let slot = self.alloc_local(name);
1039 self.local_types.insert(name.clone(), nty);
1040 self.emit(Op::StoreLocal(slot));
1041 self.compile_expr(body, tail);
1042 }
1043 a::CExpr::Block { statements, result } => {
1044 for s in statements {
1045 self.compile_expr(s, false);
1046 self.emit(Op::Pop);
1047 }
1048 self.compile_expr(result, tail);
1049 }
1050 a::CExpr::Call { callee, args } => self.compile_call(e, callee, args, tail),
1051 a::CExpr::Constructor { name, args } => {
1052 for a in args { self.compile_expr(a, false); }
1053 let name_idx = self.pool.variant(name);
1054 self.emit(Op::MakeVariant { name_idx, arity: args.len() as u16 });
1055 }
1056 a::CExpr::Match { scrutinee, arms } => self.compile_match(scrutinee, arms, tail),
1057 a::CExpr::RecordLit { fields } => {
1058 let mut idxs = Vec::with_capacity(fields.len());
1059 for f in fields {
1060 self.compile_expr(&f.value, false);
1061 idxs.push(self.pool.field(&f.name));
1062 }
1063 let field_count = idxs.len() as u16;
1064 let shape_idx = self.pool.record_shape(idxs);
1065 self.emit(Op::MakeRecord { shape_idx, field_count });
1066 }
1067 a::CExpr::TupleLit { items } => {
1068 for it in items { self.compile_expr(it, false); }
1069 self.emit(Op::MakeTuple(items.len() as u16));
1070 }
1071 a::CExpr::ListLit { items } => {
1072 for it in items { self.compile_expr(it, false); }
1073 self.emit(Op::MakeList(items.len() as u32));
1074 }
1075 a::CExpr::FieldAccess { value, field } => {
1076 self.compile_expr(value, false);
1077 let name_idx = self.pool.field(field);
1078 let site_idx = self.field_get_sites;
1079 self.field_get_sites += 1;
1080 self.emit(Op::GetField { name_idx, site_idx });
1081 }
1082 a::CExpr::BinOp { op, lhs, rhs } => self.compile_binop(op, lhs, rhs),
1083 a::CExpr::UnaryOp { op, expr } => {
1084 self.compile_expr(expr, false);
1085 match op.as_str() {
1086 "-" => self.emit(Op::NumNeg),
1087 "not" => self.emit(Op::BoolNot),
1088 other => panic!("unknown unary: {other}"),
1089 }
1090 }
1091 a::CExpr::Lambda { params, body, .. } => self.compile_lambda(params, body),
1092 a::CExpr::Return { value } => {
1093 self.compile_expr(value, true);
1094 self.emit(Op::Return);
1095 }
1096 }
1097 }
1098
1099 fn compile_lit(&mut self, l: &a::CLit) {
1100 let i = match l {
1101 a::CLit::Int { value } => self.pool.int(*value),
1102 a::CLit::Bool { value } => self.pool.bool(*value),
1103 a::CLit::Float { value } => {
1104 let f: f64 = value.parse().unwrap_or(0.0);
1105 self.pool.float(f)
1106 }
1107 a::CLit::Str { value } => self.pool.str(value),
1108 a::CLit::Bytes { value: _ } => {
1109 let i = self.pool.pool.len() as u32;
1111 self.pool.pool.push(Const::Bytes(Vec::new()));
1112 i
1113 }
1114 a::CLit::Unit => self.pool.unit(),
1115 };
1116 self.emit(Op::PushConst(i));
1117 }
1118
1119 fn compile_call(&mut self, call_expr: &a::CExpr, callee: &a::CExpr, args: &[a::CExpr], tail: bool) {
1120 let node_id = self
1121 .id_map
1122 .get(&(call_expr as *const a::CExpr))
1123 .map(|n| n.as_str().to_string())
1124 .unwrap_or_else(|| "n_?".into());
1125 let node_id_idx = self.pool.node_id(&node_id);
1126
1127 if let a::CExpr::FieldAccess { value, field } = callee {
1132 if let a::CExpr::Var { name } = value.as_ref() {
1133 if let Some(module) = self.module_aliases.get(name) {
1134 if self.try_emit_higher_order(module, field, args, node_id_idx) {
1135 let _ = tail;
1136 return;
1137 }
1138 for a in args { self.compile_expr(a, false); }
1139 let kind_idx = self.pool.str(module);
1140 let op_idx = self.pool.str(field);
1141 self.emit(Op::EffectCall {
1142 kind_idx,
1143 op_idx,
1144 arity: args.len() as u16,
1145 node_id_idx,
1146 });
1147 let _ = tail;
1148 return;
1149 }
1150 }
1151 }
1152 match callee {
1153 a::CExpr::Var { name } if self.function_names.contains_key(name) => {
1154 for a in args { self.compile_expr(a, false); }
1155 let fn_id = self.function_names[name];
1156 if tail {
1157 self.emit(Op::TailCall { fn_id, arity: args.len() as u16, node_id_idx });
1158 } else {
1159 self.emit(Op::Call { fn_id, arity: args.len() as u16, node_id_idx });
1160 }
1161 }
1162 a::CExpr::Var { name } if self.locals.contains_key(name) => {
1163 let slot = self.locals[name];
1166 self.emit(Op::LoadLocal(slot));
1167 for a in args { self.compile_expr(a, false); }
1168 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
1169 }
1170 other => {
1172 self.compile_expr(other, false);
1173 for a in args { self.compile_expr(a, false); }
1174 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
1175 }
1176 }
1177 }
1178
1179 fn compile_binop(&mut self, op: &str, lhs: &a::CExpr, rhs: &a::CExpr) {
1180 let lhs_ty = self.classify_expr(lhs);
1191 let rhs_ty = self.classify_expr(rhs);
1192 let typed = match (lhs_ty, rhs_ty) {
1193 (NumTy::Int, NumTy::Int) => NumTy::Int,
1194 (NumTy::Float, NumTy::Float) => NumTy::Float,
1195 _ => NumTy::Unknown,
1196 };
1197 self.compile_expr(lhs, false);
1198 self.compile_expr(rhs, false);
1199 match (op, typed) {
1200 ("+", NumTy::Int) => self.emit(Op::IntAdd),
1201 ("+", NumTy::Float) => self.emit(Op::FloatAdd),
1202 ("+", NumTy::Unknown) => self.emit(Op::NumAdd),
1203 ("-", NumTy::Int) => self.emit(Op::IntSub),
1204 ("-", NumTy::Float) => self.emit(Op::FloatSub),
1205 ("-", NumTy::Unknown) => self.emit(Op::NumSub),
1206 ("*", NumTy::Int) => self.emit(Op::IntMul),
1207 ("*", NumTy::Float) => self.emit(Op::FloatMul),
1208 ("*", NumTy::Unknown) => self.emit(Op::NumMul),
1209 ("/", NumTy::Int) => self.emit(Op::IntDiv),
1210 ("/", NumTy::Float) => self.emit(Op::FloatDiv),
1211 ("/", NumTy::Unknown) => self.emit(Op::NumDiv),
1212 ("%", NumTy::Int) => self.emit(Op::IntMod),
1214 ("%", _) => self.emit(Op::NumMod),
1215 ("==", NumTy::Int) => self.emit(Op::IntEq),
1216 ("==", NumTy::Float) => self.emit(Op::FloatEq),
1217 ("==", NumTy::Unknown) => self.emit(Op::NumEq),
1218 ("!=", NumTy::Int) => { self.emit(Op::IntEq); self.emit(Op::BoolNot); }
1219 ("!=", NumTy::Float) => { self.emit(Op::FloatEq); self.emit(Op::BoolNot); }
1220 ("!=", NumTy::Unknown) => { self.emit(Op::NumEq); self.emit(Op::BoolNot); }
1221 ("<", NumTy::Int) => self.emit(Op::IntLt),
1222 ("<", NumTy::Float) => self.emit(Op::FloatLt),
1223 ("<", NumTy::Unknown) => self.emit(Op::NumLt),
1224 ("<=", NumTy::Int) => self.emit(Op::IntLe),
1225 ("<=", NumTy::Float) => self.emit(Op::FloatLe),
1226 ("<=", NumTy::Unknown) => self.emit(Op::NumLe),
1227 (">", NumTy::Int) => { self.emit_swap_top2(); self.emit(Op::IntLt); }
1228 (">", NumTy::Float) => { self.emit_swap_top2(); self.emit(Op::FloatLt); }
1229 (">", NumTy::Unknown) => { self.emit_swap_top2(); self.emit(Op::NumLt); }
1230 (">=", NumTy::Int) => { self.emit_swap_top2(); self.emit(Op::IntLe); }
1231 (">=", NumTy::Float) => { self.emit_swap_top2(); self.emit(Op::FloatLe); }
1232 (">=", NumTy::Unknown) => { self.emit_swap_top2(); self.emit(Op::NumLe); }
1233 ("and", _) => self.emit(Op::BoolAnd),
1234 ("or", _) => self.emit(Op::BoolOr),
1235 (other, _) => panic!("unknown binop: {other:?}"),
1236 }
1237 }
1238
1239 fn classify_expr(&self, e: &a::CExpr) -> NumTy {
1247 match e {
1248 a::CExpr::Literal { value: a::CLit::Int { .. } } => NumTy::Int,
1249 a::CExpr::Literal { value: a::CLit::Float { .. } } => NumTy::Float,
1250 a::CExpr::Var { name } =>
1251 self.local_types.get(name).copied().unwrap_or(NumTy::Unknown),
1252 a::CExpr::BinOp { op, lhs, rhs } => {
1253 let is_numeric = matches!(op.as_str(), "+" | "-" | "*" | "/" | "%");
1257 if !is_numeric { return NumTy::Unknown; }
1258 match (self.classify_expr(lhs), self.classify_expr(rhs)) {
1259 (NumTy::Int, NumTy::Int) => NumTy::Int,
1260 (NumTy::Float, NumTy::Float) => NumTy::Float,
1261 _ => NumTy::Unknown,
1262 }
1263 }
1264 a::CExpr::UnaryOp { op, expr } if op == "-" => self.classify_expr(expr),
1265 a::CExpr::FieldAccess { value, field } => {
1272 if let a::CExpr::Var { name } = value.as_ref() {
1273 if let Some(ftypes) = self.local_record_field_types.get(name) {
1274 return ftypes.get(field).copied().unwrap_or(NumTy::Unknown);
1275 }
1276 }
1277 NumTy::Unknown
1278 }
1279 _ => NumTy::Unknown,
1283 }
1284 }
1285
1286 fn emit_swap_top2(&mut self) {
1287 let a = self.alloc_local("__swap_a");
1288 let b = self.alloc_local("__swap_b");
1289 self.emit(Op::StoreLocal(b));
1290 self.emit(Op::StoreLocal(a));
1291 self.emit(Op::LoadLocal(b));
1292 self.emit(Op::LoadLocal(a));
1293 }
1294
1295 fn compile_match(&mut self, scrutinee: &a::CExpr, arms: &[a::Arm], tail: bool) {
1296 self.compile_expr(scrutinee, false);
1297 let scrut_slot = self.alloc_local("__scrut");
1298 self.emit(Op::StoreLocal(scrut_slot));
1299
1300 let mut end_jumps: Vec<usize> = Vec::new();
1301 for arm in arms {
1302 let arm_start_locals = self.next_local;
1303 let arm_start_locals_map = self.locals.clone();
1304
1305 self.emit(Op::LoadLocal(scrut_slot));
1306 let mut bindings: Vec<(String, u16)> = Vec::new();
1307 let fail_jumps: Vec<usize> = self.compile_pattern_test(&arm.pattern, &mut bindings);
1308
1309 self.compile_expr(&arm.body, tail);
1310 let j_end = self.code.len();
1311 self.emit(Op::Jump(0));
1312 end_jumps.push(j_end);
1313
1314 let fail_target = self.code.len() as i32;
1315 for j in fail_jumps {
1316 match &mut self.code[j] {
1322 Op::JumpIfNot(off) => *off = fail_target - (j as i32 + 1),
1323 Op::Jump(off) => *off = fail_target - (j as i32 + 1),
1324 _ => {}
1325 }
1326 }
1327 self.next_local = arm_start_locals;
1328 self.locals = arm_start_locals_map;
1329 }
1330 let panic_msg_idx = self.pool.str("non-exhaustive match");
1331 self.emit(Op::Panic(panic_msg_idx));
1332
1333 let end_target = self.code.len() as i32;
1334 for j in end_jumps {
1335 if let Op::Jump(off) = &mut self.code[j] {
1336 *off = end_target - (j as i32 + 1);
1337 }
1338 }
1339 }
1340
1341 fn compile_pattern_test(&mut self, p: &a::Pattern, bindings: &mut Vec<(String, u16)>) -> Vec<usize> {
1342 let mut fails = Vec::new();
1343 match p {
1344 a::Pattern::PWild => { self.emit(Op::Pop); }
1345 a::Pattern::PVar { name } => {
1346 let slot = self.alloc_local(name);
1347 self.emit(Op::StoreLocal(slot));
1348 bindings.push((name.clone(), slot));
1349 }
1350 a::Pattern::PLiteral { value } => {
1351 self.compile_lit(value);
1352 match value {
1353 a::CLit::Str { .. } => self.emit(Op::StrEq),
1354 a::CLit::Bytes { .. } => self.emit(Op::BytesEq),
1355 a::CLit::Int { .. } => self.emit(Op::IntEq),
1366 a::CLit::Float { .. } => self.emit(Op::FloatEq),
1367 _ => self.emit(Op::NumEq),
1368 }
1369 let j = self.code.len();
1370 self.emit(Op::JumpIfNot(0));
1371 fails.push(j);
1372 }
1373 a::Pattern::PConstructor { name, args } => {
1374 let name_idx = self.pool.variant(name);
1375 self.emit(Op::Dup); self.emit(Op::TestVariant(name_idx)); let j_success = self.code.len();
1392 self.emit(Op::JumpIf(0)); self.emit(Op::Pop); let j_fail = self.code.len();
1395 self.emit(Op::Jump(0)); fails.push(j_fail);
1397 let success_target = self.code.len() as i32;
1398 if let Op::JumpIf(off) = &mut self.code[j_success] {
1399 *off = success_target - (j_success as i32 + 1);
1400 }
1401 if args.is_empty() {
1402 self.emit(Op::Pop);
1403 } else if args.len() == 1 {
1404 self.emit(Op::GetVariantArg(0));
1405 let sub_fails = self.compile_pattern_test(&args[0], bindings);
1406 fails.extend(sub_fails);
1407 } else {
1408 let slot = self.alloc_local("__variant");
1409 self.emit(Op::StoreLocal(slot));
1410 for (i, arg) in args.iter().enumerate() {
1411 self.emit(Op::LoadLocal(slot));
1412 self.emit(Op::GetVariantArg(i as u16));
1413 let sub_fails = self.compile_pattern_test(arg, bindings);
1414 fails.extend(sub_fails);
1415 }
1416 }
1417 }
1418 a::Pattern::PRecord { fields } => {
1419 let slot = self.alloc_local("__record");
1420 self.emit(Op::StoreLocal(slot));
1421 for f in fields {
1422 self.emit(Op::LoadLocal(slot));
1423 let name_idx = self.pool.field(&f.name);
1424 let site_idx = self.field_get_sites;
1425 self.field_get_sites += 1;
1426 self.emit(Op::GetField { name_idx, site_idx });
1427 let sub_fails = self.compile_pattern_test(&f.pattern, bindings);
1428 fails.extend(sub_fails);
1429 }
1430 }
1431 a::Pattern::PTuple { items } => {
1432 let slot = self.alloc_local("__tuple");
1433 self.emit(Op::StoreLocal(slot));
1434 for (i, item) in items.iter().enumerate() {
1435 self.emit(Op::LoadLocal(slot));
1436 self.emit(Op::GetElem(i as u16));
1437 let sub_fails = self.compile_pattern_test(item, bindings);
1438 fails.extend(sub_fails);
1439 }
1440 }
1441 }
1442 fails
1443 }
1444
1445 fn compile_lambda(&mut self, params: &[a::Param], body: &a::CExpr) {
1449 let mut bound: std::collections::HashSet<String> = params.iter().map(|p| p.name.clone()).collect();
1451 let mut frees: Vec<String> = Vec::new();
1452 free_vars(body, &mut bound, &mut frees);
1453
1454 let captures: Vec<String> = frees.into_iter()
1463 .filter(|n| self.locals.contains_key(n))
1464 .collect();
1465
1466 let fn_id = self.next_fn_id.len() as u32;
1468 self.next_fn_id.push(Function {
1469 name: format!("__lambda_{fn_id}"),
1470 arity: (captures.len() + params.len()) as u16,
1471 locals_count: 0,
1472 code: Vec::new(),
1473 effects: Vec::new(),
1474 body_hash: crate::program::ZERO_BODY_HASH,
1476 refinements: Vec::new(),
1481 field_ic_sites: 0,
1484 });
1485
1486 for c in &captures {
1488 let slot = *self.locals.get(c).expect("free var must be in scope");
1489 self.emit(Op::LoadLocal(slot));
1490 }
1491 self.emit(Op::MakeClosure { fn_id, capture_count: captures.len() as u16 });
1492
1493 self.pending_lambdas.push(PendingLambda {
1495 fn_id,
1496 capture_names: captures,
1497 params: params.to_vec(),
1498 body: body.clone(),
1499 });
1500 }
1501
1502 fn try_emit_higher_order(
1506 &mut self,
1507 module: &str,
1508 op: &str,
1509 args: &[a::CExpr],
1510 node_id_idx: u32,
1511 ) -> bool {
1512 match (module, op) {
1513 ("result", "map") => self.emit_variant_map(args, "Ok", true),
1514 ("result", "and_then") => self.emit_variant_map(args, "Ok", false),
1515 ("result", "map_err") => self.emit_variant_map(args, "Err", true),
1516 ("result", "or_else") => self.emit_variant_or_else(args, "Err", 1),
1517 ("option", "map") => self.emit_variant_map(args, "Some", true),
1518 ("option", "and_then") => self.emit_variant_map(args, "Some", false),
1519 ("option", "or_else") => self.emit_variant_or_else(args, "None", 0),
1520 ("option", "unwrap_or_else") => self.emit_option_unwrap_or_else(args),
1521 ("list", "map") => self.emit_list_map(args),
1522 ("list", "par_map") => self.emit_list_par_map(args),
1523 ("list", "sort_by") => self.emit_list_sort_by(args),
1524 ("list", "filter") => self.emit_list_filter(args),
1525 ("list", "fold") => self.emit_list_fold(args),
1526 ("iter", "from_list") => self.emit_iter_from_list(args),
1527 ("iter", "unfold") => self.emit_iter_unfold(args),
1528 ("iter", "next") => self.emit_iter_next(args),
1529 ("iter", "is_empty") => self.emit_iter_is_empty(args),
1530 ("iter", "count") => self.emit_iter_count(args),
1531 ("iter", "take") => self.emit_iter_take(args),
1532 ("iter", "skip") => self.emit_iter_skip(args),
1533 ("iter", "to_list") => self.emit_iter_to_list(args),
1534 ("iter", "collect") => self.emit_iter_to_list(args),
1535 ("iter", "map") => self.emit_iter_map(args),
1536 ("iter", "filter") => self.emit_iter_filter(args),
1537 ("iter", "fold") => self.emit_iter_fold(args),
1538 ("map", "fold") => self.emit_map_fold(args, node_id_idx),
1539 ("flow", "sequential") => self.emit_flow_sequential(args),
1540 ("flow", "branch") => self.emit_flow_branch(args),
1541 ("flow", "retry") => self.emit_flow_retry(args),
1542 ("flow", "retry_with_backoff") => self.emit_flow_retry_with_backoff(args),
1543 ("flow", "parallel") => self.emit_flow_parallel(args),
1544 ("flow", "parallel_list") => self.emit_flow_parallel_list(args),
1545 _ => return false,
1546 }
1547 true
1548 }
1549
1550 fn emit_list_map(&mut self, args: &[a::CExpr]) {
1556 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_map");
1559 self.emit(Op::ListMap { node_id_idx: nid });
1560 }
1561
1562 fn emit_list_par_map(&mut self, args: &[a::CExpr]) {
1568 self.compile_expr(&args[0], false);
1569 self.compile_expr(&args[1], false);
1570 let nid = self.pool.node_id("n_list_par_map");
1571 self.emit(Op::ParallelMap { node_id_idx: nid });
1572 }
1573
1574 fn emit_list_sort_by(&mut self, args: &[a::CExpr]) {
1582 self.compile_expr(&args[0], false);
1583 self.compile_expr(&args[1], false);
1584 let nid = self.pool.node_id("n_list_sort_by");
1585 self.emit(Op::SortByKey { node_id_idx: nid });
1586 }
1587
1588 fn emit_list_filter(&mut self, args: &[a::CExpr]) {
1591 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_filter");
1594 self.emit(Op::ListFilter { node_id_idx: nid });
1595 }
1596
1597 fn emit_list_fold(&mut self, args: &[a::CExpr]) {
1600 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");
1604 self.emit(Op::ListFold { node_id_idx: nid });
1605 }
1606
1607 fn emit_iter_from_list(&mut self, args: &[a::CExpr]) {
1619 self.compile_expr(&args[0], false);
1620 let zero = self.pool.int(0);
1621 self.emit(Op::PushConst(zero));
1622 let v = self.pool.variant("__IterEager");
1623 self.emit(Op::MakeVariant { name_idx: v, arity: 2 });
1624 }
1625
1626 fn emit_iter_next(&mut self, args: &[a::CExpr]) {
1638 self.compile_expr(&args[0], false);
1639 let it = self.alloc_local("__in_it");
1640 self.emit(Op::StoreLocal(it));
1641
1642 self.emit(Op::LoadLocal(it));
1644 self.emit(Op::Dup);
1645 let lazy_name = self.pool.variant("__IterLazy");
1646 self.emit(Op::TestVariant(lazy_name));
1647 let j_to_check_cursor = self.code.len();
1648 self.emit(Op::JumpIfNot(0));
1649
1650 self.emit(Op::LoadLocal(it));
1654 self.emit(Op::GetVariantArg(0)); let seed = self.alloc_local("__in_seed");
1656 self.emit(Op::StoreLocal(seed));
1657
1658 self.emit(Op::LoadLocal(it));
1659 self.emit(Op::GetVariantArg(1)); let step = self.alloc_local("__in_step");
1661 self.emit(Op::StoreLocal(step));
1662
1663 let nid_lazy = self.pool.node_id("n_iter_next_lazy");
1665 self.emit(Op::LoadLocal(step));
1666 self.emit(Op::LoadLocal(seed));
1667 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
1668 let opt = self.alloc_local("__in_opt");
1669 self.emit(Op::StoreLocal(opt));
1670
1671 self.emit(Op::LoadLocal(opt));
1673 let some_name = self.pool.variant("Some");
1674 self.emit(Op::TestVariant(some_name));
1675 let j_lazy_none = self.code.len();
1676 self.emit(Op::JumpIfNot(0));
1677
1678 self.emit(Op::LoadLocal(opt));
1681 self.emit(Op::GetVariantArg(0)); let pair = self.alloc_local("__in_pair");
1683 self.emit(Op::StoreLocal(pair));
1684
1685 self.emit(Op::LoadLocal(pair));
1686 self.emit(Op::GetElem(0)); self.emit(Op::LoadLocal(pair));
1688 self.emit(Op::GetElem(1)); self.emit(Op::LoadLocal(step)); let lazy_v = self.pool.variant("__IterLazy");
1691 self.emit(Op::MakeVariant { name_idx: lazy_v, arity: 2 }); self.emit(Op::MakeTuple(2)); let some_v = self.pool.variant("Some");
1694 self.emit(Op::MakeVariant { name_idx: some_v, arity: 1 });
1695 let j_after_lazy = self.code.len();
1696 self.emit(Op::Jump(0));
1697
1698 let none_t = self.code.len() as i32;
1700 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_none] {
1701 *off = none_t - (j_lazy_none as i32 + 1);
1702 }
1703 let none_v = self.pool.variant("None");
1704 self.emit(Op::MakeVariant { name_idx: none_v, arity: 0 });
1705 let j_after_lazy_none = self.code.len();
1706 self.emit(Op::Jump(0));
1707
1708 let cursor_check_t = self.code.len() as i32;
1710 if let Op::JumpIfNot(off) = &mut self.code[j_to_check_cursor] {
1711 *off = cursor_check_t - (j_to_check_cursor as i32 + 1);
1712 }
1713
1714 self.emit(Op::LoadLocal(it));
1715 self.emit(Op::Dup);
1716 let cursor_name = self.pool.variant("__IterCursor");
1717 self.emit(Op::TestVariant(cursor_name));
1718 let j_to_eager = self.code.len();
1719 self.emit(Op::JumpIfNot(0));
1720
1721 self.emit(Op::LoadLocal(it));
1725 self.emit(Op::GetVariantArg(0)); let handle = self.alloc_local("__in_handle");
1727 self.emit(Op::StoreLocal(handle));
1728
1729 let kind_idx = self.pool.str("sql");
1730 let op_idx = self.pool.str("cursor_next");
1731 let nid_cursor = self.pool.node_id("n_iter_next_cursor");
1732 self.emit(Op::LoadLocal(handle));
1733 self.emit(Op::EffectCall {
1734 kind_idx,
1735 op_idx,
1736 arity: 1,
1737 node_id_idx: nid_cursor,
1738 });
1739 let cur_opt = self.alloc_local("__in_cur_opt");
1740 self.emit(Op::StoreLocal(cur_opt));
1741
1742 self.emit(Op::LoadLocal(cur_opt));
1743 let some_c = self.pool.variant("Some");
1744 self.emit(Op::TestVariant(some_c));
1745 let j_cursor_none = self.code.len();
1746 self.emit(Op::JumpIfNot(0));
1747
1748 self.emit(Op::LoadLocal(cur_opt));
1750 self.emit(Op::GetVariantArg(0)); self.emit(Op::LoadLocal(handle));
1752 let cursor_v = self.pool.variant("__IterCursor");
1753 self.emit(Op::MakeVariant { name_idx: cursor_v, arity: 1 });
1754 self.emit(Op::MakeTuple(2)); let some_c2 = self.pool.variant("Some");
1756 self.emit(Op::MakeVariant { name_idx: some_c2, arity: 1 });
1757 let j_after_cursor = self.code.len();
1758 self.emit(Op::Jump(0));
1759
1760 let cursor_none_t = self.code.len() as i32;
1762 if let Op::JumpIfNot(off) = &mut self.code[j_cursor_none] {
1763 *off = cursor_none_t - (j_cursor_none as i32 + 1);
1764 }
1765 let none_c = self.pool.variant("None");
1766 self.emit(Op::MakeVariant { name_idx: none_c, arity: 0 });
1767 let j_after_cursor_none = self.code.len();
1768 self.emit(Op::Jump(0));
1769
1770 let eager_t = self.code.len() as i32;
1772 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
1773 *off = eager_t - (j_to_eager as i32 + 1);
1774 }
1775
1776 self.emit(Op::LoadLocal(it));
1777 self.emit(Op::GetVariantArg(0));
1778 let list = self.alloc_local("__in_list");
1779 self.emit(Op::StoreLocal(list));
1780
1781 self.emit(Op::LoadLocal(it));
1782 self.emit(Op::GetVariantArg(1));
1783 let idx = self.alloc_local("__in_idx");
1784 self.emit(Op::StoreLocal(idx));
1785
1786 self.emit(Op::LoadLocal(idx));
1788 self.emit(Op::LoadLocal(list));
1789 self.emit(Op::GetListLen);
1790 self.emit(Op::IntLt);
1791 let j_eager_else = self.code.len();
1792 self.emit(Op::JumpIfNot(0));
1793
1794 self.emit(Op::LoadLocal(list));
1796 self.emit(Op::LoadLocal(idx));
1797 self.emit(Op::GetListElemDyn);
1798
1799 self.emit(Op::LoadLocal(list));
1800 self.emit(Op::LoadLocal(idx));
1801 let one = self.pool.int(1);
1802 self.emit(Op::PushConst(one));
1803 self.emit(Op::IntAdd);
1804 let eager_v = self.pool.variant("__IterEager");
1805 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1806 self.emit(Op::MakeTuple(2));
1807 let some_e = self.pool.variant("Some");
1808 self.emit(Op::MakeVariant { name_idx: some_e, arity: 1 });
1809 let j_after_eager = self.code.len();
1810 self.emit(Op::Jump(0));
1811
1812 let eager_none_t = self.code.len() as i32;
1814 if let Op::JumpIfNot(off) = &mut self.code[j_eager_else] {
1815 *off = eager_none_t - (j_eager_else as i32 + 1);
1816 }
1817 let none_e = self.pool.variant("None");
1818 self.emit(Op::MakeVariant { name_idx: none_e, arity: 0 });
1819
1820 let end = self.code.len() as i32;
1822 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
1823 *off = end - (j_after_lazy as i32 + 1);
1824 }
1825 if let Op::Jump(off) = &mut self.code[j_after_lazy_none] {
1826 *off = end - (j_after_lazy_none as i32 + 1);
1827 }
1828 if let Op::Jump(off) = &mut self.code[j_after_cursor] {
1829 *off = end - (j_after_cursor as i32 + 1);
1830 }
1831 if let Op::Jump(off) = &mut self.code[j_after_cursor_none] {
1832 *off = end - (j_after_cursor_none as i32 + 1);
1833 }
1834 if let Op::Jump(off) = &mut self.code[j_after_eager] {
1835 *off = end - (j_after_eager as i32 + 1);
1836 }
1837 }
1838
1839 fn emit_iter_unfold(&mut self, args: &[a::CExpr]) {
1844 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let lazy = self.pool.variant("__IterLazy");
1847 self.emit(Op::MakeVariant { name_idx: lazy, arity: 2 });
1848 }
1849
1850 fn emit_iter_is_empty(&mut self, args: &[a::CExpr]) {
1856 self.compile_expr(&args[0], false);
1857 let it = self.alloc_local("__ie_it");
1858 self.emit(Op::StoreLocal(it));
1859
1860 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); }
1866
1867 fn emit_iter_count(&mut self, args: &[a::CExpr]) {
1869 self.compile_expr(&args[0], false);
1870 let it = self.alloc_local("__ic_it");
1871 self.emit(Op::StoreLocal(it));
1872
1873 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1874 self.emit(Op::GetListLen); self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1)); self.emit(Op::IntSub); }
1878
1879 fn emit_iter_take(&mut self, args: &[a::CExpr]) {
1881 self.compile_expr(&args[0], false);
1882 let it = self.alloc_local("__itk_it");
1883 self.emit(Op::StoreLocal(it));
1884
1885 self.compile_expr(&args[1], false);
1886 let n = self.alloc_local("__itk_n");
1887 self.emit(Op::StoreLocal(n));
1888
1889 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1890 let list = self.alloc_local("__itk_list");
1891 self.emit(Op::StoreLocal(list));
1892
1893 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1894 let i = self.alloc_local("__itk_i");
1895 self.emit(Op::StoreLocal(i));
1896
1897 self.emit(Op::MakeList(0));
1898 let out = self.alloc_local("__itk_out");
1899 self.emit(Op::StoreLocal(out));
1900
1901 let zero = self.pool.int(0);
1902 self.emit(Op::PushConst(zero));
1903 let cnt = self.alloc_local("__itk_cnt");
1904 self.emit(Op::StoreLocal(cnt));
1905
1906 let loop_top = self.code.len();
1907
1908 self.emit(Op::LoadLocal(cnt));
1910 self.emit(Op::LoadLocal(n));
1911 self.emit(Op::IntLt);
1912 let j_exit_n = self.code.len();
1913 self.emit(Op::JumpIfNot(0));
1914
1915 self.emit(Op::LoadLocal(i));
1917 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1918 self.emit(Op::IntLt);
1919 let j_exit_l = self.code.len();
1920 self.emit(Op::JumpIfNot(0));
1921
1922 self.emit(Op::LoadLocal(out));
1924 self.emit(Op::LoadLocal(list));
1925 self.emit(Op::LoadLocal(i));
1926 self.emit(Op::GetListElemDyn);
1927 self.emit(Op::ListAppend);
1928 self.emit(Op::StoreLocal(out));
1929
1930 let one = self.pool.int(1);
1931 self.emit(Op::LoadLocal(i));
1933 self.emit(Op::PushConst(one));
1934 self.emit(Op::IntAdd);
1935 self.emit(Op::StoreLocal(i));
1936 self.emit(Op::LoadLocal(cnt));
1938 self.emit(Op::PushConst(one));
1939 self.emit(Op::IntAdd);
1940 self.emit(Op::StoreLocal(cnt));
1941
1942 let jback = self.code.len();
1943 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1944
1945 let exit_t = self.code.len() as i32;
1946 if let Op::JumpIfNot(off) = &mut self.code[j_exit_n] { *off = exit_t - (j_exit_n as i32 + 1); }
1947 if let Op::JumpIfNot(off) = &mut self.code[j_exit_l] { *off = exit_t - (j_exit_l as i32 + 1); }
1948
1949 self.emit(Op::LoadLocal(out));
1951 self.emit(Op::PushConst(zero));
1952 let eager_v = self.pool.variant("__IterEager");
1953 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1954 }
1955
1956 fn emit_iter_skip(&mut self, args: &[a::CExpr]) {
1958 self.compile_expr(&args[0], false);
1959 let it = self.alloc_local("__isk_it");
1960 self.emit(Op::StoreLocal(it));
1961
1962 self.compile_expr(&args[1], false);
1963 let n = self.alloc_local("__isk_n");
1964 self.emit(Op::StoreLocal(n));
1965
1966 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1967 let list = self.alloc_local("__isk_list");
1968 self.emit(Op::StoreLocal(list));
1969
1970 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1971 let idx = self.alloc_local("__isk_idx");
1972 self.emit(Op::StoreLocal(idx));
1973
1974 self.emit(Op::LoadLocal(idx));
1976 self.emit(Op::LoadLocal(n));
1977 self.emit(Op::IntAdd);
1978 let raw = self.alloc_local("__isk_raw");
1979 self.emit(Op::StoreLocal(raw));
1980
1981 self.emit(Op::LoadLocal(raw));
1983 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1984 self.emit(Op::IntLt);
1985 let j_use_raw = self.code.len();
1986 self.emit(Op::JumpIf(0));
1987
1988 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1990 let j_end = self.code.len();
1991 self.emit(Op::Jump(0));
1992
1993 let raw_t = self.code.len() as i32;
1995 if let Op::JumpIf(off) = &mut self.code[j_use_raw] { *off = raw_t - (j_use_raw as i32 + 1); }
1996 self.emit(Op::LoadLocal(raw));
1997
1998 let end_t = self.code.len() as i32;
1999 if let Op::Jump(off) = &mut self.code[j_end] { *off = end_t - (j_end as i32 + 1); }
2000
2001 let new_idx = self.alloc_local("__isk_ni");
2003 self.emit(Op::StoreLocal(new_idx));
2004 self.emit(Op::LoadLocal(list));
2005 self.emit(Op::LoadLocal(new_idx));
2006 let eager_v = self.pool.variant("__IterEager");
2007 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
2008 }
2009
2010 fn emit_iter_to_list(&mut self, args: &[a::CExpr]) {
2019 self.compile_expr(&args[0], false);
2020 let it = self.alloc_local("__itl_it");
2021 self.emit(Op::StoreLocal(it));
2022
2023 self.emit(Op::MakeList(0));
2025 let out = self.alloc_local("__itl_out");
2026 self.emit(Op::StoreLocal(out));
2027
2028 self.emit(Op::LoadLocal(it));
2030 let lazy_name = self.pool.variant("__IterLazy");
2031 self.emit(Op::TestVariant(lazy_name));
2032 let j_to_eager = self.code.len();
2033 self.emit(Op::JumpIfNot(0));
2034
2035 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2038 let seed = self.alloc_local("__itl_seed");
2039 self.emit(Op::StoreLocal(seed));
2040
2041 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2042 let step = self.alloc_local("__itl_step");
2043 self.emit(Op::StoreLocal(step));
2044
2045 let lazy_loop = self.code.len();
2046 let nid_lazy = self.pool.node_id("n_iter_to_list_lazy");
2047 self.emit(Op::LoadLocal(step));
2048 self.emit(Op::LoadLocal(seed));
2049 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
2050 let opt = self.alloc_local("__itl_opt");
2051 self.emit(Op::StoreLocal(opt));
2052
2053 self.emit(Op::LoadLocal(opt));
2055 let some_name = self.pool.variant("Some");
2056 self.emit(Op::TestVariant(some_name));
2057 let j_lazy_exit = self.code.len();
2058 self.emit(Op::JumpIfNot(0));
2059
2060 self.emit(Op::LoadLocal(opt));
2062 self.emit(Op::GetVariantArg(0));
2063 let pair = self.alloc_local("__itl_pair");
2064 self.emit(Op::StoreLocal(pair));
2065
2066 self.emit(Op::LoadLocal(out));
2067 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(0));
2068 self.emit(Op::ListAppend);
2069 self.emit(Op::StoreLocal(out));
2070
2071 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(1));
2072 self.emit(Op::StoreLocal(seed));
2073
2074 let jback_lazy = self.code.len();
2075 self.emit(Op::Jump((lazy_loop as i32) - (jback_lazy as i32 + 1)));
2076
2077 let lazy_exit_t = self.code.len() as i32;
2078 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_exit] {
2079 *off = lazy_exit_t - (j_lazy_exit as i32 + 1);
2080 }
2081 let j_after_lazy = self.code.len();
2082 self.emit(Op::Jump(0));
2083
2084 let eager_t = self.code.len() as i32;
2086 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
2087 *off = eager_t - (j_to_eager as i32 + 1);
2088 }
2089
2090 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2091 let list = self.alloc_local("__itl_list");
2092 self.emit(Op::StoreLocal(list));
2093
2094 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2095 let i = self.alloc_local("__itl_i");
2096 self.emit(Op::StoreLocal(i));
2097
2098 let loop_top = self.code.len();
2099 self.emit(Op::LoadLocal(i));
2100 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2101 self.emit(Op::IntLt);
2102 let j_exit = self.code.len();
2103 self.emit(Op::JumpIfNot(0));
2104
2105 self.emit(Op::LoadLocal(out));
2106 self.emit(Op::LoadLocal(list));
2107 self.emit(Op::LoadLocal(i));
2108 self.emit(Op::GetListElemDyn);
2109 self.emit(Op::ListAppend);
2110 self.emit(Op::StoreLocal(out));
2111
2112 self.emit(Op::LoadLocal(i));
2113 let one = self.pool.int(1);
2114 self.emit(Op::PushConst(one));
2115 self.emit(Op::IntAdd);
2116 self.emit(Op::StoreLocal(i));
2117
2118 let jback = self.code.len();
2119 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2120
2121 let exit_t = self.code.len() as i32;
2122 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2123 *off = exit_t - (j_exit as i32 + 1);
2124 }
2125
2126 let converge = self.code.len() as i32;
2128 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
2129 *off = converge - (j_after_lazy as i32 + 1);
2130 }
2131 self.emit(Op::LoadLocal(out));
2132 }
2133
2134 fn emit_iter_map(&mut self, args: &[a::CExpr]) {
2136 self.compile_expr(&args[0], false);
2137 let it = self.alloc_local("__im_it");
2138 self.emit(Op::StoreLocal(it));
2139
2140 self.compile_expr(&args[1], false);
2141 let f = self.alloc_local("__im_f");
2142 self.emit(Op::StoreLocal(f));
2143
2144 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2145 let list = self.alloc_local("__im_list");
2146 self.emit(Op::StoreLocal(list));
2147
2148 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2149 let i = self.alloc_local("__im_i");
2150 self.emit(Op::StoreLocal(i));
2151
2152 self.emit(Op::MakeList(0));
2153 let out = self.alloc_local("__im_out");
2154 self.emit(Op::StoreLocal(out));
2155
2156 let loop_top = self.code.len();
2157 self.emit(Op::LoadLocal(i));
2158 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2159 self.emit(Op::IntLt);
2160 let j_exit = self.code.len();
2161 self.emit(Op::JumpIfNot(0));
2162
2163 let nid = self.pool.node_id("n_iter_map");
2164 self.emit(Op::LoadLocal(out));
2165 self.emit(Op::LoadLocal(f));
2166 self.emit(Op::LoadLocal(list));
2167 self.emit(Op::LoadLocal(i));
2168 self.emit(Op::GetListElemDyn);
2169 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2170 self.emit(Op::ListAppend);
2171 self.emit(Op::StoreLocal(out));
2172
2173 self.emit(Op::LoadLocal(i));
2174 let one = self.pool.int(1);
2175 self.emit(Op::PushConst(one));
2176 self.emit(Op::IntAdd);
2177 self.emit(Op::StoreLocal(i));
2178
2179 let jback = self.code.len();
2180 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2181
2182 let exit_t = self.code.len() as i32;
2183 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
2184
2185 let zero = self.pool.int(0);
2186 self.emit(Op::LoadLocal(out));
2187 self.emit(Op::PushConst(zero));
2188 let eager_v = self.pool.variant("__IterEager");
2189 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
2190 }
2191
2192 fn emit_iter_filter(&mut self, args: &[a::CExpr]) {
2194 self.compile_expr(&args[0], false);
2195 let it = self.alloc_local("__if_it");
2196 self.emit(Op::StoreLocal(it));
2197
2198 self.compile_expr(&args[1], false);
2199 let f = self.alloc_local("__if_f");
2200 self.emit(Op::StoreLocal(f));
2201
2202 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2203 let list = self.alloc_local("__if_list");
2204 self.emit(Op::StoreLocal(list));
2205
2206 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2207 let i = self.alloc_local("__if_i");
2208 self.emit(Op::StoreLocal(i));
2209
2210 self.emit(Op::MakeList(0));
2211 let out = self.alloc_local("__if_out");
2212 self.emit(Op::StoreLocal(out));
2213
2214 let loop_top = self.code.len();
2215 self.emit(Op::LoadLocal(i));
2216 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2217 self.emit(Op::IntLt);
2218 let j_exit = self.code.len();
2219 self.emit(Op::JumpIfNot(0));
2220
2221 self.emit(Op::LoadLocal(list));
2223 self.emit(Op::LoadLocal(i));
2224 self.emit(Op::GetListElemDyn);
2225 let x = self.alloc_local("__if_x");
2226 self.emit(Op::StoreLocal(x));
2227
2228 let nid = self.pool.node_id("n_iter_filter");
2229 self.emit(Op::LoadLocal(f));
2230 self.emit(Op::LoadLocal(x));
2231 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2232 let j_skip = self.code.len();
2233 self.emit(Op::JumpIfNot(0));
2234
2235 self.emit(Op::LoadLocal(out));
2236 self.emit(Op::LoadLocal(x));
2237 self.emit(Op::ListAppend);
2238 self.emit(Op::StoreLocal(out));
2239
2240 let skip_t = self.code.len() as i32;
2241 if let Op::JumpIfNot(off) = &mut self.code[j_skip] { *off = skip_t - (j_skip as i32 + 1); }
2242
2243 self.emit(Op::LoadLocal(i));
2244 let one = self.pool.int(1);
2245 self.emit(Op::PushConst(one));
2246 self.emit(Op::IntAdd);
2247 self.emit(Op::StoreLocal(i));
2248
2249 let jback = self.code.len();
2250 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2251
2252 let exit_t = self.code.len() as i32;
2253 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
2254
2255 let zero = self.pool.int(0);
2256 self.emit(Op::LoadLocal(out));
2257 self.emit(Op::PushConst(zero));
2258 let eager_v = self.pool.variant("__IterEager");
2259 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
2260 }
2261
2262 fn emit_iter_fold(&mut self, args: &[a::CExpr]) {
2264 self.compile_expr(&args[0], false);
2265 let it = self.alloc_local("__ifo_it");
2266 self.emit(Op::StoreLocal(it));
2267
2268 self.compile_expr(&args[1], false);
2269 let acc = self.alloc_local("__ifo_acc");
2270 self.emit(Op::StoreLocal(acc));
2271
2272 self.compile_expr(&args[2], false);
2273 let f = self.alloc_local("__ifo_f");
2274 self.emit(Op::StoreLocal(f));
2275
2276 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2277 let list = self.alloc_local("__ifo_list");
2278 self.emit(Op::StoreLocal(list));
2279
2280 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2281 let i = self.alloc_local("__ifo_i");
2282 self.emit(Op::StoreLocal(i));
2283
2284 let loop_top = self.code.len();
2285 self.emit(Op::LoadLocal(i));
2286 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2287 self.emit(Op::IntLt);
2288 let j_exit = self.code.len();
2289 self.emit(Op::JumpIfNot(0));
2290
2291 let nid = self.pool.node_id("n_iter_fold");
2292 self.emit(Op::LoadLocal(f));
2293 self.emit(Op::LoadLocal(acc));
2294 self.emit(Op::LoadLocal(list));
2295 self.emit(Op::LoadLocal(i));
2296 self.emit(Op::GetListElemDyn);
2297 self.emit(Op::CallClosure { arity: 2, node_id_idx: nid });
2298 self.emit(Op::StoreLocal(acc));
2299
2300 self.emit(Op::LoadLocal(i));
2301 let one = self.pool.int(1);
2302 self.emit(Op::PushConst(one));
2303 self.emit(Op::IntAdd);
2304 self.emit(Op::StoreLocal(i));
2305
2306 let jback = self.code.len();
2307 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2308
2309 let exit_t = self.code.len() as i32;
2310 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
2311 self.emit(Op::LoadLocal(acc));
2312 }
2313
2314 fn emit_map_fold(&mut self, args: &[a::CExpr], node_id_idx: u32) {
2320 self.compile_expr(&args[0], false);
2322 let map_kind = self.pool.str("map");
2323 let entries_op = self.pool.str("entries");
2324 self.emit(Op::EffectCall {
2325 kind_idx: map_kind,
2326 op_idx: entries_op,
2327 arity: 1,
2328 node_id_idx,
2329 });
2330 let xs = self.alloc_local("__mf_xs");
2331 self.emit(Op::StoreLocal(xs));
2332
2333 self.compile_expr(&args[1], false);
2335 let acc = self.alloc_local("__mf_acc");
2336 self.emit(Op::StoreLocal(acc));
2337
2338 self.compile_expr(&args[2], false);
2340 let f = self.alloc_local("__mf_f");
2341 self.emit(Op::StoreLocal(f));
2342
2343 let zero = self.pool.int(0);
2345 self.emit(Op::PushConst(zero));
2346 let i = self.alloc_local("__mf_i");
2347 self.emit(Op::StoreLocal(i));
2348
2349 let loop_top = self.code.len();
2351 self.emit(Op::LoadLocal(i));
2352 self.emit(Op::LoadLocal(xs));
2353 self.emit(Op::GetListLen);
2354 self.emit(Op::IntLt);
2355 let j_exit = self.code.len();
2356 self.emit(Op::JumpIfNot(0));
2357
2358 self.emit(Op::LoadLocal(xs));
2360 self.emit(Op::LoadLocal(i));
2361 self.emit(Op::GetListElemDyn);
2362 let pair = self.alloc_local("__mf_pair");
2363 self.emit(Op::StoreLocal(pair));
2364
2365 let nid = self.pool.node_id("n_map_fold");
2367 self.emit(Op::LoadLocal(f));
2368 self.emit(Op::LoadLocal(acc));
2369 self.emit(Op::LoadLocal(pair));
2370 self.emit(Op::GetElem(0));
2371 self.emit(Op::LoadLocal(pair));
2372 self.emit(Op::GetElem(1));
2373 self.emit(Op::CallClosure { arity: 3, node_id_idx: nid });
2374 self.emit(Op::StoreLocal(acc));
2375
2376 self.emit(Op::LoadLocal(i));
2378 let one = self.pool.int(1);
2379 self.emit(Op::PushConst(one));
2380 self.emit(Op::IntAdd);
2381 self.emit(Op::StoreLocal(i));
2382
2383 let jump_back = self.code.len();
2384 let back = (loop_top as i32) - (jump_back as i32 + 1);
2385 self.emit(Op::Jump(back));
2386
2387 let exit_target = self.code.len() as i32;
2388 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2389 *off = exit_target - (j_exit as i32 + 1);
2390 }
2391 self.emit(Op::LoadLocal(acc));
2392 }
2393
2394 fn emit_variant_map(
2400 &mut self,
2401 args: &[a::CExpr],
2402 wrap_with: &str,
2403 wrap_result: bool,
2404 ) {
2405 let wrap_idx = self.pool.variant(wrap_with);
2407
2408 self.compile_expr(&args[0], false);
2410 let val_slot = self.alloc_local("__hov");
2411 self.emit(Op::StoreLocal(val_slot));
2412
2413 self.compile_expr(&args[1], false);
2414 let f_slot = self.alloc_local("__hof");
2415 self.emit(Op::StoreLocal(f_slot));
2416
2417 self.emit(Op::LoadLocal(val_slot));
2424 self.emit(Op::Dup);
2425 self.emit(Op::TestVariant(wrap_idx));
2426 let j_skip = self.code.len();
2427 self.emit(Op::JumpIfNot(0));
2428
2429 self.emit(Op::GetVariantArg(0));
2431 let arg_slot = self.alloc_local("__hov_arg");
2432 self.emit(Op::StoreLocal(arg_slot));
2433 self.emit(Op::LoadLocal(f_slot));
2434 self.emit(Op::LoadLocal(arg_slot));
2435 let nid = self.pool.node_id("n_hov");
2436 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2437 if wrap_result {
2438 self.emit(Op::MakeVariant { name_idx: wrap_idx, arity: 1 });
2439 }
2440 let j_end = self.code.len();
2441 self.emit(Op::Jump(0));
2442
2443 let skip_target = self.code.len() as i32;
2445 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
2446 *off = skip_target - (j_skip as i32 + 1);
2447 }
2448
2449 let end_target = self.code.len() as i32;
2450 if let Op::Jump(off) = &mut self.code[j_end] {
2451 *off = end_target - (j_end as i32 + 1);
2452 }
2453 }
2454
2455 fn emit_variant_or_else(
2464 &mut self,
2465 args: &[a::CExpr],
2466 match_on: &str,
2467 closure_arity: u16,
2468 ) {
2469 let match_idx = self.pool.variant(match_on);
2470
2471 self.compile_expr(&args[0], false);
2472 let val_slot = self.alloc_local("__hoe");
2473 self.emit(Op::StoreLocal(val_slot));
2474
2475 self.compile_expr(&args[1], false);
2476 let f_slot = self.alloc_local("__hoe_f");
2477 self.emit(Op::StoreLocal(f_slot));
2478
2479 self.emit(Op::LoadLocal(val_slot));
2487 self.emit(Op::Dup);
2488 self.emit(Op::TestVariant(match_idx));
2489 let j_skip = self.code.len();
2490 self.emit(Op::JumpIfNot(0));
2491
2492 self.emit(Op::Pop);
2495 self.emit(Op::LoadLocal(f_slot));
2496 if closure_arity == 1 {
2497 self.emit(Op::LoadLocal(val_slot));
2498 self.emit(Op::GetVariantArg(0));
2499 }
2500 let nid = self.pool.node_id("n_hoe");
2501 self.emit(Op::CallClosure { arity: closure_arity, node_id_idx: nid });
2502
2503 let j_end = self.code.len();
2504 self.emit(Op::Jump(0));
2505
2506 let skip_target = self.code.len() as i32;
2508 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
2509 *off = skip_target - (j_skip as i32 + 1);
2510 }
2511
2512 let end_target = self.code.len() as i32;
2513 if let Op::Jump(off) = &mut self.code[j_end] {
2514 *off = end_target - (j_end as i32 + 1);
2515 }
2516 }
2517
2518 fn emit_option_unwrap_or_else(&mut self, args: &[a::CExpr]) {
2522 let some_idx = self.pool.variant("Some");
2523
2524 self.compile_expr(&args[0], false);
2526 let val_slot = self.alloc_local("__uoe_val");
2527 self.emit(Op::StoreLocal(val_slot));
2528
2529 self.compile_expr(&args[1], false);
2530 let f_slot = self.alloc_local("__uoe_f");
2531 self.emit(Op::StoreLocal(f_slot));
2532
2533 self.emit(Op::LoadLocal(val_slot));
2539 self.emit(Op::Dup);
2540 self.emit(Op::TestVariant(some_idx));
2541 let j_none = self.code.len();
2542 self.emit(Op::JumpIfNot(0));
2543
2544 self.emit(Op::GetVariantArg(0));
2546 let j_end = self.code.len();
2547 self.emit(Op::Jump(0));
2548
2549 let none_target = self.code.len() as i32;
2551 if let Op::JumpIfNot(off) = &mut self.code[j_none] {
2552 *off = none_target - (j_none as i32 + 1);
2553 }
2554 self.emit(Op::Pop);
2555 self.emit(Op::LoadLocal(f_slot));
2556 let nid = self.pool.node_id("n_uoe");
2557 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2558
2559 let end_target = self.code.len() as i32;
2561 if let Op::Jump(off) = &mut self.code[j_end] {
2562 *off = end_target - (j_end as i32 + 1);
2563 }
2564 }
2565
2566 fn install_trampoline(&mut self, name: &str, arity: u16, locals_count: u16, code: Vec<Op>) -> u32 {
2583 let fn_id = self.next_fn_id.len() as u32;
2584 let body_hash = crate::program::compute_body_hash(
2585 arity, locals_count, &code, &self.pool.record_shapes);
2586 self.next_fn_id.push(Function {
2587 name: name.into(),
2588 arity,
2589 locals_count,
2590 code,
2591 effects: Vec::new(),
2592 body_hash,
2593 refinements: Vec::new(),
2596 field_ic_sites: 0,
2600 });
2601 fn_id
2602 }
2603
2604 fn emit_flow_sequential(&mut self, args: &[a::CExpr]) {
2606 self.compile_expr(&args[0], false);
2608 self.compile_expr(&args[1], false);
2609 let nid = self.pool.node_id("n_flow_sequential");
2610 let code = vec![
2611 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,
2621 ];
2622 let fn_id = self.install_trampoline("__flow_sequential", 3, 4, code);
2623 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2624 }
2625
2626 fn emit_flow_parallel(&mut self, args: &[a::CExpr]) {
2634 self.compile_expr(&args[0], false);
2636 self.compile_expr(&args[1], false);
2637 let nid = self.pool.node_id("n_flow_parallel");
2638 let code = vec![
2639 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,
2646 ];
2647 let fn_id = self.install_trampoline("__flow_parallel", 2, 2, code);
2648 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2649 }
2650
2651 fn emit_flow_parallel_list(&mut self, args: &[a::CExpr]) {
2658 self.compile_expr(&args[0], false);
2660 let xs = self.alloc_local("__fpl_xs");
2661 self.emit(Op::StoreLocal(xs));
2662
2663 self.emit(Op::MakeList(0));
2665 let out = self.alloc_local("__fpl_out");
2666 self.emit(Op::StoreLocal(out));
2667
2668 let zero = self.pool.int(0);
2670 self.emit(Op::PushConst(zero));
2671 let i = self.alloc_local("__fpl_i");
2672 self.emit(Op::StoreLocal(i));
2673
2674 let loop_top = self.code.len();
2676 self.emit(Op::LoadLocal(i));
2677 self.emit(Op::LoadLocal(xs));
2678 self.emit(Op::GetListLen);
2679 self.emit(Op::IntLt);
2680 let j_exit = self.code.len();
2681 self.emit(Op::JumpIfNot(0));
2682
2683 let nid = self.pool.node_id("n_flow_parallel_list");
2685 self.emit(Op::LoadLocal(out));
2686 self.emit(Op::LoadLocal(xs));
2687 self.emit(Op::LoadLocal(i));
2688 self.emit(Op::GetListElemDyn);
2689 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2690 self.emit(Op::ListAppend);
2691 self.emit(Op::StoreLocal(out));
2692
2693 self.emit(Op::LoadLocal(i));
2695 let one = self.pool.int(1);
2696 self.emit(Op::PushConst(one));
2697 self.emit(Op::IntAdd);
2698 self.emit(Op::StoreLocal(i));
2699
2700 let jump_back = self.code.len();
2702 let back = (loop_top as i32) - (jump_back as i32 + 1);
2703 self.emit(Op::Jump(back));
2704
2705 let exit_target = self.code.len() as i32;
2707 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2708 *off = exit_target - (j_exit as i32 + 1);
2709 }
2710 self.emit(Op::LoadLocal(out));
2711 }
2712
2713 fn emit_flow_branch(&mut self, args: &[a::CExpr]) {
2715 self.compile_expr(&args[0], false);
2716 self.compile_expr(&args[1], false);
2717 self.compile_expr(&args[2], false);
2718 let nid = self.pool.node_id("n_flow_branch");
2719 let mut code = vec![
2720 Op::LoadLocal(0), Op::LoadLocal(3), Op::CallClosure { arity: 1, node_id_idx: nid }, ];
2725 let j_false = code.len();
2726 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(1));
2729 code.push(Op::LoadLocal(3));
2730 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2731 code.push(Op::Return);
2732 let false_target = code.len() as i32;
2734 if let Op::JumpIfNot(off) = &mut code[j_false] {
2735 *off = false_target - (j_false as i32 + 1);
2736 }
2737 code.push(Op::LoadLocal(2));
2738 code.push(Op::LoadLocal(3));
2739 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2740 code.push(Op::Return);
2741
2742 let fn_id = self.install_trampoline("__flow_branch", 4, 4, code);
2743 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2744 }
2745
2746 fn emit_flow_retry(&mut self, args: &[a::CExpr]) {
2750 self.compile_expr(&args[0], false);
2751 self.compile_expr(&args[1], false);
2752 let call_nid = self.pool.node_id("n_flow_retry");
2753 let ok_idx = self.pool.variant("Ok");
2754 let zero_const = self.pool.int(0);
2755 let one_const = self.pool.int(1);
2756 let mut code = vec![
2758 Op::PushConst(zero_const),
2760 Op::StoreLocal(3),
2761 ];
2762 let loop_top = code.len() as i32;
2764 code.push(Op::LoadLocal(3));
2765 code.push(Op::LoadLocal(1));
2766 code.push(Op::IntLt);
2767 let j_done = code.len();
2768 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(0));
2772 code.push(Op::LoadLocal(2));
2773 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2774 code.push(Op::StoreLocal(4));
2775
2776 code.push(Op::LoadLocal(4));
2778 code.push(Op::TestVariant(ok_idx));
2779 let j_was_err = code.len();
2780 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(4));
2782 code.push(Op::Return);
2783
2784 let was_err_target = code.len() as i32;
2786 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2787 *off = was_err_target - (j_was_err as i32 + 1);
2788 }
2789 code.push(Op::LoadLocal(3));
2790 code.push(Op::PushConst(one_const));
2791 code.push(Op::IntAdd);
2792 code.push(Op::StoreLocal(3));
2793 let pc_after_jump = code.len() as i32 + 1;
2794 code.push(Op::Jump(loop_top - pc_after_jump));
2795
2796 let done_target = code.len() as i32;
2798 if let Op::JumpIfNot(off) = &mut code[j_done] {
2799 *off = done_target - (j_done as i32 + 1);
2800 }
2801 code.push(Op::LoadLocal(4));
2802 code.push(Op::Return);
2803
2804 let fn_id = self.install_trampoline("__flow_retry", 3, 5, code);
2805 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2806 }
2807
2808 fn emit_flow_retry_with_backoff(&mut self, args: &[a::CExpr]) {
2816 self.compile_expr(&args[0], false);
2819 self.compile_expr(&args[1], false);
2820 self.compile_expr(&args[2], false);
2821 let call_nid = self.pool.node_id("n_flow_retry_backoff");
2822 let sleep_nid = self.pool.node_id("n_flow_retry_backoff_sleep");
2823 let kind_idx = self.pool.str("time");
2824 let op_idx = self.pool.str("sleep_ms");
2825 let ok_idx = self.pool.variant("Ok");
2826 let zero_const = self.pool.int(0);
2827 let one_const = self.pool.int(1);
2828 let two_const = self.pool.int(2);
2829 let mut code = vec![
2834 Op::LoadLocal(2),
2836 Op::StoreLocal(6),
2837 Op::PushConst(zero_const),
2839 Op::StoreLocal(4),
2840 ];
2841
2842 let loop_top = code.len() as i32;
2843 code.push(Op::LoadLocal(4));
2845 code.push(Op::LoadLocal(1));
2846 code.push(Op::IntLt);
2847 let j_done = code.len();
2848 code.push(Op::JumpIfNot(0)); code.push(Op::PushConst(zero_const));
2852 code.push(Op::LoadLocal(4));
2853 code.push(Op::IntLt); let j_no_sleep = code.len();
2855 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(6)); code.push(Op::EffectCall {
2859 kind_idx, op_idx, arity: 1, node_id_idx: sleep_nid,
2860 });
2861 code.push(Op::Pop); code.push(Op::LoadLocal(6));
2864 code.push(Op::PushConst(two_const));
2865 code.push(Op::NumMul);
2866 code.push(Op::StoreLocal(6));
2867 let after_sleep = code.len() as i32;
2869 if let Op::JumpIfNot(off) = &mut code[j_no_sleep] {
2870 *off = after_sleep - (j_no_sleep as i32 + 1);
2871 }
2872
2873 code.push(Op::LoadLocal(0));
2875 code.push(Op::LoadLocal(3));
2876 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2877 code.push(Op::StoreLocal(5));
2878
2879 code.push(Op::LoadLocal(5));
2881 code.push(Op::TestVariant(ok_idx));
2882 let j_was_err = code.len();
2883 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(5));
2885 code.push(Op::Return);
2886
2887 let was_err_target = code.len() as i32;
2889 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2890 *off = was_err_target - (j_was_err as i32 + 1);
2891 }
2892 code.push(Op::LoadLocal(4));
2893 code.push(Op::PushConst(one_const));
2894 code.push(Op::IntAdd);
2895 code.push(Op::StoreLocal(4));
2896 let pc_after_jump = code.len() as i32 + 1;
2897 code.push(Op::Jump(loop_top - pc_after_jump));
2898
2899 let done_target = code.len() as i32;
2901 if let Op::JumpIfNot(off) = &mut code[j_done] {
2902 *off = done_target - (j_done as i32 + 1);
2903 }
2904 code.push(Op::LoadLocal(5));
2905 code.push(Op::Return);
2906
2907 let fn_id = self.install_trampoline("__flow_retry_backoff", 4, 7, code);
2908 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2909 }
2910}
2911
2912fn free_vars(e: &a::CExpr, bound: &mut std::collections::HashSet<String>, out: &mut Vec<String>) {
2917 match e {
2918 a::CExpr::Literal { .. } => {}
2919 a::CExpr::Var { name } => {
2920 if !bound.contains(name) && !out.contains(name) {
2921 out.push(name.clone());
2922 }
2923 }
2924 a::CExpr::Call { callee, args } => {
2925 free_vars(callee, bound, out);
2926 for a in args { free_vars(a, bound, out); }
2927 }
2928 a::CExpr::Let { name, value, body, .. } => {
2929 free_vars(value, bound, out);
2930 let was_bound = bound.contains(name);
2931 bound.insert(name.clone());
2932 free_vars(body, bound, out);
2933 if !was_bound { bound.remove(name); }
2934 }
2935 a::CExpr::Match { scrutinee, arms } => {
2936 free_vars(scrutinee, bound, out);
2937 for arm in arms {
2938 let mut local_bound = bound.clone();
2939 pattern_binders(&arm.pattern, &mut local_bound);
2940 free_vars(&arm.body, &mut local_bound, out);
2941 }
2942 }
2943 a::CExpr::Block { statements, result } => {
2944 let mut local_bound = bound.clone();
2945 for s in statements { free_vars(s, &mut local_bound, out); }
2946 free_vars(result, &mut local_bound, out);
2947 }
2948 a::CExpr::Constructor { args, .. } => {
2949 for a in args { free_vars(a, bound, out); }
2950 }
2951 a::CExpr::RecordLit { fields } => {
2952 for f in fields { free_vars(&f.value, bound, out); }
2953 }
2954 a::CExpr::TupleLit { items } | a::CExpr::ListLit { items } => {
2955 for it in items { free_vars(it, bound, out); }
2956 }
2957 a::CExpr::FieldAccess { value, .. } => free_vars(value, bound, out),
2958 a::CExpr::Lambda { params, body, .. } => {
2959 let mut inner = bound.clone();
2960 for p in params { inner.insert(p.name.clone()); }
2961 free_vars(body, &mut inner, out);
2962 }
2963 a::CExpr::BinOp { lhs, rhs, .. } => {
2964 free_vars(lhs, bound, out);
2965 free_vars(rhs, bound, out);
2966 }
2967 a::CExpr::UnaryOp { expr, .. } => free_vars(expr, bound, out),
2968 a::CExpr::Return { value } => free_vars(value, bound, out),
2969 }
2970}
2971
2972fn pattern_binders(p: &a::Pattern, bound: &mut std::collections::HashSet<String>) {
2973 match p {
2974 a::Pattern::PWild | a::Pattern::PLiteral { .. } => {}
2975 a::Pattern::PVar { name } => { bound.insert(name.clone()); }
2976 a::Pattern::PConstructor { args, .. } => {
2977 for a in args { pattern_binders(a, bound); }
2978 }
2979 a::Pattern::PRecord { fields } => {
2980 for f in fields { pattern_binders(&f.pattern, bound); }
2981 }
2982 a::Pattern::PTuple { items } => {
2983 for it in items { pattern_binders(it, bound); }
2984 }
2985 }
2986}