From 566f4e6efc7625621c31a9a81983a9289dd82a5a Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 4 Jul 2026 17:27:07 +0100 Subject: [PATCH] Internals: Add new VL_RESTORER flavours (#7866) Restrict VL_RESTORER to be usable only with trivially copyable types. Introduce VL_RESTORER_COPY and VL_RESTORER_CLEAR, which are more efficient versions usable for non trivially copyable types. VL_RESTORER_COPY semantically behaves the same as VL_RESTORER, but only does one copy at initialization, and on scope exit restores via a move. VL_RESTORER_CLEAR swaps the variable with an new one constructed via the no-args constructor (e.g. empty collection), which does not require any copying at any point. Static assertions enforce picking one of the new flavours when copying might be expensive. --- src/V3Assert.cpp | 3 +- src/V3Begin.cpp | 36 ++++++++--------- src/V3Broken.cpp | 9 ++--- src/V3Class.cpp | 4 +- src/V3Coverage.cpp | 18 ++++----- src/V3Covergroup.cpp | 9 ++--- src/V3EmitV.cpp | 3 +- src/V3Fork.cpp | 3 +- src/V3Global.h | 90 ++++++++++++++++++++++++++++++++++++------ src/V3HierBlock.cpp | 9 ++--- src/V3Interface.cpp | 2 +- src/V3LibMap.cpp | 4 +- src/V3LinkCells.cpp | 4 +- src/V3LinkDot.cpp | 50 ++++++++++------------- src/V3LinkParse.cpp | 7 ++-- src/V3LinkWith.cpp | 16 ++++---- src/V3Param.cpp | 8 ++-- src/V3RandSequence.cpp | 10 ++--- src/V3Task.cpp | 3 +- src/V3TraceDecl.cpp | 8 ++-- src/V3Tristate.cpp | 6 +-- src/V3Udp.cpp | 6 +-- src/V3Width.cpp | 2 +- src/V3WidthCommit.cpp | 18 ++++----- 24 files changed, 180 insertions(+), 148 deletions(-) diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index ee18b24b4..e7724cfda 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -1153,12 +1153,11 @@ class AssertVisitor final : public VNVisitor { VL_RESTORER(m_modp); VL_RESTORER(m_modPastNum); VL_RESTORER(m_modStrobeNum); - VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp); VL_RESTORER(m_finalp); + VL_RESTORER_CLEAR(m_modExpr2Sen2DelayedAlwaysp); m_modp = nodep; m_modPastNum = 0; m_modStrobeNum = 0; - m_modExpr2Sen2DelayedAlwaysp.clear(); m_finalp = nullptr; iterateChildren(nodep); } diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 2ac71c770..5cce373b0 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -137,12 +137,9 @@ class BeginVisitor final : public VNVisitor { UINFO(8, " rename to " << nodep->name()); m_statep->userMarkChanged(nodep); } - VL_RESTORER(m_displayScope); - VL_RESTORER(m_namedScope); - VL_RESTORER(m_unnamedScope); - m_displayScope = ""; - m_namedScope = ""; - m_unnamedScope = ""; + VL_RESTORER_CLEAR(m_displayScope); + VL_RESTORER_CLEAR(m_namedScope); + VL_RESTORER_CLEAR(m_unnamedScope); iterateChildren(nodep); } void visit(AstNodeProcedure* nodep) override { @@ -163,14 +160,13 @@ class BeginVisitor final : public VNVisitor { // naming; so that any begin's inside the function will rename // inside the function. // Process children - VL_RESTORER(m_displayScope); + VL_RESTORER_COPY(m_displayScope); VL_RESTORER(m_ftaskp); VL_RESTORER(m_liftedp); - VL_RESTORER(m_namedScope); - VL_RESTORER(m_unnamedScope); + VL_RESTORER_CLEAR(m_namedScope); + VL_RESTORER_CLEAR(m_unnamedScope); m_displayScope = dot(m_displayScope, nodep->name()); - m_namedScope = ""; - m_unnamedScope = ""; + m_ftaskp = nodep; m_liftedp = nullptr; iterateChildren(nodep); @@ -191,9 +187,9 @@ class BeginVisitor final : public VNVisitor { void visit(AstGenBlock* nodep) override { // GenBlocks were only useful in variable creation, change names and delete UINFO(8, " " << nodep); - VL_RESTORER(m_displayScope); - VL_RESTORER(m_namedScope); - VL_RESTORER(m_unnamedScope); + VL_RESTORER_COPY(m_displayScope); + VL_RESTORER_COPY(m_namedScope); + VL_RESTORER_COPY(m_unnamedScope); UASSERT_OBJ(!m_keepBegins, nodep, "Should be able to eliminate all AstGenBlock"); dotNames(nodep->name(), nodep->fileline(), "__BEGIN__"); iterateAndNextNull(nodep->itemsp()); @@ -227,9 +223,9 @@ class BeginVisitor final : public VNVisitor { void visit(AstBegin* nodep) override { // Begin blocks were only useful in variable creation, change names and delete UINFO(8, " " << nodep); - VL_RESTORER(m_displayScope); - VL_RESTORER(m_namedScope); - VL_RESTORER(m_unnamedScope); + VL_RESTORER_COPY(m_displayScope); + VL_RESTORER_COPY(m_namedScope); + VL_RESTORER_COPY(m_unnamedScope); { VL_RESTORER(m_keepBegins); m_keepBegins = false; @@ -261,9 +257,9 @@ class BeginVisitor final : public VNVisitor { void visit(AstNodeBlock* nodep) override { // Begin/Fork blocks were only useful in variable creation, change names and delete UINFO(8, " " << nodep); - VL_RESTORER(m_displayScope); - VL_RESTORER(m_namedScope); - VL_RESTORER(m_unnamedScope); + VL_RESTORER_COPY(m_displayScope); + VL_RESTORER_COPY(m_namedScope); + VL_RESTORER_COPY(m_unnamedScope); { VL_RESTORER(m_keepBegins); m_keepBegins = VN_IS(nodep, Fork); diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index fab0077cc..ba5aa0135 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -252,18 +252,15 @@ private: void visit(AstScope* nodep) override { VL_RESTORER(m_inScope); m_inScope = true; - VL_RESTORER(m_cFuncNames); - m_cFuncNames.clear(); + VL_RESTORER_CLEAR(m_cFuncNames); processAndIterate(nodep); } void visit(AstNodeModule* nodep) override { - VL_RESTORER(m_cFuncNames); - m_cFuncNames.clear(); + VL_RESTORER_CLEAR(m_cFuncNames); processAndIterate(nodep); } void visit(AstNodeUOrStructDType* nodep) override { - VL_RESTORER(m_cFuncNames); - m_cFuncNames.clear(); + VL_RESTORER_CLEAR(m_cFuncNames); processAndIterate(nodep); } void visit(AstNodeVarRef* nodep) override { diff --git a/src/V3Class.cpp b/src/V3Class.cpp index 43f417313..2ccc1044b 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -114,7 +114,7 @@ class ClassVisitor final : public VNVisitor { classScopep->aboveScopep(), classScopep->aboveCellp()}; packagep->addStmtsp(scopep); // Iterate - VL_RESTORER(m_prefix); + VL_RESTORER_CLEAR(m_prefix); VL_RESTORER(m_classPackagep); VL_RESTORER(m_classScopep); VL_RESTORER(m_packageScopep); @@ -129,7 +129,7 @@ class ClassVisitor final : public VNVisitor { void visit(AstNodeModule* nodep) override { // Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule) // Classes are always under a Package (perhaps $unit) or a module - VL_RESTORER(m_prefix); + VL_RESTORER_CLEAR(m_prefix); VL_RESTORER(m_modp); m_modp = nodep; m_prefix = nodep->name() + "__03a__03a"; // :: diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 9c19319d8..9db38cb6e 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -279,8 +279,8 @@ class CoverageVisitor final : public VNVisitor { const AstNodeModule* const origModp = m_modp; VL_RESTORER(m_modp); VL_RESTORER(m_state); - VL_RESTORER(m_exprTempNames); - VL_RESTORER(m_funcTemps); + VL_RESTORER_COPY(m_exprTempNames); + VL_RESTORER_COPY(m_funcTemps); createHandle(nodep); m_modp = nodep; m_state.m_inModOff = false; // Haven't made top shell, so tops are real tops @@ -294,8 +294,8 @@ class CoverageVisitor final : public VNVisitor { void visit(AstClass* nodep) override { VL_RESTORER(m_modp); VL_RESTORER(m_state); - VL_RESTORER(m_exprTempNames); - VL_RESTORER(m_funcTemps); + VL_RESTORER_COPY(m_exprTempNames); + VL_RESTORER_COPY(m_funcTemps); createHandle(nodep); m_modp = nodep; // Covergroup declarations are not executable statements; suppress line/expr/toggle @@ -356,8 +356,8 @@ class CoverageVisitor final : public VNVisitor { void visit(AstNodeFTask* nodep) override { VL_RESTORER(m_ftaskp); - VL_RESTORER(m_exprTempNames); - VL_RESTORER(m_funcTemps); + VL_RESTORER_COPY(m_exprTempNames); + VL_RESTORER_COPY(m_funcTemps); m_ftaskp = nodep; if (!nodep->dpiImport()) iterateProcedure(nodep); } @@ -762,7 +762,7 @@ class CoverageVisitor final : public VNVisitor { } void visit(AstGenBlock* nodep) override { // Similar to AstBegin - VL_RESTORER(m_beginHier); + VL_RESTORER_COPY(m_beginHier); if (nodep->name() != "") { m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name(); } @@ -776,7 +776,7 @@ class CoverageVisitor final : public VNVisitor { // generate blocks; each point should get separate consideration. // (Currently ignored for line coverage, since any generate iteration // covers the code in that line.) - VL_RESTORER(m_beginHier); + VL_RESTORER_COPY(m_beginHier); VL_RESTORER(m_inToggleOff); VL_RESTORER(m_exprStmtsp); m_exprStmtsp = nodep; @@ -874,7 +874,7 @@ class CoverageVisitor final : public VNVisitor { UASSERT_OBJ(m_exprs.empty(), nodep, "unexpected expression coverage garbage"); VL_RESTORER(m_seeking); VL_RESTORER(m_objective); - VL_RESTORER(m_exprs); + VL_RESTORER_CLEAR(m_exprs); // Already asserted above it's empty. m_seeking = SEEKING; m_objective = false; diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index b1890e001..f243b0c33 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -1727,15 +1727,12 @@ class FunctionalCoverageVisitor final : public VNVisitor { VL_RESTORER(m_covergroupp); VL_RESTORER(m_sampleFuncp); VL_RESTORER(m_constructorp); - VL_RESTORER(m_coverpoints); - VL_RESTORER(m_coverpointMap); - VL_RESTORER(m_coverCrosses); + VL_RESTORER_CLEAR(m_coverpoints); + VL_RESTORER_CLEAR(m_coverpointMap); + VL_RESTORER_CLEAR(m_coverCrosses); m_covergroupp = nodep; m_sampleFuncp = nullptr; m_constructorp = nullptr; - m_coverpoints.clear(); - m_coverpointMap.clear(); - m_coverCrosses.clear(); // Extract and store the clocking event from AstCovergroup node // The parser creates this node to preserve the event information diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index db0798e38..405204084 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -1050,8 +1050,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { if (nodep->packed()) puts("packed "); { puts("{\n"); - VL_RESTORER(m_packedps); - m_packedps.clear(); + VL_RESTORER_CLEAR(m_packedps); for (AstMemberDType* itemp = nodep->membersp(); itemp; itemp = VN_AS(itemp->nextp(), MemberDType)) { iterateConst(itemp); diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 0071d8f75..ee02c0781 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -561,10 +561,9 @@ class ForkVisitor final : public VNVisitor { // replace body with a call to that task. Returns true iff wrapped. bool taskify(AstBegin* beginp) { // Visit statement to gather variables (And recursively process) - VL_RESTORER(m_forkLocalsp); + VL_RESTORER_CLEAR(m_forkLocalsp); VL_RESTORER(m_capturedVarsp); VL_RESTORER(m_capturedArgsp); - m_forkLocalsp.clear(); m_capturedVarsp = nullptr; m_capturedArgsp = nullptr; iterate(beginp); diff --git a/src/V3Global.h b/src/V3Global.h index 4825a540a..08f9db3a7 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -44,27 +45,90 @@ class V3ThreadPool; //====================================================================== // Restorer -/// Save a given variable's value on the stack, restoring it at end-of-scope. -// Object must be named, or it will not persist until end-of-scope. -// Constructor needs () or GCC 4.8 false warning. -#define VL_RESTORER(var) const VRestorer> restorer_##var(var); -/// Get the copy of the variable previously saved by VL_RESTORER() +// For all flavours, 'var' must be a named scalar variable (simple identifier). + +// Copy given variable's value on the stack, copy saved value back at end-of-scope. +// Only usable for trivially copyable types; use VL_RESTORER_COPY/VL_RESTORER_CLEAR for +// non-trivially copyable types, which are more efficient. +#define VL_RESTORER(var) \ + const VRestorerTrivial> restorer_##var(var); + +// This is the equivalent of VL_RESTORER for non-trivially copyable types. Still more efficient +// than VL_RESTORER as uses move to restore. +#define VL_RESTORER_COPY(var) \ + const VRestorerCopy> restorer_##var(var); + +// Swap 'var' with a no-args constructed object of the same type, effectively clearing it, then +// swap back at end-of-scope. No copying involved. Use this e.g. for std containers where they +// would be .clear()'d right after the VL_RESTORER instance. +#define VL_RESTORER_CLEAR(var) \ + const VRestorerClear> restorer_##var(var); + +// Get const reference to the saved copy #define VL_RESTORER_PREV(var) restorer_##var.saved() -// Object used by VL_RESTORER. This object must be an auto variable, not -// allocated on the heap or otherwise. +// Implementation of VL_RESTORER template -class VRestorer final { +class VRestorerTrivial final { + static_assert(std::is_trivially_copyable::value, + "Use VL_RESTORER_{COPY,CLEAR} for non trivially copyable types"); T& m_ref; // Reference to object we're saving and restoring const T m_saved; // Value saved, for later restore public: - explicit VRestorer(T& permr) - : m_ref{permr} - , m_saved{permr} {} - ~VRestorer() { m_ref = m_saved; } + explicit VRestorerTrivial(T& val) + : m_ref{val} + , m_saved{val} {} + ~VRestorerTrivial() { m_ref = m_saved; } + VL_UNCOPYABLE(VRestorerTrivial); + // Must be stack allocated + void* operator new(size_t) = delete; + void operator delete(void*) = delete; + + const T& saved() const { return m_saved; } +}; + +// Implementation of VL_RESTORER_COPY +template +class VRestorerCopy final { + static_assert(!std::is_trivially_copyable::value, + "Use VL_RESTORER for trivially copyable types"); + T& m_ref; // Reference to object we're saving and restoring + T m_saved; // Value saved, for later restore + +public: + explicit VRestorerCopy(T& val) + : m_ref{val} + , m_saved{val} {} + ~VRestorerCopy() { m_ref = std::move(m_saved); } + VL_UNCOPYABLE(VRestorerCopy); + // Must be stack allocated + void* operator new(size_t) = delete; + void operator delete(void*) = delete; + + const T& saved() const { return m_saved; } +}; + +// Implementation of VL_RESTORER_CLEAR. +template +class VRestorerClear final { + static_assert(!std::is_trivially_copyable::value, + "Use VL_RESTORER for trivially copyable types"); + T& m_ref; // Reference to object we're saving and restoring + T m_saved{}; // Owns the swapped-out value; starts empty + +public: + explicit VRestorerClear(T& val) + : m_ref{val} { + std::swap(m_ref, m_saved); + } + ~VRestorerClear() { std::swap(m_ref, m_saved); } + VL_UNCOPYABLE(VRestorerClear); + // Must be stack allocated + void* operator new(size_t) = delete; + void operator delete(void*) = delete; + const T& saved() const { return m_saved; } - VL_UNCOPYABLE(VRestorer); }; //###################################################################### diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index 79bd3667c..8a77354ce 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -331,12 +331,9 @@ class HierBlockUsageCollectVisitor final : public VNVisitorConst { } // This is a hierarchical block, gather parts - VL_RESTORER(m_params); - VL_RESTORER(m_typeParams); - VL_RESTORER(m_childrenp); - m_params.clear(); - m_typeParams.clear(); - m_childrenp.clear(); + VL_RESTORER_CLEAR(m_params); + VL_RESTORER_CLEAR(m_typeParams); + VL_RESTORER_CLEAR(m_childrenp); iterateChildrenConst(nodep); // Create the graph vertex for this hier block V3HierBlock* const blockp = new V3HierBlock{m_graphp, nodep, m_params, m_typeParams}; diff --git a/src/V3Interface.cpp b/src/V3Interface.cpp index 51cc2d11e..0d5fe47fb 100644 --- a/src/V3Interface.cpp +++ b/src/V3Interface.cpp @@ -43,7 +43,7 @@ class InlineIntfRefVisitor final : public VNVisitor { // VISITORS void visit(AstNetlist* nodep) override { iterateChildren(nodep->topModulep()); } void visit(AstCell* nodep) override { - VL_RESTORER(m_scope); + VL_RESTORER_COPY(m_scope); if (m_scope.empty()) { m_scope = nodep->name(); } else { diff --git a/src/V3LibMap.cpp b/src/V3LibMap.cpp index 5a2e153b3..619073551 100644 --- a/src/V3LibMap.cpp +++ b/src/V3LibMap.cpp @@ -47,8 +47,8 @@ class LibMapVisitor final : public VNVisitor { } void visit(AstLibrary* nodep) override { - VL_RESTORER(m_lib); - VL_RESTORER(m_rel); + VL_RESTORER_CLEAR(m_lib); + VL_RESTORER_CLEAR(m_rel); m_lib = nodep->name(); m_rel = V3Os::filenameDir(nodep->fileline()->filename()); iterateChildren(nodep); diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 4393a6955..6c4c637a0 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -157,11 +157,11 @@ class LinkConfigsVisitor final : public VNVisitor { m_isDefault = true; iterateAndNextNull(nodep->usep()); } else if (nodep->isCell()) { - VL_RESTORER(m_cell); + VL_RESTORER_CLEAR(m_cell); m_cell = nodep->cellp()->name(); iterateAndNextNull(nodep->usep()); } else { - VL_RESTORER(m_hierInst); + VL_RESTORER_COPY(m_hierInst); { VL_RESTORER(m_dotp); m_dotp = VN_AS(nodep->cellp(), Dot); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 33e2d38b5..0bb78f8e8 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1259,7 +1259,7 @@ class LinkDotFindVisitor final : public VNVisitor { const bool standalonePkg = !m_modSymp && (m_statep->forPrearray() && VN_IS(nodep, Package)); const bool doit = (m_modSymp || standalonePkg); - VL_RESTORER(m_scope); + VL_RESTORER_COPY(m_scope); VL_RESTORER(m_classOrPackagep); VL_RESTORER(m_modSymp); VL_RESTORER(m_curSymp); @@ -1335,7 +1335,7 @@ class LinkDotFindVisitor final : public VNVisitor { void visit(AstClass* nodep) override { // FindVisitor:: UASSERT_OBJ(m_curSymp, nodep, "Class not under module/package/$unit"); UINFO(8, " " << nodep); - VL_RESTORER(m_scope); + VL_RESTORER_COPY(m_scope); VL_RESTORER(m_classOrPackagep); VL_RESTORER(m_modSymp); VL_RESTORER(m_curSymp); @@ -1384,7 +1384,7 @@ class LinkDotFindVisitor final : public VNVisitor { if (nodep->recursive() && m_inRecursion) return; iterateChildren(nodep); // Recurse in, preserving state - VL_RESTORER(m_scope); + VL_RESTORER_COPY(m_scope); VL_RESTORER(m_modSymp); VL_RESTORER(m_curSymp); VL_RESTORER(m_paramNum); @@ -3584,7 +3584,7 @@ class LinkDotResolveVisitor final : public VNVisitor { void symIterateChildren(AstNode* nodep, VSymEnt* symp) { // Iterate children, changing to given context, with restore to old context - VL_RESTORER(m_ds); + VL_RESTORER_COPY(m_ds); VL_RESTORER(m_curSymp); m_curSymp = symp; m_ds.init(m_curSymp); @@ -3592,7 +3592,7 @@ class LinkDotResolveVisitor final : public VNVisitor { } void symIterateNull(AstNode* nodep, VSymEnt* symp) { // Iterate node, changing to given context, with restore to old context - VL_RESTORER(m_ds); + VL_RESTORER_COPY(m_ds); VL_RESTORER(m_curSymp); m_curSymp = symp; m_ds.init(m_curSymp); @@ -3805,10 +3805,8 @@ class LinkDotResolveVisitor final : public VNVisitor { LINKDOT_VISIT_START(); UINFO(5, indent() << "visit " << nodep); checkNoDot(nodep); - VL_RESTORER(m_usedPins); - VL_RESTORER(m_usedDefParamPins); - m_usedPins.clear(); - m_usedDefParamPins.clear(); + VL_RESTORER_CLEAR(m_usedPins); + VL_RESTORER_CLEAR(m_usedDefParamPins); UASSERT_OBJ(nodep->modp(), nodep, "Instance has unlinked module"); // V3LinkCell should have errored out VL_RESTORER(m_cellp); @@ -3845,10 +3843,8 @@ class LinkDotResolveVisitor final : public VNVisitor { LINKDOT_VISIT_START(); UINFO(5, indent() << "visit " << nodep); // Can be under dot if called as package::class and that class resolves, so no checkNoDot - VL_RESTORER(m_usedPins); - VL_RESTORER(m_usedDefParamPins); - m_usedPins.clear(); - m_usedDefParamPins.clear(); + VL_RESTORER_CLEAR(m_usedPins); + VL_RESTORER_CLEAR(m_usedDefParamPins); UASSERT_OBJ(nodep->classp(), nodep, "ClassRef has unlinked class"); UASSERT_OBJ(m_statep->forPrimary() || !nodep->paramsp() || V3Error::errorCount(), nodep, "class reference parameter not removed by V3Param"); @@ -4753,14 +4749,12 @@ class LinkDotResolveVisitor final : public VNVisitor { LINKDOT_VISIT_START(); UINFO(8, indent() << "visit " << nodep); UINFO(9, indent() << m_ds.ascii()); - VL_RESTORER(m_usedPins); - VL_RESTORER(m_usedDefParamPins); - m_usedPins.clear(); - m_usedDefParamPins.clear(); + VL_RESTORER_CLEAR(m_usedPins); + VL_RESTORER_CLEAR(m_usedDefParamPins); UASSERT_OBJ(m_statep->forPrimary() || !nodep->paramsp(), nodep, "class reference parameter not removed by V3Param"); { - VL_RESTORER(m_ds); + VL_RESTORER_COPY(m_ds); VL_RESTORER(m_pinSymp); if (!nodep->classOrPackageSkipp() && nodep->name() != "local::") { @@ -4834,7 +4828,7 @@ class LinkDotResolveVisitor final : public VNVisitor { } } VL_RESTORER(m_curSymp); - VL_RESTORER(m_ds); + VL_RESTORER_COPY(m_ds); m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep); iterateChildren(nodep); } @@ -5054,7 +5048,7 @@ class LinkDotResolveVisitor final : public VNVisitor { // Created here so should already be resolved. LINKDOT_VISIT_START(); UINFO(5, indent() << "visit " << nodep); - VL_RESTORER(m_ds); + VL_RESTORER_COPY(m_ds); VL_RESTORER(m_randSymp); VL_RESTORER(m_randMethodCallp); { @@ -5509,7 +5503,7 @@ class LinkDotResolveVisitor final : public VNVisitor { checkNoDot(nodep); { VL_RESTORER(m_curSymp); - VL_RESTORER(m_ds); + VL_RESTORER_COPY(m_ds); if (nodep->name() != "") { m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep); UINFO(5, indent() << "cur=se" << cvtToHex(m_curSymp)); @@ -5524,7 +5518,7 @@ class LinkDotResolveVisitor final : public VNVisitor { checkNoDot(nodep); { VL_RESTORER(m_curSymp); - VL_RESTORER(m_ds); + VL_RESTORER_COPY(m_ds); if (nodep->name() != "") { m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep); UINFO(5, indent() << "cur=se" << cvtToHex(m_curSymp)); @@ -5643,7 +5637,7 @@ class LinkDotResolveVisitor final : public VNVisitor { checkNoDot(nodep); VL_RESTORER(m_curSymp); VL_RESTORER(m_currentWithp); - VL_RESTORER(m_restrictedNamesUsed); + VL_RESTORER_COPY(m_restrictedNamesUsed); { m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep); m_currentWithp = nodep; @@ -5813,7 +5807,7 @@ class LinkDotResolveVisitor final : public VNVisitor { VL_RESTORER(m_curSymp); VL_RESTORER(m_modSymp); VL_RESTORER(m_modp); - VL_RESTORER(m_ifClassImpNames); + VL_RESTORER_COPY(m_ifClassImpNames); VL_RESTORER(m_insideClassExtParam); { m_ds.init(m_curSymp); @@ -6169,7 +6163,7 @@ class LinkDotResolveVisitor final : public VNVisitor { void visit(AstDisable* nodep) override { LINKDOT_VISIT_START(); checkNoDot(nodep); - VL_RESTORER(m_ds); + VL_RESTORER_COPY(m_ds); m_ds.init(m_curSymp); m_ds.m_dotPos = DP_FIRST; m_ds.m_disablep = nodep; @@ -6249,10 +6243,8 @@ class LinkDotResolveVisitor final : public VNVisitor { UASSERT_OBJ(ifacep, nodep, "Port parameters of AstIfaceRefDType without ifacep()"); if (ifacep->dead()) return; checkNoDot(nodep); - VL_RESTORER(m_usedPins); - VL_RESTORER(m_usedDefParamPins); - m_usedPins.clear(); - m_usedDefParamPins.clear(); + VL_RESTORER_CLEAR(m_usedPins); + VL_RESTORER_CLEAR(m_usedDefParamPins); VL_RESTORER(m_pinSymp); m_pinSymp = m_statep->getNodeSym(ifacep); iterateAndNextNull(nodep->paramsp()); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 5403d77c2..3e3565842 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -273,7 +273,7 @@ class LinkParseVisitor final : public VNVisitor { << nodep->verilogKwd() << "'"); } - VL_RESTORER(m_portDups); + VL_RESTORER_COPY(m_portDups); collectPorts(nodep->stmtsp()); iterateChildren(nodep); @@ -763,12 +763,12 @@ class LinkParseVisitor final : public VNVisitor { VL_RESTORER(m_genblkAbove); VL_RESTORER(m_genblkNum); VL_RESTORER(m_beginDepth); - VL_RESTORER(m_implTypedef); VL_RESTORER(m_lifetime); VL_RESTORER(m_lifetimeAllowed); VL_RESTORER(m_moduleWithGenericIface); VL_RESTORER(m_randSequenceNum); VL_RESTORER(m_valueModp); + VL_RESTORER_CLEAR(m_implTypedef); // Module: Create sim table for entire module and iterate cleanFileline(nodep); @@ -780,7 +780,6 @@ class LinkParseVisitor final : public VNVisitor { m_genblkAbove = 0; m_genblkNum = 0; m_beginDepth = 0; - m_implTypedef.clear(); m_valueModp = nodep; m_lifetime = nodep->lifetime().makeImplicit(); m_lifetimeAllowed = VN_IS(nodep, Class); @@ -797,7 +796,7 @@ class LinkParseVisitor final : public VNVisitor { "Verilator top-level internals"); } - VL_RESTORER(m_portDups); + VL_RESTORER_COPY(m_portDups); collectPorts(nodep->stmtsp()); iterateChildren(nodep); diff --git a/src/V3LinkWith.cpp b/src/V3LinkWith.cpp index 8ea84a3f4..f93d8d589 100644 --- a/src/V3LinkWith.cpp +++ b/src/V3LinkWith.cpp @@ -32,7 +32,7 @@ class LinkWithVisitor final : public VNVisitor { // STATE // Below state needs to be preserved between each module call. - string m_randcIllegalWhy; // Why randc illegal + const char* m_randcIllegalWhyp = nullptr; // Why randc illegal AstNode* m_randcIllegalp = nullptr; // Node causing randc illegal AstNodeExpr* m_currentRandomizeSelectp = nullptr; // fromp() of current `randomize()` call bool m_inRandomizeWith = false; // If in randomize() with (and no other with afterwards) @@ -49,24 +49,24 @@ class LinkWithVisitor final : public VNVisitor { iterateChildren(nodep); } void visit(AstConstraintBefore* nodep) override { - VL_RESTORER(m_randcIllegalWhy); + VL_RESTORER(m_randcIllegalWhyp); VL_RESTORER(m_randcIllegalp); - m_randcIllegalWhy = "'solve before' (IEEE 1800-2023 18.5.9)"; + m_randcIllegalWhyp = "'solve before' (IEEE 1800-2023 18.5.9)"; m_randcIllegalp = nodep; iterateChildrenConst(nodep); } void visit(AstDist* nodep) override { - VL_RESTORER(m_randcIllegalWhy); + VL_RESTORER(m_randcIllegalWhyp); VL_RESTORER(m_randcIllegalp); - m_randcIllegalWhy = "'constraint dist' (IEEE 1800-2023 18.5.3)"; + m_randcIllegalWhyp = "'constraint dist' (IEEE 1800-2023 18.5.3)"; m_randcIllegalp = nodep; iterateChildrenConst(nodep); } void visit(AstConstraintExpr* nodep) override { - VL_RESTORER(m_randcIllegalWhy); + VL_RESTORER(m_randcIllegalWhyp); VL_RESTORER(m_randcIllegalp); if (nodep->isSoft()) { - m_randcIllegalWhy = "'constraint soft' (IEEE 1800-2023 18.5.13.1)"; + m_randcIllegalWhyp = "'constraint soft' (IEEE 1800-2023 18.5.13.1)"; m_randcIllegalp = nodep; } iterateChildrenConst(nodep); @@ -76,7 +76,7 @@ class LinkWithVisitor final : public VNVisitor { if (nodep->varp()) { // Else due to dead code, might not have var pointer if (nodep->varp()->isRandC() && m_randcIllegalp) { nodep->v3error("Randc variables not allowed in " - << m_randcIllegalWhy << '\n' + << m_randcIllegalWhyp << '\n' << nodep->warnContextPrimary() << '\n' << m_randcIllegalp->warnOther() << "... Location of restricting expression\n" diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 0cf109211..72c33a6e7 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -2571,11 +2571,9 @@ class ParamVisitor final : public VNVisitor { // Iterate the body { VL_RESTORER(m_modp); - VL_RESTORER(m_ifacePortNames); - VL_RESTORER(m_ifaceInstCells); + VL_RESTORER_CLEAR(m_ifacePortNames); + VL_RESTORER_CLEAR(m_ifaceInstCells); m_modp = modp; - m_ifacePortNames.clear(); - m_ifaceInstCells.clear(); iterateChildren(modp); } } @@ -3241,7 +3239,7 @@ class ParamVisitor final : public VNVisitor { // Note this clears nodep->genforp(), so begin is no longer special } } else { - VL_RESTORER(m_generateHierName); + VL_RESTORER_COPY(m_generateHierName); m_generateHierName += "." + nodep->prettyName(); iterateChildren(nodep); } diff --git a/src/V3RandSequence.cpp b/src/V3RandSequence.cpp index f8f6b155c..b56215bac 100644 --- a/src/V3RandSequence.cpp +++ b/src/V3RandSequence.cpp @@ -136,7 +136,7 @@ class RandSequenceVisitor final : public VNVisitor { m_modp->addStmtsp(taskp); // Create local (not output) break variable - VL_RESTORER(m_localizeRemaps); + VL_RESTORER_COPY(m_localizeRemaps); AstVar* breakVarp = newBreakVar(nodep->fileline(), false); taskp->addStmtsp(breakVarp); @@ -417,9 +417,9 @@ class RandSequenceVisitor final : public VNVisitor { m_rsp = nodep; VL_RESTORER(m_startProdp); - VL_RESTORER(m_localizes); - VL_RESTORER(m_localizeNames); - VL_RESTORER(m_localizeRemaps); + VL_RESTORER_COPY(m_localizes); + VL_RESTORER_COPY(m_localizeNames); + VL_RESTORER_COPY(m_localizeRemaps); findLocalizes(nodep); // Find first production @@ -482,7 +482,7 @@ class RandSequenceVisitor final : public VNVisitor { // TODO we could do this only if break exists in the downstream production, // but as-is we'll optimize it away in most cases anyways VL_RESTORER(m_breakVarp); - VL_RESTORER(m_localizeRemaps); + VL_RESTORER_COPY(m_localizeRemaps); m_breakVarp = newBreakVar(nodep->fileline(), true); m_prodFuncp->addStmtsp(m_breakVarp); diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 0db8d04d3..72e779d1e 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -306,10 +306,9 @@ private: } void visit(AstClass* nodep) override { // Move initial statements into the constructor - VL_RESTORER(m_initialps); + VL_RESTORER_CLEAR(m_initialps); VL_RESTORER(m_ctorp); VL_RESTORER(m_classp); - m_initialps.clear(); m_ctorp = nullptr; m_classp = nodep; { // Find m_initialps, m_ctor diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 1a1ad6b89..29c625f94 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -485,7 +485,7 @@ class TraceDeclVisitor final : public VNVisitor { AstNodeDType* const skipTypep = nodep->skipRefp(); // offset and direction args added in EmitCImp std::string callArgs{"tracep, \"" + VIdProtect::protect(m_traName) + "\""}; - VL_RESTORER(m_traName); + VL_RESTORER_COPY(m_traName); FileLine* const flp = skipTypep->fileline(); const DtypeFuncKey dtypeKey{skipTypep, m_traVscp->varp()->varType()}; @@ -530,7 +530,7 @@ class TraceDeclVisitor final : public VNVisitor { void declUnpackedArray(AstUnpackArrayDType* const nodep, bool newFunc) { string prefixName(newFunc ? "name" : m_traName); - VL_RESTORER(m_traName); + VL_RESTORER_COPY(m_traName); FileLine* const flp = nodep->fileline(); addToSubFunc(new AstTracePushPrefix{flp, prefixName, VTracePrefixType::ARRAY_UNPACKED, @@ -568,7 +568,7 @@ class TraceDeclVisitor final : public VNVisitor { string prefixName(newFunc ? "name" : m_traName); AstNodeDType* const subtypep = nodep->subDTypep()->skipRefToEnump(); - VL_RESTORER(m_traName); + VL_RESTORER_COPY(m_traName); FileLine* const flp = nodep->fileline(); addToSubFunc(new AstTracePushPrefix{flp, prefixName, VTracePrefixType::ARRAY_PACKED, @@ -918,7 +918,7 @@ class TraceDeclVisitor final : public VNVisitor { return; } - VL_RESTORER(m_traName); + VL_RESTORER_COPY(m_traName); FileLine* const flp = nodep->fileline(); int nMembers = 0; diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 5bc72076b..226c5138e 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -2191,8 +2191,8 @@ class TristateVisitor final : public TristateBaseVisitor { VL_RESTORER(m_modp); VL_RESTORER(m_graphing); VL_RESTORER(m_unique); - VL_RESTORER(m_lhsmap); - VL_RESTORER(m_assigns); + VL_RESTORER_CLEAR(m_lhsmap); + VL_RESTORER_CLEAR(m_assigns); // Not preserved, needs pointer instead: TristateGraph origTgraph = m_tgraph; UASSERT_OBJ(m_tgraph.empty(), nodep, "Unsupported: NodeModule under NodeModule"); @@ -2201,8 +2201,6 @@ class TristateVisitor final : public TristateBaseVisitor { m_tgraph.clearAndCheck(); m_unique = 0; m_logicp = nullptr; - m_lhsmap.clear(); - m_assigns.clear(); m_modp = nodep; // Walk the graph, finding all variables and tristate constructs m_graphing = true; diff --git a/src/V3Udp.cpp b/src/V3Udp.cpp index 7234061ed..2ee5908bb 100644 --- a/src/V3Udp.cpp +++ b/src/V3Udp.cpp @@ -56,14 +56,12 @@ class UdpVisitor final : public VNVisitor { VL_RESTORER(m_primp); VL_RESTORER(m_outputInitVerfp); VL_RESTORER(m_isFirstOutput); - VL_RESTORER(m_inputVars); - VL_RESTORER(m_outputVars); + VL_RESTORER_CLEAR(m_inputVars); + VL_RESTORER_CLEAR(m_outputVars); m_outputInitVerfp = nullptr; m_primp = nodep; m_isFirstOutput = false; iterateChildren(nodep); - m_inputVars.clear(); - m_outputVars.clear(); } void visit(AstVar* nodep) override { // Push the input and output vars for primitive. diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a99b293ed..acc20248d 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -7852,7 +7852,7 @@ class WidthVisitor final : public VNVisitor { } void visit(AstNodeModule* nodep) override { assertAtStatement(nodep); - VL_RESTORER(m_insideTempNames); + VL_RESTORER_COPY(m_insideTempNames); if (AstClass* const classp = VN_CAST(nodep, Class)) { visitClass(classp); } else { diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 651bff963..900474423 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -47,7 +47,7 @@ class WidthCommitVisitor final : public VNVisitor { // STATE AstNodeFTask* m_ftaskp = nullptr; // Current function/task AstNodeModule* m_modp = nullptr; // Current module - std::string m_contNba; // In continuous- or non-blocking assignment + const char* m_contNbap = nullptr; // In continuous- or non-blocking assignment bool m_contReads = false; // Check read continuous automatic variables bool m_dynsizedelem = false; // Writing dynamically-sized array element, not the array itself VMemberMap m_memberMap; // Member names cached for fast lookup @@ -148,7 +148,7 @@ private: void varLifetimeCheck(AstNode* nodep, AstVar* varp) { // Skip if we are under a member select (lhs of a dot) // We don't care about lifetime of anything else than rhs of a dot - if (!m_underSel && !m_contNba.empty()) { + if (!m_underSel && m_contNbap) { std::string varType; const AstNodeDType* const varDtp = varp->dtypep()->skipRefp(); if (varp->lifetime().isAutomatic() && !VN_IS(varDtp, IfaceRefDType) @@ -162,7 +162,7 @@ private: if (!varType.empty()) { UINFO(1, " Related var dtype: " << varDtp); nodep->v3error(varType - << " variable not allowed in " << m_contNba + << " variable not allowed in " << m_contNbap << " assignment (IEEE 1800-2023 6.21): " << varp->prettyNameQ()); } } @@ -419,9 +419,9 @@ private: void visit(AstAssignCont* nodep) override { iterateAndNextNull(nodep->timingControlp()); { - VL_RESTORER(m_contNba); + VL_RESTORER(m_contNbap); VL_RESTORER(m_contReads); - m_contNba = "continuous"; + m_contNbap = "continuous"; m_contReads = true; iterateAndNextNull(nodep->lhsp()); iterateAndNextNull(nodep->rhsp()); @@ -432,9 +432,9 @@ private: iterateAndNextNull(nodep->timingControlp()); iterateAndNextNull(nodep->rhsp()); { - VL_RESTORER(m_contNba); + VL_RESTORER(m_contNbap); VL_RESTORER(m_contReads); - m_contNba = "nonblocking"; + m_contNbap = "nonblocking"; m_contReads = false; iterateAndNextNull(nodep->lhsp()); } @@ -444,9 +444,9 @@ private: iterateAndNextNull(nodep->timingControlp()); iterateAndNextNull(nodep->rhsp()); { - VL_RESTORER(m_contNba); + VL_RESTORER(m_contNbap); VL_RESTORER(m_contReads); - m_contNba = "continuous"; + m_contNbap = "continuous"; m_contReads = false; iterateAndNextNull(nodep->lhsp()); }