use crate::exec::compute::alu;
use crate::isa::op::AluOp;
const ZERO: u64 = 0;
const ONE: u64 = 1;
const NEG1: u64 = u64::MAX;
const I64_MAX: u64 = i64::MAX as u64; const I64_MIN: u64 = i64::MIN as u64;
const I32_MIN_SEXT: u64 = i32::MIN as i64 as u64;
fn alu(op: AluOp, a: u64, b: u64, is32: bool) -> u64 {
alu::execute(op, a, b, 0, is32)
}
fn sext32(val: u32) -> u64 {
val as i32 as i64 as u64
}
#[test]
fn sll_rv64_shift_by_zero() {
assert_eq!(alu(AluOp::Sll, 0xDEAD_BEEF, ZERO, false), 0xDEAD_BEEF);
}
#[test]
fn sll_rv64_shift_by_one() {
assert_eq!(alu(AluOp::Sll, ONE, ONE, false), 2);
}
#[test]
fn sll_rv64_shift_by_63() {
assert_eq!(alu(AluOp::Sll, ONE, 63, false), I64_MIN);
}
#[test]
fn sll_rv64_shift_all_ones() {
assert_eq!(alu(AluOp::Sll, NEG1, ONE, false), NEG1 - 1);
}
#[test]
fn sll_rv64_shift_out_all_bits() {
assert_eq!(alu(AluOp::Sll, NEG1, 63, false), I64_MIN);
}
#[test]
fn sll_rv64_zero_shifted() {
assert_eq!(alu(AluOp::Sll, ZERO, 32, false), 0);
}
#[test]
fn sll_rv64_shift_amount_masked_to_6_bits() {
assert_eq!(alu(AluOp::Sll, 42, 64, false), 42);
assert_eq!(alu(AluOp::Sll, 42, 65, false), 84);
assert_eq!(alu(AluOp::Sll, ONE, 127, false), I64_MIN);
}
#[test]
fn sll_rv64_upper_bits_of_shift_ignored() {
assert_eq!(alu(AluOp::Sll, ONE, 0xFFFF_FFFF_FFFF_FF01, false), 2);
}
#[test]
fn sll_rv64_power_of_two_generation() {
for i in 0..64 {
assert_eq!(alu(AluOp::Sll, ONE, i, false), 1u64 << i, "SLL failed: 1 << {i}");
}
}
#[test]
fn sllw_shift_by_zero() {
assert_eq!(alu(AluOp::Sll, 0xDEAD_BEEF, ZERO, true), sext32(0xDEAD_BEEF));
}
#[test]
fn sllw_shift_by_one() {
assert_eq!(alu(AluOp::Sll, ONE, ONE, true), 2);
}
#[test]
fn sllw_shift_by_31() {
assert_eq!(alu(AluOp::Sll, ONE, 31, true), I32_MIN_SEXT);
}
#[test]
fn sllw_shift_amount_masked_to_5_bits() {
assert_eq!(alu(AluOp::Sll, 42, 32, true), sext32(42));
assert_eq!(alu(AluOp::Sll, 42, 33, true), sext32(84));
}
#[test]
fn sllw_ignores_upper_32_bits_of_operand() {
assert_eq!(alu(AluOp::Sll, 0xFFFF_FFFF_0000_0001, 1, true), 2);
}
#[test]
fn sllw_result_sign_extends_when_bit31_set() {
assert_eq!(alu(AluOp::Sll, 0x4000_0000, 1, true), I32_MIN_SEXT);
}
#[test]
fn sllw_all_ones_shift_by_1() {
assert_eq!(alu(AluOp::Sll, 0xFFFF_FFFF, 1, true), sext32(0xFFFF_FFFE));
}
#[test]
fn srl_rv64_shift_by_zero() {
assert_eq!(alu(AluOp::Srl, 0xDEAD_BEEF, ZERO, false), 0xDEAD_BEEF);
}
#[test]
fn srl_rv64_shift_by_one() {
assert_eq!(alu(AluOp::Srl, 2, ONE, false), 1);
}
#[test]
fn srl_rv64_shift_by_63() {
assert_eq!(alu(AluOp::Srl, I64_MIN, 63, false), 1);
}
#[test]
fn srl_rv64_fills_with_zeros() {
assert_eq!(alu(AluOp::Srl, NEG1, ONE, false), I64_MAX);
}
#[test]
fn srl_rv64_all_ones_shift_by_63() {
assert_eq!(alu(AluOp::Srl, NEG1, 63, false), 1);
}
#[test]
fn srl_rv64_zero_shifted() {
assert_eq!(alu(AluOp::Srl, ZERO, 32, false), 0);
}
#[test]
fn srl_rv64_shift_amount_masked() {
assert_eq!(alu(AluOp::Srl, 42, 64, false), 42);
assert_eq!(alu(AluOp::Srl, 42, 65, false), 21);
}
#[test]
fn srl_rv64_upper_bits_of_shift_ignored() {
assert_eq!(alu(AluOp::Srl, 0x100, 0xFFFF_FFFF_FFFF_FF04, false), 0x10);
}
#[test]
fn srl_rv64_successive_shifts() {
for i in 0..64 {
let expected = NEG1 >> i;
assert_eq!(alu(AluOp::Srl, NEG1, i, false), expected, "SRL failed: 0xFFFF... >> {i}");
}
}
#[test]
fn srlw_shift_by_zero() {
assert_eq!(alu(AluOp::Srl, 0xDEAD_BEEF, ZERO, true), sext32(0xDEAD_BEEF));
}
#[test]
fn srlw_shift_by_one() {
assert_eq!(alu(AluOp::Srl, 0x8000_0000, ONE, true), sext32(0x4000_0000));
}
#[test]
fn srlw_shift_by_31() {
assert_eq!(alu(AluOp::Srl, 0x8000_0000, 31, true), 1);
}
#[test]
fn srlw_shift_amount_masked_to_5_bits() {
assert_eq!(alu(AluOp::Srl, 0xDEAD_BEEF, 32, true), sext32(0xDEAD_BEEF));
}
#[test]
fn srlw_ignores_upper_32_bits_of_operand() {
assert_eq!(alu(AluOp::Srl, 0xFFFF_FFFF_8000_0000, 1, true), sext32(0x4000_0000));
}
#[test]
fn srlw_zero_fill_clears_sign_bit() {
assert_eq!(alu(AluOp::Srl, 0xFFFF_FFFF, 1, true), sext32(0x7FFF_FFFF));
}
#[test]
fn srlw_all_ones_shift_by_31() {
assert_eq!(alu(AluOp::Srl, 0xFFFF_FFFF, 31, true), 1);
}
#[test]
fn sra_rv64_shift_by_zero() {
assert_eq!(alu(AluOp::Sra, 0xDEAD_BEEF, ZERO, false), 0xDEAD_BEEF);
}
#[test]
fn sra_rv64_positive_shift() {
assert_eq!(alu(AluOp::Sra, 100, 2, false), 25);
}
#[test]
fn sra_rv64_negative_fills_with_ones() {
assert_eq!(alu(AluOp::Sra, NEG1, ONE, false), NEG1);
}
#[test]
fn sra_rv64_negative_shift_by_63() {
assert_eq!(alu(AluOp::Sra, I64_MIN, 63, false), NEG1);
}
#[test]
fn sra_rv64_positive_shift_by_63() {
assert_eq!(alu(AluOp::Sra, I64_MAX, 63, false), 0);
}
#[test]
fn sra_vs_srl_positive_equivalent() {
let val = 0x0000_DEAD_BEEF_0000_u64;
for shift in 0..64 {
assert_eq!(
alu(AluOp::Sra, val, shift, false),
alu(AluOp::Srl, val, shift, false),
"SRA != SRL for positive value at shift {shift}"
);
}
}
#[test]
fn sra_vs_srl_negative_diverge() {
assert_eq!(alu(AluOp::Sra, I64_MIN, 1, false), 0xC000_0000_0000_0000);
assert_eq!(alu(AluOp::Srl, I64_MIN, 1, false), 0x4000_0000_0000_0000);
}
#[test]
fn sra_rv64_shift_amount_masked() {
assert_eq!(alu(AluOp::Sra, I64_MIN, 64, false), I64_MIN);
}
#[test]
fn sra_rv64_neg2_shift_by_1() {
assert_eq!(alu(AluOp::Sra, -2i64 as u64, 1, false), NEG1);
}
#[test]
fn sra_rv64_progressive_shift_negative() {
for i in 0..64 {
let expected = (i64::MIN >> i) as u64;
assert_eq!(alu(AluOp::Sra, I64_MIN, i, false), expected, "SRA failed: i64::MIN >> {i}");
}
}
#[test]
fn sraw_shift_by_zero() {
assert_eq!(alu(AluOp::Sra, 0x8000_0000, ZERO, true), I32_MIN_SEXT);
}
#[test]
fn sraw_positive_shift() {
assert_eq!(alu(AluOp::Sra, 100, 2, true), 25);
}
#[test]
fn sraw_negative_fills_with_ones() {
assert_eq!(alu(AluOp::Sra, 0x8000_0000, 1, true), sext32(0xC000_0000));
}
#[test]
fn sraw_negative_shift_by_31() {
assert_eq!(alu(AluOp::Sra, 0x8000_0000, 31, true), NEG1);
}
#[test]
fn sraw_positive_shift_by_31() {
assert_eq!(alu(AluOp::Sra, 0x7FFF_FFFF, 31, true), 0);
}
#[test]
fn sraw_shift_amount_masked_to_5_bits() {
assert_eq!(alu(AluOp::Sra, 0x8000_0000, 32, true), I32_MIN_SEXT);
}
#[test]
fn sraw_ignores_upper_32_bits_of_operand() {
assert_eq!(alu(AluOp::Sra, 0x0000_0001_8000_0000, 1, true), sext32(0xC000_0000));
}
#[test]
fn sraw_all_ones_stays_all_ones() {
for shift in 0..32 {
assert_eq!(
alu(AluOp::Sra, 0xFFFF_FFFF, shift, true),
NEG1,
"SRAW -1 >> {shift} should remain -1"
);
}
}
#[test]
fn all_w_shift_results_are_sign_extended() {
let test_cases: Vec<(AluOp, u64, u64)> = vec![
(AluOp::Sll, 1, 0),
(AluOp::Sll, 1, 31), (AluOp::Sll, 0x4000_0000, 1), (AluOp::Sll, 0xFFFF_FFFF, 16), (AluOp::Srl, 0x8000_0000, 0), (AluOp::Srl, 0x8000_0000, 1), (AluOp::Srl, 0xFFFF_FFFF, 1), (AluOp::Srl, 0xFFFF_FFFF, 31), (AluOp::Sra, 0x8000_0000, 0), (AluOp::Sra, 0x8000_0000, 1), (AluOp::Sra, 0x8000_0000, 31), (AluOp::Sra, 0x7FFF_FFFF, 1), ];
for (op, a, b) in test_cases {
let result = alu(op, a, b, true);
let bit31 = (result >> 31) & 1;
let upper = result >> 32;
let expected_upper = if bit31 == 1 { 0xFFFF_FFFF } else { 0 };
assert_eq!(
upper, expected_upper,
"Op {:?} with a={:#x}, b={}: result {:#018x} not sign-extended from bit 31",
op, a, b, result
);
}
}
#[test]
fn sll_multiply_by_power_of_two() {
assert_eq!(alu(AluOp::Sll, 7, 3, false), 56); }
#[test]
fn srl_divide_by_power_of_two() {
assert_eq!(alu(AluOp::Srl, 56, 3, false), 7); }
#[test]
fn sra_signed_divide_rounds_toward_negative_infinity() {
assert_eq!(alu(AluOp::Sra, -7i64 as u64, 1, false), -4i64 as u64);
}
#[test]
fn srl_extract_byte() {
let val = 0x1234_5678_9ABC_DEF0_u64;
let byte3 = alu(AluOp::Srl, val, 24, false) & 0xFF;
assert_eq!(byte3, 0x9A);
}