Skip to main content

harn_vm/vm/ops/
mod.rs

1//! VM opcode dispatch.
2//!
3//! The `Op` enum, the byte-to-variant mapping, the sync and async
4//! dispatch tables, the disassembly renderer, and the per-opcode
5//! classification helpers (`op_reads_outer_name`, `is_adaptive_binary_op`)
6//! are all emitted by `harn_opcode_macros::define_opcodes!` from the
7//! single declarative table below. Adding or renaming an opcode is now a
8//! one-line edit instead of paired edits to four hand-maintained match
9//! tables — see issue #2584 for the migration that collapsed them.
10//!
11//! Each entry follows the shape
12//!
13//! ```ignore
14//! VariantName { <kind>(<expr>...), disasm: <helper>("<LABEL>") [, flags: [...]] };
15//! ```
16//!
17//! where `<kind>` selects the dispatch shape (`sync`, `sync_void`,
18//! `sync_return`, `split`, `async_op`) and the `<helper>` resolves to a
19//! `disasm_<helper>` free fn in `crate::chunk`.
20
21mod arithmetic;
22mod call;
23mod call_named;
24mod collections;
25mod comparison;
26mod control_flow;
27mod exception;
28mod imports;
29mod iter;
30mod logical;
31mod misc;
32mod parallel;
33mod stack;
34
35use crate::value::{VmError, VmValue};
36
37harn_opcode_macros::define_opcodes! {
38    // === Constants & nil ===
39    Constant { sync(self.execute_constant()), disasm: const_pool_u16("CONSTANT") };
40    Nil { sync_void(self.execute_nil()), disasm: bare("NIL") };
41    True { sync_void(self.execute_true()), disasm: bare("TRUE") };
42    False { sync_void(self.execute_false()), disasm: bare("FALSE") };
43
44    // === Variables ===
45    GetVar { sync(self.execute_get_var()), disasm: const_pool_u16("GET_VAR"), flags: [reads_outer_name] };
46    DefLet { sync(self.execute_def_let()), disasm: const_pool_u16("DEF_LET") };
47    DefVar { sync(self.execute_def_var()), disasm: const_pool_u16("DEF_VAR") };
48    DefCell { sync(self.execute_def_cell()), disasm: const_pool_u16("DEF_CELL") };
49    SetVar { sync(self.execute_set_var()), disasm: const_pool_u16("SET_VAR"), flags: [reads_outer_name] };
50    PushScope { sync_void(self.execute_push_scope()), disasm: bare("PUSH_SCOPE") };
51    PopScope { sync_void(self.execute_pop_scope()), disasm: bare("POP_SCOPE") };
52
53    // === Generic arithmetic (adaptive binary IC) ===
54    Add { sync(self.execute_add()), disasm: bare("ADD"), flags: [adaptive_binary] };
55    Sub { sync(self.execute_sub()), disasm: bare("SUB"), flags: [adaptive_binary] };
56    Mul { sync(self.execute_mul()), disasm: bare("MUL"), flags: [adaptive_binary] };
57    Div { sync(self.execute_div()), disasm: bare("DIV"), flags: [adaptive_binary] };
58    Mod { sync(self.execute_mod()), disasm: bare("MOD"), flags: [adaptive_binary] };
59    Pow { sync(self.execute_pow()), disasm: bare("POW") };
60    Negate { sync(self.execute_negate()), disasm: bare("NEGATE") };
61
62    // === Generic comparison (adaptive binary IC) ===
63    Equal { sync(self.execute_equal()), disasm: bare("EQUAL"), flags: [adaptive_binary] };
64    NotEqual { sync(self.execute_not_equal()), disasm: bare("NOT_EQUAL"), flags: [adaptive_binary] };
65    Less { sync(self.execute_less()), disasm: bare("LESS"), flags: [adaptive_binary] };
66    Greater { sync(self.execute_greater()), disasm: bare("GREATER"), flags: [adaptive_binary] };
67    LessEqual { sync(self.execute_less_equal()), disasm: bare("LESS_EQUAL"), flags: [adaptive_binary] };
68    GreaterEqual { sync(self.execute_greater_equal()), disasm: bare("GREATER_EQUAL"), flags: [adaptive_binary] };
69
70    // === Logical ===
71    Not { sync(self.execute_not()), disasm: bare("NOT") };
72
73    // === Control flow ===
74    Jump { sync_void(self.execute_jump()), disasm: u16("JUMP") };
75    JumpIfFalse { sync(self.execute_jump_if_false()), disasm: u16("JUMP_IF_FALSE") };
76    JumpIfTrue { sync(self.execute_jump_if_true()), disasm: u16("JUMP_IF_TRUE") };
77    Pop { sync(self.execute_pop()), disasm: bare("POP") };
78
79    // === Functions ===
80    Call { split(self.execute_call_sync(), self.execute_call_async().await), disasm: u8("CALL"), flags: [reads_outer_name] };
81    TailCall { split(self.execute_tail_call_sync(), self.execute_tail_call_async().await), disasm: u8("TAIL_CALL"), flags: [reads_outer_name] };
82    Return { sync_return(self.execute_return()), disasm: bare("RETURN") };
83    Closure { sync_void(self.execute_closure()), disasm: u16("CLOSURE") };
84
85    // === Collections ===
86    BuildList { sync_void(self.execute_build_list()), disasm: u16("BUILD_LIST") };
87    BuildDict { sync_void(self.execute_build_dict()), disasm: u16("BUILD_DICT") };
88    Subscript { sync(self.execute_subscript(false)), disasm: bare("SUBSCRIPT") };
89    SubscriptOpt { sync(self.execute_subscript(true)), disasm: bare("SUBSCRIPT_OPT") };
90    Slice { sync(self.execute_slice()), disasm: bare("SLICE") };
91
92    // === Object operations ===
93    GetProperty { sync(self.execute_get_property(false)), disasm: const_pool_u16("GET_PROPERTY") };
94    GetPropertyOpt { sync(self.execute_get_property(true)), disasm: const_pool_u16("GET_PROPERTY_OPT") };
95    SetProperty { sync(self.execute_set_property()), disasm: const_pool_u16("SET_PROPERTY") };
96    SetSubscript { sync(self.execute_set_subscript()), disasm: const_pool_u16("SET_SUBSCRIPT") };
97    SetLocalSlotProperty { sync(self.execute_set_local_slot_property()), disasm: const_pool_local_slot("SET_LOCAL_SLOT_PROPERTY") };
98    SetLocalSlotSubscript { sync(self.execute_set_local_slot_subscript()), disasm: local_slot_u16("SET_LOCAL_SLOT_SUBSCRIPT") };
99    MethodCall { split(self.execute_method_call_sync(false), self.execute_method_call(false).await), disasm: method_call("METHOD_CALL") };
100    MethodCallOpt { split(self.execute_method_call_sync(true), self.execute_method_call(true).await), disasm: method_call("METHOD_CALL_OPT") };
101
102    // === Strings ===
103    Concat { sync_void(self.execute_concat()), disasm: u16("CONCAT") };
104
105    // === Iteration ===
106    IterInit { sync(self.execute_iter_init()), disasm: bare("ITER_INIT") };
107    IterNext { split(self.execute_iter_next_sync(), self.execute_iter_next_async().await), disasm: u16("ITER_NEXT") };
108
109    // === Pipe (async) ===
110    Pipe { async_op(self.execute_pipe().await), disasm: bare("PIPE"), flags: [reads_outer_name] };
111
112    // === Error handling ===
113    Throw { sync(self.execute_throw()), disasm: bare("THROW") };
114    TryCatchSetup { sync_void(self.execute_try_catch_setup()), disasm: try_catch_setup("TRY_CATCH_SETUP") };
115    PopHandler { sync_void(self.execute_pop_handler()), disasm: bare("POP_HANDLER") };
116
117    // === Concurrency ===
118    Parallel { async_op(self.execute_parallel().await), disasm: bare("PARALLEL") };
119    ParallelMap { async_op(self.execute_parallel_map().await), disasm: bare("PARALLEL_MAP") };
120    ParallelMapStream { async_op(self.execute_parallel_map_stream().await), disasm: bare("PARALLEL_MAP_STREAM") };
121    ParallelSettle { async_op(self.execute_parallel_settle().await), disasm: bare("PARALLEL_SETTLE") };
122    Spawn { sync(self.execute_spawn()), disasm: bare("SPAWN") };
123    SyncMutexEnter { async_op(self.execute_sync_mutex_enter().await), disasm: bare("SYNC_MUTEX_ENTER") };
124    SyncMutexEnterKeyed { async_op(self.execute_sync_mutex_enter_keyed().await), disasm: bare("SYNC_MUTEX_ENTER_KEYED") };
125    TaskScopeEnter { sync_void(self.execute_task_scope_enter()), disasm: bare("TASK_SCOPE_ENTER") };
126    TaskScopeExit { async_op(self.execute_task_scope_exit().await), disasm: bare("TASK_SCOPE_EXIT") };
127
128    // === Imports (async) ===
129    Import { async_op(self.execute_import_op().await), disasm: const_pool_u16("IMPORT") };
130    SelectiveImport { async_op(self.execute_selective_import().await), disasm: selective_import("SELECTIVE_IMPORT") };
131
132    // === Deadline ===
133    DeadlineSetup { sync(self.execute_deadline_setup()), disasm: bare("DEADLINE_SETUP") };
134    DeadlineEnd { sync_void(self.execute_deadline_end()), disasm: bare("DEADLINE_END") };
135
136    // === Enum ===
137    BuildEnum { sync(self.execute_build_enum()), disasm: build_enum("BUILD_ENUM") };
138    MatchEnum { sync(self.execute_match_enum()), disasm: match_enum("MATCH_ENUM") };
139
140    // === Loop control ===
141    PopIterator { sync_void(self.execute_pop_iterator()), disasm: bare("POP_ITERATOR") };
142
143    // === Defaults ===
144    GetArgc { sync_void(self.execute_get_argc()), disasm: bare("GET_ARGC") };
145
146    // === Type checking ===
147    CheckType { sync(self.execute_check_type()), disasm: check_type("CHECK_TYPE"), flags: [reads_outer_name] };
148
149    // === Result try operator ===
150    TryUnwrap { sync(self.execute_try_unwrap()), disasm: bare("TRY_UNWRAP") };
151    TryWrapOk { sync(self.execute_try_wrap_ok()), disasm: bare("TRY_WRAP_OK") };
152
153    // === Spread calls ===
154    CallSpread { async_op(self.execute_call_spread().await), disasm: bare("CALL_SPREAD"), flags: [reads_outer_name] };
155    CallBuiltin { split(self.execute_call_builtin_sync(), self.execute_call_builtin_async().await), disasm: call_builtin("CALL_BUILTIN"), flags: [reads_outer_name] };
156    CallBuiltinSpread { async_op(self.execute_call_builtin_spread().await), disasm: call_builtin_spread("CALL_BUILTIN_SPREAD"), flags: [reads_outer_name] };
157    MethodCallSpread { async_op(self.execute_method_call_spread().await), disasm: method_call_spread("METHOD_CALL_SPREAD") };
158
159    // === Misc ===
160    Dup { sync(self.execute_dup()), disasm: bare("DUP") };
161    Swap { sync_void(self.execute_swap()), disasm: bare("SWAP") };
162    Contains { sync(self.execute_contains()), disasm: bare("CONTAINS") };
163
164    // === Typed arithmetic fast paths ===
165    AddInt { sync(self.execute_add_int()), disasm: bare("ADD_INT") };
166    SubInt { sync(self.execute_sub_int()), disasm: bare("SUB_INT") };
167    MulInt { sync(self.execute_mul_int()), disasm: bare("MUL_INT") };
168    DivInt { sync(self.execute_div_int()), disasm: bare("DIV_INT") };
169    ModInt { sync(self.execute_mod_int()), disasm: bare("MOD_INT") };
170    AddFloat { sync(self.execute_add_float()), disasm: bare("ADD_FLOAT") };
171    SubFloat { sync(self.execute_sub_float()), disasm: bare("SUB_FLOAT") };
172    MulFloat { sync(self.execute_mul_float()), disasm: bare("MUL_FLOAT") };
173    DivFloat { sync(self.execute_div_float()), disasm: bare("DIV_FLOAT") };
174    ModFloat { sync(self.execute_mod_float()), disasm: bare("MOD_FLOAT") };
175
176    // === Typed comparison fast paths ===
177    EqualInt { sync(self.execute_equal_int()), disasm: bare("EQUAL_INT") };
178    NotEqualInt { sync(self.execute_not_equal_int()), disasm: bare("NOT_EQUAL_INT") };
179    LessInt { sync(self.execute_less_int()), disasm: bare("LESS_INT") };
180    GreaterInt { sync(self.execute_greater_int()), disasm: bare("GREATER_INT") };
181    LessEqualInt { sync(self.execute_less_equal_int()), disasm: bare("LESS_EQUAL_INT") };
182    GreaterEqualInt { sync(self.execute_greater_equal_int()), disasm: bare("GREATER_EQUAL_INT") };
183    EqualFloat { sync(self.execute_equal_float()), disasm: bare("EQUAL_FLOAT") };
184    NotEqualFloat { sync(self.execute_not_equal_float()), disasm: bare("NOT_EQUAL_FLOAT") };
185    LessFloat { sync(self.execute_less_float()), disasm: bare("LESS_FLOAT") };
186    GreaterFloat { sync(self.execute_greater_float()), disasm: bare("GREATER_FLOAT") };
187    LessEqualFloat { sync(self.execute_less_equal_float()), disasm: bare("LESS_EQUAL_FLOAT") };
188    GreaterEqualFloat { sync(self.execute_greater_equal_float()), disasm: bare("GREATER_EQUAL_FLOAT") };
189    EqualBool { sync(self.execute_equal_bool()), disasm: bare("EQUAL_BOOL") };
190    NotEqualBool { sync(self.execute_not_equal_bool()), disasm: bare("NOT_EQUAL_BOOL") };
191    EqualString { sync(self.execute_equal_string()), disasm: bare("EQUAL_STRING") };
192    NotEqualString { sync(self.execute_not_equal_string()), disasm: bare("NOT_EQUAL_STRING") };
193
194    // === Generators (async) ===
195    Yield { async_op(self.execute_yield().await), disasm: bare("YIELD") };
196
197    // === Slot-indexed locals ===
198    GetLocalSlot { sync(self.execute_get_local_slot()), disasm: local_slot_u16("GET_LOCAL_SLOT") };
199    DefLocalSlot { sync(self.execute_def_local_slot()), disasm: local_slot_u16("DEF_LOCAL_SLOT") };
200    SetLocalSlot { sync(self.execute_set_local_slot()), disasm: local_slot_u16("SET_LOCAL_SLOT") };
201    ConcatAssignLocal { sync(self.execute_concat_assign_local()), disasm: local_slot_u16("CONCAT_ASSIGN_LOCAL") };
202}
203
204impl super::Vm {
205    /// Execute a single opcode. Used by the scope-interrupt wrapper that
206    /// drives cancellable / deadlined execution; the hot interpreter loop
207    /// in `run_chunk_ref` bypasses this and calls `execute_op_sync` /
208    /// `execute_op_async` directly when no interrupt machinery is armed.
209    pub(super) async fn execute_op(&mut self, op_byte: u8) -> Result<Option<VmValue>, VmError> {
210        let op = Op::from_byte(op_byte).ok_or(VmError::InvalidInstruction(op_byte))?;
211        if let Some(result) = self.execute_op_sync(op) {
212            result?;
213            return Ok(None);
214        }
215        self.execute_op_async(op).await?;
216        Ok(None)
217    }
218}