1use crate::instruction::{DecodedInstruction, Operation};
9use crate::opcodes::{
10 AtomicOp, BitOpType, CmpOp, ControlOp, CvtType, F16Op, F16PackedOp, F64DivSqrtOp, F64Op,
11 FUnaryOp, MemWidth, MiscOp, Opcode, Scope, SyncOp, WaveOpType, WaveReduceType,
12 EXTENDED_RS3_MASK, EXTENDED_RS3_SHIFT, EXTENDED_RS4_MASK, EXTENDED_RS4_SHIFT, FLAGS_MASK,
13 MISC_OP_FLAG, MODIFIER_MASK, MODIFIER_SHIFT, OPCODE_MASK, OPCODE_SHIFT, PRED_MASK,
14 PRED_NEG_MASK, PRED_NEG_SHIFT, PRED_SHIFT, RD_MASK, RD_SHIFT, RS1_MASK, RS1_SHIFT, RS2_MASK,
15 RS2_SHIFT, SCOPE_MASK, SCOPE_SHIFT, SYNC_OP_FLAG,
16};
17use thiserror::Error;
18
19#[derive(Debug, Error)]
20pub enum DecodeError {
21 #[error("unexpected end of code at offset {offset}")]
22 UnexpectedEnd { offset: u32 },
23 #[error("invalid opcode 0x{opcode:02x} at offset {offset}")]
24 InvalidOpcode { opcode: u8, offset: u32 },
25 #[error("invalid modifier {modifier} for opcode {opcode:?} at offset {offset}")]
26 InvalidModifier {
27 opcode: Opcode,
28 modifier: u8,
29 offset: u32,
30 },
31}
32
33pub struct Decoder<'a> {
35 code: &'a [u8],
36 offset: u32,
37}
38
39impl<'a> Decoder<'a> {
40 #[must_use]
42 pub fn new(code: &'a [u8]) -> Self {
43 Self { code, offset: 0 }
44 }
45
46 #[must_use]
48 pub fn offset(&self) -> u32 {
49 self.offset
50 }
51
52 #[must_use]
54 pub fn has_more(&self) -> bool {
55 (self.offset as usize) + 4 <= self.code.len()
56 }
57
58 pub fn decode_next(&mut self) -> Result<DecodedInstruction, DecodeError> {
66 let offset = self.offset;
67
68 if (offset as usize) + 4 > self.code.len() {
69 return Err(DecodeError::UnexpectedEnd { offset });
70 }
71
72 let word0 = self.read_u32();
73 let opcode_raw = ((word0 >> OPCODE_SHIFT) & OPCODE_MASK) as u8;
74 let rd = ((word0 >> RD_SHIFT) & RD_MASK) as u8;
75 let rs1 = ((word0 >> RS1_SHIFT) & RS1_MASK) as u8;
76 let rs2 = ((word0 >> RS2_SHIFT) & RS2_MASK) as u8;
77 let modifier = ((word0 >> MODIFIER_SHIFT) & MODIFIER_MASK) as u8;
78 let scope = ((word0 >> SCOPE_SHIFT) & SCOPE_MASK) as u8;
79 let predicate = ((word0 >> PRED_SHIFT) & PRED_MASK) as u8;
80 let pred_neg = ((word0 >> PRED_NEG_SHIFT) & PRED_NEG_MASK) != 0;
81 let flags = (word0 & FLAGS_MASK) as u8;
82
83 let opcode = Opcode::from_u8(opcode_raw).ok_or(DecodeError::InvalidOpcode {
84 opcode: opcode_raw,
85 offset,
86 })?;
87
88 let operation =
89 self.decode_operation(opcode, rd, rs1, rs2, modifier, scope, flags, offset)?;
90
91 let size = if self.offset > offset + 4 { 8 } else { 4 };
92
93 Ok(DecodedInstruction {
94 offset,
95 size,
96 operation,
97 predicate,
98 predicate_negated: pred_neg,
99 })
100 }
101
102 fn read_u32(&mut self) -> u32 {
103 let off = self.offset as usize;
104 let bytes = &self.code[off..off + 4];
105 self.offset += 4;
106 u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
107 }
108
109 fn peek_u32(&self) -> Option<u32> {
110 let off = self.offset as usize;
111 if off + 4 <= self.code.len() {
112 let bytes = &self.code[off..off + 4];
113 Some(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
114 } else {
115 None
116 }
117 }
118
119 #[allow(clippy::too_many_arguments, clippy::too_many_lines)]
120 fn decode_operation(
121 &mut self,
122 opcode: Opcode,
123 rd: u8,
124 rs1: u8,
125 rs2: u8,
126 modifier: u8,
127 scope: u8,
128 flags: u8,
129 offset: u32,
130 ) -> Result<Operation, DecodeError> {
131 let op = match opcode {
132 Opcode::Iadd => Operation::Iadd { rd, rs1, rs2 },
133 Opcode::Isub => Operation::Isub { rd, rs1, rs2 },
134 Opcode::Imul => Operation::Imul { rd, rs1, rs2 },
135 Opcode::ImulHi => Operation::ImulHi { rd, rs1, rs2 },
136 Opcode::Imad => {
137 let word1 = self.read_u32();
138 let rs3 = ((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8;
139 Operation::Imad { rd, rs1, rs2, rs3 }
140 }
141 Opcode::Idiv => Operation::Idiv { rd, rs1, rs2 },
142 Opcode::Imod => Operation::Imod { rd, rs1, rs2 },
143 Opcode::Ineg => Operation::Ineg { rd, rs1 },
144 Opcode::Iabs => Operation::Iabs { rd, rs1 },
145 Opcode::Imin => Operation::Imin { rd, rs1, rs2 },
146 Opcode::Imax => Operation::Imax { rd, rs1, rs2 },
147 Opcode::Iclamp => {
148 let word1 = self.read_u32();
149 let rs3 = ((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8;
150 Operation::Iclamp { rd, rs1, rs2, rs3 }
151 }
152
153 Opcode::Fadd => Operation::Fadd { rd, rs1, rs2 },
154 Opcode::Fsub => Operation::Fsub { rd, rs1, rs2 },
155 Opcode::Fmul => Operation::Fmul { rd, rs1, rs2 },
156 Opcode::Fma => {
157 let word1 = self.read_u32();
158 let rs3 = ((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8;
159 Operation::Fma { rd, rs1, rs2, rs3 }
160 }
161 Opcode::Fdiv => Operation::Fdiv { rd, rs1, rs2 },
162 Opcode::Fneg => Operation::Fneg { rd, rs1 },
163 Opcode::Fabs => Operation::Fabs { rd, rs1 },
164 Opcode::Fmin => Operation::Fmin { rd, rs1, rs2 },
165 Opcode::Fmax => Operation::Fmax { rd, rs1, rs2 },
166 Opcode::Fclamp => {
167 let word1 = self.read_u32();
168 let rs3 = ((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8;
169 Operation::Fclamp { rd, rs1, rs2, rs3 }
170 }
171 Opcode::Fsqrt => Operation::Fsqrt { rd, rs1 },
172 Opcode::FUnaryOps => {
173 let op = FUnaryOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
174 opcode,
175 modifier,
176 offset,
177 })?;
178 Operation::FUnary { op, rd, rs1 }
179 }
180
181 Opcode::F16Ops => {
182 let op = F16Op::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
183 opcode,
184 modifier,
185 offset,
186 })?;
187 let rs3 = if op == F16Op::Hma {
188 let word1 = self.read_u32();
189 Some(((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8)
190 } else {
191 None
192 };
193 Operation::F16 {
194 op,
195 rd,
196 rs1,
197 rs2,
198 rs3,
199 }
200 }
201
202 Opcode::F16PackedOps => {
203 let op = F16PackedOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
204 opcode,
205 modifier,
206 offset,
207 })?;
208 let rs3 = if op == F16PackedOp::Hma2 {
209 let word1 = self.read_u32();
210 Some(((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8)
211 } else {
212 None
213 };
214 Operation::F16Packed {
215 op,
216 rd,
217 rs1,
218 rs2,
219 rs3,
220 }
221 }
222
223 Opcode::F64Ops => {
224 let op = F64Op::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
225 opcode,
226 modifier,
227 offset,
228 })?;
229 let rs3 = if op == F64Op::Dma {
230 let word1 = self.read_u32();
231 Some(((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8)
232 } else {
233 None
234 };
235 Operation::F64 {
236 op,
237 rd,
238 rs1,
239 rs2,
240 rs3,
241 }
242 }
243
244 Opcode::F64DivSqrt => {
245 let op = F64DivSqrtOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
246 opcode,
247 modifier,
248 offset,
249 })?;
250 let rs2_opt = if op == F64DivSqrtOp::Ddiv {
251 Some(rs2)
252 } else {
253 None
254 };
255 Operation::F64DivSqrt {
256 op,
257 rd,
258 rs1,
259 rs2: rs2_opt,
260 }
261 }
262
263 Opcode::And => Operation::And { rd, rs1, rs2 },
264 Opcode::Or => Operation::Or { rd, rs1, rs2 },
265 Opcode::Xor => Operation::Xor { rd, rs1, rs2 },
266 Opcode::Not => Operation::Not { rd, rs1 },
267 Opcode::Shl => Operation::Shl { rd, rs1, rs2 },
268 Opcode::Shr => Operation::Shr { rd, rs1, rs2 },
269 Opcode::Sar => Operation::Sar { rd, rs1, rs2 },
270 Opcode::BitOps => {
271 let op = BitOpType::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
272 opcode,
273 modifier,
274 offset,
275 })?;
276 match op {
277 BitOpType::Bitcount | BitOpType::Bitfind | BitOpType::Bitrev => {
278 Operation::BitOp {
279 op,
280 rd,
281 rs1,
282 rs2: None,
283 rs3: None,
284 rs4: None,
285 }
286 }
287 BitOpType::Bfe => {
288 let word1 = self.read_u32();
289 let rs3 = ((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8;
290 Operation::BitOp {
291 op,
292 rd,
293 rs1,
294 rs2: Some(rs2),
295 rs3: Some(rs3),
296 rs4: None,
297 }
298 }
299 BitOpType::Bfi => {
300 let word1 = self.read_u32();
301 let rs3 = ((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8;
302 let rs4 = ((word1 >> EXTENDED_RS4_SHIFT) & EXTENDED_RS4_MASK) as u8;
303 Operation::BitOp {
304 op,
305 rd,
306 rs1,
307 rs2: Some(rs2),
308 rs3: Some(rs3),
309 rs4: Some(rs4),
310 }
311 }
312 }
313 }
314
315 Opcode::Icmp => {
316 let op = CmpOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
317 opcode,
318 modifier,
319 offset,
320 })?;
321 Operation::Icmp {
322 op,
323 pd: rd,
324 rs1,
325 rs2,
326 }
327 }
328 Opcode::Ucmp => {
329 let op = CmpOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
330 opcode,
331 modifier,
332 offset,
333 })?;
334 Operation::Ucmp {
335 op,
336 pd: rd,
337 rs1,
338 rs2,
339 }
340 }
341 Opcode::Fcmp => {
342 let op = CmpOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
343 opcode,
344 modifier,
345 offset,
346 })?;
347 Operation::Fcmp {
348 op,
349 pd: rd,
350 rs1,
351 rs2,
352 }
353 }
354
355 Opcode::Select => Operation::Select {
356 rd,
357 ps: rs1,
358 rs1: rs2,
359 rs2: modifier, },
361 Opcode::Cvt => {
362 let cvt_type = CvtType::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
363 opcode,
364 modifier,
365 offset,
366 })?;
367 Operation::Cvt { cvt_type, rd, rs1 }
368 }
369
370 Opcode::LocalLoad => {
371 let width = MemWidth::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
372 opcode,
373 modifier,
374 offset,
375 })?;
376 Operation::LocalLoad {
377 width,
378 rd,
379 addr: rs1,
380 }
381 }
382 Opcode::LocalStore => {
383 let width = MemWidth::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
384 opcode,
385 modifier,
386 offset,
387 })?;
388 Operation::LocalStore {
389 width,
390 addr: rs1,
391 value: rs2,
392 }
393 }
394
395 Opcode::DeviceLoad => {
396 let width = MemWidth::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
397 opcode,
398 modifier,
399 offset,
400 })?;
401 Operation::DeviceLoad {
402 width,
403 rd,
404 addr: rs1,
405 }
406 }
407 Opcode::DeviceStore => {
408 let width = MemWidth::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
409 opcode,
410 modifier,
411 offset,
412 })?;
413 Operation::DeviceStore {
414 width,
415 addr: rs1,
416 value: rs2,
417 }
418 }
419
420 Opcode::LocalAtomic => {
421 if modifier == 8 && self.peek_u32().is_some() {
422 let word1 = self.read_u32();
423 let rs3 = ((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8;
424 let rd_opt = if rd != 0 { Some(rd) } else { None };
425 Operation::LocalAtomicCas {
426 rd: rd_opt,
427 addr: rs1,
428 expected: rs2,
429 desired: rs3,
430 }
431 } else {
432 let op = AtomicOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
433 opcode,
434 modifier,
435 offset,
436 })?;
437 let rd_opt = if rd != 0 { Some(rd) } else { None };
438 Operation::LocalAtomic {
439 op,
440 rd: rd_opt,
441 addr: rs1,
442 value: rs2,
443 }
444 }
445 }
446
447 Opcode::DeviceAtomic => {
448 let scope_val = Scope::from_u8(scope).ok_or(DecodeError::InvalidModifier {
449 opcode,
450 modifier: scope,
451 offset,
452 })?;
453
454 if modifier == 8 && self.peek_u32().is_some() {
455 let word1 = self.read_u32();
456 let rs3 = ((word1 >> EXTENDED_RS3_SHIFT) & EXTENDED_RS3_MASK) as u8;
457 let rd_opt = if rd != 0 { Some(rd) } else { None };
458 Operation::DeviceAtomicCas {
459 rd: rd_opt,
460 addr: rs1,
461 expected: rs2,
462 desired: rs3,
463 scope: scope_val,
464 }
465 } else {
466 let op = AtomicOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
467 opcode,
468 modifier,
469 offset,
470 })?;
471 let rd_opt = if rd != 0 { Some(rd) } else { None };
472 Operation::DeviceAtomic {
473 op,
474 rd: rd_opt,
475 addr: rs1,
476 value: rs2,
477 scope: scope_val,
478 }
479 }
480 }
481
482 Opcode::WaveOp => {
483 if modifier >= 8 {
484 let op = WaveReduceType::from_u8(modifier - 8).ok_or(
485 DecodeError::InvalidModifier {
486 opcode,
487 modifier,
488 offset,
489 },
490 )?;
491 Operation::WaveReduce { op, rd, rs1 }
492 } else {
493 let op = WaveOpType::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
494 opcode,
495 modifier,
496 offset,
497 })?;
498 match op {
499 WaveOpType::Ballot => Operation::WaveBallot { rd, ps: rs1 },
500 WaveOpType::Any | WaveOpType::All => Operation::WaveVote {
501 op,
502 pd: rd,
503 ps: rs1,
504 },
505 _ => Operation::WaveOp {
506 op,
507 rd,
508 rs1,
509 rs2: Some(rs2),
510 },
511 }
512 }
513 }
514
515 Opcode::Control => self.decode_control(rd, rs1, rs2, modifier, scope, flags, offset)?,
516 };
517
518 Ok(op)
519 }
520
521 #[allow(clippy::too_many_arguments)]
522 fn decode_control(
523 &mut self,
524 rd: u8,
525 rs1: u8,
526 _rs2: u8,
527 modifier: u8,
528 scope: u8,
529 flags: u8,
530 offset: u32,
531 ) -> Result<Operation, DecodeError> {
532 if (flags & SYNC_OP_FLAG) != 0 {
533 let op = SyncOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
534 opcode: Opcode::Control,
535 modifier,
536 offset,
537 })?;
538 match op {
539 SyncOp::Return => Ok(Operation::Return),
540 SyncOp::Halt => Ok(Operation::Halt),
541 SyncOp::Barrier => Ok(Operation::Barrier),
542 SyncOp::FenceAcquire => {
543 let scope_val = Scope::from_u8(scope).unwrap_or(Scope::Workgroup);
544 Ok(Operation::FenceAcquire { scope: scope_val })
545 }
546 SyncOp::FenceRelease => {
547 let scope_val = Scope::from_u8(scope).unwrap_or(Scope::Workgroup);
548 Ok(Operation::FenceRelease { scope: scope_val })
549 }
550 SyncOp::FenceAcqRel => {
551 let scope_val = Scope::from_u8(scope).unwrap_or(Scope::Workgroup);
552 Ok(Operation::FenceAcqRel { scope: scope_val })
553 }
554 SyncOp::Wait => Ok(Operation::Wait),
555 SyncOp::Nop => Ok(Operation::Nop),
556 }
557 } else if (flags & MISC_OP_FLAG) != 0 {
558 let op = MiscOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
559 opcode: Opcode::Control,
560 modifier,
561 offset,
562 })?;
563 match op {
564 MiscOp::Mov => Ok(Operation::Mov { rd, rs1 }),
565 MiscOp::MovImm => {
566 let imm = self.read_u32();
567 Ok(Operation::MovImm { rd, imm })
568 }
569 MiscOp::MovSr => Ok(Operation::MovSr { rd, sr_index: rs1 }),
570 }
571 } else {
572 let op = ControlOp::from_u8(modifier).ok_or(DecodeError::InvalidModifier {
573 opcode: Opcode::Control,
574 modifier,
575 offset,
576 })?;
577 match op {
578 ControlOp::If => Ok(Operation::If { ps: rs1 }),
579 ControlOp::Else => Ok(Operation::Else),
580 ControlOp::Endif => Ok(Operation::Endif),
581 ControlOp::Loop => Ok(Operation::Loop),
582 ControlOp::Break => Ok(Operation::Break { ps: rs1 }),
583 ControlOp::Continue => Ok(Operation::Continue { ps: rs1 }),
584 ControlOp::Endloop => Ok(Operation::Endloop),
585 ControlOp::Call => {
586 let target = self.read_u32();
587 Ok(Operation::Call { target })
588 }
589 }
590 }
591 }
592}
593
594pub fn decode_at(code: &[u8], offset: u32) -> Result<DecodedInstruction, DecodeError> {
600 let mut decoder = Decoder::new(code);
601 decoder.offset = offset;
602 decoder.decode_next()
603}
604
605pub fn decode_all(code: &[u8]) -> Result<Vec<DecodedInstruction>, DecodeError> {
611 let mut decoder = Decoder::new(code);
612 let mut instructions = Vec::new();
613
614 while decoder.has_more() {
615 instructions.push(decoder.decode_next()?);
616 }
617
618 Ok(instructions)
619}
620
621#[cfg(test)]
622mod tests {
623 use super::*;
624
625 fn encode_base(opcode: u8, rd: u8, rs1: u8, rs2: u8, modifier: u8, flags: u8) -> [u8; 4] {
626 let word = ((u32::from(opcode) & OPCODE_MASK) << OPCODE_SHIFT)
627 | ((u32::from(rd) & RD_MASK) << RD_SHIFT)
628 | ((u32::from(rs1) & RS1_MASK) << RS1_SHIFT)
629 | ((u32::from(rs2) & RS2_MASK) << RS2_SHIFT)
630 | ((u32::from(modifier) & MODIFIER_MASK) << MODIFIER_SHIFT)
631 | u32::from(flags);
632 word.to_le_bytes()
633 }
634
635 #[test]
636 fn test_decode_iadd() {
637 let code = encode_base(0x00, 5, 3, 4, 0, 0);
638 let instr = decode_at(&code, 0).unwrap();
639 assert_eq!(instr.size, 4);
640 assert_eq!(
641 instr.operation,
642 Operation::Iadd {
643 rd: 5,
644 rs1: 3,
645 rs2: 4
646 }
647 );
648 }
649
650 #[test]
651 fn test_decode_halt() {
652 let code = encode_base(0x3F, 0, 0, 0, SyncOp::Halt as u8, SYNC_OP_FLAG);
653 let instr = decode_at(&code, 0).unwrap();
654 assert_eq!(instr.operation, Operation::Halt);
655 }
656
657 #[test]
658 fn test_decode_mov_imm() {
659 let mut code = encode_base(0x3F, 5, 0, 0, MiscOp::MovImm as u8, MISC_OP_FLAG).to_vec();
660 code.extend_from_slice(&0x12345678u32.to_le_bytes());
661
662 let instr = decode_at(&code, 0).unwrap();
663 assert_eq!(instr.size, 8);
664 assert_eq!(
665 instr.operation,
666 Operation::MovImm {
667 rd: 5,
668 imm: 0x12345678
669 }
670 );
671 }
672
673 #[test]
674 fn test_decode_mov_sr() {
675 let code = encode_base(0x3F, 5, 4, 0, MiscOp::MovSr as u8, MISC_OP_FLAG);
676 let instr = decode_at(&code, 0).unwrap();
677 assert_eq!(instr.operation, Operation::MovSr { rd: 5, sr_index: 4 });
678 }
679
680 #[test]
681 fn test_decode_device_store_u32() {
682 let code = encode_base(0x39, 0, 3, 5, MemWidth::U32 as u8, 0);
683 let instr = decode_at(&code, 0).unwrap();
684 assert_eq!(
685 instr.operation,
686 Operation::DeviceStore {
687 width: MemWidth::U32,
688 addr: 3,
689 value: 5
690 }
691 );
692 }
693
694 #[test]
695 fn test_decode_all() {
696 let mut code = encode_base(0x00, 5, 3, 4, 0, 0).to_vec(); code.extend_from_slice(&encode_base(
698 0x3F,
699 0,
700 0,
701 0,
702 SyncOp::Halt as u8,
703 SYNC_OP_FLAG,
704 )); let instructions = decode_all(&code).unwrap();
707 assert_eq!(instructions.len(), 2);
708 assert_eq!(instructions[0].offset, 0);
709 assert_eq!(instructions[1].offset, 4);
710 }
711
712 #[test]
713 fn test_decode_shl() {
714 let code = encode_base(0x24, 2, 0, 1, 0, 0);
715 let instr = decode_at(&code, 0).unwrap();
716 assert_eq!(
717 instr.operation,
718 Operation::Shl {
719 rd: 2,
720 rs1: 0,
721 rs2: 1
722 }
723 );
724 }
725
726 #[test]
727 fn test_decode_predicated() {
728 let word = ((0x00u32) << OPCODE_SHIFT)
729 | ((5u32) << RD_SHIFT)
730 | ((3u32) << RS1_SHIFT)
731 | ((4u32) << RS2_SHIFT)
732 | ((1u32) << PRED_SHIFT); let code = word.to_le_bytes();
735 let instr = decode_at(&code, 0).unwrap();
736 assert_eq!(instr.predicate, 1);
737 assert!(!instr.predicate_negated);
738 }
739}