use crate::rv64::test_utils::{TestInterpreterState, initialize_state};
use crate::v::vector_registers::{VectorRegisters, VectorRegistersExt};
use crate::{ExecutableInstruction, ExecutionError, RegisterFile};
use ab_riscv_primitives::prelude::*;
fn encode_vtype(vsew: Vsew, vlmul: Vlmul) -> u64 {
(vlmul.to_bits() as u64) | ((vsew.to_bits() as u64) << 3)
}
fn setup(
vl: u32,
vsew: Vsew,
vlmul: Vlmul,
) -> TestInterpreterState<Zve64xArithInstruction<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 exec(
state: &mut TestInterpreterState<Zve64xArithInstruction<Reg<u64>>>,
instr: Zve64xArithInstruction<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 set_vreg(
state: &mut TestInterpreterState<Zve64xArithInstruction<Reg<u64>>>,
reg: VReg,
data: &[u8],
) {
let dst = &mut state.ext_state.write_vreg()[usize::from(reg.bits())];
dst.fill(0);
dst[..data.len()].copy_from_slice(data);
}
fn get_vreg(state: &TestInterpreterState<Zve64xArithInstruction<Reg<u64>>>, reg: VReg) -> [u8; 16] {
state.ext_state.read_vreg()[usize::from(reg.bits())]
}
fn read_elem(
state: &TestInterpreterState<Zve64xArithInstruction<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_elem(
state: &mut TestInterpreterState<Zve64xArithInstruction<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 mask_bit(
state: &TestInterpreterState<Zve64xArithInstruction<Reg<u64>>>,
reg: VReg,
i: u32,
) -> bool {
let byte = state.ext_state.read_vreg()[usize::from(reg.bits())][(i / u8::BITS) as usize];
(byte >> (i % u8::BITS)) & 1 != 0
}
#[test]
fn vadd_vv_e8_m1_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, (i + 1) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E8, ((i + 1) * 10) as u64);
}
exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
((i + 1) * 11) as u64,
"elem {i}"
);
}
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vadd_vv_e64_m1_wraps() {
let mut state = setup(2, 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,
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), 0);
}
#[test]
fn vadd_vx_e32_m1() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, i as u64);
}
state.regs.write(Reg::A0, 100);
exec(
&mut state,
Zve64xArithInstruction::VaddVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
i as u64 + 100,
"elem {i}"
);
}
}
#[test]
fn vadd_vi_e16_m1_negative_imm() {
let mut state = setup(4, Vsew::E16, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E16, 10);
}
exec(
&mut state,
Zve64xArithInstruction::VaddVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: -1,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E16), 9, "elem {i}");
}
}
#[test]
fn vadd_vv_e8_m2_spans_two_regs() {
let mut state = setup(32, Vsew::E8, Vlmul::M2);
for i in 0..32usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, i as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E8, 1);
}
exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V6,
vs2: VReg::V2,
vs1: VReg::V4,
vm: true,
},
)
.unwrap();
for i in 0..32usize {
assert_eq!(
read_elem(&state, VReg::V6, i, Vsew::E8),
i as u64 + 1,
"elem {i}"
);
}
}
#[test]
fn vsub_vv_e8_m1() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, (i + 10) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E8, i as u64);
}
exec(
&mut state,
Zve64xArithInstruction::VsubVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 10, "elem {i}");
}
}
#[test]
fn vsub_vx_e32_wraps() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0);
state.regs.write(Reg::A0, 1);
exec(
&mut state,
Zve64xArithInstruction::VsubVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xFFFF_FFFF);
}
#[test]
fn vrsub_vx_e8_m1() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, i as u64);
}
state.regs.write(Reg::A0, 10);
exec(
&mut state,
Zve64xArithInstruction::VrsubVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
(10 - i) as u64,
"elem {i}"
);
}
}
#[test]
fn vrsub_vi_e16_m1() {
let mut state = setup(4, Vsew::E16, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E16, i as u64);
}
exec(
&mut state,
Zve64xArithInstruction::VrsubVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 5,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
let expected = (5i64 - (i as u64).cast_signed()).rem_euclid(1 << 16) as u64;
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E16),
expected,
"elem {i}"
);
}
}
#[test]
fn vand_vv_e32_m1() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, 0xFF00_FF00);
write_elem(&mut state, VReg::V1, i, Vsew::E32, 0xF0F0_F0F0);
}
exec(
&mut state,
Zve64xArithInstruction::VandVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
0xF000_F000,
"elem {i}"
);
}
}
#[test]
fn vand_vi_sign_extends_imm() {
let mut state = setup(2, Vsew::E16, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 0xABCD);
write_elem(&mut state, VReg::V2, 1, Vsew::E16, 0x1234);
exec(
&mut state,
Zve64xArithInstruction::VandVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: -1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E16), 0xABCD);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E16), 0x1234);
}
#[test]
fn vor_vx_e64_m1() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, 0x0F0F_0F0F_0F0F_0F0F);
write_elem(&mut state, VReg::V2, 1, Vsew::E64, 0);
state.regs.write(Reg::A0, 0xF0F0_F0F0_F0F0_F0F0_u64);
exec(
&mut state,
Zve64xArithInstruction::VorVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), u64::MAX);
assert_eq!(
read_elem(&state, VReg::V4, 1, Vsew::E64),
0xF0F0_F0F0_F0F0_F0F0_u64
);
}
#[test]
fn vxor_vi_e8_m1() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, 0xAA);
}
exec(
&mut state,
Zve64xArithInstruction::VxorVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: -1, vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 0x55, "elem {i}");
}
}
#[test]
fn vsll_vv_e8_masks_shamt_to_3_bits() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0xFF);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 9);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 8);
exec(
&mut state,
Zve64xArithInstruction::VsllVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0x02);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0xFF);
}
#[test]
fn vsll_vi_e16_m1() {
let mut state = setup(4, Vsew::E16, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E16, 1);
}
exec(
&mut state,
Zve64xArithInstruction::VsllVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 4,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E16),
1 << 4,
"elem {i}"
);
}
}
#[test]
fn vsrl_vv_e32_logical_shift() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0x8000_0000);
write_elem(&mut state, VReg::V1, 0, Vsew::E32, 1);
exec(
&mut state,
Zve64xArithInstruction::VsrlVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0x4000_0000);
}
#[test]
fn vsrl_vx_e8_does_not_bleed_upper_bits() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
state.ext_state.write_vreg()[usize::from(VReg::V2.bits())][0] = 0xAB;
state.regs.write(Reg::A0, 1);
exec(
&mut state,
Zve64xArithInstruction::VsrlVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0x55);
}
#[test]
fn vsra_vv_e8_arithmetic_shift() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
state.ext_state.write_vreg()[usize::from(VReg::V2.bits())][0] = 0x80;
state.ext_state.write_vreg()[usize::from(VReg::V2.bits())][1] = 0x40;
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 1);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 1);
exec(
&mut state,
Zve64xArithInstruction::VsraVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0xC0);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0x20);
}
#[test]
fn vsra_vi_e32_m1() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0x8000_0000);
write_elem(&mut state, VReg::V2, 1, Vsew::E32, 0x0000_0010);
exec(
&mut state,
Zve64xArithInstruction::VsraVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 4,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xF800_0000);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 1);
}
#[test]
fn vsra_vx_e64_m1() {
let mut state = setup(1, Vsew::E64, Vlmul::M1);
write_elem(
&mut state,
VReg::V2,
0,
Vsew::E64,
0x8000_0000_0000_0000_u64,
);
state.regs.write(Reg::A0, 63);
exec(
&mut state,
Zve64xArithInstruction::VsraVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), u64::MAX);
}
#[test]
fn vminu_vv_e8_unsigned() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFF);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 0xFF);
exec(
&mut state,
Zve64xArithInstruction::VminuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0x01);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0x01);
}
#[test]
fn vmin_vv_e8_signed() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFF);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 0xFF);
exec(
&mut state,
Zve64xArithInstruction::VminVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0xFF);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0xFF);
}
#[test]
fn vmaxu_vx_e32() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0xFFFF_FFFF);
write_elem(&mut state, VReg::V2, 1, Vsew::E32, 5);
state.regs.write(Reg::A0, 10);
exec(
&mut state,
Zve64xArithInstruction::VmaxuVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xFFFF_FFFF);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 10);
}
#[test]
fn vmax_vx_e16_signed() {
let mut state = setup(2, Vsew::E16, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 0xFFFF);
write_elem(&mut state, VReg::V2, 1, Vsew::E16, 5);
state.regs.write(Reg::A0, 0);
exec(
&mut state,
Zve64xArithInstruction::VmaxVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E16), 0);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E16), 5);
}
#[test]
fn vmseq_vv_e8_m1_writes_mask_bits() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
for i in 0..8usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, i as u64);
write_elem(
&mut state,
VReg::V1,
i,
Vsew::E8,
if i % 2 == 0 { i as u64 } else { 99 },
);
}
set_vreg(&mut state, VReg::V4, &[0xFF; 16]);
exec(
&mut state,
Zve64xArithInstruction::VmseqVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert!(mask_bit(&state, VReg::V4, 0));
assert!(!mask_bit(&state, VReg::V4, 1));
assert!(mask_bit(&state, VReg::V4, 2));
assert!(!mask_bit(&state, VReg::V4, 3));
assert!(mask_bit(&state, VReg::V4, 4));
assert!(!mask_bit(&state, VReg::V4, 5));
assert!(mask_bit(&state, VReg::V4, 6));
assert!(!mask_bit(&state, VReg::V4, 7));
for i in 8..128usize {
assert_eq!(
(state.ext_state.read_vreg()[usize::from(VReg::V4.bits())][i / u8::BITS as usize]
>> (i % u8::BITS as usize))
& 1,
1,
"tail bit {i} was disturbed"
);
}
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vmseq_vx_e32_m1() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(
&mut state,
VReg::V2,
i,
Vsew::E32,
if i == 2 { 42 } else { i as u64 },
);
}
state.regs.write(Reg::A0, 42);
exec(
&mut state,
Zve64xArithInstruction::VmseqVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x0F, 0b0100);
}
#[test]
fn vmseq_vi_e16() {
let mut state = setup(4, Vsew::E16, Vlmul::M1);
for i in 0..4usize {
write_elem(
&mut state,
VReg::V2,
i,
Vsew::E16,
if i == 1 { 3 } else { 0 },
);
}
exec(
&mut state,
Zve64xArithInstruction::VmseqVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 3,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x0F, 0b0010);
}
#[test]
fn vmsne_vv_e8() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, i as u64);
write_elem(
&mut state,
VReg::V1,
i,
Vsew::E8,
if i == 1 { 99 } else { i as u64 },
);
}
exec(
&mut state,
Zve64xArithInstruction::VmsneVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x0F, 0b0010);
}
#[test]
fn vmsltu_vv_e8_unsigned() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFF);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 0xFF);
exec(
&mut state,
Zve64xArithInstruction::VmsltuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x03, 0b10);
}
#[test]
fn vmslt_vv_e8_signed() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFF);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 0xFF);
exec(
&mut state,
Zve64xArithInstruction::VmsltVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x03, 0b01);
}
#[test]
fn vmsleu_vv_e16() {
let mut state = setup(3, Vsew::E16, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 5);
write_elem(&mut state, VReg::V2, 1, Vsew::E16, 6);
write_elem(&mut state, VReg::V2, 2, Vsew::E16, 0xFFFF);
write_elem(&mut state, VReg::V1, 0, Vsew::E16, 5);
write_elem(&mut state, VReg::V1, 1, Vsew::E16, 10);
write_elem(&mut state, VReg::V1, 2, Vsew::E16, 0);
exec(
&mut state,
Zve64xArithInstruction::VmsleuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x07, 0b011);
}
#[test]
fn vmsleu_vi_negative_imm_always_true() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, i as u64);
}
state.ext_state.write_vreg()[usize::from(VReg::V4.bits())][0] = 0x00;
exec(
&mut state,
Zve64xArithInstruction::VmsleuVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: -1,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x0F, 0x0F);
}
#[test]
fn vmsle_vi_e8_signed() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFF);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0x00);
write_elem(&mut state, VReg::V2, 2, Vsew::E8, 0x01);
write_elem(&mut state, VReg::V2, 3, Vsew::E8, 0xFE);
exec(
&mut state,
Zve64xArithInstruction::VmsleVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: -1,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x0F, 0b1001);
}
#[test]
fn vmsgtu_vi_e8_unsigned() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 4);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 5);
write_elem(&mut state, VReg::V2, 2, Vsew::E8, 6);
write_elem(&mut state, VReg::V2, 3, Vsew::E8, 0xFF);
exec(
&mut state,
Zve64xArithInstruction::VmsgtuVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: 5,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x0F, 0b1100);
}
#[test]
fn vmsgt_vx_e32_signed() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0xFFFF_FFFF);
write_elem(&mut state, VReg::V2, 1, Vsew::E32, 0);
write_elem(&mut state, VReg::V2, 2, Vsew::E32, 1);
write_elem(&mut state, VReg::V2, 3, Vsew::E32, 100);
state.regs.write(Reg::A0, 0u64);
exec(
&mut state,
Zve64xArithInstruction::VmsgtVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x0F, 0b1100);
}
#[test]
fn vmsgt_vi_e8_signed() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0xFF);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0x00);
write_elem(&mut state, VReg::V2, 2, Vsew::E8, 0x7F);
write_elem(&mut state, VReg::V2, 3, Vsew::E8, 0xFE);
exec(
&mut state,
Zve64xArithInstruction::VmsgtVi {
vd: VReg::V4,
vs2: VReg::V2,
imm: -1,
vm: true,
},
)
.unwrap();
let vd = get_vreg(&state, VReg::V4);
assert_eq!(vd[0] & 0x0F, 0b0110);
}
#[test]
fn masked_arith_leaves_inactive_elements_undisturbed() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.ext_state.write_vreg()[0][0] = 0b0101;
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, 10);
write_elem(&mut state, VReg::V1, i, Vsew::E32, 1);
}
for i in 0..4usize {
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xDEAD_BEEF);
}
exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 11);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0xDEAD_BEEF);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 11);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 0xDEAD_BEEF);
}
#[test]
fn masked_compare_leaves_inactive_mask_bits_undisturbed() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.ext_state.write_vreg()[0][0] = 0b0101;
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, 5);
write_elem(&mut state, VReg::V1, i, Vsew::E8, 5);
}
state.ext_state.write_vreg()[usize::from(VReg::V4.bits())][0] = 0xFF;
exec(
&mut state,
Zve64xArithInstruction::VmseqVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
let vd = state.ext_state.read_vreg()[usize::from(VReg::V4.bits())][0];
assert_eq!(vd & 0x0F, 0b1111);
}
#[test]
fn compare_can_write_to_v0_when_masked() {
let mut state = setup(2, Vsew::E8, Vlmul::M1);
state.ext_state.write_vreg()[0][0] = 0xFF;
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 5);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 3);
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 5);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 5);
exec(
&mut state,
Zve64xArithInstruction::VmseqVv {
vd: VReg::V0,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
assert!(mask_bit(&state, VReg::V0, 0));
assert!(!mask_bit(&state, VReg::V0, 1));
}
#[test]
fn vstart_skips_elements_before_vstart() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, 1);
write_elem(&mut state, VReg::V1, i, Vsew::E32, 1);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xDEAD);
}
state.ext_state.set_vstart(2);
exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xDEAD);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0xDEAD);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 2);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 2);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vstart_skips_elements_before_vstart_compare() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, i as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E8, i as u64);
}
state.ext_state.write_vreg()[usize::from(VReg::V4.bits())][0] = 0x00;
state.ext_state.set_vstart(2);
exec(
&mut state,
Zve64xArithInstruction::VmseqVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
let vd = state.ext_state.read_vreg()[usize::from(VReg::V4.bits())][0];
assert_eq!(vd & 0x0F, 0b1100);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vl_zero_no_elements_written() {
let mut state = setup(0, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xDEAD_BEEF);
}
exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
0xDEAD_BEEF,
"elem {i}"
);
}
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
fn error_vector_instructions_not_allowed() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let result = exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn error_vill_set_vtype() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
state.ext_state.set_vtype(None);
state.ext_state.set_vl(0);
let result = exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn error_vd_misaligned_for_m2() {
let mut state = setup(4, Vsew::E32, Vlmul::M2);
let result = exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V3, vs2: VReg::V2,
vs1: VReg::V4,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn error_vs2_misaligned_for_m2() {
let mut state = setup(4, Vsew::E32, Vlmul::M2);
let result = exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V3, vs1: VReg::V6,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn error_masked_arith_vd_is_v0() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let result = exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V0,
vs2: VReg::V2,
vs1: VReg::V4,
vm: false,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn error_vector_not_allowed_compare() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let result = exec(
&mut state,
Zve64xArithInstruction::VmseqVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vadd_wraps_at_sew_boundary() {
for (vsew, sew_max) in [
(Vsew::E8, 0xFFu64),
(Vsew::E16, 0xFFFF),
(Vsew::E32, 0xFFFF_FFFF),
(Vsew::E64, 0xFFFF_FFFF_FFFF_FFFF_u64),
] {
let mut state = setup(1, vsew, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, vsew, sew_max);
write_elem(&mut state, VReg::V1, 0, vsew, 1);
exec(
&mut state,
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, vsew),
0,
"SEW={vsew:?}: MAX+1 should wrap to 0"
);
}
}
#[test]
fn vsra_all_sew_widths_sign_extends_correctly() {
for (vsew, msb_val) in [
(Vsew::E8, 0x80u64),
(Vsew::E16, 0x8000),
(Vsew::E32, 0x8000_0000),
(Vsew::E64, 0x8000_0000_0000_0000_u64),
] {
let mut state = setup(1, vsew, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, vsew, msb_val);
let shamt = vsew.bits() - 1;
state.regs.write(Reg::A0, shamt as u64);
exec(
&mut state,
Zve64xArithInstruction::VsraVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
let sew_mask = if vsew.bits() == 64 {
u64::MAX
} else {
(1u64 << vsew.bits()) - 1
};
assert_eq!(
read_elem(&state, VReg::V4, 0, vsew),
sew_mask,
"SEW={:?}",
vsew
);
}
}
#[test]
fn every_instruction_marks_vs_dirty_exactly_once() {
let instrs = &[
Zve64xArithInstruction::VaddVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
Zve64xArithInstruction::VsubVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
Zve64xArithInstruction::VandVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
Zve64xArithInstruction::VsllVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
Zve64xArithInstruction::VminuVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
Zve64xArithInstruction::VmseqVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
];
for (n, instr) in instrs.iter().enumerate() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
exec(&mut state, *instr).unwrap();
assert_eq!(
state.ext_state.vs_dirty_count(),
1,
"instr #{n} didn't mark dirty exactly once"
);
assert_eq!(
state.ext_state.vstart(),
0,
"instr #{n} didn't reset vstart"
);
}
}
#[test]
fn error_compare_mask_dest_overlaps_vs2_lmul_gt_1() {
let mut state = setup(8, Vsew::E32, Vlmul::M2);
let result = exec(
&mut state,
Zve64xArithInstruction::VmseqVv {
vd: VReg::V3,
vs2: VReg::V2,
vs1: VReg::V6,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn error_compare_mask_dest_overlaps_vs1_lmul_gt_1() {
let mut state = setup(8, Vsew::E32, Vlmul::M2);
let result = exec(
&mut state,
Zve64xArithInstruction::VmseqVv {
vd: VReg::V7,
vs2: VReg::V2,
vs1: VReg::V6,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn error_compare_mask_dest_overlaps_vs2_lmul_gt_1_vx() {
let mut state = setup(8, Vsew::E32, Vlmul::M2);
state.regs.write(Reg::A0, 0);
let result = exec(
&mut state,
Zve64xArithInstruction::VmseqVx {
vd: VReg::V2,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn compare_mask_dest_may_overlap_source_at_lmul_1() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, 42);
}
state.regs.write(Reg::A0, 42);
exec(
&mut state,
Zve64xArithInstruction::VmseqVx {
vd: VReg::V2,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(
state.ext_state.read_vreg()[usize::from(VReg::V2.bits())][0] & 0x0F,
0x0F
);
}
#[test]
fn compare_mask_dest_outside_source_group_lmul_gt_1_ok() {
let mut state = setup(8, Vsew::E32, Vlmul::M2);
for i in 0..8usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, i as u64);
write_elem(&mut state, VReg::V6, i, Vsew::E32, i as u64);
}
exec(
&mut state,
Zve64xArithInstruction::VmseqVv {
vd: VReg::V8,
vs2: VReg::V2,
vs1: VReg::V6,
vm: true,
},
)
.unwrap();
assert_eq!(
state.ext_state.read_vreg()[usize::from(VReg::V8.bits())][0],
0xFF
);
}