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}