use crate::exec::compute::vector::alu::integer::rounding_incr;
use crate::exec::compute::vector::context::{
VecExecCtx, VecExecResult, VecOperand, mask_active, read_op1, sign_extend, widen_sew,
};
use crate::exec::compute::vector::regfile::VectorRegFile;
use crate::isa::op::{NarrowOp, WidenMaccOp, WidenOp};
use crate::isa::rvv::{ElemIdx, Sew, VRegIdx, Vlmax, Vxrm};
#[inline]
pub(super) const fn compute_widening(
op: WidenOp,
vs2_val: u64,
op1_val: u64,
sew: Sew,
wsew: Sew,
) -> u64 {
let wmask = wsew.mask();
let s2_narrow = sign_extend(vs2_val, sew) as u64 & wmask;
let u2_narrow = vs2_val & sew.mask();
let s1 = sign_extend(op1_val, sew) as u64 & wmask;
let u1 = op1_val & sew.mask();
let s2_wide = sign_extend(vs2_val, wsew) as u64 & wmask;
let u2_wide = vs2_val & wmask;
match op {
WidenOp::AddU => u2_narrow.wrapping_add(u1) & wmask,
WidenOp::Add => s2_narrow.wrapping_add(s1) & wmask,
WidenOp::SubU => u2_narrow.wrapping_sub(u1) & wmask,
WidenOp::Sub => s2_narrow.wrapping_sub(s1) & wmask,
WidenOp::AddUW => u2_wide.wrapping_add(u1) & wmask,
WidenOp::AddW => s2_wide.wrapping_add(s1) & wmask,
WidenOp::SubUW => u2_wide.wrapping_sub(u1) & wmask,
WidenOp::SubW => s2_wide.wrapping_sub(s1) & wmask,
WidenOp::MulU => {
let prod = (u2_narrow as u128) * (u1 as u128);
prod as u64 & wmask
}
WidenOp::Mul => {
let prod = (sign_extend(vs2_val, sew) as i128) * (sign_extend(op1_val, sew) as i128);
prod as u64 & wmask
}
WidenOp::MulSU => {
let prod = (sign_extend(vs2_val, sew) as i128) * (u1 as i128);
prod as u64 & wmask
}
WidenOp::Sll => {
let wbits = wsew.bits() as u64;
let shamt = (op1_val & (wbits - 1)) as u32;
(u2_narrow << shamt) & wmask
}
}
}
#[inline]
pub(super) const fn compute_widening_macc(
op: WidenMaccOp,
vs2_val: u64,
op1_val: u64,
vd_val: u64,
sew: Sew,
wsew: Sew,
) -> u64 {
let wmask = wsew.mask();
let u2 = vs2_val & sew.mask();
let u1 = op1_val & sew.mask();
let acc = vd_val & wmask;
match op {
WidenMaccOp::MaccU => {
let prod = (u2 as u128) * (u1 as u128);
(prod as u64).wrapping_add(acc) & wmask
}
WidenMaccOp::Macc => {
let prod = (sign_extend(vs2_val, sew) as i128) * (sign_extend(op1_val, sew) as i128);
(prod as u64).wrapping_add(acc) & wmask
}
WidenMaccOp::MaccSU => {
let prod = (sign_extend(op1_val, sew) as i128) * (u2 as i128);
(prod as u64).wrapping_add(acc) & wmask
}
WidenMaccOp::MaccUS => {
let prod = (u1 as i128) * (sign_extend(vs2_val, sew) as i128);
(prod as u64).wrapping_add(acc) & wmask
}
}
}
#[inline]
pub(super) const fn compute_narrowing(
op: NarrowOp,
vs2_val: u64,
op1_val: u64,
sew: Sew,
wsew: Sew,
vxrm: Vxrm,
) -> (u64, bool) {
let mask = sew.mask();
let wbits = wsew.bits();
let shamt = (op1_val & (wbits as u64 - 1)) as u32;
match op {
NarrowOp::Srl => {
let result = vs2_val >> shamt;
(result & mask, false)
}
NarrowOp::Sra => {
let s = sign_extend(vs2_val, wsew);
let result = (s >> shamt) as u64;
(result & mask, false)
}
NarrowOp::ClipU => {
let r = rounding_incr(vs2_val, shamt, vxrm);
let shifted = (vs2_val >> shamt).wrapping_add(r);
if shifted > mask { (mask, true) } else { (shifted & mask, false) }
}
NarrowOp::Clip => {
let s = sign_extend(vs2_val, wsew);
let r = rounding_incr(vs2_val, shamt, vxrm) as i64;
let shifted = (s >> shamt).wrapping_add(r);
let max_pos = (1i64 << (sew.bits() - 1)) - 1;
let min_neg = -(1i64 << (sew.bits() - 1));
if shifted > max_pos {
(max_pos as u64 & mask, true)
} else if shifted < min_neg {
(min_neg as u64 & mask, true)
} else {
(shifted as u64 & mask, false)
}
}
}
}
pub(super) fn exec_widening(
op: WidenOp,
vpr: &mut impl VectorRegFile,
vd_idx: VRegIdx,
vs2_idx: VRegIdx,
operand1: VecOperand,
ctx: &VecExecCtx,
) -> VecExecResult {
let Some(wsew) = widen_sew(ctx.sew) else {
return VecExecResult {
vxsat: false,
scalar_result: None,
fp_flags: crate::isa::fp::FpFlags::NONE,
};
};
let vlmax = Vlmax::compute(vpr.vlen(), ctx.sew, ctx.vlmul).as_usize();
let vs2_sew = if op.reads_wide_vs2() { wsew } else { ctx.sew };
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), wsew, wsew.ones());
}
continue;
}
if !ctx.vm && !mask_active(vpr, i) {
if ctx.vma.is_agnostic() {
vpr.write_element(vd_idx, ElemIdx::new(i), wsew, wsew.ones());
}
continue;
}
let vs2_val = vpr.read_element(vs2_idx, ElemIdx::new(i), vs2_sew);
let op1_val = read_op1(vpr, &operand1, i, ctx.sew);
let result = compute_widening(op, vs2_val, op1_val, ctx.sew, wsew);
vpr.write_element(vd_idx, ElemIdx::new(i), wsew, result);
}
VecExecResult { vxsat: false, scalar_result: None, fp_flags: crate::isa::fp::FpFlags::NONE }
}
pub(super) fn exec_widening_macc(
op: WidenMaccOp,
vpr: &mut impl VectorRegFile,
vd_idx: VRegIdx,
vs2_idx: VRegIdx,
operand1: VecOperand,
ctx: &VecExecCtx,
) -> VecExecResult {
let Some(wsew) = widen_sew(ctx.sew) else {
return VecExecResult {
vxsat: false,
scalar_result: None,
fp_flags: crate::isa::fp::FpFlags::NONE,
};
};
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), wsew, wsew.ones());
}
continue;
}
if !ctx.vm && !mask_active(vpr, i) {
if ctx.vma.is_agnostic() {
vpr.write_element(vd_idx, ElemIdx::new(i), wsew, wsew.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), wsew);
let result = compute_widening_macc(op, vs2_val, op1_val, vd_val, ctx.sew, wsew);
vpr.write_element(vd_idx, ElemIdx::new(i), wsew, result);
}
VecExecResult { vxsat: false, scalar_result: None, fp_flags: crate::isa::fp::FpFlags::NONE }
}
pub(super) fn exec_narrowing(
op: NarrowOp,
vpr: &mut impl VectorRegFile,
vd_idx: VRegIdx,
vs2_idx: VRegIdx,
operand1: VecOperand,
ctx: &VecExecCtx,
) -> VecExecResult {
let Some(wsew) = widen_sew(ctx.sew) else {
return VecExecResult {
vxsat: false,
scalar_result: None,
fp_flags: crate::isa::fp::FpFlags::NONE,
};
};
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), wsew);
let op1_val = read_op1(vpr, &operand1, i, ctx.sew);
let (result, sat) = compute_narrowing(op, vs2_val, op1_val, ctx.sew, wsew, 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 }
}