1use crate::symid::SymbolUid;
12use sv_lang::{DefinitionKind, Expression, Symbol};
13use sv_lang_kinds::SymbolKind;
14
15pub fn module_items<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
19 let mut out = Vec::new();
20 push_items(body, &mut out);
21 out
22}
23
24pub fn checker_instances<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
28 fn walk<'d>(scope: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
29 for m in scope.members() {
30 if let Some(blocks) = generate_children(&m) {
31 for b in blocks {
32 walk(&b, out);
33 }
34 } else if m.kind() == SymbolKind::CheckerInstance {
35 out.push(m);
36 walk(&m, out);
37 } else if let Some(elements) = checker_array_elements(&m) {
38 for e in elements {
39 out.push(e);
40 walk(&e, out);
41 }
42 } else if m.kind() == SymbolKind::StatementBlock {
43 walk(&m, out);
46 }
47 }
48 }
49 let mut out = Vec::new();
50 walk(body, &mut out);
51 out
52}
53
54#[derive(Clone, Copy)]
57pub struct ProceduralChecker<'d> {
58 pub block: Symbol<'d>,
60 pub stmt: sv_lang::Statement<'d>,
62 pub instance: Symbol<'d>,
64}
65
66pub fn procedural_checkers<'d>(body: &Symbol<'d>) -> Vec<ProceduralChecker<'d>> {
72 if checker_instances(body).is_empty() {
73 return Vec::new();
74 }
75 let mut out = Vec::new();
76 for block in module_items(body) {
77 if block.kind() != SymbolKind::ProceduralBlock {
78 continue;
79 }
80 let Some(root) = crate::process::cfg::proc_body(&block) else {
81 continue;
82 };
83 let mut stack = vec![root.as_sem_node()];
84 while let Some(n) = stack.pop() {
85 if let Some(stmt) = n.as_statement() {
86 for instance in stmt.procedural_checker_instances() {
87 out.push(ProceduralChecker {
88 block,
89 stmt,
90 instance,
91 });
92 }
93 }
94 let mut kids = n.children();
95 kids.reverse();
96 stack.extend(kids);
97 }
98 }
99 out
100}
101
102pub fn checker_instance_body<'d>(inst: &Symbol<'d>) -> Option<Symbol<'d>> {
107 inst.members().next()?.parent()
108}
109
110#[derive(Clone, Copy)]
115pub struct CheckerOutput<'d> {
116 pub formal: Symbol<'d>,
118 pub assign: Option<sv_lang::Expression<'d>>,
122 pub initial: Option<sv_lang::Expression<'d>>,
125}
126
127pub fn checker_outputs<'d>(inst: &Symbol<'d>) -> Vec<CheckerOutput<'d>> {
131 let formals = inst.members().filter(|m| {
132 matches!(
133 m.kind(),
134 SymbolKind::AssertionPort | SymbolKind::FormalArgument
135 )
136 });
137 let mut out = Vec::new();
138 for (formal, conn) in formals.zip(inst.checker_instance_connections()) {
139 if formal.kind() == SymbolKind::FormalArgument {
140 out.push(CheckerOutput {
141 formal,
142 assign: conn.actual().and_then(|a| a.as_expression()),
143 initial: conn.output_initial_expr(),
144 });
145 }
146 }
147 out
148}
149
150pub fn checker_output_of<'d>(formal: &Symbol<'d>) -> Option<CheckerOutput<'d>> {
153 if formal.kind() != SymbolKind::FormalArgument {
154 return None;
155 }
156 let inst = formal.parent()?.checker_instance_body_parent_instance()?;
157 checker_outputs(&inst)
158 .into_iter()
159 .find(|o| o.formal.uid() == formal.uid())
160}
161
162pub fn is_in_checker(sym: &Symbol<'_>) -> bool {
167 let mut cur = Some(*sym);
168 while let Some(s) = cur {
169 let kind = s.kind();
170 if kind == SymbolKind::CheckerInstanceBody {
171 return true;
172 }
173 if kind == SymbolKind::InstanceBody {
174 return false;
175 }
176 cur = s.parent();
177 }
178 false
179}
180
181pub fn is_in_program(sym: &Symbol<'_>) -> bool {
187 enclosing_instance_body(sym)
188 .and_then(|b| b.instance_body_definition())
189 .is_some_and(|d| d.definition_kind() == Some(DefinitionKind::Program))
190}
191
192pub fn enclosing_instance_body<'d>(sym: &Symbol<'d>) -> Option<Symbol<'d>> {
199 let mut cur = Some(*sym);
200 while let Some(s) = cur {
201 let kind = s.kind();
202 if kind == SymbolKind::InstanceBody {
203 return Some(s);
204 }
205 cur = if kind == SymbolKind::CheckerInstanceBody {
206 s.checker_instance_body_parent_instance()?.parent()
207 } else {
208 s.parent()
209 };
210 }
211 None
212}
213
214pub fn enclosing_package<'d>(sym: &Symbol<'d>) -> Option<Symbol<'d>> {
217 if enclosing_instance_body(sym).is_some() {
218 return None;
219 }
220 let mut cur = sym.parent();
221 while let Some(p) = cur {
222 if matches!(p.kind(), SymbolKind::Package | SymbolKind::CompilationUnit) {
223 return Some(p);
224 }
225 cur = p.parent();
226 }
227 None
228}
229
230pub fn generate_scopes<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
233 let mut out = Vec::new();
234 let mut stack = vec![*body];
235 while let Some(scope) = stack.pop() {
236 for m in scope.members() {
237 if let Some(blocks) = generate_children(&m) {
238 for b in blocks {
239 out.push(b);
240 stack.push(b);
241 }
242 } else if m.kind() == SymbolKind::CheckerInstance
243 || m.kind() == SymbolKind::StatementBlock
244 {
245 stack.push(m);
249 } else if let Some(elements) = checker_array_elements(&m) {
250 stack.extend(elements);
251 }
252 }
253 }
254 out
255}
256
257pub fn checker_array_elements<'d>(m: &Symbol<'d>) -> Option<Vec<Symbol<'d>>> {
261 fn flatten<'d>(a: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) -> bool {
262 for e in a.instance_array_elements() {
263 if e.kind() == SymbolKind::CheckerInstance {
264 out.push(e);
265 } else if e.kind() != SymbolKind::InstanceArray || !flatten(&e, out) {
266 return false;
268 }
269 }
270 true
271 }
272 if m.kind() != SymbolKind::InstanceArray {
273 return None;
274 }
275 let mut out = Vec::new();
276 (flatten(m, &mut out) && !out.is_empty()).then_some(out)
277}
278
279fn generate_children<'d>(m: &Symbol<'d>) -> Option<Vec<Symbol<'d>>> {
281 let kind = m.kind();
282 if kind == SymbolKind::GenerateBlock {
283 return Some(if m.generate_block_is_uninstantiated() {
284 Vec::new()
285 } else {
286 vec![*m]
287 });
288 }
289 if kind == SymbolKind::GenerateBlockArray {
290 return Some(
291 m.members()
292 .filter(|e| {
293 e.kind() == SymbolKind::GenerateBlock && !e.generate_block_is_uninstantiated()
294 })
295 .collect(),
296 );
297 }
298 None
299}
300
301fn push_items<'d>(scope: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
302 for m in scope.members() {
303 match generate_children(&m) {
304 Some(blocks) => {
305 for b in blocks {
306 push_items(&b, out);
307 }
308 }
309 None if m.kind() == SymbolKind::CheckerInstance => push_items(&m, out),
316 None if checker_array_elements(&m).is_some() => {
317 for e in checker_array_elements(&m).unwrap_or_default() {
318 push_items(&e, out);
319 }
320 }
321 None if m.kind() == SymbolKind::AssertionPort => {}
322 None if m.kind() == SymbolKind::StatementBlock => {
323 out.push(m);
324 push_block_checkers(&m, out);
325 }
326 None => out.push(m),
327 }
328 }
329}
330
331fn push_block_checkers<'d>(block: &Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
337 for m in block.members() {
338 if m.kind() == SymbolKind::CheckerInstance {
339 push_items(&m, out);
340 } else if m.kind() == SymbolKind::StatementBlock {
341 push_block_checkers(&m, out);
342 }
343 }
344}
345
346#[derive(Clone, Copy, Debug, PartialEq, Eq)]
349pub enum FreeVarKind {
350 Const,
352 Active,
354 Inactive,
357}
358
359pub fn free_var_kind(var: &Symbol<'_>) -> Option<FreeVarKind> {
361 if var.kind() != SymbolKind::Variable
362 || !var
363 .variable_flags()
364 .contains(sv_lang::VariableFlags::CHECKER_FREE_VARIABLE)
365 {
366 return None;
367 }
368 if var.variable_flags().contains(sv_lang::VariableFlags::CONST) {
369 return Some(FreeVarKind::Const);
370 }
371 let assigned = var
372 .parent()
373 .and_then(|b| b.checker_instance_body_parent_instance())
374 .is_some_and(|inst| checker_assigned(&inst).contains(&var.uid()));
375 Some(if assigned {
376 FreeVarKind::Inactive
377 } else {
378 FreeVarKind::Active
379 })
380}
381
382fn checker_assigned(inst: &Symbol<'_>) -> std::collections::HashSet<crate::symid::SymbolId> {
385 let mut out = std::collections::HashSet::new();
386 let mut stack = Vec::new();
387 for m in module_items(inst) {
388 if m.kind() == SymbolKind::ProceduralBlock {
389 stack.extend(crate::process::cfg::proc_body(&m).map(|b| b.as_sem_node()));
390 } else if m.kind() == SymbolKind::ContinuousAssign {
391 stack.extend(m.continuous_assign_assignment().map(|e| e.as_sem_node()));
392 }
393 }
394 while let Some(n) = stack.pop() {
395 if let Some(e) = n.as_expression()
396 && e.kind() == sv_lang::kinds::ExpressionKind::Assignment
397 && let Some(lhs) = e.left()
398 {
399 let mut lv = vec![lhs.as_sem_node()];
400 while let Some(x) = lv.pop() {
401 if let Some(xe) = x.as_expression()
402 && crate::symid::is_value_name(xe.kind())
403 && let Some(s) = xe.referenced_symbol()
404 {
405 out.insert(s.uid());
406 }
407 lv.extend(x.children());
408 }
409 }
410 stack.extend(n.children());
411 }
412 out
413}
414
415pub fn free_var_seed(var: &Symbol<'_>) -> u64 {
419 let mut h: u64 = 0xcbf2_9ce4_8422_2325;
420 for b in var.hierarchical_path().bytes() {
421 h = (h ^ u64::from(b)).wrapping_mul(0x0000_0100_0000_01b3);
422 }
423 h
424}
425
426pub fn free_var_groups<'d>(body: &Symbol<'d>) -> Vec<(Symbol<'d>, Vec<Symbol<'d>>)> {
429 checker_instances(body)
430 .into_iter()
431 .filter_map(|inst| {
432 let vars: Vec<Symbol<'d>> = inst
433 .members()
434 .filter(|m| free_var_kind(m) == Some(FreeVarKind::Active))
435 .collect();
436 (!vars.is_empty()).then_some((inst, vars))
437 })
438 .collect()
439}
440
441pub fn state_items<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
445 let mut out = module_items(body);
446 out.extend(static_procedural_vars(body));
447 out
448}
449
450pub fn static_procedural_vars<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
457 let is_static = |v: &Symbol<'_>| {
458 matches!(v.kind(), SymbolKind::Variable | SymbolKind::FormalArgument)
459 && v.variable_lifetime() == sv_lang::VariableLifetime::Static
460 && !is_array_formal(v)
461 };
462 let mut out = Vec::new();
463 let mut items = module_items(body);
466 items.extend(super::itf::extern_impls_in(body));
467 for m in items {
468 let kind = m.kind();
469 if kind == SymbolKind::Subroutine {
470 let result = m.subroutine_return_val_var().map(|r| r.uid());
471 for v in m.members() {
472 if is_static(&v) && Some(v.uid()) != result {
473 out.push(v);
474 }
475 }
476 }
477 if kind != SymbolKind::Subroutine && kind != SymbolKind::ProceduralBlock {
478 continue;
479 }
480 let Some(code) = crate::process::cfg::proc_body(&m) else {
481 continue;
482 };
483 let mut stack = vec![code.as_sem_node()];
484 while let Some(n) = stack.pop() {
485 if let Some(s) = n.as_statement() {
486 for block in crate::process::cfg::block_scopes(&s) {
487 out.extend(block.members().filter(|v| is_static(v)));
488 }
489 }
490 let mut kids = crate::process::cfg::live_children(&n);
491 kids.reverse();
492 stack.extend(kids);
493 }
494 }
495 out
496}
497
498fn is_array_formal(var: &Symbol<'_>) -> bool {
503 var.kind() == SymbolKind::FormalArgument
504 && var
505 .value_type()
506 .is_some_and(|t| t.is_unpacked_array() && !crate::state::storage::is_collection(&t))
507}
508
509pub fn is_static_procedural_var(var: &Symbol<'_>) -> bool {
512 if !matches!(
513 var.kind(),
514 SymbolKind::Variable | SymbolKind::FormalArgument
515 ) || var.variable_lifetime() != sv_lang::VariableLifetime::Static
516 || is_array_formal(var)
517 {
518 return false;
519 }
520 let mut procedural = false;
521 let mut in_class = false;
522 let mut in_task = false;
523 let mut cur = var.parent();
524 while let Some(p) = cur {
525 let kind = p.kind();
526 if kind == SymbolKind::Subroutine {
527 if p.subroutine_return_val_var().map(|r| r.uid()) == Some(var.uid()) {
528 return false;
529 }
530 procedural = true;
531 in_task |= p.subroutine_kind() == Some(sv_lang::SubroutineKind::Task);
532 } else if kind == SymbolKind::StatementBlock || kind == SymbolKind::ProceduralBlock {
533 procedural = true;
534 } else if kind == SymbolKind::InstanceBody || kind == SymbolKind::CheckerInstanceBody {
535 return procedural && !in_class;
538 } else if kind == SymbolKind::ClassType {
539 in_class = true;
540 } else if kind == SymbolKind::Package || kind == SymbolKind::CompilationUnit {
541 return procedural && !in_class && in_task && var.kind() == SymbolKind::Variable;
547 }
548 cur = p.parent();
549 }
550 false
551}
552
553pub fn variable_initializer<'d>(var: &Symbol<'d>) -> Option<Expression<'d>> {
558 if let Some(init) = var.initializer() {
559 return Some(init);
560 }
561 if var.kind() != SymbolKind::Variable {
562 return None;
563 }
564 let body = var
565 .parent()
566 .filter(|p| p.kind() == SymbolKind::InstanceBody)?;
567 let uid = var.uid();
568 body.instance_body_ports()
569 .find(|p| p.port_internal_symbol().is_some_and(|i| i.uid() == uid))
570 .and_then(|p| p.port_initializer())
571}