use super::riscv_instr_info::RiscVOpcode;
use super::riscv_register_info::{FPR_BASE, GPR_BASE};
use crate::codegen::{MachineInstr, MachineOperand};
const OP_LUI: u8 = 0b0110111;
const OP_AUIPC: u8 = 0b0010111;
const OP_JAL: u8 = 0b1101111;
const OP_JALR: u8 = 0b1100111;
const OP_BRANCH: u8 = 0b1100011;
const OP_LOAD: u8 = 0b0000011;
const OP_STORE: u8 = 0b0100011;
const OP_ALUI: u8 = 0b0010011;
const OP_ALU: u8 = 0b0110011;
const OP_FENCE: u8 = 0b0001111;
const OP_SYSTEM: u8 = 0b1110011;
const OP_ALUIW: u8 = 0b0011011;
const OP_ALUW: u8 = 0b0111011;
const F3_BEQ: u8 = 0b000;
const F3_BNE: u8 = 0b001;
const F3_BLT: u8 = 0b100;
const F3_BGE: u8 = 0b101;
const F3_BLTU: u8 = 0b110;
const F3_BGEU: u8 = 0b111;
const F3_LB: u8 = 0b000;
const F3_LH: u8 = 0b001;
const F3_LW: u8 = 0b010;
const F3_LBU: u8 = 0b100;
const F3_LHU: u8 = 0b101;
const F3_LD: u8 = 0b011;
const F3_LWU: u8 = 0b110;
const F3_SB: u8 = 0b000;
const F3_SH: u8 = 0b001;
const F3_SW: u8 = 0b010;
const F3_SD: u8 = 0b011;
const F3_ADDI: u8 = 0b000;
const F3_SLTI: u8 = 0b010;
const F3_SLTIU: u8 = 0b011;
const F3_XORI: u8 = 0b100;
const F3_ORI: u8 = 0b110;
const F3_ANDI: u8 = 0b111;
const F3_SLLI: u8 = 0b001;
const F3_SRLI_SRAI: u8 = 0b101;
const F3_ADD_SUB: u8 = 0b000;
const F3_SLL: u8 = 0b001;
const F3_SLT: u8 = 0b010;
const F3_SLTU: u8 = 0b011;
const F3_XOR: u8 = 0b100;
const F3_SRL_SRA: u8 = 0b101;
const F3_OR: u8 = 0b110;
const F3_AND: u8 = 0b111;
const F3_MUL: u8 = 0b000;
const F3_MULH: u8 = 0b001;
const F3_MULHSU: u8 = 0b010;
const F3_MULHU: u8 = 0b011;
const F3_DIV: u8 = 0b100;
const F3_DIVU: u8 = 0b101;
const F3_REM: u8 = 0b110;
const F3_REMU: u8 = 0b111;
const F7_BASE: u8 = 0b0000000;
const F7_ALT: u8 = 0b0100000;
const F7_MULDIV: u8 = 0b0000001;
const F3_PRIV: u8 = 0b000;
const F3_CSRRW: u8 = 0b001;
const F3_CSRRS: u8 = 0b010;
const F3_CSRRC: u8 = 0b011;
const F3_CSRRWI: u8 = 0b101;
const F3_CSRRSI: u8 = 0b110;
const F3_CSRRCI: u8 = 0b111;
const F3_FENCE: u8 = 0b000;
const F3_FENCE_I: u8 = 0b001;
pub struct RiscVMCDecoder {
pub is_64bit: bool,
}
impl RiscVMCDecoder {
pub fn new(is_64bit: bool) -> Self {
Self { is_64bit }
}
pub fn decode_instruction(&self, bytes: &[u8], offset: usize) -> Option<(MachineInstr, usize)> {
if offset + 4 > bytes.len() {
return None;
}
let insn = u32::from_le_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
]);
if (insn & 0x3) != 0x3 {
return None;
}
self.decode_by_opcode(insn).map(|mi| (mi, 4))
}
pub fn decode_by_opcode(&self, insn: u32) -> Option<MachineInstr> {
let opcode = extract_opcode(insn);
match opcode {
OP_LUI => self.decode_lui(insn),
OP_AUIPC => self.decode_auipc(insn),
OP_JAL => self.decode_jal(insn),
OP_JALR => self.decode_jalr(insn),
OP_BRANCH => self.decode_branch(insn),
OP_LOAD => self.decode_load(insn),
OP_STORE => self.decode_store(insn),
OP_ALUI => self.decode_i_type(insn),
OP_ALU => self.decode_r_type(insn),
OP_FENCE => self.decode_fence(insn),
OP_SYSTEM => self.decode_csr(insn),
OP_ALUIW => self.decode_alu_iw(insn),
OP_ALUW => self.decode_alu_rw(insn),
_ => None,
}
}
fn decode_r_type(&self, insn: u32) -> Option<MachineInstr> {
let funct3 = extract_funct3(insn);
let funct7 = extract_funct7(insn);
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let rs2 = extract_rs2(insn);
if funct7 == F7_MULDIV {
let op = match funct3 {
F3_MUL => RiscVOpcode::MUL,
F3_MULH => RiscVOpcode::MULH,
F3_MULHSU => RiscVOpcode::MULHSU,
F3_MULHU => RiscVOpcode::MULHU,
F3_DIV => RiscVOpcode::DIV,
F3_DIVU => RiscVOpcode::DIVU,
F3_REM => RiscVOpcode::REM,
F3_REMU => RiscVOpcode::REMU,
_ => return None,
};
let mut mi = MachineInstr::new(op as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_reg(reg_field_to_id(rs2, false));
return Some(mi);
}
let op = match (funct3, funct7) {
(F3_ADD_SUB, F7_BASE) => RiscVOpcode::ADD,
(F3_ADD_SUB, F7_ALT) => RiscVOpcode::SUB,
(F3_SLL, F7_BASE) => RiscVOpcode::SLL,
(F3_SLT, F7_BASE) => RiscVOpcode::SLT,
(F3_SLTU, F7_BASE) => RiscVOpcode::SLTU,
(F3_XOR, F7_BASE) => RiscVOpcode::XOR,
(F3_SRL_SRA, F7_BASE) => RiscVOpcode::SRL,
(F3_SRL_SRA, F7_ALT) => RiscVOpcode::SRA,
(F3_OR, F7_BASE) => RiscVOpcode::OR,
(F3_AND, F7_BASE) => RiscVOpcode::AND,
_ => return None,
};
let mut mi = MachineInstr::new(op as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_reg(reg_field_to_id(rs2, false));
Some(mi)
}
fn decode_i_type(&self, insn: u32) -> Option<MachineInstr> {
let funct3 = extract_funct3(insn);
let funct7 = extract_funct7(insn);
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let imm = extract_i_imm(insn);
let shamt = extract_shamt(insn);
if funct3 == F3_SLLI && funct7 == F7_BASE {
let mut mi = MachineInstr::new(RiscVOpcode::SLLI as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(shamt as i64);
return Some(mi);
}
if funct3 == F3_SRLI_SRAI {
if funct7 == F7_BASE {
let mut mi = MachineInstr::new(RiscVOpcode::SRLI as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(shamt as i64);
return Some(mi);
}
if funct7 == F7_ALT {
let mut mi = MachineInstr::new(RiscVOpcode::SRAI as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(shamt as i64);
return Some(mi);
}
}
let op = match funct3 {
F3_ADDI => RiscVOpcode::ADDI,
F3_SLTI => RiscVOpcode::SLTI,
F3_SLTIU => RiscVOpcode::SLTIU,
F3_XORI => RiscVOpcode::XORI,
F3_ORI => RiscVOpcode::ORI,
F3_ANDI => RiscVOpcode::ANDI,
_ => return None,
};
let mut mi = MachineInstr::new(op as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(imm as i64);
Some(mi)
}
fn decode_load(&self, insn: u32) -> Option<MachineInstr> {
let funct3 = extract_funct3(insn);
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let imm = extract_i_imm(insn);
let op = match funct3 {
F3_LB => RiscVOpcode::LB,
F3_LH => RiscVOpcode::LH,
F3_LW => RiscVOpcode::LW,
F3_LBU => RiscVOpcode::LBU,
F3_LHU => RiscVOpcode::LHU,
F3_LD => RiscVOpcode::LD,
F3_LWU => RiscVOpcode::LWU,
_ => return None,
};
let mut mi = MachineInstr::new(op as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(imm as i64);
Some(mi)
}
fn decode_store(&self, insn: u32) -> Option<MachineInstr> {
let funct3 = extract_funct3(insn);
let rs1 = extract_rs1(insn);
let rs2 = extract_rs2(insn);
let imm = extract_s_imm(insn);
let op = match funct3 {
F3_SB => RiscVOpcode::SB,
F3_SH => RiscVOpcode::SH,
F3_SW => RiscVOpcode::SW,
F3_SD => RiscVOpcode::SD,
_ => return None,
};
let mut mi = MachineInstr::new(op as u32);
mi.push_reg(reg_field_to_id(rs2, false)); mi.push_reg(reg_field_to_id(rs1, false)); mi.push_imm(imm as i64);
Some(mi)
}
fn decode_branch(&self, insn: u32) -> Option<MachineInstr> {
let funct3 = extract_funct3(insn);
let rs1 = extract_rs1(insn);
let rs2 = extract_rs2(insn);
let imm = extract_b_imm(insn);
let op = match funct3 {
F3_BEQ => RiscVOpcode::BEQ,
F3_BNE => RiscVOpcode::BNE,
F3_BLT => RiscVOpcode::BLT,
F3_BGE => RiscVOpcode::BGE,
F3_BLTU => RiscVOpcode::BLTU,
F3_BGEU => RiscVOpcode::BGEU,
_ => return None,
};
let mut mi = MachineInstr::new(op as u32);
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_reg(reg_field_to_id(rs2, false));
mi.push_imm(imm as i64);
Some(mi)
}
fn decode_jal(&self, insn: u32) -> Option<MachineInstr> {
let rd = extract_rd(insn);
let imm = extract_j_imm(insn);
let mut mi = MachineInstr::new(RiscVOpcode::JAL as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_imm(imm as i64);
Some(mi)
}
fn decode_jalr(&self, insn: u32) -> Option<MachineInstr> {
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let imm = extract_i_imm(insn);
let mut mi = MachineInstr::new(RiscVOpcode::JALR as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(imm as i64);
Some(mi)
}
fn decode_lui(&self, insn: u32) -> Option<MachineInstr> {
let rd = extract_rd(insn);
let imm = extract_u_imm(insn);
let mut mi = MachineInstr::new(RiscVOpcode::LUI as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_imm(imm as i64);
Some(mi)
}
fn decode_auipc(&self, insn: u32) -> Option<MachineInstr> {
let rd = extract_rd(insn);
let imm = extract_u_imm(insn);
let mut mi = MachineInstr::new(RiscVOpcode::AUIPC as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_imm(imm as i64);
Some(mi)
}
fn decode_fence(&self, insn: u32) -> Option<MachineInstr> {
let funct3 = extract_funct3(insn);
let imm = extract_i_imm(insn);
match funct3 {
F3_FENCE => {
let pred = ((imm >> 4) & 0xF) as i64;
let succ = (imm & 0xF) as i64;
let mut mi = MachineInstr::new(RiscVOpcode::FENCE as u32);
mi.push_imm(pred);
mi.push_imm(succ);
Some(mi)
}
F3_FENCE_I => Some(MachineInstr::new(RiscVOpcode::FENCE_I as u32)),
_ => None,
}
}
fn decode_csr(&self, insn: u32) -> Option<MachineInstr> {
let funct3 = extract_funct3(insn);
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let imm = extract_i_imm(insn) as u32;
if funct3 == F3_PRIV {
return if imm == 0 {
Some(MachineInstr::new(RiscVOpcode::ECALL as u32))
} else if imm == 1 {
Some(MachineInstr::new(RiscVOpcode::EBREAK as u32))
} else {
None
};
}
let csr = imm & 0xFFF;
let op = match funct3 {
F3_CSRRW => RiscVOpcode::CSRRW,
F3_CSRRS => RiscVOpcode::CSRRS,
F3_CSRRC => RiscVOpcode::CSRRC,
F3_CSRRWI => RiscVOpcode::CSRRWI,
F3_CSRRSI => RiscVOpcode::CSRRSI,
F3_CSRRCI => RiscVOpcode::CSRRCI,
_ => return None,
};
let mut mi = MachineInstr::new(op as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(csr as i64);
Some(mi)
}
fn decode_alu_iw(&self, insn: u32) -> Option<MachineInstr> {
let funct3 = extract_funct3(insn);
let funct7 = extract_funct7(insn);
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let imm = extract_i_imm(insn);
let shamt = extract_shamt(insn);
if funct3 == F3_ADDI && funct7 == F7_BASE {
let mut mi = MachineInstr::new(RiscVOpcode::ADDIW as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(imm as i64);
return Some(mi);
}
if funct3 == F3_SLLI && funct7 == F7_BASE {
let mut mi = MachineInstr::new(RiscVOpcode::SLLIW as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(shamt as i64);
return Some(mi);
}
if funct3 == F3_SRLI_SRAI {
if funct7 == F7_BASE {
let mut mi = MachineInstr::new(RiscVOpcode::SRLIW as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(shamt as i64);
return Some(mi);
}
if funct7 == F7_ALT {
let mut mi = MachineInstr::new(RiscVOpcode::SRAIW as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_imm(shamt as i64);
return Some(mi);
}
}
None
}
fn decode_alu_rw(&self, insn: u32) -> Option<MachineInstr> {
let funct3 = extract_funct3(insn);
let funct7 = extract_funct7(insn);
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let rs2 = extract_rs2(insn);
let op = match (funct3, funct7) {
(F3_ADD_SUB, F7_BASE) => RiscVOpcode::ADDW,
(F3_ADD_SUB, F7_ALT) => RiscVOpcode::SUBW,
(F3_SLL, F7_BASE) => RiscVOpcode::SLLW,
(F3_SRL_SRA, F7_BASE) => RiscVOpcode::SRLW,
(F3_SRL_SRA, F7_ALT) => RiscVOpcode::SRAW,
_ => {
if funct7 == F7_MULDIV {
match funct3 {
F3_MUL => RiscVOpcode::MULW,
F3_DIV => RiscVOpcode::DIVW,
F3_DIVU => RiscVOpcode::DIVUW,
F3_REM => RiscVOpcode::REMW,
F3_REMU => RiscVOpcode::REMUW,
_ => return None,
}
} else {
return None;
}
}
};
let mut mi = MachineInstr::new(op as u32);
mi.push_reg(reg_field_to_id(rd, false));
mi.push_reg(reg_field_to_id(rs1, false));
mi.push_reg(reg_field_to_id(rs2, false));
Some(mi)
}
}
pub fn extract_rd(insn: u32) -> u8 {
((insn >> 7) & 0x1F) as u8
}
pub fn extract_rs1(insn: u32) -> u8 {
((insn >> 15) & 0x1F) as u8
}
pub fn extract_rs2(insn: u32) -> u8 {
((insn >> 20) & 0x1F) as u8
}
pub fn extract_funct3(insn: u32) -> u8 {
((insn >> 12) & 0x7) as u8
}
pub fn extract_funct7(insn: u32) -> u8 {
((insn >> 25) & 0x7F) as u8
}
pub fn extract_opcode(insn: u32) -> u8 {
(insn & 0x7F) as u8
}
pub fn extract_i_imm(insn: u32) -> i32 {
sext((insn >> 20) & 0xFFF, 12)
}
pub fn extract_shamt(insn: u32) -> u8 {
((insn >> 20) & 0x1F) as u8
}
pub fn extract_s_imm(insn: u32) -> i32 {
let imm_4_0 = (insn >> 7) & 0x1F;
let imm_11_5 = (insn >> 25) & 0x7F;
sext((imm_11_5 << 5) | imm_4_0, 12)
}
pub fn extract_b_imm(insn: u32) -> i32 {
let imm_12 = (insn >> 31) & 0x1;
let imm_11 = (insn >> 7) & 0x1;
let imm_10_5 = (insn >> 25) & 0x3F;
let imm_4_1 = (insn >> 8) & 0xF;
let imm = (imm_12 << 12) | (imm_11 << 11) | (imm_10_5 << 5) | (imm_4_1 << 1);
sext(imm, 13)
}
pub fn extract_u_imm(insn: u32) -> i32 {
(insn & 0xFFFFF000) as i32
}
pub fn extract_j_imm(insn: u32) -> i32 {
let imm_20 = (insn >> 31) & 0x1;
let imm_19_12 = (insn >> 12) & 0xFF;
let imm_11 = (insn >> 20) & 0x1;
let imm_10_1 = (insn >> 21) & 0x3FF;
let imm = (imm_20 << 20) | (imm_19_12 << 12) | (imm_11 << 11) | (imm_10_1 << 1);
sext(imm, 21)
}
fn sext(val: u32, n: u32) -> i32 {
let shift = 32 - n;
((val << shift) as i32) >> shift
}
pub fn reg_field_to_id(field: u8, is_fp: bool) -> u32 {
if is_fp {
(FPR_BASE + field as u16) as u32
} else {
(GPR_BASE + field as u16) as u32
}
}
pub fn instruction_length(first_halfword: u16) -> usize {
match first_halfword & 0x3 {
0b00 | 0b01 | 0b10 => 2,
_ => 4,
}
}
pub fn is_compressed(halfword: u16) -> bool {
(halfword & 0x3) != 0x3
}
pub fn decode_c0(insn: u16) -> Option<(u32, Vec<u32>, Vec<i64>)> {
let funct3 = ((insn >> 13) & 0x7) as u32;
let rd_prime = ((insn >> 2) & 0x7) as u32;
let rs2_prime = ((insn >> 2) & 0x7) as u32;
match funct3 {
0b000 => {
let nzuimm = (((insn >> 7) & 0x30) as u32) | (((insn >> 11) & 0xC0) as u32 >> 6) | (((insn >> 5) & 0x4) as u32) | (((insn >> 6) & 0x2) as u32); return None; }
0b010 => {
let rs1_prime = ((insn >> 7) & 0x7) as u32;
let uimm = (((insn >> 5) & 0x1) as u32) | (((insn >> 10) & 0x7) as u32) << 3 | (((insn >> 6) & 0x1) as u32) << 2; return Some((
RiscVOpcode::C_LW as u32,
vec![rd_prime + 8, rs1_prime + 8],
vec![uimm as i64],
));
}
0b110 => {
let rs1_prime = ((insn >> 7) & 0x7) as u32;
let uimm = (((insn >> 5) & 0x1) as u32) | (((insn >> 10) & 0x7) as u32) << 3 | (((insn >> 6) & 0x1) as u32) << 2; return Some((
RiscVOpcode::C_SW as u32,
vec![rs2_prime + 8, rs1_prime + 8],
vec![uimm as i64],
));
}
_ => return None,
}
}
pub fn decode_c1(insn: u16) -> Option<(u32, Vec<u32>, Vec<i64>)> {
let funct3 = ((insn >> 13) & 0x7) as u32;
match funct3 {
0b000 => {
let rd = ((insn >> 7) & 0x1F) as u32;
let imm = extract_c_imm_6(insn);
if rd == 0 {
return Some((RiscVOpcode::C_NOP as u32, vec![], vec![]));
}
return Some((RiscVOpcode::C_ADDI as u32, vec![rd], vec![imm as i64]));
}
0b001 | 0b101 => {
let offset = extract_c_j_imm(insn);
if funct3 == 0b001 {
return Some((
RiscVOpcode::C_JAL as u32,
vec![1 ],
vec![offset as i64],
));
} else {
return Some((RiscVOpcode::C_J as u32, vec![], vec![offset as i64]));
}
}
0b010 => {
let rd = ((insn >> 7) & 0x1F) as u32;
let imm = extract_c_imm_6(insn);
return Some((RiscVOpcode::C_LI as u32, vec![rd], vec![imm as i64]));
}
0b011 => {
let rd = ((insn >> 7) & 0x1F) as u32;
if rd == 2 {
let imm = extract_c_addi16sp_imm(insn);
return Some((
RiscVOpcode::C_ADDI16SP as u32,
vec![2 ],
vec![imm as i64],
));
} else {
let imm = extract_c_lui_imm(insn);
return Some((RiscVOpcode::C_LUI as u32, vec![rd], vec![imm as i64]));
}
}
0b100 => {
let funct2_5_4 = ((insn >> 10) & 0x3) as u32;
let funct1_12 = ((insn >> 12) & 0x1) as u32;
let rd_prime = ((insn >> 7) & 0x7) as u32;
match (funct1_12, funct2_5_4) {
(0, 0) => {
let shamt = (((insn >> 2) & 0x1F) | ((insn >> 7) & 0x20)) as u32;
return Some((
RiscVOpcode::C_SRLI as u32,
vec![rd_prime + 8],
vec![shamt as i64],
));
}
(0, 1) => {
let shamt = (((insn >> 2) & 0x1F) | ((insn >> 7) & 0x20)) as u32;
return Some((
RiscVOpcode::C_SRAI as u32,
vec![rd_prime + 8],
vec![shamt as i64],
));
}
(0, 2) => {
let imm = extract_c_imm_6(insn);
return Some((
RiscVOpcode::C_ANDI as u32,
vec![rd_prime + 8],
vec![imm as i64],
));
}
(0, 3) => {
let funct2_6_5 = ((insn >> 5) & 0x3) as u32;
let rs2_prime = ((insn >> 2) & 0x7) as u32;
let op = match funct2_6_5 {
0b00 => RiscVOpcode::C_SUB,
0b01 => RiscVOpcode::C_XOR,
0b10 => RiscVOpcode::C_OR,
0b11 => RiscVOpcode::C_AND,
_ => return None,
};
return Some((op as u32, vec![rd_prime + 8, rs2_prime + 8], vec![]));
}
_ => return None,
}
}
0b110 | 0b111 => {
let rs1_prime = ((insn >> 7) & 0x7) as u32;
let offset = extract_c_b_imm(insn);
let op = if funct3 == 0b110 {
RiscVOpcode::C_BEQZ
} else {
RiscVOpcode::C_BNEZ
};
return Some((op as u32, vec![rs1_prime + 8], vec![offset as i64]));
}
_ => return None,
}
}
pub fn decode_c2(insn: u16) -> Option<(u32, Vec<u32>, Vec<i64>)> {
let funct3 = ((insn >> 13) & 0x7) as u32;
match funct3 {
0b000 => {
let rd = ((insn >> 7) & 0x1F) as u32;
let shamt = (((insn >> 2) & 0x1F) | ((insn >> 7) & 0x20)) as u32;
if shamt == 0 {
return None;
}
return Some((RiscVOpcode::C_SLLI as u32, vec![rd], vec![shamt as i64]));
}
0b010 => {
let rd = ((insn >> 7) & 0x1F) as u32;
let uimm = (((insn >> 2) & 0x7) as u32) | (((insn >> 7) & 0x18) as u32) << 2 | (((insn >> 12) & 0x1) as u32) << 5; if rd == 0 {
return None;
}
return Some((RiscVOpcode::C_LWSP as u32, vec![rd], vec![uimm as i64]));
}
0b100 => {
let funct1 = (insn >> 12) & 0x1;
let rs1 = ((insn >> 7) & 0x1F) as u32;
let rs2 = ((insn >> 2) & 0x1F) as u32;
if funct1 == 0 {
if rs2 == 0 {
return Some((RiscVOpcode::C_JR as u32, vec![rs1], vec![]));
} else if rs1 != 0 && rs2 != 0 {
return Some((RiscVOpcode::C_MV as u32, vec![rs1, rs2], vec![]));
}
} else {
if rs1 == 0 && rs2 == 0 {
return Some((RiscVOpcode::C_EBREAK as u32, vec![], vec![]));
} else if rs2 == 0 {
return Some((RiscVOpcode::C_JALR as u32, vec![rs1], vec![]));
} else if rs1 != 0 && rs2 != 0 {
return Some((RiscVOpcode::C_ADD as u32, vec![rs1, rs2], vec![]));
}
}
return None;
}
0b110 => {
let rs2 = ((insn >> 2) & 0x1F) as u32;
let uimm = (((insn >> 7) & 0x3C) as u32) << 2 | (((insn >> 9) & 0x3C) as u32); return Some((RiscVOpcode::C_SWSP as u32, vec![rs2], vec![uimm as i64]));
}
_ => return None,
}
}
pub fn decode_compressed(halfword: u16) -> Option<(u32, Vec<u32>, Vec<i64>)> {
match halfword & 0x3 {
0b00 => decode_c0(halfword),
0b01 => decode_c1(halfword),
0b10 => decode_c2(halfword),
_ => None,
}
}
pub fn extract_c_imm_6(insn: u16) -> i32 {
let imm_5 = (insn >> 12) & 0x1;
let imm_4_0 = (insn >> 2) & 0x1F;
sext(((imm_5 << 5) | imm_4_0) as u32, 6)
}
pub fn extract_c_lui_imm(insn: u16) -> i32 {
let imm_17 = (insn >> 12) & 0x1;
let imm_16_12 = (insn >> 2) & 0x1F;
let imm = (((imm_17 as u32) << 17) | ((imm_16_12 as u32) << 12)) as u32;
sext(imm, 18)
}
pub fn extract_c_addi16sp_imm(insn: u16) -> i32 {
let imm_9 = (insn >> 12) & 0x1;
let imm_4 = (insn >> 11) & 0x1;
let imm_6 = (insn >> 7) & 0x1;
let imm_8_7 = (insn >> 5) & 0x3;
let imm_5 = (insn >> 4) & 0x1;
let imm_3_2 = (insn >> 2) & 0x3;
let imm =
((((((((imm_9 << 1) | imm_4) << 1) | imm_6) << 2) | imm_8_7) << 1) | imm_5) << 2 | imm_3_2;
sext(imm as u32, 10)
}
pub fn extract_c_j_imm(insn: u16) -> i32 {
let imm_11 = (insn >> 12) & 0x1;
let imm_4 = (insn >> 11) & 0x1;
let imm_9_8 = (insn >> 9) & 0x3;
let imm_10 = (insn >> 8) & 0x1;
let imm_6 = (insn >> 7) & 0x1;
let imm_7 = (insn >> 6) & 0x1;
let imm_3_1 = (insn >> 3) & 0x7;
let imm_5 = (insn >> 2) & 0x1;
let imm = (imm_11 << 11)
| (imm_4 << 4)
| (imm_9_8 << 8)
| (imm_10 << 10)
| (imm_6 << 6)
| (imm_7 << 7)
| (imm_3_1 << 1)
| (imm_5 << 5);
sext(imm as u32, 12)
}
pub fn extract_c_b_imm(insn: u16) -> i32 {
let imm_8 = (insn >> 12) & 0x1;
let imm_4_3 = (insn >> 10) & 0x3;
let imm_7_6 = (insn >> 5) & 0x3;
let imm_2_1 = (insn >> 3) & 0x3;
let imm_5 = (insn >> 2) & 0x1;
let imm = (imm_8 << 8) | (imm_4_3 << 3) | (imm_7_6 << 6) | (imm_2_1 << 1) | (imm_5 << 5);
sext(imm as u32, 9)
}
pub fn gpr_abi_name(reg: u8) -> &'static str {
match reg {
0 => "zero",
1 => "ra",
2 => "sp",
3 => "gp",
4 => "tp",
5 => "t0",
6 => "t1",
7 => "t2",
8 => "s0",
9 => "s1",
10 => "a0",
11 => "a1",
12 => "a2",
13 => "a3",
14 => "a4",
15 => "a5",
16 => "a6",
17 => "a7",
18 => "s2",
19 => "s3",
20 => "s4",
21 => "s5",
22 => "s6",
23 => "s7",
24 => "s8",
25 => "s9",
26 => "s10",
27 => "s11",
28 => "t3",
29 => "t4",
30 => "t5",
31 => "t6",
_ => "unknown",
}
}
pub fn gpr_arch_name(reg: u8) -> String {
format!("x{}", reg)
}
pub fn fpr_abi_name(reg: u8) -> &'static str {
match reg {
0 => "ft0",
1 => "ft1",
2 => "ft2",
3 => "ft3",
4 => "ft4",
5 => "ft5",
6 => "ft6",
7 => "ft7",
8 => "fs0",
9 => "fs1",
10 => "fa0",
11 => "fa1",
12 => "fa2",
13 => "fa3",
14 => "fa4",
15 => "fa5",
16 => "fa6",
17 => "fa7",
18 => "fs2",
19 => "fs3",
20 => "fs4",
21 => "fs5",
22 => "fs6",
23 => "fs7",
24 => "fs8",
25 => "fs9",
26 => "fs10",
27 => "fs11",
28 => "ft8",
29 => "ft9",
30 => "ft10",
31 => "ft11",
_ => "unknown",
}
}
pub fn fpr_arch_name(reg: u8) -> String {
format!("f{}", reg)
}
pub struct PseudoDetector;
impl PseudoDetector {
pub fn detect_pseudo(opcode: u32, operands: &[MachineOperand]) -> Option<&'static str> {
let addi = RiscVOpcode::ADDI as u32;
let xori = RiscVOpcode::XORI as u32;
let sub = RiscVOpcode::SUB as u32;
let sltiu = RiscVOpcode::SLTIU as u32;
let sltu = RiscVOpcode::SLTU as u32;
let jal = RiscVOpcode::JAL as u32;
let jalr = RiscVOpcode::JALR as u32;
let beq = RiscVOpcode::BEQ as u32;
let bne = RiscVOpcode::BNE as u32;
let blt = RiscVOpcode::BLT as u32;
let bge = RiscVOpcode::BGE as u32;
match opcode {
n if n == addi && operands.len() >= 2 => {
if let (MachineOperand::Reg(_), MachineOperand::Reg(0), MachineOperand::Imm(0)) = (
&operands[0],
&operands[1],
&operands.get(2).unwrap_or(&MachineOperand::Imm(0)),
) {
return Some("NOP");
}
None
}
n if n == addi && operands.len() >= 3 => {
if let MachineOperand::Imm(0) = &operands[2] {
return Some("MV");
}
None
}
n if n == addi && operands.len() >= 3 => {
if let MachineOperand::Reg(0) = &operands[1] {
return Some("LI");
}
None
}
n if n == xori && operands.len() >= 3 => {
if let MachineOperand::Imm(-1) = &operands[2] {
return Some("NOT");
}
None
}
n if n == sub && operands.len() >= 3 => {
if let MachineOperand::Reg(0) = &operands[1] {
return Some("NEG");
}
None
}
n if n == sltiu && operands.len() >= 3 => {
if let MachineOperand::Imm(1) = &operands[2] {
return Some("SEQZ");
}
None
}
n if n == sltu && operands.len() >= 3 => {
if let MachineOperand::Reg(0) = &operands[1] {
return Some("SNEZ");
}
None
}
n if n == jalr && operands.len() >= 3 => {
if let (MachineOperand::Reg(0), MachineOperand::Imm(0)) =
(&operands[0], &operands[2])
{
return Some("JR");
}
None
}
n if n == jal && operands.len() >= 2 => {
if let MachineOperand::Reg(0) = &operands[0] {
return Some("J");
}
None
}
n if n == beq && operands.len() >= 3 => {
if let MachineOperand::Reg(0) = &operands[1] {
return Some("BEQZ");
}
None
}
n if n == bne && operands.len() >= 3 => {
if let MachineOperand::Reg(0) = &operands[1] {
return Some("BNEZ");
}
None
}
n if n == blt && operands.len() >= 3 => {
if let MachineOperand::Reg(0) = &operands[1] {
return Some("BLTZ");
}
None
}
n if n == bge && operands.len() >= 3 => {
if let MachineOperand::Reg(0) = &operands[1] {
return Some("BGEZ");
}
None
}
_ => None,
}
}
}
pub fn decode_fp_load(insn: u32) -> Option<(u32, Vec<u32>, Vec<i64>)> {
let funct3 = extract_funct3(insn);
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let imm = extract_i_imm(insn);
let op = match funct3 {
0b010 => RiscVOpcode::FLW,
0b011 => RiscVOpcode::FLD,
_ => return None,
};
Some((op as u32, vec![rd as u32, rs1 as u32], vec![imm as i64]))
}
pub fn decode_fp_store(insn: u32) -> Option<(u32, Vec<u32>, Vec<i64>)> {
let funct3 = extract_funct3(insn);
let rs1 = extract_rs1(insn);
let rs2 = extract_rs2(insn);
let imm = extract_s_imm(insn);
let op = match funct3 {
0b010 => RiscVOpcode::FSW,
0b011 => RiscVOpcode::FSD,
_ => return None,
};
Some((op as u32, vec![rs2 as u32, rs1 as u32], vec![imm as i64]))
}
pub fn decode_atomic(insn: u32) -> Option<(u32, Vec<u32>, Vec<i64>)> {
let funct3 = extract_funct3(insn);
let funct5 = ((insn >> 27) & 0x1F) as u32;
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let rs2 = extract_rs2(insn);
let _aq = (insn >> 26) & 0x1;
let _rl = (insn >> 25) & 0x1;
let is_64bit = funct3 == 0b011;
let op = match (funct5, is_64bit) {
(0b00010, false) => RiscVOpcode::LR_W,
(0b00011, false) => RiscVOpcode::SC_W,
(0b00010, true) => RiscVOpcode::LR_D,
(0b00011, true) => RiscVOpcode::SC_D,
(0b00001, false) => RiscVOpcode::AMOSWAP_W,
(0b00000, false) => RiscVOpcode::AMOADD_W,
(0b00100, false) => RiscVOpcode::AMOXOR_W,
(0b01100, false) => RiscVOpcode::AMOAND_W,
(0b01000, false) => RiscVOpcode::AMOOR_W,
(0b10000, false) => RiscVOpcode::AMOMIN_W,
(0b10100, false) => RiscVOpcode::AMOMAX_W,
(0b11000, false) => RiscVOpcode::AMOMINU_W,
(0b11100, false) => RiscVOpcode::AMOMAXU_W,
(0b00001, true) => RiscVOpcode::AMOSWAP_D,
(0b00000, true) => RiscVOpcode::AMOADD_D,
(0b00100, true) => RiscVOpcode::AMOXOR_D,
(0b01100, true) => RiscVOpcode::AMOAND_D,
(0b01000, true) => RiscVOpcode::AMOOR_D,
(0b10000, true) => RiscVOpcode::AMOMIN_D,
(0b10100, true) => RiscVOpcode::AMOMAX_D,
_ => return None,
};
let mut regs = vec![rd as u32, rs1 as u32];
if !matches!(funct5, 0b00010) {
regs.push(rs2 as u32);
}
Some((op as u32, regs, vec![]))
}
pub fn decode_fp_compute(insn: u32, is_64bit: bool) -> Option<(u32, Vec<u32>, Vec<i64>)> {
let funct3 = extract_funct3(insn);
let funct7 = extract_funct7(insn);
let rd = extract_rd(insn);
let rs1 = extract_rs1(insn);
let rs2 = extract_rs2(insn);
let rm = funct3;
let fmt = funct7 & 0x3;
let op = match (funct7 >> 2, fmt, rm) {
(0b00000, 0, _) => RiscVOpcode::FADD_S,
(0b00001, 0, _) => RiscVOpcode::FSUB_S,
(0b00010, 0, _) => RiscVOpcode::FMUL_S,
(0b00011, 0, _) => RiscVOpcode::FDIV_S,
(0b01011, 0, _) => RiscVOpcode::FSQRT_S,
(0b00000, 1, _) => RiscVOpcode::FADD_D,
(0b00001, 1, _) => RiscVOpcode::FSUB_D,
(0b00010, 1, _) => RiscVOpcode::FMUL_D,
(0b00011, 1, _) => RiscVOpcode::FDIV_D,
(0b01011, 1, _) => RiscVOpcode::FSQRT_D,
(0b11000, 0, _) => RiscVOpcode::FCVT_W_S,
(0b11010, 0, _) => RiscVOpcode::FCVT_S_W,
(0b00100, 0, 0b000) => RiscVOpcode::FSGNJ_S,
(0b00100, 0, 0b001) => RiscVOpcode::FSGNJN_S,
(0b00100, 0, 0b010) => RiscVOpcode::FSGNJX_S,
(0b10100, 0, 0b010) => RiscVOpcode::FEQ_S,
(0b10100, 0, 0b001) => RiscVOpcode::FLT_S,
(0b10100, 0, 0b000) => RiscVOpcode::FLE_S,
(0b00101, 0, 0b000) => RiscVOpcode::FMIN_S,
(0b00101, 0, 0b001) => RiscVOpcode::FMAX_S,
(0b11100, 0, 0b000) => RiscVOpcode::FMV_X_W,
(0b11110, 0, 0b000) => RiscVOpcode::FMV_W_X,
_ => return None,
};
let mut regs = vec![rd as u32, rs1 as u32];
if !matches!(
op,
RiscVOpcode::FSQRT_S
| RiscVOpcode::FSQRT_D
| RiscVOpcode::FCVT_W_S
| RiscVOpcode::FCVT_S_W
| RiscVOpcode::FMV_X_W
| RiscVOpcode::FMV_W_X
) {
regs.push(rs2 as u32);
}
Some((op as u32, regs, vec![rm as i64]))
}
impl RiscVMCDecoder {
pub fn decode_compressed_mi(&self, halfword: u16) -> Option<(MachineInstr, usize)> {
let (opcode, regs, imms) = decode_compressed(halfword)?;
let mut mi = MachineInstr::new(opcode);
for reg in regs {
mi.push_reg(reg_field_to_id(reg as u8, false));
}
for imm in imms {
mi.push_imm(imm);
}
Some((mi, 2))
}
pub fn decode_instruction_any(
&self,
bytes: &[u8],
offset: usize,
) -> Option<(MachineInstr, usize)> {
if offset + 2 > bytes.len() {
return None;
}
let halfword = u16::from_le_bytes([bytes[offset], bytes[offset + 1]]);
if is_compressed(halfword) {
self.decode_compressed_mi(halfword)
} else {
self.decode_instruction(bytes, offset)
}
}
pub fn decode_extended(&self, insn: u32) -> Option<MachineInstr> {
let opcode = extract_opcode(insn);
match opcode {
0b0000111 => {
let (op, regs, imms) = decode_fp_load(insn)?;
let mut mi = MachineInstr::new(op);
for reg in regs {
mi.push_reg(reg_field_to_id(reg as u8, true));
}
for &imm in &imms {
mi.push_imm(imm);
}
Some(mi)
}
0b0100111 => {
let (op, regs, imms) = decode_fp_store(insn)?;
let mut mi = MachineInstr::new(op);
for reg in regs {
mi.push_reg(reg_field_to_id(reg as u8, true));
}
for &imm in &imms {
mi.push_imm(imm);
}
Some(mi)
}
0b0101111 => {
let (op, regs, imms) = decode_atomic(insn)?;
let mut mi = MachineInstr::new(op);
for reg in regs {
mi.push_reg(reg_field_to_id(reg as u8, false));
}
for &imm in &imms {
mi.push_imm(imm);
}
Some(mi)
}
0b1010011 => {
let (op, regs, imms) = decode_fp_compute(insn, self.is_64bit)?;
let mut mi = MachineInstr::new(op);
for reg in regs {
mi.push_reg(reg_field_to_id(reg as u8, true));
}
for &imm in &imms {
mi.push_imm(imm);
}
Some(mi)
}
0b1000011 | 0b1000111 | 0b1001011 | 0b1001111 => {
let rd = extract_rd(insn) as u32;
let rs1 = extract_rs1(insn) as u32;
let rs2 = extract_rs2(insn) as u32;
let rs3 = ((insn >> 27) & 0x1F) as u32;
let rm = extract_funct3(insn) as u32;
let fmt = ((insn >> 25) & 0x3) as u32;
let op = match (opcode, fmt) {
(0b1000011, 0) => RiscVOpcode::FMADD_S,
(0b1000111, 0) => RiscVOpcode::FMSUB_S,
(0b1001011, 0) => RiscVOpcode::FNMSUB_S,
(0b1001111, 0) => RiscVOpcode::FNMADD_S,
(0b1000011, 1) => RiscVOpcode::FMADD_D,
(0b1000111, 1) => RiscVOpcode::FMSUB_D,
(0b1001011, 1) => RiscVOpcode::FNMSUB_D,
(0b1001111, 1) => RiscVOpcode::FNMADD_D,
_ => return None,
};
let mut mi = MachineInstr::new(op as u32);
mi.push_reg(reg_field_to_id(rd as u8, true));
mi.push_reg(reg_field_to_id(rs1 as u8, true));
mi.push_reg(reg_field_to_id(rs2 as u8, true));
mi.push_reg(reg_field_to_id(rs3 as u8, true));
mi.push_imm(rm as i64);
Some(mi)
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_fields() {
let insn = (F7_BASE as u32) << 25
| (11u32 << 20)
| (10u32 << 15)
| (F3_ADD_SUB as u32) << 12
| (5u32 << 7)
| OP_ALU as u32;
assert_eq!(extract_opcode(insn), OP_ALU);
assert_eq!(extract_rd(insn), 5);
assert_eq!(extract_rs1(insn), 10);
assert_eq!(extract_rs2(insn), 11);
assert_eq!(extract_funct3(insn), F3_ADD_SUB);
assert_eq!(extract_funct7(insn), F7_BASE);
}
#[test]
fn test_extract_i_imm_positive() {
let insn =
(42u32 << 20) | (10u32 << 15) | (F3_ADDI as u32) << 12 | (5u32 << 7) | OP_ALUI as u32;
assert_eq!(extract_i_imm(insn), 42);
}
#[test]
fn test_extract_i_imm_negative() {
let insn = (0xFFFu32 << 20)
| (10u32 << 15)
| (F3_ADDI as u32) << 12
| (5u32 << 7)
| OP_ALUI as u32;
assert_eq!(extract_i_imm(insn), -1);
}
#[test]
fn test_extract_s_imm() {
let insn = (0u32 << 25)
| (11u32 << 20)
| (10u32 << 15)
| (F3_SW as u32) << 12
| (8u32 << 7)
| OP_STORE as u32;
assert_eq!(extract_s_imm(insn), 8);
}
#[test]
fn test_extract_b_imm() {
let off = 16u32;
let imm_12 = (off >> 12) & 0x1;
let imm_11 = (off >> 11) & 0x1;
let imm_10_5 = (off >> 5) & 0x3F;
let imm_4_1 = (off >> 1) & 0xF;
let insn = (imm_12 << 31)
| (imm_10_5 << 25)
| (11u32 << 20)
| (10u32 << 15)
| (F3_BEQ as u32) << 12
| (imm_4_1 << 8)
| (imm_11 << 7)
| OP_BRANCH as u32;
assert_eq!(extract_b_imm(insn), 16);
}
#[test]
fn test_extract_u_imm() {
let insn = 0x12345000u32 | (5u32 << 7) | OP_LUI as u32;
let imm = extract_u_imm(insn);
assert_eq!(imm as u32, 0x12345000u32);
}
#[test]
fn test_extract_j_imm() {
let off = 256u32;
let imm_20 = (off >> 20) & 0x1;
let imm_10_1 = (off >> 1) & 0x3FF;
let imm_11 = (off >> 11) & 0x1;
let imm_19_12 = (off >> 12) & 0xFF;
let insn = (imm_20 << 31)
| (imm_10_1 << 21)
| (imm_11 << 20)
| (imm_19_12 << 12)
| (1u32 << 7)
| OP_JAL as u32;
assert_eq!(extract_j_imm(insn), 256);
}
#[test]
fn test_decode_add() {
let insn = (F7_BASE as u32) << 25
| (11u32 << 20)
| (10u32 << 15)
| (F3_ADD_SUB as u32) << 12
| (5u32 << 7)
| OP_ALU as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::ADD as u32);
assert_eq!(mi.operands.len(), 3);
}
#[test]
fn test_decode_sub() {
let insn = (F7_ALT as u32) << 25
| (20u32 << 20)
| (15u32 << 15)
| (F3_ADD_SUB as u32) << 12
| (8u32 << 7)
| OP_ALU as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::SUB as u32);
}
#[test]
fn test_decode_addi() {
let insn =
(42u32 << 20) | (10u32 << 15) | (F3_ADDI as u32) << 12 | (5u32 << 7) | OP_ALUI as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::ADDI as u32);
}
#[test]
fn test_decode_lw() {
let insn =
(4u32 << 20) | (10u32 << 15) | (F3_LW as u32) << 12 | (5u32 << 7) | OP_LOAD as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::LW as u32);
}
#[test]
fn test_decode_sw() {
let insn = (0u32 << 25)
| (11u32 << 20)
| (10u32 << 15)
| (F3_SW as u32) << 12
| (8u32 << 7)
| OP_STORE as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::SW as u32);
}
#[test]
fn test_decode_beq() {
let off = 16u32;
let insn = ((off >> 12) & 0x1) << 31
| (((off >> 5) & 0x3F) << 25)
| (11u32 << 20)
| (10u32 << 15)
| (F3_BEQ as u32) << 12
| (((off >> 1) & 0xF) << 8)
| (((off >> 11) & 0x1) << 7)
| OP_BRANCH as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::BEQ as u32);
}
#[test]
fn test_decode_lui() {
let insn = 0x12345000u32 | (5u32 << 7) | OP_LUI as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::LUI as u32);
}
#[test]
fn test_decode_jal() {
let off = 256u32;
let insn = ((off >> 20) & 0x1) << 31
| (((off >> 1) & 0x3FF) << 21)
| (((off >> 11) & 0x1) << 20)
| (((off >> 12) & 0xFF) << 12)
| (1u32 << 7)
| OP_JAL as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::JAL as u32);
}
#[test]
fn test_decode_ecall() {
let insn =
(0u32 << 20) | (0u32 << 15) | (F3_PRIV as u32) << 12 | (0u32 << 7) | OP_SYSTEM as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::ECALL as u32);
}
#[test]
fn test_decode_ebreak() {
let insn =
(1u32 << 20) | (0u32 << 15) | (F3_PRIV as u32) << 12 | (0u32 << 7) | OP_SYSTEM as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::EBREAK as u32);
}
#[test]
fn test_decode_slli() {
let insn = (F7_BASE as u32) << 25
| (3u32 << 20)
| (10u32 << 15)
| (F3_SLLI as u32) << 12
| (5u32 << 7)
| OP_ALUI as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::SLLI as u32);
}
#[test]
fn test_reg_field_to_id_gpr() {
assert_eq!(reg_field_to_id(0, false), 3000);
assert_eq!(reg_field_to_id(1, false), 3001);
assert_eq!(reg_field_to_id(31, false), 3031);
}
#[test]
fn test_reg_field_to_id_fpr() {
assert_eq!(reg_field_to_id(0, true), 3050);
assert_eq!(reg_field_to_id(1, true), 3051);
assert_eq!(reg_field_to_id(31, true), 3081);
}
#[test]
fn test_decode_csr() {
let insn = (0x300u32 << 20)
| (10u32 << 15)
| (F3_CSRRW as u32) << 12
| (5u32 << 7)
| OP_SYSTEM as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::CSRRW as u32);
}
#[test]
fn test_decode_mul() {
let insn = (F7_MULDIV as u32) << 25
| (11u32 << 20)
| (10u32 << 15)
| (F3_MUL as u32) << 12
| (5u32 << 7)
| OP_ALU as u32;
let decoder = RiscVMCDecoder::new(true);
let mi = decoder.decode_by_opcode(insn).unwrap();
assert_eq!(mi.opcode, RiscVOpcode::MUL as u32);
}
#[test]
fn test_decode_instruction_bytes() {
let insn = (F7_BASE as u32) << 25
| (11u32 << 20)
| (10u32 << 15)
| (F3_ADD_SUB as u32) << 12
| (5u32 << 7)
| OP_ALU as u32;
let bytes = insn.to_le_bytes();
let decoder = RiscVMCDecoder::new(true);
let (mi, size) = decoder.decode_instruction(&bytes, 0).unwrap();
assert_eq!(size, 4);
assert_eq!(mi.opcode, RiscVOpcode::ADD as u32);
}
#[test]
fn test_instruction_length_32bit() {
assert_eq!(instruction_length(0b11), 4);
assert_eq!(instruction_length(0x0003), 4);
}
#[test]
fn test_instruction_length_compressed() {
assert_eq!(instruction_length(0b00), 2);
assert_eq!(instruction_length(0b01), 2);
assert_eq!(instruction_length(0b10), 2);
}
#[test]
fn test_is_compressed() {
assert!(is_compressed(0b00));
assert!(is_compressed(0b01));
assert!(is_compressed(0b10));
assert!(!is_compressed(0b11));
}
#[test]
fn test_gpr_abi_names() {
assert_eq!(gpr_abi_name(0), "zero");
assert_eq!(gpr_abi_name(1), "ra");
assert_eq!(gpr_abi_name(2), "sp");
assert_eq!(gpr_abi_name(3), "gp");
assert_eq!(gpr_abi_name(5), "t0");
assert_eq!(gpr_abi_name(10), "a0");
assert_eq!(gpr_abi_name(17), "a7");
assert_eq!(gpr_abi_name(8), "s0");
assert_eq!(gpr_abi_name(28), "t3");
assert_eq!(gpr_abi_name(31), "t6");
}
#[test]
fn test_fpr_abi_names() {
assert_eq!(fpr_abi_name(0), "ft0");
assert_eq!(fpr_abi_name(7), "ft7");
assert_eq!(fpr_abi_name(8), "fs0");
assert_eq!(fpr_abi_name(10), "fa0");
assert_eq!(fpr_abi_name(17), "fa7");
assert_eq!(fpr_abi_name(18), "fs2");
assert_eq!(fpr_abi_name(28), "ft8");
assert_eq!(fpr_abi_name(31), "ft11");
}
#[test]
fn test_gpr_arch_name() {
assert_eq!(gpr_arch_name(0), "x0");
assert_eq!(gpr_arch_name(15), "x15");
assert_eq!(gpr_arch_name(31), "x31");
}
#[test]
fn test_fpr_arch_name() {
assert_eq!(fpr_arch_name(0), "f0");
assert_eq!(fpr_arch_name(10), "f10");
assert_eq!(fpr_arch_name(31), "f31");
}
#[test]
fn test_pseudo_detect_nop() {
let operands = vec![
MachineOperand::Reg(5),
MachineOperand::Reg(0),
MachineOperand::Imm(0),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::ADDI as u32, &operands),
Some("NOP")
);
}
#[test]
fn test_pseudo_detect_mv() {
let operands = vec![
MachineOperand::Reg(6),
MachineOperand::Reg(10),
MachineOperand::Imm(0),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::ADDI as u32, &operands),
Some("MV")
);
}
#[test]
fn test_pseudo_detect_not() {
let operands = vec![
MachineOperand::Reg(7),
MachineOperand::Reg(11),
MachineOperand::Imm(-1),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::XORI as u32, &operands),
Some("NOT")
);
}
#[test]
fn test_pseudo_detect_neg() {
let operands = vec![
MachineOperand::Reg(8),
MachineOperand::Reg(0),
MachineOperand::Reg(12),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::SUB as u32, &operands),
Some("NEG")
);
}
#[test]
fn test_pseudo_detect_seqz() {
let operands = vec![
MachineOperand::Reg(9),
MachineOperand::Reg(13),
MachineOperand::Imm(1),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::SLTIU as u32, &operands),
Some("SEQZ")
);
}
#[test]
fn test_pseudo_detect_snez() {
let operands = vec![
MachineOperand::Reg(10),
MachineOperand::Reg(0),
MachineOperand::Reg(14),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::SLTU as u32, &operands),
Some("SNEZ")
);
}
#[test]
fn test_pseudo_detect_li() {
let operands = vec![
MachineOperand::Reg(5),
MachineOperand::Reg(0),
MachineOperand::Imm(42),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::ADDI as u32, &operands),
Some("LI")
);
}
#[test]
fn test_pseudo_detect_jr() {
let operands = vec![
MachineOperand::Reg(0),
MachineOperand::Reg(5),
MachineOperand::Imm(0),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::JALR as u32, &operands),
Some("JR")
);
}
#[test]
fn test_pseudo_detect_j() {
let operands = vec![MachineOperand::Reg(0), MachineOperand::Imm(256)];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::JAL as u32, &operands),
Some("J")
);
}
#[test]
fn test_pseudo_detect_beqz() {
let operands = vec![
MachineOperand::Reg(10),
MachineOperand::Reg(0),
MachineOperand::Imm(16),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::BEQ as u32, &operands),
Some("BEQZ")
);
}
#[test]
fn test_pseudo_detect_bnez() {
let operands = vec![
MachineOperand::Reg(10),
MachineOperand::Reg(0),
MachineOperand::Imm(16),
];
assert_eq!(
PseudoDetector::detect_pseudo(RiscVOpcode::BNE as u32, &operands),
Some("BNEZ")
);
}
#[test]
fn test_extract_c_imm_6() {
let insn: u16 = (0b010 << 13) | (0 << 12) | (5 << 7) | (21 << 2) | 0b01;
let imm = extract_c_imm_6(insn);
assert_eq!(imm, 21);
}
#[test]
fn test_decode_compressed_c_li() {
let insn: u16 = (0b010 << 13) | (0 << 12) | (5 << 7) | (21 << 2) | 0b01;
let result = decode_compressed(insn);
assert!(result.is_some());
let (opcode, regs, imms) = result.unwrap();
assert_eq!(opcode, RiscVOpcode::C_LI as u32);
assert_eq!(regs, vec![5]);
}
#[test]
fn test_decode_compressed_c_add() {
let insn: u16 = (0b1001 << 12) | (5 << 7) | (10 << 2) | 0b10;
let result = decode_c2(insn);
assert!(result.is_some());
let (opcode, regs, _) = result.unwrap();
assert_eq!(opcode, RiscVOpcode::C_ADD as u32);
assert_eq!(regs, vec![5, 10]);
}
#[test]
fn test_decode_fp_load_flw() {
let insn = (4u32 << 20) | (10u32 << 15) | (0b010 << 12) | (5u32 << 7) | 0b0000111;
let result = decode_fp_load(insn);
assert!(result.is_some());
let (opcode, _, _) = result.unwrap();
assert_eq!(opcode, RiscVOpcode::FLW as u32);
}
#[test]
fn test_decode_atomic_lr_w() {
let insn = (0b00010u32 << 27) | (10u32 << 15) | (0b010 << 12) | (5u32 << 7) | 0b0101111;
let result = decode_atomic(insn);
assert!(result.is_some());
let (opcode, _, _) = result.unwrap();
assert_eq!(opcode, RiscVOpcode::LR_W as u32);
}
#[test]
fn test_decode_fp_compute_fadd_s() {
let insn = (0b0000000u32 << 25)
| (11u32 << 20)
| (10u32 << 15)
| (0u32 << 12)
| (5u32 << 7)
| 0b1010011;
let is_64bit = false;
let result = decode_fp_compute(insn, is_64bit);
assert!(result.is_some());
let (opcode, _, _) = result.unwrap();
assert_eq!(opcode, RiscVOpcode::FADD_S as u32);
}
#[test]
fn test_decode_fp_compute_fadd_d() {
let insn = (0b0000001u32 << 25)
| (11u32 << 20)
| (10u32 << 15)
| (0u32 << 12)
| (5u32 << 7)
| 0b1010011;
let is_64bit = true;
let result = decode_fp_compute(insn, is_64bit);
assert!(result.is_some());
let (opcode, _, _) = result.unwrap();
assert_eq!(opcode, RiscVOpcode::FADD_D as u32);
}
#[test]
fn test_decode_fp_store_fsw() {
let insn =
(0u32 << 25) | (11u32 << 20) | (10u32 << 15) | (0b010 << 12) | (8u32 << 7) | 0b0100111;
let result = decode_fp_store(insn);
assert!(result.is_some());
let (opcode, _, _) = result.unwrap();
assert_eq!(opcode, RiscVOpcode::FSW as u32);
}
#[test]
fn test_decode_extended_fmadd() {
let insn = (12u32 << 27)
| (0b00 << 25)
| (11u32 << 20)
| (10u32 << 15)
| (0u32 << 12)
| (5u32 << 7)
| 0b1000011;
let decoder = RiscVMCDecoder::new(false);
let result = decoder.decode_extended(insn);
assert!(result.is_some());
let mi = result.unwrap();
assert_eq!(mi.opcode, RiscVOpcode::FMADD_S as u32);
assert_eq!(mi.operands.len(), 5);
}
#[test]
fn test_decode_extended_atomic() {
let insn = (0b00001u32 << 27)
| (12u32 << 20)
| (10u32 << 15)
| (0b010 << 12)
| (5u32 << 7)
| 0b0101111;
let decoder = RiscVMCDecoder::new(false);
let result = decoder.decode_extended(insn);
assert!(result.is_some());
let mi = result.unwrap();
assert_eq!(mi.opcode, RiscVOpcode::AMOSWAP_W as u32);
}
#[test]
fn test_decode_instruction_any_32bit() {
let insn = (F7_BASE as u32) << 25
| (11u32 << 20)
| (10u32 << 15)
| (F3_ADD_SUB as u32) << 12
| (5u32 << 7)
| OP_ALU as u32;
let bytes = insn.to_le_bytes();
assert_eq!(bytes[0] & 0x3, 0x3);
let decoder = RiscVMCDecoder::new(true);
let result = decoder.decode_instruction_any(&bytes, 0);
assert!(result.is_some());
let (mi, size) = result.unwrap();
assert_eq!(size, 4);
assert_eq!(mi.opcode, RiscVOpcode::ADD as u32);
}
#[test]
fn test_decode_instruction_any_compressed() {
let insn: u16 = (0b010 << 13) | (0 << 12) | (5 << 7) | (21 << 2) | 0b01;
let bytes = insn.to_le_bytes();
let decoder = RiscVMCDecoder::new(false);
let result = decoder.decode_instruction_any(&bytes, 0);
if let Some((mi, size)) = result {
assert_eq!(size, 2);
}
}
}