use crate::RegisterFile;
use crate::rv64::test_utils::{execute, initialize_state};
use ab_riscv_primitives::prelude::*;
#[test]
fn test_mul() {
let mut state = initialize_state([Rv64MInstruction::Mul {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 7);
state.regs.write(Reg::A1, 8);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 56);
}
#[test]
fn test_mulh() {
let mut state = initialize_state([Rv64MInstruction::Mulh {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, i64::MAX as u64);
state.regs.write(Reg::A1, 2);
execute(&mut state).unwrap();
let (_, hi) = i64::MAX.widening_mul(2);
assert_eq!(state.regs.read(Reg::A2), hi.cast_unsigned());
}
#[test]
fn test_mulhu() {
let mut state = initialize_state([Rv64MInstruction::Mulhu {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, u64::MAX);
state.regs.write(Reg::A1, u64::MAX);
execute(&mut state).unwrap();
let prod = (u64::MAX as u128) * (u64::MAX as u128);
assert_eq!(state.regs.read(Reg::A2), (prod >> 64) as u64);
}
#[test]
fn test_mulhsu() {
let mut state = initialize_state([Rv64MInstruction::Mulhsu {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, (-2i64).cast_unsigned());
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
let prod = (-2i64 as i128) * (3i128);
assert_eq!(
state.regs.read(Reg::A2),
(prod >> 64).cast_unsigned() as u64
);
}
#[test]
fn test_div() {
let mut state = initialize_state([Rv64MInstruction::Div {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2).cast_signed(), 6);
}
#[test]
fn test_div_by_zero() {
let mut state = initialize_state([Rv64MInstruction::Div {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 0);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), (-1i64).cast_unsigned());
}
#[test]
fn test_div_overflow() {
let mut state = initialize_state([Rv64MInstruction::Div {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, i64::MIN.cast_unsigned());
state.regs.write(Reg::A1, (-1i64).cast_unsigned());
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), i64::MIN.cast_unsigned());
}
#[test]
fn test_divu() {
let mut state = initialize_state([Rv64MInstruction::Divu {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 6);
}
#[test]
fn test_divu_by_zero() {
let mut state = initialize_state([Rv64MInstruction::Divu {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 0);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), u64::MAX);
}
#[test]
fn test_rem() {
let mut state = initialize_state([Rv64MInstruction::Rem {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2).cast_signed(), 2);
}
#[test]
fn test_rem_by_zero() {
let mut state = initialize_state([Rv64MInstruction::Rem {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 0);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 20);
}
#[test]
fn test_rem_overflow() {
let mut state = initialize_state([Rv64MInstruction::Rem {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, i64::MIN.cast_unsigned());
state.regs.write(Reg::A1, (-1i64).cast_unsigned());
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 0);
}
#[test]
fn test_remu() {
let mut state = initialize_state([Rv64MInstruction::Remu {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 2);
}
#[test]
fn test_remu_by_zero() {
let mut state = initialize_state([Rv64MInstruction::Remu {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 0);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 20);
}
#[test]
fn test_mulw_basic() {
let mut state = initialize_state([Rv64MInstruction::Mulw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 7);
state.regs.write(Reg::A1, 8);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 56);
}
#[test]
fn test_mulw_overflow() {
let mut state = initialize_state([Rv64MInstruction::Mulw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 0x7FFF_FFFF);
state.regs.write(Reg::A1, 2);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 0xFFFF_FFFF_FFFF_FFFE);
}
#[test]
fn test_mulw_negative() {
let mut state = initialize_state([Rv64MInstruction::Mulw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, (-3i32).cast_unsigned() as u64);
state.regs.write(Reg::A1, 4);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), (-12i64).cast_unsigned());
}
#[test]
fn test_mulw_ignores_upper_bits() {
let mut state = initialize_state([Rv64MInstruction::Mulw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 0xDEAD_BEEF_0000_0007);
state.regs.write(Reg::A1, 0xCAFE_BABE_0000_0008);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 56);
}
#[test]
fn test_divw_basic() {
let mut state = initialize_state([Rv64MInstruction::Divw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 6);
}
#[test]
fn test_divw_negative() {
let mut state = initialize_state([Rv64MInstruction::Divw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, (-20i32).cast_unsigned() as u64);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), (-6i64).cast_unsigned());
}
#[test]
fn test_divw_by_zero() {
let mut state = initialize_state([Rv64MInstruction::Divw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 0);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), (-1i64).cast_unsigned());
}
#[test]
fn test_divw_overflow() {
let mut state = initialize_state([Rv64MInstruction::Divw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, i32::MIN.cast_unsigned() as u64);
state.regs.write(Reg::A1, (-1i32).cast_unsigned() as u64);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), (i32::MIN as i64).cast_unsigned());
}
#[test]
fn test_divw_ignores_upper_bits() {
let mut state = initialize_state([Rv64MInstruction::Divw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 0xDEAD_BEEF_0000_0014); state.regs.write(Reg::A1, 0xCAFE_BABE_0000_0003);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 6);
}
#[test]
fn test_divuw_basic() {
let mut state = initialize_state([Rv64MInstruction::Divuw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 6);
}
#[test]
fn test_divuw_large_unsigned() {
let mut state = initialize_state([Rv64MInstruction::Divuw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 0xFFFF_FFFF); state.regs.write(Reg::A1, 2);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 0x0000_0000_7FFF_FFFF);
}
#[test]
fn test_divuw_by_zero() {
let mut state = initialize_state([Rv64MInstruction::Divuw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 0);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 0xFFFF_FFFF_FFFF_FFFF);
}
#[test]
fn test_divuw_ignores_upper_bits() {
let mut state = initialize_state([Rv64MInstruction::Divuw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 0xDEAD_BEEF_0000_0064); state.regs.write(Reg::A1, 0xCAFE_BABE_0000_0005);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 20);
}
#[test]
fn test_remw_basic() {
let mut state = initialize_state([Rv64MInstruction::Remw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 2);
}
#[test]
fn test_remw_negative_dividend() {
let mut state = initialize_state([Rv64MInstruction::Remw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, (-20i32).cast_unsigned() as u64);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), (-2i64).cast_unsigned());
}
#[test]
fn test_remw_negative_divisor() {
let mut state = initialize_state([Rv64MInstruction::Remw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, (-3i32).cast_unsigned() as u64);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 2);
}
#[test]
fn test_remw_by_zero() {
let mut state = initialize_state([Rv64MInstruction::Remw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 0);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 20);
}
#[test]
fn test_remw_overflow() {
let mut state = initialize_state([Rv64MInstruction::Remw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, i32::MIN.cast_unsigned() as u64);
state.regs.write(Reg::A1, (-1i32).cast_unsigned() as u64);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 0);
}
#[test]
fn test_remw_ignores_upper_bits() {
let mut state = initialize_state([Rv64MInstruction::Remw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 0xDEAD_BEEF_0000_0017); state.regs.write(Reg::A1, 0xCAFE_BABE_0000_0005);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 3);
}
#[test]
fn test_remuw_basic() {
let mut state = initialize_state([Rv64MInstruction::Remuw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 3);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 2);
}
#[test]
fn test_remuw_large_unsigned() {
let mut state = initialize_state([Rv64MInstruction::Remuw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 0xFFFF_FFFF); state.regs.write(Reg::A1, 10);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 5);
}
#[test]
fn test_remuw_by_zero() {
let mut state = initialize_state([Rv64MInstruction::Remuw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 20);
state.regs.write(Reg::A1, 0);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 20);
}
#[test]
fn test_remuw_ignores_upper_bits() {
let mut state = initialize_state([Rv64MInstruction::Remuw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 0xDEAD_BEEF_0000_0064); state.regs.write(Reg::A1, 0xCAFE_BABE_0000_0007);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 2);
}
#[test]
fn test_remuw_negative_as_unsigned() {
let mut state = initialize_state([Rv64MInstruction::Remuw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
}]);
state.regs.write(Reg::A0, 0xFFFF_FFFF); state.regs.write(Reg::A1, 2);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 1);
}
#[test]
fn test_mulw_divw_combination() {
let mut state = initialize_state([
Rv64MInstruction::Mulw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
},
Rv64MInstruction::Divw {
rd: Reg::A3,
rs1: Reg::A2,
rs2: Reg::A0,
},
]);
state.regs.write(Reg::A0, 7);
state.regs.write(Reg::A1, 8);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 56);
assert_eq!(state.regs.read(Reg::A3), 8);
}
#[test]
fn test_divw_remw_combination() {
let mut state = initialize_state([
Rv64MInstruction::Divw {
rd: Reg::A2,
rs1: Reg::A0,
rs2: Reg::A1,
},
Rv64MInstruction::Remw {
rd: Reg::A3,
rs1: Reg::A0,
rs2: Reg::A1,
},
]);
state.regs.write(Reg::A0, 23);
state.regs.write(Reg::A1, 5);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::A2), 4);
assert_eq!(state.regs.read(Reg::A3), 3);
}
#[test]
fn test_rv64m_zero_register() {
let mut state = initialize_state([
Rv64MInstruction::Mulw {
rd: Reg::Zero,
rs1: Reg::A0,
rs2: Reg::A0,
},
Rv64MInstruction::Mulw {
rd: Reg::A1,
rs1: Reg::Zero,
rs2: Reg::A0,
},
]);
state.regs.write(Reg::A0, 42);
execute(&mut state).unwrap();
assert_eq!(state.regs.read(Reg::Zero), 0);
assert_eq!(state.regs.read(Reg::A1), 0);
}