Skip to main content

mimium_lang/runtime/
vm.rs

1use core::slice;
2use slotmap::{DefaultKey, SlotMap};
3use std::{cell::RefCell, cmp::Ordering, collections::HashMap, ops::Range, rc::Rc};
4pub mod bytecode;
5pub mod heap;
6pub mod program;
7mod ringbuffer;
8pub use bytecode::*;
9use ringbuffer::Ringbuffer;
10
11use program::OpenUpValue;
12pub use program::{FuncProto, Program};
13
14use crate::{
15    compiler::bytecodegen::ByteCodeGenerator,
16    interner::{Symbol, TypeNodeId},
17    plugin::{ExtClsInfo, ExtClsType, ExtFunInfo, ExtFunType, MachineFunction},
18    runtime::vm::program::WordSize,
19    types::{Type, TypeSize},
20};
21pub type RawVal = u64;
22pub type ReturnCode = i64;
23
24#[derive(Debug, Default, PartialEq)]
25struct StateStorage {
26    pos: usize,
27    rawdata: Vec<u64>,
28}
29impl StateStorage {
30    fn resize(&mut self, size: usize) {
31        self.rawdata.resize(size, 0)
32    }
33    fn get_state(&self, size: u64) -> &[RawVal] {
34        unsafe {
35            let head = self.rawdata.as_ptr().add(self.pos);
36            slice::from_raw_parts(head, size as _)
37        }
38    }
39    fn get_state_mut(&mut self, size: usize) -> &mut [RawVal] {
40        unsafe {
41            let head = self.rawdata.as_mut_ptr().add(self.pos);
42            slice::from_raw_parts_mut(head, size as _)
43        }
44    }
45    fn get_as_ringbuffer(&mut self, size_in_samples: u64) -> Ringbuffer<'_> {
46        let data_head = unsafe { self.rawdata.as_mut_ptr().add(self.pos) };
47        Ringbuffer::new(data_head, size_in_samples)
48    }
49    fn push_pos(&mut self, offset: StateOffset) {
50        self.pos = (self.pos as u64 + (std::convert::Into::<u64>::into(offset))) as usize;
51    }
52    fn pop_pos(&mut self, offset: StateOffset) {
53        self.pos = (self.pos as u64 - (std::convert::Into::<u64>::into(offset))) as usize;
54    }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub struct ClosureIdx(pub slotmap::DefaultKey);
59
60#[derive(Debug, Clone, Default)]
61struct StateStorageStack(Vec<ClosureIdx>);
62
63impl StateStorageStack {
64    pub fn push(&mut self, i: ClosureIdx) {
65        self.0.push(i)
66    }
67    pub fn pop(&mut self) {
68        let _ = self.0.pop();
69    }
70}
71
72#[derive(Debug, Clone, Default)]
73pub(crate) struct ArrayHeap {
74    elem_word_size: u64,
75    data: Vec<RawVal>,
76}
77impl ArrayHeap {
78    pub fn get_length_array(&self) -> u64 {
79        self.data.len() as u64 / self.elem_word_size
80    }
81    pub fn get_elem_word_size(&self) -> u64 {
82        self.elem_word_size
83    }
84    pub fn get_data(&self) -> &[RawVal] {
85        &self.data
86    }
87    pub fn get_data_mut(&mut self) -> &mut [RawVal] {
88        &mut self.data
89    }
90}
91#[derive(Debug, Clone, Default)]
92pub(crate) struct ArrayStorage {
93    data: SlotMap<DefaultKey, ArrayHeap>,
94}
95pub(crate) type ArrayIdx = slotmap::DefaultKey;
96impl ArrayStorage {
97    pub fn alloc_array(&mut self, len: u64, elem_size: u64) -> RawVal {
98        let array = ArrayHeap {
99            elem_word_size: elem_size,
100            data: vec![0u64; (len * elem_size) as usize],
101        };
102        let key = self.data.insert(array);
103        debug_assert!(
104            std::mem::size_of::<ArrayIdx>() == 8,
105            "ArrayIdx size must be 8 bytes"
106        );
107        unsafe { std::mem::transmute_copy::<ArrayIdx, RawVal>(&key) }
108    }
109    pub fn get_array(&self, id: RawVal) -> &ArrayHeap {
110        let key: ArrayIdx = unsafe { std::mem::transmute_copy::<RawVal, ArrayIdx>(&id) };
111        self.data.get(key).expect("Invalid ArrayIdx")
112    }
113    pub fn get_array_mut(&mut self, id: RawVal) -> &mut ArrayHeap {
114        let key: ArrayIdx = unsafe { std::mem::transmute_copy::<RawVal, ArrayIdx>(&id) };
115        self.data.get_mut(key).expect("Invalid ArrayIdx")
116    }
117}
118// Upvalues are used with Rc<RefCell<UpValue>> because it maybe shared between multiple closures
119// Maybe it will be managed with some GC mechanism in the future.
120#[derive(Debug, Clone, PartialEq)]
121enum UpValue {
122    Open(OpenUpValue),
123    Closed(Vec<RawVal>, bool),
124}
125type SharedUpValue = Rc<RefCell<UpValue>>;
126impl From<OpenUpValue> for UpValue {
127    fn from(value: OpenUpValue) -> Self {
128        Self::Open(value)
129    }
130}
131
132#[derive(Default)]
133struct LocalUpValueMap(Vec<(Reg, SharedUpValue)>);
134
135impl LocalUpValueMap {
136    pub fn get_or_insert(&mut self, ov: OpenUpValue) -> SharedUpValue {
137        let OpenUpValue { pos, .. } = ov;
138        self.0
139            .iter()
140            .find_map(|(i2, v)| (pos == *i2 as _).then_some(v.clone()))
141            .unwrap_or_else(|| {
142                let v = Rc::new(RefCell::new(UpValue::Open(ov)));
143                self.0.push((pos as Reg, v.clone()));
144                v
145            })
146    }
147}
148
149#[derive(Debug, Default, PartialEq)]
150//closure object dynamically allocated
151pub struct Closure {
152    pub fn_proto_pos: usize, //position of function prototype in global_ftable
153    pub base_ptr: u64,       //base pointer to current closure, to calculate open upvalue
154    pub is_closed: bool,
155    pub refcount: u64,
156    pub(self) upvalues: Vec<SharedUpValue>,
157    state_storage: StateStorage,
158}
159impl Closure {
160    pub(self) fn new(
161        program: &Program,
162        base_ptr: u64,
163        fn_i: usize,
164        upv_map: &mut LocalUpValueMap,
165    ) -> Self {
166        let fnproto = &program.global_fn_table[fn_i].1;
167        let upvalues = fnproto
168            .upindexes
169            .iter()
170            .map(|ov| upv_map.get_or_insert(*ov))
171            .collect::<Vec<_>>();
172        let mut state_storage = StateStorage::default();
173        state_storage.resize(fnproto.state_skeleton.total_size() as usize);
174        Self {
175            fn_proto_pos: fn_i,
176            upvalues,
177            is_closed: false,
178            refcount: 1,
179            base_ptr,
180            state_storage,
181        }
182    }
183}
184
185pub type ClosureStorage = SlotMap<DefaultKey, Closure>;
186
187#[derive(Clone, Copy, Default)]
188enum RawValType {
189    Float,
190    #[default]
191    Int,
192    // UInt,
193}
194
195#[derive(Clone, Copy)]
196enum ExtFnIdx {
197    Fun(usize),
198    Cls(usize),
199}
200/// The virtual machine that executes mimium bytecode programs.
201///
202/// A [`Machine`] holds the compiled [`Program`], value stack and all live
203/// closures. External functions and closures installed from plugins are also
204/// managed here.
205pub struct Machine {
206    // program will be modified while its execution, e.g., higher-order external closure creates its wrapper.
207    pub prog: Program,
208    stack: Vec<RawVal>,
209    base_pointer: u64,
210    pub closures: ClosureStorage, // TODO: Will be replaced by heap in later phases
211    pub heap: heap::HeapStorage,  // New unified heap storage
212    pub ext_fun_table: Vec<(Symbol, ExtFunType)>,
213    pub ext_cls_table: Vec<(Symbol, ExtClsType)>,
214    pub arrays: ArrayStorage,
215    fn_map: HashMap<usize, ExtFnIdx>, //index from fntable index of program to it of machine.
216    // cls_map: HashMap<usize, usize>, //index from fntable index of program to it of machine.
217    global_states: StateStorage,
218    states_stack: StateStorageStack,
219    delaysizes_pos_stack: Vec<usize>,
220    global_vals: Vec<RawVal>,
221    debug_stacktype: Vec<RawValType>,
222}
223
224macro_rules! binop {
225    ($op:tt,$t:ty, $dst:expr,$src1:expr,$src2:expr,$self:ident) => {
226        {
227        $self.set_stacktype($dst as i64, RawValType::Float);
228        $self.set_stack($dst as i64, Self::to_value::<$t>(
229            Self::get_as::<$t>($self.get_stack($src1 as i64))
230        $op Self::get_as::<$t>($self.get_stack($src2 as i64))))
231    }
232    };
233}
234macro_rules! binop_bool {
235    ($op:tt, $dst:expr,$src1:expr,$src2:expr,$self:ident) => {
236        {
237        $self.set_stacktype($dst as i64, RawValType::Float);
238        let bres:bool =
239            Self::get_as::<f64>($self.get_stack($src1 as i64))
240        $op Self::get_as::<f64>($self.get_stack($src2 as i64));
241        let fres = if bres{
242            1.0f64
243        }else{
244            0.0f64
245        };
246        $self.set_stack($dst as i64,Self::to_value::<f64>(fres))
247    }
248    };
249}
250macro_rules! binop_bool_compose {//for and&or
251    ($op:tt, $dst:expr,$src1:expr,$src2:expr,$self:ident) => {
252        {
253        $self.set_stacktype($dst as i64, RawValType::Float);
254        let bres:bool =
255            Self::get_as::<f64>($self.get_stack($src1 as i64))>0.0
256        $op Self::get_as::<f64>($self.get_stack($src2 as i64))>0.0;
257        let fres = if bres{ 1.0f64 }else{ 0.0f64 };
258        $self.set_stack($dst as i64,Self::to_value::<f64>(fres))
259    }
260    };
261}
262macro_rules! binopmethod {
263    ($op:ident,$t:ty, $dst:expr,$src1:expr,$src2:expr,$self:ident) => {{
264        $self.set_stacktype($dst as i64, RawValType::Float);
265        $self.set_stack(
266            $dst as i64,
267            Self::to_value::<$t>(
268                Self::get_as::<$t>($self.get_stack($src1 as i64))
269                    .$op(Self::get_as::<$t>($self.get_stack($src2 as i64))),
270            ),
271        )
272    }};
273}
274macro_rules! uniop {
275    ($op:tt,$t:ty, $dst:expr,$src:expr,$self:ident) => {
276        $self.set_stack($dst as i64,
277            Self::to_value::<$t>(
278            $op Self::get_as::<$t>($self.get_stack($src as i64))))
279    };
280}
281macro_rules! uniop_bool {
282    ($op:tt, $dst:expr,$src:expr,$self:ident) => {{
283        let bres: bool = $op(matches!(
284            Self::get_as::<f64>($self.get_stack($src as i64)).partial_cmp(&0.0),
285            Some(std::cmp::Ordering::Greater)
286        ));
287        let fres = if bres { 1.0f64 } else { 0.0f64 };
288        $self.set_stack($dst as i64, Self::to_value::<f64>(fres))
289    }};
290}
291macro_rules! uniopmethod {
292    ($op:tt,$t:ty, $dst:expr,$src:expr,$self:ident) => {{
293        $self.set_stack(
294            $dst as i64,
295            Self::to_value::<$t>(Self::get_as::<$t>($self.get_stack($src as i64)).$op()),
296        )
297    }};
298}
299
300fn set_vec<T>(vec: &mut Vec<T>, i: usize, value: T)
301where
302    T: Clone + std::default::Default,
303{
304    match i.cmp(&vec.len()) {
305        Ordering::Less => vec[i] = value,
306        Ordering::Equal => vec.push(value),
307        Ordering::Greater => {
308            vec.resize(i, T::default());
309            vec.push(value);
310        }
311    }
312}
313fn set_vec_range<T>(vec: &mut Vec<T>, i: usize, values: &[T])
314where
315    T: std::fmt::Debug + Copy + std::default::Default,
316{
317    //do not use copy_from_slice  or extend_from_slice because the ptr range may overwrap,
318    // and copy_from_slice use ptr::copy_nonoverwrapping internally.
319    // vec[range].copy_from_slice(values)
320    let start = i;
321    let end = i + values.len();
322    if end > vec.len() {
323        vec.resize(i, T::default());
324    }
325    match start.cmp(&vec.len()) {
326        Ordering::Less => {
327            let range = i..(i + values.len());
328            for (v, i) in values.iter().zip(range.into_iter()) {
329                vec[i] = *v;
330            }
331        }
332        Ordering::Equal => values.iter().for_each(|v| vec.push(*v)),
333        Ordering::Greater => values.iter().for_each(|v| vec.push(*v)),
334    }
335}
336
337impl Machine {
338    /// Drop a closure by decrementing its reference count.
339    /// When refcount reaches 0, recursively drops captured closures and removes the closure.
340    pub fn drop_closure(&mut self, id: ClosureIdx) {
341        let cls = self.closures.get_mut(id.0).unwrap();
342        cls.refcount -= 1;
343        if cls.refcount == 0 {
344            let refs = self
345                .closures
346                .get_mut(id.0)
347                .unwrap()
348                .upvalues
349                .iter()
350                .map(|v| {
351                    let v = v.borrow();
352                    match &*v {
353                        UpValue::Closed(data, true) => {
354                            // Closure-typed upvalue: value is a HeapIdx
355                            let heap_idx = Self::get_as::<heap::HeapIdx>(data[0]);
356                            self.heap.get(heap_idx).and_then(|obj| {
357                                (!obj.data.is_empty())
358                                    .then_some((heap_idx, Self::get_as::<ClosureIdx>(obj.data[0])))
359                            })
360                        }
361                        _ => None,
362                    }
363                })
364                .collect::<Vec<_>>();
365            refs.iter().filter_map(|i| *i).for_each(|(heap_idx, clsi)| {
366                self.drop_closure(clsi);
367                heap::heap_release(&mut self.heap, heap_idx);
368            });
369            self.closures.remove(id.0);
370        }
371    }
372
373    /// Create a new VM from a compiled [`Program`] and external functions.
374    pub fn new(
375        prog: Program,
376        extfns: impl Iterator<Item = ExtFunInfo>,
377        extcls: impl Iterator<Item = Box<dyn MachineFunction>>,
378    ) -> Self {
379        let mut res = Self {
380            prog,
381            stack: vec![],
382            base_pointer: 0,
383            closures: Default::default(),
384            heap: Default::default(),
385            ext_fun_table: vec![],
386            ext_cls_table: vec![],
387            fn_map: HashMap::new(),
388            // cls_map: HashMap::new(),
389            arrays: ArrayStorage::default(),
390            global_states: Default::default(),
391            states_stack: Default::default(),
392            delaysizes_pos_stack: vec![0],
393            global_vals: vec![],
394            debug_stacktype: vec![RawValType::Int; 255],
395        };
396        extfns.for_each(|ExtFunInfo { name, fun, .. }| {
397            let _ = res.install_extern_fn(name, fun);
398        });
399        extcls.for_each(|machine_function| {
400            let _ = res.install_extern_cls(machine_function.get_name(), machine_function.get_fn());
401        });
402        res.link_functions();
403        res
404    }
405    /// Create a new VM instance with the new program, preserving the current state as possible.
406    pub fn new_resume(&self, prog: Program) -> Self {
407        let mut new_vm = Self {
408            prog,
409            stack: vec![],
410            base_pointer: 0,
411            closures: Default::default(),
412            heap: Default::default(),
413            ext_fun_table: vec![],
414            ext_cls_table: vec![],
415            fn_map: HashMap::new(),
416            // cls_map: HashMap::new(),
417            arrays: ArrayStorage::default(),
418            global_states: Default::default(),
419            states_stack: Default::default(),
420            delaysizes_pos_stack: vec![0],
421            global_vals: vec![],
422            debug_stacktype: vec![RawValType::Int; 255],
423        };
424        //expect there are no change changes in external function use for now
425
426        new_vm.ext_fun_table = self.ext_fun_table.clone();
427        new_vm.ext_cls_table = self.ext_cls_table.clone();
428        new_vm.global_vals = self.global_vals.clone();
429        new_vm.arrays = self.arrays.clone();
430
431        let new_state = state_tree::update_state_storage(
432            &self.global_states.rawdata,
433            self.prog
434                .get_dsp_state_skeleton()
435                .cloned()
436                .expect("dsp function not found"),
437            new_vm
438                .prog
439                .get_dsp_state_skeleton()
440                .cloned()
441                .expect("dsp function not found"),
442        );
443        match new_state {
444            Ok(Some(s)) => {
445                new_vm.global_states.rawdata = s;
446            }
447            Ok(None) => {
448                log::info!("No state structure change detected. Just copies buffer");
449                new_vm.global_states.rawdata = self.global_states.rawdata.clone();
450            }
451            Err(e) => {
452                log::error!("Failed to migrate global state: {e}");
453            }
454        }
455        new_vm.link_functions();
456        new_vm.execute_main();
457        new_vm
458    }
459    pub fn clear_stack(&mut self) {
460        self.stack.fill(0);
461    }
462    pub fn get_stack(&self, offset: i64) -> RawVal {
463        // unsafe {
464        //     *self
465        //         .stack
466        //         .get_unchecked((self.base_pointer + offset as u64) as usize)
467        // }
468        self.get_stack_range(offset, 1).1[0]
469    }
470    pub fn get_stack_range(&self, offset: i64, word_size: TypeSize) -> (Range<usize>, &[RawVal]) {
471        let addr_start = self.base_pointer as usize + offset as usize;
472        let addr_end = addr_start + word_size as usize;
473        let start = self.stack.as_slice().as_ptr();
474        let slice = unsafe {
475            // w/ unstable feature
476            // let (_,snd) = self.stack.as_slice().split_at_unchecked(offset as usize);
477            // snd.split_at_unchecked(n as usize)
478            let vstart = start.add(addr_start);
479            slice::from_raw_parts(vstart, word_size as usize)
480        };
481        (addr_start..addr_end, slice)
482    }
483    pub fn get_stack_range_mut(
484        &mut self,
485        offset: i64,
486        word_size: TypeSize,
487    ) -> (Range<usize>, &mut [RawVal]) {
488        let addr_start = self.base_pointer as usize + offset as usize;
489        let addr_end = addr_start + word_size as usize;
490        let start = self.stack.as_mut_ptr();
491        let slice = unsafe {
492            // w/ unstable feature
493            // let (_,snd) = self.stack.as_slice().split_at_unchecked(offset as usize);
494            // snd.split_at_unchecked(n as usize)
495            let vstart = start.add(addr_start);
496            slice::from_raw_parts_mut(vstart, word_size as usize)
497        };
498        (addr_start..addr_end, slice)
499    }
500    pub fn set_stack(&mut self, offset: i64, v: RawVal) {
501        self.set_stack_range(offset, &[v])
502    }
503    pub fn set_stack_range(&mut self, offset: i64, vs: &[RawVal]) {
504        // debug_assert!(!v.is_null());
505        // debug_assert!(v.is_aligned());
506        // let vs = unsafe { slice::from_raw_parts(v, size) };
507        set_vec_range(
508            &mut self.stack,
509            (self.base_pointer as i64 + offset) as usize,
510            vs,
511        )
512    }
513    fn move_stack_range(&mut self, offset: i64, srcrange: Range<usize>) {
514        let dest = (self.base_pointer as i64 + offset) as usize;
515        if srcrange.end > self.stack.len() {
516            self.stack.resize(srcrange.end, 0);
517        }
518        let dest_end = dest + (srcrange.end - srcrange.start);
519        if dest_end > self.stack.len() {
520            self.stack.resize(dest_end, 0);
521        }
522        self.stack.copy_within(srcrange, dest)
523    }
524    fn set_stacktype(&mut self, offset: i64, t: RawValType) {
525        // set_vec(
526        //     &mut self.debug_stacktype,
527        //     (self.base_pointer as i64 + offset) as usize,
528        //     t,
529        // );
530    }
531    pub fn get_top_n(&self, n: usize) -> &[RawVal] {
532        let len = self.stack.len();
533        &self.stack[(len - n)..]
534    }
535    /// Extract the [`ClosureIdx`] stored inside a heap-allocated closure object.
536    ///
537    /// During the migration period the heap object wraps a single `ClosureIdx`
538    /// in `data[0]`.  External callers (e.g. the scheduler plugin) can use this
539    /// to obtain the underlying closure index from a `HeapIdx` value that lives
540    /// on the VM stack.
541    pub fn get_closure_idx_from_heap(&self, heap_idx: heap::HeapIdx) -> ClosureIdx {
542        let heap_obj = self.heap.get(heap_idx).expect("Invalid HeapIdx");
543        Self::get_as::<ClosureIdx>(heap_obj.data[0])
544    }
545    fn get_upvalue_offset(upper_base: usize, offset: OpenUpValue) -> usize {
546        upper_base + offset.pos
547    }
548    pub fn get_open_upvalue(
549        &self,
550        upper_base: usize,
551        ov: OpenUpValue,
552    ) -> (Range<usize>, &[RawVal]) {
553        let OpenUpValue { size, .. } = ov;
554        // log::trace!("upper base:{}, upvalue:{}", upper_base, offset);
555        let abs_pos = Self::get_upvalue_offset(upper_base, ov);
556        let end = abs_pos + size as usize;
557        let slice = unsafe {
558            let vstart = self.stack.as_slice().as_ptr().add(abs_pos);
559            slice::from_raw_parts(vstart, size as usize)
560        };
561        (abs_pos..end, slice)
562    }
563    pub fn get_closure(&self, idx: ClosureIdx) -> &Closure {
564        debug_assert!(
565            self.closures.contains_key(idx.0),
566            "Invalid Closure Id referred"
567        );
568        unsafe { self.closures.get_unchecked(idx.0) }
569    }
570    pub(crate) fn get_closure_mut(&mut self, idx: ClosureIdx) -> &mut Closure {
571        debug_assert!(
572            self.closures.contains_key(idx.0),
573            "Invalid Closure Id referred"
574        );
575        unsafe { self.closures.get_unchecked_mut(idx.0) }
576    }
577    fn get_current_state(&mut self) -> &mut StateStorage {
578        if self.states_stack.0.is_empty() {
579            &mut self.global_states
580        } else {
581            let idx = unsafe { self.states_stack.0.last().unwrap_unchecked() };
582            &mut self.get_closure_mut(*idx).state_storage
583        }
584    }
585    fn return_general(&mut self, iret: Reg, nret: Reg) -> &[u64] {
586        let base = self.base_pointer as usize;
587        let iret_abs = base + iret as usize;
588        self.stack
589            .copy_within(iret_abs..(iret_abs + nret as usize), base - 1);
590        // clean up temporary variables to ensure that `nret`
591        // at the top of the stack is the return value
592        self.stack.truncate(base - 1 + nret as usize);
593
594        (self.stack.split_at(base).1) as _
595    }
596
597    pub fn get_as<T>(v: RawVal) -> T {
598        unsafe { std::mem::transmute_copy::<RawVal, T>(&v) }
599    }
600    pub fn get_as_array<T>(v: &[RawVal]) -> &[T] {
601        unsafe { std::mem::transmute::<&[RawVal], &[T]>(v) }
602    }
603    pub fn to_value<T>(v: T) -> RawVal {
604        assert_eq!(std::mem::size_of::<T>(), 8);
605        unsafe { std::mem::transmute_copy::<T, RawVal>(&v) }
606    }
607    fn call_function<F>(
608        &mut self,
609        func_pos: u8,
610        _nargs: u8,
611        nret_req: u8,
612        mut action: F,
613    ) -> ReturnCode
614    where
615        F: FnMut(&mut Self) -> ReturnCode,
616    {
617        let offset = (func_pos + 1) as u64;
618        self.delaysizes_pos_stack.push(0);
619        self.base_pointer += offset;
620        let nret = action(self);
621
622        if nret_req > nret as u8 {
623            panic!("invalid number of return value {nret_req} required but accepts only {nret}.");
624        }
625        // shrink stack so as to match with number of return values
626        self.stack
627            .truncate((self.base_pointer as i64 + nret_req as i64) as usize);
628        self.base_pointer -= offset;
629        self.delaysizes_pos_stack.pop();
630        nret
631    }
632    fn allocate_closure(&mut self, fn_i: usize, upv_map: &mut LocalUpValueMap) -> ClosureIdx {
633        let idx = self
634            .closures
635            .insert(Closure::new(&self.prog, self.base_pointer, fn_i, upv_map));
636        ClosureIdx(idx)
637    }
638
639    /// Allocate a closure on the heap storage (Phase 4 implementation)
640    /// Returns a HeapIdx that can be stored in a register
641    fn allocate_heap_closure(
642        &mut self,
643        fn_i: usize,
644        upv_map: &mut LocalUpValueMap,
645    ) -> heap::HeapIdx {
646        // For now, create a traditional closure and store its index in the heap
647        // TODO: Eventually migrate to storing closure data directly in heap
648        let closure_idx = self.allocate_closure(fn_i, upv_map);
649
650        // Create a heap object containing the ClosureIdx
651        // Layout: [closure_idx_as_raw_val]
652        let heap_obj = heap::HeapObject::with_data(vec![Self::to_value(closure_idx)]);
653        let heap_idx = self.heap.insert(heap_obj);
654
655        log::trace!(
656            "allocate_heap_closure: fn_i={fn_i}, heap_idx={heap_idx:?}, closure_idx={closure_idx:?}"
657        );
658        heap_idx
659    }
660
661    /// Release a heap-based closure.
662    ///
663    /// Decrements the HeapObject reference count via `heap_release`.
664    /// If the underlying closure has not escaped (`is_closed == false`),
665    /// also drops the closure via the normal refcount mechanism.
666    fn release_heap_closure(&mut self, heap_idx: heap::HeapIdx) {
667        log::trace!("release_heap_closure: heap_idx={heap_idx:?}");
668
669        // Extract the ClosureIdx before we do anything that mutably borrows heap.
670        let maybe_closure = self.heap.get(heap_idx).and_then(|obj| {
671            (!obj.data.is_empty()).then_some(Self::get_as::<ClosureIdx>(obj.data[0]))
672        });
673
674        if let Some(closure_idx) = maybe_closure {
675            if !self.get_closure(closure_idx).is_closed {
676                self.drop_closure(closure_idx);
677            }
678        }
679
680        // Always decrement the HeapObject refcount.
681        // If refcount reaches 0 the wrapper is freed.
682        // For escaped closures whose HeapIdx was CloneHeap'd before return,
683        // the refcount will still be > 0 after this release.
684        heap::heap_release(&mut self.heap, heap_idx);
685    }
686
687    /// Close upvalues of a heap-based closure (does not release the heap object)
688    fn close_heap_upvalues(&mut self, heap_idx: heap::HeapIdx) {
689        log::trace!("close_heap_upvalues: heap_idx={heap_idx:?}");
690
691        // Extract the ClosureIdx and close its upvalues
692        if let Some(heap_obj) = self.heap.get(heap_idx) {
693            if !heap_obj.data.is_empty() {
694                let closure_idx = Self::get_as::<ClosureIdx>(heap_obj.data[0]);
695                // Close upvalues directly by ClosureIdx without corrupting the stack
696                self.close_upvalues_by_idx(closure_idx);
697            }
698        }
699    }
700
701    /// Release all heap-based closures tracked by the current scope.
702    ///
703    /// Each entry in `local_heap_closures` was either:
704    /// - created by `MakeHeapClosure` (initial refcount=1), or
705    /// - cloned by `CloneHeap` (refcount was incremented and entry added).
706    ///
707    /// Calling `release_heap_closure` decrements the refcount.  Objects whose
708    /// refcount reaches 0 are freed; those that were `CloneHeap`'d before return
709    /// will survive with a positive refcount.
710    fn release_heap_closures(&mut self, local_heap_closures: &[heap::HeapIdx]) {
711        for &heap_idx in local_heap_closures {
712            self.release_heap_closure(heap_idx);
713        }
714    }
715
716    /// This API is used for defining higher-order external function that returns some external rust closure.
717    /// Because the native closure cannot be called with CallCls directly, the vm appends an additional function the program,
718    /// that wraps external closure call with an internal closure.
719    pub fn wrap_extern_cls(&mut self, extcls: ExtClsInfo) -> ClosureIdx {
720        let ExtClsInfo { name, fun, ty } = extcls;
721
722        self.prog.ext_fun_table.push((name.to_string(), ty));
723        let prog_funid = self.prog.ext_fun_table.len() - 1;
724        self.ext_cls_table.push((name, fun));
725        let vm_clsid = self.ext_cls_table.len() - 1;
726        self.fn_map.insert(prog_funid, ExtFnIdx::Cls(vm_clsid));
727        let (bytecodes, nargs, nret) = if let Type::Function { arg, ret } = ty.to_type() {
728            let mut wrap_bytecode = Vec::<Instruction>::new();
729            // todo: decouple bytecode generator dependency
730            let asize = ByteCodeGenerator::word_size_for_type(arg);
731            // if there are 2 arguments of float for instance, base pointer should be 2
732            let nargs = match arg.to_type() {
733                Type::Tuple(args) => args.len(),
734                Type::Record(fields) => fields.len(),
735                _ => unreachable!("single argument should be 1 element record"),
736            } as u8;
737            let base = nargs as u8;
738            let nret = ByteCodeGenerator::word_size_for_type(ret);
739            wrap_bytecode.push(Instruction::MoveConst(base, 0));
740            wrap_bytecode.push(Instruction::MoveRange(base + 1, 0, asize));
741
742            wrap_bytecode.extend_from_slice(&[
743                Instruction::CallExtFun(base, nargs, nret as _),
744                Instruction::Return(base, nret as _),
745            ]);
746            (wrap_bytecode, nargs, nret)
747        } else {
748            panic!("non-function type called for wrapping external closure");
749        };
750        let newfunc = FuncProto {
751            nparam: nargs as _,
752            nret: nret as _,
753            bytecodes,
754            constants: vec![prog_funid as _],
755            ..Default::default()
756        };
757        self.prog.global_fn_table.push((name.to_string(), newfunc));
758        let fn_i = self.prog.global_fn_table.len() - 1;
759        let mut cls = Closure::new(
760            &self.prog,
761            self.base_pointer,
762            fn_i,
763            &mut LocalUpValueMap(vec![]),
764        );
765        // wrapper closure will not be released automatically.
766        cls.is_closed = true;
767        let idx = self.closures.insert(cls);
768        ClosureIdx(idx)
769    }
770    fn close_upvalues(&mut self, src: Reg) {
771        let clsidx = Self::get_as::<ClosureIdx>(self.get_stack(src as _));
772        self.close_upvalues_by_idx(clsidx);
773    }
774    /// Close all open upvalues of the given closure, copying stack values into
775    /// the upvalue cells so the closure can outlive the current stack frame.
776    fn close_upvalues_by_idx(&mut self, clsidx: ClosureIdx) {
777        let closure_base_ptr = self.get_closure(clsidx).base_ptr as usize;
778
779        // Collect (ClosureIdx-to-retain, Option<HeapIdx-to-retain>) pairs for
780        // each upvalue that holds a closure reference.
781        let refs = self
782            .get_closure(clsidx)
783            .upvalues
784            .iter()
785            .map(|upv| {
786                let upv = &mut *upv.borrow_mut();
787                match upv {
788                    UpValue::Open(ov) => {
789                        let (_range, ov_raw) = self.get_open_upvalue(closure_base_ptr, *ov);
790                        let is_closure = ov.is_closure;
791                        *upv = UpValue::Closed(ov_raw.to_vec(), is_closure);
792                        if is_closure {
793                            // The raw value is a HeapIdx wrapping a ClosureIdx
794                            let heap_idx = Self::get_as::<heap::HeapIdx>(ov_raw[0]);
795                            Some(heap_idx)
796                        } else {
797                            None
798                        }
799                    }
800                    UpValue::Closed(v, is_closure) => {
801                        if *is_closure {
802                            Some(Self::get_as::<heap::HeapIdx>(v[0]))
803                        } else {
804                            None
805                        }
806                    }
807                }
808            })
809            .collect::<Vec<_>>();
810        // Retain each captured HeapObject and increment the underlying Closure refcount
811        refs.iter().for_each(|heap_opt| {
812            if let Some(heap_idx) = heap_opt {
813                // Retain the HeapObject so it is not freed when its creator exits
814                heap::heap_retain(&mut self.heap, *heap_idx);
815                // Also bump the underlying Closure refcount for the existing drop_closure chain
816                if let Some(heap_obj) = self.heap.get(*heap_idx) {
817                    if !heap_obj.data.is_empty() {
818                        let ci = Self::get_as::<ClosureIdx>(heap_obj.data[0]);
819                        self.get_closure_mut(ci).refcount += 1;
820                    }
821                }
822            }
823        });
824        let cls = self.get_closure_mut(clsidx);
825        cls.is_closed = true;
826    }
827    fn release_open_closures(&mut self, local_closures: &[ClosureIdx]) {
828        for clsidx in local_closures.iter() {
829            let cls = self.get_closure(*clsidx);
830            if !cls.is_closed {
831                // log::debug!("release {:?}", clsidx);
832                self.drop_closure(*clsidx)
833            }
834        }
835    }
836    fn get_fnproto(&self, func_i: usize) -> &FuncProto {
837        &self.prog.global_fn_table[func_i].1
838    }
839    /// Execute a function within the VM.
840    ///
841    /// `func_i` is an index into the program's function table and `cls_i` is an
842    /// optional closure that provides the environment for the call.
843    /// The returned [`ReturnCode`] is the number of values pushed on the stack
844    /// as a result of the call.
845    pub fn execute(&mut self, func_i: usize, cls_i: Option<ClosureIdx>) -> ReturnCode {
846        let mut local_closures: Vec<ClosureIdx> = vec![];
847        let mut local_heap_closures: Vec<heap::HeapIdx> = vec![];
848        let mut upv_map = LocalUpValueMap::default();
849        let mut pcounter = 0;
850        // if cfg!(test) {
851        //     log::trace!("{:?}", func);
852        // }
853
854        loop {
855            // if cfg!(debug_assertions) && log::max_level() >= log::Level::Trace {
856            //     let mut line = String::new();
857            //     line += &format!("{: <20} {}", func.bytecodes[pcounter], ": [");
858            //     for i in 0..self.stack.len() {
859            //         if i == self.base_pointer as usize {
860            //             line += "!";
861            //         }
862            //         line += &match self.debug_stacktype[i] {
863            //             RawValType::Float => format!("{0:.5}f", Self::get_as::<f64>(self.stack[i])),
864            //             RawValType::Int => format!("{0:.5}i", Self::get_as::<i64>(self.stack[i])),
865            //             RawValType::UInt => format!("{0:.5}u", Self::get_as::<u64>(self.stack[i])),
866            //         };
867            //         if i < self.stack.len() - 1 {
868            //             line += ",";
869            //         }
870            //     }
871            //     line += "]";
872            //     log::trace!("{line}");
873            // }
874            let mut increment = 1;
875            match self.get_fnproto(func_i).bytecodes[pcounter] {
876                Instruction::Move(dst, src) => {
877                    self.set_stack(dst as i64, self.get_stack(src as i64));
878                }
879                Instruction::MoveConst(dst, pos) => {
880                    self.set_stack(dst as i64, self.get_fnproto(func_i).constants[pos as usize]);
881                }
882                Instruction::MoveImmF(dst, v) => {
883                    self.set_stack(dst as i64, Self::to_value(Into::<f64>::into(v)));
884                }
885                Instruction::MoveRange(dst, src, n) => {
886                    let (range, _slice) = self.get_stack_range(src as _, n);
887                    self.move_stack_range(dst as i64, range);
888                }
889                Instruction::CallCls(func, nargs, nret_req) => {
890                    let addr = self.get_stack(func as i64);
891                    let cls_i = Self::get_as::<ClosureIdx>(addr);
892                    let cls = self.get_closure(cls_i);
893                    let pos_of_f = cls.fn_proto_pos;
894                    self.states_stack.push(cls_i);
895                    self.call_function(func, nargs, nret_req, move |machine| {
896                        machine.execute(pos_of_f, Some(cls_i))
897                    });
898                    self.states_stack.pop();
899                }
900                Instruction::Call(func, nargs, nret_req) => {
901                    let pos_of_f = Self::get_as::<usize>(self.get_stack(func as i64));
902                    self.call_function(func, nargs, nret_req, move |machine| {
903                        machine.execute(pos_of_f, None)
904                    });
905                }
906                Instruction::CallExtFun(func, nargs, nret_req) => {
907                    let ext_fn_idx = self.get_stack(func as i64) as usize;
908                    let fidx = self.fn_map.get(&ext_fn_idx).unwrap();
909                    let nret = match fidx {
910                        ExtFnIdx::Fun(fi) => {
911                            let f = self.ext_fun_table[*fi].1;
912                            self.call_function(func, nargs, nret_req, f)
913                        }
914                        ExtFnIdx::Cls(ci) => {
915                            let (_name, cls) = &self.ext_cls_table[*ci];
916                            let cls = cls.clone();
917                            self.call_function(func, nargs, nret_req, move |machine| {
918                                cls.borrow_mut()(machine)
919                            })
920                        }
921                    };
922
923                    // return
924                    let base = self.base_pointer as usize;
925                    let iret = base + func as usize + 1;
926                    self.stack
927                        .copy_within(iret..(iret + nret as usize), base + func as usize);
928                    self.stack.truncate(base + func as usize + nret as usize);
929                }
930                Instruction::Closure(dst, fn_index) => {
931                    let fn_proto_pos = self.get_stack(fn_index as i64) as usize;
932                    let vaddr = self.allocate_closure(fn_proto_pos, &mut upv_map);
933                    local_closures.push(vaddr);
934                    self.set_stack(dst as i64, Self::to_value(vaddr));
935                }
936                Instruction::Close(src) => {
937                    self.close_upvalues(src);
938                }
939                // New heap-based instructions (Phase 4)
940                Instruction::MakeHeapClosure(dst, fn_index, _size) => {
941                    let fn_proto_pos = self.get_stack(fn_index as i64) as usize;
942                    let heap_idx = self.allocate_heap_closure(fn_proto_pos, &mut upv_map);
943                    local_heap_closures.push(heap_idx);
944                    // Store the heap index (not closure index) in the register
945                    self.set_stack(dst as i64, Self::to_value(heap_idx));
946                }
947                Instruction::CloseHeapClosure(src) => {
948                    let heap_addr = self.get_stack(src as i64);
949                    let heap_idx = Self::get_as::<heap::HeapIdx>(heap_addr);
950                    self.close_heap_upvalues(heap_idx);
951                }
952                Instruction::CloneHeap(src) => {
953                    let heap_addr = self.get_stack(src as i64);
954                    let heap_idx = Self::get_as::<heap::HeapIdx>(heap_addr);
955                    heap::heap_retain(&mut self.heap, heap_idx);
956                }
957                Instruction::BoxAlloc(dst, src, inner_size) => {
958                    // Allocate a heap object and copy data from stack
959                    let (_, src_data) = self.get_stack_range(src as i64, inner_size);
960                    let data = src_data.to_vec();
961                    let heap_idx = self.heap.insert(heap::HeapObject::with_data(data));
962                    self.set_stack(dst as i64, Self::to_value(heap_idx));
963                }
964                Instruction::BoxLoad(dst, src, inner_size) => {
965                    // Load data from heap to stack
966                    let heap_addr = self.get_stack(src as i64);
967                    let heap_idx = Self::get_as::<heap::HeapIdx>(heap_addr);
968                    let heap_obj = self
969                        .heap
970                        .get(heap_idx)
971                        .expect("BoxLoad: invalid heap index");
972                    let data: Vec<u64> = heap_obj.data[..inner_size as usize].to_vec();
973                    self.set_stack_range(dst as i64, &data);
974                }
975                Instruction::BoxClone(src) => {
976                    let heap_addr = self.get_stack(src as i64);
977                    let heap_idx = Self::get_as::<heap::HeapIdx>(heap_addr);
978                    heap::heap_retain(&mut self.heap, heap_idx);
979                }
980                Instruction::BoxRelease(src) => {
981                    let heap_addr = self.get_stack(src as i64);
982                    let heap_idx = Self::get_as::<heap::HeapIdx>(heap_addr);
983                    heap::heap_release(&mut self.heap, heap_idx);
984                }
985                Instruction::BoxStore(ptr_reg, src, inner_size) => {
986                    let heap_addr = self.get_stack(ptr_reg as i64);
987                    let heap_idx = Self::get_as::<heap::HeapIdx>(heap_addr);
988                    let (_, src_data) = self.get_stack_range(src as i64, inner_size);
989                    let data = src_data.to_vec();
990                    let heap_obj = self
991                        .heap
992                        .get_mut(heap_idx)
993                        .expect("BoxStore: invalid heap index");
994                    heap_obj.data[..inner_size as usize].copy_from_slice(&data);
995                }
996                Instruction::CloneUserSum(value_reg, value_size, type_idx) => {
997                    let (_, value_data) = self.get_stack_range(value_reg as i64, value_size);
998                    let value_vec = value_data.to_vec();
999                    let ty = self
1000                        .prog
1001                        .get_type_from_table(type_idx)
1002                        .expect("Invalid type table index in CloneUserSum");
1003                    Self::clone_usersum_recursive(&value_vec, &ty, &mut self.heap);
1004                }
1005                Instruction::ReleaseUserSum(value_reg, value_size, type_idx) => {
1006                    let (_, value_data) = self.get_stack_range(value_reg as i64, value_size);
1007                    let value_vec = value_data.to_vec();
1008                    let ty = self
1009                        .prog
1010                        .get_type_from_table(type_idx)
1011                        .expect("Invalid type table index in ReleaseUserSum");
1012                    let tt = &self.prog.type_table;
1013                    Self::release_usersum_recursive(&value_vec, &ty, &mut self.heap, tt);
1014                }
1015                Instruction::CallIndirect(func, nargs, nret_req) => {
1016                    // Get heap index from the register
1017                    let heap_addr = self.get_stack(func as i64);
1018                    let heap_idx = Self::get_as::<heap::HeapIdx>(heap_addr);
1019
1020                    // Extract the ClosureIdx from the heap object
1021                    let heap_obj = self.heap.get(heap_idx).expect("Invalid heap index");
1022                    let cls_i = Self::get_as::<ClosureIdx>(heap_obj.data[0]);
1023
1024                    let cls = self.get_closure(cls_i);
1025                    let pos_of_f = cls.fn_proto_pos;
1026                    self.states_stack.push(cls_i);
1027                    self.call_function(func, nargs, nret_req, move |machine| {
1028                        machine.execute(pos_of_f, Some(cls_i))
1029                    });
1030                    self.states_stack.pop();
1031                }
1032                Instruction::Return0 => {
1033                    self.stack.truncate((self.base_pointer - 1) as usize);
1034                    self.release_open_closures(&local_closures);
1035                    self.release_heap_closures(&local_heap_closures);
1036                    return 0;
1037                }
1038                Instruction::Return(iret, nret) => {
1039                    let _ = self.return_general(iret, nret);
1040                    self.release_open_closures(&local_closures);
1041                    self.release_heap_closures(&local_heap_closures);
1042                    return nret.into();
1043                }
1044                Instruction::GetUpValue(dst, index, _size) => {
1045                    {
1046                        let up_i = cls_i.unwrap();
1047                        let cls = self.get_closure(up_i);
1048                        let upvalues = &cls.upvalues;
1049                        let rv = &upvalues[index as usize];
1050                        let vs = match &*rv.borrow() {
1051                            UpValue::Open(i) => {
1052                                let upper_base = cls.base_ptr as usize;
1053                                let (_range, rawv) = self.get_open_upvalue(upper_base, *i);
1054                                let rawv: &[RawVal] = unsafe { std::mem::transmute(rawv) };
1055                                rawv
1056                            }
1057                            UpValue::Closed(rawval, _) => {
1058                                let rawv: &[RawVal] =
1059                                    unsafe { std::mem::transmute(rawval.as_slice()) };
1060                                rawv
1061                            }
1062                        };
1063                        self.set_stack_range(dst as i64, vs);
1064                    };
1065                }
1066                Instruction::SetUpValue(index, src, size) => {
1067                    let up_i = cls_i.unwrap();
1068                    let cls = self.get_closure(up_i);
1069                    let upper_base = cls.base_ptr as usize;
1070                    let upvalues = &cls.upvalues;
1071                    let (_range, v) = self.get_stack_range(src as i64, size);
1072                    let rv = &mut *upvalues[index as usize].borrow_mut();
1073                    match rv {
1074                        UpValue::Open(OpenUpValue { pos: i, size, .. }) => {
1075                            let (range, _v) = self.get_stack_range(src as i64, *size);
1076                            let dest = upper_base + *i;
1077                            unsafe {
1078                                //force borrow because closure cell and stack never collisions
1079                                let dst = slice::from_raw_parts_mut(
1080                                    std::mem::transmute::<*const RawVal, *mut RawVal>(
1081                                        self.stack.as_ptr(),
1082                                    ),
1083                                    self.stack.len(),
1084                                );
1085                                dst.copy_within(range, dest);
1086                            }
1087                        }
1088                        UpValue::Closed(uv, _) => {
1089                            uv.as_mut_slice().copy_from_slice(v);
1090                        }
1091                    };
1092                }
1093                Instruction::GetGlobal(dst, gid, size) => {
1094                    let gvs = unsafe {
1095                        let vstart = self.global_vals.as_ptr().offset(gid as _);
1096                        debug_assert!(!vstart.is_null());
1097                        // debug_assert!(vstart.is_aligned());
1098                        slice::from_raw_parts(vstart, size as _)
1099                    };
1100                    self.set_stack_range(dst as i64, gvs)
1101                }
1102                Instruction::SetGlobal(gid, src, size) => {
1103                    let gvs = unsafe {
1104                        let vstart = self.global_vals.as_mut_ptr().offset(gid as _);
1105                        debug_assert!(!vstart.is_null());
1106                        // debug_assert!(vstart.is_aligned());
1107                        slice::from_raw_parts_mut(vstart, size as _)
1108                    };
1109                    let (_, slice) = self.get_stack_range(src as i64, size);
1110                    gvs.copy_from_slice(slice);
1111                }
1112                Instruction::Jmp(offset) => {
1113                    increment = offset;
1114                }
1115                Instruction::JmpIfNeg(cond, offset) => {
1116                    let cond_v = self.get_stack(cond as i64);
1117                    if Self::get_as::<f64>(cond_v) <= 0.0 {
1118                        increment = offset;
1119                    }
1120                }
1121                Instruction::JmpTable(scrut, table_idx) => {
1122                    let scrut_val = self.get_stack(scrut as i64);
1123                    // Scrutinee is always an integer: either a tag from union or cast from float
1124                    let val = Self::get_as::<i64>(scrut_val);
1125                    let fn_proto = self.get_fnproto(func_i);
1126                    debug_assert!(
1127                        !fn_proto.jump_tables.is_empty(),
1128                        "JmpTable instruction requires non-empty jump_tables"
1129                    );
1130                    let table = &fn_proto.jump_tables[table_idx as usize];
1131                    let idx = (val - table.min) as usize;
1132                    // Last element of offsets is the default for out-of-range values
1133                    let default_idx = table.offsets.len() - 1;
1134                    increment = table
1135                        .offsets
1136                        .get(idx)
1137                        .copied()
1138                        .unwrap_or(table.offsets[default_idx]);
1139                }
1140                Instruction::AddF(dst, src1, src2) => binop!(+,f64,dst,src1,src2,self),
1141                Instruction::SubF(dst, src1, src2) => {
1142                    binop!(-,f64,dst,src1,src2,self)
1143                }
1144                Instruction::MulF(dst, src1, src2) => binop!(*,f64,dst,src1,src2,self),
1145                Instruction::DivF(dst, src1, src2) => binop!(/,f64,dst,src1,src2,self),
1146                Instruction::ModF(dst, src1, src2) => binop!(%,f64,dst,src1,src2,self),
1147                Instruction::NegF(dst, src) => uniop!(-,f64,dst,src,self),
1148                Instruction::AbsF(dst, src) => uniopmethod!(abs, f64, dst, src, self),
1149                Instruction::SqrtF(dst, src) => uniopmethod!(sqrt, f64, dst, src, self),
1150                Instruction::SinF(dst, src) => uniopmethod!(sin, f64, dst, src, self),
1151                Instruction::CosF(dst, src) => uniopmethod!(cos, f64, dst, src, self),
1152                Instruction::PowF(dst, src1, src2) => {
1153                    binopmethod!(powf, f64, dst, src1, src2, self)
1154                }
1155                Instruction::LogF(dst, src) => uniopmethod!(ln, f64, dst, src, self),
1156                Instruction::AddI(dst, src1, src2) => binop!(+,i64,dst,src1,src2,self),
1157                Instruction::SubI(dst, src1, src2) => binop!(-,i64,dst,src1,src2,self),
1158                Instruction::MulI(dst, src1, src2) => binop!(*,i64,dst,src1,src2,self),
1159                Instruction::DivI(dst, src1, src2) => binop!(/,i64,dst,src1,src2,self),
1160                Instruction::ModI(dst, src1, src2) => binop!(%,i64,dst,src1,src2,self),
1161                Instruction::NegI(dst, src) => uniop!(-,i64,dst,src,self),
1162                Instruction::AbsI(dst, src) => uniopmethod!(abs, i64, dst, src, self),
1163                Instruction::PowI(dst, lhs, rhs) => binop!(^,i64,dst,lhs,rhs,self),
1164                Instruction::LogI(_, _, _) => todo!(),
1165                Instruction::Not(dst, src) => uniop_bool!(!, dst, src, self),
1166                Instruction::Eq(dst, src1, src2) => binop_bool!(==,dst,src1,src2,self),
1167                Instruction::Ne(dst, src1, src2) => binop_bool!(!=,dst,src1,src2,self),
1168                Instruction::Gt(dst, src1, src2) => binop_bool!(>,dst,src1,src2,self),
1169                Instruction::Ge(dst, src1, src2) => binop_bool!(>=,dst,src1,src2,self),
1170                Instruction::Lt(dst, src1, src2) => binop_bool!(<,dst,src1,src2,self),
1171                Instruction::Le(dst, src1, src2) => binop_bool!(<=,dst,src1,src2,self),
1172                Instruction::And(dst, src1, src2) => binop_bool_compose!(&&,dst,src1,src2,self),
1173                Instruction::Or(dst, src1, src2) => binop_bool_compose!(||,dst,src1,src2,self),
1174                Instruction::CastFtoI(dst, src) => self.set_stack(
1175                    dst as i64,
1176                    Self::to_value::<i64>(Self::get_as::<f64>(self.get_stack(src as i64)) as i64),
1177                ),
1178                Instruction::CastItoF(dst, src) => self.set_stack(
1179                    dst as i64,
1180                    Self::to_value::<f64>(Self::get_as::<i64>(self.get_stack(src as i64)) as f64),
1181                ),
1182                Instruction::CastItoB(dst, src) => self.set_stack(
1183                    dst as i64,
1184                    Self::to_value::<bool>(Self::get_as::<i64>(self.get_stack(src as i64)) != 0),
1185                ),
1186                Instruction::AllocArray(dst, len, elem_size) => {
1187                    // Allocate an array of the given length and element size
1188                    let key = self.arrays.alloc_array(len as _, elem_size as _);
1189                    // Set the stack to point to the start of the new array
1190                    self.set_stack(dst as i64, key);
1191                }
1192                Instruction::GetArrayElem(dst, arr, idx) => {
1193                    // Get the array and index values
1194                    let array = self.get_stack(arr as i64);
1195                    let index = self.get_stack(idx as i64);
1196                    let index_val = Self::get_as::<f64>(index);
1197                    let adata = self.arrays.get_array(array);
1198                    let elem_word_size = adata.elem_word_size as usize;
1199                    let buffer = unsafe {
1200                        let address = adata
1201                            .data
1202                            .as_ptr()
1203                            .wrapping_add(index_val as usize * elem_word_size);
1204                        std::slice::from_raw_parts(address, elem_word_size)
1205                    };
1206                    set_vec_range(
1207                        &mut self.stack,
1208                        (self.base_pointer + dst as u64) as usize,
1209                        buffer,
1210                    );
1211                    // todo: implement automatic interpolation and out-of-bounds handling for primitive arrays.
1212                }
1213                Instruction::SetArrayElem(arr, idx, val) => {
1214                    // Get the array, index, and value
1215                    let array = self.get_stack(arr as i64);
1216                    let index = self.get_stack(idx as i64);
1217                    let index_val = Self::get_as::<f64>(index);
1218                    let index_int = index_val as usize;
1219                    let adata = self.arrays.get_array_mut(array);
1220                    let elem_word_size = adata.elem_word_size as usize;
1221                    let buffer = unsafe {
1222                        let address = adata
1223                            .data
1224                            .as_mut_ptr()
1225                            .wrapping_add(index_int * elem_word_size);
1226                        std::slice::from_raw_parts_mut(address, elem_word_size)
1227                    };
1228                    let (_range, buf_src) = self.get_stack_range(val as _, elem_word_size as _);
1229                    buffer.copy_from_slice(buf_src);
1230                }
1231                Instruction::GetState(dst, size) => {
1232                    //force borrow because state storage and stack never collisions
1233                    let v: &[RawVal] = unsafe {
1234                        std::mem::transmute(self.get_current_state().get_state(size as _))
1235                    };
1236                    self.set_stack_range(dst as i64, v);
1237                }
1238                Instruction::SetState(src, size) => {
1239                    let vs = {
1240                        let (_range, v) = self.get_stack_range(src as i64, size as _);
1241                        unsafe { std::mem::transmute::<&[RawVal], &[RawVal]>(v) }
1242                    };
1243                    let dst = self.get_current_state().get_state_mut(size as _);
1244                    dst.copy_from_slice(vs);
1245                }
1246                Instruction::PushStatePos(v) => self.get_current_state().push_pos(v),
1247                Instruction::PopStatePos(v) => self.get_current_state().pop_pos(v),
1248                Instruction::Delay(dst, src, time) => {
1249                    let i = self.get_stack(src as i64);
1250                    let t = self.get_stack(time as i64);
1251                    let delaysize_i =
1252                        unsafe { self.delaysizes_pos_stack.last().unwrap_unchecked() };
1253
1254                    let size_in_samples = unsafe {
1255                        *self
1256                            .get_fnproto(func_i)
1257                            .delay_sizes
1258                            .get_unchecked(*delaysize_i)
1259                    };
1260                    let mut ringbuf = self.get_current_state().get_as_ringbuffer(size_in_samples);
1261
1262                    let res = ringbuf.process(i, t);
1263                    self.set_stack(dst as i64, res);
1264                }
1265                Instruction::Mem(dst, src) => {
1266                    let s = self.get_stack(src as i64);
1267                    let ptr = self.get_current_state().get_state_mut(1);
1268                    let v = Self::to_value(ptr[0]);
1269                    self.set_stack(dst as i64, v);
1270                    let ptr = self.get_current_state().get_state_mut(1);
1271                    ptr[0] = s;
1272                }
1273                Instruction::Dummy => {
1274                    unreachable!()
1275                }
1276            }
1277            pcounter = (pcounter as i64 + increment as i64) as usize;
1278        }
1279    }
1280    pub fn install_extern_fn(&mut self, name: Symbol, f: ExtFunType) -> usize {
1281        self.ext_fun_table.push((name, f));
1282        self.ext_fun_table.len() - 1
1283    }
1284    pub fn install_extern_cls(&mut self, name: Symbol, f: ExtClsType) -> usize {
1285        self.ext_cls_table.push((name, f));
1286        self.ext_cls_table.len() - 1
1287    }
1288
1289    fn link_functions(&mut self) {
1290        //link external functions
1291        let global_mem_size = self
1292            .prog
1293            .global_vals
1294            .iter()
1295            .map(|WordSize(size)| *size as usize)
1296            .sum();
1297        self.global_vals = vec![0; global_mem_size];
1298        self.prog
1299            .ext_fun_table
1300            .iter_mut()
1301            .enumerate()
1302            .for_each(|(i, (name, _ty))| {
1303                if let Some((j, _)) = self
1304                    .ext_fun_table
1305                    .iter()
1306                    .enumerate()
1307                    .find(|(_j, (fname, _fn))| name == fname.as_str())
1308                {
1309                    let _ = self.fn_map.insert(i, ExtFnIdx::Fun(j));
1310                } else if let Some((j, _)) = self
1311                    .ext_cls_table
1312                    .iter()
1313                    .enumerate()
1314                    .find(|(_j, (fname, _fn))| name == fname.as_str())
1315                {
1316                    let _ = self.fn_map.insert(i, ExtFnIdx::Cls(j));
1317                } else {
1318                    panic!("external function {name} cannot be found");
1319                }
1320            });
1321    }
1322    pub fn execute_idx(&mut self, idx: usize) -> ReturnCode {
1323        let (_name, func) = &self.prog.global_fn_table[idx];
1324        if !func.bytecodes.is_empty() {
1325            self.global_states
1326                .resize(func.state_skeleton.total_size() as usize);
1327            // 0 is always base pointer to the main function
1328            if !self.stack.is_empty() {
1329                self.stack[0] = 0;
1330            }
1331            self.base_pointer = 1;
1332            self.execute(idx, None)
1333        } else {
1334            0
1335        }
1336    }
1337    pub fn execute_entry(&mut self, entry: &str) -> ReturnCode {
1338        if let Some(idx) = self.prog.get_fun_index(entry) {
1339            self.execute_idx(idx)
1340        } else {
1341            -1
1342        }
1343    }
1344
1345    /// Recursively clone all boxed references within a UserSum value.
1346    /// This is called at runtime to walk the value structure and increment
1347    /// reference counts for any heap-allocated objects within variant payloads.
1348    fn clone_usersum_recursive(
1349        value_data: &[RawVal],
1350        ty: &TypeNodeId,
1351        heap: &mut heap::HeapStorage,
1352    ) {
1353        use crate::types::Type;
1354        match ty.to_type() {
1355            Type::Boxed(_) => {
1356                // Found a boxed pointer - increment its reference count
1357                if !value_data.is_empty() {
1358                    let heap_idx = Self::get_as::<heap::HeapIdx>(value_data[0]);
1359                    heap::heap_retain(heap, heap_idx);
1360                }
1361            }
1362            Type::UserSum { variants, .. } => {
1363                // UserSum value structure: [tag (1 word)] [payload (variable size)]
1364                if value_data.is_empty() {
1365                    return;
1366                }
1367                let tag = value_data[0] as usize;
1368                if tag >= variants.len() {
1369                    return;
1370                }
1371                // Get the payload type for this variant
1372                if let Some(payload_ty) = variants[tag].1 {
1373                    // Recursively clone the payload starting at offset 1 (after tag)
1374                    Self::clone_usersum_recursive(&value_data[1..], &payload_ty, heap);
1375                }
1376            }
1377            Type::Tuple(elem_types) => {
1378                // For tuples, recursively clone each element
1379                let mut offset = 0;
1380                for elem_ty in elem_types {
1381                    let elem_size = elem_ty.word_size() as usize;
1382                    if offset + elem_size <= value_data.len() {
1383                        Self::clone_usersum_recursive(
1384                            &value_data[offset..offset + elem_size],
1385                            &elem_ty,
1386                            heap,
1387                        );
1388                    }
1389                    offset += elem_size;
1390                }
1391            }
1392            Type::Record(fields) => {
1393                // For records, recursively clone each field
1394                let mut offset = 0;
1395                for field in fields {
1396                    let field_size = field.ty.word_size() as usize;
1397                    if offset + field_size <= value_data.len() {
1398                        Self::clone_usersum_recursive(
1399                            &value_data[offset..offset + field_size],
1400                            &field.ty,
1401                            heap,
1402                        );
1403                    }
1404                    offset += field_size;
1405                }
1406            }
1407            Type::TypeAlias(_name) => {
1408                // In recursive type definitions, TypeAlias represents a self-reference
1409                // that was wrapped in Boxed at the outer level but appears unboxed in
1410                // the pre-boxing variant payload. The runtime value is a HeapIdx.
1411                if !value_data.is_empty() {
1412                    let heap_idx = Self::get_as::<heap::HeapIdx>(value_data[0]);
1413                    heap::heap_retain(heap, heap_idx);
1414                }
1415            }
1416            _ => {
1417                // Primitive types don't need cloning
1418            }
1419        }
1420    }
1421
1422    /// Recursively release all boxed references within a UserSum value.
1423    /// This is called at runtime when a UserSum value goes out of scope.
1424    /// It walks the value structure and decrements reference counts for any heap-allocated objects.
1425    fn release_usersum_recursive(
1426        value_data: &[RawVal],
1427        ty: &TypeNodeId,
1428        heap: &mut heap::HeapStorage,
1429        type_table: &[TypeNodeId],
1430    ) {
1431        use crate::types::Type;
1432        match ty.to_type() {
1433            Type::Boxed(inner) => {
1434                // Found a boxed pointer - decrement its reference count.
1435                // If refcount reaches 0, recursively release inner values before freeing.
1436                if !value_data.is_empty() {
1437                    let heap_idx = Self::get_as::<heap::HeapIdx>(value_data[0]);
1438                    let should_release_inner = heap
1439                        .get(heap_idx)
1440                        .map(|obj| obj.refcount <= 1)
1441                        .unwrap_or(false);
1442                    if should_release_inner {
1443                        // Before freeing, walk the inner data and release nested boxed values
1444                        let inner_data = heap
1445                            .get(heap_idx)
1446                            .map(|obj| obj.data.clone())
1447                            .unwrap_or_default();
1448                        Self::release_usersum_recursive(&inner_data, &inner, heap, type_table);
1449                    }
1450                    heap::heap_release(heap, heap_idx);
1451                }
1452            }
1453            Type::UserSum { variants, .. } => {
1454                // UserSum value structure: [tag (1 word)] [payload (variable size)]
1455                if value_data.is_empty() {
1456                    return;
1457                }
1458                let tag = value_data[0] as usize;
1459                if tag >= variants.len() {
1460                    return;
1461                }
1462                // Get the payload type for this variant
1463                if let Some(payload_ty) = variants[tag].1 {
1464                    // Recursively release the payload starting at offset 1 (after tag)
1465                    Self::release_usersum_recursive(
1466                        &value_data[1..],
1467                        &payload_ty,
1468                        heap,
1469                        type_table,
1470                    );
1471                }
1472            }
1473            Type::Tuple(elem_types) => {
1474                // For tuples, recursively release each element
1475                let mut offset = 0;
1476                for elem_ty in elem_types {
1477                    let elem_size = elem_ty.word_size() as usize;
1478                    if offset + elem_size <= value_data.len() {
1479                        Self::release_usersum_recursive(
1480                            &value_data[offset..offset + elem_size],
1481                            &elem_ty,
1482                            heap,
1483                            type_table,
1484                        );
1485                    }
1486                    offset += elem_size;
1487                }
1488            }
1489            Type::Record(fields) => {
1490                // For records, recursively release each field
1491                let mut offset = 0;
1492                for field in fields {
1493                    let field_size = field.ty.word_size() as usize;
1494                    if offset + field_size <= value_data.len() {
1495                        Self::release_usersum_recursive(
1496                            &value_data[offset..offset + field_size],
1497                            &field.ty,
1498                            heap,
1499                            type_table,
1500                        );
1501                    }
1502                    offset += field_size;
1503                }
1504            }
1505            Type::TypeAlias(name) => {
1506                // In recursive type definitions, TypeAlias represents a self-reference
1507                // that was wrapped in Boxed at the outer level but appears unboxed in
1508                // the pre-boxing variant payload. The runtime value is a HeapIdx.
1509                // Find the post-boxing UserSum type in the type_table for cascading release.
1510                if !value_data.is_empty() {
1511                    let heap_idx = Self::get_as::<heap::HeapIdx>(value_data[0]);
1512                    // Look up the resolved UserSum type from the type_table
1513                    let resolved_sum = type_table.iter().find(
1514                        |tid| matches!(tid.to_type(), Type::UserSum { name: n, .. } if n == name),
1515                    );
1516                    let should_release_inner = heap
1517                        .get(heap_idx)
1518                        .map(|obj| obj.refcount <= 1)
1519                        .unwrap_or(false);
1520                    if should_release_inner {
1521                        if let Some(inner_ty) = resolved_sum {
1522                            let inner_data = heap
1523                                .get(heap_idx)
1524                                .map(|obj| obj.data.clone())
1525                                .unwrap_or_default();
1526                            Self::release_usersum_recursive(
1527                                &inner_data,
1528                                inner_ty,
1529                                heap,
1530                                type_table,
1531                            );
1532                        }
1533                    }
1534                    heap::heap_release(heap, heap_idx);
1535                }
1536            }
1537            _ => {
1538                // Primitive types don't need releasing
1539            }
1540        }
1541    }
1542
1543    pub fn execute_main(&mut self) -> ReturnCode {
1544        // 0 is always base pointer to the main function
1545        self.base_pointer += 1;
1546        self.execute(0, None)
1547    }
1548}
1549
1550#[cfg(test)]
1551mod test;