ab_riscv_primitives/registers/general_purpose.rs
1//! RISC-V general purpose registers
2
3#[cfg(test)]
4mod tests;
5
6use crate::registers::general_purpose::private::PhantomRegister;
7use core::fmt;
8use core::hint::unreachable_unchecked;
9use core::marker::Destruct;
10use core::ops::{Add, AddAssign, Sub, SubAssign};
11
12mod private {
13 use core::marker::PhantomData;
14
15 #[derive(Debug, Clone, Copy)]
16 pub struct PhantomRegister<Type>(PhantomData<Type>);
17}
18
19/// General purpose register
20///
21/// # Safety
22/// `Self::offset()` must return values in `0..Self::N` range.
23pub const unsafe trait Register:
24 fmt::Display + fmt::Debug + [const] Eq + [const] Destruct + Copy + Sized
25{
26 /// The number of general purpose registers.
27 ///
28 /// Canonically 32 unless E extension is used, in which case 16.
29 const N: usize;
30 /// Register type.
31 ///
32 /// `u32` for RV32 and `u64` for RV64.
33 type Type: [const] Default
34 + [const] From<u8>
35 + [const] Into<u64>
36 + [const] Eq
37 + [const] Add
38 + [const] AddAssign
39 + [const] Sub
40 + [const] SubAssign
41 + fmt::Display
42 + fmt::Debug
43 + Copy
44 + Sized;
45
46 /// Whether the register is a zero register
47 fn is_zero(&self) -> bool;
48
49 /// Create a register from its bit representation
50 fn from_bits(bits: u8) -> Option<Self>;
51
52 /// Offset in a set of registers
53 fn offset(self) -> usize;
54}
55
56/// A set of RISC-V general purpose registers
57#[derive(Debug, Clone, Copy)]
58pub struct Registers<Reg>
59where
60 Reg: Register,
61 [(); Reg::N]:,
62{
63 regs: [Reg::Type; Reg::N],
64}
65
66impl<Reg> Default for Registers<Reg>
67where
68 Reg: Register,
69 [(); Reg::N]: Default,
70{
71 #[inline(always)]
72 fn default() -> Self {
73 Self {
74 regs: [Reg::Type::default(); Reg::N],
75 }
76 }
77}
78
79const impl<Reg> Registers<Reg>
80where
81 Reg: Register + [const] Eq,
82 [(); Reg::N]:,
83{
84 #[inline(always)]
85 pub fn read(&self, reg: Reg) -> Reg::Type
86 where
87 Reg: [const] Register,
88 {
89 if reg.is_zero() {
90 // Always zero
91 return Reg::Type::default();
92 }
93
94 // SAFETY: register offset is always within bounds
95 *unsafe { self.regs.get_unchecked(reg.offset()) }
96 }
97
98 #[inline(always)]
99 pub fn write(&mut self, reg: Reg, value: Reg::Type)
100 where
101 Reg: [const] Register,
102 {
103 if reg.is_zero() {
104 // Writes are ignored
105 return;
106 }
107
108 // SAFETY: register offset is always within bounds
109 *unsafe { self.regs.get_unchecked_mut(reg.offset()) } = value;
110 }
111}
112
113/// RISC-V general purpose register for RV32E/RV64E.
114///
115/// Use `Type = u32` for RV32E and `Type = u64` for RV64E.
116#[derive(Clone, Copy)]
117#[repr(u8)]
118pub enum EReg<Type> {
119 /// Always zero: `x0`
120 Zero = 0,
121 /// Return address: `x1`
122 Ra = 1,
123 /// Stack pointer: `x2`
124 Sp = 2,
125 /// Global pointer: `x3`
126 Gp = 3,
127 /// Thread pointer: `x4`
128 Tp = 4,
129 /// Temporary/alternate return address: `x5`
130 T0 = 5,
131 /// Temporary: `x6`
132 T1 = 6,
133 /// Temporary: `x7`
134 T2 = 7,
135 /// Saved register/frame pointer: `x8`
136 S0 = 8,
137 /// Saved register: `x9`
138 S1 = 9,
139 /// Function argument/return value: `x10`
140 A0 = 10,
141 /// Function argument/return value: `x11`
142 A1 = 11,
143 /// Function argument: `x12`
144 A2 = 12,
145 /// Function argument: `x13`
146 A3 = 13,
147 /// Function argument: `x14`
148 A4 = 14,
149 /// Function argument: `x15`
150 A5 = 15,
151 /// Phantom register that is never constructed and is only used due to type system limitations
152 #[doc(hidden)]
153 Phantom(PhantomRegister<Type>),
154}
155
156impl<Type> fmt::Display for EReg<Type> {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 Self::Zero => write!(f, "zero"),
160 Self::Ra => write!(f, "ra"),
161 Self::Sp => write!(f, "sp"),
162 Self::Gp => write!(f, "gp"),
163 Self::Tp => write!(f, "tp"),
164 Self::T0 => write!(f, "t0"),
165 Self::T1 => write!(f, "t1"),
166 Self::T2 => write!(f, "t2"),
167 Self::S0 => write!(f, "s0"),
168 Self::S1 => write!(f, "s1"),
169 Self::A0 => write!(f, "a0"),
170 Self::A1 => write!(f, "a1"),
171 Self::A2 => write!(f, "a2"),
172 Self::A3 => write!(f, "a3"),
173 Self::A4 => write!(f, "a4"),
174 Self::A5 => write!(f, "a5"),
175 Self::Phantom(_) => {
176 // SAFETY: Phantom register is never constructed
177 unsafe { unreachable_unchecked() }
178 }
179 }
180 }
181}
182
183impl<Type> fmt::Debug for EReg<Type> {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 fmt::Display::fmt(self, f)
186 }
187}
188
189impl<Type> const PartialEq for EReg<Type> {
190 #[inline(always)]
191 fn eq(&self, other: &Self) -> bool {
192 // This is quite ugly, but there doesn't seem to be a much better way with `Phantom` variant
193 matches!(
194 (self, other),
195 (Self::Zero, Self::Zero)
196 | (Self::Ra, Self::Ra)
197 | (Self::Sp, Self::Sp)
198 | (Self::Gp, Self::Gp)
199 | (Self::Tp, Self::Tp)
200 | (Self::T0, Self::T0)
201 | (Self::T1, Self::T1)
202 | (Self::T2, Self::T2)
203 | (Self::S0, Self::S0)
204 | (Self::S1, Self::S1)
205 | (Self::A0, Self::A0)
206 | (Self::A1, Self::A1)
207 | (Self::A2, Self::A2)
208 | (Self::A3, Self::A3)
209 | (Self::A4, Self::A4)
210 | (Self::A5, Self::A5)
211 | (Self::Phantom(_), Self::Phantom(_))
212 )
213 }
214}
215
216impl<Type> const Eq for EReg<Type> {}
217
218// SAFETY: `Self::offset()` returns values within `0..Self::N` range
219unsafe impl const Register for EReg<u32> {
220 const N: usize = 16;
221 type Type = u32;
222
223 #[inline(always)]
224 fn is_zero(&self) -> bool {
225 matches!(self, Self::Zero)
226 }
227
228 #[inline(always)]
229 fn from_bits(bits: u8) -> Option<Self> {
230 match bits {
231 0 => Some(Self::Zero),
232 1 => Some(Self::Ra),
233 2 => Some(Self::Sp),
234 3 => Some(Self::Gp),
235 4 => Some(Self::Tp),
236 5 => Some(Self::T0),
237 6 => Some(Self::T1),
238 7 => Some(Self::T2),
239 8 => Some(Self::S0),
240 9 => Some(Self::S1),
241 10 => Some(Self::A0),
242 11 => Some(Self::A1),
243 12 => Some(Self::A2),
244 13 => Some(Self::A3),
245 14 => Some(Self::A4),
246 15 => Some(Self::A5),
247 _ => None,
248 }
249 }
250
251 #[inline(always)]
252 fn offset(self) -> usize {
253 // NOTE: `transmute()` is requited here, otherwise performance suffers A LOT for unknown
254 // reason
255 // SAFETY: Enum is `#[repr(u8)]` and doesn't have any fields
256 usize::from(unsafe { core::mem::transmute::<Self, u8>(self) })
257 // match self {
258 // Self::Zero => 0,
259 // Self::Ra => 1,
260 // Self::Sp => 2,
261 // Self::Gp => 3,
262 // Self::Tp => 4,
263 // Self::T0 => 5,
264 // Self::T1 => 6,
265 // Self::T2 => 7,
266 // Self::S0 => 8,
267 // Self::S1 => 9,
268 // Self::A0 => 10,
269 // Self::A1 => 11,
270 // Self::A2 => 12,
271 // Self::A3 => 13,
272 // Self::A4 => 14,
273 // Self::A5 => 15,
274 // Self::Phantom(_) => {
275 // // SAFETY: Phantom register is never constructed
276 // unsafe { unreachable_unchecked() }
277 // },
278 // }
279 }
280}
281
282// SAFETY: `Self::offset()` returns values within `0..Self::N` range
283unsafe impl const Register for EReg<u64> {
284 const N: usize = 16;
285 type Type = u64;
286
287 #[inline(always)]
288 fn is_zero(&self) -> bool {
289 matches!(self, Self::Zero)
290 }
291
292 #[inline(always)]
293 fn from_bits(bits: u8) -> Option<Self> {
294 match bits {
295 0 => Some(Self::Zero),
296 1 => Some(Self::Ra),
297 2 => Some(Self::Sp),
298 3 => Some(Self::Gp),
299 4 => Some(Self::Tp),
300 5 => Some(Self::T0),
301 6 => Some(Self::T1),
302 7 => Some(Self::T2),
303 8 => Some(Self::S0),
304 9 => Some(Self::S1),
305 10 => Some(Self::A0),
306 11 => Some(Self::A1),
307 12 => Some(Self::A2),
308 13 => Some(Self::A3),
309 14 => Some(Self::A4),
310 15 => Some(Self::A5),
311 _ => None,
312 }
313 }
314
315 #[inline(always)]
316 fn offset(self) -> usize {
317 // NOTE: `transmute()` is requited here, otherwise performance suffers A LOT for unknown
318 // reason
319 // SAFETY: Enum is `#[repr(u8)]` and doesn't have any fields
320 usize::from(unsafe { core::mem::transmute::<Self, u8>(self) })
321 // match self {
322 // Self::Zero => 0,
323 // Self::Ra => 1,
324 // Self::Sp => 2,
325 // Self::Gp => 3,
326 // Self::Tp => 4,
327 // Self::T0 => 5,
328 // Self::T1 => 6,
329 // Self::T2 => 7,
330 // Self::S0 => 8,
331 // Self::S1 => 9,
332 // Self::A0 => 10,
333 // Self::A1 => 11,
334 // Self::A2 => 12,
335 // Self::A3 => 13,
336 // Self::A4 => 14,
337 // Self::A5 => 15,
338 // Self::Phantom(_) => {
339 // // SAFETY: Phantom register is never constructed
340 // unsafe { unreachable_unchecked() }
341 // },
342 // }
343 }
344}
345
346/// RISC-V general purpose register for RV32I/RV64I.
347///
348/// Use `Type = u32` for RV32I and `Type = u64` for RV64I.
349#[derive(Clone, Copy)]
350#[repr(u8)]
351pub enum Reg<Type> {
352 /// Always zero: `x0`
353 Zero = 0,
354 /// Return address: `x1`
355 Ra = 1,
356 /// Stack pointer: `x2`
357 Sp = 2,
358 /// Global pointer: `x3`
359 Gp = 3,
360 /// Thread pointer: `x4`
361 Tp = 4,
362 /// Temporary/alternate return address: `x5`
363 T0 = 5,
364 /// Temporary: `x6`
365 T1 = 6,
366 /// Temporary: `x7`
367 T2 = 7,
368 /// Saved register/frame pointer: `x8`
369 S0 = 8,
370 /// Saved register: `x9`
371 S1 = 9,
372 /// Function argument/return value: `x10`
373 A0 = 10,
374 /// Function argument/return value: `x11`
375 A1 = 11,
376 /// Function argument: `x12`
377 A2 = 12,
378 /// Function argument: `x13`
379 A3 = 13,
380 /// Function argument: `x14`
381 A4 = 14,
382 /// Function argument: `x15`
383 A5 = 15,
384 /// Function argument: `x16`
385 A6 = 16,
386 /// Function argument: `x17`
387 A7 = 17,
388 /// Saved register: `x18`
389 S2 = 18,
390 /// Saved register: `x19`
391 S3 = 19,
392 /// Saved register: `x20`
393 S4 = 20,
394 /// Saved register: `x21`
395 S5 = 21,
396 /// Saved register: `x22`
397 S6 = 22,
398 /// Saved register: `x23`
399 S7 = 23,
400 /// Saved register: `x24`
401 S8 = 24,
402 /// Saved register: `x25`
403 S9 = 25,
404 /// Saved register: `x26`
405 S10 = 26,
406 /// Saved register: `x27`
407 S11 = 27,
408 /// Temporary: `x28`
409 T3 = 28,
410 /// Temporary: `x29`
411 T4 = 29,
412 /// Temporary: `x30`
413 T5 = 30,
414 /// Temporary: `x31`
415 T6 = 31,
416 /// Phantom register that is never constructed and is only used due to type system limitations
417 #[doc(hidden)]
418 Phantom(PhantomRegister<Type>),
419}
420
421impl<Type> const From<EReg<u64>> for Reg<Type> {
422 #[inline(always)]
423 fn from(reg: EReg<u64>) -> Self {
424 match reg {
425 EReg::Zero => Self::Zero,
426 EReg::Ra => Self::Ra,
427 EReg::Sp => Self::Sp,
428 EReg::Gp => Self::Gp,
429 EReg::Tp => Self::Tp,
430 EReg::T0 => Self::T0,
431 EReg::T1 => Self::T1,
432 EReg::T2 => Self::T2,
433 EReg::S0 => Self::S0,
434 EReg::S1 => Self::S1,
435 EReg::A0 => Self::A0,
436 EReg::A1 => Self::A1,
437 EReg::A2 => Self::A2,
438 EReg::A3 => Self::A3,
439 EReg::A4 => Self::A4,
440 EReg::A5 => Self::A5,
441 EReg::Phantom(_) => {
442 // SAFETY: Phantom register is never constructed
443 unsafe { unreachable_unchecked() }
444 }
445 }
446 }
447}
448
449impl<Type> fmt::Display for Reg<Type> {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 match self {
452 Self::Zero => write!(f, "zero"),
453 Self::Ra => write!(f, "ra"),
454 Self::Sp => write!(f, "sp"),
455 Self::Gp => write!(f, "gp"),
456 Self::Tp => write!(f, "tp"),
457 Self::T0 => write!(f, "t0"),
458 Self::T1 => write!(f, "t1"),
459 Self::T2 => write!(f, "t2"),
460 Self::S0 => write!(f, "s0"),
461 Self::S1 => write!(f, "s1"),
462 Self::A0 => write!(f, "a0"),
463 Self::A1 => write!(f, "a1"),
464 Self::A2 => write!(f, "a2"),
465 Self::A3 => write!(f, "a3"),
466 Self::A4 => write!(f, "a4"),
467 Self::A5 => write!(f, "a5"),
468 Self::A6 => write!(f, "a6"),
469 Self::A7 => write!(f, "a7"),
470 Self::S2 => write!(f, "s2"),
471 Self::S3 => write!(f, "s3"),
472 Self::S4 => write!(f, "s4"),
473 Self::S5 => write!(f, "s5"),
474 Self::S6 => write!(f, "s6"),
475 Self::S7 => write!(f, "s7"),
476 Self::S8 => write!(f, "s8"),
477 Self::S9 => write!(f, "s9"),
478 Self::S10 => write!(f, "s10"),
479 Self::S11 => write!(f, "s11"),
480 Self::T3 => write!(f, "t3"),
481 Self::T4 => write!(f, "t4"),
482 Self::T5 => write!(f, "t5"),
483 Self::T6 => write!(f, "t6"),
484 Self::Phantom(_) => {
485 // SAFETY: Phantom register is never constructed
486 unsafe { unreachable_unchecked() }
487 }
488 }
489 }
490}
491
492impl<Type> fmt::Debug for Reg<Type> {
493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494 fmt::Display::fmt(self, f)
495 }
496}
497
498impl<Type> const PartialEq for Reg<Type> {
499 #[inline(always)]
500 fn eq(&self, other: &Self) -> bool {
501 // This is quite ugly, but there doesn't seem to be a much better way with `Phantom` variant
502 matches!(
503 (self, other),
504 (Self::Zero, Self::Zero)
505 | (Self::Ra, Self::Ra)
506 | (Self::Sp, Self::Sp)
507 | (Self::Gp, Self::Gp)
508 | (Self::Tp, Self::Tp)
509 | (Self::T0, Self::T0)
510 | (Self::T1, Self::T1)
511 | (Self::T2, Self::T2)
512 | (Self::S0, Self::S0)
513 | (Self::S1, Self::S1)
514 | (Self::A0, Self::A0)
515 | (Self::A1, Self::A1)
516 | (Self::A2, Self::A2)
517 | (Self::A3, Self::A3)
518 | (Self::A4, Self::A4)
519 | (Self::A5, Self::A5)
520 | (Self::A6, Self::A6)
521 | (Self::A7, Self::A7)
522 | (Self::S2, Self::S2)
523 | (Self::S3, Self::S3)
524 | (Self::S4, Self::S4)
525 | (Self::S5, Self::S5)
526 | (Self::S6, Self::S6)
527 | (Self::S7, Self::S7)
528 | (Self::S8, Self::S8)
529 | (Self::S9, Self::S9)
530 | (Self::S10, Self::S10)
531 | (Self::S11, Self::S11)
532 | (Self::T3, Self::T3)
533 | (Self::T4, Self::T4)
534 | (Self::T5, Self::T5)
535 | (Self::T6, Self::T6)
536 | (Self::Phantom(_), Self::Phantom(_))
537 )
538 }
539}
540
541impl<Type> const Eq for Reg<Type> {}
542
543// SAFETY: `Self::offset()` returns values within `0..Self::N` range
544unsafe impl const Register for Reg<u32> {
545 const N: usize = 32;
546 type Type = u32;
547
548 #[inline(always)]
549 fn is_zero(&self) -> bool {
550 matches!(self, Self::Zero)
551 }
552
553 #[inline(always)]
554 fn from_bits(bits: u8) -> Option<Self> {
555 match bits {
556 0 => Some(Self::Zero),
557 1 => Some(Self::Ra),
558 2 => Some(Self::Sp),
559 3 => Some(Self::Gp),
560 4 => Some(Self::Tp),
561 5 => Some(Self::T0),
562 6 => Some(Self::T1),
563 7 => Some(Self::T2),
564 8 => Some(Self::S0),
565 9 => Some(Self::S1),
566 10 => Some(Self::A0),
567 11 => Some(Self::A1),
568 12 => Some(Self::A2),
569 13 => Some(Self::A3),
570 14 => Some(Self::A4),
571 15 => Some(Self::A5),
572 16 => Some(Self::A6),
573 17 => Some(Self::A7),
574 18 => Some(Self::S2),
575 19 => Some(Self::S3),
576 20 => Some(Self::S4),
577 21 => Some(Self::S5),
578 22 => Some(Self::S6),
579 23 => Some(Self::S7),
580 24 => Some(Self::S8),
581 25 => Some(Self::S9),
582 26 => Some(Self::S10),
583 27 => Some(Self::S11),
584 28 => Some(Self::T3),
585 29 => Some(Self::T4),
586 30 => Some(Self::T5),
587 31 => Some(Self::T6),
588 _ => None,
589 }
590 }
591
592 #[inline(always)]
593 fn offset(self) -> usize {
594 // NOTE: `transmute()` is requited here, otherwise performance suffers A LOT for unknown
595 // reason
596 // SAFETY: Enum is `#[repr(u8)]` and doesn't have any fields
597 usize::from(unsafe { core::mem::transmute::<Self, u8>(self) })
598 // match self {
599 // Self::Zero => 0,
600 // Self::Ra => 1,
601 // Self::Sp => 2,
602 // Self::Gp => 3,
603 // Self::Tp => 4,
604 // Self::T0 => 5,
605 // Self::T1 => 6,
606 // Self::T2 => 7,
607 // Self::S0 => 8,
608 // Self::S1 => 9,
609 // Self::A0 => 10,
610 // Self::A1 => 11,
611 // Self::A2 => 12,
612 // Self::A3 => 13,
613 // Self::A4 => 14,
614 // Self::A5 => 15,
615 // Self::A6 => 16,
616 // Self::A7 => 17,
617 // Self::S2 => 18,
618 // Self::S3 => 19,
619 // Self::S4 => 20,
620 // Self::S5 => 21,
621 // Self::S6 => 22,
622 // Self::S7 => 23,
623 // Self::S8 => 24,
624 // Self::S9 => 25,
625 // Self::S10 => 26,
626 // Self::S11 => 27,
627 // Self::T3 => 28,
628 // Self::T4 => 29,
629 // Self::T5 => 30,
630 // Self::T6 => 31,
631 // Self::Phantom(_) => {
632 // // SAFETY: Phantom register is never constructed
633 // unsafe { unreachable_unchecked() }
634 // }
635 // }
636 }
637}
638
639// SAFETY: `Self::offset()` returns values within `0..Self::N` range
640unsafe impl const Register for Reg<u64> {
641 const N: usize = 32;
642 type Type = u64;
643
644 #[inline(always)]
645 fn is_zero(&self) -> bool {
646 matches!(self, Self::Zero)
647 }
648
649 #[inline(always)]
650 fn from_bits(bits: u8) -> Option<Self> {
651 match bits {
652 0 => Some(Self::Zero),
653 1 => Some(Self::Ra),
654 2 => Some(Self::Sp),
655 3 => Some(Self::Gp),
656 4 => Some(Self::Tp),
657 5 => Some(Self::T0),
658 6 => Some(Self::T1),
659 7 => Some(Self::T2),
660 8 => Some(Self::S0),
661 9 => Some(Self::S1),
662 10 => Some(Self::A0),
663 11 => Some(Self::A1),
664 12 => Some(Self::A2),
665 13 => Some(Self::A3),
666 14 => Some(Self::A4),
667 15 => Some(Self::A5),
668 16 => Some(Self::A6),
669 17 => Some(Self::A7),
670 18 => Some(Self::S2),
671 19 => Some(Self::S3),
672 20 => Some(Self::S4),
673 21 => Some(Self::S5),
674 22 => Some(Self::S6),
675 23 => Some(Self::S7),
676 24 => Some(Self::S8),
677 25 => Some(Self::S9),
678 26 => Some(Self::S10),
679 27 => Some(Self::S11),
680 28 => Some(Self::T3),
681 29 => Some(Self::T4),
682 30 => Some(Self::T5),
683 31 => Some(Self::T6),
684 _ => None,
685 }
686 }
687
688 #[inline(always)]
689 fn offset(self) -> usize {
690 // NOTE: `transmute()` is requited here, otherwise performance suffers A LOT for unknown
691 // reason
692 // SAFETY: Enum is `#[repr(u8)]` and doesn't have any fields
693 usize::from(unsafe { core::mem::transmute::<Self, u8>(self) })
694 // match self {
695 // Self::Zero => 0,
696 // Self::Ra => 1,
697 // Self::Sp => 2,
698 // Self::Gp => 3,
699 // Self::Tp => 4,
700 // Self::T0 => 5,
701 // Self::T1 => 6,
702 // Self::T2 => 7,
703 // Self::S0 => 8,
704 // Self::S1 => 9,
705 // Self::A0 => 10,
706 // Self::A1 => 11,
707 // Self::A2 => 12,
708 // Self::A3 => 13,
709 // Self::A4 => 14,
710 // Self::A5 => 15,
711 // Self::A6 => 16,
712 // Self::A7 => 17,
713 // Self::S2 => 18,
714 // Self::S3 => 19,
715 // Self::S4 => 20,
716 // Self::S5 => 21,
717 // Self::S6 => 22,
718 // Self::S7 => 23,
719 // Self::S8 => 24,
720 // Self::S9 => 25,
721 // Self::S10 => 26,
722 // Self::S11 => 27,
723 // Self::T3 => 28,
724 // Self::T4 => 29,
725 // Self::T5 => 30,
726 // Self::T6 => 31,
727 // Self::Phantom(_) => {
728 // // SAFETY: Phantom register is never constructed
729 // unsafe { unreachable_unchecked() }
730 // }
731 // }
732 }
733}