1use crate::symid::SymbolUid;
16use sv_lang::{Expression, MethodFlags, Symbol};
17use sv_lang_kinds::{ExpressionKind, SymbolKind};
18
19#[derive(Clone)]
21pub struct ExternTargets<'d> {
22 pub proto: Symbol<'d>,
24 pub impls: Vec<Symbol<'d>>,
26 pub fork_join: bool,
29}
30
31pub fn extern_prototype<'d>(sym: &Symbol<'d>) -> Option<Symbol<'d>> {
34 let proto = if sym.kind() == SymbolKind::MethodPrototype {
36 *sym
37 } else if sym.kind() == SymbolKind::Subroutine {
38 sym.subroutine_prototype()?
39 } else {
40 return None;
41 };
42 (proto.kind() == SymbolKind::MethodPrototype
43 && proto
44 .method_prototype_flags()
45 .contains(MethodFlags::INTERFACE_EXTERN))
46 .then_some(proto)
47}
48
49pub fn extern_targets<'d>(callee: &Symbol<'d>) -> Option<ExternTargets<'d>> {
52 let proto = extern_prototype(callee)?;
53 let mut impls = Vec::new();
54 let mut cur = proto.method_prototype_first_extern_impl();
55 while let Some(i) = cur {
56 impls.push(i.implementation());
57 cur = i.next();
58 }
59 impls.reverse();
61 Some(ExternTargets {
62 proto,
63 impls,
64 fork_join: proto
65 .method_prototype_flags()
66 .contains(MethodFlags::FORK_JOIN),
67 })
68}
69
70pub fn extern_impls_in<'d>(body: &Symbol<'d>) -> Vec<Symbol<'d>> {
75 fn protos_of<'d>(inst: Symbol<'d>, out: &mut Vec<Symbol<'d>>) {
76 if inst.kind() == SymbolKind::InstanceArray {
77 for e in inst.members() {
78 protos_of(e, out);
79 }
80 } else if let Some(b) = inst.instance_body() {
81 out.extend(b.members().filter(|m| extern_prototype(m).is_some()));
82 }
83 }
84 let mut protos = Vec::new();
85 for port in body.members() {
86 if port.kind() == SymbolKind::InterfacePort
87 && let (Some(inst), _) = port.interface_port_connection()
88 {
89 protos_of(inst, &mut protos);
90 }
91 }
92 let mut out: Vec<Symbol<'d>> = Vec::new();
93 for p in protos {
94 let mut cur = p.method_prototype_first_extern_impl();
95 while let Some(i) = cur {
96 let imp = i.implementation();
97 if imp.parent().is_some_and(|b| b.uid() == body.uid())
98 && !out.iter().any(|o| o.uid() == imp.uid())
99 {
100 out.push(imp);
101 }
102 cur = i.next();
103 }
104 }
105 out.reverse();
106 out
107}
108
109pub fn force_extern_impl_bodies(design: &mut sv_lang::Design) {
121 {
122 let guard = design.unfreeze();
123 for imp in all_impls(guard.design()) {
124 imp.visit(|s| {
125 if let Some(t) = s.declared_type() {
126 let _ = t.canonical();
127 }
128 let _ = s.initializer();
129 sv_lang::Walk::Continue
130 });
131 if let Some(body) = imp.body() {
132 let mut stack = vec![body.as_sem_node()];
133 while let Some(n) = stack.pop() {
134 if let Some(e) = n.as_expression()
135 && let Some(t) = e.expr_type()
136 {
137 let _ = t.canonical();
138 }
139 stack.extend(n.children());
140 }
141 }
142 }
143 }
144 let eval = design.eval_session();
147 for imp in all_impls(eval.design()) {
148 let Some(body) = imp.body() else { continue };
149 let mut stack = vec![body.as_sem_node()];
150 while let Some(n) = stack.pop() {
151 if let Some(e) = n.as_expression() {
152 let _ = eval.eval_constant(e);
153 }
154 stack.extend(n.children());
155 }
156 }
157}
158
159fn all_impls<'d>(design: &'d sv_lang::Design) -> Vec<Symbol<'d>> {
161 let mut impls = Vec::new();
162 design.root().visit(|s| {
163 if s.kind() == SymbolKind::MethodPrototype {
164 let mut cur = s.method_prototype_first_extern_impl();
165 while let Some(i) = cur {
166 impls.push(i.implementation());
167 cur = i.next();
168 }
169 }
170 sv_lang::Walk::Continue
171 });
172 impls
173}
174
175pub fn invalid_impl_bodies(design: &sv_lang::Design) -> Vec<(String, usize)> {
184 let mut impls = Vec::new();
185 design.root().visit(|s| {
186 if s.kind() == SymbolKind::MethodPrototype {
187 let mut cur = s.method_prototype_first_extern_impl();
188 while let Some(i) = cur {
189 impls.push(i.implementation());
190 cur = i.next();
191 }
192 }
193 sv_lang::Walk::Continue
194 });
195 let mut out = Vec::new();
196 for imp in impls {
197 let Some(body) = imp.body() else { continue };
198 let mut stack = vec![body.as_sem_node()];
199 while let Some(n) = stack.pop() {
200 let bad = n
201 .as_statement()
202 .map(|s| {
203 (s.kind() == sv_lang_kinds::StatementKind::Invalid)
204 .then(|| s.source_range().start.offset)
205 })
206 .or_else(|| {
207 n.as_expression().map(|e| {
208 (e.kind() == ExpressionKind::Invalid).then(|| e.source_range().start.offset)
209 })
210 })
211 .flatten();
212 if let Some(off) = bad {
213 out.push((imp.hierarchical_path(), off as usize));
214 break;
215 }
216 stack.extend(n.children());
217 }
218 }
219 out
220}
221
222pub fn is_vif_extern_call(e: &Expression<'_>) -> bool {
229 super::vif::call_targets(e).is_some()
230 && e.call_subroutine()
231 .is_some_and(|c| extern_prototype(&c).is_some())
232}
233
234pub fn vif_extern_message(e: &Expression<'_>) -> String {
236 format!(
237 "call to the interface extern task `{}` through a virtual interface is not supported: \
238 selecting the implementations of the chosen instance (IEEE 1800-2023 25.7.5, 25.9) \
239 is not implemented; call it through the interface instance instead",
240 e.call_subroutine()
241 .map(|c| c.name().to_string())
242 .unwrap_or_default()
243 )
244}
245
246pub fn call_extern_targets<'d>(e: &Expression<'d>) -> Option<ExternTargets<'d>> {
249 if e.kind() != ExpressionKind::Call
250 || e.is_system_call() == Some(true)
251 || super::vif::call_targets(e).is_some()
252 {
253 return None;
254 }
255 let mut t = extern_targets(&e.call_subroutine()?)?;
256 if let Some(route) = module_route(e) {
257 let routed: Vec<Symbol<'d>> = t
260 .impls
261 .iter()
262 .copied()
263 .filter(|imp| imp.parent().is_some_and(|b| route.names(&b)))
264 .collect();
265 if !routed.is_empty() {
266 t.impls = routed;
267 }
268 }
269 Some(t)
270}
271
272struct ModuleRoute {
277 segments: Vec<String>,
278 port: String,
279}
280
281impl ModuleRoute {
282 fn names(&self, body: &Symbol<'_>) -> bool {
286 let path = body.hierarchical_path();
287 let want = self.segments.join(".");
288 (path == want || path.ends_with(&format!(".{want}")))
289 && body
290 .members()
291 .any(|m| m.kind() == SymbolKind::InterfacePort && m.name() == self.port)
292 }
293}
294
295fn module_route(e: &Expression<'_>) -> Option<ModuleRoute> {
296 let text = crate::process::cfg::source_text_range(e.source_range())?;
297 let head = text.split('(').next()?.trim();
298 let mut parts: Vec<&str> = head.split('.').map(str::trim).collect();
299 let _method = parts.pop()?;
300 if parts.len() < 2 {
301 return None;
302 }
303 if !parts.iter().all(|p| {
304 !p.is_empty()
305 && p.chars()
306 .all(|c| matches!(c, '_' | '$' | '[' | ']') || c.is_ascii_alphanumeric())
307 }) {
308 return None;
309 }
310 let port = parts.pop()?.to_owned();
311 Some(ModuleRoute {
312 segments: parts.into_iter().map(str::to_owned).collect(),
313 port,
314 })
315}
316
317pub fn is_extern_impl(sub: &Symbol<'_>) -> bool {
320 sub.kind() == SymbolKind::Subroutine
321 && sub.body().is_some()
322 && sub
323 .subroutine_prototype()
324 .is_some_and(|p| extern_prototype(&p).is_some())
325 && extern_prototype(sub)
326 .and_then(|p| p.method_prototype_subroutine())
327 .is_none_or(|stub| stub.uid() != sub.uid())
328}
329
330pub fn call_callees<'d>(e: &Expression<'d>) -> Vec<Symbol<'d>> {
334 if e.kind() != ExpressionKind::Call || e.is_system_call() == Some(true) {
335 return Vec::new();
336 }
337 match call_extern_targets(e) {
338 Some(t) => t.impls,
339 None => e.call_subroutine().into_iter().collect(),
340 }
341}
342
343pub fn call_target<'d>(e: &Expression<'d>) -> Option<Symbol<'d>> {
349 match call_extern_targets(e) {
350 Some(t) => t.impls.into_iter().next(),
351 None => e.call_subroutine(),
352 }
353}
354
355fn scope_tag(kind: char, inst: &Symbol<'_>, leaf: &str) -> String {
361 format!("\u{1}ext-{kind}:{}.{leaf}", inst.hierarchical_path())
362}
363
364pub fn is_scope_tag(label: &str) -> bool {
366 label.starts_with('\u{1}')
367}
368
369pub fn extern_scope_tags(task: &Symbol<'_>) -> Vec<String> {
374 let Some(proto) = extern_prototype(task) else {
375 return Vec::new();
376 };
377 fn inst_of<'d>(s: &Symbol<'d>) -> Option<Symbol<'d>> {
378 s.parent()?.instance_body_parent_instance()
379 }
380 let (Some(module), Some(ifc)) = (inst_of(task), inst_of(&proto)) else {
381 return Vec::new();
382 };
383 let leaf = task.name().to_string();
384 vec![scope_tag('m', &module, &leaf), scope_tag('i', &ifc, &leaf)]
385}
386
387pub fn extern_disable_tag(requester: &Symbol<'_>, path: &[String]) -> Option<String> {
393 let (leaf, hops) = path.split_last()?;
394 let mut cur = *requester;
395 let mut via_port_of: Option<Symbol<'_>> = None;
396 for (k, seg) in hops.iter().enumerate() {
397 let body = cur.instance_body()?;
398 let mut next = None;
399 for m in body.members() {
400 if m.name() != seg.as_str() {
401 continue;
402 }
403 let kind = m.kind();
404 if kind == SymbolKind::Instance {
405 next = Some((m, None));
406 } else if kind == SymbolKind::InterfacePort
407 && let (Some(i), _) = m.interface_port_connection()
408 && i.kind() == SymbolKind::Instance
409 {
410 next = Some((i, (k > 0).then_some(cur)));
412 }
413 }
414 let (n, via) = next?;
415 via_port_of = via;
416 cur = n;
417 }
418 if let Some(module) = via_port_of {
419 return Some(scope_tag('m', &module, leaf));
421 }
422 let body = cur.instance_body()?;
423 body.members()
424 .any(|m| m.name() == leaf.as_str() && extern_prototype(&m).is_some())
425 .then(|| scope_tag('i', &cur, leaf))
426}