Skip to main content

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