1use crate::v::vector_registers::VectorRegistersExt;
4use crate::v::zvexx::arith::zvexx_arith_helpers::sign_extend;
5pub use crate::v::zvexx::arith::zvexx_arith_helpers::{
6 OpSrc, check_vreg_group_alignment, sew_mask,
7};
8use crate::v::zvexx::load::zvexx_load_helpers::{mask_bit, snapshot_mask};
9use crate::v::zvexx::zvexx_helpers::INSTRUCTION_SIZE;
10use crate::{ExecutionError, PackedAddress, ProgramCounter};
11use ab_riscv_primitives::prelude::*;
12use core::hint::cold_path;
13
14#[inline(always)]
19#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
20fn round_increment(val: u64, shift: u32, mode: Vxrm, current_result_lsb: u64) -> u64 {
21 if shift == 0 {
22 return 0;
23 }
24 let d_minus1_bit = (val >> (shift - 1)) & 1;
26 let sticky = if shift >= 2 {
28 (val & ((1u64 << (shift - 1)).wrapping_sub(1))) != 0
30 } else {
31 false
32 };
33 match mode {
34 Vxrm::Rnu => d_minus1_bit,
36 Vxrm::Rne => d_minus1_bit & u64::from(sticky || current_result_lsb != 0),
38 Vxrm::Rdn => 0,
40 Vxrm::Rod => u64::from(current_result_lsb == 0 && (d_minus1_bit != 0 || sticky)),
42 }
43}
44
45#[inline(always)]
49#[doc(hidden)]
50#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
51pub fn rounded_srl(val: u64, shift: u32, mode: Vxrm) -> u64 {
52 let truncated = val >> shift;
53 let r = round_increment(val, shift, mode, truncated & 1);
54 truncated.wrapping_add(r)
55}
56
57#[inline(always)]
61#[doc(hidden)]
62#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
63pub fn rounded_sra(val: u64, shift: u32, mode: Vxrm, sew: Vsew) -> u64 {
64 let signed = sign_extend(val, sew);
65 let truncated_signed = signed >> shift;
69 let r = round_increment(val, shift, mode, truncated_signed.cast_unsigned() & 1);
70 truncated_signed.cast_unsigned().wrapping_add(r)
71}
72
73#[inline(always)]
77#[doc(hidden)]
78#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
79pub fn sat_addu(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
80 let mask = sew_mask(sew);
81 let a_w = a & mask;
82 let b_w = b & mask;
83 let result = a_w.wrapping_add(b_w);
84 if result & mask < a_w {
85 *vxsat = true;
87 mask
88 } else {
89 result & mask
90 }
91}
92
93#[inline(always)]
97#[doc(hidden)]
98#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
99pub fn sat_add(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
100 let sa = i128::from(sign_extend(a, sew));
101 let sb = i128::from(sign_extend(b, sew));
102 let result = sa.wrapping_add(sb);
103 let min_val = i128::MIN >> (i128::BITS - u32::from(sew.bits_width()));
104 let max_val = i128::MAX >> (i128::BITS - u32::from(sew.bits_width()));
105 if result < min_val {
106 *vxsat = true;
107 (min_val as i64).cast_unsigned() & sew_mask(sew)
108 } else if result > max_val {
109 *vxsat = true;
110 (max_val as i64).cast_unsigned() & sew_mask(sew)
111 } else {
112 (result as i64).cast_unsigned() & sew_mask(sew)
113 }
114}
115
116#[inline(always)]
120#[doc(hidden)]
121#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
122pub fn sat_subu(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
123 let mask = sew_mask(sew);
124 let a_w = a & mask;
125 let b_w = b & mask;
126 if a_w < b_w {
127 *vxsat = true;
128 0
129 } else {
130 (a_w - b_w) & mask
131 }
132}
133
134#[inline(always)]
138#[doc(hidden)]
139#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
140pub fn sat_sub(a: u64, b: u64, sew: Vsew, vxsat: &mut bool) -> u64 {
141 let sa = i128::from(sign_extend(a, sew));
142 let sb = i128::from(sign_extend(b, sew));
143 let result = sa.wrapping_sub(sb);
144 let min_val = i128::MIN >> (i128::BITS - u32::from(sew.bits_width()));
145 let max_val = i128::MAX >> (i128::BITS - u32::from(sew.bits_width()));
146 if result < min_val {
147 *vxsat = true;
148 (min_val as i64).cast_unsigned() & sew_mask(sew)
149 } else if result > max_val {
150 *vxsat = true;
151 (max_val as i64).cast_unsigned() & sew_mask(sew)
152 } else {
153 (result as i64).cast_unsigned() & sew_mask(sew)
154 }
155}
156
157#[inline(always)]
161#[doc(hidden)]
162#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
163pub fn avg_addu(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
164 let mask = sew_mask(sew);
165 let a_w = a & mask;
166 let b_w = b & mask;
167 let sum = a_w.wrapping_add(b_w);
171 let carry = u64::from(sum & mask < a_w);
173 let r = round_increment(sum & mask, 1, mode, (sum >> 1u8) & 1);
177 let shifted = (carry << (u32::from(sew.bits_width()) - 1)) | ((sum & mask) >> 1u8);
179 (shifted.wrapping_add(r)) & mask
180}
181
182#[inline(always)]
186#[doc(hidden)]
187#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
188pub fn avg_add(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
189 let sa = sign_extend(a, sew);
190 let sb = sign_extend(b, sew);
191 let sum = i128::from(sa).wrapping_add(i128::from(sb));
193 let r = match mode {
195 Vxrm::Rnu => (sum & 1).cast_unsigned() as u64,
196 Vxrm::Rne => {
197 let result_lsb = ((sum >> 1u8) & 1).cast_unsigned() as u64;
200 ((sum & 1).cast_unsigned() as u64) & result_lsb
201 }
202 Vxrm::Rdn => 0,
203 Vxrm::Rod => {
204 let result_lsb = (sum >> 1u8) & 1;
206 u64::from(result_lsb == 0 && (sum & 1) != 0)
207 }
208 };
209 let result = (sum >> 1u8) + i128::from(r);
210 (result as i64).cast_unsigned() & sew_mask(sew)
211}
212
213#[inline(always)]
217#[doc(hidden)]
218#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
219pub fn avg_subu(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
220 let mask = sew_mask(sew);
221 let a_w = a & mask;
222 let b_w = b & mask;
223 let diff = a_w.wrapping_sub(b_w);
225 let borrow = u64::from(a_w < b_w);
227 let r = round_increment(diff & mask, 1, mode, (diff >> 1u8) & 1);
231 let sign_fill = borrow.wrapping_neg(); let shifted = (sign_fill << (u32::from(sew.bits_width()) - 1)) | ((diff & mask) >> 1u8);
238 (shifted.wrapping_add(r)) & mask
239}
240
241#[inline(always)]
245#[doc(hidden)]
246#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
247pub fn avg_sub(a: u64, b: u64, sew: Vsew, mode: Vxrm) -> u64 {
248 let sa = sign_extend(a, sew);
249 let sb = sign_extend(b, sew);
250 let diff = i128::from(sa).wrapping_sub(i128::from(sb));
251 let r = match mode {
252 Vxrm::Rnu => (diff & 1).cast_unsigned() as u64,
253 Vxrm::Rne => {
254 let result_lsb = ((diff >> 1u8) & 1).cast_unsigned() as u64;
255 ((diff & 1).cast_unsigned() as u64) & result_lsb
256 }
257 Vxrm::Rdn => 0,
258 Vxrm::Rod => {
259 let result_lsb = (diff >> 1u8) & 1;
260 u64::from(result_lsb == 0 && (diff & 1) != 0)
261 }
262 };
263 let result = (diff >> 1u8) + i128::from(r);
264 (result as i64).cast_unsigned() & sew_mask(sew)
265}
266
267#[inline(always)]
275#[doc(hidden)]
276#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
277pub fn smul(a: u64, b: u64, sew: Vsew, mode: Vxrm, vxsat: &mut bool) -> u64 {
278 let min_sew = i64::MIN >> (i64::BITS - u32::from(sew.bits_width()));
280 let max_sew = i64::MAX >> (i64::BITS - u32::from(sew.bits_width()));
281 let sa = i128::from(sign_extend(a, sew));
282 let sb = i128::from(sign_extend(b, sew));
283 if sa == i128::from(min_sew) && sb == i128::from(min_sew) {
286 cold_path();
287 *vxsat = true;
288 return max_sew.cast_unsigned() & sew_mask(sew);
289 }
290 let product = sa * sb;
293 let doubled = product << 1u8;
296 let shift = u32::from(sew.bits_width());
299 let low_bits = (doubled.cast_unsigned() & u128::from(sew_mask(sew))) as u64;
300 let truncated = doubled >> shift;
302 let r = round_increment(
303 low_bits,
304 shift.min(64),
305 mode,
306 (truncated.cast_unsigned() as u64) & 1,
307 );
308 let result = (truncated as i64).wrapping_add(r.cast_signed());
310 if result < min_sew {
312 *vxsat = true;
313 min_sew.cast_unsigned() & sew_mask(sew)
314 } else if result > max_sew {
315 *vxsat = true;
316 max_sew.cast_unsigned() & sew_mask(sew)
317 } else {
318 result.cast_unsigned() & sew_mask(sew)
319 }
320}
321
322#[inline(always)]
334#[doc(hidden)]
335#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
336pub fn nclipu(vs2_elem: u64, shamt: u32, sew: Vsew, mode: Vxrm, vxsat: &mut bool) -> u64 {
337 let shifted = rounded_srl(vs2_elem, shamt, mode);
339 let max_dst = sew_mask(sew);
341 if shifted > max_dst {
342 *vxsat = true;
343 max_dst
344 } else {
345 shifted & max_dst
346 }
347}
348
349#[inline(always)]
354#[doc(hidden)]
355#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
356pub fn nclip(vs2_elem: u64, shamt: u32, sew: Vsew, mode: Vxrm, vxsat: &mut bool) -> u64 {
357 let double_sew_bits = sew.bits_width() * 2;
361 let shift_amt = i64::BITS - u32::from(double_sew_bits);
362 let signed_wide = (vs2_elem.cast_signed() << shift_amt) >> shift_amt;
363 let low_bits = signed_wide.cast_unsigned()
366 & if double_sew_bits == 64 {
367 u64::MAX
368 } else {
369 (1u64 << double_sew_bits) - 1
370 };
371 let truncated = signed_wide >> shamt;
372 let r = round_increment(low_bits, shamt, mode, (truncated.cast_unsigned()) & 1);
373 let rounded = truncated.wrapping_add(r.cast_signed());
374 let min_dst = i64::MIN >> (i64::BITS - u32::from(sew.bits_width()));
376 let max_dst = i64::MAX >> (i64::BITS - u32::from(sew.bits_width()));
377 if rounded < min_dst {
378 *vxsat = true;
379 min_dst.cast_unsigned() & sew_mask(sew)
380 } else if rounded > max_dst {
381 *vxsat = true;
382 max_dst.cast_unsigned() & sew_mask(sew)
383 } else {
384 rounded.cast_unsigned() & sew_mask(sew)
385 }
386}
387
388#[inline(always)]
400#[doc(hidden)]
401#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
402pub unsafe fn execute_fixed_point_op<Reg, Env, F>(
403 env: &mut Env,
404 vd: VReg,
405 vs2: VReg,
406 src: OpSrc,
407 vm: bool,
408 sew: Vsew,
409 op: F,
410) where
411 Reg: Register,
412 Env: VectorRegistersExt<Reg>,
413 [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
414 F: Fn(u64, u64, Vsew, Vxrm, &mut bool) -> u64,
416{
417 let vl = env.vl();
418 let vstart = env.vstart();
419 let vxrm = env.vxrm();
420 let mask_buf = unsafe { snapshot_mask(env.read_vregs(), vm, vl) };
422 let mut any_sat = false;
423 for i in vstart.range_to(vl) {
424 if !mask_bit(&mask_buf, i) {
425 continue;
426 }
427 let a = unsafe { env.read_vregs().read_element(vs2, i, sew) };
429 let b = match src {
430 OpSrc::Vreg(vs1_base) => {
431 unsafe { env.read_vregs().read_element(vs1_base, i, sew) }
433 }
434 OpSrc::Scalar(val) => val,
435 };
436 let result = op(a, b, sew, vxrm, &mut any_sat);
437 unsafe {
439 env.write_vregs().write_element(vd, i, sew, result);
440 }
441 }
442 if any_sat {
443 env.set_vxsat(true);
445 }
446 env.mark_vs_dirty();
447 env.reset_vstart();
448}
449
450#[inline(always)]
465#[doc(hidden)]
466#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
467pub unsafe fn execute_narrowing_clip_op<Reg, Env, F>(
468 env: &mut Env,
469 vd: VReg,
470 vs2: VReg,
471 src: OpSrc,
472 vm: bool,
473 sew: Vsew,
474 op: F,
475) where
476 Reg: Register,
477 Env: VectorRegistersExt<Reg>,
478 [(); SUPPORTED_ELEN_VLEN::<{ Env::ELEN }, { Env::VLEN }>]:,
479 F: Fn(u64, u32, Vsew, Vxrm, &mut bool) -> u64,
481{
482 let wide_sew = unsafe { sew.double_width().unwrap_unchecked() };
484 let vl = env.vl();
485 let vstart = env.vstart();
486 let vxrm = env.vxrm();
487 let mask_buf = unsafe { snapshot_mask(env.read_vregs(), vm, vl) };
489 let mut any_sat = false;
490 let shamt_mask = u64::from(sew.bits_width() * 2 - 1);
492 for i in vstart.range_to(vl) {
493 if !mask_bit(&mask_buf, i) {
494 continue;
495 }
496 let wide_a = unsafe { env.read_vregs().read_element(vs2, i, wide_sew) };
499 let shamt = match src {
500 OpSrc::Vreg(vs1_base) => {
501 let raw = unsafe { env.read_vregs().read_element(vs1_base, i, sew) };
503 (raw & shamt_mask) as u32
504 }
505 OpSrc::Scalar(val) => (val & shamt_mask) as u32,
506 };
507 let result = op(wide_a, shamt, sew, vxrm, &mut any_sat);
508 unsafe {
510 env.write_vregs().write_element(vd, i, sew, result);
511 }
512 }
513 if any_sat {
514 env.set_vxsat(true);
515 }
516 env.mark_vs_dirty();
517 env.reset_vstart();
518}
519
520#[inline(always)]
524#[doc(hidden)]
525#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
526pub fn check_narrowing_sew<Reg, Memory, PC>(
527 program_counter: &PC,
528 sew: Vsew,
529) -> Result<(), ExecutionError<Reg::Type>>
530where
531 Reg: Register,
532 PC: ProgramCounter<Reg::Type, Memory>,
533{
534 if sew.bits_width() > 32 {
535 cold_path();
536 return Err(ExecutionError::IllegalInstruction {
537 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
538 });
539 }
540 Ok(())
541}
542
543#[inline(always)]
557#[doc(hidden)]
558#[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
559pub fn check_vs2_narrowing_alignment<Reg, Memory, PC>(
560 program_counter: &PC,
561 vs2: VReg,
562 vlmul: Vlmul,
563 sew: Vsew,
564 vd: VReg,
565 group_regs: VRegGroupSize,
566) -> Result<(), ExecutionError<Reg::Type>>
567where
568 Reg: Register,
569 PC: ProgramCounter<Reg::Type, Memory>,
570{
571 let wide_eew = match sew {
574 Vsew::E8 => Eew::E16,
575 Vsew::E16 => Eew::E32,
576 Vsew::E32 => Eew::E64,
577 Vsew::E64 => {
578 cold_path();
579 return Err(ExecutionError::IllegalInstruction {
580 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
581 });
582 }
583 };
584 let Some(wide_group) = vlmul.data_register_count(wide_eew, sew) else {
586 cold_path();
587 return Err(ExecutionError::IllegalInstruction {
588 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
589 });
590 };
591 let aligned = vs2.is_group_aligned(wide_group);
592 let wide_group = wide_group.get();
593 let vs2_idx = vs2.to_bits();
594 let vd_idx = vd.to_bits();
595 let group_regs = group_regs.get();
596 let overlaps = vd_idx < vs2_idx + wide_group && vs2_idx < vd_idx + group_regs;
597 if !aligned || vs2_idx + wide_group > 32 || (overlaps && vd_idx != vs2_idx) {
598 cold_path();
599 return Err(ExecutionError::IllegalInstruction {
600 address: PackedAddress::new(program_counter.old_pc(INSTRUCTION_SIZE)),
601 });
602 }
603 Ok(())
604}