Skip to main content

runmat_vm/bytecode/
instr.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct StackEffect {
5    pub pops: usize,
6    pub pushes: usize,
7}
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub enum EmitLabel {
11    Ans,
12    Var(usize),
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub enum EndExpr {
17    End,
18    Const(f64),
19    Var(usize),
20    Call(String, Vec<EndExpr>),
21    Add(Box<EndExpr>, Box<EndExpr>),
22    Sub(Box<EndExpr>, Box<EndExpr>),
23    Mul(Box<EndExpr>, Box<EndExpr>),
24    Div(Box<EndExpr>, Box<EndExpr>),
25    LeftDiv(Box<EndExpr>, Box<EndExpr>),
26    Pow(Box<EndExpr>, Box<EndExpr>),
27    Neg(Box<EndExpr>),
28    Pos(Box<EndExpr>),
29    Floor(Box<EndExpr>),
30    Ceil(Box<EndExpr>),
31    Round(Box<EndExpr>),
32    Fix(Box<EndExpr>),
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub enum Instr {
37    // Constant and variable loads.
38    LoadConst(f64),
39    LoadComplex(f64, f64),
40    LoadBool(bool),
41    LoadString(String),
42    LoadCharRow(String),
43    LoadVar(usize),
44    StoreVar(usize),
45
46    // Scalar and matrix arithmetic.
47    Add,
48    Sub,
49    Mul,
50    RightDiv,
51    LeftDiv,
52    Pow,
53    Neg,
54    UPlus,
55    Transpose,
56    ConjugateTranspose,
57    ElemMul,
58    ElemDiv,
59    ElemPow,
60    ElemLeftDiv,
61    LessEqual,
62    Less,
63    Greater,
64    GreaterEqual,
65    Equal,
66    NotEqual,
67
68    // Short-circuit logical control flow.
69    AndAnd(usize),
70    OrOr(usize),
71    JumpIfFalse(usize),
72    Jump(usize),
73    Pop,
74
75    // Expands a single value into N outputs, padding with zero values when needed.
76    Unpack(usize),
77
78    // Direct builtin invocation.
79    CallBuiltin(String, usize),
80
81    // Specialized lowering target for the stochastic evolution fast path.
82    StochasticEvolution,
83
84    // Array construction and direct indexing.
85    CreateMatrix(usize, usize),
86    CreateMatrixDynamic(usize),
87    CreateRange(bool),
88    Index(usize),
89
90    // Slice indexing with compiler-encoded colon and plain `end` masks.
91    IndexSlice(usize, usize, u32, u32),
92
93    // General slice/index path carrying dynamic ranges and `end` arithmetic.
94    IndexSliceExpr {
95        dims: usize,
96        numeric_count: usize,
97        colon_mask: u32,
98        end_mask: u32,
99        range_dims: Vec<usize>,
100        range_has_step: Vec<bool>,
101        range_start_exprs: Vec<Option<EndExpr>>,
102        range_step_exprs: Vec<Option<EndExpr>>,
103        range_end_exprs: Vec<EndExpr>,
104        end_numeric_exprs: Vec<(usize, EndExpr)>,
105    },
106
107    // Assignment counterpart to `IndexSliceExpr`.
108    StoreSliceExpr {
109        dims: usize,
110        numeric_count: usize,
111        colon_mask: u32,
112        end_mask: u32,
113        range_dims: Vec<usize>,
114        range_has_step: Vec<bool>,
115        range_start_exprs: Vec<Option<EndExpr>>,
116        range_step_exprs: Vec<Option<EndExpr>>,
117        range_end_exprs: Vec<EndExpr>,
118        end_numeric_exprs: Vec<(usize, EndExpr)>,
119    },
120
121    // Cell array construction and indexing.
122    CreateCell2D(usize, usize),
123    IndexCell(usize),
124
125    // Expands cell contents into a comma-separated list with fixed output arity.
126    IndexCellExpand(usize, usize),
127
128    // Indexed assignment updates the base value and pushes the updated base.
129    StoreIndex(usize),
130    StoreIndexCell(usize),
131
132    // Slice assignment with compiler-encoded colon and plain `end` masks.
133    StoreSlice(usize, usize, u32, u32),
134
135    // Struct, object, and class member access.
136    LoadMember(String),
137    LoadMemberOrInit(String),
138    LoadMemberDynamic,
139    LoadMemberDynamicOrInit,
140    StoreMember(String),
141    StoreMemberOrInit(String),
142    StoreMemberDynamic,
143    StoreMemberDynamicOrInit,
144    LoadMethod(String),
145    CallMethod(String, usize),
146
147    // Ambiguous `obj.name(...)` shape resolved at runtime as method call or member indexing.
148    CallMethodOrMemberIndex(String, usize),
149
150    // Closure and static class dispatch.
151    CreateClosure(String, usize),
152    LoadStaticProperty(String, String),
153    CallStaticMethod(String, String, usize),
154
155    // Registers a runtime class definition produced by `classdef` lowering.
156    RegisterClass {
157        name: String,
158        super_class: Option<String>,
159        properties: Vec<(String, bool, String, String)>,
160        methods: Vec<(String, String, bool, String)>,
161    },
162
163    // `feval` keeps the callable value on the stack instead of naming the target statically.
164    CallFeval(usize),
165    CallFevalExpandMulti(Vec<ArgSpec>),
166
167    // Stack and exception-control operations.
168    Swap,
169    EnterTry(usize, Option<usize>),
170    PopTry,
171    Return,
172    ReturnValue,
173
174    // User-function invocation variants.
175    CallFunction(String, usize),
176
177    // Calls a user function and shapes the result list to `out_count`.
178    CallFunctionMulti(String, usize, usize),
179
180    // Expands one argument position from cell indexing at runtime.
181    CallFunctionExpandAt(String, usize, usize, usize),
182    CallBuiltinExpandLast(String, usize, usize),
183    CallBuiltinExpandAt(String, usize, usize, usize),
184    CallFunctionExpandMulti(String, Vec<ArgSpec>),
185    CallBuiltinExpandMulti(String, Vec<ArgSpec>),
186
187    // Packs the top N values into row or column tensor form.
188    PackToRow(usize),
189    PackToCol(usize),
190
191    // Local scope and local variable access.
192    EnterScope(usize),
193    ExitScope(usize),
194    LoadLocal(usize),
195    StoreLocal(usize),
196
197    // Import registration for later unqualified resolution.
198    RegisterImport {
199        path: Vec<String>,
200        wildcard: bool,
201    },
202
203    // Global and persistent declarations, including name-stable forms across units.
204    DeclareGlobal(Vec<usize>),
205    DeclarePersistent(Vec<usize>),
206    DeclareGlobalNamed(Vec<usize>, Vec<String>),
207    DeclarePersistentNamed(Vec<usize>, Vec<String>),
208
209    // Emission instructions used to produce visible workspace outputs.
210    EmitStackTop {
211        label: EmitLabel,
212    },
213    EmitVar {
214        var_index: usize,
215        label: EmitLabel,
216    },
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct ArgSpec {
221    pub is_expand: bool,
222    pub num_indices: usize,
223    pub expand_all: bool,
224}
225
226impl Instr {
227    pub fn stack_effect(&self) -> Option<StackEffect> {
228        fn effect(pops: usize, pushes: usize) -> Option<StackEffect> {
229            Some(StackEffect { pops, pushes })
230        }
231
232        match self {
233            Instr::LoadConst(_)
234            | Instr::LoadComplex(_, _)
235            | Instr::LoadBool(_)
236            | Instr::LoadString(_)
237            | Instr::LoadCharRow(_)
238            | Instr::LoadVar(_)
239            | Instr::LoadLocal(_) => effect(0, 1),
240            Instr::StoreVar(_)
241            | Instr::StoreLocal(_)
242            | Instr::Pop
243            | Instr::JumpIfFalse(_)
244            | Instr::AndAnd(_)
245            | Instr::OrOr(_) => effect(1, 0),
246            Instr::Add
247            | Instr::Sub
248            | Instr::Mul
249            | Instr::RightDiv
250            | Instr::LeftDiv
251            | Instr::Pow
252            | Instr::ElemMul
253            | Instr::ElemDiv
254            | Instr::ElemPow
255            | Instr::ElemLeftDiv
256            | Instr::LessEqual
257            | Instr::Less
258            | Instr::Greater
259            | Instr::GreaterEqual
260            | Instr::Equal
261            | Instr::NotEqual => effect(2, 1),
262            Instr::Swap => effect(2, 2),
263            Instr::Neg
264            | Instr::UPlus
265            | Instr::Transpose
266            | Instr::ConjugateTranspose
267            | Instr::LoadMember(_)
268            | Instr::LoadMemberOrInit(_)
269            | Instr::LoadMethod(_) => effect(1, 1),
270            Instr::CallBuiltin(_, argc) | Instr::CallFunction(_, argc) => effect(*argc, 1),
271            Instr::CallFunctionMulti(_, argc, out_count) => effect(*argc, *out_count),
272            Instr::CallMethod(_, argc) | Instr::CallMethodOrMemberIndex(_, argc) => {
273                effect(argc + 1, 1)
274            }
275            Instr::CallStaticMethod(_, _, argc) => effect(*argc, 1),
276            Instr::CallFeval(argc) => effect(argc + 1, 1),
277            Instr::CreateMatrix(rows, cols) | Instr::CreateCell2D(rows, cols) => {
278                effect(rows * cols, 1)
279            }
280            Instr::CreateMatrixDynamic(rows) => effect(*rows, 1),
281            Instr::CreateRange(has_step) => effect(if *has_step { 3 } else { 2 }, 1),
282            Instr::Unpack(n) => effect(1, *n),
283            Instr::Index(n) | Instr::IndexCell(n) => effect(n + 1, 1),
284            Instr::IndexCellExpand(n, out_count) => effect(n + 1, *out_count),
285            Instr::StoreIndex(n) | Instr::StoreIndexCell(n) => effect(n + 2, 1),
286            Instr::IndexSlice(dims, numeric_count, _, _)
287            | Instr::StoreSlice(dims, numeric_count, _, _) => {
288                let pops = 1 + numeric_count;
289                if matches!(self, Instr::StoreSlice(_, _, _, _)) {
290                    effect(pops + 1, 1)
291                } else {
292                    let _ = dims;
293                    effect(pops, 1)
294                }
295            }
296            Instr::IndexSliceExpr {
297                numeric_count,
298                range_dims,
299                ..
300            } => effect(1 + numeric_count + range_dims.len(), 1),
301            Instr::StoreSliceExpr {
302                numeric_count,
303                range_dims,
304                ..
305            } => effect(2 + numeric_count + range_dims.len(), 1),
306            Instr::StoreMember(_)
307            | Instr::StoreMemberOrInit(_)
308            | Instr::StoreMemberDynamic
309            | Instr::StoreMemberDynamicOrInit => effect(2, 1),
310            Instr::LoadMemberDynamic | Instr::LoadMemberDynamicOrInit => effect(2, 1),
311            Instr::CreateClosure(_, capture_count) => effect(*capture_count, 1),
312            Instr::LoadStaticProperty(_, _) => effect(0, 1),
313            Instr::RegisterClass { .. } => effect(0, 0),
314            Instr::CallFevalExpandMulti(specs)
315            | Instr::CallFunctionExpandMulti(_, specs)
316            | Instr::CallBuiltinExpandMulti(_, specs) => {
317                let fixed = specs.iter().filter(|s| !s.is_expand).count();
318                let expanded: usize = specs
319                    .iter()
320                    .filter(|s| s.is_expand)
321                    .map(|s| 1 + s.num_indices)
322                    .sum();
323                let handle = usize::from(matches!(self, Instr::CallFevalExpandMulti(_)));
324                effect(handle + fixed + expanded, 1)
325            }
326            Instr::CallFunctionExpandAt(_, before, num_indices, after)
327            | Instr::CallBuiltinExpandAt(_, before, num_indices, after) => {
328                effect(before + after + 1 + num_indices, 1)
329            }
330            Instr::CallBuiltinExpandLast(_, fixed_argc, num_indices) => {
331                effect(fixed_argc + 1 + num_indices, 1)
332            }
333            Instr::PackToRow(n) | Instr::PackToCol(n) => effect(*n, 1),
334            Instr::EnterScope(_) | Instr::ExitScope(_) | Instr::Jump(_) | Instr::PopTry => {
335                effect(0, 0)
336            }
337            Instr::EnterTry(_, _) => effect(0, 0),
338            Instr::Return => effect(0, 0),
339            Instr::ReturnValue => effect(1, 0),
340            Instr::RegisterImport { .. }
341            | Instr::DeclareGlobal(_)
342            | Instr::DeclarePersistent(_)
343            | Instr::DeclareGlobalNamed(_, _)
344            | Instr::DeclarePersistentNamed(_, _) => effect(0, 0),
345            Instr::EmitStackTop { .. } => effect(1, 1),
346            Instr::EmitVar { .. } => effect(0, 0),
347            Instr::StochasticEvolution => None,
348        }
349    }
350}