Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion src/isa/riscv/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3429,7 +3429,12 @@ impl RiscVCpu {
}
Op::Viota => {
// vd[i] = count of active set bits in vs2 strictly before i.
// This prefix scan is not restartable: non-zero vstart traps.
// This prefix scan is not restartable: non-zero vstart traps,
// and a masked (vm=0) encoding must not write the v0 mask
// register it reads from (RVV §5.3; QEMU require_vm).
if !vm && vd == 0 {
return Err(Trap::illegal(insn.raw));
}
if vstart != 0 {
return Err(Trap::illegal(insn.raw));
}
Expand Down Expand Up @@ -6024,6 +6029,30 @@ mod tests {
}
}

#[test]
fn viota_rejects_masked_vd_v0() {
// viota in masked form (vm=0) reads the mask from v0, so a
// destination of v0 is a reserved encoding (RVV §5.3; QEMU
// trans_viota_m -> require_vm: `vm != 0 || v != 0`).
// e8,m1 -> single register. vs1 field for viota.m = 10000.
let mut c = cpu_e8m1();
// viota.m v0, v2, v0.t (vs1=10000) -> illegal.
assert!(matches!(
run_one(&mut c, op_v(0b010100, 0, 2, 0b10000, 0b010, 0)),
RiscVExit::Trap(_)
));
// Controls: masked viota.m v2, v2 (vd!=v0) runs; unmasked
// viota.v v0, v2 (vm=1) runs.
assert!(matches!(
run_one(&mut c, op_v(0b010100, 0, 2, 0b10000, 0b010, 2)),
RiscVExit::Continue
));
assert!(matches!(
run_one(&mut c, op_v(0b010100, 1, 2, 0b10000, 0b010, 0)),
RiscVExit::Continue
));
}

#[test]
fn vmvr_rejects_reserved_encodings() {
// vmv<nr>r.v: funct6=0b100111, funct3=0b011, OP-V (0x57). Only nr in
Expand Down
Loading