1use crate::EmulatorError;
9
10pub use wave_decode::{
11 AtomicOp, BitOpType, CmpOp, ControlOp, CvtType, F16Op, F16PackedOp, F64DivSqrtOp, F64Op,
12 FUnaryOp, MemWidth, MiscOp, Opcode, Scope, SyncOp, WaveOpType, WaveReduceType,
13};
14
15pub use wave_decode::opcodes::{MISC_OP_FLAG, SYNC_OP_FLAG};
16
17#[derive(Debug, Clone)]
19pub struct DecodedInstruction {
20 pub opcode: Opcode,
21 pub rd: u8,
22 pub rs1: u8,
23 pub rs2: u8,
24 pub rs3: u8,
25 pub rs4: u8,
26 pub modifier: u8,
27 pub scope: u8,
28 pub pred_reg: u8,
29 pub pred_neg: bool,
30 pub flags: u8,
31 pub immediate: u32,
32 pub size: u32,
33}
34
35impl DecodedInstruction {
36 pub fn is_predicated(&self) -> bool {
37 self.pred_reg != 0 || self.pred_neg
38 }
39
40 pub fn is_sync_op(&self) -> bool {
41 self.opcode == Opcode::Control && (self.flags & SYNC_OP_FLAG) != 0
42 }
43
44 pub fn is_misc_op(&self) -> bool {
45 self.opcode == Opcode::Control && (self.flags & MISC_OP_FLAG) != 0
46 }
47
48 pub fn is_non_returning_atomic(&self) -> bool {
49 (self.opcode == Opcode::LocalAtomic || self.opcode == Opcode::DeviceAtomic) && self.rd == 0
50 }
51
52 pub fn is_wave_reduce(&self) -> bool {
53 self.opcode == Opcode::WaveOp && self.modifier >= 8
54 }
55}
56
57pub struct Decoder<'a> {
58 code: &'a [u8],
59}
60
61impl<'a> Decoder<'a> {
62 pub fn new(code: &'a [u8]) -> Self {
63 Self { code }
64 }
65
66 pub fn decode_at(&self, pc: u32) -> Result<DecodedInstruction, EmulatorError> {
67 let decoded = wave_decode::decode_at(self.code, pc).map_err(|e| {
68 EmulatorError::InvalidInstruction {
69 pc,
70 message: e.to_string(),
71 }
72 })?;
73
74 Ok(convert_instruction(&decoded))
75 }
76
77 pub fn disassemble(&self, inst: &DecodedInstruction) -> String {
78 let pred = if inst.is_predicated() {
79 let neg = if inst.pred_neg { "!" } else { "" };
80 format!("@{neg}p{} ", inst.pred_reg)
81 } else {
82 String::new()
83 };
84
85 let mnemonic = self.get_mnemonic(inst);
86 let operands = self.get_operands(inst);
87
88 if operands.is_empty() {
89 format!("{pred}{mnemonic}")
90 } else {
91 format!("{pred}{mnemonic} {operands}")
92 }
93 }
94
95 fn get_mnemonic(&self, inst: &DecodedInstruction) -> &'static str {
96 match inst.opcode {
97 Opcode::Iadd => "iadd",
98 Opcode::Isub => "isub",
99 Opcode::Imul => "imul",
100 Opcode::ImulHi => "imul_hi",
101 Opcode::Imad => "imad",
102 Opcode::Idiv => "idiv",
103 Opcode::Imod => "imod",
104 Opcode::Ineg => "ineg",
105 Opcode::Iabs => "iabs",
106 Opcode::Imin => "imin",
107 Opcode::Imax => "imax",
108 Opcode::Iclamp => "iclamp",
109 Opcode::Fadd => "fadd",
110 Opcode::Fsub => "fsub",
111 Opcode::Fmul => "fmul",
112 Opcode::Fma => "fma",
113 Opcode::Fdiv => "fdiv",
114 Opcode::Fneg => "fneg",
115 Opcode::Fabs => "fabs",
116 Opcode::Fmin => "fmin",
117 Opcode::Fmax => "fmax",
118 Opcode::Fclamp => "fclamp",
119 Opcode::Fsqrt => "fsqrt",
120 Opcode::FUnaryOps => {
121 FUnaryOp::from_u8(inst.modifier).map_or("f_unknown", |op| op.mnemonic())
122 }
123 Opcode::F16Ops => F16Op::from_u8(inst.modifier).map_or("h_unknown", |op| op.mnemonic()),
124 Opcode::F16PackedOps => {
125 F16PackedOp::from_u8(inst.modifier).map_or("h2_unknown", |op| op.mnemonic())
126 }
127 Opcode::F64Ops => F64Op::from_u8(inst.modifier).map_or("d_unknown", |op| op.mnemonic()),
128 Opcode::F64DivSqrt => {
129 F64DivSqrtOp::from_u8(inst.modifier).map_or("d_unknown", |op| op.mnemonic())
130 }
131 Opcode::And => "and",
132 Opcode::Or => "or",
133 Opcode::Xor => "xor",
134 Opcode::Not => "not",
135 Opcode::Shl => "shl",
136 Opcode::Shr => "shr",
137 Opcode::Sar => "sar",
138 Opcode::BitOps => {
139 BitOpType::from_u8(inst.modifier).map_or("bit_unknown", |op| op.mnemonic())
140 }
141 Opcode::Icmp => match inst.modifier {
142 0 => "icmp_eq",
143 1 => "icmp_ne",
144 2 => "icmp_lt",
145 3 => "icmp_le",
146 4 => "icmp_gt",
147 5 => "icmp_ge",
148 _ => "icmp_unknown",
149 },
150 Opcode::Ucmp => match inst.modifier {
151 2 => "ucmp_lt",
152 3 => "ucmp_le",
153 _ => "ucmp_unknown",
154 },
155 Opcode::Fcmp => match inst.modifier {
156 0 => "fcmp_eq",
157 1 => "fcmp_ne",
158 2 => "fcmp_lt",
159 3 => "fcmp_le",
160 4 => "fcmp_gt",
161 6 => "fcmp_ord",
162 7 => "fcmp_unord",
163 _ => "fcmp_unknown",
164 },
165 Opcode::Select => "select",
166 Opcode::Cvt => {
167 CvtType::from_u8(inst.modifier).map_or("cvt_unknown", |ct| ct.mnemonic())
168 }
169 Opcode::LocalLoad => match inst.modifier {
170 0 => "local_load_u8",
171 1 => "local_load_u16",
172 2 => "local_load_u32",
173 3 => "local_load_u64",
174 _ => "local_load_unknown",
175 },
176 Opcode::LocalStore => match inst.modifier {
177 0 => "local_store_u8",
178 1 => "local_store_u16",
179 2 => "local_store_u32",
180 3 => "local_store_u64",
181 _ => "local_store_unknown",
182 },
183 Opcode::DeviceLoad => match inst.modifier {
184 0 => "device_load_u8",
185 1 => "device_load_u16",
186 2 => "device_load_u32",
187 3 => "device_load_u64",
188 4 => "device_load_u128",
189 _ => "device_load_unknown",
190 },
191 Opcode::DeviceStore => match inst.modifier {
192 0 => "device_store_u8",
193 1 => "device_store_u16",
194 2 => "device_store_u32",
195 3 => "device_store_u64",
196 4 => "device_store_u128",
197 _ => "device_store_unknown",
198 },
199 Opcode::LocalAtomic => match inst.modifier {
200 0 => "local_atomic_add",
201 1 => "local_atomic_sub",
202 2 => "local_atomic_min",
203 3 => "local_atomic_max",
204 4 => "local_atomic_and",
205 5 => "local_atomic_or",
206 6 => "local_atomic_xor",
207 7 => "local_atomic_exchange",
208 _ => "local_atomic_cas",
209 },
210 Opcode::DeviceAtomic => match inst.modifier {
211 0 => "atomic_add",
212 1 => "atomic_sub",
213 2 => "atomic_min",
214 3 => "atomic_max",
215 4 => "atomic_and",
216 5 => "atomic_or",
217 6 => "atomic_xor",
218 7 => "atomic_exchange",
219 _ => "atomic_cas",
220 },
221 Opcode::WaveOp => {
222 if inst.is_wave_reduce() {
223 WaveReduceType::from_u8(inst.modifier - 8)
224 .map_or("wave_reduce_unknown", |op| op.mnemonic())
225 } else {
226 WaveOpType::from_u8(inst.modifier).map_or("wave_unknown", |op| op.mnemonic())
227 }
228 }
229 Opcode::Control => {
230 if inst.is_sync_op() {
231 SyncOp::from_u8(inst.modifier).map_or("sync_unknown", |op| op.mnemonic())
232 } else if inst.is_misc_op() {
233 MiscOp::from_u8(inst.modifier).map_or("misc_unknown", |op| op.mnemonic())
234 } else {
235 ControlOp::from_u8(inst.modifier).map_or("control_unknown", |op| op.mnemonic())
236 }
237 }
238 }
239 }
240
241 fn get_operands(&self, inst: &DecodedInstruction) -> String {
242 match inst.opcode {
243 Opcode::Iadd
244 | Opcode::Isub
245 | Opcode::Imul
246 | Opcode::ImulHi
247 | Opcode::Idiv
248 | Opcode::Imod
249 | Opcode::Imin
250 | Opcode::Imax
251 | Opcode::Fadd
252 | Opcode::Fsub
253 | Opcode::Fmul
254 | Opcode::Fdiv
255 | Opcode::Fmin
256 | Opcode::Fmax
257 | Opcode::And
258 | Opcode::Or
259 | Opcode::Xor
260 | Opcode::Shl
261 | Opcode::Shr
262 | Opcode::Sar => {
263 format!("r{}, r{}, r{}", inst.rd, inst.rs1, inst.rs2)
264 }
265 Opcode::Imad | Opcode::Iclamp | Opcode::Fma | Opcode::Fclamp => {
266 format!("r{}, r{}, r{}, r{}", inst.rd, inst.rs1, inst.rs2, inst.rs3)
267 }
268 Opcode::Ineg
269 | Opcode::Iabs
270 | Opcode::Fneg
271 | Opcode::Fabs
272 | Opcode::Fsqrt
273 | Opcode::Not => {
274 format!("r{}, r{}", inst.rd, inst.rs1)
275 }
276 Opcode::FUnaryOps => format!("r{}, r{}", inst.rd, inst.rs1),
277 Opcode::Icmp | Opcode::Ucmp | Opcode::Fcmp => {
278 format!("p{}, r{}, r{}", inst.rd, inst.rs1, inst.rs2)
279 }
280 Opcode::Select => {
281 format!(
282 "r{}, p{}, r{}, r{}",
283 inst.rd, inst.modifier, inst.rs1, inst.rs2
284 )
285 }
286 Opcode::Cvt => format!("r{}, r{}", inst.rd, inst.rs1),
287 Opcode::LocalLoad | Opcode::DeviceLoad => format!("r{}, r{}", inst.rd, inst.rs1),
288 Opcode::LocalStore | Opcode::DeviceStore => format!("r{}, r{}", inst.rs1, inst.rs2),
289 Opcode::LocalAtomic | Opcode::DeviceAtomic => {
290 if inst.is_non_returning_atomic() {
291 format!("r{}, r{}", inst.rs1, inst.rs2)
292 } else {
293 format!("r{}, r{}, r{}", inst.rd, inst.rs1, inst.rs2)
294 }
295 }
296 Opcode::WaveOp => {
297 let mnemonic = self.get_mnemonic(inst);
298 if mnemonic == "wave_ballot" {
299 format!("r{}, p{}", inst.rd, inst.rs1)
300 } else if mnemonic == "wave_any" || mnemonic == "wave_all" {
301 format!("p{}, p{}", inst.rd, inst.rs1)
302 } else if mnemonic.starts_with("wave_reduce") || mnemonic == "wave_prefix_sum" {
303 format!("r{}, r{}", inst.rd, inst.rs1)
304 } else {
305 format!("r{}, r{}, r{}", inst.rd, inst.rs1, inst.rs2)
306 }
307 }
308 Opcode::Control => {
309 if inst.is_misc_op() {
310 match inst.modifier {
311 0 => format!("r{}, r{}", inst.rd, inst.rs1),
312 1 => format!("r{}, 0x{:08x}", inst.rd, inst.immediate),
313 2 => format!("r{}, sr_{}", inst.rd, inst.rs1),
314 _ => String::new(),
315 }
316 } else if inst.is_sync_op() {
317 String::new()
318 } else {
319 match inst.modifier {
320 0 | 4 | 5 => format!("p{}", inst.rs1),
321 7 => format!("0x{:08x}", inst.immediate),
322 _ => String::new(),
323 }
324 }
325 }
326 _ => String::new(),
327 }
328 }
329}
330
331fn convert_instruction(decoded: &wave_decode::DecodedInstruction) -> DecodedInstruction {
332 use wave_decode::Operation;
333
334 let (opcode, rd, rs1, rs2, rs3, rs4, modifier, scope, flags, immediate) = match &decoded
335 .operation
336 {
337 Operation::Iadd { rd, rs1, rs2 } => (Opcode::Iadd, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
338 Operation::Isub { rd, rs1, rs2 } => (Opcode::Isub, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
339 Operation::Imul { rd, rs1, rs2 } => (Opcode::Imul, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
340 Operation::ImulHi { rd, rs1, rs2 } => (Opcode::ImulHi, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
341 Operation::Imad { rd, rs1, rs2, rs3 } => {
342 (Opcode::Imad, *rd, *rs1, *rs2, *rs3, 0, 0, 0, 0, 0)
343 }
344 Operation::Idiv { rd, rs1, rs2 } => (Opcode::Idiv, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
345 Operation::Imod { rd, rs1, rs2 } => (Opcode::Imod, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
346 Operation::Ineg { rd, rs1 } => (Opcode::Ineg, *rd, *rs1, 0, 0, 0, 0, 0, 0, 0),
347 Operation::Iabs { rd, rs1 } => (Opcode::Iabs, *rd, *rs1, 0, 0, 0, 0, 0, 0, 0),
348 Operation::Imin { rd, rs1, rs2 } => (Opcode::Imin, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
349 Operation::Imax { rd, rs1, rs2 } => (Opcode::Imax, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
350 Operation::Iclamp { rd, rs1, rs2, rs3 } => {
351 (Opcode::Iclamp, *rd, *rs1, *rs2, *rs3, 0, 0, 0, 0, 0)
352 }
353
354 Operation::Fadd { rd, rs1, rs2 } => (Opcode::Fadd, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
355 Operation::Fsub { rd, rs1, rs2 } => (Opcode::Fsub, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
356 Operation::Fmul { rd, rs1, rs2 } => (Opcode::Fmul, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
357 Operation::Fma { rd, rs1, rs2, rs3 } => (Opcode::Fma, *rd, *rs1, *rs2, *rs3, 0, 0, 0, 0, 0),
358 Operation::Fdiv { rd, rs1, rs2 } => (Opcode::Fdiv, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
359 Operation::Fneg { rd, rs1 } => (Opcode::Fneg, *rd, *rs1, 0, 0, 0, 0, 0, 0, 0),
360 Operation::Fabs { rd, rs1 } => (Opcode::Fabs, *rd, *rs1, 0, 0, 0, 0, 0, 0, 0),
361 Operation::Fmin { rd, rs1, rs2 } => (Opcode::Fmin, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
362 Operation::Fmax { rd, rs1, rs2 } => (Opcode::Fmax, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
363 Operation::Fclamp { rd, rs1, rs2, rs3 } => {
364 (Opcode::Fclamp, *rd, *rs1, *rs2, *rs3, 0, 0, 0, 0, 0)
365 }
366 Operation::Fsqrt { rd, rs1 } => (Opcode::Fsqrt, *rd, *rs1, 0, 0, 0, 0, 0, 0, 0),
367 Operation::FUnary { op, rd, rs1 } => {
368 (Opcode::FUnaryOps, *rd, *rs1, 0, 0, 0, *op as u8, 0, 0, 0)
369 }
370
371 Operation::F16 {
372 op,
373 rd,
374 rs1,
375 rs2,
376 rs3,
377 } => (
378 Opcode::F16Ops,
379 *rd,
380 *rs1,
381 *rs2,
382 rs3.unwrap_or(0),
383 0,
384 *op as u8,
385 0,
386 0,
387 0,
388 ),
389 Operation::F16Packed {
390 op,
391 rd,
392 rs1,
393 rs2,
394 rs3,
395 } => (
396 Opcode::F16PackedOps,
397 *rd,
398 *rs1,
399 *rs2,
400 rs3.unwrap_or(0),
401 0,
402 *op as u8,
403 0,
404 0,
405 0,
406 ),
407
408 Operation::F64 {
409 op,
410 rd,
411 rs1,
412 rs2,
413 rs3,
414 } => (
415 Opcode::F64Ops,
416 *rd,
417 *rs1,
418 *rs2,
419 rs3.unwrap_or(0),
420 0,
421 *op as u8,
422 0,
423 0,
424 0,
425 ),
426 Operation::F64DivSqrt { op, rd, rs1, rs2 } => (
427 Opcode::F64DivSqrt,
428 *rd,
429 *rs1,
430 rs2.unwrap_or(0),
431 0,
432 0,
433 *op as u8,
434 0,
435 0,
436 0,
437 ),
438
439 Operation::And { rd, rs1, rs2 } => (Opcode::And, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
440 Operation::Or { rd, rs1, rs2 } => (Opcode::Or, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
441 Operation::Xor { rd, rs1, rs2 } => (Opcode::Xor, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
442 Operation::Not { rd, rs1 } => (Opcode::Not, *rd, *rs1, 0, 0, 0, 0, 0, 0, 0),
443 Operation::Shl { rd, rs1, rs2 } => (Opcode::Shl, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
444 Operation::Shr { rd, rs1, rs2 } => (Opcode::Shr, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
445 Operation::Sar { rd, rs1, rs2 } => (Opcode::Sar, *rd, *rs1, *rs2, 0, 0, 0, 0, 0, 0),
446 Operation::BitOp {
447 op,
448 rd,
449 rs1,
450 rs2,
451 rs3,
452 rs4,
453 } => (
454 Opcode::BitOps,
455 *rd,
456 *rs1,
457 rs2.unwrap_or(0),
458 rs3.unwrap_or(0),
459 rs4.unwrap_or(0),
460 *op as u8,
461 0,
462 0,
463 0,
464 ),
465
466 Operation::Icmp { op, pd, rs1, rs2 } => {
467 (Opcode::Icmp, *pd, *rs1, *rs2, 0, 0, *op as u8, 0, 0, 0)
468 }
469 Operation::Ucmp { op, pd, rs1, rs2 } => {
470 (Opcode::Ucmp, *pd, *rs1, *rs2, 0, 0, *op as u8, 0, 0, 0)
471 }
472 Operation::Fcmp { op, pd, rs1, rs2 } => {
473 (Opcode::Fcmp, *pd, *rs1, *rs2, 0, 0, *op as u8, 0, 0, 0)
474 }
475
476 Operation::Select { rd, ps, rs1, rs2 } => {
477 (Opcode::Select, *rd, *rs1, *rs2, 0, 0, *ps, 0, 0, 0)
478 }
479 Operation::Cvt { cvt_type, rd, rs1 } => {
480 (Opcode::Cvt, *rd, *rs1, 0, 0, 0, *cvt_type as u8, 0, 0, 0)
481 }
482
483 Operation::LocalLoad { width, rd, addr } => (
484 Opcode::LocalLoad,
485 *rd,
486 *addr,
487 0,
488 0,
489 0,
490 *width as u8,
491 0,
492 0,
493 0,
494 ),
495 Operation::LocalStore { width, addr, value } => (
496 Opcode::LocalStore,
497 0,
498 *addr,
499 *value,
500 0,
501 0,
502 *width as u8,
503 0,
504 0,
505 0,
506 ),
507
508 Operation::DeviceLoad { width, rd, addr } => (
509 Opcode::DeviceLoad,
510 *rd,
511 *addr,
512 0,
513 0,
514 0,
515 *width as u8,
516 0,
517 0,
518 0,
519 ),
520 Operation::DeviceStore { width, addr, value } => (
521 Opcode::DeviceStore,
522 0,
523 *addr,
524 *value,
525 0,
526 0,
527 *width as u8,
528 0,
529 0,
530 0,
531 ),
532
533 Operation::LocalAtomic {
534 op,
535 rd,
536 addr,
537 value,
538 } => {
539 let rd_val = rd.unwrap_or(0);
540 (
541 Opcode::LocalAtomic,
542 rd_val,
543 *addr,
544 *value,
545 0,
546 0,
547 *op as u8,
548 0,
549 0,
550 0,
551 )
552 }
553 Operation::LocalAtomicCas {
554 rd,
555 addr,
556 expected,
557 desired,
558 } => {
559 let rd_val = rd.unwrap_or(0);
560 (
561 Opcode::LocalAtomic,
562 rd_val,
563 *addr,
564 *expected,
565 *desired,
566 0,
567 8,
568 0,
569 0,
570 0,
571 )
572 }
573 Operation::DeviceAtomic {
574 op,
575 rd,
576 addr,
577 value,
578 scope,
579 } => {
580 let rd_val = rd.unwrap_or(0);
581 (
582 Opcode::DeviceAtomic,
583 rd_val,
584 *addr,
585 *value,
586 0,
587 0,
588 *op as u8,
589 *scope as u8,
590 0,
591 0,
592 )
593 }
594 Operation::DeviceAtomicCas {
595 rd,
596 addr,
597 expected,
598 desired,
599 scope,
600 } => {
601 let rd_val = rd.unwrap_or(0);
602 (
603 Opcode::DeviceAtomic,
604 rd_val,
605 *addr,
606 *expected,
607 *desired,
608 0,
609 8,
610 *scope as u8,
611 0,
612 0,
613 )
614 }
615
616 Operation::WaveOp { op, rd, rs1, rs2 } => (
617 Opcode::WaveOp,
618 *rd,
619 *rs1,
620 rs2.unwrap_or(0),
621 0,
622 0,
623 *op as u8,
624 0,
625 0,
626 0,
627 ),
628 Operation::WaveReduce { op, rd, rs1 } => {
629 (Opcode::WaveOp, *rd, *rs1, 0, 0, 0, *op as u8 + 8, 0, 0, 0)
630 }
631 Operation::WaveBallot { rd, ps } => (
632 Opcode::WaveOp,
633 *rd,
634 *ps,
635 0,
636 0,
637 0,
638 WaveOpType::Ballot as u8,
639 0,
640 0,
641 0,
642 ),
643 Operation::WaveVote { op, pd, ps } => {
644 (Opcode::WaveOp, *pd, *ps, 0, 0, 0, *op as u8, 0, 0, 0)
645 }
646
647 Operation::If { ps } => (
648 Opcode::Control,
649 0,
650 *ps,
651 0,
652 0,
653 0,
654 ControlOp::If as u8,
655 0,
656 0,
657 0,
658 ),
659 Operation::Else => (
660 Opcode::Control,
661 0,
662 0,
663 0,
664 0,
665 0,
666 ControlOp::Else as u8,
667 0,
668 0,
669 0,
670 ),
671 Operation::Endif => (
672 Opcode::Control,
673 0,
674 0,
675 0,
676 0,
677 0,
678 ControlOp::Endif as u8,
679 0,
680 0,
681 0,
682 ),
683 Operation::Loop => (
684 Opcode::Control,
685 0,
686 0,
687 0,
688 0,
689 0,
690 ControlOp::Loop as u8,
691 0,
692 0,
693 0,
694 ),
695 Operation::Break { ps } => (
696 Opcode::Control,
697 0,
698 *ps,
699 0,
700 0,
701 0,
702 ControlOp::Break as u8,
703 0,
704 0,
705 0,
706 ),
707 Operation::Continue { ps } => (
708 Opcode::Control,
709 0,
710 *ps,
711 0,
712 0,
713 0,
714 ControlOp::Continue as u8,
715 0,
716 0,
717 0,
718 ),
719 Operation::Endloop => (
720 Opcode::Control,
721 0,
722 0,
723 0,
724 0,
725 0,
726 ControlOp::Endloop as u8,
727 0,
728 0,
729 0,
730 ),
731 Operation::Call { target } => (
732 Opcode::Control,
733 0,
734 0,
735 0,
736 0,
737 0,
738 ControlOp::Call as u8,
739 0,
740 0,
741 *target,
742 ),
743
744 Operation::Return => (
745 Opcode::Control,
746 0,
747 0,
748 0,
749 0,
750 0,
751 SyncOp::Return as u8,
752 0,
753 SYNC_OP_FLAG,
754 0,
755 ),
756 Operation::Halt => (
757 Opcode::Control,
758 0,
759 0,
760 0,
761 0,
762 0,
763 SyncOp::Halt as u8,
764 0,
765 SYNC_OP_FLAG,
766 0,
767 ),
768 Operation::Barrier => (
769 Opcode::Control,
770 0,
771 0,
772 0,
773 0,
774 0,
775 SyncOp::Barrier as u8,
776 0,
777 SYNC_OP_FLAG,
778 0,
779 ),
780 Operation::FenceAcquire { scope } => (
781 Opcode::Control,
782 0,
783 0,
784 0,
785 0,
786 0,
787 SyncOp::FenceAcquire as u8,
788 *scope as u8,
789 SYNC_OP_FLAG,
790 0,
791 ),
792 Operation::FenceRelease { scope } => (
793 Opcode::Control,
794 0,
795 0,
796 0,
797 0,
798 0,
799 SyncOp::FenceRelease as u8,
800 *scope as u8,
801 SYNC_OP_FLAG,
802 0,
803 ),
804 Operation::FenceAcqRel { scope } => (
805 Opcode::Control,
806 0,
807 0,
808 0,
809 0,
810 0,
811 SyncOp::FenceAcqRel as u8,
812 *scope as u8,
813 SYNC_OP_FLAG,
814 0,
815 ),
816 Operation::Wait => (
817 Opcode::Control,
818 0,
819 0,
820 0,
821 0,
822 0,
823 SyncOp::Wait as u8,
824 0,
825 SYNC_OP_FLAG,
826 0,
827 ),
828 Operation::Nop => (
829 Opcode::Control,
830 0,
831 0,
832 0,
833 0,
834 0,
835 SyncOp::Nop as u8,
836 0,
837 SYNC_OP_FLAG,
838 0,
839 ),
840
841 Operation::Mov { rd, rs1 } => (
842 Opcode::Control,
843 *rd,
844 *rs1,
845 0,
846 0,
847 0,
848 MiscOp::Mov as u8,
849 0,
850 MISC_OP_FLAG,
851 0,
852 ),
853 Operation::MovImm { rd, imm } => (
854 Opcode::Control,
855 *rd,
856 0,
857 0,
858 0,
859 0,
860 MiscOp::MovImm as u8,
861 0,
862 MISC_OP_FLAG,
863 *imm,
864 ),
865 Operation::MovSr { rd, sr_index } => (
866 Opcode::Control,
867 *rd,
868 *sr_index,
869 0,
870 0,
871 0,
872 MiscOp::MovSr as u8,
873 0,
874 MISC_OP_FLAG,
875 0,
876 ),
877
878 Operation::Unknown {
879 opcode,
880 word0,
881 word1: _,
882 } => {
883 let opc = Opcode::from_u8(*opcode).unwrap_or(Opcode::Control);
884 (opc, 0, 0, 0, 0, 0, 0, 0, 0, *word0)
885 }
886 };
887
888 DecodedInstruction {
889 opcode,
890 rd,
891 rs1,
892 rs2,
893 rs3,
894 rs4,
895 modifier,
896 scope,
897 pred_reg: decoded.predicate,
898 pred_neg: decoded.predicate_negated,
899 flags,
900 immediate,
901 size: u32::from(decoded.size),
902 }
903}
904
905#[cfg(test)]
906mod tests {
907 use super::*;
908
909 fn encode_base(opcode: u8, rd: u8, rs1: u8, rs2: u8, modifier: u8, flags: u8) -> Vec<u8> {
910 let word = ((u32::from(opcode) & 0x3F) << 26)
911 | ((u32::from(rd) & 0x1F) << 21)
912 | ((u32::from(rs1) & 0x1F) << 16)
913 | ((u32::from(rs2) & 0x1F) << 11)
914 | ((u32::from(modifier) & 0x0F) << 7)
915 | (u32::from(flags) & 0x03);
916 word.to_le_bytes().to_vec()
917 }
918
919 #[test]
920 fn test_decoder_iadd() {
921 let code = encode_base(0x00, 1, 2, 3, 0, 0);
922 let decoder = Decoder::new(&code);
923 let inst = decoder.decode_at(0).unwrap();
924
925 assert_eq!(inst.opcode, Opcode::Iadd);
926 assert_eq!(inst.rd, 1);
927 assert_eq!(inst.rs1, 2);
928 assert_eq!(inst.rs2, 3);
929 assert_eq!(inst.size, 4);
930 }
931
932 #[test]
933 fn test_decoder_halt() {
934 let code = encode_base(0x3F, 0, 0, 0, 1, SYNC_OP_FLAG);
935 let decoder = Decoder::new(&code);
936 let inst = decoder.decode_at(0).unwrap();
937
938 assert_eq!(inst.opcode, Opcode::Control);
939 assert!(inst.is_sync_op());
940 assert_eq!(inst.modifier, 1);
941 }
942
943 #[test]
944 fn test_decoder_mov_imm() {
945 let word0 = encode_base(0x3F, 5, 0, 0, 1, MISC_OP_FLAG);
946 let word1 = 0x12345678u32;
947
948 let mut code = word0;
949 code.extend_from_slice(&word1.to_le_bytes());
950
951 let decoder = Decoder::new(&code);
952 let inst = decoder.decode_at(0).unwrap();
953
954 assert_eq!(inst.opcode, Opcode::Control);
955 assert!(inst.is_misc_op());
956 assert_eq!(inst.rd, 5);
957 assert_eq!(inst.immediate, 0x12345678);
958 assert_eq!(inst.size, 8);
959 }
960
961 #[test]
962 fn test_decoder_disassemble() {
963 let code = encode_base(0x00, 1, 2, 3, 0, 0);
964 let decoder = Decoder::new(&code);
965 let inst = decoder.decode_at(0).unwrap();
966 let disasm = decoder.disassemble(&inst);
967
968 assert_eq!(disasm, "iadd r1, r2, r3");
969 }
970
971 #[test]
972 fn test_decoder_out_of_bounds() {
973 let code = vec![0u8; 2];
974 let decoder = Decoder::new(&code);
975 assert!(decoder.decode_at(0).is_err());
976 }
977}