Skip to main content

runmat_vm/bytecode/
instr.rs

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