From d7b8ede53feaa0f507890966cf54cee829dcdae1 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 13 Sep 2025 22:07:13 -0400 Subject: [PATCH] Internals: Refactoring V3Param. No functional change intended. --- src/V3Ast.h | 2 +- src/V3AstNodeOther.h | 10 +- src/V3Param.cpp | 240 +++++++++++++++++++++++++------------------ 3 files changed, 146 insertions(+), 106 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 8a1f24e75..b9a88e947 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1899,7 +1899,7 @@ public: // nodes needs to be deferred to a later time, because pointers to the // removed nodes might still exist. -class VNDeleter final { +class VNDeleter VL_NOT_FINAL { // MEMBERS std::vector m_deleteps; // Nodes to delete diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index edacef89a..f2f7ff1a7 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -947,7 +947,7 @@ public: explicit AstConstPool(FileLine* fl); ASTGEN_MEMBERS_AstConstPool; bool maybePointedTo() const override VL_MT_SAFE { return true; } - void cloneRelink() override { V3ERROR_NA; } + void cloneRelink() override { UASSERT(!clonep(), "Not cloneable"); } AstModule* modp() const { return m_modp; } // Find a table (unpacked array) within the constant pool which is initialized with the @@ -1112,7 +1112,7 @@ public: explicit AstExecGraph(FileLine* fl, const string& name) VL_MT_DISABLED; ~AstExecGraph() override; ASTGEN_MEMBERS_AstExecGraph; - void cloneRelink() override { V3ERROR_NA; } + void cloneRelink() override { UASSERT(!clonep(), "Not cloneable"); } const char* broken() const override { BROKEN_RTN(!m_depGraphp); return nullptr; @@ -1166,7 +1166,7 @@ public: explicit AstMTaskBody(FileLine* fl) : ASTGEN_SUPER_MTaskBody(fl) {} ASTGEN_MEMBERS_AstMTaskBody; - void cloneRelink() override { V3ERROR_NA; } + void cloneRelink() override { UASSERT(!clonep(), "Not cloneable"); } const char* broken() const override { BROKEN_RTN(!m_execMTaskp); return nullptr; @@ -1269,7 +1269,7 @@ public: AstNetlist(); ASTGEN_MEMBERS_AstNetlist; void deleteContents(); - void cloneRelink() override { V3ERROR_NA; } + void cloneRelink() override { UASSERT(!clonep(), "Not cloneable"); } string name() const override VL_MT_STABLE { return "$root"; } void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; @@ -1669,7 +1669,7 @@ public: explicit AstTypeTable(FileLine* fl); ASTGEN_MEMBERS_AstTypeTable; bool maybePointedTo() const override VL_MT_SAFE { return true; } - void cloneRelink() override { V3ERROR_NA; } + void cloneRelink() override { UASSERT(!clonep(), "Not cloneable"); } AstBasicDType* findBasicDType(FileLine* fl, VBasicDTypeKwd kwd); AstBasicDType* findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, int width, int widthMin, VSigning numeric); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 228ad6b8b..0ae2d89a3 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -85,8 +85,8 @@ class ParameterizedHierBlocks final { HierBlockModMap m_hierBlockMod; // Overridden parameters of the hierarchical block std::map m_hierParams; - std::map - m_modParams; // Parameter variables of hierarchical blocks + // Parameter variables of hierarchical blocks + std::map m_modParams; // METHODS @@ -655,6 +655,7 @@ class ParamProcessor final { collectPins(clonemapp, newModp, srcModp->user3p()); // Relink parameter vars to the new module relinkPins(clonemapp, paramsp); + // Fix any interface references for (auto it = ifaceRefRefs.cbegin(); it != ifaceRefRefs.cend(); ++it) { const AstIfaceRefDType* const portIrefp = it->first; @@ -1165,13 +1166,13 @@ public: const string instanceName = someInstanceName + "." + nodep->name(); srcModpr->someInstanceName(instanceName); - if (auto* cellp = VN_CAST(nodep, Cell)) { + if (AstCell* cellp = VN_CAST(nodep, Cell)) { cellDeparam(cellp, srcModpr); - } else if (auto* ifaceRefDTypep = VN_CAST(nodep, IfaceRefDType)) { + } else if (AstIfaceRefDType* ifaceRefDTypep = VN_CAST(nodep, IfaceRefDType)) { ifaceRefDeparam(ifaceRefDTypep, srcModpr); - } else if (auto* classRefp = VN_CAST(nodep, ClassRefDType)) { + } else if (AstClassRefDType* classRefp = VN_CAST(nodep, ClassRefDType)) { classRefDeparam(classRefp, srcModpr); - } else if (auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) { + } else if (AstClassOrPackageRef* classRefp = VN_CAST(nodep, ClassOrPackageRef)) { classRefDeparam(classRefp, srcModpr); } else { nodep->v3fatalSrc("Expected module parameterization"); @@ -1198,7 +1199,8 @@ public: }; //###################################################################### -// This visitor records classes that are referenced with parameter pins +// Record classes that are referenced with parameter pins + class ClassRefUnlinkerVisitor final : public VNVisitor { public: @@ -1215,6 +1217,19 @@ public: void visit(AstNode* nodep) override { iterateChildren(nodep); } }; +//###################################################################### +// Process parameter top state + +class ParamState final { +public: + // STATE - across all visitors + std::vector m_paramClasses; // Parameterized classes + std::vector m_dots; // Dot references to process + std::multimap m_workQueueNext; // Modules left to process + // Map from AstNodeModule to set of all AstNodeModules that instantiates it. + std::unordered_map> m_parentps; +}; + //###################################################################### // Process parameter visitor @@ -1222,34 +1237,33 @@ class ParamVisitor final : public VNVisitor { // NODE STATE // AstNodeModule::user1 -> bool: already fixed level (temporary) - // STATE + // STATE - across all visitors + ParamState& m_state; // Common state ParamProcessor m_processor; // De-parameterize a cell, build modules UnrollStateful m_unroller; // Loop unroller bool m_iterateModule = false; // Iterating module body - string m_generateHierName; // Generate portion of hierarchy name string m_unlinkedTxt; // Text for AstUnlinkedRef - AstNodeModule* m_modp; // Module iterating - std::vector m_dots; // Dot references to process std::multimap m_cellps; // Cells left to process (in current module) - std::multimap m_workQueue; // Modules left to process - std::vector m_paramClasses; // Parameterized classes std::deque m_strings; // Allocator for temporary strings - // Map from AstNodeModule to set of all AstNodeModules that instantiates it. - std::unordered_map> m_parentps; + // STATE - for current visit position (use VL_RESTORER) + AstNodeModule* m_modp; // Module iterating + string m_generateHierName; // Generate portion of hierarchy name // METHODS - void visitCells(AstNodeModule* nodep) { - UASSERT_OBJ(!m_iterateModule, nodep, "Should not nest"); + void processWorkQ() { + UASSERT(!m_iterateModule, "Should not nest"); std::multimap workQueue; - workQueue.emplace(nodep->level(), nodep); m_generateHierName = ""; m_iterateModule = true; // Visit all cells under module, recursively - do { + while (true) { + if (workQueue.empty()) std::swap(workQueue, m_state.m_workQueueNext); + if (workQueue.empty()) break; + const auto itm = workQueue.cbegin(); AstNodeModule* const modp = itm->second; workQueue.erase(itm); @@ -1278,14 +1292,15 @@ class ParamVisitor final : public VNVisitor { m_cellps.erase(itim); AstNodeModule* srcModp = nullptr; - if (const auto* modCellp = VN_CAST(cellp, Cell)) { + if (const AstCell* modCellp = VN_CAST(cellp, Cell)) { srcModp = modCellp->modp(); - } else if (const auto* classRefp = VN_CAST(cellp, ClassOrPackageRef)) { + } else if (const AstClassOrPackageRef* classRefp + = VN_CAST(cellp, ClassOrPackageRef)) { srcModp = classRefp->classOrPackageSkipp(); if (VN_IS(classRefp->classOrPackageNodep(), ParamTypeDType)) continue; - } else if (const auto* classRefp = VN_CAST(cellp, ClassRefDType)) { + } else if (const AstClassRefDType* classRefp = VN_CAST(cellp, ClassRefDType)) { srcModp = classRefp->classp(); - } else if (const auto* ifaceRefp = VN_CAST(cellp, IfaceRefDType)) { + } else if (const AstIfaceRefDType* ifaceRefp = VN_CAST(cellp, IfaceRefDType)) { srcModp = ifaceRefp->ifacep(); } else { cellp->v3fatalSrc("Expected module parameterization"); @@ -1306,26 +1321,13 @@ class ParamVisitor final : public VNVisitor { workQueue.emplace(srcModp->level(), srcModp); // Add to the hierarchy registry - m_parentps[srcModp].insert(modp); + m_state.m_parentps[srcModp].insert(modp); } - if (workQueue.empty()) std::swap(workQueue, m_workQueue); - } while (!workQueue.empty()); + } m_iterateModule = false; } - // Fix up level of module, based on who instantiates it - void fixLevel(AstNodeModule* modp) { - if (modp->user1SetOnce()) return; // Already fixed - if (m_parentps[modp].empty()) return; // Leave top levels alone - int maxParentLevel = 0; - for (AstNodeModule* parentp : m_parentps[modp]) { - fixLevel(parentp); // Ensure parent level is correct - maxParentLevel = std::max(maxParentLevel, parentp->level()); - } - if (modp->level() <= maxParentLevel) modp->level(maxParentLevel + 1); - } - // A generic visitor for cells and class refs void visitCellOrClassRef(AstNode* nodep, bool isIface) { // Must do ifaces first, so push to list and do in proper order @@ -1336,21 +1338,6 @@ class ParamVisitor final : public VNVisitor { m_cellps.emplace(!isIface, nodep); } - // RHSs of AstDots need a relink when LHS is a parameterized class reference - void relinkDots() { - for (AstDot* const dotp : m_dots) { - const AstClassOrPackageRef* const classRefp = VN_AS(dotp->lhsp(), ClassOrPackageRef); - const AstClass* const lhsClassp = VN_AS(classRefp->classOrPackageSkipp(), Class); - AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef); - for (auto* itemp = lhsClassp->membersp(); itemp; itemp = itemp->nextp()) { - if (itemp->name() == rhsp->name()) { - rhsp->classOrPackageNodep(itemp); - break; - } - } - } - } - // VISITORS void visit(AstNodeModule* nodep) override { if (nodep->recursiveClone()) nodep->dead(true); // Fake, made for recursive elimination @@ -1359,15 +1346,16 @@ class ParamVisitor final : public VNVisitor { if (classp->hasGParam()) { // Don't enter into a definition. // If a class is used, it will be visited through a reference and cloned - m_paramClasses.push_back(classp); + m_state.m_paramClasses.push_back(classp); return; } } - if (m_iterateModule) { // Iterating from visitCells + if (m_iterateModule) { // Iterating from processWorkQ UINFO(4, " MOD-under-MOD. " << nodep); - m_workQueue.emplace(nodep->level(), nodep); // Delay until current module is done - // visitCells (which we are returning to) will process nodep from m_workQueue later + // Delay until current module is done. + // processWorkQ() (which we are returning to) will process nodep later + m_state.m_workQueueNext.emplace(nodep->level(), nodep); return; } @@ -1375,7 +1363,8 @@ class ParamVisitor final : public VNVisitor { if (nodep->level() <= 2 // Haven't added top yet, so level 2 is the top || VN_IS(nodep, Class) // Nor moved classes || VN_IS(nodep, Package)) { // Likewise haven't done wrapTopPackages yet - visitCells(nodep); + m_state.m_workQueueNext.emplace(nodep->level(), nodep); + processWorkQ(); } } @@ -1503,7 +1492,7 @@ class ParamVisitor final : public VNVisitor { AstClassOrPackageRef* const rhsp = VN_CAST(nodep->rhsp(), ClassOrPackageRef); if (rhsp) rhsDefp = rhsp->classOrPackageNodep(); if (lhsClassp && rhsDefp) { - m_dots.push_back(nodep); + m_state.m_dots.push_back(nodep); // No need to iterate into rhsp, because there should be nothing to do } else { iterate(nodep->rhsp()); @@ -1677,57 +1666,108 @@ class ParamVisitor final : public VNVisitor { public: // CONSTRUCTORS - explicit ParamVisitor(AstNetlist* netlistp) - : m_processor{netlistp} { + explicit ParamVisitor(ParamState& state, AstNetlist* netlistp) + : m_state{state} + , m_processor{netlistp} { // Relies on modules already being in top-down-order iterate(netlistp); + } + ~ParamVisitor() override = default; + VL_UNCOPYABLE(ParamVisitor); +}; - // Mark classes which cannot be removed because they are still referenced - ClassRefUnlinkerVisitor markVisitor{netlistp}; +//###################################################################### +// Process top handler - relinkDots(); +class ParamTop final : VNDeleter { + // NODE STATE + // AstNodeModule::user1 -> bool: already fixed level (temporary) - // Re-sort module list to be in topological order and fix-up incorrect levels. We need to - // do this globally at the end due to the presence of recursive modules, which might be - // expanded in orders that reuse earlier specializations later at a lower level. - { - // Gather modules - std::vector modps; - for (AstNodeModule *modp = netlistp->modulesp(), *nextp; modp; modp = nextp) { - nextp = VN_AS(modp->nextp(), NodeModule); - modp->unlinkFrBack(); - modps.push_back(modp); - } + // STATE + ParamState m_state; // Common state - // Fix-up levels - { - const VNUser1InUse user1InUse; - for (AstNodeModule* const modp : modps) fixLevel(modp); - } + // METHODS - // Sort by level - std::stable_sort(modps.begin(), modps.end(), - [](const AstNodeModule* ap, const AstNodeModule* bp) { - return ap->level() < bp->level(); - }); - - // Re-insert modules - for (AstNodeModule* const modp : modps) netlistp->addModulesp(modp); - - for (AstClass* const classp : m_paramClasses) { - if (!classp->user3p()) { - // The default value isn't referenced, so it can be removed - VL_DO_DANGLING(pushDeletep(classp->unlinkFrBack()), classp); - } else { - // Referenced. classp became a specialized class with the default - // values of parameters and is not a parameterized class anymore - classp->hasGParam(false); + void relinkDots() { + // RHSs of AstDots need a relink when LHS is a parameterized class reference + for (AstDot* const dotp : m_state.m_dots) { + const AstClassOrPackageRef* const classRefp = VN_AS(dotp->lhsp(), ClassOrPackageRef); + const AstClass* const lhsClassp = VN_AS(classRefp->classOrPackageSkipp(), Class); + AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef); + for (auto* itemp = lhsClassp->membersp(); itemp; itemp = itemp->nextp()) { + if (itemp->name() == rhsp->name()) { + rhsp->classOrPackageNodep(itemp); + break; } } } } - ~ParamVisitor() override = default; - VL_UNCOPYABLE(ParamVisitor); + + void fixLevel(AstNodeModule* modp) { + // Fix up level of module, based on who instantiates it + if (modp->user1SetOnce()) return; // Already fixed + if (m_state.m_parentps[modp].empty()) return; // Leave top levels alone + int maxParentLevel = 0; + for (AstNodeModule* parentp : m_state.m_parentps[modp]) { + fixLevel(parentp); // Ensure parent level is correct + maxParentLevel = std::max(maxParentLevel, parentp->level()); + } + if (modp->level() <= maxParentLevel) modp->level(maxParentLevel + 1); + } + + void resortNetlistModules(AstNetlist* netlistp) { + // Re-sort module list to be in topological order and fix-up incorrect levels. We need to + // do this globally at the end due to the presence of recursive modules, which might be + // expanded in orders that reuse earlier specializations later at a lower level. + // Gather modules + std::vector modps; + for (AstNodeModule *modp = netlistp->modulesp(), *nextp; modp; modp = nextp) { + nextp = VN_AS(modp->nextp(), NodeModule); + modp->unlinkFrBack(); + modps.push_back(modp); + } + + // Fix-up levels + const VNUser1InUse user1InUse; + for (AstNodeModule* const modp : modps) fixLevel(modp); + + // Sort by level + std::stable_sort(modps.begin(), modps.end(), + [](const AstNodeModule* ap, const AstNodeModule* bp) { + return ap->level() < bp->level(); + }); + + // Re-insert modules + for (AstNodeModule* const modp : modps) netlistp->addModulesp(modp); + } + +public: + // CONSTRUCTORS + explicit ParamTop(AstNetlist* netlistp) { + // Relies on modules already being in top-down-order + ParamVisitor paramVisitor{m_state /*ref*/, netlistp}; + + // Mark classes which cannot be removed because they are still referenced + ClassRefUnlinkerVisitor classUnlinkerVisitor{netlistp}; + + relinkDots(); + + resortNetlistModules(netlistp); + + // Remove defaulted classes + for (AstClass* const classp : m_state.m_paramClasses) { + if (!classp->user3p()) { + // The default value isn't referenced, so it can be removed + VL_DO_DANGLING(pushDeletep(classp->unlinkFrBack()), classp); + } else { + // Referenced. classp became a specialized class with the default + // values of parameters and is not a parameterized class anymore + classp->hasGParam(false); + } + } + } + ~ParamTop() = default; + VL_UNCOPYABLE(ParamTop); }; //###################################################################### @@ -1735,6 +1775,6 @@ public: void V3Param::param(AstNetlist* rootp) { UINFO(2, __FUNCTION__ << ":"); - { ParamVisitor{rootp}; } // Destruct before checking + { ParamTop{rootp}; } // Destruct before checking V3Global::dumpCheckGlobalTree("param", 0, dumpTreeEitherLevel() >= 3); }