use crate::rv64::test_utils::{TestInterpreterState, initialize_state};
use crate::v::vector_registers::{VectorRegisters, VectorRegistersExt};
use crate::{ExecutableInstruction, ExecutionError};
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<Zve64xMaskInstruction<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<Zve64xMaskInstruction<Reg<u64>>>,
instr: Zve64xMaskInstruction<Reg<u64>>,
) -> Result<(), ExecutionError<u64>> {
instr.execute(state).map(|_| ())
}
fn get_vreg(state: &TestInterpreterState<Zve64xMaskInstruction<Reg<u64>>>, reg: VReg) -> [u8; 16] {
state.ext_state.read_vreg()[usize::from(reg.bits())]
}
fn set_vreg(
state: &mut TestInterpreterState<Zve64xMaskInstruction<Reg<u64>>>,
reg: VReg,
data: [u8; 16],
) {
state.ext_state.write_vreg()[usize::from(reg.bits())] = data;
}
fn read_elem(
state: &TestInterpreterState<Zve64xMaskInstruction<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 mask_bit(
state: &TestInterpreterState<Zve64xMaskInstruction<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 vmand_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xAA; 16]);
set_vreg(&mut state, VReg::V1, [0xCC; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmand {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0x88; 16]);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vmor_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xAA; 16]);
set_vreg(&mut state, VReg::V1, [0x55; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0xFF; 16]);
}
#[test]
fn vmxor_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xF0; 16]);
set_vreg(&mut state, VReg::V1, [0xFF; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmxor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0x0F; 16]);
}
#[test]
fn vmandn_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
set_vreg(&mut state, VReg::V1, [0x0F; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmandn {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0xF0; 16]);
}
#[test]
fn vmorn_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0x00; 16]);
set_vreg(&mut state, VReg::V1, [0x0F; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmorn {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0xF0; 16]);
}
#[test]
fn vmnand_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
set_vreg(&mut state, VReg::V1, [0xFF; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmnand {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0x00; 16]);
}
#[test]
fn vmnor_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0x00; 16]);
set_vreg(&mut state, VReg::V1, [0x00; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmnor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0xFF; 16]);
}
#[test]
fn vmxnor_basic() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xAA; 16]);
set_vreg(&mut state, VReg::V1, [0xAA; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmxnor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0xFF; 16]);
}
#[test]
fn vmand_operates_on_full_register_regardless_of_vl() {
let mut state = setup(0, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
set_vreg(&mut state, VReg::V1, [0xAA; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmand {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0xAA; 16]);
}
#[test]
fn vmand_vd_overlaps_vs2() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
set_vreg(&mut state, VReg::V1, [0x0F; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmand {
vd: VReg::V2,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V2), [0x0F; 16]);
}
#[test]
fn vmand_vd_overlaps_vs1() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
set_vreg(&mut state, VReg::V1, [0x0F; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmand {
vd: VReg::V1,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V1), [0x0F; 16]);
}
#[test]
fn vmand_vd_is_v0() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
set_vreg(&mut state, VReg::V1, [0xAA; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmand {
vd: VReg::V0,
vs2: VReg::V2,
vs1: VReg::V1,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V0), [0xAA; 16]);
}
#[test]
fn vmand_vector_not_allowed() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmand {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vcpop_all_set_unmasked() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 16);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vcpop_all_clear() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0x00; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 0);
}
#[test]
fn vcpop_respects_vl() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 4);
}
#[test]
fn vcpop_masked() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
set_vreg(
&mut state,
VReg::V0,
[0x55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
);
exec(
&mut state,
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: false,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 4);
}
#[test]
fn vcpop_vstart_skips_early_elements() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
state.ext_state.set_vstart(4);
exec(
&mut state,
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 4);
}
#[test]
fn vcpop_invalid_vtype() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.ext_state.set_vtype(None);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vcpop_vector_not_allowed() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vcpop_sparse_bits() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
let mut data = [0u8; 16];
data[0] = 0x89;
data[1] = 0x08;
data[1] |= 0x80;
set_vreg(&mut state, VReg::V2, data);
exec(
&mut state,
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 5);
}
#[test]
fn vfirst_basic() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
let mut data = [0u8; 16];
data[0] = 0b00001000;
set_vreg(&mut state, VReg::V2, data);
exec(
&mut state,
Zve64xMaskInstruction::Vfirst {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 3);
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vfirst_no_set_bit_returns_minus_one() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0x00; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vfirst {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), u64::MAX);
}
#[test]
fn vfirst_bit_zero() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vfirst {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 0);
}
#[test]
fn vfirst_respects_vl() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
let mut data = [0u8; 16];
data[0] = 0b00100000;
set_vreg(&mut state, VReg::V2, data);
exec(
&mut state,
Zve64xMaskInstruction::Vfirst {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), u64::MAX);
}
#[test]
fn vfirst_masked_skips_inactive() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0b00010001;
set_vreg(&mut state, VReg::V2, vs2);
let mut mask = [0u8; 16];
mask[0] = 0xFC;
set_vreg(&mut state, VReg::V0, mask);
exec(
&mut state,
Zve64xMaskInstruction::Vfirst {
rd: Reg::A0,
vs2: VReg::V2,
vm: false,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 4);
}
#[test]
fn vfirst_vstart_skips_early() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut data = [0u8; 16];
data[0] = 0b00100010;
set_vreg(&mut state, VReg::V2, data);
state.ext_state.set_vstart(3);
exec(
&mut state,
Zve64xMaskInstruction::Vfirst {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 5);
}
#[test]
fn vmsbf_first_at_position_3() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0b00001000;
set_vreg(&mut state, VReg::V2, vs2);
exec(
&mut state,
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
for i in 0..3 {
assert!(mask_bit(&state, VReg::V4, i), "bit {i} should be set");
}
for i in 3..8 {
assert!(!mask_bit(&state, VReg::V4, i), "bit {i} should be clear");
}
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vmsbf_no_set_bit() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0x00; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
for i in 0..8 {
assert!(mask_bit(&state, VReg::V4, i), "bit {i} should be set");
}
}
#[test]
fn vmsbf_first_at_position_zero() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0x01;
set_vreg(&mut state, VReg::V2, vs2);
exec(
&mut state,
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
for i in 0..8 {
assert!(!mask_bit(&state, VReg::V4, i), "bit {i} should be clear");
}
}
#[test]
fn vmsbf_masked_inactive_undisturbed() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0b00010000;
set_vreg(&mut state, VReg::V2, vs2);
set_vreg(&mut state, VReg::V4, [0xFF; 16]);
let mut mask = [0u8; 16];
mask[0] = 0xFC;
set_vreg(&mut state, VReg::V0, mask);
exec(
&mut state,
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V4,
vs2: VReg::V2,
vm: false,
},
)
.unwrap();
assert!(
mask_bit(&state, VReg::V4, 0),
"inactive bit 0 must be undisturbed"
);
assert!(
mask_bit(&state, VReg::V4, 1),
"inactive bit 1 must be undisturbed"
);
assert!(
mask_bit(&state, VReg::V4, 2),
"bit 2 should be set (before first)"
);
assert!(
mask_bit(&state, VReg::V4, 3),
"bit 3 should be set (before first)"
);
assert!(
!mask_bit(&state, VReg::V4, 4),
"bit 4 should be clear (is first)"
);
for i in 5..8 {
assert!(
!mask_bit(&state, VReg::V4, i),
"bit {i} should be clear (after first)"
);
}
}
#[test]
fn vmsbf_vd_eq_vs2_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V2,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vmsbf_vd_eq_v0_masked_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V0,
vs2: VReg::V2,
vm: false,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vmsbf_nonzero_vstart_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
state.ext_state.set_vstart(1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vmsof_first_at_position_3() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0b01001000;
set_vreg(&mut state, VReg::V2, vs2);
exec(
&mut state,
Zve64xMaskInstruction::Vmsof {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
for i in 0..8 {
assert_eq!(mask_bit(&state, VReg::V4, i), i == 3, "bit {i}");
}
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vmsof_no_set_bit() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0x00; 16]);
set_vreg(&mut state, VReg::V4, [0xFF; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmsof {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
for i in 0..8 {
assert!(!mask_bit(&state, VReg::V4, i), "bit {i} should be clear");
}
}
#[test]
fn vmsof_vd_eq_vs2_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmsof {
vd: VReg::V2,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vmsof_vd_eq_v0_masked_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmsof {
vd: VReg::V0,
vs2: VReg::V2,
vm: false,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vmsof_masked_inactive_undisturbed() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0b00000100;
set_vreg(&mut state, VReg::V2, vs2);
set_vreg(&mut state, VReg::V4, [0xFF; 16]);
let mut mask = [0u8; 16];
mask[0] = 0xFC;
set_vreg(&mut state, VReg::V0, mask);
exec(
&mut state,
Zve64xMaskInstruction::Vmsof {
vd: VReg::V4,
vs2: VReg::V2,
vm: false,
},
)
.unwrap();
assert!(mask_bit(&state, VReg::V4, 0));
assert!(mask_bit(&state, VReg::V4, 1));
assert!(mask_bit(&state, VReg::V4, 2));
for i in 3..8 {
assert!(!mask_bit(&state, VReg::V4, i), "bit {i}");
}
}
#[test]
fn vmsof_nonzero_vstart_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
state.ext_state.set_vstart(1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmsof {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vmsif_first_at_position_3() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0b00001000;
set_vreg(&mut state, VReg::V2, vs2);
exec(
&mut state,
Zve64xMaskInstruction::Vmsif {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
for i in 0..=3 {
assert!(mask_bit(&state, VReg::V4, i), "bit {i} should be set");
}
for i in 4..8 {
assert!(!mask_bit(&state, VReg::V4, i), "bit {i} should be clear");
}
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vmsif_no_set_bit() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0x00; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmsif {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
for i in 0..8 {
assert!(mask_bit(&state, VReg::V4, i), "bit {i} should be set");
}
}
#[test]
fn vmsif_first_at_position_zero() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0x01;
set_vreg(&mut state, VReg::V2, vs2);
exec(
&mut state,
Zve64xMaskInstruction::Vmsif {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert!(mask_bit(&state, VReg::V4, 0), "bit 0 should be set");
for i in 1..8 {
assert!(!mask_bit(&state, VReg::V4, i), "bit {i} should be clear");
}
}
#[test]
fn vmsif_vd_eq_vs2_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmsif {
vd: VReg::V2,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vmsif_vd_eq_v0_masked_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmsif {
vd: VReg::V0,
vs2: VReg::V2,
vm: false,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vmsif_nonzero_vstart_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
state.ext_state.set_vstart(1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vmsif {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vmsbf_vmsof_vmsif_relationship() {
let k = 5u32;
let vl = 8u32;
let mut vs2 = [0u8; 16];
vs2[(k / u8::BITS) as usize] |= 1 << (k % u8::BITS);
for i in 0..vl {
let mut sbf_state = setup(vl, Vsew::E8, Vlmul::M1);
set_vreg(&mut sbf_state, VReg::V2, vs2);
exec(
&mut sbf_state,
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
let mut sof_state = setup(vl, Vsew::E8, Vlmul::M1);
set_vreg(&mut sof_state, VReg::V2, vs2);
exec(
&mut sof_state,
Zve64xMaskInstruction::Vmsof {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
let mut sif_state = setup(vl, Vsew::E8, Vlmul::M1);
set_vreg(&mut sif_state, VReg::V2, vs2);
exec(
&mut sif_state,
Zve64xMaskInstruction::Vmsif {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(
mask_bit(&sbf_state, VReg::V4, i),
i < k,
"vmsbf bit {i}: expected {}",
i < k
);
assert_eq!(
mask_bit(&sof_state, VReg::V4, i),
i == k,
"vmsof bit {i}: expected {}",
i == k
);
assert_eq!(
mask_bit(&sif_state, VReg::V4, i),
i <= k,
"vmsif bit {i}: expected {}",
i <= k
);
}
}
#[test]
fn viota_basic_e8_m1() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0x15;
set_vreg(&mut state, VReg::V2, vs2);
exec(
&mut state,
Zve64xMaskInstruction::Viota {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
let expected: [u64; 8] = [0, 1, 1, 2, 2, 3, 3, 3];
for (i, &exp) in expected.iter().enumerate() {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), exp, "elem {i}");
}
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn viota_e32_m1() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0x06;
set_vreg(&mut state, VReg::V2, vs2);
exec(
&mut state,
Zve64xMaskInstruction::Viota {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E32), 0);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E32), 0);
assert_eq!(read_elem(&state, VReg::V4, 2, Vsew::E32), 1);
assert_eq!(read_elem(&state, VReg::V4, 3, Vsew::E32), 2);
}
#[test]
fn viota_inactive_vs2_bits_treated_as_zero() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
let mut mask = [0u8; 16];
mask[0] = 0xF0;
set_vreg(&mut state, VReg::V0, mask);
set_vreg(&mut state, VReg::V4, [0xAB; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Viota {
vd: VReg::V4,
vs2: VReg::V2,
vm: false,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
0xAB,
"inactive elem {i}"
);
}
let expected = [0, 1, 2, 3];
for (k, &exp) in expected.iter().enumerate() {
let i = 4 + k;
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
exp,
"active elem {i}"
);
}
}
#[test]
fn viota_nonzero_vstart_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
state.ext_state.set_vstart(1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Viota {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn viota_vd_eq_vs2_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Viota {
vd: VReg::V2,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn viota_vd_eq_v0_masked_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Viota {
vd: VReg::V0,
vs2: VReg::V2,
vm: false,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn viota_misaligned_vd_illegal() {
let mut state = setup(16, Vsew::E8, Vlmul::M2);
let result = exec(
&mut state,
Zve64xMaskInstruction::Viota {
vd: VReg::V3,
vs2: VReg::V2,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn viota_sew64_does_not_overflow_width_check() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
let mut vs2 = [0u8; 16];
vs2[0] = 0b11;
set_vreg(&mut state, VReg::V2, vs2);
exec(
&mut state,
Zve64xMaskInstruction::Viota {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), 0);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E64), 1);
}
#[test]
fn vid_basic_e8_m1() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
)
.unwrap();
for i in 0..16usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
i as u64,
"elem {i}"
);
}
assert_eq!(state.ext_state.vs_dirty_count(), 1);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vid_e16_m1() {
let mut state = setup(8, Vsew::E16, Vlmul::M1);
exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
)
.unwrap();
for i in 0..8usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E16),
i as u64,
"elem {i}"
);
}
}
#[test]
fn vid_e32_m1() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E32),
i as u64,
"elem {i}"
);
}
}
#[test]
fn vid_e64_m1() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
)
.unwrap();
assert_eq!(read_elem(&state, VReg::V4, 0, Vsew::E64), 0);
assert_eq!(read_elem(&state, VReg::V4, 1, Vsew::E64), 1);
}
#[test]
fn vid_respects_vl() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V4, [0xEE; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
i as u64,
"elem {i}"
);
}
for i in 4..16usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 0xEE, "elem {i}");
}
}
#[test]
fn vid_masked_inactive_undisturbed() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V4, [0xBE; 16]);
let mut mask = [0u8; 16];
mask[0] = 0x55;
set_vreg(&mut state, VReg::V0, mask);
exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: false,
},
)
.unwrap();
for i in 0..8usize {
if i % 2 == 0 {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
i as u64,
"active elem {i}"
);
} else {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
0xBE,
"inactive elem {i}"
);
}
}
}
#[test]
fn vid_vd_eq_v0_masked_illegal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V0,
vm: false,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vid_misaligned_vd_illegal() {
let mut state = setup(16, Vsew::E8, Vlmul::M2);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V3,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vid_vstart_undisturbed_below() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V4, [0xFF; 16]);
state.ext_state.set_vstart(4);
exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
)
.unwrap();
for i in 0..4usize {
assert_eq!(read_elem(&state, VReg::V4, i, Vsew::E8), 0xFF, "elem {i}");
}
for i in 4..8usize {
assert_eq!(
read_elem(&state, VReg::V4, i, Vsew::E8),
i as u64,
"elem {i}"
);
}
}
#[test]
fn vid_invalid_vtype() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
state.ext_state.set_vtype(None);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vid_vector_not_allowed() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
let result = exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vcpop_vl_zero() {
let mut state = setup(0, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), 0);
}
#[test]
fn vfirst_vl_zero() {
let mut state = setup(0, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vfirst {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(state.regs.read(Reg::A0), u64::MAX);
}
#[test]
fn vmsbf_vl_zero() {
let mut state = setup(0, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V2, [0xFF; 16]);
set_vreg(&mut state, VReg::V4, [0xAB; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0xAB; 16]);
}
#[test]
fn vid_vl_zero() {
let mut state = setup(0, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V4, [0xCD; 16]);
exec(
&mut state,
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
)
.unwrap();
assert_eq!(get_vreg(&state, VReg::V4), [0xCD; 16]);
}
#[test]
fn all_instructions_mark_vs_dirty_and_reset_vstart() {
let vstart_ok: &[Zve64xMaskInstruction<Reg<u64>>] = &[
Zve64xMaskInstruction::Vmand {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmxor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmandn {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmorn {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmnand {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmnor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmxnor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vcpop {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
Zve64xMaskInstruction::Vfirst {
rd: Reg::A0,
vs2: VReg::V2,
vm: true,
},
Zve64xMaskInstruction::Vid {
vd: VReg::V4,
vm: true,
},
];
for (idx, &instr) in vstart_ok.iter().enumerate() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.ext_state.set_vstart(2);
exec(&mut state, instr).unwrap();
assert_eq!(
state.ext_state.vs_dirty_count(),
1,
"instruction {idx}: vs_dirty"
);
assert_eq!(
state.ext_state.vstart(),
0,
"instruction {idx}: vstart reset"
);
}
let vstart_must_be_zero: &[Zve64xMaskInstruction<Reg<u64>>] = &[
Zve64xMaskInstruction::Vmsbf {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
Zve64xMaskInstruction::Vmsof {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
Zve64xMaskInstruction::Vmsif {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
Zve64xMaskInstruction::Viota {
vd: VReg::V4,
vs2: VReg::V2,
vm: true,
},
];
for (idx, &instr) in vstart_must_be_zero.iter().enumerate() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
exec(&mut state, instr).unwrap();
assert_eq!(
state.ext_state.vs_dirty_count(),
1,
"vstart=0 instruction {idx}: vs_dirty"
);
assert_eq!(state.ext_state.vstart(), 0, "vstart=0 instruction {idx}");
}
}
#[test]
fn mask_logical_invalid_vtype() {
let ops: &[Zve64xMaskInstruction<Reg<u64>>] = &[
Zve64xMaskInstruction::Vmand {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmxor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmandn {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmorn {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmnand {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmnor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
Zve64xMaskInstruction::Vmxnor {
vd: VReg::V4,
vs2: VReg::V2,
vs1: VReg::V1,
},
];
for (idx, &op) in ops.iter().enumerate() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
state.ext_state.set_vtype(None);
let result = exec(&mut state, op);
assert!(
matches!(result, Err(ExecutionError::IllegalInstruction { .. })),
"op {idx} should reject vill=1"
);
}
}