1use wave_decode::opcodes::{
11 CmpOp, ControlOp, FUnaryOp, MemWidth as DecodeMemWidth, MiscOp, Opcode, SyncOp, MODIFIER_MASK,
12 MODIFIER_SHIFT, OPCODE_SHIFT, RD_SHIFT, RS1_SHIFT, RS2_SHIFT,
13};
14
15use crate::lir::instruction::LirInst;
16use crate::lir::operand::{MemWidth, PhysReg, VReg};
17
18const SYNC_OP_FLAG: u32 = 0x01;
19const MISC_OP_FLAG: u32 = 0x02;
20
21#[derive(Debug, Clone)]
23pub struct EncodedInst {
24 pub word0: u32,
26 pub word1: Option<u32>,
28}
29
30impl EncodedInst {
31 #[must_use]
33 pub fn size(&self) -> usize {
34 if self.word1.is_some() {
35 8
36 } else {
37 4
38 }
39 }
40
41 #[must_use]
43 pub fn to_bytes(&self) -> Vec<u8> {
44 let mut bytes = self.word0.to_le_bytes().to_vec();
45 if let Some(w1) = self.word1 {
46 bytes.extend_from_slice(&w1.to_le_bytes());
47 }
48 bytes
49 }
50}
51
52pub type RegMap = std::collections::HashMap<VReg, PhysReg>;
54
55#[must_use]
60pub fn emit_instruction(inst: &LirInst, reg_map: &RegMap) -> EncodedInst {
61 match inst {
62 LirInst::Iadd { .. }
63 | LirInst::Isub { .. }
64 | LirInst::Imul { .. }
65 | LirInst::Idiv { .. }
66 | LirInst::Imod { .. }
67 | LirInst::Ineg { .. }
68 | LirInst::Fadd { .. }
69 | LirInst::Fsub { .. }
70 | LirInst::Fmul { .. }
71 | LirInst::Fdiv { .. }
72 | LirInst::Fma { .. }
73 | LirInst::Fneg { .. }
74 | LirInst::Fabs { .. }
75 | LirInst::Fsqrt { .. }
76 | LirInst::Fsin { .. }
77 | LirInst::Fcos { .. }
78 | LirInst::Fexp2 { .. }
79 | LirInst::Flog2 { .. }
80 | LirInst::Fmin { .. }
81 | LirInst::Fmax { .. }
82 | LirInst::And { .. }
83 | LirInst::Or { .. }
84 | LirInst::Xor { .. }
85 | LirInst::Not { .. }
86 | LirInst::Shl { .. }
87 | LirInst::Shr { .. }
88 | LirInst::Sar { .. }
89 | LirInst::CvtF32I32 { .. }
90 | LirInst::CvtI32F32 { .. } => emit_alu(inst, reg_map),
91 LirInst::MovImm { .. } | LirInst::MovReg { .. } | LirInst::MovSr { .. } => {
92 emit_mov(inst, reg_map)
93 }
94 LirInst::LocalLoad { .. }
95 | LirInst::LocalStore { .. }
96 | LirInst::DeviceLoad { .. }
97 | LirInst::DeviceStore { .. } => emit_memory(inst, reg_map),
98 LirInst::IcmpEq { .. }
99 | LirInst::IcmpNe { .. }
100 | LirInst::IcmpLt { .. }
101 | LirInst::IcmpLe { .. }
102 | LirInst::IcmpGt { .. }
103 | LirInst::IcmpGe { .. }
104 | LirInst::UcmpLt { .. }
105 | LirInst::FcmpEq { .. }
106 | LirInst::FcmpLt { .. }
107 | LirInst::FcmpGt { .. } => emit_compare(inst, reg_map),
108 LirInst::If { .. }
109 | LirInst::Else
110 | LirInst::Endif
111 | LirInst::Loop
112 | LirInst::Break { .. }
113 | LirInst::Continue { .. }
114 | LirInst::Endloop
115 | LirInst::Barrier
116 | LirInst::Halt => emit_control(inst, reg_map),
117 }
118}
119
120fn emit_alu(inst: &LirInst, reg_map: &RegMap) -> EncodedInst {
121 match inst {
122 LirInst::Iadd { .. }
123 | LirInst::Isub { .. }
124 | LirInst::Imul { .. }
125 | LirInst::Idiv { .. }
126 | LirInst::Imod { .. }
127 | LirInst::Ineg { .. } => emit_int_alu(inst, reg_map),
128 LirInst::Fadd { .. }
129 | LirInst::Fsub { .. }
130 | LirInst::Fmul { .. }
131 | LirInst::Fdiv { .. }
132 | LirInst::Fma { .. }
133 | LirInst::Fneg { .. }
134 | LirInst::Fabs { .. }
135 | LirInst::Fsqrt { .. }
136 | LirInst::Fsin { .. }
137 | LirInst::Fcos { .. }
138 | LirInst::Fexp2 { .. }
139 | LirInst::Flog2 { .. }
140 | LirInst::Fmin { .. }
141 | LirInst::Fmax { .. } => emit_float_alu(inst, reg_map),
142 LirInst::And { .. }
143 | LirInst::Or { .. }
144 | LirInst::Xor { .. }
145 | LirInst::Not { .. }
146 | LirInst::Shl { .. }
147 | LirInst::Shr { .. }
148 | LirInst::Sar { .. } => emit_bitwise_alu(inst, reg_map),
149 LirInst::CvtF32I32 { dest, src } => {
150 let word0 =
151 encode_base_word(Opcode::Cvt, reg(*dest, reg_map), reg(*src, reg_map), 0, 0);
152 EncodedInst { word0, word1: None }
153 }
154 LirInst::CvtI32F32 { dest, src } => {
155 let word0 =
156 encode_base_word(Opcode::Cvt, reg(*dest, reg_map), reg(*src, reg_map), 0, 2);
157 EncodedInst { word0, word1: None }
158 }
159 _ => unreachable!(),
160 }
161}
162
163fn emit_int_alu(inst: &LirInst, reg_map: &RegMap) -> EncodedInst {
164 match inst {
165 LirInst::Iadd { dest, src1, src2 } => encode_alu3(
166 Opcode::Iadd,
167 reg(*dest, reg_map),
168 reg(*src1, reg_map),
169 reg(*src2, reg_map),
170 ),
171 LirInst::Isub { dest, src1, src2 } => encode_alu3(
172 Opcode::Isub,
173 reg(*dest, reg_map),
174 reg(*src1, reg_map),
175 reg(*src2, reg_map),
176 ),
177 LirInst::Imul { dest, src1, src2 } => encode_alu3(
178 Opcode::Imul,
179 reg(*dest, reg_map),
180 reg(*src1, reg_map),
181 reg(*src2, reg_map),
182 ),
183 LirInst::Idiv { dest, src1, src2 } => encode_alu3(
184 Opcode::Idiv,
185 reg(*dest, reg_map),
186 reg(*src1, reg_map),
187 reg(*src2, reg_map),
188 ),
189 LirInst::Imod { dest, src1, src2 } => encode_alu3(
190 Opcode::Imod,
191 reg(*dest, reg_map),
192 reg(*src1, reg_map),
193 reg(*src2, reg_map),
194 ),
195 LirInst::Ineg { dest, src } => {
196 encode_alu2(Opcode::Ineg, reg(*dest, reg_map), reg(*src, reg_map))
197 }
198 _ => unreachable!(),
199 }
200}
201
202fn emit_float_alu(inst: &LirInst, reg_map: &RegMap) -> EncodedInst {
203 match inst {
204 LirInst::Fadd { dest, src1, src2 } => encode_alu3(
205 Opcode::Fadd,
206 reg(*dest, reg_map),
207 reg(*src1, reg_map),
208 reg(*src2, reg_map),
209 ),
210 LirInst::Fsub { dest, src1, src2 } => encode_alu3(
211 Opcode::Fsub,
212 reg(*dest, reg_map),
213 reg(*src1, reg_map),
214 reg(*src2, reg_map),
215 ),
216 LirInst::Fmul { dest, src1, src2 } => encode_alu3(
217 Opcode::Fmul,
218 reg(*dest, reg_map),
219 reg(*src1, reg_map),
220 reg(*src2, reg_map),
221 ),
222 LirInst::Fdiv { dest, src1, src2 } => encode_alu3(
223 Opcode::Fdiv,
224 reg(*dest, reg_map),
225 reg(*src1, reg_map),
226 reg(*src2, reg_map),
227 ),
228 LirInst::Fma {
229 dest,
230 src1,
231 src2,
232 src3,
233 } => {
234 let word0 = encode_base_word(
235 Opcode::Fma,
236 reg(*dest, reg_map),
237 reg(*src1, reg_map),
238 reg(*src2, reg_map),
239 0,
240 );
241 let word1 = u32::from(reg(*src3, reg_map)) << 27;
242 EncodedInst {
243 word0,
244 word1: Some(word1),
245 }
246 }
247 LirInst::Fneg { dest, src } => {
248 encode_alu2(Opcode::Fneg, reg(*dest, reg_map), reg(*src, reg_map))
249 }
250 LirInst::Fabs { dest, src } => {
251 encode_alu2(Opcode::Fabs, reg(*dest, reg_map), reg(*src, reg_map))
252 }
253 LirInst::Fsqrt { dest, src } => {
254 encode_alu2(Opcode::Fsqrt, reg(*dest, reg_map), reg(*src, reg_map))
255 }
256 LirInst::Fsin { dest, src } => {
257 encode_funary(FUnaryOp::Fsin, reg(*dest, reg_map), reg(*src, reg_map))
258 }
259 LirInst::Fcos { dest, src } => {
260 encode_funary(FUnaryOp::Fcos, reg(*dest, reg_map), reg(*src, reg_map))
261 }
262 LirInst::Fexp2 { dest, src } => {
263 encode_funary(FUnaryOp::Fexp2, reg(*dest, reg_map), reg(*src, reg_map))
264 }
265 LirInst::Flog2 { dest, src } => {
266 encode_funary(FUnaryOp::Flog2, reg(*dest, reg_map), reg(*src, reg_map))
267 }
268 LirInst::Fmin { dest, src1, src2 } => encode_alu3(
269 Opcode::Fmin,
270 reg(*dest, reg_map),
271 reg(*src1, reg_map),
272 reg(*src2, reg_map),
273 ),
274 LirInst::Fmax { dest, src1, src2 } => encode_alu3(
275 Opcode::Fmax,
276 reg(*dest, reg_map),
277 reg(*src1, reg_map),
278 reg(*src2, reg_map),
279 ),
280 _ => unreachable!(),
281 }
282}
283
284fn emit_bitwise_alu(inst: &LirInst, reg_map: &RegMap) -> EncodedInst {
285 match inst {
286 LirInst::And { dest, src1, src2 } => encode_alu3(
287 Opcode::And,
288 reg(*dest, reg_map),
289 reg(*src1, reg_map),
290 reg(*src2, reg_map),
291 ),
292 LirInst::Or { dest, src1, src2 } => encode_alu3(
293 Opcode::Or,
294 reg(*dest, reg_map),
295 reg(*src1, reg_map),
296 reg(*src2, reg_map),
297 ),
298 LirInst::Xor { dest, src1, src2 } => encode_alu3(
299 Opcode::Xor,
300 reg(*dest, reg_map),
301 reg(*src1, reg_map),
302 reg(*src2, reg_map),
303 ),
304 LirInst::Not { dest, src } => {
305 encode_alu2(Opcode::Not, reg(*dest, reg_map), reg(*src, reg_map))
306 }
307 LirInst::Shl { dest, src1, src2 } => encode_alu3(
308 Opcode::Shl,
309 reg(*dest, reg_map),
310 reg(*src1, reg_map),
311 reg(*src2, reg_map),
312 ),
313 LirInst::Shr { dest, src1, src2 } => encode_alu3(
314 Opcode::Shr,
315 reg(*dest, reg_map),
316 reg(*src1, reg_map),
317 reg(*src2, reg_map),
318 ),
319 LirInst::Sar { dest, src1, src2 } => encode_alu3(
320 Opcode::Sar,
321 reg(*dest, reg_map),
322 reg(*src1, reg_map),
323 reg(*src2, reg_map),
324 ),
325 _ => unreachable!(),
326 }
327}
328
329fn emit_mov(inst: &LirInst, reg_map: &RegMap) -> EncodedInst {
330 match inst {
331 LirInst::MovImm { dest, value } => {
332 let word0 =
333 encode_control_word(MiscOp::MovImm as u8, reg(*dest, reg_map), 0, 0) | MISC_OP_FLAG;
334 EncodedInst {
335 word0,
336 word1: Some(*value),
337 }
338 }
339 LirInst::MovReg { dest, src } => {
340 let word0 = encode_control_word(
341 MiscOp::Mov as u8,
342 reg(*dest, reg_map),
343 reg(*src, reg_map),
344 0,
345 ) | MISC_OP_FLAG;
346 EncodedInst { word0, word1: None }
347 }
348 LirInst::MovSr { dest, sr } => {
349 let word0 =
350 encode_control_word(MiscOp::MovSr as u8, reg(*dest, reg_map), sr.index(), 0)
351 | MISC_OP_FLAG;
352 EncodedInst { word0, word1: None }
353 }
354 _ => unreachable!(),
355 }
356}
357
358fn emit_memory(inst: &LirInst, reg_map: &RegMap) -> EncodedInst {
359 match inst {
360 LirInst::LocalLoad { dest, addr, width } => encode_mem(
361 Opcode::LocalLoad,
362 reg(*dest, reg_map),
363 reg(*addr, reg_map),
364 0,
365 lir_width_to_decode(*width),
366 ),
367 LirInst::LocalStore { addr, value, width } => encode_mem(
368 Opcode::LocalStore,
369 0,
370 reg(*addr, reg_map),
371 reg(*value, reg_map),
372 lir_width_to_decode(*width),
373 ),
374 LirInst::DeviceLoad { dest, addr, width } => encode_mem(
375 Opcode::DeviceLoad,
376 reg(*dest, reg_map),
377 reg(*addr, reg_map),
378 0,
379 lir_width_to_decode(*width),
380 ),
381 LirInst::DeviceStore { addr, value, width } => encode_mem(
382 Opcode::DeviceStore,
383 0,
384 reg(*addr, reg_map),
385 reg(*value, reg_map),
386 lir_width_to_decode(*width),
387 ),
388 _ => unreachable!(),
389 }
390}
391
392fn emit_compare(inst: &LirInst, reg_map: &RegMap) -> EncodedInst {
393 match inst {
394 LirInst::IcmpEq { dest, src1, src2 } => encode_cmp(
395 Opcode::Icmp,
396 CmpOp::Eq,
397 dest.0,
398 reg(*src1, reg_map),
399 reg(*src2, reg_map),
400 ),
401 LirInst::IcmpNe { dest, src1, src2 } => encode_cmp(
402 Opcode::Icmp,
403 CmpOp::Ne,
404 dest.0,
405 reg(*src1, reg_map),
406 reg(*src2, reg_map),
407 ),
408 LirInst::IcmpLt { dest, src1, src2 } => encode_cmp(
409 Opcode::Icmp,
410 CmpOp::Lt,
411 dest.0,
412 reg(*src1, reg_map),
413 reg(*src2, reg_map),
414 ),
415 LirInst::IcmpLe { dest, src1, src2 } => encode_cmp(
416 Opcode::Icmp,
417 CmpOp::Le,
418 dest.0,
419 reg(*src1, reg_map),
420 reg(*src2, reg_map),
421 ),
422 LirInst::IcmpGt { dest, src1, src2 } => encode_cmp(
423 Opcode::Icmp,
424 CmpOp::Gt,
425 dest.0,
426 reg(*src1, reg_map),
427 reg(*src2, reg_map),
428 ),
429 LirInst::IcmpGe { dest, src1, src2 } => encode_cmp(
430 Opcode::Icmp,
431 CmpOp::Ge,
432 dest.0,
433 reg(*src1, reg_map),
434 reg(*src2, reg_map),
435 ),
436 LirInst::UcmpLt { dest, src1, src2 } => encode_cmp(
437 Opcode::Ucmp,
438 CmpOp::Lt,
439 dest.0,
440 reg(*src1, reg_map),
441 reg(*src2, reg_map),
442 ),
443 LirInst::FcmpEq { dest, src1, src2 } => encode_cmp(
444 Opcode::Fcmp,
445 CmpOp::Eq,
446 dest.0,
447 reg(*src1, reg_map),
448 reg(*src2, reg_map),
449 ),
450 LirInst::FcmpLt { dest, src1, src2 } => encode_cmp(
451 Opcode::Fcmp,
452 CmpOp::Lt,
453 dest.0,
454 reg(*src1, reg_map),
455 reg(*src2, reg_map),
456 ),
457 LirInst::FcmpGt { dest, src1, src2 } => encode_cmp(
458 Opcode::Fcmp,
459 CmpOp::Gt,
460 dest.0,
461 reg(*src1, reg_map),
462 reg(*src2, reg_map),
463 ),
464 _ => unreachable!(),
465 }
466}
467
468fn emit_control(inst: &LirInst, _reg_map: &RegMap) -> EncodedInst {
469 match inst {
470 LirInst::If { pred } => {
471 let word0 = encode_control_word(ControlOp::If as u8, 0, pred.0, 0);
472 EncodedInst { word0, word1: None }
473 }
474 LirInst::Else => {
475 let word0 = encode_control_word(ControlOp::Else as u8, 0, 0, 0);
476 EncodedInst { word0, word1: None }
477 }
478 LirInst::Endif => {
479 let word0 = encode_control_word(ControlOp::Endif as u8, 0, 0, 0);
480 EncodedInst { word0, word1: None }
481 }
482 LirInst::Loop => {
483 let word0 = encode_control_word(ControlOp::Loop as u8, 0, 0, 0);
484 EncodedInst { word0, word1: None }
485 }
486 LirInst::Break { pred } => {
487 let word0 = encode_control_word(ControlOp::Break as u8, 0, pred.0, 0);
488 EncodedInst { word0, word1: None }
489 }
490 LirInst::Continue { pred } => {
491 let word0 = encode_control_word(ControlOp::Continue as u8, 0, pred.0, 0);
492 EncodedInst { word0, word1: None }
493 }
494 LirInst::Endloop => {
495 let word0 = encode_control_word(ControlOp::Endloop as u8, 0, 0, 0);
496 EncodedInst { word0, word1: None }
497 }
498 LirInst::Barrier => {
499 let word0 = encode_control_word(SyncOp::Barrier as u8, 0, 0, 0) | SYNC_OP_FLAG;
500 EncodedInst { word0, word1: None }
501 }
502 LirInst::Halt => {
503 let word0 = encode_control_word(SyncOp::Halt as u8, 0, 0, 0) | SYNC_OP_FLAG;
504 EncodedInst { word0, word1: None }
505 }
506 _ => unreachable!(),
507 }
508}
509
510fn reg(vreg: VReg, map: &RegMap) -> u8 {
511 map.get(&vreg)
512 .map_or(u8::try_from(vreg.0).expect("vreg index exceeds u8"), |p| {
513 p.0
514 })
515}
516
517fn encode_base_word(opcode: Opcode, rd: u8, rs1: u8, rs2: u8, modifier: u8) -> u32 {
518 (u32::from(opcode as u8) << OPCODE_SHIFT)
519 | (u32::from(rd & 0x1F) << RD_SHIFT)
520 | (u32::from(rs1 & 0x1F) << RS1_SHIFT)
521 | (u32::from(rs2 & 0x1F) << RS2_SHIFT)
522 | ((u32::from(modifier) & MODIFIER_MASK) << MODIFIER_SHIFT)
523}
524
525fn encode_alu3(opcode: Opcode, rd: u8, rs1: u8, rs2: u8) -> EncodedInst {
526 EncodedInst {
527 word0: encode_base_word(opcode, rd, rs1, rs2, 0),
528 word1: None,
529 }
530}
531
532fn encode_alu2(opcode: Opcode, rd: u8, rs1: u8) -> EncodedInst {
533 EncodedInst {
534 word0: encode_base_word(opcode, rd, rs1, 0, 0),
535 word1: None,
536 }
537}
538
539fn encode_funary(op: FUnaryOp, rd: u8, rs1: u8) -> EncodedInst {
540 EncodedInst {
541 word0: encode_base_word(Opcode::FUnaryOps, rd, rs1, 0, op as u8),
542 word1: None,
543 }
544}
545
546fn encode_mem(opcode: Opcode, rd: u8, rs1: u8, rs2: u8, width: DecodeMemWidth) -> EncodedInst {
547 EncodedInst {
548 word0: encode_base_word(opcode, rd, rs1, rs2, width as u8),
549 word1: None,
550 }
551}
552
553fn encode_cmp(opcode: Opcode, cmp_op: CmpOp, pd: u8, rs1: u8, rs2: u8) -> EncodedInst {
554 EncodedInst {
555 word0: encode_base_word(opcode, pd, rs1, rs2, cmp_op as u8),
556 word1: None,
557 }
558}
559
560fn encode_control_word(modifier: u8, rd: u8, rs1: u8, rs2: u8) -> u32 {
561 encode_base_word(Opcode::Control, rd, rs1, rs2, modifier)
562}
563
564fn lir_width_to_decode(w: MemWidth) -> DecodeMemWidth {
565 match w {
566 MemWidth::W8 => DecodeMemWidth::U8,
567 MemWidth::W16 => DecodeMemWidth::U16,
568 MemWidth::W32 => DecodeMemWidth::U32,
569 MemWidth::W64 => DecodeMemWidth::U64,
570 }
571}
572
573#[cfg(test)]
574mod tests {
575 use super::*;
576 use std::collections::HashMap;
577
578 fn empty_map() -> RegMap {
579 HashMap::new()
580 }
581
582 #[test]
583 fn test_emit_iadd() {
584 let inst = LirInst::Iadd {
585 dest: VReg(5),
586 src1: VReg(3),
587 src2: VReg(4),
588 };
589 let encoded = emit_instruction(&inst, &empty_map());
590 assert!(encoded.word1.is_none());
591 let opcode = (encoded.word0 >> OPCODE_SHIFT) & 0x3F;
592 assert_eq!(opcode, Opcode::Iadd as u32);
593 let rd = (encoded.word0 >> RD_SHIFT) & 0x1F;
594 assert_eq!(rd, 5);
595 }
596
597 #[test]
598 fn test_emit_device_store_encoding() {
599 let inst = LirInst::DeviceStore {
600 addr: VReg(3),
601 value: VReg(5),
602 width: MemWidth::W32,
603 };
604 let encoded = emit_instruction(&inst, &empty_map());
605 let rs1 = (encoded.word0 >> RS1_SHIFT) & 0x1F;
606 let rs2 = (encoded.word0 >> RS2_SHIFT) & 0x1F;
607 assert_eq!(rs1, 3);
608 assert_eq!(rs2, 5);
609 }
610
611 #[test]
612 fn test_emit_mov_imm() {
613 let inst = LirInst::MovImm {
614 dest: VReg(5),
615 value: 0x1234_5678,
616 };
617 let encoded = emit_instruction(&inst, &empty_map());
618 assert!(encoded.word1.is_some());
619 assert_eq!(encoded.word1.unwrap(), 0x1234_5678);
620 }
621
622 #[test]
623 fn test_emit_halt() {
624 let inst = LirInst::Halt;
625 let encoded = emit_instruction(&inst, &empty_map());
626 let opcode = (encoded.word0 >> OPCODE_SHIFT) & 0x3F;
627 assert_eq!(opcode, Opcode::Control as u32);
628 assert_ne!(encoded.word0 & SYNC_OP_FLAG, 0);
629 }
630
631 #[test]
632 fn test_emit_if_endif() {
633 let if_inst = LirInst::If {
634 pred: crate::lir::operand::PReg(1),
635 };
636 let encoded_if = emit_instruction(&if_inst, &empty_map());
637 let modifier = (encoded_if.word0 >> MODIFIER_SHIFT) & 0x0F;
638 assert_eq!(modifier, ControlOp::If as u32);
639
640 let endif_inst = LirInst::Endif;
641 let encoded_endif = emit_instruction(&endif_inst, &empty_map());
642 let modifier = (encoded_endif.word0 >> MODIFIER_SHIFT) & 0x0F;
643 assert_eq!(modifier, ControlOp::Endif as u32);
644 }
645
646 #[test]
647 fn test_emit_with_reg_map() {
648 let mut map = RegMap::new();
649 map.insert(VReg(0), PhysReg(10));
650 map.insert(VReg(1), PhysReg(11));
651 map.insert(VReg(2), PhysReg(12));
652
653 let inst = LirInst::Iadd {
654 dest: VReg(2),
655 src1: VReg(0),
656 src2: VReg(1),
657 };
658 let encoded = emit_instruction(&inst, &map);
659 let rd = (encoded.word0 >> RD_SHIFT) & 0x1F;
660 let rs1 = (encoded.word0 >> RS1_SHIFT) & 0x1F;
661 let rs2 = (encoded.word0 >> RS2_SHIFT) & 0x1F;
662 assert_eq!(rd, 12);
663 assert_eq!(rs1, 10);
664 assert_eq!(rs2, 11);
665 }
666}