1use crate::v::vector_registers::{VectorRegisterFile, VectorRegistersExt};
4pub use crate::v::zvexx::arith::zvexx_arith_helpers::{
5 OpSrc, check_vreg_group_alignment, sew_mask,
6};
7use crate::v::zvexx::arith::zvexx_arith_helpers::{
8 read_element_u64, sign_extend, write_element_u64,
9};
10use crate::v::zvexx::load::zvexx_load_helpers::{mask_bit, snapshot_mask};
11use crate::v::zvexx::zvexx_helpers::INSTRUCTION_SIZE;
12use crate::{ExecutionError, PackedAddress, ProgramCounter};
13use ab_riscv_primitives::prelude::*;
14use core::hint::cold_path;
15
16#[inline(always)]
21#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
22fn round_increment(val: u64, shift: u32, mode: Vxrm, current_result_lsb: u64) -> u64 {
23 if shift == 0 {
24 return 0;
25 }
26 let d_minus1_bit = (val >> (shift - 1)) & 1;
28 let sticky = if shift >= 2 {
30 (val & ((1u64 << (shift - 1)).wrapping_sub(1))) != 0
32 } else {
33 false
34 };
35 match mode {
36 Vxrm::Rnu => d_minus1_bit,
38 Vxrm::Rne => d_minus1_bit & u64::from(sticky || current_result_lsb != 0),
40 Vxrm::Rdn => 0,
42 Vxrm::Rod => u64::from(current_result_lsb == 0 && (d_minus1_bit != 0 || sticky)),
44 }
45}
46
47#[inline(always)]
51#[doc(hidden)]
52#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
53pub fn rounded_srl(val: u64, shift: u32, mode: Vxrm) -> u64 {
54 let truncated = val >> shift;
55 let r = round_increment(val, shift, mode, truncated & 1);
56 truncated.wrapping_add(r)
57}
58
59#[inline(always)]
63#[doc(hidden)]
64#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
65pub fn rounded_sra(val: u64, shift: u32, mode: Vxrm, sew: Vsew) -> u64 {
66 let signed = sign_extend(val, sew);
67 let truncated_signed = signed >> shift;
71 let r = round_increment(val, shift, mode, truncated_signed.cast_unsigned() & 1);
72 truncated_signed.cast_unsigned().wrapping_add(r)
73}
74
75#[inline(always)]
79#[doc(hidden)]
80#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
81pub fn sat_addu(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
82 let mask = sew_mask(sew);
83 let a_w = a & mask;
84 let b_w = b & mask;
85 let result = a_w.wrapping_add(b_w);
86 if result & mask < a_w {
87 *vxsat = true;
89 mask
90 } else {
91 result & mask
92 }
93}
94
95#[inline(always)]
99#[doc(hidden)]
100#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
101pub fn sat_add(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
102 let sa = i128::from(sign_extend(a, sew));
103 let sb = i128::from(sign_extend(b, sew));
104 let result = sa.wrapping_add(sb);
105 let min_val = i128::MIN >> (i128::BITS - u32::from(sew.bits_width()));
106 let max_val = i128::MAX >> (i128::BITS - u32::from(sew.bits_width()));
107 if result < min_val {
108 *vxsat = true;
109 (min_val as i64).cast_unsigned() & sew_mask(sew)
110 } else if result > max_val {
111 *vxsat = true;
112 (max_val as i64).cast_unsigned() & sew_mask(sew)
113 } else {
114 (result as i64).cast_unsigned() & sew_mask(sew)
115 }
116}
117
118#[inline(always)]
122#[doc(hidden)]
123#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
124pub fn sat_subu(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
125 let mask = sew_mask(sew);
126 let a_w = a & mask;
127 let b_w = b & mask;
128 if a_w < b_w {
129 *vxsat = true;
130 0
131 } else {
132 (a_w - b_w) & mask
133 }
134}
135
136#[inline(always)]
140#[doc(hidden)]
141#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
142pub fn sat_sub(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
143 let sa = i128::from(sign_extend(a, sew));
144 let sb = i128::from(sign_extend(b, sew));
145 let result = sa.wrapping_sub(sb);
146 let min_val = i128::MIN >> (i128::BITS - u32::from(sew.bits_width()));
147 let max_val = i128::MAX >> (i128::BITS - u32::from(sew.bits_width()));
148 if result < min_val {
149 *vxsat = true;
150 (min_val as i64).cast_unsigned() & sew_mask(sew)
151 } else if result > max_val {
152 *vxsat = true;
153 (max_val as i64).cast_unsigned() & sew_mask(sew)
154 } else {
155 (result as i64).cast_unsigned() & sew_mask(sew)
156 }
157}
158
159#[inline(always)]
163#[doc(hidden)]
164#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
165pub fn avg_addu(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
166 let mask = sew_mask(sew);
167 let a_w = a & mask;
168 let b_w = b & mask;
169 let sum = a_w.wrapping_add(b_w);
173 let carry = u64::from(sum & mask < a_w);
175 let r = round_increment(sum & mask, 1, mode, (sum >> 1u8) & 1);
179 let shifted = (carry << (u32::from(sew.bits_width()) - 1)) | ((sum & mask) >> 1u8);
181 (shifted.wrapping_add(r)) & mask
182}
183
184#[inline(always)]
188#[doc(hidden)]
189#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
190pub fn avg_add(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
191 let sa = sign_extend(a, sew);
192 let sb = sign_extend(b, sew);
193 let sum = i128::from(sa).wrapping_add(i128::from(sb));
195 let r = match mode {
197 Vxrm::Rnu => (sum & 1).cast_unsigned() as u64,
198 Vxrm::Rne => {
199 let result_lsb = ((sum >> 1u8) & 1).cast_unsigned() as u64;
202 ((sum & 1).cast_unsigned() as u64) & result_lsb
203 }
204 Vxrm::Rdn => 0,
205 Vxrm::Rod => {
206 let result_lsb = (sum >> 1u8) & 1;
208 u64::from(result_lsb == 0 && (sum & 1) != 0)
209 }
210 };
211 let result = (sum >> 1u8) + i128::from(r);
212 (result as i64).cast_unsigned() & sew_mask(sew)
213}
214
215#[inline(always)]
219#[doc(hidden)]
220#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
221pub fn avg_subu(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
222 let mask = sew_mask(sew);
223 let a_w = a & mask;
224 let b_w = b & mask;
225 let diff = a_w.wrapping_sub(b_w);
227 let borrow = u64::from(a_w < b_w);
229 let r = round_increment(diff & mask, 1, mode, (diff >> 1u8) & 1);
233 let sign_fill = borrow.wrapping_neg(); let shifted = (sign_fill << (u32::from(sew.bits_width()) - 1)) | ((diff & mask) >> 1u8);
240 (shifted.wrapping_add(r)) & mask
241}
242
243#[inline(always)]
247#[doc(hidden)]
248#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
249pub fn avg_sub(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
250 let sa = sign_extend(a, sew);
251 let sb = sign_extend(b, sew);
252 let diff = i128::from(sa).wrapping_sub(i128::from(sb));
253 let r = match mode {
254 Vxrm::Rnu => (diff & 1).cast_unsigned() as u64,
255 Vxrm::Rne => {
256 let result_lsb = ((diff >> 1u8) & 1).cast_unsigned() as u64;
257 ((diff & 1).cast_unsigned() as u64) & result_lsb
258 }
259 Vxrm::Rdn => 0,
260 Vxrm::Rod => {
261 let result_lsb = (diff >> 1u8) & 1;
262 u64::from(result_lsb == 0 && (diff & 1) != 0)
263 }
264 };
265 let result = (diff >> 1u8) + i128::from(r);
266 (result as i64).cast_unsigned() & sew_mask(sew)
267}
268
269#[inline(always)]
277#[doc(hidden)]
278#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
279pub fn smul(a: u64, b: u64, sew: Vsew, mode: Vxrm, vxsat: &mut bool) -> u64 {
280 let min_sew = i64::MIN >> (i64::BITS - u32::from(sew.bits_width()));
282 let max_sew = i64::MAX >> (i64::BITS - u32::from(sew.bits_width()));
283 let sa = i128::from(sign_extend(a, sew));
284 let sb = i128::from(sign_extend(b, sew));
285 if sa == i128::from(min_sew) && sb == i128::from(min_sew) {
288 cold_path();
289 *vxsat = true;
290 return max_sew.cast_unsigned() & sew_mask(sew);
291 }
292 let product = sa * sb;
295 let doubled = product << 1u8;
298 let shift = u32::from(sew.bits_width());
301 let low_bits = (doubled.cast_unsigned() & u128::from(sew_mask(sew))) as u64;
302 let truncated = doubled >> shift;
304 let r = round_increment(
305 low_bits,
306 shift.min(64),
307 mode,
308 (truncated.cast_unsigned() as u64) & 1,
309 );
310 let result = (truncated as i64).wrapping_add(r.cast_signed());
312 if result < min_sew {
314 *vxsat = true;
315 min_sew.cast_unsigned() & sew_mask(sew)
316 } else if result > max_sew {
317 *vxsat = true;
318 max_sew.cast_unsigned() & sew_mask(sew)
319 } else {
320 result.cast_unsigned() & sew_mask(sew)
321 }
322}
323
324#[inline(always)]
336#[doc(hidden)]
337#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
338pub fn nclipu(vs2_elem: u64, shamt: u32, sew: Vsew, mode: Vxrm, vxsat: &mut bool) -> u64 {
339 let shifted = rounded_srl(vs2_elem, shamt, mode);
341 let max_dst = sew_mask(sew);
343 if shifted > max_dst {
344 *vxsat = true;
345 max_dst
346 } else {
347 shifted & max_dst
348 }
349}
350
351#[inline(always)]
356#[doc(hidden)]
357#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
358pub fn nclip(vs2_elem: u64, shamt: u32, sew: Vsew, mode: Vxrm, vxsat: &mut bool) -> u64 {
359 let double_sew_bits = sew.bits_width() * 2;
363 let shift_amt = i64::BITS - u32::from(double_sew_bits);
364 let signed_wide = (vs2_elem.cast_signed() << shift_amt) >> shift_amt;
365 let low_bits = signed_wide.cast_unsigned()
368 & if double_sew_bits == 64 {
369 u64::MAX
370 } else {
371 (1u64 << double_sew_bits) - 1
372 };
373 let truncated = signed_wide >> shamt;
374 let r = round_increment(low_bits, shamt, mode, (truncated.cast_unsigned()) & 1);
375 let rounded = truncated.wrapping_add(r.cast_signed());
376 let min_dst = i64::MIN >> (i64::BITS - u32::from(sew.bits_width()));
378 let max_dst = i64::MAX >> (i64::BITS - u32::from(sew.bits_width()));
379 if rounded < min_dst {
380 *vxsat = true;
381 min_dst.cast_unsigned() & sew_mask(sew)
382 } else if rounded > max_dst {
383 *vxsat = true;
384 max_dst.cast_unsigned() & sew_mask(sew)
385 } else {
386 rounded.cast_unsigned() & sew_mask(sew)
387 }
388}
389
390#[inline(always)]
402#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
403pub unsafe fn read_wide_element_u64<const VLEN: Vlen>(
404 vregs: &VectorRegisterFile<VLEN>,
405 base_reg: VReg,
406 elem_i: u16,
407 sew: Vsew,
408) -> u64 {
409 let double_sew_bytes = u32::from(sew.bytes_width()) * 2;
410 let elems_per_reg = VLEN.bytes() / double_sew_bytes;
411 let reg_off = u32::from(elem_i) / elems_per_reg;
412 let byte_off = (u32::from(elem_i) % elems_per_reg) * double_sew_bytes;
413 let reg = unsafe {
415 vregs.get(VReg::from_bits(base_reg.to_bits() + reg_off as u8).unwrap_unchecked())
416 };
417 let src =
419 unsafe { reg.get_unchecked(byte_off as usize..(byte_off + double_sew_bytes) as usize) };
420 let mut buf = [0u8; 8];
421 unsafe { buf.get_unchecked_mut(..double_sew_bytes as usize) }.copy_from_slice(src);
423 u64::from_le_bytes(buf)
424}
425
426#[inline(always)]
438#[doc(hidden)]
439#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
440pub unsafe fn execute_fixed_point_op<Reg, Env, F>(
441 env: &mut Env,
442 vd: VReg,
443 vs2: VReg,
444 src: OpSrc,
445 vm: bool,
446 sew: Vsew,
447 op: F,
448) where
449 Reg: Register,
450 Env: VectorRegistersExt<Reg>,
451 [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
452 F: Fn(u64, u64, Vsew, Vxrm, &mut bool) -> u64,
454{
455 let vl = env.vl();
456 let vstart = env.vstart();
457 let vxrm = env.vxrm();
458 let mask_buf = unsafe { snapshot_mask(env.read_vregs(), vm, vl) };
460 let mut any_sat = false;
461 for i in vstart.range_to(vl) {
462 if !mask_bit(&mask_buf, i) {
463 continue;
464 }
465 let a = unsafe { read_element_u64(env.read_vregs(), vs2, i, sew) };
467 let b = match src {
468 OpSrc::Vreg(vs1_base) => {
469 unsafe { read_element_u64(env.read_vregs(), vs1_base, i, sew) }
471 }
472 OpSrc::Scalar(val) => val,
473 };
474 let result = op(a, b, sew, vxrm, &mut any_sat);
475 unsafe {
477 write_element_u64(env.write_vregs(), vd, i, sew, result);
478 }
479 }
480 if any_sat {
481 env.set_vxsat(true);
483 }
484 env.mark_vs_dirty();
485 env.reset_vstart();
486}
487
488#[inline(always)]
503#[doc(hidden)]
504#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
505pub unsafe fn execute_narrowing_clip_op<Reg, Env, F>(
506 env: &mut Env,
507 vd: VReg,
508 vs2: VReg,
509 src: OpSrc,
510 vm: bool,
511 sew: Vsew,
512 op: F,
513) where
514 Reg: Register,
515 Env: VectorRegistersExt<Reg>,
516 [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
517 F: Fn(u64, u32, Vsew, Vxrm, &mut bool) -> u64,
519{
520 let vl = env.vl();
521 let vstart = env.vstart();
522 let vxrm = env.vxrm();
523 let mask_buf = unsafe { snapshot_mask(env.read_vregs(), vm, vl) };
525 let mut any_sat = false;
526 let shamt_mask = u64::from(sew.bits_width() * 2 - 1);
528 for i in vstart.range_to(vl) {
529 if !mask_bit(&mask_buf, i) {
530 continue;
531 }
532 let wide_a = unsafe { read_wide_element_u64(env.read_vregs(), vs2, i, sew) };
535 let shamt = match src {
536 OpSrc::Vreg(vs1_base) => {
537 let raw = unsafe { read_element_u64(env.read_vregs(), vs1_base, i, sew) };
539 (raw & shamt_mask) as u32
540 }
541 OpSrc::Scalar(val) => (val & shamt_mask) as u32,
542 };
543 let result = op(wide_a, shamt, sew, vxrm, &mut any_sat);
544 unsafe {
546 write_element_u64(env.write_vregs(), vd, i, sew, result);
547 }
548 }
549 if any_sat {
550 env.set_vxsat(true);
551 }
552 env.mark_vs_dirty();
553 env.reset_vstart();
554}
555
556#[inline(always)]
560#[doc(hidden)]
561#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
562pub fn check_narrowing_sew<Reg, Memory, PC>(
563 program_counter: &PC,
564 sew: Vsew,
565) -> Result<(), ExecutionError<Reg::Type>>
566where
567 Reg: Register,
568 PC: ProgramCounter<Reg::Type, Memory>,
569{
570 if sew.bits_width() > 32 {
571 cold_path();
572 return Err(ExecutionError::IllegalInstruction {
573 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
574 });
575 }
576 Ok(())
577}
578
579#[inline(always)]
589#[doc(hidden)]
590#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
591pub fn check_vs2_narrowing_alignment<Reg, Memory, PC>(
592 program_counter: &PC,
593 vs2: VReg,
594 vlmul: Vlmul,
595 sew: Vsew,
596) -> Result<(), ExecutionError<Reg::Type>>
597where
598 Reg: Register,
599 PC: ProgramCounter<Reg::Type, Memory>,
600{
601 let wide_eew = match sew {
604 Vsew::E8 => Eew::E16,
605 Vsew::E16 => Eew::E32,
606 Vsew::E32 => Eew::E64,
607 Vsew::E64 => {
608 cold_path();
609 return Err(ExecutionError::IllegalInstruction {
610 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
611 });
612 }
613 };
614 let Some(wide_group) = vlmul.data_register_count(wide_eew, sew) else {
616 cold_path();
617 return Err(ExecutionError::IllegalInstruction {
618 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
619 });
620 };
621 let wide_group = wide_group.get();
622 let vs2_idx = vs2.to_bits();
623 if !vs2_idx.is_multiple_of(wide_group) || vs2_idx + wide_group > 32 {
624 cold_path();
625 return Err(ExecutionError::IllegalInstruction {
626 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
627 });
628 }
629 Ok(())
630}