1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! 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,
);
}