Internals: Improve DFG implementation details (#6355)

Large scale refactoring to simplify some of the more obtuse internals of
DFG. Remove multiple redundant internal APIs, simplify representation of
variables, fix potential unsoundness in circular decomposition. No
functional change intended.
This commit is contained in:
Geza Lore 2025-09-02 16:50:40 +01:00 committed by GitHub
parent 67f26508ba
commit a6f26b85b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 1763 additions and 1733 deletions

View File

@ -2617,7 +2617,7 @@ class AstAlways final : public AstNodeProcedure {
const VAlwaysKwd m_keyword; const VAlwaysKwd m_keyword;
public: public:
AstAlways(FileLine* fl, VAlwaysKwd keyword, AstSenTree* sentreep, AstNode* stmtsp) AstAlways(FileLine* fl, VAlwaysKwd keyword, AstSenTree* sentreep, AstNode* stmtsp = nullptr)
: ASTGEN_SUPER_Always(fl, stmtsp) : ASTGEN_SUPER_Always(fl, stmtsp)
, m_keyword{keyword} { , m_keyword{keyword} {
this->sentreep(sentreep); this->sentreep(sentreep);

View File

@ -115,6 +115,7 @@ void CfgGraph::rpoBlocks() {
// Assign edge IDs // Assign edge IDs
size_t edgeCount = 0; size_t edgeCount = 0;
for (V3GraphVertex& v : vertices()) { for (V3GraphVertex& v : vertices()) {
// cppcheck-suppress constVariableReference // cppcheck is wrong
for (V3GraphEdge& e : v.outEdges()) static_cast<CfgEdge&>(e).m_id = edgeCount++; for (V3GraphEdge& e : v.outEdges()) static_cast<CfgEdge&>(e).m_id = edgeCount++;
} }
UASSERT_OBJ(edgeCount == m_nEdges, m_enterp, "Inconsistent edge count"); UASSERT_OBJ(edgeCount == m_nEdges, m_enterp, "Inconsistent edge count");

View File

@ -92,15 +92,12 @@ class CfgBlock final : public V3GraphVertex {
// STATE // STATE
CfgGraph* const m_cfgp; // The control flow graph this CfgBlock is under CfgGraph* const m_cfgp; // The control flow graph this CfgBlock is under
size_t m_rpoNumber; // Reverse post-order number and unique ID of this CfgBlock size_t m_rpoNumber = 0; // Reverse post-order number and unique ID of this CfgBlock
// V3GraphEdge::user() is set to the unique id by CfgBuilder // V3GraphEdge::user() is set to the unique id by CfgBuilder
std::vector<AstNodeStmt*> m_stmtps; // statements in this CfgBlock std::vector<AstNodeStmt*> m_stmtps; // statements in this CfgBlock
// PRIVATE METHODS // PRIVATE METHODS
// ID (reverse post-order numpber) of this block
inline size_t id();
inline size_t id() const;
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only // CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
inline explicit CfgBlock(CfgGraph* cfgp); inline explicit CfgBlock(CfgGraph* cfgp);
@ -109,6 +106,10 @@ class CfgBlock final : public V3GraphVertex {
public: public:
// PUBLIC METHODS // PUBLIC METHODS
// ID (reverse post-order number) of this block
inline size_t id();
inline size_t id() const;
// Is this the entry block of the CFG? // Is this the entry block of the CFG?
bool isEnter() const { return inEmpty(); } bool isEnter() const { return inEmpty(); }
// Is this the exit block of the CFG? // Is this the exit block of the CFG?
@ -185,19 +186,20 @@ class CfgEdge final : public V3GraphEdge {
// STATE - Immutable after construction, set by CfgBuilder // STATE - Immutable after construction, set by CfgBuilder
CfgGraph* const m_cfgp; // The control flow graph this CfgEdge is under CfgGraph* const m_cfgp; // The control flow graph this CfgEdge is under
size_t m_id; // Unique ID of this vertex size_t m_id = 0; // Unique ID of this vertex
// PRIVATE METHODS // PRIVATE METHODS
// Unique ID of this CfgEdge - no particular meaning
inline size_t id();
inline size_t id() const;
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only // CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
inline CfgEdge(CfgGraph* graphp, CfgBlock* srcp, CfgBlock* dstp); inline CfgEdge(CfgGraph* graphp, CfgBlock* srcp, CfgBlock* dstp);
~CfgEdge() override = default; ~CfgEdge() override = default;
public: public:
// METHODS - all const // METHODS
// Unique ID of this CfgEdge - no particular meaning
inline size_t id();
inline size_t id() const;
// Source/destination CfgBlock // Source/destination CfgBlock
const CfgBlock* srcp() const { return static_cast<const CfgBlock*>(fromp()); } const CfgBlock* srcp() const { return static_cast<const CfgBlock*>(fromp()); }

View File

@ -205,7 +205,7 @@ class CfgBuilder final : public VNVisitorConst {
while (!unreachableps.empty()) { while (!unreachableps.empty()) {
V3GraphVertex* const vtxp = unreachableps.back(); V3GraphVertex* const vtxp = unreachableps.back();
unreachableps.pop_back(); unreachableps.pop_back();
for (V3GraphEdge& edge : vtxp->outEdges()) { for (const V3GraphEdge& edge : vtxp->outEdges()) {
--m_cfgp->m_nEdges; --m_cfgp->m_nEdges;
if (edge.top()->inSize1()) unreachableps.emplace_back(edge.top()); if (edge.top()->inSize1()) unreachableps.emplace_back(edge.top());
} }

View File

@ -24,15 +24,42 @@
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// DfgGraph // V3Dfg
// predicate for supported data types
static bool dfgGraphIsSupportedDTypePacked(const AstNodeDType* dtypep) {
dtypep = dtypep->skipRefp();
if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) {
return typep->keyword().isIntNumeric();
}
if (const AstPackArrayDType* const typep = VN_CAST(dtypep, PackArrayDType)) {
return dfgGraphIsSupportedDTypePacked(typep->subDTypep());
}
if (const AstNodeUOrStructDType* const typep = VN_CAST(dtypep, NodeUOrStructDType)) {
return typep->packed();
}
return false;
}
bool V3Dfg::isSupported(const AstNodeDType* dtypep) {
dtypep = dtypep->skipRefp();
// Support 1 dimensional unpacked arrays of packed types
if (const AstUnpackArrayDType* const typep = VN_CAST(dtypep, UnpackArrayDType)) {
return dfgGraphIsSupportedDTypePacked(typep->subDTypep());
}
// Support packed types
return dfgGraphIsSupportedDTypePacked(dtypep);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// DfgGraph
DfgGraph::DfgGraph(AstModule* modulep, const string& name) DfgGraph::DfgGraph(AstModule* modulep, const string& name)
: m_modulep{modulep} : m_modulep{modulep}
, m_name{name} {} , m_name{name} {}
DfgGraph::~DfgGraph() { DfgGraph::~DfgGraph() {
forEachVertex([](DfgVertex& vtxp) { delete &vtxp; }); forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
} }
std::unique_ptr<DfgGraph> DfgGraph::clone() const { std::unique_ptr<DfgGraph> DfgGraph::clone() const {
@ -133,40 +160,21 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
// Constants have no inputs // Constants have no inputs
// Hook up inputs of cloned variables // Hook up inputs of cloned variables
for (const DfgVertexVar& vtx : m_varVertices) { for (const DfgVertexVar& vtx : m_varVertices) {
// All variable vertices are unary DfgVertexVar* const cp = vtxp2clonep.at(&vtx)->as<DfgVertexVar>();
if (const DfgVertex* const srcp = vtx.srcp()) { if (const DfgVertex* const srcp = vtx.srcp()) cp->srcp(vtxp2clonep.at(srcp));
vtxp2clonep.at(&vtx)->as<DfgVertexVar>()->srcp(vtxp2clonep.at(srcp)); if (const DfgVertex* const defp = vtx.defaultp()) cp->defaultp(vtxp2clonep.at(defp));
}
} }
// Hook up inputs of cloned operation vertices // Hook up inputs of cloned operation vertices
for (const DfgVertex& vtx : m_opVertices) { for (const DfgVertex& vtx : m_opVertices) {
if (vtx.is<DfgVertexVariadic>()) { if (vtx.is<DfgVertexVariadic>()) {
switch (vtx.type()) { switch (vtx.type()) {
case VDfgType::atSpliceArray: { case VDfgType::atSpliceArray:
const DfgSpliceArray* const vp = vtx.as<DfgSpliceArray>();
DfgSpliceArray* const cp = vtxp2clonep.at(vp)->as<DfgSpliceArray>();
vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
if (const DfgVertex* const srcp = edge.sourcep()) {
cp->addDriver(vp->driverFileLine(i), //
vp->driverLo(i), //
vtxp2clonep.at(srcp));
}
});
break;
}
case VDfgType::atSplicePacked: { case VDfgType::atSplicePacked: {
const DfgSplicePacked* const vp = vtx.as<DfgSplicePacked>(); const DfgVertexSplice* const vp = vtx.as<DfgVertexSplice>();
DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as<DfgSplicePacked>(); DfgVertexSplice* const cp = vtxp2clonep.at(vp)->as<DfgVertexSplice>();
vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { vp->foreachDriver([&](const DfgVertex& src, uint32_t lo, FileLine* flp) {
if (const DfgVertex* const srcVp = edge.sourcep()) { cp->addDriver(vtxp2clonep.at(&src), lo, flp);
DfgVertex* const srcCp = vtxp2clonep.at(srcVp); return false;
UASSERT_OBJ(!srcCp->is<DfgLogic>(), srcCp, "Cannot clone DfgLogic");
if (srcVp == vp->defaultp()) {
cp->defaultp(srcCp);
} else {
cp->addDriver(vp->driverFileLine(i), vp->driverLo(i), srcCp);
}
}
}); });
break; break;
} }
@ -178,14 +186,8 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
} }
} else { } else {
DfgVertex* const cp = vtxp2clonep.at(&vtx); DfgVertex* const cp = vtxp2clonep.at(&vtx);
const auto oSourceEdges = vtx.sourceEdges(); for (size_t i = 0; i < vtx.nInputs(); ++i) {
auto cSourceEdges = cp->sourceEdges(); cp->inputp(i, vtxp2clonep.at(vtx.inputp(i)));
UASSERT_OBJ(oSourceEdges.second == cSourceEdges.second, &vtx,
"Mismatched source count");
for (size_t i = 0; i < oSourceEdges.second; ++i) {
if (const DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) {
cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp));
}
} }
} }
} }
@ -210,9 +212,12 @@ void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
// Variabels that are present in 'this', make them use the DfgVertexVar in 'this'. // 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->nodep()->user2u().to<DfgVertexVar*>()) {
DfgVertex* const srcp = vtxp->srcp(); DfgVertex* const srcp = vtxp->srcp();
UASSERT_OBJ(!srcp || !altp->srcp(), vtxp, "At most one alias should be driven"); DfgVertex* const defaultp = vtxp->defaultp();
UASSERT_OBJ(!(srcp || defaultp) || (!altp->srcp() && !altp->defaultp()), vtxp,
"At most one alias should be driven");
vtxp->replaceWith(altp); vtxp->replaceWith(altp);
if (srcp) altp->srcp(srcp); if (srcp) altp->srcp(srcp);
if (defaultp) altp->defaultp(defaultp);
VL_DO_DANGLING(vtxp->unlinkDelete(*otherp), vtxp); VL_DO_DANGLING(vtxp->unlinkDelete(*otherp), vtxp);
continue; continue;
} }
@ -285,68 +290,53 @@ static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&
// Dump one DfgVertex in Graphviz format // Dump one DfgVertex in Graphviz format
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) { static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
if (const DfgVertexVar* const varVtxp = vtx.cast<DfgVertexVar>()) {
if (const DfgVarPacked* const varVtxp = vtx.cast<DfgVarPacked>()) {
const AstNode* const nodep = varVtxp->nodep(); const AstNode* const nodep = varVtxp->nodep();
const AstVar* const varp = varVtxp->varp(); const AstVar* const varp = varVtxp->varp();
os << toDotId(vtx); os << toDotId(vtx);
os << " [label=\"" << nodep->prettyName() << '\n'; // Begin attributes
os << cvtToHex(varVtxp) << '\n'; os << " [";
// Begin 'label'
os << "label=\"";
// Name
os << nodep->prettyName();
// Address
os << '\n' << cvtToHex(varVtxp);
// Original variable, if any
if (const AstNode* const tmpForp = varVtxp->tmpForp()) { if (const AstNode* const tmpForp = varVtxp->tmpForp()) {
os << "temporary for: " << tmpForp->prettyName() << "\n"; if (tmpForp != nodep) os << "\ntemporary for: " << tmpForp->prettyName();
} }
// Type and fanout
os << '\n';
varVtxp->dtypep()->dumpSmall(os); varVtxp->dtypep()->dumpSmall(os);
os << " / F" << varVtxp->fanout() << '"'; os << " / F" << varVtxp->fanout();
// End 'label'
if (varp->direction() == VDirection::INPUT) { os << '"';
os << ", shape=box, style=filled, fillcolor=chartreuse2"; // Green // Shape
} else if (varp->direction() == VDirection::OUTPUT) { if (varVtxp->is<DfgVarPacked>()) {
os << ", shape=box, style=filled, fillcolor=cyan2"; // Cyan
} else if (varp->direction() == VDirection::INOUT) {
os << ", shape=box, style=filled, fillcolor=darkorchid2"; // Purple
} else if (varVtxp->hasExtRefs()) {
os << ", shape=box, style=filled, fillcolor=firebrick2"; // Red
} else if (varVtxp->hasModRefs()) {
os << ", shape=box, style=filled, fillcolor=darkorange1"; // Orange
} else if (varVtxp->hasDfgRefs()) {
os << ", shape=box, style=filled, fillcolor=gold2"; // Yellow
} else if (varVtxp->tmpForp()) {
os << ", shape=box, style=filled, fillcolor=gray80";
} else {
os << ", shape=box"; os << ", shape=box";
} } else if (varVtxp->is<DfgVarArray>()) {
os << "]\n";
return;
}
if (const DfgVarArray* const arrVtxp = vtx.cast<DfgVarArray>()) {
const AstNode* const nodep = arrVtxp->nodep();
const AstVar* const varp = arrVtxp->varp();
os << toDotId(vtx);
os << " [label=\"" << nodep->prettyName() << '\n';
os << cvtToHex(arrVtxp) << '\n';
if (const AstNode* const tmpForp = arrVtxp->tmpForp()) {
os << "temporary for: " << tmpForp->prettyName() << "\n";
}
arrVtxp->dtypep()->dumpSmall(os);
os << " / F" << arrVtxp->fanout() << '"';
if (varp->direction() == VDirection::INPUT) {
os << ", shape=box3d, style=filled, fillcolor=chartreuse2"; // Green
} else if (varp->direction() == VDirection::OUTPUT) {
os << ", shape=box3d, style=filled, fillcolor=cyan2"; // Cyan
} else if (varp->direction() == VDirection::INOUT) {
os << ", shape=box3d, style=filled, fillcolor=darkorchid2"; // Purple
} else if (arrVtxp->hasExtRefs()) {
os << ", shape=box3d, style=filled, fillcolor=firebrick2"; // Red
} else if (arrVtxp->hasModRefs()) {
os << ", shape=box3d, style=filled, fillcolor=darkorange1"; // Orange
} else if (arrVtxp->hasDfgRefs()) {
os << ", shape=box3d, style=filled, fillcolor=gold2"; // Yellow
} else if (arrVtxp->tmpForp()) {
os << ", shape=box3d, style=filled, fillcolor=gray80";
} else {
os << ", shape=box3d"; os << ", shape=box3d";
} else {
varVtxp->v3fatalSrc("Unhandled DfgVertexVar sub-type"); // LCOV_EXCL_LINE
} }
// Color
if (varp->direction() == VDirection::INPUT) {
os << ", style=filled, fillcolor=chartreuse2"; // Green
} else if (varp->direction() == VDirection::OUTPUT) {
os << ", style=filled, fillcolor=cyan2"; // Cyan
} else if (varp->direction() == VDirection::INOUT) {
os << ", style=filled, fillcolor=darkorchid2"; // Purple
} else if (varVtxp->hasExtRefs()) {
os << ", style=filled, fillcolor=firebrick2"; // Red
} else if (varVtxp->hasModRefs()) {
os << ", style=filled, fillcolor=darkorange1"; // Orange
} else if (varVtxp->hasDfgRefs()) {
os << ", style=filled, fillcolor=gold2"; // Yellow
} else if (varVtxp->tmpForp()) {
os << ", style=filled, fillcolor=gray95";
}
// End attributes
os << "]\n"; os << "]\n";
return; return;
} }
@ -405,8 +395,12 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
os << toDotId(vtx); os << toDotId(vtx);
std::stringstream ss; std::stringstream ss;
V3EmitV::debugVerilogForTree(logicp->nodep(), ss); V3EmitV::debugVerilogForTree(logicp->nodep(), ss);
std::string str = ss.str();
str = VString::quoteBackslash(str);
str = VString::quoteAny(str, '"', '\\');
str = VString::replaceSubstr(str, "\n", "\\l");
os << " [label=\""; os << " [label=\"";
os << VString::replaceSubstr(VString::replaceSubstr(ss.str(), "\n", "\\l"), "\"", "\\\""); os << str;
os << "\\n" << cvtToHex(&vtx); os << "\\n" << cvtToHex(&vtx);
os << "\"\n"; os << "\"\n";
os << ", shape=box, style=\"rounded,filled\", fillcolor=cornsilk, nojustify=true"; os << ", shape=box, style=\"rounded,filled\", fillcolor=cornsilk, nojustify=true";
@ -427,17 +421,6 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
os << "]\n"; os << "]\n";
} }
// Dump one DfgEdge in Graphviz format
static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, size_t idx) {
UASSERT(edge.sourcep(), "Can't dump unconnected DfgEdge");
const DfgVertex& sink = *edge.sinkp(); // sink is never nullptr
os << toDotId(*edge.sourcep()) << " -> " << toDotId(sink);
if (sink.arity() > 1 || sink.is<DfgVertexSplice>()) {
os << " [headlabel=\"" << sink.srcName(idx) << "\"]";
}
os << '\n';
}
void DfgGraph::dumpDot(std::ostream& os, const std::string& label, void DfgGraph::dumpDot(std::ostream& os, const std::string& label,
std::function<bool(const DfgVertex&)> p) const { std::function<bool(const DfgVertex&)> p) const {
// This generates a graphviz dump, https://www.graphviz.org // This generates a graphviz dump, https://www.graphviz.org
@ -473,12 +456,16 @@ void DfgGraph::dumpDot(std::ostream& os, const std::string& label,
dumpDotVertex(os, vtx); dumpDotVertex(os, vtx);
}); });
// Emit all edges // Emit all edges
forEachVertex([&](const DfgVertex& vtx) { // forEachVertex([&](const DfgVertex& vtx) {
if (!p(vtx)) return; if (!p(vtx)) return;
vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { // for (size_t i = 0; i < vtx.nInputs(); ++i) {
if (!e.sourcep() || !p(*e.sourcep())) return; DfgVertex* const srcp = vtx.inputp(i);
dumpDotEdge(os, e, i); if (!srcp) continue;
}); if (!p(*srcp)) continue;
os << toDotId(*srcp) << " -> " << toDotId(vtx);
os << " [headlabel=\"" << vtx.srcName(i) << "\"]";
os << '\n';
}
}); });
// Footer // Footer
@ -527,9 +514,15 @@ dfgGraphCollectCone(const std::vector<const DfgVertex*>& vtxps) {
if (!resp->insert(vtxp).second) continue; if (!resp->insert(vtxp).second) continue;
// Enqueue all siblings of this vertex. // Enqueue all siblings of this vertex.
if VL_CONSTEXPR_CXX17 (T_SinksNotSources) { if VL_CONSTEXPR_CXX17 (T_SinksNotSources) {
vtxp->forEachSink([&](const DfgVertex& sink) { queue.push_back(&sink); }); vtxp->foreachSink([&](const DfgVertex& sink) {
queue.push_back(&sink);
return false;
});
} else { } else {
vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); }); vtxp->foreachSource([&](const DfgVertex& src) {
queue.push_back(&src);
return false;
});
} }
} }
// Done // Done
@ -546,73 +539,8 @@ DfgGraph::sinkCone(const std::vector<const DfgVertex*>& vtxps) const {
return dfgGraphCollectCone<true>(vtxps); return dfgGraphCollectCone<true>(vtxps);
} }
// predicate for supported data types
static bool dfgGraphIsSupportedDTypePacked(const AstNodeDType* dtypep) {
dtypep = dtypep->skipRefp();
if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) {
return typep->keyword().isIntNumeric();
}
if (const AstPackArrayDType* const typep = VN_CAST(dtypep, PackArrayDType)) {
return dfgGraphIsSupportedDTypePacked(typep->subDTypep());
}
if (const AstNodeUOrStructDType* const typep = VN_CAST(dtypep, NodeUOrStructDType)) {
return typep->packed();
}
return false;
}
bool DfgGraph::isSupported(const AstNodeDType* dtypep) {
dtypep = dtypep->skipRefp();
// Support 1 dimensional unpacked arrays of packed types
if (const AstUnpackArrayDType* const typep = VN_CAST(dtypep, UnpackArrayDType)) {
return dfgGraphIsSupportedDTypePacked(typep->subDTypep());
}
// Support packed types
return dfgGraphIsSupportedDTypePacked(dtypep);
}
//------------------------------------------------------------------------------
// DfgEdge
//------------------------------------------------------------------------------
void DfgEdge::unlinkSource() {
if (!m_sourcep) return;
#ifdef VL_DEBUG
{
DfgEdge* currp = m_sourcep->m_sinksp;
while (currp) {
if (currp == this) break;
currp = currp->m_nextp;
}
UASSERT(currp, "'m_sourcep' does not have this edge as sink");
}
#endif
// Relink pointers of predecessor and successor
if (m_prevp) m_prevp->m_nextp = m_nextp;
if (m_nextp) m_nextp->m_prevp = m_prevp;
// If head of list in source, update source's head pointer
if (m_sourcep->m_sinksp == this) m_sourcep->m_sinksp = m_nextp;
// Mark source as unconnected
m_sourcep = nullptr;
// Clear links. This is not strictly necessary, but might catch bugs.
m_prevp = nullptr;
m_nextp = nullptr;
}
void DfgEdge::relinkSource(DfgVertex* newSourcep) {
// Unlink current source, if any
unlinkSource();
// Link new source
m_sourcep = newSourcep;
// Prepend to sink list in source
m_nextp = newSourcep->m_sinksp;
if (m_nextp) m_nextp->m_prevp = this;
newSourcep->m_sinksp = this;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// DfgVertex // DfgVertex
//------------------------------------------------------------------------------
DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep) DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: m_filelinep{flp} : m_filelinep{flp}
@ -621,12 +549,6 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType*
dfg.addVertex(*this); dfg.addVertex(*this);
} }
DfgVertex::~DfgVertex() {}
bool DfgVertex::selfEquals(const DfgVertex& that) const { return true; }
V3Hash DfgVertex::selfHash() const { return V3Hash{}; }
bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const { bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
// If same vertex, then equal // If same vertex, then equal
if (this == &that) return true; if (this == &that) return true;
@ -638,13 +560,7 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
if (this->dtypep() != that.dtypep()) return false; if (this->dtypep() != that.dtypep()) return false;
// If different number of inputs, then not equal // If different number of inputs, then not equal
auto thisPair = this->sourceEdges(); if (this->nInputs() != that.nInputs()) return false;
const DfgEdge* const thisSrcEdgesp = thisPair.first;
const size_t thisArity = thisPair.second;
auto thatPair = that.sourceEdges();
const DfgEdge* const thatSrcEdgesp = thatPair.first;
const size_t thatArity = thatPair.second;
if (thisArity != thatArity) return false;
// Check vertex specifics // Check vertex specifics
if (!this->selfEquals(that)) return false; if (!this->selfEquals(that)) return false;
@ -655,16 +571,17 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
// Note: the recursive invocation can cause a re-hash but that will not invalidate references // Note: the recursive invocation can cause a re-hash but that will not invalidate references
uint8_t& result = cache[key]; uint8_t& result = cache[key];
if (!result) { if (!result) {
result = 2; // Assume equals const bool equal = [&]() {
for (size_t i = 0; i < thisArity; ++i) { for (size_t i = 0; i < nInputs(); ++i) {
const DfgVertex* const thisSrcVtxp = thisSrcEdgesp[i].m_sourcep; const DfgVertex* const ap = this->inputp(i);
const DfgVertex* const thatSrcVtxp = thatSrcEdgesp[i].m_sourcep; const DfgVertex* const bp = that.inputp(i);
if (thisSrcVtxp == thatSrcVtxp) continue; if (!ap && !bp) continue;
if (!thisSrcVtxp || !thatSrcVtxp || !thisSrcVtxp->equals(*thatSrcVtxp, cache)) { if (!ap || !bp) return false;
result = 1; // Mark not equal if (!ap->equals(*bp, cache)) return false;
break;
} }
} return true;
}();
result = (static_cast<uint8_t>(equal) << 1) | 1;
} }
return result >> 1; return result >> 1;
} }
@ -678,17 +595,11 @@ V3Hash DfgVertex::hash() {
// variables, which we rely on. // variables, which we rely on.
if (!is<DfgVertexVar>()) { if (!is<DfgVertexVar>()) {
hash += m_type; hash += m_type;
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep(), UnpackArrayDType)) { hash += size();
hash += adtypep->elementsConst(); foreachSource([&](DfgVertex& vtx) {
// TODO: maybe include sub-dtype, but not hugely important at the moment hash += vtx.hash();
} else { return false;
hash += width(); });
}
const auto pair = sourceEdges();
const DfgEdge* const edgesp = pair.first;
const size_t nEdges = pair.second;
// Sources must always be connected in well-formed graphs
for (size_t i = 0; i < nEdges; ++i) hash += edgesp[i].m_sourcep->hash();
} }
result = hash; result = hash;
} }
@ -697,7 +608,10 @@ V3Hash DfgVertex::hash() {
uint32_t DfgVertex::fanout() const { uint32_t DfgVertex::fanout() const {
uint32_t result = 0; uint32_t result = 0;
forEachSinkEdge([&](const DfgEdge&) { ++result; }); foreachSink([&](const DfgVertex&) {
++result;
return false;
});
return result; return result;
} }
@ -708,51 +622,52 @@ DfgVertexVar* DfgVertex::getResultVar() {
// Inspect existing variables written by this vertex, and choose one // Inspect existing variables written by this vertex, and choose one
DfgVertexVar* resp = nullptr; DfgVertexVar* resp = nullptr;
// cppcheck-has-bug-suppress constParameter // cppcheck-has-bug-suppress constParameter
this->forEachSink([&resp](DfgVertex& sink) { this->foreachSink([&resp](DfgVertex& sink) {
DfgVertexVar* const varp = sink.cast<DfgVertexVar>(); DfgVertexVar* const varp = sink.cast<DfgVertexVar>();
if (!varp) return; if (!varp) return false;
// First variable found // First variable found
if (!resp) { if (!resp) {
resp = varp; resp = varp;
return; return false;
} }
// Prefer those variables that must be kept anyway // Prefer those variables that must be kept anyway
if (resp->hasExtRefs() != varp->hasExtRefs()) { if (resp->hasExtRefs() != varp->hasExtRefs()) {
if (!resp->hasExtRefs()) resp = varp; if (!resp->hasExtRefs()) resp = varp;
return; return false;
} }
if (resp->hasModWrRefs() != varp->hasModWrRefs()) { if (resp->hasModWrRefs() != varp->hasModWrRefs()) {
if (!resp->hasModWrRefs()) resp = varp; if (!resp->hasModWrRefs()) resp = varp;
return; return false;
} }
if (resp->hasDfgRefs() != varp->hasDfgRefs()) { if (resp->hasDfgRefs() != varp->hasDfgRefs()) {
if (!resp->hasDfgRefs()) resp = varp; if (!resp->hasDfgRefs()) resp = varp;
return; return false;
} }
// Prefer those that already have module references // Prefer those that already have module references
if (resp->hasModRdRefs() != varp->hasModRdRefs()) { if (resp->hasModRdRefs() != varp->hasModRdRefs()) {
if (!resp->hasModRdRefs()) resp = varp; if (!resp->hasModRdRefs()) resp = varp;
return; return false;
} }
// Prefer real variabels over temporaries // Prefer real variabels over temporaries
if (!resp->tmpForp() != !varp->tmpForp()) { if (!resp->tmpForp() != !varp->tmpForp()) {
if (resp->tmpForp()) resp = varp; if (resp->tmpForp()) resp = varp;
return; return false;
} }
// Prefer the earlier one in source order // Prefer the earlier one in source order
const FileLine& oldFlp = *(resp->fileline()); const FileLine& oldFlp = *(resp->fileline());
const FileLine& newFlp = *(varp->fileline()); const FileLine& newFlp = *(varp->fileline());
if (const int cmp = oldFlp.operatorCompare(newFlp)) { if (const int cmp = oldFlp.operatorCompare(newFlp)) {
if (cmp > 0) resp = varp; if (cmp > 0) resp = varp;
return; return false;
} }
// Prefer the one with the lexically smaller name // Prefer the one with the lexically smaller name
if (const int cmp = resp->nodep()->name().compare(varp->nodep()->name())) { if (const int cmp = resp->nodep()->name().compare(varp->nodep()->name())) {
if (cmp > 0) resp = varp; if (cmp > 0) resp = varp;
return; return false;
} }
// 'resp' and 'varp' are all the same, keep using the existing 'resp' // 'resp' and 'varp' are all the same, keep using the existing 'resp'
return false;
}); });
return resp; return resp;
} }
@ -777,14 +692,16 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED
resultr = reinterpret_cast<AstScope*>(1); resultr = reinterpret_cast<AstScope*>(1);
// Find scope based on sources, falling back on the root scope // Find scope based on sources, falling back on the root scope
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep(); AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
AstScope* foundp = rootp; AstScope* foundp = nullptr;
const auto edges = sourceEdges(); foreachSource([&](DfgVertex& src) {
for (size_t i = 0; i < edges.second; ++i) { AstScope* const scp = src.scopep(cache, true);
const DfgEdge& edge = edges.first[i]; if (scp != rootp) {
foundp = edge.sourcep()->scopep(cache, true); foundp = scp;
if (foundp != rootp) break; return true;
} }
resultr = foundp; return false;
});
resultr = foundp ? foundp : rootp;
} }
// Die on a graph circular through operation vertices // Die on a graph circular through operation vertices
@ -796,80 +713,15 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED
} }
void DfgVertex::unlinkDelete(DfgGraph& dfg) { void DfgVertex::unlinkDelete(DfgGraph& dfg) {
// Unlink source edges
forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
// Unlink sink edges // Unlink sink edges
forEachSinkEdge([](DfgEdge& edge) { edge.unlinkSource(); }); while (!m_sinks.empty()) m_sinks.frontp()->unlinkSrcp();
// Remove from graph // Remove from graph
dfg.removeVertex(*this); dfg.removeVertex(*this);
// Delete // Delete - this will unlink sources
delete this; delete this;
} }
void DfgVertex::replaceWith(DfgVertex* newSorucep) {
while (m_sinksp) m_sinksp->relinkSource(newSorucep);
}
//------------------------------------------------------------------------------
// Vertex classes
//------------------------------------------------------------------------------
// DfgConst ----------
bool DfgConst::selfEquals(const DfgVertex& that) const {
return num().isCaseEq(that.as<DfgConst>()->num());
}
V3Hash DfgConst::selfHash() const { return num().toHash(); }
// DfgSel ----------
bool DfgSel::selfEquals(const DfgVertex& that) const { return lsb() == that.as<DfgSel>()->lsb(); }
V3Hash DfgSel::selfHash() const { return V3Hash{lsb()}; }
// DfgVertexSplice ----------
bool DfgVertexSplice::selfEquals(const DfgVertex& that) const {
const DfgVertexSplice* const thatp = that.as<DfgVertexSplice>();
if (!defaultp() != !thatp->defaultp()) return false;
const size_t arity = this->arity();
for (size_t i = 0; i < arity; ++i) {
if (i == 0 && defaultp()) continue;
if (driverLo(i) != thatp->driverLo(i)) return false;
}
return true;
}
V3Hash DfgVertexSplice::selfHash() const {
V3Hash hash;
const size_t arity = this->arity();
for (size_t i = 0; i < arity; ++i) {
if (i == 0 && defaultp()) continue;
hash += driverLo(i);
}
return hash;
}
// DfgVertexVar ----------
bool DfgVertexVar::selfEquals(const DfgVertex& that) const {
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 += nodep()->name();
hash += varp()->varType();
return hash;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// DfgVisitor // DfgVisitor
//------------------------------------------------------------------------------
#include "V3Dfg__gen_visitor_defns.h" // From ./astgen #include "V3Dfg__gen_visitor_defns.h" // From ./astgen

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,7 @@ class AstToDfgVisitor final : public VNVisitor {
// STATE // STATE
DfgGraph& m_dfg; // The graph being built DfgGraph& m_dfg; // The graph being built
V3DfgAstToDfgContext& m_ctx; // The context for stats V3DfgAstToDfgContext& m_ctx; // The context for stats
AstScope* m_scopep = nullptr; // The current scope, iff T_Scoped
// METHODS // METHODS
static Variable* getTarget(const AstVarRef* refp) { static Variable* getTarget(const AstVarRef* refp) {
@ -104,7 +105,7 @@ class AstToDfgVisitor final : public VNVisitor {
if (VN_IS(vrefp, VarXRef)) return true; if (VN_IS(vrefp, VarXRef)) return true;
if (vrefp->access().isReadOnly()) return false; if (vrefp->access().isReadOnly()) return false;
Variable* const varp = getTarget(VN_AS(vrefp, VarRef)); Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
if (!DfgGraph::isSupported(varp)) return true; if (!V3Dfg::isSupported(varp)) return true;
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp)); if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
return false; return false;
}); });
@ -126,7 +127,7 @@ class AstToDfgVisitor final : public VNVisitor {
if (VN_IS(vrefp, VarXRef)) return true; if (VN_IS(vrefp, VarXRef)) return true;
if (vrefp->access().isWriteOnly()) return false; if (vrefp->access().isWriteOnly()) return false;
Variable* const varp = getTarget(VN_AS(vrefp, VarRef)); Variable* const varp = getTarget(VN_AS(vrefp, VarRef));
if (!DfgGraph::isSupported(varp)) return true; if (!V3Dfg::isSupported(varp)) return true;
if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp)); if (!varp->user3SetOnce()) resp->emplace_back(getVarVertex(varp));
return false; return false;
}); });
@ -152,7 +153,7 @@ class AstToDfgVisitor final : public VNVisitor {
std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}}; std::unique_ptr<std::vector<DfgVertexVar*>> resp{new std::vector<DfgVertexVar*>{}};
resp->reserve(varps->size()); resp->reserve(varps->size());
for (Variable* const varp : *varps) { for (Variable* const varp : *varps) {
if (!DfgGraph::isSupported(varp)) { if (!V3Dfg::isSupported(varp)) {
++m_ctx.m_nonRepVar; ++m_ctx.m_nonRepVar;
return nullptr; return nullptr;
} }
@ -188,7 +189,7 @@ class AstToDfgVisitor final : public VNVisitor {
const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherRead(nodep); const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherRead(nodep);
if (!iVarpsp) return false; if (!iVarpsp) return false;
// Create the DfgLogic // Create the DfgLogic
DfgLogic* const logicp = new DfgLogic{m_dfg, nodep}; DfgLogic* const logicp = new DfgLogic{m_dfg, nodep, m_scopep};
// Connect it up // Connect it up
connect(*logicp, *iVarpsp, *oVarpsp); connect(*logicp, *iVarpsp, *oVarpsp);
// Done // Done
@ -218,7 +219,7 @@ class AstToDfgVisitor final : public VNVisitor {
const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherLive(*cfgp); const std::unique_ptr<std::vector<DfgVertexVar*>> iVarpsp = gatherLive(*cfgp);
if (!iVarpsp) return false; if (!iVarpsp) return false;
// Create the DfgLogic // Create the DfgLogic
DfgLogic* const logicp = new DfgLogic{m_dfg, nodep, std::move(cfgp)}; DfgLogic* const logicp = new DfgLogic{m_dfg, nodep, m_scopep, std::move(cfgp)};
// Connect it up // Connect it up
connect(*logicp, *iVarpsp, *oVarpsp); connect(*logicp, *iVarpsp, *oVarpsp);
// Done // Done
@ -241,7 +242,11 @@ class AstToDfgVisitor final : public VNVisitor {
} }
} }
void visit(AstTopScope* nodep) override { iterate(nodep->scopep()); } void visit(AstTopScope* nodep) override { iterate(nodep->scopep()); }
void visit(AstScope* nodep) override { iterateChildren(nodep); } void visit(AstScope* nodep) override {
VL_RESTORER(m_scopep);
m_scopep = nodep;
iterateChildren(nodep);
}
void visit(AstActive* nodep) override { void visit(AstActive* nodep) override {
if (nodep->hasCombo()) { if (nodep->hasCombo()) {
iterateChildren(nodep); iterateChildren(nodep);
@ -268,6 +273,8 @@ class AstToDfgVisitor final : public VNVisitor {
, m_ctx{ctx} { , m_ctx{ctx} {
iterate(&root); iterate(&root);
} }
VL_UNCOPYABLE(AstToDfgVisitor);
VL_UNMOVABLE(AstToDfgVisitor);
public: public:
static void apply(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) { static void apply(DfgGraph& dfg, RootType& root, V3DfgAstToDfgContext& ctx) {

View File

@ -68,6 +68,7 @@ class TraceDriver final : public DfgVisitor {
const bool m_aggressive; // Trace aggressively, creating intermediate ops const bool m_aggressive; // Trace aggressively, creating intermediate ops
uint32_t m_lsb = 0; // LSB to extract from the currently visited Vertex uint32_t m_lsb = 0; // LSB to extract from the currently visited Vertex
uint32_t m_msb = 0; // MSB to extract from the currently visited Vertex uint32_t m_msb = 0; // MSB to extract from the currently visited Vertex
DfgVertex* m_defaultp = nullptr; // When tracing a variable, this is its 'defaultp', if any
// Result of tracing the currently visited Vertex. Use SET_RESULT below! // Result of tracing the currently visited Vertex. Use SET_RESULT below!
DfgVertex* m_resp = nullptr; DfgVertex* m_resp = nullptr;
std::vector<DfgVertex*> m_newVtxps; // New vertices created during the traversal std::vector<DfgVertex*> m_newVtxps; // New vertices created during the traversal
@ -108,7 +109,7 @@ class TraceDriver final : public DfgVisitor {
"Should only create Const, Sel, Concat, Exend Vertices without aggressive mode"); "Should only create Const, Sel, Concat, Exend Vertices without aggressive mode");
if VL_CONSTEXPR_CXX17 (std::is_same<DfgConst, Vertex>::value) { if VL_CONSTEXPR_CXX17 (std::is_same<DfgConst, Vertex>::value) {
DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width}; DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width, 0};
vtxp->template setUser<uint64_t>(0); vtxp->template setUser<uint64_t>(0);
m_newVtxps.emplace_back(vtxp); m_newVtxps.emplace_back(vtxp);
return reinterpret_cast<Vertex*>(vtxp); return reinterpret_cast<Vertex*>(vtxp);
@ -118,7 +119,7 @@ class TraceDriver final : public DfgVisitor {
// Vertex is DfgConst, in which case this code is unreachable ... // Vertex is DfgConst, in which case this code is unreachable ...
using Vtx = typename std::conditional<std::is_same<DfgConst, Vertex>::value, DfgSel, using Vtx = typename std::conditional<std::is_same<DfgConst, Vertex>::value, DfgSel,
Vertex>::type; Vertex>::type;
AstNodeDType* const dtypep = DfgGraph::dtypePacked(width); AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep}; Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep};
vtxp->template setUser<uint64_t>(0); vtxp->template setUser<uint64_t>(0);
m_newVtxps.emplace_back(vtxp); m_newVtxps.emplace_back(vtxp);
@ -158,9 +159,13 @@ class TraceDriver final : public DfgVisitor {
// Trace the vertex // Trace the vertex
onStackr = true; onStackr = true;
if (vtxp->getUser<uint64_t>() != m_component) { // If the currently traced vertex is in a different component, then we
// If the currently traced vertex is in a different component, // found what we were looking for. However, keep going past a splice,
// then we found what we were looking for. // or a unit as they cannot be use directly (they must always feed into
// a variable, so we can't make them drive arbitrary logic)
if (vtxp->getUser<uint64_t>() != m_component //
&& !vtxp->is<DfgVertexSplice>() //
&& !vtxp->is<DfgUnitArray>()) {
if (msb != vtxp->width() - 1 || lsb != 0) { if (msb != vtxp->width() - 1 || lsb != 0) {
// Apply a Sel to extract the relevant bits if only a part is needed // Apply a Sel to extract the relevant bits if only a part is needed
DfgSel* const selp = make<DfgSel>(vtxp, msb - lsb + 1); DfgSel* const selp = make<DfgSel>(vtxp, msb - lsb + 1);
@ -294,30 +299,25 @@ class TraceDriver final : public DfgVisitor {
, m_msb{msb} {} , m_msb{msb} {}
}; };
std::vector<Driver> drivers; std::vector<Driver> drivers;
DfgVertex* const defaultp = vtxp->defaultp();
// Look at all the drivers, one might cover the whole range, but also gathe all drivers // Look at all the drivers, one might cover the whole range, but also gathe all drivers
const auto pair = vtxp->sourceEdges(); bool tryWholeDefault = m_defaultp;
bool tryWholeDefault = defaultp; const bool done = vtxp->foreachDriver([&](DfgVertex& src, uint32_t lsb) {
for (size_t i = 0; i < pair.second; ++i) { const uint32_t msb = lsb + src.width() - 1;
DfgVertex* const srcp = pair.first[i].sourcep(); drivers.emplace_back(&src, lsb, msb);
if (srcp == defaultp) continue;
const uint32_t lsb = vtxp->driverLo(i);
const uint32_t msb = lsb + srcp->width() - 1;
drivers.emplace_back(srcp, lsb, msb);
// Check if this driver covers any of the bits, then we can't use whole default // Check if this driver covers any of the bits, then we can't use whole default
if (m_msb >= lsb && msb >= m_lsb) tryWholeDefault = false; if (m_msb >= lsb && msb >= m_lsb) tryWholeDefault = false;
// If it does not cover the whole searched bit range, move on // If it does not cover the whole searched bit range, move on
if (m_lsb < lsb || msb < m_msb) continue; if (m_lsb < lsb || msb < m_msb) return false;
// Driver covers whole search range, trace that and we are done // Driver covers whole search range, trace that and we are done
SET_RESULT(trace(srcp, m_msb - lsb, m_lsb - lsb)); SET_RESULT(trace(&src, m_msb - lsb, m_lsb - lsb));
return; return true;
} });
if (done) return;
// Trace the default driver if no other drivers cover the searched range // Trace the default driver if no other drivers cover the searched range
if (defaultp && tryWholeDefault) { if (tryWholeDefault) {
SET_RESULT(trace(defaultp, m_msb, m_lsb)); SET_RESULT(trace(m_defaultp, m_msb, m_lsb));
return; return;
} }
@ -336,8 +336,8 @@ class TraceDriver final : public DfgVisitor {
if (driver.m_lsb > m_msb) break; if (driver.m_lsb > m_msb) break;
// Gap below this driver, trace default to fill it // Gap below this driver, trace default to fill it
if (driver.m_lsb > m_lsb) { if (driver.m_lsb > m_lsb) {
if (!defaultp) return; if (!m_defaultp) return;
DfgVertex* const termp = trace(defaultp, driver.m_lsb - 1, m_lsb); DfgVertex* const termp = trace(m_defaultp, driver.m_lsb - 1, m_lsb);
if (!termp) return; if (!termp) return;
termps.emplace_back(termp); termps.emplace_back(termp);
m_lsb = driver.m_lsb; m_lsb = driver.m_lsb;
@ -351,8 +351,8 @@ class TraceDriver final : public DfgVisitor {
m_lsb = lim + 1; m_lsb = lim + 1;
} }
if (m_msb >= m_lsb) { if (m_msb >= m_lsb) {
if (!defaultp) return; if (!m_defaultp) return;
DfgVertex* const termp = trace(defaultp, m_msb, m_lsb); DfgVertex* const termp = trace(m_defaultp, m_msb, m_lsb);
if (!termp) return; if (!termp) return;
termps.emplace_back(termp); termps.emplace_back(termp);
} }
@ -374,6 +374,8 @@ class TraceDriver final : public DfgVisitor {
} }
void visit(DfgVarPacked* vtxp) override { void visit(DfgVarPacked* vtxp) override {
VL_RESTORER(m_defaultp);
m_defaultp = vtxp->defaultp();
if (DfgVertex* const srcp = vtxp->srcp()) { if (DfgVertex* const srcp = vtxp->srcp()) {
SET_RESULT(trace(srcp, m_msb, m_lsb)); SET_RESULT(trace(srcp, m_msb, m_lsb));
return; return;
@ -719,17 +721,30 @@ class IndependentBits final : public DfgVisitor {
void visit(DfgSplicePacked* vtxp) override { void visit(DfgSplicePacked* vtxp) override {
// Combine the masks of all drivers // Combine the masks of all drivers
V3Number& m = MASK(vtxp); V3Number& m = MASK(vtxp);
DfgVertex* const defaultp = vtxp->defaultp(); vtxp->foreachDriver([&](DfgVertex& src, uint32_t lo) {
if (defaultp) m = MASK(defaultp); m.opSelInto(MASK(&src), lo, src.width());
vtxp->forEachSourceEdge([&](DfgEdge& edge, size_t i) { return false;
const DfgVertex* const srcp = edge.sourcep();
if (srcp == defaultp) return;
m.opSelInto(MASK(srcp), vtxp->driverLo(i), srcp->width());
}); });
} }
void visit(DfgVarPacked* vtxp) override { void visit(DfgVarPacked* vtxp) override {
if (DfgVertex* const srcp = vtxp->srcp()) MASK(vtxp) = MASK(srcp); DfgVertex* const srcp = vtxp->srcp();
if (srcp && srcp->is<DfgSpliceArray>()) return;
V3Number& m = MASK(vtxp);
DfgVertex* const defaultp = vtxp->defaultp();
if (defaultp) m = MASK(defaultp);
if (!srcp) return;
if (DfgSplicePacked* const splicep = srcp->cast<DfgSplicePacked>()) {
splicep->foreachDriver([&](DfgVertex& src, uint32_t lo) {
m.opSelInto(MASK(&src), lo, src.width());
return false;
});
return;
}
m = MASK(srcp);
} }
void visit(DfgArraySel* vtxp) override { void visit(DfgArraySel* vtxp) override {
@ -924,8 +939,9 @@ class IndependentBits final : public DfgVisitor {
if (VN_IS(currp->dtypep(), UnpackArrayDType)) { if (VN_IS(currp->dtypep(), UnpackArrayDType)) {
// For an unpacked array vertex, just enque it's sinks. // For an unpacked array vertex, just enque it's sinks.
// (There can be no loops through arrays directly) // (There can be no loops through arrays directly)
currp->forEachSink([&](DfgVertex& vtx) { currp->foreachSink([&](DfgVertex& vtx) {
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx); if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
return false;
}); });
continue; continue;
} }
@ -940,8 +956,9 @@ class IndependentBits final : public DfgVisitor {
// If mask changed, enqueue sinks // If mask changed, enqueue sinks
if (!prevMask.isCaseEq(maskCurr)) { if (!prevMask.isCaseEq(maskCurr)) {
currp->forEachSink([&](DfgVertex& vtx) { currp->foreachSink([&](DfgVertex& vtx) {
if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx); if (vtx.getUser<uint64_t>() == m_component) workList.emplace_back(&vtx);
return false;
}); });
// Check the mask only ever contrects (no bit goes 0 -> 1) // Check the mask only ever contrects (no bit goes 0 -> 1)
@ -977,20 +994,21 @@ class FixUpSelDrivers final {
static size_t fixUpSelSinks(DfgGraph& dfg, DfgVertex* vtxp) { static size_t fixUpSelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
size_t nImprovements = 0; size_t nImprovements = 0;
const uint64_t component = vtxp->getUser<uint64_t>(); const uint64_t component = vtxp->getUser<uint64_t>();
vtxp->forEachSink([&](DfgVertex& sink) { vtxp->foreachSink([&](DfgVertex& sink) {
// Ignore if sink is not part of same cycle // Ignore if sink is not part of same cycle
if (sink.getUser<uint64_t>() != component) return; if (sink.getUser<uint64_t>() != component) return false;
// Only handle Sel // Only handle Sel
DfgSel* const selp = sink.cast<DfgSel>(); DfgSel* const selp = sink.cast<DfgSel>();
if (!selp) return; if (!selp) return false;
// Try to find the driver of the selected bits outside the cycle // Try to find the driver of the selected bits outside the cycle
DfgVertex* const fixp DfgVertex* const fixp
= TraceDriver::apply(dfg, vtxp, selp->lsb(), selp->width(), false); = TraceDriver::apply(dfg, vtxp, selp->lsb(), selp->width(), false);
if (!fixp) return; if (!fixp) return false;
// Found an out-of-cycle driver. We can replace this sel with that. // Found an out-of-cycle driver. We can replace this sel with that.
selp->replaceWith(fixp); selp->replaceWith(fixp);
selp->unlinkDelete(dfg); selp->unlinkDelete(dfg);
++nImprovements; ++nImprovements;
return false;
}); });
return nImprovements; return nImprovements;
} }
@ -998,25 +1016,25 @@ class FixUpSelDrivers final {
static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) { static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) {
size_t nImprovements = 0; size_t nImprovements = 0;
const uint64_t component = vtxp->getUser<uint64_t>(); const uint64_t component = vtxp->getUser<uint64_t>();
vtxp->forEachSink([&](DfgVertex& sink) { vtxp->foreachSink([&](DfgVertex& sink) {
// Ignore if sink is not part of same cycle // Ignore if sink is not part of same cycle
if (sink.getUser<uint64_t>() != component) return; if (sink.getUser<uint64_t>() != component) return false;
// Only handle ArraySels // Only handle ArraySels
DfgArraySel* const aselp = sink.cast<DfgArraySel>(); DfgArraySel* const aselp = sink.cast<DfgArraySel>();
if (!aselp) return; if (!aselp) return false;
// First, try to fix up the whole word // First, try to fix up the whole word
DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false); if (DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false)) {
if (fixp) {
// Found an out-of-cycle driver for the whole ArraySel // Found an out-of-cycle driver for the whole ArraySel
aselp->replaceWith(fixp); aselp->replaceWith(fixp);
aselp->unlinkDelete(dfg); aselp->unlinkDelete(dfg);
++nImprovements; ++nImprovements;
} else { return false;
// Attempt to fix up piece-wise at Sels applied to the ArraySel
nImprovements += fixUpSelSinks(dfg, aselp);
// Remove if became unused
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
} }
// Attempt to fix up piece-wise at Sels applied to the ArraySel
nImprovements += fixUpSelSinks(dfg, aselp);
// Remove if became unused
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
return false;
}); });
return nImprovements; return nImprovements;
} }
@ -1045,16 +1063,17 @@ class FixUpIndependentRanges final {
// Returns a bitmask set if that bit of 'vtxp' is used (has a sink) // Returns a bitmask set if that bit of 'vtxp' is used (has a sink)
static V3Number computeUsedBits(DfgVertex* vtxp) { static V3Number computeUsedBits(DfgVertex* vtxp) {
V3Number result{vtxp->fileline(), static_cast<int>(vtxp->width()), 0}; V3Number result{vtxp->fileline(), static_cast<int>(vtxp->width()), 0};
vtxp->forEachSink([&result](DfgVertex& sink) { vtxp->foreachSink([&result](DfgVertex& sink) {
// If used via a Sel, mark the selected bits used // If used via a Sel, mark the selected bits used
if (const DfgSel* const selp = sink.cast<DfgSel>()) { if (const DfgSel* const selp = sink.cast<DfgSel>()) {
uint32_t lsb = selp->lsb(); uint32_t lsb = selp->lsb();
uint32_t msb = lsb + selp->width() - 1; uint32_t msb = lsb + selp->width() - 1;
for (uint32_t i = lsb; i <= msb; ++i) result.setBit(i, '1'); for (uint32_t i = lsb; i <= msb; ++i) result.setBit(i, '1');
return; return false;
} }
// Used without a Sel, so all bits are used // Used without a Sel, so all bits are used
result.setAllBits1(); result.setAllBits1();
return false;
}); });
return result; return result;
} }
@ -1097,7 +1116,7 @@ class FixUpIndependentRanges final {
} }
// Fall back on using the part of the variable (if dependent, or trace failed) // Fall back on using the part of the variable (if dependent, or trace failed)
if (!termp) { if (!termp) {
AstNodeDType* const dtypep = DfgGraph::dtypePacked(width); AstNodeDType* const dtypep = V3Dfg::dtypePacked(width);
DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep}; DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep};
// Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp' // Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp'
selp->setUser<uint64_t>(vtxp->getUser<uint64_t>()); selp->setUser<uint64_t>(vtxp->getUser<uint64_t>());
@ -1155,7 +1174,7 @@ class FixUpIndependentRanges final {
nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb); nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb);
} else { } else {
// The range is not used, just use constant 0 as a placeholder // The range is not used, just use constant 0 as a placeholder
DfgConst* const constp = new DfgConst{dfg, flp, msb - lsb + 1}; DfgConst* const constp = new DfgConst{dfg, flp, msb - lsb + 1, 0};
constp->setUser<uint64_t>(0); constp->setUser<uint64_t>(0);
termps.emplace_back(constp); termps.emplace_back(constp);
} }
@ -1178,7 +1197,7 @@ class FixUpIndependentRanges final {
for (size_t i = 1; i < termps.size(); ++i) { for (size_t i = 1; i < termps.size(); ++i) {
DfgVertex* const termp = termps[i]; DfgVertex* const termp = termps[i];
const uint32_t catWidth = replacementp->width() + termp->width(); const uint32_t catWidth = replacementp->width() + termp->width();
AstNodeDType* const dtypep = DfgGraph::dtypePacked(catWidth); AstNodeDType* const dtypep = V3Dfg::dtypePacked(catWidth);
DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep}; DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep};
catp->rhsp(replacementp); catp->rhsp(replacementp);
catp->lhsp(termp); catp->lhsp(termp);
@ -1220,17 +1239,18 @@ public:
} else if (varp->is<DfgVarArray>()) { } else if (varp->is<DfgVarArray>()) {
// For array variables, fix up element-wise // For array variables, fix up element-wise
const uint64_t component = varp->getUser<uint64_t>(); const uint64_t component = varp->getUser<uint64_t>();
varp->forEachSink([&](DfgVertex& sink) { varp->foreachSink([&](DfgVertex& sink) {
// Ignore if sink is not part of same cycle // Ignore if sink is not part of same cycle
if (sink.getUser<uint64_t>() != component) return; if (sink.getUser<uint64_t>() != component) return false;
// Only handle ArraySels with constant index // Only handle ArraySels with constant index
DfgArraySel* const aselp = sink.cast<DfgArraySel>(); DfgArraySel* const aselp = sink.cast<DfgArraySel>();
if (!aselp) return; if (!aselp) return false;
if (!aselp->bitp()->is<DfgConst>()) return; if (!aselp->bitp()->is<DfgConst>()) return false;
// Fix up the word // Fix up the word
nImprovements += fixUpPacked(dfg, aselp); nImprovements += fixUpPacked(dfg, aselp);
// Remove if became unused // Remove if became unused
if (!aselp->hasSinks()) aselp->unlinkDelete(dfg); if (!aselp->hasSinks()) aselp->unlinkDelete(dfg);
return false;
}); });
} }

View File

@ -230,19 +230,19 @@ inline void setOperands(DfgSel* vtxp, DfgVertex* fromp, uint32_t lsb) {
} }
inline void setOperands(DfgVertexUnary* vtxp, DfgVertex* src0p) { // inline void setOperands(DfgVertexUnary* vtxp, DfgVertex* src0p) { //
vtxp->relinkSource<0>(src0p); vtxp->inputp(0, src0p);
} }
inline void setOperands(DfgVertexBinary* vtxp, DfgVertex* src0p, DfgVertex* src1p) { inline void setOperands(DfgVertexBinary* vtxp, DfgVertex* src0p, DfgVertex* src1p) {
vtxp->relinkSource<0>(src0p); vtxp->inputp(0, src0p);
vtxp->relinkSource<1>(src1p); vtxp->inputp(1, src1p);
} }
inline void setOperands(DfgVertexTernary* vtxp, DfgVertex* src0p, DfgVertex* src1p, inline void setOperands(DfgVertexTernary* vtxp, DfgVertex* src0p, DfgVertex* src1p,
DfgVertex* src2p) { DfgVertex* src2p) {
vtxp->relinkSource<0>(src0p); vtxp->inputp(0, src0p);
vtxp->relinkSource<1>(src1p); vtxp->inputp(1, src1p);
vtxp->relinkSource<2>(src2p); vtxp->inputp(2, src2p);
} }
// Get or create (and insert) vertex with given operands // Get or create (and insert) vertex with given operands
@ -265,19 +265,18 @@ inline void cache(CacheSel& cache, DfgSel* vtxp) {
} }
inline void cache(CacheUnary& cache, DfgVertexUnary* vtxp) { inline void cache(CacheUnary& cache, DfgVertexUnary* vtxp) {
DfgVertexUnary*& entrypr = getEntry(cache, vtxp->dtypep(), vtxp->source<0>()); DfgVertexUnary*& entrypr = getEntry(cache, vtxp->dtypep(), vtxp->inputp(0));
if (!entrypr) entrypr = vtxp; if (!entrypr) entrypr = vtxp;
} }
inline void cache(CacheBinary& cache, DfgVertexBinary* vtxp) { inline void cache(CacheBinary& cache, DfgVertexBinary* vtxp) {
DfgVertexBinary*& entrypr DfgVertexBinary*& entrypr = getEntry(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1));
= getEntry(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>());
if (!entrypr) entrypr = vtxp; if (!entrypr) entrypr = vtxp;
} }
inline void cache(CacheTernary& cache, DfgVertexTernary* vtxp) { inline void cache(CacheTernary& cache, DfgVertexTernary* vtxp) {
DfgVertexTernary*& entrypr DfgVertexTernary*& entrypr
= getEntry(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>(), vtxp->source<2>()); = getEntry(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2));
if (!entrypr) entrypr = vtxp; if (!entrypr) entrypr = vtxp;
} }
@ -288,18 +287,17 @@ inline void invalidateByValue(CacheSel& cache, const DfgSel* vtxp) {
} }
inline void invalidateByValue(CacheUnary& cache, const DfgVertexUnary* vtxp) { inline void invalidateByValue(CacheUnary& cache, const DfgVertexUnary* vtxp) {
const auto it = find(cache, vtxp->dtypep(), vtxp->source<0>()); const auto it = find(cache, vtxp->dtypep(), vtxp->inputp(0));
if (it != cache.end() && it->second == vtxp) cache.erase(it); if (it != cache.end() && it->second == vtxp) cache.erase(it);
} }
inline void invalidateByValue(CacheBinary& cache, const DfgVertexBinary* vtxp) { inline void invalidateByValue(CacheBinary& cache, const DfgVertexBinary* vtxp) {
const auto it = find(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>()); const auto it = find(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1));
if (it != cache.end() && it->second == vtxp) cache.erase(it); if (it != cache.end() && it->second == vtxp) cache.erase(it);
} }
inline void invalidateByValue(CacheTernary& cache, const DfgVertexTernary* vtxp) { inline void invalidateByValue(CacheTernary& cache, const DfgVertexTernary* vtxp) {
const auto it const auto it = find(cache, vtxp->dtypep(), vtxp->inputp(0), vtxp->inputp(1), vtxp->inputp(2));
= find(cache, vtxp->dtypep(), vtxp->source<0>(), vtxp->source<1>(), vtxp->source<2>());
if (it != cache.end() && it->second == vtxp) cache.erase(it); if (it != cache.end() && it->second == vtxp) cache.erase(it);
} }

View File

@ -55,7 +55,7 @@ class ColorStronglyConnectedComponents final {
const size_t rootIndex = vtxState.index = ++m_index; const size_t rootIndex = vtxState.index = ++m_index;
// Visit children // Visit children
vtx.forEachSink([&](DfgVertex& child) { vtx.foreachSink([&](DfgVertex& child) {
VertexState& childSatate = child.user<VertexState>(); VertexState& childSatate = child.user<VertexState>();
// If the child has not yet been visited, then continue traversal // If the child has not yet been visited, then continue traversal
if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate); if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate);
@ -63,6 +63,7 @@ class ColorStronglyConnectedComponents final {
if (childSatate.component == UNASSIGNED) { if (childSatate.component == UNASSIGNED) {
if (vtxState.index > childSatate.index) vtxState.index = childSatate.index; if (vtxState.index > childSatate.index) vtxState.index = childSatate.index;
} }
return false;
}); });
if (vtxState.index == rootIndex) { if (vtxState.index == rootIndex) {
@ -73,7 +74,7 @@ class ColorStronglyConnectedComponents final {
|| m_stack.back()->getUser<VertexState>().index < rootIndex; || m_stack.back()->getUser<VertexState>().index < rootIndex;
// We also need a separate component for vertices that drive themselves (which can // We also need a separate component for vertices that drive themselves (which can
// happen for input like 'assign a = a'), as we want to extract them (they are cyclic). // happen for input like 'assign a = a'), as we want to extract them (they are cyclic).
const bool drivesSelf = vtx.findSink<DfgVertex>([&vtx](const DfgVertex& sink) { // const bool drivesSelf = vtx.foreachSink([&](const DfgVertex& sink) { //
return &vtx == &sink; return &vtx == &sink;
}); });
@ -107,7 +108,7 @@ class ColorStronglyConnectedComponents final {
for (DfgVertexVar& vtx : m_dfg.varVertices()) { for (DfgVertexVar& vtx : m_dfg.varVertices()) {
VertexState& vtxState = vtx.user<VertexState>(); VertexState& vtxState = vtx.user<VertexState>();
// If it has no input or no outputs, it cannot be part of a non-trivial SCC. // If it has no input or no outputs, it cannot be part of a non-trivial SCC.
if (vtx.arity() == 0 || !vtx.hasSinks()) { if ((!vtx.srcp() && !vtx.defaultp()) || !vtx.hasSinks()) {
UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0, UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0,
&vtx, "Non circular variable must be in a trivial SCC");); &vtx, "Non circular variable must be in a trivial SCC"););
vtxState.index = 0; vtxState.index = 0;

View File

@ -65,8 +65,14 @@ class SplitIntoComponents final {
item.user<size_t>() = m_componentCounter; item.user<size_t>() = m_componentCounter;
// Enqueue all sources and sinks of this vertex. // Enqueue all sources and sinks of this vertex.
item.forEachSource([&](DfgVertex& src) { queue.push_back(&src); }); item.foreachSource([&](DfgVertex& src) {
item.forEachSink([&](DfgVertex& dst) { queue.push_back(&dst); }); queue.push_back(&src);
return false;
});
item.foreachSink([&](DfgVertex& dst) {
queue.push_back(&dst);
return false;
});
} while (!queue.empty()); } while (!queue.empty());
// Done with this component // Done with this component
@ -118,21 +124,6 @@ std::vector<std::unique_ptr<DfgGraph>> DfgGraph::splitIntoComponents(const std::
} }
class ExtractCyclicComponents final { class ExtractCyclicComponents final {
// TYPES
// We reuse the DfgVertex::user state set by V3DfgPasses::colorStronglyConnectedComponents.
// We sneak an extra flag into the MSB to indicate the vertex was merged already.
class VertexState final {
uint64_t& m_userr;
public:
explicit VertexState(DfgVertex& vtx)
: m_userr{vtx.getUser<uint64_t>()} {}
bool merged() const { return m_userr >> 63; }
void setMerged() { m_userr |= 1ULL << 63; }
uint64_t component() const { return m_userr & ~(1ULL << 63); }
void component(uint64_t value) { m_userr = (m_userr & (1ULL << 63)) | value; }
};
// STATE // STATE
DfgGraph& m_dfg; // The input graph DfgGraph& m_dfg; // The input graph
const std::string m_prefix; // Component name prefix const std::string m_prefix; // Component name prefix
@ -143,45 +134,86 @@ class ExtractCyclicComponents final {
std::unordered_map<const DfgVertexVar*, std::unordered_map<uint64_t, DfgVertexVar*>> m_clones; std::unordered_map<const DfgVertexVar*, std::unordered_map<uint64_t, DfgVertexVar*>> m_clones;
// METHODS // METHODS
void visitMergeSCCs(DfgVertex& vtx, uint64_t targetComponent) { void addVertexAndExpandSiblings(DfgVertex& vtx, uint64_t component) {
VertexState vtxState{vtx}; // Do not go past a variable, we will partition the graph there
if (vtx.is<DfgVertexVar>()) return;
// Don't need to recurse if the vertex is already in the same component,
// it was either marked through an earlier traversal, in which case it
// was processed recursively, or it will be processed later.
if (vtx.getUser<uint64_t>() == component) return;
// Because all cycles are through a variable, we can't reach another SCC.
UASSERT_OBJ(!vtx.getUser<uint64_t>(), &vtx, "Cycle without variable involvement");
// Put this vertex in the component, and continue recursively
vtx.setUser<uint64_t>(component);
expandSiblings(vtx, component);
}
// Move on if already visited void expandSiblings(DfgVertex& vtx, uint64_t component) {
if (vtxState.merged()) return; UASSERT_OBJ(vtx.getUser<uint64_t>() == component, &vtx, "Traversal didn't stop");
vtx.foreachSink([&](DfgVertex& v) {
// Visiting vertex addVertexAndExpandSiblings(v, component);
vtxState.setMerged(); return false;
// Assign vertex to the target component
vtxState.component(targetComponent);
// Visit all neighbors. We stop at variable boundaries,
// which is where we will split the graphs
vtx.forEachSource([this, targetComponent](DfgVertex& other) {
if (other.is<DfgVertexVar>()) return;
visitMergeSCCs(other, targetComponent);
}); });
vtx.forEachSink([this, targetComponent](DfgVertex& other) { vtx.foreachSource([&](DfgVertex& v) {
if (other.is<DfgVertexVar>()) return; addVertexAndExpandSiblings(v, component);
visitMergeSCCs(other, targetComponent); return false;
}); });
} }
void mergeSCCs() { void expandComponents() {
// Ensure that component boundaries are always at variables, by merging SCCs. Merging stops // Important fact that we will assume below: There are no path between
// at variable boundaries, so we don't need to iterate variables. Constants are reachable // any two SCCs that do not go through a variable before reaching the
// from their sinks, or are unused, so we don't need to iterate them either. // destination SCC. That is, to get from one SCC to another, you must
// go through a variable that is not part of the destination SCC. This
// holds because no operation vertex can have multiple sinks at this
// point (constants have no inputs, so they are not in an SCC).
if (m_doExpensiveChecks) {
for (DfgVertex& vtx : m_dfg.opVertices()) {
UASSERT_OBJ(!vtx.hasMultipleSinks(), &vtx, "Operation has multiple sinks");
}
}
// We will break the graph at variable boundaries, but we want both
// 'srcp', and 'defaultp' to be in the same component, so for each
// cyclic variable, put both its 'srcp' and 'defaultp' into the same
// component if they are not variables themselves. The assertions below
// must hold because of the assumption above.
for (DfgVertexVar& vtx : m_dfg.varVertices()) {
const uint64_t varComponent = vtx.getUser<uint64_t>();
if (!varComponent) continue;
if (DfgVertex* const srcp = vtx.srcp()) {
if (!srcp->is<DfgVertexVar>()) {
const uint64_t srcComponent = srcp->getUser<uint64_t>();
UASSERT_OBJ(!srcComponent || srcComponent == varComponent, srcp,
"Cycle through 'srcp' that does not go through variable.");
srcp->setUser<uint64_t>(varComponent);
}
}
if (DfgVertex* const defp = vtx.defaultp()) {
if (!defp->is<DfgVertexVar>()) {
const uint64_t defComponent = defp->getUser<uint64_t>();
UASSERT_OBJ(!defComponent || defComponent == varComponent, defp,
"Cycle through 'defaultp' that does not go through variable");
defp->setUser<uint64_t>(varComponent);
}
}
}
// To ensure all component boundaries are at variables, expand
// components to include all reachable non-variable vertices. Constants
// are reachable from their sinks, so only need to process op vertices.
// We do this by staring a DFS from each vertex that is part of an
// component and add all reachable non-variable vertices to the same.
for (DfgVertex& vtx : m_dfg.opVertices()) { for (DfgVertex& vtx : m_dfg.opVertices()) {
// Start DFS from each vertex that is in a non-trivial SCC, and merge everything if (const uint64_t targetComponent = vtx.getUser<uint64_t>()) {
// that is reachable from it into this component. expandSiblings(vtx, targetComponent);
if (const uint64_t target = VertexState{vtx}.component()) visitMergeSCCs(vtx, target); }
} }
} }
// Retrieve clone of vertex in the given component // Retrieve clone of vertex in the given component
DfgVertexVar& getClone(DfgVertexVar& vtx, uint64_t component) { DfgVertexVar* getClone(DfgVertexVar& vtx, uint64_t component) {
UASSERT_OBJ(VertexState{vtx}.component() != component, &vtx, UASSERT_OBJ(vtx.getUser<uint64_t>() != component, &vtx, "Vertex is in that component");
"Vertex is in that component");
DfgVertexVar*& clonep = m_clones[&vtx][component]; DfgVertexVar*& clonep = m_clones[&vtx][component];
if (!clonep) { if (!clonep) {
if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) { if (DfgVarPacked* const pVtxp = vtx.cast<DfgVarPacked>()) {
@ -201,40 +233,52 @@ class ExtractCyclicComponents final {
clonep->setUser<uint64_t>(component); clonep->setUser<uint64_t>(component);
clonep->tmpForp(vtx.tmpForp()); clonep->tmpForp(vtx.tmpForp());
} }
return *clonep; return clonep;
} }
// Fix edges that cross components // Fix edges that cross components
void fixEdges(DfgVertexVar& vtx) { void fixEdges(DfgVertexVar& vtx) {
const uint64_t component = VertexState{vtx}.component(); const uint64_t component = vtx.getUser<uint64_t>();
// Fix up sources in a different component // Fix up srcp and dstp (they must be the same component, or variable)
vtx.forEachSourceEdge([&](DfgEdge& edge, size_t) { if (DfgVertex* const sp = vtx.srcp()) {
DfgVertex* const srcp = edge.sourcep(); const uint64_t srcComponent = sp->getUser<uint64_t>();
if (!srcp) return; if (srcComponent != component) {
const uint64_t sourceComponent = VertexState{*srcp}.component(); UASSERT_OBJ(sp->is<DfgVertexVar>(), &vtx, "'srcp' in different component");
// Same component is OK getClone(vtx, srcComponent)->srcp(sp);
if (sourceComponent == component) return; vtx.srcp(nullptr);
// Relink the source to write the clone }
edge.unlinkSource(); }
getClone(vtx, sourceComponent).srcp(srcp); if (DfgVertex* const dp = vtx.defaultp()) {
const uint64_t defaultComponent = dp->getUser<uint64_t>();
if (defaultComponent != component) {
UASSERT_OBJ(dp->is<DfgVertexVar>(), &vtx, "'defaultp' in different component");
getClone(vtx, defaultComponent)->defaultp(dp);
vtx.defaultp(nullptr);
}
}
// Fix up sinks in a different component to read the clone
std::vector<DfgVertex*> sinkps;
vtx.foreachSink([&](DfgVertex& sink) {
sinkps.emplace_back(&sink);
return false;
}); });
for (DfgVertex* const sinkp : sinkps) {
// Fix up sinks in a different component const uint64_t sinkComponent = sinkp->getUser<uint64_t>();
vtx.forEachSinkEdge([&](DfgEdge& edge) {
const uint64_t sinkComponent = VertexState{*edge.sinkp()}.component();
// Same component is OK // Same component is OK
if (sinkComponent == component) return; if (sinkComponent == component) continue;
// Relink the sink to read the clone DfgVertex* const clonep = getClone(vtx, sinkComponent);
edge.relinkSource(&getClone(vtx, sinkComponent)); for (size_t i = 0; i < sinkp->nInputs(); ++i) {
}); if (sinkp->inputp(i) == &vtx) sinkp->inputp(i, clonep);
}
}
} }
template <typename Vertex> template <typename Vertex>
void moveVertices(DfgVertex::List<Vertex>& list) { void moveVertices(DfgVertex::List<Vertex>& list) {
for (DfgVertex* const vtxp : list.unlinkable()) { for (DfgVertex* const vtxp : list.unlinkable()) {
DfgVertex& vtx = *vtxp; DfgVertex& vtx = *vtxp;
if (const uint64_t component = VertexState{vtx}.component()) { if (const uint64_t component = vtx.getUser<uint64_t>()) {
m_dfg.removeVertex(vtx); m_dfg.removeVertex(vtx);
m_components[component - 1]->addVertex(vtx); m_components[component - 1]->addVertex(vtx);
} }
@ -242,20 +286,15 @@ class ExtractCyclicComponents final {
} }
void checkEdges(DfgGraph& dfg) const { void checkEdges(DfgGraph& dfg) const {
// Check that: // Check that edges only cross components at variable boundaries
// - Edges only cross components at variable boundaries
// - Variable vertex sources are all connected.
dfg.forEachVertex([&](DfgVertex& vtx) { dfg.forEachVertex([&](DfgVertex& vtx) {
const uint64_t component = VertexState{vtx}.component(); if (vtx.is<DfgVarPacked>()) return;
vtx.forEachSource([&](DfgVertex& src) { const uint64_t component = vtx.getUser<uint64_t>();
if (src.is<DfgVertexVar>()) return; // OK to cross at variables vtx.foreachSink([&](DfgVertex& snk) {
UASSERT_OBJ(component == VertexState{src}.component(), &vtx, if (snk.is<DfgVertexVar>()) return false; // OK to cross at variables
"Edge crossing components without variable involvement"); UASSERT_OBJ(component == snk.getUser<uint64_t>(), &vtx,
});
vtx.forEachSink([&](DfgVertex& snk) {
if (snk.is<DfgVertexVar>()) return; // OK to cross at variables
UASSERT_OBJ(component == VertexState{snk}.component(), &vtx,
"Edge crossing components without variable involvement"); "Edge crossing components without variable involvement");
return false;
}); });
}); });
} }
@ -267,11 +306,13 @@ class ExtractCyclicComponents final {
// Check that each edge connects to a vertex that is within the same graph // Check that each edge connects to a vertex that is within the same graph
dfg.forEachVertex([&](const DfgVertex& vtx) { dfg.forEachVertex([&](const DfgVertex& vtx) {
vtx.forEachSource([&](const DfgVertex& src) { vtx.foreachSource([&](const DfgVertex& src) {
UASSERT_OBJ(vertices.count(&src), &vtx, "Source vertex not in graph"); UASSERT_OBJ(vertices.count(&src), &vtx, "Source vertex not in graph");
return false;
}); });
vtx.forEachSink([&](const DfgVertex& snk) { vtx.foreachSink([&](const DfgVertex& snk) {
UASSERT_OBJ(vertices.count(&snk), &snk, "Sink vertex not in graph"); UASSERT_OBJ(vertices.count(&snk), &snk, "Sink vertex not in graph");
return false;
}); });
}); });
} }
@ -319,14 +360,13 @@ class ExtractCyclicComponents final {
: m_dfg{dfg} : m_dfg{dfg}
, m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} { , m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} {
// DfgVertex::user<uint64_t> is set to the SCC number by colorStronglyConnectedComponents, // DfgVertex::user<uint64_t> is set to the SCC number by colorStronglyConnectedComponents,
// Then we use VertexState to handle the MSB as an extra flag.
const auto userDataInUse = dfg.userDataInUse(); const auto userDataInUse = dfg.userDataInUse();
// Find all the non-trivial SCCs (and trivial cycles) in the graph // Find all the non-trivial SCCs (and trivial cycles) in the graph
const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg); const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg);
// If the graph was acyclic (which should be the common case), then we are done. // If the graph was acyclic (which should be the common case), then we are done.
if (!numNonTrivialSCCs) return; if (!numNonTrivialSCCs) return;
// Ensure that component boundaries are always at variables, by merging SCCs // Ensure that component boundaries are always at variables, by expanding SCCs
mergeSCCs(); expandComponents();
// Extract the components // Extract the components
extractComponents(numNonTrivialSCCs); extractComponents(numNonTrivialSCCs);
} }

View File

@ -92,32 +92,23 @@ class DfgToAstVisitor final : DfgVisitor {
const VNUser2InUse m_user2InUse; const VNUser2InUse m_user2InUse;
// TYPES // TYPES
using VariableType = std::conditional_t<T_Scoped, AstVarScope, AstVar>; using Variable = std::conditional_t<T_Scoped, AstVarScope, AstVar>;
struct Assignment final { using Container = std::conditional_t<T_Scoped, AstActive, AstNodeModule>;
FileLine* m_flp;
AstNodeExpr* m_lhsp;
AstNodeExpr* m_rhsp;
Assignment() = delete;
Assignment(FileLine* flp, AstNodeExpr* lhsp, AstNodeExpr* m_rhsp)
: m_flp{flp}
, m_lhsp{lhsp}
, m_rhsp{m_rhsp} {}
};
// STATE // STATE
AstModule* const m_modp; // The parent/result module - This is nullptr when T_Scoped AstModule* const m_modp; // The parent/result module - This is nullptr when T_Scoped
V3DfgDfgToAstContext& m_ctx; // The context for stats V3DfgDfgToAstContext& m_ctx; // The context for stats
AstNodeExpr* m_resultp = nullptr; // The result node of the current traversal AstNodeExpr* m_resultp = nullptr; // The result node of the current traversal
std::vector<Assignment> m_assignments; // Assignments to currently rendered variable AstAlways* m_alwaysp = nullptr; // Process to add assignments to, if have a default driver
std::vector<Assignment> m_defaults; // Default assignments to currently rendered variable Container* m_containerp = nullptr; // The AstNodeModule or AstActive to insert assigns into
// METHODS // METHODS
static VariableType* getNode(const DfgVertexVar* vtxp) { static Variable* getNode(const DfgVertexVar* vtxp) {
if VL_CONSTEXPR_CXX17 (T_Scoped) { if VL_CONSTEXPR_CXX17 (T_Scoped) {
return reinterpret_cast<VariableType*>(vtxp->varScopep()); return reinterpret_cast<Variable*>(vtxp->varScopep());
} else { } else {
return reinterpret_cast<VariableType*>(vtxp->varp()); return reinterpret_cast<Variable*>(vtxp->varp());
} }
} }
@ -158,49 +149,57 @@ class DfgToAstVisitor final : DfgVisitor {
return resultp; return resultp;
} }
void convertDriver(std::vector<Assignment>& assignments, FileLine* flp, AstNodeExpr* lhsp, void createAssignment(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) {
DfgVertex* driverp) { // Keep track of statisticss
++m_ctx.m_resultEquations;
// Render the driver
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp);
// Update LHS locations to reflect the location of the original driver
lhsp->foreach([&](AstNode* nodep) { nodep->fileline(flp); });
// If using a process, add Assign there
if (m_alwaysp) {
m_alwaysp->addStmtsp(new AstAssign{flp, lhsp, rhsp});
return;
}
// Otherwise create an AssignW
m_containerp->addStmtsp(new AstAssignW{flp, lhsp, rhsp});
}
void convertDriver(FileLine* flp, AstNodeExpr* lhsp, DfgVertex* driverp) {
if (DfgSplicePacked* const sPackedp = driverp->cast<DfgSplicePacked>()) { if (DfgSplicePacked* const sPackedp = driverp->cast<DfgSplicePacked>()) {
// Render defaults first // Partial assignment of packed value
if (DfgVertex* const defaultp = sPackedp->defaultp()) { sPackedp->foreachDriver([&](DfgVertex& src, uint32_t lo, FileLine* dflp) {
convertDriver(m_defaults, flp, lhsp, defaultp);
}
// Render partial assignments of packed value
sPackedp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
DfgVertex* const srcp = edge.sourcep();
if (srcp == sPackedp->defaultp()) return;
// Create Sel // Create Sel
FileLine* const dflp = sPackedp->driverFileLine(i); AstConst* const lsbp = new AstConst{dflp, lo};
AstConst* const lsbp = new AstConst{dflp, sPackedp->driverLo(i)}; const int width = static_cast<int>(src.width());
const int width = static_cast<int>(srcp->width());
AstSel* const nLhsp = new AstSel{dflp, lhsp->cloneTreePure(false), lsbp, width}; AstSel* const nLhsp = new AstSel{dflp, lhsp->cloneTreePure(false), lsbp, width};
// Convert source // Convert source
convertDriver(assignments, dflp, nLhsp, srcp); convertDriver(dflp, nLhsp, &src);
// Delete Sel - was cloned // Delete Sel - was cloned
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
return false;
}); });
return; return;
} }
if (DfgSpliceArray* const sArrayp = driverp->cast<DfgSpliceArray>()) { if (DfgSpliceArray* const sArrayp = driverp->cast<DfgSpliceArray>()) {
UASSERT_OBJ(!sArrayp->defaultp(), flp, "Should not have a default assignment yet");
// Partial assignment of array variable // Partial assignment of array variable
sArrayp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) { sArrayp->foreachDriver([&](DfgVertex& src, uint32_t lo, FileLine* dflp) {
DfgVertex* const driverp = edge.sourcep(); UASSERT_OBJ(src.size() == 1, &src, "We only handle single elements");
UASSERT_OBJ(driverp, sArrayp, "Should have removed undriven sources");
UASSERT_OBJ(driverp->size() == 1, driverp, "We only handle single elements");
// Create ArraySel // Create ArraySel
FileLine* const dflp = sArrayp->driverFileLine(i); AstConst* const idxp = new AstConst{dflp, lo};
AstConst* const idxp = new AstConst{dflp, sArrayp->driverLo(i)};
AstArraySel* const nLhsp = new AstArraySel{dflp, lhsp->cloneTreePure(false), idxp}; AstArraySel* const nLhsp = new AstArraySel{dflp, lhsp->cloneTreePure(false), idxp};
// Convert source // Convert source
if (const DfgUnitArray* const uap = driverp->cast<DfgUnitArray>()) { if (const DfgUnitArray* const uap = src.cast<DfgUnitArray>()) {
convertDriver(assignments, dflp, nLhsp, uap->srcp()); convertDriver(dflp, nLhsp, uap->srcp());
} else { } else {
convertDriver(assignments, dflp, nLhsp, driverp); convertDriver(dflp, nLhsp, &src);
} }
// Delete ArraySel - was cloned // Delete ArraySel - was cloned
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
return false;
}); });
return; return;
} }
@ -210,17 +209,14 @@ class DfgToAstVisitor final : DfgVisitor {
AstConst* const idxp = new AstConst{flp, 0}; AstConst* const idxp = new AstConst{flp, 0};
AstArraySel* const nLhsp = new AstArraySel{flp, lhsp->cloneTreePure(false), idxp}; AstArraySel* const nLhsp = new AstArraySel{flp, lhsp->cloneTreePure(false), idxp};
// Convert source // Convert source
convertDriver(assignments, flp, nLhsp, uap->srcp()); convertDriver(flp, nLhsp, uap->srcp());
// Delete ArraySel - was cloned // Delete ArraySel - was cloned
VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp); VL_DO_DANGLING(nLhsp->deleteTree(), nLhsp);
return; return;
} }
// Base case: assign vertex to current lhs // Base case: assign vertex to current lhs
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(driverp); createAssignment(flp, lhsp->cloneTreePure(false), driverp);
assignments.emplace_back(flp, lhsp->cloneTreePure(false), rhsp);
++m_ctx.m_resultEquations;
return;
} }
// VISITORS // VISITORS
@ -258,7 +254,7 @@ class DfgToAstVisitor final : DfgVisitor {
#include "V3Dfg__gen_dfg_to_ast.h" #include "V3Dfg__gen_dfg_to_ast.h"
// Constructor // Constructor
explicit DfgToAstVisitor(DfgGraph& dfg, V3DfgDfgToAstContext& ctx) DfgToAstVisitor(DfgGraph& dfg, V3DfgDfgToAstContext& ctx)
: m_modp{dfg.modulep()} : m_modp{dfg.modulep()}
, m_ctx{ctx} { , m_ctx{ctx} {
// Convert the graph back to combinational assignments // Convert the graph back to combinational assignments
@ -266,55 +262,42 @@ class DfgToAstVisitor final : DfgVisitor {
// The graph must have been regularized, so we only need to render assignments // The graph must have been regularized, so we only need to render assignments
for (DfgVertexVar& vtx : dfg.varVertices()) { for (DfgVertexVar& vtx : dfg.varVertices()) {
// If there is no driver (this vertex is an input to the graph), then nothing to do. // If there is no driver (this vertex is an input to the graph), then nothing to do.
if (!vtx.srcp()) continue; if (!vtx.srcp()) {
UASSERT_OBJ(!vtx.defaultp(), &vtx, "Only default driver on variable");
continue;
}
++m_ctx.m_outputVariables; ++m_ctx.m_outputVariables;
// Render variable assignments // Render variable assignments
FileLine* const flp = vtx.driverFileLine() ? vtx.driverFileLine() : vtx.fileline(); 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, getNode(&vtx), VAccess::WRITE};
convertDriver(m_assignments, flp, lhsp, vtx.srcp());
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);
}
// 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);
// The default assignment needs to go first
createAssignment(vtx.fileline(), lhsp->cloneTreePure(false), defaultp);
}
// Render the drivers
convertDriver(flp, lhsp, vtx.srcp());
// convetDriver always clones lhsp // convetDriver always clones lhsp
VL_DO_DANGLING(lhsp->deleteTree(), lhsp); VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
if (m_defaults.empty()) {
// If there are no default assignments, render each driver as an AssignW
for (const Assignment& a : m_assignments) {
AstAssignW* const assignp = new AstAssignW{a.m_flp, a.m_lhsp, a.m_rhsp};
a.m_lhsp->foreach([&a](AstNode* nodep) { nodep->fileline(a.m_flp); });
if VL_CONSTEXPR_CXX17 (T_Scoped) {
// Add it to the scope holding the target variable
getCombActive(vtx.varScopep()->scopep())->addStmtsp(assignp);
} else {
// Add it to the parent module of the DfgGraph
m_modp->addStmtsp(assignp);
}
}
} else {
++m_ctx.m_outputVariablesWithDefault;
// If there are default assignments, render all drivers under an AstAlways
AstAlways* const alwaysp
= new AstAlways{vtx.fileline(), VAlwaysKwd::ALWAYS_COMB, nullptr, nullptr};
if VL_CONSTEXPR_CXX17 (T_Scoped) {
// Add it to the scope holding the target variable
getCombActive(vtx.varScopep()->scopep())->addStmtsp(alwaysp);
} else {
// Add it to the parent module of the DfgGraph
m_modp->addStmtsp(alwaysp);
}
for (const Assignment& a : m_defaults) {
AstAssign* const assignp = new AstAssign{a.m_flp, a.m_lhsp, a.m_rhsp};
a.m_lhsp->foreach([&a](AstNode* nodep) { nodep->fileline(a.m_flp); });
alwaysp->addStmtsp(assignp);
}
for (const Assignment& a : m_assignments) {
AstAssign* const assignp = new AstAssign{a.m_flp, a.m_lhsp, a.m_rhsp};
a.m_lhsp->foreach([&a](AstNode* nodep) { nodep->fileline(a.m_flp); });
alwaysp->addStmtsp(assignp);
}
}
m_assignments.clear();
m_defaults.clear();
} }
} }

