1 // -*- mode: C++; c-file-style: "cc-mode" -*-
2 //*************************************************************************
3 // DESCRIPTION: Verilator: Resolve module/signal name references
4 //
5 // Code available from: https://verilator.org
6 //
7 //*************************************************************************
8 //
9 // Copyright 2003-2021 by Wilson Snyder. This program is free software; you
10 // can redistribute it and/or modify it under the terms of either the GNU
11 // Lesser General Public License Version 3 or the Perl Artistic License
12 // Version 2.0.
13 // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
14 //
15 //*************************************************************************
16 // NO EDITS: Don't replace or delete nodes, as the parser symbol table
17 //           has pointers into the ast tree.
18 //
19 // LINK TRANSFORMATIONS:
20 //      Top-down traversal
21 //          Cells:
22 //              Read module if needed
23 //              Link to module that instantiates it
24 //*************************************************************************
25 
26 #include "config_build.h"
27 #include "verilatedos.h"
28 
29 #include "V3Global.h"
30 #include "V3LinkCells.h"
31 #include "V3SymTable.h"
32 #include "V3Parse.h"
33 #include "V3Ast.h"
34 #include "V3Graph.h"
35 
36 #include <algorithm>
37 #include <map>
38 #include <vector>
39 #include <unordered_set>
40 
41 //######################################################################
42 // Graph subclasses
43 
44 class LinkCellsGraph final : public V3Graph {
45 public:
46     LinkCellsGraph() = default;
47     virtual ~LinkCellsGraph() override = default;
48     virtual void loopsMessageCb(V3GraphVertex* vertexp) override;
49 };
50 
51 class LinkCellsVertex final : public V3GraphVertex {
52     AstNodeModule* const m_modp;
53 
54 public:
LinkCellsVertex(V3Graph * graphp,AstNodeModule * modp)55     LinkCellsVertex(V3Graph* graphp, AstNodeModule* modp)
56         : V3GraphVertex{graphp}
57         , m_modp{modp} {}
58     virtual ~LinkCellsVertex() override = default;
modp() const59     AstNodeModule* modp() const { return m_modp; }
name() const60     virtual string name() const override { return modp()->name(); }
fileline() const61     virtual FileLine* fileline() const override { return modp()->fileline(); }
62     // Recursive modules get space for maximum recursion
rankAdder() const63     virtual uint32_t rankAdder() const override {
64         return m_modp->recursiveClone() ? (1 + v3Global.opt.moduleRecursionDepth()) : 1;
65     }
66 };
67 
68 class LibraryVertex final : public V3GraphVertex {
69 public:
LibraryVertex(V3Graph * graphp)70     explicit LibraryVertex(V3Graph* graphp)
71         : V3GraphVertex{graphp} {}
72     virtual ~LibraryVertex() override = default;
name() const73     virtual string name() const override { return "*LIBRARY*"; }
74 };
75 
loopsMessageCb(V3GraphVertex * vertexp)76 void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) {
77     if (const LinkCellsVertex* const vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) {
78         vvertexp->modp()->v3warn(E_UNSUPPORTED,
79                                  "Unsupported: Recursive multiple modules (module instantiates "
80                                  "something leading back to itself): "
81                                      << vvertexp->modp()->prettyNameQ() << '\n'
82                                      << V3Error::warnMore()
83                                      << "... note: self-recursion (module instantiating itself "
84                                         "directly) is supported.");
85         V3Error::abortIfErrors();
86     } else {  // Everything should match above, but...
87         v3fatalSrc("Recursive instantiations");
88     }
89 }
90 
91 //######################################################################
92 // Link state, as a visitor of each AstNode
93 
94 class LinkCellsVisitor final : public AstNVisitor {
95 private:
96     // NODE STATE
97     //  Entire netlist:
98     //   AstNodeModule::user1p()        // V3GraphVertex*    Vertex describing this module
99     //   AstNodeModule::user2p()        // AstNodeModule*    clone used for de-recursing
100     //   AstCell::user1p()              // ==V3NodeModule* if done, != if unprocessed
101     //   AstCell::user2()               // bool   clone renaming completed
102     //  Allocated across all readFiles in V3Global::readFiles:
103     //   AstNode::user4p()      // VSymEnt*    Package and typedef symbol names
104     const AstUser1InUse m_inuser1;
105     const AstUser2InUse m_inuser2;
106 
107     // STATE
108     VInFilter* m_filterp;  // Parser filter
109     V3ParseSym* m_parseSymp;  // Parser symbol table
110 
111     // Below state needs to be preserved between each module call.
112     AstNodeModule* m_modp = nullptr;  // Current module
113     VSymGraph m_mods;  // Symbol table of all module names
114     LinkCellsGraph m_graph;  // Linked graph of all cell interconnects
115     LibraryVertex* m_libVertexp = nullptr;  // Vertex at root of all libraries
116     const V3GraphVertex* m_topVertexp = nullptr;  // Vertex of top module
117     std::unordered_set<string> m_declfnWarned;  // Files we issued DECLFILENAME on
118     string m_origTopModuleName;  // original name of the top module
119 
120     VL_DEBUG_FUNC;  // Declare debug()
121 
122     // METHODS
vertex(AstNodeModule * nodep)123     V3GraphVertex* vertex(AstNodeModule* nodep) {
124         // Return corresponding vertex for this module
125         if (!nodep->user1p()) nodep->user1p(new LinkCellsVertex(&m_graph, nodep));
126         return (nodep->user1u().toGraphVertex());
127     }
128 
findModuleSym(const string & modName)129     AstNodeModule* findModuleSym(const string& modName) {
130         const VSymEnt* const foundp = m_mods.rootp()->findIdFallback(modName);
131         if (!foundp) {
132             return nullptr;
133         } else {
134             return VN_AS(foundp->nodep(), NodeModule);
135         }
136     }
137 
resolveModule(AstNode * nodep,const string & modName)138     AstNodeModule* resolveModule(AstNode* nodep, const string& modName) {
139         AstNodeModule* modp = findModuleSym(modName);
140         if (!modp) {
141             // Read-subfile
142             // If file not found, make AstNotFoundModule, rather than error out.
143             // We'll throw the error when we know the module will really be needed.
144             const string prettyName = AstNode::prettyName(modName);
145             V3Parse parser(v3Global.rootp(), m_filterp, m_parseSymp);
146             // true below -> other simulators treat modules in link-found files as library cells
147             parser.parseFile(nodep->fileline(), prettyName, true, "");
148             V3Error::abortIfErrors();
149             // We've read new modules, grab new pointers to their names
150             readModNames();
151             // Check again
152             modp = findModuleSym(modName);
153             if (!modp) {
154                 // This shouldn't throw a message as parseFile will create
155                 // a AstNotFoundModule for us
156                 nodep->v3error("Can't resolve module reference: '" << prettyName << "'");
157             }
158         }
159         return modp;
160     }
161 
162     // VISITs
visit(AstNetlist * nodep)163     virtual void visit(AstNetlist* nodep) override {
164         AstNode::user1ClearTree();
165         readModNames();
166         iterateChildren(nodep);
167         // Find levels in graph
168         m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
169         m_graph.dumpDotFilePrefixed("linkcells");
170         m_graph.rank();
171         for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
172             if (const LinkCellsVertex* const vvertexp = dynamic_cast<LinkCellsVertex*>(itp)) {
173                 // +1 so we leave level 1  for the new wrapper we'll make in a moment
174                 AstNodeModule* const modp = vvertexp->modp();
175                 modp->level(vvertexp->rank() + 1);
176             }
177         }
178         if (v3Global.opt.topModule() != "" && !m_topVertexp) {
179             v3error("Specified --top-module '" << v3Global.opt.topModule()
180                                                << "' was not found in design.");
181         }
182     }
visit(AstConstPool * nodep)183     virtual void visit(AstConstPool* nodep) override {}
visit(AstNodeModule * nodep)184     virtual void visit(AstNodeModule* nodep) override {
185         // Module: Pick up modnames, so we can resolve cells later
186         VL_RESTORER(m_modp);
187         {
188             m_modp = nodep;
189             UINFO(4, "Link Module: " << nodep << endl);
190             if (nodep->fileline()->filebasenameNoExt() != nodep->prettyName()
191                 && !v3Global.opt.isLibraryFile(nodep->fileline()->filename())
192                 && !VN_IS(nodep, NotFoundModule) && !nodep->recursiveClone()
193                 && !nodep->internal()) {
194                 // We only complain once per file, otherwise library-like files
195                 // have a huge mess of warnings
196                 if (m_declfnWarned.find(nodep->fileline()->filename()) == m_declfnWarned.end()) {
197                     m_declfnWarned.insert(nodep->fileline()->filename());
198                     nodep->v3warn(DECLFILENAME, "Filename '"
199                                                     << nodep->fileline()->filebasenameNoExt()
200                                                     << "' does not match " << nodep->typeName()
201                                                     << " name: " << nodep->prettyNameQ());
202                 }
203             }
204             if (VN_IS(nodep, Iface) || VN_IS(nodep, Package)) {
205                 nodep->inLibrary(true);  // Interfaces can't be at top, unless asked
206             }
207             const bool topMatch = (v3Global.opt.topModule() == nodep->prettyName());
208             if (topMatch) {
209                 m_topVertexp = vertex(nodep);
210                 UINFO(2, "Link --top-module: " << nodep << endl);
211                 nodep->inLibrary(false);  // Safer to make sure it doesn't disappear
212             }
213             if (v3Global.opt.topModule() == "" ? nodep->inLibrary()  // Library cells are lower
214                                                : !topMatch) {  // Any non-specified module is lower
215                 // Put under a fake vertex so that the graph ranking won't indicate
216                 // this is a top level module
217                 if (!m_libVertexp) m_libVertexp = new LibraryVertex(&m_graph);
218                 new V3GraphEdge(&m_graph, m_libVertexp, vertex(nodep), 1, false);
219             }
220             // Note AstBind also has iteration on cells
221             iterateChildren(nodep);
222             nodep->checkTree();
223         }
224     }
225 
visit(AstIfaceRefDType * nodep)226     virtual void visit(AstIfaceRefDType* nodep) override {
227         // Cell: Resolve its filename.  If necessary, parse it.
228         UINFO(4, "Link IfaceRef: " << nodep << endl);
229         // Use findIdUpward instead of findIdFlat; it doesn't matter for now
230         // but we might support modules-under-modules someday.
231         AstNodeModule* const modp = resolveModule(nodep, nodep->ifaceName());
232         if (modp) {
233             if (VN_IS(modp, Iface)) {
234                 // Track module depths, so can sort list from parent down to children
235                 new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false);
236                 if (!nodep->cellp()) nodep->ifacep(VN_AS(modp, Iface));
237             } else if (VN_IS(modp, NotFoundModule)) {  // Will error out later
238             } else {
239                 nodep->v3error("Non-interface used as an interface: " << nodep->prettyNameQ());
240             }
241         }
242         // Note cannot do modport resolution here; modports are allowed underneath generates
243     }
244 
visit(AstPackageImport * nodep)245     virtual void visit(AstPackageImport* nodep) override {
246         // Package Import: We need to do the package before the use of a package
247         iterateChildren(nodep);
248         UASSERT_OBJ(nodep->packagep(), nodep, "Unlinked package");  // Parser should set packagep
249         new V3GraphEdge(&m_graph, vertex(m_modp), vertex(nodep->packagep()), 1, false);
250     }
251 
visit(AstBind * nodep)252     virtual void visit(AstBind* nodep) override {
253         // Bind: Has cells underneath that need to be put into the new
254         // module, and cells which need resolution
255         // TODO this doesn't allow bind to dotted hier names, that would require
256         // this move to post param, which would mean we do not auto-read modules
257         // and means we cannot compute module levels until later.
258         UINFO(4, "Link Bind: " << nodep << endl);
259         AstNodeModule* const modp = resolveModule(nodep, nodep->name());
260         if (modp) {
261             AstNode* const cellsp = nodep->cellsp()->unlinkFrBackWithNext();
262             // Module may have already linked, so need to pick up these new cells
263             VL_RESTORER(m_modp);
264             {
265                 m_modp = modp;
266                 // Important that this adds to end, as next iterate assumes does all cells
267                 modp->addStmtp(cellsp);
268                 iterateAndNextNull(cellsp);
269             }
270         }
271         pushDeletep(nodep->unlinkFrBack());
272     }
273 
visit(AstCell * nodep)274     virtual void visit(AstCell* nodep) override {
275         // Cell: Resolve its filename.  If necessary, parse it.
276         // Execute only once.  Complication is that cloning may result in
277         // user1 being set (for pre-clone) so check if user1() matches the
278         // m_mod, if 0 never did it, if !=, it is an unprocessed clone
279         const bool cloned = (nodep->user1p() && nodep->user1p() != m_modp);
280         if (nodep->user1p() == m_modp) return;  // AstBind and AstNodeModule may call a cell twice
281         if (nodep->modName() == m_origTopModuleName) {
282             if (v3Global.opt.hierChild() && nodep->modName() == m_modp->origName()) {
283                 // Only the root of the recursive instantiation can be a hierarchical block.
284                 nodep->modName(m_modp->name());
285             } else {
286                 // non-top module will be the top module of this run
287                 VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
288                 return;
289             }
290         }
291         nodep->user1p(m_modp);
292         //
293         if (!nodep->modp() || cloned) {
294             UINFO(4, "Link Cell: " << nodep << endl);
295             // Use findIdFallback instead of findIdFlat; it doesn't matter for now
296             // but we might support modules-under-modules someday.
297             AstNodeModule* cellmodp = resolveModule(nodep, nodep->modName());
298             if (cellmodp) {
299                 if (cellmodp == m_modp || cellmodp->user2p() == m_modp) {
300                     UINFO(1, "Self-recursive module " << cellmodp << endl);
301                     cellmodp->recursive(true);
302                     nodep->recursive(true);
303                     if (!cellmodp->recursiveClone()) {
304                         // In the non-Vrcm, which needs to point to Vrcm flavor
305                         //
306                         // Make a clone which this cell points to
307                         // Later, the clone's cells will also point clone'd name
308                         // This lets us link the XREFs between the (uncloned) children so
309                         // they don't point to the same module which would
310                         // break parameter resolution.
311                         AstNodeModule* otherModp = VN_CAST(cellmodp->user2p(), NodeModule);
312                         if (!otherModp) {
313                             otherModp = cellmodp->cloneTree(false);
314                             otherModp->name(otherModp->name() + "__Vrcm");
315                             otherModp->user1p(nullptr);  // Need new vertex
316                             otherModp->user2p(cellmodp);
317                             otherModp->recursiveClone(true);
318                             // user1 etc will retain its pre-clone value
319                             cellmodp->user2p(otherModp);
320                             v3Global.rootp()->addModulep(otherModp);
321                             new V3GraphEdge(&m_graph, vertex(cellmodp), vertex(otherModp), 1,
322                                             false);
323                         }
324                         cellmodp = otherModp;
325                         nodep->modp(cellmodp);
326                     } else {
327                         // In the Vrcm, which needs to point back to Vrcm flavor
328                         // The cell already has the correct resolution (to Vrcm)
329                         nodep->modp(cellmodp);
330                         // We don't create a V3GraphEdge (as it would be circular)
331                     }
332                 } else {  // Non-recursive
333                     // Track module depths, so can sort list from parent down to children
334                     nodep->modp(cellmodp);
335                     new V3GraphEdge(&m_graph, vertex(m_modp), vertex(cellmodp), 1, false);
336                 }
337             }
338         }
339         // Remove AstCell(AstPin("",nullptr)), it's a side effect of how we parse "()"
340         // the empty middle is identical to the empty rule that must find pins in "(,)".
341         if (nodep->pinsp() && !nodep->pinsp()->nextp() && nodep->pinsp()->name() == ""
342             && !nodep->pinsp()->exprp()) {
343             pushDeletep(nodep->pinsp()->unlinkFrBackWithNext());
344         }
345         if (nodep->paramsp() && !nodep->paramsp()->nextp() && nodep->paramsp()->name() == ""
346             && !nodep->paramsp()->exprp()) {
347             pushDeletep(nodep->paramsp()->unlinkFrBackWithNext());
348         }
349         // Convert .* to list of pins
350         bool pinStar = false;
351         for (AstPin *nextp, *pinp = nodep->pinsp(); pinp; pinp = nextp) {
352             nextp = VN_AS(pinp->nextp(), Pin);
353             if (pinp->dotStar()) {
354                 if (pinStar) pinp->v3error("Duplicate .* in an instance");
355                 pinStar = true;
356                 // Done with this fake pin
357                 VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
358             }
359         }
360         // Convert unnamed pins to pin number based assignments
361         for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
362             if (pinp->name() == "") pinp->name("__pinNumber" + cvtToStr(pinp->pinNum()));
363         }
364         for (AstPin* pinp = nodep->paramsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
365             pinp->param(true);
366             if (pinp->name() == "") pinp->name("__paramNumber" + cvtToStr(pinp->pinNum()));
367         }
368         if (nodep->modp()) {
369             nodep->modName(nodep->modp()->name());
370             // Note what pins exist
371             std::unordered_set<string> ports;  // Symbol table of all connected port names
372             for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
373                 if (pinp->name() == "")
374                     pinp->v3error("Connect by position is illegal in .* connected instances");
375                 if (!pinp->exprp()) {
376                     if (pinp->name().substr(0, 11) == "__pinNumber") {
377                         pinp->v3warn(PINNOCONNECT,
378                                      "Cell pin is not connected: " << pinp->prettyNameQ());
379                     } else {
380                         pinp->v3warn(PINCONNECTEMPTY,
381                                      "Cell pin connected by name with empty reference: "
382                                          << pinp->prettyNameQ());
383                     }
384                 }
385                 ports.insert(pinp->name());
386             }
387             // We search ports, rather than in/out declarations as they aren't resolved yet,
388             // and it's easier to do it now than in V3LinkDot when we'd need to repeat steps.
389             for (AstNode* portnodep = nodep->modp()->stmtsp(); portnodep;
390                  portnodep = portnodep->nextp()) {
391                 if (const AstPort* const portp = VN_CAST(portnodep, Port)) {
392                     if (ports.find(portp->name()) == ports.end()
393                         && ports.find("__pinNumber" + cvtToStr(portp->pinNum())) == ports.end()) {
394                         if (pinStar) {
395                             UINFO(9, "    need .* PORT  " << portp << endl);
396                             // Create any not already connected
397                             AstPin* const newp = new AstPin(
398                                 nodep->fileline(), 0, portp->name(),
399                                 new AstParseRef(nodep->fileline(), VParseRefExp::PX_TEXT,
400                                                 portp->name(), nullptr, nullptr));
401                             newp->svImplicit(true);
402                             nodep->addPinsp(newp);
403                         } else {  // warn on the CELL that needs it, not the port
404                             nodep->v3warn(PINMISSING,
405                                           "Cell has missing pin: " << portp->prettyNameQ());
406                             AstPin* const newp
407                                 = new AstPin(nodep->fileline(), 0, portp->name(), nullptr);
408                             nodep->addPinsp(newp);
409                         }
410                     }
411                 }
412             }
413         }
414         if (VN_IS(nodep->modp(), Iface)) {
415             // Cell really is the parent's instantiation of an interface, not a normal module
416             // Make sure we have a variable to refer to this cell, so can <ifacename>.<innermember>
417             // in the same way that a child does.  Rename though to avoid conflict with cell.
418             // This is quite similar to how classes work; when unpacked
419             // classes are better supported may remap interfaces to be more
420             // like a class.
421             if (!nodep->hasIfaceVar()) {
422                 const string varName
423                     = nodep->name() + "__Viftop";  // V3LinkDot looks for this naming
424                 AstIfaceRefDType* const idtypep = new AstIfaceRefDType(
425                     nodep->fileline(), nodep->name(), nodep->modp()->name());
426                 idtypep->ifacep(nullptr);  // cellp overrides
427                 // In the case of arrayed interfaces, we replace cellp when de-arraying in V3Inst
428                 idtypep->cellp(nodep);  // Only set when real parent cell known.
429                 AstVar* varp;
430                 if (nodep->rangep()) {
431                     AstNodeArrayDType* const arrp
432                         = new AstUnpackArrayDType(nodep->fileline(), VFlagChildDType(), idtypep,
433                                                   nodep->rangep()->cloneTree(true));
434                     varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName,
435                                       VFlagChildDType(), arrp);
436                 } else {
437                     varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName,
438                                       VFlagChildDType(), idtypep);
439                 }
440                 varp->isIfaceParent(true);
441                 nodep->addNextHere(varp);
442                 nodep->hasIfaceVar(true);
443             }
444         }
445         if (nodep->modp()) {  //
446             iterateChildren(nodep);
447         }
448         UINFO(4, " Link Cell done: " << nodep << endl);
449     }
450 
visit(AstRefDType * nodep)451     virtual void visit(AstRefDType* nodep) override {
452         for (AstPin* pinp = nodep->paramsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
453             pinp->param(true);
454             if (pinp->name() == "") pinp->name("__paramNumber" + cvtToStr(pinp->pinNum()));
455         }
456     }
457 
458     // Accelerate the recursion
459     // Must do statements to support Generates, math though...
visit(AstNodeMath *)460     virtual void visit(AstNodeMath*) override {}
visit(AstNode * nodep)461     virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
462 
463     // METHODS
readModNames()464     void readModNames() {
465         // mangled_name, BlockOptions
466         const V3HierBlockOptSet& hierBlocks = v3Global.opt.hierBlocks();
467         const auto hierIt = vlstd::as_const(hierBlocks).find(v3Global.opt.topModule());
468         UASSERT((hierIt != hierBlocks.end()) == v3Global.opt.hierChild(),
469                 "information of the top module must exist if --hierarchical-child is set");
470         // Look at all modules, and store pointers to all module names
471         for (AstNodeModule *nextp, *nodep = v3Global.rootp()->modulesp(); nodep; nodep = nextp) {
472             nextp = VN_AS(nodep->nextp(), NodeModule);
473             if (v3Global.opt.hierChild() && nodep->name() == hierIt->second.origName()) {
474                 nodep->name(hierIt->first);  // Change name of this module to be mangled name
475                                              // considering parameter
476             }
477             const AstNodeModule* const foundp = findModuleSym(nodep->name());
478             if (foundp && foundp != nodep) {
479                 if (!(foundp->fileline()->warnIsOff(V3ErrorCode::MODDUP)
480                       || nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP)
481                       || hierBlocks.find(nodep->name()) != hierBlocks.end())) {
482                     nodep->v3warn(MODDUP, "Duplicate declaration of module: "
483                                               << nodep->prettyNameQ() << '\n'
484                                               << nodep->warnContextPrimary() << '\n'
485                                               << foundp->warnOther()
486                                               << "... Location of original declaration\n"
487                                               << foundp->warnContextSecondary());
488                 }
489                 nodep->unlinkFrBack();
490                 VL_DO_DANGLING(pushDeletep(nodep), nodep);
491             } else if (!foundp) {
492                 m_mods.rootp()->insert(nodep->name(), new VSymEnt(&m_mods, nodep));
493             }
494         }
495         // if (debug() >= 9) m_mods.dump(cout, "-syms: ");
496     }
497 
498 public:
499     // CONSTRUCTORS
LinkCellsVisitor(AstNetlist * nodep,VInFilter * filterp,V3ParseSym * parseSymp)500     LinkCellsVisitor(AstNetlist* nodep, VInFilter* filterp, V3ParseSym* parseSymp)
501         : m_mods{nodep} {
502         m_filterp = filterp;
503         m_parseSymp = parseSymp;
504         if (v3Global.opt.hierChild()) {
505             const V3HierBlockOptSet& hierBlocks = v3Global.opt.hierBlocks();
506             UASSERT(!v3Global.opt.topModule().empty(),
507                     "top module must be explicitly specified in hierarchical mode");
508             const V3HierBlockOptSet::const_iterator hierIt
509                 = hierBlocks.find(v3Global.opt.topModule());
510             UASSERT(hierIt != hierBlocks.end(),
511                     "top module must be listed in --hierarchical-block");
512             m_origTopModuleName = hierIt->second.origName();
513         } else {
514             m_origTopModuleName = v3Global.opt.topModule();
515         }
516         iterate(nodep);
517     }
518     virtual ~LinkCellsVisitor() override = default;
519 };
520 
521 //######################################################################
522 // Link class functions
523 
link(AstNetlist * nodep,VInFilter * filterp,V3ParseSym * parseSymp)524 void V3LinkCells::link(AstNetlist* nodep, VInFilter* filterp, V3ParseSym* parseSymp) {
525     UINFO(4, __FUNCTION__ << ": " << endl);
526     { LinkCellsVisitor{nodep, filterp, parseSymp}; }
527 }
528