Add AstNode::foreach method for simple pre-order traversal (#3276)
This commit is contained in:
parent
50094ca296
commit
64a6e1ac8b
118
src/V3Ast.h
118
src/V3Ast.h
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 { //
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
118
src/V3Task.cpp
118
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<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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)); }
|
||||
|
|
|
|||
Loading…
Reference in New Issue