use crate::isa::instruction::decode;
use crate::isa::reg::RegIdx;
use crate::isa::rvc::expand;
use crate::isa::encoding::privileged as sys_op;
use crate::isa::encoding::rv64f::opcodes as f_op;
use crate::isa::encoding::rv64i::funct3 as i_f3;
use crate::isa::encoding::rv64i::funct7 as i_f7;
use crate::isa::encoding::rv64i::opcodes as i_op;
fn expand_and_decode(cinst: u16) -> crate::isa::instruction::Decoded {
let expanded = expand(cinst);
assert_ne!(expanded, 0, "Expansion must not produce illegal instruction 0 for {cinst:#06x}");
decode(expanded)
}
#[test]
fn rvc_c_addi4spn() {
let cinst: u16 = 0b0000_1000_0000_0000; let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_IMM);
assert_eq!(d.rs1, RegIdx::new(2), "C.ADDI4SPN base must be x2 (sp)");
assert_eq!(d.rd, RegIdx::new(8), "rd' = 0 maps to x8");
assert_eq!(d.imm, 16);
}
#[test]
fn rvc_c_addi4spn_zero_is_illegal() {
let cinst: u16 = 0b0000_0000_0000_0000;
let expanded = expand(cinst);
assert_eq!(expanded, 0, "C.ADDI4SPN with nzuimm=0 must expand to illegal");
}
#[test]
fn rvc_c_lw() {
let cinst: u16 = 0b0100_0000_0000_0100; let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_LOAD);
assert_eq!(d.funct3, i_f3::LW);
assert_eq!(d.rs1, RegIdx::new(8));
assert_eq!(d.rd, RegIdx::new(9));
}
#[test]
fn rvc_c_ld() {
let cinst: u16 = 0b0110_0000_0010_0000;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_LOAD);
assert_eq!(d.funct3, i_f3::LD);
}
#[test]
fn rvc_c_fld() {
let cinst: u16 = 0b0010_0000_0010_0000;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, f_op::OP_LOAD_FP);
assert_eq!(d.funct3, i_f3::LD);
}
#[test]
fn rvc_c_sw() {
let cinst: u16 = 0b1100_0000_0010_0000;
let expanded = expand(cinst);
assert_ne!(expanded, 0);
let d = decode(expanded);
assert_eq!(d.opcode, i_op::OP_STORE);
assert_eq!(d.funct3, i_f3::SW);
}
#[test]
fn rvc_c_sd() {
let cinst: u16 = 0b1110_0000_0010_0000;
let expanded = expand(cinst);
assert_ne!(expanded, 0);
let d = decode(expanded);
assert_eq!(d.opcode, i_op::OP_STORE);
assert_eq!(d.funct3, i_f3::SD);
}
#[test]
fn rvc_c_fsd() {
let cinst: u16 = 0b1010_0000_0010_0000;
let expanded = expand(cinst);
assert_ne!(expanded, 0);
let d = decode(expanded);
assert_eq!(d.opcode, f_op::OP_STORE_FP);
}
#[test]
fn rvc_c_addi() {
let cinst: u16 = 0b0000_0000_1000_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_IMM);
assert_eq!(d.funct3, i_f3::ADD_SUB);
assert_eq!(d.rd, RegIdx::new(1));
assert_eq!(d.rs1, RegIdx::new(1));
assert_eq!(d.imm, 1);
}
#[test]
fn rvc_c_addi_negative() {
let cinst: u16 = 0b0001_0000_1111_1101;
let d = expand_and_decode(cinst);
assert_eq!(d.imm, -1);
}
#[test]
fn rvc_c_addiw() {
let cinst: u16 = 0b0010_0010_1000_1101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_IMM_32);
assert_eq!(d.rd, RegIdx::new(5));
assert_eq!(d.rs1, RegIdx::new(5));
}
#[test]
fn rvc_c_addiw_rd0_illegal() {
let cinst: u16 = 0b0010_0000_0000_1101;
let expanded = expand(cinst);
assert_eq!(expanded, 0);
}
#[test]
fn rvc_c_li() {
let cinst: u16 = 0b0100_0001_1001_1101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_IMM);
assert_eq!(d.rd, RegIdx::new(3));
assert_eq!(d.rs1, RegIdx::new(0), "C.LI uses x0 as source");
assert_eq!(d.imm, 7);
}
#[test]
fn rvc_c_addi16sp() {
let cinst: u16 = 0b0110_0001_0100_0001; let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_IMM);
assert_eq!(d.rd, RegIdx::new(2));
assert_eq!(d.rs1, RegIdx::new(2));
assert_eq!(d.imm, 16);
}
#[test]
fn rvc_c_lui() {
let cinst: u16 = 0b0110_0001_1000_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_LUI);
assert_eq!(d.rd, RegIdx::new(3));
}
#[test]
fn rvc_c_srli() {
let cinst: u16 = 0b1000_0000_0000_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_IMM);
assert_eq!(d.funct3, i_f3::SRL_SRA);
assert_eq!(d.rd, RegIdx::new(8));
}
#[test]
fn rvc_c_srai() {
let cinst: u16 = 0b1000_0100_0000_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_IMM);
assert_eq!(d.funct3, i_f3::SRL_SRA);
assert_eq!(d.funct7, i_f7::SRA);
}
#[test]
fn rvc_c_andi() {
let cinst: u16 = 0b1000_1000_0000_1101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_IMM);
assert_eq!(d.funct3, i_f3::AND);
assert_eq!(d.rd, RegIdx::new(8));
}
#[test]
fn rvc_c_sub() {
let cinst: u16 = 0b1000_1100_0000_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_REG);
assert_eq!(d.funct3, i_f3::ADD_SUB);
assert_eq!(d.funct7, i_f7::SUB);
assert_eq!(d.rd, RegIdx::new(8));
assert_eq!(d.rs1, RegIdx::new(8));
assert_eq!(d.rs2, RegIdx::new(9));
}
#[test]
fn rvc_c_xor() {
let cinst: u16 = 0b1000_1100_0010_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_REG);
assert_eq!(d.funct3, i_f3::XOR);
}
#[test]
fn rvc_c_or() {
let cinst: u16 = 0b1000_1100_0100_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_REG);
assert_eq!(d.funct3, i_f3::OR);
}
#[test]
fn rvc_c_and() {
let cinst: u16 = 0b1000_1100_0110_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_REG);
assert_eq!(d.funct3, i_f3::AND);
}
#[test]
fn rvc_c_subw() {
let cinst: u16 = 0b1001_1100_0000_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_REG_32);
assert_eq!(d.funct3, i_f3::ADD_SUB);
assert_eq!(d.funct7, i_f7::SUB);
}
#[test]
fn rvc_c_addw() {
let cinst: u16 = 0b1001_1100_0010_0101;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_REG_32);
assert_eq!(d.funct3, i_f3::ADD_SUB);
assert_eq!(d.funct7, i_f7::DEFAULT, "ADDW uses funct7=0");
}
#[test]
fn rvc_c_j() {
let cinst: u16 = 0xA009; let expanded = expand(cinst);
assert_ne!(expanded, 0);
let d = decode(expanded);
assert_eq!(d.opcode, i_op::OP_JAL);
assert_eq!(d.rd, RegIdx::new(0), "C.J links to x0");
}
#[test]
fn rvc_c_beqz() {
let cinst: u16 = 0b1100_0000_0000_0101; let expanded = expand(cinst);
assert_ne!(expanded, 0);
let d = decode(expanded);
assert_eq!(d.opcode, i_op::OP_BRANCH);
assert_eq!(d.funct3, i_f3::BEQ);
assert_eq!(d.rs1, RegIdx::new(8));
assert_eq!(d.rs2, RegIdx::new(0));
}
#[test]
fn rvc_c_bnez() {
let cinst: u16 = 0b1110_0000_0000_0101;
let expanded = expand(cinst);
assert_ne!(expanded, 0);
let d = decode(expanded);
assert_eq!(d.opcode, i_op::OP_BRANCH);
assert_eq!(d.funct3, i_f3::BNE);
assert_eq!(d.rs1, RegIdx::new(8));
}
#[test]
fn rvc_c_slli() {
let cinst: u16 = 0b0000_0000_1001_0010;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_IMM);
assert_eq!(d.funct3, i_f3::SLL);
assert_eq!(d.rd, RegIdx::new(1));
assert_eq!(d.rs1, RegIdx::new(1));
}
#[test]
fn rvc_c_slli_rd0_illegal() {
let cinst: u16 = 0b0000_0000_0001_0010;
let expanded = expand(cinst);
assert_eq!(expanded, 0);
}
#[test]
fn rvc_c_lwsp() {
let cinst: u16 = 0b0100_0000_1000_0010; let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_LOAD);
assert_eq!(d.funct3, i_f3::LW);
assert_eq!(d.rd, RegIdx::new(1));
assert_eq!(d.rs1, RegIdx::new(2), "C.LWSP base is x2 (sp)");
}
#[test]
fn rvc_c_lwsp_rd0_illegal() {
let cinst: u16 = 0b0100_0000_0000_0010;
let expanded = expand(cinst);
assert_eq!(expanded, 0, "C.LWSP with rd=0 is reserved");
}
#[test]
fn rvc_c_ldsp() {
let cinst: u16 = 0b0110_0000_1000_0010;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_LOAD);
assert_eq!(d.funct3, i_f3::LD);
assert_eq!(d.rs1, RegIdx::new(2));
}
#[test]
fn rvc_c_ldsp_rd0_illegal() {
let cinst: u16 = 0b0110_0000_0000_0010;
let expanded = expand(cinst);
assert_eq!(expanded, 0, "C.LDSP with rd=0 is reserved");
}
#[test]
fn rvc_c_fldsp() {
let cinst: u16 = 0b0010_0000_1000_0010;
let d = expand_and_decode(cinst);
assert_eq!(d.opcode, f_op::OP_LOAD_FP);
assert_eq!(d.funct3, i_f3::LD);
assert_eq!(d.rs1, RegIdx::new(2));
}
#[test]
fn rvc_c_jr() {
let cinst: u16 = 0b1000_0010_1000_0010; let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_JALR);
assert_eq!(d.rd, RegIdx::new(0), "C.JR links to x0");
assert_eq!(d.rs1, RegIdx::new(5));
}
#[test]
fn rvc_c_mv() {
let cinst: u16 = 0b1000_0001_1001_0110; let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_REG);
assert_eq!(d.funct3, i_f3::ADD_SUB);
assert_eq!(d.rd, RegIdx::new(3));
assert_eq!(d.rs1, RegIdx::new(0), "C.MV uses x0 as rs1");
assert_eq!(d.rs2, RegIdx::new(5));
}
#[test]
fn rvc_c_ebreak() {
let cinst: u16 = 0b1001_0000_0000_0010;
let expanded = expand(cinst);
assert_eq!(expanded, sys_op::EBREAK);
}
#[test]
fn rvc_c_jalr() {
let cinst: u16 = 0b1001_0010_1000_0010; let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_JALR);
assert_eq!(d.rd, RegIdx::new(1), "C.JALR links to x1 (ra)");
assert_eq!(d.rs1, RegIdx::new(5));
}
#[test]
fn rvc_c_add() {
let cinst: u16 = 0b1001_0001_1001_0110; let d = expand_and_decode(cinst);
assert_eq!(d.opcode, i_op::OP_REG);
assert_eq!(d.funct3, i_f3::ADD_SUB);
assert_eq!(d.funct7, i_f7::DEFAULT);
assert_eq!(d.rd, RegIdx::new(3));
assert_eq!(d.rs1, RegIdx::new(3), "C.ADD uses rd as rs1");
assert_eq!(d.rs2, RegIdx::new(5));
}
#[test]
fn rvc_c_swsp() {
let cinst: u16 = 0b1100_0000_0000_1110; let expanded = expand(cinst);
assert_ne!(expanded, 0);
let d = decode(expanded);
assert_eq!(d.opcode, i_op::OP_STORE);
assert_eq!(d.funct3, i_f3::SW);
assert_eq!(d.rs1, RegIdx::new(2), "C.SWSP base is x2 (sp)");
assert_eq!(d.rs2, RegIdx::new(3));
}
#[test]
fn rvc_c_sdsp() {
let cinst: u16 = 0b1110_0000_0000_1110; let expanded = expand(cinst);
assert_ne!(expanded, 0);
let d = decode(expanded);
assert_eq!(d.opcode, i_op::OP_STORE);
assert_eq!(d.funct3, i_f3::SD);
assert_eq!(d.rs1, RegIdx::new(2));
}
#[test]
fn rvc_c_fsdsp() {
let cinst: u16 = 0b1010_0000_0000_1110; let expanded = expand(cinst);
assert_ne!(expanded, 0);
let d = decode(expanded);
assert_eq!(d.opcode, f_op::OP_STORE_FP);
assert_eq!(d.rs1, RegIdx::new(2));
}
#[test]
fn rvc_quadrant_3_is_not_compressed() {
let cinst: u16 = 0x0003; let expanded = expand(cinst);
assert_eq!(expanded, 0, "Quadrant 3 (32-bit) should not be handled by RVC expander");
}
#[test]
fn rvc_all_register_mappings_q0() {
for rd_prime in 0u16..8 {
let cinst: u16 = 0b0100_0000_0000_0000 | (rd_prime << 2);
let d = expand_and_decode(cinst);
assert_eq!(
d.rd,
RegIdx::new((8 + rd_prime) as u8),
"rd'={rd_prime} should map to x{}",
8 + rd_prime
);
}
}
#[test]
fn rvc_all_register_mappings_q0_rs1() {
for rs1_prime in 0u16..8 {
let cinst: u16 = 0b0100_0000_0000_0000 | (rs1_prime << 7);
let d = expand_and_decode(cinst);
assert_eq!(
d.rs1,
RegIdx::new((8 + rs1_prime) as u8),
"rs1'={rs1_prime} should map to x{}",
8 + rs1_prime
);
}
}