use std::path::{Path, PathBuf};
use libloading::{Library, Symbol};
use vj_abi::{ProcessFn, SimHandle};
use vj_codegen::aot::{DESIGN_IMAGE_LEN_SYMBOL, DESIGN_IMAGE_PTR_SYMBOL};
use vj_sim::image::DesignImage;
use crate::dpi;
#[derive(Debug)]
pub enum AotLoadError {
Open(String),
MissingSymbol {
symbol: &'static str,
reason: String,
},
AbiVersionMismatch {
found: u32,
expected: u32,
},
Dpi(String),
TimingOptionConflict(Vec<String>),
}
impl std::fmt::Display for AotLoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AotLoadError::Open(e) => write!(f, "failed to load AOT .so: {e}"),
AotLoadError::MissingSymbol { symbol, reason } => {
write!(f, "missing symbol `{symbol}`: {reason}")
}
AotLoadError::Dpi(e) => f.write_str(e),
AotLoadError::AbiVersionMismatch { found, expected } => write!(
f,
"ABI version mismatch: .so reports {found}, this loader expects {expected}"
),
AotLoadError::TimingOptionConflict(msgs) => {
write!(
f,
"run-aot timing option conflict(s) with the built library:"
)?;
for m in msgs {
write!(f, "\n {m}")?;
}
Ok(())
}
}
}
}
impl std::error::Error for AotLoadError {}
pub struct LoadedKernel {
lib: Library,
pub process_count: u32,
}
impl LoadedKernel {
pub fn get_process_fn(&self, mangled: &str) -> Option<ProcessFn> {
unsafe {
self.lib
.get::<ProcessFn>(mangled.as_bytes())
.ok()
.map(|s| *s)
}
}
}
pub fn run_aot(path: &Path, args: &[String]) -> Result<i32, AotLoadError> {
use std::ffi::{CString, c_char};
let mut libs: Vec<PathBuf> = Vec::new();
let mut c_sources: Vec<PathBuf> = Vec::new();
let mut run_args: Vec<String> = Vec::new();
let mut vpi_libs: Vec<PathBuf> = Vec::new();
let mut timing = crate::timing_options::TimingOptions::default();
let mut it = args.iter();
while let Some(a) = it.next() {
if a == "--sv-lib" || a == "-sv_lib" {
match it.next() {
Some(lib) => libs.push(dpi::sv_lib_path(lib)),
None => return Err(AotLoadError::Dpi(format!("{a} needs a library"))),
}
} else if a == "--vpi" || a == "-vpi" {
match it.next() {
Some(lib) => vpi_libs.push(vpi_library(lib)?),
None => return Err(AotLoadError::Dpi(format!("{a} needs a library"))),
}
} else if let Some(list) = a.strip_prefix("+vpi=") {
run_args.push(a.clone());
for lib in list.split(',').filter(|l| !l.is_empty()) {
vpi_libs.push(vpi_library(lib)?);
}
} else if dpi::is_c_source(Path::new(a)) {
c_sources.push(PathBuf::from(a));
} else if a.ends_with(".so") {
libs.push(PathBuf::from(a));
} else {
match timing.parse_arg(a) {
Ok(true) => {}
Ok(false) => run_args.push(a.clone()),
Err(e) => return Err(AotLoadError::TimingOptionConflict(vec![e])),
}
}
}
if !c_sources.is_empty() {
libs.push(dpi::compile_sources(&c_sources).map_err(AotLoadError::Dpi)?);
}
for lib in &vpi_libs {
run_args.push("--vpi".to_string());
run_args.push(lib.display().to_string());
}
let args = &run_args[..];
let lib = unsafe { Library::new(path) }
.map_err(|e| AotLoadError::Open(format!("{}: {e}", path.display())))?;
let abi_version: u32 = unsafe {
let sym: Symbol<extern "C" fn() -> u32> = lib
.get(vj_abi::version::SIM_ABI_VERSION_SYMBOL.as_bytes())
.map_err(|e| AotLoadError::MissingSymbol {
symbol: "sim_abi_version",
reason: e.to_string(),
})?;
sym()
};
if abi_version != vj_abi::VJ_ABI_VERSION {
return Err(AotLoadError::AbiVersionMismatch {
found: abi_version,
expected: vj_abi::VJ_ABI_VERSION,
});
}
let image = unsafe {
let ptr: Symbol<extern "C" fn() -> *const u8> = lib
.get(DESIGN_IMAGE_PTR_SYMBOL.as_bytes())
.map_err(|e| AotLoadError::MissingSymbol {
symbol: "vj_design_image_ptr",
reason: e.to_string(),
})?;
let len: Symbol<extern "C" fn() -> u64> = lib
.get(DESIGN_IMAGE_LEN_SYMBOL.as_bytes())
.map_err(|e| AotLoadError::MissingSymbol {
symbol: "vj_design_image_len",
reason: e.to_string(),
})?;
DesignImage::decode(std::slice::from_raw_parts(ptr(), len() as usize))
.map_err(|e| AotLoadError::Dpi(format!("the design image: {e}")))?
};
let conflicts = timing.conflicts_with(&image.timing.options);
if !conflicts.is_empty() {
return Err(AotLoadError::TimingOptionConflict(conflicts));
}
let export_names: Vec<String> = image.dpi.exports.iter().map(|(n, _)| n.clone()).collect();
dpi::load_export_trampolines(&export_names).map_err(AotLoadError::Dpi)?;
if !libs.is_empty() {
dpi::load_svdpi_support().map_err(AotLoadError::Dpi)?;
}
dpi::load_libraries(&libs).map_err(AotLoadError::Dpi)?;
unsafe {
let set: Symbol<unsafe extern "C" fn(*const dpi::AotDpiHost)> = lib
.get(b"vj_aot_set_dpi_host")
.map_err(|e| AotLoadError::MissingSymbol {
symbol: "vj_aot_set_dpi_host",
reason: e.to_string(),
})?;
set(&dpi::AOT_DPI_HOST);
}
if !vpi_libs.is_empty() {
unsafe {
let table: Symbol<extern "C" fn() -> *const crate::vpi::forward::ApiTable> = lib
.get(b"vj_aot_vpi_table")
.map_err(|e| AotLoadError::MissingSymbol {
symbol: "vj_aot_vpi_table",
reason: e.to_string(),
})?;
crate::vpi::forward::install(table());
}
}
let argv: Vec<CString> = std::iter::once(path.display().to_string())
.chain(args.iter().cloned())
.map(|a| CString::new(a.replace('\0', "")).expect("NULs were removed"))
.collect();
let ptrs: Vec<*const c_char> = argv.iter().map(|a| a.as_ptr()).collect();
let code = unsafe {
let main: Symbol<unsafe extern "C" fn(i32, *const *const c_char) -> i32> = lib
.get(b"vj_aot_main")
.map_err(|e| AotLoadError::MissingSymbol {
symbol: "vj_aot_main",
reason: e.to_string(),
})?;
main(ptrs.len() as i32, ptrs.as_ptr())
};
std::mem::forget(lib);
Ok(code)
}
fn vpi_library(arg: &str) -> Result<PathBuf, AotLoadError> {
let path = Path::new(arg);
if dpi::is_c_source(path) {
dpi::compile_sources(&[path.to_path_buf()]).map_err(AotLoadError::Dpi)
} else {
Ok(dpi::sv_lib_path(arg))
}
}
pub fn load_aot_kernel(path: &Path) -> Result<LoadedKernel, AotLoadError> {
let lib = unsafe { Library::new(path) }
.map_err(|e| AotLoadError::Open(format!("{}: {e}", path.display())))?;
let abi_version: u32 = unsafe {
let sym: Symbol<extern "C" fn() -> u32> = lib
.get(vj_abi::version::SIM_ABI_VERSION_SYMBOL.as_bytes())
.map_err(|e| AotLoadError::MissingSymbol {
symbol: "sim_abi_version",
reason: e.to_string(),
})?;
sym()
};
if abi_version != vj_abi::VJ_ABI_VERSION {
return Err(AotLoadError::AbiVersionMismatch {
found: abi_version,
expected: vj_abi::VJ_ABI_VERSION,
});
}
let process_count: u32 = unsafe {
let sym: Symbol<extern "C" fn(SimHandle) -> u32> = lib
.get(vj_abi::entry::SIM_PROCESS_COUNT_SYMBOL.as_bytes())
.map_err(|e| AotLoadError::MissingSymbol {
symbol: "sim_process_count",
reason: e.to_string(),
})?;
sym(SimHandle(std::ptr::null_mut()))
};
Ok(LoadedKernel { lib, process_count })
}