use std::path::{Path, PathBuf};
use vj_codegen::aot::{AotBackend, AotKernelHandle};
use vj_codegen::error::LoweringError;
use vj_codegen::frontend::compile_source_named;
use vj_codegen::module::{build_comb_sensitivity_index, discover_processes};
use vj_codegen::pipeline::compile_to_object;
use vj_codegen::process_lower::build_lowering_plan;
use vj_sim::netlist::{self, NetlistView};
use vj_sim::process::cfg::{build_cfg, contains_unschedulable_fork_join, lower_to_segments};
use vj_sim::process::liveness::compute_liveness;
use crate::run::{
RunError, elab_to_run_error, instance_body_of, loop_kind_of, signal_slots_of,
sim_lowering_to_run_error,
};
pub fn build_aot_so(
path: &Path,
out_obj: &Path,
runtime_rlib: &Path,
out_so: &Path,
) -> Result<AotKernelHandle, RunError> {
let src = std::fs::read_to_string(path).map_err(RunError::Io)?;
let src =
crate::randseq::expand(&src, &path.to_string_lossy()).map_err(RunError::Diagnostics)?;
let mut design =
compile_source_named(&src, &path.to_string_lossy()).map_err(RunError::Compile)?;
vj_codegen::let_order::check_let_definition_order(&design.syntax_trees())
.map_err(RunError::Diagnostics)?;
vj_sim::process::cfg::set_source_text(Some(&src));
vj_sim::process::cfg::set_source_name(Some(&path.display().to_string()));
vj_sim::process::cfg::set_source_session(Some(design.session().clone()));
let comb_sensitivity = build_comb_sensitivity_index(&design).map_err(RunError::Lowering)?;
let eval = design.eval_session();
let elaborated = netlist::elaborate(&eval).map_err(|e| elab_to_run_error(e, path))?;
if elaborated.top.len() != 1 {
return Err(RunError::Lowering(LoweringError::unsupported(
&format!(
"design has {} top-level module instances; build-aot supports exactly one",
elaborated.top.len()
),
path.display().to_string().as_str(),
)));
}
let registration_order = elaborated.registration_order();
let top_id = elaborated.top[0];
if registration_order.len() != 1 || registration_order[0] != top_id {
return Err(RunError::Lowering(LoweringError::unsupported(
"build-aot does not support sub-instance hierarchy (port-wiring is a run-time/ \
STATE concern, out of this AOT-emission task's own scope -- see this module's \
doc comment)",
path.display().to_string().as_str(),
)));
}
let body = instance_body_of(&elaborated, top_id, path)?;
let processes = discover_processes(&body, &comb_sensitivity).map_err(RunError::Lowering)?;
let signals = signal_slots_of(&body)?;
let mut plans = Vec::with_capacity(processes.len());
for proc in &processes {
let root = vj_sim::process::cfg::proc_body(&proc.sym).ok_or_else(|| {
RunError::Lowering(LoweringError::unsupported_at(
"procedural block has no body statement",
&proc.sym,
))
})?;
let cfg = build_cfg(&root, loop_kind_of(proc.kind))
.map_err(|e| sim_lowering_to_run_error(e, path))?;
if contains_unschedulable_fork_join(&cfg) {
return Err(RunError::Lowering(LoweringError::unsupported(
"fork/join has no scheduling semantics yet -- construction-only this wave",
path.display().to_string().as_str(),
)));
}
let frame = compute_liveness(&cfg).map_err(|e| sim_lowering_to_run_error(e, path))?;
let segments = lower_to_segments(&cfg, &frame);
let mut plan = build_lowering_plan(
format!("vj_proc_{}", plans.len()),
proc.sym,
segments,
frame,
signals.clone(),
);
plan.owning_instance = top_id;
plans.push(plan);
}
let (_names, _count) = compile_to_object(&plans, out_obj).map_err(RunError::Codegen)?;
AotBackend::for_host()
.map_err(RunError::Codegen)?
.link(&[out_obj.to_path_buf()], runtime_rlib, out_so)
.map_err(RunError::Codegen)
}
pub fn build_aot_so_default_obj(
path: &Path,
runtime_rlib: &Path,
out_so: &Path,
) -> Result<AotKernelHandle, RunError> {
let mut obj_path: PathBuf = out_so.to_path_buf();
obj_path.set_extension("o");
build_aot_so(path, &obj_path, runtime_rlib, out_so)
}