Skip to main content

oxide_core/bytecode/
opcode.rs

1//! The opcode table: one line per opcode with its operand format.
2
3/// How an instruction's `a`, `b`, `c` and `imm` fields are read. The
4/// validator, the disassembler and the builder's checks all go through it.
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub enum Format {
7    /// No operand.
8    None,
9    /// `a` register.
10    A,
11    /// `a`, `b` registers.
12    AB,
13    /// `a` register, `imm` an index into the running function's captures.
14    ACapture,
15    /// `a`, `b`, `c` registers.
16    ABC,
17    /// `a` register, `imm` an `i32` literal.
18    AInt,
19    /// `a`, `b` registers, `imm` an `i32` literal.
20    ABInt,
21    /// `a` register, `imm` a constant-pool index.
22    AConst,
23    /// `imm` a `PropertyKey`.
24    Key,
25    /// `a` register, `imm` a `PropertyKey`.
26    AKey,
27    /// `a`, `b` registers, `imm` a `PropertyKey`.
28    ABKey,
29    /// `a` register, `imm` a count (object slots).
30    ACount,
31    /// `a` register, `b` the first of `imm` consecutive registers.
32    ABRange,
33    /// `a` register, `imm` an index into the unit's children.
34    AChild,
35    /// `a` destination, `b` callee, `c` argument count: the callee is in `b`,
36    /// the receiver in `b + 1`, the arguments in `b + 2 ..= b + 1 + c`.
37    Call,
38    /// `imm` a jump target.
39    Jump,
40    /// `a` register, `imm` a jump target.
41    AJump,
42    /// `a`, `b` registers, `imm` a jump target.
43    ABJump,
44    /// `a` register, `b | c << 8` an `i16` literal, `imm` a jump target.
45    AShortJump,
46    /// `a` register, `imm` a register above the 8-bit window.
47    AWide,
48}
49
50macro_rules! opcodes {
51    ($($(#[$doc:meta])* $name:ident: $format:ident,)*) => {
52        /// A register instruction's operation. The lowering builds instructions
53        /// as Rust values, so the main loop never decodes or validates an
54        /// opcode byte.
55        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
56        #[repr(u8)]
57        pub enum Opcode {
58            $($(#[$doc])* $name,)*
59        }
60
61        impl Opcode {
62            /// Every opcode, in discriminant order.
63            pub const ALL: &'static [Opcode] = &[$(Opcode::$name,)*];
64
65            #[inline]
66            pub const fn format(self) -> Format {
67                match self {
68                    $(Opcode::$name => Format::$format,)*
69                }
70            }
71
72            pub const fn name(self) -> &'static str {
73                match self {
74                    $(Opcode::$name => stringify!($name),)*
75                }
76            }
77        }
78    };
79}
80
81opcodes! {
82    Nop: None,
83
84    // ---- moves and constants ----
85    /// `a = b`.
86    Mov: AB,
87    /// Exchanges `a` and `b` (cyclic stack shuffles).
88    Swap: AB,
89    /// `a = register imm`, for a register above the window.
90    LoadWide: AWide,
91    /// `register imm = a`.
92    StoreWide: AWide,
93    LoadInt: AInt,
94    LoadConst: AConst,
95    LoadUndefined: A,
96    LoadNull: A,
97    LoadTrue: A,
98    LoadFalse: A,
99    /// The TDZ marker, for a lexical binding entering scope.
100    LoadEmpty: A,
101    /// The running function object, for a named function expression.
102    LoadFunction: A,
103    /// Throws `ReferenceError` naming key `imm` when `a` is the TDZ marker.
104    CheckInit: AKey,
105    /// Throws `TypeError` for an assignment to the constant named `imm`.
106    ThrowConstAssign: Key,
107
108    // ---- unary operators: a = op b ----
109    Neg: AB,
110    /// `ToNumber`, the unary `+`.
111    Plus: AB,
112    /// `ToNumeric`, the old value of a postfix update.
113    ToNumeric: AB,
114    Inc: AB,
115    Dec: AB,
116    BitNot: AB,
117    Not: AB,
118    TypeOf: AB,
119    ToPropertyKey: AB,
120    IsUndefinedOrNull: AB,
121    IsUndefined: AB,
122    IsNull: AB,
123    TypeOfIsUndefined: AB,
124    TypeOfIsFunction: AB,
125
126    // ---- binary operators: a = b op c ----
127    Add: ABC,
128    Sub: ABC,
129    Mul: ABC,
130    Div: ABC,
131    Mod: ABC,
132    Pow: ABC,
133    Shl: ABC,
134    Sar: ABC,
135    Shr: ABC,
136    BitAnd: ABC,
137    BitOr: ABC,
138    BitXor: ABC,
139    Eq: ABC,
140    Neq: ABC,
141    StrictEq: ABC,
142    StrictNeq: ABC,
143    Lt: ABC,
144    Lte: ABC,
145    Gt: ABC,
146    Gte: ABC,
147    InstanceOf: ABC,
148    In: ABC,
149
150    // ---- immediate variants: a = b op imm ----
151    AddI: ABInt,
152    SubI: ABInt,
153    MulI: ABInt,
154    BitAndI: ABInt,
155    BitOrI: ABInt,
156    BitXorI: ABInt,
157    ShlI: ABInt,
158    SarI: ABInt,
159    ShrI: ABInt,
160
161    // ---- control ----
162    Jmp: Jump,
163    JTrue: AJump,
164    JFalse: AJump,
165    JUndefinedOrNull: AJump,
166    /// Stores the index of the next instruction in `a` as an `Int32` and
167    /// jumps to `imm` (a `finally` block).
168    Gosub: AJump,
169    /// Jumps to the instruction index held in `a`.
170    Ret: A,
171
172    // ---- compare-jumps: jump to imm when a op b holds ----
173    JLt: ABJump,
174    JLte: ABJump,
175    JGt: ABJump,
176    JGte: ABJump,
177    /// `!(a < b)`, not `a >= b`: the negated forms differ on NaN.
178    JNlt: ABJump,
179    JNlte: ABJump,
180    JNgt: ABJump,
181    JNgte: ABJump,
182    JEq: ABJump,
183    JNeq: ABJump,
184    JStrictEq: ABJump,
185    JStrictNeq: ABJump,
186
187    // ---- compare-jumps against an i16 literal ----
188    JLtI: AShortJump,
189    JLteI: AShortJump,
190    JGtI: AShortJump,
191    JGteI: AShortJump,
192    JNltI: AShortJump,
193    JNlteI: AShortJump,
194    JNgtI: AShortJump,
195    JNgteI: AShortJump,
196    JEqI: AShortJump,
197    JNeqI: AShortJump,
198    JStrictEqI: AShortJump,
199    JStrictNeqI: AShortJump,
200
201    // ---- properties ----
202    /// `a = b.imm`.
203    GetField: ABKey,
204    /// `a.imm = b`.
205    SetField: ABKey,
206    /// Defines own data property `imm` of `a` as `b` (literals).
207    DefineField: ABKey,
208    /// `a = delete b.imm`.
209    DeleteField: ABKey,
210    /// Defines the getter of own accessor `imm` of `a` as function `b`
211    /// (`get x() {}` in a literal), keeping an existing setter.
212    DefineGetter: ABKey,
213    /// Defines the setter of own accessor `imm` of `a` as function `b`.
214    DefineSetter: ABKey,
215    /// `a = b[c]`.
216    GetElem: ABC,
217    /// `a[b] = c`.
218    SetElem: ABC,
219    /// Defines own data property `b` of `a` as `c` (computed literal keys).
220    DefineElem: ABC,
221    /// `a = delete b[c]`.
222    DeleteElem: ABC,
223
224    // ---- globals ----
225    GetGlobal: AKey,
226    /// `typeof name` on a name that may be undeclared.
227    GetGlobalOrUndefined: AKey,
228    SetGlobal: AKey,
229    /// A `var` or function declaration at script level; `b` holds the
230    /// [`global`] flags.
231    DefineGlobal: AKey,
232    DeleteGlobal: AKey,
233    /// A script-level declaration: defines global `imm` as `undefined` unless
234    /// it exists; `b` holds the [`global`] flags.
235    DeclareGlobal: Key,
236    /// A script-level `let` or `class`: a binding of the global lexical
237    /// environment, in the TDZ.
238    DeclareGlobalLet: Key,
239    /// A script-level `const`.
240    DeclareGlobalConst: Key,
241    /// `GlobalDeclarationInstantiation`'s check for a `var` or function
242    /// name: a `SyntaxError` if a lexical binding has it, then
243    /// `CanDeclareGlobalVar` or, with [`global::FUNCTION`] in `b`,
244    /// `CanDeclareGlobalFunction`, a `TypeError` when false.
245    CheckGlobalVar: Key,
246    /// The check for a lexical name: a `SyntaxError` if a lexical binding or
247    /// a non-configurable global property has it.
248    CheckGlobalLexical: Key,
249
250    // ---- captured bindings ----
251    // A binding captured by an inner function lives in a cell (a one-slot
252    // scope object); its register holds the cell. A closure holds the cells it
253    // captures, by index.
254    /// `a` = a new cell holding the value of `b`.
255    NewCell: AB,
256    /// `a` = a new cell in the TDZ (a lexical binding entering scope).
257    NewCellEmpty: A,
258    /// `a` = a new cell holding a copy of cell `a`'s value: the binding is
259    /// detached from closures created so far (a `for (let …)` iteration).
260    CloseCell: A,
261    /// `a` = the value in cell `b`.
262    LoadCell: AB,
263    /// Cell `a` = `b`.
264    StoreCell: AB,
265    /// Throws `ReferenceError` naming key `imm` when cell `a` is in the TDZ.
266    CheckCellInit: AKey,
267    /// `a` = the value of the running function's capture `imm`.
268    LoadCaptured: ACapture,
269    /// The running function's capture `imm` = `a`.
270    StoreCaptured: ACapture,
271
272    // ---- the spill scope of a function with more locals than the window ----
273    /// `a` = a scope of `imm` slots, all `undefined`.
274    NewSpill: ACount,
275    /// `a` = slot `imm` of the spill scope in `b`.
276    LoadSlot: ABInt,
277    /// Slot `imm` of the spill scope in `b` = `a`.
278    StoreSlot: ABInt,
279
280    // ---- rare forms, out of line ----
281    /// `a` = a new `arguments` object of the running frame: `imm` 1 for the
282    /// mapped form (the parameters' registers hold their cells), 0 for the
283    /// unmapped one.
284    NewArguments: AInt,
285    /// `a` = an array of the arguments from index `imm` (a rest parameter).
286    Rest: AInt,
287    /// `a` = new.target: the running function in a `new` call, else
288    /// `undefined`.
289    LoadNewTarget: A,
290    /// `a` = a new regular expression over constants `imm` (the source) and
291    /// `imm + 1` (the flags).
292    NewRegExp: AConst,
293    /// `a` = a `for-in` enumeration of `b`'s enumerable string keys.
294    ForInStart: AB,
295    /// The next key of enumeration `b` that still exists goes to `a` and
296    /// control to `imm`; an exhausted enumeration falls through.
297    ForInNext: ABJump,
298    /// Appends the elements of the array or `arguments` object in `c` to the
299    /// array in `a` from index `b`, leaving the next index in `b` (a spread
300    /// element).
301    AppendSpread: ABC,
302    /// Throws `InternalError` whose message is the atom `imm` (a construct
303    /// outside the surface, reached at run time).
304    ThrowInternal: Key,
305
306    // ---- classes, modules, iteration and suspension (out of line) ----
307    /// Throws `TypeError` when `a` (the `new.target` register) is
308    /// `undefined`: a class constructor called without `new`.
309    CheckCtor: A,
310    /// `a` = the running function's home object.
311    LoadHomeObject: A,
312    /// `a = ToObject(b)`.
313    ToObject: AB,
314    /// Sets the prototype of the fresh literal `a` to `b` when `b` is an
315    /// object or `null` (`__proto__: v` in a literal).
316    SetProto: AB,
317    /// Defines method `imm` of `a` as function `b` with home object `a`;
318    /// `c` holds the [`method`] flags.
319    DefineMethod: ABKey,
320    /// Defines method `b` (a canonical key) of `a` as function `c`; `imm`
321    /// holds the [`method`] flags.
322    DefineMethodComputed: ABC,
323    /// Names function `a` by the key in `b` when its name is empty
324    /// (`SetFunctionName` for anonymous functions in named positions).
325    SetFunctionName: AB,
326    /// Throws `TypeError` whose message is the atom `imm`.
327    ThrowType: Key,
328    /// Throws `ReferenceError` whose message is the atom `imm`.
329    ThrowReference: Key,
330    /// `a = import(b, c)`: the promise of module `b`'s namespace, with the
331    /// options in `c`.
332    ImportCall: ABC,
333
334    // ---- iteration: a record is three registers ----
335    /// The record at `a` (iterator, `next`, done flag; or array, the
336    /// `Value::EMPTY` mark, index) for the iterable in `b`; with `imm` set
337    /// always the generic protocol.
338    IterStart: ABInt,
339    /// The next value of record `b` to `a` and whether it is done to `c`.
340    IterNext: ABC,
341    /// Closes record `a` unless it is done (a normal completion).
342    IterClose: A,
343    /// Closes record `a` unless it is done for the throw completion in `b`;
344    /// errors of `return` are dropped.
345    IterCloseThrow: AB,
346    /// `a` = record `b`'s `next` called with `c` (`yield*`), unchecked.
347    IterCallNext: ABC,
348    /// `a` = record `b`'s `return` (`imm` 0) or `throw` (`imm` 1) called
349    /// with `c`, and `a + 1` whether the method was missing (then `a` = `c`).
350    IterCall: ABC,
351    /// Throws `TypeError` when `a` is not an object.
352    IterCheckObject: A,
353    /// The async record at `a` for `b`: its `@@asyncIterator`, or an
354    /// async-from-sync iterator over its sync one.
355    IterStartAsync: AB,
356    /// `a` = record `b`'s `next()` result, unchecked; the record is marked
357    /// done until [`Opcode::IterResult`] reads the settled result.
358    IterNextRaw: AB,
359    /// Reads the settled result in `a` of record `b`: its value to `a`, its
360    /// done flag to `c` (the record is open again unless done).
361    IterResult: ABC,
362
363    // ---- classes ----
364    /// `a` = the class constructor made from the closure in `b` with the
365    /// parent in `a` (`c` 1 when it has a heritage) and `b` = its
366    /// prototype; `imm` is the class name.
367    MakeClass: ABKey,
368    /// Installs the instance-field initializer `c` on constructor `a`, with
369    /// home object `b`.
370    InstallFields: ABC,
371    /// Runs the instance-field initializer of constructor `b` on `a`.
372    InitFields: AB,
373    /// Runs the static initializer `b` with `this` and home object `a`.
374    RunStaticInit: AB,
375    /// Calls static block `a` with the frame's `this` and home object.
376    CallStaticBlock: A,
377    /// `a` = the prototype of home object `b` (`null` allowed).
378    GetSuper: AB,
379    /// `a` = `base[key]` with `receiver` as `this`: receiver, base, key in
380    /// `b`, `b + 1`, `b + 2`; `imm` marks the call form, which reads the
381    /// same way.
382    GetSuperValue: ABInt,
383    /// `base[key] = value` with `receiver`: receiver, base, key, value in
384    /// `a` .. `a + 3`.
385    PutSuperValue: A,
386    /// `a` = the result of the implicit derived constructor: the parent
387    /// constructed with the frame's arguments and the `new.target` in `b`.
388    InitDerived: AB,
389    /// Binds a derived constructor's `this` register `a` to `b` (a
390    /// `ReferenceError` if already bound).
391    InitThis: AB,
392    /// [`Opcode::InitThis`] on the cell in `a`.
393    InitThisCell: AB,
394    /// [`Opcode::InitThis`] on the running function's capture `imm`.
395    InitThisCaptured: ACapture,
396    /// A derived constructor's return value: `a` stays if it is an object,
397    /// becomes the `this` in `b` for `undefined`; anything else throws.
398    CheckDerivedReturn: AB,
399
400    // ---- private names ----
401    /// `a` = a new private name described by atom `imm`.
402    NewPrivateName: AKey,
403    /// Sets the home object of method `a` to `b` and gives `b` a brand.
404    SetHomeObject: AB,
405    /// Names private method `a` by atom `imm` after the prefix `c` (0 none,
406    /// 1 `get `, 2 `set `).
407    NamePrivateMethod: AKey,
408    /// `a` = own private field `c` of `b` (a `TypeError` if absent).
409    GetPrivate: ABC,
410    /// Own private field `c` of `a` = `b` (a `TypeError` if absent).
411    PutPrivate: ABC,
412    /// Defines own private field `c` of `a` as `b` (a `TypeError` if
413    /// present).
414    DefinePrivate: ABC,
415    /// `a` = whether `b` has own private field `c`.
416    PrivateIn: ABC,
417    /// `a` = private method `c` after a brand check of `b`.
418    GetPrivateMethod: ABC,
419    /// `a` = the private getter `c` called on `b` after a brand check.
420    CallPrivateGetter: ABC,
421    /// Calls the private setter `c` on `a` with `b` after a brand check.
422    CallPrivateSetter: ABC,
423    /// `a` = whether `b` carries the brand of private method `c`.
424    PrivateBrandIn: ABC,
425
426    // ---- suspension ----
427    /// A generator suspends before its body: the call returns the
428    /// generator object.
429    InitialYield: None,
430    /// An async function starts: its promise is the call's result, and the
431    /// body runs until it awaits or completes.
432    AsyncStart: None,
433    /// Suspends with the value in `a`; a resumption writes its value to `a`
434    /// and its kind (0 `next`, 1 `return`) to `a + 1`, or throws here.
435    Yield: A,
436    /// `yield*`'s suspension: the iterator result in `a` is returned as is;
437    /// a `throw` resumes with kind 2 rather than throwing.
438    YieldStar: A,
439    /// Awaits the value in `a`; the fulfilled value is written to `a`, a
440    /// rejection throws here.
441    Await: A,
442
443    // ---- object spread and references ----
444    /// Copies the enumerable own properties of `b` onto `a`.
445    CopyData: AB,
446    /// [`Opcode::CopyData`] except the keys own by the object in `c`.
447    CopyDataExcluded: ABC,
448    /// `a` = the environment holding global `imm`: the lexical one, the
449    /// global object, or `undefined` when unresolvable.
450    GlobalRef: AKey,
451    /// `a` = binding `imm` of environment `b` (a `ReferenceError` if
452    /// missing; `undefined` with `c` 1).
453    GetRef: ABKey,
454    /// Binding `imm` of environment `a` = `b`.
455    PutRef: ABKey,
456
457    // ---- allocation ----
458    /// `a = {}` with room for `imm` properties.
459    NewObject: ACount,
460    /// `a = [b, b + 1, …]` of `imm` elements.
461    NewArray: ABRange,
462    /// `a` = a closure for child unit `imm`, capturing what the child's
463    /// capture list names.
464    MakeClosure: AChild,
465
466    // ---- calls ----
467    /// `a = b(b + 1, …, b + c)` with an `undefined` receiver: the callee's
468    /// window starts at `b`, so `b` becomes its `this`.
469    Call: Call,
470    /// `a = (b + 1)(b + 2, …)` with receiver `b`: the window starts at
471    /// `b + 1`, which receives the receiver.
472    CallMethod: Call,
473    /// `Call` that returns the callee's result from this frame; `a` is
474    /// ignored.
475    TailCall: Call,
476    /// `TailCall` with a receiver, as `CallMethod`.
477    TailCallMethod: Call,
478    /// `a = new b(b + 2, …)` with new.target in `b + 1`: the window starts at
479    /// `b + 1`, which receives the constructed object.
480    New: Call,
481    /// `a = b(...)` with receiver `b + 1` and the elements of the array in
482    /// `b + 2` as the arguments (a spread call): the window starts at
483    /// `b + 1`; `c` is unused.
484    CallSpread: Call,
485    /// `a = new b(...)` with new.target in `b + 1` and the elements of the
486    /// array in `b + 2` as the arguments.
487    NewSpread: Call,
488    Return: A,
489    ReturnUndefined: None,
490    Throw: A,
491    /// `a = ToPropertyKey(b)` for the base `c` of a compound assignment: a
492    /// `null` or `undefined` base throws first.
493    ToPropertyKeyOf: ABC,
494    /// Puts the cell in `a` back in the TDZ (a lexical scope entered again):
495    /// closures already holding it see the new binding state.
496    ClearCell: A,
497    /// `a = b + (b + 1) + … + (b + imm - 1)`, a chain of `+` from the left
498    /// whose string concatenations join once; past the first two, the
499    /// operands are primitive constants.
500    ConcatN: ABRange,
501    /// `Jmp` to a target at or before itself: a loop's back-edge, which
502    /// counts down to the next interrupt poll.
503    JmpBack: Jump,
504}
505
506/// The flags of `CheckGlobalVar`, `DeclareGlobal` and `DefineGlobal`, in
507/// `b`.
508pub mod global {
509    /// The name is a function declaration's.
510    pub const FUNCTION: u8 = 1 << 0;
511    /// The code is `eval` code: its bindings are configurable.
512    pub const EVAL: u8 = 1 << 1;
513    /// `CheckGlobalVar`: only whether a lexical declaration has the name
514    /// (16.1.7 step 4).
515    pub const LEXICAL_CONFLICT: u8 = 1 << 2;
516    /// `CheckGlobalVar`: only whether the global object can take the
517    /// declaration (CanDeclareGlobalFunction or CanDeclareGlobalVar).
518    pub const DEFINABLE: u8 = 1 << 3;
519}
520
521/// The flags of `DefineMethod` and `DefineMethodComputed`.
522pub mod method {
523    pub const ENUMERABLE: u32 = 1 << 0;
524    pub const GETTER: u32 = 1 << 1;
525    pub const SETTER: u32 = 1 << 2;
526}
527
528impl Opcode {
529    /// Whether control never falls through to the next instruction.
530    pub const fn is_terminator(self) -> bool {
531        matches!(
532            self,
533            Opcode::Jmp
534                | Opcode::JmpBack
535                | Opcode::Ret
536                | Opcode::TailCall
537                | Opcode::TailCallMethod
538                | Opcode::Return
539                | Opcode::ReturnUndefined
540                | Opcode::Throw
541                | Opcode::ThrowConstAssign
542                | Opcode::ThrowReference
543                | Opcode::ThrowInternal
544        )
545    }
546}