Skip to main content

wave_decode/
instruction.rs

1// Copyright 2026 Ojima Abraham
2// SPDX-License-Identifier: Apache-2.0
3
4//! Decoded instruction types. Represents WAVE instructions in a structured
5//!
6//! format suitable for disassembly and emulation.
7
8use crate::opcodes::{
9    AtomicOp, BitOpType, CmpOp, ControlOp, CvtType, F16Op, F16PackedOp, F64DivSqrtOp, F64Op,
10    FUnaryOp, MemWidth, MiscOp, Opcode, Scope, SyncOp, WaveOpType, WaveReduceType,
11};
12
13/// A fully decoded WAVE instruction
14#[derive(Debug, Clone, PartialEq)]
15pub struct DecodedInstruction {
16    /// Byte offset from start of code section
17    pub offset: u32,
18    /// Size of instruction in bytes (4 or 8)
19    pub size: u8,
20    /// The decoded operation
21    pub operation: Operation,
22    /// Predicate register (0 = unpredicated, 1-3 = p1-p3)
23    pub predicate: u8,
24    /// Whether predicate is negated
25    pub predicate_negated: bool,
26}
27
28/// All WAVE operations
29#[derive(Debug, Clone, PartialEq)]
30pub enum Operation {
31    Iadd {
32        rd: u8,
33        rs1: u8,
34        rs2: u8,
35    },
36    Isub {
37        rd: u8,
38        rs1: u8,
39        rs2: u8,
40    },
41    Imul {
42        rd: u8,
43        rs1: u8,
44        rs2: u8,
45    },
46    ImulHi {
47        rd: u8,
48        rs1: u8,
49        rs2: u8,
50    },
51    Imad {
52        rd: u8,
53        rs1: u8,
54        rs2: u8,
55        rs3: u8,
56    },
57    Idiv {
58        rd: u8,
59        rs1: u8,
60        rs2: u8,
61    },
62    Imod {
63        rd: u8,
64        rs1: u8,
65        rs2: u8,
66    },
67    Ineg {
68        rd: u8,
69        rs1: u8,
70    },
71    Iabs {
72        rd: u8,
73        rs1: u8,
74    },
75    Imin {
76        rd: u8,
77        rs1: u8,
78        rs2: u8,
79    },
80    Imax {
81        rd: u8,
82        rs1: u8,
83        rs2: u8,
84    },
85    Iclamp {
86        rd: u8,
87        rs1: u8,
88        rs2: u8,
89        rs3: u8,
90    },
91
92    Fadd {
93        rd: u8,
94        rs1: u8,
95        rs2: u8,
96    },
97    Fsub {
98        rd: u8,
99        rs1: u8,
100        rs2: u8,
101    },
102    Fmul {
103        rd: u8,
104        rs1: u8,
105        rs2: u8,
106    },
107    Fma {
108        rd: u8,
109        rs1: u8,
110        rs2: u8,
111        rs3: u8,
112    },
113    Fdiv {
114        rd: u8,
115        rs1: u8,
116        rs2: u8,
117    },
118    Fneg {
119        rd: u8,
120        rs1: u8,
121    },
122    Fabs {
123        rd: u8,
124        rs1: u8,
125    },
126    Fmin {
127        rd: u8,
128        rs1: u8,
129        rs2: u8,
130    },
131    Fmax {
132        rd: u8,
133        rs1: u8,
134        rs2: u8,
135    },
136    Fclamp {
137        rd: u8,
138        rs1: u8,
139        rs2: u8,
140        rs3: u8,
141    },
142    Fsqrt {
143        rd: u8,
144        rs1: u8,
145    },
146    FUnary {
147        op: FUnaryOp,
148        rd: u8,
149        rs1: u8,
150    },
151
152    F16 {
153        op: F16Op,
154        rd: u8,
155        rs1: u8,
156        rs2: u8,
157        rs3: Option<u8>,
158    },
159    F16Packed {
160        op: F16PackedOp,
161        rd: u8,
162        rs1: u8,
163        rs2: u8,
164        rs3: Option<u8>,
165    },
166
167    F64 {
168        op: F64Op,
169        rd: u8,
170        rs1: u8,
171        rs2: u8,
172        rs3: Option<u8>,
173    },
174    F64DivSqrt {
175        op: F64DivSqrtOp,
176        rd: u8,
177        rs1: u8,
178        rs2: Option<u8>,
179    },
180
181    And {
182        rd: u8,
183        rs1: u8,
184        rs2: u8,
185    },
186    Or {
187        rd: u8,
188        rs1: u8,
189        rs2: u8,
190    },
191    Xor {
192        rd: u8,
193        rs1: u8,
194        rs2: u8,
195    },
196    Not {
197        rd: u8,
198        rs1: u8,
199    },
200    Shl {
201        rd: u8,
202        rs1: u8,
203        rs2: u8,
204    },
205    Shr {
206        rd: u8,
207        rs1: u8,
208        rs2: u8,
209    },
210    Sar {
211        rd: u8,
212        rs1: u8,
213        rs2: u8,
214    },
215    BitOp {
216        op: BitOpType,
217        rd: u8,
218        rs1: u8,
219        rs2: Option<u8>,
220        rs3: Option<u8>,
221        rs4: Option<u8>,
222    },
223
224    Icmp {
225        op: CmpOp,
226        pd: u8,
227        rs1: u8,
228        rs2: u8,
229    },
230    Ucmp {
231        op: CmpOp,
232        pd: u8,
233        rs1: u8,
234        rs2: u8,
235    },
236    Fcmp {
237        op: CmpOp,
238        pd: u8,
239        rs1: u8,
240        rs2: u8,
241    },
242
243    Select {
244        rd: u8,
245        ps: u8,
246        rs1: u8,
247        rs2: u8,
248    },
249    Cvt {
250        cvt_type: CvtType,
251        rd: u8,
252        rs1: u8,
253    },
254
255    LocalLoad {
256        width: MemWidth,
257        rd: u8,
258        addr: u8,
259    },
260    LocalStore {
261        width: MemWidth,
262        addr: u8,
263        value: u8,
264    },
265
266    DeviceLoad {
267        width: MemWidth,
268        rd: u8,
269        addr: u8,
270    },
271    DeviceStore {
272        width: MemWidth,
273        addr: u8,
274        value: u8,
275    },
276
277    LocalAtomic {
278        op: AtomicOp,
279        rd: Option<u8>,
280        addr: u8,
281        value: u8,
282    },
283    LocalAtomicCas {
284        rd: Option<u8>,
285        addr: u8,
286        expected: u8,
287        desired: u8,
288    },
289
290    DeviceAtomic {
291        op: AtomicOp,
292        rd: Option<u8>,
293        addr: u8,
294        value: u8,
295        scope: Scope,
296    },
297    DeviceAtomicCas {
298        rd: Option<u8>,
299        addr: u8,
300        expected: u8,
301        desired: u8,
302        scope: Scope,
303    },
304
305    WaveOp {
306        op: WaveOpType,
307        rd: u8,
308        rs1: u8,
309        rs2: Option<u8>,
310    },
311    WaveReduce {
312        op: WaveReduceType,
313        rd: u8,
314        rs1: u8,
315    },
316    WaveBallot {
317        rd: u8,
318        ps: u8,
319    },
320    WaveVote {
321        op: WaveOpType,
322        pd: u8,
323        ps: u8,
324    },
325
326    If {
327        ps: u8,
328    },
329    Else,
330    Endif,
331    Loop,
332    Break {
333        ps: u8,
334    },
335    Continue {
336        ps: u8,
337    },
338    Endloop,
339    Call {
340        target: u32,
341    },
342
343    Return,
344    Halt,
345    Barrier,
346    FenceAcquire {
347        scope: Scope,
348    },
349    FenceRelease {
350        scope: Scope,
351    },
352    FenceAcqRel {
353        scope: Scope,
354    },
355    Wait,
356    Nop,
357
358    Mov {
359        rd: u8,
360        rs1: u8,
361    },
362    MovImm {
363        rd: u8,
364        imm: u32,
365    },
366    MovSr {
367        rd: u8,
368        sr_index: u8,
369    },
370
371    Unknown {
372        opcode: u8,
373        word0: u32,
374        word1: Option<u32>,
375    },
376}
377
378impl DecodedInstruction {
379    /// Returns the mnemonic string for this instruction
380    #[must_use]
381    pub fn mnemonic(&self) -> String {
382        match &self.operation {
383            Operation::Iadd { .. } => "iadd".to_string(),
384            Operation::Isub { .. } => "isub".to_string(),
385            Operation::Imul { .. } => "imul".to_string(),
386            Operation::ImulHi { .. } => "imul_hi".to_string(),
387            Operation::Imad { .. } => "imad".to_string(),
388            Operation::Idiv { .. } => "idiv".to_string(),
389            Operation::Imod { .. } => "imod".to_string(),
390            Operation::Ineg { .. } => "ineg".to_string(),
391            Operation::Iabs { .. } => "iabs".to_string(),
392            Operation::Imin { .. } => "imin".to_string(),
393            Operation::Imax { .. } => "imax".to_string(),
394            Operation::Iclamp { .. } => "iclamp".to_string(),
395
396            Operation::Fadd { .. } => "fadd".to_string(),
397            Operation::Fsub { .. } => "fsub".to_string(),
398            Operation::Fmul { .. } => "fmul".to_string(),
399            Operation::Fma { .. } => "fma".to_string(),
400            Operation::Fdiv { .. } => "fdiv".to_string(),
401            Operation::Fneg { .. } => "fneg".to_string(),
402            Operation::Fabs { .. } => "fabs".to_string(),
403            Operation::Fmin { .. } => "fmin".to_string(),
404            Operation::Fmax { .. } => "fmax".to_string(),
405            Operation::Fclamp { .. } => "fclamp".to_string(),
406            Operation::Fsqrt { .. } => "fsqrt".to_string(),
407            Operation::FUnary { op, .. } => op.mnemonic().to_string(),
408
409            Operation::F16 { op, .. } => op.mnemonic().to_string(),
410            Operation::F16Packed { op, .. } => op.mnemonic().to_string(),
411            Operation::F64 { op, .. } => op.mnemonic().to_string(),
412            Operation::F64DivSqrt { op, .. } => op.mnemonic().to_string(),
413
414            Operation::And { .. } => "and".to_string(),
415            Operation::Or { .. } => "or".to_string(),
416            Operation::Xor { .. } => "xor".to_string(),
417            Operation::Not { .. } => "not".to_string(),
418            Operation::Shl { .. } => "shl".to_string(),
419            Operation::Shr { .. } => "shr".to_string(),
420            Operation::Sar { .. } => "sar".to_string(),
421            Operation::BitOp { op, .. } => op.mnemonic().to_string(),
422
423            Operation::Icmp { op, .. } => format!("icmp_{}", op.suffix()),
424            Operation::Ucmp { op, .. } => format!("ucmp_{}", op.suffix()),
425            Operation::Fcmp { op, .. } => format!("fcmp_{}", op.suffix()),
426
427            Operation::Select { .. } => "select".to_string(),
428            Operation::Cvt { cvt_type, .. } => cvt_type.mnemonic().to_string(),
429
430            Operation::LocalLoad { width, .. } => format!("local_load_{}", width.suffix()),
431            Operation::LocalStore { width, .. } => format!("local_store_{}", width.suffix()),
432            Operation::DeviceLoad { width, .. } => format!("device_load_{}", width.suffix()),
433            Operation::DeviceStore { width, .. } => format!("device_store_{}", width.suffix()),
434
435            Operation::LocalAtomic { op, .. } => format!("local_atomic_{}", op.suffix()),
436            Operation::LocalAtomicCas { .. } => "local_atomic_cas".to_string(),
437            Operation::DeviceAtomic { op, .. } => format!("atomic_{}", op.suffix()),
438            Operation::DeviceAtomicCas { .. } => "atomic_cas".to_string(),
439
440            Operation::WaveOp { op, .. } | Operation::WaveVote { op, .. } => {
441                op.mnemonic().to_string()
442            }
443            Operation::WaveReduce { op, .. } => op.mnemonic().to_string(),
444            Operation::WaveBallot { .. } => "wave_ballot".to_string(),
445
446            Operation::If { .. } => "if".to_string(),
447            Operation::Else => "else".to_string(),
448            Operation::Endif => "endif".to_string(),
449            Operation::Loop => "loop".to_string(),
450            Operation::Break { .. } => "break".to_string(),
451            Operation::Continue { .. } => "continue".to_string(),
452            Operation::Endloop => "endloop".to_string(),
453            Operation::Call { .. } => "call".to_string(),
454
455            Operation::Return => "return".to_string(),
456            Operation::Halt => "halt".to_string(),
457            Operation::Barrier => "barrier".to_string(),
458            Operation::FenceAcquire { .. } => "fence_acquire".to_string(),
459            Operation::FenceRelease { .. } => "fence_release".to_string(),
460            Operation::FenceAcqRel { .. } => "fence_acq_rel".to_string(),
461            Operation::Wait => "wait".to_string(),
462            Operation::Nop => "nop".to_string(),
463
464            Operation::Mov { .. } | Operation::MovSr { .. } => "mov".to_string(),
465            Operation::MovImm { .. } => "mov_imm".to_string(),
466
467            Operation::Unknown { opcode, .. } => format!(".unknown 0x{opcode:02x}"),
468        }
469    }
470
471    /// Check if this is a control flow instruction
472    #[must_use]
473    pub fn is_control_flow(&self) -> bool {
474        matches!(
475            self.operation,
476            Operation::If { .. }
477                | Operation::Else
478                | Operation::Endif
479                | Operation::Loop
480                | Operation::Break { .. }
481                | Operation::Continue { .. }
482                | Operation::Endloop
483                | Operation::Call { .. }
484                | Operation::Return
485                | Operation::Halt
486        )
487    }
488
489    /// Check if this is a memory operation
490    #[must_use]
491    pub fn is_memory(&self) -> bool {
492        matches!(
493            self.operation,
494            Operation::LocalLoad { .. }
495                | Operation::LocalStore { .. }
496                | Operation::DeviceLoad { .. }
497                | Operation::DeviceStore { .. }
498                | Operation::LocalAtomic { .. }
499                | Operation::LocalAtomicCas { .. }
500                | Operation::DeviceAtomic { .. }
501                | Operation::DeviceAtomicCas { .. }
502        )
503    }
504
505    /// Check if this is a barrier or fence
506    #[must_use]
507    pub fn is_sync(&self) -> bool {
508        matches!(
509            self.operation,
510            Operation::Barrier
511                | Operation::FenceAcquire { .. }
512                | Operation::FenceRelease { .. }
513                | Operation::FenceAcqRel { .. }
514                | Operation::Wait
515        )
516    }
517
518    /// Check if this is a wave operation
519    #[must_use]
520    pub fn is_wave_op(&self) -> bool {
521        matches!(
522            self.operation,
523            Operation::WaveOp { .. }
524                | Operation::WaveReduce { .. }
525                | Operation::WaveBallot { .. }
526                | Operation::WaveVote { .. }
527        )
528    }
529}
530
531/// Helper to get raw opcode and modifier from instruction
532#[must_use]
533pub fn extract_opcode_modifier(instruction: &DecodedInstruction) -> (Opcode, Option<u8>) {
534    match &instruction.operation {
535        Operation::Iadd { .. } => (Opcode::Iadd, None),
536        Operation::Isub { .. } => (Opcode::Isub, None),
537        Operation::Imul { .. } => (Opcode::Imul, None),
538        Operation::ImulHi { .. } => (Opcode::ImulHi, None),
539        Operation::Imad { .. } => (Opcode::Imad, None),
540        Operation::Idiv { .. } => (Opcode::Idiv, None),
541        Operation::Imod { .. } => (Opcode::Imod, None),
542        Operation::Ineg { .. } => (Opcode::Ineg, None),
543        Operation::Iabs { .. } => (Opcode::Iabs, None),
544        Operation::Imin { .. } => (Opcode::Imin, None),
545        Operation::Imax { .. } => (Opcode::Imax, None),
546        Operation::Iclamp { .. } => (Opcode::Iclamp, None),
547
548        Operation::Fadd { .. } => (Opcode::Fadd, None),
549        Operation::Fsub { .. } => (Opcode::Fsub, None),
550        Operation::Fmul { .. } => (Opcode::Fmul, None),
551        Operation::Fma { .. } => (Opcode::Fma, None),
552        Operation::Fdiv { .. } => (Opcode::Fdiv, None),
553        Operation::Fneg { .. } => (Opcode::Fneg, None),
554        Operation::Fabs { .. } => (Opcode::Fabs, None),
555        Operation::Fmin { .. } => (Opcode::Fmin, None),
556        Operation::Fmax { .. } => (Opcode::Fmax, None),
557        Operation::Fclamp { .. } => (Opcode::Fclamp, None),
558        Operation::Fsqrt { .. } => (Opcode::Fsqrt, None),
559        Operation::FUnary { op, .. } => (Opcode::FUnaryOps, Some(*op as u8)),
560
561        Operation::F16 { op, .. } => (Opcode::F16Ops, Some(*op as u8)),
562        Operation::F16Packed { op, .. } => (Opcode::F16PackedOps, Some(*op as u8)),
563        Operation::F64 { op, .. } => (Opcode::F64Ops, Some(*op as u8)),
564        Operation::F64DivSqrt { op, .. } => (Opcode::F64DivSqrt, Some(*op as u8)),
565
566        Operation::And { .. } => (Opcode::And, None),
567        Operation::Or { .. } => (Opcode::Or, None),
568        Operation::Xor { .. } => (Opcode::Xor, None),
569        Operation::Not { .. } => (Opcode::Not, None),
570        Operation::Shl { .. } => (Opcode::Shl, None),
571        Operation::Shr { .. } => (Opcode::Shr, None),
572        Operation::Sar { .. } => (Opcode::Sar, None),
573        Operation::BitOp { op, .. } => (Opcode::BitOps, Some(*op as u8)),
574
575        Operation::Icmp { op, .. } => (Opcode::Icmp, Some(*op as u8)),
576        Operation::Ucmp { op, .. } => (Opcode::Ucmp, Some(*op as u8)),
577        Operation::Fcmp { op, .. } => (Opcode::Fcmp, Some(*op as u8)),
578
579        Operation::Select { .. } => (Opcode::Select, None),
580        Operation::Cvt { cvt_type, .. } => (Opcode::Cvt, Some(*cvt_type as u8)),
581
582        Operation::LocalLoad { width, .. } => (Opcode::LocalLoad, Some(*width as u8)),
583        Operation::LocalStore { width, .. } => (Opcode::LocalStore, Some(*width as u8)),
584        Operation::DeviceLoad { width, .. } => (Opcode::DeviceLoad, Some(*width as u8)),
585        Operation::DeviceStore { width, .. } => (Opcode::DeviceStore, Some(*width as u8)),
586
587        Operation::LocalAtomic { op, .. } => (Opcode::LocalAtomic, Some(*op as u8)),
588        Operation::LocalAtomicCas { .. } => (Opcode::LocalAtomic, None),
589        Operation::DeviceAtomic { op, .. } => (Opcode::DeviceAtomic, Some(*op as u8)),
590        Operation::DeviceAtomicCas { .. } => (Opcode::DeviceAtomic, None),
591
592        Operation::WaveOp { op, .. } | Operation::WaveVote { op, .. } => {
593            (Opcode::WaveOp, Some(*op as u8))
594        }
595        Operation::WaveReduce { op, .. } => (Opcode::WaveOp, Some(*op as u8)),
596        Operation::WaveBallot { .. } => (Opcode::WaveOp, Some(WaveOpType::Ballot as u8)),
597
598        Operation::If { .. } => (Opcode::Control, Some(ControlOp::If as u8)),
599        Operation::Else => (Opcode::Control, Some(ControlOp::Else as u8)),
600        Operation::Endif => (Opcode::Control, Some(ControlOp::Endif as u8)),
601        Operation::Loop => (Opcode::Control, Some(ControlOp::Loop as u8)),
602        Operation::Break { .. } => (Opcode::Control, Some(ControlOp::Break as u8)),
603        Operation::Continue { .. } => (Opcode::Control, Some(ControlOp::Continue as u8)),
604        Operation::Endloop => (Opcode::Control, Some(ControlOp::Endloop as u8)),
605        Operation::Call { .. } => (Opcode::Control, Some(ControlOp::Call as u8)),
606
607        Operation::Return => (Opcode::Control, Some(SyncOp::Return as u8)),
608        Operation::Halt => (Opcode::Control, Some(SyncOp::Halt as u8)),
609        Operation::Barrier => (Opcode::Control, Some(SyncOp::Barrier as u8)),
610        Operation::FenceAcquire { .. } => (Opcode::Control, Some(SyncOp::FenceAcquire as u8)),
611        Operation::FenceRelease { .. } => (Opcode::Control, Some(SyncOp::FenceRelease as u8)),
612        Operation::FenceAcqRel { .. } => (Opcode::Control, Some(SyncOp::FenceAcqRel as u8)),
613        Operation::Wait => (Opcode::Control, Some(SyncOp::Wait as u8)),
614        Operation::Nop => (Opcode::Control, Some(SyncOp::Nop as u8)),
615
616        Operation::Mov { .. } => (Opcode::Control, Some(MiscOp::Mov as u8)),
617        Operation::MovImm { .. } => (Opcode::Control, Some(MiscOp::MovImm as u8)),
618        Operation::MovSr { .. } => (Opcode::Control, Some(MiscOp::MovSr as u8)),
619
620        Operation::Unknown { opcode, .. } => {
621            Opcode::from_u8(*opcode).map_or((Opcode::Control, None), |op| (op, None))
622        }
623    }
624}