View File

@ -324,7 +324,7 @@ class DataflowOptimize final {
// Mark interfaces that might be referenced by a virtual interface // Mark interfaces that might be referenced by a virtual interface
if (v3Global.hasVirtIfaces()) { if (v3Global.hasVirtIfaces()) {
netlistp->typeTablep()->foreach([](AstIfaceRefDType* nodep) { netlistp->typeTablep()->foreach([](const AstIfaceRefDType* nodep) {
if (!nodep->isVirtual()) return; if (!nodep->isVirtual()) return;
nodep->ifaceViaCellp()->setHasVirtualRef(); nodep->ifaceViaCellp()->setHasVirtualRef();
}); });

View File

@ -84,8 +84,7 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
for (DfgVertexVar& vtx : dfg.varVertices()) { for (DfgVertexVar& vtx : dfg.varVertices()) {
if (DfgVarPacked* const varp = vtx.cast<DfgVarPacked>()) { if (DfgVarPacked* const varp = vtx.cast<DfgVarPacked>()) {
if (varp->hasSinks() && varp->isDrivenFullyByDfg()) { if (varp->hasSinks() && varp->isDrivenFullyByDfg()) {
DfgVertex* const driverp = varp->srcp(); varp->replaceWith(varp->srcp());
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
} }
} }
} }
@ -148,16 +147,17 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) {
if (varp->hasDfgRefs()) continue; if (varp->hasDfgRefs()) continue;
} }
// Add sources of unused vertex to work list // Add sources of unused vertex to work list
vtxp->forEachSource([&](DfgVertex& src) { vtxp->foreachSource([&](DfgVertex& src) {
// We only remove actual operation vertices and synthesis temporaries in this loop // We only remove actual operation vertices and synthesis temporaries in this loop
if (src.is<DfgConst>()) return; if (src.is<DfgConst>()) return false;
const DfgVertexVar* const varp = src.cast<DfgVertexVar>(); const DfgVertexVar* const varp = src.cast<DfgVertexVar>();
if (varp && !varp->tmpForp()) return; if (varp && !varp->tmpForp()) return false;
// If already in work list then nothing to do // If already in work list then nothing to do
if (src.getUser<DfgVertex*>()) return; if (src.getUser<DfgVertex*>()) return false;
// Actually add to work list. // Actually add to work list.
src.setUser<DfgVertex*>(workListp); src.setUser<DfgVertex*>(workListp);
workListp = &src; workListp = &src;
return false;
}); });
// Remove the unused vertex // Remove the unused vertex
vtxp->unlinkDelete(dfg); vtxp->unlinkDelete(dfg);
@ -214,10 +214,13 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
if (DfgVertex* const sinkp = vtxp->singleSink()) { if (DfgVertex* const sinkp = vtxp->singleSink()) {
if (sinkp->is<DfgNot>()) return useOk(sinkp, !inv); if (sinkp->is<DfgNot>()) return useOk(sinkp, !inv);
} }
return !vtxp->findSink<DfgCond>([vtxp, inv](const DfgCond& sink) { const bool condTree = vtxp->foreachSink([&](const DfgVertex& sink) {
if (sink.condp() != vtxp) return false; const DfgCond* const condp = sink.cast<DfgCond>();
return inv ? sink.thenp()->is<DfgCond>() : sink.elsep()->is<DfgCond>(); if (!condp) return false;
if (condp->condp() != vtxp) return false;
return inv ? condp->thenp()->is<DfgCond>() : condp->elsep()->is<DfgCond>();
}); });
return !condTree;
}; };
// Look at all comparison nodes and build the 'Val2Terms' map for each source vertex // Look at all comparison nodes and build the 'Val2Terms' map for each source vertex
@ -306,7 +309,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
// Required data types // Required data types
AstNodeDType* const idxDTypep = srcp->dtypep(); AstNodeDType* const idxDTypep = srcp->dtypep();
AstNodeDType* const bitDTypep = DfgGraph::dtypePacked(1); AstNodeDType* const bitDTypep = V3Dfg::dtypePacked(1);
AstUnpackArrayDType* const tabDTypep = new AstUnpackArrayDType{ AstUnpackArrayDType* const tabDTypep = new AstUnpackArrayDType{
flp, bitDTypep, new AstRange{flp, static_cast<int>(nBits - 1), 0}}; flp, bitDTypep, new AstRange{flp, static_cast<int>(nBits - 1), 0}};
v3Global.rootp()->typeTablep()->addTypesp(tabDTypep); v3Global.rootp()->typeTablep()->addTypesp(tabDTypep);
@ -438,10 +441,11 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
const auto addToWorkList = [&](DfgVertex& vtx) { const auto addToWorkList = [&](DfgVertex& vtx) {
// If already in work list then nothing to do // If already in work list then nothing to do
DfgVertex*& nextInWorklistp = vtx.user<DfgVertex*>(); DfgVertex*& nextInWorklistp = vtx.user<DfgVertex*>();
if (nextInWorklistp) return; if (nextInWorklistp) return false;
// Actually add to work list. // Actually add to work list.
nextInWorklistp = workListp; nextInWorklistp = workListp;
workListp = &vtx; workListp = &vtx;
return false;
}; };
// List of variables (AstVar or AstVarScope) we are replacing // List of variables (AstVar or AstVarScope) we are replacing
@ -467,7 +471,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
// Remove unused non-variable vertices // Remove unused non-variable vertices
if (!vtxp->is<DfgVertexVar>() && !vtxp->hasSinks()) { if (!vtxp->is<DfgVertexVar>() && !vtxp->hasSinks()) {
// Add sources of removed vertex to work list // Add sources of removed vertex to work list
vtxp->forEachSource(addToWorkList); vtxp->foreachSource(addToWorkList);
// Remove the unused vertex // Remove the unused vertex
vtxp->unlinkDelete(dfg); vtxp->unlinkDelete(dfg);
continue; continue;
@ -517,7 +521,7 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
} }
// Add sources of redundant variable to the work list // Add sources of redundant variable to the work list
vtxp->forEachSource(addToWorkList); vtxp->foreachSource(addToWorkList);
// Remove the redundant variable // Remove the redundant variable
vtxp->unlinkDelete(dfg); vtxp->unlinkDelete(dfg);
} }

