use crate::rv64::test_utils::initialize_state;
use crate::v::vector_registers::{VectorRegisters, VectorRegistersExt};
use crate::{ExecutableInstruction, ExecutionError};
use ab_riscv_primitives::instructions::v::zve64x::perm::Zve64xPermInstruction;
use ab_riscv_primitives::instructions::v::{Vlmul, Vsew, Vtype};
use ab_riscv_primitives::registers::general_purpose::Reg;
use ab_riscv_primitives::registers::vector::VReg;
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,
) -> crate::rv64::test_utils::TestInterpreterState<ZVPerm> {
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
}
type ZVPerm = Zve64xPermInstruction<Reg<u64>>;
fn exec(
state: &mut crate::rv64::test_utils::TestInterpreterState<ZVPerm>,
instr: ZVPerm,
) -> Result<(), ExecutionError<u64>> {
instr.execute(state).map(|_| ())
}
fn read_elem(
state: &crate::rv64::test_utils::TestInterpreterState<ZVPerm>,
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 crate::rv64::test_utils::TestInterpreterState<ZVPerm>,
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 set_vreg_bytes(
state: &mut crate::rv64::test_utils::TestInterpreterState<ZVPerm>,
reg: VReg,
value: u8,
) {
state.ext_state.write_vreg()[usize::from(reg.bits())].fill(value);
}
fn get_vreg_bytes(
state: &crate::rv64::test_utils::TestInterpreterState<ZVPerm>,
reg: VReg,
) -> [u8; 16] {
state.ext_state.read_vreg()[usize::from(reg.bits())]
}
fn set_mask_bit(
state: &mut crate::rv64::test_utils::TestInterpreterState<ZVPerm>,
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));
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_x_s_e8_reads_element_0() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0x42);
write_elem(&mut state, VReg::V2, 1, Vsew::E8, 0xFF);
exec(
&mut state,
ZVPerm::VmvXS {
rd: Reg::A0,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 0x42u64);
assert_eq!(state.ext_state.vstart(), 0);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_x_s_e8_sign_extends_negative() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E8, 0x80);
exec(
&mut state,
ZVPerm::VmvXS {
rd: Reg::A0,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 0xFFFFFFFFFFFFFF80u64);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_x_s_e16_sign_extends_negative() {
let mut state = setup(4, Vsew::E16, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E16, 0x8000);
exec(
&mut state,
ZVPerm::VmvXS {
rd: Reg::A0,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 0xFFFFFFFFFFFF8000u64);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_x_s_e32_sign_extends_negative() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0x8000_0000);
exec(
&mut state,
ZVPerm::VmvXS {
rd: Reg::A0,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 0xFFFFFFFF80000000u64);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_x_s_e64_full_width() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, 0xDEAD_BEEF_CAFE_F00D);
exec(
&mut state,
ZVPerm::VmvXS {
rd: Reg::A0,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 0xDEAD_BEEF_CAFE_F00Du64);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_x_s_vl_zero_still_reads() {
let mut state = setup(0, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0x1234_5678);
exec(
&mut state,
ZVPerm::VmvXS {
rd: Reg::A0,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 0x1234_5678u64);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_x_s_illegal_when_vector_disabled() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let err = exec(
&mut state,
ZVPerm::VmvXS {
rd: Reg::A0,
vs2: VReg::V2,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_x_s_illegal_when_vtype_invalid() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
state.ext_state.set_vtype(None);
let err = exec(
&mut state,
ZVPerm::VmvXS {
rd: Reg::A0,
vs2: VReg::V2,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_s_x_e8_writes_element_0() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.regs.write(Reg::A0, 0xAB);
set_vreg_bytes(&mut state, VReg::V4, 0xFF);
exec(
&mut state,
ZVPerm::VmvSX {
vd: VReg::V4,
rs1: Reg::A0,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0xAB);
for i in 1..16 {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 0xFF, "elem {i}");
}
assert_eq!(state.ext_state.vstart(), 0);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_s_x_e64_writes_element_0() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
state.regs.write(Reg::A1, 0x0102_0304_0506_0708u64);
exec(
&mut state,
ZVPerm::VmvSX {
vd: VReg::V4,
rs1: Reg::A1,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E64),
0x0102_0304_0506_0708u64
);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_s_x_vl_zero_suppresses_write() {
let mut state = setup(0, Vsew::E32, Vlmul::M1);
set_vreg_bytes(&mut state, VReg::V4, 0xCC);
state.regs.write(Reg::A0, 0x1234_5678);
exec(
&mut state,
ZVPerm::VmvSX {
vd: VReg::V4,
rs1: Reg::A0,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xCCCC_CCCCu64);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_s_x_truncates_to_sew() {
let mut state = setup(1, Vsew::E8, Vlmul::M1);
state.regs.write(Reg::A0, 0xABCD);
exec(
&mut state,
ZVPerm::VmvSX {
vd: VReg::V4,
rs1: Reg::A0,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0xCD);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_s_x_illegal_when_vector_disabled() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let err = exec(
&mut state,
ZVPerm::VmvSX {
vd: VReg::V4,
rs1: Reg::A0,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_vx_e8_basic() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
for i in 0..8usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, (i + 1) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E8, 0xDD);
}
state.regs.write(Reg::A0, 2u64);
exec(
&mut state,
ZVPerm::VslideupVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 0xDD);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 0xDD);
for i in 2..8usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
(i - 1) as u64,
"elem {i}"
);
}
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_vi_e32_basic() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i * 100) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xFFFF_FFFF);
}
exec(
&mut state,
ZVPerm::VslideupVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 1,
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), 0);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 100);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 200);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_vx_offset_zero_copies_all() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
}
state.regs.write(Reg::A0, 0u64);
exec(
&mut state,
ZVPerm::VslideupVx {
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 + 1) as u64,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_vx_offset_ge_vl_no_write() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xBEEF);
}
state.regs.write(Reg::A0, 4u64);
exec(
&mut state,
ZVPerm::VslideupVx {
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),
0xBEEF,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_vx_masked() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
for i in 0..8usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, (i + 1) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E8, 0xAA);
}
state.ext_state.write_vreg()[0].fill(0);
set_mask_bit(&mut state, VReg::V0, 2, true);
set_mask_bit(&mut state, VReg::V0, 4, true);
set_mask_bit(&mut state, VReg::V0, 6, true);
state.regs.write(Reg::A0, 2u64);
exec(
&mut state,
ZVPerm::VslideupVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E8), 1);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E8), 0xAA);
assert_eq!(read_elem(&state, VReg::V4, 4, Vsew::E8), 3);
assert_eq!(read_elem(&state, VReg::V4, 6, Vsew::E8), 5);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_overlap_vd_vs2_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.regs.write(Reg::A0, 1u64);
let err = exec(
&mut state,
ZVPerm::VslideupVx {
vd: VReg::V2,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_masked_vd_v0_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.regs.write(Reg::A0, 1u64);
let err = exec(
&mut state,
ZVPerm::VslideupVx {
vd: VReg::V0,
vs2: VReg::V2,
rs1: Reg::A0,
vm: false,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_vstart_skips_lower_elements() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
for i in 0..8usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, (i + 10) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E8, 0xBB);
}
state.ext_state.set_vstart(3);
state.regs.write(Reg::A0, 2u64);
exec(
&mut state,
ZVPerm::VslideupVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
for i in 0..3usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 0xBB, "elem {i}");
}
for i in 3..8usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
(i - 2 + 10) as u64,
"elem {i}"
);
}
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslidedown_vx_e8_basic() {
let mut state = setup(6, Vsew::E8, Vlmul::M1);
for i in 0..16usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, (i + 1) as u64);
}
state.regs.write(Reg::A0, 2u64);
exec(
&mut state,
ZVPerm::VslidedownVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
for i in 0..6usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
(i + 3) as u64,
"elem {i}"
);
}
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslidedown_vi_e32_fills_zeros_past_end() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
}
exec(
&mut state,
ZVPerm::VslidedownVi {
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::E32), 0, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslidedown_vx_partial_fill() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
}
state.regs.write(Reg::A0, 2u64);
exec(
&mut state,
ZVPerm::VslidedownVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 30);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 40);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 0);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslidedown_vx_offset_zero_is_copy() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
}
state.regs.write(Reg::A0, 0u64);
exec(
&mut state,
ZVPerm::VslidedownVx {
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 + 1) as u64,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslidedown_overlap_allowed() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
}
state.regs.write(Reg::A0, 1u64);
exec(
&mut state,
ZVPerm::VslidedownVx {
vd: VReg::V2,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V2, 0, Vsew::E32), 20);
assert_eq!(read_elem(&state, VReg::V2, 1, Vsew::E32), 30);
assert_eq!(read_elem(&state, VReg::V2, 2, Vsew::E32), 40);
assert_eq!(read_elem(&state, VReg::V2, 3, Vsew::E32), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslidedown_masked() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 100) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xDEAD);
}
state.ext_state.write_vreg()[0].fill(0);
set_mask_bit(&mut state, VReg::V0, 0, true);
set_mask_bit(&mut state, VReg::V0, 2, true);
state.regs.write(Reg::A0, 1u64);
exec(
&mut state,
ZVPerm::VslidedownVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 200);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0xDEAD);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 400);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 0xDEAD);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1up_vx_e32_basic() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
}
state.regs.write(Reg::A0, 99u64);
exec(
&mut state,
ZVPerm::Vslide1upVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 99);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 10);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 20);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 30);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1up_vx_e64_scalar_inserted() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, 0xAAAA_AAAA_AAAA_AAAA);
write_elem(&mut state, VReg::V2, 1, Vsew::E64, 0xBBBB_BBBB_BBBB_BBBB);
state.regs.write(Reg::A0, 0x1234_5678_9ABC_DEF0u64);
exec(
&mut state,
ZVPerm::Vslide1upVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E64),
0x1234_5678_9ABC_DEF0
);
assert_eq!(
read_elem(&state, VReg::V4, 1, Vsew::E64),
0xAAAA_AAAA_AAAA_AAAA
);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1up_overlap_vd_vs2_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::Vslide1upVx {
vd: VReg::V2,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1up_masked() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xDEAD);
}
state.ext_state.write_vreg()[0].fill(0);
set_mask_bit(&mut state, VReg::V0, 0, true);
set_mask_bit(&mut state, VReg::V0, 2, true);
state.regs.write(Reg::A0, 99u64);
exec(
&mut state,
ZVPerm::Vslide1upVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 99);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0xDEAD);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 20);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 0xDEAD);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1down_vx_e32_basic() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
}
state.regs.write(Reg::A0, 999u64);
exec(
&mut state,
ZVPerm::Vslide1downVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 20);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 30);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 40);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 999);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1down_vx_e64_basic() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, 0xAAAA_AAAA);
write_elem(&mut state, VReg::V2, 1, Vsew::E64, 0xBBBB_BBBB);
state.regs.write(Reg::A0, 0xCCCC_CCCCu64);
exec(
&mut state,
ZVPerm::Vslide1downVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), 0xBBBB_BBBB);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E64), 0xCCCC_CCCC);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1down_vl_one_only_scalar() {
let mut state = setup(1, Vsew::E32, Vlmul::M1);
write_elem(&mut state, VReg::V2, 0, Vsew::E32, 0xDEAD_BEEF);
state.regs.write(Reg::A0, 42u64);
exec(
&mut state,
ZVPerm::Vslide1downVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 42);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1down_overlap_allowed() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
}
state.regs.write(Reg::A0, 50u64);
exec(
&mut state,
ZVPerm::Vslide1downVx {
vd: VReg::V2,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V2, 0, Vsew::E32), 20);
assert_eq!(read_elem(&state, VReg::V2, 1, Vsew::E32), 30);
assert_eq!(read_elem(&state, VReg::V2, 2, Vsew::E32), 40);
assert_eq!(read_elem(&state, VReg::V2, 3, Vsew::E32), 50);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1down_masked() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xFFFF);
}
state.ext_state.write_vreg()[0].fill(0);
set_mask_bit(&mut state, VReg::V0, 1, true);
set_mask_bit(&mut state, VReg::V0, 3, true);
state.regs.write(Reg::A0, 77u64);
exec(
&mut state,
ZVPerm::Vslide1downVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xFFFF);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 30);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 0xFFFF);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 77);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vv_e8_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
for i in 0..16usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, ((i + 1) * 10) as u64);
}
write_elem(&mut state, VReg::V1, 0, Vsew::E8, 3);
write_elem(&mut state, VReg::V1, 1, Vsew::E8, 0);
write_elem(&mut state, VReg::V1, 2, Vsew::E8, 2);
write_elem(&mut state, VReg::V1, 3, Vsew::E8, 1);
exec(
&mut state,
ZVPerm::VrgatherVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E8), 40);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E8), 10);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E8), 30);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E8), 20);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vv_index_out_of_range_gives_zero() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E32, 100);
}
exec(
&mut state,
ZVPerm::VrgatherVv {
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), 0, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vv_vd_overlap_vs2_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::VrgatherVv {
vd: VReg::V2,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vv_vd_overlap_vs1_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::VrgatherVv {
vd: VReg::V1,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vv_masked() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 100) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E32, (3 - i) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xABCD);
}
state.ext_state.write_vreg()[0].fill(0);
set_mask_bit(&mut state, VReg::V0, 0, true);
set_mask_bit(&mut state, VReg::V0, 3, true);
exec(
&mut state,
ZVPerm::VrgatherVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 400);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0xABCD);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 0xABCD);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 100);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vx_e32_all_same_element() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 11) as u64);
}
state.regs.write(Reg::A0, 2u64);
exec(
&mut state,
ZVPerm::VrgatherVx {
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), 33, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vx_index_out_of_range_gives_zero() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
}
state.regs.write(Reg::A0, 99u64);
exec(
&mut state,
ZVPerm::VrgatherVx {
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), 0, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vi_e8_immediate_index() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
for i in 0..16usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, (i * 3) as u64);
}
exec(
&mut state,
ZVPerm::VrgatherVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 5,
vm: true,
},
)
.unwrap();
for i in 0..8usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 15, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vi_index_zero() {
let mut state = setup(4, Vsew::E16, Vlmul::M1);
for i in 0..8usize {
write_elem(&mut state, VReg::V2, i, Vsew::E16, (i * 7) as u64);
}
exec(
&mut state,
ZVPerm::VrgatherVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E16), 0, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vx_vd_overlap_vs2_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.regs.write(Reg::A0, 0u64);
let err = exec(
&mut state,
ZVPerm::VrgatherVx {
vd: VReg::V2,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgatherei16_vv_e8_m1_basic() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
for i in 0..16usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, ((i + 1) * 5) as u64);
}
let indices: [u16; 8] = [7, 2, 0, 15, 1, 14, 3, 13];
for (i, &idx) in indices.iter().enumerate() {
let byte_off = i * 2;
let reg_off = byte_off / 16;
let b = byte_off % 16;
let bytes = idx.to_le_bytes();
state.ext_state.write_vreg()[usize::from(VReg::V6.bits()) + reg_off][b] = bytes[0];
state.ext_state.write_vreg()[usize::from(VReg::V6.bits()) + reg_off][b + 1] = bytes[1];
}
exec(
&mut state,
ZVPerm::Vrgatherei16Vv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V6,
vm: true,
},
)
.unwrap();
let expected: [u64; 8] = [40, 15, 5, 80, 10, 75, 20, 70];
for (i, &exp) in expected.iter().enumerate() {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), exp, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgatherei16_vv_index_out_of_range_gives_zero() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
}
for i in 0..8usize {
let byte_off = i * 2;
let bytes = 100u16.to_le_bytes();
state.ext_state.write_vreg()[usize::from(VReg::V6.bits())][byte_off] = bytes[0];
state.ext_state.write_vreg()[usize::from(VReg::V6.bits())][byte_off + 1] = bytes[1];
}
exec(
&mut state,
ZVPerm::Vrgatherei16Vv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V6,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E32), 0, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgatherei16_vv_vd_overlap_vs2_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::Vrgatherei16Vv {
vd: VReg::V2,
vs2: VReg::V2,
vs1: VReg::V6,
vm: true,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_v_broadcasts_all_elements() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V1, i, Vsew::E32, ((i + 1) * 10) as u64);
}
exec(
&mut state,
ZVPerm::VmergeVvm {
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),
((i + 1) * 10) as u64,
"elem {i}"
);
}
assert_eq!(state.ext_state.vstart(), 0);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_v_vl_zero_leaves_vd_undisturbed() {
let mut state = setup(0, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V1, i, Vsew::E32, 0xABCD);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xDEAD);
}
exec(
&mut state,
ZVPerm::VmergeVvm {
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,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vvm_blends_vs2_and_vs1() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i * 100) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E32, (i * 10 + 1) as u64);
}
state.ext_state.write_vreg()[0].fill(0);
set_mask_bit(&mut state, VReg::V0, 1, true);
set_mask_bit(&mut state, VReg::V0, 3, true);
exec(
&mut state,
ZVPerm::VmergeVvm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 11);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 200);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 31);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vvm_all_mask_bits_set_equals_vmv_v_v() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, 0xDEAD);
write_elem(&mut state, VReg::V1, i, Vsew::E32, (i + 1) as u64);
}
state.ext_state.write_vreg()[0].fill(0xFF);
exec(
&mut state,
ZVPerm::VmergeVvm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
(i + 1) as u64,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vvm_all_mask_bits_clear_equals_copy_vs2() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 7) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E32, 0xDEAD);
}
state.ext_state.write_vreg()[0].fill(0x00);
exec(
&mut state,
ZVPerm::VmergeVvm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
((i + 1) * 7) as u64,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vvm_vd_overlap_v0_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::VmergeVvm {
vd: VReg::V0,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_v_vd_may_equal_v0() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V1, i, Vsew::E32, (i + 1) as u64);
}
exec(
&mut state,
ZVPerm::VmergeVvm {
vd: VReg::V0,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V0, i, Vsew::E32),
(i + 1) as u64,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vvm_vstart_skips_early_elements() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i * 100) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E32, (i * 10 + 1) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xBEEF);
}
state.ext_state.write_vreg()[0].fill(0xFF);
state.ext_state.set_vstart(2);
exec(
&mut state,
ZVPerm::VmergeVvm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xBEEF);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0xBEEF);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 21);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 31);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vvm_e8_full_register() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
for i in 0..16usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, (i * 2) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E8, (i * 2 + 1) as u64);
}
state.ext_state.write_vreg()[0][0] = 0b1010_1010;
state.ext_state.write_vreg()[0][1] = 0b1010_1010;
exec(
&mut state,
ZVPerm::VmergeVvm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: false,
},
)
.unwrap();
for i in 0..16usize {
let expected = if i % 2 == 1 { i * 2 + 1 } else { i * 2 } as u64;
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
expected,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_x_broadcasts_scalar() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.regs.write(Reg::A0, 0x1234_5678u64);
exec(
&mut state,
ZVPerm::VmergeVxm {
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),
0x1234_5678,
"elem {i}"
);
}
assert_eq!(state.ext_state.vstart(), 0);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_x_e64_full_width() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
state.regs.write(Reg::A0, 0xDEAD_BEEF_CAFE_F00Du64);
exec(
&mut state,
ZVPerm::VmergeVxm {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E64),
0xDEAD_BEEF_CAFE_F00D
);
assert_eq!(
read_elem(&state, VReg::V4, 1, Vsew::E64),
0xDEAD_BEEF_CAFE_F00D
);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_x_truncates_to_sew() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.regs.write(Reg::A0, 0xABCD_EF01u64);
exec(
&mut state,
ZVPerm::VmergeVxm {
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), 0x01, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vxm_blends_vs2_and_scalar() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i * 100) as u64);
}
state.regs.write(Reg::A0, 999u64);
state.ext_state.write_vreg()[0].fill(0);
set_mask_bit(&mut state, VReg::V0, 0, true);
set_mask_bit(&mut state, VReg::V0, 2, true);
exec(
&mut state,
ZVPerm::VmergeVxm {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 999);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 100);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 999);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 300);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vxm_vd_overlap_v0_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::VmergeVxm {
vd: VReg::V0,
vs2: VReg::V2,
rs1: Reg::A0,
vm: false,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_x_vl_zero_leaves_vd_undisturbed() {
let mut state = setup(0, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xFACE);
}
state.regs.write(Reg::A0, 0x1234u64);
exec(
&mut state,
ZVPerm::VmergeVxm {
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),
0xFACE,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_i_broadcasts_positive_immediate() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
exec(
&mut state,
ZVPerm::VmergeVim {
vd: VReg::V4,
vs2: VReg::V2,
simm5: 15,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E32), 15, "elem {i}");
}
assert_eq!(state.ext_state.vstart(), 0);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_i_sign_extends_negative_immediate() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
exec(
&mut state,
ZVPerm::VmergeVim {
vd: VReg::V4,
vs2: VReg::V2,
simm5: -1,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
0xFFFF_FFFF,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_i_sign_extends_negative_e64() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
exec(
&mut state,
ZVPerm::VmergeVim {
vd: VReg::V4,
vs2: VReg::V2,
simm5: -1,
vm: true,
},
)
.unwrap();
assert_eq!(
read_elem(&state, VReg::V4, 0, Vsew::E64),
0xFFFF_FFFF_FFFF_FFFF
);
assert_eq!(
read_elem(&state, VReg::V4, 1, Vsew::E64),
0xFFFF_FFFF_FFFF_FFFF
);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_i_negative_imm_truncated_to_sew_e8() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
exec(
&mut state,
ZVPerm::VmergeVim {
vd: VReg::V4,
vs2: VReg::V2,
simm5: -1,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 0xFF, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vim_blends_vs2_and_immediate() {
let mut state = setup(4, Vsew::E16, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E16, (i * 1000) as u64);
}
state.ext_state.write_vreg()[0].fill(0);
set_mask_bit(&mut state, VReg::V0, 1, true);
set_mask_bit(&mut state, VReg::V0, 3, true);
exec(
&mut state,
ZVPerm::VmergeVim {
vd: VReg::V4,
vs2: VReg::V2,
simm5: 7,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E16), 0);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E16), 7);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E16), 2000);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E16), 7);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vim_vd_overlap_v0_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::VmergeVim {
vd: VReg::V0,
vs2: VReg::V2,
simm5: 1,
vm: false,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_i_vd_may_equal_v0_when_unmasked() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
exec(
&mut state,
ZVPerm::VmergeVim {
vd: VReg::V0,
vs2: VReg::V2,
simm5: 5,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(read_elem(&state, VReg::V0, i, Vsew::E32), 5, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vim_vstart_skips_early_elements() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i * 100) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xABCD);
}
state.ext_state.write_vreg()[0].fill(0xFF);
state.ext_state.set_vstart(2);
exec(
&mut state,
ZVPerm::VmergeVim {
vd: VReg::V4,
vs2: VReg::V2,
simm5: 42,
vm: false,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xABCD);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0xABCD);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 42);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 42);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_variants_illegal_when_vector_disabled() {
let instrs: &[(ZVPerm, &str)] = &[
(
ZVPerm::VmergeVvm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
"VmergeVvm",
),
(
ZVPerm::VmergeVxm {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VmergeVxm",
),
(
ZVPerm::VmergeVim {
vd: VReg::V4,
vs2: VReg::V2,
simm5: 1,
vm: true,
},
"VmergeVim",
),
];
for (instr, name) in instrs {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let err = exec(&mut state, *instr).unwrap_err();
assert!(
matches!(err, ExecutionError::IllegalInstruction { .. }),
"expected IllegalInstruction for {name}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_variants_illegal_when_vtype_invalid() {
let instrs: &[(ZVPerm, &str)] = &[
(
ZVPerm::VmergeVvm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
"VmergeVvm",
),
(
ZVPerm::VmergeVxm {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VmergeVxm",
),
(
ZVPerm::VmergeVim {
vd: VReg::V4,
vs2: VReg::V2,
simm5: 1,
vm: true,
},
"VmergeVim",
),
];
for (instr, name) in instrs {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.ext_state.set_vtype(None);
let err = exec(&mut state, *instr).unwrap_err();
assert!(
matches!(err, ExecutionError::IllegalInstruction { .. }),
"expected IllegalInstruction for {name}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_variants_reset_vstart_and_mark_dirty() {
let instrs: &[(ZVPerm, &str)] = &[
(
ZVPerm::VmergeVvm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
"VmergeVvm",
),
(
ZVPerm::VmergeVxm {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VmergeVxm",
),
(
ZVPerm::VmergeVim {
vd: VReg::V4,
vs2: VReg::V2,
simm5: 1,
vm: true,
},
"VmergeVim",
),
];
for (instr, name) in instrs {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V1, i, Vsew::E32, (i + 1) as u64);
}
state.regs.write(Reg::A0, 99u64);
state.ext_state.set_vstart(2);
let before = state.ext_state.vs_dirty_count();
exec(&mut state, *instr).unwrap();
assert_eq!(state.ext_state.vstart(), 0, "vstart not reset for {name}");
assert_eq!(
state.ext_state.vs_dirty_count(),
before + 1,
"vs_dirty not incremented for {name}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv_v_x_m2_e32_broadcasts_across_group() {
let mut state = setup(8, Vsew::E32, Vlmul::M2);
state.regs.write(Reg::A0, 0xCAFEu64);
exec(
&mut state,
ZVPerm::VmergeVxm {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
for i in 0..8usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
0xCAFE,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmerge_vxm_m2_e32_blends_across_group() {
let mut state = setup(8, Vsew::E32, Vlmul::M2);
for i in 0..8usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i * 100) as u64);
}
state.regs.write(Reg::A0, 777u64);
state.ext_state.write_vreg()[0].fill(0);
state.ext_state.write_vreg()[0][0] = 0b0101_0101;
exec(
&mut state,
ZVPerm::VmergeVxm {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: false,
},
)
.unwrap();
for i in 0..8usize {
let expected = if i % 2 == 0 { 777 } else { (i * 100) as u64 };
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
expected,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vcompress_vm_e32_basic() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xBEEF);
}
state.ext_state.write_vreg()[usize::from(VReg::V1.bits())].fill(0);
set_mask_bit(&mut state, VReg::V1, 1, true);
set_mask_bit(&mut state, VReg::V1, 3, true);
exec(
&mut state,
ZVPerm::VcompressVm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 20);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 40);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 0xBEEF);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 0xBEEF);
assert_eq!(state.ext_state.vstart(), 0);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vcompress_vm_all_active() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 7) as u64);
}
state.ext_state.write_vreg()[usize::from(VReg::V1.bits())].fill(0xFF);
exec(
&mut state,
ZVPerm::VcompressVm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
((i + 1) * 7) as u64,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vcompress_vm_none_active() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xABCD);
}
state.ext_state.write_vreg()[usize::from(VReg::V1.bits())].fill(0x00);
exec(
&mut state,
ZVPerm::VcompressVm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
0xABCD,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vcompress_vm_e8_all_elements() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
for i in 0..16usize {
write_elem(&mut state, VReg::V2, i, Vsew::E8, (15 - i) as u64);
}
state.ext_state.write_vreg()[usize::from(VReg::V1.bits())].fill(0xFF);
exec(
&mut state,
ZVPerm::VcompressVm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
for i in 0..16usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
(15 - i) as u64,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vcompress_vm_vd_overlap_vs2_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::VcompressVm {
vd: VReg::V2,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vcompress_vm_vd_overlap_vs1_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::VcompressVm {
vd: VReg::V1,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vcompress_vm_vstart_resumes_output_at_vstart() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xDEAD);
}
state.ext_state.write_vreg()[usize::from(VReg::V1.bits())].fill(0xFF);
state.ext_state.set_vstart(1);
exec(
&mut state,
ZVPerm::VcompressVm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xDEAD);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 20);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 30);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 40);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv1r_v_copies_single_register() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let src: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
state.ext_state.write_vreg()[usize::from(VReg::V2.bits())] = src;
set_vreg_bytes(&mut state, VReg::V4, 0xCC);
exec(
&mut state,
ZVPerm::Vmv1rV {
vd: VReg::V4,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(get_vreg_bytes(&state, VReg::V4), src);
assert_eq!(state.ext_state.vstart(), 0);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv1r_v_src_eq_dst_nop() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
set_vreg_bytes(&mut state, VReg::V2, 0xAB);
exec(
&mut state,
ZVPerm::Vmv1rV {
vd: VReg::V2,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(get_vreg_bytes(&state, VReg::V2), [0xAB; 16]);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv2r_v_copies_two_registers() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
set_vreg_bytes(&mut state, VReg::V2, 0x11);
set_vreg_bytes(&mut state, VReg::V3, 0x22);
set_vreg_bytes(&mut state, VReg::V4, 0xCC);
set_vreg_bytes(&mut state, VReg::V5, 0xCC);
exec(
&mut state,
ZVPerm::Vmv2rV {
vd: VReg::V4,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(get_vreg_bytes(&state, VReg::V4), [0x11; 16]);
assert_eq!(get_vreg_bytes(&state, VReg::V5), [0x22; 16]);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv2r_v_misaligned_vd_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::Vmv2rV {
vd: VReg::V3,
vs2: VReg::V2,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv2r_v_misaligned_vs2_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::Vmv2rV {
vd: VReg::V4,
vs2: VReg::V3,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv4r_v_copies_four_registers() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for k in 0u8..4 {
set_vreg_bytes(
&mut state,
VReg::from_bits(VReg::V8.bits() + k).unwrap(),
k + 1,
);
set_vreg_bytes(
&mut state,
VReg::from_bits(VReg::V12.bits() + k).unwrap(),
0xCC,
);
}
exec(
&mut state,
ZVPerm::Vmv4rV {
vd: VReg::V12,
vs2: VReg::V8,
},
)
.unwrap();
for k in 0u8..4 {
assert_eq!(
get_vreg_bytes(&state, VReg::from_bits(VReg::V12.bits() + k).unwrap()),
[k + 1; 16],
"reg offset {k}"
);
}
assert_eq!(state.ext_state.vstart(), 0);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv4r_v_misaligned_vd_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::Vmv4rV {
vd: VReg::V6,
vs2: VReg::V8,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv4r_v_misaligned_vs2_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::Vmv4rV {
vd: VReg::V12,
vs2: VReg::V6,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv8r_v_copies_eight_registers() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for k in 0u8..8 {
set_vreg_bytes(
&mut state,
VReg::from_bits(VReg::V8.bits() + k).unwrap(),
k + 10,
);
set_vreg_bytes(
&mut state,
VReg::from_bits(VReg::V16.bits() + k).unwrap(),
0xCC,
);
}
exec(
&mut state,
ZVPerm::Vmv8rV {
vd: VReg::V16,
vs2: VReg::V8,
},
)
.unwrap();
for k in 0u8..8 {
assert_eq!(
get_vreg_bytes(&state, VReg::from_bits(VReg::V16.bits() + k).unwrap()),
[k + 10; 16],
"reg offset {k}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv8r_v_misaligned_vd_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::Vmv8rV {
vd: VReg::V4,
vs2: VReg::V8,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmv8r_v_misaligned_vs2_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let err = exec(
&mut state,
ZVPerm::Vmv8rV {
vd: VReg::V0,
vs2: VReg::V4,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vmvr_does_not_require_valid_vtype() {
let mut state = setup(0, Vsew::E32, Vlmul::M1);
state.ext_state.set_vtype(None);
set_vreg_bytes(&mut state, VReg::V2, 0xAB);
set_vreg_bytes(&mut state, VReg::V4, 0x00);
exec(
&mut state,
ZVPerm::Vmv1rV {
vd: VReg::V4,
vs2: VReg::V2,
},
)
.unwrap();
assert_eq!(get_vreg_bytes(&state, VReg::V4), [0xAB; 16]);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_vx_m2_e32() {
let mut state = setup(8, Vsew::E32, Vlmul::M2);
for i in 0..8usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xDEAD);
}
state.regs.write(Reg::A0, 3u64);
exec(
&mut state,
ZVPerm::VslideupVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
for i in 0..3usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
0xDEAD,
"elem {i}"
);
}
for i in 3..8usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
((i - 3 + 1) * 10) as u64,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslidedown_vx_m2_e32_partial() {
let mut state = setup(8, Vsew::E32, Vlmul::M2);
for i in 0..8usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 100) as u64);
}
state.regs.write(Reg::A0, 5u64);
exec(
&mut state,
ZVPerm::VslidedownVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 600);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 700);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 800);
for i in 3..8usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E32), 0, "elem {i}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vv_m2_e32() {
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 + 1) * 100);
write_elem(&mut state, VReg::V6, i, Vsew::E32, (7 - i) as u64);
}
exec(
&mut state,
ZVPerm::VrgatherVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V6,
vm: true,
},
)
.unwrap();
for i in 0..8usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
(8 - i) as u64 * 100,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_unaligned_group_vd_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M2);
state.regs.write(Reg::A0, 1u64);
let err = exec(
&mut state,
ZVPerm::VslideupVx {
vd: VReg::V3,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap_err();
assert!(matches!(err, ExecutionError::IllegalInstruction { .. }));
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslide1down_vstart_skips_early_elements() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, ((i + 1) * 10) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xAA);
}
state.ext_state.set_vstart(2);
state.regs.write(Reg::A0, 999u64);
exec(
&mut state,
ZVPerm::Vslide1downVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xAA);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0xAA);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 40);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 999);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vrgather_vstart_skips_early_elements() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E32, (3 - i) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xCC);
}
state.ext_state.set_vstart(2);
exec(
&mut state,
ZVPerm::VrgatherVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0xCC);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0xCC);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 2);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
#[cfg_attr(miri, ignore)]
fn all_instructions_reset_vstart() {
let cases = &[
(
ZVPerm::VmvXS {
rd: Reg::A1,
vs2: VReg::V2,
},
"VmvXS",
),
(
ZVPerm::VmvSX {
vd: VReg::V4,
rs1: Reg::A0,
},
"VmvSX",
),
(
ZVPerm::VslideupVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VslideupVx",
),
(
ZVPerm::VslideupVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
"VslideupVi",
),
(
ZVPerm::VslidedownVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VslidedownVx",
),
(
ZVPerm::VslidedownVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
"VslidedownVi",
),
(
ZVPerm::Vslide1upVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"Vslide1upVx",
),
(
ZVPerm::Vslide1downVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"Vslide1downVx",
),
(
ZVPerm::VrgatherVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
"VrgatherVv",
),
(
ZVPerm::VrgatherVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VrgatherVx",
),
(
ZVPerm::VrgatherVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
"VrgatherVi",
),
(
ZVPerm::VcompressVm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
"VcompressVm",
),
(
ZVPerm::Vmv1rV {
vd: VReg::V4,
vs2: VReg::V2,
},
"Vmv1rV",
),
(
ZVPerm::Vmv2rV {
vd: VReg::V4,
vs2: VReg::V2,
},
"Vmv2rV",
),
(
ZVPerm::Vmv4rV {
vd: VReg::V12,
vs2: VReg::V8,
},
"Vmv4rV",
),
(
ZVPerm::Vmv8rV {
vd: VReg::V16,
vs2: VReg::V8,
},
"Vmv8rV",
),
];
for (instr, name) in cases {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E32, i as u64);
}
state.ext_state.write_vreg()[usize::from(VReg::V1.bits())].fill(0xFF);
state.ext_state.set_vstart(2);
state.regs.write(Reg::A0, 1u64);
let _ = exec(&mut state, *instr);
assert_eq!(state.ext_state.vstart(), 0, "vstart not reset for {name}");
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn all_vector_instructions_mark_vs_dirty() {
let cases = &[
(
ZVPerm::VmvXS {
rd: Reg::A1,
vs2: VReg::V2,
},
"VmvXS",
),
(
ZVPerm::VmvSX {
vd: VReg::V4,
rs1: Reg::A0,
},
"VmvSX",
),
(
ZVPerm::VslideupVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VslideupVx",
),
(
ZVPerm::VslideupVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
"VslideupVi",
),
(
ZVPerm::VslidedownVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VslidedownVx",
),
(
ZVPerm::VslidedownVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
"VslidedownVi",
),
(
ZVPerm::Vslide1upVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"Vslide1upVx",
),
(
ZVPerm::Vslide1downVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"Vslide1downVx",
),
(
ZVPerm::VrgatherVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
"VrgatherVv",
),
(
ZVPerm::VrgatherVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VrgatherVx",
),
(
ZVPerm::VrgatherVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
"VrgatherVi",
),
(
ZVPerm::VcompressVm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
"VcompressVm",
),
(
ZVPerm::Vmv1rV {
vd: VReg::V4,
vs2: VReg::V2,
},
"Vmv1rV",
),
(
ZVPerm::Vmv2rV {
vd: VReg::V4,
vs2: VReg::V2,
},
"Vmv2rV",
),
(
ZVPerm::Vmv4rV {
vd: VReg::V12,
vs2: VReg::V8,
},
"Vmv4rV",
),
(
ZVPerm::Vmv8rV {
vd: VReg::V16,
vs2: VReg::V8,
},
"Vmv8rV",
),
];
for (instr, name) in cases {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
write_elem(&mut state, VReg::V1, i, Vsew::E32, i as u64);
}
state.regs.write(Reg::A0, 1u64);
state.ext_state.write_vreg()[usize::from(VReg::V1.bits())].fill(0xFF);
let before = state.ext_state.vs_dirty_count();
let _ = exec(&mut state, *instr);
assert_eq!(
state.ext_state.vs_dirty_count(),
before + 1,
"vs_dirty not incremented for {name}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn all_instructions_illegal_when_vector_disabled() {
let cases = &[
(
ZVPerm::VmvXS {
rd: Reg::A1,
vs2: VReg::V2,
},
"VmvXS",
),
(
ZVPerm::VmvSX {
vd: VReg::V4,
rs1: Reg::A0,
},
"VmvSX",
),
(
ZVPerm::VslideupVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VslideupVx",
),
(
ZVPerm::VslideupVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
"VslideupVi",
),
(
ZVPerm::VslidedownVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VslidedownVx",
),
(
ZVPerm::VslidedownVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
"VslidedownVi",
),
(
ZVPerm::Vslide1upVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"Vslide1upVx",
),
(
ZVPerm::Vslide1downVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"Vslide1downVx",
),
(
ZVPerm::VrgatherVv {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
vm: true,
},
"VrgatherVv",
),
(
ZVPerm::VrgatherVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
"VrgatherVx",
),
(
ZVPerm::VrgatherVi {
vd: VReg::V4,
vs2: VReg::V2,
uimm: 0,
vm: true,
},
"VrgatherVi",
),
(
ZVPerm::VcompressVm {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
"VcompressVm",
),
(
ZVPerm::Vmv1rV {
vd: VReg::V4,
vs2: VReg::V2,
},
"Vmv1rV",
),
(
ZVPerm::Vmv2rV {
vd: VReg::V4,
vs2: VReg::V2,
},
"Vmv2rV",
),
(
ZVPerm::Vmv4rV {
vd: VReg::V12,
vs2: VReg::V8,
},
"Vmv4rV",
),
(
ZVPerm::Vmv8rV {
vd: VReg::V16,
vs2: VReg::V8,
},
"Vmv8rV",
),
];
for (instr, name) in cases {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
}
state.regs.write(Reg::A0, 1u64);
let err = exec(&mut state, *instr).unwrap_err();
assert!(
matches!(err, ExecutionError::IllegalInstruction { .. }),
"expected IllegalInstruction for {name}, got {err:?}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vl_zero_leaves_vd_undisturbed_slide() {
let mut state = setup(0, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xFACE);
}
state.regs.write(Reg::A0, 1u64);
exec(
&mut state,
ZVPerm::VslideupVx {
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),
0xFACE,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vl_zero_leaves_vd_undisturbed_rgather() {
let mut state = setup(0, Vsew::E32, Vlmul::M1);
for i in 0..4usize {
write_elem(&mut state, VReg::V2, i, Vsew::E32, (i + 1) as u64);
write_elem(&mut state, VReg::V4, i, Vsew::E32, 0xFACE);
}
state.regs.write(Reg::A0, 0u64);
exec(
&mut state,
ZVPerm::VrgatherVx {
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),
0xFACE,
"elem {i}"
);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslideup_mf2_e64_offset_ge_vlmax_no_write() {
let mut state = setup(1, Vsew::E64, Vlmul::Mf2);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, 0xABCD);
write_elem(&mut state, VReg::V4, 0, Vsew::E64, 0xDEAD);
state.regs.write(Reg::A0, 1u64);
exec(
&mut state,
ZVPerm::VslideupVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), 0xDEAD);
}
#[test]
#[cfg_attr(miri, ignore)]
fn vslidedown_mf2_e64_offset_zero_copies() {
let mut state = setup(1, Vsew::E64, Vlmul::Mf2);
write_elem(&mut state, VReg::V2, 0, Vsew::E64, 0x1234);
state.regs.write(Reg::A0, 0u64);
exec(
&mut state,
ZVPerm::VslidedownVx {
vd: VReg::V4,
vs2: VReg::V2,
rs1: Reg::A0,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), 0x1234);
}