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 for f in p.functions.iter_mut() {
308 apply_peephole(&mut f.code, &pool.pool);
309 apply_peephole_slice2(&mut f.code);
310 apply_peephole_slice3(&mut f.code);
311 apply_peephole_slice4(&mut f.code);
312 apply_peephole_slice5(&mut f.code, &pool.pool);
323 apply_peephole_slice6(&mut f.code);
328 apply_peephole_slice7(&mut f.code);
333 apply_peephole_slice9(&mut f.code);
343 }
344
345 for f in p.functions.iter_mut() {
350 if f.body_hash == crate::program::ZERO_BODY_HASH {
351 f.body_hash = crate::program::compute_body_hash(
352 f.arity, f.locals_count, &f.code, &pool.record_shapes);
353 }
354 }
355
356 p.constants = pool.pool;
357 p.record_shapes = pool.record_shapes;
358 p
359}
360
361fn apply_escape_lowering(
389 code: &mut [Op],
390 fn_name: &str,
391 escape_index: &std::collections::HashMap<(String, u32), bool>,
392) {
393 for (pc, op) in code.iter_mut().enumerate() {
394 let key = (fn_name.to_string(), pc as u32);
401 if !matches!(escape_index.get(&key), Some(false)) {
402 continue;
403 }
404 match *op {
405 Op::MakeRecord { shape_idx, field_count } => {
406 *op = Op::AllocStackRecord { shape_idx, field_count };
407 }
408 Op::MakeTuple(arity) => {
410 *op = Op::AllocStackTuple { arity };
411 }
412 _ => {}
413 }
414 }
415}
416
417fn apply_peephole(code: &mut [Op], constants: &[Const]) {
418 if code.len() < 3 { return; }
419 let jump_targets = collect_jump_targets(code);
420
421 let n = code.len();
422 let mut k = 0;
423 while k + 2 < n {
424 if let (Op::LoadLocal(local_idx), Op::PushConst(imm_const_idx), Op::IntAdd)
425 = (code[k], code[k + 1], code[k + 2])
426 {
427 let imm_is_int = matches!(
428 constants.get(imm_const_idx as usize),
429 Some(Const::Int(_))
430 );
431 let safe = imm_is_int
435 && !jump_targets.contains(&(k + 1))
436 && !jump_targets.contains(&(k + 2));
437 if safe {
438 code[k] = Op::LoadLocalAddIntConst { local_idx, imm_const_idx };
439 k += 3;
440 continue;
441 }
442 }
443 k += 1;
444 }
445}
446
447fn apply_peephole_slice2(code: &mut [Op]) {
454 if code.len() < 4 { return; }
455 let jump_targets = collect_jump_targets(code);
456
457 let n = code.len();
458 let mut k = 0;
459 while k + 3 < n {
460 if let (
461 Op::LoadLocalAddIntConst { local_idx: src, imm_const_idx },
462 _,
463 _,
464 Op::StoreLocal(dest),
465 ) = (code[k], code[k + 1], code[k + 2], code[k + 3])
466 {
467 let safe = !jump_targets.contains(&(k + 1))
477 && !jump_targets.contains(&(k + 2))
478 && !jump_targets.contains(&(k + 3));
479 if safe {
480 code[k] = Op::LoadLocalAddIntConstStoreLocal {
481 src,
482 imm_const_idx,
483 dest,
484 };
485 k += 4;
486 continue;
487 }
488 }
489 k += 1;
490 }
491}
492
493fn apply_peephole_slice3(code: &mut [Op]) {
511 if code.len() < 3 { return; }
512 let jump_targets = collect_jump_targets(code);
513
514 let n = code.len();
515 let mut k = 0;
516 while k + 2 < n {
517 if let (Op::LoadLocal(lhs_idx), Op::LoadLocal(rhs_idx), Op::IntAdd)
518 = (code[k], code[k + 1], code[k + 2])
519 {
520 let safe = !jump_targets.contains(&(k + 1))
521 && !jump_targets.contains(&(k + 2));
522 if safe {
523 code[k] = Op::LoadLocalAddLocal { lhs_idx, rhs_idx };
524 k += 3;
525 continue;
526 }
527 }
528 k += 1;
529 }
530}
531
532fn apply_peephole_slice4(code: &mut [Op]) {
544 if code.len() < 3 { return; }
545 let jump_targets = collect_jump_targets(code);
546
547 let n = code.len();
548 let mut k = 0;
549 while k + 2 < n {
550 if let (Op::LoadLocal(lhs_idx), Op::LoadLocal(rhs_idx), terminator)
551 = (code[k], code[k + 1], code[k + 2])
552 {
553 let fused = match terminator {
554 Op::IntSub => Some(Op::LoadLocalSubLocal { lhs_idx, rhs_idx }),
555 Op::IntMul => Some(Op::LoadLocalMulLocal { lhs_idx, rhs_idx }),
556 _ => None,
557 };
558 if let Some(fused_op) = fused {
559 let safe = !jump_targets.contains(&(k + 1))
560 && !jump_targets.contains(&(k + 2));
561 if safe {
562 code[k] = fused_op;
563 k += 3;
564 continue;
565 }
566 }
567 }
568 k += 1;
569 }
570}
571
572fn apply_peephole_slice5(code: &mut [Op], constants: &[Const]) {
592 if code.len() < 4 { return; }
593 let jump_targets = collect_jump_targets(code);
594
595 let n = code.len();
596 let mut k = 0;
597 while k + 3 < n {
598 let lhs_idx = match code[k] {
600 Op::LoadLocal(i) => i,
601 _ => { k += 1; continue; }
602 };
603 let fused = match (code[k + 1], code[k + 2], code[k + 3]) {
606 (Op::PushConst(imm_const_idx), Op::IntEq, Op::JumpIfNot(jump_offset))
607 if matches!(constants.get(imm_const_idx as usize), Some(Const::Int(_))) =>
608 Some(Op::LoadLocalEqIntConstJumpIfNot {
609 local_idx: lhs_idx, imm_const_idx, jump_offset,
610 }),
611 _ => None,
612 };
613 if let Some(fused_op) = fused {
614 let safe = !jump_targets.contains(&(k + 1))
615 && !jump_targets.contains(&(k + 2))
616 && !jump_targets.contains(&(k + 3));
617 if safe {
618 code[k] = fused_op;
619 k += 4;
620 continue;
621 }
622 }
623 k += 1;
624 }
625}
626
627fn apply_peephole_slice6(code: &mut [Op]) {
650 if code.len() < 3 { return; }
651 let jump_targets = collect_jump_targets(code);
652
653 let n = code.len();
654 let mut k = 0;
655 while k + 2 < n {
656 if let (
657 Op::LoadLocal(src),
658 Op::StoreLocal(dst),
659 Op::LoadLocalEqIntConstJumpIfNot { local_idx, imm_const_idx, jump_offset },
660 ) = (code[k], code[k + 1], code[k + 2]) {
661 if local_idx == dst {
665 let safe = !jump_targets.contains(&(k + 1))
666 && !jump_targets.contains(&(k + 2));
667 if safe {
668 code[k] = Op::LoadLocalStoreEqIntConstJumpIfNot {
669 src, dst, imm_const_idx, jump_offset,
670 };
671 k += 3;
676 continue;
677 }
678 }
679 }
680 k += 1;
681 }
682}
683
684fn apply_peephole_slice7(code: &mut [Op]) {
710 if code.len() < 3 { return; }
711 let jump_targets = collect_jump_targets(code);
712
713 let n = code.len();
714 let mut k = 0;
715 while k + 2 < n {
716 if let (Op::LoadLocal(local_idx), Op::GetField { name_idx, site_idx })
717 = (code[k], code[k + 1])
718 {
719 let fused = match code[k + 2] {
720 Op::IntAdd => Some(Op::LoadLocalGetFieldAdd { local_idx, name_idx, site_idx }),
721 Op::IntSub => Some(Op::LoadLocalGetFieldSub { local_idx, name_idx, site_idx }),
722 Op::IntMul => Some(Op::LoadLocalGetFieldMul { local_idx, name_idx, site_idx }),
723 _ => None,
724 };
725 if let Some(op) = fused {
726 let safe = !jump_targets.contains(&(k + 1))
727 && !jump_targets.contains(&(k + 2));
728 if safe {
729 code[k] = op;
730 k += 3;
731 continue;
732 }
733 }
734 }
735 k += 1;
736 }
737}
738
739fn apply_peephole_slice9(code: &mut [Op]) {
763 if code.len() < 2 { return; }
764 let jump_targets = collect_jump_targets(code);
765
766 let n = code.len();
767 let mut k = 0;
768 while k + 1 < n {
769 if let (Op::LoadLocal(local_idx), Op::GetField { name_idx, site_idx })
770 = (code[k], code[k + 1])
771 {
772 if !jump_targets.contains(&(k + 1)) {
773 code[k] = Op::LoadLocalGetField { local_idx, name_idx, site_idx };
774 k += 2;
775 continue;
776 }
777 }
778 k += 1;
779 }
780}
781
782fn collect_jump_targets(code: &[Op]) -> std::collections::HashSet<usize> {
783 let mut targets = std::collections::HashSet::new();
784 for (pc, op) in code.iter().enumerate() {
785 let off = match op {
786 Op::Jump(off) | Op::JumpIf(off) | Op::JumpIfNot(off) => Some(*off),
787 _ => None,
788 };
789 if let Some(off) = off {
790 let target = (pc as i32 + 1 + off) as usize;
791 targets.insert(target);
792 }
793 }
794 targets
795}
796
797#[derive(Debug, Clone)]
798struct PendingLambda {
799 fn_id: u32,
800 capture_names: Vec<String>,
802 params: Vec<a::Param>,
803 body: a::CExpr,
804}
805
806struct FnCompiler<'a> {
807 code: Vec<Op>,
808 locals: IndexMap<String, u16>,
809 next_local: u16,
810 peak_local: u16,
812 local_types: IndexMap<String, NumTy>,
827 local_record_field_types: IndexMap<String, IndexMap<String, NumTy>>,
842 field_get_sites: u32,
849 pool: &'a mut ConstPool,
850 function_names: &'a IndexMap<String, u32>,
851 module_aliases: &'a IndexMap<String, String>,
852 id_map: &'a std::collections::HashMap<*const a::CExpr, lex_ast::NodeId>,
854 pending_lambdas: &'a mut Vec<PendingLambda>,
857 next_fn_id: &'a mut Vec<Function>,
860}
861
862#[derive(Debug, Clone, Copy, PartialEq, Eq)]
868enum NumTy { Int, Float, Unknown }
869
870fn record_field_types(
875 ty: &a::TypeExpr,
876 type_aliases: &IndexMap<String, a::TypeExpr>,
877) -> Option<IndexMap<String, NumTy>> {
878 match ty {
879 a::TypeExpr::Record { fields } => {
880 let mut m = IndexMap::new();
881 for f in fields {
882 m.insert(f.name.clone(), classify_type_expr(&f.ty));
883 }
884 Some(m)
885 }
886 a::TypeExpr::Refined { base, .. } => record_field_types(base, type_aliases),
887 a::TypeExpr::Named { name, args } if args.is_empty() => {
888 type_aliases.get(name).and_then(|t| record_field_types(t, type_aliases))
892 }
893 _ => None,
894 }
895}
896
897fn classify_type_expr(ty: &a::TypeExpr) -> NumTy {
898 match ty {
899 a::TypeExpr::Named { name, args } if args.is_empty() => match name.as_str() {
900 "Int" => NumTy::Int,
901 "Float" => NumTy::Float,
902 _ => NumTy::Unknown,
903 },
904 a::TypeExpr::Refined { base, .. } => classify_type_expr(base),
907 _ => NumTy::Unknown,
908 }
909}
910
911impl<'a> FnCompiler<'a> {
912 fn alloc_local(&mut self, name: &str) -> u16 {
913 let i = self.next_local;
914 self.locals.insert(name.into(), i);
915 self.next_local += 1;
916 if self.next_local > self.peak_local { self.peak_local = self.next_local; }
917 i
918 }
919 fn emit(&mut self, op: Op) { self.code.push(op); }
920
921 fn compile_expr(&mut self, e: &a::CExpr, tail: bool) {
922 match e {
923 a::CExpr::Literal { value } => self.compile_lit(value),
924 a::CExpr::Var { name } => {
925 if let Some(slot) = self.locals.get(name) {
926 self.emit(Op::LoadLocal(*slot));
927 } else if let Some(&fn_id) = self.function_names.get(name) {
928 self.emit(Op::MakeClosure { fn_id, capture_count: 0 });
934 } else {
935 panic!("unknown var in compiler: {name}");
939 }
940 }
941 a::CExpr::Let { name, ty, value, body } => {
942 let nty = match ty {
947 Some(t) => classify_type_expr(t),
948 None => self.classify_expr(value),
949 };
950 if let a::CExpr::RecordLit { fields } = value.as_ref() {
956 let mut ftypes = IndexMap::new();
957 for f in fields {
958 let fty = self.classify_expr(&f.value);
959 ftypes.insert(f.name.clone(), fty);
960 }
961 self.local_record_field_types.insert(name.clone(), ftypes);
962 }
963 self.compile_expr(value, false);
964 let slot = self.alloc_local(name);
965 self.local_types.insert(name.clone(), nty);
966 self.emit(Op::StoreLocal(slot));
967 self.compile_expr(body, tail);
968 }
969 a::CExpr::Block { statements, result } => {
970 for s in statements {
971 self.compile_expr(s, false);
972 self.emit(Op::Pop);
973 }
974 self.compile_expr(result, tail);
975 }
976 a::CExpr::Call { callee, args } => self.compile_call(e, callee, args, tail),
977 a::CExpr::Constructor { name, args } => {
978 for a in args { self.compile_expr(a, false); }
979 let name_idx = self.pool.variant(name);
980 self.emit(Op::MakeVariant { name_idx, arity: args.len() as u16 });
981 }
982 a::CExpr::Match { scrutinee, arms } => self.compile_match(scrutinee, arms, tail),
983 a::CExpr::RecordLit { fields } => {
984 let mut idxs = Vec::with_capacity(fields.len());
985 for f in fields {
986 self.compile_expr(&f.value, false);
987 idxs.push(self.pool.field(&f.name));
988 }
989 let field_count = idxs.len() as u16;
990 let shape_idx = self.pool.record_shape(idxs);
991 self.emit(Op::MakeRecord { shape_idx, field_count });
992 }
993 a::CExpr::TupleLit { items } => {
994 for it in items { self.compile_expr(it, false); }
995 self.emit(Op::MakeTuple(items.len() as u16));
996 }
997 a::CExpr::ListLit { items } => {
998 for it in items { self.compile_expr(it, false); }
999 self.emit(Op::MakeList(items.len() as u32));
1000 }
1001 a::CExpr::FieldAccess { value, field } => {
1002 self.compile_expr(value, false);
1003 let name_idx = self.pool.field(field);
1004 let site_idx = self.field_get_sites;
1005 self.field_get_sites += 1;
1006 self.emit(Op::GetField { name_idx, site_idx });
1007 }
1008 a::CExpr::BinOp { op, lhs, rhs } => self.compile_binop(op, lhs, rhs),
1009 a::CExpr::UnaryOp { op, expr } => {
1010 self.compile_expr(expr, false);
1011 match op.as_str() {
1012 "-" => self.emit(Op::NumNeg),
1013 "not" => self.emit(Op::BoolNot),
1014 other => panic!("unknown unary: {other}"),
1015 }
1016 }
1017 a::CExpr::Lambda { params, body, .. } => self.compile_lambda(params, body),
1018 a::CExpr::Return { value } => {
1019 self.compile_expr(value, true);
1020 self.emit(Op::Return);
1021 }
1022 }
1023 }
1024
1025 fn compile_lit(&mut self, l: &a::CLit) {
1026 let i = match l {
1027 a::CLit::Int { value } => self.pool.int(*value),
1028 a::CLit::Bool { value } => self.pool.bool(*value),
1029 a::CLit::Float { value } => {
1030 let f: f64 = value.parse().unwrap_or(0.0);
1031 self.pool.float(f)
1032 }
1033 a::CLit::Str { value } => self.pool.str(value),
1034 a::CLit::Bytes { value: _ } => {
1035 let i = self.pool.pool.len() as u32;
1037 self.pool.pool.push(Const::Bytes(Vec::new()));
1038 i
1039 }
1040 a::CLit::Unit => self.pool.unit(),
1041 };
1042 self.emit(Op::PushConst(i));
1043 }
1044
1045 fn compile_call(&mut self, call_expr: &a::CExpr, callee: &a::CExpr, args: &[a::CExpr], tail: bool) {
1046 let node_id = self
1047 .id_map
1048 .get(&(call_expr as *const a::CExpr))
1049 .map(|n| n.as_str().to_string())
1050 .unwrap_or_else(|| "n_?".into());
1051 let node_id_idx = self.pool.node_id(&node_id);
1052
1053 if let a::CExpr::FieldAccess { value, field } = callee {
1058 if let a::CExpr::Var { name } = value.as_ref() {
1059 if let Some(module) = self.module_aliases.get(name) {
1060 if self.try_emit_higher_order(module, field, args, node_id_idx) {
1061 let _ = tail;
1062 return;
1063 }
1064 for a in args { self.compile_expr(a, false); }
1065 let kind_idx = self.pool.str(module);
1066 let op_idx = self.pool.str(field);
1067 self.emit(Op::EffectCall {
1068 kind_idx,
1069 op_idx,
1070 arity: args.len() as u16,
1071 node_id_idx,
1072 });
1073 let _ = tail;
1074 return;
1075 }
1076 }
1077 }
1078 match callee {
1079 a::CExpr::Var { name } if self.function_names.contains_key(name) => {
1080 for a in args { self.compile_expr(a, false); }
1081 let fn_id = self.function_names[name];
1082 if tail {
1083 self.emit(Op::TailCall { fn_id, arity: args.len() as u16, node_id_idx });
1084 } else {
1085 self.emit(Op::Call { fn_id, arity: args.len() as u16, node_id_idx });
1086 }
1087 }
1088 a::CExpr::Var { name } if self.locals.contains_key(name) => {
1089 let slot = self.locals[name];
1092 self.emit(Op::LoadLocal(slot));
1093 for a in args { self.compile_expr(a, false); }
1094 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
1095 }
1096 other => {
1098 self.compile_expr(other, false);
1099 for a in args { self.compile_expr(a, false); }
1100 self.emit(Op::CallClosure { arity: args.len() as u16, node_id_idx });
1101 }
1102 }
1103 }
1104
1105 fn compile_binop(&mut self, op: &str, lhs: &a::CExpr, rhs: &a::CExpr) {
1106 let lhs_ty = self.classify_expr(lhs);
1117 let rhs_ty = self.classify_expr(rhs);
1118 let typed = match (lhs_ty, rhs_ty) {
1119 (NumTy::Int, NumTy::Int) => NumTy::Int,
1120 (NumTy::Float, NumTy::Float) => NumTy::Float,
1121 _ => NumTy::Unknown,
1122 };
1123 self.compile_expr(lhs, false);
1124 self.compile_expr(rhs, false);
1125 match (op, typed) {
1126 ("+", NumTy::Int) => self.emit(Op::IntAdd),
1127 ("+", NumTy::Float) => self.emit(Op::FloatAdd),
1128 ("+", NumTy::Unknown) => self.emit(Op::NumAdd),
1129 ("-", NumTy::Int) => self.emit(Op::IntSub),
1130 ("-", NumTy::Float) => self.emit(Op::FloatSub),
1131 ("-", NumTy::Unknown) => self.emit(Op::NumSub),
1132 ("*", NumTy::Int) => self.emit(Op::IntMul),
1133 ("*", NumTy::Float) => self.emit(Op::FloatMul),
1134 ("*", NumTy::Unknown) => self.emit(Op::NumMul),
1135 ("/", NumTy::Int) => self.emit(Op::IntDiv),
1136 ("/", NumTy::Float) => self.emit(Op::FloatDiv),
1137 ("/", NumTy::Unknown) => self.emit(Op::NumDiv),
1138 ("%", NumTy::Int) => self.emit(Op::IntMod),
1140 ("%", _) => self.emit(Op::NumMod),
1141 ("==", NumTy::Int) => self.emit(Op::IntEq),
1142 ("==", NumTy::Float) => self.emit(Op::FloatEq),
1143 ("==", NumTy::Unknown) => self.emit(Op::NumEq),
1144 ("!=", NumTy::Int) => { self.emit(Op::IntEq); self.emit(Op::BoolNot); }
1145 ("!=", NumTy::Float) => { self.emit(Op::FloatEq); self.emit(Op::BoolNot); }
1146 ("!=", NumTy::Unknown) => { self.emit(Op::NumEq); self.emit(Op::BoolNot); }
1147 ("<", NumTy::Int) => self.emit(Op::IntLt),
1148 ("<", NumTy::Float) => self.emit(Op::FloatLt),
1149 ("<", NumTy::Unknown) => self.emit(Op::NumLt),
1150 ("<=", NumTy::Int) => self.emit(Op::IntLe),
1151 ("<=", NumTy::Float) => self.emit(Op::FloatLe),
1152 ("<=", NumTy::Unknown) => self.emit(Op::NumLe),
1153 (">", NumTy::Int) => { self.emit_swap_top2(); self.emit(Op::IntLt); }
1154 (">", NumTy::Float) => { self.emit_swap_top2(); self.emit(Op::FloatLt); }
1155 (">", NumTy::Unknown) => { self.emit_swap_top2(); self.emit(Op::NumLt); }
1156 (">=", NumTy::Int) => { self.emit_swap_top2(); self.emit(Op::IntLe); }
1157 (">=", NumTy::Float) => { self.emit_swap_top2(); self.emit(Op::FloatLe); }
1158 (">=", NumTy::Unknown) => { self.emit_swap_top2(); self.emit(Op::NumLe); }
1159 ("and", _) => self.emit(Op::BoolAnd),
1160 ("or", _) => self.emit(Op::BoolOr),
1161 (other, _) => panic!("unknown binop: {other:?}"),
1162 }
1163 }
1164
1165 fn classify_expr(&self, e: &a::CExpr) -> NumTy {
1173 match e {
1174 a::CExpr::Literal { value: a::CLit::Int { .. } } => NumTy::Int,
1175 a::CExpr::Literal { value: a::CLit::Float { .. } } => NumTy::Float,
1176 a::CExpr::Var { name } =>
1177 self.local_types.get(name).copied().unwrap_or(NumTy::Unknown),
1178 a::CExpr::BinOp { op, lhs, rhs } => {
1179 let is_numeric = matches!(op.as_str(), "+" | "-" | "*" | "/" | "%");
1183 if !is_numeric { return NumTy::Unknown; }
1184 match (self.classify_expr(lhs), self.classify_expr(rhs)) {
1185 (NumTy::Int, NumTy::Int) => NumTy::Int,
1186 (NumTy::Float, NumTy::Float) => NumTy::Float,
1187 _ => NumTy::Unknown,
1188 }
1189 }
1190 a::CExpr::UnaryOp { op, expr } if op == "-" => self.classify_expr(expr),
1191 a::CExpr::FieldAccess { value, field } => {
1198 if let a::CExpr::Var { name } = value.as_ref() {
1199 if let Some(ftypes) = self.local_record_field_types.get(name) {
1200 return ftypes.get(field).copied().unwrap_or(NumTy::Unknown);
1201 }
1202 }
1203 NumTy::Unknown
1204 }
1205 _ => NumTy::Unknown,
1209 }
1210 }
1211
1212 fn emit_swap_top2(&mut self) {
1213 let a = self.alloc_local("__swap_a");
1214 let b = self.alloc_local("__swap_b");
1215 self.emit(Op::StoreLocal(b));
1216 self.emit(Op::StoreLocal(a));
1217 self.emit(Op::LoadLocal(b));
1218 self.emit(Op::LoadLocal(a));
1219 }
1220
1221 fn compile_match(&mut self, scrutinee: &a::CExpr, arms: &[a::Arm], tail: bool) {
1222 self.compile_expr(scrutinee, false);
1223 let scrut_slot = self.alloc_local("__scrut");
1224 self.emit(Op::StoreLocal(scrut_slot));
1225
1226 let mut end_jumps: Vec<usize> = Vec::new();
1227 for arm in arms {
1228 let arm_start_locals = self.next_local;
1229 let arm_start_locals_map = self.locals.clone();
1230
1231 self.emit(Op::LoadLocal(scrut_slot));
1232 let mut bindings: Vec<(String, u16)> = Vec::new();
1233 let fail_jumps: Vec<usize> = self.compile_pattern_test(&arm.pattern, &mut bindings);
1234
1235 self.compile_expr(&arm.body, tail);
1236 let j_end = self.code.len();
1237 self.emit(Op::Jump(0));
1238 end_jumps.push(j_end);
1239
1240 let fail_target = self.code.len() as i32;
1241 for j in fail_jumps {
1242 match &mut self.code[j] {
1248 Op::JumpIfNot(off) => *off = fail_target - (j as i32 + 1),
1249 Op::Jump(off) => *off = fail_target - (j as i32 + 1),
1250 _ => {}
1251 }
1252 }
1253 self.next_local = arm_start_locals;
1254 self.locals = arm_start_locals_map;
1255 }
1256 let panic_msg_idx = self.pool.str("non-exhaustive match");
1257 self.emit(Op::Panic(panic_msg_idx));
1258
1259 let end_target = self.code.len() as i32;
1260 for j in end_jumps {
1261 if let Op::Jump(off) = &mut self.code[j] {
1262 *off = end_target - (j as i32 + 1);
1263 }
1264 }
1265 }
1266
1267 fn compile_pattern_test(&mut self, p: &a::Pattern, bindings: &mut Vec<(String, u16)>) -> Vec<usize> {
1268 let mut fails = Vec::new();
1269 match p {
1270 a::Pattern::PWild => { self.emit(Op::Pop); }
1271 a::Pattern::PVar { name } => {
1272 let slot = self.alloc_local(name);
1273 self.emit(Op::StoreLocal(slot));
1274 bindings.push((name.clone(), slot));
1275 }
1276 a::Pattern::PLiteral { value } => {
1277 self.compile_lit(value);
1278 match value {
1279 a::CLit::Str { .. } => self.emit(Op::StrEq),
1280 a::CLit::Bytes { .. } => self.emit(Op::BytesEq),
1281 a::CLit::Int { .. } => self.emit(Op::IntEq),
1292 a::CLit::Float { .. } => self.emit(Op::FloatEq),
1293 _ => self.emit(Op::NumEq),
1294 }
1295 let j = self.code.len();
1296 self.emit(Op::JumpIfNot(0));
1297 fails.push(j);
1298 }
1299 a::Pattern::PConstructor { name, args } => {
1300 let name_idx = self.pool.variant(name);
1301 self.emit(Op::Dup); self.emit(Op::TestVariant(name_idx)); let j_success = self.code.len();
1318 self.emit(Op::JumpIf(0)); self.emit(Op::Pop); let j_fail = self.code.len();
1321 self.emit(Op::Jump(0)); fails.push(j_fail);
1323 let success_target = self.code.len() as i32;
1324 if let Op::JumpIf(off) = &mut self.code[j_success] {
1325 *off = success_target - (j_success as i32 + 1);
1326 }
1327 if args.is_empty() {
1328 self.emit(Op::Pop);
1329 } else if args.len() == 1 {
1330 self.emit(Op::GetVariantArg(0));
1331 let sub_fails = self.compile_pattern_test(&args[0], bindings);
1332 fails.extend(sub_fails);
1333 } else {
1334 let slot = self.alloc_local("__variant");
1335 self.emit(Op::StoreLocal(slot));
1336 for (i, arg) in args.iter().enumerate() {
1337 self.emit(Op::LoadLocal(slot));
1338 self.emit(Op::GetVariantArg(i as u16));
1339 let sub_fails = self.compile_pattern_test(arg, bindings);
1340 fails.extend(sub_fails);
1341 }
1342 }
1343 }
1344 a::Pattern::PRecord { fields } => {
1345 let slot = self.alloc_local("__record");
1346 self.emit(Op::StoreLocal(slot));
1347 for f in fields {
1348 self.emit(Op::LoadLocal(slot));
1349 let name_idx = self.pool.field(&f.name);
1350 let site_idx = self.field_get_sites;
1351 self.field_get_sites += 1;
1352 self.emit(Op::GetField { name_idx, site_idx });
1353 let sub_fails = self.compile_pattern_test(&f.pattern, bindings);
1354 fails.extend(sub_fails);
1355 }
1356 }
1357 a::Pattern::PTuple { items } => {
1358 let slot = self.alloc_local("__tuple");
1359 self.emit(Op::StoreLocal(slot));
1360 for (i, item) in items.iter().enumerate() {
1361 self.emit(Op::LoadLocal(slot));
1362 self.emit(Op::GetElem(i as u16));
1363 let sub_fails = self.compile_pattern_test(item, bindings);
1364 fails.extend(sub_fails);
1365 }
1366 }
1367 }
1368 fails
1369 }
1370
1371 fn compile_lambda(&mut self, params: &[a::Param], body: &a::CExpr) {
1375 let mut bound: std::collections::HashSet<String> = params.iter().map(|p| p.name.clone()).collect();
1377 let mut frees: Vec<String> = Vec::new();
1378 free_vars(body, &mut bound, &mut frees);
1379
1380 let captures: Vec<String> = frees.into_iter()
1389 .filter(|n| self.locals.contains_key(n))
1390 .collect();
1391
1392 let fn_id = self.next_fn_id.len() as u32;
1394 self.next_fn_id.push(Function {
1395 name: format!("__lambda_{fn_id}"),
1396 arity: (captures.len() + params.len()) as u16,
1397 locals_count: 0,
1398 code: Vec::new(),
1399 effects: Vec::new(),
1400 body_hash: crate::program::ZERO_BODY_HASH,
1402 refinements: Vec::new(),
1407 field_ic_sites: 0,
1410 });
1411
1412 for c in &captures {
1414 let slot = *self.locals.get(c).expect("free var must be in scope");
1415 self.emit(Op::LoadLocal(slot));
1416 }
1417 self.emit(Op::MakeClosure { fn_id, capture_count: captures.len() as u16 });
1418
1419 self.pending_lambdas.push(PendingLambda {
1421 fn_id,
1422 capture_names: captures,
1423 params: params.to_vec(),
1424 body: body.clone(),
1425 });
1426 }
1427
1428 fn try_emit_higher_order(
1432 &mut self,
1433 module: &str,
1434 op: &str,
1435 args: &[a::CExpr],
1436 node_id_idx: u32,
1437 ) -> bool {
1438 match (module, op) {
1439 ("result", "map") => self.emit_variant_map(args, "Ok", true),
1440 ("result", "and_then") => self.emit_variant_map(args, "Ok", false),
1441 ("result", "map_err") => self.emit_variant_map(args, "Err", true),
1442 ("result", "or_else") => self.emit_variant_or_else(args, "Err", 1),
1443 ("option", "map") => self.emit_variant_map(args, "Some", true),
1444 ("option", "and_then") => self.emit_variant_map(args, "Some", false),
1445 ("option", "or_else") => self.emit_variant_or_else(args, "None", 0),
1446 ("option", "unwrap_or_else") => self.emit_option_unwrap_or_else(args),
1447 ("list", "map") => self.emit_list_map(args),
1448 ("list", "par_map") => self.emit_list_par_map(args),
1449 ("list", "sort_by") => self.emit_list_sort_by(args),
1450 ("list", "filter") => self.emit_list_filter(args),
1451 ("list", "fold") => self.emit_list_fold(args),
1452 ("iter", "from_list") => self.emit_iter_from_list(args),
1453 ("iter", "unfold") => self.emit_iter_unfold(args),
1454 ("iter", "next") => self.emit_iter_next(args),
1455 ("iter", "is_empty") => self.emit_iter_is_empty(args),
1456 ("iter", "count") => self.emit_iter_count(args),
1457 ("iter", "take") => self.emit_iter_take(args),
1458 ("iter", "skip") => self.emit_iter_skip(args),
1459 ("iter", "to_list") => self.emit_iter_to_list(args),
1460 ("iter", "collect") => self.emit_iter_to_list(args),
1461 ("iter", "map") => self.emit_iter_map(args),
1462 ("iter", "filter") => self.emit_iter_filter(args),
1463 ("iter", "fold") => self.emit_iter_fold(args),
1464 ("map", "fold") => self.emit_map_fold(args, node_id_idx),
1465 ("flow", "sequential") => self.emit_flow_sequential(args),
1466 ("flow", "branch") => self.emit_flow_branch(args),
1467 ("flow", "retry") => self.emit_flow_retry(args),
1468 ("flow", "retry_with_backoff") => self.emit_flow_retry_with_backoff(args),
1469 ("flow", "parallel") => self.emit_flow_parallel(args),
1470 ("flow", "parallel_list") => self.emit_flow_parallel_list(args),
1471 _ => return false,
1472 }
1473 true
1474 }
1475
1476 fn emit_list_map(&mut self, args: &[a::CExpr]) {
1482 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_map");
1485 self.emit(Op::ListMap { node_id_idx: nid });
1486 }
1487
1488 fn emit_list_par_map(&mut self, args: &[a::CExpr]) {
1494 self.compile_expr(&args[0], false);
1495 self.compile_expr(&args[1], false);
1496 let nid = self.pool.node_id("n_list_par_map");
1497 self.emit(Op::ParallelMap { node_id_idx: nid });
1498 }
1499
1500 fn emit_list_sort_by(&mut self, args: &[a::CExpr]) {
1508 self.compile_expr(&args[0], false);
1509 self.compile_expr(&args[1], false);
1510 let nid = self.pool.node_id("n_list_sort_by");
1511 self.emit(Op::SortByKey { node_id_idx: nid });
1512 }
1513
1514 fn emit_list_filter(&mut self, args: &[a::CExpr]) {
1517 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_filter");
1520 self.emit(Op::ListFilter { node_id_idx: nid });
1521 }
1522
1523 fn emit_list_fold(&mut self, args: &[a::CExpr]) {
1526 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");
1530 self.emit(Op::ListFold { node_id_idx: nid });
1531 }
1532
1533 fn emit_iter_from_list(&mut self, args: &[a::CExpr]) {
1545 self.compile_expr(&args[0], false);
1546 let zero = self.pool.int(0);
1547 self.emit(Op::PushConst(zero));
1548 let v = self.pool.variant("__IterEager");
1549 self.emit(Op::MakeVariant { name_idx: v, arity: 2 });
1550 }
1551
1552 fn emit_iter_next(&mut self, args: &[a::CExpr]) {
1564 self.compile_expr(&args[0], false);
1565 let it = self.alloc_local("__in_it");
1566 self.emit(Op::StoreLocal(it));
1567
1568 self.emit(Op::LoadLocal(it));
1570 self.emit(Op::Dup);
1571 let lazy_name = self.pool.variant("__IterLazy");
1572 self.emit(Op::TestVariant(lazy_name));
1573 let j_to_check_cursor = self.code.len();
1574 self.emit(Op::JumpIfNot(0));
1575
1576 self.emit(Op::LoadLocal(it));
1580 self.emit(Op::GetVariantArg(0)); let seed = self.alloc_local("__in_seed");
1582 self.emit(Op::StoreLocal(seed));
1583
1584 self.emit(Op::LoadLocal(it));
1585 self.emit(Op::GetVariantArg(1)); let step = self.alloc_local("__in_step");
1587 self.emit(Op::StoreLocal(step));
1588
1589 let nid_lazy = self.pool.node_id("n_iter_next_lazy");
1591 self.emit(Op::LoadLocal(step));
1592 self.emit(Op::LoadLocal(seed));
1593 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
1594 let opt = self.alloc_local("__in_opt");
1595 self.emit(Op::StoreLocal(opt));
1596
1597 self.emit(Op::LoadLocal(opt));
1599 let some_name = self.pool.variant("Some");
1600 self.emit(Op::TestVariant(some_name));
1601 let j_lazy_none = self.code.len();
1602 self.emit(Op::JumpIfNot(0));
1603
1604 self.emit(Op::LoadLocal(opt));
1607 self.emit(Op::GetVariantArg(0)); let pair = self.alloc_local("__in_pair");
1609 self.emit(Op::StoreLocal(pair));
1610
1611 self.emit(Op::LoadLocal(pair));
1612 self.emit(Op::GetElem(0)); self.emit(Op::LoadLocal(pair));
1614 self.emit(Op::GetElem(1)); self.emit(Op::LoadLocal(step)); let lazy_v = self.pool.variant("__IterLazy");
1617 self.emit(Op::MakeVariant { name_idx: lazy_v, arity: 2 }); self.emit(Op::MakeTuple(2)); let some_v = self.pool.variant("Some");
1620 self.emit(Op::MakeVariant { name_idx: some_v, arity: 1 });
1621 let j_after_lazy = self.code.len();
1622 self.emit(Op::Jump(0));
1623
1624 let none_t = self.code.len() as i32;
1626 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_none] {
1627 *off = none_t - (j_lazy_none as i32 + 1);
1628 }
1629 let none_v = self.pool.variant("None");
1630 self.emit(Op::MakeVariant { name_idx: none_v, arity: 0 });
1631 let j_after_lazy_none = self.code.len();
1632 self.emit(Op::Jump(0));
1633
1634 let cursor_check_t = self.code.len() as i32;
1636 if let Op::JumpIfNot(off) = &mut self.code[j_to_check_cursor] {
1637 *off = cursor_check_t - (j_to_check_cursor as i32 + 1);
1638 }
1639
1640 self.emit(Op::LoadLocal(it));
1641 self.emit(Op::Dup);
1642 let cursor_name = self.pool.variant("__IterCursor");
1643 self.emit(Op::TestVariant(cursor_name));
1644 let j_to_eager = self.code.len();
1645 self.emit(Op::JumpIfNot(0));
1646
1647 self.emit(Op::LoadLocal(it));
1651 self.emit(Op::GetVariantArg(0)); let handle = self.alloc_local("__in_handle");
1653 self.emit(Op::StoreLocal(handle));
1654
1655 let kind_idx = self.pool.str("sql");
1656 let op_idx = self.pool.str("cursor_next");
1657 let nid_cursor = self.pool.node_id("n_iter_next_cursor");
1658 self.emit(Op::LoadLocal(handle));
1659 self.emit(Op::EffectCall {
1660 kind_idx,
1661 op_idx,
1662 arity: 1,
1663 node_id_idx: nid_cursor,
1664 });
1665 let cur_opt = self.alloc_local("__in_cur_opt");
1666 self.emit(Op::StoreLocal(cur_opt));
1667
1668 self.emit(Op::LoadLocal(cur_opt));
1669 let some_c = self.pool.variant("Some");
1670 self.emit(Op::TestVariant(some_c));
1671 let j_cursor_none = self.code.len();
1672 self.emit(Op::JumpIfNot(0));
1673
1674 self.emit(Op::LoadLocal(cur_opt));
1676 self.emit(Op::GetVariantArg(0)); self.emit(Op::LoadLocal(handle));
1678 let cursor_v = self.pool.variant("__IterCursor");
1679 self.emit(Op::MakeVariant { name_idx: cursor_v, arity: 1 });
1680 self.emit(Op::MakeTuple(2)); let some_c2 = self.pool.variant("Some");
1682 self.emit(Op::MakeVariant { name_idx: some_c2, arity: 1 });
1683 let j_after_cursor = self.code.len();
1684 self.emit(Op::Jump(0));
1685
1686 let cursor_none_t = self.code.len() as i32;
1688 if let Op::JumpIfNot(off) = &mut self.code[j_cursor_none] {
1689 *off = cursor_none_t - (j_cursor_none as i32 + 1);
1690 }
1691 let none_c = self.pool.variant("None");
1692 self.emit(Op::MakeVariant { name_idx: none_c, arity: 0 });
1693 let j_after_cursor_none = self.code.len();
1694 self.emit(Op::Jump(0));
1695
1696 let eager_t = self.code.len() as i32;
1698 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
1699 *off = eager_t - (j_to_eager as i32 + 1);
1700 }
1701
1702 self.emit(Op::LoadLocal(it));
1703 self.emit(Op::GetVariantArg(0));
1704 let list = self.alloc_local("__in_list");
1705 self.emit(Op::StoreLocal(list));
1706
1707 self.emit(Op::LoadLocal(it));
1708 self.emit(Op::GetVariantArg(1));
1709 let idx = self.alloc_local("__in_idx");
1710 self.emit(Op::StoreLocal(idx));
1711
1712 self.emit(Op::LoadLocal(idx));
1714 self.emit(Op::LoadLocal(list));
1715 self.emit(Op::GetListLen);
1716 self.emit(Op::IntLt);
1717 let j_eager_else = self.code.len();
1718 self.emit(Op::JumpIfNot(0));
1719
1720 self.emit(Op::LoadLocal(list));
1722 self.emit(Op::LoadLocal(idx));
1723 self.emit(Op::GetListElemDyn);
1724
1725 self.emit(Op::LoadLocal(list));
1726 self.emit(Op::LoadLocal(idx));
1727 let one = self.pool.int(1);
1728 self.emit(Op::PushConst(one));
1729 self.emit(Op::IntAdd);
1730 let eager_v = self.pool.variant("__IterEager");
1731 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1732 self.emit(Op::MakeTuple(2));
1733 let some_e = self.pool.variant("Some");
1734 self.emit(Op::MakeVariant { name_idx: some_e, arity: 1 });
1735 let j_after_eager = self.code.len();
1736 self.emit(Op::Jump(0));
1737
1738 let eager_none_t = self.code.len() as i32;
1740 if let Op::JumpIfNot(off) = &mut self.code[j_eager_else] {
1741 *off = eager_none_t - (j_eager_else as i32 + 1);
1742 }
1743 let none_e = self.pool.variant("None");
1744 self.emit(Op::MakeVariant { name_idx: none_e, arity: 0 });
1745
1746 let end = self.code.len() as i32;
1748 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
1749 *off = end - (j_after_lazy as i32 + 1);
1750 }
1751 if let Op::Jump(off) = &mut self.code[j_after_lazy_none] {
1752 *off = end - (j_after_lazy_none as i32 + 1);
1753 }
1754 if let Op::Jump(off) = &mut self.code[j_after_cursor] {
1755 *off = end - (j_after_cursor as i32 + 1);
1756 }
1757 if let Op::Jump(off) = &mut self.code[j_after_cursor_none] {
1758 *off = end - (j_after_cursor_none as i32 + 1);
1759 }
1760 if let Op::Jump(off) = &mut self.code[j_after_eager] {
1761 *off = end - (j_after_eager as i32 + 1);
1762 }
1763 }
1764
1765 fn emit_iter_unfold(&mut self, args: &[a::CExpr]) {
1770 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let lazy = self.pool.variant("__IterLazy");
1773 self.emit(Op::MakeVariant { name_idx: lazy, arity: 2 });
1774 }
1775
1776 fn emit_iter_is_empty(&mut self, args: &[a::CExpr]) {
1782 self.compile_expr(&args[0], false);
1783 let it = self.alloc_local("__ie_it");
1784 self.emit(Op::StoreLocal(it));
1785
1786 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); }
1792
1793 fn emit_iter_count(&mut self, args: &[a::CExpr]) {
1795 self.compile_expr(&args[0], false);
1796 let it = self.alloc_local("__ic_it");
1797 self.emit(Op::StoreLocal(it));
1798
1799 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1800 self.emit(Op::GetListLen); self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1)); self.emit(Op::IntSub); }
1804
1805 fn emit_iter_take(&mut self, args: &[a::CExpr]) {
1807 self.compile_expr(&args[0], false);
1808 let it = self.alloc_local("__itk_it");
1809 self.emit(Op::StoreLocal(it));
1810
1811 self.compile_expr(&args[1], false);
1812 let n = self.alloc_local("__itk_n");
1813 self.emit(Op::StoreLocal(n));
1814
1815 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1816 let list = self.alloc_local("__itk_list");
1817 self.emit(Op::StoreLocal(list));
1818
1819 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1820 let i = self.alloc_local("__itk_i");
1821 self.emit(Op::StoreLocal(i));
1822
1823 self.emit(Op::MakeList(0));
1824 let out = self.alloc_local("__itk_out");
1825 self.emit(Op::StoreLocal(out));
1826
1827 let zero = self.pool.int(0);
1828 self.emit(Op::PushConst(zero));
1829 let cnt = self.alloc_local("__itk_cnt");
1830 self.emit(Op::StoreLocal(cnt));
1831
1832 let loop_top = self.code.len();
1833
1834 self.emit(Op::LoadLocal(cnt));
1836 self.emit(Op::LoadLocal(n));
1837 self.emit(Op::IntLt);
1838 let j_exit_n = self.code.len();
1839 self.emit(Op::JumpIfNot(0));
1840
1841 self.emit(Op::LoadLocal(i));
1843 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1844 self.emit(Op::IntLt);
1845 let j_exit_l = self.code.len();
1846 self.emit(Op::JumpIfNot(0));
1847
1848 self.emit(Op::LoadLocal(out));
1850 self.emit(Op::LoadLocal(list));
1851 self.emit(Op::LoadLocal(i));
1852 self.emit(Op::GetListElemDyn);
1853 self.emit(Op::ListAppend);
1854 self.emit(Op::StoreLocal(out));
1855
1856 let one = self.pool.int(1);
1857 self.emit(Op::LoadLocal(i));
1859 self.emit(Op::PushConst(one));
1860 self.emit(Op::IntAdd);
1861 self.emit(Op::StoreLocal(i));
1862 self.emit(Op::LoadLocal(cnt));
1864 self.emit(Op::PushConst(one));
1865 self.emit(Op::IntAdd);
1866 self.emit(Op::StoreLocal(cnt));
1867
1868 let jback = self.code.len();
1869 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1870
1871 let exit_t = self.code.len() as i32;
1872 if let Op::JumpIfNot(off) = &mut self.code[j_exit_n] { *off = exit_t - (j_exit_n as i32 + 1); }
1873 if let Op::JumpIfNot(off) = &mut self.code[j_exit_l] { *off = exit_t - (j_exit_l as i32 + 1); }
1874
1875 self.emit(Op::LoadLocal(out));
1877 self.emit(Op::PushConst(zero));
1878 let eager_v = self.pool.variant("__IterEager");
1879 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1880 }
1881
1882 fn emit_iter_skip(&mut self, args: &[a::CExpr]) {
1884 self.compile_expr(&args[0], false);
1885 let it = self.alloc_local("__isk_it");
1886 self.emit(Op::StoreLocal(it));
1887
1888 self.compile_expr(&args[1], false);
1889 let n = self.alloc_local("__isk_n");
1890 self.emit(Op::StoreLocal(n));
1891
1892 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1893 let list = self.alloc_local("__isk_list");
1894 self.emit(Op::StoreLocal(list));
1895
1896 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1897 let idx = self.alloc_local("__isk_idx");
1898 self.emit(Op::StoreLocal(idx));
1899
1900 self.emit(Op::LoadLocal(idx));
1902 self.emit(Op::LoadLocal(n));
1903 self.emit(Op::IntAdd);
1904 let raw = self.alloc_local("__isk_raw");
1905 self.emit(Op::StoreLocal(raw));
1906
1907 self.emit(Op::LoadLocal(raw));
1909 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1910 self.emit(Op::IntLt);
1911 let j_use_raw = self.code.len();
1912 self.emit(Op::JumpIf(0));
1913
1914 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1916 let j_end = self.code.len();
1917 self.emit(Op::Jump(0));
1918
1919 let raw_t = self.code.len() as i32;
1921 if let Op::JumpIf(off) = &mut self.code[j_use_raw] { *off = raw_t - (j_use_raw as i32 + 1); }
1922 self.emit(Op::LoadLocal(raw));
1923
1924 let end_t = self.code.len() as i32;
1925 if let Op::Jump(off) = &mut self.code[j_end] { *off = end_t - (j_end as i32 + 1); }
1926
1927 let new_idx = self.alloc_local("__isk_ni");
1929 self.emit(Op::StoreLocal(new_idx));
1930 self.emit(Op::LoadLocal(list));
1931 self.emit(Op::LoadLocal(new_idx));
1932 let eager_v = self.pool.variant("__IterEager");
1933 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1934 }
1935
1936 fn emit_iter_to_list(&mut self, args: &[a::CExpr]) {
1945 self.compile_expr(&args[0], false);
1946 let it = self.alloc_local("__itl_it");
1947 self.emit(Op::StoreLocal(it));
1948
1949 self.emit(Op::MakeList(0));
1951 let out = self.alloc_local("__itl_out");
1952 self.emit(Op::StoreLocal(out));
1953
1954 self.emit(Op::LoadLocal(it));
1956 let lazy_name = self.pool.variant("__IterLazy");
1957 self.emit(Op::TestVariant(lazy_name));
1958 let j_to_eager = self.code.len();
1959 self.emit(Op::JumpIfNot(0));
1960
1961 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1964 let seed = self.alloc_local("__itl_seed");
1965 self.emit(Op::StoreLocal(seed));
1966
1967 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1968 let step = self.alloc_local("__itl_step");
1969 self.emit(Op::StoreLocal(step));
1970
1971 let lazy_loop = self.code.len();
1972 let nid_lazy = self.pool.node_id("n_iter_to_list_lazy");
1973 self.emit(Op::LoadLocal(step));
1974 self.emit(Op::LoadLocal(seed));
1975 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
1976 let opt = self.alloc_local("__itl_opt");
1977 self.emit(Op::StoreLocal(opt));
1978
1979 self.emit(Op::LoadLocal(opt));
1981 let some_name = self.pool.variant("Some");
1982 self.emit(Op::TestVariant(some_name));
1983 let j_lazy_exit = self.code.len();
1984 self.emit(Op::JumpIfNot(0));
1985
1986 self.emit(Op::LoadLocal(opt));
1988 self.emit(Op::GetVariantArg(0));
1989 let pair = self.alloc_local("__itl_pair");
1990 self.emit(Op::StoreLocal(pair));
1991
1992 self.emit(Op::LoadLocal(out));
1993 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(0));
1994 self.emit(Op::ListAppend);
1995 self.emit(Op::StoreLocal(out));
1996
1997 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(1));
1998 self.emit(Op::StoreLocal(seed));
1999
2000 let jback_lazy = self.code.len();
2001 self.emit(Op::Jump((lazy_loop as i32) - (jback_lazy as i32 + 1)));
2002
2003 let lazy_exit_t = self.code.len() as i32;
2004 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_exit] {
2005 *off = lazy_exit_t - (j_lazy_exit as i32 + 1);
2006 }
2007 let j_after_lazy = self.code.len();
2008 self.emit(Op::Jump(0));
2009
2010 let eager_t = self.code.len() as i32;
2012 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
2013 *off = eager_t - (j_to_eager as i32 + 1);
2014 }
2015
2016 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2017 let list = self.alloc_local("__itl_list");
2018 self.emit(Op::StoreLocal(list));
2019
2020 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2021 let i = self.alloc_local("__itl_i");
2022 self.emit(Op::StoreLocal(i));
2023
2024 let loop_top = self.code.len();
2025 self.emit(Op::LoadLocal(i));
2026 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2027 self.emit(Op::IntLt);
2028 let j_exit = self.code.len();
2029 self.emit(Op::JumpIfNot(0));
2030
2031 self.emit(Op::LoadLocal(out));
2032 self.emit(Op::LoadLocal(list));
2033 self.emit(Op::LoadLocal(i));
2034 self.emit(Op::GetListElemDyn);
2035 self.emit(Op::ListAppend);
2036 self.emit(Op::StoreLocal(out));
2037
2038 self.emit(Op::LoadLocal(i));
2039 let one = self.pool.int(1);
2040 self.emit(Op::PushConst(one));
2041 self.emit(Op::IntAdd);
2042 self.emit(Op::StoreLocal(i));
2043
2044 let jback = self.code.len();
2045 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2046
2047 let exit_t = self.code.len() as i32;
2048 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2049 *off = exit_t - (j_exit as i32 + 1);
2050 }
2051
2052 let converge = self.code.len() as i32;
2054 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
2055 *off = converge - (j_after_lazy as i32 + 1);
2056 }
2057 self.emit(Op::LoadLocal(out));
2058 }
2059
2060 fn emit_iter_map(&mut self, args: &[a::CExpr]) {
2062 self.compile_expr(&args[0], false);
2063 let it = self.alloc_local("__im_it");
2064 self.emit(Op::StoreLocal(it));
2065
2066 self.compile_expr(&args[1], false);
2067 let f = self.alloc_local("__im_f");
2068 self.emit(Op::StoreLocal(f));
2069
2070 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2071 let list = self.alloc_local("__im_list");
2072 self.emit(Op::StoreLocal(list));
2073
2074 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2075 let i = self.alloc_local("__im_i");
2076 self.emit(Op::StoreLocal(i));
2077
2078 self.emit(Op::MakeList(0));
2079 let out = self.alloc_local("__im_out");
2080 self.emit(Op::StoreLocal(out));
2081
2082 let loop_top = self.code.len();
2083 self.emit(Op::LoadLocal(i));
2084 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2085 self.emit(Op::IntLt);
2086 let j_exit = self.code.len();
2087 self.emit(Op::JumpIfNot(0));
2088
2089 let nid = self.pool.node_id("n_iter_map");
2090 self.emit(Op::LoadLocal(out));
2091 self.emit(Op::LoadLocal(f));
2092 self.emit(Op::LoadLocal(list));
2093 self.emit(Op::LoadLocal(i));
2094 self.emit(Op::GetListElemDyn);
2095 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2096 self.emit(Op::ListAppend);
2097 self.emit(Op::StoreLocal(out));
2098
2099 self.emit(Op::LoadLocal(i));
2100 let one = self.pool.int(1);
2101 self.emit(Op::PushConst(one));
2102 self.emit(Op::IntAdd);
2103 self.emit(Op::StoreLocal(i));
2104
2105 let jback = self.code.len();
2106 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2107
2108 let exit_t = self.code.len() as i32;
2109 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
2110
2111 let zero = self.pool.int(0);
2112 self.emit(Op::LoadLocal(out));
2113 self.emit(Op::PushConst(zero));
2114 let eager_v = self.pool.variant("__IterEager");
2115 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
2116 }
2117
2118 fn emit_iter_filter(&mut self, args: &[a::CExpr]) {
2120 self.compile_expr(&args[0], false);
2121 let it = self.alloc_local("__if_it");
2122 self.emit(Op::StoreLocal(it));
2123
2124 self.compile_expr(&args[1], false);
2125 let f = self.alloc_local("__if_f");
2126 self.emit(Op::StoreLocal(f));
2127
2128 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2129 let list = self.alloc_local("__if_list");
2130 self.emit(Op::StoreLocal(list));
2131
2132 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2133 let i = self.alloc_local("__if_i");
2134 self.emit(Op::StoreLocal(i));
2135
2136 self.emit(Op::MakeList(0));
2137 let out = self.alloc_local("__if_out");
2138 self.emit(Op::StoreLocal(out));
2139
2140 let loop_top = self.code.len();
2141 self.emit(Op::LoadLocal(i));
2142 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2143 self.emit(Op::IntLt);
2144 let j_exit = self.code.len();
2145 self.emit(Op::JumpIfNot(0));
2146
2147 self.emit(Op::LoadLocal(list));
2149 self.emit(Op::LoadLocal(i));
2150 self.emit(Op::GetListElemDyn);
2151 let x = self.alloc_local("__if_x");
2152 self.emit(Op::StoreLocal(x));
2153
2154 let nid = self.pool.node_id("n_iter_filter");
2155 self.emit(Op::LoadLocal(f));
2156 self.emit(Op::LoadLocal(x));
2157 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2158 let j_skip = self.code.len();
2159 self.emit(Op::JumpIfNot(0));
2160
2161 self.emit(Op::LoadLocal(out));
2162 self.emit(Op::LoadLocal(x));
2163 self.emit(Op::ListAppend);
2164 self.emit(Op::StoreLocal(out));
2165
2166 let skip_t = self.code.len() as i32;
2167 if let Op::JumpIfNot(off) = &mut self.code[j_skip] { *off = skip_t - (j_skip as i32 + 1); }
2168
2169 self.emit(Op::LoadLocal(i));
2170 let one = self.pool.int(1);
2171 self.emit(Op::PushConst(one));
2172 self.emit(Op::IntAdd);
2173 self.emit(Op::StoreLocal(i));
2174
2175 let jback = self.code.len();
2176 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2177
2178 let exit_t = self.code.len() as i32;
2179 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
2180
2181 let zero = self.pool.int(0);
2182 self.emit(Op::LoadLocal(out));
2183 self.emit(Op::PushConst(zero));
2184 let eager_v = self.pool.variant("__IterEager");
2185 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
2186 }
2187
2188 fn emit_iter_fold(&mut self, args: &[a::CExpr]) {
2190 self.compile_expr(&args[0], false);
2191 let it = self.alloc_local("__ifo_it");
2192 self.emit(Op::StoreLocal(it));
2193
2194 self.compile_expr(&args[1], false);
2195 let acc = self.alloc_local("__ifo_acc");
2196 self.emit(Op::StoreLocal(acc));
2197
2198 self.compile_expr(&args[2], false);
2199 let f = self.alloc_local("__ifo_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("__ifo_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("__ifo_i");
2208 self.emit(Op::StoreLocal(i));
2209
2210 let loop_top = self.code.len();
2211 self.emit(Op::LoadLocal(i));
2212 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2213 self.emit(Op::IntLt);
2214 let j_exit = self.code.len();
2215 self.emit(Op::JumpIfNot(0));
2216
2217 let nid = self.pool.node_id("n_iter_fold");
2218 self.emit(Op::LoadLocal(f));
2219 self.emit(Op::LoadLocal(acc));
2220 self.emit(Op::LoadLocal(list));
2221 self.emit(Op::LoadLocal(i));
2222 self.emit(Op::GetListElemDyn);
2223 self.emit(Op::CallClosure { arity: 2, node_id_idx: nid });
2224 self.emit(Op::StoreLocal(acc));
2225
2226 self.emit(Op::LoadLocal(i));
2227 let one = self.pool.int(1);
2228 self.emit(Op::PushConst(one));
2229 self.emit(Op::IntAdd);
2230 self.emit(Op::StoreLocal(i));
2231
2232 let jback = self.code.len();
2233 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2234
2235 let exit_t = self.code.len() as i32;
2236 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
2237 self.emit(Op::LoadLocal(acc));
2238 }
2239
2240 fn emit_map_fold(&mut self, args: &[a::CExpr], node_id_idx: u32) {
2246 self.compile_expr(&args[0], false);
2248 let map_kind = self.pool.str("map");
2249 let entries_op = self.pool.str("entries");
2250 self.emit(Op::EffectCall {
2251 kind_idx: map_kind,
2252 op_idx: entries_op,
2253 arity: 1,
2254 node_id_idx,
2255 });
2256 let xs = self.alloc_local("__mf_xs");
2257 self.emit(Op::StoreLocal(xs));
2258
2259 self.compile_expr(&args[1], false);
2261 let acc = self.alloc_local("__mf_acc");
2262 self.emit(Op::StoreLocal(acc));
2263
2264 self.compile_expr(&args[2], false);
2266 let f = self.alloc_local("__mf_f");
2267 self.emit(Op::StoreLocal(f));
2268
2269 let zero = self.pool.int(0);
2271 self.emit(Op::PushConst(zero));
2272 let i = self.alloc_local("__mf_i");
2273 self.emit(Op::StoreLocal(i));
2274
2275 let loop_top = self.code.len();
2277 self.emit(Op::LoadLocal(i));
2278 self.emit(Op::LoadLocal(xs));
2279 self.emit(Op::GetListLen);
2280 self.emit(Op::IntLt);
2281 let j_exit = self.code.len();
2282 self.emit(Op::JumpIfNot(0));
2283
2284 self.emit(Op::LoadLocal(xs));
2286 self.emit(Op::LoadLocal(i));
2287 self.emit(Op::GetListElemDyn);
2288 let pair = self.alloc_local("__mf_pair");
2289 self.emit(Op::StoreLocal(pair));
2290
2291 let nid = self.pool.node_id("n_map_fold");
2293 self.emit(Op::LoadLocal(f));
2294 self.emit(Op::LoadLocal(acc));
2295 self.emit(Op::LoadLocal(pair));
2296 self.emit(Op::GetElem(0));
2297 self.emit(Op::LoadLocal(pair));
2298 self.emit(Op::GetElem(1));
2299 self.emit(Op::CallClosure { arity: 3, node_id_idx: nid });
2300 self.emit(Op::StoreLocal(acc));
2301
2302 self.emit(Op::LoadLocal(i));
2304 let one = self.pool.int(1);
2305 self.emit(Op::PushConst(one));
2306 self.emit(Op::IntAdd);
2307 self.emit(Op::StoreLocal(i));
2308
2309 let jump_back = self.code.len();
2310 let back = (loop_top as i32) - (jump_back as i32 + 1);
2311 self.emit(Op::Jump(back));
2312
2313 let exit_target = self.code.len() as i32;
2314 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2315 *off = exit_target - (j_exit as i32 + 1);
2316 }
2317 self.emit(Op::LoadLocal(acc));
2318 }
2319
2320 fn emit_variant_map(
2326 &mut self,
2327 args: &[a::CExpr],
2328 wrap_with: &str,
2329 wrap_result: bool,
2330 ) {
2331 let wrap_idx = self.pool.variant(wrap_with);
2333
2334 self.compile_expr(&args[0], false);
2336 let val_slot = self.alloc_local("__hov");
2337 self.emit(Op::StoreLocal(val_slot));
2338
2339 self.compile_expr(&args[1], false);
2340 let f_slot = self.alloc_local("__hof");
2341 self.emit(Op::StoreLocal(f_slot));
2342
2343 self.emit(Op::LoadLocal(val_slot));
2350 self.emit(Op::Dup);
2351 self.emit(Op::TestVariant(wrap_idx));
2352 let j_skip = self.code.len();
2353 self.emit(Op::JumpIfNot(0));
2354
2355 self.emit(Op::GetVariantArg(0));
2357 let arg_slot = self.alloc_local("__hov_arg");
2358 self.emit(Op::StoreLocal(arg_slot));
2359 self.emit(Op::LoadLocal(f_slot));
2360 self.emit(Op::LoadLocal(arg_slot));
2361 let nid = self.pool.node_id("n_hov");
2362 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2363 if wrap_result {
2364 self.emit(Op::MakeVariant { name_idx: wrap_idx, arity: 1 });
2365 }
2366 let j_end = self.code.len();
2367 self.emit(Op::Jump(0));
2368
2369 let skip_target = self.code.len() as i32;
2371 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
2372 *off = skip_target - (j_skip as i32 + 1);
2373 }
2374
2375 let end_target = self.code.len() as i32;
2376 if let Op::Jump(off) = &mut self.code[j_end] {
2377 *off = end_target - (j_end as i32 + 1);
2378 }
2379 }
2380
2381 fn emit_variant_or_else(
2390 &mut self,
2391 args: &[a::CExpr],
2392 match_on: &str,
2393 closure_arity: u16,
2394 ) {
2395 let match_idx = self.pool.variant(match_on);
2396
2397 self.compile_expr(&args[0], false);
2398 let val_slot = self.alloc_local("__hoe");
2399 self.emit(Op::StoreLocal(val_slot));
2400
2401 self.compile_expr(&args[1], false);
2402 let f_slot = self.alloc_local("__hoe_f");
2403 self.emit(Op::StoreLocal(f_slot));
2404
2405 self.emit(Op::LoadLocal(val_slot));
2413 self.emit(Op::Dup);
2414 self.emit(Op::TestVariant(match_idx));
2415 let j_skip = self.code.len();
2416 self.emit(Op::JumpIfNot(0));
2417
2418 self.emit(Op::Pop);
2421 self.emit(Op::LoadLocal(f_slot));
2422 if closure_arity == 1 {
2423 self.emit(Op::LoadLocal(val_slot));
2424 self.emit(Op::GetVariantArg(0));
2425 }
2426 let nid = self.pool.node_id("n_hoe");
2427 self.emit(Op::CallClosure { arity: closure_arity, node_id_idx: nid });
2428
2429 let j_end = self.code.len();
2430 self.emit(Op::Jump(0));
2431
2432 let skip_target = self.code.len() as i32;
2434 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
2435 *off = skip_target - (j_skip as i32 + 1);
2436 }
2437
2438 let end_target = self.code.len() as i32;
2439 if let Op::Jump(off) = &mut self.code[j_end] {
2440 *off = end_target - (j_end as i32 + 1);
2441 }
2442 }
2443
2444 fn emit_option_unwrap_or_else(&mut self, args: &[a::CExpr]) {
2448 let some_idx = self.pool.variant("Some");
2449
2450 self.compile_expr(&args[0], false);
2452 let val_slot = self.alloc_local("__uoe_val");
2453 self.emit(Op::StoreLocal(val_slot));
2454
2455 self.compile_expr(&args[1], false);
2456 let f_slot = self.alloc_local("__uoe_f");
2457 self.emit(Op::StoreLocal(f_slot));
2458
2459 self.emit(Op::LoadLocal(val_slot));
2465 self.emit(Op::Dup);
2466 self.emit(Op::TestVariant(some_idx));
2467 let j_none = self.code.len();
2468 self.emit(Op::JumpIfNot(0));
2469
2470 self.emit(Op::GetVariantArg(0));
2472 let j_end = self.code.len();
2473 self.emit(Op::Jump(0));
2474
2475 let none_target = self.code.len() as i32;
2477 if let Op::JumpIfNot(off) = &mut self.code[j_none] {
2478 *off = none_target - (j_none as i32 + 1);
2479 }
2480 self.emit(Op::Pop);
2481 self.emit(Op::LoadLocal(f_slot));
2482 let nid = self.pool.node_id("n_uoe");
2483 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2484
2485 let end_target = self.code.len() as i32;
2487 if let Op::Jump(off) = &mut self.code[j_end] {
2488 *off = end_target - (j_end as i32 + 1);
2489 }
2490 }
2491
2492 fn install_trampoline(&mut self, name: &str, arity: u16, locals_count: u16, code: Vec<Op>) -> u32 {
2509 let fn_id = self.next_fn_id.len() as u32;
2510 let body_hash = crate::program::compute_body_hash(
2511 arity, locals_count, &code, &self.pool.record_shapes);
2512 self.next_fn_id.push(Function {
2513 name: name.into(),
2514 arity,
2515 locals_count,
2516 code,
2517 effects: Vec::new(),
2518 body_hash,
2519 refinements: Vec::new(),
2522 field_ic_sites: 0,
2526 });
2527 fn_id
2528 }
2529
2530 fn emit_flow_sequential(&mut self, args: &[a::CExpr]) {
2532 self.compile_expr(&args[0], false);
2534 self.compile_expr(&args[1], false);
2535 let nid = self.pool.node_id("n_flow_sequential");
2536 let code = vec![
2537 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,
2547 ];
2548 let fn_id = self.install_trampoline("__flow_sequential", 3, 4, code);
2549 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2550 }
2551
2552 fn emit_flow_parallel(&mut self, args: &[a::CExpr]) {
2560 self.compile_expr(&args[0], false);
2562 self.compile_expr(&args[1], false);
2563 let nid = self.pool.node_id("n_flow_parallel");
2564 let code = vec![
2565 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,
2572 ];
2573 let fn_id = self.install_trampoline("__flow_parallel", 2, 2, code);
2574 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2575 }
2576
2577 fn emit_flow_parallel_list(&mut self, args: &[a::CExpr]) {
2584 self.compile_expr(&args[0], false);
2586 let xs = self.alloc_local("__fpl_xs");
2587 self.emit(Op::StoreLocal(xs));
2588
2589 self.emit(Op::MakeList(0));
2591 let out = self.alloc_local("__fpl_out");
2592 self.emit(Op::StoreLocal(out));
2593
2594 let zero = self.pool.int(0);
2596 self.emit(Op::PushConst(zero));
2597 let i = self.alloc_local("__fpl_i");
2598 self.emit(Op::StoreLocal(i));
2599
2600 let loop_top = self.code.len();
2602 self.emit(Op::LoadLocal(i));
2603 self.emit(Op::LoadLocal(xs));
2604 self.emit(Op::GetListLen);
2605 self.emit(Op::IntLt);
2606 let j_exit = self.code.len();
2607 self.emit(Op::JumpIfNot(0));
2608
2609 let nid = self.pool.node_id("n_flow_parallel_list");
2611 self.emit(Op::LoadLocal(out));
2612 self.emit(Op::LoadLocal(xs));
2613 self.emit(Op::LoadLocal(i));
2614 self.emit(Op::GetListElemDyn);
2615 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2616 self.emit(Op::ListAppend);
2617 self.emit(Op::StoreLocal(out));
2618
2619 self.emit(Op::LoadLocal(i));
2621 let one = self.pool.int(1);
2622 self.emit(Op::PushConst(one));
2623 self.emit(Op::IntAdd);
2624 self.emit(Op::StoreLocal(i));
2625
2626 let jump_back = self.code.len();
2628 let back = (loop_top as i32) - (jump_back as i32 + 1);
2629 self.emit(Op::Jump(back));
2630
2631 let exit_target = self.code.len() as i32;
2633 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2634 *off = exit_target - (j_exit as i32 + 1);
2635 }
2636 self.emit(Op::LoadLocal(out));
2637 }
2638
2639 fn emit_flow_branch(&mut self, args: &[a::CExpr]) {
2641 self.compile_expr(&args[0], false);
2642 self.compile_expr(&args[1], false);
2643 self.compile_expr(&args[2], false);
2644 let nid = self.pool.node_id("n_flow_branch");
2645 let mut code = vec![
2646 Op::LoadLocal(0), Op::LoadLocal(3), Op::CallClosure { arity: 1, node_id_idx: nid }, ];
2651 let j_false = code.len();
2652 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(1));
2655 code.push(Op::LoadLocal(3));
2656 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2657 code.push(Op::Return);
2658 let false_target = code.len() as i32;
2660 if let Op::JumpIfNot(off) = &mut code[j_false] {
2661 *off = false_target - (j_false as i32 + 1);
2662 }
2663 code.push(Op::LoadLocal(2));
2664 code.push(Op::LoadLocal(3));
2665 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2666 code.push(Op::Return);
2667
2668 let fn_id = self.install_trampoline("__flow_branch", 4, 4, code);
2669 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2670 }
2671
2672 fn emit_flow_retry(&mut self, args: &[a::CExpr]) {
2676 self.compile_expr(&args[0], false);
2677 self.compile_expr(&args[1], false);
2678 let call_nid = self.pool.node_id("n_flow_retry");
2679 let ok_idx = self.pool.variant("Ok");
2680 let zero_const = self.pool.int(0);
2681 let one_const = self.pool.int(1);
2682 let mut code = vec![
2684 Op::PushConst(zero_const),
2686 Op::StoreLocal(3),
2687 ];
2688 let loop_top = code.len() as i32;
2690 code.push(Op::LoadLocal(3));
2691 code.push(Op::LoadLocal(1));
2692 code.push(Op::IntLt);
2693 let j_done = code.len();
2694 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(0));
2698 code.push(Op::LoadLocal(2));
2699 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2700 code.push(Op::StoreLocal(4));
2701
2702 code.push(Op::LoadLocal(4));
2704 code.push(Op::TestVariant(ok_idx));
2705 let j_was_err = code.len();
2706 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(4));
2708 code.push(Op::Return);
2709
2710 let was_err_target = code.len() as i32;
2712 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2713 *off = was_err_target - (j_was_err as i32 + 1);
2714 }
2715 code.push(Op::LoadLocal(3));
2716 code.push(Op::PushConst(one_const));
2717 code.push(Op::IntAdd);
2718 code.push(Op::StoreLocal(3));
2719 let pc_after_jump = code.len() as i32 + 1;
2720 code.push(Op::Jump(loop_top - pc_after_jump));
2721
2722 let done_target = code.len() as i32;
2724 if let Op::JumpIfNot(off) = &mut code[j_done] {
2725 *off = done_target - (j_done as i32 + 1);
2726 }
2727 code.push(Op::LoadLocal(4));
2728 code.push(Op::Return);
2729
2730 let fn_id = self.install_trampoline("__flow_retry", 3, 5, code);
2731 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2732 }
2733
2734 fn emit_flow_retry_with_backoff(&mut self, args: &[a::CExpr]) {
2742 self.compile_expr(&args[0], false);
2745 self.compile_expr(&args[1], false);
2746 self.compile_expr(&args[2], false);
2747 let call_nid = self.pool.node_id("n_flow_retry_backoff");
2748 let sleep_nid = self.pool.node_id("n_flow_retry_backoff_sleep");
2749 let kind_idx = self.pool.str("time");
2750 let op_idx = self.pool.str("sleep_ms");
2751 let ok_idx = self.pool.variant("Ok");
2752 let zero_const = self.pool.int(0);
2753 let one_const = self.pool.int(1);
2754 let two_const = self.pool.int(2);
2755 let mut code = vec![
2760 Op::LoadLocal(2),
2762 Op::StoreLocal(6),
2763 Op::PushConst(zero_const),
2765 Op::StoreLocal(4),
2766 ];
2767
2768 let loop_top = code.len() as i32;
2769 code.push(Op::LoadLocal(4));
2771 code.push(Op::LoadLocal(1));
2772 code.push(Op::IntLt);
2773 let j_done = code.len();
2774 code.push(Op::JumpIfNot(0)); code.push(Op::PushConst(zero_const));
2778 code.push(Op::LoadLocal(4));
2779 code.push(Op::IntLt); let j_no_sleep = code.len();
2781 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(6)); code.push(Op::EffectCall {
2785 kind_idx, op_idx, arity: 1, node_id_idx: sleep_nid,
2786 });
2787 code.push(Op::Pop); code.push(Op::LoadLocal(6));
2790 code.push(Op::PushConst(two_const));
2791 code.push(Op::NumMul);
2792 code.push(Op::StoreLocal(6));
2793 let after_sleep = code.len() as i32;
2795 if let Op::JumpIfNot(off) = &mut code[j_no_sleep] {
2796 *off = after_sleep - (j_no_sleep as i32 + 1);
2797 }
2798
2799 code.push(Op::LoadLocal(0));
2801 code.push(Op::LoadLocal(3));
2802 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2803 code.push(Op::StoreLocal(5));
2804
2805 code.push(Op::LoadLocal(5));
2807 code.push(Op::TestVariant(ok_idx));
2808 let j_was_err = code.len();
2809 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(5));
2811 code.push(Op::Return);
2812
2813 let was_err_target = code.len() as i32;
2815 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2816 *off = was_err_target - (j_was_err as i32 + 1);
2817 }
2818 code.push(Op::LoadLocal(4));
2819 code.push(Op::PushConst(one_const));
2820 code.push(Op::IntAdd);
2821 code.push(Op::StoreLocal(4));
2822 let pc_after_jump = code.len() as i32 + 1;
2823 code.push(Op::Jump(loop_top - pc_after_jump));
2824
2825 let done_target = code.len() as i32;
2827 if let Op::JumpIfNot(off) = &mut code[j_done] {
2828 *off = done_target - (j_done as i32 + 1);
2829 }
2830 code.push(Op::LoadLocal(5));
2831 code.push(Op::Return);
2832
2833 let fn_id = self.install_trampoline("__flow_retry_backoff", 4, 7, code);
2834 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2835 }
2836}
2837
2838fn free_vars(e: &a::CExpr, bound: &mut std::collections::HashSet<String>, out: &mut Vec<String>) {
2843 match e {
2844 a::CExpr::Literal { .. } => {}
2845 a::CExpr::Var { name } => {
2846 if !bound.contains(name) && !out.contains(name) {
2847 out.push(name.clone());
2848 }
2849 }
2850 a::CExpr::Call { callee, args } => {
2851 free_vars(callee, bound, out);
2852 for a in args { free_vars(a, bound, out); }
2853 }
2854 a::CExpr::Let { name, value, body, .. } => {
2855 free_vars(value, bound, out);
2856 let was_bound = bound.contains(name);
2857 bound.insert(name.clone());
2858 free_vars(body, bound, out);
2859 if !was_bound { bound.remove(name); }
2860 }
2861 a::CExpr::Match { scrutinee, arms } => {
2862 free_vars(scrutinee, bound, out);
2863 for arm in arms {
2864 let mut local_bound = bound.clone();
2865 pattern_binders(&arm.pattern, &mut local_bound);
2866 free_vars(&arm.body, &mut local_bound, out);
2867 }
2868 }
2869 a::CExpr::Block { statements, result } => {
2870 let mut local_bound = bound.clone();
2871 for s in statements { free_vars(s, &mut local_bound, out); }
2872 free_vars(result, &mut local_bound, out);
2873 }
2874 a::CExpr::Constructor { args, .. } => {
2875 for a in args { free_vars(a, bound, out); }
2876 }
2877 a::CExpr::RecordLit { fields } => {
2878 for f in fields { free_vars(&f.value, bound, out); }
2879 }
2880 a::CExpr::TupleLit { items } | a::CExpr::ListLit { items } => {
2881 for it in items { free_vars(it, bound, out); }
2882 }
2883 a::CExpr::FieldAccess { value, .. } => free_vars(value, bound, out),
2884 a::CExpr::Lambda { params, body, .. } => {
2885 let mut inner = bound.clone();
2886 for p in params { inner.insert(p.name.clone()); }
2887 free_vars(body, &mut inner, out);
2888 }
2889 a::CExpr::BinOp { lhs, rhs, .. } => {
2890 free_vars(lhs, bound, out);
2891 free_vars(rhs, bound, out);
2892 }
2893 a::CExpr::UnaryOp { expr, .. } => free_vars(expr, bound, out),
2894 a::CExpr::Return { value } => free_vars(value, bound, out),
2895 }
2896}
2897
2898fn pattern_binders(p: &a::Pattern, bound: &mut std::collections::HashSet<String>) {
2899 match p {
2900 a::Pattern::PWild | a::Pattern::PLiteral { .. } => {}
2901 a::Pattern::PVar { name } => { bound.insert(name.clone()); }
2902 a::Pattern::PConstructor { args, .. } => {
2903 for a in args { pattern_binders(a, bound); }
2904 }
2905 a::Pattern::PRecord { fields } => {
2906 for f in fields { pattern_binders(&f.pattern, bound); }
2907 }
2908 a::Pattern::PTuple { items } => {
2909 for it in items { pattern_binders(it, bound); }
2910 }
2911 }
2912}