bytecode_lang/inst.rs
1//! The instruction set.
2//!
3//! One table, the `isa!` invocation below, defines every instruction: its
4//! opcode byte, its mnemonic, and each operand's name, type, and slot in the
5//! eight-byte word. The [`Inst`] enum, [`Opcode`], the per-opcode
6//! [`FieldSpec`] metadata, the encoder ([`Inst::to_bytes`]), the decoder
7//! ([`Inst::from_bytes`]), and the disassembler's rendering are all generated
8//! from that one table, so they cannot disagree.
9//!
10//! # The eight-byte word
11//!
12//! ```text
13//! byte 0 1 2..4 4..6 6..8
14//! opcode A B C D
15//! \----W----/
16//! ```
17//!
18//! `A` is one byte (a modifier: a type, a policy, a count), `B`, `C`, and `D`
19//! are 16-bit (registers and per-function refs), and `W` is the 32-bit word
20//! overlapping `C` and `D` (module indices, branch targets, immediates). All
21//! little-endian. Bytes of slots an instruction does not use are zero, and the
22//! decoder rejects anything else, so every instruction has exactly one
23//! encoding. The decoded [`Inst`] is itself eight bytes and `Copy`: a function
24//! body is a flat `&[Inst]` that an interpreter walks with a program counter
25//! and never re-parses (the bvm-lang design).
26
27use core::fmt;
28
29use crate::ids::{
30 ConstId, FieldIdx, FuncId, GlobalId, ImportId, NameRef, Reg, TableId, Target, TypeRef, UpvalIdx,
31};
32use crate::module::{Const, Function, Module};
33use crate::policy::{FloatTy, IntConv, IntOp, IntPair, IntTy, Overflow, Policy};
34use crate::types::{Kind, Prim};
35
36/// Where an operand lives in the eight-byte instruction word.
37///
38/// # Examples
39///
40/// ```
41/// use bytecode_lang::Slot;
42///
43/// assert_eq!(Slot::A.width(), 8);
44/// assert_eq!(Slot::W.width(), 32);
45/// assert_eq!(Slot::C.shift(), 32);
46/// ```
47#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
48pub enum Slot {
49 /// Byte 1: an 8-bit modifier or count.
50 A,
51 /// Bytes 2–3: a 16-bit operand.
52 B,
53 /// Bytes 4–5: a 16-bit operand.
54 C,
55 /// Bytes 6–7: a 16-bit operand.
56 D,
57 /// Bytes 4–7: a 32-bit operand (overlaps `C` and `D`).
58 W,
59}
60
61impl Slot {
62 /// The slot's bit offset within the little-endian 64-bit word.
63 ///
64 /// # Examples
65 ///
66 /// ```
67 /// use bytecode_lang::Slot;
68 ///
69 /// assert_eq!(Slot::A.shift(), 8);
70 /// assert_eq!(Slot::D.shift(), 48);
71 /// ```
72 #[must_use]
73 pub const fn shift(self) -> u32 {
74 match self {
75 Slot::A => 8,
76 Slot::B => 16,
77 Slot::C | Slot::W => 32,
78 Slot::D => 48,
79 }
80 }
81
82 /// The slot's width in bits.
83 ///
84 /// # Examples
85 ///
86 /// ```
87 /// use bytecode_lang::Slot;
88 ///
89 /// assert_eq!(Slot::B.width(), 16);
90 /// ```
91 #[must_use]
92 pub const fn width(self) -> u32 {
93 match self {
94 Slot::A => 8,
95 Slot::B | Slot::C | Slot::D => 16,
96 Slot::W => 32,
97 }
98 }
99
100 const fn mask(self) -> u64 {
101 (1u64 << self.width()) - 1
102 }
103}
104
105/// What an operand is: the type of one [`Inst`] field.
106///
107/// Modifier kinds ([`is_modifier`](FieldKind::is_modifier)) print as a
108/// mnemonic suffix (`iadd.i64.wrap`); the others print as operands.
109///
110/// # Examples
111///
112/// ```
113/// use bytecode_lang::FieldKind;
114///
115/// assert!(FieldKind::IntOp.is_modifier());
116/// assert!(!FieldKind::Reg.is_modifier());
117/// assert_eq!(FieldKind::Target.bits(), 32);
118/// ```
119#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
120pub enum FieldKind {
121 /// A [`Reg`].
122 Reg,
123 /// A branch [`Target`].
124 Target,
125 /// A [`ConstId`].
126 Const,
127 /// A [`FuncId`].
128 Func,
129 /// An [`ImportId`].
130 Import,
131 /// A [`GlobalId`].
132 Global,
133 /// A [`TableId`].
134 Table,
135 /// A [`NameRef`].
136 Name,
137 /// A [`TypeRef`].
138 TypeRef,
139 /// A [`FieldIdx`].
140 Field,
141 /// An [`UpvalIdx`].
142 Upval,
143 /// A 32-bit signed immediate.
144 Imm32,
145 /// An 8-bit count (arguments, string parts).
146 Count,
147 /// A boolean flag.
148 Bool,
149 /// An [`IntTy`] modifier.
150 IntTy,
151 /// A [`FloatTy`] modifier.
152 FloatTy,
153 /// A [`Policy`] modifier.
154 Policy,
155 /// An [`IntOp`] modifier.
156 IntOp,
157 /// An [`IntConv`] modifier.
158 IntConv,
159 /// An [`IntPair`] modifier.
160 IntPair,
161 /// A [`Kind`] modifier.
162 Kind,
163 /// A [`Prim`] modifier.
164 Prim,
165}
166
167impl FieldKind {
168 /// Whether the field is a modifier (printed as a mnemonic suffix).
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use bytecode_lang::FieldKind;
174 ///
175 /// assert!(FieldKind::Kind.is_modifier());
176 /// assert!(!FieldKind::Count.is_modifier());
177 /// ```
178 #[must_use]
179 pub const fn is_modifier(self) -> bool {
180 matches!(
181 self,
182 FieldKind::IntTy
183 | FieldKind::FloatTy
184 | FieldKind::Policy
185 | FieldKind::IntOp
186 | FieldKind::IntConv
187 | FieldKind::IntPair
188 | FieldKind::Kind
189 | FieldKind::Prim
190 )
191 }
192
193 /// The field's width in bits, which is also the width of the slot it
194 /// must occupy.
195 ///
196 /// # Examples
197 ///
198 /// ```
199 /// use bytecode_lang::FieldKind;
200 ///
201 /// assert_eq!(FieldKind::Reg.bits(), 16);
202 /// assert_eq!(FieldKind::Policy.bits(), 8);
203 /// ```
204 #[must_use]
205 pub const fn bits(self) -> u32 {
206 match self {
207 FieldKind::Reg
208 | FieldKind::Name
209 | FieldKind::TypeRef
210 | FieldKind::Field
211 | FieldKind::Upval => 16,
212 FieldKind::Target
213 | FieldKind::Const
214 | FieldKind::Func
215 | FieldKind::Import
216 | FieldKind::Global
217 | FieldKind::Table
218 | FieldKind::Imm32 => 32,
219 _ => 8,
220 }
221 }
222}
223
224/// One operand of an opcode: its field name, its kind, and its slot.
225///
226/// # Examples
227///
228/// ```
229/// use bytecode_lang::{FieldKind, Opcode, Slot};
230///
231/// let fields = Opcode::Mov.fields();
232/// assert_eq!(fields[0].name, "dst");
233/// assert_eq!((fields[0].kind, fields[0].slot), (FieldKind::Reg, Slot::B));
234/// ```
235#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
236pub struct FieldSpec {
237 /// The [`Inst`] field's name.
238 pub name: &'static str,
239 /// What the field is.
240 pub kind: FieldKind,
241 /// Where it lives in the instruction word.
242 pub slot: Slot,
243}
244
245/// Why eight bytes are not an instruction.
246///
247/// # Examples
248///
249/// ```
250/// use bytecode_lang::{Inst, InstError, Opcode};
251///
252/// assert_eq!(Inst::from_bytes([0xff, 0, 0, 0, 0, 0, 0, 0]), Err(InstError::UnknownOpcode(0xff)));
253/// // `nop` with a non-zero unused byte has two encodings otherwise, so it is refused.
254/// assert_eq!(Inst::from_bytes([0x00, 1, 0, 0, 0, 0, 0, 0]), Err(InstError::NonCanonical(Opcode::Nop)));
255/// ```
256#[derive(Clone, Copy, PartialEq, Eq, Debug)]
257#[non_exhaustive]
258pub enum InstError {
259 /// The opcode byte names no instruction.
260 UnknownOpcode(u8),
261 /// A modifier field holds a value its type does not define (for
262 /// example an integer type code above 7, or a boolean above 1).
263 InvalidOperand {
264 /// The instruction.
265 opcode: Opcode,
266 /// The field's name.
267 field: &'static str,
268 },
269 /// A byte the instruction does not use is not zero.
270 NonCanonical(Opcode),
271}
272
273impl fmt::Display for InstError {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 match self {
276 InstError::UnknownOpcode(op) => write!(f, "unknown opcode {op:#04x}"),
277 InstError::InvalidOperand { opcode, field } => {
278 write!(f, "invalid `{field}` operand of `{}`", opcode.mnemonic())
279 }
280 InstError::NonCanonical(opcode) => {
281 write!(
282 f,
283 "`{}` has non-zero bytes in unused slots",
284 opcode.mnemonic()
285 )
286 }
287 }
288 }
289}
290
291impl core::error::Error for InstError {}
292
293/// Rendering context: label positions and the module, when the caller has
294/// them (the module disassembler), or nothing (`Inst`'s `Display`).
295#[derive(Clone, Copy)]
296pub(crate) struct Cx<'a> {
297 /// Sorted pcs that are branch targets; `L<i>` names `labels[i]`.
298 pub(crate) labels: &'a [u32],
299 pub(crate) module: Option<&'a Module>,
300 pub(crate) func: Option<&'a Function>,
301}
302
303impl Cx<'_> {
304 pub(crate) const EMPTY: Cx<'static> = Cx {
305 labels: &[],
306 module: None,
307 func: None,
308 };
309}
310
311/// Writes ` ; a, b` comments after an instruction, starting the comment
312/// only if there is something to say.
313pub(crate) struct Note<'w> {
314 w: &'w mut dyn fmt::Write,
315 started: bool,
316}
317
318impl Note<'_> {
319 fn item(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
320 self.w.write_str(if self.started { ", " } else { " ; " })?;
321 self.started = true;
322 self.w.write_fmt(args)
323 }
324}
325
326/// Writes `s` as a quoted, escaped string of at most 40 characters.
327pub(crate) fn write_quoted(w: &mut dyn fmt::Write, s: &str) -> fmt::Result {
328 const MAX: usize = 40;
329 match s.char_indices().nth(MAX) {
330 Some((cut, _)) => write!(w, "{:?}...", s.get(..cut).unwrap_or(s)),
331 None => write!(w, "{s:?}"),
332 }
333}
334
335/// A short description of a constant for disassembly comments.
336pub(crate) fn write_const_preview(
337 w: &mut dyn fmt::Write,
338 module: &Module,
339 c: &Const,
340) -> fmt::Result {
341 match c {
342 Const::Str(id) => match module.string(*id) {
343 Some(s) => write_quoted(w, s),
344 None => write!(w, "str {id} <invalid>"),
345 },
346 Const::Bytes(bytes) => write!(w, "bytes[{}]", bytes.len()),
347 Const::Array(items) => write!(w, "array[{}]", items.len()),
348 Const::Map(entries) => write!(w, "map[{}]", entries.len()),
349 other => write!(w, "{other}"),
350 }
351}
352
353/// A type that can sit in an instruction slot.
354pub(crate) trait Operand: Copy + fmt::Display {
355 const KIND: FieldKind;
356
357 fn to_raw(self) -> u32;
358
359 /// The value for a raw slot, or `None` if the raw value names none.
360 /// Strict: `to_raw(from_raw(r)?) == r` for every accepted `r`.
361 fn from_raw(raw: u32) -> Option<Self>;
362
363 /// Modifiers print `.value` after the mnemonic.
364 fn suffix(self, w: &mut dyn fmt::Write) -> fmt::Result {
365 if Self::KIND.is_modifier() {
366 write!(w, ".{self}")
367 } else {
368 Ok(())
369 }
370 }
371
372 fn render(self, w: &mut dyn fmt::Write, _cx: &Cx<'_>) -> fmt::Result {
373 write!(w, "{self}")
374 }
375
376 fn comment(self, _cx: &Cx<'_>, _note: &mut Note<'_>) -> fmt::Result {
377 Ok(())
378 }
379
380 /// The overflow policy this field carries, if it carries one.
381 fn overflow(self) -> Option<Overflow> {
382 None
383 }
384
385 /// The register this field names, if it is a register.
386 fn as_reg(self) -> Option<Reg> {
387 None
388 }
389
390 /// The branch target this field holds, if it is one.
391 #[inline(always)]
392 fn as_target(self) -> Option<Target> {
393 None
394 }
395
396 /// Whether this field carries `overflow = promote`. A constant `false`
397 /// for every kind but the three policy modifiers, so the generated
398 /// per-instruction test folds to a bit test or to nothing.
399 #[inline(always)]
400 fn is_promote(self) -> bool {
401 false
402 }
403}
404
405/// Whether a field name is the conventional destination register.
406fn is_dst(name: &str) -> bool {
407 name == "dst"
408}
409
410macro_rules! operand_u16 {
411 ($($ty:ident => $kind:ident),* $(,)?) => {$(
412 impl Operand for $ty {
413 const KIND: FieldKind = FieldKind::$kind;
414 fn to_raw(self) -> u32 {
415 u32::from(self.0)
416 }
417 fn from_raw(raw: u32) -> Option<Self> {
418 u16::try_from(raw).ok().map($ty)
419 }
420 }
421 )*};
422}
423
424macro_rules! operand_u32 {
425 ($($ty:ident => $kind:ident),* $(,)?) => {$(
426 impl Operand for $ty {
427 const KIND: FieldKind = FieldKind::$kind;
428 fn to_raw(self) -> u32 {
429 self.0
430 }
431 fn from_raw(raw: u32) -> Option<Self> {
432 Some($ty(raw))
433 }
434 }
435 )*};
436}
437
438macro_rules! operand_code {
439 ($($ty:ident => $kind:ident),* $(,)?) => {$(
440 impl Operand for $ty {
441 const KIND: FieldKind = FieldKind::$kind;
442 fn to_raw(self) -> u32 {
443 u32::from(self.code())
444 }
445 fn from_raw(raw: u32) -> Option<Self> {
446 u8::try_from(raw).ok().and_then($ty::from_code)
447 }
448 }
449 )*};
450}
451
452macro_rules! operand_bits {
453 ($($ty:ident => $kind:ident),* $(,)?) => {$(
454 impl Operand for $ty {
455 const KIND: FieldKind = FieldKind::$kind;
456 fn to_raw(self) -> u32 {
457 u32::from(self.bits())
458 }
459 fn from_raw(raw: u32) -> Option<Self> {
460 u8::try_from(raw).ok().and_then($ty::from_bits)
461 }
462 }
463 )*};
464}
465
466operand_u16!(UpvalIdx => Upval);
467operand_u32!(TableId => Table);
468operand_code!(IntTy => IntTy, FloatTy => FloatTy, Kind => Kind, Prim => Prim);
469operand_bits!(IntPair => IntPair);
470
471impl Operand for Reg {
472 const KIND: FieldKind = FieldKind::Reg;
473 fn to_raw(self) -> u32 {
474 u32::from(self.0)
475 }
476 fn from_raw(raw: u32) -> Option<Self> {
477 u16::try_from(raw).ok().map(Reg)
478 }
479 fn as_reg(self) -> Option<Reg> {
480 Some(self)
481 }
482}
483
484impl Operand for IntOp {
485 const KIND: FieldKind = FieldKind::IntOp;
486 fn to_raw(self) -> u32 {
487 u32::from(self.bits())
488 }
489 fn from_raw(raw: u32) -> Option<Self> {
490 u8::try_from(raw).ok().and_then(IntOp::from_bits)
491 }
492 fn overflow(self) -> Option<Overflow> {
493 Some(self.policy().overflow())
494 }
495 #[inline(always)]
496 fn is_promote(self) -> bool {
497 (self.bits() >> 3) & 3 == 3
498 }
499}
500
501impl Operand for IntConv {
502 const KIND: FieldKind = FieldKind::IntConv;
503 fn to_raw(self) -> u32 {
504 u32::from(self.bits())
505 }
506 fn from_raw(raw: u32) -> Option<Self> {
507 u8::try_from(raw).ok().and_then(IntConv::from_bits)
508 }
509 fn overflow(self) -> Option<Overflow> {
510 Some(IntConv::overflow(self))
511 }
512 #[inline(always)]
513 fn is_promote(self) -> bool {
514 self.bits() >> 6 == 3
515 }
516}
517
518impl Operand for FieldIdx {
519 const KIND: FieldKind = FieldKind::Field;
520 fn to_raw(self) -> u32 {
521 u32::from(self.0)
522 }
523 fn from_raw(raw: u32) -> Option<Self> {
524 u16::try_from(raw).ok().map(FieldIdx)
525 }
526}
527
528impl Operand for Policy {
529 const KIND: FieldKind = FieldKind::Policy;
530 fn to_raw(self) -> u32 {
531 u32::from(self.bits())
532 }
533 fn from_raw(raw: u32) -> Option<Self> {
534 u8::try_from(raw).ok().and_then(Policy::from_bits)
535 }
536 /// The default policy adds no suffix.
537 fn suffix(self, w: &mut dyn fmt::Write) -> fmt::Result {
538 if self.is_default() {
539 Ok(())
540 } else {
541 write!(w, ".{self}")
542 }
543 }
544 fn overflow(self) -> Option<Overflow> {
545 Some(Policy::overflow(self))
546 }
547 #[inline(always)]
548 fn is_promote(self) -> bool {
549 self.bits() & 3 == 3
550 }
551}
552
553impl Operand for Target {
554 const KIND: FieldKind = FieldKind::Target;
555 fn to_raw(self) -> u32 {
556 self.0
557 }
558 fn from_raw(raw: u32) -> Option<Self> {
559 Some(Target(raw))
560 }
561 #[inline(always)]
562 fn as_target(self) -> Option<Target> {
563 Some(self)
564 }
565 /// `L<n>` when the target has a label, `@pc` otherwise.
566 fn render(self, w: &mut dyn fmt::Write, cx: &Cx<'_>) -> fmt::Result {
567 match cx.labels.binary_search(&self.0) {
568 Ok(label) => write!(w, "L{label}"),
569 Err(_) => write!(w, "{self}"),
570 }
571 }
572}
573
574impl Operand for ConstId {
575 const KIND: FieldKind = FieldKind::Const;
576 fn to_raw(self) -> u32 {
577 self.0
578 }
579 fn from_raw(raw: u32) -> Option<Self> {
580 Some(ConstId(raw))
581 }
582 fn comment(self, cx: &Cx<'_>, note: &mut Note<'_>) -> fmt::Result {
583 let Some(module) = cx.module else {
584 return Ok(());
585 };
586 match module.constant(self) {
587 Some(c) => {
588 let mut text = alloc::string::String::new();
589 write_const_preview(&mut text, module, c)?;
590 note.item(format_args!("{text}"))
591 }
592 None => note.item(format_args!("{self} <invalid>")),
593 }
594 }
595}
596
597impl Operand for FuncId {
598 const KIND: FieldKind = FieldKind::Func;
599 fn to_raw(self) -> u32 {
600 self.0
601 }
602 fn from_raw(raw: u32) -> Option<Self> {
603 Some(FuncId(raw))
604 }
605 fn comment(self, cx: &Cx<'_>, note: &mut Note<'_>) -> fmt::Result {
606 let Some(module) = cx.module else {
607 return Ok(());
608 };
609 match module.function(self).and_then(|f| module.string(f.name())) {
610 Some(name) => note.item(format_args!("{name:?}")),
611 None => note.item(format_args!("{self} <invalid>")),
612 }
613 }
614}
615
616impl Operand for ImportId {
617 const KIND: FieldKind = FieldKind::Import;
618 fn to_raw(self) -> u32 {
619 self.0
620 }
621 fn from_raw(raw: u32) -> Option<Self> {
622 Some(ImportId(raw))
623 }
624 fn comment(self, cx: &Cx<'_>, note: &mut Note<'_>) -> fmt::Result {
625 let Some(module) = cx.module else {
626 return Ok(());
627 };
628 let names = module
629 .import(self)
630 .and_then(|i| Some((module.string(i.module)?, module.string(i.name)?)));
631 match names {
632 Some((m, n)) => note.item(format_args!("{m:?}.{n:?}")),
633 None => note.item(format_args!("{self} <invalid>")),
634 }
635 }
636}
637
638impl Operand for GlobalId {
639 const KIND: FieldKind = FieldKind::Global;
640 fn to_raw(self) -> u32 {
641 self.0
642 }
643 fn from_raw(raw: u32) -> Option<Self> {
644 Some(GlobalId(raw))
645 }
646 fn comment(self, cx: &Cx<'_>, note: &mut Note<'_>) -> fmt::Result {
647 let Some(module) = cx.module else {
648 return Ok(());
649 };
650 match module.global(self).and_then(|g| module.string(g.name)) {
651 Some(name) => note.item(format_args!("{name:?}")),
652 None => note.item(format_args!("{self} <invalid>")),
653 }
654 }
655}
656
657impl Operand for NameRef {
658 const KIND: FieldKind = FieldKind::Name;
659 fn to_raw(self) -> u32 {
660 u32::from(self.0)
661 }
662 fn from_raw(raw: u32) -> Option<Self> {
663 u16::try_from(raw).ok().map(NameRef)
664 }
665 fn comment(self, cx: &Cx<'_>, note: &mut Note<'_>) -> fmt::Result {
666 let (Some(module), Some(func)) = (cx.module, cx.func) else {
667 return Ok(());
668 };
669 match func
670 .names()
671 .get(self.index())
672 .and_then(|&s| module.string(s))
673 {
674 Some(name) => note.item(format_args!("{name:?}")),
675 None => note.item(format_args!("{self} <invalid>")),
676 }
677 }
678}
679
680impl Operand for TypeRef {
681 const KIND: FieldKind = FieldKind::TypeRef;
682 fn to_raw(self) -> u32 {
683 u32::from(self.0)
684 }
685 fn from_raw(raw: u32) -> Option<Self> {
686 u16::try_from(raw).ok().map(TypeRef)
687 }
688 fn comment(self, cx: &Cx<'_>, note: &mut Note<'_>) -> fmt::Result {
689 let Some(func) = cx.func else { return Ok(()) };
690 match func.type_refs().get(self.index()) {
691 Some(ty) => note.item(format_args!("{ty}")),
692 None => note.item(format_args!("{self} <invalid>")),
693 }
694 }
695}
696
697impl Operand for i32 {
698 const KIND: FieldKind = FieldKind::Imm32;
699 fn to_raw(self) -> u32 {
700 u32::from_le_bytes(self.to_le_bytes())
701 }
702 fn from_raw(raw: u32) -> Option<Self> {
703 Some(i32::from_le_bytes(raw.to_le_bytes()))
704 }
705}
706
707impl Operand for u8 {
708 const KIND: FieldKind = FieldKind::Count;
709 fn to_raw(self) -> u32 {
710 u32::from(self)
711 }
712 fn from_raw(raw: u32) -> Option<Self> {
713 u8::try_from(raw).ok()
714 }
715}
716
717impl Operand for bool {
718 const KIND: FieldKind = FieldKind::Bool;
719 fn to_raw(self) -> u32 {
720 u32::from(self)
721 }
722 fn from_raw(raw: u32) -> Option<Self> {
723 match raw {
724 0 => Some(false),
725 1 => Some(true),
726 _ => None,
727 }
728 }
729}
730
731/// The bits of the word an opcode's fields (and the opcode byte) occupy.
732const fn used_bits(fields: &[FieldSpec]) -> u64 {
733 let mut used = 0xff;
734 let mut i = 0;
735 while i < fields.len() {
736 let slot = fields[i].slot;
737 used |= slot.mask() << slot.shift();
738 i += 1;
739 }
740 used
741}
742
743/// Whether `word` sets a bit outside `used`. (A function rather than an
744/// inline expression: for opcodes that use every slot the mask is zero,
745/// which is correct but reads as a mistake to a linter.)
746#[inline(always)]
747const fn has_unused_bits(word: u64, used: u64) -> bool {
748 word & !used != 0
749}
750
751/// Reads one field from a raw word.
752#[inline]
753fn read_field<T: Operand>(
754 word: u64,
755 slot: Slot,
756 opcode: Opcode,
757 field: &'static str,
758) -> Result<T, InstError> {
759 // The mask keeps the value within the slot's width, which is at most 32
760 // bits, so the narrowing is exact.
761 let raw = ((word >> slot.shift()) & slot.mask()) as u32;
762 T::from_raw(raw).ok_or(InstError::InvalidOperand { opcode, field })
763}
764
765macro_rules! isa {
766 (
767 $(
768 $(#[$meta:meta])*
769 $code:literal $name:ident $mnemonic:literal {
770 $( $(#[$fmeta:meta])* $field:ident : $ty:ident @ $slot:ident ),* $(,)?
771 }
772 )*
773 ) => {
774 /// One LSB instruction, decoded.
775 ///
776 /// Every variant is eight bytes and `Copy`. Field order is display
777 /// order; [`Opcode::fields`] gives each field's slot in the encoded
778 /// word. The normative semantics of every instruction, every operand
779 /// type rule, and every error it can raise are in `specs/LSB.md`;
780 /// each variant's documentation summarizes them.
781 ///
782 /// *Window operands.* Instructions that need more registers than
783 /// eight bytes hold read them from consecutive registers: a call's
784 /// arguments are `dst+1 ..= dst+argc`, a closure's captures follow
785 /// its destination, `str_slice` reads its end index from `range+1`.
786 ///
787 /// The enum is deliberately exhaustive: every execution tier must
788 /// implement every instruction, and a new one is a format change.
789 ///
790 /// # Examples
791 ///
792 /// ```
793 /// use bytecode_lang::{Inst, IntOp, IntTy, Opcode, Reg};
794 ///
795 /// let add = Inst::IAdd { op: IntOp::new(IntTy::I64), dst: Reg(2), lhs: Reg(0), rhs: Reg(1) };
796 /// assert_eq!(add.opcode(), Opcode::IAdd);
797 /// assert_eq!(add.to_string(), "iadd.i64 r2, r0, r1");
798 /// assert_eq!(Inst::from_bytes(add.to_bytes()), Ok(add));
799 /// assert_eq!(core::mem::size_of::<Inst>(), 8);
800 /// ```
801 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
802 pub enum Inst {
803 $(
804 $(#[$meta])*
805 $name { $( $(#[$fmeta])* $field: $ty ),* },
806 )*
807 }
808
809 /// The operation an [`Inst`] performs: its first encoded byte.
810 ///
811 /// # Examples
812 ///
813 /// ```
814 /// use bytecode_lang::Opcode;
815 ///
816 /// assert_eq!(Opcode::from_u8(0x01), Some(Opcode::Mov));
817 /// assert_eq!(Opcode::Mov.mnemonic(), "mov");
818 /// assert_eq!(Opcode::Mov as u8, 0x01);
819 /// assert!(Opcode::ALL.len() > 150);
820 /// ```
821 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
822 #[repr(u8)]
823 pub enum Opcode {
824 $(
825 $(#[$meta])*
826 $name = $code,
827 )*
828 }
829
830 impl Opcode {
831 /// Every opcode, in table order.
832 pub const ALL: &'static [Opcode] = &[ $( Opcode::$name ),* ];
833
834 /// The opcode with this byte, or `None`.
835 ///
836 /// # Examples
837 ///
838 /// ```
839 /// use bytecode_lang::Opcode;
840 ///
841 /// assert_eq!(Opcode::from_u8(0xa0), Some(Opcode::Jmp));
842 /// assert_eq!(Opcode::from_u8(0xff), None);
843 /// ```
844 #[must_use]
845 pub const fn from_u8(byte: u8) -> Option<Opcode> {
846 match byte {
847 $( $code => Some(Opcode::$name), )*
848 _ => None,
849 }
850 }
851
852 /// The assembler name.
853 ///
854 /// # Examples
855 ///
856 /// ```
857 /// use bytecode_lang::Opcode;
858 ///
859 /// assert_eq!(Opcode::TailCall.mnemonic(), "tail_call");
860 /// ```
861 #[must_use]
862 pub const fn mnemonic(self) -> &'static str {
863 match self {
864 $( Opcode::$name => $mnemonic, )*
865 }
866 }
867
868 /// The opcode's operands, in display order, with their slots.
869 ///
870 /// # Examples
871 ///
872 /// ```
873 /// use bytecode_lang::{FieldKind, Opcode, Slot};
874 ///
875 /// let f = Opcode::Jmp.fields();
876 /// assert_eq!(f.len(), 1);
877 /// assert_eq!((f[0].kind, f[0].slot), (FieldKind::Target, Slot::W));
878 /// ```
879 #[must_use]
880 pub const fn fields(self) -> &'static [FieldSpec] {
881 match self {
882 $(
883 Opcode::$name => {
884 const FIELDS: &[FieldSpec] = &[
885 $( FieldSpec {
886 name: stringify!($field),
887 kind: <$ty as Operand>::KIND,
888 slot: Slot::$slot,
889 } ),*
890 ];
891 FIELDS
892 }
893 )*
894 }
895 }
896 }
897
898 impl Inst {
899 /// The instruction's opcode.
900 ///
901 /// # Examples
902 ///
903 /// ```
904 /// use bytecode_lang::{Inst, Opcode};
905 ///
906 /// assert_eq!(Inst::RetVoid {}.opcode(), Opcode::RetVoid);
907 /// ```
908 #[must_use]
909 pub const fn opcode(&self) -> Opcode {
910 match self {
911 $( Inst::$name { .. } => Opcode::$name, )*
912 }
913 }
914
915 /// The canonical eight-byte encoding.
916 ///
917 /// # Examples
918 ///
919 /// ```
920 /// use bytecode_lang::{Inst, Reg};
921 ///
922 /// let mov = Inst::Mov { dst: Reg(1), src: Reg(2) };
923 /// assert_eq!(mov.to_bytes(), [0x01, 0, 1, 0, 2, 0, 0, 0]);
924 /// ```
925 #[must_use]
926 #[inline]
927 pub fn to_bytes(&self) -> [u8; 8] {
928 let word: u64 = match *self {
929 $(
930 Inst::$name { $( $field ),* } => {
931 #[allow(unused_mut)]
932 let mut word = u64::from(Opcode::$name as u8);
933 $( word |= u64::from(Operand::to_raw($field)) << Slot::$slot.shift(); )*
934 word
935 }
936 )*
937 };
938 word.to_le_bytes()
939 }
940
941 /// Decodes eight bytes. Fails on an unknown opcode, on a modifier
942 /// value its type does not define, and on non-zero bytes in
943 /// unused slots, so every instruction has exactly one encoding.
944 ///
945 /// # Errors
946 ///
947 /// [`InstError`] says which of the three applies.
948 ///
949 /// # Examples
950 ///
951 /// ```
952 /// use bytecode_lang::{Inst, InstError, Opcode};
953 ///
954 /// assert_eq!(Inst::from_bytes([0, 0, 0, 0, 0, 0, 0, 0]), Ok(Inst::Nop {}));
955 /// // `load_bool` with 2 in its boolean slot.
956 /// assert_eq!(
957 /// Inst::from_bytes([0x06, 2, 0, 0, 0, 0, 0, 0]),
958 /// Err(InstError::InvalidOperand { opcode: Opcode::LoadBool, field: "val" }),
959 /// );
960 /// ```
961 #[inline]
962 pub fn from_bytes(bytes: [u8; 8]) -> Result<Inst, InstError> {
963 let word = u64::from_le_bytes(bytes);
964 let [opcode, ..] = bytes;
965 Ok(match opcode {
966 $(
967 $code => {
968 // Bits outside the opcode's slots must be zero;
969 // with every field strict (`from_raw` accepts
970 // only values it re-encodes identically), that
971 // makes the encoding unique.
972 const USED: u64 = used_bits(Opcode::$name.fields());
973 if has_unused_bits(word, USED) {
974 return Err(InstError::NonCanonical(Opcode::$name));
975 }
976 Inst::$name {
977 $( $field: read_field::<$ty>(
978 word,
979 Slot::$slot,
980 Opcode::$name,
981 stringify!($field),
982 )? ),*
983 }
984 }
985 )*
986 other => return Err(InstError::UnknownOpcode(other)),
987 })
988 }
989
990 /// The overflow policy the instruction carries (in an
991 /// [`IntOp`], [`Policy`], or [`IntConv`] modifier), or `None` for
992 /// instructions that carry none.
993 ///
994 /// # Examples
995 ///
996 /// ```
997 /// use bytecode_lang::{Inst, Overflow, Policy, Reg};
998 ///
999 /// let p = Policy::new().with_overflow(Overflow::Promote);
1000 /// let add = Inst::DAdd { dst: Reg(0), lhs: Reg(1), rhs: Reg(2), pol: p };
1001 /// assert_eq!(add.overflow(), Some(Overflow::Promote));
1002 /// assert_eq!(Inst::Nop {}.overflow(), None);
1003 /// ```
1004 #[must_use]
1005 #[allow(unused_mut, unused_variables, clippy::let_and_return)]
1006 pub fn overflow(&self) -> Option<Overflow> {
1007 match *self {
1008 $(
1009 Inst::$name { $( $field ),* } => {
1010 let mut found = None;
1011 $(
1012 if let Some(o) = Operand::overflow($field) {
1013 found = Some(o);
1014 }
1015 )*
1016 found
1017 }
1018 )*
1019 }
1020 }
1021
1022 /// What the builder checks of every instruction, in one
1023 /// dispatch: its branch target (if it is a branch) and whether
1024 /// it carries `overflow = promote`. Every arm but the branches
1025 /// and the policy-carrying instructions folds to a constant.
1026 #[inline]
1027 #[allow(unused_variables)]
1028 pub(crate) fn build_checks(&self) -> (Option<Target>, bool) {
1029 match *self {
1030 $(
1031 Inst::$name { $( $field ),* } => {
1032 let target: Option<Target> = None $( .or(Operand::as_target($field)) )*;
1033 (target, false $( || Operand::is_promote($field) )*)
1034 }
1035 )*
1036 }
1037 }
1038
1039 /// The register in the field named `dst`, if the instruction has one.
1040 #[allow(unused_mut, unused_variables, clippy::let_and_return)]
1041 pub(crate) fn dst(&self) -> Option<Reg> {
1042 match *self {
1043 $(
1044 Inst::$name { $( $field ),* } => {
1045 let mut dst = None;
1046 $(
1047 if is_dst(stringify!($field)) {
1048 dst = Operand::as_reg($field);
1049 }
1050 )*
1051 dst
1052 }
1053 )*
1054 }
1055 }
1056
1057 /// Renders `mnemonic.modifiers op, op ... ; comments`.
1058 #[allow(unused_assignments, unused_mut, unused_variables)]
1059 pub(crate) fn render(&self, w: &mut dyn fmt::Write, cx: &Cx<'_>) -> fmt::Result {
1060 match *self {
1061 $(
1062 Inst::$name { $( $field ),* } => {
1063 w.write_str($mnemonic)?;
1064 $( Operand::suffix($field, w)?; )*
1065 let mut sep = " ";
1066 $(
1067 if !<$ty as Operand>::KIND.is_modifier() {
1068 w.write_str(sep)?;
1069 sep = ", ";
1070 Operand::render($field, w, cx)?;
1071 }
1072 )*
1073 let mut note = Note { w, started: false };
1074 $( Operand::comment($field, cx, &mut note)?; )*
1075 Ok(())
1076 }
1077 )*
1078 }
1079 }
1080 }
1081 };
1082}
1083
1084isa! {
1085 // ---------------------------------------------------------------------
1086 // Moves, constants, globals
1087 // ---------------------------------------------------------------------
1088
1089 /// Does nothing.
1090 0x00 Nop "nop" {}
1091
1092 /// `dst = src`. Both registers have the same declared type; the 64-bit
1093 /// slot is copied as is.
1094 0x01 Mov "mov" {
1095 /// Destination register.
1096 dst: Reg @ B,
1097 /// Source register.
1098 src: Reg @ C,
1099 }
1100
1101 /// `dst = constants[k]` in the destination's typed representation (an
1102 /// `int` constant into an integer register it fits, a `str` or `bytes`
1103 /// constant into a `str` register, an aggregate into a matching `ref`).
1104 0x02 LoadConst "load_const" {
1105 /// Destination register.
1106 dst: Reg @ B,
1107 /// The constant.
1108 k: ConstId @ W,
1109 }
1110
1111 /// `dst = constants[k]` boxed as a `dyn` value (integers become dynamic
1112 /// ints and must fit `i64`; floats become dynamic floats; aggregates
1113 /// become dynamic arrays and maps).
1114 0x03 DLoadConst "dload_const" {
1115 /// Destination `dyn` register.
1116 dst: Reg @ B,
1117 /// The constant.
1118 k: ConstId @ W,
1119 }
1120
1121 /// `dst = val` as an integer of type `ty`; the immediate is
1122 /// sign-extended and must be representable in `ty`.
1123 0x04 LoadInt "load_int" {
1124 /// Destination integer register.
1125 dst: Reg @ B,
1126 /// The immediate.
1127 val: i32 @ W,
1128 /// The destination's integer type.
1129 ty: IntTy @ A,
1130 }
1131
1132 /// `dst = val` as a dynamic integer.
1133 0x05 DLoadInt "dload_int" {
1134 /// Destination `dyn` register.
1135 dst: Reg @ B,
1136 /// The immediate.
1137 val: i32 @ W,
1138 }
1139
1140 /// `dst = val`.
1141 0x06 LoadBool "load_bool" {
1142 /// Destination `bool` register.
1143 dst: Reg @ B,
1144 /// The immediate.
1145 val: bool @ A,
1146 }
1147
1148 /// `dst = nil`: the dynamic nil, or the null `str`/`ref`.
1149 0x07 LoadNil "load_nil" {
1150 /// Destination `dyn`, `str`, or `ref` register.
1151 dst: Reg @ B,
1152 }
1153
1154 /// `dst =` a callable reference to a host function.
1155 0x08 LoadImport "load_import" {
1156 /// Destination `ref` (function type) or `dyn` register.
1157 dst: Reg @ B,
1158 /// The import.
1159 import: ImportId @ W,
1160 }
1161
1162 /// `dst = globals[global]`.
1163 0x09 GetGlobal "get_global" {
1164 /// Destination register of the global's type.
1165 dst: Reg @ B,
1166 /// The global.
1167 global: GlobalId @ W,
1168 }
1169
1170 /// `globals[global] = src`; the global must be mutable.
1171 0x0A SetGlobal "set_global" {
1172 /// The global.
1173 global: GlobalId @ W,
1174 /// Source register of the global's type.
1175 src: Reg @ B,
1176 }
1177
1178 // ---------------------------------------------------------------------
1179 // Integer arithmetic (OPS §3). Every one carries an IntOp: the operand
1180 // type and the complete policy set.
1181 // ---------------------------------------------------------------------
1182
1183 /// `dst = lhs + rhs`; overflow per `op.policy().overflow()`.
1184 0x10 IAdd "iadd" {
1185 /// Destination register.
1186 dst: Reg @ B,
1187 /// Left operand.
1188 lhs: Reg @ C,
1189 /// Right operand.
1190 rhs: Reg @ D,
1191 /// Operand type and policy.
1192 op: IntOp @ A,
1193 }
1194
1195 /// `dst = lhs - rhs`; overflow per policy.
1196 0x11 ISub "isub" {
1197 /// Destination register.
1198 dst: Reg @ B,
1199 /// Left operand.
1200 lhs: Reg @ C,
1201 /// Right operand.
1202 rhs: Reg @ D,
1203 /// Operand type and policy.
1204 op: IntOp @ A,
1205 }
1206
1207 /// `dst = lhs * rhs`; overflow per policy.
1208 0x12 IMul "imul" {
1209 /// Destination register.
1210 dst: Reg @ B,
1211 /// Left operand.
1212 lhs: Reg @ C,
1213 /// Right operand.
1214 rhs: Reg @ D,
1215 /// Operand type and policy.
1216 op: IntOp @ A,
1217 }
1218
1219 /// `dst = lhs / rhs`, truncating toward zero; `rhs == 0` per `div_zero`,
1220 /// signed `MIN / -1` per `overflow` (`wrap` gives `MIN`).
1221 0x13 IDiv "idiv" {
1222 /// Destination register.
1223 dst: Reg @ B,
1224 /// Dividend.
1225 lhs: Reg @ C,
1226 /// Divisor.
1227 rhs: Reg @ D,
1228 /// Operand type and policy.
1229 op: IntOp @ A,
1230 }
1231
1232 /// `dst = lhs rem rhs`, the sign following the dividend; `rhs == 0` per
1233 /// `div_zero`; signed `MIN rem -1` is `0`, never an error.
1234 0x14 IRem "irem" {
1235 /// Destination register.
1236 dst: Reg @ B,
1237 /// Dividend.
1238 lhs: Reg @ C,
1239 /// Divisor.
1240 rhs: Reg @ D,
1241 /// Operand type and policy.
1242 op: IntOp @ A,
1243 }
1244
1245 /// `dst = floor(lhs / rhs)` (Python `//`); zero and overflow rules as
1246 /// `idiv`.
1247 0x15 IFloorDiv "ifloor_div" {
1248 /// Destination register.
1249 dst: Reg @ B,
1250 /// Dividend.
1251 lhs: Reg @ C,
1252 /// Divisor.
1253 rhs: Reg @ D,
1254 /// Operand type and policy.
1255 op: IntOp @ A,
1256 }
1257
1258 /// `dst = lhs - rhs * floor_div(lhs, rhs)`, the sign following the
1259 /// divisor; `rhs == 0` per `div_zero`; signed `MIN floor_mod -1` is `0`.
1260 0x16 IFloorMod "ifloor_mod" {
1261 /// Destination register.
1262 dst: Reg @ B,
1263 /// Dividend.
1264 lhs: Reg @ C,
1265 /// Divisor.
1266 rhs: Reg @ D,
1267 /// Operand type and policy.
1268 op: IntOp @ A,
1269 }
1270
1271 /// `dst = lhs & rhs`.
1272 0x17 IAnd "iand" {
1273 /// Destination register.
1274 dst: Reg @ B,
1275 /// Left operand.
1276 lhs: Reg @ C,
1277 /// Right operand.
1278 rhs: Reg @ D,
1279 /// Operand type and policy.
1280 op: IntOp @ A,
1281 }
1282
1283 /// `dst = lhs | rhs`.
1284 0x18 IOr "ior" {
1285 /// Destination register.
1286 dst: Reg @ B,
1287 /// Left operand.
1288 lhs: Reg @ C,
1289 /// Right operand.
1290 rhs: Reg @ D,
1291 /// Operand type and policy.
1292 op: IntOp @ A,
1293 }
1294
1295 /// `dst = lhs ^ rhs`.
1296 0x19 IXor "ixor" {
1297 /// Destination register.
1298 dst: Reg @ B,
1299 /// Left operand.
1300 lhs: Reg @ C,
1301 /// Right operand.
1302 rhs: Reg @ D,
1303 /// Operand type and policy.
1304 op: IntOp @ A,
1305 }
1306
1307 /// `dst = lhs << rhs`; bits shifted out are discarded; an amount at least
1308 /// the width (or negative, for a signed type) per `shift`.
1309 0x1A IShl "ishl" {
1310 /// Destination register.
1311 dst: Reg @ B,
1312 /// Value to shift.
1313 lhs: Reg @ C,
1314 /// Shift amount, of the same type.
1315 rhs: Reg @ D,
1316 /// Operand type and policy.
1317 op: IntOp @ A,
1318 }
1319
1320 /// `dst = lhs >> rhs`, arithmetic for signed types and logical for
1321 /// unsigned; amount rule as `ishl`.
1322 0x1B IShr "ishr" {
1323 /// Destination register.
1324 dst: Reg @ B,
1325 /// Value to shift.
1326 lhs: Reg @ C,
1327 /// Shift amount, of the same type.
1328 rhs: Reg @ D,
1329 /// Operand type and policy.
1330 op: IntOp @ A,
1331 }
1332
1333 /// `dst = min(lhs, rhs)`.
1334 0x1C IMin "imin" {
1335 /// Destination register.
1336 dst: Reg @ B,
1337 /// Left operand.
1338 lhs: Reg @ C,
1339 /// Right operand.
1340 rhs: Reg @ D,
1341 /// Operand type and policy.
1342 op: IntOp @ A,
1343 }
1344
1345 /// `dst = max(lhs, rhs)`.
1346 0x1D IMax "imax" {
1347 /// Destination register.
1348 dst: Reg @ B,
1349 /// Left operand.
1350 lhs: Reg @ C,
1351 /// Right operand.
1352 rhs: Reg @ D,
1353 /// Operand type and policy.
1354 op: IntOp @ A,
1355 }
1356
1357 /// `dst = 0 - src`; `neg(MIN)` per `overflow`.
1358 0x1E INeg "ineg" {
1359 /// Destination register.
1360 dst: Reg @ B,
1361 /// Operand.
1362 src: Reg @ C,
1363 /// Operand type and policy.
1364 op: IntOp @ A,
1365 }
1366
1367 /// `dst = !src` (bitwise complement).
1368 0x1F INot "inot" {
1369 /// Destination register.
1370 dst: Reg @ B,
1371 /// Operand.
1372 src: Reg @ C,
1373 /// Operand type and policy.
1374 op: IntOp @ A,
1375 }
1376
1377 /// `dst = |src|`; `abs(MIN)` per `overflow`.
1378 0x20 IAbs "iabs" {
1379 /// Destination register.
1380 dst: Reg @ B,
1381 /// Operand.
1382 src: Reg @ C,
1383 /// Operand type and policy.
1384 op: IntOp @ A,
1385 }
1386
1387 /// `dst = lhs == rhs` (bool).
1388 0x21 IEq "ieq" {
1389 /// Destination `bool` register.
1390 dst: Reg @ B,
1391 /// Left operand.
1392 lhs: Reg @ C,
1393 /// Right operand.
1394 rhs: Reg @ D,
1395 /// Operand type (signedness decides ordering).
1396 ty: IntTy @ A,
1397 }
1398
1399 /// `dst = lhs != rhs` (bool).
1400 0x22 INe "ine" {
1401 /// Destination `bool` register.
1402 dst: Reg @ B,
1403 /// Left operand.
1404 lhs: Reg @ C,
1405 /// Right operand.
1406 rhs: Reg @ D,
1407 /// Operand type.
1408 ty: IntTy @ A,
1409 }
1410
1411 /// `dst = lhs < rhs` (bool), signed or unsigned per `ty`.
1412 0x23 ILt "ilt" {
1413 /// Destination `bool` register.
1414 dst: Reg @ B,
1415 /// Left operand.
1416 lhs: Reg @ C,
1417 /// Right operand.
1418 rhs: Reg @ D,
1419 /// Operand type.
1420 ty: IntTy @ A,
1421 }
1422
1423 /// `dst = lhs <= rhs` (bool).
1424 0x24 ILe "ile" {
1425 /// Destination `bool` register.
1426 dst: Reg @ B,
1427 /// Left operand.
1428 lhs: Reg @ C,
1429 /// Right operand.
1430 rhs: Reg @ D,
1431 /// Operand type.
1432 ty: IntTy @ A,
1433 }
1434
1435 /// `dst = lhs > rhs` (bool).
1436 0x25 IGt "igt" {
1437 /// Destination `bool` register.
1438 dst: Reg @ B,
1439 /// Left operand.
1440 lhs: Reg @ C,
1441 /// Right operand.
1442 rhs: Reg @ D,
1443 /// Operand type.
1444 ty: IntTy @ A,
1445 }
1446
1447 /// `dst = lhs >= rhs` (bool).
1448 0x26 IGe "ige" {
1449 /// Destination `bool` register.
1450 dst: Reg @ B,
1451 /// Left operand.
1452 lhs: Reg @ C,
1453 /// Right operand.
1454 rhs: Reg @ D,
1455 /// Operand type.
1456 ty: IntTy @ A,
1457 }
1458
1459 // ---------------------------------------------------------------------
1460 // Float arithmetic (OPS §4). Never an error: IEEE results throughout.
1461 // ---------------------------------------------------------------------
1462
1463 /// `dst = lhs + rhs` (round to nearest, ties to even).
1464 0x30 FAdd "fadd" {
1465 /// Destination register.
1466 dst: Reg @ B,
1467 /// Left operand.
1468 lhs: Reg @ C,
1469 /// Right operand.
1470 rhs: Reg @ D,
1471 /// Operand type.
1472 ty: FloatTy @ A,
1473 }
1474
1475 /// `dst = lhs - rhs`.
1476 0x31 FSub "fsub" {
1477 /// Destination register.
1478 dst: Reg @ B,
1479 /// Left operand.
1480 lhs: Reg @ C,
1481 /// Right operand.
1482 rhs: Reg @ D,
1483 /// Operand type.
1484 ty: FloatTy @ A,
1485 }
1486
1487 /// `dst = lhs * rhs`.
1488 0x32 FMul "fmul" {
1489 /// Destination register.
1490 dst: Reg @ B,
1491 /// Left operand.
1492 lhs: Reg @ C,
1493 /// Right operand.
1494 rhs: Reg @ D,
1495 /// Operand type.
1496 ty: FloatTy @ A,
1497 }
1498
1499 /// `dst = lhs / rhs`; division by zero gives ±inf or NaN.
1500 0x33 FDiv "fdiv" {
1501 /// Destination register.
1502 dst: Reg @ B,
1503 /// Dividend.
1504 lhs: Reg @ C,
1505 /// Divisor.
1506 rhs: Reg @ D,
1507 /// Operand type.
1508 ty: FloatTy @ A,
1509 }
1510
1511 /// `dst = fmod(lhs, rhs)`: the truncated remainder, sign of the dividend.
1512 0x34 FRem "frem" {
1513 /// Destination register.
1514 dst: Reg @ B,
1515 /// Dividend.
1516 lhs: Reg @ C,
1517 /// Divisor.
1518 rhs: Reg @ D,
1519 /// Operand type.
1520 ty: FloatTy @ A,
1521 }
1522
1523 /// `dst = remainder(lhs, rhs)`: IEEE 754 remainder (nearest quotient).
1524 0x35 FIeeeRem "fieee_rem" {
1525 /// Destination register.
1526 dst: Reg @ B,
1527 /// Dividend.
1528 lhs: Reg @ C,
1529 /// Divisor.
1530 rhs: Reg @ D,
1531 /// Operand type.
1532 ty: FloatTy @ A,
1533 }
1534
1535 /// `dst = min(lhs, rhs)`; NaN if either is NaN; `min(-0, +0) = -0`.
1536 0x36 FMin "fmin" {
1537 /// Destination register.
1538 dst: Reg @ B,
1539 /// Left operand.
1540 lhs: Reg @ C,
1541 /// Right operand.
1542 rhs: Reg @ D,
1543 /// Operand type.
1544 ty: FloatTy @ A,
1545 }
1546
1547 /// `dst = max(lhs, rhs)`; NaN if either is NaN; `max(-0, +0) = +0`.
1548 0x37 FMax "fmax" {
1549 /// Destination register.
1550 dst: Reg @ B,
1551 /// Left operand.
1552 lhs: Reg @ C,
1553 /// Right operand.
1554 rhs: Reg @ D,
1555 /// Operand type.
1556 ty: FloatTy @ A,
1557 }
1558
1559 /// `dst = fma(lhs, rhs, dst)`: fused multiply-add, correctly rounded.
1560 /// `dst` is both the addend and the result.
1561 0x38 FFma "ffma" {
1562 /// Addend in, result out.
1563 dst: Reg @ B,
1564 /// First factor.
1565 lhs: Reg @ C,
1566 /// Second factor.
1567 rhs: Reg @ D,
1568 /// Operand type.
1569 ty: FloatTy @ A,
1570 }
1571
1572 /// `dst = -src` (sign flip; payload preserved).
1573 0x39 FNeg "fneg" {
1574 /// Destination register.
1575 dst: Reg @ B,
1576 /// Operand.
1577 src: Reg @ C,
1578 /// Operand type.
1579 ty: FloatTy @ A,
1580 }
1581
1582 /// `dst = |src|` (sign clear; payload preserved).
1583 0x3A FAbs "fabs" {
1584 /// Destination register.
1585 dst: Reg @ B,
1586 /// Operand.
1587 src: Reg @ C,
1588 /// Operand type.
1589 ty: FloatTy @ A,
1590 }
1591
1592 /// `dst = sqrt(src)`, correctly rounded.
1593 0x3B FSqrt "fsqrt" {
1594 /// Destination register.
1595 dst: Reg @ B,
1596 /// Operand.
1597 src: Reg @ C,
1598 /// Operand type.
1599 ty: FloatTy @ A,
1600 }
1601
1602 /// `dst = floor(src)`.
1603 0x3C FFloor "ffloor" {
1604 /// Destination register.
1605 dst: Reg @ B,
1606 /// Operand.
1607 src: Reg @ C,
1608 /// Operand type.
1609 ty: FloatTy @ A,
1610 }
1611
1612 /// `dst = ceil(src)`.
1613 0x3D FCeil "fceil" {
1614 /// Destination register.
1615 dst: Reg @ B,
1616 /// Operand.
1617 src: Reg @ C,
1618 /// Operand type.
1619 ty: FloatTy @ A,
1620 }
1621
1622 /// `dst = trunc(src)` (toward zero).
1623 0x3E FTrunc "ftrunc" {
1624 /// Destination register.
1625 dst: Reg @ B,
1626 /// Operand.
1627 src: Reg @ C,
1628 /// Operand type.
1629 ty: FloatTy @ A,
1630 }
1631
1632 /// `dst = round(src)`, ties away from zero.
1633 0x3F FRound "fround" {
1634 /// Destination register.
1635 dst: Reg @ B,
1636 /// Operand.
1637 src: Reg @ C,
1638 /// Operand type.
1639 ty: FloatTy @ A,
1640 }
1641
1642 /// `dst = round(src)`, ties to even.
1643 0x40 FRoundEven "fround_even" {
1644 /// Destination register.
1645 dst: Reg @ B,
1646 /// Operand.
1647 src: Reg @ C,
1648 /// Operand type.
1649 ty: FloatTy @ A,
1650 }
1651
1652 /// `dst = lhs == rhs` (bool); false if either is NaN.
1653 0x41 FEq "feq" {
1654 /// Destination `bool` register.
1655 dst: Reg @ B,
1656 /// Left operand.
1657 lhs: Reg @ C,
1658 /// Right operand.
1659 rhs: Reg @ D,
1660 /// Operand type.
1661 ty: FloatTy @ A,
1662 }
1663
1664 /// `dst = lhs != rhs` (bool); true if either is NaN.
1665 0x42 FNe "fne" {
1666 /// Destination `bool` register.
1667 dst: Reg @ B,
1668 /// Left operand.
1669 lhs: Reg @ C,
1670 /// Right operand.
1671 rhs: Reg @ D,
1672 /// Operand type.
1673 ty: FloatTy @ A,
1674 }
1675
1676 /// `dst = lhs < rhs` (bool); false if either is NaN.
1677 0x43 FLt "flt" {
1678 /// Destination `bool` register.
1679 dst: Reg @ B,
1680 /// Left operand.
1681 lhs: Reg @ C,
1682 /// Right operand.
1683 rhs: Reg @ D,
1684 /// Operand type.
1685 ty: FloatTy @ A,
1686 }
1687
1688 /// `dst = lhs <= rhs` (bool); false if either is NaN.
1689 0x44 FLe "fle" {
1690 /// Destination `bool` register.
1691 dst: Reg @ B,
1692 /// Left operand.
1693 lhs: Reg @ C,
1694 /// Right operand.
1695 rhs: Reg @ D,
1696 /// Operand type.
1697 ty: FloatTy @ A,
1698 }
1699
1700 /// `dst = lhs > rhs` (bool); false if either is NaN.
1701 0x45 FGt "fgt" {
1702 /// Destination `bool` register.
1703 dst: Reg @ B,
1704 /// Left operand.
1705 lhs: Reg @ C,
1706 /// Right operand.
1707 rhs: Reg @ D,
1708 /// Operand type.
1709 ty: FloatTy @ A,
1710 }
1711
1712 /// `dst = lhs >= rhs` (bool); false if either is NaN.
1713 0x46 FGe "fge" {
1714 /// Destination `bool` register.
1715 dst: Reg @ B,
1716 /// Left operand.
1717 lhs: Reg @ C,
1718 /// Right operand.
1719 rhs: Reg @ D,
1720 /// Operand type.
1721 ty: FloatTy @ A,
1722 }
1723
1724 /// `dst = totalOrder(lhs, rhs)` as an `i8` of -1, 0, or 1 (IEEE 754
1725 /// total order: `-NaN < -inf < ... < -0 < +0 < ... < +inf < +NaN`).
1726 0x47 FTotalCmp "ftotal_cmp" {
1727 /// Destination `i8` register.
1728 dst: Reg @ B,
1729 /// Left operand.
1730 lhs: Reg @ C,
1731 /// Right operand.
1732 rhs: Reg @ D,
1733 /// Operand type.
1734 ty: FloatTy @ A,
1735 }
1736
1737 // ---------------------------------------------------------------------
1738 // Booleans, chars, references
1739 // ---------------------------------------------------------------------
1740
1741 /// `dst = !src` (logical).
1742 0x50 BNot "bnot" {
1743 /// Destination `bool` register.
1744 dst: Reg @ B,
1745 /// Operand.
1746 src: Reg @ C,
1747 }
1748
1749 /// `dst = lhs & rhs` (both evaluated; short-circuit is control flow).
1750 0x51 BAnd "band" {
1751 /// Destination `bool` register.
1752 dst: Reg @ B,
1753 /// Left operand.
1754 lhs: Reg @ C,
1755 /// Right operand.
1756 rhs: Reg @ D,
1757 }
1758
1759 /// `dst = lhs | rhs`.
1760 0x52 BOr "bor" {
1761 /// Destination `bool` register.
1762 dst: Reg @ B,
1763 /// Left operand.
1764 lhs: Reg @ C,
1765 /// Right operand.
1766 rhs: Reg @ D,
1767 }
1768
1769 /// `dst = lhs ^ rhs` (also boolean inequality).
1770 0x53 BXor "bxor" {
1771 /// Destination `bool` register.
1772 dst: Reg @ B,
1773 /// Left operand.
1774 lhs: Reg @ C,
1775 /// Right operand.
1776 rhs: Reg @ D,
1777 }
1778
1779 /// `dst = lhs == rhs` on `char` scalar values.
1780 0x54 CEq "ceq" {
1781 /// Destination `bool` register.
1782 dst: Reg @ B,
1783 /// Left operand.
1784 lhs: Reg @ C,
1785 /// Right operand.
1786 rhs: Reg @ D,
1787 }
1788
1789 /// `dst = lhs != rhs` on `char` scalar values.
1790 0x55 CNe "cne" {
1791 /// Destination `bool` register.
1792 dst: Reg @ B,
1793 /// Left operand.
1794 lhs: Reg @ C,
1795 /// Right operand.
1796 rhs: Reg @ D,
1797 }
1798
1799 /// `dst = lhs < rhs` on `char` scalar values.
1800 0x56 CLt "clt" {
1801 /// Destination `bool` register.
1802 dst: Reg @ B,
1803 /// Left operand.
1804 lhs: Reg @ C,
1805 /// Right operand.
1806 rhs: Reg @ D,
1807 }
1808
1809 /// `dst = lhs <= rhs` on `char` scalar values.
1810 0x57 CLe "cle" {
1811 /// Destination `bool` register.
1812 dst: Reg @ B,
1813 /// Left operand.
1814 lhs: Reg @ C,
1815 /// Right operand.
1816 rhs: Reg @ D,
1817 }
1818
1819 /// `dst = lhs > rhs` on `char` scalar values.
1820 0x58 CGt "cgt" {
1821 /// Destination `bool` register.
1822 dst: Reg @ B,
1823 /// Left operand.
1824 lhs: Reg @ C,
1825 /// Right operand.
1826 rhs: Reg @ D,
1827 }
1828
1829 /// `dst = lhs >= rhs` on `char` scalar values.
1830 0x59 CGe "cge" {
1831 /// Destination `bool` register.
1832 dst: Reg @ B,
1833 /// Left operand.
1834 lhs: Reg @ C,
1835 /// Right operand.
1836 rhs: Reg @ D,
1837 }
1838
1839 /// `dst = lhs` and `rhs` are the same object (or both `nil`). Operands
1840 /// are `str` or `ref`; string contents are not compared.
1841 0x5A RefEq "ref_eq" {
1842 /// Destination `bool` register.
1843 dst: Reg @ B,
1844 /// Left operand.
1845 lhs: Reg @ C,
1846 /// Right operand.
1847 rhs: Reg @ D,
1848 }
1849
1850 // ---------------------------------------------------------------------
1851 // Conversions (OPS §5)
1852 // ---------------------------------------------------------------------
1853
1854 /// `dst = src` converted between integer types: exact if it fits, else
1855 /// per the conversion's `overflow` (`wrap` truncates or sign-extends).
1856 0x60 IntCast "int_cast" {
1857 /// Destination register of type `conv.to()`.
1858 dst: Reg @ B,
1859 /// Source register of type `conv.from()`.
1860 src: Reg @ C,
1861 /// Source type, destination type, overflow policy.
1862 conv: IntConv @ A,
1863 }
1864
1865 /// Zero-extends a narrower integer to a wider one; never an error.
1866 0x61 Zext "zext" {
1867 /// Destination register.
1868 dst: Reg @ B,
1869 /// Source register.
1870 src: Reg @ C,
1871 /// Source and destination types (destination at least as wide).
1872 pair: IntPair @ A,
1873 }
1874
1875 /// Sign-extends a narrower integer to a wider one; never an error.
1876 0x62 Sext "sext" {
1877 /// Destination register.
1878 dst: Reg @ B,
1879 /// Source register.
1880 src: Reg @ C,
1881 /// Source and destination types (destination at least as wide).
1882 pair: IntPair @ A,
1883 }
1884
1885 /// Keeps the low bits of a wider integer; never an error.
1886 0x63 Trunc "trunc" {
1887 /// Destination register.
1888 dst: Reg @ B,
1889 /// Source register.
1890 src: Reg @ C,
1891 /// Source and destination types (destination at most as wide).
1892 pair: IntPair @ A,
1893 }
1894
1895 /// `dst = src` as `f32`, rounded to nearest, ties to even.
1896 0x64 IntToF32 "int_to_f32" {
1897 /// Destination `f32` register.
1898 dst: Reg @ B,
1899 /// Source integer register.
1900 src: Reg @ C,
1901 /// The source's integer type.
1902 ty: IntTy @ A,
1903 }
1904
1905 /// `dst = src` as `f64`, rounded to nearest, ties to even.
1906 0x65 IntToF64 "int_to_f64" {
1907 /// Destination `f64` register.
1908 dst: Reg @ B,
1909 /// Source integer register.
1910 src: Reg @ C,
1911 /// The source's integer type.
1912 ty: IntTy @ A,
1913 }
1914
1915 /// `dst = src` truncated toward zero to the integer type `op.ty()`;
1916 /// NaN or out of range per `op.policy().float_to_int()`.
1917 0x66 F32ToInt "f32_to_int" {
1918 /// Destination integer register.
1919 dst: Reg @ B,
1920 /// Source `f32` register.
1921 src: Reg @ C,
1922 /// Destination type and policy.
1923 op: IntOp @ A,
1924 }
1925
1926 /// `dst = src` truncated toward zero to the integer type `op.ty()`;
1927 /// NaN or out of range per `op.policy().float_to_int()`.
1928 0x67 F64ToInt "f64_to_int" {
1929 /// Destination integer register.
1930 dst: Reg @ B,
1931 /// Source `f64` register.
1932 src: Reg @ C,
1933 /// Destination type and policy.
1934 op: IntOp @ A,
1935 }
1936
1937 /// `dst = src` widened to `f64` (exact).
1938 0x68 F32ToF64 "f32_to_f64" {
1939 /// Destination `f64` register.
1940 dst: Reg @ B,
1941 /// Source `f32` register.
1942 src: Reg @ C,
1943 }
1944
1945 /// `dst = src` narrowed to `f32`, rounded to nearest, ties to even.
1946 0x69 F64ToF32 "f64_to_f32" {
1947 /// Destination `f32` register.
1948 dst: Reg @ B,
1949 /// Source `f64` register.
1950 src: Reg @ C,
1951 }
1952
1953 /// `dst` = the IEEE bits of `src` as an integer of the same width
1954 /// (`ty` is a 32- or 64-bit integer type).
1955 0x6A FloatToBits "float_to_bits" {
1956 /// Destination integer register.
1957 dst: Reg @ B,
1958 /// Source float register.
1959 src: Reg @ C,
1960 /// The destination's integer type.
1961 ty: IntTy @ A,
1962 }
1963
1964 /// `dst` = the float whose IEEE bits are `src` (`ty` is a 32- or 64-bit
1965 /// integer type; the float has the same width).
1966 0x6B BitsToFloat "bits_to_float" {
1967 /// Destination float register.
1968 dst: Reg @ B,
1969 /// Source integer register.
1970 src: Reg @ C,
1971 /// The source's integer type.
1972 ty: IntTy @ A,
1973 }
1974
1975 /// `dst = char(src)`; `InvalidChar` (E0005) if `src` (a `u32`) is not a
1976 /// Unicode scalar value.
1977 0x6C CharFromU32 "char_from_u32" {
1978 /// Destination `char` register.
1979 dst: Reg @ B,
1980 /// Source `u32` register.
1981 src: Reg @ C,
1982 }
1983
1984 /// `dst = src` as its `u32` scalar value. Total; an LSB addition to OPS.
1985 0x6D CharToU32 "char_to_u32" {
1986 /// Destination `u32` register.
1987 dst: Reg @ B,
1988 /// Source `char` register.
1989 src: Reg @ C,
1990 }
1991
1992 /// `dst = 1` if `src` is true, else `0`.
1993 0x6E BoolToInt "bool_to_int" {
1994 /// Destination integer register.
1995 dst: Reg @ B,
1996 /// Source `bool` register.
1997 src: Reg @ C,
1998 /// The destination's integer type.
1999 ty: IntTy @ A,
2000 }
2001
2002 // ---------------------------------------------------------------------
2003 // Dynamic values. Operands and results are `dyn`. Numbers take a fast
2004 // path per OPS (int is i64, mixed int/float computes in f64); every
2005 // other operand combination calls the module's hook or raises TypeError.
2006 // ---------------------------------------------------------------------
2007
2008 /// Dynamic `+`.
2009 0x70 DAdd "dadd" {
2010 /// Destination register.
2011 dst: Reg @ B,
2012 /// Left operand.
2013 lhs: Reg @ C,
2014 /// Right operand.
2015 rhs: Reg @ D,
2016 /// Policy for the integer path.
2017 pol: Policy @ A,
2018 }
2019
2020 /// Dynamic `-`.
2021 0x71 DSub "dsub" {
2022 /// Destination register.
2023 dst: Reg @ B,
2024 /// Left operand.
2025 lhs: Reg @ C,
2026 /// Right operand.
2027 rhs: Reg @ D,
2028 /// Policy for the integer path.
2029 pol: Policy @ A,
2030 }
2031
2032 /// Dynamic `*`.
2033 0x72 DMul "dmul" {
2034 /// Destination register.
2035 dst: Reg @ B,
2036 /// Left operand.
2037 lhs: Reg @ C,
2038 /// Right operand.
2039 rhs: Reg @ D,
2040 /// Policy for the integer path.
2041 pol: Policy @ A,
2042 }
2043
2044 /// Dynamic division: truncating for two ints, IEEE otherwise.
2045 0x73 DDiv "ddiv" {
2046 /// Destination register.
2047 dst: Reg @ B,
2048 /// Dividend.
2049 lhs: Reg @ C,
2050 /// Divisor.
2051 rhs: Reg @ D,
2052 /// Policy for the integer path.
2053 pol: Policy @ A,
2054 }
2055
2056 /// Dynamic remainder: `irem` for two ints, `frem` otherwise.
2057 0x74 DRem "drem" {
2058 /// Destination register.
2059 dst: Reg @ B,
2060 /// Dividend.
2061 lhs: Reg @ C,
2062 /// Divisor.
2063 rhs: Reg @ D,
2064 /// Policy for the integer path.
2065 pol: Policy @ A,
2066 }
2067
2068 /// Dynamic floor division (Python `//`).
2069 0x75 DFloorDiv "dfloor_div" {
2070 /// Destination register.
2071 dst: Reg @ B,
2072 /// Dividend.
2073 lhs: Reg @ C,
2074 /// Divisor.
2075 rhs: Reg @ D,
2076 /// Policy for the integer path.
2077 pol: Policy @ A,
2078 }
2079
2080 /// Dynamic floor modulo (Python `%`).
2081 0x76 DFloorMod "dfloor_mod" {
2082 /// Destination register.
2083 dst: Reg @ B,
2084 /// Dividend.
2085 lhs: Reg @ C,
2086 /// Divisor.
2087 rhs: Reg @ D,
2088 /// Policy for the integer path.
2089 pol: Policy @ A,
2090 }
2091
2092 /// Dynamic bitwise and (ints only, else hook).
2093 0x77 DAnd "dand" {
2094 /// Destination register.
2095 dst: Reg @ B,
2096 /// Left operand.
2097 lhs: Reg @ C,
2098 /// Right operand.
2099 rhs: Reg @ D,
2100 /// Policy for the integer path.
2101 pol: Policy @ A,
2102 }
2103
2104 /// Dynamic bitwise or.
2105 0x78 DOr "dor" {
2106 /// Destination register.
2107 dst: Reg @ B,
2108 /// Left operand.
2109 lhs: Reg @ C,
2110 /// Right operand.
2111 rhs: Reg @ D,
2112 /// Policy for the integer path.
2113 pol: Policy @ A,
2114 }
2115
2116 /// Dynamic bitwise xor.
2117 0x79 DXor "dxor" {
2118 /// Destination register.
2119 dst: Reg @ B,
2120 /// Left operand.
2121 lhs: Reg @ C,
2122 /// Right operand.
2123 rhs: Reg @ D,
2124 /// Policy for the integer path.
2125 pol: Policy @ A,
2126 }
2127
2128 /// Dynamic shift left (64-bit; amount rule per `shift`).
2129 0x7A DShl "dshl" {
2130 /// Destination register.
2131 dst: Reg @ B,
2132 /// Value to shift.
2133 lhs: Reg @ C,
2134 /// Shift amount.
2135 rhs: Reg @ D,
2136 /// Policy for the integer path.
2137 pol: Policy @ A,
2138 }
2139
2140 /// Dynamic arithmetic shift right.
2141 0x7B DShr "dshr" {
2142 /// Destination register.
2143 dst: Reg @ B,
2144 /// Value to shift.
2145 lhs: Reg @ C,
2146 /// Shift amount.
2147 rhs: Reg @ D,
2148 /// Policy for the integer path.
2149 pol: Policy @ A,
2150 }
2151
2152 /// Dynamic negation.
2153 0x7C DNeg "dneg" {
2154 /// Destination register.
2155 dst: Reg @ B,
2156 /// Operand.
2157 src: Reg @ C,
2158 /// Policy for the integer path.
2159 pol: Policy @ A,
2160 }
2161
2162 /// Dynamic bitwise complement (ints only, else hook).
2163 0x7D DNot "dnot" {
2164 /// Destination register.
2165 dst: Reg @ B,
2166 /// Operand.
2167 src: Reg @ C,
2168 /// Policy for the integer path.
2169 pol: Policy @ A,
2170 }
2171
2172 /// Dynamic equality into a `bool` register (numbers exactly, strings by
2173 /// content, other kinds through the `eq` hook or by identity).
2174 0x7E DEq "deq" {
2175 /// Destination `bool` register.
2176 dst: Reg @ B,
2177 /// Left operand.
2178 lhs: Reg @ C,
2179 /// Right operand.
2180 rhs: Reg @ D,
2181 }
2182
2183 /// Dynamic inequality: the negation of `deq`.
2184 0x7F DNe "dne" {
2185 /// Destination `bool` register.
2186 dst: Reg @ B,
2187 /// Left operand.
2188 lhs: Reg @ C,
2189 /// Right operand.
2190 rhs: Reg @ D,
2191 }
2192
2193 /// Dynamic `<` into a `bool` register (numbers, strings bytewise, chars;
2194 /// else the `lt` hook or TypeError).
2195 0x80 DLt "dlt" {
2196 /// Destination `bool` register.
2197 dst: Reg @ B,
2198 /// Left operand.
2199 lhs: Reg @ C,
2200 /// Right operand.
2201 rhs: Reg @ D,
2202 }
2203
2204 /// Dynamic `<=`.
2205 0x81 DLe "dle" {
2206 /// Destination `bool` register.
2207 dst: Reg @ B,
2208 /// Left operand.
2209 lhs: Reg @ C,
2210 /// Right operand.
2211 rhs: Reg @ D,
2212 }
2213
2214 /// Dynamic `>` (`lt` with the operands swapped).
2215 0x82 DGt "dgt" {
2216 /// Destination `bool` register.
2217 dst: Reg @ B,
2218 /// Left operand.
2219 lhs: Reg @ C,
2220 /// Right operand.
2221 rhs: Reg @ D,
2222 }
2223
2224 /// Dynamic `>=` (`le` with the operands swapped).
2225 0x83 DGe "dge" {
2226 /// Destination `bool` register.
2227 dst: Reg @ B,
2228 /// Left operand.
2229 lhs: Reg @ C,
2230 /// Right operand.
2231 rhs: Reg @ D,
2232 }
2233
2234 /// `dst` = the truthiness of `src` as a `bool`: nil is false, bools are
2235 /// themselves, numbers are true unless zero; other kinds go to the
2236 /// `truthy` hook, or are true unless an empty string, array, or map.
2237 0x84 DTruthy "dtruthy" {
2238 /// Destination `bool` register.
2239 dst: Reg @ B,
2240 /// Operand.
2241 src: Reg @ C,
2242 }
2243
2244 /// Dynamic string concatenation (two strings, else the `concat` hook).
2245 0x85 DConcat "dconcat" {
2246 /// Destination register.
2247 dst: Reg @ B,
2248 /// Left operand.
2249 lhs: Reg @ C,
2250 /// Right operand.
2251 rhs: Reg @ D,
2252 }
2253
2254 /// `dst = src` boxed into a `dyn` (`from` names `src`'s static type;
2255 /// `u64` above `i64::MAX` raises ArithOverflow; `str`/`ref` are a no-op).
2256 0x86 ToDyn "to_dyn" {
2257 /// Destination `dyn` register.
2258 dst: Reg @ B,
2259 /// Source register.
2260 src: Reg @ C,
2261 /// The source's static type.
2262 from: Prim @ A,
2263 }
2264
2265 /// `dst = src` unboxed to the static type `to`; TypeError if the kind
2266 /// does not match, ArithOverflow if an int does not fit. (`ref` targets
2267 /// use `cast`.)
2268 0x87 FromDyn "from_dyn" {
2269 /// Destination register.
2270 dst: Reg @ B,
2271 /// Source `dyn` register.
2272 src: Reg @ C,
2273 /// The destination's static type.
2274 to: Prim @ A,
2275 }
2276
2277 /// `dst` = the [`Kind`] code of `src`, as a `u8`.
2278 0x88 TypeOf "type_of" {
2279 /// Destination `u8` register.
2280 dst: Reg @ B,
2281 /// Source `dyn`, `str`, or `ref` register.
2282 src: Reg @ C,
2283 }
2284
2285 /// `dst` = whether `src` is of kind `kind`.
2286 0x89 IsKind "is_kind" {
2287 /// Destination `bool` register.
2288 dst: Reg @ B,
2289 /// Source `dyn`, `str`, or `ref` register.
2290 src: Reg @ C,
2291 /// The kind to test.
2292 kind: Kind @ A,
2293 }
2294
2295 /// `dst` = whether `src` is a non-nil instance of struct type `ty` or of
2296 /// a type that has it as an ancestor.
2297 0x8A IsType "is_type" {
2298 /// Destination `bool` register.
2299 dst: Reg @ B,
2300 /// Source `dyn` or `ref` register.
2301 src: Reg @ C,
2302 /// The struct type.
2303 ty: TypeRef @ D,
2304 }
2305
2306 /// `dst = src` as `ref ty`; TypeError if `src` is not nil and not an
2307 /// instance of `ty` (or a descendant).
2308 0x8B Cast "cast" {
2309 /// Destination `ref ty` register.
2310 dst: Reg @ B,
2311 /// Source `dyn` or `ref` register.
2312 src: Reg @ C,
2313 /// The target type.
2314 ty: TypeRef @ D,
2315 }
2316
2317 /// Dynamic indexing `dst = obj[key]`: arrays by int, maps by key, strings
2318 /// by int (the byte); misses and other kinds go to the `get_index` hook.
2319 0x8C DGetIndex "dget_index" {
2320 /// Destination `dyn` register.
2321 dst: Reg @ B,
2322 /// The indexed value.
2323 obj: Reg @ C,
2324 /// The key.
2325 key: Reg @ D,
2326 }
2327
2328 /// Dynamic indexed store `obj[key] = src`.
2329 0x8D DSetIndex "dset_index" {
2330 /// The indexed value.
2331 obj: Reg @ B,
2332 /// The key.
2333 key: Reg @ C,
2334 /// The value to store.
2335 src: Reg @ D,
2336 }
2337
2338 /// Dynamic property read `dst = obj.name`: a struct field or method by
2339 /// name, a map entry with a string key, else the `get_prop` hook.
2340 0x8E GetProp "get_prop" {
2341 /// Destination `dyn` register.
2342 dst: Reg @ B,
2343 /// The object.
2344 obj: Reg @ C,
2345 /// The property name.
2346 name: NameRef @ D,
2347 }
2348
2349 /// Dynamic property write `obj.name = src`.
2350 0x8F SetProp "set_prop" {
2351 /// The object.
2352 obj: Reg @ B,
2353 /// The property name.
2354 name: NameRef @ D,
2355 /// The value to store.
2356 src: Reg @ C,
2357 }
2358
2359 /// `dst` = whether `obj` has property `name`.
2360 0x90 HasProp "has_prop" {
2361 /// Destination `bool` register.
2362 dst: Reg @ B,
2363 /// The object.
2364 obj: Reg @ C,
2365 /// The property name.
2366 name: NameRef @ D,
2367 }
2368
2369 /// Dynamic call: `dst = callee(dst+1, ..., dst+argc)` with `dyn`
2370 /// arguments, converted to the callee's parameter types; TypeError on an
2371 /// arity or type mismatch; non-callables go to the `call` hook.
2372 0x91 DCall "dcall" {
2373 /// Result register (`dyn`); arguments follow it.
2374 dst: Reg @ B,
2375 /// The callee.
2376 callee: Reg @ C,
2377 /// The number of arguments.
2378 argc: u8 @ A,
2379 }
2380
2381 /// `dst` = a dynamic iterator over `src` (array or map; else the `iter`
2382 /// hook).
2383 0x92 DIterNew "diter_new" {
2384 /// Destination `dyn` register.
2385 dst: Reg @ B,
2386 /// The value to iterate.
2387 src: Reg @ C,
2388 }
2389
2390 /// `dst` = the length of `src` as an `i64` (string bytes, array or map
2391 /// entries; else the `len` hook).
2392 0x93 DLen "dlen" {
2393 /// Destination `i64` register.
2394 dst: Reg @ B,
2395 /// The value.
2396 src: Reg @ C,
2397 }
2398
2399 /// Dynamic logical not (PHP `!`, Python `not`): `dst = !truthy(src)` as a
2400 /// `bool`, with exactly the truthiness rules of `dtruthy` (the `truthy`
2401 /// hook included). `dnot` is the bitwise `~`.
2402 0x94 DLNot "dlnot" {
2403 /// Destination `bool` register.
2404 dst: Reg @ B,
2405 /// Operand.
2406 src: Reg @ C,
2407 }
2408
2409 // ---------------------------------------------------------------------
2410 // Control flow, calls, exceptions
2411 // ---------------------------------------------------------------------
2412
2413 /// Continue at `target`.
2414 0xA0 Jmp "jmp" {
2415 /// The next instruction.
2416 target: Target @ W,
2417 }
2418
2419 /// Continue at `target` if `cond` is true, else fall through.
2420 0xA1 JmpIf "jmp_if" {
2421 /// A `bool` register.
2422 cond: Reg @ B,
2423 /// Taken when `cond` is true.
2424 target: Target @ W,
2425 }
2426
2427 /// Continue at `target` if `cond` is false, else fall through.
2428 0xA2 JmpIfNot "jmp_if_not" {
2429 /// A `bool` register.
2430 cond: Reg @ B,
2431 /// Taken when `cond` is false.
2432 target: Target @ W,
2433 }
2434
2435 /// Continue at `tables[table].targets[src]` if `0 <= src < len`, else at
2436 /// the table's default.
2437 0xA3 Switch "switch" {
2438 /// The selector.
2439 src: Reg @ B,
2440 /// The jump table.
2441 table: TableId @ W,
2442 /// The selector's integer type.
2443 ty: IntTy @ A,
2444 }
2445
2446 /// `dst = func(dst+1, ..., dst+argc)`; a call to a function with no
2447 /// result leaves `dst` untouched.
2448 0xA4 Call "call" {
2449 /// Result register; arguments follow it.
2450 dst: Reg @ B,
2451 /// The callee.
2452 func: FuncId @ W,
2453 /// The number of arguments.
2454 argc: u8 @ A,
2455 }
2456
2457 /// `dst = callee(dst+1, ..., dst+argc)` through a typed function
2458 /// reference (`ref` of a function type).
2459 0xA5 CallIndirect "call_indirect" {
2460 /// Result register; arguments follow it.
2461 dst: Reg @ B,
2462 /// The function reference.
2463 callee: Reg @ C,
2464 /// The number of arguments.
2465 argc: u8 @ A,
2466 }
2467
2468 /// `dst = import(dst+1, ..., dst+argc)`: a host call.
2469 0xA6 CallImport "call_import" {
2470 /// Result register; arguments follow it.
2471 dst: Reg @ B,
2472 /// The host function.
2473 import: ImportId @ W,
2474 /// The number of arguments.
2475 argc: u8 @ A,
2476 }
2477
2478 /// Replaces the running frame with a call to `func(args, ...,
2479 /// args+argc-1)`; the callee's results equal the caller's.
2480 0xA7 TailCall "tail_call" {
2481 /// The callee.
2482 func: FuncId @ W,
2483 /// First argument register.
2484 args: Reg @ B,
2485 /// The number of arguments.
2486 argc: u8 @ A,
2487 }
2488
2489 /// Tail call through a typed function reference.
2490 0xA8 TailCallIndirect "tail_call_indirect" {
2491 /// The function reference.
2492 callee: Reg @ B,
2493 /// First argument register.
2494 args: Reg @ C,
2495 /// The number of arguments.
2496 argc: u8 @ A,
2497 }
2498
2499 /// Return `src` to the caller.
2500 0xA9 Ret "ret" {
2501 /// The result register.
2502 src: Reg @ B,
2503 }
2504
2505 /// Return with no result.
2506 0xAA RetVoid "ret_void" {}
2507
2508 /// Raise `src` as an error: control moves to the innermost covering
2509 /// handler, in this frame or a caller's.
2510 0xAB Throw "throw" {
2511 /// The error value (`dyn` or `ref`).
2512 src: Reg @ B,
2513 }
2514
2515 /// `dst` = the numeric [`ErrorKind`](crate::ErrorKind) code of a caught
2516 /// runtime error, or 0 for any other value.
2517 0xAC ErrCode "err_code" {
2518 /// Destination `u32` register.
2519 dst: Reg @ B,
2520 /// The caught value.
2521 src: Reg @ C,
2522 }
2523
2524 /// A point where the garbage collector may run and fuel is charged.
2525 /// Calls and allocations are safepoints too; every loop must pass one.
2526 0xAD Safepoint "safepoint" {}
2527
2528 /// Abort the run with `Unreachable` (E0109).
2529 0xAE Unreachable "unreachable" {}
2530
2531 // ---------------------------------------------------------------------
2532 // Closures and cells
2533 // ---------------------------------------------------------------------
2534
2535 /// `dst` = a closure of `func` capturing, by value, the registers
2536 /// `dst+1 ..= dst+n` (`n` = the function's capture count).
2537 0xB0 MakeClosure "make_closure" {
2538 /// Destination register; the captured values follow it.
2539 dst: Reg @ B,
2540 /// The function.
2541 func: FuncId @ W,
2542 }
2543
2544 /// `dst` = the running closure's captured value `idx`.
2545 0xB1 GetUpval "get_upval" {
2546 /// Destination register of the capture's type.
2547 dst: Reg @ B,
2548 /// The capture.
2549 idx: UpvalIdx @ C,
2550 }
2551
2552 /// `dst` = a new cell of type `ty` holding `src`.
2553 0xB2 NewCell "new_cell" {
2554 /// Destination `ref` register.
2555 dst: Reg @ B,
2556 /// The initial value.
2557 src: Reg @ C,
2558 /// The cell type.
2559 ty: TypeRef @ D,
2560 }
2561
2562 /// `dst` = the value in `cell`.
2563 0xB3 CellGet "cell_get" {
2564 /// Destination register.
2565 dst: Reg @ B,
2566 /// The cell.
2567 cell: Reg @ C,
2568 }
2569
2570 /// Store `src` into `cell`.
2571 0xB4 CellSet "cell_set" {
2572 /// The cell.
2573 cell: Reg @ B,
2574 /// The value.
2575 src: Reg @ C,
2576 }
2577
2578 // ---------------------------------------------------------------------
2579 // Typed heap objects. A nil object raises NullReference (E0101).
2580 // ---------------------------------------------------------------------
2581
2582 /// `dst` = a new instance of struct type `ty`, every field at its
2583 /// type's default.
2584 0xC0 NewStruct "new_struct" {
2585 /// Destination `ref` register.
2586 dst: Reg @ B,
2587 /// The struct type.
2588 ty: TypeRef @ C,
2589 }
2590
2591 /// `dst = obj.field`.
2592 0xC1 GetField "get_field" {
2593 /// Destination register of the field's type.
2594 dst: Reg @ B,
2595 /// The instance.
2596 obj: Reg @ C,
2597 /// The field slot.
2598 field: FieldIdx @ D,
2599 }
2600
2601 /// `obj.field = src`.
2602 0xC2 SetField "set_field" {
2603 /// The instance.
2604 obj: Reg @ B,
2605 /// The field slot.
2606 field: FieldIdx @ D,
2607 /// The value.
2608 src: Reg @ C,
2609 }
2610
2611 /// `dst` = a new array of type `ty` with `len` (an `i64`, at least 0)
2612 /// default elements.
2613 0xC3 NewArray "new_array" {
2614 /// Destination `ref` register.
2615 dst: Reg @ B,
2616 /// The length.
2617 len: Reg @ C,
2618 /// The array type.
2619 ty: TypeRef @ D,
2620 }
2621
2622 /// `dst` = the array's length (`i64`).
2623 0xC4 ArrayLen "array_len" {
2624 /// Destination `i64` register.
2625 dst: Reg @ B,
2626 /// The array.
2627 arr: Reg @ C,
2628 }
2629
2630 /// `dst = arr[idx]`; IndexOutOfBounds unless `0 <= idx < len`.
2631 0xC5 ArrayGet "array_get" {
2632 /// Destination register of the element type.
2633 dst: Reg @ B,
2634 /// The array.
2635 arr: Reg @ C,
2636 /// The `i64` index.
2637 idx: Reg @ D,
2638 }
2639
2640 /// `arr[idx] = src`; IndexOutOfBounds unless `0 <= idx < len`.
2641 0xC6 ArraySet "array_set" {
2642 /// The array.
2643 arr: Reg @ B,
2644 /// The `i64` index.
2645 idx: Reg @ C,
2646 /// The value.
2647 src: Reg @ D,
2648 }
2649
2650 /// Append `src` to the array.
2651 0xC7 ArrayPush "array_push" {
2652 /// The array.
2653 arr: Reg @ B,
2654 /// The value.
2655 src: Reg @ C,
2656 }
2657
2658 /// `dst` = the removed last element; IndexOutOfBounds if empty.
2659 0xC8 ArrayPop "array_pop" {
2660 /// Destination register of the element type.
2661 dst: Reg @ B,
2662 /// The array.
2663 arr: Reg @ C,
2664 }
2665
2666 /// `dst` = a new, empty map of type `ty`.
2667 0xC9 NewMap "new_map" {
2668 /// Destination `ref` register.
2669 dst: Reg @ B,
2670 /// The map type.
2671 ty: TypeRef @ C,
2672 }
2673
2674 /// `dst` = the number of entries (`i64`).
2675 0xCA MapLen "map_len" {
2676 /// Destination `i64` register.
2677 dst: Reg @ B,
2678 /// The map.
2679 map: Reg @ C,
2680 }
2681
2682 /// `dst = map[key]`; KeyNotFound if absent.
2683 0xCB MapGet "map_get" {
2684 /// Destination register of the value type.
2685 dst: Reg @ B,
2686 /// The map.
2687 map: Reg @ C,
2688 /// The key.
2689 key: Reg @ D,
2690 }
2691
2692 /// `dst = map[key]`, or `nil` if absent (value type `dyn`, `str`, or
2693 /// `ref`).
2694 0xCC MapFind "map_find" {
2695 /// Destination register of the value type.
2696 dst: Reg @ B,
2697 /// The map.
2698 map: Reg @ C,
2699 /// The key.
2700 key: Reg @ D,
2701 }
2702
2703 /// `dst` = whether `key` is present.
2704 0xCD MapHas "map_has" {
2705 /// Destination `bool` register.
2706 dst: Reg @ B,
2707 /// The map.
2708 map: Reg @ C,
2709 /// The key.
2710 key: Reg @ D,
2711 }
2712
2713 /// `map[key] = src`: a new key goes last; an existing key keeps its
2714 /// position.
2715 0xCE MapSet "map_set" {
2716 /// The map.
2717 map: Reg @ B,
2718 /// The key.
2719 key: Reg @ C,
2720 /// The value.
2721 src: Reg @ D,
2722 }
2723
2724 /// Remove `key` if present; the other entries keep their order.
2725 0xCF MapDel "map_del" {
2726 /// The map.
2727 map: Reg @ B,
2728 /// The key.
2729 key: Reg @ C,
2730 }
2731
2732 /// Append `src` under the next integer key (PHP `$a[] = v`): one more
2733 /// than the largest integer key ever inserted, or 0.
2734 0xD0 MapPush "map_push" {
2735 /// The map.
2736 map: Reg @ B,
2737 /// The value.
2738 src: Reg @ C,
2739 }
2740
2741 /// `dst` = a typed iterator over the array or map `src`.
2742 0xD1 IterNew "iter_new" {
2743 /// Destination `ref` (iterator type) register.
2744 dst: Reg @ B,
2745 /// The collection.
2746 src: Reg @ C,
2747 }
2748
2749 /// Advance: if another element exists, `val` = its value and `has` =
2750 /// true; else `has` = false and `val` is untouched.
2751 0xD2 IterNext "iter_next" {
2752 /// Destination `bool` register.
2753 has: Reg @ B,
2754 /// The iterator.
2755 iter: Reg @ C,
2756 /// Destination register of the value type.
2757 val: Reg @ D,
2758 }
2759
2760 /// `dst` = the key of the element the last `iter_next` produced.
2761 0xD3 IterKey "iter_key" {
2762 /// Destination register of the key type.
2763 dst: Reg @ B,
2764 /// The iterator.
2765 iter: Reg @ C,
2766 }
2767
2768 /// `dst` = a shallow copy of `src` (array, map, struct, or cell; a new
2769 /// identity, so value semantics such as PHP's array assignment are
2770 /// explicit; implementations may copy lazily). Strings and callables
2771 /// are returned as is.
2772 0xD4 Dup "dup" {
2773 /// Destination register.
2774 dst: Reg @ B,
2775 /// Source register.
2776 src: Reg @ C,
2777 }
2778
2779 // ---------------------------------------------------------------------
2780 // Strings (immutable byte strings)
2781 // ---------------------------------------------------------------------
2782
2783 /// `dst` = the length in bytes (`i64`).
2784 0xE0 StrLen "str_len" {
2785 /// Destination `i64` register.
2786 dst: Reg @ B,
2787 /// The string.
2788 s: Reg @ C,
2789 }
2790
2791 /// `dst = lhs ++ rhs`.
2792 0xE1 StrConcat "str_concat" {
2793 /// Destination `str` register.
2794 dst: Reg @ B,
2795 /// First part.
2796 lhs: Reg @ C,
2797 /// Second part.
2798 rhs: Reg @ D,
2799 }
2800
2801 /// `dst` = the concatenation of the `count` strings in `first ..
2802 /// first+count` (the empty string if `count` is 0).
2803 0xE2 StrConcatN "str_concat_n" {
2804 /// Destination `str` register.
2805 dst: Reg @ B,
2806 /// First part.
2807 first: Reg @ C,
2808 /// Number of parts.
2809 count: u8 @ A,
2810 }
2811
2812 /// `dst` = whether the strings have equal bytes.
2813 0xE3 StrEq "str_eq" {
2814 /// Destination `bool` register.
2815 dst: Reg @ B,
2816 /// Left operand.
2817 lhs: Reg @ C,
2818 /// Right operand.
2819 rhs: Reg @ D,
2820 }
2821
2822 /// `dst` = -1, 0, or 1 (`i8`) by bytewise lexicographic order.
2823 0xE4 StrCmp "str_cmp" {
2824 /// Destination `i8` register.
2825 dst: Reg @ B,
2826 /// Left operand.
2827 lhs: Reg @ C,
2828 /// Right operand.
2829 rhs: Reg @ D,
2830 }
2831
2832 /// `dst = s[start..end]` by byte offset, `start` in `range` and `end` in
2833 /// `range+1` (both `i64`); IndexOutOfBounds unless `0 <= start <= end <=
2834 /// len`; with `utf8`, InvalidStrIndex if a bound splits a character.
2835 0xE5 StrSlice "str_slice" {
2836 /// Destination `str` register.
2837 dst: Reg @ B,
2838 /// The string.
2839 s: Reg @ C,
2840 /// Start index; the end index is in the next register.
2841 range: Reg @ D,
2842 /// Whether bounds must fall on UTF-8 character boundaries.
2843 utf8: bool @ A,
2844 }
2845
2846 /// `dst` = the byte at `idx` (`u8`); IndexOutOfBounds unless `0 <= idx
2847 /// < len`.
2848 0xE6 StrByte "str_byte" {
2849 /// Destination `u8` register.
2850 dst: Reg @ B,
2851 /// The string.
2852 s: Reg @ C,
2853 /// The `i64` index.
2854 idx: Reg @ D,
2855 }
2856
2857 // ---------------------------------------------------------------------
2858 // Coroutines, generators, async. Coroutines are stackful and asymmetric:
2859 // `yield`/`await` suspend the innermost running coroutine with every
2860 // frame above its body; `resume` continues it. Values crossing a
2861 // suspension are `dyn`. Every instruction here is a safepoint.
2862 // ---------------------------------------------------------------------
2863
2864 /// `dst` = a new coroutine, state `created`, that will run
2865 /// `func(dst+1, ..., dst+argc)` when first resumed. The arguments are
2866 /// copied now. (A)
2867 0xF0 CoroNew "coro_new" {
2868 /// Destination `dyn` or `ref coroutine` register; arguments follow it.
2869 dst: Reg @ B,
2870 /// The body.
2871 func: FuncId @ W,
2872 /// The number of arguments.
2873 argc: u8 @ A,
2874 }
2875
2876 /// `dst` = a new coroutine running the closure or function reference
2877 /// `callee` with the arguments `dst+1 ..= dst+argc` (converted from
2878 /// `dyn` as `dcall` does when `callee` is `dyn`). (A)
2879 0xF1 CoroNewIndirect "coro_new_indirect" {
2880 /// Destination `dyn` or `ref coroutine` register; arguments follow it.
2881 dst: Reg @ B,
2882 /// The callable.
2883 callee: Reg @ C,
2884 /// The number of arguments.
2885 argc: u8 @ A,
2886 }
2887
2888 /// Suspend the running coroutine in state `yielded`, handing `src` to
2889 /// its resumer under the next automatic key (PHP's rule: one more than
2890 /// the largest integer key yielded so far, starting at 0); when resumed,
2891 /// `dst` = the value sent. The yield is an unwind point: `resume_throw`
2892 /// and `coro_close` raise here. CannotSuspend (E0111) outside a
2893 /// coroutine or across a host frame.
2894 0xF2 Yield "yield" {
2895 /// Receives the value sent by the next `resume` (`dyn`).
2896 dst: Reg @ B,
2897 /// The value yielded (`dyn`).
2898 src: Reg @ C,
2899 }
2900
2901 /// Suspend the running coroutine in state `awaiting`, handing the
2902 /// awaitable `src` to its resumer (the scheduler); when resumed, `dst` =
2903 /// the value sent (the awaited result), or the error sent by
2904 /// `resume_throw` is raised here. CannotSuspend as `yield`.
2905 0xF3 Await "await" {
2906 /// Receives the awaited result (`dyn`).
2907 dst: Reg @ B,
2908 /// The awaitable (`dyn`).
2909 src: Reg @ C,
2910 }
2911
2912 /// Resume `coro` (state `created`, `yielded`, or `awaiting`), sending
2913 /// `src`; when it next suspends or finishes, `dst` = the value it
2914 /// yielded, the awaitable it awaits, or its return value (nil for a void
2915 /// body). An error escaping its body marks it `failed` and is raised
2916 /// here. InvalidCoroState (E0110) for any other state.
2917 0xF4 Resume "resume" {
2918 /// Receives the yielded, awaited, or returned value (`dyn`).
2919 dst: Reg @ B,
2920 /// The coroutine.
2921 coro: Reg @ C,
2922 /// The value sent (`dyn`; ignored by a `created` coroutine).
2923 src: Reg @ D,
2924 }
2925
2926 /// Resume `coro` by raising `src` at its suspension point (in a
2927 /// `created` coroutine: before its first instruction, where no handler
2928 /// covers it). Otherwise as `resume`.
2929 0xF5 ResumeThrow "resume_throw" {
2930 /// Receives the next yielded, awaited, or returned value (`dyn`).
2931 dst: Reg @ B,
2932 /// The coroutine.
2933 coro: Reg @ C,
2934 /// The error value raised inside it (`dyn`).
2935 src: Reg @ D,
2936 }
2937
2938 /// `dst` = the [`CoroState`](crate::CoroState) code of `coro`, as a
2939 /// `u8`.
2940 0xF6 CoroStatus "coro_status" {
2941 /// Destination `u8` register.
2942 dst: Reg @ B,
2943 /// The coroutine.
2944 coro: Reg @ C,
2945 }
2946
2947 /// `dst` = the running coroutine, or nil on the main stack.
2948 0xF7 CoroCurrent "coro_current" {
2949 /// Destination `dyn` or `ref coroutine` register.
2950 dst: Reg @ B,
2951 }
2952
2953 /// As `yield`, with an explicit key (PHP `yield $k => $v`); an integer
2954 /// key larger than any before it moves the automatic-key counter.
2955 0xF9 YieldKv "yield_kv" {
2956 /// Receives the value sent by the next `resume` (`dyn`).
2957 dst: Reg @ B,
2958 /// The key (`dyn`).
2959 key: Reg @ C,
2960 /// The value yielded (`dyn`).
2961 src: Reg @ D,
2962 }
2963
2964 /// Close `coro` (Python `close()`/`aclose()`, PHP generator
2965 /// destruction): `created` → `returned` without running; `returned` or
2966 /// `failed` → nothing. A suspended coroutine is marked *closing* and
2967 /// `src` (the close signal, e.g. `GeneratorExit`) is raised at its
2968 /// suspension point, so its `finally` blocks run. If the body then
2969 /// returns, or the error escaping it is `src` itself, the close
2970 /// succeeds (`returned`; `dst` = the return value or nil). Another
2971 /// error marks it `failed` and is raised here. An `await` while closing
2972 /// suspends normally (`dst` = the awaitable; drive it with `resume`); a
2973 /// `yield` while closing leaves it suspended there and raises
2974 /// CloseIgnored (E0113) here.
2975 0xFA CoroClose "coro_close" {
2976 /// Receives the return value, the awaitable, or nil (`dyn`).
2977 dst: Reg @ B,
2978 /// The coroutine.
2979 coro: Reg @ C,
2980 /// The close signal raised inside it (`dyn`).
2981 src: Reg @ D,
2982 }
2983
2984 /// `dst` = the key of the value `coro` most recently yielded, or nil
2985 /// before its first yield (PHP `Generator::key()`).
2986 0xFB CoroKey "coro_key" {
2987 /// Destination `dyn` register.
2988 dst: Reg @ B,
2989 /// The coroutine.
2990 coro: Reg @ C,
2991 }
2992
2993 /// `dst` = the return value of a `returned` coroutine (Python's
2994 /// `StopIteration.value`, PHP `Generator::getReturn()`), nil for a void
2995 /// body. InvalidCoroState (E0110) in any other state.
2996 0xFC CoroResult "coro_result" {
2997 /// Destination `dyn` register.
2998 dst: Reg @ B,
2999 /// The coroutine.
3000 coro: Reg @ C,
3001 }
3002
3003 /// Start a task: make a coroutine as `coro_new_indirect` does and pass
3004 /// it to the module's `spawn` hook (the host scheduler); `dst` = the
3005 /// hook's result (a task handle). NoScheduler (E0112) without the hook.
3006 /// (A)
3007 0xF8 Spawn "spawn" {
3008 /// Receives the task handle (`dyn`); arguments follow it.
3009 dst: Reg @ B,
3010 /// The callable.
3011 callee: Reg @ C,
3012 /// The number of arguments.
3013 argc: u8 @ A,
3014 }
3015}
3016
3017impl Inst {
3018 /// The branch target of `jmp`, `jmp_if`, or `jmp_if_not`, if this is
3019 /// one. (Switch targets live in the function's jump tables.)
3020 ///
3021 /// # Examples
3022 ///
3023 /// ```
3024 /// use bytecode_lang::{Inst, Reg, Target};
3025 ///
3026 /// assert_eq!(Inst::Jmp { target: Target(4) }.branch_target(), Some(Target(4)));
3027 /// assert_eq!(Inst::Mov { dst: Reg(0), src: Reg(1) }.branch_target(), None);
3028 /// ```
3029 #[must_use]
3030 pub const fn branch_target(&self) -> Option<Target> {
3031 match *self {
3032 Inst::Jmp { target } | Inst::JmpIf { target, .. } | Inst::JmpIfNot { target, .. } => {
3033 Some(target)
3034 }
3035 _ => None,
3036 }
3037 }
3038
3039 /// The assembler name of this instruction's opcode.
3040 ///
3041 /// # Examples
3042 ///
3043 /// ```
3044 /// use bytecode_lang::Inst;
3045 ///
3046 /// assert_eq!(Inst::Safepoint {}.mnemonic(), "safepoint");
3047 /// ```
3048 #[must_use]
3049 pub const fn mnemonic(&self) -> &'static str {
3050 self.opcode().mnemonic()
3051 }
3052}
3053
3054/// `mnemonic.modifiers operand, operand`, with raw `@pc` branch targets.
3055impl fmt::Display for Inst {
3056 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3057 self.render(f, &Cx::EMPTY)
3058 }
3059}
3060
3061#[cfg(test)]
3062mod tests {
3063 use alloc::string::ToString;
3064
3065 use super::*;
3066
3067 #[test]
3068 fn decoded_instructions_stay_eight_bytes() {
3069 assert_eq!(core::mem::size_of::<Inst>(), 8);
3070 }
3071
3072 #[test]
3073 fn opcodes_round_trip_through_their_byte() {
3074 for &op in Opcode::ALL {
3075 assert_eq!(Opcode::from_u8(op as u8), Some(op));
3076 }
3077 }
3078
3079 #[test]
3080 fn modifiers_print_as_suffixes() {
3081 let i = Inst::IntCast {
3082 dst: Reg(1),
3083 src: Reg(2),
3084 conv: IntConv::new(IntTy::I64, IntTy::U8, crate::Overflow::Wrap),
3085 };
3086 assert_eq!(i.to_string(), "int_cast.i64.u8.wrap r1, r2");
3087 let d = Inst::DAdd {
3088 dst: Reg(0),
3089 lhs: Reg(1),
3090 rhs: Reg(2),
3091 pol: Policy::new(),
3092 };
3093 assert_eq!(d.to_string(), "dadd r0, r1, r2");
3094 let c = Inst::Call {
3095 dst: Reg(3),
3096 func: FuncId(1),
3097 argc: 2,
3098 };
3099 assert_eq!(c.to_string(), "call r3, f1, 2");
3100 }
3101
3102 #[test]
3103 fn immediates_keep_their_sign() {
3104 let i = Inst::LoadInt {
3105 dst: Reg(0),
3106 val: -5,
3107 ty: IntTy::I32,
3108 };
3109 assert_eq!(Inst::from_bytes(i.to_bytes()), Ok(i));
3110 assert_eq!(i.to_string(), "load_int.i32 r0, -5");
3111 }
3112}