From 12c524ac06654c2577861ff6970b2109e71c7fb1 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 14 Sep 2025 21:50:19 -0400 Subject: [PATCH] Internals: Refactoring (#6433 prep). No functional change intended. --- src/V3AstNodes.cpp | 5 +- src/V3LinkDot.cpp | 207 ++++++++++++++++++++++----------------------- src/V3Param.cpp | 29 ++++--- 3 files changed, 121 insertions(+), 120 deletions(-) diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index c458dca2f..7b02d6f77 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2230,9 +2230,12 @@ void AstRefDType::dump(std::ostream& str) const { static bool s_recursing = false; if (!s_recursing) { // Prevent infinite dump if circular typedefs s_recursing = true; + if (classOrPackagep()) str << " cpkg=" << nodeAddr(classOrPackagep()); + if (refDTypep()) str << " refDTypep=" << nodeAddr(refDTypep()); + if (typedefp()) str << " typedefp=" << nodeAddr(typedefp()); + str << " -> "; if (const AstNodeDType* const subp = subDTypep()) { - if (typedefp()) str << "typedef=" << static_cast(typedefp()) << " -> "; subp->dump(str); } else if (const AstTypedef* const subp = typedefp()) { subp->dump(str); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index f5673a393..c2f1e0486 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -909,6 +909,17 @@ class LinkDotFindVisitor final : public VNVisitor { return hierBlocks.find(name) != hierBlocks.end(); } + void moveExternFuncDecl(AstNodeFTask* nodep, AstClass* classp) { + // Move an 'extern function|task' to inside a class or, from an outer to inner-class + if (!nodep->isExternDef()) { + // Move it to proper spot under the target class + nodep->unlinkFrBack(); + classp->addStmtsp(nodep); + nodep->isExternDef(true); // So we check there's a matching extern + nodep->classOrPackagep()->unlinkFrBack()->deleteTree(); + } + } + // VISITORS void visit(AstNetlist* nodep) override { // FindVisitor:: // Process $unit or other packages @@ -1238,114 +1249,100 @@ class LinkDotFindVisitor final : public VNVisitor { VL_RESTORER(m_classOrPackagep); VL_RESTORER(m_curSymp); VSymEnt* upSymp = m_curSymp; - { - if (VN_IS(m_curSymp->nodep(), Class)) { - if (VN_AS(m_curSymp->nodep(), Class)->isInterfaceClass() && !nodep->pureVirtual() - && !nodep->isConstructor()) { - nodep->v3error("Interface class functions must be pure virtual" - << " (IEEE 1800-2023 8.26): " << nodep->prettyNameQ()); - } - if (m_statep->forPrimary() - && (nodep->name() == "randomize" || nodep->name() == "srandom")) { - nodep->v3error(nodep->prettyNameQ() - << " is a predefined class method; redefinition not allowed" - " (IEEE 1800-2023 18.6.3)"); - } + + if (VN_IS(m_curSymp->nodep(), Class)) { + if (VN_AS(m_curSymp->nodep(), Class)->isInterfaceClass() && !nodep->pureVirtual() + && !nodep->isConstructor()) { + nodep->v3error("Interface class functions must be pure virtual" + << " (IEEE 1800-2023 8.26): " << nodep->prettyNameQ()); } - // Change to appropriate package if extern declaration (vs definition) - if (nodep->classOrPackagep()) { - // class-in-class - AstDot* const dotp = VN_CAST(nodep->classOrPackagep(), Dot); - AstClassOrPackageRef* const cpackagerefp - = VN_CAST(nodep->classOrPackagep(), ClassOrPackageRef); - if (dotp) { - AstClassOrPackageRef* const lhsp = VN_AS(dotp->lhsp(), ClassOrPackageRef); - m_statep->resolveClassOrPackage(m_curSymp, lhsp, true, false, - "External definition :: reference"); - AstClass* const lhsclassp = VN_CAST(lhsp->classOrPackageSkipp(), Class); - if (!lhsclassp) { - nodep->v3error("Extern declaration's scope is not a defined class"); - } else { - m_curSymp = m_statep->getNodeSym(lhsclassp); - upSymp = m_curSymp; - AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef); - m_statep->resolveClassOrPackage(m_curSymp, rhsp, true, false, - "External definition :: reference"); - AstClass* const rhsclassp = VN_CAST(rhsp->classOrPackageSkipp(), Class); - if (!rhsclassp) { - nodep->v3error("Extern declaration's scope is not a defined class"); - } else { - m_curSymp = m_statep->getNodeSym(rhsclassp); - upSymp = m_curSymp; - if (!nodep->isExternDef()) { - // Move it to proper spot under the target class - nodep->unlinkFrBack(); - rhsclassp->addStmtsp(nodep); - nodep->isExternDef(true); // So we check there's a matching extern - nodep->classOrPackagep()->unlinkFrBack()->deleteTree(); - } - } - } - } else if (cpackagerefp) { - if (!cpackagerefp->classOrPackageSkipp()) { - m_statep->resolveClassOrPackage(m_curSymp, cpackagerefp, true, false, - "External definition :: reference"); - } - AstClass* const classp = VN_CAST(cpackagerefp->classOrPackageSkipp(), Class); - if (!classp) { - nodep->v3error("Extern declaration's scope is not a defined class"); - } else { - m_curSymp = m_statep->getNodeSym(classp); - upSymp = m_curSymp; - if (!nodep->isExternDef()) { - // Move it to proper spot under the target class - nodep->unlinkFrBack(); - classp->addStmtsp(nodep); - nodep->isExternDef(true); // So we check there's a matching extern - nodep->classOrPackagep()->unlinkFrBack()->deleteTree(); - } - } - } else { - v3fatalSrc("Unhandled extern function definition package"); - } + if (m_statep->forPrimary() + && (nodep->name() == "randomize" || nodep->name() == "srandom")) { + nodep->v3error(nodep->prettyNameQ() + << " is a predefined class method; redefinition not allowed" + " (IEEE 1800-2023 18.6.3)"); } - // Set the class as package for iteration - if (VN_IS(m_curSymp->nodep(), Class)) { - m_classOrPackagep = VN_AS(m_curSymp->nodep(), Class); - } - // Create symbol table for the task's vars - const string name = (nodep->isExternProto() ? "extern "s : ""s) + nodep->name(); - m_curSymp = m_statep->insertBlock(m_curSymp, name, nodep, m_classOrPackagep); - m_curSymp->fallbackp(upSymp); - // Convert the func's range to the output variable - // This should probably be done in the Parser instead, as then we could - // just attach normal signal attributes to it. - if (!nodep->isExternProto() && nodep->fvarp() && !VN_IS(nodep->fvarp(), Var)) { - AstNodeDType* dtypep = VN_CAST(nodep->fvarp(), NodeDType); - // If unspecified, function returns one bit; however when we - // support NEW() it could also return the class reference. - if (dtypep) { - dtypep->unlinkFrBack(); - } else { - dtypep = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::LOGIC}; - } - AstVar* const newvarp - = new AstVar{nodep->fileline(), VVarType::VAR, nodep->name(), - VFlagChildDType{}, dtypep}; // Not dtype resolved yet - newvarp->direction(VDirection::OUTPUT); - newvarp->lifetime(VLifetime::AUTOMATIC); - newvarp->funcReturn(true); - newvarp->trace(false); // Not user visible - newvarp->attrIsolateAssign(nodep->attrIsolateAssign()); - nodep->fvarp(newvarp); - // Explicit insert required, as the var name shadows the upper level's task name - m_statep->insertSym(m_curSymp, newvarp->name(), newvarp, - nullptr /*classOrPackagep*/); - } - VL_RESTORER(m_ftaskp); - m_ftaskp = nodep; - iterateChildren(nodep); } + // Change to appropriate package if extern declaration (vs definition) + if (nodep->classOrPackagep()) { + // class-in-class + AstDot* const dotp = VN_CAST(nodep->classOrPackagep(), Dot); + AstClassOrPackageRef* const cpackagerefp + = VN_CAST(nodep->classOrPackagep(), ClassOrPackageRef); + if (dotp) { + AstClassOrPackageRef* const lhsp = VN_AS(dotp->lhsp(), ClassOrPackageRef); + m_statep->resolveClassOrPackage(m_curSymp, lhsp, true, false, + "External definition :: reference"); + AstClass* const lhsclassp = VN_CAST(lhsp->classOrPackageSkipp(), Class); + if (!lhsclassp) { + nodep->v3error("Extern declaration's scope is not a defined class"); + } else { + m_curSymp = m_statep->getNodeSym(lhsclassp); + upSymp = m_curSymp; + AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef); + m_statep->resolveClassOrPackage(m_curSymp, rhsp, true, false, + "External definition :: reference"); + AstClass* const rhsclassp = VN_CAST(rhsp->classOrPackageSkipp(), Class); + if (!rhsclassp) { + nodep->v3error("Extern declaration's scope is not a defined class"); + } else { + m_curSymp = m_statep->getNodeSym(rhsclassp); + upSymp = m_curSymp; + moveExternFuncDecl(nodep, rhsclassp); + } + } + } else if (cpackagerefp) { + if (!cpackagerefp->classOrPackageSkipp()) { + m_statep->resolveClassOrPackage(m_curSymp, cpackagerefp, true, false, + "External definition :: reference"); + } + AstClass* const classp = VN_CAST(cpackagerefp->classOrPackageSkipp(), Class); + if (!classp) { + nodep->v3error("Extern declaration's scope is not a defined class"); + } else { + m_curSymp = m_statep->getNodeSym(classp); + upSymp = m_curSymp; + moveExternFuncDecl(nodep, classp); + } + } else { + v3fatalSrc("Unhandled extern function definition package"); + } + } + // Set the class as package for iteration + if (VN_IS(m_curSymp->nodep(), Class)) { + m_classOrPackagep = VN_AS(m_curSymp->nodep(), Class); + } + // Create symbol table for the task's vars + const string name = (nodep->isExternProto() ? "extern "s : ""s) + nodep->name(); + m_curSymp = m_statep->insertBlock(m_curSymp, name, nodep, m_classOrPackagep); + m_curSymp->fallbackp(upSymp); + // Convert the func's range to the output variable + // This should probably be done in the Parser instead, as then we could + // just attach normal signal attributes to it. + if (!nodep->isExternProto() && nodep->fvarp() && !VN_IS(nodep->fvarp(), Var)) { + AstNodeDType* dtypep = VN_CAST(nodep->fvarp(), NodeDType); + // If unspecified, function returns one bit; however when we + // support NEW() it could also return the class reference. + if (dtypep) { + dtypep->unlinkFrBack(); + } else { + dtypep = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::LOGIC}; + } + AstVar* const newvarp + = new AstVar{nodep->fileline(), VVarType::VAR, nodep->name(), VFlagChildDType{}, + dtypep}; // Not dtype resolved yet + newvarp->direction(VDirection::OUTPUT); + newvarp->lifetime(VLifetime::AUTOMATIC); + newvarp->funcReturn(true); + newvarp->trace(false); // Not user visible + newvarp->attrIsolateAssign(nodep->attrIsolateAssign()); + nodep->fvarp(newvarp); + // Explicit insert required, as the var name shadows the upper level's task name + m_statep->insertSym(m_curSymp, newvarp->name(), newvarp, nullptr /*classOrPackagep*/); + } + VL_RESTORER(m_ftaskp); + m_ftaskp = nodep; + iterateChildren(nodep); } void visit(AstClocking* nodep) override { // FindVisitor:: VL_RESTORER(m_clockingp); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index dd0c195ee..731d84f6d 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1004,32 +1004,32 @@ class ParamProcessor final { for (size_t paramIdx = pinsByIndex.size(); paramIdx < m_classParams.size(); paramIdx++) { const int sourceParamIdx = m_classParams[paramIdx].second; - AstPin* newPin = nullptr; + AstPin* newPinp = nullptr; // Case 1: Dependent default -> clone the source pin's type - if (sourceParamIdx >= 0) { newPin = pinsByIndex[sourceParamIdx]->cloneTree(false); } + if (sourceParamIdx >= 0) newPinp = pinsByIndex[sourceParamIdx]->cloneTree(false); // Case 2: Direct default type (e.g., int), create a new pin with that dtype - if (!newPin && defaultTypeNodes[paramIdx]) { + if (!newPinp && defaultTypeNodes[paramIdx]) { AstNodeDType* const dtypep = defaultTypeNodes[paramIdx]; - newPin = new AstPin{dtypep->fileline(), static_cast(paramIdx) + 1, - "__paramNumber" + cvtToStr(paramIdx + 1), - dtypep->cloneTree(false)}; + newPinp = new AstPin{dtypep->fileline(), static_cast(paramIdx) + 1, + "__paramNumber" + cvtToStr(paramIdx + 1), + dtypep->cloneTree(false)}; } - if (newPin) { - newPin->name("__paramNumber" + cvtToStr(paramIdx + 1)); - newPin->param(true); - newPin->modPTypep(m_classParams[paramIdx].first); + if (newPinp) { + newPinp->name("__paramNumber" + cvtToStr(paramIdx + 1)); + newPinp->param(true); + newPinp->modPTypep(m_classParams[paramIdx].first); if (classOrPackageRef) { - classOrPackageRef->addParamsp(newPin); + classOrPackageRef->addParamsp(newPinp); } else if (classRefDType) { - classRefDType->addParamsp(newPin); + classRefDType->addParamsp(newPinp); } // Update local tracking so future dependent defaults can find it pinsByIndex.resize(paramIdx + 1, nullptr); - pinsByIndex[paramIdx] = newPin; - if (!paramsp) paramsp = newPin; + pinsByIndex[paramIdx] = newPinp; + if (!paramsp) paramsp = newPinp; } } } @@ -1083,6 +1083,7 @@ class ParamProcessor final { AstNodeModule* nodeCopyp = srcModp->cloneTree(false); // It is a temporary copy of the original class node, stored in order to create // another instances. It is needed only during class instantiation. + UINFO(8, " Created clone " << nodeCopyp); m_deleter.pushDeletep(nodeCopyp); srcModp->user3p(nodeCopyp); storeOriginalParams(nodeCopyp);