use crate::exec::compute::vector::alu::frac_sew;
use crate::exec::compute::vector::context::{
VecExecCtx, VecExecResult, VecOperand, mask_active, read_op1, sign_extend,
};
use crate::exec::compute::vector::regfile::VectorRegFile;
use crate::isa::op::{ExtendOp, IntOp, MaccOp};
use crate::isa::rvv::{ElemIdx, Sew, VRegIdx, Vlmax, Vxrm};
#[inline]
pub(super) const fn rounding_incr(v: u64, d: u32, vxrm: Vxrm) -> u64 {
if d == 0 {
return 0;
}
match vxrm {
Vxrm::RoundToNearestUp => (v >> (d - 1)) & 1,
Vxrm::RoundToNearestEven => {
let r = (v >> (d - 1)) & 1;
let sticky = if d >= 2 { v & ((1u64 << (d - 1)) - 1) } else { 0 };
let lsb = (v >> d) & 1;
r & (sticky | lsb)
}
Vxrm::RoundDown => 0,
Vxrm::RoundToOdd => {
let dropped = v & ((1u64 << d) - 1);
let result_lsb = (v >> d) & 1;
if dropped != 0 && result_lsb == 0 { 1 } else { 0 }
}
}
}
#[inline]
pub(super) fn compute_standard(op: IntOp, vs2: u64, op1: u64, sew: Sew, vxrm: Vxrm) -> (u64, bool) {
let mask = sew.mask();
let bits = sew.bits();
let s2 = sign_extend(vs2, sew);
let s1 = sign_extend(op1, sew);
match op {
IntOp::Add => (vs2.wrapping_add(op1) & mask, false),
IntOp::Sub => (vs2.wrapping_sub(op1) & mask, false),
IntOp::Rsub => (op1.wrapping_sub(vs2) & mask, false),
IntOp::And => (vs2 & op1, false),
IntOp::Or => (vs2 | op1, false),
IntOp::Xor => (vs2 ^ op1, false),
IntOp::Sll => {
let shamt = (op1 & (bits as u64 - 1)) as u32;
((vs2 << shamt) & mask, false)
}
IntOp::Srl => {
let shamt = (op1 & (bits as u64 - 1)) as u32;
((vs2 >> shamt) & mask, false)
}
IntOp::Sra => {
let shamt = (op1 & (bits as u64 - 1)) as u32;
let result = (s2 >> shamt) as u64;
(result & mask, false)
}
IntOp::Min => ((if s2 < s1 { vs2 } else { op1 }) & mask, false),
IntOp::MinU => (if vs2 < op1 { vs2 } else { op1 }, false),
IntOp::Max => ((if s2 > s1 { vs2 } else { op1 }) & mask, false),
IntOp::MaxU => (if vs2 > op1 { vs2 } else { op1 }, false),
IntOp::Mul => (vs2.wrapping_mul(op1) & mask, false),
IntOp::Mulh => {
let prod = (s2 as i128).wrapping_mul(s1 as i128);
let hi = (prod >> bits) as u64;
(hi & mask, false)
}
IntOp::Mulhu => {
let prod = (vs2 as u128).wrapping_mul(op1 as u128);
let hi = (prod >> bits) as u64;
(hi & mask, false)
}
IntOp::Mulhsu => {
let prod = (s2 as i128).wrapping_mul(op1 as i128);
let hi = (prod >> bits) as u64;
(hi & mask, false)
}
IntOp::DivU => (vs2.checked_div(op1).map_or(mask, |q| q & mask), false),
IntOp::Div => {
if op1 == 0 {
(mask, false)
} else {
let min_int = 1u64 << (bits - 1);
let neg_one = mask;
if vs2 == min_int && op1 == neg_one {
(min_int & mask, false)
} else {
let result = s2.wrapping_div(s1) as u64;
(result & mask, false)
}
}
}
IntOp::RemU => {
if op1 == 0 {
(vs2, false)
} else {
((vs2 % op1) & mask, false)
}
}
IntOp::Rem => {
if op1 == 0 {
(vs2, false)
} else {
let min_int = 1u64 << (bits - 1);
let neg_one = mask;
if vs2 == min_int && op1 == neg_one {
(0, false)
} else {
let result = s2.wrapping_rem(s1) as u64;
(result & mask, false)
}
}
}
IntOp::SAddU => {
let sum = vs2.wrapping_add(op1) & mask;
if sum < vs2 { (mask, true) } else { (sum, false) }
}
IntOp::SAdd => {
let sum = s2 as i128 + s1 as i128;
if sum > sew.signed_max() as i128 {
((sew.signed_max() as u64) & mask, true)
} else if sum < sew.signed_min() as i128 {
((sew.signed_min() as u64) & mask, true)
} else {
(sum as u64 & mask, false)
}
}
IntOp::SSubU => {
if vs2 < op1 {
(0, true)
} else {
(vs2.wrapping_sub(op1) & mask, false)
}
}
IntOp::SSub => {
let diff = s2 as i128 - s1 as i128;
if diff > sew.signed_max() as i128 {
((sew.signed_max() as u64) & mask, true)
} else if diff < sew.signed_min() as i128 {
((sew.signed_min() as u64) & mask, true)
} else {
(diff as u64 & mask, false)
}
}
IntOp::AAddU => {
let sum = (vs2 as u128) + (op1 as u128);
let r = rounding_incr(sum as u64, 1, vxrm);
let result = ((sum >> 1) as u64).wrapping_add(r);
(result & mask, false)
}
IntOp::AAdd => {
let sum = (s2 as i128) + (s1 as i128);
let r = rounding_incr(sum as u64, 1, vxrm);
let result = ((sum >> 1) as u64).wrapping_add(r);
(result & mask, false)
}
IntOp::ASubU => {
let diff = (vs2 as i128) - (op1 as i128);
let r = rounding_incr(diff as u64, 1, vxrm);
let result = ((diff >> 1) as u64).wrapping_add(r);
(result & mask, false)
}
IntOp::ASub => {
let diff = (s2 as i128) - (s1 as i128);
let r = rounding_incr(diff as u64, 1, vxrm);
let result = ((diff >> 1) as u64).wrapping_add(r);
(result & mask, false)
}
IntOp::Smul => {
let prod = (s2 as i128) * (s1 as i128);
let shift = bits - 1;
let r = rounding_incr(prod as u64, shift as u32, vxrm);
let result_wide = (prod >> shift) + r as i128;
let max_pos = (1i64 << (bits - 1)) - 1;
let min_neg = -(1i64 << (bits - 1));
let sat;
let clamped = if result_wide > max_pos as i128 {
sat = true;
max_pos as u64
} else if result_wide < min_neg as i128 {
sat = true;
min_neg as u64
} else {
sat = false;
result_wide as u64
};
(clamped & mask, sat)
}
IntOp::SSrl => {
let shamt = (op1 & (bits as u64 - 1)) as u32;
let r = rounding_incr(vs2, shamt, vxrm);
let result = (vs2 >> shamt).wrapping_add(r);
(result & mask, false)
}
IntOp::SSra => {
let shamt = (op1 & (bits as u64 - 1)) as u32;
let r = rounding_incr(vs2, shamt, vxrm);
let result = ((s2 >> shamt) as u64).wrapping_add(r);
(result & mask, false)
}
IntOp::AndN => ((vs2 & !op1) & mask, false),
IntOp::Brev => {
let v = vs2 & mask;
let r = bit_reverse(v, bits);
(r & mask, false)
}
IntOp::Brev8 => {
let mut out: u64 = 0;
let nbytes = bits / 8;
for i in 0..nbytes {
let b = ((vs2 >> (i * 8)) & 0xff) as u8;
out |= u64::from(b.reverse_bits()) << (i * 8);
}
(out & mask, false)
}
IntOp::Rev8 => {
let mut out: u64 = 0;
let nbytes = bits / 8;
for i in 0..nbytes {
let b = (vs2 >> (i * 8)) & 0xff;
let dst = nbytes - 1 - i;
out |= b << (dst * 8);
}
(out & mask, false)
}
IntOp::Clz => {
let v = vs2 & mask;
let lz = if v == 0 { bits as u32 } else { v.leading_zeros() - (64 - bits as u32) };
(u64::from(lz) & mask, false)
}
IntOp::Ctz => {
let v = vs2 & mask;
let tz = if v == 0 { bits as u32 } else { v.trailing_zeros() };
(u64::from(tz) & mask, false)
}
IntOp::CpopV => {
let v = vs2 & mask;
(u64::from(v.count_ones()) & mask, false)
}
IntOp::Rol => {
let shamt = (op1 & (bits as u64 - 1)) as u32;
let v = vs2 & mask;
let r =
if shamt == 0 { v } else { ((v << shamt) | (v >> (bits as u32 - shamt))) & mask };
(r, false)
}
IntOp::Ror => {
let shamt = (op1 & (bits as u64 - 1)) as u32;
let v = vs2 & mask;
let r =
if shamt == 0 { v } else { ((v >> shamt) | (v << (bits as u32 - shamt))) & mask };
(r, false)
}
IntOp::ClMul => {
let a = vs2 & mask;
let b = op1 & mask;
(clmul_low(a, b, bits) & mask, false)
}
IntOp::ClMulH => {
let a = vs2 & mask;
let b = op1 & mask;
(clmul_high(a, b, bits) & mask, false)
}
}
}
#[inline]
pub(super) const fn bit_reverse(v: u64, bits: usize) -> u64 {
v.reverse_bits() >> (64 - bits)
}
#[inline]
pub(super) const fn clmul_low(a: u64, b: u64, bits: usize) -> u64 {
let mut acc: u64 = 0;
let mut i = 0;
while i < bits {
if (a >> i) & 1 != 0 {
acc ^= b << i;
}
i += 1;
}
acc
}
#[inline]
pub(super) const fn clmul_high(a: u64, b: u64, bits: usize) -> u64 {
let mut acc: u64 = 0;
let mut i = 1; while i < bits {
if (a >> i) & 1 != 0 {
acc ^= b >> (bits - i);
}
i += 1;
}
acc
}
pub(super) fn exec_standard(
op: IntOp,
vpr: &mut impl VectorRegFile,
vd_idx: VRegIdx,
vs2_idx: VRegIdx,
operand1: VecOperand,
ctx: &VecExecCtx,
) -> VecExecResult {
let vlmax = Vlmax::compute(vpr.vlen(), ctx.sew, ctx.vlmul).as_usize();
let mut vxsat = false;
for i in 0..vlmax {
if i < ctx.vstart {
continue;
}
if i >= ctx.vl {
if ctx.vta.is_agnostic() {
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, ctx.sew.ones());
}
continue;
}
if !ctx.vm && !mask_active(vpr, i) {
if ctx.vma.is_agnostic() {
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, ctx.sew.ones());
}
continue;
}
let vs2_val = vpr.read_element(vs2_idx, ElemIdx::new(i), ctx.sew);
let op1_val = read_op1(vpr, &operand1, i, ctx.sew);
let (result, sat) = compute_standard(op, vs2_val, op1_val, ctx.sew, ctx.vxrm);
vxsat |= sat;
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, result);
}
VecExecResult { vxsat, scalar_result: None, fp_flags: crate::isa::fp::FpFlags::NONE }
}
pub(super) fn exec_macc(
op: MaccOp,
vpr: &mut impl VectorRegFile,
vd_idx: VRegIdx,
vs2_idx: VRegIdx,
operand1: VecOperand,
ctx: &VecExecCtx,
) -> VecExecResult {
let vlmax = Vlmax::compute(vpr.vlen(), ctx.sew, ctx.vlmul).as_usize();
let mask = ctx.sew.mask();
for i in 0..vlmax {
if i < ctx.vstart {
continue;
}
if i >= ctx.vl {
if ctx.vta.is_agnostic() {
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, ctx.sew.ones());
}
continue;
}
if !ctx.vm && !mask_active(vpr, i) {
if ctx.vma.is_agnostic() {
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, ctx.sew.ones());
}
continue;
}
let vs2_val = vpr.read_element(vs2_idx, ElemIdx::new(i), ctx.sew);
let op1_val = read_op1(vpr, &operand1, i, ctx.sew);
let vd_val = vpr.read_element(vd_idx, ElemIdx::new(i), ctx.sew);
let result = match op {
MaccOp::Macc => op1_val.wrapping_mul(vs2_val).wrapping_add(vd_val) & mask,
MaccOp::NMSac => vd_val.wrapping_sub(op1_val.wrapping_mul(vs2_val)) & mask,
MaccOp::Madd => op1_val.wrapping_mul(vd_val).wrapping_add(vs2_val) & mask,
MaccOp::NMSub => vs2_val.wrapping_sub(op1_val.wrapping_mul(vd_val)) & mask,
};
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, result);
}
VecExecResult { vxsat: false, scalar_result: None, fp_flags: crate::isa::fp::FpFlags::NONE }
}
pub(super) fn exec_merge(
vpr: &mut impl VectorRegFile,
vd_idx: VRegIdx,
vs2_idx: VRegIdx,
operand1: VecOperand,
ctx: &VecExecCtx,
) -> VecExecResult {
let vlmax = Vlmax::compute(vpr.vlen(), ctx.sew, ctx.vlmul).as_usize();
for i in 0..vlmax {
if i < ctx.vstart {
continue;
}
if i >= ctx.vl {
if ctx.vta.is_agnostic() {
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, ctx.sew.ones());
}
continue;
}
let use_op1 = ctx.vm || mask_active(vpr, i);
let result = if use_op1 {
read_op1(vpr, &operand1, i, ctx.sew)
} else {
vpr.read_element(vs2_idx, ElemIdx::new(i), ctx.sew)
};
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, result);
}
VecExecResult { vxsat: false, scalar_result: None, fp_flags: crate::isa::fp::FpFlags::NONE }
}
pub(super) fn exec_extension(
op: ExtendOp,
vpr: &mut impl VectorRegFile,
vd_idx: VRegIdx,
vs2_idx: VRegIdx,
ctx: &VecExecCtx,
) -> VecExecResult {
let vlmax = Vlmax::compute(vpr.vlen(), ctx.sew, ctx.vlmul).as_usize();
let (factor, is_signed) = (usize::from(op.factor), op.signed);
let Some(src_sew) = frac_sew(ctx.sew, factor) else {
return VecExecResult {
vxsat: false,
scalar_result: None,
fp_flags: crate::isa::fp::FpFlags::NONE,
};
};
for i in 0..vlmax {
if i < ctx.vstart {
continue;
}
if i >= ctx.vl {
if ctx.vta.is_agnostic() {
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, ctx.sew.ones());
}
continue;
}
if !ctx.vm && !mask_active(vpr, i) {
if ctx.vma.is_agnostic() {
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, ctx.sew.ones());
}
continue;
}
let src_val = vpr.read_element(vs2_idx, ElemIdx::new(i), src_sew);
let result =
if is_signed { sign_extend(src_val, src_sew) as u64 & ctx.sew.mask() } else { src_val };
vpr.write_element(vd_idx, ElemIdx::new(i), ctx.sew, result);
}
VecExecResult { vxsat: false, scalar_result: None, fp_flags: crate::isa::fp::FpFlags::NONE }
}