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