Add AstNode::foreach method for simple pre-order traversal (#3276)

This commit is contained in:
Geza Lore 2022-01-09 22:34:10 +00:00 committed by GitHub
parent 50094ca296
commit 64a6e1ac8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 207 additions and 317 deletions

View File

@ -27,6 +27,7 @@
#include "V3Broken.h"
#include <cmath>
#include <functional>
#include <map>
#include <set>
#include <type_traits>
@ -1866,8 +1867,16 @@ private:
void iterateListBackwards(VNVisitor& v);
// For internal use only.
// Note: specializations for particular node types are provided by 'astgen'
template <typename T> inline static bool privateTypeTest(const AstNode* nodep);
// For internal use only.
// Note: specializations for particular node types are provided below
template <typename T_Node> inline static bool privateMayBeUnder(const AstNode* nodep) {
return true;
}
// For internal use only.
template <typename TargetType, typename DeclType> constexpr static bool uselessCast() {
using NonRef = typename std::remove_reference<DeclType>::type;
using NonPtr = typename std::remove_pointer<NonRef>::type;
@ -1875,6 +1884,7 @@ private:
return std::is_base_of<TargetType, NonCV>::value;
}
// For internal use only.
template <typename TargetType, typename DeclType> constexpr static bool impossibleCast() {
using NonRef = typename std::remove_reference<DeclType>::type;
using NonPtr = typename std::remove_pointer<NonRef>::type;
@ -1921,11 +1931,117 @@ public:
<< "'");
return reinterpret_cast<const T*>(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 <typename T_Node> inline static bool mayBeUnder(const AstNode* nodep) {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
return privateMayBeUnder<T_Node>(nodep);
}
private:
template <typename T_Arg, bool VisitNext>
static void foreachImpl(
// Using std::conditional for const correctness in the public 'foreach' functions
typename std::conditional<std::is_const<T_Arg>::value, const AstNode*, AstNode*>::type
nodep,
std::function<void(T_Arg*)> 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<typename std::remove_const<T_Arg>::type>(nodep)) {
f(static_cast<T_Arg*>(nodep));
}
// Traverse children (including their 'nextp()' chains), unless futile
if (mayBeUnder<typename std::remove_const<T_Arg>::type>(nodep)) {
if (AstNode* const op1p = nodep->op1p()) foreachImpl<T_Arg, true>(op1p, f);
if (AstNode* const op2p = nodep->op2p()) foreachImpl<T_Arg, true>(op2p, f);
if (AstNode* const op3p = nodep->op3p()) foreachImpl<T_Arg, true>(op3p, f);
if (AstNode* const op4p = nodep->op4p()) foreachImpl<T_Arg, true>(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 <typename T_Node> void foreach (std::function<void(T_Node*)> f) {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
foreachImpl<T_Node, /* VisitNext: */ false>(this, f);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void foreach (std::function<void(const T_Node*)> f) const {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
foreachImpl<const T_Node, /* VisitNext: */ false>(this, f);
}
// Same as 'foreach' but also follows 'this->nextp()'
template <typename T_Node> void foreachAndNext(std::function<void(T_Node*)> f) {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
foreachImpl<T_Node, /* VisitNext: */ true>(this, f);
}
// Same as 'foreach' but also follows 'this->nextp()'
template <typename T_Node> void foreachAndNext(std::function<void(const T_Node*)> f) const {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
foreachImpl<const T_Node, /* VisitNext: */ true>(this, f);
}
int nodeCount() const {
// TODO: this should really return size_t, but need to fix use sites
int count = 0;
this->foreach<AstNode>([&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<AstCell>(const AstNode* nodep) {
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
}
template <> inline bool AstNode::mayBeUnder<AstNodeAssign>(const AstNode* nodep) {
return !VN_IS(nodep, NodeMath);
}
inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) {
if (!rhs) {
os << "nullptr";

View File

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

View File

@ -32,7 +32,6 @@
#include "V3Global.h"
#include "V3Ast.h"
#include "V3Changed.h"
#include "V3EmitCBase.h"
#include <algorithm>
@ -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 { //

View File

@ -33,7 +33,6 @@
#include "V3Global.h"
#include "V3Clock.h"
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include <algorithm>
@ -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

View File

@ -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<AstVarRef>([](const AstVarRef* nodep) {
if (nodep->varp()) nodep->varp()->user4(1);
});
nodep->rhsp()->foreach<AstVarRef>([&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

View File

@ -43,28 +43,6 @@
#include <map>
#include <vector>
//######################################################################
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<AstCell>([](const AstCell* cellp) { //
cellp->modp()->user1Inc(-1);
});
}
VL_DO_DANGLING(modp->unlinkFrBack()->deleteTree(), modp);
retry = true;

View File

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

View File

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

View File

@ -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>([](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(),

View File

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

View File

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

View File

@ -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<AstVarRef>([](const AstVarRef* refp) {
if (refp->access().isWriteOrRW()) refp->varp()->user3(true);
});
nodep->rhsp()->foreach<AstVarRef>([&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

View File

@ -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<AstVarScope*> 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<AstVarScope*> 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>([](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<AstVarScope*> writtenps = TaskGatherWrittenVisitor::gather(cfuncp);
// Gather non-local variables written by the exported function
std::vector<AstVarScope*> writtenps;
{
const VNUser5InUse user5InUse; // AstVarScope::user5 -> Already added variable
cfuncp->foreach<AstVarRef>([&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();

View File

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

View File

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