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, ProgramCounter};
13use ab_riscv_primitives::prelude::*;
14use core::fmt;
15use core::hint::cold_path;
16
17#[inline(always)]
22#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
23fn round_increment(val: u64, shift: u32, mode: Vxrm, current_result_lsb: u64) -> u64 {
24 if shift == 0 {
25 return 0;
26 }
27 let d_minus1_bit = (val >> (shift - 1)) & 1;
29 let sticky = if shift >= 2 {
31 (val & ((1u64 << (shift - 1)).wrapping_sub(1))) != 0
33 } else {
34 false
35 };
36 match mode {
37 Vxrm::Rnu => d_minus1_bit,
39 Vxrm::Rne => d_minus1_bit & u64::from(sticky || current_result_lsb != 0),
41 Vxrm::Rdn => 0,
43 Vxrm::Rod => u64::from(current_result_lsb == 0 && (d_minus1_bit != 0 || sticky)),
45 }
46}
47
48#[inline(always)]
52#[doc(hidden)]
53#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
54pub fn rounded_srl(val: u64, shift: u32, mode: Vxrm) -> u64 {
55 let truncated = val >> shift;
56 let r = round_increment(val, shift, mode, truncated & 1);
57 truncated.wrapping_add(r)
58}
59
60#[inline(always)]
64#[doc(hidden)]
65#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
66pub fn rounded_sra(val: u64, shift: u32, mode: Vxrm, sew: Vsew) -> u64 {
67 let signed = sign_extend(val, sew);
68 let truncated_signed = signed >> shift;
72 let r = round_increment(val, shift, mode, truncated_signed.cast_unsigned() & 1);
73 truncated_signed.cast_unsigned().wrapping_add(r)
74}
75
76#[inline(always)]
80#[doc(hidden)]
81#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
82pub fn sat_addu(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
83 let mask = sew_mask(sew);
84 let a_w = a & mask;
85 let b_w = b & mask;
86 let result = a_w.wrapping_add(b_w);
87 if result & mask < a_w {
88 *vxsat = true;
90 mask
91 } else {
92 result & mask
93 }
94}
95
96#[inline(always)]
100#[doc(hidden)]
101#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
102pub fn sat_add(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
103 let sa = i128::from(sign_extend(a, sew));
104 let sb = i128::from(sign_extend(b, sew));
105 let result = sa.wrapping_add(sb);
106 let min_val = i128::MIN >> (i128::BITS - u32::from(sew.bits_width()));
107 let max_val = i128::MAX >> (i128::BITS - u32::from(sew.bits_width()));
108 if result < min_val {
109 *vxsat = true;
110 (min_val as i64).cast_unsigned() & sew_mask(sew)
111 } else if result > max_val {
112 *vxsat = true;
113 (max_val as i64).cast_unsigned() & sew_mask(sew)
114 } else {
115 (result as i64).cast_unsigned() & sew_mask(sew)
116 }
117}
118
119#[inline(always)]
123#[doc(hidden)]
124#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
125pub fn sat_subu(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
126 let mask = sew_mask(sew);
127 let a_w = a & mask;
128 let b_w = b & mask;
129 if a_w < b_w {
130 *vxsat = true;
131 0
132 } else {
133 (a_w - b_w) & mask
134 }
135}
136
137#[inline(always)]
141#[doc(hidden)]
142#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
143pub fn sat_sub(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
144 let sa = i128::from(sign_extend(a, sew));
145 let sb = i128::from(sign_extend(b, sew));
146 let result = sa.wrapping_sub(sb);
147 let min_val = i128::MIN >> (i128::BITS - u32::from(sew.bits_width()));
148 let max_val = i128::MAX >> (i128::BITS - u32::from(sew.bits_width()));
149 if result < min_val {
150 *vxsat = true;
151 (min_val as i64).cast_unsigned() & sew_mask(sew)
152 } else if result > max_val {
153 *vxsat = true;
154 (max_val as i64).cast_unsigned() & sew_mask(sew)
155 } else {
156 (result as i64).cast_unsigned() & sew_mask(sew)
157 }
158}
159
160#[inline(always)]
164#[doc(hidden)]
165#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
166pub fn avg_addu(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
167 let mask = sew_mask(sew);
168 let a_w = a & mask;
169 let b_w = b & mask;
170 let sum = a_w.wrapping_add(b_w);
174 let carry = u64::from(sum & mask < a_w);
176 let r = round_increment(sum & mask, 1, mode, (sum >> 1u8) & 1);
180 let shifted = (carry << (u32::from(sew.bits_width()) - 1)) | ((sum & mask) >> 1u8);
182 (shifted.wrapping_add(r)) & mask
183}
184
185#[inline(always)]
189#[doc(hidden)]
190#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
191pub fn avg_add(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
192 let sa = sign_extend(a, sew);
193 let sb = sign_extend(b, sew);
194 let sum = i128::from(sa).wrapping_add(i128::from(sb));
196 let r = match mode {
198 Vxrm::Rnu => (sum & 1).cast_unsigned() as u64,
199 Vxrm::Rne => {
200 let result_lsb = ((sum >> 1u8) & 1).cast_unsigned() as u64;
203 ((sum & 1).cast_unsigned() as u64) & result_lsb
204 }
205 Vxrm::Rdn => 0,
206 Vxrm::Rod => {
207 let result_lsb = (sum >> 1u8) & 1;
209 u64::from(result_lsb == 0 && (sum & 1) != 0)
210 }
211 };
212 let result = (sum >> 1u8) + i128::from(r);
213 (result as i64).cast_unsigned() & sew_mask(sew)
214}
215
216#[inline(always)]
220#[doc(hidden)]
221#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
222pub fn avg_subu(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
223 let mask = sew_mask(sew);
224 let a_w = a & mask;
225 let b_w = b & mask;
226 let diff = a_w.wrapping_sub(b_w);
228 let borrow = u64::from(a_w < b_w);
230 let r = round_increment(diff & mask, 1, mode, (diff >> 1u8) & 1);
234 let sign_fill = borrow.wrapping_neg(); let shifted = (sign_fill << (u32::from(sew.bits_width()) - 1)) | ((diff & mask) >> 1u8);
241 (shifted.wrapping_add(r)) & mask
242}
243
244#[inline(always)]
248#[doc(hidden)]
249#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
250pub fn avg_sub(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
251 let sa = sign_extend(a, sew);
252 let sb = sign_extend(b, sew);
253 let diff = i128::from(sa).wrapping_sub(i128::from(sb));
254 let r = match mode {
255 Vxrm::Rnu => (diff & 1).cast_unsigned() as u64,
256 Vxrm::Rne => {
257 let result_lsb = ((diff >> 1u8) & 1).cast_unsigned() as u64;
258 ((diff & 1).cast_unsigned() as u64) & result_lsb
259 }
260 Vxrm::Rdn => 0,
261 Vxrm::Rod => {
262 let result_lsb = (diff >> 1u8) & 1;
263 u64::from(result_lsb == 0 && (diff & 1) != 0)
264 }
265 };
266 let result = (diff >> 1u8) + i128::from(r);
267 (result as i64).cast_unsigned() & sew_mask(sew)
268}
269
270#[inline(always)]
278#[doc(hidden)]
279#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
280pub fn smul(a: u64, b: u64, sew: Vsew, mode: Vxrm, vxsat: &mut bool) -> u64 {
281 let min_sew = i64::MIN >> (i64::BITS - u32::from(sew.bits_width()));
283 let max_sew = i64::MAX >> (i64::BITS - u32::from(sew.bits_width()));
284 let sa = i128::from(sign_extend(a, sew));
285 let sb = i128::from(sign_extend(b, sew));
286 if sa == i128::from(min_sew) && sb == i128::from(min_sew) {
289 cold_path();
290 *vxsat = true;
291 return max_sew.cast_unsigned() & sew_mask(sew);
292 }
293 let product = sa * sb;
296 let doubled = product << 1u8;
299 let shift = u32::from(sew.bits_width());
302 let low_bits = (doubled.cast_unsigned() & u128::from(sew_mask(sew))) as u64;
303 let truncated = doubled >> shift;
305 let r = round_increment(
306 low_bits,
307 shift.min(64),
308 mode,
309 (truncated.cast_unsigned() as u64) & 1,
310 );
311 let result = (truncated as i64).wrapping_add(r.cast_signed());
313 if result < min_sew {
315 *vxsat = true;
316 min_sew.cast_unsigned() & sew_mask(sew)
317 } else if result > max_sew {
318 *vxsat = true;
319 max_sew.cast_unsigned() & sew_mask(sew)
320 } else {
321 result.cast_unsigned() & sew_mask(sew)
322 }
323}
324
325#[inline(always)]
337#[doc(hidden)]
338#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
339pub fn nclipu(vs2_elem: u64, shamt: u32, sew: Vsew, mode: Vxrm, vxsat: &mut bool) -> u64 {
340 let shifted = rounded_srl(vs2_elem, shamt, mode);
342 let max_dst = sew_mask(sew);
344 if shifted > max_dst {
345 *vxsat = true;
346 max_dst
347 } else {
348 shifted & max_dst
349 }
350}
351
352#[inline(always)]
357#[doc(hidden)]
358#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
359pub fn nclip(vs2_elem: u64, shamt: u32, sew: Vsew, mode: Vxrm, vxsat: &mut bool) -> u64 {
360 let double_sew_bits = sew.bits_width() * 2;
364 let shift_amt = i64::BITS - u32::from(double_sew_bits);
365 let signed_wide = (vs2_elem.cast_signed() << shift_amt) >> shift_amt;
366 let low_bits = signed_wide.cast_unsigned()
369 & if double_sew_bits == 64 {
370 u64::MAX
371 } else {
372 (1u64 << double_sew_bits) - 1
373 };
374 let truncated = signed_wide >> shamt;
375 let r = round_increment(low_bits, shamt, mode, (truncated.cast_unsigned()) & 1);
376 let rounded = truncated.wrapping_add(r.cast_signed());
377 let min_dst = i64::MIN >> (i64::BITS - u32::from(sew.bits_width()));
379 let max_dst = i64::MAX >> (i64::BITS - u32::from(sew.bits_width()));
380 if rounded < min_dst {
381 *vxsat = true;
382 min_dst.cast_unsigned() & sew_mask(sew)
383 } else if rounded > max_dst {
384 *vxsat = true;
385 max_dst.cast_unsigned() & sew_mask(sew)
386 } else {
387 rounded.cast_unsigned() & sew_mask(sew)
388 }
389}
390
391#[inline(always)]
403#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
404pub unsafe fn read_wide_element_u64<const VLEN: Vlen>(
405 vregs: &VectorRegisterFile<VLEN>,
406 base_reg: VReg,
407 elem_i: u16,
408 sew: Vsew,
409) -> u64 {
410 let double_sew_bytes = u32::from(sew.bytes_width()) * 2;
411 let elems_per_reg = VLEN.bytes() / double_sew_bytes;
412 let reg_off = u32::from(elem_i) / elems_per_reg;
413 let byte_off = (u32::from(elem_i) % elems_per_reg) * double_sew_bytes;
414 let reg = unsafe {
416 vregs.get(VReg::from_bits(base_reg.to_bits() + reg_off as u8).unwrap_unchecked())
417 };
418 let src =
420 unsafe { reg.get_unchecked(byte_off as usize..(byte_off + double_sew_bytes) as usize) };
421 let mut buf = [0u8; 8];
422 unsafe { buf.get_unchecked_mut(..double_sew_bytes as usize) }.copy_from_slice(src);
424 u64::from_le_bytes(buf)
425}
426
427#[inline(always)]
435#[doc(hidden)]
436pub unsafe fn execute_fixed_point_op<Reg, ExtState, CustomError, F>(
438 ext_state: &mut ExtState,
439 vd: VReg,
440 vs2: VReg,
441 src: OpSrc,
442 vm: bool,
443 sew: Vsew,
444 op: F,
445) where
446 Reg: Register,
447 ExtState: VectorRegistersExt<Reg, CustomError>,
448 [(); SUPPORTED_ELEN_VLEN::<{ ExtState::ELEN }, { ExtState::VLEN }>]:,
449 CustomError: fmt::Debug,
450 F: Fn(u64, u64, Vsew, Vxrm, &mut bool) -> u64,
452{
453 let vl = ext_state.vl();
454 let vstart = ext_state.vstart();
455 let vxrm = ext_state.vxrm();
456 let mask_buf = unsafe { snapshot_mask(ext_state.read_vregs(), vm, vl) };
458 let mut any_sat = false;
459 for i in vstart.range_to(vl) {
460 if !mask_bit(&mask_buf, i) {
461 continue;
462 }
463 let a = unsafe { read_element_u64(ext_state.read_vregs(), vs2, i, sew) };
465 let b = match src {
466 OpSrc::Vreg(vs1_base) => {
467 unsafe { read_element_u64(ext_state.read_vregs(), vs1_base, i, sew) }
469 }
470 OpSrc::Scalar(val) => val,
471 };
472 let result = op(a, b, sew, vxrm, &mut any_sat);
473 unsafe {
475 write_element_u64(ext_state.write_vregs(), vd, i, sew, result);
476 }
477 }
478 if any_sat {
479 ext_state.set_vxsat(true);
481 }
482 ext_state.mark_vs_dirty();
483 ext_state.reset_vstart();
484}
485
486#[inline(always)]
501#[doc(hidden)]
502pub unsafe fn execute_narrowing_clip_op<Reg, ExtState, CustomError, F>(
504 ext_state: &mut ExtState,
505 vd: VReg,
506 vs2: VReg,
507 src: OpSrc,
508 vm: bool,
509 sew: Vsew,
510 op: F,
511) where
512 Reg: Register,
513 ExtState: VectorRegistersExt<Reg, CustomError>,
514 [(); SUPPORTED_ELEN_VLEN::<{ ExtState::ELEN }, { ExtState::VLEN }>]:,
515 CustomError: fmt::Debug,
516 F: Fn(u64, u32, Vsew, Vxrm, &mut bool) -> u64,
518{
519 let vl = ext_state.vl();
520 let vstart = ext_state.vstart();
521 let vxrm = ext_state.vxrm();
522 let mask_buf = unsafe { snapshot_mask(ext_state.read_vregs(), vm, vl) };
524 let mut any_sat = false;
525 let shamt_mask = u64::from(sew.bits_width() * 2 - 1);
527 for i in vstart.range_to(vl) {
528 if !mask_bit(&mask_buf, i) {
529 continue;
530 }
531 let wide_a = unsafe { read_wide_element_u64(ext_state.read_vregs(), vs2, i, sew) };
534 let shamt = match src {
535 OpSrc::Vreg(vs1_base) => {
536 let raw = unsafe { read_element_u64(ext_state.read_vregs(), vs1_base, i, sew) };
538 (raw & shamt_mask) as u32
539 }
540 OpSrc::Scalar(val) => (val & shamt_mask) as u32,
541 };
542 let result = op(wide_a, shamt, sew, vxrm, &mut any_sat);
543 unsafe {
545 write_element_u64(ext_state.write_vregs(), vd, i, sew, result);
546 }
547 }
548 if any_sat {
549 ext_state.set_vxsat(true);
550 }
551 ext_state.mark_vs_dirty();
552 ext_state.reset_vstart();
553}
554
555#[inline(always)]
559#[doc(hidden)]
560#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
561pub fn check_narrowing_sew<Reg, Memory, PC, CustomError>(
562 program_counter: &PC,
563 sew: Vsew,
564) -> Result<(), ExecutionError<Reg::Type, CustomError>>
565where
566 Reg: Register,
567 PC: ProgramCounter<Reg::Type, Memory, CustomError>,
568{
569 if sew.bits_width() > 32 {
570 cold_path();
571 return Err(ExecutionError::IllegalInstruction {
572 address: program_counter.old_pc(INSTRUCTION_SIZE),
573 });
574 }
575 Ok(())
576}
577
578#[inline(always)]
588#[doc(hidden)]
589#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
590pub fn check_vs2_narrowing_alignment<Reg, Memory, PC, CustomError>(
591 program_counter: &PC,
592 vs2: VReg,
593 vlmul: Vlmul,
594 sew: Vsew,
595) -> Result<(), ExecutionError<Reg::Type, CustomError>>
596where
597 Reg: Register,
598 PC: ProgramCounter<Reg::Type, Memory, CustomError>,
599{
600 let wide_eew = match sew {
603 Vsew::E8 => Eew::E16,
604 Vsew::E16 => Eew::E32,
605 Vsew::E32 => Eew::E64,
606 Vsew::E64 => {
607 cold_path();
608 return Err(ExecutionError::IllegalInstruction {
609 address: program_counter.old_pc(INSTRUCTION_SIZE),
610 });
611 }
612 };
613 let Some(wide_group) = vlmul.data_register_count(wide_eew, sew) else {
615 cold_path();
616 return Err(ExecutionError::IllegalInstruction {
617 address: program_counter.old_pc(INSTRUCTION_SIZE),
618 });
619 };
620 let wide_group = wide_group.get();
621 let vs2_idx = vs2.to_bits();
622 if !vs2_idx.is_multiple_of(wide_group) || vs2_idx + wide_group > 32 {
623 cold_path();
624 return Err(ExecutionError::IllegalInstruction {
625 address: program_counter.old_pc(INSTRUCTION_SIZE),
626 });
627 }
628 Ok(())
629}