use crate::symid::SymbolUid;
use sv_lang::{DefinitionKind, Expression, Symbol};
use sv_lang_kinds::SymbolKind;
pub fn module_items<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
let mut out = Vec::new();
push_items(body, &mut out);
out
}
pub fn checker_instances<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
fn walk<'d>(scope: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
for m in scope.members() {
if let Some(blocks) = generate_children(&m) {
for b in blocks {
walk(&b, out);
}
} else if m.kind() == SymbolKind::CheckerInstance {
out.push(m);
walk(&m, out);
} else if let Some(elements) = checker_array_elements(&m) {
for e in elements {
out.push(e);
walk(&e, out);
}
} else if m.kind() == SymbolKind::StatementBlock {
walk(&m, out);
}
}
}
let mut out = Vec::new();
walk(body, &mut out);
out
}
#[derive(Clone, Copy)]
pub struct ProceduralChecker<'d> {
pub block: Symbol<'d>,
pub stmt: sv_lang::Statement<'d>,
pub instance: Symbol<'d>,
}
pub fn procedural_checkers<'d>(body: &Symbol<'d>) -> Vec<ProceduralChecker<'d>> {
if checker_instances(body).is_empty() {
return Vec::new();
}
let mut out = Vec::new();
for block in module_items(body) {
if block.kind() != SymbolKind::ProceduralBlock {
continue;
}
let Some(root) = crate::process::cfg::proc_body(&block) else {
continue;
};
let mut stack = vec![root.as_sem_node()];
while let Some(n) = stack.pop() {
if let Some(stmt) = n.as_statement() {
for instance in stmt.procedural_checker_instances() {
out.push(ProceduralChecker {
block,
stmt,
instance,
});
}
}
let mut kids = n.children();
kids.reverse();
stack.extend(kids);
}
}
out
}
pub fn checker_instance_body<'d>(inst: &Symbol<'d>) -> Option<Symbol<'d>> {
inst.members().next()?.parent()
}
#[derive(Clone, Copy)]
pub struct CheckerOutput<'d> {
pub formal: Symbol<'d>,
pub assign: Option<sv_lang::Expression<'d>>,
pub initial: Option<sv_lang::Expression<'d>>,
}
pub fn checker_outputs<'d>(inst: &Symbol<'d>) -> Vec<CheckerOutput<'d>> {
let formals = inst.members().filter(|m| {
matches!(
m.kind(),
SymbolKind::AssertionPort | SymbolKind::FormalArgument
)
});
let mut out = Vec::new();
for (formal, conn) in formals.zip(inst.checker_instance_connections()) {
if formal.kind() == SymbolKind::FormalArgument {
out.push(CheckerOutput {
formal,
assign: conn.actual().and_then(|a| a.as_expression()),
initial: conn.output_initial_expr(),
});
}
}
out
}
pub fn checker_output_of<'d>(formal: &Symbol<'d>) -> Option<CheckerOutput<'d>> {
if formal.kind() != SymbolKind::FormalArgument {
return None;
}
let inst = formal.parent()?.checker_instance_body_parent_instance()?;
checker_outputs(&inst)
.into_iter()
.find(|o| o.formal.uid() == formal.uid())
}
pub fn is_in_checker(sym: &Symbol<'_>) -> bool {
let mut cur = Some(*sym);
while let Some(s) = cur {
let kind = s.kind();
if kind == SymbolKind::CheckerInstanceBody {
return true;
}
if kind == SymbolKind::InstanceBody {
return false;
}
cur = s.parent();
}
false
}
pub fn is_in_program(sym: &Symbol<'_>) -> bool {
enclosing_instance_body(sym)
.and_then(|b| b.instance_body_definition())
.is_some_and(|d| d.definition_kind() == Some(DefinitionKind::Program))
}
pub fn enclosing_instance_body<'d>(sym: &Symbol<'d>) -> Option<Symbol<'d>> {
let mut cur = Some(*sym);
while let Some(s) = cur {
let kind = s.kind();
if kind == SymbolKind::InstanceBody {
return Some(s);
}
cur = if kind == SymbolKind::CheckerInstanceBody {
s.checker_instance_body_parent_instance()?.parent()
} else {
s.parent()
};
}
None
}
pub fn enclosing_package<'d>(sym: &Symbol<'d>) -> Option<Symbol<'d>> {
if enclosing_instance_body(sym).is_some() {
return None;
}
let mut cur = sym.parent();
while let Some(p) = cur {
if matches!(p.kind(), SymbolKind::Package | SymbolKind::CompilationUnit) {
return Some(p);
}
cur = p.parent();
}
None
}
pub fn generate_scopes<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
let mut out = Vec::new();
let mut stack = vec![*body];
while let Some(scope) = stack.pop() {
for m in scope.members() {
if let Some(blocks) = generate_children(&m) {
for b in blocks {
out.push(b);
stack.push(b);
}
} else if m.kind() == SymbolKind::CheckerInstance
|| m.kind() == SymbolKind::StatementBlock
{
stack.push(m);
} else if let Some(elements) = checker_array_elements(&m) {
stack.extend(elements);
}
}
}
out
}
pub fn checker_array_elements<'d>(m: &Symbol<'d>) -> Option<Vec<Symbol<'d>>> {
fn flatten<'d>(a: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) -> bool {
for e in a.instance_array_elements() {
if e.kind() == SymbolKind::CheckerInstance {
out.push(e);
} else if e.kind() != SymbolKind::InstanceArray || !flatten(&e, out) {
return false;
}
}
true
}
if m.kind() != SymbolKind::InstanceArray {
return None;
}
let mut out = Vec::new();
(flatten(m, &mut out) && !out.is_empty()).then_some(out)
}
fn generate_children<'d>(m: &Symbol<'d>) -> Option<Vec<Symbol<'d>>> {
let kind = m.kind();
if kind == SymbolKind::GenerateBlock {
return Some(if m.generate_block_is_uninstantiated() {
Vec::new()
} else {
vec![*m]
});
}
if kind == SymbolKind::GenerateBlockArray {
return Some(
m.members()
.filter(|e| {
e.kind() == SymbolKind::GenerateBlock && !e.generate_block_is_uninstantiated()
})
.collect(),
);
}
None
}
fn push_items<'d>(scope: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
for m in scope.members() {
match generate_children(&m) {
Some(blocks) => {
for b in blocks {
push_items(&b, out);
}
}
None if m.kind() == SymbolKind::CheckerInstance => push_items(&m, out),
None if checker_array_elements(&m).is_some() => {
for e in checker_array_elements(&m).unwrap_or_default() {
push_items(&e, out);
}
}
None if m.kind() == SymbolKind::AssertionPort => {}
None if m.kind() == SymbolKind::StatementBlock => {
out.push(m);
push_block_checkers(&m, out);
}
None => out.push(m),
}
}
}
fn push_block_checkers<'d>(block: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
for m in block.members() {
if m.kind() == SymbolKind::CheckerInstance {
push_items(&m, out);
} else if m.kind() == SymbolKind::StatementBlock {
push_block_checkers(&m, out);
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FreeVarKind {
Const,
Active,
Inactive,
}
pub fn free_var_kind(var: &Symbol<'_>) -> Option<FreeVarKind> {
if var.kind() != SymbolKind::Variable
|| !var
.variable_flags()
.contains(sv_lang::VariableFlags::CHECKER_FREE_VARIABLE)
{
return None;
}
if var.variable_flags().contains(sv_lang::VariableFlags::CONST) {
return Some(FreeVarKind::Const);
}
let assigned = var
.parent()
.and_then(|b| b.checker_instance_body_parent_instance())
.is_some_and(|inst| checker_assigned(&inst).contains(&var.uid()));
Some(if assigned {
FreeVarKind::Inactive
} else {
FreeVarKind::Active
})
}
fn checker_assigned(inst: &Symbol<'_>) -> std::collections::HashSet<crate::symid::SymbolId> {
let mut out = std::collections::HashSet::new();
let mut stack = Vec::new();
for m in module_items(inst) {
if m.kind() == SymbolKind::ProceduralBlock {
stack.extend(crate::process::cfg::proc_body(&m).map(|b| b.as_sem_node()));
} else if m.kind() == SymbolKind::ContinuousAssign {
stack.extend(m.continuous_assign_assignment().map(|e| e.as_sem_node()));
}
}
while let Some(n) = stack.pop() {
if let Some(e) = n.as_expression()
&& e.kind() == sv_lang::kinds::ExpressionKind::Assignment
&& let Some(lhs) = e.left()
{
let mut lv = vec![lhs.as_sem_node()];
while let Some(x) = lv.pop() {
if let Some(xe) = x.as_expression()
&& crate::symid::is_value_name(xe.kind())
&& let Some(s) = xe.referenced_symbol()
{
out.insert(s.uid());
}
lv.extend(x.children());
}
}
stack.extend(n.children());
}
out
}
pub fn free_var_seed(var: &Symbol<'_>) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for b in var.hierarchical_path().bytes() {
h = (h ^ u64::from(b)).wrapping_mul(0x0000_0100_0000_01b3);
}
h
}
pub fn free_var_groups<'d>(body: &Symbol<'d>) -> Vec<(Symbol<'d>, Vec<Symbol<'d>>)> {
checker_instances(body)
.into_iter()
.filter_map(|inst| {
let vars: Vec<Symbol<'d>> = inst
.members()
.filter(|m| free_var_kind(m) == Some(FreeVarKind::Active))
.collect();
(!vars.is_empty()).then_some((inst, vars))
})
.collect()
}
pub fn state_items<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
let mut out = module_items(body);
out.extend(static_procedural_vars(body));
out
}
pub fn static_procedural_vars<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
let is_static = |v: &Symbol<'_>| {
matches!(v.kind(), SymbolKind::Variable | SymbolKind::FormalArgument)
&& v.variable_lifetime() == sv_lang::VariableLifetime::Static
&& !is_array_formal(v)
};
let mut out = Vec::new();
let mut items = module_items(body);
items.extend(super::itf::extern_impls_in(body));
for m in items {
let kind = m.kind();
if kind == SymbolKind::Subroutine {
let result = m.subroutine_return_val_var().map(|r| r.uid());
for v in m.members() {
if is_static(&v) && Some(v.uid()) != result {
out.push(v);
}
}
}
if kind != SymbolKind::Subroutine && kind != SymbolKind::ProceduralBlock {
continue;
}
let Some(code) = crate::process::cfg::proc_body(&m) else {
continue;
};
let mut stack = vec![code.as_sem_node()];
while let Some(n) = stack.pop() {
if let Some(s) = n.as_statement() {
for block in crate::process::cfg::block_scopes(&s) {
out.extend(block.members().filter(|v| is_static(v)));
}
}
let mut kids = crate::process::cfg::live_children(&n);
kids.reverse();
stack.extend(kids);
}
}
out
}
fn is_array_formal(var: &Symbol<'_>) -> bool {
var.kind() == SymbolKind::FormalArgument
&& var
.value_type()
.is_some_and(|t| t.is_unpacked_array() && !crate::state::storage::is_collection(&t))
}
pub fn is_static_procedural_var(var: &Symbol<'_>) -> bool {
if !matches!(
var.kind(),
SymbolKind::Variable | SymbolKind::FormalArgument
) || var.variable_lifetime() != sv_lang::VariableLifetime::Static
|| is_array_formal(var)
{
return false;
}
let mut procedural = false;
let mut in_class = false;
let mut in_task = false;
let mut cur = var.parent();
while let Some(p) = cur {
let kind = p.kind();
if kind == SymbolKind::Subroutine {
if p.subroutine_return_val_var().map(|r| r.uid()) == Some(var.uid()) {
return false;
}
procedural = true;
in_task |= p.subroutine_kind() == Some(sv_lang::SubroutineKind::Task);
} else if kind == SymbolKind::StatementBlock || kind == SymbolKind::ProceduralBlock {
procedural = true;
} else if kind == SymbolKind::InstanceBody || kind == SymbolKind::CheckerInstanceBody {
return procedural && !in_class;
} else if kind == SymbolKind::ClassType {
in_class = true;
} else if kind == SymbolKind::Package || kind == SymbolKind::CompilationUnit {
return procedural && !in_class && in_task && var.kind() == SymbolKind::Variable;
}
cur = p.parent();
}
false
}
pub fn variable_initializer<'d>(var: &Symbol<'d>) -> Option<Expression<'d>> {
if let Some(init) = var.initializer() {
return Some(init);
}
if var.kind() != SymbolKind::Variable {
return None;
}
let body = var
.parent()
.filter(|p| p.kind() == SymbolKind::InstanceBody)?;
let uid = var.uid();
body.instance_body_ports()
.find(|p| p.port_internal_symbol().is_some_and(|i| i.uid() == uid))
.and_then(|p| p.port_initializer())
}