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 ("result", "unwrap_or_else") => self.emit_result_unwrap_or_else(args),
1522 ("list", "map") => self.emit_list_map(args),
1523 ("list", "par_map") => self.emit_list_par_map(args),
1524 ("list", "sort_by") => self.emit_list_sort_by(args),
1525 ("list", "filter") => self.emit_list_filter(args),
1526 ("list", "fold") => self.emit_list_fold(args),
1527 ("iter", "from_list") => self.emit_iter_from_list(args),
1528 ("iter", "unfold") => self.emit_iter_unfold(args),
1529 ("iter", "next") => self.emit_iter_next(args),
1530 ("iter", "is_empty") => self.emit_iter_is_empty(args),
1531 ("iter", "count") => self.emit_iter_count(args),
1532 ("iter", "take") => self.emit_iter_take(args),
1533 ("iter", "skip") => self.emit_iter_skip(args),
1534 ("iter", "to_list") => self.emit_iter_to_list(args),
1535 ("iter", "collect") => self.emit_iter_to_list(args),
1536 ("iter", "map") => self.emit_iter_map(args),
1537 ("iter", "filter") => self.emit_iter_filter(args),
1538 ("iter", "fold") => self.emit_iter_fold(args),
1539 ("map", "fold") => self.emit_map_fold(args, node_id_idx),
1540 ("flow", "sequential") => self.emit_flow_sequential(args),
1541 ("flow", "branch") => self.emit_flow_branch(args),
1542 ("flow", "retry") => self.emit_flow_retry(args),
1543 ("flow", "retry_with_backoff") => self.emit_flow_retry_with_backoff(args),
1544 ("flow", "parallel") => self.emit_flow_parallel(args),
1545 ("flow", "parallel_list") => self.emit_flow_parallel_list(args),
1546 _ => return false,
1547 }
1548 true
1549 }
1550
1551 fn emit_list_map(&mut self, args: &[a::CExpr]) {
1557 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_map");
1560 self.emit(Op::ListMap { node_id_idx: nid });
1561 }
1562
1563 fn emit_list_par_map(&mut self, args: &[a::CExpr]) {
1569 self.compile_expr(&args[0], false);
1570 self.compile_expr(&args[1], false);
1571 let nid = self.pool.node_id("n_list_par_map");
1572 self.emit(Op::ParallelMap { node_id_idx: nid });
1573 }
1574
1575 fn emit_list_sort_by(&mut self, args: &[a::CExpr]) {
1583 self.compile_expr(&args[0], false);
1584 self.compile_expr(&args[1], false);
1585 let nid = self.pool.node_id("n_list_sort_by");
1586 self.emit(Op::SortByKey { node_id_idx: nid });
1587 }
1588
1589 fn emit_list_filter(&mut self, args: &[a::CExpr]) {
1592 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let nid = self.pool.node_id("n_list_filter");
1595 self.emit(Op::ListFilter { node_id_idx: nid });
1596 }
1597
1598 fn emit_list_fold(&mut self, args: &[a::CExpr]) {
1601 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");
1605 self.emit(Op::ListFold { node_id_idx: nid });
1606 }
1607
1608 fn emit_iter_from_list(&mut self, args: &[a::CExpr]) {
1620 self.compile_expr(&args[0], false);
1621 let zero = self.pool.int(0);
1622 self.emit(Op::PushConst(zero));
1623 let v = self.pool.variant("__IterEager");
1624 self.emit(Op::MakeVariant { name_idx: v, arity: 2 });
1625 }
1626
1627 fn emit_iter_next(&mut self, args: &[a::CExpr]) {
1639 self.compile_expr(&args[0], false);
1640 let it = self.alloc_local("__in_it");
1641 self.emit(Op::StoreLocal(it));
1642
1643 self.emit(Op::LoadLocal(it));
1645 self.emit(Op::Dup);
1646 let lazy_name = self.pool.variant("__IterLazy");
1647 self.emit(Op::TestVariant(lazy_name));
1648 let j_to_check_cursor = self.code.len();
1649 self.emit(Op::JumpIfNot(0));
1650
1651 self.emit(Op::LoadLocal(it));
1655 self.emit(Op::GetVariantArg(0)); let seed = self.alloc_local("__in_seed");
1657 self.emit(Op::StoreLocal(seed));
1658
1659 self.emit(Op::LoadLocal(it));
1660 self.emit(Op::GetVariantArg(1)); let step = self.alloc_local("__in_step");
1662 self.emit(Op::StoreLocal(step));
1663
1664 let nid_lazy = self.pool.node_id("n_iter_next_lazy");
1666 self.emit(Op::LoadLocal(step));
1667 self.emit(Op::LoadLocal(seed));
1668 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
1669 let opt = self.alloc_local("__in_opt");
1670 self.emit(Op::StoreLocal(opt));
1671
1672 self.emit(Op::LoadLocal(opt));
1674 let some_name = self.pool.variant("Some");
1675 self.emit(Op::TestVariant(some_name));
1676 let j_lazy_none = self.code.len();
1677 self.emit(Op::JumpIfNot(0));
1678
1679 self.emit(Op::LoadLocal(opt));
1682 self.emit(Op::GetVariantArg(0)); let pair = self.alloc_local("__in_pair");
1684 self.emit(Op::StoreLocal(pair));
1685
1686 self.emit(Op::LoadLocal(pair));
1687 self.emit(Op::GetElem(0)); self.emit(Op::LoadLocal(pair));
1689 self.emit(Op::GetElem(1)); self.emit(Op::LoadLocal(step)); let lazy_v = self.pool.variant("__IterLazy");
1692 self.emit(Op::MakeVariant { name_idx: lazy_v, arity: 2 }); self.emit(Op::MakeTuple(2)); let some_v = self.pool.variant("Some");
1695 self.emit(Op::MakeVariant { name_idx: some_v, arity: 1 });
1696 let j_after_lazy = self.code.len();
1697 self.emit(Op::Jump(0));
1698
1699 let none_t = self.code.len() as i32;
1701 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_none] {
1702 *off = none_t - (j_lazy_none as i32 + 1);
1703 }
1704 let none_v = self.pool.variant("None");
1705 self.emit(Op::MakeVariant { name_idx: none_v, arity: 0 });
1706 let j_after_lazy_none = self.code.len();
1707 self.emit(Op::Jump(0));
1708
1709 let cursor_check_t = self.code.len() as i32;
1711 if let Op::JumpIfNot(off) = &mut self.code[j_to_check_cursor] {
1712 *off = cursor_check_t - (j_to_check_cursor as i32 + 1);
1713 }
1714
1715 self.emit(Op::LoadLocal(it));
1716 self.emit(Op::Dup);
1717 let cursor_name = self.pool.variant("__IterCursor");
1718 self.emit(Op::TestVariant(cursor_name));
1719 let j_to_eager = self.code.len();
1720 self.emit(Op::JumpIfNot(0));
1721
1722 self.emit(Op::LoadLocal(it));
1726 self.emit(Op::GetVariantArg(0)); let handle = self.alloc_local("__in_handle");
1728 self.emit(Op::StoreLocal(handle));
1729
1730 let kind_idx = self.pool.str("sql");
1731 let op_idx = self.pool.str("cursor_next");
1732 let nid_cursor = self.pool.node_id("n_iter_next_cursor");
1733 self.emit(Op::LoadLocal(handle));
1734 self.emit(Op::EffectCall {
1735 kind_idx,
1736 op_idx,
1737 arity: 1,
1738 node_id_idx: nid_cursor,
1739 });
1740 let cur_opt = self.alloc_local("__in_cur_opt");
1741 self.emit(Op::StoreLocal(cur_opt));
1742
1743 self.emit(Op::LoadLocal(cur_opt));
1744 let some_c = self.pool.variant("Some");
1745 self.emit(Op::TestVariant(some_c));
1746 let j_cursor_none = self.code.len();
1747 self.emit(Op::JumpIfNot(0));
1748
1749 self.emit(Op::LoadLocal(cur_opt));
1751 self.emit(Op::GetVariantArg(0)); self.emit(Op::LoadLocal(handle));
1753 let cursor_v = self.pool.variant("__IterCursor");
1754 self.emit(Op::MakeVariant { name_idx: cursor_v, arity: 1 });
1755 self.emit(Op::MakeTuple(2)); let some_c2 = self.pool.variant("Some");
1757 self.emit(Op::MakeVariant { name_idx: some_c2, arity: 1 });
1758 let j_after_cursor = self.code.len();
1759 self.emit(Op::Jump(0));
1760
1761 let cursor_none_t = self.code.len() as i32;
1763 if let Op::JumpIfNot(off) = &mut self.code[j_cursor_none] {
1764 *off = cursor_none_t - (j_cursor_none as i32 + 1);
1765 }
1766 let none_c = self.pool.variant("None");
1767 self.emit(Op::MakeVariant { name_idx: none_c, arity: 0 });
1768 let j_after_cursor_none = self.code.len();
1769 self.emit(Op::Jump(0));
1770
1771 let eager_t = self.code.len() as i32;
1773 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
1774 *off = eager_t - (j_to_eager as i32 + 1);
1775 }
1776
1777 self.emit(Op::LoadLocal(it));
1778 self.emit(Op::GetVariantArg(0));
1779 let list = self.alloc_local("__in_list");
1780 self.emit(Op::StoreLocal(list));
1781
1782 self.emit(Op::LoadLocal(it));
1783 self.emit(Op::GetVariantArg(1));
1784 let idx = self.alloc_local("__in_idx");
1785 self.emit(Op::StoreLocal(idx));
1786
1787 self.emit(Op::LoadLocal(idx));
1789 self.emit(Op::LoadLocal(list));
1790 self.emit(Op::GetListLen);
1791 self.emit(Op::IntLt);
1792 let j_eager_else = self.code.len();
1793 self.emit(Op::JumpIfNot(0));
1794
1795 self.emit(Op::LoadLocal(list));
1797 self.emit(Op::LoadLocal(idx));
1798 self.emit(Op::GetListElemDyn);
1799
1800 self.emit(Op::LoadLocal(list));
1801 self.emit(Op::LoadLocal(idx));
1802 let one = self.pool.int(1);
1803 self.emit(Op::PushConst(one));
1804 self.emit(Op::IntAdd);
1805 let eager_v = self.pool.variant("__IterEager");
1806 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1807 self.emit(Op::MakeTuple(2));
1808 let some_e = self.pool.variant("Some");
1809 self.emit(Op::MakeVariant { name_idx: some_e, arity: 1 });
1810 let j_after_eager = self.code.len();
1811 self.emit(Op::Jump(0));
1812
1813 let eager_none_t = self.code.len() as i32;
1815 if let Op::JumpIfNot(off) = &mut self.code[j_eager_else] {
1816 *off = eager_none_t - (j_eager_else as i32 + 1);
1817 }
1818 let none_e = self.pool.variant("None");
1819 self.emit(Op::MakeVariant { name_idx: none_e, arity: 0 });
1820
1821 let end = self.code.len() as i32;
1823 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
1824 *off = end - (j_after_lazy as i32 + 1);
1825 }
1826 if let Op::Jump(off) = &mut self.code[j_after_lazy_none] {
1827 *off = end - (j_after_lazy_none as i32 + 1);
1828 }
1829 if let Op::Jump(off) = &mut self.code[j_after_cursor] {
1830 *off = end - (j_after_cursor as i32 + 1);
1831 }
1832 if let Op::Jump(off) = &mut self.code[j_after_cursor_none] {
1833 *off = end - (j_after_cursor_none as i32 + 1);
1834 }
1835 if let Op::Jump(off) = &mut self.code[j_after_eager] {
1836 *off = end - (j_after_eager as i32 + 1);
1837 }
1838 }
1839
1840 fn emit_iter_unfold(&mut self, args: &[a::CExpr]) {
1845 self.compile_expr(&args[0], false); self.compile_expr(&args[1], false); let lazy = self.pool.variant("__IterLazy");
1848 self.emit(Op::MakeVariant { name_idx: lazy, arity: 2 });
1849 }
1850
1851 fn emit_iter_is_empty(&mut self, args: &[a::CExpr]) {
1857 self.compile_expr(&args[0], false);
1858 let it = self.alloc_local("__ie_it");
1859 self.emit(Op::StoreLocal(it));
1860
1861 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); }
1867
1868 fn emit_iter_count(&mut self, args: &[a::CExpr]) {
1870 self.compile_expr(&args[0], false);
1871 let it = self.alloc_local("__ic_it");
1872 self.emit(Op::StoreLocal(it));
1873
1874 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1875 self.emit(Op::GetListLen); self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1)); self.emit(Op::IntSub); }
1879
1880 fn emit_iter_take(&mut self, args: &[a::CExpr]) {
1882 self.compile_expr(&args[0], false);
1883 let it = self.alloc_local("__itk_it");
1884 self.emit(Op::StoreLocal(it));
1885
1886 self.compile_expr(&args[1], false);
1887 let n = self.alloc_local("__itk_n");
1888 self.emit(Op::StoreLocal(n));
1889
1890 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1891 let list = self.alloc_local("__itk_list");
1892 self.emit(Op::StoreLocal(list));
1893
1894 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1895 let i = self.alloc_local("__itk_i");
1896 self.emit(Op::StoreLocal(i));
1897
1898 self.emit(Op::MakeList(0));
1899 let out = self.alloc_local("__itk_out");
1900 self.emit(Op::StoreLocal(out));
1901
1902 let zero = self.pool.int(0);
1903 self.emit(Op::PushConst(zero));
1904 let cnt = self.alloc_local("__itk_cnt");
1905 self.emit(Op::StoreLocal(cnt));
1906
1907 let loop_top = self.code.len();
1908
1909 self.emit(Op::LoadLocal(cnt));
1911 self.emit(Op::LoadLocal(n));
1912 self.emit(Op::IntLt);
1913 let j_exit_n = self.code.len();
1914 self.emit(Op::JumpIfNot(0));
1915
1916 self.emit(Op::LoadLocal(i));
1918 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1919 self.emit(Op::IntLt);
1920 let j_exit_l = self.code.len();
1921 self.emit(Op::JumpIfNot(0));
1922
1923 self.emit(Op::LoadLocal(out));
1925 self.emit(Op::LoadLocal(list));
1926 self.emit(Op::LoadLocal(i));
1927 self.emit(Op::GetListElemDyn);
1928 self.emit(Op::ListAppend);
1929 self.emit(Op::StoreLocal(out));
1930
1931 let one = self.pool.int(1);
1932 self.emit(Op::LoadLocal(i));
1934 self.emit(Op::PushConst(one));
1935 self.emit(Op::IntAdd);
1936 self.emit(Op::StoreLocal(i));
1937 self.emit(Op::LoadLocal(cnt));
1939 self.emit(Op::PushConst(one));
1940 self.emit(Op::IntAdd);
1941 self.emit(Op::StoreLocal(cnt));
1942
1943 let jback = self.code.len();
1944 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
1945
1946 let exit_t = self.code.len() as i32;
1947 if let Op::JumpIfNot(off) = &mut self.code[j_exit_n] { *off = exit_t - (j_exit_n as i32 + 1); }
1948 if let Op::JumpIfNot(off) = &mut self.code[j_exit_l] { *off = exit_t - (j_exit_l as i32 + 1); }
1949
1950 self.emit(Op::LoadLocal(out));
1952 self.emit(Op::PushConst(zero));
1953 let eager_v = self.pool.variant("__IterEager");
1954 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
1955 }
1956
1957 fn emit_iter_skip(&mut self, args: &[a::CExpr]) {
1959 self.compile_expr(&args[0], false);
1960 let it = self.alloc_local("__isk_it");
1961 self.emit(Op::StoreLocal(it));
1962
1963 self.compile_expr(&args[1], false);
1964 let n = self.alloc_local("__isk_n");
1965 self.emit(Op::StoreLocal(n));
1966
1967 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
1968 let list = self.alloc_local("__isk_list");
1969 self.emit(Op::StoreLocal(list));
1970
1971 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
1972 let idx = self.alloc_local("__isk_idx");
1973 self.emit(Op::StoreLocal(idx));
1974
1975 self.emit(Op::LoadLocal(idx));
1977 self.emit(Op::LoadLocal(n));
1978 self.emit(Op::IntAdd);
1979 let raw = self.alloc_local("__isk_raw");
1980 self.emit(Op::StoreLocal(raw));
1981
1982 self.emit(Op::LoadLocal(raw));
1984 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1985 self.emit(Op::IntLt);
1986 let j_use_raw = self.code.len();
1987 self.emit(Op::JumpIf(0));
1988
1989 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
1991 let j_end = self.code.len();
1992 self.emit(Op::Jump(0));
1993
1994 let raw_t = self.code.len() as i32;
1996 if let Op::JumpIf(off) = &mut self.code[j_use_raw] { *off = raw_t - (j_use_raw as i32 + 1); }
1997 self.emit(Op::LoadLocal(raw));
1998
1999 let end_t = self.code.len() as i32;
2000 if let Op::Jump(off) = &mut self.code[j_end] { *off = end_t - (j_end as i32 + 1); }
2001
2002 let new_idx = self.alloc_local("__isk_ni");
2004 self.emit(Op::StoreLocal(new_idx));
2005 self.emit(Op::LoadLocal(list));
2006 self.emit(Op::LoadLocal(new_idx));
2007 let eager_v = self.pool.variant("__IterEager");
2008 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
2009 }
2010
2011 fn emit_iter_to_list(&mut self, args: &[a::CExpr]) {
2020 self.compile_expr(&args[0], false);
2021 let it = self.alloc_local("__itl_it");
2022 self.emit(Op::StoreLocal(it));
2023
2024 self.emit(Op::MakeList(0));
2026 let out = self.alloc_local("__itl_out");
2027 self.emit(Op::StoreLocal(out));
2028
2029 self.emit(Op::LoadLocal(it));
2031 let lazy_name = self.pool.variant("__IterLazy");
2032 self.emit(Op::TestVariant(lazy_name));
2033 let j_to_eager = self.code.len();
2034 self.emit(Op::JumpIfNot(0));
2035
2036 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2039 let seed = self.alloc_local("__itl_seed");
2040 self.emit(Op::StoreLocal(seed));
2041
2042 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2043 let step = self.alloc_local("__itl_step");
2044 self.emit(Op::StoreLocal(step));
2045
2046 let lazy_loop = self.code.len();
2047 let nid_lazy = self.pool.node_id("n_iter_to_list_lazy");
2048 self.emit(Op::LoadLocal(step));
2049 self.emit(Op::LoadLocal(seed));
2050 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid_lazy });
2051 let opt = self.alloc_local("__itl_opt");
2052 self.emit(Op::StoreLocal(opt));
2053
2054 self.emit(Op::LoadLocal(opt));
2056 let some_name = self.pool.variant("Some");
2057 self.emit(Op::TestVariant(some_name));
2058 let j_lazy_exit = self.code.len();
2059 self.emit(Op::JumpIfNot(0));
2060
2061 self.emit(Op::LoadLocal(opt));
2063 self.emit(Op::GetVariantArg(0));
2064 let pair = self.alloc_local("__itl_pair");
2065 self.emit(Op::StoreLocal(pair));
2066
2067 self.emit(Op::LoadLocal(out));
2068 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(0));
2069 self.emit(Op::ListAppend);
2070 self.emit(Op::StoreLocal(out));
2071
2072 self.emit(Op::LoadLocal(pair)); self.emit(Op::GetElem(1));
2073 self.emit(Op::StoreLocal(seed));
2074
2075 let jback_lazy = self.code.len();
2076 self.emit(Op::Jump((lazy_loop as i32) - (jback_lazy as i32 + 1)));
2077
2078 let lazy_exit_t = self.code.len() as i32;
2079 if let Op::JumpIfNot(off) = &mut self.code[j_lazy_exit] {
2080 *off = lazy_exit_t - (j_lazy_exit as i32 + 1);
2081 }
2082 let j_after_lazy = self.code.len();
2083 self.emit(Op::Jump(0));
2084
2085 let eager_t = self.code.len() as i32;
2087 if let Op::JumpIfNot(off) = &mut self.code[j_to_eager] {
2088 *off = eager_t - (j_to_eager as i32 + 1);
2089 }
2090
2091 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2092 let list = self.alloc_local("__itl_list");
2093 self.emit(Op::StoreLocal(list));
2094
2095 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2096 let i = self.alloc_local("__itl_i");
2097 self.emit(Op::StoreLocal(i));
2098
2099 let loop_top = self.code.len();
2100 self.emit(Op::LoadLocal(i));
2101 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2102 self.emit(Op::IntLt);
2103 let j_exit = self.code.len();
2104 self.emit(Op::JumpIfNot(0));
2105
2106 self.emit(Op::LoadLocal(out));
2107 self.emit(Op::LoadLocal(list));
2108 self.emit(Op::LoadLocal(i));
2109 self.emit(Op::GetListElemDyn);
2110 self.emit(Op::ListAppend);
2111 self.emit(Op::StoreLocal(out));
2112
2113 self.emit(Op::LoadLocal(i));
2114 let one = self.pool.int(1);
2115 self.emit(Op::PushConst(one));
2116 self.emit(Op::IntAdd);
2117 self.emit(Op::StoreLocal(i));
2118
2119 let jback = self.code.len();
2120 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2121
2122 let exit_t = self.code.len() as i32;
2123 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2124 *off = exit_t - (j_exit as i32 + 1);
2125 }
2126
2127 let converge = self.code.len() as i32;
2129 if let Op::Jump(off) = &mut self.code[j_after_lazy] {
2130 *off = converge - (j_after_lazy as i32 + 1);
2131 }
2132 self.emit(Op::LoadLocal(out));
2133 }
2134
2135 fn emit_iter_map(&mut self, args: &[a::CExpr]) {
2137 self.compile_expr(&args[0], false);
2138 let it = self.alloc_local("__im_it");
2139 self.emit(Op::StoreLocal(it));
2140
2141 self.compile_expr(&args[1], false);
2142 let f = self.alloc_local("__im_f");
2143 self.emit(Op::StoreLocal(f));
2144
2145 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2146 let list = self.alloc_local("__im_list");
2147 self.emit(Op::StoreLocal(list));
2148
2149 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2150 let i = self.alloc_local("__im_i");
2151 self.emit(Op::StoreLocal(i));
2152
2153 self.emit(Op::MakeList(0));
2154 let out = self.alloc_local("__im_out");
2155 self.emit(Op::StoreLocal(out));
2156
2157 let loop_top = self.code.len();
2158 self.emit(Op::LoadLocal(i));
2159 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2160 self.emit(Op::IntLt);
2161 let j_exit = self.code.len();
2162 self.emit(Op::JumpIfNot(0));
2163
2164 let nid = self.pool.node_id("n_iter_map");
2165 self.emit(Op::LoadLocal(out));
2166 self.emit(Op::LoadLocal(f));
2167 self.emit(Op::LoadLocal(list));
2168 self.emit(Op::LoadLocal(i));
2169 self.emit(Op::GetListElemDyn);
2170 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2171 self.emit(Op::ListAppend);
2172 self.emit(Op::StoreLocal(out));
2173
2174 self.emit(Op::LoadLocal(i));
2175 let one = self.pool.int(1);
2176 self.emit(Op::PushConst(one));
2177 self.emit(Op::IntAdd);
2178 self.emit(Op::StoreLocal(i));
2179
2180 let jback = self.code.len();
2181 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2182
2183 let exit_t = self.code.len() as i32;
2184 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
2185
2186 let zero = self.pool.int(0);
2187 self.emit(Op::LoadLocal(out));
2188 self.emit(Op::PushConst(zero));
2189 let eager_v = self.pool.variant("__IterEager");
2190 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
2191 }
2192
2193 fn emit_iter_filter(&mut self, args: &[a::CExpr]) {
2195 self.compile_expr(&args[0], false);
2196 let it = self.alloc_local("__if_it");
2197 self.emit(Op::StoreLocal(it));
2198
2199 self.compile_expr(&args[1], false);
2200 let f = self.alloc_local("__if_f");
2201 self.emit(Op::StoreLocal(f));
2202
2203 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2204 let list = self.alloc_local("__if_list");
2205 self.emit(Op::StoreLocal(list));
2206
2207 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2208 let i = self.alloc_local("__if_i");
2209 self.emit(Op::StoreLocal(i));
2210
2211 self.emit(Op::MakeList(0));
2212 let out = self.alloc_local("__if_out");
2213 self.emit(Op::StoreLocal(out));
2214
2215 let loop_top = self.code.len();
2216 self.emit(Op::LoadLocal(i));
2217 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2218 self.emit(Op::IntLt);
2219 let j_exit = self.code.len();
2220 self.emit(Op::JumpIfNot(0));
2221
2222 self.emit(Op::LoadLocal(list));
2224 self.emit(Op::LoadLocal(i));
2225 self.emit(Op::GetListElemDyn);
2226 let x = self.alloc_local("__if_x");
2227 self.emit(Op::StoreLocal(x));
2228
2229 let nid = self.pool.node_id("n_iter_filter");
2230 self.emit(Op::LoadLocal(f));
2231 self.emit(Op::LoadLocal(x));
2232 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2233 let j_skip = self.code.len();
2234 self.emit(Op::JumpIfNot(0));
2235
2236 self.emit(Op::LoadLocal(out));
2237 self.emit(Op::LoadLocal(x));
2238 self.emit(Op::ListAppend);
2239 self.emit(Op::StoreLocal(out));
2240
2241 let skip_t = self.code.len() as i32;
2242 if let Op::JumpIfNot(off) = &mut self.code[j_skip] { *off = skip_t - (j_skip as i32 + 1); }
2243
2244 self.emit(Op::LoadLocal(i));
2245 let one = self.pool.int(1);
2246 self.emit(Op::PushConst(one));
2247 self.emit(Op::IntAdd);
2248 self.emit(Op::StoreLocal(i));
2249
2250 let jback = self.code.len();
2251 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2252
2253 let exit_t = self.code.len() as i32;
2254 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
2255
2256 let zero = self.pool.int(0);
2257 self.emit(Op::LoadLocal(out));
2258 self.emit(Op::PushConst(zero));
2259 let eager_v = self.pool.variant("__IterEager");
2260 self.emit(Op::MakeVariant { name_idx: eager_v, arity: 2 });
2261 }
2262
2263 fn emit_iter_fold(&mut self, args: &[a::CExpr]) {
2265 self.compile_expr(&args[0], false);
2266 let it = self.alloc_local("__ifo_it");
2267 self.emit(Op::StoreLocal(it));
2268
2269 self.compile_expr(&args[1], false);
2270 let acc = self.alloc_local("__ifo_acc");
2271 self.emit(Op::StoreLocal(acc));
2272
2273 self.compile_expr(&args[2], false);
2274 let f = self.alloc_local("__ifo_f");
2275 self.emit(Op::StoreLocal(f));
2276
2277 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(0));
2278 let list = self.alloc_local("__ifo_list");
2279 self.emit(Op::StoreLocal(list));
2280
2281 self.emit(Op::LoadLocal(it)); self.emit(Op::GetVariantArg(1));
2282 let i = self.alloc_local("__ifo_i");
2283 self.emit(Op::StoreLocal(i));
2284
2285 let loop_top = self.code.len();
2286 self.emit(Op::LoadLocal(i));
2287 self.emit(Op::LoadLocal(list)); self.emit(Op::GetListLen);
2288 self.emit(Op::IntLt);
2289 let j_exit = self.code.len();
2290 self.emit(Op::JumpIfNot(0));
2291
2292 let nid = self.pool.node_id("n_iter_fold");
2293 self.emit(Op::LoadLocal(f));
2294 self.emit(Op::LoadLocal(acc));
2295 self.emit(Op::LoadLocal(list));
2296 self.emit(Op::LoadLocal(i));
2297 self.emit(Op::GetListElemDyn);
2298 self.emit(Op::CallClosure { arity: 2, node_id_idx: nid });
2299 self.emit(Op::StoreLocal(acc));
2300
2301 self.emit(Op::LoadLocal(i));
2302 let one = self.pool.int(1);
2303 self.emit(Op::PushConst(one));
2304 self.emit(Op::IntAdd);
2305 self.emit(Op::StoreLocal(i));
2306
2307 let jback = self.code.len();
2308 self.emit(Op::Jump((loop_top as i32) - (jback as i32 + 1)));
2309
2310 let exit_t = self.code.len() as i32;
2311 if let Op::JumpIfNot(off) = &mut self.code[j_exit] { *off = exit_t - (j_exit as i32 + 1); }
2312 self.emit(Op::LoadLocal(acc));
2313 }
2314
2315 fn emit_map_fold(&mut self, args: &[a::CExpr], node_id_idx: u32) {
2321 self.compile_expr(&args[0], false);
2323 let map_kind = self.pool.str("map");
2324 let entries_op = self.pool.str("entries");
2325 self.emit(Op::EffectCall {
2326 kind_idx: map_kind,
2327 op_idx: entries_op,
2328 arity: 1,
2329 node_id_idx,
2330 });
2331 let xs = self.alloc_local("__mf_xs");
2332 self.emit(Op::StoreLocal(xs));
2333
2334 self.compile_expr(&args[1], false);
2336 let acc = self.alloc_local("__mf_acc");
2337 self.emit(Op::StoreLocal(acc));
2338
2339 self.compile_expr(&args[2], false);
2341 let f = self.alloc_local("__mf_f");
2342 self.emit(Op::StoreLocal(f));
2343
2344 let zero = self.pool.int(0);
2346 self.emit(Op::PushConst(zero));
2347 let i = self.alloc_local("__mf_i");
2348 self.emit(Op::StoreLocal(i));
2349
2350 let loop_top = self.code.len();
2352 self.emit(Op::LoadLocal(i));
2353 self.emit(Op::LoadLocal(xs));
2354 self.emit(Op::GetListLen);
2355 self.emit(Op::IntLt);
2356 let j_exit = self.code.len();
2357 self.emit(Op::JumpIfNot(0));
2358
2359 self.emit(Op::LoadLocal(xs));
2361 self.emit(Op::LoadLocal(i));
2362 self.emit(Op::GetListElemDyn);
2363 let pair = self.alloc_local("__mf_pair");
2364 self.emit(Op::StoreLocal(pair));
2365
2366 let nid = self.pool.node_id("n_map_fold");
2368 self.emit(Op::LoadLocal(f));
2369 self.emit(Op::LoadLocal(acc));
2370 self.emit(Op::LoadLocal(pair));
2371 self.emit(Op::GetElem(0));
2372 self.emit(Op::LoadLocal(pair));
2373 self.emit(Op::GetElem(1));
2374 self.emit(Op::CallClosure { arity: 3, node_id_idx: nid });
2375 self.emit(Op::StoreLocal(acc));
2376
2377 self.emit(Op::LoadLocal(i));
2379 let one = self.pool.int(1);
2380 self.emit(Op::PushConst(one));
2381 self.emit(Op::IntAdd);
2382 self.emit(Op::StoreLocal(i));
2383
2384 let jump_back = self.code.len();
2385 let back = (loop_top as i32) - (jump_back as i32 + 1);
2386 self.emit(Op::Jump(back));
2387
2388 let exit_target = self.code.len() as i32;
2389 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2390 *off = exit_target - (j_exit as i32 + 1);
2391 }
2392 self.emit(Op::LoadLocal(acc));
2393 }
2394
2395 fn emit_variant_map(
2401 &mut self,
2402 args: &[a::CExpr],
2403 wrap_with: &str,
2404 wrap_result: bool,
2405 ) {
2406 let wrap_idx = self.pool.variant(wrap_with);
2408
2409 self.compile_expr(&args[0], false);
2411 let val_slot = self.alloc_local("__hov");
2412 self.emit(Op::StoreLocal(val_slot));
2413
2414 self.compile_expr(&args[1], false);
2415 let f_slot = self.alloc_local("__hof");
2416 self.emit(Op::StoreLocal(f_slot));
2417
2418 self.emit(Op::LoadLocal(val_slot));
2425 self.emit(Op::Dup);
2426 self.emit(Op::TestVariant(wrap_idx));
2427 let j_skip = self.code.len();
2428 self.emit(Op::JumpIfNot(0));
2429
2430 self.emit(Op::GetVariantArg(0));
2432 let arg_slot = self.alloc_local("__hov_arg");
2433 self.emit(Op::StoreLocal(arg_slot));
2434 self.emit(Op::LoadLocal(f_slot));
2435 self.emit(Op::LoadLocal(arg_slot));
2436 let nid = self.pool.node_id("n_hov");
2437 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2438 if wrap_result {
2439 self.emit(Op::MakeVariant { name_idx: wrap_idx, arity: 1 });
2440 }
2441 let j_end = self.code.len();
2442 self.emit(Op::Jump(0));
2443
2444 let skip_target = self.code.len() as i32;
2446 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
2447 *off = skip_target - (j_skip as i32 + 1);
2448 }
2449
2450 let end_target = self.code.len() as i32;
2451 if let Op::Jump(off) = &mut self.code[j_end] {
2452 *off = end_target - (j_end as i32 + 1);
2453 }
2454 }
2455
2456 fn emit_variant_or_else(
2465 &mut self,
2466 args: &[a::CExpr],
2467 match_on: &str,
2468 closure_arity: u16,
2469 ) {
2470 let match_idx = self.pool.variant(match_on);
2471
2472 self.compile_expr(&args[0], false);
2473 let val_slot = self.alloc_local("__hoe");
2474 self.emit(Op::StoreLocal(val_slot));
2475
2476 self.compile_expr(&args[1], false);
2477 let f_slot = self.alloc_local("__hoe_f");
2478 self.emit(Op::StoreLocal(f_slot));
2479
2480 self.emit(Op::LoadLocal(val_slot));
2488 self.emit(Op::Dup);
2489 self.emit(Op::TestVariant(match_idx));
2490 let j_skip = self.code.len();
2491 self.emit(Op::JumpIfNot(0));
2492
2493 self.emit(Op::Pop);
2496 self.emit(Op::LoadLocal(f_slot));
2497 if closure_arity == 1 {
2498 self.emit(Op::LoadLocal(val_slot));
2499 self.emit(Op::GetVariantArg(0));
2500 }
2501 let nid = self.pool.node_id("n_hoe");
2502 self.emit(Op::CallClosure { arity: closure_arity, node_id_idx: nid });
2503
2504 let j_end = self.code.len();
2505 self.emit(Op::Jump(0));
2506
2507 let skip_target = self.code.len() as i32;
2509 if let Op::JumpIfNot(off) = &mut self.code[j_skip] {
2510 *off = skip_target - (j_skip as i32 + 1);
2511 }
2512
2513 let end_target = self.code.len() as i32;
2514 if let Op::Jump(off) = &mut self.code[j_end] {
2515 *off = end_target - (j_end as i32 + 1);
2516 }
2517 }
2518
2519 fn emit_option_unwrap_or_else(&mut self, args: &[a::CExpr]) {
2523 let some_idx = self.pool.variant("Some");
2524
2525 self.compile_expr(&args[0], false);
2527 let val_slot = self.alloc_local("__uoe_val");
2528 self.emit(Op::StoreLocal(val_slot));
2529
2530 self.compile_expr(&args[1], false);
2531 let f_slot = self.alloc_local("__uoe_f");
2532 self.emit(Op::StoreLocal(f_slot));
2533
2534 self.emit(Op::LoadLocal(val_slot));
2540 self.emit(Op::Dup);
2541 self.emit(Op::TestVariant(some_idx));
2542 let j_none = self.code.len();
2543 self.emit(Op::JumpIfNot(0));
2544
2545 self.emit(Op::GetVariantArg(0));
2547 let j_end = self.code.len();
2548 self.emit(Op::Jump(0));
2549
2550 let none_target = self.code.len() as i32;
2552 if let Op::JumpIfNot(off) = &mut self.code[j_none] {
2553 *off = none_target - (j_none as i32 + 1);
2554 }
2555 self.emit(Op::Pop);
2556 self.emit(Op::LoadLocal(f_slot));
2557 let nid = self.pool.node_id("n_uoe");
2558 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2559
2560 let end_target = self.code.len() as i32;
2562 if let Op::Jump(off) = &mut self.code[j_end] {
2563 *off = end_target - (j_end as i32 + 1);
2564 }
2565 }
2566
2567 fn emit_result_unwrap_or_else(&mut self, args: &[a::CExpr]) {
2573 let ok_idx = self.pool.variant("Ok");
2574
2575 self.compile_expr(&args[0], false);
2576 let val_slot = self.alloc_local("__ruoe_val");
2577 self.emit(Op::StoreLocal(val_slot));
2578
2579 self.compile_expr(&args[1], false);
2580 let f_slot = self.alloc_local("__ruoe_f");
2581 self.emit(Op::StoreLocal(f_slot));
2582
2583 self.emit(Op::LoadLocal(val_slot));
2589 self.emit(Op::Dup);
2590 self.emit(Op::TestVariant(ok_idx));
2591 let j_err = self.code.len();
2592 self.emit(Op::JumpIfNot(0));
2593
2594 self.emit(Op::GetVariantArg(0));
2596 let j_end = self.code.len();
2597 self.emit(Op::Jump(0));
2598
2599 let err_target = self.code.len() as i32;
2601 if let Op::JumpIfNot(off) = &mut self.code[j_err] {
2602 *off = err_target - (j_err as i32 + 1);
2603 }
2604 self.emit(Op::Pop);
2605 self.emit(Op::LoadLocal(f_slot));
2606 self.emit(Op::LoadLocal(val_slot));
2607 self.emit(Op::GetVariantArg(0));
2608 let nid = self.pool.node_id("n_ruoe");
2609 self.emit(Op::CallClosure { arity: 1, node_id_idx: nid });
2610
2611 let end_target = self.code.len() as i32;
2613 if let Op::Jump(off) = &mut self.code[j_end] {
2614 *off = end_target - (j_end as i32 + 1);
2615 }
2616 }
2617
2618 fn install_trampoline(&mut self, name: &str, arity: u16, locals_count: u16, code: Vec<Op>) -> u32 {
2635 let fn_id = self.next_fn_id.len() as u32;
2636 let body_hash = crate::program::compute_body_hash(
2637 arity, locals_count, &code, &self.pool.record_shapes);
2638 self.next_fn_id.push(Function {
2639 name: name.into(),
2640 arity,
2641 locals_count,
2642 code,
2643 effects: Vec::new(),
2644 body_hash,
2645 refinements: Vec::new(),
2648 field_ic_sites: 0,
2652 });
2653 fn_id
2654 }
2655
2656 fn emit_flow_sequential(&mut self, args: &[a::CExpr]) {
2658 self.compile_expr(&args[0], false);
2660 self.compile_expr(&args[1], false);
2661 let nid = self.pool.node_id("n_flow_sequential");
2662 let code = vec![
2663 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,
2673 ];
2674 let fn_id = self.install_trampoline("__flow_sequential", 3, 4, code);
2675 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2676 }
2677
2678 fn emit_flow_parallel(&mut self, args: &[a::CExpr]) {
2686 self.compile_expr(&args[0], false);
2688 self.compile_expr(&args[1], false);
2689 let nid = self.pool.node_id("n_flow_parallel");
2690 let code = vec![
2691 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,
2698 ];
2699 let fn_id = self.install_trampoline("__flow_parallel", 2, 2, code);
2700 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2701 }
2702
2703 fn emit_flow_parallel_list(&mut self, args: &[a::CExpr]) {
2710 self.compile_expr(&args[0], false);
2712 let xs = self.alloc_local("__fpl_xs");
2713 self.emit(Op::StoreLocal(xs));
2714
2715 self.emit(Op::MakeList(0));
2717 let out = self.alloc_local("__fpl_out");
2718 self.emit(Op::StoreLocal(out));
2719
2720 let zero = self.pool.int(0);
2722 self.emit(Op::PushConst(zero));
2723 let i = self.alloc_local("__fpl_i");
2724 self.emit(Op::StoreLocal(i));
2725
2726 let loop_top = self.code.len();
2728 self.emit(Op::LoadLocal(i));
2729 self.emit(Op::LoadLocal(xs));
2730 self.emit(Op::GetListLen);
2731 self.emit(Op::IntLt);
2732 let j_exit = self.code.len();
2733 self.emit(Op::JumpIfNot(0));
2734
2735 let nid = self.pool.node_id("n_flow_parallel_list");
2737 self.emit(Op::LoadLocal(out));
2738 self.emit(Op::LoadLocal(xs));
2739 self.emit(Op::LoadLocal(i));
2740 self.emit(Op::GetListElemDyn);
2741 self.emit(Op::CallClosure { arity: 0, node_id_idx: nid });
2742 self.emit(Op::ListAppend);
2743 self.emit(Op::StoreLocal(out));
2744
2745 self.emit(Op::LoadLocal(i));
2747 let one = self.pool.int(1);
2748 self.emit(Op::PushConst(one));
2749 self.emit(Op::IntAdd);
2750 self.emit(Op::StoreLocal(i));
2751
2752 let jump_back = self.code.len();
2754 let back = (loop_top as i32) - (jump_back as i32 + 1);
2755 self.emit(Op::Jump(back));
2756
2757 let exit_target = self.code.len() as i32;
2759 if let Op::JumpIfNot(off) = &mut self.code[j_exit] {
2760 *off = exit_target - (j_exit as i32 + 1);
2761 }
2762 self.emit(Op::LoadLocal(out));
2763 }
2764
2765 fn emit_flow_branch(&mut self, args: &[a::CExpr]) {
2767 self.compile_expr(&args[0], false);
2768 self.compile_expr(&args[1], false);
2769 self.compile_expr(&args[2], false);
2770 let nid = self.pool.node_id("n_flow_branch");
2771 let mut code = vec![
2772 Op::LoadLocal(0), Op::LoadLocal(3), Op::CallClosure { arity: 1, node_id_idx: nid }, ];
2777 let j_false = code.len();
2778 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(1));
2781 code.push(Op::LoadLocal(3));
2782 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2783 code.push(Op::Return);
2784 let false_target = code.len() as i32;
2786 if let Op::JumpIfNot(off) = &mut code[j_false] {
2787 *off = false_target - (j_false as i32 + 1);
2788 }
2789 code.push(Op::LoadLocal(2));
2790 code.push(Op::LoadLocal(3));
2791 code.push(Op::CallClosure { arity: 1, node_id_idx: nid });
2792 code.push(Op::Return);
2793
2794 let fn_id = self.install_trampoline("__flow_branch", 4, 4, code);
2795 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2796 }
2797
2798 fn emit_flow_retry(&mut self, args: &[a::CExpr]) {
2802 self.compile_expr(&args[0], false);
2803 self.compile_expr(&args[1], false);
2804 let call_nid = self.pool.node_id("n_flow_retry");
2805 let ok_idx = self.pool.variant("Ok");
2806 let zero_const = self.pool.int(0);
2807 let one_const = self.pool.int(1);
2808 let mut code = vec![
2810 Op::PushConst(zero_const),
2812 Op::StoreLocal(3),
2813 ];
2814 let loop_top = code.len() as i32;
2816 code.push(Op::LoadLocal(3));
2817 code.push(Op::LoadLocal(1));
2818 code.push(Op::IntLt);
2819 let j_done = code.len();
2820 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(0));
2824 code.push(Op::LoadLocal(2));
2825 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2826 code.push(Op::StoreLocal(4));
2827
2828 code.push(Op::LoadLocal(4));
2830 code.push(Op::TestVariant(ok_idx));
2831 let j_was_err = code.len();
2832 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(4));
2834 code.push(Op::Return);
2835
2836 let was_err_target = code.len() as i32;
2838 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2839 *off = was_err_target - (j_was_err as i32 + 1);
2840 }
2841 code.push(Op::LoadLocal(3));
2842 code.push(Op::PushConst(one_const));
2843 code.push(Op::IntAdd);
2844 code.push(Op::StoreLocal(3));
2845 let pc_after_jump = code.len() as i32 + 1;
2846 code.push(Op::Jump(loop_top - pc_after_jump));
2847
2848 let done_target = code.len() as i32;
2850 if let Op::JumpIfNot(off) = &mut code[j_done] {
2851 *off = done_target - (j_done as i32 + 1);
2852 }
2853 code.push(Op::LoadLocal(4));
2854 code.push(Op::Return);
2855
2856 let fn_id = self.install_trampoline("__flow_retry", 3, 5, code);
2857 self.emit(Op::MakeClosure { fn_id, capture_count: 2 });
2858 }
2859
2860 fn emit_flow_retry_with_backoff(&mut self, args: &[a::CExpr]) {
2868 self.compile_expr(&args[0], false);
2871 self.compile_expr(&args[1], false);
2872 self.compile_expr(&args[2], false);
2873 let call_nid = self.pool.node_id("n_flow_retry_backoff");
2874 let sleep_nid = self.pool.node_id("n_flow_retry_backoff_sleep");
2875 let kind_idx = self.pool.str("time");
2876 let op_idx = self.pool.str("sleep_ms");
2877 let ok_idx = self.pool.variant("Ok");
2878 let zero_const = self.pool.int(0);
2879 let one_const = self.pool.int(1);
2880 let two_const = self.pool.int(2);
2881 let mut code = vec![
2886 Op::LoadLocal(2),
2888 Op::StoreLocal(6),
2889 Op::PushConst(zero_const),
2891 Op::StoreLocal(4),
2892 ];
2893
2894 let loop_top = code.len() as i32;
2895 code.push(Op::LoadLocal(4));
2897 code.push(Op::LoadLocal(1));
2898 code.push(Op::IntLt);
2899 let j_done = code.len();
2900 code.push(Op::JumpIfNot(0)); code.push(Op::PushConst(zero_const));
2904 code.push(Op::LoadLocal(4));
2905 code.push(Op::IntLt); let j_no_sleep = code.len();
2907 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(6)); code.push(Op::EffectCall {
2911 kind_idx, op_idx, arity: 1, node_id_idx: sleep_nid,
2912 });
2913 code.push(Op::Pop); code.push(Op::LoadLocal(6));
2916 code.push(Op::PushConst(two_const));
2917 code.push(Op::NumMul);
2918 code.push(Op::StoreLocal(6));
2919 let after_sleep = code.len() as i32;
2921 if let Op::JumpIfNot(off) = &mut code[j_no_sleep] {
2922 *off = after_sleep - (j_no_sleep as i32 + 1);
2923 }
2924
2925 code.push(Op::LoadLocal(0));
2927 code.push(Op::LoadLocal(3));
2928 code.push(Op::CallClosure { arity: 1, node_id_idx: call_nid });
2929 code.push(Op::StoreLocal(5));
2930
2931 code.push(Op::LoadLocal(5));
2933 code.push(Op::TestVariant(ok_idx));
2934 let j_was_err = code.len();
2935 code.push(Op::JumpIfNot(0)); code.push(Op::LoadLocal(5));
2937 code.push(Op::Return);
2938
2939 let was_err_target = code.len() as i32;
2941 if let Op::JumpIfNot(off) = &mut code[j_was_err] {
2942 *off = was_err_target - (j_was_err as i32 + 1);
2943 }
2944 code.push(Op::LoadLocal(4));
2945 code.push(Op::PushConst(one_const));
2946 code.push(Op::IntAdd);
2947 code.push(Op::StoreLocal(4));
2948 let pc_after_jump = code.len() as i32 + 1;
2949 code.push(Op::Jump(loop_top - pc_after_jump));
2950
2951 let done_target = code.len() as i32;
2953 if let Op::JumpIfNot(off) = &mut code[j_done] {
2954 *off = done_target - (j_done as i32 + 1);
2955 }
2956 code.push(Op::LoadLocal(5));
2957 code.push(Op::Return);
2958
2959 let fn_id = self.install_trampoline("__flow_retry_backoff", 4, 7, code);
2960 self.emit(Op::MakeClosure { fn_id, capture_count: 3 });
2961 }
2962}
2963
2964fn free_vars(e: &a::CExpr, bound: &mut std::collections::HashSet<String>, out: &mut Vec<String>) {
2969 match e {
2970 a::CExpr::Literal { .. } => {}
2971 a::CExpr::Var { name } => {
2972 if !bound.contains(name) && !out.contains(name) {
2973 out.push(name.clone());
2974 }
2975 }
2976 a::CExpr::Call { callee, args } => {
2977 free_vars(callee, bound, out);
2978 for a in args { free_vars(a, bound, out); }
2979 }
2980 a::CExpr::Let { name, value, body, .. } => {
2981 free_vars(value, bound, out);
2982 let was_bound = bound.contains(name);
2983 bound.insert(name.clone());
2984 free_vars(body, bound, out);
2985 if !was_bound { bound.remove(name); }
2986 }
2987 a::CExpr::Match { scrutinee, arms } => {
2988 free_vars(scrutinee, bound, out);
2989 for arm in arms {
2990 let mut local_bound = bound.clone();
2991 pattern_binders(&arm.pattern, &mut local_bound);
2992 free_vars(&arm.body, &mut local_bound, out);
2993 }
2994 }
2995 a::CExpr::Block { statements, result } => {
2996 let mut local_bound = bound.clone();
2997 for s in statements { free_vars(s, &mut local_bound, out); }
2998 free_vars(result, &mut local_bound, out);
2999 }
3000 a::CExpr::Constructor { args, .. } => {
3001 for a in args { free_vars(a, bound, out); }
3002 }
3003 a::CExpr::RecordLit { fields } => {
3004 for f in fields { free_vars(&f.value, bound, out); }
3005 }
3006 a::CExpr::TupleLit { items } | a::CExpr::ListLit { items } => {
3007 for it in items { free_vars(it, bound, out); }
3008 }
3009 a::CExpr::FieldAccess { value, .. } => free_vars(value, bound, out),
3010 a::CExpr::Lambda { params, body, .. } => {
3011 let mut inner = bound.clone();
3012 for p in params { inner.insert(p.name.clone()); }
3013 free_vars(body, &mut inner, out);
3014 }
3015 a::CExpr::BinOp { lhs, rhs, .. } => {
3016 free_vars(lhs, bound, out);
3017 free_vars(rhs, bound, out);
3018 }
3019 a::CExpr::UnaryOp { expr, .. } => free_vars(expr, bound, out),
3020 a::CExpr::Return { value } => free_vars(value, bound, out),
3021 }
3022}
3023
3024fn pattern_binders(p: &a::Pattern, bound: &mut std::collections::HashSet<String>) {
3025 match p {
3026 a::Pattern::PWild | a::Pattern::PLiteral { .. } => {}
3027 a::Pattern::PVar { name } => { bound.insert(name.clone()); }
3028 a::Pattern::PConstructor { args, .. } => {
3029 for a in args { pattern_binders(a, bound); }
3030 }
3031 a::Pattern::PRecord { fields } => {
3032 for f in fields { pattern_binders(&f.pattern, bound); }
3033 }
3034 a::Pattern::PTuple { items } => {
3035 for it in items { pattern_binders(it, bound); }
3036 }
3037 }
3038}