vj-codegen 0.1.0

LLVM code generator of the vj SystemVerilog simulator.
//! Statement-coverage probes (LRM 40.2.1 a): with `--coverage`, every
//! statement of the kinds Table 38-6 lists (the VPI `cbStmt` set, minus the
//! `begin`/`end` wrappers) calls `sv_rt_cov(StmtHit, site, &SimHandle)` as it
//! begins executing. Statement sites are numbered per (module definition,
//! source statement), so instances of one definition (and parameterisations of
//! it) share their sites; the runtime attributes a hit to the instance whose
//! `SimHandle` the code runs with ([`sv_sim_runtime::codecov::stmt_hit`]).
//!
//! With coverage off nothing is emitted: the entry points test one
//! thread-local flag at code-generation time.
//!
//! Not counted: statements of a subroutine that touches no module state (it
//! has no instance handle to attribute to) and statements of class methods.

use std::cell::{Cell, RefCell};
use std::collections::HashMap;

use sv_lang::Statement;
use sv_lang::kinds::{StatementKind, SymbolKind};
use sv_sim_runtime::covergroup_rt::CovOp;
use sv_sim_sim::image::StmtSiteImage;

use crate::expr::LowerCtx;
use crate::vpi_dyn::{StmtKey, key_of};

/// Site number -> defining module name, and the reverse.
type SiteTable = (Vec<StmtSiteImage>, HashMap<(String, StmtKey), u32>);

thread_local! {
    static ENABLED: Cell<bool> = const { Cell::new(false) };
    static SITES: RefCell<SiteTable> = RefCell::new((Vec::new(), HashMap::new()));
}

/// Turns the probes on or off for the design about to be lowered, and forgets
/// the sites of any earlier one.
pub fn set_enabled(on: bool) {
    ENABLED.with(|e| e.set(on));
    SITES.with(|s| *s.borrow_mut() = (Vec::new(), HashMap::new()));
}

/// Whether probes are being emitted.
#[inline]
pub fn enabled() -> bool {
    ENABLED.with(Cell::get)
}

/// Each statement site probed since [`set_enabled`] (defining module and
/// source identity), by site number.
pub fn take_sites() -> Vec<StmtSiteImage> {
    SITES.with(|s| std::mem::take(&mut s.borrow_mut().0))
}

/// The name of the module definition `sym` is declared in.
fn module_def(sym: &sv_lang::Symbol<'_>) -> Option<String> {
    let mut scope = Some(*sym);
    while let Some(s) = scope {
        if s.kind() == SymbolKind::InstanceBody {
            return s.instance_body_definition().map(|d| d.name().to_string());
        }
        scope = s.parent();
    }
    None
}

/// The probe of statement `st`, if coverage is on and `st` is a counted kind.
pub(crate) fn emit_probe(cx: &LowerCtx<'_, '_, '_>, st: &Statement<'_>) {
    if !enabled()
        || matches!(
            st.kind(),
            StatementKind::Block | StatementKind::List | StatementKind::Empty
        )
        || crate::stmt_shared::current_block_terminated(cx)
    {
        return;
    }
    let (Some(acc), Some(def)) = (cx.mem_accessor.as_ref(), module_def(&cx.fn_sym)) else {
        return;
    };
    let sk = key_of(st);
    let key = (def.clone(), sk);
    let site = SITES.with(|s| {
        let mut s = s.borrow_mut();
        if let Some(&i) = s.1.get(&key) {
            return i;
        }
        s.0.push(StmtSiteImage {
            def,
            buffer: sk.buffer,
            start: sk.start,
            end: sk.end,
            kind: sk.kind,
            nest: sk.nest,
        });
        let i = (s.0.len() - 1) as u32;
        s.1.insert(key, i);
        i
    });
    let i64t = cx.ctx.i64_type();
    let handle = cx.b.build_ptr_to_int(acc.handle, i64t, "cov_inst").unwrap();
    let null = cx
        .ctx
        .ptr_type(inkwell::AddressSpace::default())
        .const_null();
    crate::covergroup::rt_cov(
        cx,
        CovOp::StmtHit,
        i64t.const_int(u64::from(site), false),
        handle,
        null,
        0,
    );
}