diff --git a/src/V3Ast.h b/src/V3Ast.h index 406b13cd2..2d404953b 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -27,6 +27,7 @@ #include "V3Broken.h" #include +#include #include #include #include @@ -1866,8 +1867,16 @@ private: void iterateListBackwards(VNVisitor& v); // For internal use only. + // Note: specializations for particular node types are provided by 'astgen' template inline static bool privateTypeTest(const AstNode* nodep); + // For internal use only. + // Note: specializations for particular node types are provided below + template inline static bool privateMayBeUnder(const AstNode* nodep) { + return true; + } + + // For internal use only. template constexpr static bool uselessCast() { using NonRef = typename std::remove_reference::type; using NonPtr = typename std::remove_pointer::type; @@ -1875,6 +1884,7 @@ private: return std::is_base_of::value; } + // For internal use only. template constexpr static bool impossibleCast() { using NonRef = typename std::remove_reference::type; using NonPtr = typename std::remove_pointer::type; @@ -1921,11 +1931,117 @@ public: << "'"); return reinterpret_cast(nodep); } + + // Predicate that returns true if the given 'nodep' might have a descendant of type 'T_Node'. + // This is conservative and is used to speed up traversals. + template inline static bool mayBeUnder(const AstNode* nodep) { + static_assert(!std::is_const::value, + "Type parameter 'T_Node' should not be const qualified"); + static_assert(std::is_base_of::value, + "Type parameter 'T_Node' must be a subtype of AstNode"); + return privateMayBeUnder(nodep); + } + +private: + template + static void foreachImpl( + // Using std::conditional for const correctness in the public 'foreach' functions + typename std::conditional::value, const AstNode*, AstNode*>::type + nodep, + std::function f) { + + // Note: Using a loop to iterate the nextp() chain, instead of tail recursion, because + // debug builds don't eliminate tail calls, causing stack overflow on long lists of nodes. + do { + // Prefetch children and next + ASTNODE_PREFETCH(nodep->op1p()); + ASTNODE_PREFETCH(nodep->op2p()); + ASTNODE_PREFETCH(nodep->op3p()); + ASTNODE_PREFETCH(nodep->op4p()); + if /* TODO: 'constexpr' in C++17 */ (VisitNext) ASTNODE_PREFETCH(nodep->nextp()); + + // Apply function in pre-order + if (privateTypeTest::type>(nodep)) { + f(static_cast(nodep)); + } + + // Traverse children (including their 'nextp()' chains), unless futile + if (mayBeUnder::type>(nodep)) { + if (AstNode* const op1p = nodep->op1p()) foreachImpl(op1p, f); + if (AstNode* const op2p = nodep->op2p()) foreachImpl(op2p, f); + if (AstNode* const op3p = nodep->op3p()) foreachImpl(op3p, f); + if (AstNode* const op4p = nodep->op4p()) foreachImpl(op4p, f); + } + + // Traverse 'nextp()' chain if requested + if /* TODO: 'constexpr' in C++17 */ (VisitNext) { + nodep = nodep->nextp(); + } else { + break; + } + } while (nodep); + } + +public: + // Traverse subtree and call given function 'f' in pre-order on each node that has type 'T'. + // Prefer 'foreach' over simple VNVisitor that only needs to handle a single (or a few) node + // types, as it's easier to write, but more importantly, the dispatch to the operation function + // in 'foreach' should be completely predictable by branch target caches in modern CPUs, + // while it is basically unpredictable for VNVisitor. + template void foreach (std::function f) { + static_assert(!std::is_const::value, + "Type parameter 'T_Node' should not be const qualified"); + static_assert(std::is_base_of::value, + "Type parameter 'T_Node' must be a subtype of AstNode"); + foreachImpl(this, f); + } + + // Same as above, but for 'const' nodes + template void foreach (std::function f) const { + static_assert(!std::is_const::value, + "Type parameter 'T_Node' should not be const qualified"); + static_assert(std::is_base_of::value, + "Type parameter 'T_Node' must be a subtype of AstNode"); + foreachImpl(this, f); + } + + // Same as 'foreach' but also follows 'this->nextp()' + template void foreachAndNext(std::function f) { + static_assert(!std::is_const::value, + "Type parameter 'T_Node' should not be const qualified"); + static_assert(std::is_base_of::value, + "Type parameter 'T_Node' must be a subtype of AstNode"); + foreachImpl(this, f); + } + + // Same as 'foreach' but also follows 'this->nextp()' + template void foreachAndNext(std::function f) const { + static_assert(!std::is_const::value, + "Type parameter 'T_Node' should not be const qualified"); + static_assert(std::is_base_of::value, + "Type parameter 'T_Node' must be a subtype of AstNode"); + foreachImpl(this, f); + } + + int nodeCount() const { + // TODO: this should really return size_t, but need to fix use sites + int count = 0; + this->foreach([&count](const AstNode*) { ++count; }); + return count; + } }; -// Specialisations of privateIs/privateCast +// Specialisations of privateTypeTest #include "V3Ast__gen_impl.h" // From ./astgen +// Specializations of privateMayBeUnder +template <> inline bool AstNode::mayBeUnder(const AstNode* nodep) { + return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath); +} +template <> inline bool AstNode::mayBeUnder(const AstNode* nodep) { + return !VN_IS(nodep, NodeMath); +} + inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) { if (!rhs) { os << "nullptr"; diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index 918df5292..bb3123dcb 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -137,32 +137,6 @@ public: bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); } -//###################################################################### -// Mark every node in the tree - -class BrokenMarkVisitor final : public VNVisitor { -private: - const uint8_t m_brokenCntCurrent = s_brokenCntGlobal.get(); - - // VISITORS - virtual void visit(AstNode* nodep) override { -#ifdef VL_LEAK_CHECKS - UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep, - "AstNode is in tree, but not allocated"); -#endif - UASSERT_OBJ(nodep->brokenState() != m_brokenCntCurrent, nodep, - "AstNode is already in tree at another location"); - if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep); - nodep->brokenState(m_brokenCntCurrent); - iterateChildrenConst(nodep); - } - -public: - // CONSTRUCTORS - explicit BrokenMarkVisitor(AstNetlist* nodep) { iterate(nodep); } - virtual ~BrokenMarkVisitor() override = default; -}; - //###################################################################### // Check every node in tree @@ -343,8 +317,23 @@ void V3Broken::brokenAll(AstNetlist* nodep) { UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE } else { inBroken = true; - const BrokenMarkVisitor mvisitor{nodep}; + + // Mark every node in the tree + const uint8_t brokenCntCurrent = s_brokenCntGlobal.get(); + nodep->foreach([brokenCntCurrent](AstNode* nodep) { +#ifdef VL_LEAK_CHECKS + UASSERT_OBJ(s_allocTable.isAllocated(nodep), nodep, + "AstNode is in tree, but not allocated"); +#endif + UASSERT_OBJ(nodep->brokenState() != brokenCntCurrent, nodep, + "AstNode is already in tree at another location"); + if (nodep->maybePointedTo()) s_linkableTable.addLinkable(nodep); + nodep->brokenState(brokenCntCurrent); + }); + + // Check every node in tree const BrokenCheckVisitor cvisitor{nodep}; + s_allocTable.checkForLeaks(); s_linkableTable.clear(); s_brokenCntGlobal.inc(); diff --git a/src/V3Changed.cpp b/src/V3Changed.cpp index b1fde0773..5bb6bd1f8 100644 --- a/src/V3Changed.cpp +++ b/src/V3Changed.cpp @@ -32,7 +32,6 @@ #include "V3Global.h" #include "V3Ast.h" #include "V3Changed.h" -#include "V3EmitCBase.h" #include @@ -154,8 +153,7 @@ private: // Later code will expand words which adds to GCC compile time, // so add penalty based on word width also - const EmitCBaseCounterVisitor visitor{initp}; - m_statep->m_numStmts += visitor.count() + m_varEqnp->widthWords(); + m_statep->m_numStmts += initp->nodeCount() + m_varEqnp->widthWords(); } virtual void visit(AstBasicDType*) override { // diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index dc2d73c68..74e5899f8 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -33,7 +33,6 @@ #include "V3Global.h" #include "V3Clock.h" #include "V3Ast.h" -#include "V3EmitCBase.h" #include @@ -207,7 +206,7 @@ private: } void splitCheck(AstCFunc* ofuncp) { if (!v3Global.opt.outputSplitCFuncs() || !ofuncp->stmtsp()) return; - if (EmitCBaseCounterVisitor(ofuncp).count() < v3Global.opt.outputSplitCFuncs()) return; + if (ofuncp->nodeCount() < v3Global.opt.outputSplitCFuncs()) return; int funcnum = 0; int func_stmts = 0; @@ -219,7 +218,7 @@ private: if (ofuncp->finalsp()) tempp->addStmtsp(ofuncp->finalsp()->unlinkFrBackWithNext()); while (tempp->stmtsp()) { AstNode* const itemp = tempp->stmtsp()->unlinkFrBack(); - const int stmts = EmitCBaseCounterVisitor(itemp).count(); + const int stmts = itemp->nodeCount(); if (!funcp || (func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) { // Make a new function funcp diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 45d9bcfcb..93622e8fc 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -38,46 +38,6 @@ //###################################################################### // Utilities -class ConstVarMarkVisitor final : public VNVisitor { - // NODE STATE - // AstVar::user4p -> bool, Var marked, 0=not set yet -private: - // VISITORS - virtual void visit(AstVarRef* nodep) override { - if (nodep->varp()) nodep->varp()->user4(1); - } - virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - // CONSTRUCTORS - explicit ConstVarMarkVisitor(AstNode* nodep) { - AstNode::user4ClearTree(); // Check marked InUse before we're called - iterate(nodep); - } - virtual ~ConstVarMarkVisitor() override = default; -}; - -class ConstVarFindVisitor final : public VNVisitor { - // NODE STATE - // AstVar::user4p -> bool, input from ConstVarMarkVisitor - // MEMBERS - bool m_found = false; - -private: - // VISITORS - virtual void visit(AstVarRef* nodep) override { - if (nodep->varp() && nodep->varp()->user4()) m_found = true; - } - virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - // CONSTRUCTORS - explicit ConstVarFindVisitor(AstNode* nodep) { iterateAndNextNull(nodep); } - virtual ~ConstVarFindVisitor() override = default; - // METHODS - bool found() const { return m_found; } -}; - static bool isConst(const AstNode* nodep, uint64_t v) { const AstConst* const constp = VN_CAST(nodep, Const); return constp && constp->toUQuad() == v; @@ -811,7 +771,7 @@ private: // NODE STATE // ** only when m_warn/m_doExpensive is set. If state is needed other times, // ** must track down everywhere V3Const is called and make sure no overlaps. - // AstVar::user4p -> Used by ConstVarMarkVisitor/ConstVarFindVisitor + // AstVar::user4p -> Used by variable marking/finding // AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label // AstEnum::user4 -> bool. Recursing. @@ -1980,9 +1940,12 @@ private: // Note only do this (need user4) when m_warn, which is // done as unique visitor const VNUser4InUse m_inuser4; - const ConstVarMarkVisitor mark{nodep->lhsp()}; - const ConstVarFindVisitor find{nodep->rhsp()}; - if (find.found()) need_temp = true; + nodep->lhsp()->foreach([](const AstVarRef* nodep) { + if (nodep->varp()) nodep->varp()->user4(1); + }); + nodep->rhsp()->foreach([&need_temp](const AstVarRef* nodep) { + if (nodep->varp() && nodep->varp()->user4()) need_temp = true; + }); } if (need_temp) { // The first time we constify, there may be the same variable on the LHS diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index a8f0fa485..6347fc098 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -43,28 +43,6 @@ #include #include -//###################################################################### - -class DeadModVisitor final : public VNVisitor { - // In a module that is dead, cleanup the in-use counts of the modules -private: - // NODE STATE - // ** Shared with DeadVisitor ** - // VISITORS - virtual void visit(AstCell* nodep) override { - iterateChildren(nodep); - nodep->modp()->user1Inc(-1); - } - //----- - virtual void visit(AstNodeMath*) override {} // Accelerate - virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - // CONSTRUCTORS - explicit DeadModVisitor(AstNodeModule* nodep) { iterate(nodep); } - virtual ~DeadModVisitor() override = default; -}; - //###################################################################### // Dead state, as a visitor of each AstNode @@ -328,7 +306,9 @@ private: // And its children may now be killable too; correct counts // Recurse, as cells may not be directly under the module but in a generate if (!modp->dead()) { // If was dead didn't increment user1's - DeadModVisitor{modp}; + modp->foreach([](const AstCell* cellp) { // + cellp->modp()->user1Inc(-1); + }); } VL_DO_DANGLING(modp->unlinkFrBack()->deleteTree(), modp); retry = true; diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 287ef07fd..5f3f09f33 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -113,24 +113,4 @@ public: virtual ~EmitCBaseVisitor() override = default; }; -//###################################################################### -// Count operations under the given node, as a visitor of each AstNode - -class EmitCBaseCounterVisitor final : public VNVisitor { -private: - // MEMBERS - int m_count = 0; // Number of statements - // VISITORS - virtual void visit(AstNode* nodep) override { - ++m_count; - iterateChildren(nodep); - } - -public: - // CONSTRUCTORS - explicit EmitCBaseCounterVisitor(AstNode* nodep) { iterate(nodep); } - virtual ~EmitCBaseCounterVisitor() override = default; - int count() const { return m_count; } -}; - #endif // guard diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 56cca8464..3910417aa 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -134,7 +134,7 @@ public: // ACCESSORS void splitSizeInc(int count) { m_splitSize += count; } - void splitSizeInc(AstNode* nodep) { splitSizeInc(EmitCBaseCounterVisitor(nodep).count()); } + void splitSizeInc(AstNode* nodep) { splitSizeInc(nodep->nodeCount()); } void splitSizeReset() { m_splitSize = 0; } bool splitNeeded() const { return v3Global.opt.outputSplit() && m_splitSize >= v3Global.opt.outputSplit(); diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index d2f600a2c..eabbca566 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -243,36 +243,6 @@ public: } }; -//###################################################################### -// Using clonep(), find cell cross references. -// clone() must not be called inside this visitor - -class InlineCollectVisitor final : public VNVisitor { -private: - // NODE STATE - // Output: - // AstCell::user4p() // AstCell* of the created clone - - // METHODS - VL_DEBUG_FUNC; // Declare debug() - - // VISITORS - virtual void visit(AstCell* nodep) override { - // clonep is nullptr when inlining the last instance, if so the use original node - nodep->user4p(nodep->clonep() ? nodep->clonep() : nodep); - } - //-------------------- - virtual void visit(AstNodeStmt*) override {} // Accelerate - virtual void visit(AstNodeMath*) override {} // Accelerate - virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } - -public: - // CONSTRUCTORS - explicit InlineCollectVisitor(AstNodeModule* nodep) { // passed OLD module, not new one - iterate(nodep); - } -}; - //###################################################################### // After cell is cloned, relink the new module's contents @@ -546,7 +516,10 @@ private: newmodp = nodep->modp(); } // Find cell cross-references - InlineCollectVisitor{nodep->modp()}; + nodep->modp()->foreach([](AstCell* cellp) { + // clonep is nullptr when inlining the last instance, if so the use original node + cellp->user4p(cellp->clonep() ? cellp->clonep() : cellp); + }); // Create data for dotted variable resolution AstCellInline* const inlinep = new AstCellInline(nodep->fileline(), nodep->name(), nodep->modp()->origName(), diff --git a/src/V3MergeCond.cpp b/src/V3MergeCond.cpp index f99b436b3..79a73d4e3 100644 --- a/src/V3MergeCond.cpp +++ b/src/V3MergeCond.cpp @@ -109,23 +109,6 @@ public: } }; -class MarkVarsVisitor final : public VNVisitor { -private: - // METHODS - VL_DEBUG_FUNC; // Declare debug() - - // VISITORS - virtual void visit(AstVarRef* nodep) override { nodep->varp()->user1(1); } - virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } - -public: - // Remove marks from AstVars (clear user1) - static void clear() { AstNode::user1ClearTree(); } - - // Mark all AstVars referenced by setting user1 - void mark(AstNode* node) { iterate(node); } -}; - class MergeCondVisitor final : public VNVisitor { private: // NODE STATE @@ -146,7 +129,6 @@ private: uint32_t m_listLenght = 0; // Length of current list CheckMergeableVisitor m_checkMergeable; // Sub visitor for encapsulation & speed - MarkVarsVisitor m_markVars; // Sub visitor for encapsulation & speed // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -337,7 +319,7 @@ private: m_mgCondp = nullptr; m_mgLastp = nullptr; m_mgNextp = nullptr; - m_markVars.clear(); + AstNode::user1ClearTree(); // Clear marked variables AstNode::user2ClearTree(); // Merge recursively within the branches if (recursivep) { @@ -407,7 +389,8 @@ private: m_mgFirstp = nodep; m_mgCondp = condp; m_listLenght = 0; - m_markVars.mark(condp); + // Mark variable references in the condition + condp->foreach([](const AstVarRef* nodep) { nodep->varp()->user1(1); }); // Add any preceding nodes to the list that would allow us to extend the merge range for (;;) { AstNode* const backp = m_mgFirstp->backp(); @@ -521,10 +504,7 @@ private: public: // CONSTRUCTORS - explicit MergeCondVisitor(AstNetlist* nodep) { - m_markVars.clear(); - iterate(nodep); - } + explicit MergeCondVisitor(AstNetlist* nodep) { iterate(nodep); } virtual ~MergeCondVisitor() override { V3Stats::addStat("Optimizations, MergeCond merges", m_statMerges); V3Stats::addStat("Optimizations, MergeCond merged items", m_statMergedItems); diff --git a/src/V3Order.cpp b/src/V3Order.cpp index ff17392b5..62b720528 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -82,7 +82,6 @@ #include "V3Ast.h" #include "V3AstUserAllocator.h" #include "V3Const.h" -#include "V3EmitCBase.h" #include "V3EmitV.h" #include "V3File.h" #include "V3Global.h" @@ -1831,8 +1830,7 @@ AstActive* OrderProcess::processMoveOneLogic(const OrderLogicVertex* lvertexp, newFuncpr->addStmtsp(nodep); if (v3Global.opt.outputSplitCFuncs()) { // Add in the number of nodes we're adding - const EmitCBaseCounterVisitor visitor{nodep}; - newStmtsr += visitor.count(); + newStmtsr += nodep->nodeCount(); } } diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 4e373e09b..7501cd456 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -37,52 +37,6 @@ constexpr int STATIC_CONST_MIN_WIDTH = 256; // Minimum size to extract to static constant -//###################################################################### -// Structure for global state - -class PremitAssignVisitor final : public VNVisitor { -private: - // NODE STATE - // AstVar::user3() // bool; occurs on LHS of current assignment - const VNUser3InUse m_inuser3; - - // STATE - bool m_noopt = false; // Disable optimization of variables in this block - - // METHODS - VL_DEBUG_FUNC; // Declare debug() - - // VISITORS - virtual void visit(AstNodeAssign* nodep) override { - // AstNode::user3ClearTree(); // Implied by VNUser3InUse - // LHS first as fewer varrefs - iterateAndNextNull(nodep->lhsp()); - // Now find vars marked as lhs - iterateAndNextNull(nodep->rhsp()); - } - virtual void visit(AstVarRef* nodep) override { - // it's LHS var is used so need a deep temporary - if (nodep->access().isWriteOrRW()) { - nodep->varp()->user3(true); - } else { - if (nodep->varp()->user3()) { - if (!m_noopt) UINFO(4, "Block has LHS+RHS var: " << nodep << endl); - m_noopt = true; - } - } - } - virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - // CONSTRUCTORS - explicit PremitAssignVisitor(AstNodeAssign* nodep) { - UINFO(4, " PremitAssignVisitor on " << nodep << endl); - iterate(nodep); - } - virtual ~PremitAssignVisitor() override = default; - bool noOpt() const { return m_noopt; } -}; - //###################################################################### // Premit state, as a visitor of each AstNode @@ -92,7 +46,7 @@ private: // AstNodeMath::user() -> bool. True if iterated already // AstShiftL::user2() -> bool. True if converted to conditional // AstShiftR::user2() -> bool. True if converted to conditional - // *::user3() -> See PremitAssignVisitor + // *::user3() -> Used when visiting AstNodeAssign const VNUser1InUse m_inuser1; const VNUser2InUse m_inuser2; @@ -231,7 +185,17 @@ private: virtual void visit(AstNodeAssign* nodep) override { startStatement(nodep); { - const bool noopt = PremitAssignVisitor(nodep).noOpt(); + bool noopt = false; + { + const VNUser3InUse user3InUse; + nodep->lhsp()->foreach([](const AstVarRef* refp) { + if (refp->access().isWriteOrRW()) refp->varp()->user3(true); + }); + nodep->rhsp()->foreach([&noopt](const AstVarRef* refp) { + if (refp->access().isReadOnly() && refp->varp()->user3()) noopt = true; + }); + } + if (noopt && !nodep->user1()) { nodep->user1(true); // Need to do this even if not wide, as e.g. a select may be on a wide operator diff --git a/src/V3Task.cpp b/src/V3Task.cpp index b80cd2d44..217a58e1f 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -286,41 +286,6 @@ public: VL_UNCOPYABLE(TaskStateVisitor); }; -//###################################################################### - -class TaskRelinkVisitor final : public VNVisitor { - // Replace varrefs with new var pointer -private: - // NODE STATE - // Input: - // AstVar::user2p // AstVarScope* to replace varref with - - // VISITORS - virtual void visit(AstVarRef* nodep) override { - // Similar code in V3Inline - if (nodep->varp()->user2p()) { // It's being converted to an alias. - UINFO(9, - " relinkVar " << cvtToHex(nodep->varp()->user2p()) << " " << nodep << endl); - AstVarScope* const newvscp = VN_AS(nodep->varp()->user2p(), VarScope); - UASSERT_OBJ(newvscp, nodep, "not linked"); - nodep->varScopep(newvscp); - nodep->varp(nodep->varScopep()->varp()); - nodep->name(nodep->varp()->name()); - } - iterateChildren(nodep); - } - - //-------------------- - virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - // CONSTRUCTORS - explicit TaskRelinkVisitor(AstBegin* nodep) { // Passed temporary tree - iterate(nodep); - } - virtual ~TaskRelinkVisitor() override = default; -}; - //###################################################################### // DPI related utility functions @@ -365,39 +330,6 @@ struct TaskDpiUtils { } }; -//###################################################################### -// Gather non-local variables written by an AstCFunc - -class TaskGatherWrittenVisitor final : public VNVisitor { - // NODE STATE - // AstVarScope::user5 -> Already considered variable - const VNUser5InUse m_user5InUse; - - std::vector m_writtenVariables; // Variables written - - virtual void visit(AstVarRef* nodep) override { - if (nodep->access().isReadOnly()) return; // Ignore read reference - AstVarScope* const varScopep = nodep->varScopep(); - if (varScopep->user5()) return; // Ignore already processed variable - varScopep->user5(true); // Mark as already processed - // Note: We are ignoring function locals as they should not be referenced anywhere outside - // of the enclosing AstCFunc, and therefore they are irrelevant for code ordering. This is - // simply an optimization to avoid adding useless nodes to the ordering graph in V3Order. - if (varScopep->varp()->isFuncLocal()) return; - m_writtenVariables.push_back(varScopep); - } - virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } - - explicit TaskGatherWrittenVisitor(AstNode* nodep) { iterate(nodep); } - -public: - // Gather all written non-local variables - static const std::vector gather(AstCFunc* funcp) { - const TaskGatherWrittenVisitor visitor{funcp}; - return std::move(visitor.m_writtenVariables); - } -}; - //###################################################################### // Task state, as a visitor of each AstNode @@ -407,7 +339,7 @@ private: // Each module: // AstNodeFTask::user1 // True if its been expanded // Each funccall - // to TaskRelinkVisitor: + // to 'relink' function: // AstVar::user2p // AstVarScope* to replace varref with const VNUser1InUse m_inuser1; const VNUser2InUse m_inuser2; @@ -471,6 +403,18 @@ private: } } + // Replace varrefs with new var pointer + void relink(AstNode* nodep) { + nodep->foreachAndNext([](AstVarRef* refp) { + if (refp->varp()->user2p()) { // It's being converted to an alias. + AstVarScope* const newvscp = VN_AS(refp->varp()->user2p(), VarScope); + refp->varScopep(newvscp); + refp->varp(refp->varScopep()->varp()); + refp->name(refp->varp()->name()); + } + }); + } + AstNode* createInlinedFTask(AstNodeFTaskRef* refp, const string& namePrefix, AstVarScope* outvscp) { // outvscp is the variable for functions only, if nullptr, it's a task @@ -581,13 +525,7 @@ private: refp->taskp()->fvarp()->user2p(outvscp); } // Replace variable refs - // Iteration requires a back, so put under temporary node - { - AstBegin* const tempp = new AstBegin(beginp->fileline(), "[EditWrapper]", beginp); - const TaskRelinkVisitor visitor{tempp}; - tempp->stmtsp()->unlinkFrBackWithNext(); - VL_DO_DANGLING(tempp->deleteTree(), tempp); - } + relink(beginp); // if (debug() >= 9) beginp->dumpTreeAndNext(cout, "-iotask: "); return beginp; @@ -1326,18 +1264,30 @@ private: rtnvscp->fileline(), new AstVarRef(rtnvscp->fileline(), rtnvscp, VAccess::READ))); } // Replace variable refs - // Iteration requires a back, so put under temporary node - { - AstBegin* const tempp = new AstBegin(cfuncp->fileline(), "[EditWrapper]", cfuncp); - const TaskRelinkVisitor visitor{tempp}; - tempp->stmtsp()->unlinkFrBackWithNext(); - VL_DO_DANGLING(tempp->deleteTree(), tempp); - } + relink(cfuncp); if (cfuncp->dpiExportImpl()) { // Mark all non-local variables written by the DPI exported function as being updated // by DPI exports. This ensures correct ordering and change detection later. - const std::vector writtenps = TaskGatherWrittenVisitor::gather(cfuncp); + + // Gather non-local variables written by the exported function + std::vector writtenps; + { + const VNUser5InUse user5InUse; // AstVarScope::user5 -> Already added variable + cfuncp->foreach([&writtenps](AstVarRef* refp) { + if (refp->access().isReadOnly()) return; // Ignore read reference + AstVarScope* const varScopep = refp->varScopep(); + if (varScopep->user5()) return; // Ignore already added variable + varScopep->user5(true); // Mark as already added + // Note: We are ignoring function locals as they should not be referenced + // anywhere outside of the enclosing AstCFunc, and therefore they are + // irrelevant for code ordering. This is simply an optimization to avoid adding + // useless nodes to the ordering graph in V3Order. + if (varScopep->varp()->isFuncLocal()) return; + writtenps.push_back(varScopep); + }); + } + if (!writtenps.empty()) { AstVarScope* const dpiExportTriggerp = makeDpiExporTrigger(); FileLine* const fl = cfuncp->fileline(); diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index b2f265d97..477d97d2e 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -591,7 +591,7 @@ private: AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, /* full: */ true); subFuncp->addStmtsp(incp); - subStmts += EmitCBaseCounterVisitor(incp).count(); + subStmts += incp->nodeCount(); // Track partitioning nCodes += declp->codeInc(); @@ -659,7 +659,7 @@ private: ifp = new AstIf(flp, condp, nullptr, nullptr); if (!always) ifp->branchPred(VBranchPred::BP_UNLIKELY); subFuncp->addStmtsp(ifp); - subStmts += EmitCBaseCounterVisitor(ifp).count(); + subStmts += ifp->nodeCount(); prevActSet = &actSet; } @@ -667,7 +667,7 @@ private: AstTraceInc* const incp = new AstTraceInc(declp->fileline(), declp, /* full: */ false, baseCode); ifp->addIfsp(incp); - subStmts += EmitCBaseCounterVisitor(incp).count(); + subStmts += incp->nodeCount(); // Track partitioning nCodes += declp->codeInc(); diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index d40809b00..5859ff819 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -170,7 +170,7 @@ private: m_topFuncps.push_back(funcp); } m_topFuncps.back()->addStmtsp(stmtp); - m_topFuncSize += EmitCBaseCounterVisitor{stmtp}.count(); + m_topFuncSize += stmtp->nodeCount(); } void addToSubFunc(AstNodeStmt* stmtp) { @@ -185,7 +185,7 @@ private: m_subFuncps.push_back(funcp); } m_subFuncps.back()->addStmtsp(stmtp); - m_subFuncSize += EmitCBaseCounterVisitor{stmtp}.count(); + m_subFuncSize += stmtp->nodeCount(); } std::string getScopeChar(VltTraceScope sct) { return std::string(1, (char)(0x80 + sct)); }