Skip to main content

ab_riscv_primitives/instructions/
v.rs

1//! V extension
2
3#[cfg(test)]
4mod tests;
5pub mod zvexx;
6
7use crate::instructions::Instruction;
8use crate::registers::general_purpose::{RegType, Register};
9use core::any::TypeId;
10use core::hint::{assert_unchecked, cold_path};
11use core::marker::ConstParamTy;
12use core::ops::RangeInclusive;
13use core::{cmp, fmt};
14
15/// Vector start element index
16#[derive(Debug, Clone, Copy)]
17#[derive_const(Default, PartialEq, Eq, Ord, PartialOrd)]
18pub struct Vstart(u16);
19
20impl fmt::Display for Vstart {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        write!(f, "{}", self.0)
23    }
24}
25
26const impl From<u16> for Vstart {
27    #[inline(always)]
28    fn from(value: u16) -> Self {
29        Self(value)
30    }
31}
32
33const impl From<Vstart> for u16 {
34    #[inline(always)]
35    fn from(value: Vstart) -> Self {
36        value.0
37    }
38}
39
40impl PartialEq<Vl> for Vstart {
41    fn eq(&self, other: &Vl) -> bool {
42        u32::from(self.0) == u32::from(*other)
43    }
44}
45
46impl PartialOrd<Vl> for Vstart {
47    #[inline(always)]
48    fn partial_cmp(&self, other: &Vl) -> Option<cmp::Ordering> {
49        Some(u32::from(self.0).cmp(&u32::from(*other)))
50    }
51}
52
53impl Vstart {
54    /// Zero vector start element index
55    pub const ZERO: Self = Self(0);
56
57    /// Returns a range of vector offsets from `self` to `vl`.
58    ///
59    /// Note: the range might be empty if `vl` is zero.
60    #[inline(always)]
61    pub const fn range_to(self, vl: Vl) -> RangeInclusive<u16> {
62        let vl = u32::from(vl);
63        if vl == 0 {
64            // Empty range
65            #[expect(clippy::reversed_empty_ranges, reason = "Intentional empty range")]
66            {
67                1..=0
68            }
69        } else {
70            self.0..=(vl - 1) as u16
71        }
72    }
73}
74
75/// Vector length
76#[derive(Debug, Clone, Copy)]
77#[derive_const(Default, PartialEq, Eq, Ord, PartialOrd)]
78pub struct Vl(u32);
79
80impl fmt::Display for Vl {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "{}", self.0)
83    }
84}
85
86const impl From<u8> for Vl {
87    #[inline(always)]
88    fn from(value: u8) -> Self {
89        Self(u32::from(value))
90    }
91}
92
93const impl From<u16> for Vl {
94    #[inline(always)]
95    fn from(value: u16) -> Self {
96        Self(u32::from(value))
97    }
98}
99
100const impl From<Vl> for u32 {
101    #[inline(always)]
102    fn from(value: Vl) -> Self {
103        value.0
104    }
105}
106
107const impl From<Vl> for u64 {
108    #[inline(always)]
109    fn from(value: Vl) -> Self {
110        u64::from(value.0)
111    }
112}
113
114impl PartialEq<Vstart> for Vl {
115    fn eq(&self, other: &Vstart) -> bool {
116        self.0 == u32::from(u16::from(*other))
117    }
118}
119
120impl PartialOrd<Vstart> for Vl {
121    #[inline(always)]
122    fn partial_cmp(&self, other: &Vstart) -> Option<cmp::Ordering> {
123        Some(self.0.cmp(&u32::from(u16::from(*other))))
124    }
125}
126
127impl Vl {
128    /// Zero vector length
129    pub const ZERO: Self = Self(0);
130
131    /// Create a new vector length from a value.
132    ///
133    /// Returns `None` if the value is greater than the maximum vector length.
134    #[inline(always)]
135    pub const fn new(n: u32) -> Option<Self> {
136        if n > u32::from(Vlen::L65_536) {
137            None
138        } else {
139            Some(Self(n))
140        }
141    }
142
143    /// Create a new vector length from a value with saturation
144    #[inline(always)]
145    pub const fn new_saturating(n: u32) -> Self {
146        let n = u32::from(Vlen::L65_536).min(n);
147        Self(n)
148    }
149
150    /// Returns the vector length in bytes
151    #[inline(always)]
152    pub const fn bytes(self) -> u16 {
153        self.0.div_ceil(u8::BITS) as u16
154    }
155}
156
157/// Element length
158#[derive(ConstParamTy, Debug, Clone, Copy)]
159#[derive_const(PartialEq, Eq)]
160#[repr(u32)]
161pub enum Elen {
162    /// Element length is 8 bits
163    L8 = 8,
164    /// Element length is 16 bits
165    L16 = 16,
166    /// Element length is 32 bits
167    L32 = 32,
168    /// Element length is 64 bits
169    L64 = 64,
170    /// Element length is 128 bits
171    L128 = 128,
172    /// Element length is 256 bits
173    L256 = 256,
174    /// Element length is 512 bits
175    L512 = 512,
176    /// Element length is 1024 bits
177    L1024 = 1024,
178    /// Element length is 2048 bits
179    L2048 = 2048,
180    /// Element length is 4096 bits
181    L4096 = 4096,
182    /// Element length is 8192 bits
183    L8192 = 8192,
184    /// Element length is 16_384 bits
185    L16_384 = 16_384,
186    /// Element length is 32_768 bits
187    L32_768 = 32_768,
188    /// Element length is 65_536 bits
189    L65_536 = 65_536,
190}
191
192const impl From<Elen> for u32 {
193    #[inline(always)]
194    fn from(value: Elen) -> Self {
195        value as u32
196    }
197}
198
199/// Vector length
200#[derive(ConstParamTy, Debug, Clone, Copy)]
201#[derive_const(PartialEq, Eq)]
202#[repr(u32)]
203pub enum Vlen {
204    /// Vector length is 8 bits
205    L8 = 8,
206    /// Vector length is 16 bits
207    L16 = 16,
208    /// Vector length is 32 bits
209    L32 = 32,
210    /// Vector length is 64 bits
211    L64 = 64,
212    /// Vector length is 128 bits
213    L128 = 128,
214    /// Vector length is 256 bits
215    L256 = 256,
216    /// Vector length is 512 bits
217    L512 = 512,
218    /// Vector length is 1024 bits
219    L1024 = 1024,
220    /// Vector length is 2048 bits
221    L2048 = 2048,
222    /// Vector length is 4096 bits
223    L4096 = 4096,
224    /// Vector length is 8192 bits
225    L8192 = 8192,
226    /// Vector length is 16_384 bits
227    L16_384 = 16_384,
228    /// Vector length is 32_768 bits
229    L32_768 = 32_768,
230    /// Vector length is 65_536 bits
231    L65_536 = 65_536,
232}
233
234const impl From<Vlen> for u32 {
235    #[inline(always)]
236    fn from(value: Vlen) -> Self {
237        value as u32
238    }
239}
240
241impl Vlen {
242    /// Vlen in bytes
243    #[inline(always)]
244    pub const fn bytes(self) -> u32 {
245        self as u32 / u8::BITS
246    }
247}
248
249/// Assertion for supported ELEN + VLEN combinations, to be used in `where` bounds (panics on
250/// invalid input)
251pub const SUPPORTED_ELEN_VLEN<const ELEN: Elen, const VLEN: Vlen>: usize = {
252    assert!(
253        u32::from(ELEN) <= u32::from(VLEN),
254        "ELEN must be <= VLEN"
255    );
256    0
257};
258
259/// `mstatus.VS` / `sstatus.VS` / `vsstatus.VS` field encoding.
260///
261/// Context status for the vector extension, analogous to `mstatus.FS`.
262/// Located at bits `[10:9]` in the respective status registers.
263#[derive(Debug, Clone, Copy)]
264#[derive_const(PartialEq, Eq)]
265#[repr(u8)]
266pub enum VsStatus {
267    /// Vector unit is off; any vector instruction or CSR access raises illegal instruction
268    Off = 0,
269    /// Vector state is known to be in its initial state
270    Initial = 1,
271    /// Vector state is potentially modified but matches the last saved state
272    Clean = 2,
273    /// Vector state has been modified since the last save
274    Dirty = 3,
275}
276
277impl VsStatus {
278    /// Decode from a 2-bit field value
279    #[inline(always)]
280    pub const fn from_bits(bits: u8) -> Self {
281        match bits & 0b11 {
282            0 => Self::Off,
283            1 => Self::Initial,
284            2 => Self::Clean,
285            _ => Self::Dirty,
286        }
287    }
288
289    /// Encode to a 2-bit field value
290    #[inline(always)]
291    pub const fn to_bits(self) -> u8 {
292        self as u8
293    }
294}
295
296/// Vector length multiplier (LMUL) setting.
297///
298/// Encoded in `vtype[2:0]` as a signed 3-bit value.
299/// `LMUL = 2^vlmul` where `vlmul` is sign-extended. Positive values give integer multipliers,
300/// negative values give fractional.
301#[derive(Debug, Clone, Copy)]
302#[derive_const(PartialEq, Eq)]
303#[repr(u8)]
304pub enum Vlmul {
305    /// LMUL = 1 (`vlmul` encoding 0b000)
306    M1 = 0b000,
307    /// LMUL = 2 (`vlmul` encoding 0b001)
308    M2 = 0b001,
309    /// LMUL = 4 (`vlmul` encoding 0b010)
310    M4 = 0b010,
311    /// LMUL = 8 (`vlmul` encoding 0b011)
312    M8 = 0b011,
313    /// LMUL = 1/8 (`vlmul` encoding 0b101)
314    Mf8 = 0b101,
315    /// LMUL = 1/4 (`vlmul` encoding 0b110)
316    Mf4 = 0b110,
317    /// LMUL = 1/2 (`vlmul` encoding 0b111)
318    Mf2 = 0b111,
319}
320
321impl Vlmul {
322    /// Decode from the 3-bit `vlmul` field. Returns `None` for reserved encoding 0b100.
323    #[inline(always)]
324    pub const fn from_bits(bits: u8) -> Option<Self> {
325        match bits & 0b111 {
326            0b000 => Some(Self::M1),
327            0b001 => Some(Self::M2),
328            0b010 => Some(Self::M4),
329            0b011 => Some(Self::M8),
330            0b101 => Some(Self::Mf8),
331            0b110 => Some(Self::Mf4),
332            0b111 => Some(Self::Mf2),
333            _ => None,
334        }
335    }
336
337    /// Encode to the 3-bit `vlmul` field
338    #[inline(always)]
339    pub const fn to_bits(self) -> u8 {
340        self as u8
341    }
342
343    /// Compute `VLMAX = LMUL * VLEN / SEW`.
344    ///
345    /// For fractional LMUL, this is `VLEN / (SEW * denominator)`.
346    /// Returns `Vl::ZERO` when the result would be less than 1 (insufficient bits).
347    #[inline(always)]
348    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
349    pub const fn vlmax<const VLEN: Vlen>(self, sew: Vsew) -> Vl {
350        let sew_bits = u32::from(sew.bits_width());
351        let vl = match self {
352            Self::M1 => u32::from(VLEN) / sew_bits,
353            Self::M2 => (u32::from(VLEN) * 2) / sew_bits,
354            Self::M4 => (u32::from(VLEN) * 4) / sew_bits,
355            Self::M8 => (u32::from(VLEN) * 8) / sew_bits,
356            Self::Mf2 => u32::from(VLEN) / (sew_bits * 2),
357            Self::Mf4 => u32::from(VLEN) / (sew_bits * 4),
358            Self::Mf8 => u32::from(VLEN) / (sew_bits * 8),
359        };
360        // SAFETY: Can't exceed the maximum vector length for any `sew` value
361        unsafe { Vl::new(vl).unwrap_unchecked() }
362    }
363
364    /// Number of vector registers occupied by one register group at this `LMUL`.
365    ///
366    /// Fractional `LMUL` values (`Mf2`, `Mf4`, `Mf8`) each occupy exactly 1 register.
367    /// Integer `LMUL` values occupy 1, 2, 4, or 8 registers respectively.
368    #[inline(always)]
369    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
370    pub const fn register_count(self) -> VRegGroupSize {
371        match self {
372            Self::Mf8 | Self::Mf4 | Self::Mf2 | Self::M1 => VRegGroupSize::R1,
373            Self::M2 => VRegGroupSize::R2,
374            Self::M4 => VRegGroupSize::R4,
375            Self::M8 => VRegGroupSize::R8,
376        }
377    }
378
379    /// Whether this is a fractional multiplier, so a register group is a part of one register
380    #[inline(always)]
381    pub const fn is_fractional(self) -> bool {
382        matches!(self, Self::Mf8 | Self::Mf4 | Self::Mf2)
383    }
384
385    /// Twice this multiplier, `None` above `M8`
386    #[inline(always)]
387    pub const fn double(self) -> Option<Self> {
388        match self {
389            Self::Mf8 => Some(Self::Mf4),
390            Self::Mf4 => Some(Self::Mf2),
391            Self::Mf2 => Some(Self::M1),
392            Self::M1 => Some(Self::M2),
393            Self::M2 => Some(Self::M4),
394            Self::M4 => Some(Self::M8),
395            Self::M8 => {
396                cold_path();
397                None
398            }
399        }
400    }
401
402    /// `LMUL` in eighths, so that fractional multipliers are integers too
403    #[inline(always)]
404    const fn eighths(self) -> u32 {
405        let eighths: u32 = match self {
406            Self::Mf8 => 1,
407            Self::Mf4 => 2,
408            Self::Mf2 => 4,
409            Self::M1 => 8,
410            Self::M2 => 16,
411            Self::M4 => 32,
412            Self::M8 => 64,
413        };
414        // TODO: Remove once rustc stops folding this `match` into a cast, which hides the
415        //  power of two from LLVM: https://github.com/rust-lang/rust/issues/162513
416        // SAFETY: Every variant is a power of two
417        unsafe {
418            assert_unchecked(eighths.is_power_of_two());
419        }
420        eighths
421    }
422
423    /// Inverse of [`Self::eighths()`], `None` outside the legal range `[1/8, 8]`
424    #[inline(always)]
425    const fn from_eighths(eighths: u32) -> Option<Self> {
426        match eighths {
427            1 => Some(Self::Mf8),
428            2 => Some(Self::Mf4),
429            4 => Some(Self::Mf2),
430            8 => Some(Self::M1),
431            16 => Some(Self::M2),
432            32 => Some(Self::M4),
433            64 => Some(Self::M8),
434            _ => {
435                cold_path();
436                None
437            }
438        }
439    }
440
441    /// Effective multiplier `EMUL = LMUL * EEW / SEW` of an operand with element width `eew`
442    /// under this `LMUL` and `sew`.
443    ///
444    /// Returns `None` when `EMUL` falls outside the legal range `[1/8, 8]`.
445    #[inline(always)]
446    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
447    pub const fn emul(self, eew: Eew, sew: Vsew) -> Option<Self> {
448        let eighths = self.eighths() * u32::from(eew.bits_width()) / u32::from(sew.bits_width());
449        Self::from_eighths(eighths)
450    }
451
452    /// Number of vector registers occupied by the destination group of a widening instruction,
453    /// whose `EMUL = 2 * LMUL`.
454    ///
455    /// Returns `None` for `M8`, where `EMUL` would be `16`.
456    #[inline(always)]
457    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
458    pub const fn widening_register_count(self) -> Option<VRegGroupSize> {
459        Some(self.double()?.register_count())
460    }
461
462    /// Compute `EMUL` for an indexed load: `EMUL = (index_eew / sew) * LMUL`.
463    ///
464    /// Returns the register count for the index register group, or `None` when `EMUL` falls
465    /// outside the legal range `[1/8, 8]`.
466    #[inline(always)]
467    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
468    pub const fn index_register_count(self, index_eew: Eew, sew: Vsew) -> Option<VRegGroupSize> {
469        Some(self.emul(index_eew, sew)?.register_count())
470    }
471
472    /// Compute EMUL for a data operand of a memory instruction with a given effective element
473    /// width: `EMUL = (eew / sew) * LMUL`.
474    ///
475    /// Mathematically identical to [`Self::index_register_count`], but exposed under a distinct
476    /// name for call sites where the EEW describes the *data* being loaded or stored rather than an
477    /// index. Keeping the two entry points separate avoids accidental semantic drift if
478    /// one of them is later specialised.
479    ///
480    /// Returns `None` when the resulting EMUL falls outside the legal range `[1/8, 8]`.
481    #[inline(always)]
482    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
483    pub const fn data_register_count(self, eew: Eew, sew: Vsew) -> Option<VRegGroupSize> {
484        self.index_register_count(eew, sew)
485    }
486}
487
488impl fmt::Display for Vlmul {
489    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490        match self {
491            Self::M1 => write!(f, "m1"),
492            Self::M2 => write!(f, "m2"),
493            Self::M4 => write!(f, "m4"),
494            Self::M8 => write!(f, "m8"),
495            Self::Mf8 => write!(f, "mf8"),
496            Self::Mf4 => write!(f, "mf4"),
497            Self::Mf2 => write!(f, "mf2"),
498        }
499    }
500}
501
502/// Number of vector registers in a register group.
503///
504/// Register groups always consist of a power of two number of registers, whether from
505/// `LMUL`, `EMUL` of a memory operand or a whole-register move.
506#[derive(Debug, Clone, Copy)]
507#[derive_const(PartialEq, Eq)]
508#[repr(u8)]
509pub enum VRegGroupSize {
510    /// A single register
511    R1 = 1,
512    /// Two registers
513    R2 = 2,
514    /// Four registers
515    R4 = 4,
516    /// Eight registers
517    R8 = 8,
518}
519
520impl VRegGroupSize {
521    /// Number of registers in the group
522    #[inline(always)]
523    pub const fn get(self) -> u8 {
524        let count = self as u8;
525        // TODO: Remove once rustc tells LLVM which values an enum can have rather than their
526        //  range, which hides the power of two: https://github.com/rust-lang/rust/issues/162513
527        // SAFETY: Every variant is a power of two
528        unsafe {
529            assert_unchecked(count.is_power_of_two());
530        }
531        count
532    }
533
534    /// Size of a group with `factor` times fewer registers, at least one.
535    ///
536    /// This is the source group of a `vzext`/`vsext`, whose `EMUL = LMUL / factor`.
537    #[inline(always)]
538    pub const fn divide_by_factor(self, factor: VsewFactor) -> Self {
539        match self.get() / factor.factor() {
540            2 => Self::R2,
541            4 => Self::R4,
542            _ => Self::R1,
543        }
544    }
545}
546
547impl fmt::Display for VRegGroupSize {
548    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549        write!(f, "{}", self.get())
550    }
551}
552
553/// Factor by which Vsew width is divided
554#[derive(Debug, Clone, Copy)]
555#[derive_const(PartialEq, Eq)]
556#[repr(u8)]
557pub enum VsewFactor {
558    /// Divide width by 2
559    F2 = 2,
560    /// Divide width by 4
561    F4 = 4,
562    /// Divide width by 8
563    F8 = 8,
564}
565
566impl VsewFactor {
567    /// Return the numeric divisor used to scale down a [`Vsew`] bit-width
568    #[inline(always)]
569    pub const fn factor(self) -> u8 {
570        let factor = self as u8;
571        // TODO: Remove once rustc tells LLVM which values an enum can have rather than their
572        //  range, which hides the power of two: https://github.com/rust-lang/rust/issues/162513
573        // SAFETY: Every variant is a power of two
574        unsafe {
575            assert_unchecked(factor.is_power_of_two());
576        }
577        factor
578    }
579}
580
581/// Selected element width (SEW).
582///
583/// Encoded in `vtype[5:3]` as `vsew`. `SEW = 8 * 2^vsew`.
584#[derive(ConstParamTy, Debug, Clone, Copy)]
585#[derive_const(PartialEq, Eq)]
586#[repr(u8)]
587pub enum Vsew {
588    /// SEW = 8 bits (vsew = 0b000)
589    E8 = 8,
590    /// SEW = 16 bits (vsew = 0b001)
591    E16 = 16,
592    /// SEW = 32 bits (vsew = 0b010)
593    E32 = 32,
594    /// SEW = 64 bits (vsew = 0b011)
595    E64 = 64,
596}
597
598impl Vsew {
599    /// Decode from the 3-bit vsew field. Returns `None` for reserved encodings.
600    #[inline(always)]
601    pub const fn from_bits(bits: u8) -> Option<Self> {
602        match bits & 0b111 {
603            0b000 => Some(Self::E8),
604            0b001 => Some(Self::E16),
605            0b010 => Some(Self::E32),
606            0b011 => Some(Self::E64),
607            _ => {
608                cold_path();
609                None
610            }
611        }
612    }
613
614    /// Create a selected element width that matches the register width
615    #[inline(always)]
616    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
617    pub const fn from_xlen<Reg>() -> Self
618    where
619        Reg: [const] Register,
620    {
621        const {
622            match Reg::XLEN {
623                32 => Self::E32,
624                64 => Self::E64,
625                _ => {
626                    // TODO: Should have been `unreachable!()`:
627                    //  https://github.com/rust-lang/rust/issues/159645
628                    panic!("Invalid register width")
629                }
630            }
631        }
632    }
633
634    /// Encode to the 3-bit vsew field
635    #[inline(always)]
636    pub const fn to_bits(self) -> u8 {
637        match self {
638            Vsew::E8 => 0b000,
639            Vsew::E16 => 0b001,
640            Vsew::E32 => 0b010,
641            Vsew::E64 => 0b011,
642        }
643    }
644
645    /// Get the double element width, if available
646    #[inline(always)]
647    pub const fn double_width(self) -> Option<Self> {
648        match self {
649            Self::E8 => Some(Self::E16),
650            Self::E16 => Some(Self::E32),
651            Self::E32 => Some(Self::E64),
652            Self::E64 => {
653                cold_path();
654                None
655            }
656        }
657    }
658
659    /// Divide Vsew width by a given factor
660    #[inline(always)]
661    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
662    pub const fn divide_by_factor(self, factor: VsewFactor) -> Option<Self> {
663        let Some(divide_by_factor) = self.bits_width().div_exact(factor.factor()) else {
664            cold_path();
665            return None;
666        };
667        match divide_by_factor {
668            8 => Some(Self::E8),
669            16 => Some(Self::E16),
670            32 => Some(Self::E32),
671            _ => {
672                cold_path();
673                None
674            }
675        }
676    }
677
678    /// Element width in bits
679    #[inline(always)]
680    pub const fn bits_width(self) -> u8 {
681        let bits: u8 = match self {
682            Self::E8 => 8,
683            Self::E16 => 16,
684            Self::E32 => 32,
685            Self::E64 => 64,
686        };
687        // TODO: Remove once rustc stops folding this `match` into a cast, which hides the
688        //  power of two from LLVM: https://github.com/rust-lang/rust/issues/162513
689        // SAFETY: Every variant is a power of two
690        unsafe {
691            assert_unchecked(bits.is_power_of_two());
692        }
693        bits
694    }
695
696    /// Element width in bytes
697    #[inline(always)]
698    pub const fn bytes_width(self) -> u8 {
699        let bytes: u8 = match self {
700            Self::E8 => 1,
701            Self::E16 => 2,
702            Self::E32 => 4,
703            Self::E64 => 8,
704        };
705        // TODO: Remove once rustc stops folding this `match` into a cast, which hides the
706        //  power of two from LLVM: https://github.com/rust-lang/rust/issues/162513
707        // SAFETY: Every variant is a power of two
708        unsafe {
709            assert_unchecked(bytes.is_power_of_two());
710        }
711        bytes
712    }
713
714    /// Convert to the corresponding `Eew` variant.
715    ///
716    /// Every valid `Vsew` value has a directly corresponding `Eew` value because both
717    /// enumerate the same set of widths (8/16/32/64 bits). The conversion is always
718    /// successful.
719    #[inline(always)]
720    pub const fn as_eew(self) -> Eew {
721        match self {
722            Self::E8 => Eew::E8,
723            Self::E16 => Eew::E16,
724            Self::E32 => Eew::E32,
725            Self::E64 => Eew::E64,
726        }
727    }
728}
729
730impl fmt::Display for Vsew {
731    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
732        match self {
733            Self::E8 => write!(f, "e8"),
734            Self::E16 => write!(f, "e16"),
735            Self::E32 => write!(f, "e32"),
736            Self::E64 => write!(f, "e64"),
737        }
738    }
739}
740
741/// Effective element width of a vector operand.
742///
743/// Memory operands carry it in the instruction, register operands derive it from `SEW`.
744#[derive(ConstParamTy, Debug, Clone, Copy)]
745#[derive_const(PartialEq, Eq)]
746#[repr(u8)]
747pub enum Eew {
748    /// 8-bit elements
749    E8 = 1,
750    /// 16-bit elements
751    E16 = 2,
752    /// 32-bit elements
753    E32 = 4,
754    /// 64-bit elements
755    E64 = 8,
756}
757
758impl Eew {
759    /// Max element width in bytes
760    pub const MAX_BYTES: u8 = Self::E64.bytes_width();
761
762    /// Decode the width field into an element width
763    #[inline(always)]
764    pub const fn from_width(width: u8) -> Option<Self> {
765        match width {
766            0b000 => Some(Self::E8),
767            0b101 => Some(Self::E16),
768            0b110 => Some(Self::E32),
769            0b111 => Some(Self::E64),
770            _ => {
771                cold_path();
772                None
773            }
774        }
775    }
776
777    /// Encode to the 3-bit Eew field
778    #[inline(always)]
779    pub const fn to_bits(self) -> u8 {
780        match self {
781            Eew::E8 => 0b000,
782            Eew::E16 => 0b101,
783            Eew::E32 => 0b110,
784            Eew::E64 => 0b111,
785        }
786    }
787
788    /// Element width in bits
789    #[inline(always)]
790    pub const fn bits_width(self) -> u8 {
791        let bits: u8 = match self {
792            Self::E8 => 8,
793            Self::E16 => 16,
794            Self::E32 => 32,
795            Self::E64 => 64,
796        };
797        // TODO: Remove once rustc stops folding this `match` into a cast, which hides the
798        //  power of two from LLVM: https://github.com/rust-lang/rust/issues/162513
799        // SAFETY: Every variant is a power of two
800        unsafe {
801            assert_unchecked(bits.is_power_of_two());
802        }
803        bits
804    }
805
806    /// Element width in bytes.
807    ///
808    /// Guaranteed to be `<= Self::MAX_BYTES`.
809    #[inline(always)]
810    pub const fn bytes_width(self) -> u8 {
811        let bytes: u8 = match self {
812            Self::E8 => 1,
813            Self::E16 => 2,
814            Self::E32 => 4,
815            Self::E64 => 8,
816        };
817        // TODO: Remove once rustc stops folding this `match` into a cast, which hides the
818        //  power of two from LLVM: https://github.com/rust-lang/rust/issues/162513
819        // SAFETY: Every variant is a power of two
820        unsafe {
821            assert_unchecked(bytes.is_power_of_two());
822        }
823        bytes
824    }
825}
826
827const impl From<Vsew> for Eew {
828    #[inline(always)]
829    fn from(sew: Vsew) -> Self {
830        sew.as_eew()
831    }
832}
833
834impl fmt::Display for Eew {
835    #[inline]
836    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
837        fmt::Display::fmt(&self.bits_width(), f)
838    }
839}
840
841/// Vector fixed-point rounding mode.
842///
843/// Encoded in the `vxrm` CSR bits `[1:0]` and mirrored in `vcsr[2:1]`.
844#[derive(Debug, Clone, Copy)]
845#[derive_const(Default, PartialEq, Eq)]
846#[repr(u8)]
847pub enum Vxrm {
848    /// Round-to-nearest-up (rnu)
849    #[default]
850    Rnu = 0b00,
851    /// Round-to-nearest-even (rne)
852    Rne = 0b01,
853    /// Round-down / truncate (rdn)
854    Rdn = 0b10,
855    /// Round-to-odd (rod)
856    Rod = 0b11,
857}
858
859impl Vxrm {
860    /// Decode from a 2-bit field
861    #[inline(always)]
862    pub const fn from_bits(bits: u8) -> Self {
863        match bits & 0b11 {
864            0b00 => Self::Rnu,
865            0b01 => Self::Rne,
866            0b10 => Self::Rdn,
867            _ => Self::Rod,
868        }
869    }
870
871    /// Encode to a 2-bit field
872    #[inline(always)]
873    pub const fn to_bits(self) -> u8 {
874        self as u8
875    }
876}
877
878/// Decoded `vtype` register contents.
879///
880/// The vtype CSR controls the interpretation of the vector register file: element width, register
881/// grouping, and tail/mask agnostic policies.
882///
883/// The raw encoding is XLEN-dependent (vill is at bit XLEN-1), but this decoded form is
884/// XLEN-independent.
885#[derive(Debug, Clone, Copy)]
886#[derive_const(PartialEq, Eq)]
887pub struct Vtype<const ELEN: Elen, const VLEN: Vlen>
888where
889    [(); SUPPORTED_ELEN_VLEN::<ELEN, VLEN>]:,
890{
891    /// Vector mask agnostic policy (bit `7`)
892    vma: bool,
893    /// Vector tail agnostic policy (bit `6`)
894    vta: bool,
895    /// Selected element width (bits `[5:3]`)
896    vsew: Vsew,
897    /// Vector length multiplier (bits `[2:0]`)
898    vlmul: Vlmul,
899}
900
901impl<const ELEN: Elen, const VLEN: Vlen> Vtype<ELEN, VLEN>
902where
903    [(); SUPPORTED_ELEN_VLEN::<ELEN, VLEN>]:,
904{
905    /// Vector mask agnostic policy (bit `7`)
906    pub const fn vma(&self) -> bool {
907        self.vma
908    }
909
910    /// Vector tail agnostic policy (bit `6`)
911    pub const fn vta(&self) -> bool {
912        self.vta
913    }
914
915    /// Selected element width (bits `[5:3]`)
916    pub const fn vsew(&self) -> Vsew {
917        self.vsew
918    }
919
920    /// Vector length multiplier (bits `[2:0]`)
921    pub const fn vlmul(&self) -> Vlmul {
922        self.vlmul
923    }
924
925    /// Decode from raw register value.
926    ///
927    /// The `XLEN` is taken from `Reg::XLEN` and must be 32 for RV32 or 64 for RV64. The `vill` bit
928    /// is placed at bit position `Reg::XLEN - 1`.
929    ///
930    /// All bits in `[Reg::XLEN-1:8]` must be zero; non-zero bits indicate an unrecognized
931    /// encoding and cause `None` to be returned (this includes `vill`).
932    #[inline(always)]
933    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
934    pub const fn from_raw<Reg>(raw: Reg::Type) -> Option<Self>
935    where
936        Reg: [const] Register,
937    {
938        let raw = raw.as_u64();
939
940        // All bits in [XLEN-1:8] must be zero
941        if (raw >> 8u8) != 0 {
942            cold_path();
943            return None;
944        }
945
946        let vlmul_bits = (raw & 0b111) as u8;
947        let vsew_bits = ((raw >> 3u8) & 0b111) as u8;
948        let vta = ((raw >> 6u8) & 1) != 0;
949        let vma = ((raw >> 7u8) & 1) != 0;
950
951        let Some(vlmul) = Vlmul::from_bits(vlmul_bits) else {
952            cold_path();
953            return None;
954        };
955        let Some(vsew) = Vsew::from_bits(vsew_bits) else {
956            cold_path();
957            return None;
958        };
959
960        let sew = vsew.bits_width();
961        if u32::from(sew) > u32::from(ELEN) {
962            cold_path();
963            return None;
964        }
965
966        if u32::from(vlmul.vlmax::<VLEN>(vsew)) == 0 {
967            cold_path();
968            return None;
969        }
970
971        Some(Self {
972            vma,
973            vta,
974            vsew,
975            vlmul,
976        })
977    }
978
979    /// Encode to a raw `vtype` register value of type `Reg::Type`.
980    ///
981    /// The encoded value contains `vlmul`, `vsew`, `vta`, and `vma` in bits `[7:0]` with
982    /// `vill = 0`. To construct a raw value with `vill = 1` (illegal configuration), use
983    /// [`Self::illegal_raw`].
984    #[inline(always)]
985    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
986    pub const fn to_raw<Reg>(self) -> Reg::Type
987    where
988        Reg: [const] Register,
989    {
990        let mut raw = 0u8;
991        raw |= self.vlmul.to_bits();
992        raw |= self.vsew.to_bits() << 3u8;
993
994        if self.vta {
995            raw |= 1 << 6u8;
996        }
997
998        if self.vma {
999            raw |= 1 << 7u8;
1000        }
1001
1002        Reg::Type::from(raw)
1003    }
1004
1005    /// Construct a raw value for `vtype` with `vill=1` (illegal configuration).
1006    ///
1007    /// Per spec: when `vill` is set, the remaining bits are zero and `vl` is also set to zero. Any
1008    /// subsequent vector instruction that depends on `vtype` will raise an illegal-instruction
1009    /// exception.
1010    #[inline(always)]
1011    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
1012    pub const fn illegal_raw<Reg>() -> Reg::Type
1013    where
1014        Reg: [const] Register,
1015    {
1016        let vill_bit = Reg::XLEN - 1;
1017        Reg::Type::from(1u8) << vill_bit
1018    }
1019}
1020
1021/// RISC-V V instruction (placeholder)
1022#[derive(Debug, Clone, Copy)]
1023#[derive_const(PartialEq, Eq)]
1024#[doc(hidden)]
1025pub enum V<Reg> {
1026    V(Reg, !),
1027}
1028
1029const impl<Reg> Instruction for V<Reg>
1030where
1031    Reg: [const] Register,
1032{
1033    const IMPLEMENTED_EXTENSIONS: &'static [TypeId] = &[];
1034
1035    const ALIGNMENT: u8 = align_of::<u32>() as u8;
1036
1037    type Reg = Reg;
1038
1039    #[inline(always)]
1040    fn try_decode(_instruction: u32) -> Option<Self> {
1041        None
1042    }
1043
1044    #[inline(always)]
1045    fn size(&self) -> u8 {
1046        size_of::<u32>() as u8
1047    }
1048}
1049
1050impl<Reg> fmt::Display for V<Reg>
1051where
1052    Reg: fmt::Display,
1053{
1054    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
1055        match self {
1056            V::V(_, _) => {
1057                unreachable!("Impossible to construct V instruction")
1058            }
1059        }
1060    }
1061}