This commit is contained in:
Geza Lore 2026-04-07 22:16:24 -07:00 committed by GitHub
commit 9b323b7786
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 497 additions and 1282 deletions

View File

@ -649,9 +649,12 @@ Summary:
.. option:: -fno-dfg
Rarely needed. Disable all use of the DFG-based combinational logic
optimizer. Alias for :vlopt:`-fno-dfg-pre-inline`,
:vlopt:`-fno-dfg-post-inline` and :vlopt:`-fno-dfg-scoped`.
Rarely needed. Disable the DFG-based combinational logic optimizer.
In versions before 5.048:
Alias for :vlopt:`-fno-dfg-pre-inline`, :vlopt:`-fno-dfg-post-inline` and
:vlopt:`-fno-dfg-scoped`.
.. option:: -fno-dfg-break-cycles
@ -667,11 +670,15 @@ Summary:
.. option:: -fno-dfg-post-inline
Rarely needed. Do not apply the DFG optimizer after inlining.
Deprecated and has no effect (ignored).
In versions before 5.048: Do not apply the DFG optimizer after inlining.
.. option:: -fno-dfg-pre-inline
Rarely needed. Do not apply the DFG optimizer before inlining.
Deprecated and has no effect (ignored).
In versions before 5.048: Do not apply the DFG optimizer before inlining.
.. option:: -fno-dfg-push-down-sels
@ -679,7 +686,10 @@ Summary:
.. option:: -fno-dfg-scoped
Rarely needed. Do not apply the DFG optimizer across module scopes.
Deprecated; use :vlopt:`-fno-dfg` instead.
In versions before 5.048: Do not apply the DFG optimizer across module
scopes.
.. option:: -fno-expand

View File

