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
24 changed files with 1763 additions and 1733 deletions
+147 -295
View File
@@ -24,15 +24,42 @@
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)
: m_modulep{modulep}
, m_name{name} {}
DfgGraph::~DfgGraph() {
forEachVertex([](DfgVertex& vtxp) { delete &vtxp; });
forEachVertex([&](DfgVertex& vtx) { vtx.unlinkDelete(*this); });
}
std::unique_ptr<DfgGraph> DfgGraph::clone() const {
@@ -133,40 +160,21 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
// Constants have no inputs
// Hook up inputs of cloned variables
for (const DfgVertexVar& vtx : m_varVertices) {
// All variable vertices are unary
if (const DfgVertex* const srcp = vtx.srcp()) {
vtxp2clonep.at(&vtx)->as<DfgVertexVar>()->srcp(vtxp2clonep.at(srcp));
}
DfgVertexVar* const cp = vtxp2clonep.at(&vtx)->as<DfgVertexVar>();
if (const DfgVertex* const srcp = vtx.srcp()) cp->srcp(vtxp2clonep.at(srcp));
if (const DfgVertex* const defp = vtx.defaultp()) cp->defaultp(vtxp2clonep.at(defp));
}
// Hook up inputs of cloned operation vertices
for (const DfgVertex& vtx : m_opVertices) {
if (vtx.is<DfgVertexVariadic>()) {
switch (vtx.type()) {
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::atSpliceArray:
case VDfgType::atSplicePacked: {
const DfgSplicePacked* const vp = vtx.as<DfgSplicePacked>();
DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as<DfgSplicePacked>();
vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
if (const DfgVertex* const srcVp = edge.sourcep()) {
DfgVertex* const srcCp = vtxp2clonep.at(srcVp);
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);
}
}
const DfgVertexSplice* const vp = vtx.as<DfgVertexSplice>();
DfgVertexSplice* const cp = vtxp2clonep.at(vp)->as<DfgVertexSplice>();
vp->foreachDriver([&](const DfgVertex& src, uint32_t lo, FileLine* flp) {
cp->addDriver(vtxp2clonep.at(&src), lo, flp);
return false;
});
break;
}
@@ -178,14 +186,8 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
}
} else {
DfgVertex* const cp = vtxp2clonep.at(&vtx);
const auto oSourceEdges = vtx.sourceEdges();
auto cSourceEdges = cp->sourceEdges();
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));
}
for (size_t i = 0; i < vtx.nInputs(); ++i) {
cp->inputp(i, vtxp2clonep.at(vtx.inputp(i)));
}
}
}
@@ -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'.
if (DfgVertexVar* const altp = vtxp->nodep()->user2u().to<DfgVertexVar*>()) {
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);
if (srcp) altp->srcp(srcp);
if (defaultp) altp->defaultp(defaultp);
VL_DO_DANGLING(vtxp->unlinkDelete(*otherp), vtxp);
continue;
}
@@ -285,68 +290,53 @@ static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&
// Dump one DfgVertex in Graphviz format
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
if (const DfgVarPacked* const varVtxp = vtx.cast<DfgVarPacked>()) {
if (const DfgVertexVar* const varVtxp = vtx.cast<DfgVertexVar>()) {
const AstNode* const nodep = varVtxp->nodep();
const AstVar* const varp = varVtxp->varp();
os << toDotId(vtx);
os << " [label=\"" << nodep->prettyName() << '\n';
os << cvtToHex(varVtxp) << '\n';
// Begin attributes
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()) {
os << "temporary for: " << tmpForp->prettyName() << "\n";
if (tmpForp != nodep) os << "\ntemporary for: " << tmpForp->prettyName();
}
// Type and fanout
os << '\n';
varVtxp->dtypep()->dumpSmall(os);
os << " / F" << varVtxp->fanout() << '"';
if (varp->direction() == VDirection::INPUT) {
os << ", shape=box, style=filled, fillcolor=chartreuse2"; // Green
} else if (varp->direction() == VDirection::OUTPUT) {
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 << " / F" << varVtxp->fanout();
// End 'label'
os << '"';
// Shape
if (varVtxp->is<DfgVarPacked>()) {
os << ", shape=box";
}
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 {
} else if (varVtxp->is<DfgVarArray>()) {
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";
return;
}
@@ -405,8 +395,12 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
os << toDotId(vtx);
std::stringstream 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 << VString::replaceSubstr(VString::replaceSubstr(ss.str(), "\n", "\\l"), "\"", "\\\"");
os << str;
os << "\\n" << cvtToHex(&vtx);
os << "\"\n";
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";
}
// 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,
std::function<bool(const DfgVertex&)> p) const {
// 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);
});
// Emit all edges
forEachVertex([&](const DfgVertex& vtx) { //
forEachVertex([&](const DfgVertex& vtx) {
if (!p(vtx)) return;
vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { //
if (!e.sourcep() || !p(*e.sourcep())) return;
dumpDotEdge(os, e, i);
});
for (size_t i = 0; i < vtx.nInputs(); ++i) {
DfgVertex* const srcp = vtx.inputp(i);
if (!srcp) continue;
if (!p(*srcp)) continue;
os << toDotId(*srcp) << " -> " << toDotId(vtx);
os << " [headlabel=\"" << vtx.srcName(i) << "\"]";
os << '\n';
}
});
// Footer
@@ -527,9 +514,15 @@ dfgGraphCollectCone(const std::vector<const DfgVertex*>& vtxps) {
if (!resp->insert(vtxp).second) continue;
// Enqueue all siblings of this vertex.
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 {
vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); });
vtxp->foreachSource([&](const DfgVertex& src) {
queue.push_back(&src);
return false;
});
}
}
// Done
@@ -546,73 +539,8 @@ DfgGraph::sinkCone(const std::vector<const DfgVertex*>& vtxps) const {
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(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
: m_filelinep{flp}
@@ -621,12 +549,6 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType*
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 {
// If same vertex, then equal
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 different number of inputs, then not equal
auto thisPair = this->sourceEdges();
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;
if (this->nInputs() != that.nInputs()) return false;
// Check vertex specifics
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
uint8_t& result = cache[key];
if (!result) {
result = 2; // Assume equals
for (size_t i = 0; i < thisArity; ++i) {
const DfgVertex* const thisSrcVtxp = thisSrcEdgesp[i].m_sourcep;
const DfgVertex* const thatSrcVtxp = thatSrcEdgesp[i].m_sourcep;
if (thisSrcVtxp == thatSrcVtxp) continue;
if (!thisSrcVtxp || !thatSrcVtxp || !thisSrcVtxp->equals(*thatSrcVtxp, cache)) {
result = 1; // Mark not equal
break;
const bool equal = [&]() {
for (size_t i = 0; i < nInputs(); ++i) {
const DfgVertex* const ap = this->inputp(i);
const DfgVertex* const bp = that.inputp(i);
if (!ap && !bp) continue;
if (!ap || !bp) return false;
if (!ap->equals(*bp, cache)) return false;
}
}
return true;
}();
result = (static_cast<uint8_t>(equal) << 1) | 1;
}
return result >> 1;
}
@@ -678,17 +595,11 @@ V3Hash DfgVertex::hash() {
// variables, which we rely on.
if (!is<DfgVertexVar>()) {
hash += m_type;
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep(), UnpackArrayDType)) {
hash += adtypep->elementsConst();
// TODO: maybe include sub-dtype, but not hugely important at the moment
} else {
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();
hash += size();
foreachSource([&](DfgVertex& vtx) {
hash += vtx.hash();
return false;
});
}
result = hash;
}
@@ -697,7 +608,10 @@ V3Hash DfgVertex::hash() {
uint32_t DfgVertex::fanout() const {
uint32_t result = 0;
forEachSinkEdge([&](const DfgEdge&) { ++result; });
foreachSink([&](const DfgVertex&) {
++result;
return false;
});
return result;
}
@@ -708,51 +622,52 @@ DfgVertexVar* DfgVertex::getResultVar() {
// Inspect existing variables written by this vertex, and choose one
DfgVertexVar* resp = nullptr;
// cppcheck-has-bug-suppress constParameter
this->forEachSink([&resp](DfgVertex& sink) {
this->foreachSink([&resp](DfgVertex& sink) {
DfgVertexVar* const varp = sink.cast<DfgVertexVar>();
if (!varp) return;
if (!varp) return false;
// First variable found
if (!resp) {
resp = varp;
return;
return false;
}
// Prefer those variables that must be kept anyway
if (resp->hasExtRefs() != varp->hasExtRefs()) {
if (!resp->hasExtRefs()) resp = varp;
return;
return false;
}
if (resp->hasModWrRefs() != varp->hasModWrRefs()) {
if (!resp->hasModWrRefs()) resp = varp;
return;
return false;
}
if (resp->hasDfgRefs() != varp->hasDfgRefs()) {
if (!resp->hasDfgRefs()) resp = varp;
return;
return false;
}
// Prefer those that already have module references
if (resp->hasModRdRefs() != varp->hasModRdRefs()) {
if (!resp->hasModRdRefs()) resp = varp;
return;
return false;
}
// Prefer real variabels over temporaries
if (!resp->tmpForp() != !varp->tmpForp()) {
if (resp->tmpForp()) resp = varp;
return;
return false;
}
// Prefer the earlier one in source order
const FileLine& oldFlp = *(resp->fileline());
const FileLine& newFlp = *(varp->fileline());
if (const int cmp = oldFlp.operatorCompare(newFlp)) {
if (cmp > 0) resp = varp;
return;
return false;
}
// Prefer the one with the lexically smaller name
if (const int cmp = resp->nodep()->name().compare(varp->nodep()->name())) {
if (cmp > 0) resp = varp;
return;
return false;
}
// 'resp' and 'varp' are all the same, keep using the existing 'resp'
return false;
});
return resp;
}
@@ -777,14 +692,16 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED
resultr = reinterpret_cast<AstScope*>(1);
// Find scope based on sources, falling back on the root scope
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
AstScope* foundp = rootp;
const auto edges = sourceEdges();
for (size_t i = 0; i < edges.second; ++i) {
const DfgEdge& edge = edges.first[i];
foundp = edge.sourcep()->scopep(cache, true);
if (foundp != rootp) break;
}
resultr = foundp;
AstScope* foundp = nullptr;
foreachSource([&](DfgVertex& src) {
AstScope* const scp = src.scopep(cache, true);
if (scp != rootp) {
foundp = scp;
return true;
}
return false;
});
resultr = foundp ? foundp : rootp;
}
// 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) {
// Unlink source edges
forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
// Unlink sink edges
forEachSinkEdge([](DfgEdge& edge) { edge.unlinkSource(); });
while (!m_sinks.empty()) m_sinks.frontp()->unlinkSrcp();
// Remove from graph
dfg.removeVertex(*this);
// Delete
// Delete - this will unlink sources
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
//------------------------------------------------------------------------------
#include "V3Dfg__gen_visitor_defns.h" // From ./astgen