1use std::collections::HashMap;
13
14use crate::ast::stmt::{BinaryOpKind, Expr, Literal, Stmt};
15use crate::intern::{Interner, Symbol};
16
17use super::instruction::{
18 BoundaryType, CompiledFunction, CompiledProgram, ConstIdx, Constant, FuncIdx, Op, Reg, StructTypeDef,
19};
20use super::{MAX_EXPR_DEPTH, MAX_REGISTERS_PER_FRAME};
21
22pub fn magic_div_enabled() -> bool {
28 crate::optimize::active_config().is_on(crate::optimization::Opt::FastDiv)
29}
30
31pub fn narrow_vm_enabled() -> bool {
43 crate::optimize::active_config().is_on(crate::optimization::Opt::NarrowVm)
44}
45
46pub fn magic_u64_gen(c: u64) -> (u64, u8) {
54 logicaffeine_data::LogosDivU64::new(c).parts()
55}
56
57pub fn magic_eval(x: i64, magic: u64, more: u8, mul_back: i64) -> i64 {
65 const SHIFT_MASK: u8 = 0x3F;
66 const ADD_MARKER: u8 = 0x40;
67 const SHIFT_PATH: u8 = 0x80;
68 let n = x as u64;
69 let q = if more & SHIFT_PATH != 0 {
70 n >> (more & SHIFT_MASK)
71 } else {
72 let hi = (((magic as u128) * (n as u128)) >> 64) as u64;
73 if more & ADD_MARKER != 0 {
74 let t = (n.wrapping_sub(hi) >> 1).wrapping_add(hi);
75 t >> (more & SHIFT_MASK)
76 } else {
77 hi >> (more & SHIFT_MASK)
78 }
79 };
80 if mul_back == 0 {
81 q as i64
82 } else {
83 x.wrapping_sub((q as i64).wrapping_mul(mul_back))
84 }
85}
86
87#[derive(Clone, PartialEq, Eq, Hash)]
90enum ConstKey {
91 Int(i64),
92 FloatBits(u64),
93 Bool(bool),
94 Text(String),
95 Char(char),
96 Nothing,
97 Duration(i64),
98 Date(i32),
99 Moment(i64),
100 Span { months: i32, days: i32 },
101 Time(i64),
102}
103
104fn const_key(c: &Constant) -> ConstKey {
105 match c {
106 Constant::Int(n) => ConstKey::Int(*n),
107 Constant::Float(f) => ConstKey::FloatBits(f.to_bits()),
108 Constant::Bool(b) => ConstKey::Bool(*b),
109 Constant::Text(s) => ConstKey::Text(s.clone()),
110 Constant::Char(c) => ConstKey::Char(*c),
111 Constant::Nothing => ConstKey::Nothing,
112 Constant::Duration(n) => ConstKey::Duration(*n),
113 Constant::Date(d) => ConstKey::Date(*d),
114 Constant::Moment(n) => ConstKey::Moment(*n),
115 Constant::Span { months, days } => ConstKey::Span { months: *months, days: *days },
116 Constant::Time(n) => ConstKey::Time(*n),
117 }
118}
119
120fn slot_kind_of_type(
122 ty: &crate::ast::stmt::TypeExpr,
123 interner: &Interner,
124) -> Option<super::native_tier::SlotKind> {
125 use super::native_tier::SlotKind;
126 use crate::ast::stmt::TypeExpr;
127 match ty {
128 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => match interner.resolve(*sym) {
131 "Int" | "Nat" => Some(SlotKind::Int),
132 "Float" => Some(SlotKind::Float),
133 "Bool" => Some(SlotKind::Bool),
134 _ => None,
135 },
136 TypeExpr::Refinement { base, .. } => slot_kind_of_type(base, interner),
137 _ => None,
138 }
139}
140
141fn param_kind_of_type(
144 ty: &crate::ast::stmt::TypeExpr,
145 interner: &Interner,
146) -> Option<super::native_tier::ParamKind> {
147 use super::native_tier::{ParamKind, PinElem, SlotKind};
148 use crate::ast::stmt::TypeExpr;
149 if let TypeExpr::Mutable { inner } = ty {
150 return param_kind_of_type(inner, interner);
151 }
152 if let Some(k) = slot_kind_of_type(ty, interner) {
153 return Some(ParamKind::Scalar(k));
154 }
155 if let TypeExpr::Generic { base, params } = ty {
156 if interner.resolve(*base) == "Seq" && params.len() == 1 {
157 return match slot_kind_of_type(¶ms[0], interner) {
158 Some(SlotKind::Int) => Some(ParamKind::List(PinElem::Int)),
159 Some(SlotKind::Float) => Some(ParamKind::List(PinElem::Float)),
160 Some(SlotKind::Bool) => Some(ParamKind::List(PinElem::Bool)),
161 None => None,
162 };
163 }
164 }
165 if let TypeExpr::Refinement { base, .. } = ty {
166 return param_kind_of_type(base, interner);
167 }
168 None
169}
170
171fn boundary_of_name(
175 s: Symbol,
176 user_types: &std::collections::HashMap<Symbol, bool>,
177 interner: &Interner,
178) -> Option<BoundaryType> {
179 Some(match interner.resolve(s) {
180 "Int" | "Nat" => BoundaryType::Int,
181 "Float" => BoundaryType::Float,
182 "Bool" => BoundaryType::Bool,
183 "Text" => BoundaryType::Text,
184 "Date" => BoundaryType::Date,
185 "Moment" => BoundaryType::Moment,
186 "Word32" => BoundaryType::Word32,
187 "Word64" => BoundaryType::Word64,
188 name @ ("Duration" | "Time" | "Span" | "Rational" | "Complex" | "Decimal" | "Modular"
189 | "Money" | "Quantity" | "Uuid") => BoundaryType::Builtin(name.to_string()),
190 other => match user_types.get(&s) {
191 Some(true) => BoundaryType::Struct(other.to_string()),
192 Some(false) => BoundaryType::Enum(other.to_string()),
193 None => return None,
194 },
195 })
196}
197
198fn boundary_of_type_expr(
200 ty: &crate::ast::stmt::TypeExpr,
201 user_types: &std::collections::HashMap<Symbol, bool>,
202 interner: &Interner,
203) -> Option<BoundaryType> {
204 use crate::ast::stmt::TypeExpr;
205 match ty {
206 TypeExpr::Primitive(s) | TypeExpr::Named(s) => boundary_of_name(*s, user_types, interner),
207 TypeExpr::Generic { base, params }
208 if matches!(interner.resolve(*base), "Seq" | "List") && params.len() == 1 =>
209 {
210 boundary_of_type_expr(¶ms[0], user_types, interner).map(|e| BoundaryType::Seq(Box::new(e)))
211 }
212 TypeExpr::Generic { base, params }
215 if matches!(interner.resolve(*base), "Map" | "HashMap") && params.len() == 2 =>
216 {
217 let k = boundary_of_type_expr(¶ms[0], user_types, interner)?;
218 let v = boundary_of_type_expr(¶ms[1], user_types, interner)?;
219 Some(BoundaryType::Map(Box::new(k), Box::new(v)))
220 }
221 TypeExpr::Generic { base, params } if matches!(interner.resolve(*base), "Pair" | "Triple") => {
226 let elems: Vec<BoundaryType> =
227 params.iter().filter_map(|p| boundary_of_type_expr(p, user_types, interner)).collect();
228 if elems.len() != params.len() {
229 None
230 } else if elems.windows(2).all(|w| w[0] == w[1]) {
231 Some(BoundaryType::Seq(Box::new(elems[0].clone())))
232 } else {
233 Some(BoundaryType::Tuple(elems))
234 }
235 }
236 TypeExpr::Refinement { base, .. } => boundary_of_type_expr(base, user_types, interner),
237 TypeExpr::Mutable { inner } => boundary_of_type_expr(inner, user_types, interner),
238 _ => None,
239 }
240}
241
242fn boundary_of_field_type(
245 ft: &crate::analysis::registry::FieldType,
246 user_types: &std::collections::HashMap<Symbol, bool>,
247 interner: &Interner,
248) -> Option<BoundaryType> {
249 use crate::analysis::registry::FieldType;
250 match ft {
251 FieldType::Primitive(s) | FieldType::Named(s) => boundary_of_name(*s, user_types, interner),
252 FieldType::Generic { base, params }
253 if matches!(interner.resolve(*base), "Seq" | "List") && params.len() == 1 =>
254 {
255 boundary_of_field_type(¶ms[0], user_types, interner).map(|e| BoundaryType::Seq(Box::new(e)))
256 }
257 FieldType::Generic { base, params }
261 if matches!(interner.resolve(*base), "Map" | "HashMap" | "SharedMap") && params.len() == 2 =>
262 {
263 let k = boundary_of_field_type(¶ms[0], user_types, interner)?;
264 let v = boundary_of_field_type(¶ms[1], user_types, interner)?;
265 Some(BoundaryType::Map(Box::new(k), Box::new(v)))
266 }
267 FieldType::Generic { base, params } if matches!(interner.resolve(*base), "Pair" | "Triple") => {
271 let elems: Vec<BoundaryType> =
272 params.iter().filter_map(|p| boundary_of_field_type(p, user_types, interner)).collect();
273 if elems.len() != params.len() {
274 None
275 } else if elems.windows(2).all(|w| w[0] == w[1]) {
276 Some(BoundaryType::Seq(Box::new(elems[0].clone())))
277 } else {
278 Some(BoundaryType::Tuple(elems))
279 }
280 }
281 FieldType::TypeParam(_) | FieldType::Generic { .. } => None,
282 }
283}
284
285fn boundary_of_value_expr(
291 e: &crate::ast::stmt::Expr,
292 user_types: &std::collections::HashMap<Symbol, bool>,
293 interner: &Interner,
294) -> Option<BoundaryType> {
295 use crate::ast::stmt::{Expr, Literal};
296 match e {
297 Expr::New { type_name, type_args, .. } => {
298 if matches!(interner.resolve(*type_name), "Map" | "HashMap") && type_args.len() == 2 {
299 let k = boundary_of_type_expr(&type_args[0], user_types, interner)?;
300 let v = boundary_of_type_expr(&type_args[1], user_types, interner)?;
301 Some(BoundaryType::Map(Box::new(k), Box::new(v)))
302 } else {
303 boundary_of_name(*type_name, user_types, interner)
304 }
305 }
306 Expr::NewVariant { enum_name, .. } => boundary_of_name(*enum_name, user_types, interner),
307 Expr::Literal(Literal::Number(_)) => Some(BoundaryType::Int),
309 Expr::Literal(Literal::Float(_)) => Some(BoundaryType::Float),
310 Expr::Literal(Literal::Boolean(_)) => Some(BoundaryType::Bool),
311 Expr::Literal(Literal::Text(_)) => Some(BoundaryType::Text),
312 Expr::Tuple(elems) if !elems.is_empty() => {
315 let bts: Vec<BoundaryType> =
316 elems.iter().filter_map(|el| boundary_of_value_expr(el, user_types, interner)).collect();
317 if bts.len() != elems.len() {
318 None
319 } else if bts.windows(2).all(|w| w[0] == w[1]) {
320 Some(BoundaryType::Seq(Box::new(bts[0].clone())))
321 } else {
322 Some(BoundaryType::Tuple(bts))
323 }
324 }
325 _ => None,
326 }
327}
328
329pub struct Compiler<'i> {
330 interner: &'i Interner,
331 code: Vec<Op>,
332 constants: Vec<Constant>,
333 const_map: HashMap<ConstKey, ConstIdx>,
334 functions: Vec<CompiledFunction>,
335 fn_index: HashMap<Symbol, FuncIdx>,
336 struct_defs: HashMap<Symbol, Vec<(Symbol, Symbol, bool)>>,
340 user_types: HashMap<Symbol, bool>,
344 scopes: Vec<HashMap<Symbol, Reg>>,
349 next_reg: Reg,
350 debug_names: bool,
353 dbg_names: Vec<(Reg, Symbol)>,
356 named: Vec<bool>,
363 loop_stack: Vec<usize>,
368 loop_locals: std::collections::HashMap<usize, Vec<bool>>,
375 max_reg: Reg,
379 expr_depth: usize,
380 flow_stack: Vec<FlowCtx>,
384 in_function: bool,
387 tail_fn: Option<(Symbol, usize, u16)>,
395 promoted: HashMap<Symbol, u16>,
398 closure_ctx: Option<HashMap<Symbol, (Reg, Reg)>>,
402 oracle: Option<crate::optimize::OracleFacts>,
409 hoist_enabled: Vec<std::collections::HashSet<Symbol>>,
414 narrowable_seqs: std::collections::HashSet<Symbol>,
420 mut_borrow_params: HashMap<Symbol, std::collections::HashSet<usize>>,
427 mut_borrow_alias_syms: HashMap<Symbol, std::collections::HashSet<Symbol>>,
431 current_exempt_syms: std::collections::HashSet<Symbol>,
435 current_exempt_regs: Vec<Reg>,
438}
439
440enum NameRef {
442 Local(Reg),
443 CaptureOrGlobal { value: Reg, flag: Reg, global: u16 },
446 Global(u16),
447 Unbound,
448}
449
450enum FlowCtx {
451 Loop {
452 breaks: Vec<usize>,
453 is_repeat: bool,
456 },
457 Zone {
458 exits: Vec<usize>,
459 },
460}
461
462impl<'i> Compiler<'i> {
463 pub fn compile(stmts: &[Stmt], interner: &'i Interner) -> Result<CompiledProgram, String> {
465 Self::compile_with_types(stmts, interner, None)
466 }
467
468 pub fn compile_with_types(
471 stmts: &[Stmt],
472 interner: &'i Interner,
473 types: Option<&crate::analysis::TypeRegistry>,
474 ) -> Result<CompiledProgram, String> {
475 Self::compile_with_oracle(stmts, interner, types, None)
476 }
477
478 pub fn compile_with_oracle(
484 stmts: &[Stmt],
485 interner: &'i Interner,
486 types: Option<&crate::analysis::TypeRegistry>,
487 oracle: Option<crate::optimize::OracleFacts>,
488 ) -> Result<CompiledProgram, String> {
489 Self::compile_inner(stmts, interner, types, oracle, false)
490 }
491
492 pub fn compile_for_debug(
496 stmts: &[Stmt],
497 interner: &'i Interner,
498 types: Option<&crate::analysis::TypeRegistry>,
499 ) -> Result<CompiledProgram, String> {
500 Self::compile_inner(stmts, interner, types, None, true)
501 }
502
503 fn compile_inner(
504 stmts: &[Stmt],
505 interner: &'i Interner,
506 types: Option<&crate::analysis::TypeRegistry>,
507 oracle: Option<crate::optimize::OracleFacts>,
508 debug_names: bool,
509 ) -> Result<CompiledProgram, String> {
510 let mut c = Compiler {
511 interner,
512 debug_names,
513 dbg_names: Vec::new(),
514 code: Vec::new(),
515 constants: Vec::new(),
516 const_map: HashMap::new(),
517 functions: Vec::new(),
518 fn_index: HashMap::new(),
519 struct_defs: HashMap::new(),
520 user_types: HashMap::new(),
521 scopes: vec![HashMap::new()],
522 next_reg: 0,
523 named: Vec::new(),
524 loop_stack: Vec::new(),
525 loop_locals: std::collections::HashMap::new(),
526 max_reg: 0,
527 expr_depth: 0,
528 flow_stack: Vec::new(),
529 tail_fn: None,
530 hoist_enabled: Vec::new(),
531 in_function: false,
532 promoted: HashMap::new(),
533 closure_ctx: None,
534 oracle,
535 narrowable_seqs: std::collections::HashSet::new(),
536 mut_borrow_params: HashMap::new(),
537 mut_borrow_alias_syms: HashMap::new(),
538 current_exempt_syms: std::collections::HashSet::new(),
539 current_exempt_regs: Vec::new(),
540 };
541
542 let mut user_types: std::collections::HashMap<Symbol, bool> = std::collections::HashMap::new();
546 for s in stmts {
547 if let Stmt::StructDef { name, .. } = s {
548 user_types.insert(*name, true);
549 }
550 }
551 let mut struct_types: Vec<StructTypeDef> = Vec::new();
555 let mut enum_types: Vec<crate::vm::instruction::EnumTypeDef> = Vec::new();
558
559 if let Some(registry) = types {
562 use crate::analysis::registry::{FieldType, TypeDef};
563 for (name_sym, type_def) in registry.iter_types() {
564 match type_def {
565 TypeDef::Struct { .. } => {
566 user_types.insert(*name_sym, true);
567 }
568 TypeDef::Enum { .. } => {
569 user_types.insert(*name_sym, false);
570 }
571 _ => {}
572 }
573 }
574 for (name_sym, type_def) in registry.iter_types() {
575 if let TypeDef::Struct { fields, .. } = type_def {
576 let field_defs: Vec<(Symbol, Symbol, bool)> = fields
577 .iter()
578 .map(|f| {
579 let type_sym = match &f.ty {
580 FieldType::Primitive(s)
581 | FieldType::Named(s)
582 | FieldType::TypeParam(s) => *s,
583 FieldType::Generic { base, .. } => *base,
584 };
585 (f.name, type_sym, f.is_public)
586 })
587 .collect();
588 c.struct_defs.insert(*name_sym, field_defs);
589 let resolved: Option<Vec<(String, BoundaryType)>> = fields
593 .iter()
594 .map(|f| {
595 boundary_of_field_type(&f.ty, &user_types, interner)
596 .map(|t| (interner.resolve(f.name).to_string(), t))
597 })
598 .collect();
599 if let Some(field_layout) = resolved {
600 struct_types.push(StructTypeDef {
601 name: interner.resolve(*name_sym).to_string(),
602 fields: field_layout,
603 });
604 }
605 }
606 }
607 for (name_sym, type_def) in registry.iter_types() {
611 if let TypeDef::Enum { variants, .. } = type_def {
612 let resolved: Option<Vec<crate::vm::instruction::EnumVariantDef>> = variants
613 .iter()
614 .map(|v| {
615 let field_types: Option<Vec<BoundaryType>> = v
616 .fields
617 .iter()
618 .map(|f| boundary_of_field_type(&f.ty, &user_types, interner))
619 .collect();
620 field_types.map(|ft| crate::vm::instruction::EnumVariantDef {
621 name: interner.resolve(v.name).to_string(),
622 field_types: ft,
623 })
624 })
625 .collect();
626 if let Some(variant_layouts) = resolved {
627 enum_types.push(crate::vm::instruction::EnumTypeDef {
628 name: interner.resolve(*name_sym).to_string(),
629 variants: variant_layouts,
630 });
631 }
632 }
633 }
634 }
635 c.user_types = user_types.clone();
638
639 if let Some(registry) = types {
646 if crate::semantics::collections::value_semantics_enabled() {
647 use crate::analysis::readonly::{detect_consume_alias, MutableBorrowParams};
648 use crate::codegen::detection::is_vec_type_expr;
649 use crate::codegen::program::body_contains_escape;
650 use crate::codegen::tce::is_tail_recursive;
651 use crate::optimization::Opt;
652 use crate::optimize::active_config;
653 use crate::tail_call::detect_accumulator_pattern;
654 let cg = crate::analysis::callgraph::CallGraph::build(stmts, interner);
655 let type_env = crate::analysis::TypeEnv::infer_program(stmts, interner, registry);
656 let mb = MutableBorrowParams::analyze(stmts, &cg, &type_env);
657 for s in stmts {
658 if let Stmt::FunctionDef {
659 name, params, body, is_native, is_exported, opt_flags, ..
660 } = s
661 {
662 if *is_native || *is_exported {
663 continue;
664 }
665 if !active_config().merged(opt_flags).is_on(Opt::Borrow) {
666 continue;
667 }
668 if is_tail_recursive(*name, body)
669 || detect_accumulator_pattern(*name, body).is_some()
670 || body_contains_escape(body)
671 {
672 continue;
673 }
674 let mut idxs = std::collections::HashSet::new();
675 let mut aliases = std::collections::HashSet::new();
676 for (i, (psym, pty)) in params.iter().enumerate() {
677 if mb.is_mutable_borrow(*name, *psym) && is_vec_type_expr(pty, interner) {
678 idxs.insert(i);
679 if let Some(alias) = detect_consume_alias(body, *psym) {
680 aliases.insert(alias);
681 }
682 }
683 }
684 if !idxs.is_empty() {
685 c.mut_borrow_params.insert(*name, idxs);
686 c.mut_borrow_alias_syms.insert(*name, aliases);
687 }
688 }
689 }
690 }
691 }
692
693 for s in stmts {
696 if let Stmt::StructDef { name, fields, .. } = s {
697 c.struct_defs.insert(*name, fields.clone());
698 }
699 if let Stmt::FunctionDef { name, params, return_type, .. } = s {
700 if c.fn_index.contains_key(name) {
701 return Err(format!("vm: function '{}' defined twice", interner.resolve(*name)));
702 }
703 let idx = c.functions.len() as FuncIdx;
704 c.fn_index.insert(*name, idx);
705 c.functions.push(CompiledFunction {
706 name: *name,
707 entry_pc: 0,
708 param_count: u16::try_from(params.len())
709 .map_err(|_| "vm: too many parameters".to_string())?,
710 register_count: 0,
711 captures: Vec::new(),
712 named_regs: Vec::new(),
713 param_kinds: params
714 .iter()
715 .map(|(_, ty)| param_kind_of_type(ty, interner))
716 .collect(),
717 ret_kind: return_type.and_then(|ty| slot_kind_of_type(ty, interner)),
718 param_types: params
719 .iter()
720 .map(|(_, ty)| boundary_of_type_expr(ty, &user_types, interner))
721 .collect(),
722 return_type: return_type.and_then(|ty| boundary_of_type_expr(ty, &user_types, interner)),
723 mutable_param_regs: {
724 let inferred = c.mut_borrow_params.get(name);
727 let mut mp: Vec<Reg> = Vec::new();
728 for (i, (_psym, ty)) in params.iter().enumerate() {
729 if matches!(ty, crate::ast::stmt::TypeExpr::Mutable { .. })
730 || inferred.is_some_and(|s| s.contains(&i))
731 {
732 mp.push(i as Reg);
733 }
734 }
735 mp
736 },
737 });
738 }
739 }
740
741 let mut nonlocal_idents: std::collections::HashSet<Symbol> = std::collections::HashSet::new();
744 for s in stmts {
745 collect_nonlocal_idents_stmt(s, true, &mut nonlocal_idents);
746 }
747 let mut global_names: Vec<String> = Vec::new();
748 let mut global_types: Vec<Option<BoundaryType>> = Vec::new();
751 for s in stmts {
752 if let Stmt::Let { var, value, .. } = s {
753 if nonlocal_idents.contains(var) && !c.promoted.contains_key(var) {
754 let idx = u16::try_from(c.promoted.len())
755 .map_err(|_| "vm: too many globals".to_string())?;
756 c.promoted.insert(*var, idx);
757 global_names.push(interner.resolve(*var).to_string());
758 global_types.push(boundary_of_value_expr(value, &user_types, interner));
759 }
760 }
761 }
762
763 if narrow_vm_enabled() {
771 c.narrowable_seqs = crate::codegen::narrow::narrowable_seqs_for_body(stmts, interner);
772 if !c.narrowable_seqs.is_empty() {
773 crate::optimize::mark_fired(crate::optimization::Opt::NarrowVm);
774 }
775 }
776 c.begin_scope();
777 for s in stmts {
778 if !matches!(s, Stmt::FunctionDef { .. }) {
779 c.compile_stmt(s)?;
780 }
781 }
782 c.narrowable_seqs.clear();
783 c.emit(Op::Halt);
784 let main_regs = c.max_reg as usize;
785 let mut named_regs = c.named.clone();
786 named_regs.resize(main_regs, false);
787 let reg_names: Vec<(u16, String)> = c
790 .dbg_names
791 .iter()
792 .map(|(r, sym)| (*r, c.interner.resolve(*sym).to_string()))
793 .collect();
794
795 for s in stmts {
797 if let Stmt::FunctionDef { name, params, body, .. } = s {
798 let idx = c.fn_index[name];
799 let entry_pc = c.code.len();
800 c.begin_scope();
801 c.in_function = true;
802 c.named.clear();
803 for (i, (psym, _ty)) in params.iter().enumerate() {
804 c.scopes.last_mut().unwrap().insert(*psym, i as Reg);
805 c.mark_named(i as Reg);
806 }
807 c.next_reg = params.len() as Reg;
808 c.max_reg = c.max_reg.max(c.next_reg);
812 debug_assert!(params.len() <= MAX_REGISTERS_PER_FRAME);
813 c.tail_fn = Some((*name, entry_pc, params.len() as u16));
814 c.current_exempt_syms = c.mut_borrow_alias_syms.get(name).cloned().unwrap_or_default();
817 c.current_exempt_regs.clear();
818 let body_slice: &[Stmt] = body;
819 let mut bk = 0;
820 while bk < body_slice.len() {
821 if bk + 1 < body_slice.len() {
827 if let Some(args) = crate::tail_call::tail_pair_args(
828 &body_slice[bk],
829 &body_slice[bk + 1],
830 *name,
831 params.len(),
832 ) {
833 c.emit_tail_call(args, entry_pc, params.len() as u16)?;
834 bk += 2;
835 continue;
836 }
837 }
838 c.compile_stmt(&body_slice[bk])?;
839 bk += 1;
840 }
841 c.tail_fn = None;
842 c.emit(Op::ReturnNothing);
844 let exempt = std::mem::take(&mut c.current_exempt_regs);
847 c.current_exempt_syms.clear();
848 let f = &mut c.functions[idx as usize];
849 for r in exempt {
850 if !f.mutable_param_regs.contains(&r) {
851 f.mutable_param_regs.push(r);
852 }
853 }
854 f.entry_pc = entry_pc;
855 f.register_count = c.max_reg as usize;
856 let mut fnamed = c.named.clone();
857 fnamed.resize(f.register_count, false);
858 f.named_regs = fnamed;
859 }
860 }
861
862 Ok(CompiledProgram {
863 constants: c.constants,
864 code: c.code,
865 register_count: main_regs,
866 functions: c.functions,
867 fn_index: c.fn_index,
868 globals: global_names,
869 named_regs,
870 loop_locals: c.loop_locals,
871 reg_names,
872 struct_types,
873 enum_types,
874 global_types,
875 })
876 }
877
878 fn begin_scope(&mut self) {
879 self.scopes = vec![HashMap::new()];
880 self.next_reg = 0;
881 self.max_reg = 0;
882 self.in_function = false;
883 self.dbg_names.clear();
884 }
885
886 fn enter_block(&mut self) -> Reg {
888 self.scopes.push(HashMap::new());
889 self.next_reg
890 }
891
892 fn exit_block(&mut self, mark: Reg) {
895 self.scopes.pop();
896 self.next_reg = mark;
897 }
898
899 fn lookup_local(&self, sym: Symbol) -> Option<Reg> {
901 self.scopes.iter().rev().find_map(|s| s.get(&sym).copied())
902 }
903
904 fn is_hoist_enabled(&self, sym: Symbol) -> bool {
906 self.hoist_enabled.iter().any(|s| s.contains(&sym))
907 }
908
909 fn index_in_bounds(&self, collection: &Expr, index: &Expr) -> bool {
914 let Some(o) = self.oracle.as_ref() else { return false };
915 if o.index_provably_in_bounds(collection, index) {
916 return o.index_positivity_guards(index).is_empty();
922 }
923 o.index_is_speculative(index)
924 && matches!(collection, Expr::Identifier(s) if self.is_hoist_enabled(*s))
925 }
926
927 fn divpow2_shift(&self, left: &Expr, right: &Expr) -> Option<u32> {
934 let Expr::Literal(Literal::Number(d)) = right else { return None };
935 let d = *d;
936 if d < 2 || (d & (d - 1)) != 0 {
937 return None;
938 }
939 let k = d.trailing_zeros();
940 if !(1..=62).contains(&k) {
941 return None;
942 }
943 if self.oracle.as_ref()?.expr_scalar(left)? != crate::optimize::ScalarKind::Int {
944 return None;
945 }
946 Some(k)
947 }
948
949 fn modpow2_mask(&self, left: &Expr, right: &Expr) -> Option<i64> {
961 let Expr::Literal(Literal::Number(d)) = right else { return None };
962 let d = *d;
963 if d < 2 || (d & (d - 1)) != 0 {
964 return None;
965 }
966 let k = d.trailing_zeros();
967 if !(1..=62).contains(&k) {
968 return None;
969 }
970 let oracle = self.oracle.as_ref()?;
971 if oracle.expr_scalar(left)? != crate::optimize::ScalarKind::Int {
972 return None;
973 }
974 if !oracle.expr_proven_nonneg(left) {
975 return None;
976 }
977 Some(d - 1)
978 }
979
980 fn magic_div_const(&self, left: &Expr, right: &Expr) -> Option<(u64, u8)> {
995 if !magic_div_enabled() {
996 return None;
997 }
998 let Expr::Literal(Literal::Number(d)) = right else { return None };
999 let d = *d;
1000 if d < 2 || (d & (d - 1)) == 0 {
1002 return None;
1003 }
1004 let oracle = self.oracle.as_ref()?;
1005 if oracle.expr_scalar(left)? != crate::optimize::ScalarKind::Int {
1006 return None;
1007 }
1008 if !oracle.expr_proven_nonneg(left) {
1009 return None;
1010 }
1011 crate::optimize::mark_fired(crate::optimization::Opt::FastDiv);
1012 Some(magic_u64_gen(d as u64))
1013 }
1014
1015 fn emit_hoist_guards(&mut self, stmt: &Stmt) -> std::collections::HashSet<Symbol> {
1021 let mut enabled = std::collections::HashSet::new();
1022 let Some(oracle) = self.oracle.as_ref() else { return enabled };
1023 let descs = oracle
1024 .hoist_descs_for(stmt as *const Stmt as usize)
1025 .to_vec();
1026 for d in descs {
1027 let (Some(array), Some(bound), Some(iv)) = (
1028 self.lookup_local(d.array),
1029 self.lookup_local(d.bound),
1030 self.lookup_local(d.iv),
1031 ) else {
1032 continue;
1033 };
1034 self.emit(Op::RegionBoundsGuard {
1035 array,
1036 bound,
1037 iv,
1038 add_max: d.add_max,
1039 add_min: d.add_min,
1040 });
1041 enabled.insert(d.array);
1042 }
1043 enabled
1044 }
1045
1046 fn emit_unbound(&mut self, sym: Symbol) -> Result<(), String> {
1049 let msg = format!("Undefined variable: {}", self.interner.resolve(sym));
1050 let idx = self.add_const(Constant::Text(msg))?;
1051 self.emit(Op::FailWith { msg: idx });
1052 Ok(())
1053 }
1054
1055 fn emit_fail(&mut self, msg: &str) -> Result<(), String> {
1058 let idx = self.add_const(Constant::Text(msg.to_string()))?;
1059 self.emit(Op::FailWith { msg: idx });
1060 Ok(())
1061 }
1062
1063 fn resolve_name(&self, sym: Symbol) -> NameRef {
1067 if let Some(r) = self.lookup_local(sym) {
1068 if let Some(ctx) = &self.closure_ctx {
1069 if let Some(&(value, flag)) = ctx.get(&sym) {
1070 if r == value {
1073 if let Some(&global) = self.promoted.get(&sym) {
1074 return NameRef::CaptureOrGlobal { value, flag, global };
1075 }
1076 }
1077 }
1078 }
1079 NameRef::Local(r)
1080 } else if let Some(&g) = self.promoted.get(&sym) {
1081 NameRef::Global(g)
1082 } else {
1083 NameRef::Unbound
1084 }
1085 }
1086
1087 fn emit_read(&mut self, sym: Symbol, dst: Reg) -> Result<(), String> {
1089 match self.resolve_name(sym) {
1090 NameRef::Local(src) => {
1091 if src != dst {
1092 self.emit(Op::Move { dst, src });
1093 }
1094 Ok(())
1095 }
1096 NameRef::CaptureOrGlobal { value, flag, global } => {
1097 let jg = self.emit_placeholder_jump_if_false(flag);
1098 self.emit(Op::Move { dst, src: value });
1099 let jend = self.emit_placeholder_jump();
1100 self.patch_jump_target(jg, self.current_pc())?;
1101 self.emit(Op::GlobalGet { dst, idx: global });
1102 self.patch_jump_target(jend, self.current_pc())?;
1103 Ok(())
1104 }
1105 NameRef::Global(idx) => {
1106 self.emit(Op::GlobalGet { dst, idx });
1107 Ok(())
1108 }
1109 NameRef::Unbound => self.emit_unbound(sym),
1110 }
1111 }
1112
1113 fn emit_write(&mut self, sym: Symbol, src: Reg) -> Result<(), String> {
1116 match self.resolve_name(sym) {
1117 NameRef::Local(dst) => {
1118 if src != dst {
1119 self.emit(Op::Move { dst, src });
1120 }
1121 Ok(())
1122 }
1123 NameRef::CaptureOrGlobal { value, flag, global } => {
1124 let jg = self.emit_placeholder_jump_if_false(flag);
1125 self.emit(Op::Move { dst: value, src });
1126 let jend = self.emit_placeholder_jump();
1127 self.patch_jump_target(jg, self.current_pc())?;
1128 self.emit(Op::GlobalSet { idx: global, src });
1129 self.patch_jump_target(jend, self.current_pc())?;
1130 Ok(())
1131 }
1132 NameRef::Global(idx) => {
1133 self.emit(Op::GlobalSet { idx, src });
1134 Ok(())
1135 }
1136 NameRef::Unbound => self.emit_unbound(sym),
1137 }
1138 }
1139
1140 fn alloc_reg(&mut self) -> Result<Reg, String> {
1141 self.reserve_regs(1)
1142 }
1143
1144 fn emit_tail_call(
1151 &mut self,
1152 args: &[&Expr],
1153 entry_pc: usize,
1154 param_count: u16,
1155 ) -> Result<(), String> {
1156 let mut temps = Vec::with_capacity(args.len());
1157 for a in args {
1158 let t = self.alloc_reg()?;
1159 self.compile_expr_into(a, t)?;
1160 temps.push(t);
1161 }
1162 for (i, t) in temps.into_iter().enumerate() {
1163 if t != i as Reg {
1164 self.emit(Op::Move { dst: i as Reg, src: t });
1165 }
1166 }
1167 self.emit(Op::Jump { target: entry_pc });
1168 Ok(())
1169 }
1170
1171 fn reserve_regs(&mut self, n: u16) -> Result<Reg, String> {
1173 let start = self.next_reg;
1174 let next = (self.next_reg as usize) + n as usize;
1175 if next > MAX_REGISTERS_PER_FRAME {
1176 return Err(format!(
1177 "vm: out of registers (frame limit is {})",
1178 MAX_REGISTERS_PER_FRAME
1179 ));
1180 }
1181 self.next_reg = next as Reg;
1182 if self.next_reg > self.max_reg {
1183 self.max_reg = self.next_reg;
1184 }
1185 if !self.loop_stack.is_empty() {
1190 for r in start..(next as Reg) {
1191 self.record_loop_local(r);
1192 }
1193 }
1194 Ok(start)
1195 }
1196
1197 fn resolve_collection(&mut self, e: &Expr) -> Result<(Symbol, NameRef), String> {
1202 match e {
1203 Expr::Identifier(sym) => Ok((*sym, self.resolve_name(*sym))),
1204 _ => Err("vm: expected an identifier collection".to_string()),
1205 }
1206 }
1207
1208 fn collection_reg(&mut self, sym: Symbol, nr: &NameRef) -> Result<Option<Reg>, String> {
1210 match nr {
1211 NameRef::Local(r) => Ok(Some(*r)),
1212 NameRef::Unbound => Ok(None),
1213 _ => {
1214 let scratch = self.alloc_reg()?;
1215 self.emit_read(sym, scratch)?;
1216 Ok(Some(scratch))
1217 }
1218 }
1219 }
1220
1221 fn let_reg(&mut self, sym: Symbol) -> Result<Reg, String> {
1225 let r = if let Some(&r) = self.scopes.last().unwrap().get(&sym) {
1226 self.mark_named(r);
1227 r
1228 } else {
1229 let r = self.alloc_reg()?;
1230 self.scopes.last_mut().unwrap().insert(sym, r);
1231 self.mark_named(r);
1232 r
1233 };
1234 self.record_dbg_name(sym, r);
1235 if self.current_exempt_syms.contains(&sym) && !self.current_exempt_regs.contains(&r) {
1238 self.current_exempt_regs.push(r);
1239 }
1240 Ok(r)
1241 }
1242
1243 fn record_dbg_name(&mut self, sym: Symbol, r: Reg) {
1247 if !self.debug_names {
1248 return;
1249 }
1250 match self.dbg_names.iter_mut().find(|(rr, _)| *rr == r) {
1251 Some(slot) => slot.1 = sym,
1252 None => self.dbg_names.push((r, sym)),
1253 }
1254 }
1255
1256 fn mark_named(&mut self, r: Reg) {
1258 let i = r as usize;
1259 if self.named.len() <= i {
1260 self.named.resize(i + 1, false);
1261 }
1262 self.named[i] = true;
1263 self.record_loop_local(r);
1264 }
1265
1266 fn record_loop_local(&mut self, r: Reg) {
1275 let i = r as usize;
1276 for &head in &self.loop_stack {
1277 let m = self.loop_locals.entry(head).or_default();
1278 if m.len() <= i {
1279 m.resize(i + 1, false);
1280 }
1281 m[i] = true;
1282 }
1283 }
1284
1285 fn add_const(&mut self, c: Constant) -> Result<ConstIdx, String> {
1286 let key = const_key(&c);
1287 if let Some(&idx) = self.const_map.get(&key) {
1288 return Ok(idx);
1289 }
1290 let idx = ConstIdx::try_from(self.constants.len())
1291 .map_err(|_| "vm: constant pool overflow".to_string())?;
1292 self.constants.push(c);
1293 self.const_map.insert(key, idx);
1294 Ok(idx)
1295 }
1296
1297 fn emit(&mut self, op: Op) {
1298 self.code.push(op);
1299 }
1300
1301 fn compile_stmt(&mut self, s: &Stmt) -> Result<(), String> {
1302 match s {
1303 Stmt::Splice { body } => {
1304 for inner in body.iter() {
1307 self.compile_stmt(inner)?;
1308 }
1309 Ok(())
1310 }
1311 Stmt::Let { var, value, .. } => {
1312 if !self.in_function && self.scopes.len() == 1 && self.promoted.contains_key(var) {
1316 let scratch = self.alloc_reg()?;
1317 self.compile_expr_into(value, scratch)?;
1318 let idx = self.promoted[var];
1319 self.emit(Op::GlobalSet { idx, src: scratch });
1320 return Ok(());
1321 }
1322 if let Expr::New { type_name, init_fields, .. } = value {
1330 if init_fields.is_empty()
1331 && matches!(
1332 self.interner.resolve(*type_name),
1333 "Seq" | "List" | "Set" | "HashSet" | "Map" | "HashMap"
1334 )
1335 {
1336 let dst = self.let_reg(*var)?;
1337 if self.narrowable_seqs.contains(var)
1341 && matches!(self.interner.resolve(*type_name), "Seq" | "List")
1342 {
1343 self.emit(Op::NewEmptyListI32 { dst });
1344 } else {
1345 self.compile_expr_into(value, dst)?;
1346 }
1347 return Ok(());
1348 }
1349 }
1350 let scratch = self.alloc_reg()?;
1355 self.compile_expr_into(value, scratch)?;
1356 let dst = self.let_reg(*var)?;
1357 self.emit(Op::Move { dst, src: scratch });
1358 Ok(())
1359 }
1360 Stmt::Set { target, value } => {
1361 match self.resolve_name(*target) {
1362 NameRef::Local(dst) => {
1363 if let Some(terms) = add_assign_chain(*target, value) {
1368 for term in terms {
1369 let scratch = self.alloc_reg()?;
1370 self.compile_expr_into(term, scratch)?;
1371 self.emit(Op::AddAssign { dst, src: scratch });
1372 }
1373 return Ok(());
1374 }
1375 if matches!(value, Expr::Call { .. }) && !self.current_exempt_syms.contains(target) {
1386 self.compile_expr_into(value, dst)?;
1387 return Ok(());
1388 }
1389 let scratch = self.alloc_reg()?;
1394 self.compile_expr_into(value, scratch)?;
1395 self.emit(Op::Move { dst, src: scratch });
1396 Ok(())
1397 }
1398 NameRef::Unbound => {
1399 let scratch = self.alloc_reg()?;
1403 self.compile_expr_into(value, scratch)?;
1404 self.emit_unbound(*target)
1405 }
1406 _ => {
1407 let scratch = self.alloc_reg()?;
1408 self.compile_expr_into(value, scratch)?;
1409 self.emit_write(*target, scratch)
1410 }
1411 }
1412 }
1413 Stmt::Return { value } => {
1414 let zone_pos = self
1418 .flow_stack
1419 .iter()
1420 .rposition(|c| matches!(c, FlowCtx::Zone { .. }));
1421 if let Some(pos) = zone_pos {
1422 if let Some(e) = value {
1423 let scratch = self.alloc_reg()?;
1424 self.compile_expr_into(e, scratch)?;
1425 }
1426 let crossed_repeats = self.flow_stack[pos + 1..]
1427 .iter()
1428 .filter(|c| matches!(c, FlowCtx::Loop { is_repeat: true, .. }))
1429 .count();
1430 for _ in 0..crossed_repeats {
1431 self.emit(Op::IterPop);
1432 }
1433 let j = self.code.len();
1434 self.emit(Op::Jump { target: usize::MAX });
1435 if let FlowCtx::Zone { exits } = &mut self.flow_stack[pos] {
1436 exits.push(j);
1437 }
1438 } else if self.in_function {
1439 match value {
1440 Some(e) => {
1441 if let Some((tf, entry_pc, pc)) = self.tail_fn {
1446 let in_repeat = self.flow_stack.iter().any(|c| {
1452 matches!(c, FlowCtx::Loop { is_repeat: true, .. })
1453 });
1454 if !in_repeat {
1455 if let Some(args) =
1456 crate::tail_call::direct_self_tail_args(*e, tf, pc as usize)
1457 {
1458 self.emit_tail_call(args, entry_pc, pc)?;
1459 return Ok(());
1460 }
1461 }
1462 }
1463 let src = self.compile_expr(e)?;
1464 self.emit(Op::Return { src });
1465 }
1466 None => self.emit(Op::ReturnNothing),
1467 }
1468 } else {
1469 if let Some(e) = value {
1472 let scratch = self.alloc_reg()?;
1473 self.compile_expr_into(e, scratch)?;
1474 }
1475 self.emit(Op::Halt);
1476 }
1477 Ok(())
1478 }
1479 Stmt::Break => {
1480 match self.flow_stack.last_mut() {
1482 Some(FlowCtx::Loop { breaks, .. }) => {
1483 let j = self.code.len();
1484 breaks.push(j);
1485 self.emit(Op::Jump { target: usize::MAX });
1486 }
1487 Some(FlowCtx::Zone { .. }) => {
1488 let j = self.code.len();
1489 self.emit(Op::Jump { target: usize::MAX });
1490 if let Some(FlowCtx::Zone { exits }) = self.flow_stack.last_mut() {
1491 exits.push(j);
1492 }
1493 }
1494 None => {
1495 if self.in_function {
1496 self.emit(Op::ReturnNothing);
1500 } else {
1501 self.emit(Op::Halt);
1503 }
1504 }
1505 }
1506 Ok(())
1507 }
1508 Stmt::Call { function, args } => {
1509 let dst = self.alloc_reg()?;
1511 self.compile_call(*function, args, dst)
1512 }
1513 Stmt::Push { value, collection } => match collection {
1514 Expr::Identifier(_) => {
1515 let (sym, nr) = self.resolve_collection(collection)?;
1516 match self.collection_reg(sym, &nr)? {
1517 Some(list) => {
1518 let val = self.compile_expr(value)?;
1519 self.emit(Op::ListPush { list, value: val });
1520 if !matches!(nr, NameRef::Local(_)) {
1525 self.emit_write(sym, list)?;
1526 }
1527 Ok(())
1528 }
1529 None => {
1530 let scratch = self.alloc_reg()?;
1531 self.compile_expr_into(value, scratch)?;
1532 self.emit_unbound(sym)
1533 }
1534 }
1535 }
1536 Expr::FieldAccess { object: Expr::Identifier(obj_sym), field } => {
1537 let val = self.compile_expr(value)?;
1539 let obj = self.alloc_reg()?;
1540 self.emit_read(*obj_sym, obj)?;
1541 let name = self.interner.resolve(*field).to_string();
1542 let fidx = self.add_const(Constant::Text(name))?;
1543 self.emit(Op::ListPushField { obj, field: fidx, src: val });
1544 Ok(())
1545 }
1546 Expr::FieldAccess { .. } => {
1547 let scratch = self.alloc_reg()?;
1548 self.compile_expr_into(value, scratch)?;
1549 self.emit_fail("Push to nested field access not supported")
1550 }
1551 _ => {
1552 let val = self.compile_expr(value)?;
1558 let list = self.compile_expr(collection)?;
1559 self.emit(Op::ListPush { list, value: val });
1560 Ok(())
1561 }
1562 },
1563 Stmt::Add { value, collection } => {
1564 if !matches!(collection, Expr::Identifier(_)) {
1565 let val = self.compile_expr(value)?;
1570 let set = self.compile_expr(collection)?;
1571 self.emit(Op::SetAdd { set, value: val });
1572 return Ok(());
1573 }
1574 let (sym, nr) = self.resolve_collection(collection)?;
1575 match self.collection_reg(sym, &nr)? {
1576 Some(set) => {
1577 let val = self.compile_expr(value)?;
1578 self.emit(Op::SetAdd { set, value: val });
1579 if !matches!(nr, NameRef::Local(_)) {
1580 self.emit_write(sym, set)?;
1581 }
1582 Ok(())
1583 }
1584 None => {
1585 let scratch = self.alloc_reg()?;
1586 self.compile_expr_into(value, scratch)?;
1587 self.emit_unbound(sym)
1588 }
1589 }
1590 }
1591 Stmt::Remove { value, collection } => {
1592 if !matches!(collection, Expr::Identifier(_)) {
1593 let val = self.compile_expr(value)?;
1595 let coll = self.compile_expr(collection)?;
1596 self.emit(Op::RemoveFrom { collection: coll, value: val });
1597 return Ok(());
1598 }
1599 let (sym, nr) = self.resolve_collection(collection)?;
1600 match self.collection_reg(sym, &nr)? {
1601 Some(coll) => {
1602 let val = self.compile_expr(value)?;
1603 self.emit(Op::RemoveFrom { collection: coll, value: val });
1604 if !matches!(nr, NameRef::Local(_)) {
1605 self.emit_write(sym, coll)?;
1606 }
1607 Ok(())
1608 }
1609 None => {
1610 let scratch = self.alloc_reg()?;
1611 self.compile_expr_into(value, scratch)?;
1612 self.emit_unbound(sym)
1613 }
1614 }
1615 }
1616 Stmt::SetIndex { collection, index, value } => {
1617 if !matches!(collection, Expr::Identifier(_)) {
1618 let idx = self.compile_expr(index)?;
1623 let val = self.compile_expr(value)?;
1624 let coll = self.compile_expr(collection)?;
1625 self.emit(Op::SetIndex { collection: coll, index: idx, value: val });
1626 return Ok(());
1627 }
1628 let proven = self.index_in_bounds(collection, index);
1629 let (sym, nr) = self.resolve_collection(collection)?;
1630 match self.collection_reg(sym, &nr)? {
1631 Some(coll) => {
1632 let idx = self.compile_expr(index)?;
1633 let val = self.compile_expr(value)?;
1634 if proven {
1635 self.emit(Op::SetIndexUnchecked { collection: coll, index: idx, value: val });
1636 } else {
1637 self.emit(Op::SetIndex { collection: coll, index: idx, value: val });
1638 }
1639 if !matches!(nr, NameRef::Local(_)) {
1644 self.emit_write(sym, coll)?;
1645 }
1646 Ok(())
1647 }
1648 None => {
1649 let s1 = self.alloc_reg()?;
1651 self.compile_expr_into(index, s1)?;
1652 let s2 = self.alloc_reg()?;
1653 self.compile_expr_into(value, s2)?;
1654 self.emit_unbound(sym)
1655 }
1656 }
1657 }
1658 Stmt::Show { object, recipient } => {
1659 if let Expr::Identifier(sym) = recipient {
1660 self.compile_recipient_call(*sym, object)
1661 } else {
1662 let scratch = self.alloc_reg()?;
1665 self.compile_expr_into(object, scratch)?;
1666 Ok(())
1667 }
1668 }
1669 Stmt::Give { object, recipient } => {
1670 if let Expr::Identifier(sym) = recipient {
1671 self.compile_recipient_call(*sym, object)
1672 } else {
1673 let scratch = self.alloc_reg()?;
1676 self.compile_expr_into(object, scratch)?;
1677 Ok(())
1678 }
1679 }
1680 Stmt::If { cond, then_block, else_block } => {
1681 let c = self.compile_expr(cond)?;
1682 let jif = self.emit_placeholder_jump_if_false(c);
1683 let mark = self.enter_block();
1684 for st in *then_block {
1685 self.compile_stmt(st)?;
1686 }
1687 self.exit_block(mark);
1688 match else_block {
1689 Some(eb) => {
1690 let jend = self.emit_placeholder_jump();
1691 self.patch_jump_target(jif, self.current_pc())?;
1692 let mark = self.enter_block();
1693 for st in *eb {
1694 self.compile_stmt(st)?;
1695 }
1696 self.exit_block(mark);
1697 self.patch_jump_target(jend, self.current_pc())?;
1698 }
1699 None => {
1700 self.patch_jump_target(jif, self.current_pc())?;
1701 }
1702 }
1703 Ok(())
1704 }
1705 Stmt::While { cond, body, .. } => {
1706 let loop_start = self.current_pc();
1709 self.loop_stack.push(loop_start);
1714 let enabled = self.emit_hoist_guards(s);
1715 self.hoist_enabled.push(enabled);
1716 let c = self.compile_expr(cond)?;
1717 let jexit = self.emit_placeholder_jump_if_false(c);
1718 self.flow_stack.push(FlowCtx::Loop { breaks: Vec::new(), is_repeat: false });
1719 let mark = self.enter_block();
1720 for st in *body {
1721 self.compile_stmt(st)?;
1722 }
1723 self.exit_block(mark);
1724 self.hoist_enabled.pop();
1725 self.loop_stack.pop();
1726 self.emit(Op::Jump { target: loop_start });
1727 let exit_pc = self.current_pc();
1728 self.patch_jump_target(jexit, exit_pc)?;
1729 if let Some(FlowCtx::Loop { breaks, .. }) = self.flow_stack.pop() {
1730 for j in breaks {
1731 self.patch_jump_target(j, exit_pc)?;
1732 }
1733 }
1734 Ok(())
1735 }
1736 Stmt::Repeat { pattern, iterable, body } => {
1737 self.compile_repeat(pattern, iterable, body)
1738 }
1739 Stmt::Pop { collection, into } => {
1740 if !matches!(collection, Expr::Identifier(_)) {
1741 return self.emit_fail("Pop collection must be an identifier");
1742 }
1743 let (sym, nr) = self.resolve_collection(collection)?;
1744 match self.collection_reg(sym, &nr)? {
1745 Some(list) => {
1746 let dst = match into {
1747 Some(var) => self.let_reg(*var)?,
1748 None => self.alloc_reg()?,
1749 };
1750 self.emit(Op::ListPop { list, dst });
1751 if !matches!(nr, NameRef::Local(_)) {
1752 self.emit_write(sym, list)?;
1753 }
1754 Ok(())
1755 }
1756 None => self.emit_unbound(sym),
1757 }
1758 }
1759 Stmt::RuntimeAssert { condition, .. } => {
1760 let c = self.compile_expr(condition)?;
1761 let jok = self.code.len();
1762 self.emit(Op::JumpIfTrue { cond: c, target: usize::MAX });
1763 let idx = self.add_const(Constant::Text("Assertion failed".to_string()))?;
1764 self.emit(Op::FailWith { msg: idx });
1765 self.patch_jump_target(jok, self.current_pc())?;
1766 Ok(())
1767 }
1768 Stmt::Zone { name, body, .. } => self.compile_zone(*name, body),
1769 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => {
1770 for task in tasks.iter() {
1774 self.flow_stack.push(FlowCtx::Zone { exits: Vec::new() });
1775 self.compile_stmt(task)?;
1776 let end_pc = self.current_pc();
1777 if let Some(FlowCtx::Zone { exits }) = self.flow_stack.pop() {
1778 for j in exits {
1779 self.patch_jump_target(j, end_pc)?;
1780 }
1781 }
1782 }
1783 Ok(())
1784 }
1785 Stmt::Sleep { milliseconds } => {
1786 let duration = self.compile_expr(milliseconds)?;
1787 self.emit(Op::Sleep { duration });
1788 Ok(())
1789 }
1790 Stmt::Assert { .. } | Stmt::Trust { .. } | Stmt::Require { .. }
1792 | Stmt::Theorem(_) | Stmt::Definition(_) | Stmt::Axiom(_) | Stmt::Theory(_) => {
1793 Ok(())
1794 }
1795 Stmt::StructDef { .. } => Ok(()),
1797 Stmt::SetField { object, field, value } => {
1798 let field_name = self.interner.resolve(*field).to_string();
1799 let fidx = self.add_const(Constant::Text(field_name))?;
1800 if let Expr::Identifier(obj_sym) = object {
1801 match self.resolve_name(*obj_sym) {
1803 NameRef::Local(obj) => {
1804 let v = self.compile_expr(value)?;
1805 self.emit(Op::StructInsert { obj, field: fidx, value: v });
1806 Ok(())
1807 }
1808 NameRef::Unbound => {
1809 let scratch = self.alloc_reg()?;
1810 self.compile_expr_into(value, scratch)?;
1811 self.emit_unbound(*obj_sym)
1812 }
1813 _ => {
1814 let v = self.compile_expr(value)?;
1817 let obj = self.alloc_reg()?;
1818 self.emit_read(*obj_sym, obj)?;
1819 self.emit(Op::StructInsert { obj, field: fidx, value: v });
1820 self.emit_write(*obj_sym, obj)
1821 }
1822 }
1823 } else {
1824 let scratch = self.alloc_reg()?;
1825 self.compile_expr_into(value, scratch)?;
1826 let idx = self
1827 .add_const(Constant::Text("SetField target must be an identifier".to_string()))?;
1828 self.emit(Op::FailWith { msg: idx });
1829 Ok(())
1830 }
1831 }
1832 Stmt::Inspect { target, arms, .. } => self.compile_inspect(target, arms),
1833 Stmt::IncreaseCrdt { object, field, amount }
1834 | Stmt::DecreaseCrdt { object, field, amount } => {
1835 let negate = matches!(s, Stmt::DecreaseCrdt { .. });
1836 let amt = self.compile_expr(amount)?;
1837 let fname = self.interner.resolve(*field).to_string();
1838 let fidx = self.add_const(Constant::Text(fname))?;
1839 if let Expr::Identifier(obj_sym) = object {
1840 match self.resolve_name(*obj_sym) {
1841 NameRef::Local(obj) => {
1842 self.emit(Op::CrdtBump { obj, field: fidx, amount: amt, negate });
1843 Ok(())
1844 }
1845 NameRef::Unbound => self.emit_unbound(*obj_sym),
1846 _ => {
1847 let obj = self.alloc_reg()?;
1848 self.emit_read(*obj_sym, obj)?;
1849 self.emit(Op::CrdtBump { obj, field: fidx, amount: amt, negate });
1850 self.emit_write(*obj_sym, obj)
1851 }
1852 }
1853 } else {
1854 let msg = if negate {
1855 "DecreaseCrdt target must be an identifier"
1856 } else {
1857 "IncreaseCrdt target must be an identifier"
1858 };
1859 let idx = self.add_const(Constant::Text(msg.to_string()))?;
1860 self.emit(Op::FailWith { msg: idx });
1861 Ok(())
1862 }
1863 }
1864 Stmt::MergeCrdt { source, target } => {
1865 let src = self.compile_expr(source)?;
1866 if let Expr::Identifier(target_sym) = target {
1867 match self.resolve_name(*target_sym) {
1868 NameRef::Local(tgt) => {
1869 self.emit(Op::CrdtMerge { target: tgt, source: src });
1870 Ok(())
1871 }
1872 NameRef::Unbound => self.emit_unbound(*target_sym),
1873 _ => {
1874 let tgt = self.alloc_reg()?;
1875 self.emit_read(*target_sym, tgt)?;
1876 self.emit(Op::CrdtMerge { target: tgt, source: src });
1877 self.emit_write(*target_sym, tgt)
1878 }
1879 }
1880 } else {
1881 let idx = self
1882 .add_const(Constant::Text("Merge target must be an identifier".to_string()))?;
1883 self.emit(Op::FailWith { msg: idx });
1884 Ok(())
1885 }
1886 }
1887 Stmt::ReadFrom { var, source } => {
1888 use crate::ast::stmt::ReadSource;
1889 match source {
1890 ReadSource::Console => {
1891 let dst = self.let_reg(*var)?;
1893 let idx = self.add_const(Constant::Text(String::new()))?;
1894 self.emit(Op::LoadConst { dst, idx });
1895 Ok(())
1896 }
1897 ReadSource::File(path_expr) => {
1898 let scratch = self.alloc_reg()?;
1901 self.compile_expr_into(path_expr, scratch)?;
1902 let idx = self.add_const(Constant::Text(
1903 "VFS not initialized. Use Interpreter::with_vfs()".to_string(),
1904 ))?;
1905 self.emit(Op::FailWith { msg: idx });
1906 Ok(())
1907 }
1908 }
1909 }
1910 Stmt::WriteFile { content, path } => {
1911 let s1 = self.alloc_reg()?;
1912 self.compile_expr_into(content, s1)?;
1913 let s2 = self.alloc_reg()?;
1914 self.compile_expr_into(path, s2)?;
1915 let idx = self.add_const(Constant::Text(
1916 "VFS not initialized. Use Interpreter::with_vfs()".to_string(),
1917 ))?;
1918 self.emit(Op::FailWith { msg: idx });
1919 Ok(())
1920 }
1921 Stmt::Mount { path, .. } => {
1922 let scratch = self.alloc_reg()?;
1923 self.compile_expr_into(path, scratch)?;
1924 let idx = self.add_const(Constant::Text(
1925 "VFS not initialized. Use Interpreter::with_vfs()".to_string(),
1926 ))?;
1927 self.emit(Op::FailWith { msg: idx });
1928 Ok(())
1929 }
1930 Stmt::Spawn { name, .. } => {
1931 let dst = self.let_reg(*name)?;
1933 let idx = self.add_const(Constant::Nothing)?;
1934 self.emit(Op::LoadConst { dst, idx });
1935 Ok(())
1936 }
1937 Stmt::SendMessage { message, destination, .. } => {
1943 let to = self.compile_expr(destination)?;
1944 let msg = self.compile_expr(message)?;
1945 self.emit(Op::NetSend { to, msg });
1946 Ok(())
1947 }
1948 Stmt::StreamMessage { values, destination } => {
1949 let to = self.compile_expr(destination)?;
1950 let vals = self.compile_expr(values)?;
1951 self.emit(Op::NetStream { to, values: vals });
1952 Ok(())
1953 }
1954 Stmt::AwaitMessage { source, into, stream, .. } => {
1955 let from = self.compile_expr(source)?;
1956 let dst = self.let_reg(*into)?;
1957 self.emit(Op::NetAwait { dst, from, stream: *stream });
1958 Ok(())
1959 }
1960 Stmt::Check { subject, predicate, is_capability, object, source_text, .. } => {
1961 let subj = self.alloc_reg()?;
1962 self.emit_read(*subject, subj)?;
1963 let obj = if *is_capability {
1965 match object {
1966 Some(obj_sym) => {
1967 let r = self.alloc_reg()?;
1968 self.emit_read(*obj_sym, r)?;
1969 r
1970 }
1971 None => Reg::MAX,
1972 }
1973 } else {
1974 Reg::MAX
1975 };
1976 let st = self.add_const(Constant::Text(source_text.clone()))?;
1977 self.emit(Op::CheckPolicy {
1978 subject: subj,
1979 predicate: *predicate,
1980 is_capability: *is_capability,
1981 object: obj,
1982 source_text: st,
1983 });
1984 Ok(())
1985 }
1986 Stmt::AppendToSequence { sequence, value } => {
1987 let val = self.compile_expr(value)?;
1991 let seq = self.compile_expr(sequence)?;
1992 self.emit(Op::CrdtAppend { seq, value: val });
1993 Ok(())
1994 }
1995 Stmt::ResolveConflict { object, field, value } => {
1996 let val = self.compile_expr(value)?;
1997 let fname = self.interner.resolve(*field).to_string();
1998 let fidx = self.add_const(Constant::Text(fname))?;
1999 if let Expr::Identifier(obj_sym) = object {
2003 match self.resolve_name(*obj_sym) {
2004 NameRef::Local(obj) => {
2005 self.emit(Op::CrdtResolve { obj, field: fidx, value: val });
2006 Ok(())
2007 }
2008 NameRef::Unbound => self.emit_unbound(*obj_sym),
2009 _ => {
2010 let obj = self.alloc_reg()?;
2011 self.emit_read(*obj_sym, obj)?;
2012 self.emit(Op::CrdtResolve { obj, field: fidx, value: val });
2013 self.emit_write(*obj_sym, obj)
2014 }
2015 }
2016 } else {
2017 let idx = self.add_const(Constant::Text(
2018 "Resolve target must be a struct field".to_string(),
2019 ))?;
2020 self.emit(Op::FailWith { msg: idx });
2021 Ok(())
2022 }
2023 }
2024 Stmt::Listen { address, secure } => {
2028 if secure.is_some() {
2029 return Err("the PNP one-time pad (`with pad`) is only supported by the interpreter tier, not the bytecode VM".to_string());
2030 }
2031 let topic = self.compile_expr(address)?;
2032 self.emit(Op::NetListen { topic });
2033 Ok(())
2034 }
2035 Stmt::ConnectTo { address, secure } => {
2036 if secure.is_some() {
2037 return Err("the PNP one-time pad (`with pad`) is only supported by the interpreter tier, not the bytecode VM".to_string());
2038 }
2039 let url = self.compile_expr(address)?;
2040 self.emit(Op::NetConnect { url });
2041 Ok(())
2042 }
2043 Stmt::LetPeerAgent { var, address } => {
2044 let addr = self.compile_expr(address)?;
2045 let dst = self.let_reg(*var)?;
2046 self.emit(Op::NetMakePeer { dst, addr });
2047 Ok(())
2048 }
2049 Stmt::Sync { var, topic } => {
2050 let topic_reg = self.compile_expr(topic)?;
2053 let tmp = self.alloc_reg()?;
2054 self.emit_read(*var, tmp)?;
2055 self.emit(Op::NetSync { dst: tmp, topic: topic_reg });
2056 self.emit_write(*var, tmp)
2057 }
2058 Stmt::CreatePipe { var, element_type, capacity } => {
2064 let dst = self.let_reg(*var)?;
2065 let cap = capacity.map(|c| c as i32).unwrap_or(-1);
2066 let elem = crate::vm::instruction::ChanElem::from_type_name(self.interner.resolve(*element_type));
2067 self.emit(Op::ChanNew { dst, cap, elem });
2068 Ok(())
2069 }
2070 Stmt::SendPipe { value, pipe } => {
2071 let val = self.compile_expr(value)?;
2072 let chan = self.compile_expr(pipe)?;
2073 self.emit(Op::ChanSend { chan, val });
2074 Ok(())
2075 }
2076 Stmt::ReceivePipe { var, pipe } => {
2077 let chan = self.compile_expr(pipe)?;
2078 let dst = self.let_reg(*var)?;
2079 self.emit(Op::ChanRecv { dst, chan });
2080 Ok(())
2081 }
2082 Stmt::TrySendPipe { value, pipe, result } => {
2083 let val = self.compile_expr(value)?;
2084 let chan = self.compile_expr(pipe)?;
2085 let dst = match result {
2086 Some(sym) => self.let_reg(*sym)?,
2087 None => self.alloc_reg()?,
2088 };
2089 self.emit(Op::ChanTrySend { dst, chan, val });
2090 Ok(())
2091 }
2092 Stmt::TryReceivePipe { var, pipe } => {
2093 let chan = self.compile_expr(pipe)?;
2094 let dst = self.let_reg(*var)?;
2095 self.emit(Op::ChanTryRecv { dst, chan });
2096 Ok(())
2097 }
2098 Stmt::LaunchTask { function, args } => self.compile_spawn(*function, args, None),
2099 Stmt::LaunchTaskWithHandle { handle, function, args } => {
2100 self.compile_spawn(*function, args, Some(*handle))
2101 }
2102 Stmt::StopTask { handle } => {
2103 let h = self.compile_expr(handle)?;
2104 self.emit(Op::TaskAbort { handle: h });
2105 Ok(())
2106 }
2107 Stmt::Select { branches } => {
2108 use crate::ast::stmt::SelectBranch;
2109 for branch in branches.iter() {
2113 match branch {
2114 SelectBranch::Receive { var, pipe, .. } => {
2115 let chan = self.compile_expr(pipe)?;
2116 let var_reg = self.let_reg(*var)?;
2117 self.emit(Op::SelectArmRecv { chan, var: var_reg });
2118 }
2119 SelectBranch::Timeout { milliseconds, .. } => {
2120 let ticks = self.compile_expr(milliseconds)?;
2121 self.emit(Op::SelectArmTimeout { ticks });
2122 }
2123 }
2124 }
2125 let dst_arm = self.alloc_reg()?;
2126 self.emit(Op::SelectWait { dst_arm });
2127 let mut end_jumps = Vec::new();
2129 for (i, branch) in branches.iter().enumerate() {
2130 let lit = self.alloc_reg()?;
2131 let idx = self.add_const(Constant::Int(i as i64))?;
2132 self.emit(Op::LoadConst { dst: lit, idx });
2133 let cmp = self.alloc_reg()?;
2134 self.emit(Op::Eq { dst: cmp, lhs: dst_arm, rhs: lit });
2135 let jskip = self.emit_placeholder_jump_if_false(cmp);
2136 let body = match branch {
2137 SelectBranch::Receive { body, .. } | SelectBranch::Timeout { body, .. } => {
2138 *body
2139 }
2140 };
2141 let mark = self.enter_block();
2142 for st in body {
2143 self.compile_stmt(st)?;
2144 }
2145 self.exit_block(mark);
2146 let jend = self.emit_placeholder_jump();
2147 end_jumps.push(jend);
2148 self.patch_jump_target(jskip, self.current_pc())?;
2149 }
2150 for j in end_jumps {
2151 self.patch_jump_target(j, self.current_pc())?;
2152 }
2153 Ok(())
2154 }
2155 Stmt::Escape { .. } => self.emit_fail(
2156 "Escape blocks contain raw Rust code and cannot be interpreted. \
2157 Use `largo build` or `largo run` to compile and run this program.",
2158 ),
2159 Stmt::FunctionDef { .. } => {
2160 Err("vm: nested function definitions are not supported".to_string())
2165 }
2166 }
2167 }
2168
2169 fn current_pc(&self) -> usize {
2170 self.code.len()
2171 }
2172
2173 fn emit_placeholder_jump(&mut self) -> usize {
2174 let idx = self.code.len();
2175 self.emit(Op::Jump { target: usize::MAX });
2176 idx
2177 }
2178
2179 fn emit_placeholder_jump_if_false(&mut self, cond: Reg) -> usize {
2180 let idx = self.code.len();
2181 self.emit(Op::JumpIfFalse { cond, target: usize::MAX });
2182 idx
2183 }
2184
2185 fn patch_jump_target(&mut self, idx: usize, target: usize) -> Result<(), String> {
2186 patch_jump(&mut self.code, idx, target)
2187 }
2188
2189 fn compile_expr(&mut self, e: &Expr) -> Result<Reg, String> {
2193 match e {
2194 Expr::Identifier(sym) => match self.interner.resolve(*sym) {
2195 "today" | "now" => {
2196 let scratch = self.alloc_reg()?;
2197 self.compile_expr_into(e, scratch)?;
2198 Ok(scratch)
2199 }
2200 _ => match self.resolve_name(*sym) {
2201 NameRef::Local(r) => Ok(r),
2202 _ => {
2203 let scratch = self.alloc_reg()?;
2204 self.emit_read(*sym, scratch)?;
2205 Ok(scratch)
2206 }
2207 },
2208 },
2209 _ => {
2210 let dst = self.alloc_reg()?;
2211 self.compile_expr_into(e, dst)?;
2212 Ok(dst)
2213 }
2214 }
2215 }
2216
2217 fn compile_expr_into(&mut self, e: &Expr, dst: Reg) -> Result<(), String> {
2221 self.expr_depth += 1;
2222 if self.expr_depth > MAX_EXPR_DEPTH {
2223 self.expr_depth -= 1;
2224 return Err("vm: expression too deeply nested".to_string());
2225 }
2226 let result = self.compile_expr_into_inner(e, dst);
2227 self.expr_depth -= 1;
2228 result
2229 }
2230
2231 fn compile_expr_into_inner(&mut self, e: &Expr, dst: Reg) -> Result<(), String> {
2232 match e {
2233 Expr::Literal(Literal::Text(sym)) => {
2234 let idx = self.add_const(Constant::Text(self.interner.resolve(*sym).to_string()))?;
2235 self.emit(Op::LoadConst { dst, idx });
2236 Ok(())
2237 }
2238 Expr::Literal(lit) => {
2239 let idx = self.add_const(literal_const(lit)?)?;
2240 self.emit(Op::LoadConst { dst, idx });
2241 Ok(())
2242 }
2243 Expr::Identifier(sym) => {
2244 match self.interner.resolve(*sym) {
2247 "today" => {
2248 self.emit(Op::LoadToday { dst });
2249 Ok(())
2250 }
2251 "now" => {
2252 self.emit(Op::LoadNow { dst });
2253 Ok(())
2254 }
2255 _ => self.emit_read(*sym, dst),
2256 }
2257 }
2258 Expr::BinaryOp { op: BinaryOpKind::And, left, right } => {
2259 self.compile_short_circuit(true, left, right, dst)
2260 }
2261 Expr::BinaryOp { op: BinaryOpKind::Or, left, right } => {
2262 self.compile_short_circuit(false, left, right, dst)
2263 }
2264 Expr::BinaryOp { op, left, right } => {
2265 if matches!(op, BinaryOpKind::Divide) {
2269 if let Some(k) = self.divpow2_shift(left, right) {
2270 let lhs = self.compile_expr(left)?;
2271 self.emit(Op::DivPow2 { dst, lhs, k: k as u8 });
2272 return Ok(());
2273 }
2274 if let Some((magic, more)) = self.magic_div_const(left, right) {
2277 let lhs = self.compile_expr(left)?;
2278 self.emit(Op::MagicDivU { dst, lhs, magic, more, mul_back: 0 });
2279 return Ok(());
2280 }
2281 }
2282 if matches!(op, BinaryOpKind::Modulo) {
2287 if let Some(mask) = self.modpow2_mask(left, right) {
2288 let lhs = self.compile_expr(left)?;
2289 let rhs = self.alloc_reg()?;
2290 let idx = self.add_const(Constant::Int(mask))?;
2291 self.emit(Op::LoadConst { dst: rhs, idx });
2292 self.emit(Op::BitAnd { dst, lhs, rhs });
2293 return Ok(());
2294 }
2295 if let Expr::Literal(Literal::Number(c)) = right {
2299 if let Some((magic, more)) = self.magic_div_const(left, right) {
2300 let lhs = self.compile_expr(left)?;
2301 self.emit(Op::MagicDivU { dst, lhs, magic, more, mul_back: *c });
2302 return Ok(());
2303 }
2304 }
2305 }
2306 let lhs = self.compile_expr(left)?;
2307 let rhs = self.compile_expr(right)?;
2308 self.emit(binop_op(*op, dst, lhs, rhs)?);
2309 Ok(())
2310 }
2311 Expr::Not { operand } => {
2312 let src = self.compile_expr(operand)?;
2313 self.emit(Op::Not { dst, src });
2314 Ok(())
2315 }
2316 Expr::Call { function, args } => self.compile_call(*function, args, dst),
2317 Expr::List(items) => {
2318 let count = u16::try_from(items.len())
2319 .map_err(|_| "vm: list literal too long (max 65535 elements)".to_string())?;
2320 let start = self.reserve_regs(count)?;
2321 for (i, item) in items.iter().enumerate() {
2322 self.compile_expr_into(item, start + i as Reg)?;
2323 }
2324 self.emit(Op::NewList { dst, start, count });
2325 Ok(())
2326 }
2327 Expr::New { type_name, init_fields, .. } => {
2328 self.compile_new(*type_name, init_fields, dst)
2329 }
2330 Expr::FieldAccess { object, field } => {
2331 let obj = self.compile_expr(object)?;
2332 let fname = self.interner.resolve(*field).to_string();
2333 let fidx = self.add_const(Constant::Text(fname))?;
2334 self.emit(Op::GetField { dst, obj, field: fidx });
2335 Ok(())
2336 }
2337 Expr::NewVariant { enum_name, variant, fields } => {
2338 let tname = self.interner.resolve(*enum_name).to_string();
2339 let cname = self.interner.resolve(*variant).to_string();
2340 let tidx = self.add_const(Constant::Text(tname))?;
2341 let cidx = self.add_const(Constant::Text(cname))?;
2342 let count = u16::try_from(fields.len())
2343 .map_err(|_| "vm: too many variant fields".to_string())?;
2344 let args_start = self.reserve_regs(count)?;
2345 for (i, (_, field_expr)) in fields.iter().enumerate() {
2346 self.compile_expr_into(field_expr, args_start + i as Reg)?;
2347 }
2348 self.emit(Op::NewInductive { dst, type_name: tidx, ctor: cidx, args_start, count });
2349 Ok(())
2350 }
2351 Expr::Range { start, end } => {
2352 let s = self.compile_expr(start)?;
2353 let e = self.compile_expr(end)?;
2354 self.emit(Op::NewRange { dst, start: s, end: e });
2355 Ok(())
2356 }
2357 Expr::Length { collection } => {
2358 let c = self.compile_expr(collection)?;
2359 self.emit(Op::Length { dst, collection: c });
2360 Ok(())
2361 }
2362 Expr::Index { collection, index } => {
2363 let proven = self.index_in_bounds(collection, index);
2367 let c = self.compile_expr(collection)?;
2368 let i = self.compile_expr(index)?;
2369 if proven {
2370 self.emit(Op::IndexUnchecked { dst, collection: c, index: i });
2371 } else {
2372 self.emit(Op::Index { dst, collection: c, index: i });
2373 }
2374 Ok(())
2375 }
2376 Expr::Contains { collection, value } => {
2377 let c = self.compile_expr(collection)?;
2378 let v = self.compile_expr(value)?;
2379 self.emit(Op::Contains { dst, collection: c, value: v });
2380 Ok(())
2381 }
2382 Expr::InterpolatedString(parts) => self.compile_interpolation(parts, dst),
2383 Expr::Slice { collection, start, end } => {
2384 let c = self.compile_expr(collection)?;
2385 let st = self.compile_expr(start)?;
2386 let en = self.compile_expr(end)?;
2387 self.emit(Op::SliceOp { dst, collection: c, start: st, end: en });
2388 Ok(())
2389 }
2390 Expr::Copy { expr } => {
2391 let src = self.compile_expr(expr)?;
2392 self.emit(Op::DeepClone { dst, src });
2393 Ok(())
2394 }
2395 Expr::Give { value } => self.compile_expr_into(value, dst),
2396 Expr::Tuple(items) => {
2397 let count = u16::try_from(items.len())
2398 .map_err(|_| "vm: tuple literal too long".to_string())?;
2399 let start = self.reserve_regs(count)?;
2400 for (i, item) in items.iter().enumerate() {
2401 self.compile_expr_into(item, start + i as Reg)?;
2402 }
2403 self.emit(Op::NewTuple { dst, start, count });
2404 Ok(())
2405 }
2406 Expr::Union { left, right } => {
2407 let l = self.compile_expr(left)?;
2408 let r = self.compile_expr(right)?;
2409 self.emit(Op::UnionOp { dst, lhs: l, rhs: r });
2410 Ok(())
2411 }
2412 Expr::Intersection { left, right } => {
2413 let l = self.compile_expr(left)?;
2414 let r = self.compile_expr(right)?;
2415 self.emit(Op::IntersectOp { dst, lhs: l, rhs: r });
2416 Ok(())
2417 }
2418 Expr::OptionSome { value } => self.compile_expr_into(value, dst),
2419 Expr::OptionNone => {
2420 let idx = self.add_const(Constant::Nothing)?;
2421 self.emit(Op::LoadConst { dst, idx });
2422 Ok(())
2423 }
2424 Expr::WithCapacity { value, .. } => self.compile_expr_into(value, dst),
2425 Expr::ManifestOf { .. } => {
2426 self.emit(Op::NewEmptyList { dst });
2427 Ok(())
2428 }
2429 Expr::ChunkAt { .. } => {
2430 let idx = self.add_const(Constant::Nothing)?;
2431 self.emit(Op::LoadConst { dst, idx });
2432 Ok(())
2433 }
2434 Expr::Escape { .. } => {
2435 let idx = self.add_const(Constant::Text(
2436 "Escape expressions contain raw Rust code and cannot be interpreted. \
2437 Use `largo build` or `largo run` to compile and run this program."
2438 .to_string(),
2439 ))?;
2440 self.emit(Op::FailWith { msg: idx });
2441 Ok(())
2442 }
2443 Expr::Closure { params, body, .. } => self.compile_closure(params, body, dst),
2444 Expr::CallExpr { callee, args } => {
2445 let c = self.compile_expr(callee)?;
2446 let arg_count =
2447 u16::try_from(args.len()).map_err(|_| "vm: too many arguments".to_string())?;
2448 let args_start = self.reserve_regs(arg_count)?;
2449 for (i, arg) in args.iter().enumerate() {
2450 self.compile_expr_into(arg, args_start + i as Reg)?;
2451 }
2452 self.emit(Op::CallValue {
2453 dst,
2454 callee: c,
2455 args_start,
2456 arg_count,
2457 name_for_err: u32::MAX,
2458 });
2459 Ok(())
2460 }
2461 _ => Err("vm: unsupported expression".to_string()),
2462 }
2463 }
2464
2465 fn compile_repeat(
2468 &mut self,
2469 pattern: &crate::ast::stmt::Pattern,
2470 iterable: &Expr,
2471 body: &[Stmt],
2472 ) -> Result<(), String> {
2473 use crate::ast::stmt::Pattern;
2474
2475 let it_reg = self.compile_expr(iterable)?;
2476 self.emit(Op::IterPrepare { iterable: it_reg });
2477
2478 let outer_mark = self.enter_block();
2481 let loop_start;
2482 let next_idx;
2483 match pattern {
2484 Pattern::Identifier(sym) => {
2485 let dst = self.let_reg(*sym)?;
2486 loop_start = self.current_pc();
2487 next_idx = self.code.len();
2488 self.emit(Op::IterNext { dst, exit: usize::MAX });
2489 }
2490 Pattern::Tuple(syms) => {
2491 let tmp = self.alloc_reg()?;
2492 let count = u16::try_from(syms.len())
2493 .map_err(|_| "vm: tuple pattern too long".to_string())?;
2494 let start = self.reserve_regs(count)?;
2495 for (i, sym) in syms.iter().enumerate() {
2496 self.scopes.last_mut().unwrap().insert(*sym, start + i as Reg);
2497 self.mark_named(start + i as Reg);
2498 }
2499 loop_start = self.current_pc();
2500 next_idx = self.code.len();
2501 self.emit(Op::IterNext { dst: tmp, exit: usize::MAX });
2502 self.emit(Op::DestructureTuple { src: tmp, start, count });
2503 }
2504 }
2505
2506 self.flow_stack.push(FlowCtx::Loop { breaks: Vec::new(), is_repeat: true });
2507 let body_mark = self.enter_block();
2508 for st in body {
2509 self.compile_stmt(st)?;
2510 }
2511 self.exit_block(body_mark);
2512 self.emit(Op::Jump { target: loop_start });
2513
2514 let exit_pc = self.current_pc();
2515 self.patch_jump_target(next_idx, exit_pc)?;
2516 if let Some(FlowCtx::Loop { breaks, .. }) = self.flow_stack.pop() {
2517 for j in breaks {
2518 self.patch_jump_target(j, exit_pc)?;
2519 }
2520 }
2521 self.emit(Op::IterPop);
2522 self.exit_block(outer_mark);
2523 Ok(())
2524 }
2525
2526 fn compile_zone(&mut self, name: Symbol, body: &[Stmt]) -> Result<(), String> {
2529 let outer_mark = self.enter_block();
2530 let name_reg = self.let_reg(name)?;
2531 let idx = self.add_const(Constant::Nothing)?;
2532 self.emit(Op::LoadConst { dst: name_reg, idx });
2533 self.flow_stack.push(FlowCtx::Zone { exits: Vec::new() });
2534 let body_mark = self.enter_block();
2535 for st in body {
2536 self.compile_stmt(st)?;
2537 }
2538 self.exit_block(body_mark);
2539 let end_pc = self.current_pc();
2540 if let Some(FlowCtx::Zone { exits }) = self.flow_stack.pop() {
2541 for j in exits {
2542 self.patch_jump_target(j, end_pc)?;
2543 }
2544 }
2545 self.exit_block(outer_mark);
2546 Ok(())
2547 }
2548
2549 fn compile_inspect(
2552 &mut self,
2553 target: &Expr,
2554 arms: &[crate::ast::stmt::MatchArm],
2555 ) -> Result<(), String> {
2556 let t = self.compile_expr(target)?;
2557 let mut end_jumps: Vec<usize> = Vec::new();
2558 let mut has_otherwise = false;
2559 for arm in arms.iter() {
2560 match arm.variant {
2561 None => {
2562 has_otherwise = true;
2564 let mark = self.enter_block();
2565 for st in arm.body {
2566 self.compile_stmt(st)?;
2567 }
2568 self.exit_block(mark);
2569 break;
2570 }
2571 Some(variant) => {
2572 let vname = self.interner.resolve(variant).to_string();
2573 let vidx = self.add_const(Constant::Text(vname))?;
2574 let flag = self.alloc_reg()?;
2575 self.emit(Op::TestArm { dst: flag, target: t, variant: vidx });
2576 let jnext = self.emit_placeholder_jump_if_false(flag);
2577
2578 let mark = self.enter_block();
2579 for (i, (field_name, binding_name)) in arm.bindings.iter().enumerate() {
2583 let dst = self.let_reg(*binding_name)?;
2584 let fname = self.interner.resolve(*field_name).to_string();
2585 let fidx = self.add_const(Constant::Text(fname))?;
2586 self.emit(Op::BindArm {
2587 dst,
2588 target: t,
2589 field: fidx,
2590 index: u16::try_from(i)
2591 .map_err(|_| "vm: too many arm bindings".to_string())?,
2592 });
2593 }
2594 for st in arm.body {
2595 self.compile_stmt(st)?;
2596 }
2597 self.exit_block(mark);
2598 let j = self.emit_placeholder_jump();
2599 end_jumps.push(j);
2600 self.patch_jump_target(jnext, self.current_pc())?;
2601 }
2602 }
2603 }
2604 if !has_otherwise {
2608 self.emit_fail(
2609 "Inspect has no arm for the value and no Otherwise (matches must be exhaustive)",
2610 )?;
2611 }
2612 let end_pc = self.current_pc();
2613 for j in end_jumps {
2614 self.patch_jump_target(j, end_pc)?;
2615 }
2616 Ok(())
2617 }
2618
2619 fn compile_new(
2621 &mut self,
2622 type_name: Symbol,
2623 init_fields: &[(Symbol, &Expr)],
2624 dst: Reg,
2625 ) -> Result<(), String> {
2626 match self.interner.resolve(type_name) {
2628 "Seq" | "List" => { self.emit(Op::NewEmptyList { dst }); return Ok(()); }
2629 "Set" | "HashSet" => { self.emit(Op::NewEmptySet { dst }); return Ok(()); }
2630 "Map" | "HashMap" => { self.emit(Op::NewEmptyMap { dst }); return Ok(()); }
2631 _ => {}
2632 }
2633 let name = self.interner.resolve(type_name).to_string();
2634 let name_idx = self.add_const(Constant::Text(name))?;
2635 self.emit(Op::NewStruct { dst, type_name: name_idx });
2636
2637 let mut provided: std::collections::HashSet<Symbol> = std::collections::HashSet::new();
2638 for (field_sym, field_expr) in init_fields {
2639 provided.insert(*field_sym);
2640 let fname = self.interner.resolve(*field_sym).to_string();
2641 let fidx = self.add_const(Constant::Text(fname))?;
2642 let v = self.compile_expr(field_expr)?;
2643 self.emit(Op::StructInsert { obj: dst, field: fidx, value: v });
2644 }
2645
2646 if let Some(def) = self.struct_defs.get(&type_name).cloned() {
2649 for (field_sym, type_sym, _) in def {
2650 if provided.contains(&field_sym) {
2651 continue;
2652 }
2653 let fname = self.interner.resolve(field_sym).to_string();
2654 let fidx = self.add_const(Constant::Text(fname))?;
2655 let v = self.alloc_reg()?;
2656 match self.interner.resolve(type_sym) {
2657 "Int" | "Byte" => {
2658 let i = self.add_const(Constant::Int(0))?;
2659 self.emit(Op::LoadConst { dst: v, idx: i });
2660 }
2661 "Float" => {
2662 let i = self.add_const(Constant::Float(0.0))?;
2663 self.emit(Op::LoadConst { dst: v, idx: i });
2664 }
2665 "Bool" => {
2666 let i = self.add_const(Constant::Bool(false))?;
2667 self.emit(Op::LoadConst { dst: v, idx: i });
2668 }
2669 "Text" | "String" => {
2670 let i = self.add_const(Constant::Text(String::new()))?;
2671 self.emit(Op::LoadConst { dst: v, idx: i });
2672 }
2673 "Char" => {
2674 let i = self.add_const(Constant::Char('\0'))?;
2675 self.emit(Op::LoadConst { dst: v, idx: i });
2676 }
2677 "Seq" | "List" => self.emit(Op::NewEmptyList { dst: v }),
2678 "Set" | "HashSet" => self.emit(Op::NewEmptySet { dst: v }),
2679 "Map" | "HashMap" => self.emit(Op::NewEmptyMap { dst: v }),
2680 "SharedSet" | "ORSet" | "SharedSet_AddWins" => {
2683 self.emit(Op::NewCrdt { dst: v, kind: 0 })
2684 }
2685 "SharedSet_RemoveWins" => self.emit(Op::NewCrdt { dst: v, kind: 3 }),
2686 "SharedSequence" | "RGA" | "SharedSequence_YATA" | "CollaborativeSequence" => {
2687 self.emit(Op::NewCrdt { dst: v, kind: 1 })
2688 }
2689 _ => {
2690 let i = self.add_const(Constant::Nothing)?;
2691 self.emit(Op::LoadConst { dst: v, idx: i });
2692 }
2693 }
2694 self.emit(Op::StructInsert { obj: dst, field: fidx, value: v });
2695 }
2696 }
2697 Ok(())
2698 }
2699
2700 fn compile_short_circuit(
2720 &mut self,
2721 is_and: bool,
2722 left: &Expr,
2723 right: &Expr,
2724 dst: Reg,
2725 ) -> Result<(), String> {
2726 let l = self.compile_expr(left)?;
2727
2728 let j_eval = self.current_pc();
2729 if is_and {
2730 self.emit(Op::JumpIfTrue { cond: l, target: usize::MAX });
2731 } else {
2732 self.emit(Op::JumpIfFalse { cond: l, target: usize::MAX });
2733 }
2734
2735 let idx_false = self.add_const(Constant::Bool(false))?;
2737 let idx_true = self.add_const(Constant::Bool(true))?;
2738 self.emit(Op::LoadConst { dst, idx: if is_and { idx_false } else { idx_true } });
2739 let j_end = self.emit_placeholder_jump();
2740
2741 let eval_pc = self.current_pc();
2742 self.patch_jump_target(j_eval, eval_pc)?;
2743 let r = self.compile_expr(right)?;
2744 let j_true = self.current_pc();
2745 self.emit(Op::JumpIfTrue { cond: r, target: usize::MAX });
2746 self.emit(Op::LoadConst { dst, idx: idx_false });
2747 let j_end2 = self.emit_placeholder_jump();
2748 let t_pc = self.current_pc();
2749 self.patch_jump_target(j_true, t_pc)?;
2750 self.emit(Op::LoadConst { dst, idx: idx_true });
2751 self.patch_jump_target(j_end, self.current_pc())?;
2752 self.patch_jump_target(j_end2, self.current_pc())?;
2753 Ok(())
2754 }
2755
2756 fn compile_spawn(
2764 &mut self,
2765 function: Symbol,
2766 args: &[&Expr],
2767 handle: Option<Symbol>,
2768 ) -> Result<(), String> {
2769 let func = match self.fn_index.get(&function) {
2770 Some(&f) => f,
2771 None => {
2772 return self.emit_fail(&format!(
2773 "Unknown task function: {}",
2774 self.interner.resolve(function)
2775 ))
2776 }
2777 };
2778 let arg_count =
2779 u16::try_from(args.len()).map_err(|_| "vm: too many task arguments".to_string())?;
2780 let args_start = self.reserve_regs(arg_count)?;
2781 for (i, arg) in args.iter().enumerate() {
2782 self.compile_expr_into(arg, args_start + i as Reg)?;
2783 }
2784 match handle {
2785 Some(h) => {
2786 let dst = self.let_reg(h)?;
2787 self.emit(Op::SpawnHandle { dst, func, args_start, arg_count });
2788 }
2789 None => self.emit(Op::Spawn { func, args_start, arg_count }),
2790 }
2791 Ok(())
2792 }
2793
2794 fn compile_call(&mut self, function: Symbol, args: &[&Expr], dst: Reg) -> Result<(), String> {
2795 use crate::semantics::builtins::{builtin_from_name, check_arity, BuiltinId};
2796
2797 let name = self.interner.resolve(function);
2798 if name == "show" {
2799 for arg in args {
2801 let src = self.compile_expr(arg)?;
2802 self.emit(Op::Show { src });
2803 }
2804 let idx = self.add_const(Constant::Nothing)?;
2805 self.emit(Op::LoadConst { dst, idx });
2806 return Ok(());
2807 }
2808
2809 if name == "args" {
2814 self.emit(Op::Args { dst });
2815 return Ok(());
2816 }
2817
2818 if matches!(name, "mapOf" | "setOf") {
2823 let id = builtin_from_name(name).expect("mapOf/setOf are registered builtins");
2824 if let Err(msg) = check_arity(id, args.len()) {
2825 let idx = self.add_const(Constant::Text(msg))?;
2826 self.emit(Op::FailWith { msg: idx });
2827 return Ok(());
2828 }
2829 if id == BuiltinId::MapOf {
2830 self.emit(Op::NewEmptyMap { dst });
2831 for pair in args.chunks(2) {
2832 let k = self.compile_expr(pair[0])?;
2833 let v = self.compile_expr(pair[1])?;
2834 self.emit(Op::SetIndex { collection: dst, index: k, value: v });
2835 }
2836 } else {
2837 self.emit(Op::NewEmptySet { dst });
2838 for arg in args {
2839 let v = self.compile_expr(arg)?;
2840 self.emit(Op::SetAdd { set: dst, value: v });
2841 }
2842 }
2843 return Ok(());
2844 }
2845
2846 if let Some(id) = builtin_from_name(name) {
2847 if let Err(msg) = check_arity(id, args.len()) {
2848 let idx = self.add_const(Constant::Text(msg))?;
2849 self.emit(Op::FailWith { msg: idx });
2850 return Ok(());
2851 }
2852 let used: &[&Expr] = if id == BuiltinId::Format && !args.is_empty() {
2854 &args[..1]
2855 } else {
2856 args
2857 };
2858 let arg_count =
2859 u16::try_from(used.len()).map_err(|_| "vm: too many arguments".to_string())?;
2860 let args_start = self.reserve_regs(arg_count)?;
2861 for (i, arg) in used.iter().enumerate() {
2862 self.compile_expr_into(arg, args_start + i as Reg)?;
2863 }
2864 self.emit(Op::CallBuiltin { dst, builtin: id, args_start, arg_count });
2865 return Ok(());
2866 }
2867
2868 if let Some(&func) = self.fn_index.get(&function) {
2869 let param_count = self.functions[func as usize].param_count as usize;
2870 if args.len() != param_count {
2871 let msg = format!(
2872 "Function {} expects {} arguments, got {}",
2873 name,
2874 param_count,
2875 args.len()
2876 );
2877 let idx = self.add_const(Constant::Text(msg))?;
2878 self.emit(Op::FailWith { msg: idx });
2879 return Ok(());
2880 }
2881 let arg_count =
2882 u16::try_from(args.len()).map_err(|_| "vm: too many arguments".to_string())?;
2883 let args_start = self.reserve_regs(arg_count)?;
2884 if let Some(mb_idxs) = self.mut_borrow_params.get(&function).cloned() {
2891 for (i, arg) in args.iter().enumerate() {
2892 if mb_idxs.contains(&i) {
2893 if let Expr::Identifier(x) = arg {
2894 if self.current_exempt_syms.contains(x) {
2901 continue;
2902 }
2903 if let NameRef::Local(x_reg) = self.resolve_name(*x) {
2904 self.emit(Op::EnsureOwned { reg: x_reg });
2905 }
2906 }
2907 }
2908 }
2909 }
2910 for (i, arg) in args.iter().enumerate() {
2911 self.compile_expr_into(arg, args_start + i as Reg)?;
2912 }
2913 self.emit(Op::Call { dst, func, args_start, arg_count });
2914 return Ok(());
2915 }
2916
2917 if !matches!(self.resolve_name(function), NameRef::Unbound) {
2921 let callee = self.compile_expr(&Expr::Identifier(function))?;
2922 let arg_count =
2923 u16::try_from(args.len()).map_err(|_| "vm: too many arguments".to_string())?;
2924 let args_start = self.reserve_regs(arg_count)?;
2925 for (i, arg) in args.iter().enumerate() {
2926 self.compile_expr_into(arg, args_start + i as Reg)?;
2927 }
2928 let name_idx = self.add_const(Constant::Text(name.to_string()))?;
2929 self.emit(Op::CallValue { dst, callee, args_start, arg_count, name_for_err: name_idx });
2930 return Ok(());
2931 }
2932
2933 let msg = format!("Unknown function: {}", name);
2934 let idx = self.add_const(Constant::Text(msg))?;
2935 self.emit(Op::FailWith { msg: idx });
2936 Ok(())
2937 }
2938
2939 fn compile_recipient_call(&mut self, recipient: Symbol, object: &Expr) -> Result<(), String> {
2944 let name = self.interner.resolve(recipient);
2945 if name == "show" {
2946 let src = self.compile_expr(object)?;
2947 self.emit(Op::Show { src });
2948 return Ok(());
2949 }
2950 if let Some(&func) = self.fn_index.get(&recipient) {
2951 let param_count = self.functions[func as usize].param_count as usize;
2952 let args_start = self.reserve_regs(1)?;
2953 self.compile_expr_into(object, args_start)?;
2954 if param_count != 1 {
2955 let msg = format!(
2956 "Function {} expects {} arguments, got 1",
2957 name, param_count
2958 );
2959 let idx = self.add_const(Constant::Text(msg))?;
2960 self.emit(Op::FailWith { msg: idx });
2961 return Ok(());
2962 }
2963 let dst = self.alloc_reg()?;
2964 self.emit(Op::Call { dst, func, args_start, arg_count: 1 });
2965 return Ok(());
2966 }
2967 if !matches!(self.resolve_name(recipient), NameRef::Unbound) {
2969 let args_start = self.reserve_regs(1)?;
2970 self.compile_expr_into(object, args_start)?;
2971 let callee = self.compile_expr(&Expr::Identifier(recipient))?;
2972 let dst = self.alloc_reg()?;
2973 let name_idx = self.add_const(Constant::Text(name.to_string()))?;
2974 self.emit(Op::CallValue { dst, callee, args_start, arg_count: 1, name_for_err: name_idx });
2975 return Ok(());
2976 }
2977 let scratch = self.alloc_reg()?;
2979 self.compile_expr_into(object, scratch)?;
2980 let msg = format!("Unknown function: {}", name);
2981 let idx = self.add_const(Constant::Text(msg))?;
2982 self.emit(Op::FailWith { msg: idx });
2983 Ok(())
2984 }
2985
2986 fn compile_interpolation(
2990 &mut self,
2991 parts: &[crate::ast::stmt::StringPart],
2992 dst: Reg,
2993 ) -> Result<(), String> {
2994 use crate::ast::stmt::StringPart;
2995
2996 let empty = self.add_const(Constant::Text(String::new()))?;
2997 self.emit(Op::LoadConst { dst, idx: empty });
2998 for part in parts {
2999 match part {
3000 StringPart::Literal(sym) => {
3001 let idx =
3002 self.add_const(Constant::Text(self.interner.resolve(*sym).to_string()))?;
3003 let lit = self.alloc_reg()?;
3004 self.emit(Op::LoadConst { dst: lit, idx });
3005 self.emit(Op::Concat { dst, lhs: dst, rhs: lit });
3006 }
3007 StringPart::Expr { value, format_spec, debug } => {
3008 let v = self.compile_expr(value)?;
3009 let needs_format = format_spec.is_some() || *debug;
3010 let piece = if needs_format {
3011 let spec = match format_spec {
3012 Some(sym) => self.add_const(Constant::Text(
3013 self.interner.resolve(*sym).to_string(),
3014 ))?,
3015 None => u32::MAX,
3016 };
3017 let debug_prefix = if *debug {
3018 let prefix = match value {
3019 Expr::Identifier(sym) => {
3020 self.interner.resolve(*sym).to_string()
3021 }
3022 _ => "expr".to_string(),
3023 };
3024 self.add_const(Constant::Text(prefix))?
3025 } else {
3026 u32::MAX
3027 };
3028 let formatted = self.alloc_reg()?;
3029 self.emit(Op::FormatValue { dst: formatted, src: v, spec, debug_prefix });
3030 formatted
3031 } else {
3032 v
3033 };
3034 self.emit(Op::Concat { dst, lhs: dst, rhs: piece });
3035 }
3036 }
3037 }
3038 Ok(())
3039 }
3040
3041 fn compile_closure(
3047 &mut self,
3048 params: &[(Symbol, &crate::ast::stmt::TypeExpr)],
3049 body: &crate::ast::stmt::ClosureBody,
3050 dst: Reg,
3051 ) -> Result<(), String> {
3052 use crate::ast::stmt::ClosureBody;
3053 use crate::interpreter::Interpreter;
3054
3055 let free = Interpreter::free_vars_in_closure(params, body);
3056 let mut captures: Vec<(Symbol, Option<u16>)> = Vec::new();
3057 let mut local_sources: Vec<Reg> = Vec::new();
3058 for sym in free {
3059 match self.resolve_name(sym) {
3060 NameRef::Local(r) => {
3061 captures.push((sym, None));
3062 local_sources.push(r);
3063 }
3064 NameRef::CaptureOrGlobal { value, global, .. } => {
3065 let _ = global;
3069 captures.push((sym, None));
3070 local_sources.push(value);
3071 }
3072 NameRef::Global(g) => captures.push((sym, Some(g))),
3073 NameRef::Unbound => {}
3074 }
3075 }
3076
3077 let local_count =
3079 u16::try_from(local_sources.len()).map_err(|_| "vm: too many captures".to_string())?;
3080 let locals_start = self.reserve_regs(local_count)?;
3081 for (i, src) in local_sources.iter().enumerate() {
3082 let w = locals_start + i as Reg;
3083 if *src != w {
3084 self.emit(Op::Move { dst: w, src: *src });
3085 }
3086 }
3087
3088 let func_idx = u16::try_from(self.functions.len())
3090 .map_err(|_| "vm: too many functions".to_string())?;
3091 let param_count =
3092 u16::try_from(params.len()).map_err(|_| "vm: too many parameters".to_string())?;
3093 let jover = self.emit_placeholder_jump();
3094 self.functions.push(CompiledFunction {
3095 name: Symbol::default(),
3096 entry_pc: self.code.len(),
3097 param_count,
3098 register_count: 0,
3099 captures: captures.clone(),
3100 named_regs: Vec::new(),
3101 param_kinds: vec![
3104 Some(super::native_tier::ParamKind::Scalar(
3105 super::native_tier::SlotKind::Int
3106 ));
3107 param_count as usize
3108 ],
3109 ret_kind: None,
3110 param_types: params
3116 .iter()
3117 .map(|(_, ty)| boundary_of_type_expr(ty, &self.user_types, self.interner))
3118 .collect(),
3119 return_type: None,
3120 mutable_param_regs: Vec::new(),
3122 });
3123
3124 let saved_scopes = std::mem::replace(&mut self.scopes, vec![HashMap::new()]);
3126 let saved_next = self.next_reg;
3127 let saved_max = self.max_reg;
3128 let saved_flow = std::mem::take(&mut self.flow_stack);
3129 let saved_in_fn = self.in_function;
3130 let saved_ctx = self.closure_ctx.take();
3131 let saved_named = std::mem::take(&mut self.named);
3132
3133 self.in_function = true;
3134 let p = param_count;
3135 let cap_n = captures.len() as Reg;
3136 for (i, (psym, _)) in params.iter().enumerate() {
3137 self.scopes.last_mut().unwrap().insert(*psym, i as Reg);
3138 self.mark_named(i as Reg);
3139 }
3140 let mut ctx: HashMap<Symbol, (Reg, Reg)> = HashMap::new();
3141 for (k, (sym, global)) in captures.iter().enumerate() {
3142 let value = p + k as Reg;
3143 self.scopes.last_mut().unwrap().insert(*sym, value);
3144 self.mark_named(value);
3145 if global.is_some() {
3146 ctx.insert(*sym, (value, p + cap_n + k as Reg));
3147 }
3148 }
3149 self.next_reg = p + 2 * cap_n;
3150 self.max_reg = self.next_reg;
3151 self.closure_ctx = Some(ctx);
3152
3153 let body_result = (|| -> Result<(), String> {
3154 match body {
3155 ClosureBody::Expression(e) => {
3156 let r = self.compile_expr(e)?;
3157 self.emit(Op::Return { src: r });
3158 }
3159 ClosureBody::Block(block) => {
3160 for st in block.iter() {
3161 self.compile_stmt(st)?;
3162 }
3163 self.emit(Op::ReturnNothing);
3164 }
3165 }
3166 Ok(())
3167 })();
3168 self.functions[func_idx as usize].register_count = self.max_reg as usize;
3169 let mut cnamed = std::mem::replace(&mut self.named, saved_named);
3170 cnamed.resize(self.functions[func_idx as usize].register_count, false);
3171 self.functions[func_idx as usize].named_regs = cnamed;
3172
3173 self.scopes = saved_scopes;
3175 self.next_reg = saved_next;
3176 self.max_reg = saved_max.max(self.next_reg);
3177 self.flow_stack = saved_flow;
3178 self.in_function = saved_in_fn;
3179 self.closure_ctx = saved_ctx;
3180 body_result?;
3181
3182 self.patch_jump_target(jover, self.current_pc())?;
3183 self.emit(Op::MakeClosure { dst, func: func_idx, locals_start });
3184 Ok(())
3185 }
3186}
3187
3188fn collect_nonlocal_idents_stmt(
3194 s: &Stmt,
3195 at_main: bool,
3196 out: &mut std::collections::HashSet<Symbol>,
3197) {
3198 use crate::ast::stmt::ClosureBody;
3199 use crate::interpreter::Interpreter;
3200
3201 if let Stmt::FunctionDef { params, body, .. } = s {
3202 let free = Interpreter::free_vars_in_closure(params, &ClosureBody::Block(body));
3204 out.extend(free);
3205 return;
3206 }
3207 visit_stmt_exprs(s, &mut |e| {
3210 if let Expr::Closure { params, body, .. } = e {
3211 let free = Interpreter::free_vars_in_closure(params, body);
3212 out.extend(free);
3213 }
3214 });
3215 for_each_child_block(s, &mut |block| {
3216 for st in block {
3217 collect_nonlocal_idents_stmt(st, at_main, out);
3218 }
3219 });
3220}
3221
3222fn visit_stmt_exprs(s: &Stmt, f: &mut dyn FnMut(&Expr)) {
3226 fn walk_expr<'a>(root: &'a Expr<'a>, f: &mut dyn FnMut(&Expr)) {
3229 let mut stack: Vec<&Expr> = vec![root];
3230 while let Some(e) = stack.pop() {
3231 f(e);
3232 match e {
3233 Expr::BinaryOp { left, right, .. } => {
3234 stack.push(left);
3235 stack.push(right);
3236 }
3237 Expr::Not { operand } => stack.push(operand),
3238 Expr::Call { args, .. } => stack.extend(args.iter().copied()),
3239 Expr::CallExpr { callee, args } => {
3240 stack.push(callee);
3241 stack.extend(args.iter().copied());
3242 }
3243 Expr::Index { collection, index } => {
3244 stack.push(collection);
3245 stack.push(index);
3246 }
3247 Expr::Slice { collection, start, end } => {
3248 stack.push(collection);
3249 stack.push(start);
3250 stack.push(end);
3251 }
3252 Expr::Copy { expr } => stack.push(expr),
3253 Expr::Give { value } => stack.push(value),
3254 Expr::Length { collection } => stack.push(collection),
3255 Expr::Contains { collection, value } => {
3256 stack.push(collection);
3257 stack.push(value);
3258 }
3259 Expr::Union { left, right } | Expr::Intersection { left, right } => {
3260 stack.push(left);
3261 stack.push(right);
3262 }
3263 Expr::List(items) | Expr::Tuple(items) => stack.extend(items.iter().copied()),
3264 Expr::Range { start, end } => {
3265 stack.push(start);
3266 stack.push(end);
3267 }
3268 Expr::FieldAccess { object, .. } => stack.push(object),
3269 Expr::New { init_fields, .. } => {
3270 stack.extend(init_fields.iter().map(|(_, fe)| *fe));
3271 }
3272 Expr::NewVariant { fields, .. } => {
3273 stack.extend(fields.iter().map(|(_, fe)| *fe));
3274 }
3275 Expr::OptionSome { value } => stack.push(value),
3276 Expr::WithCapacity { value, .. } => stack.push(value),
3277 _ => {}
3278 }
3279 }
3280 }
3281
3282 use crate::ast::stmt::ReadSource;
3283 match s {
3284 Stmt::Let { value, .. } | Stmt::Set { value, .. } => walk_expr(value, f),
3285 Stmt::Return { value: Some(e) } => walk_expr(e, f),
3286 Stmt::Call { args, .. } => {
3287 for a in args {
3288 walk_expr(a, f);
3289 }
3290 }
3291 Stmt::If { cond, .. } | Stmt::While { cond, .. } => walk_expr(cond, f),
3292 Stmt::Repeat { iterable, .. } => walk_expr(iterable, f),
3293 Stmt::Show { object, .. } | Stmt::Give { object, .. } => walk_expr(object, f),
3294 Stmt::Push { value, collection }
3295 | Stmt::Add { value, collection }
3296 | Stmt::Remove { value, collection } => {
3297 walk_expr(value, f);
3298 walk_expr(collection, f);
3299 }
3300 Stmt::SetIndex { collection, index, value } => {
3301 walk_expr(collection, f);
3302 walk_expr(index, f);
3303 walk_expr(value, f);
3304 }
3305 Stmt::SetField { object, value, .. } => {
3306 walk_expr(object, f);
3307 walk_expr(value, f);
3308 }
3309 Stmt::Inspect { target, .. } => walk_expr(target, f),
3310 Stmt::RuntimeAssert { condition, .. } => walk_expr(condition, f),
3311 Stmt::Sleep { milliseconds } => walk_expr(milliseconds, f),
3312 Stmt::IncreaseCrdt { amount, .. } | Stmt::DecreaseCrdt { amount, .. } => {
3313 walk_expr(amount, f)
3314 }
3315 Stmt::MergeCrdt { source, .. } => walk_expr(source, f),
3316 Stmt::WriteFile { content, path } => {
3317 walk_expr(content, f);
3318 walk_expr(path, f);
3319 }
3320 Stmt::ReadFrom { source: ReadSource::File(p), .. } => walk_expr(p, f),
3321 _ => {}
3322 }
3323}
3324
3325fn for_each_child_block<'a>(s: &Stmt<'a>, f: &mut dyn FnMut(&[Stmt<'a>])) {
3327 match s {
3328 Stmt::If { then_block, else_block, .. } => {
3329 f(then_block);
3330 if let Some(eb) = else_block {
3331 f(eb);
3332 }
3333 }
3334 Stmt::While { body, .. } | Stmt::Repeat { body, .. } | Stmt::Zone { body, .. } => f(body),
3335 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => f(tasks),
3336 Stmt::Inspect { arms, .. } => {
3337 for arm in arms {
3338 f(arm.body);
3339 }
3340 }
3341 _ => {}
3342 }
3343}
3344
3345pub(super) fn patch_jump(code: &mut [Op], idx: usize, target: usize) -> Result<(), String> {
3348 match code.get_mut(idx) {
3349 Some(Op::Jump { target: t })
3350 | Some(Op::JumpIfFalse { target: t, .. })
3351 | Some(Op::JumpIfTrue { target: t, .. })
3352 | Some(Op::IterNext { exit: t, .. }) => {
3353 *t = target;
3354 Ok(())
3355 }
3356 Some(other) => Err(format!("vm: patch_jump on non-jump op {:?}", other)),
3357 None => Err(format!("vm: patch_jump index {} out of bounds", idx)),
3358 }
3359}
3360
3361fn literal_const(lit: &Literal) -> Result<Constant, String> {
3362 match lit {
3363 Literal::Number(n) => Ok(Constant::Int(*n)),
3364 Literal::Float(f) => Ok(Constant::Float(*f)),
3365 Literal::Boolean(b) => Ok(Constant::Bool(*b)),
3366 Literal::Nothing => Ok(Constant::Nothing),
3367 Literal::Char(c) => Ok(Constant::Char(*c)),
3368 Literal::Duration(nanos) => Ok(Constant::Duration(*nanos)),
3369 Literal::Date(days) => Ok(Constant::Date(*days)),
3370 Literal::Moment(nanos) => Ok(Constant::Moment(*nanos)),
3371 Literal::Span { months, days } => Ok(Constant::Span { months: *months, days: *days }),
3372 Literal::Time(nanos) => Ok(Constant::Time(*nanos)),
3373 Literal::Text(_) => unreachable!("Text literals are interned and handled by the caller"),
3374 }
3375}
3376
3377fn add_assign_chain<'a>(target: Symbol, value: &'a Expr<'a>) -> Option<Vec<&'a Expr<'a>>> {
3388 let mut terms: Vec<&Expr> = Vec::new();
3389 let mut cur = value;
3390 loop {
3391 match cur {
3392 Expr::BinaryOp { op: BinaryOpKind::Add, left, right } => {
3393 terms.push(right);
3394 cur = left;
3395 }
3396 Expr::Identifier(sym) if *sym == target => break,
3397 _ => return None,
3398 }
3399 }
3400 if terms.is_empty() {
3401 return None;
3402 }
3403 terms.reverse();
3404 if terms[1..].iter().all(|t| expr_avoids(t, target)) {
3405 Some(terms)
3406 } else {
3407 None
3408 }
3409}
3410
3411fn expr_avoids(e: &Expr, sym: Symbol) -> bool {
3419 use crate::ast::stmt::StringPart;
3420 let mut stack: Vec<&Expr> = vec![e];
3421 while let Some(e) = stack.pop() {
3422 match e {
3423 Expr::Identifier(s) => {
3424 if *s == sym {
3425 return false;
3426 }
3427 }
3428 Expr::Literal(_) | Expr::OptionNone => {}
3429 Expr::BinaryOp { left, right, .. }
3430 | Expr::Union { left, right }
3431 | Expr::Intersection { left, right }
3432 | Expr::Range { start: left, end: right } => {
3433 stack.push(left);
3434 stack.push(right);
3435 }
3436 Expr::Not { operand } => stack.push(operand),
3437 Expr::Call { args, .. } => stack.extend(args.iter().copied()),
3438 Expr::CallExpr { callee, args } => {
3439 stack.push(callee);
3440 stack.extend(args.iter().copied());
3441 }
3442 Expr::Index { collection, index } => {
3443 stack.push(collection);
3444 stack.push(index);
3445 }
3446 Expr::Slice { collection, start, end } => {
3447 stack.push(collection);
3448 stack.push(start);
3449 stack.push(end);
3450 }
3451 Expr::Copy { expr } => stack.push(expr),
3452 Expr::Length { collection } => stack.push(collection),
3453 Expr::Contains { collection, value } => {
3454 stack.push(collection);
3455 stack.push(value);
3456 }
3457 Expr::List(items) | Expr::Tuple(items) => stack.extend(items.iter().copied()),
3458 Expr::FieldAccess { object, .. } => stack.push(object),
3459 Expr::New { init_fields, .. } => {
3460 stack.extend(init_fields.iter().map(|(_, fe)| *fe));
3461 }
3462 Expr::NewVariant { fields, .. } => {
3463 stack.extend(fields.iter().map(|(_, fe)| *fe));
3464 }
3465 Expr::OptionSome { value } => stack.push(value),
3466 Expr::WithCapacity { value, capacity } => {
3467 stack.push(value);
3468 stack.push(capacity);
3469 }
3470 Expr::InterpolatedString(parts) => {
3471 for p in parts {
3472 if let StringPart::Expr { value, .. } = p {
3473 stack.push(value);
3474 }
3475 }
3476 }
3477 _ => return false,
3478 }
3479 }
3480 true
3481}
3482
3483fn binop_op(op: BinaryOpKind, dst: Reg, lhs: Reg, rhs: Reg) -> Result<Op, String> {
3484 use BinaryOpKind::*;
3485 Ok(match op {
3486 Add => Op::Add { dst, lhs, rhs },
3487 Subtract => Op::Sub { dst, lhs, rhs },
3488 Multiply => Op::Mul { dst, lhs, rhs },
3489 Divide => Op::Div { dst, lhs, rhs },
3490 ExactDivide => Op::ExactDiv { dst, lhs, rhs },
3491 FloorDivide => Op::FloorDiv { dst, lhs, rhs },
3492 Modulo => Op::Mod { dst, lhs, rhs },
3493 Lt => Op::Lt { dst, lhs, rhs },
3494 Gt => Op::Gt { dst, lhs, rhs },
3495 LtEq => Op::LtEq { dst, lhs, rhs },
3496 GtEq => Op::GtEq { dst, lhs, rhs },
3497 Eq => Op::Eq { dst, lhs, rhs },
3498 ApproxEq => Op::ApproxEq { dst, lhs, rhs },
3499 NotEq => Op::NotEq { dst, lhs, rhs },
3500 Concat => Op::Concat { dst, lhs, rhs },
3501 SeqConcat => Op::SeqConcat { dst, lhs, rhs },
3502 Pow => Op::Pow { dst, lhs, rhs },
3503 BitXor => Op::BitXor { dst, lhs, rhs },
3504 BitAnd => Op::BitAnd { dst, lhs, rhs },
3505 BitOr => Op::BitOr { dst, lhs, rhs },
3506 Shl => Op::Shl { dst, lhs, rhs },
3507 Shr => Op::Shr { dst, lhs, rhs },
3508 And | Or => return Err("vm: internal — And/Or must use compile_short_circuit".to_string()),
3510 })
3511}