Skip to main content

runmat_vm/bytecode/
instr.rs

1use runmat_hir::{CallableFallbackPolicy, CallableIdentity, FunctionId};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct StackEffect {
6    pub pops: usize,
7    pub pushes: usize,
8}
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub enum EmitLabel {
12    Ans,
13    Var(usize),
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub enum EndExpr {
18    End,
19    Const(f64),
20    Var(usize),
21    ResolvedCall {
22        identity: CallableIdentity,
23        fallback_policy: CallableFallbackPolicy,
24        args: Vec<EndExpr>,
25    },
26    Add(Box<EndExpr>, Box<EndExpr>),
27    Sub(Box<EndExpr>, Box<EndExpr>),
28    Mul(Box<EndExpr>, Box<EndExpr>),
29    Div(Box<EndExpr>, Box<EndExpr>),
30    LeftDiv(Box<EndExpr>, Box<EndExpr>),
31    Pow(Box<EndExpr>, Box<EndExpr>),
32    Neg(Box<EndExpr>),
33    Pos(Box<EndExpr>),
34    Floor(Box<EndExpr>),
35    Ceil(Box<EndExpr>),
36    Round(Box<EndExpr>),
37    Fix(Box<EndExpr>),
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub enum PropertyDefaultLiteral {
42    Num(f64),
43    Bool(bool),
44    String(String),
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub enum Instr {
49    // Constant and variable loads.
50    LoadConst(f64),
51    LoadComplex(f64, f64),
52    LoadBool(bool),
53    LoadString(String),
54    LoadCharRow(String),
55    LoadVar(usize),
56    LoadVarForIndexAssignment(usize),
57    StoreVar(usize),
58
59    // Scalar and matrix arithmetic.
60    Add,
61    Sub,
62    Mul,
63    RightDiv,
64    LeftDiv,
65    Pow,
66    Neg,
67    UPlus,
68    Transpose,
69    ConjugateTranspose,
70    ElemMul,
71    ElemDiv,
72    ElemPow,
73    ElemLeftDiv,
74    LessEqual,
75    Less,
76    Greater,
77    GreaterEqual,
78    Equal,
79    NotEqual,
80    LogicalNot,
81    LogicalAnd,
82    LogicalOr,
83
84    // Short-circuit logical control flow.
85    AndAnd(usize),
86    OrOr(usize),
87    JumpIfFalse(usize),
88    Jump(usize),
89    Pop,
90
91    // Expands a single value into N outputs, padding with zero values when needed.
92    Unpack(usize),
93
94    // Specialized lowering target for the stochastic evolution fast path.
95    StochasticEvolution,
96
97    // Array construction and direct indexing.
98    CreateMatrix(usize, usize),
99    CreateMatrixDynamic(usize),
100    CreateRange(bool),
101    Index(usize),
102
103    // Slice indexing with compiler-encoded colon and plain `end` masks.
104    IndexSlice(usize, usize, u32, u32),
105
106    // General slice/index path carrying dynamic ranges and `end` arithmetic.
107    IndexSliceExpr {
108        dims: usize,
109        numeric_count: usize,
110        colon_mask: u32,
111        end_mask: u32,
112        range_dims: Vec<usize>,
113        range_has_step: Vec<bool>,
114        range_start_exprs: Vec<Option<EndExpr>>,
115        range_step_exprs: Vec<Option<EndExpr>>,
116        range_end_exprs: Vec<EndExpr>,
117        end_numeric_exprs: Vec<(usize, EndExpr)>,
118    },
119
120    // Assignment counterpart to `IndexSliceExpr`.
121    StoreSliceExpr {
122        dims: usize,
123        numeric_count: usize,
124        colon_mask: u32,
125        end_mask: u32,
126        range_dims: Vec<usize>,
127        range_has_step: Vec<bool>,
128        range_start_exprs: Vec<Option<EndExpr>>,
129        range_step_exprs: Vec<Option<EndExpr>>,
130        range_end_exprs: Vec<EndExpr>,
131        end_numeric_exprs: Vec<(usize, EndExpr)>,
132    },
133    StoreSliceExprDelete {
134        dims: usize,
135        numeric_count: usize,
136        colon_mask: u32,
137        end_mask: u32,
138        range_dims: Vec<usize>,
139        range_has_step: Vec<bool>,
140        range_start_exprs: Vec<Option<EndExpr>>,
141        range_step_exprs: Vec<Option<EndExpr>>,
142        range_end_exprs: Vec<EndExpr>,
143        end_numeric_exprs: Vec<(usize, EndExpr)>,
144    },
145
146    // Cell array construction and indexing.
147    CreateCell2D(usize, usize),
148    CreateStructLiteral(Vec<String>),
149    CreateObjectLiteral {
150        class_name: String,
151        fields: Vec<String>,
152    },
153    IndexCell {
154        num_indices: usize,
155        end_offsets: Vec<(usize, isize)>,
156        end_exprs: Vec<(usize, EndExpr)>,
157    },
158
159    // Expands cell contents into a comma-separated list with fixed output arity.
160    IndexCellExpand {
161        num_indices: usize,
162        out_count: usize,
163        end_offsets: Vec<(usize, isize)>,
164        end_exprs: Vec<(usize, EndExpr)>,
165    },
166
167    // Expands cell contents into a first-class comma-separated list value.
168    IndexCellList {
169        num_indices: usize,
170        end_offsets: Vec<(usize, isize)>,
171        end_exprs: Vec<(usize, EndExpr)>,
172    },
173
174    // Indexed assignment updates the base value and pushes the updated base.
175    StoreIndex(usize),
176    StoreIndexCell {
177        num_indices: usize,
178        end_offsets: Vec<(usize, isize)>,
179        end_exprs: Vec<(usize, EndExpr)>,
180    },
181    StoreIndexDelete(usize),
182    StoreIndexCellDelete {
183        num_indices: usize,
184        end_offsets: Vec<(usize, isize)>,
185        end_exprs: Vec<(usize, EndExpr)>,
186    },
187
188    // Slice assignment with compiler-encoded colon and plain `end` masks.
189    StoreSlice(usize, usize, u32, u32),
190    StoreSliceDelete(usize, usize, u32, u32),
191
192    // Struct, object, and class member access.
193    LoadMember(String),
194    LoadMemberOrInit(String),
195    LoadMemberDynamic,
196    LoadMemberDynamicOrInit,
197    StoreMember(String),
198    StoreMemberOrInit(String),
199    StoreMemberDynamic,
200    StoreMemberDynamicOrInit,
201    LoadMethod(String),
202
203    // Ambiguous `obj.name(...)` shape resolved at runtime as method call or member indexing.
204    CallMethodOrMemberIndexMulti {
205        identity: CallableIdentity,
206        fallback_policy: CallableFallbackPolicy,
207        arg_count: usize,
208        out_count: usize,
209    },
210    CallMethodOrMemberIndexExpandMultiOutput {
211        identity: CallableIdentity,
212        fallback_policy: CallableFallbackPolicy,
213        specs: Vec<ArgSpec>,
214        out_count: usize,
215    },
216
217    // Closure and static class dispatch.
218    CreateFunctionHandle(String),
219    CreateExternalFunctionHandle(String),
220    CreateMethodFunctionHandle(String),
221    CreateBoundFunctionHandle(FunctionId, String),
222    CreateClosure(String, usize),
223    CreateSemanticClosure(FunctionId, String, usize),
224    LoadStaticProperty(String, String),
225
226    // Registers a runtime class definition produced by `classdef` lowering.
227    RegisterClass {
228        name: String,
229        super_class: Option<String>,
230        is_sealed: bool,
231        is_abstract: bool,
232        properties: Vec<(
233            String,
234            bool,
235            bool,
236            Option<PropertyDefaultLiteral>,
237            String,
238            String,
239        )>,
240        methods: Vec<(String, String, bool, bool, bool, String)>,
241        enumerations: Vec<String>,
242    },
243
244    // `feval` keeps the callable value on the stack instead of naming the target statically.
245    CallFevalMulti(usize, usize),
246    CallFevalMultiUsingOutputSlot(usize, usize),
247    CallFevalExpandMultiOutput(Vec<ArgSpec>, usize),
248    CallFevalExpandMultiOutputUsingOutputSlot(Vec<ArgSpec>, usize),
249    // Create a lazy semantic-future descriptor from call arguments.
250    CreateSemanticFuture(FunctionId, usize, usize),
251    CreateSemanticFutureExpandMultiOutput(FunctionId, Vec<ArgSpec>, usize),
252    // Explicit async spawn boundary.
253    Spawn,
254    // Explicit await boundary.
255    Await,
256
257    // Stack and exception-control operations.
258    Swap,
259    EnterTry(usize, Option<usize>),
260    PopTry,
261    Return,
262    ReturnValue,
263
264    // User-function invocation variants.
265    CallBuiltinMulti(String, usize, usize),
266    CallBuiltinMultiUsingOutputSlot(String, usize, usize),
267    CallSuperConstructorMulti {
268        current_class: String,
269        super_class: String,
270        arg_count: usize,
271        out_count: usize,
272    },
273    CallSuperMethodMulti {
274        current_class: String,
275        super_class: String,
276        method: String,
277        arg_count: usize,
278        out_count: usize,
279    },
280
281    // Calls a user function and shapes the result list to `out_count`.
282    CallFunctionMulti {
283        identity: CallableIdentity,
284        fallback_policy: CallableFallbackPolicy,
285        arg_count: usize,
286        out_count: usize,
287    },
288    CallFunctionMultiUsingOutputSlot {
289        identity: CallableIdentity,
290        fallback_policy: CallableFallbackPolicy,
291        arg_count: usize,
292        out_count_slot: usize,
293    },
294    CallSemanticFunctionMulti(FunctionId, usize, usize),
295    CallSemanticFunctionMultiUsingOutputSlot(FunctionId, usize, usize),
296    CallSemanticNestedFunctionMulti {
297        function: FunctionId,
298        capture_slots: Vec<usize>,
299        arg_count: usize,
300        out_count: usize,
301    },
302    CallSemanticNestedFunctionMultiUsingOutputSlot {
303        function: FunctionId,
304        capture_slots: Vec<usize>,
305        arg_count: usize,
306        out_count_slot: usize,
307    },
308
309    CallFunctionExpandMultiOutput {
310        identity: CallableIdentity,
311        fallback_policy: CallableFallbackPolicy,
312        specs: Vec<ArgSpec>,
313        out_count: usize,
314    },
315    CallSemanticFunctionExpandMultiOutput(FunctionId, Vec<ArgSpec>, usize),
316    CallSemanticNestedFunctionExpandMultiOutput {
317        function: FunctionId,
318        capture_slots: Vec<usize>,
319        specs: Vec<ArgSpec>,
320        out_count: usize,
321    },
322    CallBuiltinExpandMultiOutput(String, Vec<ArgSpec>, usize),
323    CallSuperConstructorExpandMultiOutput {
324        current_class: String,
325        super_class: String,
326        specs: Vec<ArgSpec>,
327        out_count: usize,
328    },
329    CallSuperMethodExpandMultiOutput {
330        current_class: String,
331        super_class: String,
332        method: String,
333        specs: Vec<ArgSpec>,
334        out_count: usize,
335    },
336
337    // Packs the top N values into row or column tensor form.
338    PackToRow(usize),
339    PackToCol(usize),
340
341    // Local scope and local variable access.
342    EnterScope(usize),
343    ExitScope(usize),
344    LoadLocal(usize),
345    StoreLocal(usize),
346
347    // Import registration for later unqualified resolution.
348    RegisterImport {
349        path: Vec<String>,
350        wildcard: bool,
351    },
352
353    // Global and persistent declarations, including name-stable forms across units.
354    DeclareGlobal(Vec<usize>),
355    DeclarePersistent(Vec<usize>),
356    DeclareGlobalNamed(Vec<usize>, Vec<String>),
357    DeclarePersistentNamed(Vec<usize>, Vec<String>),
358
359    // Emission instructions used to produce visible workspace outputs.
360    EmitStackTop {
361        label: EmitLabel,
362    },
363    EmitVar {
364        var_index: usize,
365        label: EmitLabel,
366    },
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct ArgSpec {
371    pub is_expand: bool,
372    pub num_indices: usize,
373    pub expand_all: bool,
374}
375
376impl Instr {
377    pub fn stack_effect(&self) -> Option<StackEffect> {
378        fn effect(pops: usize, pushes: usize) -> Option<StackEffect> {
379            Some(StackEffect { pops, pushes })
380        }
381
382        match self {
383            Instr::LoadConst(_)
384            | Instr::LoadComplex(_, _)
385            | Instr::LoadBool(_)
386            | Instr::LoadString(_)
387            | Instr::LoadCharRow(_)
388            | Instr::CreateFunctionHandle(_)
389            | Instr::CreateExternalFunctionHandle(_)
390            | Instr::CreateMethodFunctionHandle(_)
391            | Instr::CreateBoundFunctionHandle(_, _)
392            | Instr::LoadVar(_)
393            | Instr::LoadVarForIndexAssignment(_)
394            | Instr::LoadLocal(_) => effect(0, 1),
395            Instr::StoreVar(_)
396            | Instr::StoreLocal(_)
397            | Instr::Pop
398            | Instr::JumpIfFalse(_)
399            | Instr::AndAnd(_)
400            | Instr::OrOr(_) => effect(1, 0),
401            Instr::Add
402            | Instr::Sub
403            | Instr::Mul
404            | Instr::RightDiv
405            | Instr::LeftDiv
406            | Instr::Pow
407            | Instr::ElemMul
408            | Instr::ElemDiv
409            | Instr::ElemPow
410            | Instr::ElemLeftDiv
411            | Instr::LessEqual
412            | Instr::Less
413            | Instr::Greater
414            | Instr::GreaterEqual
415            | Instr::Equal
416            | Instr::NotEqual
417            | Instr::LogicalAnd
418            | Instr::LogicalOr => effect(2, 1),
419            Instr::Swap => effect(2, 2),
420            Instr::Neg
421            | Instr::UPlus
422            | Instr::LogicalNot
423            | Instr::Transpose
424            | Instr::ConjugateTranspose
425            | Instr::LoadMember(_)
426            | Instr::LoadMemberOrInit(_)
427            | Instr::LoadMethod(_) => effect(1, 1),
428            Instr::CallBuiltinMulti(_, argc, _) => effect(*argc, 1),
429            Instr::CallBuiltinMultiUsingOutputSlot(_, argc, _) => effect(*argc, 1),
430            Instr::CallSuperConstructorMulti { arg_count, .. } => effect(*arg_count, 1),
431            Instr::CallSuperMethodMulti { arg_count, .. } => effect(*arg_count, 1),
432            Instr::CallFunctionMulti {
433                arg_count,
434                out_count,
435                ..
436            } => effect(*arg_count, *out_count),
437            Instr::CallFunctionMultiUsingOutputSlot { arg_count, .. } => effect(*arg_count, 1),
438            Instr::CallSemanticFunctionMulti(_, argc, out_count) => effect(*argc, *out_count),
439            Instr::CallSemanticFunctionMultiUsingOutputSlot(_, argc, _) => effect(*argc, 1),
440            Instr::CallSemanticNestedFunctionMulti {
441                arg_count,
442                out_count,
443                ..
444            } => effect(*arg_count, *out_count),
445            Instr::CallSemanticNestedFunctionMultiUsingOutputSlot { arg_count, .. } => {
446                effect(*arg_count, 1)
447            }
448            Instr::CallMethodOrMemberIndexMulti { arg_count, .. } => effect(arg_count + 1, 1),
449            Instr::CallFevalMulti(argc, _) => effect(argc + 1, 1),
450            Instr::CallFevalMultiUsingOutputSlot(argc, _) => effect(argc + 1, 1),
451            Instr::CreateSemanticFuture(_, arg_count, _) => effect(*arg_count, 1),
452            Instr::CreateMatrix(rows, cols) | Instr::CreateCell2D(rows, cols) => {
453                effect(rows * cols, 1)
454            }
455            Instr::CreateStructLiteral(fields) => effect(fields.len(), 1),
456            Instr::CreateObjectLiteral { fields, .. } => effect(fields.len(), 1),
457            Instr::CreateMatrixDynamic(rows) => effect(*rows, 1),
458            Instr::CreateRange(has_step) => effect(if *has_step { 3 } else { 2 }, 1),
459            Instr::Unpack(n) => effect(1, *n),
460            Instr::Index(n) => effect(n + 1, 1),
461            Instr::IndexCell { num_indices, .. } | Instr::IndexCellList { num_indices, .. } => {
462                effect(num_indices + 1, 1)
463            }
464            Instr::IndexCellExpand {
465                num_indices,
466                out_count,
467                ..
468            } => effect(num_indices + 1, *out_count),
469            Instr::StoreIndex(n)
470            | Instr::StoreIndexDelete(n)
471            | Instr::StoreIndexCell { num_indices: n, .. }
472            | Instr::StoreIndexCellDelete { num_indices: n, .. } => effect(n + 2, 1),
473            Instr::IndexSlice(dims, numeric_count, _, _)
474            | Instr::StoreSlice(dims, numeric_count, _, _)
475            | Instr::StoreSliceDelete(dims, numeric_count, _, _) => {
476                let pops = 1 + numeric_count;
477                if matches!(
478                    self,
479                    Instr::StoreSlice(_, _, _, _) | Instr::StoreSliceDelete(_, _, _, _)
480                ) {
481                    effect(pops + 1, 1)
482                } else {
483                    let _ = dims;
484                    effect(pops, 1)
485                }
486            }
487            Instr::IndexSliceExpr {
488                numeric_count,
489                range_dims,
490                ..
491            } => effect(1 + numeric_count + range_dims.len(), 1),
492            Instr::StoreSliceExpr {
493                numeric_count,
494                range_dims,
495                ..
496            }
497            | Instr::StoreSliceExprDelete {
498                numeric_count,
499                range_dims,
500                ..
501            } => effect(2 + numeric_count + range_dims.len(), 1),
502            Instr::StoreMember(_)
503            | Instr::StoreMemberOrInit(_)
504            | Instr::StoreMemberDynamic
505            | Instr::StoreMemberDynamicOrInit => effect(2, 1),
506            Instr::LoadMemberDynamic | Instr::LoadMemberDynamicOrInit => effect(2, 1),
507            Instr::CreateClosure(_, capture_count)
508            | Instr::CreateSemanticClosure(_, _, capture_count) => effect(*capture_count, 1),
509            Instr::LoadStaticProperty(_, _) => effect(0, 1),
510            Instr::RegisterClass { .. } => effect(0, 0),
511            Instr::CallFevalExpandMultiOutput(specs, _)
512            | Instr::CallFevalExpandMultiOutputUsingOutputSlot(specs, _)
513            | Instr::CreateSemanticFutureExpandMultiOutput(_, specs, _)
514            | Instr::CallFunctionExpandMultiOutput { specs, .. }
515            | Instr::CallSemanticFunctionExpandMultiOutput(_, specs, _)
516            | Instr::CallSemanticNestedFunctionExpandMultiOutput { specs, .. }
517            | Instr::CallBuiltinExpandMultiOutput(_, specs, _)
518            | Instr::CallSuperConstructorExpandMultiOutput { specs, .. }
519            | Instr::CallSuperMethodExpandMultiOutput { specs, .. }
520            | Instr::CallMethodOrMemberIndexExpandMultiOutput { specs, .. } => {
521                let fixed = specs.iter().filter(|s| !s.is_expand).count();
522                let expanded: usize = specs
523                    .iter()
524                    .filter(|s| s.is_expand)
525                    .map(|s| 1 + s.num_indices)
526                    .sum();
527                let handle = usize::from(matches!(
528                    self,
529                    Instr::CallFevalExpandMultiOutput(_, _)
530                        | Instr::CallFevalExpandMultiOutputUsingOutputSlot(_, _)
531                ));
532                effect(handle + fixed + expanded, 1)
533            }
534            Instr::PackToRow(n) | Instr::PackToCol(n) => effect(*n, 1),
535            Instr::EnterScope(_) | Instr::ExitScope(_) | Instr::Jump(_) | Instr::PopTry => {
536                effect(0, 0)
537            }
538            Instr::EnterTry(_, _) => effect(0, 0),
539            Instr::Return => effect(0, 0),
540            Instr::ReturnValue => effect(1, 0),
541            Instr::RegisterImport { .. }
542            | Instr::DeclareGlobal(_)
543            | Instr::DeclarePersistent(_)
544            | Instr::DeclareGlobalNamed(_, _)
545            | Instr::DeclarePersistentNamed(_, _) => effect(0, 0),
546            Instr::Spawn => effect(1, 1),
547            Instr::Await => effect(1, 1),
548            Instr::EmitStackTop { .. } => effect(1, 1),
549            Instr::EmitVar { .. } => effect(0, 0),
550            Instr::StochasticEvolution => None,
551        }
552    }
553}