Skip to main content

tinywasm_types/
instructions.rs

1use alloc::boxed::Box;
2
3use super::{FuncAddr, GlobalAddr, LocalAddr, TableAddr, TagAddr, TypeAddr, ValueCounts};
4use crate::operands::{read_field, write_field};
5use crate::{
6    DataAddr, ElemAddr, MemAddr, ModuleFuncIdx, Operand64, Operand64Idx, Operand128, Operand128Idx, OperandIdx, RefType,
7};
8
9/// Identifies the packed full-width memory operand layout.
10pub enum MemoryOperand {}
11
12/// Identifies the packed compact memory operand layout.
13pub enum CompactMemoryOperand {}
14
15/// Identifies a packed branch-table operand.
16pub enum BranchTableOperand {}
17
18/// Identifies a packed constant memory-fill operand.
19pub enum MemoryFillOperand {}
20
21/// Identifies a packed local update and branch operand.
22pub enum LocalUpdateOperand {}
23
24/// Identifies a packed global update and branch operand.
25pub enum GlobalUpdateOperand {}
26
27/// Identifies a packed local update, comparison, and branch operand.
28pub enum LocalUpdateCmpOperand {}
29
30/// A compact memory immediate used by optimized instructions.
31///
32/// Optimized instructions use this representation when the offset and
33/// module-local memory index fit its narrower fields.
34#[derive(Copy, Clone, PartialEq, Eq)]
35#[cfg_attr(feature = "debug", derive(Debug))]
36#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
37#[repr(Rust, packed)]
38pub struct CompactMemoryArg {
39    offset: u32,
40    mem_addr: u16,
41}
42
43/// An indexed memory argument and up to two local operands.
44#[derive(Copy, Clone, PartialEq, Eq)]
45#[cfg_attr(feature = "debug", derive(Debug))]
46#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
47#[repr(Rust, packed)]
48pub struct MemoryLocalArg {
49    pub memory_arg_idx: Operand64Idx<CompactMemoryOperand>,
50    pub local1: u8,
51    pub local2: u8,
52}
53
54/// An indexed memory argument and SIMD lane.
55#[derive(Copy, Clone, PartialEq, Eq)]
56#[cfg_attr(feature = "debug", derive(Debug))]
57#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
58#[repr(Rust, packed)]
59pub struct MemoryLaneArg {
60    pub memory_arg_idx: Operand128Idx<MemoryOperand>,
61    pub lane: u8,
62}
63
64/// A 32-bit immediate and local operand that fit in an instruction payload.
65#[derive(Copy, Clone, PartialEq, Eq)]
66#[cfg_attr(feature = "debug", derive(Debug))]
67#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
68#[repr(Rust, packed)]
69pub struct I32LocalArg {
70    pub value: i32,
71    pub local: LocalAddr,
72}
73
74/// Three local operands that fit in an instruction payload.
75#[derive(Copy, Clone, PartialEq, Eq)]
76#[cfg_attr(feature = "debug", derive(Debug))]
77#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
78pub struct LocalTripleArg {
79    pub left: LocalAddr,
80    pub right: LocalAddr,
81    pub dst: LocalAddr,
82}
83
84/// A branch target and local operand that fit in an instruction payload.
85#[derive(Copy, Clone, PartialEq, Eq)]
86#[cfg_attr(feature = "debug", derive(Debug))]
87#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
88#[repr(Rust, packed)]
89pub struct TargetLocalArg {
90    pub target_ip: u32,
91    pub local: LocalAddr,
92}
93
94/// An operation packed inline with an indexed operand.
95#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
96#[cfg_attr(
97    feature = "archive",
98    serde(bound(serialize = "Op: Copy + serde::Serialize", deserialize = "Op: Copy + serde::de::DeserializeOwned",))
99)]
100#[repr(Rust, packed)]
101pub struct PackedOp<Op, T, const WIDTH: usize> {
102    pub op: Op,
103    pub index: OperandIdx<T, WIDTH>,
104}
105
106/// An operation packed with an index into the 64-bit operand lane.
107pub type PackedOp64<Op, T> = PackedOp<Op, T, 8>;
108
109/// An operation packed with an index into the 128-bit operand lane.
110pub type PackedOp128<Op, T> = PackedOp<Op, T, 16>;
111
112impl<Op: Copy, T, const WIDTH: usize> Copy for PackedOp<Op, T, WIDTH> {}
113
114impl<Op: Copy, T, const WIDTH: usize> Clone for PackedOp<Op, T, WIDTH> {
115    fn clone(&self) -> Self {
116        *self
117    }
118}
119
120impl<Op: Copy + PartialEq, T, const WIDTH: usize> PartialEq for PackedOp<Op, T, WIDTH> {
121    fn eq(&self, other: &Self) -> bool {
122        let (op, index) = (self.op, self.index);
123        let (other_op, other_index) = (other.op, other.index);
124        op == other_op && index == other_index
125    }
126}
127
128impl<Op: Copy + Eq, T, const WIDTH: usize> Eq for PackedOp<Op, T, WIDTH> {}
129
130#[cfg(feature = "debug")]
131impl<Op: Copy + core::fmt::Debug, T, const WIDTH: usize> core::fmt::Debug for PackedOp<Op, T, WIDTH> {
132    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
133        let (op, index) = (self.op, self.index);
134        formatter.debug_struct("PackedOp").field("op", &op).field("index", &index).finish()
135    }
136}
137
138impl<Op, T, const WIDTH: usize> PackedOp<Op, T, WIDTH> {
139    /// Creates a packed operation from an operator and operand index.
140    #[inline]
141    pub const fn new(op: Op, index: OperandIdx<T, WIDTH>) -> Self {
142        Self { op, index }
143    }
144}
145
146/// A catch clause attached to a lowered `try_table` instruction.
147#[derive(Clone, Copy, PartialEq, Eq)]
148#[cfg_attr(feature = "debug", derive(Debug))]
149#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
150pub enum ExceptionCatch {
151    /// Catch exceptions carrying the specified module-local tag.
152    Tag { tag: TagAddr, landing_pad: u32, base: ValueCounts, with_ref: bool },
153    /// Catch any exception.
154    All { landing_pad: u32, base: ValueCounts, with_ref: bool },
155}
156
157impl ExceptionCatch {
158    /// Returns the catch landing pad instruction pointer.
159    pub const fn landing_pad(self) -> u32 {
160        match self {
161            Self::Tag { landing_pad, .. } | Self::All { landing_pad, .. } => landing_pad,
162        }
163    }
164
165    /// Returns whether this clause exposes the caught exception reference.
166    pub const fn with_ref(self) -> bool {
167        match self {
168            Self::Tag { with_ref, .. } | Self::All { with_ref, .. } => with_ref,
169        }
170    }
171}
172
173/// A statically lowered exception handler range.
174#[derive(Clone, PartialEq, Eq)]
175#[cfg_attr(feature = "debug", derive(Debug))]
176#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
177pub struct ExceptionHandler {
178    /// First protected instruction, inclusive.
179    pub start_ip: u32,
180    /// End of the protected instruction range, exclusive.
181    pub end_ip: u32,
182    /// Catch clauses in source order.
183    pub catches: Box<[ExceptionCatch]>,
184}
185
186impl Operand128<MemoryOperand> {
187    /// Creates a full-width memory operand.
188    #[inline]
189    pub fn new(offset: u64, memory: MemAddr) -> Self {
190        let mut operand = Self::default();
191        write_field!(operand, u64, 0, offset);
192        write_field!(operand, u32, 8, memory);
193        operand
194    }
195
196    /// Returns the static byte offset.
197    #[inline(always)]
198    pub fn offset(&self) -> u64 {
199        read_field!(self, u64, 0)
200    }
201
202    /// Returns the module-local memory index.
203    #[inline(always)]
204    pub fn memory(&self) -> MemAddr {
205        read_field!(self, u32, 8)
206    }
207}
208
209impl Operand64<CompactMemoryOperand> {
210    /// Creates a compact memory operand.
211    #[inline]
212    pub fn new(offset: u32, memory: u16) -> Self {
213        let mut operand = Self::default();
214        write_field!(operand, u32, 0, offset);
215        write_field!(operand, u16, 4, memory);
216        operand
217    }
218
219    /// Returns the static byte offset.
220    #[inline(always)]
221    pub fn offset(&self) -> u32 {
222        read_field!(self, u32, 0)
223    }
224
225    /// Returns the module-local memory index.
226    #[inline(always)]
227    pub fn memory(&self) -> u16 {
228        read_field!(self, u16, 4)
229    }
230}
231
232impl Operand128<BranchTableOperand> {
233    #[inline]
234    pub fn new(target: u32, start: u32, len: u32) -> Self {
235        let mut operand = Self::default();
236        write_field!(operand, u32, 0, target);
237        write_field!(operand, u32, 4, start);
238        write_field!(operand, u32, 8, len);
239        operand
240    }
241    #[inline(always)]
242    pub fn start(&self) -> u32 {
243        read_field!(self, u32, 4)
244    }
245    #[inline(always)]
246    pub fn size(&self) -> u32 {
247        read_field!(self, u32, 8)
248    }
249}
250
251impl Operand128<MemoryFillOperand> {
252    #[inline]
253    pub fn new(memory: MemAddr, byte: u8, value: i32) -> Self {
254        let mut operand = Self::default();
255        write_field!(operand, u32, 0, memory);
256        write_field!(operand, u8, 4, byte);
257        write_field!(operand, i32, 5, value);
258        operand
259    }
260    #[inline(always)]
261    pub fn memory(&self) -> MemAddr {
262        read_field!(self, u32, 0)
263    }
264    #[inline(always)]
265    pub fn byte(&self) -> u8 {
266        read_field!(self, u8, 4)
267    }
268    #[inline(always)]
269    pub fn value(&self) -> i32 {
270        read_field!(self, i32, 5)
271    }
272}
273
274impl Operand128<LocalUpdateOperand> {
275    #[inline]
276    pub fn new(target: u32, value: i32, local: LocalAddr, on_zero: bool) -> Self {
277        let mut operand = Self::default();
278        write_field!(operand, u32, 0, target);
279        write_field!(operand, i32, 4, value);
280        write_field!(operand, u16, 8, local);
281        write_field!(operand, u8, 10, u8::from(on_zero));
282        operand
283    }
284    #[inline(always)]
285    pub fn value(&self) -> i32 {
286        read_field!(self, i32, 4)
287    }
288    #[inline(always)]
289    pub fn local(&self) -> LocalAddr {
290        read_field!(self, u16, 8)
291    }
292    #[inline(always)]
293    pub fn on_zero(&self) -> bool {
294        read_field!(self, u8, 10) != 0
295    }
296}
297
298impl Operand128<GlobalUpdateOperand> {
299    #[inline]
300    pub fn new(target: u32, value: i32, global: GlobalAddr, on_zero: bool) -> Self {
301        let mut operand = Self::default();
302        write_field!(operand, u32, 0, target);
303        write_field!(operand, i32, 4, value);
304        write_field!(operand, u32, 8, global);
305        write_field!(operand, u8, 12, u8::from(on_zero));
306        operand
307    }
308    #[inline(always)]
309    pub fn value(&self) -> i32 {
310        read_field!(self, i32, 4)
311    }
312    #[inline(always)]
313    pub fn global(&self) -> GlobalAddr {
314        read_field!(self, u32, 8)
315    }
316    #[inline(always)]
317    pub fn on_zero(&self) -> bool {
318        read_field!(self, u8, 12) != 0
319    }
320}
321
322impl Operand128<LocalUpdateCmpOperand> {
323    #[inline]
324    pub fn new(target: u32, value: i32, local: LocalAddr, right: LocalAddr) -> Self {
325        let mut operand = Self::default();
326        write_field!(operand, u32, 0, target);
327        write_field!(operand, i32, 4, value);
328        write_field!(operand, u16, 8, local);
329        write_field!(operand, u16, 10, right);
330        operand
331    }
332    #[inline(always)]
333    pub fn value(&self) -> i32 {
334        read_field!(self, i32, 4)
335    }
336    #[inline(always)]
337    pub fn local(&self) -> LocalAddr {
338        read_field!(self, u16, 8)
339    }
340    #[inline(always)]
341    pub fn right(&self) -> LocalAddr {
342        read_field!(self, u16, 10)
343    }
344}
345
346impl CompactMemoryArg {
347    /// Returns the static byte offset.
348    #[inline]
349    pub const fn offset(self) -> u64 {
350        self.offset as u64
351    }
352
353    /// Returns the module-local memory index.
354    #[inline]
355    pub const fn mem_addr(self) -> MemAddr {
356        self.mem_addr as MemAddr
357    }
358}
359
360impl TryFrom<Operand128<MemoryOperand>> for CompactMemoryArg {
361    type Error = ();
362
363    #[inline]
364    fn try_from(arg: Operand128<MemoryOperand>) -> Result<Self, Self::Error> {
365        Ok(Self {
366            offset: u32::try_from(arg.offset()).map_err(|_| ())?,
367            mem_addr: u16::try_from(arg.memory()).map_err(|_| ())?,
368        })
369    }
370}
371
372impl From<CompactMemoryArg> for Operand64<CompactMemoryOperand> {
373    #[inline]
374    fn from(arg: CompactMemoryArg) -> Self {
375        Self::new(arg.offset, arg.mem_addr)
376    }
377}
378
379const _: () = {
380    assert!(core::mem::size_of::<Operand64Idx<i64>>() == 4);
381    assert!(core::mem::size_of::<Operand64>() == 8);
382    assert!(core::mem::size_of::<Operand128>() == 16);
383    assert!(core::mem::size_of::<CompactMemoryArg>() == 6);
384    assert!(core::mem::size_of::<MemoryLocalArg>() == 6);
385    assert!(core::mem::size_of::<LocalTripleArg>() == 6);
386};
387
388#[derive(Clone, Copy, PartialEq)]
389#[cfg_attr(feature = "debug", derive(Debug))]
390#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
391pub enum ConstInstruction {
392    I32Const(i32),
393    I64Const(i64),
394    F32Const(f32),
395    F64Const(f64),
396    V128Const([u8; 16]),
397    GlobalGet32(GlobalAddr),
398    GlobalGet64(GlobalAddr),
399    GlobalGet128(GlobalAddr),
400    GlobalGetRef(GlobalAddr),
401    RefNull(RefType),
402    RefFunc(ModuleFuncIdx),
403    RefI31,
404    AnyConvertExtern,
405    ExternConvertAny,
406    StructNew(TypeAddr),
407    StructNewDefault(TypeAddr),
408    ArrayNew(TypeAddr),
409    ArrayNewDefault(TypeAddr),
410    ArrayNewFixed(TypeAddr, u32),
411    I32Add,
412    I32Sub,
413    I32Mul,
414    I64Add,
415    I64Sub,
416    I64Mul,
417}
418
419/// An integer comparison operator, currently only used for conditional jumps.
420#[derive(Clone, Copy, PartialEq, Eq)]
421#[cfg_attr(feature = "debug", derive(Debug))]
422#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
423pub enum CmpOp {
424    Eq,
425    Ne,
426    LtS,
427    LtU,
428    GtS,
429    GtU,
430    LeS,
431    LeU,
432    GeS,
433    GeU,
434}
435
436impl CmpOp {
437    /// Returns the comparison that is true exactly when `self` is false.
438    #[inline]
439    pub const fn inverse(self) -> Self {
440        match self {
441            Self::Eq => Self::Ne,
442            Self::Ne => Self::Eq,
443            Self::LtS => Self::GeS,
444            Self::LtU => Self::GeU,
445            Self::GtS => Self::LeS,
446            Self::GtU => Self::LeU,
447            Self::LeS => Self::GtS,
448            Self::LeU => Self::GtU,
449            Self::GeS => Self::LtS,
450            Self::GeU => Self::LtU,
451        }
452    }
453}
454
455#[derive(Clone, Copy, PartialEq, Eq)]
456#[cfg_attr(feature = "debug", derive(Debug))]
457#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
458pub enum BinOp {
459    IAdd,
460    ISub,
461    IMul,
462    IAnd,
463    IOr,
464    IXor,
465    IShl,
466    IShrS,
467    IShrU,
468    IRotl,
469    IRotr,
470    FAdd,
471    FSub,
472    FMul,
473    FDiv,
474    FMin,
475    FMax,
476    FCopysign,
477}
478
479impl BinOp {
480    /// Returns the update delta for `local OP immediate`, when it is a simple increment.
481    #[inline]
482    pub const fn inc_delta(self, immediate: i32) -> Option<i32> {
483        match self {
484            Self::IAdd => Some(immediate),
485            Self::ISub => Some(immediate.wrapping_neg()),
486            _ => None,
487        }
488    }
489}
490
491#[derive(Clone, Copy, PartialEq, Eq)]
492#[cfg_attr(feature = "debug", derive(Debug))]
493#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
494pub enum BinOp128 {
495    And,
496    AndNot,
497    Or,
498    Xor,
499    I64x2Add,
500    I64x2Mul,
501}
502
503macro_rules! define_instructions {
504    ($($variant:ident $(($($field:ty),* $(,)?))? $({ $($name:ident: $named_field:ty),* $(,)? })?),* $(,)?) => {
505        /// A TinyWasm bytecode instruction.
506        ///
507        /// These instructions are an internal, version-specific representation and do not
508        /// map one-to-one to WebAssembly instructions. Their variants and serialized form
509        /// may change between TinyWasm releases.
510        #[derive(Clone, Copy, PartialEq)]
511        #[cfg_attr(feature = "debug", derive(Debug))]
512        #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
513        pub enum Instruction {
514            $($variant $(($($field),*))? $({ $($name: $named_field),* })?),*
515        }
516
517        #[doc(hidden)]
518        #[repr(u16)]
519        #[derive(Clone, Copy)]
520        pub enum InstructionOpcode {
521            $($variant,)*
522        }
523
524        impl InstructionOpcode {
525            #[doc(hidden)]
526            pub const COUNT: usize = [$(Self::$variant),*].len();
527        }
528
529        impl Instruction {
530            /// Returns the compact opcode used to dispatch this instruction.
531            #[inline(always)]
532            pub const fn opcode(&self) -> InstructionOpcode {
533                match self {
534                    $(Self::$variant { .. } => InstructionOpcode::$variant,)*
535                }
536            }
537        }
538    };
539}
540
541define_instructions! {
542    LocalCopy32(LocalAddr, LocalAddr), LocalCopy64(LocalAddr, LocalAddr), LocalCopy128(LocalAddr, LocalAddr),
543    AddConst32(i32), AndConst32(i32), XorConst32(i32), ShrUConst32(i32), AddConst64(Operand64Idx<i64>),
544    IncLocal32(I32LocalArg), IncLocal64(PackedOp64<LocalAddr, i64>),
545
546    // The 32/64 suffix describes the operand width. Future compare-style ops may still yield i32 results.
547    BinOpLocalLocal32(BinOp, LocalAddr, LocalAddr), BinOpLocalLocal64(BinOp, LocalAddr, LocalAddr),
548    BinOpLocalLocal128(BinOp128, LocalAddr, LocalAddr),
549    CmpLocalLocal32(CmpOp, LocalAddr, LocalAddr), CmpLocalLocal64(CmpOp, LocalAddr, LocalAddr),
550    AddLocalLocalSet32(LocalTripleArg), AddLocalLocalTee32(LocalTripleArg),
551    BinOpLocalLocalSet32(PackedOp64<BinOp, (u16, u16, u16)>), BinOpLocalLocalSet64(PackedOp64<BinOp, (u16, u16, u16)>), BinOpLocalLocalSet128(PackedOp64<BinOp128, (u16, u16, u16)>),
552    BinOpLocalLocalTee32(PackedOp64<BinOp, (u16, u16, u16)>), BinOpLocalLocalTee64(PackedOp64<BinOp, (u16, u16, u16)>), BinOpLocalLocalTee128(PackedOp64<BinOp128, (u16, u16, u16)>),
553
554    AddLocalConst32(I32LocalArg), SubLocalConst32(I32LocalArg), MulLocalConst32(I32LocalArg),
555    AndLocalConst32(I32LocalArg), ShrULocalConst32(I32LocalArg),
556    BinOpLocalConst32(PackedOp64<BinOp, (u16, u32)>), BinOpLocalConst64(PackedOp128<BinOp, (u16, u64)>),
557    BinOpLocalConst128(PackedOp64<BinOp128, (u16, Operand128Idx<[u8; 16]>)>),
558    BinOpLocalConstSet32(PackedOp64<BinOp, (u16, u16, u32)>), BinOpLocalConstSet64(PackedOp128<BinOp, (u16, u16, u64)>), BinOpLocalConstSet128(PackedOp64<BinOp128, (u16, u16, Operand128Idx<[u8; 16]>)>),
559    BinOpLocalConstTee32(PackedOp64<BinOp, (u16, u16, u32)>), BinOpLocalConstTee64(PackedOp128<BinOp, (u16, u16, u64)>), BinOpLocalConstTee128(PackedOp64<BinOp128, (u16, u16, Operand128Idx<[u8; 16]>)>),
560
561    BinOpStackConst32(BinOp, i32), BinOpStackConst64(PackedOp64<BinOp, i64>),
562
563    BinOpStackLocal32(BinOp, LocalAddr),
564    BinOpStackLocalSet32(BinOp, LocalAddr, LocalAddr),
565    BinOpStackLocalTee32(BinOp, LocalAddr, LocalAddr),
566    BinOpStackLocal128(BinOp128, LocalAddr),
567    BinOpStackGlobal32(BinOp, u32),
568    BinOpStackGlobal64(BinOp, u32),
569    BinOpGlobalConst32(PackedOp64<BinOp, (u32, u32)>), BinOpGlobalConst64(PackedOp128<BinOp, (u32, u64)>),
570    BinOpGlobalConst128(PackedOp64<BinOp128, (u32, Operand128Idx<[u8; 16]>)>),
571
572    SetLocalConst32(I32LocalArg), SetLocalConst64(PackedOp64<LocalAddr, i64>), SetLocalConst128(PackedOp128<LocalAddr, [u8; 16]>),
573    IncMemoryLocal32(MemoryLocalArg), IncMemoryLocal64(MemoryLocalArg),
574    StoreLocalLocal32(MemoryLocalArg), StoreLocalLocal64(MemoryLocalArg), StoreLocalLocal128(MemoryLocalArg),
575    LoadLocal32(MemoryLocalArg), LoadLocal64(MemoryLocalArg),
576    LoadLocal8S32(MemoryLocalArg), LoadLocal8U32(MemoryLocalArg),
577    LoadLocal16S32(MemoryLocalArg), LoadLocal16U32(MemoryLocalArg),
578    LoadLocalTee32(MemoryLocalArg), LoadLocalSet32(MemoryLocalArg),
579    LoadLocalTee8S32(MemoryLocalArg), LoadLocalTee8U32(MemoryLocalArg),
580    LoadLocalTee16S32(MemoryLocalArg), LoadLocalTee16U32(MemoryLocalArg),
581    LoadLocalSet8S32(MemoryLocalArg), LoadLocalSet8U32(MemoryLocalArg),
582    LoadLocalSet16S32(MemoryLocalArg), LoadLocalSet16U32(MemoryLocalArg),
583    LoadLocalTee128(MemoryLocalArg), LoadLocalSet128(MemoryLocalArg),
584    AndConstTee32(I32LocalArg), SubConstTee32(I32LocalArg),
585    AndConstTee64(PackedOp64<LocalAddr, i64>), SubConstTee64(PackedOp64<LocalAddr, i64>),
586    MulAccLocal32(LocalAddr), MulAccLocal64(LocalAddr),
587    FMulAccLocal32(LocalAddr), FMulAccLocal64(LocalAddr),
588    I32Add3,
589    I64Add3,
590    FMaStoreF32(CompactMemoryArg),
591    FMaStoreF64(CompactMemoryArg),
592
593    // > Control Instructions (jump-oriented, lowered from structured control during parsing)
594    // See <https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions>
595    Unreachable,
596    Jump(u32),
597    JumpIfZero32(u32),
598    JumpIfNonZero32(u32),
599    JumpIfZero64(u32),
600    JumpIfNonZero64(u32),
601    JumpIfRefNull(u32),
602    JumpIfRefNonNull(u32),
603    JumpIfLocalZero32(TargetLocalArg), JumpIfLocalNonZero32(TargetLocalArg),
604    JumpIfLocalZero64(TargetLocalArg), JumpIfLocalNonZero64(TargetLocalArg),
605    JumpCmpStackConst32(PackedOp64<CmpOp, (u32, i32)>), JumpCmpStackConst64(PackedOp128<CmpOp, (u32, i64)>),
606    JumpCmpStackLocal32(PackedOp64<CmpOp, (u32, u16)>), JumpCmpStackLocal64(PackedOp64<CmpOp, (u32, u16)>),
607    BinOpLocalConstJump32(PackedOp128<BinOp, LocalUpdateOperand>), BinOpLocalConstJumpCmpLocal32(PackedOp128<(BinOp, CmpOp), LocalUpdateCmpOperand>),
608    BinOpStackConstTeeLocalJump32(PackedOp128<BinOp, LocalUpdateOperand>), BinOpGlobalConstJump32(PackedOp128<BinOp, GlobalUpdateOperand>),
609    IncLocalJump32(Operand128Idx<LocalUpdateOperand>), IncStackTeeLocalJump32(Operand128Idx<LocalUpdateOperand>), IncGlobalJump32(Operand128Idx<GlobalUpdateOperand>),
610    IncLocalJumpCmpLocal32(PackedOp128<CmpOp, LocalUpdateCmpOperand>), JumpCmpLocalConst32(PackedOp128<CmpOp, (u32, i32, u16)>), JumpCmpLocalConst64(PackedOp128<CmpOp, (u32, i32, u16)>),
611    JumpCmpLocalLocal32(PackedOp64<CmpOp, (u32, u16, u16)>), JumpCmpLocalLocal64(PackedOp64<CmpOp, (u32, u16, u16)>),
612    DropKeep32 { base: u16, keep: u16 }, DropKeep64 { base: u16, keep: u16 }, DropKeep128 { base: u16, keep: u16 }, BranchTable(Operand128Idx<BranchTableOperand>),
613    Return,
614    ReturnVoid,
615    Return32,
616    Return64,
617    Return128,
618    Call(FuncAddr),
619    CallSelf,
620    CallIndirect(Operand64Idx<(u32, u32)>),
621    CallRef(TypeAddr),
622    ReturnCall(FuncAddr),
623    ReturnCallSelf,
624    ReturnCallIndirect(Operand64Idx<(u32, u32)>),
625    ReturnCallRef(TypeAddr),
626    Throw(TagAddr),
627    ThrowRef,
628
629    // > Parametric Instructions
630    // See <https://webassembly.github.io/spec/core/binary/instructions.html#parametric-instructions>
631    Drop32, Select32,
632    Drop64, Select64,
633    Drop128, Select128,
634    SelectMulti(ValueCounts),
635
636    // > Variable Instructions
637    // See <https://webassembly.github.io/spec/core/binary/instructions.html#variable-instructions>
638    GlobalGet32(GlobalAddr), GlobalSet32(GlobalAddr), GlobalTee32(GlobalAddr), LocalGet32(LocalAddr), LocalSet32(LocalAddr), LocalTee32(LocalAddr),
639    GlobalGet64(GlobalAddr), GlobalSet64(GlobalAddr), GlobalTee64(GlobalAddr), LocalGet64(LocalAddr), LocalSet64(LocalAddr), LocalTee64(LocalAddr),
640    GlobalGet128(GlobalAddr), GlobalSet128(GlobalAddr), GlobalTee128(GlobalAddr), LocalGet128(LocalAddr), LocalSet128(LocalAddr), LocalTee128(LocalAddr),
641
642    // > Memory Instructions
643    I32Load(Operand128Idx<MemoryOperand>), I64Load(Operand128Idx<MemoryOperand>), F32Load(Operand128Idx<MemoryOperand>), F64Load(Operand128Idx<MemoryOperand>),
644    I32Load8S(Operand128Idx<MemoryOperand>), I32Load8U(Operand128Idx<MemoryOperand>), I32Load16S(Operand128Idx<MemoryOperand>), I32Load16U(Operand128Idx<MemoryOperand>),
645    I64Load8S(Operand128Idx<MemoryOperand>), I64Load8U(Operand128Idx<MemoryOperand>), I64Load16S(Operand128Idx<MemoryOperand>), I64Load16U(Operand128Idx<MemoryOperand>), I64Load32S(Operand128Idx<MemoryOperand>), I64Load32U(Operand128Idx<MemoryOperand>),
646    I32Store(Operand128Idx<MemoryOperand>), I64Store(Operand128Idx<MemoryOperand>), F32Store(Operand128Idx<MemoryOperand>), F64Store(Operand128Idx<MemoryOperand>),
647    I32Store8(Operand128Idx<MemoryOperand>), I32Store16(Operand128Idx<MemoryOperand>), I64Store8(Operand128Idx<MemoryOperand>), I64Store16(Operand128Idx<MemoryOperand>), I64Store32(Operand128Idx<MemoryOperand>),
648    MemorySize(MemAddr),
649    MemoryGrow(MemAddr),
650
651    // > Constants
652    Const32(i32),
653    Const64Imm(i32),
654    Const64(Operand64Idx<i64>),
655
656    // > Reference Types
657    RefNull(RefType),
658    RefFunc(FuncAddr),
659    RefIsNull,
660    RefAsNonNull,
661    RefI31,
662    I31GetS,
663    I31GetU,
664    RefEq,
665    RefTest(RefType),
666    RefCast(RefType),
667    BrOnCast(Operand64Idx<(u32, u32)>),
668    BrOnCastFail(Operand64Idx<(u32, u32)>),
669
670    // > GC Objects
671    StructNew(TypeAddr),
672    StructNewDefault(TypeAddr),
673    StructGet(Operand64Idx<(u32, u32)>), StructGetS(Operand64Idx<(u32, u32)>), StructGetU(Operand64Idx<(u32, u32)>), StructSet(Operand64Idx<(u32, u32)>),
674    ArrayNew(TypeAddr),
675    ArrayNewDefault(TypeAddr),
676    ArrayNewFixed(Operand64Idx<(u32, u32)>),
677    ArrayNewData(Operand64Idx<(u32, u32)>), ArrayNewElem(Operand64Idx<(u32, u32)>),
678    ArrayGet(TypeAddr),
679    ArrayGetS(TypeAddr),
680    ArrayGetU(TypeAddr),
681    ArraySet(TypeAddr),
682    ArrayLen,
683    ArrayFill(TypeAddr),
684    ArrayCopy(Operand64Idx<(u32, u32)>), ArrayInitData(Operand64Idx<(u32, u32)>), ArrayInitElem(Operand64Idx<(u32, u32)>),
685
686    // > Numeric Instructions
687    // See <https://webassembly.github.io/spec/core/binary/instructions.html#numeric-instructions>
688    I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU,
689    I64Eqz, I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU,
690
691    // Comparisons
692    F32Eq, F32Ne, F32Lt, F32Gt, F32Le, F32Ge,
693    F64Eq, F64Ne, F64Lt, F64Gt, F64Le, F64Ge,
694    I32Clz, I32Ctz, I32Popcnt, I32Add, I32Sub, I32Mul, I32DivS, I32DivU, I32RemS, I32RemU,
695    I64Clz, I64Ctz, I64Popcnt, I64Add, I64Sub, I64Mul, I64DivS, I64DivU, I64RemS, I64RemU,
696
697    // Bitwise
698    I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr,
699    I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr,
700
701    // Floating Point
702    F32Abs, F32Neg, F32Ceil, F32Floor, F32Trunc, F32Nearest, F32Sqrt, F32Add, F32Sub, F32Mul, F32Div, F32Min, F32Max, F32Copysign,
703    F64Abs, F64Neg, F64Ceil, F64Floor, F64Trunc, F64Nearest, F64Sqrt, F64Add, F64Sub, F64Mul, F64Div, F64Min, F64Max, F64Copysign,
704    I32WrapI64, I32TruncF32S, I32TruncF32U, I32TruncF64S, I32TruncF64U, I32Extend8S, I32Extend16S,
705    I64Extend8S, I64Extend16S, I64Extend32S, I64ExtendI32S, I64ExtendI32U, I64TruncF32S, I64TruncF32U, I64TruncF64S, I64TruncF64U,
706    F32ConvertI32S, F32ConvertI32U, F32ConvertI64S, F32ConvertI64U, F32DemoteF64,
707    F64ConvertI32S, F64ConvertI32U, F64ConvertI64S, F64ConvertI64U, F64PromoteF32,
708
709    // Saturating Float-to-Int Conversions
710    I32TruncSatF32S, I32TruncSatF32U, I32TruncSatF64S, I32TruncSatF64U,
711    I64TruncSatF32S, I64TruncSatF32U, I64TruncSatF64S, I64TruncSatF64U,
712
713    // > Table Instructions
714    TableInit(Operand64Idx<(u32, u32)>),
715    TableGet(TableAddr),
716    TableSet(TableAddr),
717    TableCopy(Operand64Idx<(u32, u32)>),
718    TableGrow(TableAddr),
719    TableSize(TableAddr),
720    TableFill(TableAddr),
721
722    // > Bulk Memory Instructions
723    MemoryInit(Operand64Idx<(u32, u32)>), MemoryCopy(Operand64Idx<(u32, u32)>),
724    MemoryFill(MemAddr),
725    MemoryFillConst(Operand128Idx<MemoryFillOperand>),
726    DataDrop(DataAddr),
727    ElemDrop(ElemAddr),
728
729    // > Wide Arithmetic
730    I64Add128, I64Sub128, I64MulWideS, I64MulWideU,
731
732    // > SIMD
733    V128Load(Operand128Idx<MemoryOperand>), V128Load8x8S(Operand128Idx<MemoryOperand>), V128Load8x8U(Operand128Idx<MemoryOperand>),
734    V128Load16x4S(Operand128Idx<MemoryOperand>), V128Load16x4U(Operand128Idx<MemoryOperand>), V128Load32x2S(Operand128Idx<MemoryOperand>), V128Load32x2U(Operand128Idx<MemoryOperand>),
735
736    V128Load8Splat(Operand128Idx<MemoryOperand>), V128Load16Splat(Operand128Idx<MemoryOperand>), V128Load32Splat(Operand128Idx<MemoryOperand>), V128Load64Splat(Operand128Idx<MemoryOperand>),
737    V128Load8Lane(MemoryLaneArg), V128Load16Lane(MemoryLaneArg), V128Load32Lane(MemoryLaneArg), V128Load64Lane(MemoryLaneArg),
738
739    V128Load32Zero(Operand128Idx<MemoryOperand>), V128Load64Zero(Operand128Idx<MemoryOperand>),
740
741    V128Store(Operand128Idx<MemoryOperand>), V128Store8Lane(MemoryLaneArg), V128Store16Lane(MemoryLaneArg), V128Store32Lane(MemoryLaneArg), V128Store64Lane(MemoryLaneArg),
742
743    I8x16Shuffle(Operand128Idx<[u8; 16]>),
744    Const128Imm(u32), Const128(Operand128Idx<[u8; 16]>),
745
746    I8x16ExtractLaneS(u8), I8x16ExtractLaneU(u8), I8x16ReplaceLane(u8),
747    I16x8ExtractLaneS(u8), I16x8ExtractLaneU(u8), I16x8ReplaceLane(u8),
748    I32x4ExtractLane(u8), I32x4ReplaceLane(u8),
749    I64x2ExtractLane(u8), I64x2ReplaceLane(u8),
750    F32x4ExtractLane(u8), F32x4ReplaceLane(u8),
751    F64x2ExtractLane(u8), F64x2ReplaceLane(u8),
752
753    V128Not, V128And, V128AndNot, V128Or, V128Xor, V128Bitselect, V128AnyTrue, I8x16Swizzle,
754    I8x16Splat, I8x16Eq, I8x16Ne, I8x16LtS, I8x16LtU, I8x16GtS, I8x16GtU, I8x16LeS, I8x16LeU, I8x16GeS, I8x16GeU,
755    I16x8Splat, I16x8Eq, I16x8Ne, I16x8LtS, I16x8LtU, I16x8GtS, I16x8GtU, I16x8LeS, I16x8LeU, I16x8GeS, I16x8GeU,
756    I32x4Splat, I32x4Eq, I32x4Ne, I32x4LtS, I32x4LtU, I32x4GtS, I32x4GtU, I32x4LeS, I32x4LeU, I32x4GeS, I32x4GeU,
757    I64x2Splat, I64x2Eq, I64x2Ne, I64x2LtS, I64x2GtS, I64x2LeS, I64x2GeS,
758    F32x4Splat, F32x4Eq, F32x4Ne, F32x4Lt, F32x4Gt, F32x4Le, F32x4Ge,
759    F64x2Splat, F64x2Eq, F64x2Ne, F64x2Lt, F64x2Gt, F64x2Le, F64x2Ge,
760
761    I8x16Abs, I8x16Neg, I8x16AllTrue, I8x16Bitmask, I8x16Shl, I8x16ShrS, I8x16ShrU, I8x16Add, I8x16Sub, I8x16MinS, I8x16MinU, I8x16MaxS, I8x16MaxU,
762    I16x8Abs, I16x8Neg, I16x8AllTrue, I16x8Bitmask, I16x8Shl, I16x8ShrS, I16x8ShrU, I16x8Add, I16x8Sub, I16x8MinS, I16x8MinU, I16x8MaxS, I16x8MaxU,
763    I32x4Abs, I32x4Neg, I32x4AllTrue, I32x4Bitmask, I32x4Shl, I32x4ShrS, I32x4ShrU, I32x4Add, I32x4Sub, I32x4MinS, I32x4MinU, I32x4MaxS, I32x4MaxU,
764    I64x2Abs, I64x2Neg, I64x2AllTrue, I64x2Bitmask, I64x2Shl, I64x2ShrS, I64x2ShrU, I64x2Add, I64x2Sub, I64x2Mul,
765
766    I8x16NarrowI16x8S, I8x16NarrowI16x8U, I8x16AddSatS, I8x16AddSatU, I8x16SubSatS, I8x16SubSatU, I8x16AvgrU,
767    I16x8NarrowI32x4S, I16x8NarrowI32x4U, I16x8AddSatS, I16x8AddSatU, I16x8SubSatS, I16x8SubSatU, I16x8AvgrU,
768
769    I16x8ExtAddPairwiseI8x16S, I16x8ExtAddPairwiseI8x16U, I16x8Mul,
770    I32x4ExtAddPairwiseI16x8S, I32x4ExtAddPairwiseI16x8U, I32x4Mul,
771
772    I16x8ExtMulLowI8x16S, I16x8ExtMulLowI8x16U, I16x8ExtMulHighI8x16S, I16x8ExtMulHighI8x16U,
773    I32x4ExtMulLowI16x8S, I32x4ExtMulLowI16x8U, I32x4ExtMulHighI16x8S, I32x4ExtMulHighI16x8U,
774    I64x2ExtMulLowI32x4S, I64x2ExtMulLowI32x4U, I64x2ExtMulHighI32x4S, I64x2ExtMulHighI32x4U,
775
776    I16x8ExtendLowI8x16S, I16x8ExtendLowI8x16U, I16x8ExtendHighI8x16S, I16x8ExtendHighI8x16U,
777    I32x4ExtendLowI16x8S, I32x4ExtendLowI16x8U, I32x4ExtendHighI16x8S, I32x4ExtendHighI16x8U,
778    I64x2ExtendLowI32x4S, I64x2ExtendLowI32x4U, I64x2ExtendHighI32x4S, I64x2ExtendHighI32x4U,
779
780    I8x16Popcnt, I16x8Q15MulrSatS, I32x4DotI16x8S,
781
782    F32x4Ceil, F32x4Floor, F32x4Trunc, F32x4Nearest, F32x4Abs, F32x4Neg, F32x4Sqrt, F32x4Add, F32x4Sub, F32x4Mul, F32x4Div, F32x4Min, F32x4Max, F32x4PMin, F32x4PMax,
783    F64x2Ceil, F64x2Floor, F64x2Trunc, F64x2Nearest, F64x2Abs, F64x2Neg, F64x2Sqrt, F64x2Add, F64x2Sub, F64x2Mul, F64x2Div, F64x2Min, F64x2Max, F64x2PMin, F64x2PMax,
784    I32x4TruncSatF32x4S, I32x4TruncSatF32x4U,
785    F32x4ConvertI32x4S, F32x4ConvertI32x4U,
786    I32x4TruncSatF64x2SZero, I32x4TruncSatF64x2UZero,
787    F64x2ConvertLowI32x4S, F64x2ConvertLowI32x4U,
788    F32x4DemoteF64x2Zero, F64x2PromoteLowF32x4,
789
790    // > Relaxed SIMD
791    I8x16RelaxedSwizzle,
792    I32x4RelaxedTruncF32x4S, I32x4RelaxedTruncF32x4U,
793    I32x4RelaxedTruncF64x2SZero, I32x4RelaxedTruncF64x2UZero,
794    F32x4RelaxedMadd, F32x4RelaxedNmadd,
795    F64x2RelaxedMadd, F64x2RelaxedNmadd,
796    I8x16RelaxedLaneselect, I16x8RelaxedLaneselect,
797    I32x4RelaxedLaneselect, I64x2RelaxedLaneselect,
798    F32x4RelaxedMin, F32x4RelaxedMax,
799    F64x2RelaxedMin, F64x2RelaxedMax,
800    I16x8RelaxedQ15mulrS,
801    I16x8RelaxedDotI8x16I7x16S,
802    I32x4RelaxedDotI8x16I7x16AddS,
803
804    SelectStore32(Operand128Idx<MemoryOperand>), SelectStore64(Operand128Idx<MemoryOperand>),
805}
806
807const _: () = assert!(core::mem::size_of::<Instruction>() == 8);
808
809#[cfg(test)]
810mod tests {
811    use super::*;
812
813    #[test]
814    fn compact_instruction_layout_is_exact() {
815        assert_eq!(core::mem::size_of::<Instruction>(), 8);
816        assert_eq!(core::mem::size_of::<CompactMemoryArg>(), 6);
817        assert_eq!(core::mem::size_of::<MemoryLocalArg>(), 6);
818        assert_eq!(core::mem::size_of::<LocalTripleArg>(), 6);
819    }
820
821    #[test]
822    fn operand_lanes_round_trip_max_values() {
823        let memory = Operand128::<MemoryOperand>::new(u64::MAX, u32::MAX);
824        assert_eq!(memory.offset(), u64::MAX);
825        assert_eq!(memory.memory(), u32::MAX);
826
827        let value = Operand128::<LocalUpdateOperand>::new(u32::MAX, i32::MIN, u16::MAX, true);
828        assert_eq!(value.target(), u32::MAX);
829        assert_eq!(value.value(), i32::MIN);
830        assert_eq!(value.local(), u16::MAX);
831        assert!(value.on_zero());
832    }
833}