1#[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#[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 pub const ZERO: Self = Self(0);
56
57 #[inline(always)]
61 pub const fn range_to(self, vl: Vl) -> RangeInclusive<u16> {
62 let vl = u32::from(vl);
63 if vl == 0 {
64 #[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#[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 pub const ZERO: Self = Self(0);
130
131 #[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 #[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 #[inline(always)]
152 pub const fn bytes(self) -> u16 {
153 self.0.div_ceil(u8::BITS) as u16
154 }
155}
156
157#[derive(ConstParamTy, Debug, Clone, Copy)]
159#[derive_const(PartialEq, Eq)]
160#[repr(u32)]
161pub enum Elen {
162 L8 = 8,
164 L16 = 16,
166 L32 = 32,
168 L64 = 64,
170 L128 = 128,
172 L256 = 256,
174 L512 = 512,
176 L1024 = 1024,
178 L2048 = 2048,
180 L4096 = 4096,
182 L8192 = 8192,
184 L16_384 = 16_384,
186 L32_768 = 32_768,
188 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#[derive(ConstParamTy, Debug, Clone, Copy)]
201#[derive_const(PartialEq, Eq)]
202#[repr(u32)]
203pub enum Vlen {
204 L8 = 8,
206 L16 = 16,
208 L32 = 32,
210 L64 = 64,
212 L128 = 128,
214 L256 = 256,
216 L512 = 512,
218 L1024 = 1024,
220 L2048 = 2048,
222 L4096 = 4096,
224 L8192 = 8192,
226 L16_384 = 16_384,
228 L32_768 = 32_768,
230 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 #[inline(always)]
244 pub const fn bytes(self) -> u32 {
245 self as u32 / u8::BITS
246 }
247}
248
249pub 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#[derive(Debug, Clone, Copy)]
264#[derive_const(PartialEq, Eq)]
265#[repr(u8)]
266pub enum VsStatus {
267 Off = 0,
269 Initial = 1,
271 Clean = 2,
273 Dirty = 3,
275}
276
277impl VsStatus {
278 #[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 #[inline(always)]
291 pub const fn to_bits(self) -> u8 {
292 self as u8
293 }
294}
295
296#[derive(Debug, Clone, Copy)]
302#[derive_const(PartialEq, Eq)]
303#[repr(u8)]
304pub enum Vlmul {
305 M1 = 0b000,
307 M2 = 0b001,
309 M4 = 0b010,
311 M8 = 0b011,
313 Mf8 = 0b101,
315 Mf4 = 0b110,
317 Mf2 = 0b111,
319}
320
321impl Vlmul {
322 #[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 #[inline(always)]
339 pub const fn to_bits(self) -> u8 {
340 self as u8
341 }
342
343 #[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 unsafe { Vl::new(vl).unwrap_unchecked() }
362 }
363
364 #[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 #[inline(always)]
381 pub const fn is_fractional(self) -> bool {
382 matches!(self, Self::Mf8 | Self::Mf4 | Self::Mf2)
383 }
384
385 #[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 #[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 unsafe {
418 assert_unchecked(eighths.is_power_of_two());
419 }
420 eighths
421 }
422
423 #[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 #[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 #[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 #[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 #[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#[derive(Debug, Clone, Copy)]
507#[derive_const(PartialEq, Eq)]
508#[repr(u8)]
509pub enum VRegGroupSize {
510 R1 = 1,
512 R2 = 2,
514 R4 = 4,
516 R8 = 8,
518}
519
520impl VRegGroupSize {
521 #[inline(always)]
523 pub const fn get(self) -> u8 {
524 let count = self as u8;
525 unsafe {
529 assert_unchecked(count.is_power_of_two());
530 }
531 count
532 }
533
534 #[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#[derive(Debug, Clone, Copy)]
555#[derive_const(PartialEq, Eq)]
556#[repr(u8)]
557pub enum VsewFactor {
558 F2 = 2,
560 F4 = 4,
562 F8 = 8,
564}
565
566impl VsewFactor {
567 #[inline(always)]
569 pub const fn factor(self) -> u8 {
570 let factor = self as u8;
571 unsafe {
575 assert_unchecked(factor.is_power_of_two());
576 }
577 factor
578 }
579}
580
581#[derive(ConstParamTy, Debug, Clone, Copy)]
585#[derive_const(PartialEq, Eq)]
586#[repr(u8)]
587pub enum Vsew {
588 E8 = 8,
590 E16 = 16,
592 E32 = 32,
594 E64 = 64,
596}
597
598impl Vsew {
599 #[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 #[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 panic!("Invalid register width")
629 }
630 }
631 }
632 }
633
634 #[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 #[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 #[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 #[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 unsafe {
691 assert_unchecked(bits.is_power_of_two());
692 }
693 bits
694 }
695
696 #[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 unsafe {
709 assert_unchecked(bytes.is_power_of_two());
710 }
711 bytes
712 }
713
714 #[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#[derive(ConstParamTy, Debug, Clone, Copy)]
745#[derive_const(PartialEq, Eq)]
746#[repr(u8)]
747pub enum Eew {
748 E8 = 1,
750 E16 = 2,
752 E32 = 4,
754 E64 = 8,
756}
757
758impl Eew {
759 pub const MAX_BYTES: u8 = Self::E64.bytes_width();
761
762 #[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 #[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 #[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 unsafe {
801 assert_unchecked(bits.is_power_of_two());
802 }
803 bits
804 }
805
806 #[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 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#[derive(Debug, Clone, Copy)]
845#[derive_const(Default, PartialEq, Eq)]
846#[repr(u8)]
847pub enum Vxrm {
848 #[default]
850 Rnu = 0b00,
851 Rne = 0b01,
853 Rdn = 0b10,
855 Rod = 0b11,
857}
858
859impl Vxrm {
860 #[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 #[inline(always)]
873 pub const fn to_bits(self) -> u8 {
874 self as u8
875 }
876}
877
878#[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 vma: bool,
893 vta: bool,
895 vsew: Vsew,
897 vlmul: Vlmul,
899}
900
901impl<const ELEN: Elen, const VLEN: Vlen> Vtype<ELEN, VLEN>
902where
903 [(); SUPPORTED_ELEN_VLEN::<ELEN, VLEN>]:,
904{
905 pub const fn vma(&self) -> bool {
907 self.vma
908 }
909
910 pub const fn vta(&self) -> bool {
912 self.vta
913 }
914
915 pub const fn vsew(&self) -> Vsew {
917 self.vsew
918 }
919
920 pub const fn vlmul(&self) -> Vlmul {
922 self.vlmul
923 }
924
925 #[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 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 #[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 #[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#[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}