1use crate::v::vector_registers::{VLENB_USIZE, VectorRegisterFile, VectorRegistersExt};
4pub use crate::v::zvexx::arith::zvexx_arith_helpers::{OpSrc, check_vreg_group_alignment};
5use crate::v::zvexx::zvexx_helpers::INSTRUCTION_SIZE;
6use crate::{ExecutionError, ProgramCounter};
7use ab_riscv_primitives::instructions::v::Vsew;
8use ab_riscv_primitives::prelude::*;
9use core::fmt;
10use core::hint::cold_path;
11use core::num::NonZeroU8;
12
13#[inline(always)]
16#[doc(hidden)]
17#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
18pub fn check_vd_widen_no_src_check<Reg, Memory, PC, CustomError>(
19 program_counter: &PC,
20 vd: VReg,
21 wide_group_regs: NonZeroU8,
22) -> Result<(), ExecutionError<Reg::Type, CustomError>>
23where
24 Reg: Register,
25 PC: ProgramCounter<Reg::Type, Memory, CustomError>,
26{
27 let wide_group_regs = wide_group_regs.get();
28 let vd_idx = vd.to_bits();
29 if !vd_idx.is_multiple_of(wide_group_regs) || vd_idx + wide_group_regs > 32 {
30 cold_path();
31 return Err(ExecutionError::IllegalInstruction {
32 address: program_counter.old_pc(INSTRUCTION_SIZE),
33 });
34 }
35 Ok(())
36}
37
38#[inline(always)]
47#[doc(hidden)]
48#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
49pub fn check_vs_ext_alignment<Reg, Memory, PC, CustomError>(
50 program_counter: &PC,
51 vs2: VReg,
52 src_group_regs: NonZeroU8,
53 vd: VReg,
54 group_regs: NonZeroU8,
55) -> Result<(), ExecutionError<Reg::Type, CustomError>>
56where
57 Reg: Register,
58 PC: ProgramCounter<Reg::Type, Memory, CustomError>,
59{
60 let src_group_regs = src_group_regs.get();
61 let group_regs = group_regs.get();
62 let vs2_idx = vs2.to_bits();
63 if !vs2_idx.is_multiple_of(src_group_regs) || vs2_idx + src_group_regs > 32 {
64 cold_path();
65 return Err(ExecutionError::IllegalInstruction {
66 address: program_counter.old_pc(INSTRUCTION_SIZE),
67 });
68 }
69 if widen_src_overlap_illegal(vd.to_bits(), group_regs, vs2_idx, src_group_regs) {
72 cold_path();
73 return Err(ExecutionError::IllegalInstruction {
74 address: program_counter.old_pc(INSTRUCTION_SIZE),
75 });
76 }
77 Ok(())
78}
79
80#[inline(always)]
93#[doc(hidden)]
94#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
95pub fn check_vd_widen_alignment<Reg, Memory, PC, CustomError>(
96 program_counter: &PC,
97 vd: VReg,
98 vs_a: VReg,
99 vs_b_opt: Option<VReg>,
100 group_regs: NonZeroU8,
101 wide_group_regs: NonZeroU8,
102) -> Result<(), ExecutionError<Reg::Type, CustomError>>
103where
104 Reg: Register,
105 PC: ProgramCounter<Reg::Type, Memory, CustomError>,
106{
107 let wide_group_regs = wide_group_regs.get();
108 let group_regs = group_regs.get();
109 let vd_idx = vd.to_bits();
110 if !vd_idx.is_multiple_of(wide_group_regs) || vd_idx + wide_group_regs > 32 {
111 cold_path();
112 return Err(ExecutionError::IllegalInstruction {
113 address: program_counter.old_pc(INSTRUCTION_SIZE),
114 });
115 }
116 if widen_src_overlap_illegal(vd_idx, wide_group_regs, vs_a.to_bits(), group_regs) {
117 cold_path();
118 return Err(ExecutionError::IllegalInstruction {
119 address: program_counter.old_pc(INSTRUCTION_SIZE),
120 });
121 }
122 if let Some(vs_b) = vs_b_opt
123 && widen_src_overlap_illegal(vd_idx, wide_group_regs, vs_b.to_bits(), group_regs)
124 {
125 cold_path();
126 return Err(ExecutionError::IllegalInstruction {
127 address: program_counter.old_pc(INSTRUCTION_SIZE),
128 });
129 }
130 Ok(())
131}
132
133#[inline(always)]
142#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
143fn widen_src_overlap_illegal(vd_idx: u8, wide_group_regs: u8, vs_idx: u8, group_regs: u8) -> bool {
144 if !ranges_overlap(vd_idx, wide_group_regs, vs_idx, group_regs) {
145 return false;
146 }
147 let high_part_overlap =
148 wide_group_regs > group_regs && vs_idx == vd_idx + wide_group_regs - group_regs;
149 !high_part_overlap
150}
151
152#[inline(always)]
155#[doc(hidden)]
156#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
157pub fn check_vs_wide_alignment<Reg, Memory, PC, CustomError>(
158 program_counter: &PC,
159 vs: VReg,
160 wide_group_regs: NonZeroU8,
161) -> Result<(), ExecutionError<Reg::Type, CustomError>>
162where
163 Reg: Register,
164 PC: ProgramCounter<Reg::Type, Memory, CustomError>,
165{
166 let wide_group_regs = wide_group_regs.get();
167 let vs_idx = vs.to_bits();
168 if !vs_idx.is_multiple_of(wide_group_regs) || vs_idx + wide_group_regs > 32 {
169 cold_path();
170 return Err(ExecutionError::IllegalInstruction {
171 address: program_counter.old_pc(INSTRUCTION_SIZE),
172 });
173 }
174 Ok(())
175}
176
177#[inline(always)]
183#[doc(hidden)]
184#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
185pub fn check_vd_narrow_alignment<Reg, Memory, PC, CustomError>(
186 program_counter: &PC,
187 vd: VReg,
188 group_regs: NonZeroU8,
189) -> Result<(), ExecutionError<Reg::Type, CustomError>>
190where
191 Reg: Register,
192 PC: ProgramCounter<Reg::Type, Memory, CustomError>,
193{
194 let group_regs = group_regs.get();
195 let vd_idx = vd.to_bits();
196 if !vd_idx.is_multiple_of(group_regs) || vd_idx + group_regs > 32 {
197 cold_path();
198 return Err(ExecutionError::IllegalInstruction {
199 address: program_counter.old_pc(INSTRUCTION_SIZE),
200 });
201 }
202 Ok(())
203}
204
205#[inline(always)]
207#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
208fn ranges_overlap(a_start: u8, a_len: u8, b_start: u8, b_len: u8) -> bool {
209 a_start < b_start + b_len && b_start < a_start + a_len
210}
211
212#[inline(always)]
214#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
215fn mask_bit(mask: &[u8], i: u16) -> bool {
216 mask.get(usize::from(i / u8::BITS as u16))
217 .is_some_and(|b| (b >> (i % u8::BITS as u16)) & 1 != 0)
218}
219
220#[inline(always)]
227#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
228unsafe fn snapshot_mask<const VLEN: Vlen>(
229 vregs: &VectorRegisterFile<VLEN>,
230 vm: bool,
231 vl: Vl,
232) -> [u8; VLENB_USIZE::<VLEN>] {
233 let mut buf = [0u8; _];
234 if vm {
235 buf = [0xffu8; _];
236 } else {
237 let mask_bytes = usize::from(vl.bytes());
238 unsafe {
240 buf.get_unchecked_mut(..mask_bytes)
241 .copy_from_slice(vregs.get(VReg::V0).get_unchecked(..mask_bytes));
242 }
243 }
244 buf
245}
246
247#[inline(always)]
253#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
254unsafe fn read_element_u64<const VLEN: Vlen>(
255 vregs: &VectorRegisterFile<VLEN>,
256 base_reg: VReg,
257 elem_i: u16,
258 sew: Vsew,
259) -> u64 {
260 let sew_bytes = u32::from(sew.bytes_width());
261 let elems_per_reg = VLEN.bytes() / sew_bytes;
262 let reg_off = u32::from(elem_i) / elems_per_reg;
263 let byte_off = (u32::from(elem_i) % elems_per_reg) * sew_bytes;
264 let reg = unsafe {
266 vregs.get(VReg::from_bits(base_reg.to_bits() + reg_off as u8).unwrap_unchecked())
267 };
268 let src = unsafe { reg.get_unchecked(byte_off as usize..(byte_off + sew_bytes) as usize) };
270 let mut buf = [0u8; 8];
271 unsafe { buf.get_unchecked_mut(..sew_bytes as usize) }.copy_from_slice(src);
273 u64::from_le_bytes(buf)
274}
275
276#[inline(always)]
281#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
282unsafe fn write_element_u64<const VLEN: Vlen>(
283 vregs: &mut VectorRegisterFile<VLEN>,
284 base_reg: VReg,
285 elem_i: u16,
286 sew: Vsew,
287 value: u64,
288) {
289 let sew_bytes = u32::from(sew.bytes_width());
290 let elems_per_reg = VLEN.bytes() / sew_bytes;
291 let reg_off = u32::from(elem_i) / elems_per_reg;
292 let byte_off = (u32::from(elem_i) % elems_per_reg) * sew_bytes;
293 let buf = value.to_le_bytes();
294 let reg = unsafe {
296 vregs.get_mut(VReg::from_bits(base_reg.to_bits() + reg_off as u8).unwrap_unchecked())
297 };
298 let dst = unsafe { reg.get_unchecked_mut(byte_off as usize..(byte_off + sew_bytes) as usize) };
300 dst.copy_from_slice(unsafe { buf.get_unchecked(..sew_bytes as usize) });
302}
303
304#[inline(always)]
306#[doc(hidden)]
307#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
308pub fn sign_extend_bits(val: u64, bits: u8) -> i64 {
309 let shift = u64::BITS - u32::from(bits);
310 (val.cast_signed() << shift) >> shift
311}
312
313#[inline(always)]
334#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
335fn scalar_unsigned_for_sew(val: u64, sew_bits: u8) -> u64 {
336 val & (u64::MAX >> (u64::BITS - u32::from(sew_bits)))
337}
338
339#[inline(always)]
361#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
362fn scalar_signed_for_sew(val: u64, sew_bits: u8) -> u64 {
363 sign_extend_bits(val, sew_bits).cast_unsigned()
364}
365
366#[inline(always)]
384#[doc(hidden)]
385pub unsafe fn execute_widen_op<const ZERO_EXTEND_AB: bool, Reg, ExtState, CustomError, F>(
387 ext_state: &mut ExtState,
388 vd: VReg,
389 vs2: VReg,
390 src: OpSrc,
391 vm: bool,
392 sew: Vsew,
393 op: F,
394) where
395 Reg: Register,
396 ExtState: VectorRegistersExt<Reg, CustomError>,
397 [(); SUPPORTED_ELEN_VLEN::<{ ExtState::ELEN }, { ExtState::VLEN }>]:,
398 CustomError: fmt::Debug,
399 F: Fn(u64, u64) -> u64,
400{
401 let vl = ext_state.vl();
402 let vstart = ext_state.vstart();
403 let wide_sew = sew
404 .double_width()
405 .expect("SEW < 64 is enforced by caller, hence this is always valid; qed");
406
407 let mask_buf = unsafe { snapshot_mask(ext_state.read_vregs(), vm, vl) };
409
410 for i in vstart.range_to(vl) {
411 if !mask_bit(&mask_buf, i) {
412 continue;
413 }
414 let raw_a = unsafe { read_element_u64(ext_state.read_vregs(), vs2, i, sew) };
417 let wide_a = if ZERO_EXTEND_AB {
418 raw_a
419 } else {
420 sign_extend_bits(raw_a, sew.bits_width()).cast_unsigned()
421 };
422 let wide_b = match src {
423 OpSrc::Vreg(vs1_base) => {
424 let raw_b = unsafe { read_element_u64(ext_state.read_vregs(), vs1_base, i, sew) };
426 if ZERO_EXTEND_AB {
427 raw_b
428 } else {
429 sign_extend_bits(raw_b, sew.bits_width()).cast_unsigned()
430 }
431 }
432 OpSrc::Scalar(val) => {
433 if ZERO_EXTEND_AB {
434 scalar_unsigned_for_sew(val, sew.bits_width())
435 } else {
436 scalar_signed_for_sew(val, sew.bits_width())
437 }
438 }
439 };
440 let result = op(wide_a, wide_b);
441 unsafe {
446 write_element_u64(ext_state.write_vregs(), vd, i, wide_sew, result);
447 }
448 }
449 ext_state.mark_vs_dirty();
450 ext_state.reset_vstart();
451}
452
453#[inline(always)]
466#[doc(hidden)]
467pub unsafe fn execute_widen_w_op<const ZERO_EXTEND_B: bool, Reg, ExtState, CustomError, F>(
469 ext_state: &mut ExtState,
470 vd: VReg,
471 vs2: VReg,
472 src: OpSrc,
473 vm: bool,
474 sew: Vsew,
475 op: F,
476) where
477 Reg: Register,
478 ExtState: VectorRegistersExt<Reg, CustomError>,
479 [(); SUPPORTED_ELEN_VLEN::<{ ExtState::ELEN }, { ExtState::VLEN }>]:,
480 CustomError: fmt::Debug,
481 F: Fn(u64, u64) -> u64,
482{
483 let vl = ext_state.vl();
484 let vstart = ext_state.vstart();
485 let wide_sew = sew
486 .double_width()
487 .expect("SEW < 64 is enforced by caller, hence this is always valid; qed");
488
489 let mask_buf = unsafe { snapshot_mask(ext_state.read_vregs(), vm, vl) };
491
492 for i in vstart.range_to(vl) {
493 if !mask_bit(&mask_buf, i) {
494 continue;
495 }
496 let wide_a = unsafe { read_element_u64(ext_state.read_vregs(), vs2, i, wide_sew) };
499 let wide_b = match src {
500 OpSrc::Vreg(vs1) => {
501 let raw_b = unsafe { read_element_u64(ext_state.read_vregs(), vs1, i, sew) };
505 if ZERO_EXTEND_B {
506 raw_b
507 } else {
508 sign_extend_bits(raw_b, sew.bits_width()).cast_unsigned()
509 }
510 }
511 OpSrc::Scalar(val) => {
512 if ZERO_EXTEND_B {
513 scalar_unsigned_for_sew(val, sew.bits_width())
514 } else {
515 scalar_signed_for_sew(val, sew.bits_width())
516 }
517 }
518 };
519 let result = op(wide_a, wide_b);
520 unsafe {
522 write_element_u64(ext_state.write_vregs(), vd, i, wide_sew, result);
523 }
524 }
525 ext_state.mark_vs_dirty();
526 ext_state.reset_vstart();
527}
528
529#[inline(always)]
545#[doc(hidden)]
546pub unsafe fn execute_narrow_shift<const ARITHMETIC: bool, Reg, ExtState, CustomError>(
548 ext_state: &mut ExtState,
549 vd: VReg,
550 vs2: VReg,
551 src: OpSrc,
552 vm: bool,
553 sew: Vsew,
554) where
555 Reg: Register,
556 ExtState: VectorRegistersExt<Reg, CustomError>,
557 [(); SUPPORTED_ELEN_VLEN::<{ ExtState::ELEN }, { ExtState::VLEN }>]:,
558 CustomError: fmt::Debug,
559{
560 let vl = ext_state.vl();
561 let vstart = ext_state.vstart();
562 let wide_sew = sew
563 .double_width()
564 .expect("SEW < 64 is enforced by caller, hence this is always valid; qed");
565 let shamt_mask = u64::from(wide_sew.bits_width() - 1);
567
568 let mask_buf = unsafe { snapshot_mask(ext_state.read_vregs(), vm, vl) };
570
571 for i in vstart.range_to(vl) {
572 if !mask_bit(&mask_buf, i) {
573 continue;
574 }
575 let wide_val = unsafe { read_element_u64(ext_state.read_vregs(), vs2, i, wide_sew) };
577 let shamt = match src {
578 OpSrc::Vreg(vs1_base) => {
579 let raw = unsafe { read_element_u64(ext_state.read_vregs(), vs1_base, i, sew) };
583 raw & shamt_mask
584 }
585 OpSrc::Scalar(val) => val & shamt_mask,
587 };
588 let result_wide = if ARITHMETIC {
589 (sign_extend_bits(wide_val, wide_sew.bits_width()) >> shamt).cast_unsigned()
593 } else {
594 wide_val >> shamt
595 };
596 let result = result_wide & ((1u64 << sew.bits_width()) - 1);
598 unsafe {
600 write_element_u64(ext_state.write_vregs(), vd, i, sew, result);
601 }
602 }
603 ext_state.mark_vs_dirty();
604 ext_state.reset_vstart();
605}
606
607#[inline(always)]
622#[doc(hidden)]
623pub unsafe fn execute_extension<const SIGN: bool, Reg, ExtState, CustomError>(
625 ext_state: &mut ExtState,
626 vd: VReg,
627 vs2: VReg,
628 vm: bool,
629 sew: Vsew,
630 factor: VsewFactor,
631) where
632 Reg: Register,
633 ExtState: VectorRegistersExt<Reg, CustomError>,
634 [(); SUPPORTED_ELEN_VLEN::<{ ExtState::ELEN }, { ExtState::VLEN }>]:,
635 CustomError: fmt::Debug,
636{
637 let vl = ext_state.vl();
638 let vstart = ext_state.vstart();
639 let src_sew = sew
640 .divide_by_factor(factor)
641 .expect("SEW >= factor*8 and valid according to function contract; qed");
642
643 let mask_buf = unsafe { snapshot_mask(ext_state.read_vregs(), vm, vl) };
645
646 for i in vstart.range_to(vl) {
647 if !mask_bit(&mask_buf, i) {
648 continue;
649 }
650 let raw = unsafe { read_element_u64(ext_state.read_vregs(), vs2, i, src_sew) };
652 let result = if SIGN {
653 sign_extend_bits(raw, src_sew.bits_width()).cast_unsigned()
654 } else {
655 raw
656 };
657 unsafe {
659 write_element_u64(ext_state.write_vregs(), vd, i, sew, result);
660 }
661 }
662 ext_state.mark_vs_dirty();
663 ext_state.reset_vstart();
664}