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.
This commit is contained in:
Geza Lore 2026-07-04 17:27:07 +01:00 committed by GitHub
parent 83eb54ec7e
commit 566f4e6efc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 180 additions and 148 deletions

View File

@ -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);
}

View File

@ -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);

View File

@ -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 {

View File

@ -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"; // ::

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -33,6 +33,7 @@
#include <string>
#include <thread>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
@ -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<typename std::decay_t<decltype(var)>> 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<typename std::decay_t<decltype(var)>> 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<typename std::decay_t<decltype(var)>> 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<typename std::decay_t<decltype(var)>> 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 <typename T>
class VRestorer final {
class VRestorerTrivial final {
static_assert(std::is_trivially_copyable<T>::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 <typename T>
class VRestorerCopy final {
static_assert(!std::is_trivially_copyable<T>::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 <typename T>
class VRestorerClear final {
static_assert(!std::is_trivially_copyable<T>::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);
};
//######################################################################

View File

@ -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};

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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());

View File

@ -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);

View File

@ -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"

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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 {

View File

@ -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());
}