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    CreateExternalBoundFunctionHandle(FunctionId, String),
223    CreateClosure(String, usize),
224    CreateSemanticClosure(FunctionId, String, usize),
225    LoadStaticProperty(String, String),
226    LoadWorkspaceFirstStaticProperty {
227        name: String,
228        class_name: String,
229        property: String,
230    },
231
232    // Registers a runtime class definition produced by `classdef` lowering.
233    RegisterClass {
234        name: String,
235        super_class: Option<String>,
236        is_sealed: bool,
237        is_abstract: bool,
238        properties: Vec<(
239            String,
240            bool,
241            bool,
242            Option<PropertyDefaultLiteral>,
243            String,
244            String,
245        )>,
246        methods: Vec<(String, String, bool, bool, bool, String)>,
247        enumerations: Vec<String>,
248    },
249
250    // `feval` keeps the callable value on the stack instead of naming the target statically.
251    CallFevalMulti(usize, usize),
252    CallFevalMultiUsingOutputSlot(usize, usize),
253    CallFevalExpandMultiOutput(Vec<ArgSpec>, usize),
254    CallFevalExpandMultiOutputUsingOutputSlot(Vec<ArgSpec>, usize),
255    // Create a lazy semantic-future descriptor from call arguments.
256    CreateSemanticFuture(FunctionId, usize, usize),
257    CreateSemanticFutureExpandMultiOutput(FunctionId, Vec<ArgSpec>, usize),
258    // Explicit async spawn boundary.
259    Spawn,
260    // Explicit await boundary.
261    Await,
262
263    // Stack and exception-control operations.
264    Swap,
265    EnterTry(usize, Option<usize>),
266    PopTry,
267    Return,
268    ReturnValue,
269
270    // User-function invocation variants.
271    CallBuiltinMulti(String, usize, usize),
272    CallBuiltinMultiUsingOutputSlot(String, usize, usize),
273    CallSuperConstructorMulti {
274        current_class: String,
275        super_class: String,
276        arg_count: usize,
277        out_count: usize,
278    },
279    CallSuperMethodMulti {
280        current_class: String,
281        super_class: String,
282        method: String,
283        arg_count: usize,
284        out_count: usize,
285    },
286
287    // Calls a user function and shapes the result list to `out_count`.
288    CallFunctionMulti {
289        identity: CallableIdentity,
290        fallback_policy: CallableFallbackPolicy,
291        arg_count: usize,
292        out_count: usize,
293    },
294    CallFunctionMultiUsingOutputSlot {
295        identity: CallableIdentity,
296        fallback_policy: CallableFallbackPolicy,
297        arg_count: usize,
298        out_count_slot: usize,
299    },
300    CallWorkspaceFirstMulti {
301        name: String,
302        identity: CallableIdentity,
303        fallback_policy: CallableFallbackPolicy,
304        bare_identifier: bool,
305        arg_count: usize,
306        out_count: usize,
307    },
308    CallWorkspaceFirstMultiUsingOutputSlot {
309        name: String,
310        identity: CallableIdentity,
311        fallback_policy: CallableFallbackPolicy,
312        bare_identifier: bool,
313        arg_count: usize,
314        out_count_slot: usize,
315    },
316    CallSemanticFunctionMulti(FunctionId, usize, usize),
317    CallSemanticFunctionMultiUsingOutputSlot(FunctionId, usize, usize),
318    CallSemanticNestedFunctionMulti {
319        function: FunctionId,
320        capture_slots: Vec<usize>,
321        arg_count: usize,
322        out_count: usize,
323    },
324    CallSemanticNestedFunctionMultiUsingOutputSlot {
325        function: FunctionId,
326        capture_slots: Vec<usize>,
327        arg_count: usize,
328        out_count_slot: usize,
329    },
330
331    CallFunctionExpandMultiOutput {
332        identity: CallableIdentity,
333        fallback_policy: CallableFallbackPolicy,
334        specs: Vec<ArgSpec>,
335        out_count: usize,
336    },
337    CallWorkspaceFirstExpandMultiOutput {
338        name: String,
339        identity: CallableIdentity,
340        fallback_policy: CallableFallbackPolicy,
341        bare_identifier: bool,
342        specs: Vec<ArgSpec>,
343        out_count: usize,
344    },
345    CallWorkspaceFirstExpandMultiOutputUsingOutputSlot {
346        name: String,
347        identity: CallableIdentity,
348        fallback_policy: CallableFallbackPolicy,
349        bare_identifier: bool,
350        specs: Vec<ArgSpec>,
351        out_count_slot: usize,
352    },
353    CallSemanticFunctionExpandMultiOutput(FunctionId, Vec<ArgSpec>, usize),
354    CallSemanticNestedFunctionExpandMultiOutput {
355        function: FunctionId,
356        capture_slots: Vec<usize>,
357        specs: Vec<ArgSpec>,
358        out_count: usize,
359    },
360    CallBuiltinExpandMultiOutput(String, Vec<ArgSpec>, usize),
361    CallSuperConstructorExpandMultiOutput {
362        current_class: String,
363        super_class: String,
364        specs: Vec<ArgSpec>,
365        out_count: usize,
366    },
367    CallSuperMethodExpandMultiOutput {
368        current_class: String,
369        super_class: String,
370        method: String,
371        specs: Vec<ArgSpec>,
372        out_count: usize,
373    },
374
375    // Packs the top N values into row or column tensor form.
376    PackToRow(usize),
377    PackToCol(usize),
378
379    // Local scope and local variable access.
380    EnterScope(usize),
381    ExitScope(usize),
382    LoadLocal(usize),
383    StoreLocal(usize),
384
385    // Import registration for later unqualified resolution.
386    RegisterImport {
387        path: Vec<String>,
388        wildcard: bool,
389    },
390
391    // Global and persistent declarations, including name-stable forms across units.
392    DeclareGlobal(Vec<usize>),
393    DeclarePersistent(Vec<usize>),
394    DeclareGlobalNamed(Vec<usize>, Vec<String>),
395    DeclarePersistentNamed(Vec<usize>, Vec<String>),
396
397    // Emission instructions used to produce visible workspace outputs.
398    EmitStackTop {
399        label: EmitLabel,
400    },
401    EmitVar {
402        var_index: usize,
403        label: EmitLabel,
404    },
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct ArgSpec {
409    pub is_expand: bool,
410    pub num_indices: usize,
411    pub expand_all: bool,
412}
413
414impl Instr {
415    pub fn stack_effect(&self) -> Option<StackEffect> {
416        fn effect(pops: usize, pushes: usize) -> Option<StackEffect> {
417            Some(StackEffect { pops, pushes })
418        }
419
420        match self {
421            Instr::LoadConst(_)
422            | Instr::LoadComplex(_, _)
423            | Instr::LoadBool(_)
424            | Instr::LoadString(_)
425            | Instr::LoadCharRow(_)
426            | Instr::CreateFunctionHandle(_)
427            | Instr::CreateExternalFunctionHandle(_)
428            | Instr::CreateMethodFunctionHandle(_)
429            | Instr::CreateBoundFunctionHandle(_, _)
430            | Instr::CreateExternalBoundFunctionHandle(_, _)
431            | Instr::LoadVar(_)
432            | Instr::LoadVarForIndexAssignment(_)
433            | Instr::LoadLocal(_) => effect(0, 1),
434            Instr::StoreVar(_)
435            | Instr::StoreLocal(_)
436            | Instr::Pop
437            | Instr::JumpIfFalse(_)
438            | Instr::AndAnd(_)
439            | Instr::OrOr(_) => effect(1, 0),
440            Instr::Add
441            | Instr::Sub
442            | Instr::Mul
443            | Instr::RightDiv
444            | Instr::LeftDiv
445            | Instr::Pow
446            | Instr::ElemMul
447            | Instr::ElemDiv
448            | Instr::ElemPow
449            | Instr::ElemLeftDiv
450            | Instr::LessEqual
451            | Instr::Less
452            | Instr::Greater
453            | Instr::GreaterEqual
454            | Instr::Equal
455            | Instr::NotEqual
456            | Instr::LogicalAnd
457            | Instr::LogicalOr => effect(2, 1),
458            Instr::Swap => effect(2, 2),
459            Instr::Neg
460            | Instr::UPlus
461            | Instr::LogicalNot
462            | Instr::Transpose
463            | Instr::ConjugateTranspose
464            | Instr::LoadMember(_)
465            | Instr::LoadMemberOrInit(_)
466            | Instr::LoadMethod(_) => effect(1, 1),
467            Instr::CallBuiltinMulti(_, argc, _) => effect(*argc, 1),
468            Instr::CallBuiltinMultiUsingOutputSlot(_, argc, _) => effect(*argc, 1),
469            Instr::CallSuperConstructorMulti { arg_count, .. } => effect(*arg_count, 1),
470            Instr::CallSuperMethodMulti { arg_count, .. } => effect(*arg_count, 1),
471            Instr::CallFunctionMulti {
472                arg_count,
473                out_count,
474                ..
475            } => effect(*arg_count, *out_count),
476            Instr::CallFunctionMultiUsingOutputSlot { arg_count, .. } => effect(*arg_count, 1),
477            Instr::CallWorkspaceFirstMulti {
478                arg_count,
479                out_count,
480                ..
481            } => effect(*arg_count, *out_count),
482            Instr::CallWorkspaceFirstMultiUsingOutputSlot { arg_count, .. } => {
483                effect(*arg_count, 1)
484            }
485            Instr::CallSemanticFunctionMulti(_, argc, out_count) => effect(*argc, *out_count),
486            Instr::CallSemanticFunctionMultiUsingOutputSlot(_, argc, _) => effect(*argc, 1),
487            Instr::CallSemanticNestedFunctionMulti {
488                arg_count,
489                out_count,
490                ..
491            } => effect(*arg_count, *out_count),
492            Instr::CallSemanticNestedFunctionMultiUsingOutputSlot { arg_count, .. } => {
493                effect(*arg_count, 1)
494            }
495            Instr::CallMethodOrMemberIndexMulti { arg_count, .. } => effect(arg_count + 1, 1),
496            Instr::CallFevalMulti(argc, _) => effect(argc + 1, 1),
497            Instr::CallFevalMultiUsingOutputSlot(argc, _) => effect(argc + 1, 1),
498            Instr::CreateSemanticFuture(_, arg_count, _) => effect(*arg_count, 1),
499            Instr::CreateMatrix(rows, cols) | Instr::CreateCell2D(rows, cols) => {
500                effect(rows * cols, 1)
501            }
502            Instr::CreateStructLiteral(fields) => effect(fields.len(), 1),
503            Instr::CreateObjectLiteral { fields, .. } => effect(fields.len(), 1),
504            Instr::CreateMatrixDynamic(rows) => effect(*rows, 1),
505            Instr::CreateRange(has_step) => effect(if *has_step { 3 } else { 2 }, 1),
506            Instr::Unpack(n) => effect(1, *n),
507            Instr::Index(n) => effect(n + 1, 1),
508            Instr::IndexCell { num_indices, .. } | Instr::IndexCellList { num_indices, .. } => {
509                effect(num_indices + 1, 1)
510            }
511            Instr::IndexCellExpand {
512                num_indices,
513                out_count,
514                ..
515            } => effect(num_indices + 1, *out_count),
516            Instr::StoreIndex(n)
517            | Instr::StoreIndexDelete(n)
518            | Instr::StoreIndexCell { num_indices: n, .. }
519            | Instr::StoreIndexCellDelete { num_indices: n, .. } => effect(n + 2, 1),
520            Instr::IndexSlice(dims, numeric_count, _, _)
521            | Instr::StoreSlice(dims, numeric_count, _, _)
522            | Instr::StoreSliceDelete(dims, numeric_count, _, _) => {
523                let pops = 1 + numeric_count;
524                if matches!(
525                    self,
526                    Instr::StoreSlice(_, _, _, _) | Instr::StoreSliceDelete(_, _, _, _)
527                ) {
528                    effect(pops + 1, 1)
529                } else {
530                    let _ = dims;
531                    effect(pops, 1)
532                }
533            }
534            Instr::IndexSliceExpr {
535                numeric_count,
536                range_dims,
537                ..
538            } => effect(1 + numeric_count + range_dims.len(), 1),
539            Instr::StoreSliceExpr {
540                numeric_count,
541                range_dims,
542                ..
543            }
544            | Instr::StoreSliceExprDelete {
545                numeric_count,
546                range_dims,
547                ..
548            } => effect(2 + numeric_count + range_dims.len(), 1),
549            Instr::StoreMember(_)
550            | Instr::StoreMemberOrInit(_)
551            | Instr::StoreMemberDynamic
552            | Instr::StoreMemberDynamicOrInit => effect(2, 1),
553            Instr::LoadMemberDynamic | Instr::LoadMemberDynamicOrInit => effect(2, 1),
554            Instr::CreateClosure(_, capture_count)
555            | Instr::CreateSemanticClosure(_, _, capture_count) => effect(*capture_count, 1),
556            Instr::LoadStaticProperty(_, _) | Instr::LoadWorkspaceFirstStaticProperty { .. } => {
557                effect(0, 1)
558            }
559            Instr::RegisterClass { .. } => effect(0, 0),
560            Instr::CallFevalExpandMultiOutput(specs, _)
561            | Instr::CallFevalExpandMultiOutputUsingOutputSlot(specs, _)
562            | Instr::CreateSemanticFutureExpandMultiOutput(_, specs, _)
563            | Instr::CallFunctionExpandMultiOutput { specs, .. }
564            | Instr::CallWorkspaceFirstExpandMultiOutput { specs, .. }
565            | Instr::CallWorkspaceFirstExpandMultiOutputUsingOutputSlot { specs, .. }
566            | Instr::CallSemanticFunctionExpandMultiOutput(_, specs, _)
567            | Instr::CallSemanticNestedFunctionExpandMultiOutput { specs, .. }
568            | Instr::CallBuiltinExpandMultiOutput(_, specs, _)
569            | Instr::CallSuperConstructorExpandMultiOutput { specs, .. }
570            | Instr::CallSuperMethodExpandMultiOutput { specs, .. }
571            | Instr::CallMethodOrMemberIndexExpandMultiOutput { specs, .. } => {
572                let fixed = specs.iter().filter(|s| !s.is_expand).count();
573                let expanded: usize = specs
574                    .iter()
575                    .filter(|s| s.is_expand)
576                    .map(|s| 1 + s.num_indices)
577                    .sum();
578                let handle = usize::from(matches!(
579                    self,
580                    Instr::CallFevalExpandMultiOutput(_, _)
581                        | Instr::CallFevalExpandMultiOutputUsingOutputSlot(_, _)
582                ));
583                effect(handle + fixed + expanded, 1)
584            }
585            Instr::PackToRow(n) | Instr::PackToCol(n) => effect(*n, 1),
586            Instr::EnterScope(_) | Instr::ExitScope(_) | Instr::Jump(_) | Instr::PopTry => {
587                effect(0, 0)
588            }
589            Instr::EnterTry(_, _) => effect(0, 0),
590            Instr::Return => effect(0, 0),
591            Instr::ReturnValue => effect(1, 0),
592            Instr::RegisterImport { .. }
593            | Instr::DeclareGlobal(_)
594            | Instr::DeclarePersistent(_)
595            | Instr::DeclareGlobalNamed(_, _)
596            | Instr::DeclarePersistentNamed(_, _) => effect(0, 0),
597            Instr::Spawn => effect(1, 1),
598            Instr::Await => effect(1, 1),
599            Instr::EmitStackTop { .. } => effect(1, 1),
600            Instr::EmitVar { .. } => effect(0, 0),
601            Instr::StochasticEvolution => None,
602        }
603    }
604}