use super::riscv_instr_info::RiscVOpcode;
use crate::opcode::Opcode;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct BitmanipPattern {
pub ir_opcode: Opcode,
pub description: &'static str,
pub result_opcode: RiscVOpcode,
pub priority: u32,
pub num_operands: u8,
pub required_extension: Option<&'static str>,
pub is_rv64_only: bool,
}
pub struct RiscVBitmanipIselTable {
pub patterns: Vec<BitmanipPattern>,
pub total_patterns: usize,
pub extensions_covered: Vec<String>,
}
pub fn riscv_bitmanip_isel_table() -> Vec<BitmanipPattern> {
let mut table = Vec::new();
table.push(BitmanipPattern {
ir_opcode: Opcode::Add,
description: "sh1add rd, rs1, rs2 — shift left 1 and add (address gen)",
result_opcode: RiscVOpcode::SH1ADD,
priority: 100,
num_operands: 3,
required_extension: Some("zba"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Add,
description: "sh2add rd, rs1, rs2 — shift left 2 and add (address gen)",
result_opcode: RiscVOpcode::SH2ADD,
priority: 101,
num_operands: 3,
required_extension: Some("zba"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Add,
description: "sh3add rd, rs1, rs2 — shift left 3 and add (address gen)",
result_opcode: RiscVOpcode::SH3ADD,
priority: 102,
num_operands: 3,
required_extension: Some("zba"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Add,
description: "sh1add rd, rs1, rs2 — shift left 1 and add (RV64)",
result_opcode: RiscVOpcode::SH1ADD,
priority: 103,
num_operands: 3,
required_extension: Some("zba"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Add,
description: "sh2add rd, rs1, rs2 — shift left 2 and add (RV64)",
result_opcode: RiscVOpcode::SH2ADD,
priority: 104,
num_operands: 3,
required_extension: Some("zba"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Add,
description: "sh3add rd, rs1, rs2 — shift left 3 and add (RV64)",
result_opcode: RiscVOpcode::SH3ADD,
priority: 105,
num_operands: 3,
required_extension: Some("zba"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::And,
description: "andn rd, rs1, rs2 — bitwise AND with complement",
result_opcode: RiscVOpcode::ANDN,
priority: 100,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Or,
description: "orn rd, rs1, rs2 — bitwise OR with complement",
result_opcode: RiscVOpcode::ORN,
priority: 100,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Xor,
description: "xnor rd, rs1, rs2 — bitwise exclusive-NOR",
result_opcode: RiscVOpcode::XNOR,
priority: 100,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "clz rd, rs1 — count leading zero bits (32-bit)",
result_opcode: RiscVOpcode::CLZ,
priority: 100,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "clz rd, rs1 — count leading zero bits (64-bit)",
result_opcode: RiscVOpcode::CLZW,
priority: 101,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "ctz rd, rs1 — count trailing zero bits (32-bit)",
result_opcode: RiscVOpcode::CTZ,
priority: 102,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "ctz rd, rs1 — count trailing zero bits (64-bit)",
result_opcode: RiscVOpcode::CTZW,
priority: 103,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "cpop rd, rs1 — count set bits (population count 32-bit)",
result_opcode: RiscVOpcode::CPOP,
priority: 104,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "cpop rd, rs1 — count set bits (population count 64-bit)",
result_opcode: RiscVOpcode::CPOPW,
priority: 105,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::ICmp,
description: "max rd, rs1, rs2 — signed maximum",
result_opcode: RiscVOpcode::MAX,
priority: 100,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::ICmp,
description: "maxu rd, rs1, rs2 — unsigned maximum",
result_opcode: RiscVOpcode::MAXU,
priority: 101,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::ICmp,
description: "min rd, rs1, rs2 — signed minimum",
result_opcode: RiscVOpcode::MIN,
priority: 102,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::ICmp,
description: "minu rd, rs1, rs2 — unsigned minimum",
result_opcode: RiscVOpcode::MINU,
priority: 103,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::SExt,
description: "sext.b rd, rs1 — sign-extend byte (8→XLEN)",
result_opcode: RiscVOpcode::SEXT_B,
priority: 100,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::SExt,
description: "sext.h rd, rs1 — sign-extend halfword (16→XLEN)",
result_opcode: RiscVOpcode::SEXT_H,
priority: 101,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::ZExt,
description: "zext.h rd, rs1 — zero-extend halfword (16→XLEN)",
result_opcode: RiscVOpcode::ZEXT_H,
priority: 100,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Shl,
description: "rol rd, rs1, rs2 — rotate left by register (32-bit)",
result_opcode: RiscVOpcode::ROL,
priority: 100,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Shl,
description: "rol rd, rs1, rs2 — rotate left by register (64-bit)",
result_opcode: RiscVOpcode::ROLW,
priority: 101,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::LShr,
description: "ror rd, rs1, rs2 — rotate right by register (32-bit)",
result_opcode: RiscVOpcode::ROR,
priority: 102,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::LShr,
description: "ror rd, rs1, rs2 — rotate right by register (64-bit)",
result_opcode: RiscVOpcode::RORW,
priority: 103,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Shl,
description: "rori rd, rs1, shamt — rotate right by immediate (32-bit)",
result_opcode: RiscVOpcode::RORI,
priority: 104,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Shl,
description: "rori rd, rs1, shamt — rotate right by immediate (64-bit)",
result_opcode: RiscVOpcode::RORIW,
priority: 105,
num_operands: 3,
required_extension: Some("zbb"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Or,
description: "orc.b rd, rs1 — OR-combine bytes within word",
result_opcode: RiscVOpcode::ORC_B,
priority: 100,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "rev8 rd, rs1 — byte-reverse register (32-bit)",
result_opcode: RiscVOpcode::REV8,
priority: 100,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "rev8 rd, rs1 — byte-reverse register (64-bit)",
result_opcode: RiscVOpcode::REV8_64,
priority: 101,
num_operands: 2,
required_extension: Some("zbb"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Mul,
description: "clmul rd, rs1, rs2 — carry-less multiply (low half)",
result_opcode: RiscVOpcode::CLMUL,
priority: 100,
num_operands: 3,
required_extension: Some("zbc"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Mul,
description: "clmulh rd, rs1, rs2 — carry-less multiply (high half)",
result_opcode: RiscVOpcode::CLMULH,
priority: 101,
num_operands: 3,
required_extension: Some("zbc"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Mul,
description: "clmulr rd, rs1, rs2 — carry-less multiply (reversed)",
result_opcode: RiscVOpcode::CLMULR,
priority: 102,
num_operands: 3,
required_extension: Some("zbc"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::And,
description: "bclr rd, rs1, rs2 — bit clear (register index)",
result_opcode: RiscVOpcode::BCLR,
priority: 100,
num_operands: 3,
required_extension: Some("zbs"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::And,
description: "bclri rd, rs1, imm — bit clear (immediate)",
result_opcode: RiscVOpcode::BCLRI,
priority: 101,
num_operands: 3,
required_extension: Some("zbs"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Or,
description: "bset rd, rs1, rs2 — bit set (register index)",
result_opcode: RiscVOpcode::BSET,
priority: 100,
num_operands: 3,
required_extension: Some("zbs"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Or,
description: "bseti rd, rs1, imm — bit set (immediate)",
result_opcode: RiscVOpcode::BSETI,
priority: 101,
num_operands: 3,
required_extension: Some("zbs"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Xor,
description: "binv rd, rs1, rs2 — bit invert (register index)",
result_opcode: RiscVOpcode::BINV,
priority: 100,
num_operands: 3,
required_extension: Some("zbs"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Xor,
description: "binvi rd, rs1, imm — bit invert (immediate)",
result_opcode: RiscVOpcode::BINVI,
priority: 101,
num_operands: 3,
required_extension: Some("zbs"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::And,
description: "bext rd, rs1, rs2 — bit extract (register index)",
result_opcode: RiscVOpcode::BEXT,
priority: 102,
num_operands: 3,
required_extension: Some("zbs"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::And,
description: "bexti rd, rs1, imm — bit extract (immediate)",
result_opcode: RiscVOpcode::BEXTI,
priority: 103,
num_operands: 3,
required_extension: Some("zbs"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "pack rd, rs1, rs2 — pack low halves of two registers",
result_opcode: RiscVOpcode::PACK,
priority: 100,
num_operands: 3,
required_extension: Some("zbkb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "packh rd, rs1, rs2 — pack low bytes of two registers",
result_opcode: RiscVOpcode::PACKH,
priority: 101,
num_operands: 3,
required_extension: Some("zbkb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "packw rd, rs1, rs2 — pack low 16-bit halves (RV64)",
result_opcode: RiscVOpcode::PACKW,
priority: 102,
num_operands: 3,
required_extension: Some("zbkb"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "brev8 rd, rs1 — bit-reverse each byte in register",
result_opcode: RiscVOpcode::BREV8,
priority: 100,
num_operands: 2,
required_extension: Some("zbkb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "unzip rd, rs1 — deinterleave odd/even bits",
result_opcode: RiscVOpcode::UNZIP,
priority: 100,
num_operands: 2,
required_extension: Some("zbkb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "zip rd, rs1 — interleave odd/even bits",
result_opcode: RiscVOpcode::ZIP,
priority: 101,
num_operands: 2,
required_extension: Some("zbkb"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "xperm4 rd, rs1, rs2 — 4-bit nibble crossbar permutation",
result_opcode: RiscVOpcode::XPERM4,
priority: 100,
num_operands: 3,
required_extension: Some("zbkx"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "xperm8 rd, rs1, rs2 — 8-bit byte crossbar permutation",
result_opcode: RiscVOpcode::XPERM8,
priority: 101,
num_operands: 3,
required_extension: Some("zbkx"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "aes32dsi rd, rs1, rs2, bs — AES-32 decrypt (immediate round)",
result_opcode: RiscVOpcode::AES32DSI,
priority: 100,
num_operands: 4,
required_extension: Some("zknd"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "aes32dsmi rd, rs1, rs2, bs — AES-32 decrypt middle round",
result_opcode: RiscVOpcode::AES32DSMI,
priority: 101,
num_operands: 4,
required_extension: Some("zknd"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "aes64ds rd, rs1, rs2 — AES-64 decrypt (RV64)",
result_opcode: RiscVOpcode::AES64DS,
priority: 102,
num_operands: 3,
required_extension: Some("zknd"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "aes64dsm rd, rs1, rs2 — AES-64 decrypt middle round (RV64)",
result_opcode: RiscVOpcode::AES64DSM,
priority: 103,
num_operands: 3,
required_extension: Some("zknd"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "aes32esi rd, rs1, rs2, bs — AES-32 encrypt (immediate round)",
result_opcode: RiscVOpcode::AES32ESI,
priority: 104,
num_operands: 4,
required_extension: Some("zkne"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "aes32esmi rd, rs1, rs2, bs — AES-32 encrypt middle round",
result_opcode: RiscVOpcode::AES32ESMI,
priority: 105,
num_operands: 4,
required_extension: Some("zkne"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "aes64es rd, rs1, rs2 — AES-64 encrypt (RV64)",
result_opcode: RiscVOpcode::AES64ES,
priority: 106,
num_operands: 3,
required_extension: Some("zkne"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "aes64esm rd, rs1, rs2 — AES-64 encrypt middle round (RV64)",
result_opcode: RiscVOpcode::AES64ESM,
priority: 107,
num_operands: 3,
required_extension: Some("zkne"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sha256sig0 rd, rs1 — SHA-256 sigma 0",
result_opcode: RiscVOpcode::SHA256SIG0,
priority: 100,
num_operands: 2,
required_extension: Some("zknh"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sha256sig1 rd, rs1 — SHA-256 sigma 1",
result_opcode: RiscVOpcode::SHA256SIG1,
priority: 101,
num_operands: 2,
required_extension: Some("zknh"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sha256sum0 rd, rs1 — SHA-256 sum 0",
result_opcode: RiscVOpcode::SHA256SUM0,
priority: 102,
num_operands: 2,
required_extension: Some("zknh"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sha256sum1 rd, rs1 — SHA-256 sum 1",
result_opcode: RiscVOpcode::SHA256SUM1,
priority: 103,
num_operands: 2,
required_extension: Some("zknh"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sha512sig0 rd, rs1 — SHA-512 sigma 0 (RV64)",
result_opcode: RiscVOpcode::SHA512SIG0,
priority: 104,
num_operands: 2,
required_extension: Some("zknh"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sha512sig1 rd, rs1 — SHA-512 sigma 1 (RV64)",
result_opcode: RiscVOpcode::SHA512SIG1,
priority: 105,
num_operands: 2,
required_extension: Some("zknh"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sha512sum0 rd, rs1 — SHA-512 sum 0 (RV64)",
result_opcode: RiscVOpcode::SHA512SUM0,
priority: 106,
num_operands: 2,
required_extension: Some("zknh"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sha512sum1 rd, rs1 — SHA-512 sum 1 (RV64)",
result_opcode: RiscVOpcode::SHA512SUM1,
priority: 107,
num_operands: 2,
required_extension: Some("zknh"),
is_rv64_only: true,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sm4ed rd, rs1, rs2, bs — SM4 encrypt/decrypt round",
result_opcode: RiscVOpcode::SM4ED,
priority: 100,
num_operands: 4,
required_extension: Some("zksed"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sm4ks rd, rs1, rs2, bs — SM4 key schedule round",
result_opcode: RiscVOpcode::SM4KS,
priority: 101,
num_operands: 4,
required_extension: Some("zksed"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sm3p0 rd, rs1 — SM3 P0 permutation",
result_opcode: RiscVOpcode::SM3P0,
priority: 100,
num_operands: 2,
required_extension: Some("zksh"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "sm3p1 rd, rs1 — SM3 P1 permutation",
result_opcode: RiscVOpcode::SM3P1,
priority: 101,
num_operands: 2,
required_extension: Some("zksh"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "seed rd — poll entropy source for random seed",
result_opcode: RiscVOpcode::SEED,
priority: 100,
num_operands: 1,
required_extension: Some("zkr"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Call,
description: "zkt — data-independent execution timing constraint (applies to Zb* crypto instructions)",
result_opcode: RiscVOpcode::ANDN,
priority: 200,
num_operands: 0,
required_extension: Some("zkt"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Select,
description: "czero.eqz rd, rs1, rs2 — zero if rs2 == 0, else rs1",
result_opcode: RiscVOpcode::CZERO_EQZ,
priority: 100,
num_operands: 3,
required_extension: Some("zicond"),
is_rv64_only: false,
});
table.push(BitmanipPattern {
ir_opcode: Opcode::Select,
description: "czero.nez rd, rs1, rs2 — zero if rs2 != 0, else rs1",
result_opcode: RiscVOpcode::CZERO_NEZ,
priority: 101,
num_operands: 3,
required_extension: Some("zicond"),
is_rv64_only: false,
});
table
}
pub struct RiscVBitmanipIselEngine {
pub patterns: Vec<BitmanipPattern>,
pub enabled_extensions: std::collections::HashSet<String>,
pub is_rv64: bool,
}
impl RiscVBitmanipIselEngine {
pub fn new(enabled_extensions: &[&str], is_rv64: bool) -> Self {
let patterns = riscv_bitmanip_isel_table();
let ext_set: std::collections::HashSet<String> =
enabled_extensions.iter().map(|s| s.to_string()).collect();
Self {
patterns,
enabled_extensions: ext_set,
is_rv64,
}
}
pub fn lookup(
&self,
ir_opcode: Opcode,
) -> Option<(RiscVOpcode, &BitmanipPattern)> {
for pattern in &self.patterns {
if pattern.ir_opcode == ir_opcode {
if let Some(ext) = pattern.required_extension {
if !self.enabled_extensions.contains(ext) {
continue;
}
}
if pattern.is_rv64_only && !self.is_rv64 {
continue;
}
return Some((pattern.result_opcode, pattern));
}
}
None
}
pub fn patterns_for(&self, ir_opcode: Opcode) -> Vec<&BitmanipPattern> {
self.patterns
.iter()
.filter(|p| p.ir_opcode == ir_opcode)
.collect()
}
pub fn available_patterns(&self) -> Vec<&BitmanipPattern> {
self.patterns
.iter()
.filter(|p| {
let ext_ok = p
.required_extension
.map_or(true, |ext| self.enabled_extensions.contains(ext));
let rv64_ok = !p.is_rv64_only || self.is_rv64;
ext_ok && rv64_ok
})
.collect()
}
pub fn pattern_count(&self) -> usize {
self.patterns.len()
}
pub fn available_count(&self) -> usize {
self.available_patterns().len()
}
pub fn patterns_by_extension(&self) -> HashMap<&str, usize> {
let mut counts: HashMap<&str, usize> = HashMap::new();
for p in &self.patterns {
if let Some(ext) = p.required_extension {
*counts.entry(ext).or_insert(0) += 1;
}
}
counts
}
pub fn coverage_report(&self, all_opcodes: &[Opcode]) -> String {
let covered: std::collections::HashSet<_> =
self.patterns.iter().map(|p| p.ir_opcode as u32).collect();
let uncovered: Vec<_> = all_opcodes
.iter()
.filter(|op| !covered.contains(&(**op as u32)))
.collect();
format!(
"RISC-V Bitmanip ISel Coverage: {}/{} opcodes covered ({} uncovered: {:?})",
covered.len(),
all_opcodes.len(),
uncovered.len(),
uncovered
)
}
pub fn detailed_stats(&self) -> String {
let ext_counts = self.patterns_by_extension();
let available = self.available_count();
format!(
"RISC-V Bitmanip ISel: {} total patterns, {} available (RV64: {}), extensions: {:?}",
self.pattern_count(),
available,
self.is_rv64,
ext_counts
)
}
}
impl Default for RiscVBitmanipIselEngine {
fn default() -> Self {
Self::new(&[], false)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitmanip_table_not_empty() {
let table = riscv_bitmanip_isel_table();
assert!(!table.is_empty());
assert!(table.len() >= 50, "Expected at least 50 bitmanip patterns");
}
#[test]
fn test_bitmanip_sh1add() {
let engine = RiscVBitmanipIselEngine::new(&["zba"], false);
let result = engine.lookup(Opcode::Add);
assert!(result.is_some(), "SH1ADD should match Add with zba");
}
#[test]
fn test_bitmanip_andn() {
let engine = RiscVBitmanipIselEngine::new(&["zbb"], false);
let result = engine.lookup(Opcode::And);
assert!(result.is_some(), "ANDN should match And with zbb");
}
#[test]
fn test_bitmanip_clmul() {
let engine = RiscVBitmanipIselEngine::new(&["zbc"], false);
let result = engine.lookup(Opcode::Mul);
assert!(result.is_some(), "CLMUL should match Mul with zbc");
}
#[test]
fn test_bitmanip_bclr() {
let engine = RiscVBitmanipIselEngine::new(&["zbs"], false);
let result = engine.lookup(Opcode::And);
assert!(result.is_some(), "BCLR should match And with zbs");
}
#[test]
fn test_bitmanip_pack() {
let engine = RiscVBitmanipIselEngine::new(&["zbkb"], false);
let result = engine.lookup(Opcode::Call);
assert!(result.is_some(), "PACK should match Call with zbkb");
}
#[test]
fn test_bitmanip_aes32esi() {
let engine = RiscVBitmanipIselEngine::new(&["zkne"], false);
let result = engine.lookup(Opcode::Call);
assert!(result.is_some(), "AES32ESI should match Call with zkne");
}
#[test]
fn test_bitmanip_sha256sig0() {
let engine = RiscVBitmanipIselEngine::new(&["zknh"], false);
let result = engine.lookup(Opcode::Call);
assert!(result.is_some(), "SHA256SIG0 should match Call with zknh");
}
#[test]
fn test_bitmanip_sm4ed() {
let engine = RiscVBitmanipIselEngine::new(&["zksed"], false);
let result = engine.lookup(Opcode::Call);
assert!(result.is_some(), "SM4ED should match Call with zksed");
}
#[test]
fn test_bitmanip_sm3p0() {
let engine = RiscVBitmanipIselEngine::new(&["zksh"], false);
let result = engine.lookup(Opcode::Call);
assert!(result.is_some(), "SM3P0 should match Call with zksh");
}
#[test]
fn test_bitmanip_seed() {
let engine = RiscVBitmanipIselEngine::new(&["zkr"], false);
let result = engine.lookup(Opcode::Call);
assert!(result.is_some(), "SEED should match Call with zkr");
}
#[test]
fn test_bitmanip_czero_eqz() {
let engine = RiscVBitmanipIselEngine::new(&["zicond"], false);
let result = engine.lookup(Opcode::Select);
assert!(result.is_some(), "CZERO.EQZ should match Select with zicond");
}
#[test]
fn test_bitmanip_extension_guard() {
let engine = RiscVBitmanipIselEngine::new(&[], false);
let result = engine.lookup(Opcode::Add);
assert!(result.is_none(), "No bitmanip patterns without extensions");
}
#[test]
fn test_bitmanip_rv64_guard() {
let engine = RiscVBitmanipIselEngine::new(&["zknd"], false);
let result = engine.lookup(Opcode::Call);
assert!(result.is_some(), "AES32 should match even on RV32");
}
#[test]
fn test_bitmanip_rv64_enabled() {
let engine = RiscVBitmanipIselEngine::new(&["zknd"], true);
let result = engine.lookup(Opcode::Call);
assert!(result.is_some(), "Both AES32 and AES64 match on RV64");
}
#[test]
fn test_bitmanip_pattern_count() {
let engine = RiscVBitmanipIselEngine::new(
&["zba", "zbb", "zbc", "zbs", "zbkb", "zbkx", "zknd", "zkne", "zknh", "zksed", "zksh", "zkr", "zicond"],
true,
);
assert!(engine.pattern_count() >= 50);
}
#[test]
fn test_bitmanip_available_patterns() {
let engine = RiscVBitmanipIselEngine::new(&["zbb"], false);
let available = engine.available_patterns();
assert!(!available.is_empty());
for p in &available {
assert_eq!(p.required_extension, Some("zbb"));
}
}
#[test]
fn test_bitmanip_patterns_by_extension() {
let engine = RiscVBitmanipIselEngine::new(
&["zba", "zbb", "zbc"],
false,
);
let counts = engine.patterns_by_extension();
assert!(counts.contains_key("zba"));
assert!(counts.contains_key("zbb"));
assert!(counts.contains_key("zbc"));
}
#[test]
fn test_bitmanip_coverage_report() {
let engine = RiscVBitmanipIselEngine::new(&["zba", "zbb"], false);
let all_ops = &[
Opcode::Add,
Opcode::And,
Opcode::Or,
Opcode::Xor,
Opcode::Shl,
Opcode::LShr,
Opcode::ICmp,
Opcode::SExt,
Opcode::ZExt,
Opcode::Call,
Opcode::Mul,
Opcode::Select,
];
let report = engine.coverage_report(all_ops);
assert!(report.contains("RISC-V Bitmanip ISel Coverage"));
}
#[test]
fn test_bitmanip_detailed_stats() {
let engine = RiscVBitmanipIselEngine::new(&["zba", "zbb", "zbc", "zbs"], true);
let stats = engine.detailed_stats();
assert!(stats.contains("RISC-V Bitmanip ISel"));
assert!(stats.contains("patterns"));
}
#[test]
fn test_bitmanip_xperm4() {
let engine = RiscVBitmanipIselEngine::new(&["zbkx"], false);
let result = engine.lookup(Opcode::Call);
assert!(result.is_some(), "XPERM4 should match Call with zbkx");
}
}