Skip to main content

mimium_lang/runtime/vm/
bytecode.rs

1use crate::{interner::TypeNodeId, types::TypeSize, utils::half_float::HFloat};
2
3pub type Reg = u8; // register position
4pub type ConstPos = u16;
5pub type GlobalPos = u8;
6pub type Offset = i16;
7pub type ShotrOffset = i8;
8pub type TypeTableIndex = u8;
9//24bit unsigned integer for shiftsate
10pub type StateOffset = intx::U24;
11/// Instructions for bytecode. Currently, each instructon has the 64 bit size(Tag, up to 3 bytes arguments.)
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub enum Instruction {
14    /// Move Single Value, Destination, Source
15    Move(Reg, Reg),
16    /// Load Single Value from Constants. Destination, Source
17    MoveConst(Reg, ConstPos),
18    /// Load Immediate float from half precision. Destination, Value
19    MoveImmF(Reg, HFloat),
20    // Move the range of registers (e.g. tuple) Destination, Source, Wordsize
21    MoveRange(Reg, Reg, TypeSize),
22    /// Call to internal function
23    /// Function Address,Nargs,Word Size of Return Value
24    Call(Reg, u8, TypeSize),
25    //call internal closure
26    CallCls(Reg, u8, TypeSize),
27    /// Call external rust functions or closure,
28    /// Currently, The execution branches into the invocation of a raw function item(function pointer), or Rust Closure depending on the information on the VM.
29    /// Previously there was another operation `CallExtCls` for Rust Closure Invocation separately but keeping distinction between raw function pointer and closure at the type checking and program generation stage, makes compiler's design complex thus it was removed.
30    /// The distincion may also be resolved statically at linking time.
31    /// Function Address,Nargs,Nret
32    CallExtFun(Reg, u8, TypeSize),
33    /// Create new closure. Destination, index of inner function prototype in global function table.
34    Closure(Reg, Reg),
35    /// register of the closure to be closed. other local closures will be released with this instruction.
36    Close(Reg),
37
38    // New heap-based closure instructions (Phase 3)
39    /// Create a closure on the heap. Destination, Function Index, Closure Size
40    /// The closure data is allocated on the heap and a HeapIdx is stored in the destination register.
41    MakeHeapClosure(Reg, Reg, TypeSize),
42    /// Close upvalues of a heap-based closure. Address Register (HeapIdx)
43    CloseHeapClosure(Reg),
44    /// Increment reference count of a heap object and register it for release at scope exit.
45    /// Used for call-by-value cloning of heap-allocated closures.
46    CloneHeap(Reg),
47    /// Call a closure indirectly through heap storage. Function register (HeapIdx), nargs, nret
48    CallIndirect(Reg, u8, TypeSize),
49
50    /// Box a value by allocating it on the heap.
51    /// BoxAlloc(destination_for_HeapIdx, source_value, inner_word_size)
52    /// Allocates a heap object of inner_word_size, copies from source, stores HeapIdx in destination.
53    BoxAlloc(Reg, Reg, TypeSize),
54    /// Unbox a heap-allocated value by loading from heap.
55    /// BoxLoad(destination, source_HeapIdx, inner_word_size)
56    /// Reads inner_word_size words from heap and stores to destination registers.
57    BoxLoad(Reg, Reg, TypeSize),
58    /// Increment the reference count of a boxed heap object.
59    /// BoxClone(heap_ptr_reg)
60    BoxClone(Reg),
61    /// Decrement the reference count of a boxed heap object and free if count reaches 0.
62    /// BoxRelease(heap_ptr_reg)
63    BoxRelease(Reg),
64    /// Write a value into an already-allocated heap object (destructive update).
65    /// BoxStore(heap_ptr_reg, src_reg, inner_word_size)
66    BoxStore(Reg, Reg, TypeSize),
67    /// Clone all boxed references within a UserSum value.
68    /// CloneUserSum(value_reg, value_size, type_table_index)
69    /// The type_table_index is used to look up TypeNodeId from Program.type_table.
70    CloneUserSum(Reg, TypeSize, TypeTableIndex),
71    /// Release all boxed references within a UserSum value.
72    /// ReleaseUserSum(value_reg, value_size, type_table_index)
73    /// The type_table_index is used to look up TypeNodeId from Program.type_table.
74    ReleaseUserSum(Reg, TypeSize, TypeTableIndex),
75
76    /// destination,source, size
77    GetUpValue(Reg, Reg, TypeSize),
78    SetUpValue(Reg, Reg, TypeSize),
79
80    /// destination,source
81    GetGlobal(Reg, GlobalPos, TypeSize),
82    SetGlobal(GlobalPos, Reg, TypeSize),
83    /// Call internal state over time, destination,source
84    GetState(Reg, TypeSize),
85    SetState(Reg, TypeSize),
86    PushStatePos(StateOffset),
87    PopStatePos(StateOffset),
88
89    /// Return from current function without return value.
90    Return0,
91    /// value start position, Nrets
92    Return(Reg, TypeSize),
93    //dst,src,time,idx
94    Delay(Reg, Reg, Reg),
95    Mem(Reg, Reg),
96
97    /// jump to instruction over the offset.
98    Jmp(Offset),
99    /// jump to instruction over the offset if the value in the first argument was negative.
100    JmpIfNeg(Reg, Offset),
101    /// Jump table for switch/match expressions.
102    /// (scrutinee_reg, table_index)
103    /// Looks up the scrutinee value in the jump table and jumps to the corresponding offset.
104    /// If not found, jumps to the default offset stored in the jump table.
105    JmpTable(Reg, u8),
106
107    /// Primitive Operations.
108    /// Destination, Src1, Src2
109    AddF(Reg, Reg, Reg),
110    SubF(Reg, Reg, Reg),
111    MulF(Reg, Reg, Reg),
112    DivF(Reg, Reg, Reg),
113    ModF(Reg, Reg, Reg),
114    NegF(Reg, Reg),
115    AbsF(Reg, Reg),
116    SqrtF(Reg, Reg),
117    SinF(Reg, Reg),
118    CosF(Reg, Reg),
119    PowF(Reg, Reg, Reg),
120    LogF(Reg, Reg),
121
122    // Primitive Operations for int
123    AddI(Reg, Reg, Reg),
124    SubI(Reg, Reg, Reg),
125    MulI(Reg, Reg, Reg),
126    DivI(Reg, Reg, Reg),
127    ModI(Reg, Reg, Reg),
128    NegI(Reg, Reg),
129    AbsI(Reg, Reg),
130
131    PowI(Reg, Reg, Reg),
132    LogI(Reg, Reg, Reg),
133    // primitive Operations for bool
134    Not(Reg, Reg),
135    Eq(Reg, Reg, Reg),
136    Ne(Reg, Reg, Reg),
137    Gt(Reg, Reg, Reg),
138    Ge(Reg, Reg, Reg),
139    Lt(Reg, Reg, Reg),
140    Le(Reg, Reg, Reg),
141    And(Reg, Reg, Reg),
142    Or(Reg, Reg, Reg),
143
144    CastFtoI(Reg, Reg),
145    CastItoF(Reg, Reg),
146    CastItoB(Reg, Reg),
147    /// Allocate new array in the heap.
148    /// Destination, size of array, type size of each element
149    AllocArray(Reg, Reg, TypeSize),
150    /// Get array element: destination, array, index
151    GetArrayElem(Reg, Reg, Reg),
152    /// Set array element. Because the array is immutable, this instruction is mostly used for initialization.
153    /// array, index, value.
154    SetArrayElem(Reg, Reg, Reg),
155
156    /// Dummy instruction for testing
157    Dummy,
158}
159
160impl std::fmt::Display for Instruction {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        match self {
163            Instruction::Return0 => write!(f, "ret0"),
164            Instruction::Jmp(dst) => write!(f, "{:<10} {}", "jmp", dst),
165            Instruction::GetState(dst, size) => write!(f, "{:<10} {} {}", "getstate", dst, size),
166            Instruction::SetState(src, size) => write!(f, "{:<10} {} {}", "setstate", src, size),
167            Instruction::PushStatePos(v) => write!(f, "{:<10} {}", "pushsttpos", v),
168            Instruction::PopStatePos(v) => write!(f, "{:<10} {}", "popsttpos", v),
169            Instruction::Move(dst, src) => write!(f, "{:<10} {} {}", "mov", dst, src),
170            Instruction::MoveConst(dst, num) => write!(f, "{:<10} {} {}", "movc", dst, num),
171            Instruction::MoveImmF(dst, v) => write!(f, "{:<10} {} {}", "movimmF", dst, v),
172            Instruction::MoveRange(dst, src, n) => {
173                write!(
174                    f,
175                    "{:<10} {}-{} {}-{}",
176                    "mov",
177                    dst,
178                    dst + n - 1,
179                    src,
180                    src + n - 1
181                )
182            }
183            Instruction::Closure(dst, src) => {
184                write!(f, "{:<10} {} {}", "closure", dst, src)
185            }
186            Instruction::Close(src) => {
187                write!(f, "{:<10} {}", "close", src)
188            }
189            // New heap-based instructions (Phase 3)
190            Instruction::MakeHeapClosure(dst, fn_idx, size) => {
191                write!(f, "{:<10} {} {} {}", "mkheapcls", dst, fn_idx, size)
192            }
193            Instruction::CloseHeapClosure(addr) => {
194                write!(f, "{:<10} {}", "clsheapcls", addr)
195            }
196            Instruction::CloneHeap(addr) => {
197                write!(f, "{:<10} {}", "cloneheap", addr)
198            }
199            Instruction::CallIndirect(func, nargs, nret_req) => {
200                write!(f, "{:<10} {} {} {}", "callind", func, nargs, nret_req)
201            }
202            Instruction::BoxAlloc(dst, src, size) => {
203                write!(f, "{:<10} {} {} {}", "boxalloc", dst, src, size)
204            }
205            Instruction::BoxLoad(dst, src, size) => {
206                write!(f, "{:<10} {} {} {}", "boxload", dst, src, size)
207            }
208            Instruction::BoxClone(src) => {
209                write!(f, "{:<10} {}", "boxclone", src)
210            }
211            Instruction::BoxRelease(src) => {
212                write!(f, "{:<10} {}", "boxrelease", src)
213            }
214            Instruction::BoxStore(dst, src, size) => {
215                write!(f, "{:<10} {} {} {}", "boxstore", dst, src, size)
216            }
217            Instruction::CloneUserSum(src, size, type_idx) => {
218                write!(
219                    f,
220                    "{:<10} {} {} type_idx:{}",
221                    "clone_usersum", src, size, type_idx
222                )
223            }
224            Instruction::ReleaseUserSum(src, size, type_idx) => {
225                write!(
226                    f,
227                    "{:<10} {} {} type_idx:{}",
228                    "release_usersum", src, size, type_idx
229                )
230            }
231            Instruction::Delay(dst, src, time) => {
232                write!(f, "{:<10} {} {} {}", "delay", dst, src, time)
233            }
234            Instruction::Mem(dst, src) => {
235                write!(f, "{:<10} {} {}", "mem", dst, src)
236            }
237            Instruction::Return(iret, nret) => write!(f, "{:<10} {} {}", "ret", iret, nret),
238            Instruction::GetUpValue(dst, srcup, size) => {
239                write!(f, "{:<10} {} {} {}", "getupv", dst, srcup, size)
240            }
241            Instruction::SetUpValue(dstup, src, size) => {
242                write!(f, "{:<10} {} {} {}", "setupv", dstup, src, size)
243            }
244            Instruction::GetGlobal(dst, src, size) => {
245                write!(f, "{:<10} {} {} {}", "getglobal", dst, src, size)
246            }
247            Instruction::SetGlobal(dst, src, size) => {
248                write!(f, "{:<10} {} {} {}", "setglobal", dst, src, size)
249            }
250            Instruction::JmpIfNeg(dst, cond) => write!(f, "{:<10} {} {}", "jmpifneg", dst, cond),
251            Instruction::JmpTable(scrut, table_idx) => {
252                write!(f, "{:<10} {} {}", "jmptable", scrut, table_idx)
253            }
254            Instruction::AbsF(dst, src) => write!(f, "{:<10} {} {}", "absf", dst, src),
255            Instruction::NegF(dst, src) => write!(f, "{:<10} {} {}", "negf", dst, src),
256            Instruction::SinF(dst, src) => write!(f, "{:<10} {} {}", "sin", dst, src),
257            Instruction::CosF(dst, src) => write!(f, "{:<10} {} {}", "cos", dst, src),
258            Instruction::SqrtF(dst, src) => write!(f, "{:<10} {} {}", "sqrt", dst, src),
259            Instruction::LogF(dst, src) => write!(f, "{:<10} {} {} ", "logf", dst, src),
260            Instruction::AbsI(dst, src) => write!(f, "{:<10} {} {}", "abs", dst, src),
261            Instruction::NegI(dst, src) => write!(f, "{:<10} {} {}", "neg", dst, src),
262            Instruction::Not(dst, src) => write!(f, "{:<10} {} {}", "not", dst, src),
263            Instruction::CastFtoI(dst, src) => write!(f, "{:<10} {} {}", "f2i", dst, src),
264            Instruction::CastItoF(dst, src) => write!(f, "{:<10} {} {}", "i2f", dst, src),
265            Instruction::CastItoB(dst, src) => write!(f, "{:<10} {} {}", "i2b", dst, src),
266            Instruction::Call(func, nargs, nret_req) => {
267                write!(f, "{:<10} {} {} {}", "call", func, nargs, nret_req)
268            }
269            Instruction::CallCls(func, nargs, nret_req) => {
270                write!(f, "{:<10} {} {} {}", "callcls", func, nargs, nret_req)
271            }
272            Instruction::CallExtFun(func, nargs, nret_req) => {
273                write!(f, "{:<10} {} {} {}", "callext", func, nargs, nret_req)
274            }
275            Instruction::LogI(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "logi", dst, lhs, rhs),
276            Instruction::PowI(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "powi", dst, lhs, rhs),
277            Instruction::AddF(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "addf", dst, lhs, rhs),
278            Instruction::SubF(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "subf", dst, lhs, rhs),
279            Instruction::MulF(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "mulf", dst, lhs, rhs),
280            Instruction::DivF(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "divf", dst, lhs, rhs),
281            Instruction::ModF(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "modf", dst, lhs, rhs),
282            Instruction::PowF(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "powf", dst, lhs, rhs),
283            Instruction::AddI(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "add", dst, lhs, rhs),
284            Instruction::SubI(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "sub", dst, lhs, rhs),
285            Instruction::MulI(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "mul", dst, lhs, rhs),
286            Instruction::DivI(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "div", dst, lhs, rhs),
287            Instruction::ModI(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "mod", dst, lhs, rhs),
288            Instruction::Eq(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "eq", dst, lhs, rhs),
289            Instruction::Ne(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "ne", dst, lhs, rhs),
290            Instruction::Gt(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "gt", dst, lhs, rhs),
291            Instruction::Ge(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "ge", dst, lhs, rhs),
292            Instruction::Lt(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "le", dst, lhs, rhs),
293            Instruction::Le(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "lt", dst, lhs, rhs),
294            Instruction::And(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "and", dst, lhs, rhs),
295            Instruction::Or(dst, lhs, rhs) => write!(f, "{:<10} {} {} {}", "or", dst, lhs, rhs),
296            Instruction::AllocArray(dst, size, typesize) => {
297                write!(f, "{:<10} {} {} {}", "array", dst, size, typesize)
298            }
299            Instruction::GetArrayElem(dst, arr, idx) => {
300                write!(f, "{:<10} {} {} {}", "getarray", dst, arr, idx)
301            }
302            Instruction::SetArrayElem(arr, idx, val) => {
303                write!(f, "{:<10} {} {} {}", "setarray", arr, idx, val)
304            }
305            Instruction::Dummy => write!(f, "dummy"),
306        }
307    }
308}
309
310#[cfg(test)]
311#[test]
312fn ensure_bytecode_size() {
313    let size = std::mem::size_of::<Instruction>();
314    assert_eq!(4, size);
315}