Optimize DFG before V3Gate (#6141)
This commit is contained in:
parent
e015805194
commit
7a3f1f16ca
|
@ -597,8 +597,8 @@ Summary:
|
|||
.. option:: -fno-dfg
|
||||
|
||||
Rarely needed. Disable all use of the DFG-based combinational logic
|
||||
optimizer. Alias for :vlopt:`-fno-dfg-pre-inline` and
|
||||
:vlopt:`-fno-dfg-post-inline`.
|
||||
optimizer. Alias for :vlopt:`-fno-dfg-pre-inline`,
|
||||
:vlopt:`-fno-dfg-post-inline` and :vlopt:`-fno-dfg-scoped`.
|
||||
|
||||
.. option:: -fno-dfg-peephole
|
||||
|
||||
|
@ -616,6 +616,10 @@ Summary:
|
|||
|
||||
Rarely needed. Do not apply the DFG optimizer before inlining.
|
||||
|
||||
.. option:: -fno-dfg-scoped
|
||||
|
||||
Rarely needed. Do not apply the DFG optimizer across module scopes.
|
||||
|
||||
.. option:: -fno-expand
|
||||
|
||||
.. option:: -fno-func-opt
|
||||
|
|
|
@ -26,8 +26,8 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
// DfgGraph
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DfgGraph::DfgGraph(AstModule& module, const string& name)
|
||||
: m_modulep{&module}
|
||||
DfgGraph::DfgGraph(AstModule* modulep, const string& name)
|
||||
: m_modulep{modulep}
|
||||
, m_name{name} {}
|
||||
|
||||
DfgGraph::~DfgGraph() {
|
||||
|
@ -69,14 +69,31 @@ std::string DfgGraph::makeUniqueName(const std::string& prefix, size_t n) {
|
|||
return "__Vdfg" + prefix + m_tmpNameStub + std::to_string(n);
|
||||
}
|
||||
|
||||
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name, AstNodeDType* dtypep) {
|
||||
// Add AstVar to containing module
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
|
||||
modulep()->addStmtsp(varp);
|
||||
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name, AstNodeDType* dtypep,
|
||||
AstScope* scopep) {
|
||||
UASSERT_OBJ(!!scopep != !!modulep(), flp,
|
||||
"makeNewVar scopep should only be provided for a scoped DfgGraph");
|
||||
|
||||
// Create and return the corresponding variable vertex
|
||||
if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) return new DfgVarArray{*this, varp};
|
||||
return new DfgVarPacked{*this, varp};
|
||||
// Create AstVar
|
||||
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
|
||||
|
||||
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 (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) 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 (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) return new DfgVarArray{*this, varp};
|
||||
return new DfgVarPacked{*this, varp};
|
||||
}
|
||||
}
|
||||
|
||||
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
|
||||
|
@ -85,9 +102,10 @@ static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx)
|
|||
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
||||
|
||||
if (const DfgVarPacked* const varVtxp = vtx.cast<DfgVarPacked>()) {
|
||||
AstNode* const nodep = varVtxp->nodep();
|
||||
AstVar* const varp = varVtxp->varp();
|
||||
os << toDotId(vtx);
|
||||
os << " [label=\"" << varp->name() << "\nW" << varVtxp->width() << " / F"
|
||||
os << " [label=\"" << nodep->name() << "\nW" << varVtxp->width() << " / F"
|
||||
<< varVtxp->fanout() << '"';
|
||||
|
||||
if (varp->direction() == VDirection::INPUT) {
|
||||
|
@ -112,10 +130,11 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
|||
}
|
||||
|
||||
if (const DfgVarArray* const arrVtxp = vtx.cast<DfgVarArray>()) {
|
||||
AstNode* const nodep = arrVtxp->nodep();
|
||||
AstVar* const varp = arrVtxp->varp();
|
||||
const int elements = VN_AS(arrVtxp->dtypep(), UnpackArrayDType)->elementsConst();
|
||||
os << toDotId(vtx);
|
||||
os << " [label=\"" << varp->name() << "[" << elements << "]\"";
|
||||
os << " [label=\"" << nodep->name() << "[" << elements << "]\"";
|
||||
if (varp->direction() == VDirection::INPUT) {
|
||||
os << ", shape=box3d, style=filled, fillcolor=chartreuse2"; // Green
|
||||
} else if (varp->direction() == VDirection::OUTPUT) {
|
||||
|
@ -299,7 +318,7 @@ void DfgGraph::dumpDotAllVarConesPrefixed(const string& label) const {
|
|||
if (!sinkp) return;
|
||||
|
||||
// Open output file
|
||||
const string coneName{prefix + sinkp->varp()->name()};
|
||||
const string coneName{prefix + sinkp->nodep()->name()};
|
||||
const string fileName{v3Global.debugFilename(coneName) + ".dot"};
|
||||
const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(fileName)};
|
||||
if (os->fail()) v3fatal("Can't write file: " << fileName);
|
||||
|
@ -476,7 +495,7 @@ DfgVarPacked* DfgVertex::getResultVar() {
|
|||
return;
|
||||
}
|
||||
// Prefer the one with the lexically smaller name
|
||||
if (const int cmp = resp->varp()->name().compare(varp->varp()->name())) {
|
||||
if (const int cmp = resp->nodep()->name().compare(varp->nodep()->name())) {
|
||||
if (cmp > 0) resp = varp;
|
||||
return;
|
||||
}
|
||||
|
@ -485,6 +504,38 @@ DfgVarPacked* DfgVertex::getResultVar() {
|
|||
return resp;
|
||||
}
|
||||
|
||||
AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED {
|
||||
// If this is a variable, we are done
|
||||
if (DfgVertexVar* const varp = this->cast<DfgVertexVar>()) return varp->varScopep()->scopep();
|
||||
|
||||
// Try the result var first if instructed (usully only in the recursive case)
|
||||
if (tryResultVar) {
|
||||
if (DfgVertexVar* const varp = this->getResultVar()) return varp->varScopep()->scopep();
|
||||
}
|
||||
|
||||
// Look up cache
|
||||
const auto pair = cache.emplace(this, nullptr);
|
||||
if (pair.second) {
|
||||
// Find scope based on sources, falling back on the root scope
|
||||
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
|
||||
AstScope* foundp = rootp;
|
||||
const auto edges = sourceEdges();
|
||||
for (size_t i = 0; i < edges.second; ++i) {
|
||||
DfgEdge& edge = edges.first[i];
|
||||
foundp = edge.sourcep()->scopep(cache, true);
|
||||
if (foundp != rootp) break;
|
||||
}
|
||||
pair.first->second = foundp;
|
||||
}
|
||||
|
||||
// If the cache entry exists, but have not set the mapping yet, then we have a circualr graph
|
||||
UASSERT_OBJ(pair.first->second, this,
|
||||
"DfgVertex::scopep called on graph with circular operations");
|
||||
|
||||
// Done
|
||||
return pair.first->second;
|
||||
}
|
||||
|
||||
void DfgVertex::unlinkDelete(DfgGraph& dfg) {
|
||||
// Unlink source edges
|
||||
forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
|
||||
|
@ -521,15 +572,17 @@ V3Hash DfgSel::selfHash() const { return V3Hash{lsb()}; }
|
|||
// DfgVertexVar ----------
|
||||
|
||||
bool DfgVertexVar::selfEquals(const DfgVertex& that) const {
|
||||
UASSERT_OBJ(varp() != that.as<DfgVertexVar>()->varp(), this,
|
||||
"There should only be one DfgVertexVar for a given AstVar");
|
||||
UASSERT_OBJ(nodep()->type() == that.as<DfgVertexVar>()->nodep()->type(), this,
|
||||
"Both DfgVertexVar should be scoped or unscoped");
|
||||
UASSERT_OBJ(nodep() != that.as<DfgVertexVar>()->nodep(), this,
|
||||
"There should only be one DfgVertexVar for a given AstVar or AstVarScope");
|
||||
return false;
|
||||
}
|
||||
|
||||
V3Hash DfgVertexVar::selfHash() const {
|
||||
V3Hash hash;
|
||||
hash += m_varp->name();
|
||||
hash += m_varp->varType();
|
||||
hash += nodep()->name();
|
||||
hash += varp()->varType();
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
|
51
src/V3Dfg.h
51
src/V3Dfg.h
|
@ -203,16 +203,17 @@ public:
|
|||
|
||||
// Return data type used to represent the type of 'nodep' when converted to a DfgVertex
|
||||
static AstNodeDType* dtypeFor(const AstNode* nodep) {
|
||||
UDEBUGONLY(UASSERT_OBJ(isSupportedDType(nodep->dtypep()), nodep, "Unsupported dtype"););
|
||||
const AstNodeDType* const dtypep = nodep->dtypep()->skipRefp();
|
||||
UDEBUGONLY(UASSERT_OBJ(isSupportedDType(dtypep), nodep, "Unsupported dtype"););
|
||||
// For simplicity, all packed types are represented with a fixed type
|
||||
if (AstUnpackArrayDType* const typep = VN_CAST(nodep->dtypep(), UnpackArrayDType)) {
|
||||
if (const AstUnpackArrayDType* const typep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
AstNodeDType* const adtypep = new AstUnpackArrayDType{
|
||||
typep->fileline(), dtypeForWidth(typep->subDTypep()->width()),
|
||||
typep->rangep()->cloneTree(false)};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(adtypep);
|
||||
return adtypep;
|
||||
}
|
||||
return dtypeForWidth(nodep->width());
|
||||
return dtypeForWidth(dtypep->width());
|
||||
}
|
||||
|
||||
// Source location
|
||||
|
@ -284,6 +285,18 @@ public:
|
|||
// or nullptr if no such variable exists in the graph. This is O(fanout).
|
||||
DfgVarPacked* getResultVar() VL_MT_DISABLED;
|
||||
|
||||
// Cache type for 'scopep' below
|
||||
using ScopeCache = std::unordered_map<const DfgVertex*, AstScope*>;
|
||||
|
||||
// Retrieve the prefred AstScope this vertex belongs to. For variable
|
||||
// vertices this is defined. For operation vertices, we try to find a
|
||||
// scope based on variables in the upstream logic cone (inputs). If
|
||||
// there isn't one, (beceuse the whole upstream cone is constant...),
|
||||
// then the root scope is returned. If 'tryResultVar' is true, we will
|
||||
// condier the scope of 'getResultVar' first, if it exists.
|
||||
// Only call this with a scoped DfgGraph
|
||||
AstScope* scopep(ScopeCache& cache, bool tryResultVar = false) VL_MT_DISABLED;
|
||||
|
||||
// If the node has a single sink, return it, otherwise return nullptr
|
||||
DfgVertex* singleSink() const {
|
||||
return m_sinksp && !m_sinksp->m_nextp ? m_sinksp->m_sinkp : nullptr;
|
||||
|
@ -643,14 +656,15 @@ class DfgGraph final {
|
|||
size_t m_size = 0; // Number of vertices in the graph
|
||||
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
|
||||
uint32_t m_userCnt = 0; // Vertex user data generation counter
|
||||
// Parent of the graph (i.e.: the module containing the logic represented by this 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
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED;
|
||||
explicit DfgGraph(AstModule* modulep, const string& name = "") VL_MT_DISABLED;
|
||||
~DfgGraph() VL_MT_DISABLED;
|
||||
VL_UNCOPYABLE(DfgGraph);
|
||||
|
||||
|
@ -662,7 +676,7 @@ public:
|
|||
inline void removeVertex(DfgVertex& vtx);
|
||||
// Number of vertices in this graph
|
||||
size_t size() const { return m_size; }
|
||||
// Parent module
|
||||
// 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; }
|
||||
|
@ -699,8 +713,11 @@ public:
|
|||
// must be unique (as a pair) in each invocation for this graph.
|
||||
std::string makeUniqueName(const std::string& prefix, size_t n) VL_MT_DISABLED;
|
||||
|
||||
// Create a new variable with the given name and data type
|
||||
DfgVertexVar* makeNewVar(FileLine*, const std::string& name, AstNodeDType*) VL_MT_DISABLED;
|
||||
// Create a new variable with the given name and data type. For a Scoped
|
||||
// Dfg, the AstScope where the corresponding AstVarScope will be inserted
|
||||
// must be provided
|
||||
DfgVertexVar* makeNewVar(FileLine*, const std::string& name, AstNodeDType*,
|
||||
AstScope*) VL_MT_DISABLED;
|
||||
|
||||
// Split this graph into individual components (unique sub-graphs with no edges between them).
|
||||
// Also removes any vertices that are not weakly connected to any variable.
|
||||
|
@ -890,6 +907,24 @@ bool DfgVertex::isOnes() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions - for DfgVertexVar
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity)
|
||||
: DfgVertexVariadic{dfg, type, varp->fileline(), dtypeFor(varp), initialCapacity}
|
||||
, m_varp{varp}
|
||||
, m_varScopep{nullptr} {
|
||||
UASSERT_OBJ(dfg.modulep(), varp, "Un-scoped DfgVertexVar created in scoped DfgGraph");
|
||||
}
|
||||
DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp,
|
||||
uint32_t initialCapacity)
|
||||
: DfgVertexVariadic{dfg, type, vscp->fileline(), dtypeFor(vscp), initialCapacity}
|
||||
, m_varp{vscp->varp()}
|
||||
, m_varScopep{vscp} {
|
||||
UASSERT_OBJ(!dfg.modulep(), vscp, "Scoped DfgVertexVar created in un-scoped DfgGraph");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Inline method definitions - for DfgGraph
|
||||
//------------------------------------------------------------------------------
|
||||
|
|
|
@ -69,6 +69,7 @@ DfgSliceSel* makeVertex<DfgSliceSel, AstSliceSel>(const AstSliceSel*, DfgGraph&)
|
|||
|
||||
} // namespace
|
||||
|
||||
template <bool T_Scoped>
|
||||
class AstToDfgVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
|
||||
|
@ -87,6 +88,9 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
, m_lsb{lsb} {}
|
||||
};
|
||||
|
||||
using RootType = std::conditional_t<T_Scoped, AstNetlist, AstModule>;
|
||||
using VariableType = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
||||
|
||||
// STATE
|
||||
|
||||
DfgGraph* const m_dfgp; // The graph being built
|
||||
|
@ -98,14 +102,33 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
std::vector<DfgVarArray*> m_varArrayps; // All the DfgVarArray vertices we created.
|
||||
|
||||
// METHODS
|
||||
static VariableType* 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<VariableType*>(refp->varScopep());
|
||||
} else {
|
||||
return reinterpret_cast<VariableType*>(refp->varp());
|
||||
}
|
||||
}
|
||||
|
||||
static AstVar* getAstVar(VariableType* 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);
|
||||
}
|
||||
}
|
||||
|
||||
void markReferenced(AstNode* nodep) {
|
||||
nodep->foreach([this](const AstVarRef* refp) {
|
||||
// No need to (and in fact cannot) mark variables with unsupported dtypes
|
||||
if (!DfgVertex::isSupportedDType(refp->varp()->dtypep())) return;
|
||||
VariableType* const tgtp = getTarget(refp);
|
||||
// Mark vertex as having a module reference outside current DFG
|
||||
getNet(refp->varp())->setHasModRefs();
|
||||
getNet(tgtp)->setHasModRefs();
|
||||
// Mark variable as written from non-DFG logic
|
||||
if (refp->access().isWriteOrRW()) refp->varp()->user3(true);
|
||||
if (refp->access().isWriteOrRW()) tgtp->user3(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -116,24 +139,24 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
m_uncommittedVertices.clear();
|
||||
}
|
||||
|
||||
DfgVertexVar* getNet(AstVar* varp) {
|
||||
if (!varp->user1p()) {
|
||||
// Note DfgVertexVar vertices are not added to m_uncommittedVertices, because we
|
||||
DfgVertexVar* getNet(VariableType* vp) {
|
||||
if (!vp->user1p()) {
|
||||
// vp DfgVertexVar vertices are not added to m_uncommittedVertices, because we
|
||||
// want to hold onto them via AstVar::user1p, and the AstVar might be referenced via
|
||||
// multiple AstVarRef instances, so we will never revert a DfgVertexVar once
|
||||
// created. We will delete unconnected variable vertices at the end.
|
||||
if (VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
DfgVarArray* const vtxp = new DfgVarArray{*m_dfgp, varp};
|
||||
varp->user1p();
|
||||
if (VN_IS(vp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
DfgVarArray* const vtxp = new DfgVarArray{*m_dfgp, vp};
|
||||
vp->user1p();
|
||||
m_varArrayps.push_back(vtxp);
|
||||
varp->user1p(vtxp);
|
||||
vp->user1p(vtxp);
|
||||
} else {
|
||||
DfgVarPacked* const vtxp = new DfgVarPacked{*m_dfgp, varp};
|
||||
DfgVarPacked* const vtxp = new DfgVarPacked{*m_dfgp, vp};
|
||||
m_varPackedps.push_back(vtxp);
|
||||
varp->user1p(vtxp);
|
||||
vp->user1p(vtxp);
|
||||
}
|
||||
}
|
||||
return varp->user1u().to<DfgVertexVar*>();
|
||||
return vp->user1u().template to<DfgVertexVar*>();
|
||||
}
|
||||
|
||||
DfgVertex* getVertex(AstNode* nodep) {
|
||||
|
@ -167,7 +190,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
visit(vrefp);
|
||||
// cppcheck-has-bug-suppress knownConditionTrueFalse
|
||||
if (m_foundUnhandled) return false;
|
||||
getVertex(vrefp)->as<DfgVarPacked>()->addDriver(flp, 0, vtxp);
|
||||
getVertex(vrefp)->template as<DfgVarPacked>()->addDriver(flp, 0, vtxp);
|
||||
return true;
|
||||
}
|
||||
if (AstSel* const selp = VN_CAST(nodep, Sel)) {
|
||||
|
@ -181,7 +204,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
visit(vrefp);
|
||||
// cppcheck-has-bug-suppress knownConditionTrueFalse
|
||||
if (m_foundUnhandled) return false;
|
||||
getVertex(vrefp)->as<DfgVarPacked>()->addDriver(flp, lsbp->toUInt(), vtxp);
|
||||
getVertex(vrefp)->template as<DfgVarPacked>()->addDriver(flp, lsbp->toUInt(), vtxp);
|
||||
return true;
|
||||
}
|
||||
if (AstArraySel* const selp = VN_CAST(nodep, ArraySel)) {
|
||||
|
@ -195,7 +218,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
visit(vrefp);
|
||||
// cppcheck-has-bug-suppress knownConditionTrueFalse
|
||||
if (m_foundUnhandled) return false;
|
||||
getVertex(vrefp)->as<DfgVarArray>()->addDriver(flp, idxp->toUInt(), vtxp);
|
||||
getVertex(vrefp)->template as<DfgVarArray>()->addDriver(flp, idxp->toUInt(), vtxp);
|
||||
return true;
|
||||
}
|
||||
if (AstConcat* const concatp = VN_CAST(nodep, Concat)) {
|
||||
|
@ -334,18 +357,21 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
const uint32_t bEnd = b.m_lsb + bWidth;
|
||||
const uint32_t overlapEnd = std::min(aEnd, bEnd) - 1;
|
||||
|
||||
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0) {
|
||||
varp->varp()->v3warn( //
|
||||
if (a.m_fileline->operatorCompare(*b.m_fileline) != 0
|
||||
&& !varp->varp()->isUsedLoopIdx() // Loop index often abused, so suppress
|
||||
) {
|
||||
AstNode* const vp = varp->varScopep()
|
||||
? static_cast<AstNode*>(varp->varScopep())
|
||||
: static_cast<AstNode*>(varp->varp());
|
||||
vp->v3warn( //
|
||||
MULTIDRIVEN,
|
||||
"Bits [" //
|
||||
<< overlapEnd << ":" << b.m_lsb << "] of signal "
|
||||
<< varp->varp()->prettyNameQ()
|
||||
<< " have multiple combinational drivers\n"
|
||||
<< vp->prettyNameQ() << " have multiple combinational drivers\n"
|
||||
<< a.m_fileline->warnOther() << "... Location of first driver\n"
|
||||
<< a.m_fileline->warnContextPrimary() << '\n'
|
||||
<< b.m_fileline->warnOther() << "... Location of other driver\n"
|
||||
<< b.m_fileline->warnContextSecondary()
|
||||
<< varp->varp()->warnOther()
|
||||
<< b.m_fileline->warnContextSecondary() << vp->warnOther()
|
||||
<< "... Only the first driver will be respected");
|
||||
}
|
||||
|
||||
|
@ -437,19 +463,55 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
m_foundUnhandled = true;
|
||||
markReferenced(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); }
|
||||
void visit(AstModule* nodep) override { iterateAndNextNull(nodep->stmtsp()); }
|
||||
void visit(AstTopScope* nodep) override { iterate(nodep->scopep()); }
|
||||
void visit(AstScope* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstActive* nodep) override {
|
||||
if (nodep->hasCombo()) {
|
||||
iterateChildren(nodep);
|
||||
} else {
|
||||
markReferenced(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstCell* nodep) override { markReferenced(nodep); }
|
||||
void visit(AstNodeProcedure* nodep) override { markReferenced(nodep); }
|
||||
void visit(AstVar* nodep) override {
|
||||
if (nodep->isSc()) return;
|
||||
// No need to (and in fact cannot) handle variables with unsupported dtypes
|
||||
if (!DfgVertex::isSupportedDType(nodep->dtypep())) return;
|
||||
|
||||
// Mark variables with external references
|
||||
if (nodep->isIO() // Ports
|
||||
|| nodep->user2() // Target of a hierarchical reference
|
||||
|| nodep->isForced() // Forced
|
||||
) {
|
||||
getNet(nodep)->setHasExtRefs();
|
||||
void visit(AstVar* nodep) override {
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
return;
|
||||
} else {
|
||||
if (nodep->isSc()) return;
|
||||
// No need to (and in fact cannot) handle variables with unsupported dtypes
|
||||
if (!DfgVertex::isSupportedDType(nodep->dtypep())) return;
|
||||
|
||||
// Mark variables with external references
|
||||
if (nodep->isIO() // Ports
|
||||
|| nodep->user2() // Target of a hierarchical reference
|
||||
|| nodep->isForced() // Forced
|
||||
) {
|
||||
getNet(reinterpret_cast<VariableType*>(nodep))->setHasExtRefs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstVarScope* nodep) override {
|
||||
if VL_CONSTEXPR_CXX17 (!T_Scoped) {
|
||||
return;
|
||||
} else {
|
||||
if (nodep->varp()->isSc()) return;
|
||||
// No need to (and in fact cannot) handle variables with unsupported dtypes
|
||||
if (!DfgVertex::isSupportedDType(nodep->dtypep())) return;
|
||||
|
||||
// Mark variables with external references
|
||||
if (nodep->varp()->isIO() // Ports
|
||||
|| nodep->user2() // Target of a hierarchical reference
|
||||
|| nodep->varp()->isForced() // Forced
|
||||
) {
|
||||
getNet(reinterpret_cast<VariableType*>(nodep))->setHasExtRefs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,6 +600,17 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
return;
|
||||
}
|
||||
|
||||
// If the referenced variable is not in a regular module, then do not
|
||||
// convert it. This is especially needed for variabels in interfaces
|
||||
// which might be referenced via virtual intefaces, which cannot be
|
||||
// resovled statically.
|
||||
if (T_Scoped && !VN_IS(nodep->varScopep()->scopep()->modp(), Module)) {
|
||||
markReferenced(nodep);
|
||||
m_foundUnhandled = true;
|
||||
++m_ctx.m_nonRepVarRef;
|
||||
return;
|
||||
}
|
||||
|
||||
// Sadly sometimes AstVarRef does not have the same dtype as the referenced variable
|
||||
if (!DfgVertex::isSupportedDType(nodep->varp()->dtypep())) {
|
||||
m_foundUnhandled = true;
|
||||
|
@ -545,7 +618,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
return;
|
||||
}
|
||||
|
||||
nodep->user1p(getNet(nodep->varp()));
|
||||
nodep->user1p(getNet(getTarget(nodep)));
|
||||
}
|
||||
|
||||
void visit(AstConst* nodep) override {
|
||||
|
@ -585,13 +658,22 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
// The rest of the 'visit' methods are generated by 'astgen'
|
||||
#include "V3Dfg__gen_ast_to_dfg.h"
|
||||
|
||||
static DfgGraph* makeDfg(RootType& root) {
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
return new DfgGraph{nullptr, "netlist"};
|
||||
} else {
|
||||
AstModule* const modp = VN_AS((AstNode*)&(root), Module); // Remove this when C++17
|
||||
return new DfgGraph{modp, modp->name()};
|
||||
}
|
||||
}
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit AstToDfgVisitor(AstModule& module, V3DfgOptimizationContext& ctx)
|
||||
: m_dfgp{new DfgGraph{module, module.name()}}
|
||||
explicit AstToDfgVisitor(RootType& root, V3DfgOptimizationContext& ctx)
|
||||
: m_dfgp{makeDfg(root)}
|
||||
, m_ctx{ctx} {
|
||||
// Build the DFG
|
||||
iterateChildren(&module);
|
||||
UASSERT_OBJ(m_uncommittedVertices.empty(), &module, "Uncommitted vertices remain");
|
||||
iterate(&root);
|
||||
UASSERT_OBJ(m_uncommittedVertices.empty(), &root, "Uncommitted vertices remain");
|
||||
|
||||
// Canonicalize variables
|
||||
canonicalizePacked();
|
||||
|
@ -599,11 +681,15 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
public:
|
||||
static DfgGraph* apply(AstModule& module, V3DfgOptimizationContext& ctx) {
|
||||
return AstToDfgVisitor{module, ctx}.m_dfgp;
|
||||
static DfgGraph* apply(RootType& root, V3DfgOptimizationContext& ctx) {
|
||||
return AstToDfgVisitor{root, ctx}.m_dfgp;
|
||||
}
|
||||
};
|
||||
|
||||
DfgGraph* V3DfgPasses::astToDfg(AstModule& module, V3DfgOptimizationContext& ctx) {
|
||||
return AstToDfgVisitor::apply(module, ctx);
|
||||
return AstToDfgVisitor</* T_Scoped: */ false>::apply(module, ctx);
|
||||
}
|
||||
|
||||
DfgGraph* V3DfgPasses::astToDfg(AstNetlist& netlist, V3DfgOptimizationContext& ctx) {
|
||||
return AstToDfgVisitor</* T_Scoped: */ true>::apply(netlist, ctx);
|
||||
}
|
||||
|
|
|
@ -96,7 +96,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_dfg.modulep(), m_prefix + cvtToStr(i - 1)});
|
||||
}
|
||||
// Move the vertices to the component graphs
|
||||
moveVertices(m_dfg.varVertices());
|
||||
|
@ -317,9 +317,17 @@ class ExtractCyclicComponents final {
|
|||
DfgVertexVar*& clonep = m_clones[&vtx][component];
|
||||
if (!clonep) {
|
||||
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
|
||||
clonep = new DfgVarPacked{m_dfg, pVtxp->varp()};
|
||||
if (AstVarScope* const vscp = pVtxp->varScopep()) {
|
||||
clonep = new DfgVarPacked{m_dfg, vscp};
|
||||
} else {
|
||||
clonep = new DfgVarPacked{m_dfg, pVtxp->varp()};
|
||||
}
|
||||
} else if (DfgVarArray* const aVtxp = vtx.cast<DfgVarArray>()) {
|
||||
clonep = new DfgVarArray{m_dfg, aVtxp->varp()};
|
||||
if (AstVarScope* const vscp = aVtxp->varScopep()) {
|
||||
clonep = new DfgVarArray{m_dfg, vscp};
|
||||
} else {
|
||||
clonep = new DfgVarArray{m_dfg, aVtxp->varp()};
|
||||
}
|
||||
}
|
||||
UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type");
|
||||
if (vtx.hasModRefs()) clonep->setHasModRefs();
|
||||
|
@ -466,7 +474,7 @@ class ExtractCyclicComponents final {
|
|||
// Allocate result graphs
|
||||
m_components.resize(m_nonTrivialSCCs);
|
||||
for (size_t i = 0; i < m_nonTrivialSCCs; ++i) {
|
||||
m_components[i].reset(new DfgGraph{*m_dfg.modulep(), m_prefix + cvtToStr(i)});
|
||||
m_components[i].reset(new DfgGraph{m_dfg.modulep(), m_prefix + cvtToStr(i)});
|
||||
}
|
||||
|
||||
// Fix up edges crossing components (we can only do this at variable boundaries, and the
|
||||
|
|
|
@ -125,20 +125,57 @@ AstSliceSel* makeNode<AstSliceSel, DfgSliceSel, AstNodeExpr*, AstNodeExpr*, AstN
|
|||
|
||||
} // namespace
|
||||
|
||||
template <bool T_Scoped>
|
||||
class DfgToAstVisitor final : DfgVisitor {
|
||||
// NODE STATE
|
||||
|
||||
// AstScope::user1p // The combinational AstActive under this scope
|
||||
const VNUser1InUse m_user1InUse;
|
||||
|
||||
// TYPES
|
||||
using VariableType = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
|
||||
|
||||
// STATE
|
||||
|
||||
AstModule* const m_modp; // The parent/result module
|
||||
AstModule* const m_modp; // The parent/result module - This is nullptr when T_Scoped
|
||||
V3DfgOptimizationContext& m_ctx; // The optimization context for stats
|
||||
AstNodeExpr* m_resultp = nullptr; // The result node of the current traversal
|
||||
// Map from DfgVertex to the AstVar holding the value of that DfgVertex after conversion
|
||||
std::unordered_map<const DfgVertex*, AstVar*> m_resultVars;
|
||||
// Map from an AstVar, to the canonical AstVar that can be substituted for that AstVar
|
||||
std::unordered_map<AstVar*, AstVar*> m_canonVars;
|
||||
V3UniqueNames m_tmpNames{"__VdfgTmp"}; // For generating temporary names
|
||||
|
||||
// METHODS
|
||||
|
||||
static VariableType* getNode(const DfgVertexVar* vtxp) {
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
return reinterpret_cast<VariableType*>(vtxp->varScopep());
|
||||
} else {
|
||||
return reinterpret_cast<VariableType*>(vtxp->varp());
|
||||
}
|
||||
}
|
||||
|
||||
static AstActive* getCombActive(AstScope* scopep) {
|
||||
if (!scopep->user1p()) {
|
||||
// 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->hasCombo()) {
|
||||
scopep->user1p(activep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If there isn't one, create a new one
|
||||
if (!scopep->user1p()) {
|
||||
FileLine* const flp = scopep->fileline();
|
||||
AstSenTree* const senTreep
|
||||
= new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Combo{}}};
|
||||
AstActive* const activep = new AstActive{flp, "", senTreep};
|
||||
activep->sensesStorep(senTreep);
|
||||
scopep->addBlocksp(activep);
|
||||
scopep->user1p(activep);
|
||||
}
|
||||
}
|
||||
return VN_AS(scopep->user1p(), Active);
|
||||
}
|
||||
|
||||
AstNodeExpr* convertDfgVertexToAstNodeExpr(DfgVertex* vtxp) {
|
||||
UASSERT_OBJ(!m_resultp, vtxp, "Result already computed");
|
||||
UASSERT_OBJ(!vtxp->hasMultipleSinks() || vtxp->is<DfgVertexVar>()
|
||||
|
@ -151,8 +188,16 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
return resultp;
|
||||
}
|
||||
|
||||
void addResultEquation(FileLine* flp, AstNodeExpr* lhsp, AstNodeExpr* rhsp) {
|
||||
m_modp->addStmtsp(new AstAssignW{flp, lhsp, rhsp});
|
||||
void addResultEquation(const DfgVertexVar* vtxp, FileLine* flp, AstNodeExpr* lhsp,
|
||||
AstNodeExpr* rhsp) {
|
||||
AstAssignW* const assignp = new AstAssignW{flp, lhsp, rhsp};
|
||||
if VL_CONSTEXPR_CXX17 (T_Scoped) {
|
||||
// Add it to the scope holding the target variable
|
||||
getCombActive(vtxp->varScopep()->scopep())->addStmtsp(assignp);
|
||||
} else {
|
||||
// Add it to the parend module of the DfgGraph
|
||||
m_modp->addStmtsp(assignp);
|
||||
}
|
||||
++m_ctx.m_resultEquations;
|
||||
}
|
||||
|
||||
|
@ -160,9 +205,9 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
if (dfgVarp->isDrivenFullyByDfg()) {
|
||||
// Whole variable is driven. Render driver and assign directly to whole variable.
|
||||
FileLine* const flp = dfgVarp->driverFileLine(0);
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, dfgVarp->varp(), VAccess::WRITE};
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->source(0));
|
||||
addResultEquation(flp, lhsp, rhsp);
|
||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
||||
} else {
|
||||
// Variable is driven partially. Render each driver as a separate assignment.
|
||||
dfgVarp->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) {
|
||||
|
@ -171,12 +216,12 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep());
|
||||
// Create select LValue
|
||||
FileLine* const flp = dfgVarp->driverFileLine(idx);
|
||||
AstVarRef* const refp = new AstVarRef{flp, dfgVarp->varp(), VAccess::WRITE};
|
||||
AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
||||
AstConst* const lsbp = new AstConst{flp, dfgVarp->driverLsb(idx)};
|
||||
const int width = static_cast<int>(edge.sourcep()->width());
|
||||
AstSel* const lhsp = new AstSel{flp, refp, lsbp, width};
|
||||
// Add assignment of the value to the selected bits
|
||||
addResultEquation(flp, lhsp, rhsp);
|
||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -189,11 +234,11 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep());
|
||||
// Create select LValue
|
||||
FileLine* const flp = dfgVarp->driverFileLine(idx);
|
||||
AstVarRef* const refp = new AstVarRef{flp, dfgVarp->varp(), VAccess::WRITE};
|
||||
AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
||||
AstConst* const idxp = new AstConst{flp, dfgVarp->driverIndex(idx)};
|
||||
AstArraySel* const lhsp = new AstArraySel{flp, refp, idxp};
|
||||
// Add assignment of the value to the selected bits
|
||||
addResultEquation(flp, lhsp, rhsp);
|
||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -203,11 +248,11 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
} // LCOV_EXCL_STOP
|
||||
|
||||
void visit(DfgVarPacked* vtxp) override {
|
||||
m_resultp = new AstVarRef{vtxp->fileline(), vtxp->varp(), VAccess::READ};
|
||||
m_resultp = new AstVarRef{vtxp->fileline(), getNode(vtxp), VAccess::READ};
|
||||
}
|
||||
|
||||
void visit(DfgVarArray* vtxp) override {
|
||||
m_resultp = new AstVarRef{vtxp->fileline(), vtxp->varp(), VAccess::READ};
|
||||
m_resultp = new AstVarRef{vtxp->fileline(), getNode(vtxp), VAccess::READ};
|
||||
}
|
||||
|
||||
void visit(DfgConst* vtxp) override { //
|
||||
|
@ -254,11 +299,13 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
}
|
||||
|
||||
public:
|
||||
static AstModule* apply(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
return DfgToAstVisitor{dfg, ctx}.m_modp;
|
||||
}
|
||||
static void apply(DfgGraph& dfg, V3DfgOptimizationContext& ctx) { DfgToAstVisitor{dfg, ctx}; }
|
||||
};
|
||||
|
||||
AstModule* V3DfgPasses::dfgToAst(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
return DfgToAstVisitor::apply(dfg, ctx);
|
||||
void V3DfgPasses::dfgToAst(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
if (dfg.modulep()) {
|
||||
DfgToAstVisitor</* T_Scoped: */ false>::apply(dfg, ctx);
|
||||
} else {
|
||||
DfgToAstVisitor</* T_Scoped: */ true>::apply(dfg, ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -236,75 +236,101 @@ void V3DfgOptimizer::extract(AstNetlist* netlistp) {
|
|||
V3Global::dumpCheckGlobalTree("dfg-extract", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
||||
static void process(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
// Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a
|
||||
// DAG, and large, mostly acyclic graphs could not be optimized due to the presence of
|
||||
// small cycles.
|
||||
const std::vector<std::unique_ptr<DfgGraph>>& cyclicComponents
|
||||
= dfg.extractCyclicComponents("cyclic");
|
||||
|
||||
// Split the remaining acyclic DFG into [weakly] connected components
|
||||
const std::vector<std::unique_ptr<DfgGraph>>& acyclicComponents
|
||||
= dfg.splitIntoComponents("acyclic");
|
||||
|
||||
// Quick sanity check
|
||||
UASSERT_OBJ(dfg.size() == 0, dfg.modulep(), "DfgGraph should have become empty");
|
||||
|
||||
// For each acyclic component
|
||||
for (auto& component : acyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// Optimize the component
|
||||
V3DfgPasses::optimize(*component, ctx);
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
dfg.addGraph(*component);
|
||||
}
|
||||
|
||||
// Eliminate redundant variables. Run this on the whole acyclic DFG. It needs to traverse
|
||||
// the module/netlist to perform variable substitutions. Doing this by component would do
|
||||
// redundant traversals and can be extremely slow when we have many components.
|
||||
V3DfgPasses::eliminateVars(dfg, ctx.m_eliminateVarsContext);
|
||||
|
||||
// For each cyclic component
|
||||
for (auto& component : cyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// Converting back to Ast assumes the 'regularize' pass was run, so we must run it
|
||||
V3DfgPasses::regularize(*component, ctx.m_regularizeContext);
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
dfg.addGraph(*component);
|
||||
}
|
||||
}
|
||||
|
||||
void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
|
||||
// NODE STATE
|
||||
// AstVar::user1 -> Used by V3DfgPasses::astToDfg, V3DfgPasses::eliminateVars
|
||||
// AstVar::user1 -> Used by:
|
||||
// - V3DfgPasses::astToDfg,
|
||||
// - V3DfgPasses::eliminateVars,
|
||||
// - V3DfgPasses::dfgToAst,
|
||||
// AstVar::user2 -> bool: Flag indicating referenced by AstVarXRef (set just below)
|
||||
// AstVar::user3 -> bool: Flag indicating written by logic not representable as DFG
|
||||
// AstVar::user3, AstVarScope::user3
|
||||
// -> bool: Flag indicating written by logic not representable as DFG
|
||||
// (set by V3DfgPasses::astToDfg)
|
||||
const VNUser2InUse user2InUse;
|
||||
const VNUser3InUse user3InUse;
|
||||
|
||||
// Mark cross-referenced variables
|
||||
netlistp->foreach([](const AstVarXRef* xrefp) { xrefp->varp()->user2(true); });
|
||||
|
||||
V3DfgOptimizationContext ctx{label};
|
||||
|
||||
// Run the optimization phase
|
||||
for (AstNode* nodep = netlistp->modulesp(); nodep; nodep = nodep->nextp()) {
|
||||
// Only optimize proper modules
|
||||
AstModule* const modp = VN_CAST(nodep, Module);
|
||||
if (!modp) continue;
|
||||
if (!netlistp->topScopep()) {
|
||||
// Pre V3Scope application. Run on each module separately.
|
||||
|
||||
UINFO(4, "Applying DFG optimization to module '" << modp->name() << "'");
|
||||
++ctx.m_modules;
|
||||
// Mark cross-referenced variables
|
||||
netlistp->foreach([](const AstVarXRef* xrefp) { xrefp->varp()->user2(true); });
|
||||
|
||||
// Build the DFG of this module
|
||||
const std::unique_ptr<DfgGraph> dfg{V3DfgPasses::astToDfg(*modp, ctx)};
|
||||
// Run the optimization phase
|
||||
for (AstNode* nodep = netlistp->modulesp(); nodep; nodep = nodep->nextp()) {
|
||||
// Only optimize proper modules
|
||||
AstModule* const modp = VN_CAST(nodep, Module);
|
||||
if (!modp) continue;
|
||||
|
||||
UINFO(4, "Applying DFG optimization to module '" << modp->name() << "'");
|
||||
++ctx.m_modules;
|
||||
|
||||
// Build the DFG of this module
|
||||
const std::unique_ptr<DfgGraph> dfg{V3DfgPasses::astToDfg(*modp, ctx)};
|
||||
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input");
|
||||
|
||||
// Actually process the graph
|
||||
process(*dfg, ctx);
|
||||
|
||||
// Convert back to Ast
|
||||
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized");
|
||||
V3DfgPasses::dfgToAst(*dfg, ctx);
|
||||
}
|
||||
} else {
|
||||
// Post V3Scope application. Run on whole netlist.
|
||||
UINFO(4, "Applying DFG optimization to entire netlist");
|
||||
|
||||
// Build the DFG of the whole design
|
||||
const std::unique_ptr<DfgGraph> dfg{V3DfgPasses::astToDfg(*netlistp, ctx)};
|
||||
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-input");
|
||||
|
||||
// Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a
|
||||
// DAG, and large, mostly acyclic graphs could not be optimized due to the presence of
|
||||
// small cycles.
|
||||
const std::vector<std::unique_ptr<DfgGraph>>& cyclicComponents
|
||||
= dfg->extractCyclicComponents("cyclic");
|
||||
|
||||
// Split the remaining acyclic DFG into [weakly] connected components
|
||||
const std::vector<std::unique_ptr<DfgGraph>>& acyclicComponents
|
||||
= dfg->splitIntoComponents("acyclic");
|
||||
|
||||
// Quick sanity check
|
||||
UASSERT_OBJ(dfg->size() == 0, nodep, "DfgGraph should have become empty");
|
||||
|
||||
// For each acyclic component
|
||||
for (auto& component : acyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// Optimize the component
|
||||
V3DfgPasses::optimize(*component, ctx);
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
dfg->addGraph(*component);
|
||||
}
|
||||
|
||||
// Eliminate redundant variables. Run this on the whole acyclic DFG. It needs to traverse
|
||||
// the module to perform variable substitutions. Doing this by component would do
|
||||
// redundant traversals and can be extremely slow in large modules with many components.
|
||||
V3DfgPasses::eliminateVars(*dfg, ctx.m_eliminateVarsContext);
|
||||
|
||||
// For each cyclic component
|
||||
for (auto& component : cyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
// Converting back to Ast assumes the 'regularize' pass was run, so we must run it
|
||||
V3DfgPasses::regularize(*component, ctx.m_regularizeContext);
|
||||
// Add back under the main DFG (we will convert everything back in one go)
|
||||
dfg->addGraph(*component);
|
||||
}
|
||||
// Actually process the graph
|
||||
process(*dfg, ctx);
|
||||
|
||||
// Convert back to Ast
|
||||
if (dumpDfgLevel() >= 8) dfg->dumpDotFilePrefixed(ctx.prefix() + "whole-optimized");
|
||||
AstModule* const resultModp = V3DfgPasses::dfgToAst(*dfg, ctx);
|
||||
UASSERT_OBJ(resultModp == modp, modp, "Should be the same module");
|
||||
V3DfgPasses::dfgToAst(*dfg, ctx);
|
||||
}
|
||||
|
||||
V3Global::dumpCheckGlobalTree("dfg-optimize", 0, dumpTreeEitherLevel() >= 3);
|
||||
|
|
|
@ -232,6 +232,8 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
|
|||
}
|
||||
|
||||
void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
||||
UASSERT(dfg.modulep(), "binToOneHot only works with unscoped DfgGraphs for now");
|
||||
|
||||
const auto userDataInUse = dfg.userDataInUse();
|
||||
|
||||
// Structure to keep track of comparison details
|
||||
|
@ -372,7 +374,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
DfgVarPacked* varp = srcp->getResultVar();
|
||||
if (!varp) {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
|
||||
varp = dfg.makeNewVar(flp, name, idxDTypep)->as<DfgVarPacked>();
|
||||
varp = dfg.makeNewVar(flp, name, idxDTypep, nullptr)->as<DfgVarPacked>();
|
||||
varp->varp()->isInternal(true);
|
||||
varp->addDriver(flp, 0, srcp);
|
||||
}
|
||||
|
@ -392,7 +394,8 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
// The table variable
|
||||
DfgVarArray* const tabVtxp = [&]() {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Tab", nTables);
|
||||
DfgVarArray* const varp = dfg.makeNewVar(flp, name, tabDTypep)->as<DfgVarArray>();
|
||||
DfgVarArray* const varp
|
||||
= dfg.makeNewVar(flp, name, tabDTypep, nullptr)->as<DfgVarArray>();
|
||||
varp->varp()->isInternal(true);
|
||||
varp->varp()->noReset(true);
|
||||
varp->setHasModRefs();
|
||||
|
@ -496,9 +499,10 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
workListp = &vtx;
|
||||
};
|
||||
|
||||
// List of variables we are replacing
|
||||
std::vector<AstVar*> replacedVariables;
|
||||
// List of variables (AstVar or AstVarScope) we are replacing
|
||||
std::vector<AstNode*> replacedVariables;
|
||||
// AstVar::user1p() : AstVar* -> The replacement variables
|
||||
// AstVarScope::user1p() : AstVarScope* -> The replacement variables
|
||||
const VNUser1InUse user1InUse;
|
||||
|
||||
// Process the work list
|
||||
|
@ -541,7 +545,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
// If it is only referenced in this DFG, it can be removed
|
||||
++ctx.m_varsRemoved;
|
||||
varp->replaceWith(varp->source(0));
|
||||
varp->varp()->unlinkFrBack()->deleteTree();
|
||||
varp->nodep()->unlinkFrBack()->deleteTree();
|
||||
} else if (DfgVarPacked* const driverp = varp->source(0)->cast<DfgVarPacked>()) {
|
||||
// If it's driven from another variable, it can be replaced by that. However, we do not
|
||||
// want to propagate SystemC variables into the design.
|
||||
|
@ -549,9 +553,11 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
// Mark it for replacement
|
||||
++ctx.m_varsReplaced;
|
||||
UASSERT_OBJ(!varp->hasSinks(), varp, "Variable inlining should make this impossible");
|
||||
UASSERT(!varp->varp()->user1p(), "Replacement already exists");
|
||||
replacedVariables.emplace_back(varp->varp());
|
||||
varp->varp()->user1p(driverp->varp());
|
||||
// Grab the AstVar/AstVarScope
|
||||
AstNode* const nodep = varp->nodep();
|
||||
UASSERT_OBJ(!nodep->user1p(), nodep, "Replacement already exists");
|
||||
replacedVariables.emplace_back(nodep);
|
||||
nodep->user1p(driverp->nodep());
|
||||
} else {
|
||||
// Otherwise this *is* the canonical var
|
||||
continue;
|
||||
|
@ -566,16 +572,24 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
// Job done if no replacements possible
|
||||
if (replacedVariables.empty()) return;
|
||||
|
||||
// Apply variable replacements in the module
|
||||
VNDeleter deleter;
|
||||
dfg.modulep()->foreach([&](AstVarRef* refp) {
|
||||
AstVar* varp = refp->varp();
|
||||
while (AstVar* const replacementp = VN_AS(varp->user1p(), Var)) varp = replacementp;
|
||||
refp->varp(varp);
|
||||
});
|
||||
// Apply variable replacements
|
||||
if (AstModule* const modp = dfg.modulep()) {
|
||||
modp->foreach([&](AstVarRef* refp) {
|
||||
AstVar* varp = refp->varp();
|
||||
while (AstVar* const replacep = VN_AS(varp->user1p(), Var)) varp = replacep;
|
||||
refp->varp(varp);
|
||||
});
|
||||
} else {
|
||||
v3Global.rootp()->foreach([&](AstVarRef* refp) {
|
||||
AstVarScope* vscp = refp->varScopep();
|
||||
while (AstVarScope* const replacep = VN_AS(vscp->user1p(), VarScope)) vscp = replacep;
|
||||
refp->varScopep(vscp);
|
||||
refp->varp(vscp->varp());
|
||||
});
|
||||
}
|
||||
|
||||
// Remove the replaced variables
|
||||
for (AstVar* const varp : replacedVariables) varp->unlinkFrBack()->deleteTree();
|
||||
for (AstNode* const nodep : replacedVariables) nodep->unlinkFrBack()->deleteTree();
|
||||
}
|
||||
|
||||
void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
||||
|
@ -599,7 +613,9 @@ void V3DfgPasses::optimize(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
|||
apply(3, "input ", [&]() {});
|
||||
apply(4, "inlineVars ", [&]() { inlineVars(dfg); });
|
||||
apply(4, "cse0 ", [&]() { cse(dfg, ctx.m_cseContext0); });
|
||||
apply(4, "binToOneHot ", [&]() { binToOneHot(dfg, ctx.m_binToOneHotContext); });
|
||||
if (dfg.modulep()) {
|
||||
apply(4, "binToOneHot ", [&]() { binToOneHot(dfg, ctx.m_binToOneHotContext); });
|
||||
}
|
||||
if (v3Global.opt.fDfgPeephole()) {
|
||||
apply(4, "peephole ", [&]() { peephole(dfg, ctx.m_peepholeContext); });
|
||||
// We just did CSE above, so without peephole there is no need to run it again these
|
||||
|
|
|
@ -117,12 +117,14 @@ namespace V3DfgPasses {
|
|||
// constructed DfgGraph.
|
||||
DfgGraph* astToDfg(AstModule&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
||||
|
||||
// Same as above, but for the entire netlist, after V3Scope
|
||||
DfgGraph* astToDfg(AstNetlist&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
||||
|
||||
// Optimize the given DfgGraph
|
||||
void optimize(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
||||
|
||||
// Convert DfgGraph back into Ast, and insert converted graph back into its parent module.
|
||||
// Returns the parent module.
|
||||
AstModule* dfgToAst(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
||||
// Convert DfgGraph back into Ast, and insert converted graph back into the Ast.
|
||||
void dfgToAst(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
||||
|
||||
//===========================================================================
|
||||
// Intermediate/internal operations
|
||||
|
|
|
@ -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 AstVar*, std::string> m_internedVars; // Interned variables
|
||||
std::map<const AstNode*, 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.varp(), "v");
|
||||
const auto pair = m_internedVars.emplace(vtx.nodep(), "v");
|
||||
if (pair.second) pair.first->second += toLetters(m_internedVars.size() - 1);
|
||||
return pair.first->second;
|
||||
}
|
||||
|
|
|
@ -403,8 +403,8 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
// If both sides are variable references, order the side in some defined way. This
|
||||
// allows CSE to later merge 'a op b' with 'b op a'.
|
||||
if (lhsp->is<DfgVertexVar>() && rhsp->is<DfgVertexVar>()) {
|
||||
AstVar* const lVarp = lhsp->as<DfgVertexVar>()->varp();
|
||||
AstVar* const rVarp = rhsp->as<DfgVertexVar>()->varp();
|
||||
AstNode* const lVarp = lhsp->as<DfgVertexVar>()->nodep();
|
||||
AstNode* const rVarp = rhsp->as<DfgVertexVar>()->nodep();
|
||||
if (lVarp->name() > rVarp->name()) {
|
||||
APPLYING(SWAP_VAR_IN_COMMUTATIVE_BINARY) {
|
||||
Vertex* const replacementp = make<Vertex>(vtxp, rhsp, lhsp);
|
||||
|
|
|
@ -38,6 +38,10 @@ class DfgRegularize final {
|
|||
: m_dfg{dfg}
|
||||
, m_ctx{ctx} {
|
||||
|
||||
// Scope cache for below
|
||||
const bool scoped = !dfg.modulep();
|
||||
DfgVertex::ScopeCache scopeCache;
|
||||
|
||||
// Ensure intermediate values used multiple times are written to variables
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
const bool needsIntermediateVariable = [&]() {
|
||||
|
@ -71,8 +75,9 @@ class DfgRegularize final {
|
|||
// Need to create an intermediate variable
|
||||
const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps);
|
||||
FileLine* const flp = vtx.fileline();
|
||||
AstScope* const scopep = scoped ? vtx.scopep(scopeCache) : nullptr;
|
||||
DfgVarPacked* const newp
|
||||
= m_dfg.makeNewVar(flp, name, vtx.dtypep())->as<DfgVarPacked>();
|
||||
= m_dfg.makeNewVar(flp, name, vtx.dtypep(), scopep)->as<DfgVarPacked>();
|
||||
++m_nTmps;
|
||||
++m_ctx.m_temporariesIntroduced;
|
||||
// Replace vertex with the variable and add back driver
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
class DfgVertexVar VL_NOT_FINAL : public DfgVertexVariadic {
|
||||
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)
|
||||
bool m_hasDfgRefs = false; // This AstVar is referenced in a different DFG of the module
|
||||
bool m_hasModRefs = false; // This AstVar is referenced outside the DFG, but in the module
|
||||
bool m_hasExtRefs = false; // This AstVar is referenced from outside the module
|
||||
|
@ -48,14 +49,17 @@ class DfgVertexVar VL_NOT_FINAL : public DfgVertexVariadic {
|
|||
V3Hash selfHash() const final VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity)
|
||||
: DfgVertexVariadic{dfg, type, varp->fileline(), dtypeFor(varp), initialCapacity}
|
||||
, m_varp{varp} {}
|
||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity);
|
||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp, uint32_t initialCapacity);
|
||||
ASTGEN_MEMBERS_DfgVertexVar;
|
||||
|
||||
bool isDrivenByDfg() const { return arity() > 0; }
|
||||
|
||||
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);
|
||||
}
|
||||
bool hasDfgRefs() const { return m_hasDfgRefs; }
|
||||
void setHasDfgRefs() { m_hasDfgRefs = true; }
|
||||
bool hasModRefs() const { return m_hasModRefs; }
|
||||
|
@ -73,7 +77,7 @@ public:
|
|||
// Keep if public
|
||||
if (varp()->isSigPublic()) return true;
|
||||
// Keep if written in non-DFG code
|
||||
if (varp()->user3()) return true;
|
||||
if (nodep()->user3()) return true;
|
||||
// Otherwise it can be removed
|
||||
return false;
|
||||
}
|
||||
|
@ -181,7 +185,11 @@ class DfgVarArray final : public DfgVertexVar {
|
|||
public:
|
||||
DfgVarArray(DfgGraph& dfg, AstVar* varp)
|
||||
: DfgVertexVar{dfg, dfgType(), varp, 4u} {
|
||||
UASSERT_OBJ(VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType), varp, "Non array DfgVarArray");
|
||||
UASSERT_OBJ(VN_IS(dtypep(), UnpackArrayDType), varp, "Non array DfgVarArray");
|
||||
}
|
||||
DfgVarArray(DfgGraph& dfg, AstVarScope* vscp)
|
||||
: DfgVertexVar{dfg, dfgType(), vscp, 4u} {
|
||||
UASSERT_OBJ(VN_IS(dtypep(), UnpackArrayDType), vscp, "Non array DfgVarArray");
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgVarArray;
|
||||
|
||||
|
@ -239,6 +247,8 @@ class DfgVarPacked final : public DfgVertexVar {
|
|||
public:
|
||||
DfgVarPacked(DfgGraph& dfg, AstVar* varp)
|
||||
: DfgVertexVar{dfg, dfgType(), varp, 1u} {}
|
||||
DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp)
|
||||
: DfgVertexVar{dfg, dfgType(), vscp, 1u} {}
|
||||
ASTGEN_MEMBERS_DfgVarPacked;
|
||||
|
||||
bool isDrivenFullyByDfg() const {
|
||||
|
|
|
@ -1331,6 +1331,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
|||
DECL_OPTION("-fdfg", CbFOnOff, [this](bool flag) {
|
||||
m_fDfgPreInline = flag;
|
||||
m_fDfgPostInline = flag;
|
||||
m_fDfgScoped = flag;
|
||||
});
|
||||
DECL_OPTION("-fdfg-peephole", FOnOff, &m_fDfgPeephole);
|
||||
DECL_OPTION("-fdfg-peephole-", CbPartialMatch, [this](const char* optp) { //
|
||||
|
@ -1341,6 +1342,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
|||
});
|
||||
DECL_OPTION("-fdfg-pre-inline", FOnOff, &m_fDfgPreInline);
|
||||
DECL_OPTION("-fdfg-post-inline", FOnOff, &m_fDfgPostInline);
|
||||
DECL_OPTION("-fdfg-scoped", FOnOff, &m_fDfgScoped);
|
||||
DECL_OPTION("-fexpand", FOnOff, &m_fExpand);
|
||||
DECL_OPTION("-ffunc-opt", CbFOnOff, [this](bool flag) { //
|
||||
m_fFuncSplitCat = flag;
|
||||
|
@ -2182,6 +2184,7 @@ void V3Options::optimize(int level) {
|
|||
m_fDedupe = flag;
|
||||
m_fDfgPreInline = flag;
|
||||
m_fDfgPostInline = flag;
|
||||
m_fDfgScoped = flag;
|
||||
m_fDeadAssigns = flag;
|
||||
m_fDeadCells = flag;
|
||||
m_fExpand = flag;
|
||||
|
|
|
@ -423,6 +423,7 @@ private:
|
|||
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_fDfgScoped; // main switch: -fno-dfg-scoped and -fno-dfg
|
||||
bool m_fDeadAssigns; // main switch: -fno-dead-assigns: remove dead assigns
|
||||
bool m_fDeadCells; // main switch: -fno-dead-cells: remove dead cells
|
||||
bool m_fExpand; // main switch: -fno-expand: expansion of C macros
|
||||
|
@ -737,6 +738,7 @@ public:
|
|||
bool fDfgPeephole() const { return m_fDfgPeephole; }
|
||||
bool fDfgPreInline() const { return m_fDfgPreInline; }
|
||||
bool fDfgPostInline() const { return m_fDfgPostInline; }
|
||||
bool fDfgScoped() const { return m_fDfgScoped; }
|
||||
bool fDfgPeepholeEnabled(const std::string& name) const {
|
||||
return !m_fDfgPeepholeDisabled.count(name);
|
||||
}
|
||||
|
|
|
@ -405,6 +405,11 @@ static void process() {
|
|||
// forcing.
|
||||
V3Force::forceAll(v3Global.rootp());
|
||||
|
||||
if (v3Global.opt.fDfgScoped()) {
|
||||
// Scoped DFG optimization
|
||||
V3DfgOptimizer::optimize(v3Global.rootp(), "scoped");
|
||||
}
|
||||
|
||||
// Gate-based logic elimination; eliminate signals and push constant across cell
|
||||
// boundaries Instant propagation makes lots-o-constant reduction possibilities.
|
||||
if (v3Global.opt.fGate()) {
|
||||
|
|
|
@ -96,7 +96,7 @@ def check(name):
|
|||
name = name.lower()
|
||||
name = re.sub(r'_', ' ', name)
|
||||
test.file_grep(test.obj_dir + "/obj_opt/Vopt__stats.txt",
|
||||
r'DFG\s+(pre|post) inline Peephole, ' + name + r'\s+([1-9]\d*)')
|
||||
r'DFG\s+(pre inline|post inline|scoped) Peephole, ' + name + r'\s+([1-9]\d*)')
|
||||
|
||||
|
||||
# Check all optimizations defined in
|
||||
|
|
|
@ -12,7 +12,7 @@ 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"])
|
||||
test.compile(verilator_flags2=["--stats --no-skip-identical -fno-dfg-pre-inline -fno-dfg-scoped"])
|
||||
|
||||
fn = test.glob_one(test.obj_dir + "/" + test.vm_prefix + "__stats_dfg_patterns*")
|
||||
test.files_identical(fn, test.golden_filename)
|
||||
|
|
|
@ -12,7 +12,7 @@ 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"])
|
||||
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)
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
DFG 'scoped' patterns with depth 1
|
||||
3 (NOT vA:a)*:a
|
||||
2 (AND _A:a _B:a):a
|
||||
2 (REPLICATE _A:a cA:a)*:b
|
||||
1 (AND _A:a _B:a)*:a
|
||||
1 (CONCAT '0:a _A:b):A
|
||||
1 (NOT _A:a):a
|
||||
1 (REPLICATE _A:1 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 'scoped' patterns with depth 2
|
||||
2 (AND (NOT vA:a)*:a (NOT vB:a)*:a):a
|
||||
1 (AND (NOT vA:a)*:a (NOT vB:a)*:a)*:a
|
||||
1 (CONCAT '0:a (REPLICATE _A:a cA:a)*:b):A
|
||||
1 (NOT (REPLICATE _A:a cA:b)*:b):b
|
||||
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 'scoped' patterns with depth 3
|
||||
1 (CONCAT '0:a (REPLICATE (REPLICATE _A:b cA:a)*:a cA:a)*:c):A
|
||||
1 (NOT (REPLICATE (REPLICATE _A:a cA:b)*:c cA:b)*:b):b
|
||||
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 'scoped' patterns with depth 4
|
||||
1 (CONCAT '0:a (REPLICATE (REPLICATE (REPLICATE _A:b cA:a)*:c cA:a)*:a cA:a)*:d):A
|
||||
1 (NOT (REPLICATE (REPLICATE (REPLICATE _A:1 cA:a)*:b cB:a)*:c cB:a)*:a):a
|
||||
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
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. 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-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()
|
|
@ -33,9 +33,9 @@ test.compile(
|
|||
|
||||
if test.vltmt:
|
||||
test.file_grep(test.obj_dir + "/V" + test.name + "__hier.dir/V" + test.name + "__stats.txt",
|
||||
r'Optimizations, Thread schedule count\s+(\d+)', 1)
|
||||
r'Optimizations, Thread schedule count\s+(\d+)', 2)
|
||||
test.file_grep(test.obj_dir + "/V" + test.name + "__hier.dir/V" + test.name + "__stats.txt",
|
||||
r'Optimizations, Thread schedule total tasks\s+(\d+)', 2)
|
||||
r'Optimizations, Thread schedule total tasks\s+(\d+)', 3)
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ if test.vlt_all:
|
|||
# We expect to combine sequent functions across multiple instances of
|
||||
# l2, l3, l4, l5. If this number drops, please confirm this has not broken.
|
||||
test.file_grep(test.stats, r'Optimizations, Combined CFuncs\s+(\d+)',
|
||||
(85 if test.vltmt else 67))
|
||||
(91 if test.vltmt else 87))
|
||||
|
||||
# Everything should use relative references
|
||||
check_relative_refs("t", True)
|
||||
|
|
|
@ -15,7 +15,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", test.t_dir + "/t_inst_tree_inl1_pub0.vlt"
|
||||
"--no-json-edit-nums", "-fno-dfg-post-inline", "-fno-dfg-scoped", test.t_dir +
|
||||
"/t_inst_tree_inl1_pub0.vlt"
|
||||
])
|
||||
|
||||
if test.vlt_all:
|
||||
|
|
|
@ -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+)', 43)
|
||||
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 38)
|
||||
test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
|
||||
|
||||
test.passes()
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
... 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_unopt_combo.v:23:25: Example path: t.b
|
||||
t/t_unopt_combo.v:124:4: Example path: ALWAYS
|
||||
t/t_unopt_combo.v:137:14: Example path: ASSIGNW
|
||||
t/t_unopt_combo.v:24:25: Example path: t.c
|
||||
t/t_unopt_combo.v:81:4: Example path: ALWAYS
|
||||
t/t_unopt_combo.v:23:25: Example path: t.b
|
||||
|
|
Loading…
Reference in New Issue