View File

@ -118,9 +118,10 @@ class V3DfgPatternStats final {
} }
// Operands // Operands
vtx.forEachSource([&](const DfgVertex& src) { vtx.foreachSource([&](const DfgVertex& src) {
ss << ' '; ss << ' ';
if (render(ss, src, depth - 1)) deep = true; if (render(ss, src, depth - 1)) deep = true;
return false;
}); });
// S-expression end // S-expression end
ss << ')'; ss << ')';

View File

@ -131,7 +131,7 @@ class V3DfgPeephole final : public DfgVisitor {
// STATE // STATE
DfgGraph& m_dfg; // The DfgGraph being visited DfgGraph& m_dfg; // The DfgGraph being visited
V3DfgPeepholeContext& m_ctx; // The config structure V3DfgPeepholeContext& m_ctx; // The config structure
AstNodeDType* const m_bitDType = DfgGraph::dtypePacked(1); // Common, so grab it up front AstNodeDType* const m_bitDType = V3Dfg::dtypePacked(1); // Common, so grab it up front
// Head of work list. Note that we want all next pointers in the list to be non-zero (including // Head of work list. Note that we want all next pointers in the list to be non-zero (including
// that of the last element). This allows as to do two important things: detect if an element // that of the last element). This allows as to do two important things: detect if an element
// is in the list by checking for a non-zero next pointer, and easy prefetching without // is in the list by checking for a non-zero next pointer, and easy prefetching without
@ -163,11 +163,17 @@ class V3DfgPeephole final : public DfgVisitor {
} }
void addSourcesToWorkList(DfgVertex* vtxp) { void addSourcesToWorkList(DfgVertex* vtxp) {
vtxp->forEachSource([&](DfgVertex& src) { addToWorkList(&src); }); vtxp->foreachSource([&](DfgVertex& src) {
addToWorkList(&src);
return false;
});
} }
void addSinksToWorkList(DfgVertex* vtxp) { void addSinksToWorkList(DfgVertex* vtxp) {
vtxp->forEachSink([&](DfgVertex& src) { addToWorkList(&src); }); vtxp->foreachSink([&](DfgVertex& src) {
addToWorkList(&src);
return false;
});
} }
void deleteVertex(DfgVertex* vtxp) { void deleteVertex(DfgVertex* vtxp) {
@ -179,8 +185,6 @@ class V3DfgPeephole final : public DfgVisitor {
// Otherwise we can delete it now. // Otherwise we can delete it now.
// Remove from cache // Remove from cache
m_cache.invalidateByValue(vtxp); m_cache.invalidateByValue(vtxp);
// Unlink source edges
vtxp->forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
// Should not have sinks // Should not have sinks
UASSERT_OBJ(!vtxp->hasSinks(), vtxp, "Should not delete used vertex"); UASSERT_OBJ(!vtxp->hasSinks(), vtxp, "Should not delete used vertex");
// //
@ -197,21 +201,29 @@ class V3DfgPeephole final : public DfgVisitor {
// Add replacement to the work list // Add replacement to the work list
addToWorkList(replacementp); addToWorkList(replacementp);
// Replace vertex with the replacement // Replace vertex with the replacement
vtxp->forEachSink([&](DfgVertex& sink) { m_cache.invalidateByValue(&sink); }); vtxp->foreachSink([&](DfgVertex& sink) {
m_cache.invalidateByValue(&sink);
return false;
});
vtxp->replaceWith(replacementp); vtxp->replaceWith(replacementp);
replacementp->forEachSink([&](DfgVertex& sink) { m_cache.cache(&sink); }); replacementp->foreachSink([&](DfgVertex& sink) {
m_cache.cache(&sink);
return false;
});
// Vertex is now unused, so delete it // Vertex is now unused, so delete it
deleteVertex(vtxp); deleteVertex(vtxp);
} }
// Shorthand // Shorthand
static AstNodeDType* dtypePacked(uint32_t width) { return DfgGraph::dtypePacked(width); } static AstNodeDType* dtypePacked(uint32_t width) { return V3Dfg::dtypePacked(width); }
// Create a 32-bit DfgConst vertex // Create a 32-bit DfgConst vertex
DfgConst* makeI32(FileLine* flp, uint32_t val) { return new DfgConst{m_dfg, flp, 32, val}; } DfgConst* makeI32(FileLine* flp, uint32_t val) { return new DfgConst{m_dfg, flp, 32, val}; }
// Create a DfgConst vertex with the given width and value zero // Create a DfgConst vertex with the given width and value zero
DfgConst* makeZero(FileLine* flp, uint32_t width) { return new DfgConst{m_dfg, flp, width}; } DfgConst* makeZero(FileLine* flp, uint32_t width) {
return new DfgConst{m_dfg, flp, width, 0};
}
// Create a new vertex of the given type // Create a new vertex of the given type
template <typename Vertex, typename... Operands> template <typename Vertex, typename... Operands>
@ -246,6 +258,16 @@ class V3DfgPeephole final : public DfgVisitor {
return aConstp->num().isCaseEq(bConstp->num()); return aConstp->num().isCaseEq(bConstp->num());
} }
static bool isZero(const DfgVertex* vtxp) {
if (const DfgConst* const constp = vtxp->cast<DfgConst>()) return constp->isZero();
return false;
}
static bool isOnes(const DfgVertex* vtxp) {
if (const DfgConst* const constp = vtxp->cast<DfgConst>()) return constp->isOnes();
return false;
}
// Note: If any of the following transformers return true, then the vertex was replaced and the // Note: If any of the following transformers return true, then the vertex was replaced and the
// caller must not do any further changes, so the caller must check the return value, otherwise // caller must not do any further changes, so the caller must check the return value, otherwise
// there will be hard to debug issues. // there will be hard to debug issues.
@ -271,8 +293,8 @@ class V3DfgPeephole final : public DfgVisitor {
VL_ATTR_WARN_UNUSED_RESULT bool foldBinary(Vertex* vtxp) { VL_ATTR_WARN_UNUSED_RESULT bool foldBinary(Vertex* vtxp) {
static_assert(std::is_base_of<DfgVertexBinary, Vertex>::value, "Must invoke on binary"); static_assert(std::is_base_of<DfgVertexBinary, Vertex>::value, "Must invoke on binary");
static_assert(std::is_final<Vertex>::value, "Must invoke on final class"); static_assert(std::is_final<Vertex>::value, "Must invoke on final class");
if (DfgConst* const lhsp = vtxp->lhsp()->template cast<DfgConst>()) { if (DfgConst* const lhsp = vtxp->inputp(0)->template cast<DfgConst>()) {
if (DfgConst* const rhsp = vtxp->rhsp()->template cast<DfgConst>()) { if (DfgConst* const rhsp = vtxp->inputp(1)->template cast<DfgConst>()) {
APPLYING(FOLD_BINARY) { APPLYING(FOLD_BINARY) {
DfgConst* const resultp = makeZero(vtxp->fileline(), vtxp->width()); DfgConst* const resultp = makeZero(vtxp->fileline(), vtxp->width());
foldOp<Vertex>(resultp->num(), lhsp->num(), rhsp->num()); foldOp<Vertex>(resultp->num(), lhsp->num(), rhsp->num());
@ -642,7 +664,7 @@ class V3DfgPeephole final : public DfgVisitor {
static_assert(std::is_base_of<DfgVertexBinary, Shift>::value, "Must invoke on binary"); static_assert(std::is_base_of<DfgVertexBinary, Shift>::value, "Must invoke on binary");
static_assert(std::is_final<Shift>::value, "Must invoke on final class"); static_assert(std::is_final<Shift>::value, "Must invoke on final class");
if (const DfgConcat* const concatp = vtxp->rhsp()->template cast<DfgConcat>()) { if (const DfgConcat* const concatp = vtxp->rhsp()->template cast<DfgConcat>()) {
if (concatp->lhsp()->isZero()) { // Drop redundant zero extension if (isZero(concatp->lhsp())) { // Drop redundant zero extension
APPLYING(REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT) { APPLYING(REMOVE_REDUNDANT_ZEXT_ON_RHS_OF_SHIFT) {
Shift* const replacementp = make<Shift>(vtxp, vtxp->lhsp(), concatp->rhsp()); Shift* const replacementp = make<Shift>(vtxp, vtxp->lhsp(), concatp->rhsp());
replace(vtxp, replacementp); replace(vtxp, replacementp);
@ -919,27 +941,25 @@ class V3DfgPeephole final : public DfgVisitor {
// Must be a splice, otherwise it would have been inlined // Must be a splice, otherwise it would have been inlined
DfgSplicePacked* const splicep = varp->srcp()->as<DfgSplicePacked>(); DfgSplicePacked* const splicep = varp->srcp()->as<DfgSplicePacked>();
const auto pair = splicep->sourceEdges(); DfgSel* replacementp = nullptr;
for (size_t i = 0; i < pair.second; ++i) { splicep->foreachDriver([&](DfgVertex& src, const uint32_t dLsb) {
DfgVertex* const driverp = pair.first[i].sourcep(); const uint32_t dMsb = dLsb + src.width() - 1;
// Ignore default, we won't select into that for now ...
if (driverp == splicep->defaultp()) continue;
const uint32_t dLsb = splicep->driverLo(i);
const uint32_t dMsb = dLsb + driverp->width() - 1;
// If it does not cover the whole searched bit range, move on // If it does not cover the whole searched bit range, move on
if (lsb < dLsb || dMsb < msb) continue; if (lsb < dLsb || dMsb < msb) return false;
// Replace with sel from driver
replacementp = make<DfgSel>(vtxp, &src, lsb - dLsb);
return true;
});
if (replacementp) {
// Replace with sel from driver // Replace with sel from driver
APPLYING(PUSH_SEL_THROUGH_SPLICE) { APPLYING(PUSH_SEL_THROUGH_SPLICE) {
DfgSel* const replacementp = make<DfgSel>(vtxp, driverp, lsb - dLsb);
replace(vtxp, replacementp); replace(vtxp, replacementp);
// Special case just for this pattern: delete temporary if became unsued // Special case just for this pattern: delete temporary if became unsued
if (!varp->hasSinks() && !varp->hasDfgRefs()) { if (!varp->hasSinks() && !varp->hasDfgRefs()) {
addToWorkList(splicep); // So it can be delete itself if unused addToWorkList(splicep); // So it can be delete itself if unused
VL_DO_DANGLING(varp->unlinkDelete(m_dfg), varp); // Delete it VL_DO_DANGLING(varp->unlinkDelete(m_dfg), varp); // Delete it
} }
return;
} }
} }
} }
@ -1086,7 +1106,7 @@ class V3DfgPeephole final : public DfgVisitor {
if (DfgConcat* const lhsConcatp = lhsp->cast<DfgConcat>()) { if (DfgConcat* const lhsConcatp = lhsp->cast<DfgConcat>()) {
if (DfgConcat* const rhsConcatp = rhsp->cast<DfgConcat>()) { if (DfgConcat* const rhsConcatp = rhsp->cast<DfgConcat>()) {
if (lhsConcatp->lhsp()->dtypep() == rhsConcatp->lhsp()->dtypep()) { if (lhsConcatp->lhsp()->dtypep() == rhsConcatp->lhsp()->dtypep()) {
if (lhsConcatp->lhsp()->isZero() && rhsConcatp->rhsp()->isZero()) { if (isZero(lhsConcatp->lhsp()) && isZero(rhsConcatp->rhsp())) {
APPLYING(REPLACE_OR_OF_CONCAT_ZERO_LHS_AND_CONCAT_RHS_ZERO) { APPLYING(REPLACE_OR_OF_CONCAT_ZERO_LHS_AND_CONCAT_RHS_ZERO) {
DfgConcat* const replacementp DfgConcat* const replacementp
= make<DfgConcat>(vtxp, rhsConcatp->lhsp(), lhsConcatp->rhsp()); = make<DfgConcat>(vtxp, rhsConcatp->lhsp(), lhsConcatp->rhsp());
@ -1094,7 +1114,7 @@ class V3DfgPeephole final : public DfgVisitor {
return; return;
} }
} }
if (lhsConcatp->rhsp()->isZero() && rhsConcatp->lhsp()->isZero()) { if (isZero(lhsConcatp->rhsp()) && isZero(rhsConcatp->lhsp())) {
APPLYING(REPLACE_OR_OF_CONCAT_LHS_ZERO_AND_CONCAT_ZERO_RHS) { APPLYING(REPLACE_OR_OF_CONCAT_LHS_ZERO_AND_CONCAT_ZERO_RHS) {
DfgConcat* const replacementp DfgConcat* const replacementp
= make<DfgConcat>(vtxp, lhsConcatp->lhsp(), rhsConcatp->rhsp()); = make<DfgConcat>(vtxp, lhsConcatp->lhsp(), rhsConcatp->rhsp());
@ -1261,7 +1281,7 @@ class V3DfgPeephole final : public DfgVisitor {
FileLine* const flp = vtxp->fileline(); FileLine* const flp = vtxp->fileline();
if (lhsp->isZero()) { if (isZero(lhsp)) {
DfgConst* const lConstp = lhsp->as<DfgConst>(); DfgConst* const lConstp = lhsp->as<DfgConst>();
if (DfgSel* const rSelp = rhsp->cast<DfgSel>()) { if (DfgSel* const rSelp = rhsp->cast<DfgSel>()) {
if (vtxp->dtypep() == rSelp->fromp()->dtypep() if (vtxp->dtypep() == rSelp->fromp()->dtypep()
@ -1276,7 +1296,7 @@ class V3DfgPeephole final : public DfgVisitor {
} }
} }
if (rhsp->isZero()) { if (isZero(rhsp)) {
DfgConst* const rConstp = rhsp->as<DfgConst>(); DfgConst* const rConstp = rhsp->as<DfgConst>();
if (DfgSel* const lSelp = lhsp->cast<DfgSel>()) { if (DfgSel* const lSelp = lhsp->cast<DfgSel>()) {
if (vtxp->dtypep() == lSelp->fromp()->dtypep() && lSelp->lsb() == 0) { if (vtxp->dtypep() == lSelp->fromp()->dtypep() && lSelp->lsb() == 0) {
@ -1561,14 +1581,14 @@ class V3DfgPeephole final : public DfgVisitor {
if (condp->dtypep() != m_bitDType) return; if (condp->dtypep() != m_bitDType) return;
if (condp->isOnes()) { if (isOnes(condp)) {
APPLYING(REMOVE_COND_WITH_TRUE_CONDITION) { APPLYING(REMOVE_COND_WITH_TRUE_CONDITION) {
replace(vtxp, thenp); replace(vtxp, thenp);
return; return;
} }
} }
if (condp->isZero()) { if (isZero(condp)) {
APPLYING(REMOVE_COND_WITH_FALSE_CONDITION) { APPLYING(REMOVE_COND_WITH_FALSE_CONDITION) {
replace(vtxp, elsep); replace(vtxp, elsep);
return; return;
@ -1689,7 +1709,7 @@ class V3DfgPeephole final : public DfgVisitor {
} }
if (vtxp->dtypep() == m_bitDType) { if (vtxp->dtypep() == m_bitDType) {
if (thenp->isZero()) { // a ? 0 : b becomes ~a & b if (isZero(thenp)) { // a ? 0 : b becomes ~a & b
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) { APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
DfgNot* const notp = make<DfgNot>(vtxp, condp); DfgNot* const notp = make<DfgNot>(vtxp, condp);
DfgAnd* const repalcementp = make<DfgAnd>(vtxp, notp, elsep); DfgAnd* const repalcementp = make<DfgAnd>(vtxp, notp, elsep);
@ -1704,21 +1724,21 @@ class V3DfgPeephole final : public DfgVisitor {
return; return;
} }
} }
if (thenp->isOnes()) { // a ? 1 : b becomes a | b if (isOnes(thenp)) { // a ? 1 : b becomes a | b
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) { APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) {
DfgOr* const repalcementp = make<DfgOr>(vtxp, condp, elsep); DfgOr* const repalcementp = make<DfgOr>(vtxp, condp, elsep);
replace(vtxp, repalcementp); replace(vtxp, repalcementp);
return; return;
} }
} }
if (elsep->isZero()) { // a ? b : 0 becomes a & b if (isZero(elsep)) { // a ? b : 0 becomes a & b
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) { APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) {
DfgAnd* const repalcementp = make<DfgAnd>(vtxp, condp, thenp); DfgAnd* const repalcementp = make<DfgAnd>(vtxp, condp, thenp);
replace(vtxp, repalcementp); replace(vtxp, repalcementp);
return; return;
} }
} }
if (elsep->isOnes()) { // a ? b : 1 becomes ~a | b if (isOnes(elsep)) { // a ? b : 1 becomes ~a | b
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) { APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) {
DfgNot* const notp = make<DfgNot>(vtxp, condp); DfgNot* const notp = make<DfgNot>(vtxp, condp);
DfgOr* const repalcementp = make<DfgOr>(vtxp, notp, thenp); DfgOr* const repalcementp = make<DfgOr>(vtxp, notp, thenp);

View File

@ -63,7 +63,7 @@ class DfgRegularize final {
// This is an op that requires a result variable. Ensure it is // This is an op that requires a result variable. Ensure it is
// assigned to one, and redirect other sinks read that variable. // assigned to one, and redirect other sinks read that variable.
if (DfgVertexVar* const varp = vtx.getResultVar()) { if (DfgVertexVar* const varp = vtx.getResultVar()) {
varp->sourceEdge<0>()->unlinkSource(); varp->srcp(nullptr);
vtx.replaceWith(varp); vtx.replaceWith(varp);
varp->srcp(&vtx); varp->srcp(&vtx);
} else { } else {

File diff suppressed because it is too large Load Diff

View File

@ -36,39 +36,101 @@
#define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE #define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE
#endif #endif
// === Abstract base node types (DfgVertex*) =================================== // Include macros generated by 'astgen'. These include DFGGEN_MEMBERS_<Node>
// for each DfgVertex sub-type. The generated members include boilerplate
// methods related to cloning, visitor dispatch, and other functionality.
// For precise details please read the generated macros.
#include "V3Dfg__gen_macros.h"
//------------------------------------------------------------------------------
// Variable vertices - represent a variables
class DfgVertexVar VL_NOT_FINAL : public DfgVertex {
// Represents a variable. It has 2 optional inputs, 'srcp' and 'defaultp'.
class DfgVertexVar VL_NOT_FINAL : public DfgVertexUnary {
AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex) 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_varScopep; // The AstVarScope associated with this vertex (not owned)
// Location of driver of this variable. Only used for converting back to Ast. Might be nullptr. // Location of driver of this variable. Only used for converting back to Ast. Might be nullptr.
FileLine* m_driverFileLine = nullptr; FileLine* m_driverFileLine = nullptr;
// If this DfgVertexVar is a synthesized temporary, this is the Var/VarScope it stands for. // 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; AstNode* m_tmpForp = nullptr;
bool selfEquals(const DfgVertex& that) const final VL_MT_DISABLED; bool selfEquals(const DfgVertex& that) const final {
V3Hash selfHash() const final VL_MT_DISABLED; UASSERT_OBJ(nodep() != that.as<DfgVertexVar>()->nodep(), this,
"There should only be one DfgVertexVar for a given AstVar/AstVarScope");
return false;
}
V3Hash selfHash() const final {
V3Hash hash;
hash += nodep()->name();
hash += varp()->varType();
return hash;
}
DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, AstVarScope* vscp)
: DfgVertex{dfg, type, varp->fileline(), V3Dfg::toDfgDType(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");
}
UASSERT_OBJ(V3Dfg::isSupported(dtypep()), varp, "Not representable by DfgVertexVar");
#endif
// Increment reference count
AstNode* const variablep = nodep();
variablep->user1(variablep->user1() + 0x10);
UASSERT_OBJ((variablep->user1() >> 4) > 0, variablep, "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: public:
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp); ~DfgVertexVar() {
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp); // Decrement reference count
inline ~DfgVertexVar(); AstNode* const variablep = nodep();
variablep->user1(variablep->user1() - 0x10);
UASSERT_OBJ((variablep->user1() >> 4) >= 0, variablep, "Reference count underflow");
}
ASTGEN_MEMBERS_DfgVertexVar; ASTGEN_MEMBERS_DfgVertexVar;
const std::string srcName(size_t) const override { return ""; } // The driver of the variable. Might be nullptr if driven exernally (input to DfgGraph).
DfgVertex* srcp() const { return inputp(0); }
void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
// The default value of the variable. This defines the parts not driven by 'srcp', maybe null
DfgVertex* defaultp() const { return inputp(1); }
void defaultp(DfgVertex* vtxp) { inputp(1, vtxp); }
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; } AstVar* varp() const { return m_varp; }
AstVarScope* varScopep() const { return m_varScopep; } AstVarScope* varScopep() const { return m_varScopep; }
AstNode* nodep() const { AstNode* nodep() const {
return m_varScopep ? static_cast<AstNode*>(m_varScopep) : static_cast<AstNode*>(m_varp); return m_varScopep ? static_cast<AstNode*>(m_varScopep) : static_cast<AstNode*>(m_varp);
} }
FileLine* driverFileLine() const { return m_driverFileLine; } // If this is a temporary, the Ast variable it stands for, or same as
void driverFileLine(FileLine* flp) { m_driverFileLine = flp; } // 'nodep()' if it's a temporary with no associated original Ast variable.
AstNode* tmpForp() const { return m_tmpForp; } AstNode* tmpForp() const { return m_tmpForp; }
void tmpForp(AstNode* nodep) { m_tmpForp = nodep; } void tmpForp(AstNode* 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; }
bool isDrivenFullyByDfg() const { bool isDrivenFullyByDfg() const {
return srcp() && !srcp()->is<DfgVertexSplice>() && !varp()->isForced() return srcp() && !srcp()->is<DfgVertexSplice>() && !varp()->isForced()
&& !varp()->isSigUserRWPublic(); && !varp()->isSigUserRWPublic();
@ -105,212 +167,7 @@ public:
return false; return false;
} }
}; };
class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic {
protected:
struct DriverData final {
FileLine* m_flp; // Location of this driver
uint32_t m_lo; // Low index of range driven by this driver
DriverData() = delete;
DriverData(FileLine* flp, uint32_t lo)
: m_flp{flp}
, m_lo{lo} {}
};
std::vector<DriverData> m_driverData; // Additional data associated with each driver
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
V3Hash selfHash() const override VL_MT_DISABLED;
public:
DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexVariadic{dfg, type, flp, dtypep, 2u} {
// Add optional source for 'defaultp'
addSource();
}
ASTGEN_MEMBERS_DfgVertexSplice;
std::pair<const DfgEdge*, size_t> sourceEdges() const override {
const std::pair<const DfgEdge*, size_t> pair = DfgVertexVariadic::sourceEdges();
UASSERT_OBJ(pair.second > 0, this, "default driver edge is missing");
// If it has a default driver that's it
if (pair.first->sourcep()) return pair;
// Otherwise there is one less source
return {pair.first + 1, pair.second - 1};
}
std::pair<DfgEdge*, size_t> sourceEdges() override {
const auto pair = const_cast<const DfgVertexSplice*>(this)->sourceEdges();
return {const_cast<DfgEdge*>(pair.first), pair.second};
}
// Named getter/setter for optional default driver
DfgVertex* defaultp() const { return DfgVertexVariadic::source(0); }
void defaultp(DfgVertex* vtxp) {
UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "default driver can't be a DfgLogic");
const bool found = findSourceEdge([vtxp](const DfgEdge& e, size_t) -> bool { //
return e.sourcep() == vtxp;
});
UASSERT_OBJ(!found, this, "adding existing driver as default");
DfgVertexVariadic::sourceEdge(0)->relinkSource(vtxp);
}
// Add resolved driver
void addDriver(FileLine* flp, uint32_t lo, DfgVertex* vtxp) {
UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "addDriver called with DfgLogic");
UASSERT_OBJ(vtxp != defaultp(), this, "adding default driver as resolved");
m_driverData.emplace_back(flp, lo);
DfgVertexVariadic::addSource()->relinkSource(vtxp);
}
FileLine* driverFileLine(size_t idx) const {
UASSERT_OBJ(!defaultp() || idx > 0, this, "'driverFileLine' called on default driver");
if (defaultp()) --idx;
return m_driverData.at(idx).m_flp;
}
uint32_t driverLo(size_t idx) const {
UASSERT_OBJ(!defaultp() || idx > 0, this, "'driverLo' called on default driver");
if (defaultp()) --idx;
const DriverData& dd = m_driverData.at(idx);
return dd.m_lo;
}
DfgVertex* driverAt(size_t idx) const {
const DfgEdge* const edgep = findSourceEdge([this, idx](const DfgEdge& e, size_t i) { //
// Don't pick the default driver
if (i == 0 && defaultp()) return false;
return driverLo(i) == idx;
});
return edgep ? edgep->sourcep() : nullptr;
}
// If drives the whole result explicitly (not through defaultp), this is
// the actual driver this DfgVertexSplice can be replaced with.
inline DfgVertex* wholep() const;
// cppcheck-suppress duplInheritedMember
void resetSources() {
m_driverData.clear();
// Unlink default driver
DfgVertex* const dp = defaultp();
DfgVertexVariadic::sourceEdge(0)->unlinkSource();
// Reset DfgVertexVariadic sources
DfgVertexVariadic::resetSources();
// Add back the default driver if present
DfgEdge* const edgep = DfgVertexVariadic::addSource();
if (dp) edgep->relinkSource(dp);
}
const std::string srcName(size_t idx) const override {
if (idx == 0 && defaultp()) return "default";
const uint32_t lo = driverLo(idx);
const uint32_t hi = lo + DfgVertexVariadic::source(idx + !defaultp())->size() - 1;
return '[' + std::to_string(hi) + ':' + std::to_string(lo) + ']';
}
};
// === Concrete node types =====================================================
// === DfgVertex ===
class DfgConst final : public DfgVertex {
friend class DfgVertex;
friend class DfgVisitor;
V3Number m_num; // Constant value
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
V3Hash selfHash() const override VL_MT_DISABLED;
public:
inline DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num);
inline DfgConst(DfgGraph& dfg, FileLine* flp, uint32_t width, uint32_t value = 0);
ASTGEN_MEMBERS_DfgConst;
V3Number& num() { return m_num; }
const V3Number& num() const { return m_num; }
size_t toSizeT() const {
if VL_CONSTEXPR_CXX17 (sizeof(size_t) > sizeof(uint32_t)) {
return static_cast<size_t>(num().toUQuad());
}
return static_cast<size_t>(num().toUInt());
}
uint32_t toU32() const { return static_cast<size_t>(num().toUInt()); }
// cppcheck-suppress duplInheritedMember
bool isZero() const { return num().isEqZero(); }
// cppcheck-suppress duplInheritedMember
bool isOnes() const { return num().isEqAllOnes(width()); }
// Does this DfgConst have the given value? Note this is not easy to answer if wider than 32.
bool hasValue(uint32_t value) const {
return !num().isFourState() && num().edataWord(0) == value && num().mostSetBitP1() <= 32;
}
std::pair<DfgEdge*, size_t> sourceEdges() override { return {nullptr, 0}; }
std::pair<const DfgEdge*, size_t> sourceEdges() const override { return {nullptr, 0}; }
const string srcName(size_t) const override { // LCOV_EXCL_START
VL_UNREACHABLE;
return "";
} // LCOV_EXCL_STOP
};
// === DfgVertexBinary ===
class DfgMux final : public DfgVertexBinary {
// AstSel is ternary, but the 'widthp' is always constant and is hence redundant, and
// 'lsbp' is very often constant. As AstSel is fairly common, we special case as a DfgSel for
// the constant 'lsbp', and as 'DfgMux` for the non-constant 'lsbp'.
public:
DfgMux(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexBinary{dfg, dfgType(), flp, dtypep} {}
ASTGEN_MEMBERS_DfgMux;
DfgVertex* fromp() const { return source<0>(); }
void fromp(DfgVertex* vtxp) { relinkSource<0>(vtxp); }
DfgVertex* lsbp() const { return source<1>(); }
void lsbp(DfgVertex* vtxp) { relinkSource<1>(vtxp); }
const string srcName(size_t idx) const override { return idx ? "lsbp" : "fromp"; }
};
// === DfgVertexUnary ===
class DfgSel final : public DfgVertexUnary {
// AstSel is ternary, but the 'widthp' is always constant and is hence redundant, and
// 'lsbp' is very often constant. As AstSel is fairly common, we special case as a DfgSel for
// the constant 'lsbp', and as 'DfgMux` for the non-constant 'lsbp'.
uint32_t m_lsb = 0; // The LSB index
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
V3Hash selfHash() const override VL_MT_DISABLED;
public:
DfgSel(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexUnary{dfg, dfgType(), flp, dtypep} {}
ASTGEN_MEMBERS_DfgSel;
DfgVertex* fromp() const { return source<0>(); }
void fromp(DfgVertex* vtxp) { relinkSource<0>(vtxp); }
uint32_t lsb() const { return m_lsb; }
void lsb(uint32_t value) { m_lsb = value; }
const string srcName(size_t) const override { return "fromp"; }
};
class DfgUnitArray final : public DfgVertexUnary {
// This is a type adapter for modeling arrays. It's a single element array,
// with the value of the single element being the source operand.
public:
DfgUnitArray(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexUnary{dfg, dfgType(), flp, dtypep} {
UASSERT_OBJ(this->dtypep(), flp, "Non array DfgUnitArray");
UASSERT_OBJ(this->size() == 1, flp, "DfgUnitArray must have a single element");
}
ASTGEN_MEMBERS_DfgUnitArray;
const std::string srcName(size_t) const override { return ""; }
};
// === DfgVertexVar ===
class DfgVarArray final : public DfgVertexVar { class DfgVarArray final : public DfgVertexVar {
friend class DfgVertex; friend class DfgVertex;
friend class DfgVisitor; friend class DfgVisitor;
@ -326,6 +183,7 @@ public:
} }
ASTGEN_MEMBERS_DfgVarArray; ASTGEN_MEMBERS_DfgVarArray;
}; };
class DfgVarPacked final : public DfgVertexVar { class DfgVarPacked final : public DfgVertexVar {
friend class DfgVertex; friend class DfgVertex;
friend class DfgVisitor; friend class DfgVisitor;
@ -342,59 +200,286 @@ public:
ASTGEN_MEMBERS_DfgVarPacked; ASTGEN_MEMBERS_DfgVarPacked;
}; };
// === DfgVertexVariadic === //------------------------------------------------------------------------------
class DfgLogic final : public DfgVertexVariadic { // Nullary vertices - 0 inputs
// Generic vertex representing a whole combinational process
AstNode* const m_nodep; // The Ast logic represented by this vertex class DfgVertexNullary VL_NOT_FINAL : public DfgVertex {
const std::unique_ptr<CfgGraph> m_cfgp; protected:
// Vertices this logic was synthesized into. Excluding variables DfgVertexNullary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
std::vector<DfgVertex*> m_synth; : DfgVertex{dfg, type, flp, dtypep} {}
public: public:
DfgLogic(DfgGraph& dfg, AstAssignW* nodep) ASTGEN_MEMBERS_DfgVertexNullary;
: DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr, 1u} std::string srcName(size_t) const override final { V3ERROR_NA_RETURN(""); }
, m_nodep{nodep}
, m_cfgp{nullptr} {}
DfgLogic(DfgGraph& dfg, AstAlways* nodep, std::unique_ptr<CfgGraph> cfgp)
: DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr, 1u}
, m_nodep{nodep}
, m_cfgp{std::move(cfgp)} {}
ASTGEN_MEMBERS_DfgLogic;
void addInput(DfgVertexVar* varp) { addSource()->relinkSource(varp); }
AstNode* nodep() const { return m_nodep; }
CfgGraph& cfg() { return *m_cfgp; }
const CfgGraph& cfg() const { return *m_cfgp; }
std::vector<DfgVertex*>& synth() { return m_synth; }
const std::vector<DfgVertex*>& synth() const { return m_synth; }
const std::string srcName(size_t) const override { return ""; }
}; };
class DfgUnresolved final : public DfgVertexVariadic { class DfgConst final : public DfgVertexNullary {
// Represents a collection of unresolved variable drivers before synthesis friend class DfgVertex;
friend class DfgVisitor;
V3Number m_num; // Constant value
bool selfEquals(const DfgVertex& that) const override {
return num().isCaseEq(that.as<DfgConst>()->num());
}
V3Hash selfHash() const override { return num().toHash(); }
public: public:
DfgUnresolved(DfgGraph& dfg, const DfgVertexVar* vtxp) DfgConst(DfgGraph& dfg, FileLine* flp, const V3Number& num)
: DfgVertexVariadic{dfg, dfgType(), vtxp->fileline(), vtxp->dtypep(), 1u} {} : DfgVertexNullary{dfg, dfgType(), flp, V3Dfg::dtypePacked(num.width())}
ASTGEN_MEMBERS_DfgUnresolved; , m_num{num} {}
DfgConst(DfgGraph& dfg, FileLine* flp, uint32_t width, uint32_t value)
: DfgVertexNullary{dfg, dfgType(), flp, V3Dfg::dtypePacked(width)}
, m_num{flp, static_cast<int>(width), value} {}
// Can only be driven by DfgLogic or DfgVertexSplice ASTGEN_MEMBERS_DfgConst;
void addDriver(DfgLogic* vtxp) { addSource()->relinkSource(vtxp); }
void addDriver(DfgVertexSplice* vtxp) { addSource()->relinkSource(vtxp); }
// cppcheck-suppress duplInheritedMember V3Number& num() { return m_num; }
void clearSources() { DfgVertexVariadic::clearSources(); } const V3Number& num() const { return m_num; }
DfgVertex* singleSource() const { return arity() == 1 ? source(0) : nullptr; } size_t toSizeT() const { return static_cast<size_t>(num().toUQuad()); }
uint32_t toU32() const { return num().toUInt(); }
const std::string srcName(size_t) const override { return ""; } bool isZero() const { return num().isEqZero(); }
bool isOnes() const { return num().isEqAllOnes(width()); }
// Does this DfgConst have the given value? Note this is not easy to answer if wider than 32.
bool hasValue(uint32_t value) const {
return !num().isFourState() && num().edataWord(0) == value && num().mostSetBitP1() <= 32;
}
};
//------------------------------------------------------------------------------
// Unary vertices - 1 inputs
class DfgVertexUnary VL_NOT_FINAL : public DfgVertex {
protected:
DfgVertexUnary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertex{dfg, type, flp, dtypep} {
newInput();
}
public:
ASTGEN_MEMBERS_DfgVertexUnary;
DfgVertex* srcp() const { return inputp(0); }
void srcp(DfgVertex* vtxp) { inputp(0, vtxp); }
std::string srcName(size_t) const override final { return ""; }
};
class DfgSel final : public DfgVertexUnary {
// AstSel is binary, but 'lsbp' is very often constant. As AstSel is fairly
// common, we special case as a DfgSel for the constant 'lsbp', and as
// 'DfgMux` for the non-constant 'lsbp'.
uint32_t m_lsb = 0; // The LSB index
bool selfEquals(const DfgVertex& that) const override {
return lsb() == that.as<DfgSel>()->lsb();
}
V3Hash selfHash() const override { return V3Hash{lsb()}; }
public:
DfgSel(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexUnary{dfg, dfgType(), flp, dtypep} {}
ASTGEN_MEMBERS_DfgSel;
DfgVertex* fromp() const { return srcp(); }
void fromp(DfgVertex* vtxp) { srcp(vtxp); }
uint32_t lsb() const { return m_lsb; }
void lsb(uint32_t value) { m_lsb = value; }
};
class DfgUnitArray final : public DfgVertexUnary {
// This is a type adapter for modeling arrays. It's a single element array,
// with the value of the single element being the source operand.
bool selfEquals(const DfgVertex&) const final { return true; }
V3Hash selfHash() const final { return V3Hash{}; }
public:
DfgUnitArray(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexUnary{dfg, dfgType(), flp, dtypep} {
UASSERT_OBJ(this->dtypep(), flp, "Non array DfgUnitArray");
UASSERT_OBJ(this->size() == 1, flp, "DfgUnitArray must have a single element");
}
ASTGEN_MEMBERS_DfgUnitArray;
};
//------------------------------------------------------------------------------
// Binary vertices - 2 inputs
class DfgVertexBinary VL_NOT_FINAL : public DfgVertex {
protected:
DfgVertexBinary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertex{dfg, type, flp, dtypep} {
newInput();
newInput();
}
public:
ASTGEN_MEMBERS_DfgVertexBinary;
};
class DfgMux final : public DfgVertexBinary {
// AstSel is binary, but 'lsbp' is very often constant. As AstSel is fairly
// common, we special case as a DfgSel for the constant 'lsbp', and as
// 'DfgMux` for the non-constant 'lsbp'.
bool selfEquals(const DfgVertex&) const override { return true; }
V3Hash selfHash() const override { return V3Hash{}; }
public:
DfgMux(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexBinary{dfg, dfgType(), flp, dtypep} {}
ASTGEN_MEMBERS_DfgMux;
DfgVertex* fromp() const { return inputp(0); }
void fromp(DfgVertex* vtxp) { inputp(0, vtxp); }
DfgVertex* lsbp() const { return inputp(1); }
void lsbp(DfgVertex* vtxp) { inputp(1, vtxp); }
std::string srcName(size_t idx) const override { return idx ? "lsbp" : "fromp"; }
};
//------------------------------------------------------------------------------
// Ternary vertices - 3 inputs
class DfgVertexTernary VL_NOT_FINAL : public DfgVertex {
protected:
DfgVertexTernary(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertex{dfg, type, flp, dtypep} {
newInput();
newInput();
newInput();
}
public:
ASTGEN_MEMBERS_DfgVertexTernary;
};
//------------------------------------------------------------------------------
// Variadic vertices - variable number of inputs
class DfgVertexVariadic VL_NOT_FINAL : public DfgVertex {
protected:
DfgVertexVariadic(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertex{dfg, type, flp, dtypep} {}
public:
ASTGEN_MEMBERS_DfgVertexVariadic;
};
class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic {
// Represents a partial update to a varibale
struct DriverData final {
uint32_t m_lo; // Low index of range driven by this driver
FileLine* m_flp; // Location of this driver
DriverData() = delete;
DriverData(uint32_t lo, FileLine* flp)
: m_lo{lo}
, m_flp{flp} {}
};
std::vector<DriverData> m_driverData; // Additional data associated with each driver
bool selfEquals(const DfgVertex& that) const override final {
const DfgVertexSplice* const thatp = that.as<DfgVertexSplice>();
for (size_t i = 0; i < nInputs(); ++i) {
if (m_driverData[i].m_lo != thatp->m_driverData[i].m_lo) return false;
}
return true;
}
V3Hash selfHash() const override final {
V3Hash hash;
const size_t n = nInputs();
for (size_t i = 0; i < n; ++i) hash += m_driverData[i].m_lo;
return hash;
}
protected:
DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: DfgVertexVariadic{dfg, type, flp, dtypep} {}
public:
ASTGEN_MEMBERS_DfgVertexSplice;
// Add driver
void addDriver(DfgVertex* vtxp, uint32_t lo, FileLine* flp) {
UASSERT_OBJ(!vtxp->is<DfgLogic>(), vtxp, "addDriver called with DfgLogic");
m_driverData.emplace_back(lo, flp);
newInput()->relinkSrcp(vtxp);
}
void resetDrivers() {
resetInputs();
m_driverData.clear();
}
std::string srcName(size_t idx) const override final {
const uint32_t lo = m_driverData[idx].m_lo;
const uint32_t hi = lo + inputp(idx)->size() - 1;
return '[' + std::to_string(hi) + ':' + std::to_string(lo) + ']';
}
FileLine* driverFileLine(size_t idx) const { return m_driverData.at(idx).m_flp; }
DfgVertex* driverAt(size_t idx) {
const size_t n = nInputs();
for (size_t i = 0; i < n; ++i) {
if (m_driverData[i].m_lo == idx) return inputp(i);
}
return nullptr;
}
const DfgVertex* driverAt(size_t idx) const {
for (const DriverData& dd : m_driverData) {
if (dd.m_lo == idx) return inputp(idx);
}
return nullptr;
}
// If drives the whole result explicitly (not through defaultp), this is
// the actual driver this DfgVertexSplice can be replaced with.
DfgVertex* wholep() {
if (nInputs() != 1) return nullptr;
if (m_driverData[0].m_lo != 0) return nullptr;
DfgVertex* const vtxp = inputp(0);
if (vtxp->size() != size()) return nullptr;
if (const DfgUnitArray* const uap = vtxp->cast<DfgUnitArray>()) {
if (DfgVertexSplice* const splicep = uap->srcp()->cast<DfgVertexSplice>()) {
if (!splicep->wholep()) return nullptr;
}
}
return vtxp;
}
bool foreachDriver(std::function<bool(DfgVertex&, uint32_t, FileLine*)> f) {
const size_t n = nInputs();
for (size_t i = 0; i < n; ++i) {
if (f(*inputp(i), m_driverData[i].m_lo, m_driverData[i].m_flp)) return true;
}
return false;
}
bool foreachDriver(std::function<bool(const DfgVertex&, uint32_t, FileLine*)> f) const {
const size_t n = nInputs();
for (size_t i = 0; i < n; ++i) {
if (f(*inputp(i), m_driverData[i].m_lo, m_driverData[i].m_flp)) return true;
}
return false;
}
bool foreachDriver(std::function<bool(DfgVertex&, uint32_t)> f) {
const size_t n = nInputs();
for (size_t i = 0; i < n; ++i) {
if (f(*inputp(i), m_driverData[i].m_lo)) return true;
}
return false;
}
bool foreachDriver(std::function<bool(const DfgVertex&, uint32_t)> f) const {
const size_t n = nInputs();
for (size_t i = 0; i < n; ++i) {
if (f(*inputp(i), m_driverData[i].m_lo)) return true;
}
return false;
}
}; };
// === DfgVertexSplice ===
class DfgSpliceArray final : public DfgVertexSplice { class DfgSpliceArray final : public DfgVertexSplice {
friend class DfgVertex; friend class DfgVertex;
friend class DfgVisitor; friend class DfgVisitor;
@ -406,6 +491,7 @@ public:
} }
ASTGEN_MEMBERS_DfgSpliceArray; ASTGEN_MEMBERS_DfgSpliceArray;
}; };
class DfgSplicePacked final : public DfgVertexSplice { class DfgSplicePacked final : public DfgVertexSplice {
friend class DfgVertex; friend class DfgVertex;
friend class DfgVisitor; friend class DfgVisitor;
@ -418,4 +504,70 @@ public:
ASTGEN_MEMBERS_DfgSplicePacked; ASTGEN_MEMBERS_DfgSplicePacked;
}; };
class DfgLogic final : public DfgVertexVariadic {
// Generic vertex representing a whole combinational process
AstNode* const m_nodep; // The Ast logic represented by this vertex
AstScope* const m_scopep; // The AstScope m_nodep is under, iff scoped
const std::unique_ptr<CfgGraph> m_cfgp;
// Vertices this logic was synthesized into. Excluding variables
std::vector<DfgVertex*> m_synth;
// Used very early, should never be needed
bool selfEquals(const DfgVertex&) const final { V3ERROR_NA_RETURN(false); }
V3Hash selfHash() const final { V3ERROR_NA_RETURN(V3Hash{}); }
public:
DfgLogic(DfgGraph& dfg, AstAssignW* nodep, AstScope* scopep)
: DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr}
, m_nodep{nodep}
, m_scopep{scopep}
, m_cfgp{nullptr} {}
DfgLogic(DfgGraph& dfg, AstAlways* nodep, AstScope* scopep, std::unique_ptr<CfgGraph> cfgp)
: DfgVertexVariadic{dfg, dfgType(), nodep->fileline(), nullptr}
, m_nodep{nodep}
, m_scopep{scopep}
, m_cfgp{std::move(cfgp)} {}
ASTGEN_MEMBERS_DfgLogic;
std::string srcName(size_t) const override final { return ""; }
// Can only be driven by DfgVertexVar
void addInput(DfgVertexVar* varp) { newInput()->relinkSrcp(varp); }
// Accessors
AstNode* nodep() const { return m_nodep; }
AstScope* scopep() const { return m_scopep; }
CfgGraph& cfg() { return *m_cfgp; }
const CfgGraph& cfg() const { return *m_cfgp; }
std::vector<DfgVertex*>& synth() { return m_synth; }
const std::vector<DfgVertex*>& synth() const { return m_synth; }
};
class DfgUnresolved final : public DfgVertexVariadic {
// Represents a collection of unresolved variable drivers before synthesis
bool selfEquals(const DfgVertex&) const final { return true; }
V3Hash selfHash() const final { return V3Hash{}; }
public:
DfgUnresolved(DfgGraph& dfg, const DfgVertexVar* vtxp)
: DfgVertexVariadic{dfg, dfgType(), vtxp->fileline(), vtxp->dtypep()} {}
ASTGEN_MEMBERS_DfgUnresolved;
std::string srcName(size_t) const override final { return ""; }
// Can only be driven by DfgLogic or DfgVertexSplice
void addDriver(DfgLogic* vtxp) { newInput()->relinkSrcp(vtxp); }
void addDriver(DfgVertexSplice* vtxp) { newInput()->relinkSrcp(vtxp); }
};
//------------------------------------------------------------------------------
// The rest of the vertex types are generated by 'astgen' from AstNodeExpr
#include "V3Dfg__gen_auto_classes.h"
//------------------------------------------------------------------------------
// Inline method definitions
#endif #endif

View File

@ -678,6 +678,10 @@ def check_types(sortedTypes, prefix, abstractPrefix):
sys.exit("%Error: Non-final {b} subclasses must be named {b}*: {p}{n}".format( sys.exit("%Error: Non-final {b} subclasses must be named {b}*: {p}{n}".format(
b=baseClass, p=prefix, n=node.name)) b=baseClass, p=prefix, n=node.name))
# Skip ordering check for Dfg
if prefix == "Dfg":
return
# Check ordering of node definitions # Check ordering of node definitions
hasOrderingError = False hasOrderingError = False
@ -1169,6 +1173,9 @@ def write_dfg_macros(filename):
" ").format(**fmt).replace("\n", " \\\n")) " ").format(**fmt).replace("\n", " \\\n"))
for node in DfgVertexList: for node in DfgVertexList:
if node.isRoot:
continue
fh.write("#define ASTGEN_MEMBERS_Dfg{t} \\\n".format(t=node.name)) fh.write("#define ASTGEN_MEMBERS_Dfg{t} \\\n".format(t=node.name))
if node.isLeaf: if node.isLeaf:
@ -1183,22 +1190,24 @@ def write_dfg_macros(filename):
for n in range(1, node.arity + 1): for n in range(1, node.arity + 1):
name, _, _, _ = node.getOp(n) name, _, _, _ = node.getOp(n)
emitBlock('''\ emitBlock('''\
DfgVertex* {name}() const {{ return source<{n}>(); }} DfgVertex* {name}() const {{ return inputp({n}); }}
void {name}(DfgVertex* vtxp) {{ relinkSource<{n}>(vtxp); }} void {name}(DfgVertex* vtxp) {{ inputp({n}, vtxp); }}
''', ''',
name=name, name=name,
n=n - 1) n=n - 1)
operandNames = tuple(node.getOp(n)[0] for n in range(1, node.arity + 1)) if node.isLeaf and node.arity > 1:
if operandNames: operandNames = tuple(node.getOp(n)[0] for n in range(1, node.arity + 1))
emitBlock('''\ if operandNames:
const std::string srcName(size_t idx) const override {{ emitBlock('''\
static const char* names[{a}] = {{ {ns} }}; std::string srcName(size_t idx) const override final {{
return names[idx]; static const char* names[{a}] = {{ {ns} }};
}} return names[idx];
''', }}
a=node.arity, ''',
ns=", ".join(map(lambda _: '"' + _ + '"', operandNames))) a=node.arity,
ns=", ".join(map(lambda _: '"' + _ + '"', operandNames)))
fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon fh.write(" static_assert(true, \"\")\n") # Swallowing the semicolon
@ -1215,6 +1224,8 @@ def write_dfg_auto_classes(filename):
emitBlock('''\ emitBlock('''\
class Dfg{t} final : public Dfg{s} {{ class Dfg{t} final : public Dfg{s} {{
bool selfEquals(const DfgVertex&) const final {{ return true; }}
V3Hash selfHash() const final {{ return V3Hash{{}}; }}
public: public:
Dfg{t}(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep) Dfg{t}(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
: Dfg{s}{{dfg, dfgType(), flp, dtypep}} {{}} : Dfg{s}{{dfg, dfgType(), flp, dtypep}} {{}}
@ -1275,12 +1286,12 @@ def write_dfg_ast_to_dfg(filename):
fh.write(" m_foundUnhandled = true;\n") fh.write(" m_foundUnhandled = true;\n")
fh.write(" ++m_ctx.m_conv.nonRepNode;\n") fh.write(" ++m_ctx.m_conv.nonRepNode;\n")
fh.write(" return;\n") fh.write(" return;\n")
fh.write(" }\n\n") fh.write(" }\n")
fh.write(" m_logicp->synth().emplace_back(vtxp);") fh.write(" m_logicp->synth().emplace_back(vtxp);\n\n")
for i in range(node.arity): for i in range(node.arity):
fh.write( fh.write(
" vtxp->relinkSource<{i}>(nodep->op{j}p()->user2u().to<DfgVertex*>());\n". " vtxp->inputp({i}, nodep->op{j}p()->user2u().to<DfgVertex*>());\n".format(
format(i=i, j=i + 1)) i=i, j=i + 1))
fh.write("\n") fh.write("\n")
fh.write(" nodep->user2p(vtxp);\n") fh.write(" nodep->user2p(vtxp);\n")
fh.write("}\n") fh.write("}\n")
@ -1296,7 +1307,7 @@ def write_dfg_dfg_to_ast(filename):
fh.write("void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name)) fh.write("void visit(Dfg{t}* vtxp) override {{\n".format(t=node.name))
for i in range(node.arity): for i in range(node.arity):
fh.write( fh.write(
" AstNodeExpr* const op{j}p = convertDfgVertexToAstNodeExpr(vtxp->source<{i}>());\n" " AstNodeExpr* const op{j}p = convertDfgVertexToAstNodeExpr(vtxp->inputp({i}));\n"
.format(i=i, j=i + 1)) .format(i=i, j=i + 1))
fh.write(" m_resultp = makeNode<Ast{t}>(vtxp".format(t=node.name)) fh.write(" m_resultp = makeNode<Ast{t}>(vtxp".format(t=node.name))
for i in range(node.arity): for i in range(node.arity):
@ -1355,14 +1366,6 @@ check_types(AstNodeList, "Ast", "Node")
# Set up the root DfgVertex type and some other hand-written base types. # Set up the root DfgVertex type and some other hand-written base types.
# These are standalone so we don't need to parse the sources for this. # These are standalone so we don't need to parse the sources for this.
DfgVertices["Vertex"] = Node("Vertex", None) DfgVertices["Vertex"] = Node("Vertex", None)
DfgVertices["VertexUnary"] = Node("VertexUnary", DfgVertices["Vertex"])
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexUnary"])
DfgVertices["VertexBinary"] = Node("VertexBinary", DfgVertices["Vertex"])
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexBinary"])
DfgVertices["VertexTernary"] = Node("VertexTernary", DfgVertices["Vertex"])
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexTernary"])
DfgVertices["VertexVariadic"] = Node("VertexVariadic", DfgVertices["Vertex"])
DfgVertices["Vertex"].addSubClass(DfgVertices["VertexVariadic"])
# AstNodeExpr that are not representable in Dfg # AstNodeExpr that are not representable in Dfg
DfgIgnored = ( DfgIgnored = (
@ -1424,6 +1427,7 @@ DfgIgnored = (
"ReplicateN", "ReplicateN",
"SubstrN", "SubstrN",
"ToLowerN", "ToLowerN",
"ToStringN",
"ToUpperN", "ToUpperN",
# Effectful # Effectful
"PostAdd", "PostAdd",

View File

@ -39,7 +39,9 @@ unusedPrivateFunction:src/V3Const.cpp
// These are all inappropriate // These are all inappropriate
constVariablePointer:src/V3DfgPeephole.cpp constVariablePointer:src/V3DfgPeephole.cpp
constParameterPointer:src/V3DfgPeephole.cpp constParameterPointer:src/V3DfgPeephole.cpp
// Class in non-copyable
noCopyConstructor:src/V3DfgDfgToAst.cpp
noOperatorEq:src/V3DfgDfgToAst.cpp
// TODO: these need fixing/signing off in source // TODO: these need fixing/signing off in source
constParameterPointer:src/V3EmitCFunc.cpp constParameterPointer:src/V3EmitCFunc.cpp

View File

@ -0,0 +1,60 @@
//
// DESCRIPTION: Verilator: DFG optimizer equivalence testing
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by 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

@ -0,0 +1,100 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 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_all')
test.sim_time = 2000000
root = ".."
if not os.path.exists(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

@ -0,0 +1,34 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by 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