use crate::rv64::test_utils::{TEST_BASE_ADDR, TestInterpreterState, initialize_state};
use crate::v::vector_registers::{VectorRegisters, VectorRegistersExt};
use crate::{ExecutableInstruction, ExecutionError, VirtualMemory};
use ab_riscv_primitives::prelude::*;
use core::array;
fn setup(
vl: u32,
vsew: Vsew,
vlmul: Vlmul,
) -> TestInterpreterState<Zve64xStoreInstruction<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 encode_vtype(vsew: Vsew, vlmul: Vlmul) -> u64 {
(vlmul.to_bits() as u64) | ((vsew.to_bits() as u64) << 3)
}
fn set_vreg(
state: &mut TestInterpreterState<Zve64xStoreInstruction<Reg<u64>>>,
reg: VReg,
data: &[u8],
) {
let dst = &mut state.ext_state.write_vreg()[usize::from(reg.bits())];
dst[..data.len()].copy_from_slice(data);
}
fn read_mem_bytes<const N: usize>(
state: &TestInterpreterState<Zve64xStoreInstruction<Reg<u64>>>,
addr: u64,
) -> &[u8; N] {
state
.memory
.read_slice(addr, N as u32)
.unwrap()
.try_into()
.unwrap()
}
fn exec_one(
state: &mut TestInterpreterState<Zve64xStoreInstruction<Reg<u64>>>,
instr: Zve64xStoreInstruction<Reg<u64>>,
) -> Result<(), ExecutionError<u64>> {
instr.execute(state).map(|_| ())
}
#[test]
fn vsr_single_register_stores_vlenb_bytes() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
let data = array::from_fn::<_, 16, _>(|i| i as u8 + 1);
set_vreg(&mut state, VReg::V2, &data);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V2,
rs1: Reg::A0,
nreg: 1,
},
)
.unwrap();
assert_eq!(read_mem_bytes::<16>(&state, TEST_BASE_ADDR), &data);
assert_eq!(state.ext_state.vs_dirty_count(), 0);
}
#[test]
fn vsr_two_registers_stores_two_vlenb_blocks() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
let data0 = array::from_fn::<_, 16, _>(|i| i as u8);
let data1 = array::from_fn::<_, 16, _>(|i| i as u8 + 16);
set_vreg(&mut state, VReg::V2, &data0);
set_vreg(&mut state, VReg::V3, &data1);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V2,
rs1: Reg::A0,
nreg: 2,
},
)
.unwrap();
assert_eq!(read_mem_bytes::<16>(&state, TEST_BASE_ADDR), &data0);
assert_eq!(read_mem_bytes::<16>(&state, TEST_BASE_ADDR + 16), &data1);
}
#[test]
fn vsr_four_registers_stores_four_vlenb_blocks() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
for i in 0u8..4 {
let data = array::from_fn::<_, 16, _>(|j| i * 16 + j as u8);
set_vreg(&mut state, VReg::from_bits(4 + i).unwrap(), &data);
}
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V4,
rs1: Reg::A0,
nreg: 4,
},
)
.unwrap();
for i in 0u8..4 {
let expected = array::from_fn::<_, 16, _>(|j| i * 16 + j as u8);
assert_eq!(
read_mem_bytes::<16>(&state, TEST_BASE_ADDR + u64::from(i) * 16),
&expected
);
}
}
#[test]
fn vsr_eight_registers_stores_eight_vlenb_blocks() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
for i in 0u8..8 {
let data = array::from_fn::<_, 16, _>(|j| i * 16 + j as u8);
set_vreg(&mut state, VReg::from_bits(8 + i).unwrap(), &data);
}
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V8,
rs1: Reg::A0,
nreg: 8,
},
)
.unwrap();
for i in 0u8..8 {
let expected = array::from_fn::<_, 16, _>(|j| i * 16 + j as u8);
assert_eq!(
read_mem_bytes::<16>(&state, TEST_BASE_ADDR + u64::from(i) * 16),
&expected
);
}
}
#[test]
fn vsr_misaligned_register_returns_illegal_instruction() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
state.regs.write(Reg::A0, TEST_BASE_ADDR);
let result = exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V3,
rs1: Reg::A0,
nreg: 2,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsr_ignores_vtype_and_vl() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
state.ext_state.set_vtype(None);
state.ext_state.set_vl(0);
let data = array::from_fn::<_, 16, _>(|i| i as u8 + 0xAA);
set_vreg(&mut state, VReg::V0, &data);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V0,
rs1: Reg::A0,
nreg: 1,
},
)
.unwrap();
assert_eq!(read_mem_bytes::<16>(&state, TEST_BASE_ADDR), &data);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsr_vector_not_allowed_returns_illegal_instruction() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
state.ext_state.set_vector_allowed(false);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
let result = exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V0,
rs1: Reg::A0,
nreg: 1,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsr_honors_nonzero_vstart() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
let data = array::from_fn::<_, 16, _>(|i| i as u8 + 1);
set_vreg(&mut state, VReg::V2, &data);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
for i in 0u64..16 {
state.memory.write::<u8>(TEST_BASE_ADDR + i, 0xEE).unwrap();
}
state.ext_state.set_vstart(5);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V2,
rs1: Reg::A0,
nreg: 1,
},
)
.unwrap();
for i in 0u64..5 {
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + i).unwrap(), 0xEE);
}
for i in 5u64..16 {
assert_eq!(
state.memory.read::<u8>(TEST_BASE_ADDR + i).unwrap(),
i as u8 + 1
);
}
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsr_vstart_at_or_past_evl_writes_nothing() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
set_vreg(&mut state, VReg::V2, &[0xAA; 16]);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.memory.write::<u8>(TEST_BASE_ADDR, 0x55).unwrap();
state.ext_state.set_vstart(16);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V2,
rs1: Reg::A0,
nreg: 1,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 0x55);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsr_nreg2_vstart_spans_register_boundary() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
let d0 = array::from_fn::<_, 16, _>(|i| i as u8);
let d1 = array::from_fn::<_, 16, _>(|i| i as u8 + 16);
set_vreg(&mut state, VReg::V2, &d0);
set_vreg(&mut state, VReg::V3, &d1);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
for i in 0u64..32 {
state.memory.write::<u8>(TEST_BASE_ADDR + i, 0xEE).unwrap();
}
state.ext_state.set_vstart(20);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsr {
vs3: VReg::V2,
rs1: Reg::A0,
nreg: 2,
},
)
.unwrap();
for i in 0u64..20 {
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + i).unwrap(), 0xEE);
}
for i in 20u64..32 {
let in_reg = (i - 16) as u8;
assert_eq!(
state.memory.read::<u8>(TEST_BASE_ADDR + i).unwrap(),
in_reg + 16
);
}
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsm_stores_ceil_vl_over_8_bytes() {
let mut state = setup(9, Vsew::E8, Vlmul::M1);
let mask = [
0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
];
set_vreg(&mut state, VReg::V1, &mask);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsm {
vs3: VReg::V1,
rs1: Reg::A0,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 0xFF);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 1).unwrap(), 0x01);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 2).unwrap(), 0x00);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsm_vl_zero_writes_nothing() {
let mut state = setup(0, Vsew::E8, Vlmul::M1);
let mask = [0xFF; 16];
set_vreg(&mut state, VReg::V0, &mask);
state.memory.write::<u8>(TEST_BASE_ADDR, 0xAB).unwrap();
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsm {
vs3: VReg::V0,
rs1: Reg::A0,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 0xAB);
}
#[test]
fn vsm_vl_exactly_8_writes_one_byte() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut mask = [0u8; 16];
mask[0] = 0b10110101;
set_vreg(&mut state, VReg::V3, &mask);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsm {
vs3: VReg::V3,
rs1: Reg::A0,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 0b10110101);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 1).unwrap(), 0x00);
}
#[test]
fn vsm_vector_not_allowed_returns_illegal_instruction() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
let result = exec_one(
&mut state,
Zve64xStoreInstruction::Vsm {
vs3: VReg::V0,
rs1: Reg::A0,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsm_honors_vstart_in_byte_units_non_multiple_of_eight() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
let mut mask = [0u8; 16];
mask[0] = 0xAA; mask[1] = 0xBB; set_vreg(&mut state, VReg::V1, &mask);
state.memory.write::<u8>(TEST_BASE_ADDR, 0x11).unwrap();
state.memory.write::<u8>(TEST_BASE_ADDR + 1, 0x22).unwrap();
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.ext_state.set_vstart(1);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsm {
vs3: VReg::V1,
rs1: Reg::A0,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 0x11);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 1).unwrap(), 0xBB);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 2).unwrap(), 0x00);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsm_vstart_past_evl_writes_nothing() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V1, &[0xFF; 16]);
state.memory.write::<u8>(TEST_BASE_ADDR, 0x77).unwrap();
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.ext_state.set_vstart(8);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsm {
vs3: VReg::V1,
rs1: Reg::A0,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 0x77);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vse_e8_m1_stores_all_elements() {
let mut state = setup(16, Vsew::E8, Vlmul::M1);
let data = array::from_fn::<_, 16, _>(|i| i as u8 + 1);
set_vreg(&mut state, VReg::V4, &data);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V4,
rs1: Reg::A0,
vm: true,
eew: Eew::E8,
},
)
.unwrap();
assert_eq!(read_mem_bytes::<16>(&state, TEST_BASE_ADDR), &data);
assert_eq!(state.ext_state.vs_dirty_count(), 0);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vse_e32_m1_stores_partial_vl() {
let mut state = setup(3, Vsew::E32, Vlmul::M1);
let mut vreg = [0u8; 16];
vreg[0..4].copy_from_slice(&1u32.to_le_bytes());
vreg[4..8].copy_from_slice(&2u32.to_le_bytes());
vreg[8..12].copy_from_slice(&3u32.to_le_bytes());
set_vreg(&mut state, VReg::V0, &vreg);
state
.memory
.write::<u32>(TEST_BASE_ADDR + 12, 0xDEAD_BEEF)
.unwrap();
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V0,
rs1: Reg::A0,
vm: true,
eew: Eew::E32,
},
)
.unwrap();
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR).unwrap(), 1u32);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 4).unwrap(), 2u32);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 8).unwrap(), 3u32);
assert_eq!(
state.memory.read::<u32>(TEST_BASE_ADDR + 12).unwrap(),
0xDEAD_BEEF
);
}
#[test]
fn vse_e64_m1_stores_two_elements() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
let mut vreg = [0u8; 16];
vreg[0..8].copy_from_slice(&0x0102030405060708u64.to_le_bytes());
vreg[8..16].copy_from_slice(&0xAABBCCDDEEFF0011u64.to_le_bytes());
set_vreg(&mut state, VReg::V8, &vreg);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V8,
rs1: Reg::A0,
vm: true,
eew: Eew::E64,
},
)
.unwrap();
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR).unwrap(),
0x0102030405060708u64
);
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR + 8).unwrap(),
0xAABBCCDDEEFF0011u64
);
}
#[test]
fn vse_masked_skips_inactive_elements() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut mask = [0u8; 16];
mask[0] = 0b01010101;
set_vreg(&mut state, VReg::V0, &mask);
let data = array::from_fn::<_, 16, _>(|i| (i as u8 + 1) * 10);
set_vreg(&mut state, VReg::V2, &data);
for i in 0u64..8 {
state.memory.write::<u8>(TEST_BASE_ADDR + i, 0xFF).unwrap();
}
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V2,
rs1: Reg::A0,
vm: false,
eew: Eew::E8,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 10);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 2).unwrap(), 30);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 4).unwrap(), 50);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 6).unwrap(), 70);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 1).unwrap(), 0xFF);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 3).unwrap(), 0xFF);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 5).unwrap(), 0xFF);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 7).unwrap(), 0xFF);
}
#[test]
fn vse_vstart_nonzero_skips_earlier_elements() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let mut vreg = [0u8; 16];
for i in 0u32..4 {
vreg[i as usize * 4..(i as usize + 1) * 4].copy_from_slice(&(i + 1).to_le_bytes());
}
set_vreg(&mut state, VReg::V0, &vreg);
state.memory.write::<u32>(TEST_BASE_ADDR, 0xDEAD).unwrap();
state
.memory
.write::<u32>(TEST_BASE_ADDR + 4, 0xBEEF)
.unwrap();
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.ext_state.set_vstart(2);
exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V0,
rs1: Reg::A0,
vm: true,
eew: Eew::E32,
},
)
.unwrap();
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR).unwrap(), 0xDEAD);
assert_eq!(
state.memory.read::<u32>(TEST_BASE_ADDR + 4).unwrap(),
0xBEEF
);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 8).unwrap(), 3u32);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 12).unwrap(), 4u32);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vse_masked_vs3_equals_v0_is_legal() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
let mut mask_and_data = [0u8; 16];
mask_and_data[0] = 0b11111111;
set_vreg(&mut state, VReg::V0, &mask_and_data);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V0,
rs1: Reg::A0,
vm: false,
eew: Eew::E8,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 0b11111111);
}
#[test]
fn vse_vtype_illegal_returns_illegal_instruction() {
let mut state = initialize_state([]);
state.ext_state.init_vector_csrs();
state.ext_state.set_vtype(None);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
let result = exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V4,
rs1: Reg::A0,
vm: true,
eew: Eew::E8,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vse_vector_not_allowed_returns_illegal_instruction() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
state.ext_state.set_vector_allowed(false);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
let result = exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V4,
rs1: Reg::A0,
vm: true,
eew: Eew::E32,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsse_positive_stride_stores_with_gap() {
let mut state = setup(3, Vsew::E32, Vlmul::M1);
let mut vreg = [0u8; 16];
vreg[0..4].copy_from_slice(&10u32.to_le_bytes());
vreg[4..8].copy_from_slice(&20u32.to_le_bytes());
vreg[8..12].copy_from_slice(&30u32.to_le_bytes());
set_vreg(&mut state, VReg::V0, &vreg);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.regs.write(Reg::A1, 8);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsse {
vs3: VReg::V0,
rs1: Reg::A0,
rs2: Reg::A1,
vm: true,
eew: Eew::E32,
},
)
.unwrap();
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR).unwrap(), 10u32);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 8).unwrap(), 20u32);
assert_eq!(
state.memory.read::<u32>(TEST_BASE_ADDR + 16).unwrap(),
30u32
);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 4).unwrap(), 0u32);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 12).unwrap(), 0u32);
}
#[test]
fn vsse_zero_stride_writes_same_address_repeatedly() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
let data = array::from_fn::<_, 16, _>(|i| i as u8 + 1);
set_vreg(&mut state, VReg::V1, &data);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.regs.write(Reg::A1, 0);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsse {
vs3: VReg::V1,
rs1: Reg::A0,
rs2: Reg::A1,
vm: true,
eew: Eew::E8,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 4u8);
}
#[test]
fn vsse_negative_stride_stores_in_reverse() {
let mut state = setup(3, Vsew::E32, Vlmul::M1);
let mut vreg = [0u8; 16];
vreg[0..4].copy_from_slice(&1u32.to_le_bytes());
vreg[4..8].copy_from_slice(&2u32.to_le_bytes());
vreg[8..12].copy_from_slice(&3u32.to_le_bytes());
set_vreg(&mut state, VReg::V0, &vreg);
state.regs.write(Reg::A0, TEST_BASE_ADDR + 8);
state.regs.write(Reg::A1, (-4i64).cast_unsigned());
exec_one(
&mut state,
Zve64xStoreInstruction::Vsse {
vs3: VReg::V0,
rs1: Reg::A0,
rs2: Reg::A1,
vm: true,
eew: Eew::E32,
},
)
.unwrap();
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 8).unwrap(), 1u32);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 4).unwrap(), 2u32);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR).unwrap(), 3u32);
}
#[test]
fn vsse_masked_skips_inactive_elements() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
let mut mask = [0u8; 16];
mask[0] = 0b00000001;
set_vreg(&mut state, VReg::V0, &mask);
let mut vreg = [0u8; 16];
vreg[0..8].copy_from_slice(&0xAAAAAAAAAAAAAAAAu64.to_le_bytes());
vreg[8..16].copy_from_slice(&0xBBBBBBBBBBBBBBBBu64.to_le_bytes());
set_vreg(&mut state, VReg::V2, &vreg);
state
.memory
.write::<u64>(TEST_BASE_ADDR + 16, 0x1234567890ABCDEFu64)
.unwrap();
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.regs.write(Reg::A1, 16);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsse {
vs3: VReg::V2,
rs1: Reg::A0,
rs2: Reg::A1,
vm: false,
eew: Eew::E64,
},
)
.unwrap();
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR).unwrap(),
0xAAAAAAAAAAAAAAAAu64
);
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR + 16).unwrap(),
0x1234567890ABCDEFu64
);
}
#[test]
fn vsuxei_e32_data_e32_index_stores_at_indexed_addresses() {
let mut state = setup(3, Vsew::E32, Vlmul::M1);
let mut data_reg = [0u8; 16];
data_reg[0..4].copy_from_slice(&100u32.to_le_bytes());
data_reg[4..8].copy_from_slice(&200u32.to_le_bytes());
data_reg[8..12].copy_from_slice(&300u32.to_le_bytes());
set_vreg(&mut state, VReg::V2, &data_reg);
let mut idx_reg = [0u8; 16];
idx_reg[0..4].copy_from_slice(&0u32.to_le_bytes());
idx_reg[4..8].copy_from_slice(&8u32.to_le_bytes());
idx_reg[8..12].copy_from_slice(&16u32.to_le_bytes());
set_vreg(&mut state, VReg::V4, &idx_reg);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsuxei {
vs3: VReg::V2,
rs1: Reg::A0,
vs2: VReg::V4,
vm: true,
eew: Eew::E32,
},
)
.unwrap();
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR).unwrap(), 100u32);
assert_eq!(
state.memory.read::<u32>(TEST_BASE_ADDR + 8).unwrap(),
200u32
);
assert_eq!(
state.memory.read::<u32>(TEST_BASE_ADDR + 16).unwrap(),
300u32
);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsoxei_e64_data_e64_index_stores_at_indexed_addresses() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
let mut data_reg = [0u8; 16];
data_reg[0..8].copy_from_slice(&0xDEADBEEFDEADBEEFu64.to_le_bytes());
data_reg[8..16].copy_from_slice(&0xCAFEBABECAFEBABEu64.to_le_bytes());
set_vreg(&mut state, VReg::V2, &data_reg);
let mut idx_reg = [0u8; 16];
idx_reg[0..8].copy_from_slice(&0u64.to_le_bytes());
idx_reg[8..16].copy_from_slice(&32u64.to_le_bytes());
set_vreg(&mut state, VReg::V4, &idx_reg);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsoxei {
vs3: VReg::V2,
rs1: Reg::A0,
vs2: VReg::V4,
vm: true,
eew: Eew::E64,
},
)
.unwrap();
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR).unwrap(),
0xDEADBEEFDEADBEEFu64
);
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR + 32).unwrap(),
0xCAFEBABECAFEBABEu64
);
}
#[test]
fn vsuxei_e8_index_scatter_e8_data() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
let mut data_reg = [0u8; 16];
data_reg[0] = 0xAA;
data_reg[1] = 0xBB;
data_reg[2] = 0xCC;
data_reg[3] = 0xDD;
set_vreg(&mut state, VReg::V8, &data_reg);
let mut idx_reg = [0u8; 16];
idx_reg[0] = 5;
idx_reg[1] = 2;
idx_reg[2] = 0;
idx_reg[3] = 7;
set_vreg(&mut state, VReg::V10, &idx_reg);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsuxei {
vs3: VReg::V8,
rs1: Reg::A0,
vs2: VReg::V10,
vm: true,
eew: Eew::E8,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 5).unwrap(), 0xAA);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 2).unwrap(), 0xBB);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 0xCC);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 7).unwrap(), 0xDD);
}
#[test]
fn vsuxei_masked_skips_inactive_elements() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let mut mask = [0u8; 16];
mask[0] = 0b00001001;
set_vreg(&mut state, VReg::V0, &mask);
let mut data_reg = [0u8; 16];
for i in 0u32..4 {
data_reg[i as usize * 4..(i as usize + 1) * 4]
.copy_from_slice(&((i + 1) * 100).to_le_bytes());
}
set_vreg(&mut state, VReg::V2, &data_reg);
let mut idx_reg = [0u8; 16];
for i in 0u32..4 {
idx_reg[i as usize * 4..(i as usize + 1) * 4].copy_from_slice(&(i * 8).to_le_bytes());
}
set_vreg(&mut state, VReg::V4, &idx_reg);
state
.memory
.write::<u32>(TEST_BASE_ADDR + 8, 0xDEAD)
.unwrap();
state
.memory
.write::<u32>(TEST_BASE_ADDR + 16, 0xBEEF)
.unwrap();
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsuxei {
vs3: VReg::V2,
rs1: Reg::A0,
vs2: VReg::V4,
vm: false,
eew: Eew::E32,
},
)
.unwrap();
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR).unwrap(), 100u32);
assert_eq!(
state.memory.read::<u32>(TEST_BASE_ADDR + 24).unwrap(),
400u32
);
assert_eq!(
state.memory.read::<u32>(TEST_BASE_ADDR + 8).unwrap(),
0xDEAD
);
assert_eq!(
state.memory.read::<u32>(TEST_BASE_ADDR + 16).unwrap(),
0xBEEF
);
}
#[test]
fn vsuxei_misaligned_data_register_returns_illegal() {
let mut state = setup(4, Vsew::E32, Vlmul::M2);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
let idx_reg = [0u8; 16];
set_vreg(&mut state, VReg::V4, &idx_reg);
let result = exec_one(
&mut state,
Zve64xStoreInstruction::Vsuxei {
vs3: VReg::V3,
rs1: Reg::A0,
vs2: VReg::V4,
vm: true,
eew: Eew::E32,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsseg_nf2_e8_m1_stores_two_fields_interleaved() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
let f0 = array::from_fn::<_, 16, _>(|i| i as u8 + 1);
let f1 = array::from_fn::<_, 16, _>(|i| i as u8 + 17);
set_vreg(&mut state, VReg::V2, &f0);
set_vreg(&mut state, VReg::V3, &f1);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsseg {
vs3: VReg::V2,
rs1: Reg::A0,
vm: true,
eew: Eew::E8,
nf: 2,
},
)
.unwrap();
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR).unwrap(), 1);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 1).unwrap(), 17);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 2).unwrap(), 2);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 3).unwrap(), 18);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 4).unwrap(), 3);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 5).unwrap(), 19);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 6).unwrap(), 4);
assert_eq!(state.memory.read::<u8>(TEST_BASE_ADDR + 7).unwrap(), 20);
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsseg_nf3_e32_m1_stores_three_fields_per_element() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
for (f, base_val) in [(VReg::V0, 1u32), (VReg::V1, 2u32), (VReg::V2, 3u32)] {
let mut reg = [0u8; 16];
reg[0..4].copy_from_slice(&base_val.to_le_bytes());
reg[4..8].copy_from_slice(&(base_val + 10).to_le_bytes());
set_vreg(&mut state, f, ®);
}
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsseg {
vs3: VReg::V0,
rs1: Reg::A0,
vm: true,
eew: Eew::E32,
nf: 3,
},
)
.unwrap();
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR).unwrap(), 1);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 4).unwrap(), 2);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 8).unwrap(), 3);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 12).unwrap(), 11);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 16).unwrap(), 12);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 20).unwrap(), 13);
}
#[test]
fn vsseg_register_group_out_of_bounds_returns_illegal() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
let result = exec_one(
&mut state,
Zve64xStoreInstruction::Vsseg {
vs3: VReg::V30,
rs1: Reg::A0,
vm: true,
eew: Eew::E32,
nf: 4,
},
);
assert!(matches!(
result,
Err(ExecutionError::IllegalInstruction { .. })
));
}
#[test]
fn vsseg_masked_vs3_equals_v0_is_legal() {
let mut state = setup(4, Vsew::E8, Vlmul::M1);
let mut mask_and_f0 = [0u8; 16];
mask_and_f0[0] = 0b00001111;
set_vreg(&mut state, VReg::V0, &mask_and_f0);
let f1 = array::from_fn::<_, 16, _>(|i| i as u8 + 100);
set_vreg(&mut state, VReg::V1, &f1);
for i in 0u64..8 {
state.memory.write::<u8>(TEST_BASE_ADDR + i, 0xEE).unwrap();
}
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsseg {
vs3: VReg::V0,
rs1: Reg::A0,
vm: false,
eew: Eew::E8,
nf: 2,
},
)
.unwrap();
for i in 0u64..4 {
assert_eq!(
state.memory.read::<u8>(TEST_BASE_ADDR + i * 2).unwrap(),
if i == 0 { 0b00001111 } else { 0 }
);
assert_eq!(
state.memory.read::<u8>(TEST_BASE_ADDR + i * 2 + 1).unwrap(),
i as u8 + 100
);
}
}
#[test]
fn vssseg_nf2_e32_m1_stride_16_stores_correctly() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
let mut f0 = [0u8; 16];
f0[0..4].copy_from_slice(&10u32.to_le_bytes());
f0[4..8].copy_from_slice(&30u32.to_le_bytes());
let mut f1 = [0u8; 16];
f1[0..4].copy_from_slice(&20u32.to_le_bytes());
f1[4..8].copy_from_slice(&40u32.to_le_bytes());
set_vreg(&mut state, VReg::V2, &f0);
set_vreg(&mut state, VReg::V3, &f1);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.regs.write(Reg::A1, 16);
exec_one(
&mut state,
Zve64xStoreInstruction::Vssseg {
vs3: VReg::V2,
rs1: Reg::A0,
rs2: Reg::A1,
vm: true,
eew: Eew::E32,
nf: 2,
},
)
.unwrap();
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR).unwrap(), 10);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 4).unwrap(), 20);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 16).unwrap(), 30);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 20).unwrap(), 40);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 8).unwrap(), 0);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 12).unwrap(), 0);
}
#[test]
fn vsuxseg_nf2_e32_index_e32_data_stores_segments_at_indexed_addresses() {
let mut state = setup(2, Vsew::E32, Vlmul::M1);
let mut f0 = [0u8; 16];
f0[0..4].copy_from_slice(&100u32.to_le_bytes());
f0[4..8].copy_from_slice(&200u32.to_le_bytes());
let mut f1 = [0u8; 16];
f1[0..4].copy_from_slice(&101u32.to_le_bytes());
f1[4..8].copy_from_slice(&201u32.to_le_bytes());
set_vreg(&mut state, VReg::V2, &f0);
set_vreg(&mut state, VReg::V3, &f1);
let mut idx = [0u8; 16];
idx[0..4].copy_from_slice(&0u32.to_le_bytes());
idx[4..8].copy_from_slice(&32u32.to_le_bytes());
set_vreg(&mut state, VReg::V6, &idx);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsuxseg {
vs3: VReg::V2,
rs1: Reg::A0,
vs2: VReg::V6,
vm: true,
eew: Eew::E32,
nf: 2,
},
)
.unwrap();
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR).unwrap(), 100);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 4).unwrap(), 101);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 32).unwrap(), 200);
assert_eq!(state.memory.read::<u32>(TEST_BASE_ADDR + 36).unwrap(), 201);
}
#[test]
fn vsoxseg_nf2_e64_index_e64_data_stores_in_element_order() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
let mut f0 = [0u8; 16];
f0[0..8].copy_from_slice(&0xAAAAAAAAAAAAAAAAu64.to_le_bytes());
f0[8..16].copy_from_slice(&0xBBBBBBBBBBBBBBBBu64.to_le_bytes());
let mut f1 = [0u8; 16];
f1[0..8].copy_from_slice(&0xCCCCCCCCCCCCCCCCu64.to_le_bytes());
f1[8..16].copy_from_slice(&0xDDDDDDDDDDDDDDDDu64.to_le_bytes());
set_vreg(&mut state, VReg::V2, &f0);
set_vreg(&mut state, VReg::V3, &f1);
let mut idx = [0u8; 16];
idx[0..8].copy_from_slice(&64u64.to_le_bytes());
idx[8..16].copy_from_slice(&0u64.to_le_bytes());
set_vreg(&mut state, VReg::V6, &idx);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsoxseg {
vs3: VReg::V2,
rs1: Reg::A0,
vs2: VReg::V6,
vm: true,
eew: Eew::E64,
nf: 2,
},
)
.unwrap();
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR + 64).unwrap(),
0xAAAAAAAAAAAAAAAAu64
);
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR + 72).unwrap(),
0xCCCCCCCCCCCCCCCCu64
);
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR).unwrap(),
0xBBBBBBBBBBBBBBBBu64
);
assert_eq!(
state.memory.read::<u64>(TEST_BASE_ADDR + 8).unwrap(),
0xDDDDDDDDDDDDDDDDu64
);
}
#[test]
fn vse_resets_vstart_to_zero() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let vreg = [0u8; 16];
set_vreg(&mut state, VReg::V4, &vreg);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.ext_state.set_vstart(2);
exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V4,
rs1: Reg::A0,
vm: true,
eew: Eew::E32,
},
)
.unwrap();
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsse_resets_vstart_to_zero() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
let vreg = [0u8; 16];
set_vreg(&mut state, VReg::V4, &vreg);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.regs.write(Reg::A1, 8);
state.ext_state.set_vstart(1);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsse {
vs3: VReg::V4,
rs1: Reg::A0,
rs2: Reg::A1,
vm: true,
eew: Eew::E64,
},
)
.unwrap();
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn vsm_resets_vstart_to_zero() {
let mut state = setup(8, Vsew::E8, Vlmul::M1);
set_vreg(&mut state, VReg::V1, &[0u8; 16]);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.ext_state.set_vstart(3);
exec_one(
&mut state,
Zve64xStoreInstruction::Vsm {
vs3: VReg::V1,
rs1: Reg::A0,
},
)
.unwrap();
assert_eq!(state.ext_state.vstart(), 0);
}
#[test]
fn stores_never_mark_vs_dirty() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let vreg = [0u8; 16];
set_vreg(&mut state, VReg::V4, &vreg);
state.regs.write(Reg::A0, TEST_BASE_ADDR);
state.regs.write(Reg::A1, 4);
exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V4,
rs1: Reg::A0,
vm: true,
eew: Eew::E32,
},
)
.unwrap();
exec_one(
&mut state,
Zve64xStoreInstruction::Vsse {
vs3: VReg::V4,
rs1: Reg::A0,
rs2: Reg::A1,
vm: true,
eew: Eew::E32,
},
)
.unwrap();
assert_eq!(state.ext_state.vs_dirty_count(), 0);
}
#[test]
fn vse_out_of_bounds_write_returns_memory_access_error() {
let mut state = setup(4, Vsew::E32, Vlmul::M1);
let vreg = [0u8; 16];
set_vreg(&mut state, VReg::V0, &vreg);
state.regs.write(Reg::A0, TEST_BASE_ADDR + 8192 - 4);
let result = exec_one(
&mut state,
Zve64xStoreInstruction::Vse {
vs3: VReg::V0,
rs1: Reg::A0,
vm: true,
eew: Eew::E32,
},
);
assert!(matches!(result, Err(ExecutionError::MemoryAccess(_))));
}
#[test]
fn vsse_out_of_bounds_write_returns_memory_access_error() {
let mut state = setup(2, Vsew::E64, Vlmul::M1);
let vreg = [0u8; 16];
set_vreg(&mut state, VReg::V0, &vreg);
state.regs.write(Reg::A0, TEST_BASE_ADDR + 8192 - 8);
state.regs.write(Reg::A1, 8);
let result = exec_one(
&mut state,
Zve64xStoreInstruction::Vsse {
vs3: VReg::V0,
rs1: Reg::A0,
rs2: Reg::A1,
vm: true,
eew: Eew::E64,
},
);
assert!(matches!(result, Err(ExecutionError::MemoryAccess(_))));
}