use super::riscv_instr_info::RiscVOpcode;
use crate::opcode::Opcode;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct RiscVHypervisorPattern {
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_privileged: bool,
pub csr_address: Option<u16>,
}
pub struct RiscVHypervisorIselTable {
pub patterns: Vec<RiscVHypervisorPattern>,
pub total_patterns: usize,
pub hfence_patterns: usize,
pub load_store_patterns: usize,
pub csr_patterns: usize,
pub page_table_patterns: usize,
}
pub fn riscv_hypervisor_isel_table() -> Vec<RiscVHypervisorPattern> {
let mut table = Vec::new();
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Fence,
description: "hfence.vvma rs1, rs2 — hypervisor fence virtual memory by virtual address",
result_opcode: RiscVOpcode::HFENCE_VVMA,
priority: 100,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Fence,
description: "hfence.gvma rs1, rs2 — hypervisor fence guest virtual memory",
result_opcode: RiscVOpcode::HFENCE_GVMA,
priority: 101,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Load,
description: "hlv.b rd, (rs1) — hypervisor load byte using VS-stage translation",
result_opcode: RiscVOpcode::HLV_B,
priority: 200,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Load,
description: "hlv.h rd, (rs1) — hypervisor load halfword using VS-stage translation",
result_opcode: RiscVOpcode::HLV_H,
priority: 201,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Load,
description: "hlv.w rd, (rs1) — hypervisor load word using VS-stage translation",
result_opcode: RiscVOpcode::HLV_W,
priority: 202,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Load,
description: "hlv.d rd, (rs1) — hypervisor load doubleword using VS-stage translation",
result_opcode: RiscVOpcode::HLV_D,
priority: 203,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Load,
description: "hlvx.hu rd, (rs1) — hypervisor load execute halfword unsigned",
result_opcode: RiscVOpcode::HLVX_HU,
priority: 210,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Load,
description: "hlvx.wu rd, (rs1) — hypervisor load execute word unsigned",
result_opcode: RiscVOpcode::HLVX_WU,
priority: 211,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Store,
description: "hsv.b rs2, (rs1) — hypervisor store byte using VS-stage translation",
result_opcode: RiscVOpcode::HSV_B,
priority: 200,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Store,
description: "hsv.h rs2, (rs1) — hypervisor store halfword using VS-stage translation",
result_opcode: RiscVOpcode::HSV_H,
priority: 201,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Store,
description: "hsv.w rs2, (rs1) — hypervisor store word using VS-stage translation",
result_opcode: RiscVOpcode::HSV_W,
priority: 202,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Store,
description: "hsv.d rs2, (rs1) — hypervisor store doubleword using VS-stage translation",
result_opcode: RiscVOpcode::HSV_D,
priority: 203,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, vsstatus, rs1 — atomic read/write virtual supervisor status",
result_opcode: RiscVOpcode::CSRRW,
priority: 300,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x200),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, vsie, rs1 — atomic read/write virtual supervisor interrupt enable",
result_opcode: RiscVOpcode::CSRRW,
priority: 301,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x204),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, vstvec, rs1 — atomic read/write virtual supervisor trap vector",
result_opcode: RiscVOpcode::CSRRW,
priority: 302,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x205),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, vsepc, rs1 — atomic read/write virtual supervisor EPC",
result_opcode: RiscVOpcode::CSRRW,
priority: 303,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x241),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, vscause, rs1 — atomic read/write virtual supervisor cause",
result_opcode: RiscVOpcode::CSRRW,
priority: 304,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x242),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, vstval, rs1 — atomic read/write virtual supervisor trap value",
result_opcode: RiscVOpcode::CSRRW,
priority: 305,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x243),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, vsip, rs1 — atomic read/write virtual supervisor IP",
result_opcode: RiscVOpcode::CSRRW,
priority: 306,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x244),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, vsatp, rs1 — atomic read/write virtual supervisor ATP",
result_opcode: RiscVOpcode::CSRRW,
priority: 307,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x280),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, vsscratch, rs1 — atomic read/write virtual supervisor scratch",
result_opcode: RiscVOpcode::CSRRW,
priority: 308,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x240),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description:
"csrrw rd, vstimecmp, rs1 — atomic read/write virtual supervisor timer compare",
result_opcode: RiscVOpcode::CSRRW,
priority: 309,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x24D),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, hvip, rs1 — read/write hypervisor virtual interrupt pending",
result_opcode: RiscVOpcode::CSRRW,
priority: 400,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x645),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, hvien, rs1 — read/write hypervisor virtual interrupt enable",
result_opcode: RiscVOpcode::CSRRW,
priority: 401,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x641),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, hstatus, rs1 — read/write hypervisor status register",
result_opcode: RiscVOpcode::CSRRW,
priority: 402,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x600),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, hedeleg, rs1 — read/write hypervisor exception delegation",
result_opcode: RiscVOpcode::CSRRW,
priority: 403,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x602),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, hideleg, rs1 — read/write hypervisor interrupt delegation",
result_opcode: RiscVOpcode::CSRRW,
priority: 404,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x603),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, hie, rs1 — read/write hypervisor interrupt enable",
result_opcode: RiscVOpcode::CSRRW,
priority: 405,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x604),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, htimedelta, rs1 — read/write hypervisor timer delta",
result_opcode: RiscVOpcode::CSRRW,
priority: 406,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x605),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, hcounteren, rs1 — read/write hypervisor counter enable",
result_opcode: RiscVOpcode::CSRRW,
priority: 407,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x606),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, htval, rs1 — read/write hypervisor trap value",
result_opcode: RiscVOpcode::CSRRW,
priority: 408,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x643),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, htinst, rs1 — read/write hypervisor trap instruction",
result_opcode: RiscVOpcode::CSRRW,
priority: 409,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x64A),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "csrrw rd, hgatp, rs1 — read/write hypervisor guest address translation",
result_opcode: RiscVOpcode::CSRRW,
priority: 410,
num_operands: 3,
required_extension: Some("h"),
is_privileged: true,
csr_address: Some(0x680),
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Call,
description: "hlv.d rd, (rs1) — hypervisor load virtual machine ID",
result_opcode: RiscVOpcode::HLV_D,
priority: 420,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Fence,
description: "sfence.vma — supervisor fence virtual memory (with Svnapot N=1 hints)",
result_opcode: RiscVOpcode::FENCE,
priority: 500,
num_operands: 2,
required_extension: Some("svnapot"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Fence,
description: "sfence.vma — supervisor fence with non-cacheable / I/O memory types",
result_opcode: RiscVOpcode::FENCE,
priority: 510,
num_operands: 2,
required_extension: Some("svpbmt"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Fence,
description: "sinval.vma rs1, rs2 — supervisor invalidate virtual memory address",
result_opcode: RiscVOpcode::HFENCE_VVMA, priority: 520,
num_operands: 2,
required_extension: Some("svinval"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Fence,
description: "sfence.w.inval — fence on writes before sinval.vma",
result_opcode: RiscVOpcode::FENCE,
priority: 521,
num_operands: 0,
required_extension: Some("svinval"),
is_privileged: true,
csr_address: None,
});
table.push(RiscVHypervisorPattern {
ir_opcode: Opcode::Fence,
description: "sfence.inval.ir — fence between sinval.vma and subsequent implicit refs",
result_opcode: RiscVOpcode::FENCE,
priority: 522,
num_operands: 0,
required_extension: Some("svinval"),
is_privileged: true,
csr_address: None,
});
table
}
pub fn build_riscv_hypervisor_isel_table() -> RiscVHypervisorIselTable {
let patterns = riscv_hypervisor_isel_table();
let total = patterns.len();
let hfence = patterns
.iter()
.filter(|p| {
matches!(
p.result_opcode,
RiscVOpcode::HFENCE_VVMA | RiscVOpcode::HFENCE_GVMA
)
})
.count();
let loads_stores = patterns
.iter()
.filter(|p| {
matches!(
p.result_opcode,
RiscVOpcode::HLV_B
| RiscVOpcode::HLV_H
| RiscVOpcode::HLV_W
| RiscVOpcode::HLV_D
| RiscVOpcode::HLVX_HU
| RiscVOpcode::HLVX_WU
| RiscVOpcode::HSV_B
| RiscVOpcode::HSV_H
| RiscVOpcode::HSV_W
| RiscVOpcode::HSV_D
)
})
.count();
let csr = patterns.iter().filter(|p| p.csr_address.is_some()).count();
let pt = patterns
.iter()
.filter(|p| {
matches!(
p.required_extension,
Some("svnapot") | Some("svpbmt") | Some("svinval")
)
})
.count();
RiscVHypervisorIselTable {
patterns,
total_patterns: total,
hfence_patterns: hfence,
load_store_patterns: loads_stores,
csr_patterns: csr,
page_table_patterns: pt,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RiscVHypervisorFeatures {
pub has_h: bool,
pub has_svnapot: bool,
pub has_svpbmt: bool,
pub has_svinval: bool,
pub has_vs_stage: bool,
pub has_g_stage: bool,
}
impl Default for RiscVHypervisorFeatures {
fn default() -> Self {
Self {
has_h: false,
has_svnapot: false,
has_svpbmt: false,
has_svinval: false,
has_vs_stage: false,
has_g_stage: false,
}
}
}
impl RiscVHypervisorFeatures {
pub fn full_hypervisor() -> Self {
Self {
has_h: true,
has_svnapot: true,
has_svpbmt: true,
has_svinval: true,
has_vs_stage: true,
has_g_stage: true,
}
}
pub fn minimal_hypervisor() -> Self {
Self {
has_h: true,
has_vs_stage: true,
has_g_stage: true,
..Default::default()
}
}
pub fn has_extension(&self, ext: &str) -> bool {
match ext {
"h" => self.has_h,
"svnapot" => self.has_svnapot,
"svpbmt" => self.has_svpbmt,
"svinval" => self.has_svinval,
_ => false,
}
}
}
pub struct RiscVHypervisorIselEngine {
pub table: RiscVHypervisorIselTable,
pub features: RiscVHypervisorFeatures,
}
impl RiscVHypervisorIselEngine {
pub fn new(features: RiscVHypervisorFeatures) -> Self {
let table = build_riscv_hypervisor_isel_table();
Self { table, features }
}
pub fn is_pattern_applicable(&self, pattern: &RiscVHypervisorPattern) -> bool {
if pattern.is_privileged && !self.features.has_h {
return false;
}
if let Some(ext) = pattern.required_extension {
if !self.features.has_extension(ext) {
return false;
}
}
true
}
pub fn select_pattern(&self, ir_opcodes: &[Opcode]) -> Option<&RiscVHypervisorPattern> {
let mut best: Option<&RiscVHypervisorPattern> = None;
for p in &self.table.patterns {
if ir_opcodes.contains(&p.ir_opcode) && self.is_pattern_applicable(p) {
match best {
None => best = Some(p),
Some(b) if p.priority < b.priority => best = Some(p),
_ => {}
}
}
}
best
}
pub fn stats(&self) -> String {
format!(
"RISC-V Hypervisor ISel: {} patterns (HFENCE: {}, load/store: {}, CSR: {}, page table: {})",
self.table.total_patterns,
self.table.hfence_patterns,
self.table.load_store_patterns,
self.table.csr_patterns,
self.table.page_table_patterns,
)
}
}
pub mod hypervisor_csrs {
pub const HSTATUS: u16 = 0x600;
pub const HEDELEG: u16 = 0x602;
pub const HIDELEG: u16 = 0x603;
pub const HIE: u16 = 0x604;
pub const HTIMEDELTA: u16 = 0x605;
pub const HCOUNTEREN: u16 = 0x606;
pub const HGEIP: u16 = 0x607;
pub const HGEIE: u16 = 0x608;
pub const HVIP: u16 = 0x645;
pub const HVIEN: u16 = 0x641;
pub const HTVAL: u16 = 0x643;
pub const HTINST: u16 = 0x64A;
pub const HGATP: u16 = 0x680;
pub const VSSTATUS: u16 = 0x200;
pub const VSIE: u16 = 0x204;
pub const VSTVEC: u16 = 0x205;
pub const VSSCRATCH: u16 = 0x240;
pub const VSEPC: u16 = 0x241;
pub const VSCAUSE: u16 = 0x242;
pub const VSTVAL: u16 = 0x243;
pub const VSIP: u16 = 0x244;
pub const VSTIMECMP: u16 = 0x24D;
pub const VSATP: u16 = 0x280;
pub fn csr_name(addr: u16) -> &'static str {
match addr {
HSTATUS => "hstatus",
HEDELEG => "hedeleg",
HIDELEG => "hideleg",
HIE => "hie",
HTIMEDELTA => "htimedelta",
HCOUNTEREN => "hcounteren",
HGEIP => "hgeip",
HGEIE => "hgeie",
HVIP => "hvip",
HVIEN => "hvien",
HTVAL => "htval",
HTINST => "htinst",
HGATP => "hgatp",
VSSTATUS => "vsstatus",
VSIE => "vsie",
VSTVEC => "vstvec",
VSSCRATCH => "vsscratch",
VSEPC => "vsepc",
VSCAUSE => "vscause",
VSTVAL => "vstval",
VSIP => "vsip",
VSTIMECMP => "vstimecmp",
VSATP => "vsatp",
_ => "unknown",
}
}
}
pub const HV_CSR_MIN: u16 = 0x200;
pub const HV_CSR_MAX: u16 = 0x680;
pub fn is_hypervisor_csr(addr: u16) -> bool {
(addr >= 0x200 && addr <= 0x2FF) || (addr >= 0x600 && addr <= 0x6FF)
}
pub fn is_vs_csr(addr: u16) -> bool {
addr >= 0x200 && addr <= 0x2FF
}
pub fn is_hs_csr(addr: u16) -> bool {
addr >= 0x600 && addr <= 0x6FF
}
pub const MAX_HV_CSR_COUNT: usize = 22;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hypervisor_isel_table_builds() {
let table = build_riscv_hypervisor_isel_table();
assert!(table.total_patterns > 20);
assert!(table.hfence_patterns > 0);
assert!(table.load_store_patterns > 0);
assert!(table.csr_patterns > 0);
assert!(table.page_table_patterns > 0);
}
#[test]
fn test_hypervisor_features_default() {
let f = RiscVHypervisorFeatures::default();
assert!(!f.has_h);
assert!(!f.has_extension("h"));
assert!(!f.has_extension("svnapot"));
}
#[test]
fn test_hypervisor_features_full() {
let f = RiscVHypervisorFeatures::full_hypervisor();
assert!(f.has_h);
assert!(f.has_extension("h"));
assert!(f.has_extension("svnapot"));
assert!(f.has_extension("svpbmt"));
assert!(f.has_extension("svinval"));
assert!(f.has_vs_stage);
assert!(f.has_g_stage);
}
#[test]
fn test_hypervisor_features_minimal() {
let f = RiscVHypervisorFeatures::minimal_hypervisor();
assert!(f.has_h);
assert!(f.has_vs_stage);
assert!(!f.has_svnapot);
}
#[test]
fn test_hypervisor_isel_engine_pattern_applicable() {
let engine = RiscVHypervisorIselEngine::new(RiscVHypervisorFeatures::full_hypervisor());
let pat = RiscVHypervisorPattern {
ir_opcode: Opcode::Load,
description: "test",
result_opcode: RiscVOpcode::HLV_B,
priority: 100,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
};
assert!(engine.is_pattern_applicable(&pat));
}
#[test]
fn test_hypervisor_isel_engine_pattern_disabled() {
let engine = RiscVHypervisorIselEngine::new(RiscVHypervisorFeatures::default());
let pat = RiscVHypervisorPattern {
ir_opcode: Opcode::Load,
description: "test",
result_opcode: RiscVOpcode::HLV_B,
priority: 100,
num_operands: 2,
required_extension: Some("h"),
is_privileged: true,
csr_address: None,
};
assert!(!engine.is_pattern_applicable(&pat));
}
#[test]
fn test_csr_name_lookup() {
assert_eq!(hypervisor_csrs::csr_name(0x600), "hstatus");
assert_eq!(hypervisor_csrs::csr_name(0x200), "vsstatus");
assert_eq!(hypervisor_csrs::csr_name(0x280), "vsatp");
assert_eq!(hypervisor_csrs::csr_name(0x680), "hgatp");
assert_eq!(hypervisor_csrs::csr_name(0x999), "unknown");
}
#[test]
fn test_hypervisor_all_patterns() {
let table = build_riscv_hypervisor_isel_table();
for p in &table.patterns {
assert!(p.num_operands > 0);
assert!(p.priority > 0);
}
}
#[test]
fn test_hypervisor_select_priority() {
let engine = RiscVHypervisorIselEngine::new(RiscVHypervisorFeatures::full_hypervisor());
let pat = engine.select_pattern(&[Opcode::Fence]);
assert!(pat.is_some());
let pat_load = engine.select_pattern(&[Opcode::Load]);
assert!(pat_load.is_some());
}
#[test]
fn test_hypervisor_csr_addresses() {
let engine = RiscVHypervisorIselEngine::new(RiscVHypervisorFeatures::full_hypervisor());
let csr_pats: Vec<_> = engine
.table
.patterns
.iter()
.filter(|p| p.csr_address.is_some())
.collect();
assert!(!csr_pats.is_empty());
let mut addrs = std::collections::HashSet::new();
for p in &csr_pats {
assert!(addrs.insert(p.csr_address.unwrap()));
}
}
#[test]
fn test_hypervisor_page_table_extensions() {
let features = RiscVHypervisorFeatures::full_hypervisor();
let engine = RiscVHypervisorIselEngine::new(features);
let pt_pats: Vec<_> = engine
.table
.patterns
.iter()
.filter(|p| {
matches!(
p.required_extension,
Some("svnapot") | Some("svpbmt") | Some("svinval")
)
})
.collect();
assert!(pt_pats.len() >= 3);
}
#[test]
fn test_hypervisor_hfence_coverage() {
let table = build_riscv_hypervisor_isel_table();
let hfence_count = table
.patterns
.iter()
.filter(|p| {
matches!(
p.result_opcode,
RiscVOpcode::HFENCE_VVMA | RiscVOpcode::HFENCE_GVMA
)
})
.count();
assert!(hfence_count >= 2);
}
#[test]
fn test_hypervisor_csr_address_uniqueness() {
let table = build_riscv_hypervisor_isel_table();
let mut addrs = std::collections::HashMap::new();
for p in &table.patterns {
if let Some(addr) = p.csr_address {
if let Some(prev) = addrs.insert(addr, p.description) {
panic!(
"Duplicate CSR 0x{:03X}: '{}' vs '{}'",
addr, prev, p.description
);
}
}
}
}
#[test]
fn test_hypervisor_load_store_opcode_coverage() {
let table = build_riscv_hypervisor_isel_table();
for p in &table.patterns {
if p.ir_opcode == Opcode::Load && p.required_extension == Some("h") {
assert!(matches!(
p.result_opcode,
RiscVOpcode::HLV_B
| RiscVOpcode::HLV_H
| RiscVOpcode::HLV_W
| RiscVOpcode::HLV_D
| RiscVOpcode::HLVX_HU
| RiscVOpcode::HLVX_WU
));
}
}
}
#[test]
fn test_hypervisor_csr_name_exhaustive() {
let csrs = [
hypervisor_csrs::HSTATUS,
hypervisor_csrs::HEDELEG,
hypervisor_csrs::HIDELEG,
hypervisor_csrs::HIE,
hypervisor_csrs::HTIMEDELTA,
hypervisor_csrs::HCOUNTEREN,
hypervisor_csrs::HVIP,
hypervisor_csrs::HVIEN,
hypervisor_csrs::HTVAL,
hypervisor_csrs::HTINST,
hypervisor_csrs::HGATP,
hypervisor_csrs::VSSTATUS,
hypervisor_csrs::VSIE,
hypervisor_csrs::VSTVEC,
hypervisor_csrs::VSSCRATCH,
hypervisor_csrs::VSEPC,
hypervisor_csrs::VSCAUSE,
hypervisor_csrs::VSTVAL,
hypervisor_csrs::VSIP,
hypervisor_csrs::VSTIMECMP,
hypervisor_csrs::VSATP,
];
for addr in &csrs {
let name = hypervisor_csrs::csr_name(*addr);
assert!(!name.is_empty());
assert_ne!(name, "unknown");
}
}
}