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 slang::ast::VisitFlags::AllGood> {
19 InstBodyVisitor(
20 Context &context, const slang::ast::Symbol &outermostModule,
21 DenseSet<const slang::ast::InstanceBodySymbol *> &visitedBodies)
22 : context(context), outermostModule(outermostModule),
23 visitedBodies(visitedBodies) {}
24
25 void handle(const slang::ast::InstanceSymbol &instNode) {
26 traverseInstanceBody(context, instNode, visitedBodies);
27 // Also visit port connection expressions to find hier refs used as
28 // port arguments (e.g., .in_val(b_inst.local_val)).
29 for (auto *conn : instNode.getPortConnections())
30 if (auto *connExpr = conn->getExpression())
31 connExpr->visit(*this);
32 }
33
34 void handle(const slang::ast::HierarchicalValueExpression &expr) {
35 auto builder = context.builder;
36 auto *currentInstBody =
37 expr.symbol.getParentScope()->getContainingInstance();
38 auto *outermostInstBody =
39 outermostModule.as_if<slang::ast::InstanceBodySymbol>();
40
41 // Like module Foo; int a; Foo.a; endmodule.
42 // Ignore "Foo.a" invoked by this module itself.
43 if (currentInstBody == outermostInstBody)
44 return;
45
46 // References resolved via an interface port (e.g. `bus.member` where `bus`
47 // is an `Iface.modport` port) are not cross-instance hierarchical accesses;
48 // they are handled by the interface port lowering machinery in
49 // Structure.cpp. Recording them here would add a spurious hierPath input
50 // to the module signature that nothing fills in at the instance site.
51 if (expr.ref.isViaIfacePort())
52 return;
53
54 if (isCompileTimeConstant(expr.symbol.kind))
55 return;
56
57 auto hierName = builder.getStringAttr(expr.symbol.name);
58 const slang::ast::InstanceBodySymbol *parentInstBody = nullptr;
59
60 // Record `name` on `body`, or add this symbol as an alias if another
61 // instance already recorded the same path.
62 auto addHierPath = [&](const slang::ast::InstanceBodySymbol *body,
63 mlir::StringAttr name,
64 slang::ast::ArgumentDirection direction) {
65 for (auto &path : context.hierPaths[body])
66 if (path.hierName == name) {
67 if (!llvm::any_of(path.valueSyms, [&](auto &alias) {
68 return alias.first == &expr.symbol;
69 }))
70 path.valueSyms.push_back({&expr.symbol, currentInstBody});
71 return;
72 }
73 context.hierPaths[body].push_back(
74 HierPathInfo{name, {}, direction, {{&expr.symbol, currentInstBody}}});
75 };
76
77 auto *tempInstBody = currentInstBody;
78 while (tempInstBody) {
79 tempInstBody = tempInstBody->parentInstance->getParentScope()
80 ->getContainingInstance();
81 if (tempInstBody == outermostInstBody) {
82 // The value flows upward: expose it as an output port on every module
83 // boundary between the symbol and the outermost module.
84 SmallVector<const slang::ast::InstanceSymbol *> instChain;
85 for (auto *b = currentInstBody; b && b != outermostInstBody;
86 b = b->parentInstance->getParentScope()->getContainingInstance())
87 instChain.push_back(b->parentInstance);
88 // Map each level of the chain to the body that module conversion
89 // will actually visit, descending from the outermost module: slang's
90 // getCanonicalBody only relates instances elaborated within the same
91 // parent body, so a body forced into existence by this reference
92 // does not defer to its equivalent in the canonical sibling subtree.
93 // The by-name lookup only happens at levels that left the canonical
94 // subtree, references on the canonical path skip it entirely.
95 SmallVector<const slang::ast::InstanceBodySymbol *> canonChain(
96 instChain.size());
97 const slang::ast::InstanceBodySymbol *parentCanon = outermostInstBody;
98 for (size_t i = instChain.size(); i-- > 0;) {
99 const slang::ast::InstanceSymbol *inst = instChain[i];
100 if (inst->getParentScope()->getContainingInstance() != parentCanon)
101 if (auto *resolved = parentCanon->find(inst->name))
102 if (auto *resolvedInst =
103 resolved->as_if<slang::ast::InstanceSymbol>())
104 inst = resolvedInst;
105 canonChain[i] = getCanonicalBody(*inst);
106 parentCanon = canonChain[i];
107 }
108 for (size_t i = 0; i < instChain.size(); ++i) {
109 addHierPath(canonChain[i], hierName,
110 slang::ast::ArgumentDirection::Out);
111 hierName = builder.getStringAttr(
112 instChain[i]->name + llvm::Twine(".") + hierName.getValue());
113 }
114 return;
115 }
116 }
117
118 // The value flows downward: route it as an input port from the common
119 // ancestor down to the referencing module.
120 std::function<void(const slang::ast::InstanceBodySymbol *)>
121 collectHierarchicalPaths = [&](auto sym) {
122 addHierPath(getCanonicalBody(*sym->parentInstance), hierName,
123 slang::ast::ArgumentDirection::In);
124 parentInstBody =
125 sym->parentInstance->getParentScope()->getContainingInstance();
126 if (parentInstBody && parentInstBody != currentInstBody)
127 collectHierarchicalPaths(parentInstBody);
128 };
129 hierName = builder.getStringAttr(currentInstBody->parentInstance->name +
130 llvm::Twine(".") + hierName.getValue());
131 collectHierarchicalPaths(outermostInstBody);
132 }
133
134 Context &context;
135 const slang::ast::Symbol &outermostModule;
136 DenseSet<const slang::ast::InstanceBodySymbol *> &visitedBodies;
137
138 static void traverseInstanceBody(
139 Context &context, const slang::ast::InstanceSymbol &symbol,
140 DenseSet<const slang::ast::InstanceBodySymbol *> &visitedBodies) {
141 const slang::ast::InstanceBodySymbol *body = getCanonicalBody(symbol);
142 if (visitedBodies.insert(body).second) {
143 for (auto &member : body->members()) {
144 auto &outermostModule = member.getParentScope()->asSymbol();
145 InstBodyVisitor visitor(context, outermostModule, visitedBodies);
146 member.visit(visitor);
147 }
148 }
149 }
150};
151
152} // namespace
153
154void Context::traverseInstanceBody(const slang::ast::InstanceSymbol &symbol) {
155 // Top-level entry point: create a fresh visitedBodies set to prevent
156 // infinite recursion and to skip identical module bodies.
157 DenseSet<const slang::ast::InstanceBodySymbol *> visitedBodies;
158 InstBodyVisitor::traverseInstanceBody(*this, symbol, visitedBodies);
159}
const slang::ast::InstanceBodySymbol * getCanonicalBody(const slang::ast::InstanceSymbol &inst)
Get the slang canonical body for the given instance, if there is one.
bool isCompileTimeConstant(slang::ast::SymbolKind kind)
Return true if symbols of this kind are elaboration-time constants.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.