CIRCT 23.0.0git
Loading...
Searching...
No Matches
HierarchicalNames.cpp
Go to the documentation of this file.
1//===- Expressions.cpp - Slang expression conversion ----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
11using namespace circt;
12using namespace ImportVerilog;
13
14/// Traverse the instance body.
15namespace {
16struct InstBodyVisitor
17 : public slang::ast::ASTVisitor<InstBodyVisitor,
18 /*VisitStatements=*/true,
19 /*VisitExpressions=*/true> {
20 InstBodyVisitor(
21 Context &context, const slang::ast::Symbol &outermostModule,
22 DenseSet<const slang::ast::InstanceBodySymbol *> &visitedBodies)
23 : context(context), outermostModule(outermostModule),
24 visitedBodies(visitedBodies) {}
25
26 void handle(const slang::ast::InstanceSymbol &instNode) {
27 traverseInstanceBody(context, instNode, visitedBodies);
28 // Also visit port connection expressions to find hier refs used as
29 // port arguments (e.g., .in_val(b_inst.local_val)).
30 for (auto *conn : instNode.getPortConnections())
31 if (auto *connExpr = conn->getExpression())
32 connExpr->visit(*this);
33 }
34
35 void handle(const slang::ast::HierarchicalValueExpression &expr) {
36 auto builder = context.builder;
37 auto *currentInstBody =
38 expr.symbol.getParentScope()->getContainingInstance();
39 auto *outermostInstBody =
40 outermostModule.as_if<slang::ast::InstanceBodySymbol>();
41
42 // Like module Foo; int a; Foo.a; endmodule.
43 // Ignore "Foo.a" invoked by this module itself.
44 if (currentInstBody == outermostInstBody)
45 return;
46
47 // References resolved via an interface port (e.g. `bus.member` where `bus`
48 // is an `Iface.modport` port) are not cross-instance hierarchical accesses;
49 // they are handled by the interface port lowering machinery in
50 // Structure.cpp. Recording them here would add a spurious hierPath input
51 // to the module signature that nothing fills in at the instance site.
52 if (expr.ref.isViaIfacePort())
53 return;
54
55 auto hierName = builder.getStringAttr(expr.symbol.name);
56 const slang::ast::InstanceBodySymbol *parentInstBody = nullptr;
57
58 // Collect hierarchical names that are added to the port list.
59 std::function<void(const slang::ast::InstanceBodySymbol *, bool)>
60 collectHierarchicalPaths = [&](auto sym, bool isUpward) {
61 // Check if this path already exists globally for this module
62 HierPathInfo *existing = nullptr;
63 if (context.hierPaths.contains(sym)) {
64 for (auto &path : context.hierPaths[sym]) {
65 if (path.hierName == hierName) {
66 existing = &path;
67 break;
68 }
69 }
70 }
71
72 if (!existing) {
73 context.hierPaths[sym].push_back(
74 HierPathInfo{hierName,
75 {},
76 isUpward ? slang::ast::ArgumentDirection::Out
77 : slang::ast::ArgumentDirection::In,
78 {&expr.symbol}});
79 } else {
80 // The path already exists, but this may be a different instance
81 // resolving to a different symbol object. Add as an alias.
82 if (!llvm::is_contained(existing->valueSyms, &expr.symbol))
83 existing->valueSyms.push_back(&expr.symbol);
84 }
85
86 // Iterate up from the current instance body symbol until meeting the
87 // outermost module.
88 parentInstBody =
89 sym->parentInstance->getParentScope()->getContainingInstance();
90 if (!parentInstBody)
91 return;
92
93 if (isUpward) {
94 // Avoid collecting hierarchical names into the outermost module.
95 if (parentInstBody && parentInstBody != outermostInstBody) {
96 hierName =
97 builder.getStringAttr(sym->parentInstance->name +
98 llvm::Twine(".") + hierName.getValue());
99 collectHierarchicalPaths(parentInstBody, isUpward);
100 }
101 } else {
102 if (parentInstBody && parentInstBody != currentInstBody)
103 collectHierarchicalPaths(parentInstBody, isUpward);
104 }
105 };
106
107 // Determine whether hierarchical names are upward or downward.
108 auto *tempInstBody = currentInstBody;
109 while (tempInstBody) {
110 tempInstBody = tempInstBody->parentInstance->getParentScope()
111 ->getContainingInstance();
112 if (tempInstBody == outermostInstBody) {
113 collectHierarchicalPaths(currentInstBody, true);
114 return;
115 }
116 }
117
118 hierName = builder.getStringAttr(currentInstBody->parentInstance->name +
119 llvm::Twine(".") + hierName.getValue());
120 collectHierarchicalPaths(outermostInstBody, false);
121 }
122
123 Context &context;
124 const slang::ast::Symbol &outermostModule;
125 DenseSet<const slang::ast::InstanceBodySymbol *> &visitedBodies;
126
127 static void traverseInstanceBody(
128 Context &context, const slang::ast::InstanceSymbol &symbol,
129 DenseSet<const slang::ast::InstanceBodySymbol *> &visitedBodies) {
130 const slang::ast::InstanceBodySymbol *body = getCanonicalBody(symbol);
131 if (visitedBodies.insert(body).second) {
132 for (auto &member : body->members()) {
133 auto &outermostModule = member.getParentScope()->asSymbol();
134 InstBodyVisitor visitor(context, outermostModule, visitedBodies);
135 member.visit(visitor);
136 }
137 }
138 }
139};
140
141} // namespace
142
143void Context::traverseInstanceBody(const slang::ast::InstanceSymbol &symbol) {
144 // Top-level entry point: create a fresh visitedBodies set to prevent
145 // infinite recursion and to skip identical module bodies.
146 DenseSet<const slang::ast::InstanceBodySymbol *> visitedBodies;
147 InstBodyVisitor::traverseInstanceBody(*this, symbol, visitedBodies);
148}
const slang::ast::InstanceBodySymbol * getCanonicalBody(const slang::ast::InstanceSymbol &inst)
Get the slang canonical body for the given instance, if there is one.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
llvm::SmallVector< const slang::ast::ValueSymbol *, 2 > valueSyms
The value symbols associated with this hierarchical path.