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