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, PackedAddress, ProgramCounter};
7use ab_riscv_primitives::instructions::v::Vsew;
8use ab_riscv_primitives::prelude::*;
9use core::hint::cold_path;
10use core::num::NonZeroU8;
11
12#[inline(always)]
15#[doc(hidden)]
16#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
17pub fn check_vd_widen_no_src_check<Reg, Memory, PC>(
18 program_counter: &PC,
19 vd: VReg,
20 wide_group_regs: NonZeroU8,
21) -> Result<(), ExecutionError<Reg::Type>>
22where
23 Reg: Register,
24 PC: ProgramCounter<Reg::Type, Memory>,
25{
26 let wide_group_regs = wide_group_regs.get();
27 let vd_idx = vd.to_bits();
28 if !vd_idx.is_multiple_of(wide_group_regs) || vd_idx + wide_group_regs > 32 {
29 cold_path();
30 return Err(ExecutionError::IllegalInstruction {
31 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
32 });
33 }
34 Ok(())
35}
36
37#[inline(always)]
46#[doc(hidden)]
47#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
48pub fn check_vs_ext_alignment<Reg, Memory, PC>(
49 program_counter: &PC,
50 vs2: VReg,
51 src_group_regs: NonZeroU8,
52 vd: VReg,
53 group_regs: NonZeroU8,
54) -> Result<(), ExecutionError<Reg::Type>>
55where
56 Reg: Register,
57 PC: ProgramCounter<Reg::Type, Memory>,
58{
59 let src_group_regs = src_group_regs.get();
60 let group_regs = group_regs.get();
61 let vs2_idx = vs2.to_bits();
62 if !vs2_idx.is_multiple_of(src_group_regs) || vs2_idx + src_group_regs > 32 {
63 cold_path();
64 return Err(ExecutionError::IllegalInstruction {
65 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
66 });
67 }
68 if widen_src_overlap_illegal(vd.to_bits(), group_regs, vs2_idx, src_group_regs) {
71 cold_path();
72 return Err(ExecutionError::IllegalInstruction {
73 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
74 });
75 }
76 Ok(())
77}
78
79#[inline(always)]
92#[doc(hidden)]
93#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
94pub fn check_vd_widen_alignment<Reg, Memory, PC>(
95 program_counter: &PC,
96 vd: VReg,
97 vs_a: VReg,
98 vs_b_opt: Option<VReg>,
99 group_regs: NonZeroU8,
100 wide_group_regs: NonZeroU8,
101) -> Result<(), ExecutionError<Reg::Type>>
102where
103 Reg: Register,
104 PC: ProgramCounter<Reg::Type, Memory>,
105{
106 let wide_group_regs = wide_group_regs.get();
107 let group_regs = group_regs.get();
108 let vd_idx = vd.to_bits();
109 if !vd_idx.is_multiple_of(wide_group_regs) || vd_idx + wide_group_regs > 32 {
110 cold_path();
111 return Err(ExecutionError::IllegalInstruction {
112 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
113 });
114 }
115 if widen_src_overlap_illegal(vd_idx, wide_group_regs, vs_a.to_bits(), group_regs) {
116 cold_path();
117 return Err(ExecutionError::IllegalInstruction {
118 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
119 });
120 }
121 if let Some(vs_b) = vs_b_opt
122 && widen_src_overlap_illegal(vd_idx, wide_group_regs, vs_b.to_bits(), group_regs)
123 {
124 cold_path();
125 return Err(ExecutionError::IllegalInstruction {
126 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
127 });
128 }
129 Ok(())
130}
131
132#[inline(always)]
141#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
142fn widen_src_overlap_illegal(vd_idx: u8, wide_group_regs: u8, vs_idx: u8, group_regs: u8) -> bool {
143 if !ranges_overlap(vd_idx, wide_group_regs, vs_idx, group_regs) {
144 return false;
145 }
146 let high_part_overlap =
147 wide_group_regs > group_regs && vs_idx == vd_idx + wide_group_regs - group_regs;
148 !high_part_overlap
149}
150
151#[inline(always)]
154#[doc(hidden)]
155#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
156pub fn check_vs_wide_alignment<Reg, Memory, PC>(
157 program_counter: &PC,
158 vs: VReg,
159 wide_group_regs: NonZeroU8,
160) -> Result<(), ExecutionError<Reg::Type>>
161where
162 Reg: Register,
163 PC: ProgramCounter<Reg::Type, Memory>,
164{
165 let wide_group_regs = wide_group_regs.get();
166 let vs_idx = vs.to_bits();
167 if !vs_idx.is_multiple_of(wide_group_regs) || vs_idx + wide_group_regs > 32 {
168 cold_path();
169 return Err(ExecutionError::IllegalInstruction {
170 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
171 });
172 }
173 Ok(())
174}
175
176#[inline(always)]
182#[doc(hidden)]
183#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
184pub fn check_vd_narrow_alignment<Reg, Memory, PC>(
185 program_counter: &PC,
186 vd: VReg,
187 group_regs: NonZeroU8,
188) -> Result<(), ExecutionError<Reg::Type>>
189where
190 Reg: Register,
191 PC: ProgramCounter<Reg::Type, Memory>,
192{
193 let group_regs = group_regs.get();
194 let vd_idx = vd.to_bits();
195 if !vd_idx.is_multiple_of(group_regs) || vd_idx + group_regs > 32 {
196 cold_path();
197 return Err(ExecutionError::IllegalInstruction {
198 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
199 });
200 }
201 Ok(())
202}
203
204#[inline(always)]
206#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
207fn ranges_overlap(a_start: u8, a_len: u8, b_start: u8, b_len: u8) -> bool {
208 a_start < b_start + b_len && b_start < a_start + a_len
209}
210
211#[inline(always)]
213#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
214fn mask_bit(mask: &[u8], i: u16) -> bool {
215 mask.get(usize::from(i / u8::BITS as u16))
216 .is_some_and(|b| (b >> (i % u8::BITS as u16)) & 1 != 0)
217}
218
219#[inline(always)]
226#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
227unsafe fn snapshot_mask<const VLEN: Vlen>(
228 vregs: &VectorRegisterFile<VLEN>,
229 vm: bool,
230 vl: Vl,
231) -> [u8; VLENB_USIZE::<VLEN>] {
232 let mut buf = [0u8; _];
233 if vm {
234 buf = [0xffu8; _];
235 } else {
236 let mask_bytes = usize::from(vl.bytes());
237 unsafe {
239 buf.get_unchecked_mut(..mask_bytes)
240 .copy_from_slice(vregs.get(VReg::V0).get_unchecked(..mask_bytes));
241 }
242 }
243 buf
244}
245
246#[inline(always)]
252#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
253unsafe fn read_element_u64<const VLEN: Vlen>(
254 vregs: &VectorRegisterFile<VLEN>,
255 base_reg: VReg,
256 elem_i: u16,
257 sew: Vsew,
258) -> u64 {
259 let sew_bytes = u32::from(sew.bytes_width());
260 let elems_per_reg = VLEN.bytes() / sew_bytes;
261 let reg_off = u32::from(elem_i) / elems_per_reg;
262 let byte_off = (u32::from(elem_i) % elems_per_reg) * sew_bytes;
263 let reg = unsafe {
265 vregs.get(VReg::from_bits(base_reg.to_bits() + reg_off as u8).unwrap_unchecked())
266 };
267 let src = unsafe { reg.get_unchecked(byte_off as usize..(byte_off + sew_bytes) as usize) };
269 let mut buf = [0u8; 8];
270 unsafe { buf.get_unchecked_mut(..sew_bytes as usize) }.copy_from_slice(src);
272 u64::from_le_bytes(buf)
273}
274
275#[inline(always)]
280#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
281unsafe fn write_element_u64<const VLEN: Vlen>(
282 vregs: &mut VectorRegisterFile<VLEN>,
283 base_reg: VReg,
284 elem_i: u16,
285 sew: Vsew,
286 value: u64,
287) {
288 let sew_bytes = u32::from(sew.bytes_width());
289 let elems_per_reg = VLEN.bytes() / sew_bytes;
290 let reg_off = u32::from(elem_i) / elems_per_reg;
291 let byte_off = (u32::from(elem_i) % elems_per_reg) * sew_bytes;
292 let buf = value.to_le_bytes();
293 let reg = unsafe {
295 vregs.get_mut(VReg::from_bits(base_reg.to_bits() + reg_off as u8).unwrap_unchecked())
296 };
297 let dst = unsafe { reg.get_unchecked_mut(byte_off as usize..(byte_off + sew_bytes) as usize) };
299 dst.copy_from_slice(unsafe { buf.get_unchecked(..sew_bytes as usize) });
301}
302
303#[inline(always)]
305#[doc(hidden)]
306#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
307pub fn sign_extend_bits(val: u64, sew: Vsew) -> i64 {
308 let shift = u64::BITS - u32::from(sew.bits_width());
309 (val.cast_signed() << shift) >> shift
310}
311
312#[inline(always)]
333#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
334fn scalar_unsigned_for_sew(val: u64, sew: Vsew) -> u64 {
335 val & (u64::MAX >> (u64::BITS - u32::from(sew.bits_width())))
336}
337
338#[inline(always)]
360#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
361fn scalar_signed_for_sew(val: u64, sew: Vsew) -> u64 {
362 sign_extend_bits(val, sew).cast_unsigned()
363}
364
365#[inline(always)]
383#[doc(hidden)]
384#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
385pub unsafe fn execute_widen_op<const ZERO_EXTEND_AB: bool, Reg, Env, F>(
386 env: &mut Env,
387 vd: VReg,
388 vs2: VReg,
389 src: OpSrc,
390 vm: bool,
391 sew: Vsew,
392 op: F,
393) where
394 Reg: Register,
395 Env: VectorRegistersExt<Reg>,
396 [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
397 F: Fn(u64, u64) -> u64,
398{
399 let vl = env.vl();
400 let vstart = env.vstart();
401 let wide_sew = unsafe { sew.double_width().unwrap_unchecked() };
403
404 let mask_buf = unsafe { snapshot_mask(env.read_vregs(), vm, vl) };
406
407 for i in vstart.range_to(vl) {
408 if !mask_bit(&mask_buf, i) {
409 continue;
410 }
411 let raw_a = unsafe { read_element_u64(env.read_vregs(), vs2, i, sew) };
414 let wide_a = if ZERO_EXTEND_AB {
415 raw_a
416 } else {
417 sign_extend_bits(raw_a, sew).cast_unsigned()
418 };
419 let wide_b = match src {
420 OpSrc::Vreg(vs1_base) => {
421 let raw_b = unsafe { read_element_u64(env.read_vregs(), vs1_base, i, sew) };
423 if ZERO_EXTEND_AB {
424 raw_b
425 } else {
426 sign_extend_bits(raw_b, sew).cast_unsigned()
427 }
428 }
429 OpSrc::Scalar(val) => {
430 if ZERO_EXTEND_AB {
431 scalar_unsigned_for_sew(val, sew)
432 } else {
433 scalar_signed_for_sew(val, sew)
434 }
435 }
436 };
437 let result = op(wide_a, wide_b);
438 unsafe {
443 write_element_u64(env.write_vregs(), vd, i, wide_sew, result);
444 }
445 }
446 env.mark_vs_dirty();
447 env.reset_vstart();
448}
449
450#[inline(always)]
463#[doc(hidden)]
464#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
465pub unsafe fn execute_widen_w_op<const ZERO_EXTEND_B: bool, Reg, Env, F>(
466 env: &mut Env,
467 vd: VReg,
468 vs2: VReg,
469 src: OpSrc,
470 vm: bool,
471 sew: Vsew,
472 op: F,
473) where
474 Reg: Register,
475 Env: VectorRegistersExt<Reg>,
476 [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
477 F: Fn(u64, u64) -> u64,
478{
479 let vl = env.vl();
480 let vstart = env.vstart();
481 let wide_sew = unsafe { sew.double_width().unwrap_unchecked() };
483
484 let mask_buf = unsafe { snapshot_mask(env.read_vregs(), vm, vl) };
486
487 for i in vstart.range_to(vl) {
488 if !mask_bit(&mask_buf, i) {
489 continue;
490 }
491 let wide_a = unsafe { read_element_u64(env.read_vregs(), vs2, i, wide_sew) };
494 let wide_b = match src {
495 OpSrc::Vreg(vs1) => {
496 let raw_b = unsafe { read_element_u64(env.read_vregs(), vs1, i, sew) };
500 if ZERO_EXTEND_B {
501 raw_b
502 } else {
503 sign_extend_bits(raw_b, sew).cast_unsigned()
504 }
505 }
506 OpSrc::Scalar(val) => {
507 if ZERO_EXTEND_B {
508 scalar_unsigned_for_sew(val, sew)
509 } else {
510 scalar_signed_for_sew(val, sew)
511 }
512 }
513 };
514 let result = op(wide_a, wide_b);
515 unsafe {
517 write_element_u64(env.write_vregs(), vd, i, wide_sew, result);
518 }
519 }
520 env.mark_vs_dirty();
521 env.reset_vstart();
522}
523
524#[inline(always)]
540#[doc(hidden)]
541#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
542pub unsafe fn execute_narrow_shift<const ARITHMETIC: bool, Reg, Env>(
543 env: &mut Env,
544 vd: VReg,
545 vs2: VReg,
546 src: OpSrc,
547 vm: bool,
548 sew: Vsew,
549) where
550 Reg: Register,
551 Env: VectorRegistersExt<Reg>,
552 [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
553{
554 let vl = env.vl();
555 let vstart = env.vstart();
556 let wide_sew = unsafe { sew.double_width().unwrap_unchecked() };
558 let shamt_mask = u64::from(wide_sew.bits_width() - 1);
560
561 let mask_buf = unsafe { snapshot_mask(env.read_vregs(), vm, vl) };
563
564 for i in vstart.range_to(vl) {
565 if !mask_bit(&mask_buf, i) {
566 continue;
567 }
568 let wide_val = unsafe { read_element_u64(env.read_vregs(), vs2, i, wide_sew) };
570 let shamt = match src {
571 OpSrc::Vreg(vs1_base) => {
572 let raw = unsafe { read_element_u64(env.read_vregs(), vs1_base, i, sew) };
576 raw & shamt_mask
577 }
578 OpSrc::Scalar(val) => val & shamt_mask,
580 };
581 let result_wide = if ARITHMETIC {
582 (sign_extend_bits(wide_val, wide_sew) >> shamt).cast_unsigned()
586 } else {
587 wide_val >> shamt
588 };
589 let result = result_wide & ((1u64 << sew.bits_width()) - 1);
591 unsafe {
593 write_element_u64(env.write_vregs(), vd, i, sew, result);
594 }
595 }
596 env.mark_vs_dirty();
597 env.reset_vstart();
598}
599
600#[inline(always)]
615#[doc(hidden)]
616#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
617pub unsafe fn execute_extension<const SIGN: bool, Reg, Env>(
618 env: &mut Env,
619 vd: VReg,
620 vs2: VReg,
621 vm: bool,
622 sew: Vsew,
623 factor: VsewFactor,
624) where
625 Reg: Register,
626 Env: VectorRegistersExt<Reg>,
627 [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
628{
629 let vl = env.vl();
630 let vstart = env.vstart();
631 let src_sew = unsafe { sew.divide_by_factor(factor).unwrap_unchecked() };
633
634 let mask_buf = unsafe { snapshot_mask(env.read_vregs(), vm, vl) };
636
637 for i in vstart.range_to(vl) {
638 if !mask_bit(&mask_buf, i) {
639 continue;
640 }
641 let raw = unsafe { read_element_u64(env.read_vregs(), vs2, i, src_sew) };
643 let result = if SIGN {
644 sign_extend_bits(raw, src_sew).cast_unsigned()
645 } else {
646 raw
647 };
648 unsafe {
650 write_element_u64(env.write_vregs(), vd, i, sew, result);
651 }
652 }
653 env.mark_vs_dirty();
654 env.reset_vstart();
655}