@ -1964,7 +1964,6 @@ class AstVar final : public AstNode {
bool m_ignorePostRead : 1; // Ignore reads in 'Post' blocks during ordering
bool m_ignorePostWrite : 1; // Ignore writes in 'Post' blocks during ordering
bool m_ignoreSchedWrite : 1; // Ignore writes in scheduling (for special optimizations)
bool m_dfgMultidriven : 1; // Singal is multidriven, used by DFG to avoid repeat processing
bool m_dfgTriLowered : 1; // Signal/temporary introduced by tristate lowering
bool m_dfgAllowMultidriveTri : 1; // Allow DFG MULTIDRIVEN warning for intentional tri nets
bool m_globalConstrained : 1; // Global constraint per IEEE 1800-2023 18.5.8
@ -2024,7 +2023,6 @@ class AstVar final : public AstNode {
m_ignorePostRead = false;
m_ignorePostWrite = false;
m_ignoreSchedWrite = false;
m_dfgMultidriven = false;
m_dfgTriLowered = false;
m_dfgAllowMultidriveTri = false;
m_globalConstrained = false;
@ -2206,8 +2204,6 @@ public:
void setIgnorePostWrite() { m_ignorePostWrite = true; }
bool ignoreSchedWrite() const { return m_ignoreSchedWrite; }
void setIgnoreSchedWrite() { m_ignoreSchedWrite = true; }
bool dfgMultidriven() const { return m_dfgMultidriven; }
void setDfgMultidriven() { m_dfgMultidriven = true; }
bool dfgTriLowered() const { return m_dfgTriLowered; }
void setDfgTriLowered() { m_dfgTriLowered = true; }
bool dfgAllowMultidriveTri() const { return m_dfgAllowMultidriveTri; }

View File

@ -18,6 +18,7 @@
#include "V3Dfg.h"
#include "V3Ast.h"
#include "V3EmitV.h"
#include "V3File.h"
@ -26,18 +27,16 @@ VL_DEFINE_DEBUG_FUNCTIONS;
//------------------------------------------------------------------------------
// DfgGraph
DfgGraph::DfgGraph(AstModule* modulep, const string& name)
: m_modulep{modulep}
, m_name{name} {}
DfgGraph::DfgGraph(const string& name)
: m_name{name} {}
DfgGraph::~DfgGraph() {
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
}
std::unique_ptr<DfgGraph> DfgGraph::clone() const {
const bool scoped = !modulep();
DfgGraph* const clonep = new DfgGraph{modulep(), name()};
// Create the new graph
DfgGraph* const clonep = new DfgGraph{name()};
// Map from original vertex to clone
std::unordered_map<const DfgVertex*, DfgVertex*> vtxp2clonep(size() * 2);
@ -54,20 +53,12 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
switch (vtx.type()) {
case VDfgType::VarArray: {
if (scoped) {
cp = new DfgVarArray{*clonep, vp->varScopep()};
} else {
cp = new DfgVarArray{*clonep, vp->varp()};
}
cp = new DfgVarArray{*clonep, vp->vscp()};
vtxp2clonep.emplace(&vtx, cp);
break;
}
case VDfgType::VarPacked: {
if (scoped) {
cp = new DfgVarPacked{*clonep, vp->varScopep()};
} else {
cp = new DfgVarPacked{*clonep, vp->varp()};
}
cp = new DfgVarPacked{*clonep, vp->vscp()};
vtxp2clonep.emplace(&vtx, cp);
break;
}
@ -78,7 +69,7 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
}
}
if (AstNode* const tmpForp = vp->tmpForp()) cp->tmpForp(tmpForp);
if (AstVarScope* const tmpForp = vp->tmpForp()) cp->tmpForp(tmpForp);
}
// Clone ast reference vertices
for (const DfgVertexAst& vtx : m_astVertices) { // LCOV_EXCL_START
@ -203,18 +194,18 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
if (otherps.empty()) return;
// NODE STATE
// AstVar/AstVarScope::user2p() -> corresponding DfgVertexVar* in 'this' graph
// AstVarScope::user2p() -> corresponding DfgVertexVar* in 'this' graph
const VNUser2InUse user2InUse;
// Set up Ast Variable -> DfgVertexVar map for 'this' graph
for (DfgVertexVar& vtx : m_varVertices) vtx.nodep()->user2p(&vtx);
for (DfgVertexVar& vtx : m_varVertices) vtx.vscp()->user2p(&vtx);
// Merge in each of the other graphs
for (const std::unique_ptr<DfgGraph>& otherp : otherps) {
// Process variables
for (DfgVertexVar* const vtxp : otherp->m_varVertices.unlinkable()) {
// Variabels that are present in 'this', make them use the DfgVertexVar in 'this'.
if (DfgVertexVar* const altp = vtxp->nodep()->user2u().to<DfgVertexVar*>()) {
if (DfgVertexVar* const altp = vtxp->vscp()->user2u().to<DfgVertexVar*>()) {
DfgVertex* const srcp = vtxp->srcp();
DfgVertex* const defaultp = vtxp->defaultp();
UASSERT_OBJ(!(srcp || defaultp) || (!altp->srcp() && !altp->defaultp()), vtxp,
@ -226,7 +217,7 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
continue;
}
// Otherwise they will be moved
vtxp->nodep()->user2p(vtxp);
vtxp->vscp()->user2p(vtxp);
vtxp->m_userGeneration = 0;
#ifdef VL_DEBUG
vtxp->m_dfgp = this;
@ -279,29 +270,17 @@ std::string DfgGraph::makeUniqueName(const std::string& prefix, size_t n) {
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name,
const DfgDataType& dtype, AstScope* scopep) {
UASSERT_OBJ(!!scopep != !!modulep(), flp,
"makeNewVar scopep should only be provided for a scoped DfgGraph");
// Create AstVar
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtype.astDtypep()};
if (scopep) {
// Add AstVar to the scope's module
scopep->modp()->addStmtsp(varp);
// Create AstVarScope
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
// Add to scope
scopep->addVarsp(vscp);
// Create and return the corresponding variable vertex
if (dtype.isArray()) return new DfgVarArray{*this, vscp};
return new DfgVarPacked{*this, vscp};
} else {
// Add AstVar to containing module
modulep()->addStmtsp(varp);
// Create and return the corresponding variable vertex
if (dtype.isArray()) return new DfgVarArray{*this, varp};
return new DfgVarPacked{*this, varp};
}
// Add AstVar to the scope's module
scopep->modp()->addStmtsp(varp);
// Create AstVarScope
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
// Add to scope
scopep->addVarsp(vscp);
// Create and return the corresponding variable vertex
if (dtype.isArray()) return new DfgVarArray{*this, vscp};
return new DfgVarPacked{*this, vscp};
}
static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
@ -309,19 +288,19 @@ static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&
// Dump one DfgVertex in Graphviz format
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
if (const DfgVertexVar* const varVtxp = vtx.cast<DfgVertexVar>()) {
const AstNode* const nodep = varVtxp->nodep();
const AstVarScope* const vscp = varVtxp->vscp();
os << toDotId(vtx);
// Begin attributes
os << " [";
// Begin 'label'
os << "label=\"";
// Name
os << nodep->prettyName();
os << vscp->prettyName();
// Address
os << '\n' << cvtToHex(varVtxp);
// Original variable, if any
if (const AstNode* const tmpForp = varVtxp->tmpForp()) {
if (tmpForp != nodep) os << "\ntemporary for: " << tmpForp->prettyName();
if (const AstVarScope* const tmpForp = varVtxp->tmpForp()) {
if (tmpForp != vscp) os << "\ntemporary for: " << tmpForp->prettyName();
}
// Type and fanout
os << '\n';
@ -877,10 +856,8 @@ DfgVertexVar* DfgVertex::getResultVar() {
}
// Prefer real variables over temporaries
const bool resIsTemp
= resp->varp() ? resp->varp()->isTemp() : resp->varScopep()->varp()->isTemp();
const bool varIsTemp
= varp->varp() ? varp->varp()->isTemp() : varp->varScopep()->varp()->isTemp();
const bool resIsTemp = resp->vscp()->varp()->isTemp();
const bool varIsTemp = varp->vscp()->varp()->isTemp();
if (resIsTemp != varIsTemp) {
if (resIsTemp) resp = varp;
return false;
@ -898,7 +875,7 @@ DfgVertexVar* DfgVertex::getResultVar() {
return false;
}
// Prefer the one with the lexically smaller name
if (const int cmp = resp->nodep()->name().compare(varp->nodep()->name())) {
if (const int cmp = resp->vscp()->name().compare(varp->vscp()->name())) {
if (cmp > 0) resp = varp;
return false;
}
@ -911,13 +888,13 @@ DfgVertexVar* DfgVertex::getResultVar() {
AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED {
// If this is a variable, we are done
if (const DfgVertexVar* const varp = this->cast<DfgVertexVar>()) {
return varp->varScopep()->scopep();
return varp->vscp()->scopep();
}
// Try the result var first if instructed (usully only in the recursive case)
if (tryResultVar) {
if (const DfgVertexVar* const varp = this->getResultVar()) {
return varp->varScopep()->scopep();
return varp->vscp()->scopep();
}
}

View File

@ -386,9 +386,6 @@ class DfgGraph final {
DfgVertex::List<DfgConst> m_constVertices; // The constant vertices in the graph
DfgVertex::List<DfgVertex> m_opVertices; // The operation vertices in the graph
size_t m_size = 0; // Number of vertices in the graph
// Parent of the graph (i.e.: the module containing the logic represented by this graph),
// or nullptr when run after V3Scope
AstModule* const m_modulep;
const std::string m_name; // Name of graph - need not be unique
std::string m_tmpNameStub{""}; // Name stub for temporary variables - computed lazy
@ -399,15 +396,13 @@ class DfgGraph final {
public:
// CONSTRUCTOR
explicit DfgGraph(AstModule* modulep, const string& name = "") VL_MT_DISABLED;
explicit DfgGraph(const string& name = "") VL_MT_DISABLED;
~DfgGraph() VL_MT_DISABLED;
VL_UNCOPYABLE(DfgGraph);
// METHODS
// Number of vertices in this graph
size_t size() const { return m_size; }
// Parent module - or nullptr when run after V3Scope
AstModule* modulep() const { return m_modulep; }
// Name of this graph
const string& name() const { return m_name; }
@ -558,16 +553,6 @@ namespace V3Dfg {
//-----------------------------------------------------------------------
// Functions for compatibility tests
// Returns true if variable can be represented in the graph
inline bool isSupported(const AstVar* varp) {
if (varp->isIfaceRef()) return false; // Cannot handle interface references
if (varp->delayp()) return false; // Cannot handle delayed variables
if (varp->isSc()) return false; // SystemC variables are special and rare, we can ignore
if (varp->dfgMultidriven()) return false; // Discovered as multidriven on earlier DFG run
if (DfgVertexVar::hasRWRefs(varp)) return false; // Referenced via READWRITE references
return DfgDataType::fromAst(varp->dtypep());
}
// Returns true if variable can be represented in the graph
inline bool isSupported(const AstVarScope* vscp) {
const AstNodeModule* const modp = vscp->scopep()->modp();
@ -582,7 +567,11 @@ inline bool isSupported(const AstVarScope* vscp) {
}
if (DfgVertexVar::hasRWRefs(vscp)) return false; // Referenced via READWRITE references
// Check the AstVar
return isSupported(vscp->varp());
AstVar* const varp = vscp->varp();
if (varp->isIfaceRef()) return false; // Cannot handle interface references
if (varp->delayp()) return false; // Cannot handle delayed variables
if (varp->isSc()) return false; // SystemC variables are special and rare, we can ignore
return DfgDataType::fromAst(varp->dtypep());
}
} //namespace V3Dfg

View File

@ -30,28 +30,14 @@
VL_DEFINE_DEBUG_FUNCTIONS;
template <bool T_Scoped>
class AstToDfgAddAstRefs final : public VNVisitorConst {
// TYPES
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
// STATE
DfgGraph& m_dfg; // The graph being processed
// Function to get the DfgVertexVar for a Variable
const std::function<DfgVertexVar*(Variable*)> m_getVarVertex;
// Function to get the DfgVertexVar for a AstVarScope
const std::function<DfgVertexVar*(AstVarScope*)> m_getVarVertex;
bool m_inSenItem = false; // Inside an AstSenItem
bool m_inLoop = false; // Inside an AstLoop
// METHODS
static Variable* getTarget(const AstVarRef* refp) {
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
if VL_CONSTEXPR_CXX17 (T_Scoped) {
return reinterpret_cast<Variable*>(refp->varScopep());
} else {
return reinterpret_cast<Variable*>(refp->varp());
}
}
// VISITORS
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
@ -71,12 +57,12 @@ class AstToDfgAddAstRefs final : public VNVisitorConst {
// Disguised hierarchical reference handled as external reference, ignore
if (nodep->classOrPackagep()) return;
// If target is not supported, ignore
Variable* const tgtp = getTarget(nodep);
if (!V3Dfg::isSupported(tgtp)) return;
AstVarScope* const vscp = nodep->varScopep();
if (!V3Dfg::isSupported(vscp)) return;
// V3Dfg::isSupported should reject vars with READWRITE references
UASSERT_OBJ(!nodep->access().isRW(), nodep, "Variable with READWRITE ref not rejected");
UASSERT_OBJ(!nodep->access().isRW(), nodep, "AstVarScope with READWRITE ref not rejected");
// Get target variable vergtex, ignore if not given one
DfgVertexVar* const varp = m_getVarVertex(tgtp);
DfgVertexVar* const varp = m_getVarVertex(vscp);
if (!varp) return;
// Create Ast reference vertices
if (nodep->access().isReadOnly()) {
@ -85,11 +71,11 @@ class AstToDfgAddAstRefs final : public VNVisitorConst {
return;
}
// Mark as written from non-DFG logic
DfgVertexVar::setHasModWrRefs(tgtp);
DfgVertexVar::setHasModWrRefs(vscp);
}
AstToDfgAddAstRefs(DfgGraph& dfg, AstNode* nodep,
std::function<DfgVertexVar*(Variable*)> getVarVertex)
std::function<DfgVertexVar*(AstVarScope*)> getVarVertex)
: m_dfg{dfg}
, m_getVarVertex{getVarVertex} {
iterateConst(nodep);
@ -97,67 +83,37 @@ class AstToDfgAddAstRefs final : public VNVisitorConst {
public:
static void apply(DfgGraph& dfg, AstNode* nodep,
std::function<DfgVertexVar*(Variable*)> getVarVertex) {
std::function<DfgVertexVar*(AstVarScope*)> getVarVertex) {
AstToDfgAddAstRefs{dfg, nodep, getVarVertex};
}
};
void V3DfgPasses::addAstRefs(DfgGraph& dfg, AstNode* nodep,
std::function<DfgVertexVar*(AstNode*)> getVarVertex) {
if (dfg.modulep()) {
AstToDfgAddAstRefs</* T_Scoped: */ false>::apply(dfg, nodep, getVarVertex);
} else {
AstToDfgAddAstRefs</* T_Scoped: */ true>::apply(dfg, nodep, getVarVertex);
}
std::function<DfgVertexVar*(AstVarScope*)> getVarVertex) {
AstToDfgAddAstRefs::apply(dfg, nodep, getVarVertex);
}
template <bool T_Scoped>
class AstToDfgVisitor final : public VNVisitor {
// NODE STATE
// AstVar/AstVarScope::user2() -> DfgVertexVar* : the corresponding variable vertex
// AstVar/AstVarScope::user3() -> bool : Already gathered - used fine grained below
// AstVarScope::user2() -> DfgVertexVar* : the corresponding variable vertex
// AstVarScope::user3() -> bool : Already gathered - used fine grained below
const VNUser2InUse m_user2InUse;
// TYPES
using RootType = std::conditional_t<T_Scoped, AstNetlist, AstModule>;
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
// STATE
DfgGraph& m_dfg; // The graph being built
V3DfgAstToDfgContext& m_ctx; // The context for stats
AstScope* m_scopep = nullptr; // The current scope, iff T_Scoped
// METHODS
static Variable* getTarget(const AstVarRef* refp) {
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
if VL_CONSTEXPR_CXX17 (T_Scoped) {
return reinterpret_cast<Variable*>(refp->varScopep());
} else {
return reinterpret_cast<Variable*>(refp->varp());
}
}
std::unique_ptr<std::vector<Variable*>> getLiveVariables(const CfgGraph& cfg) {
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
if VL_CONSTEXPR_CXX17 (T_Scoped) {
std::unique_ptr<std::vector<AstVarScope*>> result = V3Cfg::liveVarScopes(cfg);
const auto resultp = reinterpret_cast<std::vector<Variable*>*>(result.release());
return std::unique_ptr<std::vector<Variable*>>{resultp};
} else {
std::unique_ptr<std::vector<AstVar*>> result = V3Cfg::liveVars(cfg);
const auto resultp = reinterpret_cast<std::vector<Variable*>*>(result.release());
return std::unique_ptr<std::vector<Variable*>>{resultp};
}
}
// Mark variables referenced under node
void markReferenced(AstNode* nodep) {
V3DfgPasses::addAstRefs(m_dfg, nodep, [this](AstNode* varp) { //
return getVarVertex(static_cast<Variable*>(varp));
V3DfgPasses::addAstRefs(m_dfg, nodep, [this](AstVarScope* vscp) { //
return getVarVertex(vscp);
});
}
DfgVertexVar* getVarVertex(Variable* varp) {
DfgVertexVar* getVarVertex(AstVarScope* varp) {
if (!varp->user2p()) {
// TODO: fix this up when removing the different flavours of DfgVar
const AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
@ -175,14 +131,11 @@ class AstToDfgVisitor final : public VNVisitor {
std::unique_ptr<std::vector<DfgVertexVar*>> gatherWritten(const AstNode* nodep) {
const VNUser3InUse user3InUse;
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
// We can ignore AstVarXRef here. The only thing we can do with DfgLogic is
// synthesize it into regular vertices, which will fail on a VarXRef at that point.
const bool abort = nodep->exists([&](const AstNodeVarRef* vrefp) -> bool {
if (VN_IS(vrefp, VarXRef)) return true;
if (vrefp->access().isReadOnly()) return false;
Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
if (!V3Dfg::isSupported(varp)) return true;
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
const bool abort = nodep->exists([&](const AstVarRef* refp) -> bool {
if (refp->access().isReadOnly()) return false;
AstVarScope* const vscp = refp->varScopep();
if (!V3Dfg::isSupported(vscp)) return true;
if (!vscp->user3SetOnce()) resp->emplace_back(getVarVertex(vscp));
return false;
});
if (abort) {
@ -197,14 +150,11 @@ class AstToDfgVisitor final : public VNVisitor {
std::unique_ptr<std::vector<DfgVertexVar*>> gatherRead(const AstNode* nodep) {
const VNUser3InUse user3InUse;
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
// We can ignore AstVarXRef here. The only thing we can do with DfgLogic is
// synthesize it into regular vertices, which will fail on a VarXRef at that point.
const bool abort = nodep->exists([&](const AstNodeVarRef* vrefp) -> bool {
if (VN_IS(vrefp, VarXRef)) return true;
if (vrefp->access().isWriteOnly()) return false;
Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
if (!V3Dfg::isSupported(varp)) return true;
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
const bool abort = nodep->exists([&](const AstVarRef* refp) -> bool {
if (refp->access().isWriteOnly()) return false;
AstVarScope* const vscp = refp->varScopep();
if (!V3Dfg::isSupported(vscp)) return true;
if (!vscp->user3SetOnce()) resp->emplace_back(getVarVertex(vscp));
return false;
});
if (abort) {
@ -218,7 +168,7 @@ class AstToDfgVisitor final : public VNVisitor {
// Return nullptr if any are not supported.
std::unique_ptr<std::vector<DfgVertexVar*>> gatherLive(const CfgGraph& cfg) {
// Run analysis
std::unique_ptr<std::vector<Variable*>> varps = getLiveVariables(cfg);
std::unique_ptr<std::vector<AstVarScope*>> varps = V3Cfg::liveVarScopes(cfg);
if (!varps) {
++m_ctx.m_nonRepLive;
return nullptr;
@ -228,7 +178,7 @@ class AstToDfgVisitor final : public VNVisitor {
const VNUser3InUse user3InUse;
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
resp->reserve(varps->size());
for (Variable* const varp : *varps) {
for (AstVarScope* const varp : *varps) {
if (!V3Dfg::isSupported(varp)) {
++m_ctx.m_nonRepVar;
return nullptr;
@ -347,7 +297,7 @@ class AstToDfgVisitor final : public VNVisitor {
}
// CONSTRUCTOR
AstToDfgVisitor(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx)
AstToDfgVisitor(DfgGraph& dfg, AstNetlist& root, V3DfgAstToDfgContext& ctx)
: m_dfg{dfg}
, m_ctx{ctx} {
iterate(&root);
@ -356,7 +306,7 @@ class AstToDfgVisitor final : public VNVisitor {
VL_UNMOVABLE(AstToDfgVisitor);
public:
static void apply(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) {
static void apply(DfgGraph& dfg, AstNetlist& root, V3DfgAstToDfgContext& ctx) {
// Convert all logic under 'root'
AstToDfgVisitor{dfg, root, ctx};
// Remove unread and undriven variables (created when something failed to convert)
@ -371,14 +321,8 @@ public:
}
};
std::unique_ptr<DfgGraph> V3DfgPasses::astToDfg(AstModule& module, V3DfgContext& ctx) {
DfgGraph* const dfgp = new DfgGraph{&module, module.name()};
AstToDfgVisitor</* T_Scoped: */ false>::apply(*dfgp, module, ctx.m_ast2DfgContext);
return std::unique_ptr<DfgGraph>{dfgp};
}
std::unique_ptr<DfgGraph> V3DfgPasses::astToDfg(AstNetlist& netlist, V3DfgContext& ctx) {
DfgGraph* const dfgp = new DfgGraph{nullptr, "netlist"};
AstToDfgVisitor</* T_Scoped: */ true>::apply(*dfgp, netlist, ctx.m_ast2DfgContext);
DfgGraph* const dfgp = new DfgGraph{"netlist"};
AstToDfgVisitor::apply(*dfgp, netlist, ctx.m_ast2DfgContext);
return std::unique_ptr<DfgGraph>{dfgp};
}

View File

@ -115,15 +115,14 @@ class TraceDriver final : public DfgVisitor {
// Create temporary capable of holding the result of 'vtxp'
DfgVertexVar* createTmp(const char* prefix, DfgVertex* vtxp) {
AstNode* nodep = m_dfg.modulep();
if (!nodep) nodep = v3Global.rootp();
AstNode* nodep = v3Global.rootp();
const std::string name = m_dfg.makeUniqueName(prefix, nodep->user2Inc());
FileLine* const flp = vtxp->fileline();
DfgVertex::ScopeCache scopeCache;
AstScope* const scopep = m_dfg.modulep() ? nullptr : vtxp->scopep(scopeCache);
AstScope* const scopep = vtxp->scopep(scopeCache);
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
varp->varp()->isInternal(true);
varp->tmpForp(varp->nodep());
varp->vscp()->varp()->isInternal(true);
varp->tmpForp(varp->vscp());
m_vtx2Scc[varp] = 0;
return varp;
}
@ -1015,7 +1014,7 @@ class FixUp final {
return debugStr(*aselp->fromp()) + "[" + std::to_string(i) + "]";
}
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
return varp->nodep()->name();
return varp->vscp()->name();
}
vtx.v3fatalSrc("Unhandled node type");
} // LCOV_EXCL_STOP
@ -1128,7 +1127,7 @@ class FixUp final {
}
void main(DfgVertexVar& var) {
UINFO(9, "FixUp of " << var.nodep()->name());
UINFO(9, "FixUp of " << var.vscp()->name());
if (var.is<DfgVarPacked>()) {
// For Packed variables, fix up as whole
@ -1179,7 +1178,7 @@ std::pair<std::unique_ptr<DfgGraph>, bool> //
breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) {
// Shorthand for dumping graph at given dump level
const auto dump = [&](int level, const DfgGraph& dfg, const std::string& name) {
if (dumpDfgLevel() >= level) dfg.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-" + name);
if (dumpDfgLevel() >= level) dfg.dumpDotFilePrefixed("breakCycles-" + name);
};
// Can't do much with trivial things ('a = a' or 'a[1] = a[0]'), so bail
@ -1237,7 +1236,7 @@ breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) {
if (dumpDfgLevel() >= 9) {
Vtx2Scc vtx2Scc = res.makeUserMap<uint64_t>();
V3DfgPasses::colorStronglyConnectedComponents(res, vtx2Scc);
res.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-remaining", [&](const DfgVertex& vtx) {
res.dumpDotFilePrefixed("breakCycles-remaining", [&](const DfgVertex& vtx) {
return vtx2Scc[vtx]; //
});
}

View File

@ -37,25 +37,17 @@ class V3DfgContext;
// Base class for all context objects
class V3DfgSubContext VL_NOT_FINAL {
V3DfgContext& m_ctx; // The whole context
const std::string m_label; // Label to add to stats, etc.
const std::string m_name; // Pass/algorithm name, to be used in statistics
protected:
VL_DEFINE_DEBUG_FUNCTIONS;
V3DfgSubContext(V3DfgContext& ctx, const std::string& label, const char* name)
: m_ctx{ctx}
, m_label{label}
, m_name{name} {}
V3DfgSubContext(const std::string& name)
: m_name{name} {}
void addStat(const std::string& what, double value) {
V3Stats::addStat("Optimizations, DFG " + m_label + " " + m_name + ", " + what, value);
V3Stats::addStat("Optimizations, DFG, " + m_name + ", " + what, value);
}
public:
inline const std::string& prefix() const;
const std::string& label() const { return m_label; }
};
//######################################################################
@ -74,8 +66,8 @@ public:
VDouble0 m_nonRepVar; // Non-representable due to unsupported variable properties
private:
V3DfgAstToDfgContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "AstToDfg"} {}
V3DfgAstToDfgContext()
: V3DfgSubContext{"AstToDfg"} {}
~V3DfgAstToDfgContext() {
addStat("input processes", m_inputs);
addStat("representable", m_representable);
@ -96,8 +88,8 @@ public:
VDouble0 m_decodersCreated; // Number of bianry to one-hot decoders created
private:
V3DfgBinToOneHotContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "BinToOneHot"} {}
V3DfgBinToOneHotContext()
: V3DfgSubContext{"BinToOneHot"} {}
~V3DfgBinToOneHotContext() { addStat("decoders created", m_decodersCreated); }
};
class V3DfgBreakCyclesContext final : public V3DfgSubContext {
@ -113,8 +105,8 @@ public:
VDouble0 m_nImprovements; // Number of changes made to graphs
private:
V3DfgBreakCyclesContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "BreakCycles"} {}
V3DfgBreakCyclesContext()
: V3DfgSubContext{"BreakCycles"} {}
~V3DfgBreakCyclesContext() {
addStat("made acyclic", m_nFixed);
addStat("improved", m_nImproved);
@ -132,8 +124,8 @@ public:
VDouble0 m_eliminated; // Number of common sub-expressions eliminated
private:
V3DfgCseContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "CSE"} {}
explicit V3DfgCseContext(const std::string& label)
: V3DfgSubContext{"CSE " + label} {}
~V3DfgCseContext() { addStat("expressions eliminated", m_eliminated); }
};
@ -150,8 +142,8 @@ public:
VDouble0 m_varRefsSubstituted; // Number of variable references substituted in the Ast
private:
V3DfgDfgToAstContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "DfgToAst"} {}
V3DfgDfgToAstContext()
: V3DfgSubContext{"DfgToAst"} {}
~V3DfgDfgToAstContext() {
addStat("output variables", m_outputVariables);
addStat("output variables with default driver", m_outputVariablesWithDefault);
@ -173,8 +165,34 @@ public:
std::vector<AstNode*> m_deleteps; // AstVar/AstVarScope that can be deleted at the end
private:
V3DfgPeepholeContext(V3DfgContext& ctx, const std::string& label) VL_MT_DISABLED;
~V3DfgPeepholeContext() VL_MT_DISABLED;
V3DfgPeepholeContext()
: V3DfgSubContext{"Peephole"} {
const auto checkEnabled = [this](VDfgPeepholePattern id) {
std::string str{id.ascii()};
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { //
return c == '_' ? '-' : std::tolower(c);
});
m_enabled[id] = v3Global.opt.fDfgPeepholeEnabled(str);
};
#define OPTIMIZATION_CHECK_ENABLED(id, name) checkEnabled(VDfgPeepholePattern::id);
FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION(OPTIMIZATION_CHECK_ENABLED)
#undef OPTIMIZATION_CHECK_ENABLED
}
~V3DfgPeepholeContext() {
for (AstNode* const nodep : m_deleteps) {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
const auto emitStat = [this](VDfgPeepholePattern id) {
std::string str{id.ascii()};
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { //
return c == '_' ? ' ' : std::tolower(c);
});
addStat(str, m_count[id]);
};
#define OPTIMIZATION_EMIT_STATS(id, name) emitStat(VDfgPeepholePattern::id);
FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION(OPTIMIZATION_EMIT_STATS)
#undef OPTIMIZATION_EMIT_STATS
}
};
class V3DfgPushDownSelsContext final : public V3DfgSubContext {
// Only V3DfgContext can create an instance
@ -185,8 +203,8 @@ public:
size_t m_pushedDown = 0; // Number of selects pushed down through concatenations
size_t m_wouldBeCyclic = 0; // Number of selects not pushed due to cycle
private:
V3DfgPushDownSelsContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "PushDownSels"} {}
V3DfgPushDownSelsContext()
: V3DfgSubContext{"PushDownSels"} {}
~V3DfgPushDownSelsContext() {
addStat("sels pushed down", m_pushedDown);
addStat("would be cyclic", m_wouldBeCyclic);
@ -204,8 +222,8 @@ public:
VDouble0 m_temporariesIntroduced; // Number of temporaries introduced for reuse
private:
V3DfgRegularizeContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "Regularize"} {}
V3DfgRegularizeContext()
: V3DfgSubContext{"Regularize"} {}
~V3DfgRegularizeContext() {
for (AstNode* const nodep : m_deleteps) {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
@ -227,8 +245,8 @@ public:
VDouble0 m_logicDeleted; // Number of logic blocks removed from the Dfg and the Ast
private:
V3DfgRemoveUnobservableContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "RemoveUnobservable"} {}
V3DfgRemoveUnobservableContext()
: V3DfgSubContext{"RemoveUnobservable"} {}
~V3DfgRemoveUnobservableContext() {
addStat("variables removed", m_varsRemoved);
addStat("variables deleted", m_varsDeleted);
@ -291,8 +309,8 @@ public:
} m_synt;
private:
V3DfgSynthesisContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "Synthesis"} {}
V3DfgSynthesisContext()
: V3DfgSubContext{"Synthesis"} {}
~V3DfgSynthesisContext() {
// Conversion statistics
addStat("conv / input assignments", m_conv.inputAssignments);
@ -367,64 +385,40 @@ private:
// Top level V3DfgContext
class V3DfgContext final {
const std::string m_label; // Label to add to stats, etc.
const std::string m_prefix; // Prefix to add to file dumps (derived from label)
public:
// STATE
// Global statistics
VDouble0 m_modules; // Number of modules optimized
// Sub contexts - keep sorted by type
V3DfgAstToDfgContext m_ast2DfgContext{*this, m_label};
V3DfgBinToOneHotContext m_binToOneHotContext{*this, m_label};
V3DfgBreakCyclesContext m_breakCyclesContext{*this, m_label};
V3DfgCseContext m_cseContext0{*this, m_label + " 1st"};
V3DfgCseContext m_cseContext1{*this, m_label + " 2nd"};
V3DfgDfgToAstContext m_dfg2AstContext{*this, m_label};
V3DfgPeepholeContext m_peepholeContext{*this, m_label};
V3DfgPushDownSelsContext m_pushDownSelsContext{*this, m_label};
V3DfgRegularizeContext m_regularizeContext{*this, m_label};
V3DfgRemoveUnobservableContext m_removeUnobservableContext{*this, m_label};
V3DfgSynthesisContext m_synthContext{*this, m_label};
V3DfgAstToDfgContext m_ast2DfgContext;
V3DfgBinToOneHotContext m_binToOneHotContext;
V3DfgBreakCyclesContext m_breakCyclesContext;
V3DfgCseContext m_cseContext0{"1st"};
V3DfgCseContext m_cseContext1{"2nd"};
V3DfgDfgToAstContext m_dfg2AstContext;
V3DfgPeepholeContext m_peepholeContext;
V3DfgPushDownSelsContext m_pushDownSelsContext;
V3DfgRegularizeContext m_regularizeContext;
V3DfgRemoveUnobservableContext m_removeUnobservableContext;
V3DfgSynthesisContext m_synthContext;
// Node pattern collector
V3DfgPatternStats m_patternStats;
// CONSTRUCTOR
explicit V3DfgContext(const std::string& label)
: m_label{label}
, m_prefix{VString::removeWhitespace(label) + "-"} {}
V3DfgContext() = default;
~V3DfgContext() {
const string front = "Optimizations, DFG " + label() + " General, ";
V3Stats::addStat(front + "modules", m_modules);
// Print the collected patterns
if (v3Global.opt.stats()) {
// Label to lowercase, without spaces
std::string ident = label();
std::transform(ident.begin(), ident.end(), ident.begin(), [](unsigned char c) { //
return c == ' ' ? '_' : std::tolower(c);
});
// File to dump to
const std::string filename = v3Global.opt.hierTopDataDir() + "/"
+ v3Global.opt.prefix() + "__stats_dfg_patterns__" + ident
+ ".txt";
+ v3Global.opt.prefix() + "__stats_dfg_patterns.txt";
// Open, write, close
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
if (ofp->fail()) v3fatal("Can't write file: " << filename);
m_patternStats.dump(label(), *ofp);
m_patternStats.dump(*ofp);
}
}
// ACCESSORS
const std::string& label() const { return m_label; }
const std::string& prefix() const { return m_prefix; }
};
const std::string& V3DfgSubContext::prefix() const { return m_ctx.prefix(); }
#endif //VERILATOR_V3DFGCONTEXT_H_

View File

@ -22,9 +22,7 @@
#include "V3Dfg.h"
#include "V3DfgPasses.h"
#include "V3File.h"
#include <deque>
#include <unordered_map>
#include <vector>
@ -103,7 +101,7 @@ class SplitIntoComponents final {
// Allocate the component graphs
m_components.resize(m_componentCounter - 1);
for (size_t i = 1; i < m_componentCounter; ++i) {
m_components[i - 1].reset(new DfgGraph{m_dfg.modulep(), m_prefix + cvtToStr(i - 1)});
m_components[i - 1].reset(new DfgGraph{m_prefix + cvtToStr(i - 1)});
}
// Move the vertices to the component graphs
moveVertices(m_dfg.varVertices());
@ -222,17 +220,9 @@ class ExtractCyclicComponents final {
DfgVertexVar*& clonep = m_clones[&vtx][component];
if (!clonep) {
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
if (AstVarScope* const vscp = pVtxp->varScopep()) {
clonep = new DfgVarPacked{m_dfg, vscp};
} else {
clonep = new DfgVarPacked{m_dfg, pVtxp->varp()};
}
clonep = new DfgVarPacked{m_dfg, pVtxp->vscp()};
} else if (DfgVarArray* const aVtxp = vtx.cast<DfgVarArray>()) {
if (AstVarScope* const vscp = aVtxp->varScopep()) {
clonep = new DfgVarArray{m_dfg, vscp};
} else {
clonep = new DfgVarArray{m_dfg, aVtxp->varp()};
}
clonep = new DfgVarArray{m_dfg, aVtxp->vscp()};
}
UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type");
m_component[clonep] = component;
@ -326,7 +316,7 @@ class ExtractCyclicComponents final {
// Allocate result graphs
m_components.resize(nComponents);
for (uint32_t i = 0; i < nComponents; ++i) {
m_components[i].reset(new DfgGraph{m_dfg.modulep(), m_prefix + cvtToStr(i)});
m_components[i].reset(new DfgGraph{m_prefix + cvtToStr(i)});
}
// Fix up edges crossing components (we can only do this at variable boundaries, and the

View File

@ -32,8 +32,6 @@
#include "V3DfgPasses.h"
#include "V3UniqueNames.h"
#include <unordered_map>
VL_DEFINE_DEBUG_FUNCTIONS;
namespace {
@ -92,34 +90,19 @@ AstShiftRS* makeNode<AstShiftRS, DfgShiftRS, AstNodeExpr*, AstNodeExpr*>( //
} // namespace
template <bool T_Scoped>
class DfgToAstVisitor final : DfgVisitor {
// NODE STATE
// AstScope::user2p // The combinational AstActive under this scope
const VNUser2InUse m_user2InUse;
// TYPES
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
using Container = std::conditional_t<T_Scoped, AstActive, AstNodeModule>;
// STATE
AstModule* const m_modp; // The parent/result module - This is nullptr when T_Scoped
V3DfgDfgToAstContext& m_ctx; // The context for stats
AstNodeExpr* m_resultp = nullptr; // The result node of the current traversal
AstAlways* m_alwaysp = nullptr; // Process to add assignments to, if have a default driver
Container* m_containerp = nullptr; // The AstNodeModule or AstActive to insert assigns into
AstActive* m_activep = nullptr; // The AstNodeModule or AstActive to insert assigns into
// METHODS
static Variable* getNode(const DfgVertexVar* vtxp) {
if VL_CONSTEXPR_CXX17 (T_Scoped) {
return reinterpret_cast<Variable*>(vtxp->varScopep());
} else {
return reinterpret_cast<Variable*>(vtxp->varp());
}
}
static AstActive* getCombActive(AstScope* scopep) {
if (!scopep->user2p()) {
// Try to find the existing combinational AstActive
@ -173,7 +156,7 @@ class DfgToAstVisitor final : DfgVisitor {
// Otherwise create an AssignW
AstAssignW* const ap = new AstAssignW{flp, lhsp, rhsp};
m_containerp->addStmtsp(new AstAlways{ap});
m_activep->addStmtsp(new AstAlways{ap});
}
void convertDriver(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) {
@ -234,11 +217,11 @@ class DfgToAstVisitor final : DfgVisitor {
} // LCOV_EXCL_STOP
void visit(DfgVarPacked* vtxp) override {
m_resultp = new AstVarRef{vtxp->fileline(), getNode(vtxp), VAccess::READ};
m_resultp = new AstVarRef{vtxp->fileline(), vtxp->vscp(), VAccess::READ};
}
void visit(DfgVarArray* vtxp) override {
m_resultp = new AstVarRef{vtxp->fileline(), getNode(vtxp), VAccess::READ};
m_resultp = new AstVarRef{vtxp->fileline(), vtxp->vscp(), VAccess::READ};
}
void visit(DfgConst* vtxp) override { //
@ -264,8 +247,7 @@ class DfgToAstVisitor final : DfgVisitor {
// Constructor
DfgToAstVisitor(DfgGraph& dfg, V3DfgDfgToAstContext& ctx)
: m_modp{dfg.modulep()}
, m_ctx{ctx} {
: m_ctx{ctx} {
if (v3Global.opt.debugCheck()) V3DfgPasses::typeCheck(dfg);
// Convert the graph back to combinational assignments. The graph must have been
@ -283,24 +265,18 @@ class DfgToAstVisitor final : DfgVisitor {
// Render variable assignments
FileLine* const flp = vtx.driverFileLine() ? vtx.driverFileLine() : vtx.fileline();
AstVarRef* const lhsp = new AstVarRef{flp, getNode(&vtx), VAccess::WRITE};
AstVarRef* const lhsp = new AstVarRef{flp, vtx.vscp(), VAccess::WRITE};
VL_RESTORER(m_containerp);
if VL_CONSTEXPR_CXX17 (T_Scoped) {
// Add it to the scope holding the target variable
AstActive* const activep = getCombActive(vtx.varScopep()->scopep());
m_containerp = reinterpret_cast<Container*>(activep);
} else {
// Add it to the parent module of the DfgGraph
m_containerp = reinterpret_cast<Container*>(m_modp);
}
// Add it to the scope holding the target variable
VL_RESTORER(m_activep);
m_activep = getCombActive(vtx.vscp()->scopep());
// If there is a default value, render all drivers under an AstAlways
VL_RESTORER(m_alwaysp);
if (DfgVertex* const defaultp = vtx.defaultp()) {
++m_ctx.m_outputVariablesWithDefault;
m_alwaysp = new AstAlways{vtx.fileline(), VAlwaysKwd::ALWAYS_COMB, nullptr};
m_containerp->addStmtsp(m_alwaysp);
m_activep->addStmtsp(m_alwaysp);
// The default assignment needs to go first
createAssignment(vtx.fileline(), lhsp->cloneTreePure(false), defaultp);
}
@ -326,8 +302,6 @@ class DfgToAstVisitor final : DfgVisitor {
if (VN_IS(exprp, VarRef)) {
++m_ctx.m_varRefsSubstituted;
} else {
UASSERT_OBJ(!dfg.modulep(), &vtx,
"Expressions should only be inlined on final scoped run");
++m_ctx.m_expressionsInlined;
}
rVtxp->exprp()->replaceWith(exprp);
@ -342,9 +316,5 @@ public:
};
void V3DfgPasses::dfgToAst(DfgGraph& dfg, V3DfgContext& ctx) {
if (dfg.modulep()) {
DfgToAstVisitor</* T_Scoped: */ false>::apply(dfg, ctx.m_dfg2AstContext);
} else {
DfgToAstVisitor</* T_Scoped: */ true>::apply(dfg, ctx.m_dfg2AstContext);
}
DfgToAstVisitor::apply(dfg, ctx.m_dfg2AstContext);
}

View File

@ -26,6 +26,7 @@
#include "V3Const.h"
#include "V3Dfg.h"
#include "V3DfgPasses.h"
#include "V3Error.h"
#include "V3Graph.h"
#include "V3UniqueNames.h"
@ -255,84 +256,39 @@ class DataflowOptimize final {
// STATE
V3DfgContext m_ctx; // The context holding values that need to persist across multiple graphs
const bool m_scoped; // Running after V3Scope
void endOfStage(const std::string& name, const DfgGraph* dfgp = nullptr) {
// Dump the graph for debugging if given one
if (VL_UNLIKELY(dumpDfgLevel() >= 8 && dfgp)) {
dfgp->dumpDotFilePrefixed(m_ctx.prefix() + name);
}
if (VL_UNLIKELY(dumpDfgLevel() >= 8 && dfgp)) dfgp->dumpDotFilePrefixed(name);
// Dump stage stats only in scoped mode when running on the whole netlist
if (VL_UNLIKELY(v3Global.opt.stats() && m_scoped)) {
V3Stats::statsStage("dfg-optimize-" + name);
}
if (VL_UNLIKELY(v3Global.opt.stats())) V3Stats::statsStage("dfg-optimize-" + name);
}
// Mark variables with external references
void markExternallyReferencedVariables(AstNetlist* netlistp) {
netlistp->foreach([this](AstNode* nodep) {
netlistp->foreach([](AstNode* nodep) {
// Check variable flags
if (m_scoped) {
if (AstVarScope* const vscp = VN_CAST(nodep, VarScope)) {
const AstVar* const varp = vscp->varp();
// Force and trace have already been processed
const bool hasExtRd = varp->isPrimaryIO() || varp->isSigUserRdPublic();
const bool hasExtWr = varp->isPrimaryIO() || varp->isSigUserRWPublic();
if (hasExtRd) DfgVertexVar::setHasExtRdRefs(vscp);
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(vscp);
return;
}
// Check direct references
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
if (refp->access().isRW()) DfgVertexVar::setHasRWRefs(refp->varScopep());
UASSERT_OBJ(!refp->classOrPackagep(), refp, "V3Scope should have removed");
return;
}
} else {
if (AstVar* const varp = VN_CAST(nodep, Var)) {
const bool hasExtRd = varp->isPrimaryIO() || varp->isSigUserRdPublic() //
|| varp->isForced() || varp->isTrace();
const bool hasExtWr = varp->isPrimaryIO() || varp->isSigUserRWPublic() //
|| varp->isForced();
if (hasExtRd) DfgVertexVar::setHasExtRdRefs(varp);
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(varp);
return;
}
// Check direct references
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
AstVar* const varp = refp->varp();
if (refp->access().isRW()) DfgVertexVar::setHasRWRefs(varp);
// With classOrPackagep set this is a disguised hierarchical reference, mark
if (refp->classOrPackagep()) {
if (refp->access().isReadOrRW()) DfgVertexVar::setHasExtRdRefs(varp);
if (refp->access().isWriteOrRW()) DfgVertexVar::setHasExtWrRefs(varp);
}
return;
}
}
// Check hierarchical references
if (const AstVarXRef* const xrefp = VN_CAST(nodep, VarXRef)) {
AstVar* const tgtp = xrefp->varp();
if (!tgtp) return;
if (xrefp->access().isReadOrRW()) DfgVertexVar::setHasExtRdRefs(tgtp);
if (xrefp->access().isWriteOrRW()) DfgVertexVar::setHasExtWrRefs(tgtp);
if (xrefp->access().isRW()) DfgVertexVar::setHasRWRefs(tgtp);
if (AstVarScope* const vscp = VN_CAST(nodep, VarScope)) {
const AstVar* const varp = vscp->varp();
// Force and trace have already been processed
const bool hasExtRd = varp->isPrimaryIO() || varp->isSigUserRdPublic();
const bool hasExtWr
= (varp->isPrimaryIO() && varp->isNonOutput()) || varp->isSigUserRWPublic();
if (hasExtRd) DfgVertexVar::setHasExtRdRefs(vscp);
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(vscp);
return;
}
// Check references
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
if (refp->access().isRW()) DfgVertexVar::setHasRWRefs(refp->varScopep());
UASSERT_OBJ(!refp->classOrPackagep(), refp, "V3Scope should have removed");
return;
}
UASSERT_OBJ(!VN_IS(nodep, VarXRef), nodep, "V3Scope should have removed");
// Check cell ports
if (const AstCell* const cellp = VN_CAST(nodep, Cell)) {
for (const AstPin *pinp = cellp->pinsp(), *nextp; pinp; pinp = nextp) {
nextp = VN_AS(pinp->nextp(), Pin);
AstVar* const tgtp = pinp->modVarp();
if (!tgtp) return;
const VDirection dir = tgtp->direction();
// hasExtRd/hasExtWr from perspective of Pin
const bool hasExtRd = dir == VDirection::OUTPUT || dir.isInoutOrRef();
const bool hasExtWr = dir == VDirection::INPUT || dir.isInoutOrRef();
if (hasExtRd) DfgVertexVar::setHasExtRdRefs(tgtp);
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(tgtp);
}
// Why does this not hold?
UASSERT_OBJ(true || !cellp->pinsp(), cellp, "Pins should have been lowered");
return;
}
});
@ -415,9 +371,7 @@ class DataflowOptimize final {
}
}
DataflowOptimize(AstNetlist* netlistp, const string& label)
: m_ctx{label}
, m_scoped{!!netlistp->topScopep()} {
DataflowOptimize(AstNetlist* netlistp) {
// Mark interfaces that might be referenced by a virtual interface
if (v3Global.hasVirtIfaces()) {
@ -430,51 +384,29 @@ class DataflowOptimize final {
// Mark variables with external references
markExternallyReferencedVariables(netlistp);
if (!m_scoped) {
// Pre V3Scope application. Run on each module separately.
for (AstNode* nodep = netlistp->modulesp(); nodep; nodep = nodep->nextp()) {
// Only optimize proper modules
AstModule* const modp = VN_CAST(nodep, Module);
if (!modp) continue;
// Pre V3Scope application. Run on module.
UINFO(4, "Applying DFG optimization to module '" << modp->name() << "'");
++m_ctx.m_modules;
// Build the DFG of this module or netlist
const std::unique_ptr<DfgGraph> dfgp = V3DfgPasses::astToDfg(*modp, m_ctx);
endOfStage("ast-to-dfg", dfgp.get());
// Actually process the graph
optimize(*dfgp);
// Convert back to Ast
V3DfgPasses::dfgToAst(*dfgp, m_ctx);
endOfStage("dfg-to-ast", dfgp.get());
}
} else {
// Post V3Scope application. Run on whole netlist.
UINFO(4, "Applying DFG optimization to entire netlist");
// Build the DFG of the entire netlist
const std::unique_ptr<DfgGraph> dfgp = V3DfgPasses::astToDfg(*netlistp, m_ctx);
endOfStage("ast-to-dfg", dfgp.get());
// Actually process the graph
optimize(*dfgp);
// Convert back to Ast
V3DfgPasses::dfgToAst(*dfgp, m_ctx);
endOfStage("dfg-to-ast", dfgp.get());
// Some sentrees might have become constant, remove them
removeNeverActives(netlistp);
}
// Post V3Scope application. Run on whole netlist.
UINFO(4, "Applying DFG optimization to entire netlist");
// Build the DFG of the entire netlist
const std::unique_ptr<DfgGraph> dfgp = V3DfgPasses::astToDfg(*netlistp, m_ctx);
endOfStage("ast-to-dfg", dfgp.get());
// Actually process the graph
optimize(*dfgp);
// Convert back to Ast
V3DfgPasses::dfgToAst(*dfgp, m_ctx);
endOfStage("dfg-to-ast", dfgp.get());
// Some sentrees might have become constant, remove them
removeNeverActives(netlistp);
// Reset interned types so the corresponding Ast types can be garbage collected
DfgDataType::reset();
}
public:
static void apply(AstNetlist* netlistp, const string& label) {
DataflowOptimize{netlistp, label};
}
static void apply(AstNetlist* netlistp) { DataflowOptimize{netlistp}; }
};
void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
void V3DfgOptimizer::optimize(AstNetlist* netlistp) {
UINFO(2, __FUNCTION__ << ":");
DataflowOptimize::apply(netlistp, label);
DataflowOptimize::apply(netlistp);
V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTreeEitherLevel() >= 3);
}

View File

@ -29,7 +29,7 @@ namespace V3DfgOptimizer {
void extract(AstNetlist*) VL_MT_DISABLED;
// Optimize the design
void optimize(AstNetlist*, const string& label) VL_MT_DISABLED;
void optimize(AstNetlist*) VL_MT_DISABLED;
} // namespace V3DfgOptimizer
#endif // Guard

View File

@ -18,6 +18,7 @@
#include "V3DfgPasses.h"
#include "V3Ast.h"
#include "V3Dfg.h"
#include "V3File.h"
#include "V3Global.h"
@ -71,7 +72,7 @@ void V3DfgPasses::removeUnobservable(DfgGraph& dfg, V3DfgContext& dfgCtx) {
if (vVtxp->hasSinks()) continue;
if (vVtxp->isObserved()) continue;
DfgVertex* const srcp = vVtxp->srcp(); // Must be a DfgUnresolved or nullptr
AstNode* const varp = vVtxp->nodep();
AstVarScope* const vscp = vVtxp->vscp();
// Can delete the Ast variable too if it has no other references
const bool delAst = (!srcp || !srcp->nInputs()) //
&& !vVtxp->hasExtWrRefs() //
@ -79,7 +80,7 @@ void V3DfgPasses::removeUnobservable(DfgGraph& dfg, V3DfgContext& dfgCtx) {
VL_DO_DANGLING(vVtxp->unlinkDelete(dfg), vVtxp);
if (srcp) VL_DO_DANGLING(srcp->unlinkDelete(dfg), srcp);
if (delAst) {
VL_DO_DANGLING(varp->unlinkFrBack()->deleteTree(), varp);
VL_DO_DANGLING(vscp->unlinkFrBack()->deleteTree(), vscp);
++ctx.m_varsDeleted;
} else {
++ctx.m_varsRemoved;
@ -96,7 +97,7 @@ void V3DfgPasses::removeUnobservable(DfgGraph& dfg, V3DfgContext& dfgCtx) {
// AstVar/AstVarScope::user2p() -> corresponding DfgVertexVar* in the graph
const VNUser2InUse m_user2InUse;
logicp->foreachSource([](DfgVertex& src) {
src.as<DfgVertexVar>()->nodep()->user2p(&src);
src.as<DfgVertexVar>()->vscp()->user2p(&src);
return false;
});
V3DfgPasses::addAstRefs(dfg, logicp->nodep(), [](AstNode* varp) { //
@ -181,8 +182,6 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
}
void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
if (!dfg.modulep()) return; // binToOneHot only works with unscoped DfgGraphs for now
// Structure to keep track of comparison details
struct Term final {
DfgVertex* m_vtxp = nullptr; // Vertex to replace
@ -291,6 +290,8 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
// Sequence numbers for name generation
size_t nTables = 0;
DfgVertex::ScopeCache scopeCache;
// Create decoders for each srcp
for (DfgVertex* const srcp : srcps) {
const Val2Terms& val2Terms = vtx2Val2Terms[srcp];
@ -318,6 +319,38 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
// - and replace the comparisons with 'tab[const]'
FileLine* const flp = srcp->fileline();
AstScope* const scopep = srcp->scopep(scopeCache, true);
AstActive* const staticActivep = [scopep]() {
// Try to find the existing combinational AstActive
for (AstNode* nodep = scopep->blocksp(); nodep; nodep = nodep->nextp()) {
AstActive* const activep = VN_CAST(nodep, Active);
if (!activep) continue;
if (activep->sentreep()->hasStatic()) return activep;
}
// If there isn't one, create a new one
FileLine* const flp = scopep->fileline();
AstSenTree* const stp = new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Static{}}};
AstActive* const activep = new AstActive{flp, "", stp};
activep->senTreeStorep(stp);
scopep->addBlocksp(activep);
return activep;
}();
AstActive* const combActivep = [scopep]() {
// Try to find the existing combinational AstActive
for (AstNode* nodep = scopep->blocksp(); nodep; nodep = nodep->nextp()) {
AstActive* const activep = VN_CAST(nodep, Active);
if (!activep) continue;
if (activep->sentreep()->hasCombo()) return activep;
}
// If there isn't one, create a new one
FileLine* const flp = scopep->fileline();
AstSenTree* const stp = new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Combo{}}};
AstActive* const activep = new AstActive{flp, "", stp};
activep->senTreeStorep(stp);
scopep->addBlocksp(activep);
return activep;
}();
// Required data types
const DfgDataType& idxDType = srcp->dtype();
@ -330,31 +363,31 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
if (DfgVertexVar* const vp = srcp->getResultVar()) return vp->as<DfgVarPacked>();
// Otherwise create a new variable
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, nullptr);
vtxp->varp()->isInternal(true);
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, scopep);
vtxp->vscp()->varp()->isInternal(true);
vtxp->srcp(srcp);
return vtxp->as<DfgVarPacked>();
}();
AstVar* const idxVarp = idxVtxp->varp();
AstVarScope* const idxVscp = idxVtxp->vscp();
// The previous index variable - we don't need a vertex for this
AstVar* const preVarp = [&]() {
AstVarScope* const preVscp = [&]() {
const std::string name = dfg.makeUniqueName("BinToOneHot_Pre", nTables);
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, idxDType.astDtypep()};
dfg.modulep()->addStmtsp(varp);
varp->isInternal(true);
varp->noReset(true);
varp->setIgnoreSchedWrite();
return varp;
DfgVertexVar* const vtxp = dfg.makeNewVar(flp, name, idxDType, scopep);
AstVarScope* const vscp = vtxp->vscp();
VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp);
vscp->varp()->isInternal(true);
vscp->varp()->noReset(true);
vscp->varp()->setIgnoreSchedWrite();
return vscp;
}();
// The table variable
DfgVarArray* const tabVtxp = [&]() {
const std::string name = dfg.makeUniqueName("BinToOneHot_Tab", nTables);
DfgVarArray* const varp
= dfg.makeNewVar(flp, name, tabDType, nullptr)->as<DfgVarArray>();
varp->varp()->isInternal(true);
varp->varp()->noReset(true);
DfgVertexVar* const varp = dfg.makeNewVar(flp, name, tabDType, scopep);
varp->vscp()->varp()->isInternal(true);
varp->vscp()->varp()->noReset(true);
varp->setHasModWrRefs();
return varp;
return varp->as<DfgVarArray>();
}();
++nTables;
@ -362,16 +395,16 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
// Initialize 'tab' and 'pre' variables statically
AstInitialStatic* const initp = new AstInitialStatic{flp, nullptr};
dfg.modulep()->addStmtsp(initp);
staticActivep->addStmtsp(initp);
{ // pre = 0
initp->addStmtsp(new AstAssign{
flp, //
new AstVarRef{flp, preVarp, VAccess::WRITE}, //
new AstVarRef{flp, preVscp, VAccess::WRITE}, //
new AstConst{flp, AstConst::WidthedValue{}, static_cast<int>(width), 0}});
}
{ // tab.fill(0)
AstCMethodHard* const callp = new AstCMethodHard{
flp, new AstVarRef{flp, tabVtxp->varp(), VAccess::WRITE}, VCMethod::UNPACKED_FILL};
flp, new AstVarRef{flp, tabVtxp->vscp(), VAccess::WRITE}, VCMethod::UNPACKED_FILL};
callp->addPinsp(new AstConst{flp, AstConst::BitFalse{}});
callp->dtypeSetVoid();
initp->addStmtsp(callp->makeStmt());
@ -379,28 +412,28 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
// Build the decoder logic
AstAlways* const logicp = new AstAlways{flp, VAlwaysKwd::ALWAYS_COMB, nullptr, nullptr};
dfg.modulep()->addStmtsp(logicp);
combActivep->addStmtsp(logicp);
{ // tab[pre] = 0;
logicp->addStmtsp(new AstAssign{
flp, //
new AstArraySel{flp, new AstVarRef{flp, tabVtxp->varp(), VAccess::WRITE},
new AstVarRef{flp, preVarp, VAccess::READ}}, //
new AstArraySel{flp, new AstVarRef{flp, tabVtxp->vscp(), VAccess::WRITE},
new AstVarRef{flp, preVscp, VAccess::READ}}, //
new AstConst{flp, AstConst::BitFalse{}}});
}
{ // tab[idx] = 1
AstVarRef* const idxRefp = new AstVarRef{flp, idxVarp, VAccess::READ};
AstVarRef* const idxRefp = new AstVarRef{flp, idxVscp, VAccess::READ};
logicp->addStmtsp(new AstAssign{
flp, //
new AstArraySel{flp, new AstVarRef{flp, tabVtxp->varp(), VAccess::WRITE},
new AstArraySel{flp, new AstVarRef{flp, tabVtxp->vscp(), VAccess::WRITE},
idxRefp}, //
new AstConst{flp, AstConst::BitTrue{}}});
DfgAstRd* const astRdp = new DfgAstRd{dfg, idxRefp, false, false};
astRdp->srcp(idxVtxp);
}
{ // pre = idx
AstVarRef* const idxRefp = new AstVarRef{flp, idxVarp, VAccess::READ};
AstVarRef* const idxRefp = new AstVarRef{flp, idxVscp, VAccess::READ};
logicp->addStmtsp(new AstAssign{flp, //
new AstVarRef{flp, preVarp, VAccess::WRITE}, //
new AstVarRef{flp, preVscp, VAccess::WRITE}, //
idxRefp});
DfgAstRd* const astRdp = new DfgAstRd{dfg, idxRefp, false, false};
astRdp->srcp(idxVtxp);
@ -442,7 +475,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgContext& ctx) {
// Apply the pass
pass();
// Debug dump
if (dump) dfg.dumpDotFilePrefixed(ctx.prefix() + "opt-" + VString::removeWhitespace(name));
if (dump) dfg.dumpDotFilePrefixed("opt-" + VString::removeWhitespace(name));
// Internal type check
if (v3Global.opt.debugCheck()) V3DfgPasses::typeCheck(dfg);
};
@ -456,10 +489,7 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgContext& ctx) {
run("cse0 ", dumpLvl >= 4, [&]() { cse(dfg, ctx.m_cseContext0); });
run("binToOneHot ", dumpLvl >= 4, [&]() { binToOneHot(dfg, ctx.m_binToOneHotContext); });
run("peephole ", dumpLvl >= 4, [&]() { peephole(dfg, ctx.m_peepholeContext); });
// Run only on final scoped DfgGraphs, as otherwise later DfgPeephole wold just undo this work
if (!dfg.modulep()) {
run("pushDownSels", dumpLvl >= 4, [&]() { pushDownSels(dfg, ctx.m_pushDownSelsContext); });
}
run("pushDownSels", dumpLvl >= 4, [&]() { pushDownSels(dfg, ctx.m_pushDownSelsContext); });
run("cse1 ", dumpLvl >= 4, [&]() { cse(dfg, ctx.m_cseContext1); });
run("output ", dumpLvl >= 3, [&]() { /* debug dump only */ });

View File

@ -30,19 +30,16 @@ namespace V3DfgPasses {
// Top level entry points
//===========================================================================
// Construct a DfGGraph representing the combinational logic in the given AstModule. The logic
// that is represented by the graph is removed from the given AstModule. Returns the
// constructed DfgGraph.
std::unique_ptr<DfgGraph> astToDfg(AstModule&, V3DfgContext&) VL_MT_DISABLED;
// Same as above, but for the entire netlist, after V3Scope
// Construct a DfGGraph representing the combinational logic in the netlist.
// The logic that is represented by the graph is removed from the netlist.
// Returns the constructed DfgGraph.
std::unique_ptr<DfgGraph> astToDfg(AstNetlist&, V3DfgContext&) VL_MT_DISABLED;
// Add DfgVertexAst to the given DfgGraph for all references in the given AstNode.
// The function 'getVarVertex' is used to get the DfgVertexVar for an AstVar/AstVarScope.
// The function 'getVarVertex' is used to get the DfgVertexVar for an AstVarScope.
// If it returns nullptr, the reference will be ignored.
void addAstRefs(DfgGraph& dfg, AstNode* nodep,
std::function<DfgVertexVar*(AstNode*)> getVarVertex) VL_MT_DISABLED;
std::function<DfgVertexVar*(AstVarScope*)> getVarVertex) VL_MT_DISABLED;
// Remove unobservable variabels and logic that drives only such variables
void removeUnobservable(DfgGraph&, V3DfgContext&) VL_MT_DISABLED;

View File

@ -28,7 +28,7 @@ class V3DfgPatternStats final {
static constexpr uint32_t MAX_PATTERN_DEPTH = 4;
std::map<std::string, std::string> m_internedConsts; // Interned constants
std::map<const AstNode*, std::string> m_internedVars; // Interned variables
std::map<const AstVarScope*, std::string> m_internedVars; // Interned variables
std::map<uint32_t, std::string> m_internedSelLsbs; // Interned lsb value for selects
std::map<uint32_t, std::string> m_internedWordWidths; // Interned widths
std::map<uint32_t, std::string> m_internedWideWidths; // Interned widths
@ -51,7 +51,7 @@ class V3DfgPatternStats final {
}
const std::string& internVar(const DfgVertexVar& vtx) {
const auto pair = m_internedVars.emplace(vtx.nodep(), "v");
const auto pair = m_internedVars.emplace(vtx.vscp(), "v");
if (pair.second) pair.first->second += toLetters(m_internedVars.size() - 1);
return pair.first->second;
}
@ -167,10 +167,10 @@ public:
});
}
void dump(const std::string& stage, std::ostream& os) {
void dump(std::ostream& os) {
using Line = std::pair<std::string, size_t>;
for (uint32_t i = MIN_PATTERN_DEPTH; i <= MAX_PATTERN_DEPTH; ++i) {
os << "DFG '" << stage << "' patterns with depth " << i << '\n';
os << "DFG patterns with depth " << i << '\n';
// Pick up pattern accumulators with given depth
const auto& patternCounts = m_patterCounts[i];

View File

@ -38,11 +38,11 @@
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3Ast.h"
#include "V3Dfg.h"
#include "V3DfgCache.h"
#include "V3DfgPasses.h"
#include "V3DfgPeepholePatterns.h"
#include "V3Stats.h"
#include <algorithm>
#include <cctype>
@ -50,36 +50,6 @@
VL_DEFINE_DEBUG_FUNCTIONS;
V3DfgPeepholeContext::V3DfgPeepholeContext(V3DfgContext& ctx, const std::string& label)
: V3DfgSubContext{ctx, label, "Peephole"} {
const auto checkEnabled = [this](VDfgPeepholePattern id) {
std::string str{id.ascii()};
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { //
return c == '_' ? '-' : std::tolower(c);
});
m_enabled[id] = v3Global.opt.fDfgPeepholeEnabled(str);
};
#define OPTIMIZATION_CHECK_ENABLED(id, name) checkEnabled(VDfgPeepholePattern::id);
FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION(OPTIMIZATION_CHECK_ENABLED)
#undef OPTIMIZATION_CHECK_ENABLED
}
V3DfgPeepholeContext::~V3DfgPeepholeContext() {
for (AstNode* const nodep : m_deleteps) {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
const auto emitStat = [this](VDfgPeepholePattern id) {
std::string str{id.ascii()};
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { //
return c == '_' ? ' ' : std::tolower(c);
});
addStat(str, m_count[id]);
};
#define OPTIMIZATION_EMIT_STATS(id, name) emitStat(VDfgPeepholePattern::id);
FOR_EACH_DFG_PEEPHOLE_OPTIMIZATION(OPTIMIZATION_EMIT_STATS)
#undef OPTIMIZATION_EMIT_STATS
}
// clang-format off
template <typename T_Reduction>
struct ReductionToBitwiseImpl {};
@ -250,9 +220,8 @@ class V3DfgPeephole final : public DfgVisitor {
// to the variable and no references in other graph then delete the Ast var too.
const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>();
if (varp && !varp->isVolatile() && !varp->hasDfgRefs()) {
AstNode* const nodep = varp->nodep();
m_ctx.m_deleteps.push_back(varp->vscp());
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
m_ctx.m_deleteps.push_back(nodep);
} else {
VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp);
}
@ -1617,8 +1586,8 @@ class V3DfgPeephole final : public DfgVisitor {
if (!idxp) return;
DfgVarArray* const varp = vtxp->fromp()->cast<DfgVarArray>();
if (!varp) return;
if (varp->varp()->isForced()) return;
if (varp->varp()->isSigUserRWPublic()) return;
if (varp->vscp()->varp()->isForced()) return;
if (varp->vscp()->varp()->isSigUserRWPublic()) return;
DfgVertex* const srcp = varp->srcp();
if (!srcp) return;
@ -1862,16 +1831,13 @@ class V3DfgPeephole final : public DfgVisitor {
DfgSplicePacked* const sp = new DfgSplicePacked{m_dfg, flp, vtxp->dtype()};
m_vInfo[sp].m_id = ++m_lastId;
sp->addDriver(catp, lsb, flp);
AstScope* const scopep = [&]() -> AstScope* {
if (m_dfg.modulep()) return nullptr;
DfgVertex::ScopeCache scopeCache;
return vtxp->scopep(scopeCache, true);
}();
DfgVertex::ScopeCache scopeCache;
AstScope* const scopep = vtxp->scopep(scopeCache, true);
const std::string name = m_dfg.makeUniqueName("PeepholeNarrow", m_nTemps++);
DfgVertexVar* const varp = m_dfg.makeNewVar(flp, name, vtxp->dtype(), scopep);
varp->tmpForp(varp->nodep());
varp->tmpForp(varp->vscp());
m_vInfo[varp].m_id = ++m_lastId;
varp->varp()->isInternal(true);
varp->vscp()->varp()->isInternal(true);
varp->srcp(sp);
replace(varp);
return;
@ -2591,8 +2557,6 @@ class V3DfgPeephole final : public DfgVisitor {
if (const DfgVertexVar* const varp = sink.cast<DfgVertexVar>()) {
if (!varp->hasSinks() && !varp->isObserved()) return false;
}
// Keep before final scoped run if feeds an Ast reference
if (sink.is<DfgVertexAst>() && m_dfg.modulep()) return true;
// Keep if found more than one sink
if (foundOne) return true;
// Mark first sink found

View File

@ -54,26 +54,6 @@ class DfgRegularize final {
vtx.replaceWith(varp);
varp->srcp(&vtx);
}
// Const vertices driving an Ast reference can only be inlined in scoped
// mode as some algorithms assume VarRefs in certain places.
if (m_dfg.modulep()) {
for (DfgConst& vtx : m_dfg.constVertices()) {
const bool drivesAstRef = vtx.foreachSink([](const DfgVertex& dst) { //
return dst.is<DfgAstRd>();
});
if (!drivesAstRef) continue;
// The prefered result variable is the canonical one if exists
DfgVertexVar* const varp = vtx.getResultVar();
if (!varp) continue;
// Relink all other sinks reading this vertex to read 'varp'
varp->srcp(nullptr);
vtx.replaceWith(varp);
varp->srcp(&vtx);
}
}
}
std::unordered_set<const DfgVertexVar*> gatherCyclicVariables() {
@ -108,9 +88,6 @@ class DfgRegularize final {
UASSERT_OBJ(!aVtx.is<DfgVertexVar>(), &aVtx, "Should be an operation vertex");
if (bVtx.hasMultipleSinks()) {
// We are not inlining expressions prior to the final scoped run
if (m_dfg.modulep()) return true;
// Add a temporary if it's cheaper to store and load from memory than recompute
if (!aVtx.isCheaperThanLoad()) return true;
@ -123,10 +100,8 @@ class DfgRegularize final {
// No need to add a temporary if the single sink is a variable already
if (sink.is<DfgVertexVar>()) return false;
// Do not inline expressions prior to the final scoped run, or if they are in a loop
if (const DfgAstRd* const astRdp = sink.cast<DfgAstRd>()) {
return m_dfg.modulep() || astRdp->inLoop();
}
// Do not inline expressions into a loop body
if (const DfgAstRd* const astRdp = sink.cast<DfgAstRd>()) { return astRdp->inLoop(); }
// Make sure roots of wide concatenation trees are written to variables,
// this enables V3FuncOpt to split them which can be a big speed gain
@ -163,7 +138,7 @@ class DfgRegularize final {
});
// Delete corresponsing Ast variable at the end
if (const DfgVertexVar* const varp = vtx.cast<DfgVertexVar>()) {
m_ctx.m_deleteps.push_back(varp->nodep());
m_ctx.m_deleteps.push_back(varp->vscp());
}
// Remove the unused vertex
vtx.unlinkDelete(m_dfg);
@ -218,7 +193,6 @@ class DfgRegularize final {
// Insert a temporary variable for all vertices that have multiple non-variable sinks
// Scope cache for below
const bool scoped = !m_dfg.modulep();
DfgVertex::ScopeCache scopeCache;
// Ensure intermediate values used multiple times are written to variables
@ -233,7 +207,7 @@ class DfgRegularize final {
++m_ctx.m_temporariesIntroduced;
const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps);
FileLine* const flp = vtx.fileline();
AstScope* const scopep = scoped ? vtx.scopep(scopeCache) : nullptr;
AstScope* const scopep = vtx.scopep(scopeCache);
DfgVertexVar* const newp = m_dfg.makeNewVar(flp, name, vtx.dtype(), scopep);
++m_nTmps;
// Replace vertex with the variable, make it drive the variable
@ -248,13 +222,13 @@ class DfgRegularize final {
, m_ctx{ctx} {
uninlineVariables();
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "regularize-uninlined");
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed("regularize-uninlined");
eliminateVars();
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "regularize-eliminate");
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed("regularize-eliminate");
insertTemporaries();
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "regularize-inserttmp");
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed("regularize-inserttmp");
}
public:

View File

@ -21,12 +21,12 @@
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3Ast.h"
#include "V3Cfg.h"
#include "V3Const.h"
#include "V3Dfg.h"
#include "V3DfgPasses.h"
#include "V3EmitV.h"
#include "V3Os.h"
#include <algorithm>
#include <iterator>
@ -55,16 +55,12 @@ DfgArraySel* makeVertex<DfgArraySel, AstArraySel>(const AstArraySel* nodep, DfgG
} // namespace
// Visitor that can convert Ast statements and expressions in Dfg vertices
template <bool T_Scoped>
class AstToDfgConverter final : public VNVisitor {
// NODE STATE
// AstNodeExpr/AstVar/AstVarScope::user2p -> DfgVertex* for this Node
// AstVar::user3() -> int temporary counter for variable
const VNUser3InUse m_user3InUse;
// TYPES
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
// STATE
DfgGraph& m_dfg; // The graph being built
V3DfgSynthesisContext& m_ctx; // The context for stats
@ -73,7 +69,7 @@ class AstToDfgConverter final : public VNVisitor {
DfgLogic* m_logicp = nullptr;
// Variable updates produced by currently converted statement. This almost
// always have a single element, so a vector is ok
std::vector<std::pair<Variable*, DfgVertexVar*>>* m_updatesp = nullptr;
std::vector<std::pair<AstVarScope*, DfgVertexVar*>>* m_updatesp = nullptr;
bool m_foundUnhandled = false; // Found node not implemented as DFG or not implemented 'visit'
bool m_converting = false; // We are trying to convert some logic at the moment
@ -81,14 +77,6 @@ class AstToDfgConverter final : public VNVisitor {
size_t m_nUnpack = 0; // Sequence numbers for temporaries
// METHODS
static Variable* getTarget(const AstVarRef* refp) {
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
if VL_CONSTEXPR_CXX17 (T_Scoped) {
return reinterpret_cast<Variable*>(refp->varScopep());
} else {
return reinterpret_cast<Variable*>(refp->varp());
}
}
// Allocate a new non-variable vertex, add it to the currently synthesized logic
template <typename Vertex, typename... Args>
@ -117,7 +105,7 @@ class AstToDfgConverter final : public VNVisitor {
// Cannot represent cross module references
if (nodep->classOrPackagep()) return false;
// Check target
return V3Dfg::isSupported(getTarget(nodep));
return V3Dfg::isSupported(nodep->varScopep());
}
// Given an RValue expression, return the equivalent Vertex, or nullptr if not representable.
@ -154,16 +142,16 @@ class AstToDfgConverter final : public VNVisitor {
// Get (or create a new) temporary for this variable
const DfgVertexVar* const vtxp = [&]() -> DfgVertexVar* {
// The variable being assigned
Variable* const tgtp = getTarget(vrefp);
AstVarScope* const vscp = vrefp->varScopep();
// Find existing one, if any
for (const auto& pair : *m_updatesp) {
if (pair.first == tgtp) return pair.second;
if (pair.first == vscp) return pair.second;
}
// Create new one
DfgVertexVar* const newp = createTmp(*m_logicp, tgtp, "SynthAssign");
m_updatesp->emplace_back(tgtp, newp);
DfgVertexVar* const newp = createTmp(*m_logicp, vscp, "SynthAssign");
m_updatesp->emplace_back(vscp, newp);
// Create the Splice driver for the new temporary
if (newp->is<DfgVarPacked>()) {
@ -375,7 +363,7 @@ class AstToDfgConverter final : public VNVisitor {
}
// Variable should have been bound before starting conversion
DfgVertex* const vtxp = getTarget(nodep)->user2u().template to<DfgVertexVar*>();
DfgVertex* const vtxp = nodep->varScopep()->user2u().template to<DfgVertexVar*>();
UASSERT_OBJ(vtxp, nodep, "Referenced variable has no associated DfgVertexVar");
nodep->user2p(vtxp);
}
@ -450,27 +438,26 @@ public:
const std::string name = m_dfg.makeUniqueName(prefix, tmpCount);
DfgVertexVar* const vtxp = m_dfg.makeNewVar(flp, name, dtype, logic.scopep());
logic.synth().emplace_back(vtxp);
vtxp->varp()->isInternal(true);
vtxp->tmpForp(vtxp->nodep());
vtxp->vscp()->varp()->isInternal(true);
vtxp->tmpForp(vtxp->vscp());
return vtxp;
}
// Create a new temporary variable capable of holding 'varp'
DfgVertexVar* createTmp(DfgLogic& logic, Variable* varp, const std::string& prefix) {
AstVar* const astVarp = T_Scoped ? reinterpret_cast<AstVarScope*>(varp)->varp()
: reinterpret_cast<AstVar*>(varp);
DfgVertexVar* createTmp(DfgLogic& logic, AstVarScope* vscp, const std::string& prefix) {
AstVar* const astVarp = vscp->varp();
FileLine* const flp = astVarp->fileline();
const DfgDataType& dtype = *DfgDataType::fromAst(astVarp->dtypep());
const std::string prfx = prefix + "_" + astVarp->name();
const size_t tmpCount = astVarp->user3Inc();
DfgVertexVar* const vtxp = createTmp(logic, flp, dtype, prfx, tmpCount);
vtxp->tmpForp(varp);
vtxp->tmpForp(vscp);
return vtxp;
}
// Convert AstAssign to Dfg, return true if successful.
// Fills 'updates' with bindings for assigned variables.
bool convert(std::vector<std::pair<Variable*, DfgVertexVar*>>& updates, DfgLogic& vtx,
bool convert(std::vector<std::pair<AstVarScope*, DfgVertexVar*>>& updates, DfgLogic& vtx,
AstNodeAssign* nodep) {
UASSERT_OBJ(VN_IS(nodep, Assign) || VN_IS(nodep, AssignW), nodep, "Bad NodeAssign");
UASSERT_OBJ(updates.empty(), nodep, "'updates' should be empty");
@ -530,21 +517,19 @@ public:
// Debug aid - outisde 'AstToDfgSynthesize' as it is a template, but want one instance
V3DebugBisect s_dfgSynthDebugBisect{"DfgSynthesize"};
template <bool T_Scoped>
class AstToDfgSynthesize final {
// NODE STATE
// AstNodeExpr/AstVar/AstVarScope::user2p -> DfgVertex* for this Node
// TYPES
using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
// SymTab must be ordered in order to yield stable results
struct VariableComparator final {
bool operator()(const Variable* lhs, const Variable* rhs) const {
struct AstVarScopeComparator final {
bool operator()(const AstVarScope* lhs, const AstVarScope* rhs) const {
return lhs->name() < rhs->name();
}
};
using SymTab = std::map<Variable*, DfgVertexVar*, VariableComparator>;
using SymTab = std::map<AstVarScope*, DfgVertexVar*, AstVarScopeComparator>;
// Represents a [potentially partial] driver of a variable
struct Driver final {
@ -573,7 +558,7 @@ class AstToDfgSynthesize final {
// STATE - Persistent
DfgGraph& m_dfg; // The graph being built
V3DfgSynthesisContext& m_ctx; // The context for stats
AstToDfgConverter<T_Scoped> m_converter; // The convert instance to use for each construct
AstToDfgConverter m_converter; // The convert instance to use for each construct
size_t m_nBranchCond = 0; // Sequence numbers for temporaries
size_t m_nPathPred = 0; // Sequence numbers for temporaries
DfgWorklist m_toRevert{m_dfg}; // We need a worklist for reverting synthesis
@ -609,26 +594,16 @@ class AstToDfgSynthesize final {
}
if (VL_UNLIKELY(dumpDfgLevel() >= 9 || m_debugOSrcConep)) {
const auto label = m_ctx.prefix() + name;
m_dfg.dumpDotFilePrefixed(label);
m_dfg.dumpDotFilePrefixed(name);
if (m_debugOSrcConep) {
// Dump only the subgraph involving the inputs and outputs of the bad vertex
m_dfg.dumpDotFilePrefixed(label + "-min", [&](const DfgVertex& v) -> bool {
m_dfg.dumpDotFilePrefixed(name + "-min"s, [&](const DfgVertex& v) -> bool {
return m_debugOSrcConep->count(&v);
});
}
}
}
static AstVar* getAstVar(Variable* vp) {
// TODO: remove the useless reinterpret_casts when C++17 'if constexpr' actually works
if VL_CONSTEXPR_CXX17 (T_Scoped) {
return reinterpret_cast<AstVarScope*>(vp)->varp();
} else {
return reinterpret_cast<AstVar*>(vp);
}
}
// Allocate a new non-variable vertex, add it to the currently synthesized logic
template <typename Vertex, typename... Args>
Vertex* make(Args&&... args) {
@ -674,14 +649,7 @@ class AstToDfgSynthesize final {
if (std::find(visited.begin(), visited.end(), vtxp) != visited.end()) continue;
visited.emplace_back(vtxp);
if (const DfgVertexVar* const varp = vtxp->cast<DfgVertexVar>()) {
AstVar* const astVarp = [&]() -> AstVar* {
if VL_CONSTEXPR_CXX17 (T_Scoped) {
return reinterpret_cast<AstVarScope*>(varp->nodep())->varp();
} else {
return reinterpret_cast<AstVar*>(varp->nodep());
}
}();
if (astVarp->dfgTriLowered()) return true;
if (varp->vscp()->varp()->dfgTriLowered()) return true;
}
vtxp->foreachSource([&](const DfgVertex& src) {
stack.emplace_back(&src);
@ -849,15 +817,14 @@ class AstToDfgSynthesize final {
if (warned) continue;
// The variable to warn on
AstNode* const nodep = var.tmpForp() ? var.tmpForp() : var.nodep();
Variable* const varp = reinterpret_cast<Variable*>(nodep);
AstVarScope* const vscp = var.tmpForp() ? var.tmpForp() : var.vscp();
// Loop index often abused, so suppress
if (getAstVar(varp)->isUsedLoopIdx()) continue;
if (vscp->varp()->isUsedLoopIdx()) continue;
// Tristate lowering can intentionally create overlapping contributors.
// Keep the signal marked multidriven for DFG fallback, but suppress
// warning only when both overlapping driver cones look tri-lowered.
if (getAstVar(varp)->dfgAllowMultidriveTri()) {
if (vscp->varp()->dfgAllowMultidriveTri()) {
const bool iTri = containsTriLoweredVar(iD.m_vtxp);
const bool jTri = containsTriLoweredVar(jD.m_vtxp);
const bool triPair = iTri && jTri;
@ -872,9 +839,9 @@ class AstToDfgSynthesize final {
const std::string kind = isPacked ? "Bit" : "Element";
const std::string part = hi == lo ? (" [" + lo + "]") : ("s [" + hi + ":" + lo + "]");
varp->v3warn( //
vscp->v3warn( //
MULTIDRIVEN, //
kind << part << " of signal '" << varp->prettyName() << sub << "'"
kind << part << " of signal '" << vscp->prettyName() << sub << "'"
<< " have multiple combinational drivers."
<< " This can cause performance degradation.\n"
<< iD.m_flp->warnOther() << "... Location of offending driver\n"
@ -961,29 +928,28 @@ class AstToDfgSynthesize final {
void initializeEntrySymbolTable(SymTab& iSymTab) {
m_logicp->foreachSource([&](DfgVertex& src) {
DfgVertexVar* const vvp = src.as<DfgVertexVar>();
Variable* const varp = reinterpret_cast<Variable*>(vvp->nodep());
iSymTab[varp] = vvp;
iSymTab[vvp->vscp()] = vvp;
return false;
});
}
// Join variable drivers across a control flow confluence (insert muxes ...)
DfgVertexVar* joinDrivers(Variable* varp, DfgVertexVar* predicatep, //
DfgVertexVar* joinDrivers(AstVarScope* vscp, DfgVertexVar* predicatep, //
DfgVertexVar* thenp, DfgVertexVar* elsep) {
AstNode* const thenVarp = thenp->tmpForp() ? thenp->tmpForp() : thenp->nodep();
AstNode* const elseVarp = elsep->tmpForp() ? elsep->tmpForp() : elsep->nodep();
UASSERT_OBJ(thenVarp == elseVarp, varp, "Attempting to join unrelated variables");
AstVarScope* const thenVscp = thenp->tmpForp() ? thenp->tmpForp() : thenp->vscp();
AstVarScope* const elseVscp = elsep->tmpForp() ? elsep->tmpForp() : elsep->vscp();
UASSERT_OBJ(thenVscp == elseVscp, vscp, "Attempting to join unrelated variables");
// If both bindings are the the same (variable not updated through either path),
// then there is nothing to do, can use the same binding
if (thenp == elsep) return thenp;
// We can't join the input variable just yet, so bail
if (thenp->nodep() == varp) {
if (thenp->vscp() == vscp) {
++m_ctx.m_synt.nonSynJoinInput;
return nullptr;
}
if (elsep->nodep() == varp) {
if (elsep->vscp() == vscp) {
++m_ctx.m_synt.nonSynJoinInput;
return nullptr;
}
@ -1003,13 +969,13 @@ class AstToDfgSynthesize final {
std::vector<Driver> eDrivers = gatherDrivers(elsep->srcp()->as<DfgVertexSplice>());
// Default drivers should be the same or not present on either
UASSERT_OBJ(tDefaultp == eDefaultp, varp, "Different default drivers");
UASSERT_OBJ(tDefaultp == eDefaultp, vscp, "Different default drivers");
// Location to use for the join vertices
FileLine* const flp = predicatep->fileline();
// Create a fresh temporary for the joined value
DfgVertexVar* const joinp = m_converter.createTmp(*m_logicp, varp, "SynthJoin");
DfgVertexVar* const joinp = m_converter.createTmp(*m_logicp, vscp, "SynthJoin");
DfgVertexSplice* const joinSplicep = make<DfgSplicePacked>(flp, joinp->dtype());
joinp->srcp(joinSplicep);
@ -1020,7 +986,7 @@ class AstToDfgSynthesize final {
&& eDrivers.size() == 1 //
&& eDrivers[0].m_lo == 0 //
&& eDrivers[0].m_hi == elsep->width() - 1) {
UASSERT_OBJ(!tDefaultp, varp, "Fully driven variable have default driver");
UASSERT_OBJ(!tDefaultp, vscp, "Fully driven variable have default driver");
DfgCond* const condp = make<DfgCond>(flp, joinp->dtype());
condp->condp(predicatep);
@ -1081,11 +1047,11 @@ class AstToDfgSynthesize final {
// Any variable that does not have a binding on both paths will be removed. These might be
// temporaries, loop vars, etc used only in one branch. Conversion will fail if the
// variable is actually referenced later.
std::vector<Variable*> toRemove;
std::vector<AstVarScope*> toRemove;
// Join each symbol
for (std::pair<Variable* const, DfgVertexVar*>& pair : elseSymTab) {
Variable* const varp = pair.first;
for (std::pair<AstVarScope* const, DfgVertexVar*>& pair : elseSymTab) {
AstVarScope* const varp = pair.first;
// Find same variable on the else path
const auto it = thenSymTab.find(varp);
// Record for removal if not assigned on both paths
@ -1102,7 +1068,7 @@ class AstToDfgSynthesize final {
}
// Remove variables not assigned on both paths
for (Variable* const varp : toRemove) elseSymTab.erase(varp);
for (AstVarScope* const varp : toRemove) elseSymTab.erase(varp);
// Done
return true;
@ -1285,8 +1251,8 @@ class AstToDfgSynthesize final {
// Given the drivers of a variable after converting a single statement
// 'newp', add drivers from 'oldp' that were not reassigned be drivers
// in newp. This computes the total result of all previous assignments.
bool incorporatePreviousValue(Variable* varp, DfgVertexVar* newp, DfgVertexVar* oldp) {
UASSERT_OBJ(newp->srcp(), varp, "Assigned variable has no driver");
bool incorporatePreviousValue(AstVarScope* vscp, DfgVertexVar* newp, DfgVertexVar* oldp) {
UASSERT_OBJ(newp->srcp(), vscp, "Assigned variable has no driver");
// Easy if there is no old value...
if (!oldp) return true;
@ -1296,12 +1262,12 @@ class AstToDfgSynthesize final {
// If the old value is the real variable we just computed the new value for,
// then it is the circular feedback into the synthesized block, add it as default driver.
if (oldp->nodep() == varp) {
if (oldp->vscp() == vscp) {
if (!nSplicep->wholep()) newp->defaultp(oldp);
return true;
}
UASSERT_OBJ(oldp->srcp(), varp, "Previously assigned variable has no driver");
UASSERT_OBJ(oldp->srcp(), vscp, "Previously assigned variable has no driver");
// Can't do arrays yet
if (!newp->isPacked()) {
@ -1347,16 +1313,16 @@ class AstToDfgSynthesize final {
// Use fresh set of vertices in m_converter
const VNUser2InUse user2InUse;
// Initialize Variable -> Vertex bindings available in this block
// Initialize AstVarScope -> Vertex bindings available in this block
for (const auto& pair : iSymTab) {
Variable* const varp = pair.first;
AstVarScope* const varp = pair.first;
DfgVertexVar* const vtxp = pair.second;
varp->user2p(vtxp);
oSymTab[varp] = vtxp;
}
// Synthesize each statement one after the other
std::vector<std::pair<Variable*, DfgVertexVar*>> updates;
std::vector<std::pair<AstVarScope*, DfgVertexVar*>> updates;
for (AstNodeStmt* const stmtp : stmtps) {
// Regular statements
AstNodeAssign* const ap = VN_CAST(stmtp, NodeAssign);
@ -1369,7 +1335,7 @@ class AstToDfgSynthesize final {
// Apply variable updates from this statement
for (const auto& pair : updates) {
// The target variable that was assigned to
Variable* const varp = pair.first;
AstVarScope* const vscp = pair.first;
// The new, potentially partially assigned value
DfgVertexVar* const newp = pair.second;
// Normalize drivers within this statement, bail if multidriven
@ -1377,7 +1343,6 @@ class AstToDfgSynthesize final {
std::vector<Driver> drivers = gatherDrivers(srcp);
const bool single = drivers.size() == 1;
if (!normalizeDrivers(*newp, drivers)) {
getAstVar(varp)->setDfgMultidriven();
++m_ctx.m_synt.nonSynMultidrive;
return false;
}
@ -1387,13 +1352,13 @@ class AstToDfgSynthesize final {
for (const Driver& d : drivers) srcp->addDriver(d.m_vtxp, d.m_lo, d.m_flp);
}
// The old value, if any
DfgVertexVar* const oldp = varp->user2u().template to<DfgVertexVar*>();
DfgVertexVar* const oldp = vscp->user2u().template to<DfgVertexVar*>();
// Inncorporate old value into the new value
if (!incorporatePreviousValue(varp, newp, oldp)) return false;
if (!incorporatePreviousValue(vscp, newp, oldp)) return false;
// Update binding of target variable
varp->user2p(newp);
vscp->user2p(newp);
// Update output symbol table of this block
oSymTab[varp] = newp;
oSymTab[vscp] = newp;
}
updates.clear();
continue;
@ -1518,10 +1483,8 @@ class AstToDfgSynthesize final {
// braces and check here too. We can't touch any output variables if so.
const bool missing = m_logicp->foreachSink([&](const DfgVertex& sink) {
const DfgUnresolved* const unresolvedp = sink.as<DfgUnresolved>();
AstNode* const tgtp = unresolvedp->singleSink()->as<DfgVertexVar>()->nodep();
// cppcheck-suppress constVariablePointer
Variable* const varp = reinterpret_cast<Variable*>(tgtp);
return !oSymTab.count(varp);
AstVarScope* const vscp = unresolvedp->singleSink()->as<DfgVertexVar>()->vscp();
return !oSymTab.count(vscp);
});
if (missing) {
++m_ctx.m_synt.nonSynFalseWrite;
@ -1532,7 +1495,7 @@ class AstToDfgSynthesize final {
return !m_logicp->foreachSink([&](DfgVertex& sink) {
DfgUnresolved* const unresolvedp = sink.as<DfgUnresolved>();
const DfgVertexVar* const varp = unresolvedp->singleSink()->as<DfgVertexVar>();
DfgVertexVar* const resp = oSymTab.at(reinterpret_cast<Variable*>(varp->nodep()));
DfgVertexVar* const resp = oSymTab.at(varp->vscp());
UASSERT_OBJ(resp->srcp(), resp, "Undriven result");
// If the output is not used further in the synthesized logic itself,
@ -1750,10 +1713,10 @@ class AstToDfgSynthesize final {
// There should be no sinks left for unselected DfgLogic, delete them here
UASSERT_OBJ(!logicp->hasSinks(), vtxp, "Unselected 'DfgLogic' with sinks remaining");
// Input variables will be read in Ast code, add Ast reference vertices
// AstVar/AstVarScope::user4p() -> corresponding DfgVertexVar* in the graph
// AstVarScope::user4p() -> corresponding DfgVertexVar* in the graph
const VNUser4InUse m_user4InUse;
logicp->foreachSource([](DfgVertex& src) {
src.as<DfgVertexVar>()->nodep()->user4p(&src);
src.as<DfgVertexVar>()->vscp()->user4p(&src);
return false;
});
V3DfgPasses::addAstRefs(m_dfg, logicp->nodep(), [](AstNode* varp) { //
@ -1831,8 +1794,6 @@ class AstToDfgSynthesize final {
const bool newEntry = resolvedDrivers.emplace(&var, resolvedp).second;
UASSERT_OBJ(newEntry, &var, "Dupliacte driver");
}
// Mark as multidriven for future DFG runs - here, so we get all warnings above
for (const DfgVertexVar* const vtxp : multidrivenps) vtxp->varp()->setDfgMultidriven();
// Revert and remove drivers of multi-driven variables
revert(m_ctx.m_synt.revertMultidrive);
// Replace all DfgUnresolved with the resolved drivers
@ -1866,10 +1827,10 @@ class AstToDfgSynthesize final {
} else {
// Not synthesized. Logic stays in Ast. Add Ast reference vertices.
// Outputs already marked by revertTransivelyAndRemove.
// AstVar/AstVarScope::user4p() -> corresponding DfgVertexVar* in the graph
// AstVarScope::user4p() -> corresponding DfgVertexVar* in the graph
const VNUser4InUse m_user4InUse;
logicp->foreachSource([](DfgVertex& src) {
src.as<DfgVertexVar>()->nodep()->user4p(&src);
src.as<DfgVertexVar>()->vscp()->user4p(&src);
return false;
});
V3DfgPasses::addAstRefs(m_dfg, logicp->nodep(), [](AstNode* varp) { //
@ -1926,7 +1887,7 @@ public:
// Final step outside, as both AstToDfgSynthesize and removeUnused used DfgUserMap
UINFO(5, "Step 6: Remove all unused vertices");
V3DfgPasses::removeUnused(dfg);
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed(ctx.prefix() + "synth-rmunused");
if (dumpDfgLevel() >= 9) dfg.dumpDotFilePrefixed("synth-rmunused");
// No operation vertex should have multiple sinks. Cyclic decomoposition
// depends on this and it can easily be ensured by using temporaries.
@ -2021,9 +1982,5 @@ void V3DfgPasses::synthesize(DfgGraph& dfg, V3DfgContext& ctx) {
// Select which DfgLogic to attempt to synthesize
dfgSelectLogicForSynthesis(dfg);
// Synthesize them - also removes un-synthesized DfgLogic, so must run even if nothing selected
if (dfg.modulep()) {
AstToDfgSynthesize</* T_Scoped: */ false>::apply(dfg, ctx.m_synthContext);
} else {
AstToDfgSynthesize</* T_Scoped: */ true>::apply(dfg, ctx.m_synthContext);
}
AstToDfgSynthesize::apply(dfg, ctx.m_synthContext);
}

View File

@ -48,47 +48,32 @@
class DfgVertexVar VL_NOT_FINAL : public DfgVertex {
// Represents a variable. It has 2 optional inputs, 'srcp' and 'defaultp'.
AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex)
AstVarScope* const m_varScopep; // The AstVarScope associated with this vertex (not owned)
AstVarScope* const m_vscp; // The AstVarScope associated with this vertex (not owned)
// Location of driver of this variable. Only used for converting back to Ast. Might be nullptr.
FileLine* m_driverFileLine = nullptr;
// If this DfgVertexVar is a synthesized temporary, this is the original Var/VarScope it stands
// for. It might point to m_varp/m_varScopep itself to indicate it's a temporary without an
// associated input Var/VarScope.
AstNode* m_tmpForp = nullptr;
// If this DfgVertexVar is a synthesized temporary, this is the original AstVarScope it stands
// for. It might point to m_vscp itself to indicate it's a temporary without an associated
// input AstVarScope.
AstVarScope* m_tmpForp = nullptr;
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, AstVarScope* vscp)
: DfgVertex{dfg, type, varp->fileline(), *DfgDataType::fromAst(varp->dtypep())}
, m_varp{varp}
, m_varScopep{vscp} {
#ifdef VL_DEBUG
if (v3Global.rootp()->topScopep()) {
UASSERT_OBJ(vscp, varp, "Un-scoped DfgVertexVar created in scoped DfgGraph");
} else {
UASSERT_OBJ(!vscp, varp, "Scoped DfgVertexVar created in un-scoped DfgGraph");
}
#endif
protected:
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp)
: DfgVertex{dfg, type, vscp->varp()->fileline(),
*DfgDataType::fromAst(vscp->varp()->dtypep())}
, m_vscp{vscp} {
// Increment reference count
AstNode* const variablep = nodep();
variablep->user1(variablep->user1() + 0x20);
UASSERT_OBJ((variablep->user1() >> 5) > 0, variablep, "Reference count overflow");
m_vscp->user1(m_vscp->user1() + 0x20);
UASSERT_OBJ((m_vscp->user1() >> 5) > 0, m_vscp, "Reference count overflow");
// Allocate sources
newInput();
newInput();
}
protected:
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp)
: DfgVertexVar{dfg, type, varp, nullptr} {}
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp)
: DfgVertexVar{dfg, type, vscp->varp(), vscp} {}
public:
~DfgVertexVar() {
// Decrement reference count
AstNode* const variablep = nodep();
variablep->user1(variablep->user1() - 0x20);
UASSERT_OBJ((variablep->user1() >> 5) >= 0, variablep, "Reference count underflow");
m_vscp->user1(m_vscp->user1() - 0x20);
UASSERT_OBJ((m_vscp->user1() >> 5) >= 0, m_vscp, "Reference count underflow");
}
ASTGEN_MEMBERS_DfgVertexVar;
@ -102,42 +87,39 @@ public:
std::string srcName(size_t idx) const override final { return idx ? "defaultp" : "srcp"; }
// The Ast variable this vertex representess
AstVar* varp() const { return m_varp; }
AstVarScope* varScopep() const { return m_varScopep; }
AstNode* nodep() const {
return m_varScopep ? static_cast<AstNode*>(m_varScopep) : static_cast<AstNode*>(m_varp);
}
// AstVar* varp() const { return m_varp; }
AstVarScope* vscp() const { return m_vscp; }
// If this is a temporary, the Ast variable it stands for, or same as
// 'nodep()' if it's a temporary with no associated original Ast variable.
AstNode* tmpForp() const { return m_tmpForp; }
void tmpForp(AstNode* nodep) { m_tmpForp = nodep; }
AstVarScope* tmpForp() const { return m_tmpForp; }
void tmpForp(AstVarScope* nodep) { m_tmpForp = nodep; }
// Location of driver of variable (only used if 'srcp' is not a splice)
FileLine* driverFileLine() const { return m_driverFileLine; }
void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }
// Variable referenced from other DFG in the same module/netlist
bool hasDfgRefs() const { return nodep()->user1() >> 6; } // I.e.: (nodep()->user1() >> 5) > 1
bool hasDfgRefs() const { return m_vscp->user1() >> 6; } // I.e.: (nodep()->user1() >> 5) > 1
// Variable referenced from Ast code in the same module/netlist
static bool hasModWrRefs(const AstNode* nodep) { return nodep->user1() & 0x08; }
static void setHasModWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x08); }
bool hasModWrRefs() const { return hasModWrRefs(nodep()); }
void setHasModWrRefs() const { setHasModWrRefs(nodep()); }
static bool hasModWrRefs(const AstVarScope* nodep) { return nodep->user1() & 0x08; }
static void setHasModWrRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x08); }
bool hasModWrRefs() const { return hasModWrRefs(m_vscp); }
void setHasModWrRefs() const { setHasModWrRefs(m_vscp); }
// Variable referenced outside the containing module/netlist.
static bool hasExtRdRefs(const AstNode* nodep) { return nodep->user1() & 0x01; }
static bool hasExtWrRefs(const AstNode* nodep) { return nodep->user1() & 0x02; }
static void setHasExtRdRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x01); }
static void setHasExtWrRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x02); }
bool hasExtRdRefs() const { return hasExtRdRefs(nodep()); }
bool hasExtWrRefs() const { return hasExtWrRefs(nodep()); }
static bool hasExtRdRefs(const AstVarScope* nodep) { return nodep->user1() & 0x01; }
static bool hasExtWrRefs(const AstVarScope* nodep) { return nodep->user1() & 0x02; }
static void setHasExtRdRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x01); }
static void setHasExtWrRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x02); }
bool hasExtRdRefs() const { return hasExtRdRefs(m_vscp); }
bool hasExtWrRefs() const { return hasExtWrRefs(m_vscp); }
bool hasExtRefs() const { return hasExtRdRefs() || hasExtWrRefs(); }
// Variable referenced via READWRITE references
static bool hasRWRefs(const AstNode* nodep) { return nodep->user1() & 0x10; }
static void setHasRWRefs(AstNode* nodep) { nodep->user1(nodep->user1() | 0x10); }
static bool hasRWRefs(const AstVarScope* nodep) { return nodep->user1() & 0x10; }
static void setHasRWRefs(AstVarScope* nodep) { nodep->user1(nodep->user1() | 0x10); }
// True iff the value of this variable is read outside this DfgGraph
bool isObserved() const {
@ -150,10 +132,10 @@ public:
// The value of this vertex might differ from what is defined by its drivers
// 'srcp' and 'defaultp'. That is, it might be assigned, possibly partially,
// or abruptly outside the graph, hence it is not equivalent to its 'srcp'.
static bool isVolatile(const AstNode* nodep) {
static bool isVolatile(const AstVarScope* nodep) {
return hasModWrRefs(nodep) || hasExtWrRefs(nodep);
}
bool isVolatile() const { return isVolatile(nodep()); }
bool isVolatile() const { return isVolatile(m_vscp); }
};
class DfgVarArray final : public DfgVertexVar {
@ -161,10 +143,6 @@ class DfgVarArray final : public DfgVertexVar {
friend class DfgVisitor;
public:
DfgVarArray(DfgGraph& dfg, AstVar* varp)
: DfgVertexVar{dfg, dfgType(), varp} {
UASSERT_OBJ(isArray(), varp, "Non-array DfgVarArray");
}
DfgVarArray(DfgGraph& dfg, AstVarScope* vscp)
: DfgVertexVar{dfg, dfgType(), vscp} {
UASSERT_OBJ(isArray(), vscp, "Non-array DfgVarArray");
@ -177,10 +155,6 @@ class DfgVarPacked final : public DfgVertexVar {
friend class DfgVisitor;
public:
DfgVarPacked(DfgGraph& dfg, AstVar* varp)
: DfgVertexVar{dfg, dfgType(), varp} {
UASSERT_OBJ(isPacked(), varp, "Non-packed DfgVarPacked");
}
DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp)
: DfgVertexVar{dfg, dfgType(), vscp} {
UASSERT_OBJ(isPacked(), vscp, "Non-packed DfgVarPacked");

View File

@ -1455,11 +1455,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-fdead-assigns", FOnOff, &m_fDeadAssigns);
DECL_OPTION("-fdead-cells", FOnOff, &m_fDeadCells);
DECL_OPTION("-fdedup", FOnOff, &m_fDedupe);
DECL_OPTION("-fdfg", CbFOnOff, [this](bool flag) {
m_fDfgPreInline = flag;
m_fDfgPostInline = flag;
m_fDfgScoped = flag;
});
DECL_OPTION("-fdfg", CbFOnOff, [this](bool flag) { m_fDfg = flag; });
DECL_OPTION("-fdfg-break-cycles", FOnOff, &m_fDfgBreakCycles);
DECL_OPTION("-fdfg-peephole", FOnOff, &m_fDfgPeephole);
DECL_OPTION("-fdfg-peephole-", CbPartialMatch, [this](const char* optp) { //
@ -1468,11 +1464,18 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-fno-dfg-peephole-", CbPartialMatch, [this](const char* optp) { //
m_fDfgPeepholeDisabled.emplace(optp);
});
DECL_OPTION("-fdfg-pre-inline", FOnOff, &m_fDfgPreInline);
DECL_OPTION("-fdfg-post-inline", FOnOff, &m_fDfgPostInline);
DECL_OPTION("-fdfg-pre-inline", CbFOnOff, [fl](bool) {
fl->v3warn(DEPRECATED, "Option '-fno-dfg-pre-inline' is deprecated and has no effect");
});
DECL_OPTION("-fdfg-post-inline", CbFOnOff, [fl](bool) {
fl->v3warn(DEPRECATED, "Option '-fno-dfg-post-inline' is deprecated and has no effect");
});
DECL_OPTION("-fdfg-push-down-sels", FOnOff, &m_fDfgPushDownSels);
DECL_OPTION("-fdfg-scoped", FOnOff, &m_fDfgScoped);
DECL_OPTION("-fdfg-synthesize-all", FOnOff, &m_fDfgSynthesizeAll);
DECL_OPTION("-fdfg-scoped", CbFOnOff, [this, fl](bool flag) {
fl->v3warn(DEPRECATED, "Option '-fno-dfg-scoped' is deprecated, use '-fno-dfg' instead.");
m_fDfg = flag;
});
DECL_OPTION("-fexpand", FOnOff, &m_fExpand);
DECL_OPTION("-ffunc-opt", CbFOnOff, [this](bool flag) { //
m_fFuncSplitCat = flag;
@ -2351,9 +2354,7 @@ void V3Options::optimize(int level) {
m_fConst = flag;
m_fConstBitOpTree = flag;
m_fDedupe = flag;
m_fDfgPreInline = flag;
m_fDfgPostInline = flag;
m_fDfgScoped = flag;
m_fDfg = flag;
m_fDeadAssigns = flag;
m_fDeadCells = flag;
m_fExpand = flag;

View File

@ -399,10 +399,8 @@ private:
bool m_fDedupe; // main switch: -fno-dedupe: logic deduplication
bool m_fDfgBreakCycles = true; // main switch: -fno-dfg-break-cycles
bool m_fDfgPeephole = true; // main switch: -fno-dfg-peephole
bool m_fDfgPreInline; // main switch: -fno-dfg-pre-inline and -fno-dfg
bool m_fDfgPostInline; // main switch: -fno-dfg-post-inline and -fno-dfg
bool m_fDfgPushDownSels = true; // main switch: -fno-dfg-push-down-sels
bool m_fDfgScoped; // main switch: -fno-dfg-scoped and -fno-dfg
bool m_fDfg; // main switch: -fno-dfg
bool m_fDfgSynthesizeAll = false; // main switch: -fdfg-synthesize-all
bool m_fDeadAssigns; // main switch: -fno-dead-assigns: remove dead assigns
bool m_fDeadCells; // main switch: -fno-dead-cells: remove dead cells
@ -718,12 +716,10 @@ public:
bool fConstBitOpTree() const { return m_fConstBitOpTree; }
bool fConstEager() const { return m_fConstEager; }
bool fDedupe() const { return m_fDedupe; }
bool fDfg() const { return m_fDfg; }
bool fDfgBreakCycles() const { return m_fDfgBreakCycles; }
bool fDfgPeephole() const { return m_fDfgPeephole; }
bool fDfgPreInline() const { return m_fDfgPreInline; }
bool fDfgPostInline() const { return m_fDfgPostInline; }
bool fDfgPushDownSels() const { return m_fDfgPushDownSels; }
bool fDfgScoped() const { return m_fDfgScoped; }
bool fDfgSynthesizeAll() const { return m_fDfgSynthesizeAll; }
bool fDfgPeepholeEnabled(const std::string& name) const {
return !m_fDfgPeepholeDisabled.count(name);

View File

@ -307,15 +307,8 @@ static void process() {
v3Global.constRemoveXs(true);
}
if (v3Global.opt.fDfgPreInline() || v3Global.opt.fDfgPostInline()) {
// If doing DFG optimization, extract some additional candidates
V3DfgOptimizer::extract(v3Global.rootp());
}
if (v3Global.opt.fDfgPreInline()) {
// Pre inline DFG optimization
V3DfgOptimizer::optimize(v3Global.rootp(), "pre inline");
}
// If doing DFG optimization, extract some additional candidates
if (v3Global.opt.fDfg()) V3DfgOptimizer::extract(v3Global.rootp());
if (!(v3Global.opt.serializeOnly() && !v3Global.opt.flatten())) {
// Module inlining
@ -329,15 +322,10 @@ static void process() {
if (v3Global.opt.trace()) V3Interface::interfaceAll(v3Global.rootp());
if (v3Global.opt.fDfgPostInline()) {
// Post inline DFG optimization
V3DfgOptimizer::optimize(v3Global.rootp(), "post inline");
}
// --PRE-FLAT OPTIMIZATIONS------------------
// Initial const/dead to reduce work for ordering code
V3Const::constifyAll(v3Global.rootp());
if (v3Global.opt.fConstBeforeDfg()) V3Const::constifyAll(v3Global.rootp());
v3Global.checkTree();
V3Dead::deadifyDTypes(v3Global.rootp());
@ -355,7 +343,7 @@ static void process() {
V3Inst::instAll(v3Global.rootp());
// Inst may have made lots of concats; fix them
V3Const::constifyAll(v3Global.rootp());
if (v3Global.opt.fConstBeforeDfg()) V3Const::constifyAll(v3Global.rootp());
// Flatten hierarchy, creating a SCOPE for each module's usage as a cell
// No more AstAlias after linkDotScope
@ -370,7 +358,7 @@ static void process() {
if (!(v3Global.opt.serializeOnly() && !v3Global.opt.flatten())) {
// Cleanup
V3Const::constifyAll(v3Global.rootp());
if (v3Global.opt.fConstBeforeDfg()) V3Const::constifyAll(v3Global.rootp());
V3Dead::deadifyDTypesScoped(v3Global.rootp());
v3Global.checkTree();
}
@ -398,7 +386,7 @@ static void process() {
V3Slice::sliceAll(v3Global.rootp());
// Push constants across variables and remove redundant assignments
V3Const::constifyAll(v3Global.rootp());
if (v3Global.opt.fConstBeforeDfg()) V3Const::constifyAll(v3Global.rootp());
if (v3Global.opt.fLife()) V3Life::lifeAll(v3Global.rootp());
@ -409,7 +397,7 @@ static void process() {
}
// Cleanup
V3Const::constifyAll(v3Global.rootp());
if (v3Global.opt.fConstBeforeDfg()) V3Const::constifyAll(v3Global.rootp());
V3Dead::deadifyDTypesScoped(v3Global.rootp());
v3Global.checkTree();
@ -429,10 +417,8 @@ static void process() {
// forcing.
V3Force::forceAll(v3Global.rootp());
if (v3Global.opt.fDfgScoped()) {
// Scoped DFG optimization
V3DfgOptimizer::optimize(v3Global.rootp(), "scoped");
}
// DFG optimization
if (v3Global.opt.fDfg()) V3DfgOptimizer::optimize(v3Global.rootp());
// Gate-based logic elimination; eliminate signals and push constant across cell
// boundaries Instant propagation makes lots-o-constant reduction possibilities.

View File

@ -465,9 +465,9 @@
+000010 point: type=line comment=block hier=top.t.cond1
endfunction
~000011 assign a = (cyc == 0) ? clk : 1'bz;
~000031 assign a = (cyc == 0) ? clk : 1'bz;
-000000 point: type=branch comment=cond_then hier=top.t.cond1
+000011 point: type=branch comment=cond_else hier=top.t.cond1
+000031 point: type=branch comment=cond_else hier=top.t.cond1
~000028 assign b = (cyc == 1) ? clk : 0;
-000003 point: type=branch comment=cond_then hier=top.t.cond1
+000028 point: type=branch comment=cond_else hier=top.t.cond1

View File

@ -156,9 +156,9 @@ DA:325,1
DA:328,10
DA:329,10
DA:330,10
DA:333,11
DA:333,31
BRDA:333,0,0,0
BRDA:333,0,1,11
BRDA:333,0,1,31
DA:334,28
BRDA:334,0,0,3
BRDA:334,0,1,28

View File

@ -11,11 +11,10 @@ import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--stats"])
test.compile(verilator_flags2=["--stats", "-fno-table"])
test.execute()
test.file_grep(test.stats, r'Optimizations, DFG pre inline BinToOneHot, decoders created\s+(\d+)',
4)
test.file_grep(test.stats, r'Optimizations, DFG, BinToOneHot, decoders created\s+(\d+)', 4)
test.passes()

View File

@ -80,8 +80,6 @@ test.file_grep(test.obj_dir + "/obj_ref/Vref__stats.txt",
test.compile(verilator_flags2=[
"--stats",
"--build",
"-fno-dfg-post-inline",
"-fno-dfg-scoped",
"--exe",
"+incdir+" + test.obj_dir,
"-Mdir", test.obj_dir + "/obj_opt",

View File

@ -1,5 +1,4 @@
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:45:16: Bit [1] of signal 'y' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:45:16: Bit [1] of signal 't.y' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:48:22: ... Location of offending driver
48 | {y[1:0], y[2:1]} = i[3:0] + 4'd5;
| ^
@ -8,88 +7,77 @@
| ^
... For warning description see https://verilator.org/warn/MULTIDRIVEN?v=latest
... Use "/* verilator lint_off MULTIDRIVEN */" and lint_on around source to disable this message.
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:16: Bits [3:1] of signal 'a' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:16: Bits [3:1] of signal 't.a' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:17:17: ... Location of offending driver
17 | assign a[3:0] = i[3:0];
| ^
t/t_dfg_multidriver_dfg_bad.v:18:17: ... Location of offending driver
18 | assign a[4:1] = ~i[4:1];
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:16: Bit [3] of signal 'a' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:16: Bit [3] of signal 't.a' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:17:17: ... Location of offending driver
17 | assign a[3:0] = i[3:0];
| ^
t/t_dfg_multidriver_dfg_bad.v:19:15: ... Location of offending driver
19 | assign a[3] = ~i[3];
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:16: Bits [7:6] of signal 'a' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:16: Bits [7:6] of signal 't.a' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:20:17: ... Location of offending driver
20 | assign a[8:5] = i[8:5];
| ^
t/t_dfg_multidriver_dfg_bad.v:21:17: ... Location of offending driver
21 | assign a[7:6] = ~i[7:6];
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:16: Bit [9] of signal 'a' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:16:16: Bit [9] of signal 't.a' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:22:15: ... Location of offending driver
22 | assign a[9] = i[9];
| ^
t/t_dfg_multidriver_dfg_bad.v:23:15: ... Location of offending driver
23 | assign a[9] = ~i[9];
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:26:16: Elements [3:0] of signal 'u' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:26:16: Elements [3:0] of signal 't.u' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:27:12: ... Location of offending driver
27 | assign u = j;
| ^
t/t_dfg_multidriver_dfg_bad.v:28:12: ... Location of offending driver
28 | assign u = k;
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:30:16: Element [1] of signal 'v' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:30:16: Element [1] of signal 't.v' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:31:12: ... Location of offending driver
31 | assign v = j;
| ^
t/t_dfg_multidriver_dfg_bad.v:32:11: ... Location of offending driver
32 | assign v[1] = i;
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:34:16: Element [0] of signal 'w' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:34:16: Element [0] of signal 't.w' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:35:11: ... Location of offending driver
35 | assign w[0] = i;
| ^
t/t_dfg_multidriver_dfg_bad.v:36:12: ... Location of offending driver
36 | assign w = j;
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:38:16: Bits [3:2] of signal 'x[3]' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:38:16: Bits [3:2] of signal 't.x[3]' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:39:15: ... Location of offending driver
39 | assign x[3] = i;
| ^
t/t_dfg_multidriver_dfg_bad.v:40:20: ... Location of offending driver
40 | assign x[3][3:2] = ~i[1:0];
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:51:16: Bits [2:1] of signal 'z' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:51:16: Bits [2:1] of signal 't.z' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:53:12: ... Location of offending driver
53 | z[2:0] = i[2:0];
| ^
t/t_dfg_multidriver_dfg_bad.v:58:15: ... Location of offending driver
58 | z[3:1] = i[3:1];
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:51:16: Bits [6:5] of signal 'z' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:51:16: Bits [6:5] of signal 't.z' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:57:12: ... Location of offending driver
57 | z[6:4] = i[6:4];
| ^
t/t_dfg_multidriver_dfg_bad.v:54:12: ... Location of offending driver
54 | z[7:5] = i[7:5];
| ^
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:51:16: Bit [7] of signal 'z' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_dfg_multidriver_dfg_bad.v:51:16: Bit [7] of signal 't.z' have multiple combinational drivers. This can cause performance degradation.
t/t_dfg_multidriver_dfg_bad.v:54:12: ... Location of offending driver
54 | z[7:5] = i[7:5];
| ^
@ -117,4 +105,7 @@
t/t_dfg_multidriver_dfg_bad.v:66:24: ... Location of offending driver
66 | assign sub_2.a[10:5] = i[10:5];
| ^
%Error: Exiting due to
%Error: Internal Error: t/t_dfg_multidriver_dfg_bad.v:48:12: ../V3Gate.cpp:#: Concat on LHS of assignment; V3Const should have deleted it
48 | {y[1:0], y[2:1]} = i[3:0] + 4'd5;
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.

View File

@ -13,8 +13,7 @@ test.scenarios('vlt')
test.compile(verilator_flags2=["--stats"])
test.file_grep(
test.stats,
r'Optimizations, DFG scoped Synthesis, conv / non-representable \(oobsel\)\s+(\d+)', 1)
test.file_grep(test.stats,
r'Optimizations, DFG, Synthesis, conv / non-representable \(oobsel\)\s+(\d+)', 1)
test.passes()

View File

@ -106,7 +106,7 @@ test.compile(verilator_flags2=[
def check(name, enabled):
name = name.lower()
name = re.sub(r'_', ' ', name)
pattern = r'DFG\s+(pre inline|post inline|scoped) Peephole, ' + name + r'\s+([1-9]\d*)\s*$'
pattern = r'DFG, Peephole, ' + name + r'\s+([1-9]\d*)\s*$'
if enabled:
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt", pattern)
else:

View File

@ -11,16 +11,12 @@ import vltest_bootstrap
test.scenarios('simulator_st')
test.compile(verilator_flags2=[
"--binary", "--stats", "-fno-dfg-pre-inline", "-fno-dfg-post-inline", "-fno-dfg-peephole"
])
test.compile(verilator_flags2=["--binary", "--stats", "-fno-dfg-peephole"])
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, DFG scoped PushDownSels, sels pushed down\s+(\d+)',
49)
test.file_grep(test.stats, r'Optimizations, DFG scoped PushDownSels, would be cyclic\s+(\d+)',
1)
test.file_grep(test.stats, r'Optimizations, DFG, PushDownSels, sels pushed down\s+(\d+)', 49)
test.file_grep(test.stats, r'Optimizations, DFG, PushDownSels, would be cyclic\s+(\d+)', 1)
test.passes()

View File

@ -13,17 +13,13 @@ test.scenarios('simulator_st')
test.top_filename = "t/t_dfg_push_sel.v"
test.compile(verilator_flags2=[
"--binary", "--stats", "-fno-dfg-pre-inline", "-fno-dfg-post-inline", "-fno-dfg-peephole",
"-fno-dfg-push-down-sels"
])
test.compile(
verilator_flags2=["--binary", "--stats", "-fno-dfg-peephole", "-fno-dfg-push-down-sels"])
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, DFG scoped PushDownSels, sels pushed down\s+(\d+)',
0)
test.file_grep(test.stats, r'Optimizations, DFG scoped PushDownSels, would be cyclic\s+(\d+)',
0)
test.file_grep(test.stats, r'Optimizations, DFG, PushDownSels, sels pushed down\s+(\d+)', 0)
test.file_grep(test.stats, r'Optimizations, DFG, PushDownSels, would be cyclic\s+(\d+)', 0)
test.passes()

View File

@ -1,4 +1,4 @@
DFG 'scoped' patterns with depth 1
DFG patterns with depth 1
9 (CONCAT _A:1 _B:a):b
8 (REDXOR _A:a):1
3 (NOT vA:a)*:a
@ -16,7 +16,7 @@ DFG 'scoped' patterns with depth 1
1 (SEL@0 _A:a):b
1 (SEL@A _A:a):1
DFG 'scoped' patterns with depth 2
DFG patterns with depth 2
6 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:b):c):d
2 (AND (NOT vA:a)*:a (NOT vB:a)*:a):a
2 (REDXOR (AND _A:a _B:a):a):1
@ -42,7 +42,7 @@ DFG 'scoped' patterns with depth 2
1 (SEL@0 (REPLICATE _A:a cA:a)*:b):c
1 (SEL@A (AND _A:a _B:a)*:a):1
DFG 'scoped' patterns with depth 3
DFG patterns with depth 3
2 (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a):a):1
1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e
1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e
@ -71,7 +71,7 @@ DFG 'scoped' patterns with depth 3
1 (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b):c
1 (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
DFG 'scoped' patterns with depth 4
DFG patterns with depth 4
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e):f
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a):a):1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (SEL@0 _C:a):1 (CONCAT _D:1 _E:b):c):d):e):f
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a):a):1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b):f

View File

@ -12,9 +12,9 @@ import vltest_bootstrap
test.scenarios('vlt')
test.top_filename = "t/t_dfg_stats_patterns.v"
test.compile(verilator_flags2=["--stats --no-skip-identical -fno-dfg-pre-inline -fno-dfg-scoped"])
test.compile(verilator_flags2=["--stats --no-skip-identical"])
fn = test.glob_one(test.obj_dir + "/" + test.vm_prefix + "__stats_dfg_patterns*")
test.files_identical(fn, test.golden_filename)
test.files_identical(test.obj_dir + "/" + test.vm_prefix + "__stats_dfg_patterns.txt",
test.golden_filename)
test.passes()

View File

@ -1,99 +0,0 @@
DFG 'post inline' patterns with depth 1
9 (CONCAT _A:1 _B:a):b
8 (REDXOR _A:a):1
4 (ASTRD vA:a):a
3 (NOT vA:a)*:a
2 (AND _A:a _B:a):a
1 (AND _A:a _B:a)*:a
1 (CONCAT _A:1 _B:1):a
1 (NOT _A:a):a
1 (REDXOR _A:a)*:1
1 (REPLICATE _A:1 cA:a)*:b
1 (REPLICATE _A:a cA:a)*:b
1 (REPLICATE _A:a cA:a):b
1 (REPLICATE _A:a cA:b)*:b
1 (REPLICATE _A:a cA:b)*:c
1 (SEL@0 _A:a):1
1 (SEL@0 _A:a):b
1 (SEL@A _A:a):1
DFG 'post inline' patterns with depth 2
6 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:b):c):d
2 (AND (NOT vA:a)*:a (NOT vB:a)*:a):a
2 (REDXOR (AND _A:a _B:a):a):1
1 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a
1 (CONCAT (REDXOR _A:a)*:1 (CONCAT _B:1 _C:b):c):d
1 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:1):b):c
1 (CONCAT (REDXOR _A:a):1 (REDXOR _B:b)*:1):c
1 (CONCAT (SEL@0 _A:a):1 (CONCAT _B:1 _C:b):c):d
1 (NOT (REPLICATE _A:a cA:b)*:b):b
1 (REDXOR (AND _A:a _B:a)*:a):1
1 (REDXOR (REPLICATE _A:1 cA:a)*:b):1
1 (REDXOR (REPLICATE _A:a cA:a)*:b):1
1 (REDXOR (REPLICATE _A:a cA:a):b)*:1
1 (REDXOR (REPLICATE _A:a cA:b)*:b):1
1 (REDXOR (REPLICATE _A:a cA:b)*:c):1
1 (REDXOR (SEL@0 _A:a):b):1
1 (REPLICATE (NOT _A:a):a cA:a)*:b
1 (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c
1 (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b):c
1 (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b
1 (REPLICATE (SEL@A _A:a):1 cA:b)*:c
1 (SEL@0 (AND _A:a _B:a)*:a):1
1 (SEL@0 (REPLICATE _A:a cA:a)*:b):c
1 (SEL@A (AND _A:a _B:a)*:a):1
DFG 'post inline' patterns with depth 3
2 (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a):a):1
1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e
1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e
1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (SEL@0 _C:a):1 (CONCAT _D:1 _E:b):c):d):e
1 (CONCAT (REDXOR (REPLICATE _A:1 cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (CONCAT _C:1 _D:d):e):f):g
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (REDXOR _C:b)*:1):d):e
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a):b)*:1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:1):c):d):e
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:b):1 (CONCAT (REDXOR _B:c)*:1 (CONCAT _C:1 _D:d):e):f):g
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:c):1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:d):e):f):g
1 (CONCAT (REDXOR (SEL@0 _A:a):b):1 (REDXOR (REPLICATE _B:c cA:c):a)*:1):d
1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b
1 (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b
1 (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
1 (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1
1 (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b):c)*:1
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1
1 (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1
1 (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b):c):1
1 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c
1 (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a
1 (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b):d
1 (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d
1 (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c
1 (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
1 (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b):c
1 (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
DFG 'post inline' patterns with depth 4
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e):f
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a):a):1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (SEL@0 _C:a):1 (CONCAT _D:1 _E:b):c):d):e):f
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a):a):1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b):f
1 (CONCAT (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1 (CONCAT (REDXOR (SEL@0 _B:b):c):1 (REDXOR (REPLICATE _A:a cA:a):b)*:1):d):e
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:a)*:a):1 (CONCAT (REDXOR _C:d)*:1 (CONCAT _D:1 _E:e):f):g):h):i
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b):c)*:1 (CONCAT (REDXOR (REPLICATE _B:b cA:b)*:c):1 (CONCAT (REDXOR _C:d):1 (REDXOR _D:c)*:1):e):f):g
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1 (CONCAT (REDXOR (REPLICATE _B:b cA:b):d)*:1 (CONCAT (REDXOR _C:d):1 (CONCAT _D:1 _E:1):e):f):g):h
1 (CONCAT (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:b)*:d):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:e):a):f):g):h
1 (CONCAT (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b):c):1 (REDXOR (REPLICATE (REPLICATE _B:d cA:a)*:a cA:a):b)*:1):e
1 (CONCAT (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (REPLICATE _A:1 cA:b)*:c):1 (CONCAT (REDXOR _B:d):1 (CONCAT _C:1 _D:a):e):f):g):c
1 (NOT (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):a
1 (REDXOR (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):1
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):1
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b):d)*:1
1 (REDXOR (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d):1
1 (REDXOR (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c):1
1 (REDXOR (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b):c):1
1 (REPLICATE (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b cA:b)*:d
1 (REPLICATE (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a cB:a):d
1 (REPLICATE (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d cB:b)*:b
1 (REPLICATE (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c cB:b)*:d
1 (REPLICATE (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 cA:b)*:c
1 (SEL@0 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):d

View File

@ -1,98 +0,0 @@
DFG 'pre inline' patterns with depth 1
9 (CONCAT _A:1 _B:a):b
8 (REDXOR _A:a):1
3 (NOT vA:a)*:a
2 (AND _A:a _B:a):a
1 (AND _A:a _B:a)*:a
1 (CONCAT _A:1 _B:1):a
1 (NOT _A:a):a
1 (REDXOR _A:a)*:1
1 (REPLICATE _A:1 cA:a)*:b
1 (REPLICATE _A:a cA:a)*:b
1 (REPLICATE _A:a cA:a):b
1 (REPLICATE _A:a cA:b)*:b
1 (REPLICATE _A:a cA:b)*:c
1 (SEL@0 _A:a):1
1 (SEL@0 _A:a):b
1 (SEL@A _A:a):1
DFG 'pre inline' patterns with depth 2
6 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:b):c):d
2 (AND (NOT vA:a)*:a (NOT vB:a)*:a):a
2 (REDXOR (AND _A:a _B:a):a):1
1 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a
1 (CONCAT (REDXOR _A:a)*:1 (CONCAT _B:1 _C:b):c):d
1 (CONCAT (REDXOR _A:a):1 (CONCAT _B:1 _C:1):b):c
1 (CONCAT (REDXOR _A:a):1 (REDXOR _B:b)*:1):c
1 (CONCAT (SEL@0 _A:a):1 (CONCAT _B:1 _C:b):c):d
1 (NOT (REPLICATE _A:a cA:b)*:b):b
1 (REDXOR (AND _A:a _B:a)*:a):1
1 (REDXOR (REPLICATE _A:1 cA:a)*:b):1
1 (REDXOR (REPLICATE _A:a cA:a)*:b):1
1 (REDXOR (REPLICATE _A:a cA:a):b)*:1
1 (REDXOR (REPLICATE _A:a cA:b)*:b):1
1 (REDXOR (REPLICATE _A:a cA:b)*:c):1
1 (REDXOR (SEL@0 _A:a):b):1
1 (REPLICATE (NOT _A:a):a cA:a)*:b
1 (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c
1 (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b):c
1 (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b
1 (REPLICATE (SEL@A _A:a):1 cA:b)*:c
1 (SEL@0 (AND _A:a _B:a)*:a):1
1 (SEL@0 (REPLICATE _A:a cA:a)*:b):c
1 (SEL@A (AND _A:a _B:a)*:a):1
DFG 'pre inline' patterns with depth 3
2 (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a):a):1
1 (CONCAT (REDXOR (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e
1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e
1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (SEL@0 _C:a):1 (CONCAT _D:1 _E:b):c):d):e
1 (CONCAT (REDXOR (REPLICATE _A:1 cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (CONCAT _C:1 _D:d):e):f):g
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a)*:b):1 (CONCAT (REDXOR _B:c):1 (REDXOR _C:b)*:1):d):e
1 (CONCAT (REDXOR (REPLICATE _A:a cA:a):b)*:1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:1):c):d):e
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:b):1 (CONCAT (REDXOR _B:c)*:1 (CONCAT _C:1 _D:d):e):f):g
1 (CONCAT (REDXOR (REPLICATE _A:a cA:b)*:c):1 (CONCAT (REDXOR _B:b):1 (CONCAT _C:1 _D:d):e):f):g
1 (CONCAT (REDXOR (SEL@0 _A:a):b):1 (REDXOR (REPLICATE _B:c cA:c):a)*:1):d
1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b
1 (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b
1 (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
1 (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1
1 (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b):c)*:1
1 (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1
1 (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1
1 (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b):c):1
1 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c
1 (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a
1 (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b):d
1 (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d
1 (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c
1 (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
1 (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b):c
1 (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1
DFG 'pre inline' patterns with depth 4
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (REDXOR _C:a):1 (CONCAT _D:1 _E:b):c):d):e):f
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a):a):1 (CONCAT (REDXOR (AND _A:a _B:a):a):1 (CONCAT (SEL@0 _C:a):1 (CONCAT _D:1 _E:b):c):d):e):f
1 (CONCAT (REDXOR (AND (NOT vA:a)*:a (NOT vB:a)*:a):a):1 (CONCAT (SEL@0 (AND _A:a _B:a)*:a):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:c):d):e):b):f
1 (CONCAT (REDXOR (REPLICATE (NOT _A:a):a cA:a)*:b):1 (CONCAT (REDXOR (SEL@0 _B:b):c):1 (REDXOR (REPLICATE _A:a cA:a):b)*:1):d):e
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:a)*:a):1 (CONCAT (REDXOR _C:d)*:1 (CONCAT _D:1 _E:e):f):g):h):i
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:b cA:b):c)*:1 (CONCAT (REDXOR (REPLICATE _B:b cA:b)*:c):1 (CONCAT (REDXOR _C:d):1 (REDXOR _D:c)*:1):e):f):g
1 (CONCAT (REDXOR (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):1 (CONCAT (REDXOR (REPLICATE _B:b cA:b):d)*:1 (CONCAT (REDXOR _C:d):1 (CONCAT _D:1 _E:1):e):f):g):h
1 (CONCAT (REDXOR (REPLICATE (SEL@A _A:a):1 cA:b)*:c):1 (CONCAT (REDXOR (REPLICATE _B:c cB:b)*:d):1 (CONCAT (REDXOR _C:b):1 (CONCAT _D:1 _E:e):a):f):g):h
1 (CONCAT (REDXOR (SEL@0 (REPLICATE _A:a cA:a)*:b):c):1 (REDXOR (REPLICATE (REPLICATE _B:d cA:a)*:a cA:a):b)*:1):e
1 (CONCAT (SEL@0 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 (CONCAT (REDXOR (REPLICATE _A:1 cA:b)*:c):1 (CONCAT (REDXOR _B:d):1 (CONCAT _C:1 _D:a):e):f):g):c
1 (NOT (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):a
1 (REDXOR (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):1
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):1
1 (REDXOR (REPLICATE (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b cA:b):d)*:1
1 (REDXOR (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d):1
1 (REDXOR (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c):1
1 (REDXOR (SEL@0 (REPLICATE (NOT _A:a):a cA:a)*:b):c):1
1 (REPLICATE (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b cA:b)*:d
1 (REPLICATE (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a cB:a):d
1 (REPLICATE (REPLICATE (REPLICATE (SEL@A _A:a):1 cA:b)*:c cB:b)*:d cB:b)*:b
1 (REPLICATE (REPLICATE (SEL@A (AND _A:a _B:a)*:a):1 cA:b)*:c cB:b)*:d
1 (REPLICATE (SEL@A (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a):1 cA:b)*:c
1 (SEL@0 (REPLICATE (NOT (REPLICATE _A:a cA:b)*:b):b cA:b)*:c):d

View File

@ -1,20 +0,0 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.top_filename = "t/t_dfg_stats_patterns.v"
test.compile(verilator_flags2=["--stats --no-skip-identical -fno-dfg-post-inline -fno-dfg-scoped"])
fn = test.glob_one(test.obj_dir + "/" + test.vm_prefix + "__stats_dfg_patterns*")
test.files_identical(fn, test.golden_filename)
test.passes()

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.top_filename = "t/t_dfg_stats_patterns.v"
test.compile(
verilator_flags2=["--stats --no-skip-identical -fno-dfg-pre-inline -fno-dfg-post-inline"])
fn = test.glob_one(test.obj_dir + "/" + test.vm_prefix + "__stats_dfg_patterns*")
test.files_identical(fn, test.golden_filename)
test.passes()

View File

@ -70,13 +70,10 @@ test.compile(verilator_flags2=[
"--stats",
"--build",
"--fdfg-synthesize-all",
"-fno-dfg-pre-inline",
"-fno-dfg-post-inline",
"--exe",
"+incdir+" + test.obj_dir,
"-Mdir", test.obj_dir + "/obj_opt",
"--prefix", "Vopt",
"-fno-const-before-dfg", # Otherwise V3Const makes testing painful
"-fno-split", # Dfg will take care of it
"--debug", "--debugi", "0", "--dumpi-tree", "0",
"-CFLAGS \"-I .. -I ../obj_ref\"",
@ -85,13 +82,13 @@ test.compile(verilator_flags2=[
]) # yapf:disable
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
r'DFG scoped Synthesis, synt / always blocks considered\s+(\d+)$',
r'DFG, Synthesis, synt / always blocks considered\s+(\d+)$',
nAlwaysSynthesized + nAlwaysReverted + nAlwaysNotSynthesized)
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
r'DFG scoped Synthesis, synt / always blocks synthesized\s+(\d+)$',
r'DFG, Synthesis, synt / always blocks synthesized\s+(\d+)$',
nAlwaysSynthesized + nAlwaysReverted)
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
r'DFG scoped Synthesis, synt / reverted \(multidrive\)\s+(\d)$', nAlwaysReverted)
r'DFG, Synthesis, synt / reverted \(multidrive\)\s+(\d)$', nAlwaysReverted)
# Execute test to check equivalence
test.execute(executable=test.obj_dir + "/obj_opt/Vopt")

View File

@ -1,60 +0,0 @@
//
// DESCRIPTION: Verilator: DFG optimizer equivalence testing
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2022 Geza Lore
// SPDX-License-Identifier: CC0-1.0
//
#include <verilated.h>
#include <verilated_cov.h>
#include <Vopt.h>
#include <Vref.h>
#include <iostream>
void rngUpdate(uint64_t& x) {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
}
int main(int, char**) {
// Create contexts
VerilatedContext ctx;
// Create models
Vref ref{&ctx};
Vopt opt{&ctx};
uint64_t rand_a = 0x5aef0c8dd70a4497;
uint64_t rand_b = 0xf0c0a8dd75ae4497;
uint64_t srand_a = 0x00fa8dcc7ae4957;
uint64_t srand_b = 0x0fa8dc7ae3c9574;
for (size_t n = 0; n < 200000; ++n) {
// Update rngs
rngUpdate(rand_a);
rngUpdate(rand_b);
rngUpdate(srand_a);
rngUpdate(srand_b);
// Assign inputs
ref.rand_a = opt.rand_a = rand_a;
ref.rand_b = opt.rand_b = rand_b;
ref.srand_a = opt.srand_a = srand_a;
ref.srand_b = opt.srand_b = srand_b;
// Evaluate both models
ref.eval();
opt.eval();
// Check equivalence
#include "checks.h"
// increment time
ctx.timeInc(1);
}
std::cout << "*-* All Finished *-*\n";
}

View File

@ -1,98 +0,0 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2025 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt_all')
test.sim_time = 2000000
if not os.path.exists(test.root + "/.git"):
test.skip("Not in a git repository")
# Generate the equivalence checks and declaration boilerplate
rdFile = test.top_filename
plistFile = test.obj_dir + "/portlist.vh"
pdeclFile = test.obj_dir + "/portdecl.vh"
checkFile = test.obj_dir + "/checks.h"
nAlwaysSynthesized = 0
nAlwaysNotSynthesized = 0
nAlwaysReverted = 0
with open(rdFile, 'r', encoding="utf8") as rdFh, \
open(plistFile, 'w', encoding="utf8") as plistFh, \
open(pdeclFile, 'w', encoding="utf8") as pdeclFh, \
open(checkFile, 'w', encoding="utf8") as checkFh:
for line in rdFh:
if re.search(r'^\s*always.*//\s*nosynth$', line):
nAlwaysNotSynthesized += 1
elif re.search(r'^\s*always.*//\s*revert$', line):
nAlwaysReverted += 1
elif re.search(r'^\s*always', line):
nAlwaysSynthesized += 1
line = line.split("//")[0]
m = re.search(r'`signal\((\w+),', line)
if not m:
continue
sig = m.group(1)
plistFh.write(sig + ",\n")
pdeclFh.write("output " + sig + ";\n")
checkFh.write("if (ref." + sig + " != opt." + sig + ") {\n")
checkFh.write(" std::cout << \"Mismatched " + sig + "\" << std::endl;\n")
checkFh.write(" std::cout << \"Ref: 0x\" << std::hex << (ref." + sig +
" + 0) << std::endl;\n")
checkFh.write(" std::cout << \"Opt: 0x\" << std::hex << (opt." + sig +
" + 0) << std::endl;\n")
checkFh.write(" std::exit(1);\n")
checkFh.write("}\n")
# Compile un-optimized
test.compile(verilator_flags2=[
"--stats",
"--build",
"-fno-dfg",
"+incdir+" + test.obj_dir,
"-Mdir", test.obj_dir + "/obj_ref",
"--prefix", "Vref",
"-Wno-UNOPTFLAT"
]) # yapf:disable
test.file_grep_not(test.obj_dir + "/obj_ref/Vref__stats.txt", r'DFG.*Synthesis')
# Compile optimized - also builds executable
test.compile(verilator_flags2=[
"--stats",
"--build",
"--fdfg-synthesize-all",
"-fno-dfg-post-inline",
"-fno-dfg-scoped",
"--exe",
"+incdir+" + test.obj_dir,
"-Mdir", test.obj_dir + "/obj_opt",
"--prefix", "Vopt",
"-fno-const-before-dfg", # Otherwise V3Const makes testing painful
"-fno-split", # Dfg will take care of it
"--debug", "--debugi", "0", "--dumpi-tree", "0",
"-CFLAGS \"-I .. -I ../obj_ref\"",
"../obj_ref/Vref__ALL.a",
"../../t/" + test.name + ".cpp"
]) # yapf:disable
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
r'DFG pre inline Synthesis, synt / always blocks considered\s+(\d+)$',
nAlwaysSynthesized + nAlwaysReverted + nAlwaysNotSynthesized)
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
r'DFG pre inline Synthesis, synt / always blocks synthesized\s+(\d+)$',
nAlwaysSynthesized + nAlwaysReverted)
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
r'DFG pre inline Synthesis, synt / reverted \(multidrive\)\s+(\d)$',
nAlwaysReverted)
# Execute test to check equivalence
test.execute(executable=test.obj_dir + "/obj_opt/Vopt")
test.passes()

View File

@ -1,34 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2025 Geza Lore
// SPDX-License-Identifier: CC0-1.0
`define signal(name, expr) wire [$bits(expr)-1:0] ``name = expr
module t (
`include "portlist.vh" // Boilerplate generated by t_dfg_break_cycles.py
rand_a, rand_b, srand_a, srand_b
);
`include "portdecl.vh" // Boilerplate generated by t_dfg_break_cycles.py
input rand_a;
input rand_b;
input srand_a;
input srand_b;
wire logic [63:0] rand_a;
wire logic [63:0] rand_b;
wire logic signed [63:0] srand_a;
wire logic signed [63:0] srand_b;
//////////////////////////////////////////////////////////////////////////
logic concat_lhs_a;
logic concat_lhs_b;
always_comb begin
{concat_lhs_a, concat_lhs_b} = rand_a[1:0] + rand_b[1:0];
end
`signal(CONCAT_LHS, {concat_lhs_a, concat_lhs_b});
endmodule

View File

@ -4,6 +4,6 @@
... For warning description see https://verilator.org/warn/UNOPTFLAT?v=latest
... Use "/* verilator lint_off UNOPTFLAT */" and lint_on around source to disable this message.
t/t_dfg_true_cycle_bad.v:10:23: Example path: o
t/t_dfg_true_cycle_bad.v:12:20: Example path: ASSIGNW
t/t_dfg_true_cycle_bad.v:12:17: Example path: ASSIGNW
t/t_dfg_true_cycle_bad.v:10:23: Example path: o
%Error: Exiting due to

View File

@ -4,4 +4,7 @@
%Warning-DEPRECATED: Option order-clock-delay is deprecated and has no effect.
%Warning-DEPRECATED: Option '--clk' is deprecated and has no effect.
%Warning-DEPRECATED: Option '--no-clk' is deprecated and has no effect.
%Warning-DEPRECATED: Option '-fno-dfg-pre-inline' is deprecated and has no effect
%Warning-DEPRECATED: Option '-fno-dfg-post-inline' is deprecated and has no effect
%Warning-DEPRECATED: Option '-fno-dfg-scoped' is deprecated, use '-fno-dfg' instead.
%Error: Exiting due to

View File

@ -11,7 +11,9 @@ import vltest_bootstrap
test.scenarios('vlt')
test.lint(verilator_flags2=["--trace-fst-thread --order-clock-delay --clk foo --no-clk bar"],
test.lint(verilator_flags2=[
"--trace-fst-thread --order-clock-delay --clk foo --no-clk bar -fno-dfg-pre-inline -fno-dfg-post-inline -fno-dfg-scoped"
],
fails=True,
expect_filename=test.golden_filename)

View File

@ -14,10 +14,8 @@ test.top_filename = "t/t_inst_tree.v"
out_filename = test.obj_dir + "/V" + test.name + ".tree.json"
test.compile(v_flags2=[
"--no-json-edit-nums", "-fno-dfg-post-inline", "-fno-dfg-scoped", test.t_dir +
"/t_inst_tree_inl1_pub0.vlt"
])
test.compile(
v_flags2=["--no-json-edit-nums", "-fno-dfg", test.t_dir + "/t_inst_tree_inl1_pub0.vlt"])
if test.vlt_all:
test.file_grep(

View File

@ -15,8 +15,7 @@ test.top_filename = "t/t_inst_tree.v"
out_filename = test.obj_dir + "/V" + test.name + ".tree.json"
test.compile(v_flags2=[
"--no-json-edit-nums", "-fno-dfg-post-inline", "t/" + test.name +
".vlt", test.wno_unopthreads_for_few_cores
"--no-json-edit-nums", "t/" + test.name + ".vlt", test.wno_unopthreads_for_few_cores
])
if test.vlt_all:

View File

@ -46,10 +46,10 @@
"stmtsp": [
{"type":"ASSIGNW","name":"","addr":"(IB)","loc":"d,56:12,56:13","dtypep":"(G)",
"rhsp": [
{"type":"VARREF","name":"d","addr":"(JB)","loc":"d,52:17,52:18","dtypep":"(G)","access":"RD","varp":"(Z)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
{"type":"VARREF","name":"d","addr":"(JB)","loc":"d,56:14,56:15","dtypep":"(G)","access":"RD","varp":"(Z)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
],
"lhsp": [
{"type":"VARREF","name":"q","addr":"(KB)","loc":"d,56:12,56:13","dtypep":"(G)","access":"WR","varp":"(CB)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
{"type":"VARREF","name":"q","addr":"(KB)","loc":"d,56:10,56:11","dtypep":"(G)","access":"WR","varp":"(CB)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
],"timingControlp": [],"strengthSpecp": []}
]}
]},
@ -88,19 +88,18 @@
]}
],"filesp": [],
"miscsp": [
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(YB)",
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"UNLINKED",
"typesp": [
{"type":"BASICDTYPE","name":"logic","addr":"(MB)","loc":"d,37:15,37:20","dtypep":"(MB)","keyword":"logic","range":"31:0","generic":true,"signed":true,"rangep": []},
{"type":"BASICDTYPE","name":"logic","addr":"(I)","loc":"d,39:11,39:14","dtypep":"(I)","keyword":"logic","generic":true,"rangep": []},
{"type":"BASICDTYPE","name":"logic","addr":"(G)","loc":"d,16:15,16:16","dtypep":"(G)","keyword":"logic","range":"3:0","generic":true,"rangep": []},
{"type":"BASICDTYPE","name":"bit","addr":"(OB)","loc":"d,21:14,21:15","dtypep":"(OB)","keyword":"bit","range":"31:0","generic":true,"signed":true,"rangep": []},
{"type":"VOIDDTYPE","name":"","addr":"(YB)","loc":"a,0:0,0:0","dtypep":"(YB)"}
{"type":"BASICDTYPE","name":"bit","addr":"(OB)","loc":"d,21:14,21:15","dtypep":"(OB)","keyword":"bit","range":"31:0","generic":true,"signed":true,"rangep": []}
]},
{"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0",
"modulep": [
{"type":"MODULE","name":"@CONST-POOL@","addr":"(ZB)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [],
{"type":"MODULE","name":"@CONST-POOL@","addr":"(YB)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [],
"stmtsp": [
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(AC)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(ZB)","varsp": [],"blocksp": [],"inlinesp": []}
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(ZB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(YB)","varsp": [],"blocksp": [],"inlinesp": []}
]}
]}
]}

View File

@ -157,10 +157,10 @@
"stmtsp": [
{"type":"ASSIGNW","name":"","addr":"(JD)","loc":"d,56:12,56:13","dtypep":"(H)",
"rhsp": [
{"type":"VARREF","name":"t.between","addr":"(KD)","loc":"d,18:15,18:22","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(UB)","classOrPackagep":"UNLINKED"}
{"type":"VARREF","name":"t.between","addr":"(KD)","loc":"d,56:14,56:15","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(UB)","classOrPackagep":"UNLINKED"}
],
"lhsp": [
{"type":"VARREF","name":"q","addr":"(LD)","loc":"d,56:12,56:13","dtypep":"(H)","access":"WR","varp":"(G)","varScopep":"(CB)","classOrPackagep":"UNLINKED"}
{"type":"VARREF","name":"q","addr":"(LD)","loc":"d,56:10,56:11","dtypep":"(H)","access":"WR","varp":"(G)","varScopep":"(CB)","classOrPackagep":"UNLINKED"}
],"timingControlp": [],"strengthSpecp": []}
]}
],"inlinesp": []}
@ -168,19 +168,18 @@
]}
],"filesp": [],
"miscsp": [
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(MD)",
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"UNLINKED",
"typesp": [
{"type":"BASICDTYPE","name":"logic","addr":"(Q)","loc":"d,37:15,37:20","dtypep":"(Q)","keyword":"logic","range":"31:0","generic":true,"signed":true,"rangep": []},
{"type":"BASICDTYPE","name":"logic","addr":"(J)","loc":"d,39:11,39:14","dtypep":"(J)","keyword":"logic","generic":true,"rangep": []},
{"type":"BASICDTYPE","name":"logic","addr":"(H)","loc":"d,16:15,16:16","dtypep":"(H)","keyword":"logic","range":"3:0","generic":true,"rangep": []},
{"type":"BASICDTYPE","name":"bit","addr":"(S)","loc":"d,21:14,21:15","dtypep":"(S)","keyword":"bit","range":"31:0","generic":true,"signed":true,"rangep": []},
{"type":"VOIDDTYPE","name":"","addr":"(MD)","loc":"a,0:0,0:0","dtypep":"(MD)"}
{"type":"BASICDTYPE","name":"bit","addr":"(S)","loc":"d,21:14,21:15","dtypep":"(S)","keyword":"bit","range":"31:0","generic":true,"signed":true,"rangep": []}
]},
{"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0",
"modulep": [
{"type":"MODULE","name":"@CONST-POOL@","addr":"(ND)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [],
{"type":"MODULE","name":"@CONST-POOL@","addr":"(MD)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [],
"stmtsp": [
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(OD)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(ND)","varsp": [],"blocksp": [],"inlinesp": []}
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(ND)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(MD)","varsp": [],"blocksp": [],"inlinesp": []}
]}
]}
]}

View File

@ -329,7 +329,7 @@
]}
],"filesp": [],
"miscsp": [
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(IG)",
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"UNLINKED",
"typesp": [
{"type":"BASICDTYPE","name":"bit","addr":"(BD)","loc":"d,18:18,18:19","dtypep":"(BD)","keyword":"bit","generic":true,"rangep": []},
{"type":"BASICDTYPE","name":"bit","addr":"(ID)","loc":"d,19:34,19:39","dtypep":"(ID)","keyword":"bit","range":"1:0","generic":true,"rangep": []},
@ -343,14 +343,13 @@
{"type":"BASICDTYPE","name":"logic","addr":"(KD)","loc":"d,19:20,19:21","dtypep":"(KD)","keyword":"logic","range":"1:0","generic":true,"rangep": []},
{"type":"BASICDTYPE","name":"logic","addr":"(ND)","loc":"d,19:20,19:21","dtypep":"(ND)","keyword":"logic","range":"3:0","generic":true,"signed":true,"rangep": []},
{"type":"BASICDTYPE","name":"logic","addr":"(CE)","loc":"d,18:24,18:26","dtypep":"(CE)","keyword":"logic","range":"31:0","generic":true,"rangep": []},
{"type":"BASICDTYPE","name":"bit","addr":"(WC)","loc":"d,18:12,18:13","dtypep":"(WC)","keyword":"bit","range":"31:0","generic":true,"signed":true,"rangep": []},
{"type":"VOIDDTYPE","name":"","addr":"(IG)","loc":"a,0:0,0:0","dtypep":"(IG)"}
{"type":"BASICDTYPE","name":"bit","addr":"(WC)","loc":"d,18:12,18:13","dtypep":"(WC)","keyword":"bit","range":"31:0","generic":true,"signed":true,"rangep": []}
]},
{"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0",
"modulep": [
{"type":"MODULE","name":"@CONST-POOL@","addr":"(JG)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [],
{"type":"MODULE","name":"@CONST-POOL@","addr":"(IG)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [],
"stmtsp": [
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(KG)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(JG)","varsp": [],"blocksp": [],"inlinesp": []}
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(JG)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(IG)","varsp": [],"blocksp": [],"inlinesp": []}
]}
]}
]}

View File

@ -26,10 +26,10 @@
"stmtsp": [
{"type":"ASSIGNW","name":"","addr":"(X)","loc":"d,26:16,26:17","dtypep":"(G)",
"rhsp": [
{"type":"VARREF","name":"ready_reg","addr":"(Y)","loc":"d,18:8,18:17","dtypep":"(G)","access":"RD","varp":"(K)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
{"type":"VARREF","name":"ready_reg","addr":"(Y)","loc":"d,26:18,26:27","dtypep":"(G)","access":"RD","varp":"(K)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
],
"lhsp": [
{"type":"VARREF","name":"ready","addr":"(Z)","loc":"d,26:16,26:17","dtypep":"(G)","access":"WR","varp":"(J)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
{"type":"VARREF","name":"ready","addr":"(Z)","loc":"d,26:10,26:15","dtypep":"(G)","access":"WR","varp":"(J)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
],"timingControlp": [],"strengthSpecp": []}
]}
]},
@ -44,29 +44,28 @@
"rhsp": [
{"type":"AND","name":"","addr":"(CB)","loc":"d,34:19,34:20","dtypep":"(G)",
"lhsp": [
{"type":"VARREF","name":"a1","addr":"(DB)","loc":"d,30:16,30:18","dtypep":"(G)","access":"RD","varp":"(O)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
{"type":"VARREF","name":"a1","addr":"(DB)","loc":"d,34:16,34:18","dtypep":"(G)","access":"RD","varp":"(O)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
],
"rhsp": [
{"type":"VARREF","name":"a2","addr":"(EB)","loc":"d,31:16,31:18","dtypep":"(G)","access":"RD","varp":"(R)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
{"type":"VARREF","name":"a2","addr":"(EB)","loc":"d,34:21,34:23","dtypep":"(G)","access":"RD","varp":"(R)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
]}
],
"lhsp": [
{"type":"VARREF","name":"zn","addr":"(FB)","loc":"d,34:13,34:14","dtypep":"(G)","access":"WR","varp":"(U)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
{"type":"VARREF","name":"zn","addr":"(FB)","loc":"d,34:10,34:12","dtypep":"(G)","access":"WR","varp":"(U)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
],"timingControlp": [],"strengthSpecp": []}
]}
]}
],"filesp": [],
"miscsp": [
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(GB)",
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"UNLINKED",
"typesp": [
{"type":"BASICDTYPE","name":"logic","addr":"(G)","loc":"d,30:16,30:18","dtypep":"(G)","keyword":"logic","generic":true,"rangep": []},
{"type":"VOIDDTYPE","name":"","addr":"(GB)","loc":"a,0:0,0:0","dtypep":"(GB)"}
{"type":"BASICDTYPE","name":"logic","addr":"(G)","loc":"d,30:16,30:18","dtypep":"(G)","keyword":"logic","generic":true,"rangep": []}
]},
{"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0",
"modulep": [
{"type":"MODULE","name":"@CONST-POOL@","addr":"(HB)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [],
{"type":"MODULE","name":"@CONST-POOL@","addr":"(GB)","loc":"a,0:0,0:0","origName":"@CONST-POOL@","verilogName":"@CONST-POOL@","level":0,"timeunit":"NONE","inlinesp": [],
"stmtsp": [
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(IB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(HB)","varsp": [],"blocksp": [],"inlinesp": []}
{"type":"SCOPE","name":"@CONST-POOL@","addr":"(HB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(GB)","varsp": [],"blocksp": [],"inlinesp": []}
]}
]}
]}

View File

@ -49,7 +49,6 @@
40 | always_comb out6 = d;
| ^~~~
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:17:14: Bit [0] of signal 'out2' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
t/t_lint_always_comb_multidriven_bad.v:28:15: ... Location of offending driver
28 | assign out2 = d;
| ^
@ -57,7 +56,6 @@
29 | always_comb out2 = 1'b0;
| ^
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:19:14: Bit [0] of signal 'out4' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
t/t_lint_always_comb_multidriven_bad.v:34:20: ... Location of offending driver
34 | always_comb out4 = 1'b0;
| ^
@ -65,7 +63,6 @@
35 | assign out4 = d;
| ^
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:20:14: Bit [0] of signal 'out5' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
t/t_lint_always_comb_multidriven_bad.v:37:20: ... Location of offending driver
37 | always_comb out5 = 1'b0;
| ^
@ -73,7 +70,6 @@
38 | always_comb out5 = d;
| ^
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:21:14: Bit [0] of signal 'out6' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
t/t_lint_always_comb_multidriven_bad.v:40:20: ... Location of offending driver
40 | always_comb out6 = d;
| ^

View File

@ -48,32 +48,28 @@
t/t_lint_always_comb_multidriven_bad.v:40:15: ... Location of other write
40 | always_comb out6 = d;
| ^~~~
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:17:14: Bit [0] of signal 'out2' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:17:14: Bit [0] of signal 't.out2' have multiple combinational drivers. This can cause performance degradation.
t/t_lint_always_comb_multidriven_bad.v:28:15: ... Location of offending driver
28 | assign out2 = d;
| ^
t/t_lint_always_comb_multidriven_bad.v:29:20: ... Location of offending driver
29 | always_comb out2 = 1'b0;
| ^
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:19:14: Bit [0] of signal 'out4' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:19:14: Bit [0] of signal 't.out4' have multiple combinational drivers. This can cause performance degradation.
t/t_lint_always_comb_multidriven_bad.v:34:20: ... Location of offending driver
34 | always_comb out4 = 1'b0;
| ^
t/t_lint_always_comb_multidriven_bad.v:35:15: ... Location of offending driver
35 | assign out4 = d;
| ^
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:20:14: Bit [0] of signal 'out5' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:20:14: Bit [0] of signal 't.out5' have multiple combinational drivers. This can cause performance degradation.
t/t_lint_always_comb_multidriven_bad.v:37:20: ... Location of offending driver
37 | always_comb out5 = 1'b0;
| ^
t/t_lint_always_comb_multidriven_bad.v:38:20: ... Location of offending driver
38 | always_comb out5 = d;
| ^
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:21:14: Bit [0] of signal 'out6' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_lint_always_comb_multidriven_bad.v:21:14: Bit [0] of signal 't.out6' have multiple combinational drivers. This can cause performance degradation.
t/t_lint_always_comb_multidriven_bad.v:40:20: ... Location of offending driver
40 | always_comb out6 = d;
| ^

View File

@ -1,5 +1,4 @@
%Warning-MULTIDRIVEN: t/t_lint_multidriven_coverage_bad.v:13:15: Bit [0] of signal 'w' have multiple combinational drivers. This can cause performance degradation.
: ... note: In instance 't'
%Warning-MULTIDRIVEN: t/t_lint_multidriven_coverage_bad.v:13:15: Bit [0] of signal 't.w' have multiple combinational drivers. This can cause performance degradation.
t/t_lint_multidriven_coverage_bad.v:15:15: ... Location of offending driver
15 | assign w[0] = a;
| ^

View File

@ -18,7 +18,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", test.pli_filename
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 42)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 43)
test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
test.passes()