aver/vm/opcode.rs
1// Aver VM bytecode opcodes.
2//
3// Stack-based: operands are pushed/popped from the operand stack.
4// Variable-width encoding: opcode (1 byte) + operands (0-3 bytes).
5
6// -- Stack / locals ----------------------------------------------------------
7
8/// Push `stack[bp + slot]` onto the operand stack.
9pub const LOAD_LOCAL: u8 = 0x01; // slot:u8
10
11/// Pop top and store into `stack[bp + slot]`.
12pub const STORE_LOCAL: u8 = 0x02; // slot:u8
13
14/// Push `constants[idx]` onto the operand stack.
15pub const LOAD_CONST: u8 = 0x03; // idx:u16
16
17/// Push `globals[idx]` onto the operand stack.
18pub const LOAD_GLOBAL: u8 = 0x04; // idx:u16
19
20/// Pop top and store into `globals[idx]`.
21pub const STORE_GLOBAL: u8 = 0x0A; // idx:u16
22
23/// Discard the top value.
24pub const POP: u8 = 0x05;
25
26/// Duplicate the top value.
27pub const DUP: u8 = 0x06;
28
29/// Push `NanValue::UNIT`.
30pub const LOAD_UNIT: u8 = 0x07;
31
32/// Push `NanValue::TRUE`.
33pub const LOAD_TRUE: u8 = 0x08;
34
35/// Push `NanValue::FALSE`.
36pub const LOAD_FALSE: u8 = 0x09;
37
38// -- Arithmetic --------------------------------------------------------------
39
40/// Pop b, pop a, push a + b.
41pub const ADD: u8 = 0x10;
42
43/// Pop b, pop a, push a - b.
44pub const SUB: u8 = 0x11;
45
46/// Pop b, pop a, push a * b.
47pub const MUL: u8 = 0x12;
48
49/// Pop b, pop a, push a / b.
50pub const DIV: u8 = 0x13;
51
52/// Pop b, pop a, push a % b.
53pub const MOD: u8 = 0x14;
54
55/// Pop a, push -a.
56pub const NEG: u8 = 0x15;
57
58/// Pop a, push !a (boolean not).
59pub const NOT: u8 = 0x16;
60
61// -- Comparison --------------------------------------------------------------
62
63/// Pop b, pop a, push a == b.
64pub const EQ: u8 = 0x20;
65
66/// Pop b, pop a, push a < b.
67pub const LT: u8 = 0x21;
68
69/// Pop b, pop a, push a > b.
70pub const GT: u8 = 0x22;
71
72// -- String ------------------------------------------------------------------
73
74/// Pop b, pop a, push str(a) ++ str(b).
75pub const CONCAT: u8 = 0x28;
76
77// -- Control flow ------------------------------------------------------------
78
79/// Unconditional relative jump: ip += offset.
80pub const JUMP: u8 = 0x30; // offset:i16
81
82/// Pop top, if falsy: ip += offset.
83pub const JUMP_IF_FALSE: u8 = 0x31; // offset:i16
84
85// -- Calls -------------------------------------------------------------------
86
87/// Call a known function by id. Args already on stack.
88pub const CALL_KNOWN: u8 = 0x40; // fn_id:u16, argc:u8
89
90/// Call a function value on the stack (under args).
91pub const CALL_VALUE: u8 = 0x41; // argc:u8
92
93/// Call a builtin service function.
94pub const CALL_BUILTIN: u8 = 0x42; // builtin_id:u16, argc:u8
95
96/// Self tail-call: reuse current frame with new args.
97pub const TAIL_CALL_SELF: u8 = 0x43; // argc:u8
98
99/// Mutual tail-call to a known function: reuse frame, switch target.
100pub const TAIL_CALL_KNOWN: u8 = 0x44; // fn_id:u16, argc:u8
101
102/// Return top of stack to caller.
103pub const RETURN: u8 = 0x50;
104
105// -- Structured values -------------------------------------------------------
106
107/// Push Nil (empty cons list).
108pub const LIST_NIL: u8 = 0x60;
109
110/// Pop tail, pop head, push Cons(head, tail).
111pub const LIST_CONS: u8 = 0x61;
112
113/// Pop `count` items, build cons list from them (first item = head), push list.
114pub const LIST_NEW: u8 = 0x62; // count:u8
115
116/// Pop `count` field values, push a new record with `type_id`.
117pub const RECORD_NEW: u8 = 0x63; // type_id:u16, count:u8
118
119/// Pop record, push `fields[field_idx]` (compile-time resolved index).
120pub const RECORD_GET: u8 = 0x64; // field_idx:u8
121
122/// Pop record, lookup field by name (constants[name_idx] is string), push value.
123pub const RECORD_GET_NAMED: u8 = 0x67; // name_idx:u16
124
125/// Pop `count` field values, push a new variant.
126pub const VARIANT_NEW: u8 = 0x65; // type_id:u16, variant_id:u16, count:u8
127
128/// Pop value, push wrapped value. kind: 0=Ok, 1=Err, 2=Some.
129pub const WRAP: u8 = 0x66; // kind:u8
130
131/// Pop `count` items, build a tuple from them, push tuple.
132pub const TUPLE_NEW: u8 = 0x68; // count:u8
133
134/// Update selected fields on a record, preserving the rest from the base value.
135/// Stack: [..., base_record, update_0, ..., update_n-1] -> [..., updated_record]
136pub const RECORD_UPDATE: u8 = 0x69; // type_id:u16, count:u8, field_idx[count]:u8
137
138/// Propagate `Result.Err` to caller or unwrap `Result.Ok` in place.
139pub const PROPAGATE_ERR: u8 = 0x6A;
140
141/// Pop list, push its length as Int.
142pub const LIST_LEN: u8 = 0x6B;
143
144/// Pop index, pop list, push Option.Some(value) or Option.None.
145pub const LIST_GET: u8 = 0x6C;
146
147/// Pop value, pop list, push appended list.
148pub const LIST_APPEND: u8 = 0x6D;
149
150/// Pop list, pop value, push prepended list.
151pub const LIST_PREPEND: u8 = 0x6E;
152
153/// Pop index, pop list. If found, push value then true; otherwise push false.
154pub const LIST_GET_MATCH: u8 = 0x6F;
155
156// -- Pattern matching --------------------------------------------------------
157
158/// Peek top: if NaN tag != expected, ip += fail_offset.
159pub const MATCH_TAG: u8 = 0x70; // expected_tag:u8, fail_offset:i16
160
161/// Peek top (must be variant): if variant_id != expected, ip += fail_offset.
162pub const MATCH_VARIANT: u8 = 0x71; // variant_id:u16, fail_offset:i16
163
164/// Peek top: if not wrapper of `kind`, ip += fail_offset.
165/// If matches, replace top with inner value (unwrap in-place).
166/// kind: 0=Ok, 1=Err, 2=Some.
167pub const MATCH_UNWRAP: u8 = 0x72; // kind:u8, fail_offset:i16
168
169/// Peek top: if not Nil, ip += fail_offset.
170pub const MATCH_NIL: u8 = 0x73; // fail_offset:i16
171
172/// Peek top: if Nil (not a cons), ip += fail_offset.
173pub const MATCH_CONS: u8 = 0x74; // fail_offset:i16
174
175/// Pop cons cell, push tail then push head.
176pub const LIST_HEAD_TAIL: u8 = 0x75;
177
178/// Peek top (record/variant), push `fields[field_idx]` (non-destructive).
179pub const EXTRACT_FIELD: u8 = 0x76; // field_idx:u8
180
181/// Peek top: if not a tuple of `count` items, ip += fail_offset.
182pub const MATCH_TUPLE: u8 = 0x78; // count:u8, fail_offset:i16
183
184/// Peek top tuple, push `items[item_idx]` (non-destructive).
185pub const EXTRACT_TUPLE_ITEM: u8 = 0x79; // item_idx:u8
186
187/// Non-exhaustive match error at source line.
188pub const MATCH_FAIL: u8 = 0x77; // line:u16
189
190/// Opcode name for debug/disassembly.
191pub fn opcode_name(op: u8) -> &'static str {
192 match op {
193 LOAD_LOCAL => "LOAD_LOCAL",
194 STORE_LOCAL => "STORE_LOCAL",
195 LOAD_CONST => "LOAD_CONST",
196 LOAD_GLOBAL => "LOAD_GLOBAL",
197 POP => "POP",
198 DUP => "DUP",
199 LOAD_UNIT => "LOAD_UNIT",
200 LOAD_TRUE => "LOAD_TRUE",
201 LOAD_FALSE => "LOAD_FALSE",
202 ADD => "ADD",
203 SUB => "SUB",
204 MUL => "MUL",
205 DIV => "DIV",
206 MOD => "MOD",
207 NEG => "NEG",
208 NOT => "NOT",
209 EQ => "EQ",
210 LT => "LT",
211 GT => "GT",
212 CONCAT => "CONCAT",
213 JUMP => "JUMP",
214 JUMP_IF_FALSE => "JUMP_IF_FALSE",
215 CALL_KNOWN => "CALL_KNOWN",
216 CALL_VALUE => "CALL_VALUE",
217 CALL_BUILTIN => "CALL_BUILTIN",
218 TAIL_CALL_SELF => "TAIL_CALL_SELF",
219 TAIL_CALL_KNOWN => "TAIL_CALL_KNOWN",
220 RETURN => "RETURN",
221 LIST_NIL => "LIST_NIL",
222 LIST_CONS => "LIST_CONS",
223 LIST_NEW => "LIST_NEW",
224 RECORD_NEW => "RECORD_NEW",
225 STORE_GLOBAL => "STORE_GLOBAL",
226 RECORD_GET => "RECORD_GET",
227 RECORD_GET_NAMED => "RECORD_GET_NAMED",
228 VARIANT_NEW => "VARIANT_NEW",
229 WRAP => "WRAP",
230 TUPLE_NEW => "TUPLE_NEW",
231 RECORD_UPDATE => "RECORD_UPDATE",
232 PROPAGATE_ERR => "PROPAGATE_ERR",
233 LIST_LEN => "LIST_LEN",
234 LIST_GET => "LIST_GET",
235 LIST_APPEND => "LIST_APPEND",
236 LIST_PREPEND => "LIST_PREPEND",
237 LIST_GET_MATCH => "LIST_GET_MATCH",
238 MATCH_TAG => "MATCH_TAG",
239 MATCH_VARIANT => "MATCH_VARIANT",
240 MATCH_UNWRAP => "MATCH_UNWRAP",
241 MATCH_NIL => "MATCH_NIL",
242 MATCH_CONS => "MATCH_CONS",
243 LIST_HEAD_TAIL => "LIST_HEAD_TAIL",
244 EXTRACT_FIELD => "EXTRACT_FIELD",
245 MATCH_TUPLE => "MATCH_TUPLE",
246 EXTRACT_TUPLE_ITEM => "EXTRACT_TUPLE_ITEM",
247 MATCH_FAIL => "MATCH_FAIL",
248 _ => "UNKNOWN",
249 }
250}