use crate::rv64::test_utils::{TestInterpreterState, initialize_state};
use crate::v::vector_registers::{VectorRegisters, VectorRegistersExt};
use crate::v::zve64x::arith::zve64x_arith_helpers::sign_extend;
use crate::{ExecutableInstruction, ExecutionError, RegisterFile};
use ab_riscv_primitives::prelude::*;
fn encode_vtype(vsew: Vsew, vlmul: Vlmul) -> u64 {
u64::from(vlmul.to_bits()) | (u64::from(vsew.to_bits()) << 3)
}
fn setup(
vl: u32,
vsew: Vsew,
vlmul: Vlmul,
) -> TestInterpreterState<Zve64xFixedPointInstruction<Reg<u64>>> {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
let vtype = Vtype::from_raw::<Reg<u64>>(encode_vtype(vsew, vlmul)).unwrap();
state.ext_state.set_vtype(Some(vtype));
state.ext_state.set_vl(vl);
state.ext_state.set_vstart(0);
state
}
fn setup_with_vxrm(
vl: u32,
vsew: Vsew,
vlmul: Vlmul,
vxrm: Vxrm,
) -> TestInterpreterState<Zve64xFixedPointInstruction<Reg<u64>>> {
let mut state = setup(vl, vsew, vlmul);
state.ext_state.set_vxrm(vxrm);
state
}
fn exec(
state: &mut TestInterpreterState<Zve64xFixedPointInstruction<Reg<u64>>>,
instr: Zve64xFixedPointInstruction<Reg<u64>>,
) -> Result<(), ExecutionError<u64>> {
instr
.execute(
&mut state.regs,
&mut state.ext_state,
&mut state.memory,
&mut state.instruction_fetcher,
&mut state.system_instruction_handler,
)
.map(|_| ())
}
fn write_elem(
state: &mut TestInterpreterState<Zve64xFixedPointInstruction<Reg<u64>>>,
base_reg: VReg,
elem_i: usize,
sew: Vsew,
value: u64,
) {
let sew_bytes = usize::from(sew.bytes());
let elems_per_reg = 16 / sew_bytes;
let reg_off = elem_i / elems_per_reg;
let byte_off = (elem_i % elems_per_reg) * sew_bytes;
let reg = &mut state.ext_state.write_vreg()[usize::from(base_reg.bits()) + reg_off];
let buf = value.to_le_bytes();
reg[byte_off..byte_off + sew_bytes].copy_from_slice(&buf[..sew_bytes]);
}
fn read_elem(
state: &TestInterpreterState<Zve64xFixedPointInstruction<Reg<u64>>>,
base_reg: VReg,
elem_i: usize,
sew: Vsew,
) -> u64 {
let sew_bytes = usize::from(sew.bytes());
let elems_per_reg = 16 / sew_bytes;
let reg_off = elem_i / elems_per_reg;
let byte_off = (elem_i % elems_per_reg) * sew_bytes;
let reg = &state.ext_state.read_vreg()[usize::from(base_reg.bits()) + reg_off];
let mut buf = [0u8; 8];
buf[..sew_bytes].copy_from_slice(®[byte_off..byte_off + sew_bytes]);
u64::from_le_bytes(buf)
}
fn write_wide_elem(
state: &mut TestInterpreterState<Zve64xFixedPointInstruction<Reg<u64>>>,
base_reg: VReg,
elem_i: usize,
sew: Vsew,
value: u64,
) {
let wide_bytes = usize::from(sew.bytes()) * 2;
let elems_per_reg = 16 / wide_bytes;
let reg_off = elem_i / elems_per_reg;
let byte_off = (elem_i % elems_per_reg) * wide_bytes;
let reg = &mut state.ext_state.write_vreg()[usize::from(base_reg.bits()) + reg_off];
let buf = value.to_le_bytes();
reg[byte_off..byte_off + wide_bytes].copy_from_slice(&buf[..wide_bytes]);
}
fn set_mask_bit(
state: &mut TestInterpreterState<Zve64xFixedPointInstruction<Reg<u64>>>,
reg: VReg,
i: u32,
val: bool,
) {
let byte = &mut state.ext_state.write_vreg()[usize::from(reg.bits())][(i / u8::BITS) as usize];
if val {
*byte |= 1 << (i % u8::BITS);
} else {
*byte &= !(1 << (i % u8::BITS));
}
}
fn vxsat(state: &TestInterpreterState<Zve64xFixedPointInstruction<Reg<u64>>>) -> bool {
state.ext_state.vxsat()
}
#[test]
fn vsaddu_vv_e8_no_overflow() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, 10);
write_elem(&mut state, VReg::V1, i, Vsew::E8, 20);
}
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
for i in 0..4 {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 30, "elem {i}");
}
assert!(!vxsat(&state), "vxsat must not be set on no-overflow");
assert_eq!(state.ext_state.vstart(), 0);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
fn vsaddu_vv_e8_saturates_at_max() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 200);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 100);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 0);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E8),
255,
"saturated elem"
);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0, "no-sat elem");
assert!(vxsat(&state), "vxsat must be set");
}
#[test]
fn vsaddu_vv_e32_saturates() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0xFFFF_FFFE);
write_elem(&mut state, VReg::V1, 0, Vsew::E32, 3);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xFFFF_FFFF);
assert!(vxsat(&state));
}
#[test]
fn vsaddu_vv_e64_saturates() {
let mut state = setup(1, Vsew::E64, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, u64::MAX);
write_elem(&mut state, VReg::V1, 0, Vsew::E64, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), u64::MAX);
assert!(vxsat(&state));
}
#[test]
fn vsaddu_vx_e16() {
let mut state = setup(4, Vsew::E16, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E16, 0xFFF0);
}
state.regs.write(Reg::A0, 0x20);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
for i in 0..4 {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E16), 0xFFFF);
}
assert!(vxsat(&state));
}
#[test]
fn vsaddu_vi_e8() {
let mut state = setup(3, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 250);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 251);
write_elem(&mut state, VReg::V2, 2, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 5,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E8),
255,
"exact max, no saturation"
);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 255, "saturated");
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E8), 6);
assert!(vxsat(&state), "elem 1 saturated so vxsat must be set");
}
#[test]
fn vsaddu_vi_e8_high_bit_immediate_sign_extends() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 10);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: -1,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E8),
255,
"10 + 255 saturates"
);
assert_eq!(
read_elem(&state, VReg::V4, 1, Vsew::E8),
255,
"0 + 255 = 255 exact"
);
assert!(vxsat(&state), "elem 0 saturated");
}
#[test]
fn vsaddu_vi_e16_sign_extends_to_sew() {
let mut state = setup(1, Vsew::E16, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: -1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E16), 0xFFFF);
assert!(vxsat(&state));
}
#[test]
fn vsaddu_vxsat_is_sticky() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 200);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 100);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert!(vxsat(&state));
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 1);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert!(vxsat(&state), "vxsat must remain set (sticky)");
}
#[test]
fn vsadd_vv_e8_positive_overflow() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 127);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E8),
127,
"clamped at i8::MAX"
);
assert!(vxsat(&state));
}
#[test]
fn vsadd_vv_e8_negative_overflow() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0x80u64);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0xFFu64);
exec(
&mut state,
Zve64xFixedPointInstruction::VsaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E8),
0x80,
"clamped at i8::MIN"
);
assert!(vxsat(&state));
}
#[test]
fn vsadd_vv_e8_no_overflow() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 50);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 50);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0xF0u64);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 10);
exec(
&mut state,
Zve64xFixedPointInstruction::VsaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 100);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8) as i8, -6i8);
assert!(!vxsat(&state));
}
#[test]
fn vsadd_vv_e32_max_plus_one() {
let mut state = setup(1, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0x7FFF_FFFFu64);
write_elem(&mut state, VReg::V1, 0, Vsew::E32, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0x7FFF_FFFF);
assert!(vxsat(&state));
}
#[test]
fn vsadd_vi_sign_extends_immediate() {
let mut state = setup(1, Vsew::E16, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 0);
exec(
&mut state,
Zve64xFixedPointInstruction::VsaddVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: -1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E16), 0xFFFF);
assert!(!vxsat(&state));
}
#[test]
fn vssubu_vv_e8_no_underflow() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, 50);
write_elem(&mut state, VReg::V1, i, Vsew::E8, 30);
}
exec(
&mut state,
Zve64xFixedPointInstruction::VssubuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
for i in 0..4 {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 20);
}
assert!(!vxsat(&state));
}
#[test]
fn vssubu_vv_e8_clamps_at_zero() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 10);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 20);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VssubuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0);
assert!(vxsat(&state));
}
#[test]
fn vssubu_vx_e64_clamps() {
let mut state = setup(1, Vsew::E64, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, 5);
state.regs.write(Reg::A0, 10u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VssubuVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), 0);
assert!(vxsat(&state));
}
#[test]
fn vssub_vv_e8_positive_underflow() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0x80u64);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VssubVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E8),
0x80,
"clamped at i8::MIN"
);
assert!(vxsat(&state));
}
#[test]
fn vssub_vv_e8_positive_overflow() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 127);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0xFFu64);
exec(
&mut state,
Zve64xFixedPointInstruction::VssubVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E8),
127,
"clamped at i8::MAX"
);
assert!(vxsat(&state));
}
#[test]
fn vssub_vx_e32_no_overflow() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 100);
write_elem(&mut state, VReg::V2, 1, Vsew::E32, 0xFFFF_FF00u64);
state.regs.write(Reg::A1, 50u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VssubVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 50);
assert_eq!(
read_elem(&state, VReg::V4, 1, Vsew::E32),
(0xFFFF_FF00u64.wrapping_sub(50)) & 0xFFFF_FFFF
);
assert!(!vxsat(&state));
}
#[test]
fn vaaddu_vv_e8_rnu_basic() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 3);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 4);
exec(
&mut state,
Zve64xFixedPointInstruction::VaadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 4);
assert!(!vxsat(&state), "averaging does not set vxsat");
}
#[test]
fn vaaddu_vv_e8_rdn_truncates() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 3);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 4);
exec(
&mut state,
Zve64xFixedPointInstruction::VaadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 3);
}
#[test]
fn vaaddu_vv_e8_even_sum_all_modes_same() {
for mode in [Vxrm::Rnu, Vxrm::Rne, Vxrm::Rdn, Vxrm::Rod] {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, mode);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 2);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 4);
exec(
&mut state,
Zve64xFixedPointInstruction::VaadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 3, "mode={mode:?}");
}
}
#[test]
fn vaaddu_vv_e8_overflow_wraps_correctly() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 255);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 255);
exec(
&mut state,
Zve64xFixedPointInstruction::VaadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 255);
}
#[test]
fn vaaddu_vx_e32() {
let mut state = setup_with_vxrm(2, Vsew::E32, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 5);
write_elem(&mut state, VReg::V2, 1, Vsew::E32, 6);
state.regs.write(Reg::A0, 5u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VaadduVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 5);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 6);
}
#[test]
fn vaadd_vv_e8_rnu_signed() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFDu64);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0xFCu64);
exec(
&mut state,
Zve64xFixedPointInstruction::VaaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
let result = read_elem(&state, VReg::V4, 0, Vsew::E8);
assert_eq!(sign_extend(result, Vsew::E8), -3);
}
#[test]
fn vaadd_vv_e8_rdn_signed() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFDu64);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0xFCu64);
exec(
&mut state,
Zve64xFixedPointInstruction::VaaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
let result = read_elem(&state, VReg::V4, 0, Vsew::E8);
assert_eq!(sign_extend(result, Vsew::E8), -4);
}
#[test]
fn vaadd_vv_e8_no_overflow() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 127);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0xFFu64);
exec(
&mut state,
Zve64xFixedPointInstruction::VaaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E8), Vsew::E8),
63
);
assert!(!vxsat(&state));
}
#[test]
fn vaadd_vx_e64_rne() {
let mut state = setup_with_vxrm(1, Vsew::E64, Vlmul::M1, Vxrm::Rne);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, 3);
state.regs.write(Reg::A0, 4u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VaaddVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), 4);
}
#[test]
fn vasubu_vv_e8_rdn() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 5);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 2);
exec(
&mut state,
Zve64xFixedPointInstruction::VasubuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 1);
assert!(!vxsat(&state));
}
#[test]
fn vasubu_vv_e8_rnu_odd_diff() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 5);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 2);
exec(
&mut state,
Zve64xFixedPointInstruction::VasubuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 2);
}
#[test]
fn vasubu_vx_e8_underflow_wraps_to_large() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 2);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 5);
exec(
&mut state,
Zve64xFixedPointInstruction::VasubuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 254);
assert!(!vxsat(&state));
}
#[test]
fn vasub_vv_e8_rnu() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFDu64);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 2);
exec(
&mut state,
Zve64xFixedPointInstruction::VasubVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E8), Vsew::E8),
-2
);
assert!(!vxsat(&state));
}
#[test]
fn vasub_vx_e16_rdn() {
let mut state = setup_with_vxrm(1, Vsew::E16, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 0x8000);
state.regs.write(Reg::A0, 0xFFFF_FFFF_FFFF_8001u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VasubVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E16), Vsew::E16),
-1
);
}
#[test]
fn vsmul_vv_e8_basic() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 2);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 3);
exec(
&mut state,
Zve64xFixedPointInstruction::VsmulVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0);
assert!(!vxsat(&state));
}
#[test]
fn vsmul_vv_e8_larger_values() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 64);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 64);
exec(
&mut state,
Zve64xFixedPointInstruction::VsmulVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E8), Vsew::E8),
32
);
assert!(!vxsat(&state));
}
#[test]
fn vsmul_vv_e8_int_min_saturates() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0x80u64);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0x80u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VsmulVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E8), Vsew::E8),
127
);
assert!(vxsat(&state));
}
#[test]
fn vsmul_vv_e64_int_min_saturates() {
let mut state = setup_with_vxrm(1, Vsew::E64, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, i64::MIN.cast_unsigned());
write_elem(&mut state, VReg::V1, 0, Vsew::E64, i64::MIN.cast_unsigned());
exec(
&mut state,
Zve64xFixedPointInstruction::VsmulVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E64), Vsew::E64),
i64::MAX,
"INT64_MIN * INT64_MIN must saturate to INT64_MAX"
);
assert!(vxsat(&state));
}
#[test]
fn vsmul_vv_e16_rnu_rounding() {
let mut state = setup_with_vxrm(1, Vsew::E16, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 181);
write_elem(&mut state, VReg::V1, 0, Vsew::E16, 181);
exec(
&mut state,
Zve64xFixedPointInstruction::VsmulVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E16), Vsew::E16),
1
);
assert!(!vxsat(&state));
let mut state2 = setup_with_vxrm(1, Vsew::E16, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state2, VReg::V2, 0, Vsew::E16, 181);
write_elem(&mut state2, VReg::V1, 0, Vsew::E16, 181);
exec(
&mut state2,
Zve64xFixedPointInstruction::VsmulVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state2, VReg::V4, 0, Vsew::E16), Vsew::E16),
0
);
}
#[test]
fn vsmul_vx_e16_negative() {
let mut state = setup_with_vxrm(1, Vsew::E16, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 0xFF9Cu64);
state.regs.write(Reg::A0, 200u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VsmulVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E16), Vsew::E16),
-1
);
assert!(!vxsat(&state));
}
#[test]
fn vssrl_vv_e8_rdn_basic() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0b1010_1010);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 2);
exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 42);
assert!(!vxsat(&state));
}
#[test]
fn vssrl_vv_e8_rnu_rounds_up() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 3);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 2);
}
#[test]
fn vssrl_vv_e8_shift_zero() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xAB);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0);
exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0xAB);
}
#[test]
fn vssrl_vv_e8_shift_masked_to_log2_sew() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFF);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 11);
exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 31);
}
#[test]
fn vssrl_vx_e32_rne() {
let mut state = setup_with_vxrm(1, Vsew::E32, Vlmul::M1, Vxrm::Rne);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 7);
state.regs.write(Reg::A0, 1u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 4);
}
#[test]
fn vssrl_vi_e16_rod() {
let mut state = setup_with_vxrm(1, Vsew::E16, Vlmul::M1, Vxrm::Rod);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 6);
exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 2,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E16), 1);
}
#[test]
fn vssrl_vi_e16_rod_sets_lsb() {
let mut state = setup_with_vxrm(1, Vsew::E16, Vlmul::M1, Vxrm::Rod);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 10);
exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 2,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E16), 3);
}
#[test]
fn vssra_vv_e8_rdn_negative() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xF8u64);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 2);
exec(
&mut state,
Zve64xFixedPointInstruction::VssraVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E8), Vsew::E8),
-2
);
assert!(!vxsat(&state));
}
#[test]
fn vssra_vv_e8_rnu_negative() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xF9u64);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VssraVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E8), Vsew::E8),
-3
);
}
#[test]
fn vssra_vv_e8_positive_rnu() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rnu);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 7);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VssraVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E8), Vsew::E8),
4
);
}
#[test]
fn vssra_vx_e32() {
let mut state = setup_with_vxrm(1, Vsew::E32, Vlmul::M1, Vxrm::Rdn);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0xFFFF_FF00u64);
state.regs.write(Reg::A0, 4u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VssraVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E32), Vsew::E32),
-16
);
}
#[test]
fn vssra_vi_e64_rne_tie_to_even() {
let mut state = setup_with_vxrm(1, Vsew::E64, Vlmul::M1, Vxrm::Rne);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, 6);
exec(
&mut state,
Zve64xFixedPointInstruction::VssraVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 2,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E64), Vsew::E64),
2
);
}
#[test]
fn vnclipu_wv_e8_no_clip() {
let mut state = setup_with_vxrm(2, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E8, 0x00F0);
write_wide_elem(&mut state, VReg::V4, 1, Vsew::E8, 0x0080);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 4);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 4);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWv {
vd: VReg::V8,
vs2: VReg::V4,
vs1: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V8, 0, Vsew::E8), 15);
assert_eq!(read_elem(&state, VReg::V8, 1, Vsew::E8), 8);
assert!(!vxsat(&state));
}
#[test]
fn vnclipu_wv_e8_saturates() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E8, 0x0200);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWv {
vd: VReg::V8,
vs2: VReg::V4,
vs1: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V8, 0, Vsew::E8), 255);
assert!(vxsat(&state));
}
#[test]
fn vnclipu_wx_e16_rnu() {
let mut state = setup_with_vxrm(1, Vsew::E16, Vlmul::M1, Vxrm::Rnu);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E16, 0x0001_FFFF);
state.regs.write(Reg::A0, 16u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWx {
vd: VReg::V8,
vs2: VReg::V4,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V8, 0, Vsew::E16), 2);
assert!(!vxsat(&state));
}
#[test]
fn vnclipu_wi_e8_shift_zero() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E8, 0x01FF);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWi {
vd: VReg::V8,
vs2: VReg::V4,
imm: 0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V8, 0, Vsew::E8), 255);
assert!(vxsat(&state));
}
#[test]
fn vnclipu_e64_illegal() {
let mut state = setup(1, Vsew::E64, Vlmul::M1);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWi {
vd: VReg::V8,
vs2: VReg::V4,
imm: 0,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vnclipu_shamt_masked_to_log2_2sew() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E8, 0xFFFF);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWi {
vd: VReg::V8,
vs2: VReg::V4,
imm: 0x1F,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V8, 0, Vsew::E8), 1);
}
#[test]
fn vnclipu_lmul8_illegal() {
let mut state = setup(1, Vsew::E8, Vlmul::M8);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWi {
vd: VReg::V8,
vs2: VReg::V0,
imm: 0,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vnclip_lmul8_illegal() {
let mut state = setup(1, Vsew::E8, Vlmul::M8);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWi {
vd: VReg::V8,
vs2: VReg::V0,
imm: 0,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vnclip_wv_e8_no_clip() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E8, 0xFFF6u64);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 2);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWv {
vd: VReg::V8,
vs2: VReg::V4,
vs1: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V8, 0, Vsew::E8), Vsew::E8),
-3
);
assert!(!vxsat(&state));
}
#[test]
fn vnclip_wv_e8_positive_saturates_at_max() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E8, 0x7FFF);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 7);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWv {
vd: VReg::V8,
vs2: VReg::V4,
vs1: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V8, 0, Vsew::E8), Vsew::E8),
127
);
assert!(vxsat(&state));
}
#[test]
fn vnclip_wv_e8_negative_saturates_at_min() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E8, 0x8000);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 7);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWv {
vd: VReg::V8,
vs2: VReg::V4,
vs1: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V8, 0, Vsew::E8), Vsew::E8),
-128
);
assert!(vxsat(&state));
}
#[test]
fn vnclip_wx_e16_rnu() {
let mut state = setup_with_vxrm(1, Vsew::E16, Vlmul::M1, Vxrm::Rnu);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E16, 0xFFFF_FFFFu64);
state.regs.write(Reg::A0, 1u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWx {
vd: VReg::V8,
vs2: VReg::V4,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V8, 0, Vsew::E16), Vsew::E16),
0
);
assert!(!vxsat(&state));
}
#[test]
fn vnclip_wi_e8() {
let mut state = setup_with_vxrm(1, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E8, 127);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWi {
vd: VReg::V8,
vs2: VReg::V4,
imm: 0,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V8, 0, Vsew::E8), Vsew::E8),
127
);
assert!(!vxsat(&state));
}
#[test]
fn vnclip_e64_illegal() {
let mut state = setup(1, Vsew::E64, Vlmul::M1);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWi {
vd: VReg::V8,
vs2: VReg::V4,
imm: 0,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsaddu_masked_skips_inactive_elements() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, 100);
write_elem(&mut state, VReg::V1, i, Vsew::E8, 200);
write_elem(&mut state, VReg::V4, i, Vsew::E8, 0x55);
}
set_mask_bit(&mut state, VReg::V0, 0, true);
set_mask_bit(&mut state, VReg::V0, 1, false);
set_mask_bit(&mut state, VReg::V0, 2, true);
set_mask_bit(&mut state, VReg::V0, 3, false);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 255);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E8), 255);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0x55);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E8), 0x55);
}
#[test]
fn vsaddu_masked_vd_overlap_v0_illegal() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V0,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vssrl_masked_only_active_written() {
let mut state = setup_with_vxrm(4, Vsew::E8, Vlmul::M1, Vxrm::Rdn);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, 0xFF);
write_elem(&mut state, VReg::V4, i, Vsew::E8, 0xAA);
}
set_mask_bit(&mut state, VReg::V0, 0, true);
set_mask_bit(&mut state, VReg::V0, 1, false);
set_mask_bit(&mut state, VReg::V0, 2, false);
set_mask_bit(&mut state, VReg::V0, 3, true);
state.regs.write(Reg::A0, 4u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0xFF >> 4);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0xAA);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E8), 0xAA);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E8), 0xFF >> 4);
}
#[test]
fn vsaddu_vstart_skips_early_elements() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, 200);
write_elem(&mut state, VReg::V1, i, Vsew::E8, 100);
write_elem(&mut state, VReg::V4, i, Vsew::E8, 0x55);
}
state.ext_state.set_vstart(2);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0x55);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0x55);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E8), 255);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E8), 255);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsaddu_vector_not_allowed_faults() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsmul_vector_not_allowed_faults() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VsmulVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vnclip_vector_not_allowed_faults() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWi {
vd: VReg::V8,
vs2: VReg::V4,
imm: 0,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsaddu_vtype_none_faults() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
state.ext_state.set_vtype(None);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vssrl_vtype_none_faults() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
state.ext_state.set_vtype(None);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 1,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsaddu_vd_misaligned_m2_faults() {
let mut state = setup(2, Vsew::E8, Vlmul::M2);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V3,
vs2: VReg::V2,
vs1: VReg::V0,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsaddu_vs2_misaligned_m2_faults() {
let mut state = setup(2, Vsew::E8, Vlmul::M2);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V2,
vs2: VReg::V3,
vs1: VReg::V0,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vnclipu_vs2_misaligned_m1_faults() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWi {
vd: VReg::V8,
vs2: VReg::V3,
imm: 0,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsaddu_aligned_m4_ok() {
let mut state = setup(1, Vsew::E8, Vlmul::M4);
for i in 0..4usize {
write_elem(&mut state, VReg::V8, i, Vsew::E8, 1);
write_elem(&mut state, VReg::V12, i, Vsew::E8, 2);
}
let result = exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V8,
vs1: VReg::V12,
vm: true,
},
);
assert!(result.is_ok());
}
#[test]
fn vs_dirty_increments_per_instruction() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 1);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(state.ext_state.vs_dirty_count(), 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(state.ext_state.vs_dirty_count(), 2);
}
#[test]
fn vstart_resets_to_zero_after_execution() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.ext_state.set_vstart(2);
write_elem(&mut state, VReg::V2, 2, Vsew::E8, 1);
write_elem(&mut state, VReg::V1, 2, Vsew::E8, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(state.ext_state.vstart(), 0, "vstart must be reset to 0");
}
#[test]
fn vsaddu_vl_zero_no_writes() {
let mut state = setup(0, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V4, i, Vsew::E8, 0xAB);
}
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
for i in 0..4 {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
0xAB,
"elem {i} must be undisturbed"
);
}
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsadd_all_sew_sizes_max_overflow() {
for (vsew, max_val, min_val) in [
(Vsew::E8, 0x7Fu64, 0x80u64),
(Vsew::E16, 0x7FFFu64, 0x8000u64),
(Vsew::E32, 0x7FFF_FFFFu64, 0x8000_0000u64),
(
Vsew::E64,
i64::MAX.cast_unsigned(),
i64::MIN.cast_unsigned(),
),
] {
let mut state = setup(1, vsew, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, vsew, max_val);
write_elem(&mut state, VReg::V1, 0, vsew, 1);
exec(
&mut state,
Zve64xFixedPointInstruction::VsaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, vsew), max_val, "SEW={vsew}");
assert!(vxsat(&state), "SEW={vsew}");
let mut state2 = setup(1, vsew, Vlmul::M1);
write_elem(&mut state2, VReg::V2, 0, vsew, min_val);
write_elem(&mut state2, VReg::V1, 0, vsew, 0xFFFF_FFFF_FFFF_FFFFu64);
exec(
&mut state2,
Zve64xFixedPointInstruction::VsaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state2, VReg::V4, 0, vsew),
min_val,
"SEW={vsew} underflow"
);
assert!(vxsat(&state2), "SEW={vsew} underflow vxsat");
}
}
#[test]
fn vssubu_all_sew_sizes_clamps_zero() {
for vsew in [Vsew::E8, Vsew::E16, Vsew::E32, Vsew::E64] {
let mut state = setup(1, vsew, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, vsew, 5);
write_elem(&mut state, VReg::V1, 0, vsew, 10);
exec(
&mut state,
Zve64xFixedPointInstruction::VssubuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, vsew), 0, "SEW={vsew}");
assert!(vxsat(&state), "SEW={vsew}");
}
}
#[test]
fn vnclipu_e32_no_clip() {
let mut state = setup_with_vxrm(1, Vsew::E32, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E32, 0x0000_0001_0000_0000u64);
state.regs.write(Reg::A0, 32u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWx {
vd: VReg::V8,
vs2: VReg::V4,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V8, 0, Vsew::E32), 1);
assert!(!vxsat(&state));
}
#[test]
fn vnclipu_e32_saturates() {
let mut state = setup_with_vxrm(1, Vsew::E32, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E32, u64::MAX);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipuWi {
vd: VReg::V8,
vs2: VReg::V4,
imm: 0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V8, 0, Vsew::E32), u32::MAX as u64);
assert!(vxsat(&state));
}
#[test]
fn vnclip_e32_no_clip() {
let mut state = setup_with_vxrm(1, Vsew::E32, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E32, u64::MAX);
state.regs.write(Reg::A0, 32u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWx {
vd: VReg::V8,
vs2: VReg::V4,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V8, 0, Vsew::E32), Vsew::E32),
-1
);
assert!(!vxsat(&state));
}
#[test]
fn vnclip_e32_saturates_positive() {
let mut state = setup_with_vxrm(1, Vsew::E32, Vlmul::M1, Vxrm::Rdn);
write_wide_elem(&mut state, VReg::V4, 0, Vsew::E32, i64::MAX as u64);
exec(
&mut state,
Zve64xFixedPointInstruction::VnclipWi {
vd: VReg::V8,
vs2: VReg::V4,
imm: 31,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V8, 0, Vsew::E32), i32::MAX as u64);
assert!(vxsat(&state));
}
#[test]
fn vssrl_rod_result_even_sets_lsb() {
let mut state = setup_with_vxrm(2, Vsew::E8, Vlmul::M1, Vxrm::Rod);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 8);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 10);
exec(
&mut state,
Zve64xFixedPointInstruction::VssrlVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 2,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E8),
2,
"no discarded bits"
);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 3, "rod sets lsb");
}
#[test]
fn vssra_rod_result_even_sets_lsb() {
let mut state = setup_with_vxrm(2, Vsew::E8, Vlmul::M1, Vxrm::Rod);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xF8u64);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0xFAu64);
exec(
&mut state,
Zve64xFixedPointInstruction::VssraVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 2,
vm: true,
},
)
.unwrap();
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 0, Vsew::E8), Vsew::E8),
-2,
"no discarded bits"
);
assert_eq!(
sign_extend(read_elem(&state, VReg::V4, 1, Vsew::E8), Vsew::E8),
-1,
"rod sets lsb"
);
}
#[test]
fn vsaddu_mixed_sat_e16_m1() {
let mut state = setup(8, Vsew::E16, Vlmul::M1);
let inputs: [(u64, u64); 8] = [
(0, 0),
(0xFFFF, 1), (0x8000, 0x7FFF), (100, 200),
(0xFFFE, 1), (0xFFFE, 0), (1, 1),
(0xFFFF, 0),
];
for (i, (a, b)) in inputs.iter().enumerate() {
write_elem(&mut state, VReg::V2, i, Vsew::E16, *a);
write_elem(&mut state, VReg::V1, i, Vsew::E16, *b);
}
exec(
&mut state,
Zve64xFixedPointInstruction::VsadduVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E16), 0);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E16), 0xFFFF);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E16), 0xFFFF);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E16), 300);
assert_eq!(read_elem(&state, VReg::V4, 4, Vsew::E16), 0xFFFF);
assert_eq!(read_elem(&state, VReg::V4, 5, Vsew::E16), 0xFFFE);
assert_eq!(read_elem(&state, VReg::V4, 6, Vsew::E16), 2);
assert_eq!(read_elem(&state, VReg::V4, 7, Vsew::E16), 0xFFFF);
assert!(vxsat(&state));
}