verilator/src/V3Dfg.cpp

803 lines
29 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Data flow graph (DFG) representation of logic
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-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
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3Dfg.h"
#include "V3File.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//------------------------------------------------------------------------------
// DfgGraph
//------------------------------------------------------------------------------
DfgGraph::DfgGraph(AstModule* modulep, const string& name)
: m_modulep{modulep}
, m_name{name} {}
DfgGraph::~DfgGraph() {
forEachVertex([](DfgVertex& vtxp) { delete &vtxp; });
}
std::unique_ptr<DfgGraph> DfgGraph::clone() const {
const bool scoped = !modulep();
DfgGraph* const clonep = new DfgGraph{modulep(), name()};
// Map from original vertex to clone
std::unordered_map<const DfgVertex*, DfgVertex*> vtxp2clonep(size() * 2);
// Clone constVertices
for (const DfgConst& vtx : m_constVertices) {
DfgConst* const cp = new DfgConst{*clonep, vtx.fileline(), vtx.num()};
vtxp2clonep.emplace(&vtx, cp);
}
// Clone variable vertices
for (const DfgVertexVar& vtx : m_varVertices) {
const DfgVertexVar* const vp = vtx.as<DfgVertexVar>();
DfgVertexVar* cp = nullptr;
switch (vtx.type()) {
case VDfgType::atVarArray: {
if (scoped) {
cp = new DfgVarArray{*clonep, vp->varScopep()};
} else {
cp = new DfgVarArray{*clonep, vp->varp()};
}
vtxp2clonep.emplace(&vtx, cp);
break;
}
case VDfgType::atVarPacked: {
if (scoped) {
cp = new DfgVarPacked{*clonep, vp->varScopep()};
} else {
cp = new DfgVarPacked{*clonep, vp->varp()};
}
vtxp2clonep.emplace(&vtx, cp);
break;
}
default: {
vtx.v3fatalSrc("Unhandled variable vertex type: " + vtx.typeName());
VL_UNREACHABLE;
break;
}
}
}
// Clone operation vertices
for (const DfgVertex& vtx : m_opVertices) {
switch (vtx.type()) {
#include "V3Dfg__gen_clone_cases.h" // From ./astgen
case VDfgType::atSel: {
DfgSel* const cp = new DfgSel{*clonep, vtx.fileline(), vtx.dtypep()};
cp->lsb(vtx.as<DfgSel>()->lsb());
vtxp2clonep.emplace(&vtx, cp);
break;
}
case VDfgType::atMux: {
DfgMux* const cp = new DfgMux{*clonep, vtx.fileline(), vtx.dtypep()};
vtxp2clonep.emplace(&vtx, cp);
break;
}
case VDfgType::atSpliceArray: {
DfgSpliceArray* const cp = new DfgSpliceArray{*clonep, vtx.fileline(), vtx.dtypep()};
vtxp2clonep.emplace(&vtx, cp);
break;
}
case VDfgType::atSplicePacked: {
DfgSplicePacked* const cp = new DfgSplicePacked{*clonep, vtx.fileline(), vtx.dtypep()};
vtxp2clonep.emplace(&vtx, cp);
break;
}
default: {
vtx.v3fatalSrc("Unhandled operation vertex type: " + vtx.typeName());
VL_UNREACHABLE;
break;
}
}
}
UASSERT(size() == clonep->size(), "Size of clone should be the same");
// Constants have no inputs
// Hook up inputs of cloned variables
for (const DfgVertexVar& vtx : m_varVertices) {
// All variable vertices are unary
if (DfgVertex* const srcp = vtx.srcp()) {
vtxp2clonep.at(&vtx)->as<DfgVertexVar>()->srcp(vtxp2clonep.at(srcp));
}
}
// 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 (DfgVertex* const srcp = edge.sourcep()) {
cp->addDriver(vp->driverFileLine(i), //
vp->driverIndex(i), //
vtxp2clonep.at(srcp));
}
});
break;
}
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 (DfgVertex* const srcp = edge.sourcep()) {
cp->addDriver(vp->driverFileLine(i), //
vp->driverLsb(i), //
vtxp2clonep.at(srcp));
}
});
break;
}
default: {
vtx.v3fatalSrc("Unhandled DfgVertexVariadic sub type: " + vtx.typeName());
VL_UNREACHABLE;
break;
}
}
} 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 (DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) {
cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp));
}
}
}
}
return std::unique_ptr<DfgGraph>{clonep};
}
void DfgGraph::mergeGraphs(std::vector<std::unique_ptr<DfgGraph>>&& otherps) {
if (otherps.empty()) return;
// NODE STATE
// AstVar/AstVarScope::user2p() -> corresponding DfgVertexVar* in 'this' graph
const VNUser2InUse user2InUse;
// Set up Ast Variable -> DfgVertexVar map for 'this' graph
for (DfgVertexVar& vtx : m_varVertices) vtx.nodep()->user2p(&vtx);
// Merge in each of the other graphs
for (const std::unique_ptr<DfgGraph>& otherp : otherps) {
// Process variables
for (DfgVertexVar* const vtxp : otherp->m_varVertices.unlinkable()) {
// Variabels that are present in 'this', make them use the DfgVertexVar in 'this'.
if (DfgVertexVar* const altp = vtxp->nodep()->user2u().to<DfgVertexVar*>()) {
DfgVertex* const srcp = vtxp->srcp();
UASSERT_OBJ(!srcp || !altp->srcp(), vtxp, "At most one alias should be driven");
vtxp->replaceWith(altp);
if (srcp) altp->srcp(srcp);
VL_DO_DANGLING(vtxp->unlinkDelete(*otherp), vtxp);
continue;
}
// Otherwise they will be moved
vtxp->nodep()->user2p(vtxp);
vtxp->m_userCnt = 0;
vtxp->m_graphp = this;
}
m_varVertices.splice(m_varVertices.end(), otherp->m_varVertices);
// Process constants
for (DfgConst& vtx : otherp->m_constVertices) {
vtx.m_userCnt = 0;
vtx.m_graphp = this;
}
m_constVertices.splice(m_constVertices.end(), otherp->m_constVertices);
// Process operations
for (DfgVertex& vtx : otherp->m_opVertices) {
vtx.m_userCnt = 0;
vtx.m_graphp = this;
}
m_opVertices.splice(m_opVertices.end(), otherp->m_opVertices);
// Update graph sizes
m_size += otherp->m_size;
otherp->m_size = 0;
}
}
std::string DfgGraph::makeUniqueName(const std::string& prefix, size_t n) {
// Construct the tmpNameStub if we have not done so yet
if (m_tmpNameStub.empty()) {
// Use the hash of the graph name (avoid long names and non-identifiers)
const std::string name = V3Hash{m_name}.toString();
// We need to keep every variable globally unique, and graph hashed
// names might not be, so keep a static table to track multiplicity
static std::unordered_map<std::string, uint32_t> s_multiplicity;
m_tmpNameStub += '_' + name + '_' + std::to_string(s_multiplicity[name]++) + '_';
}
// Assemble the globally unique name
return "__Vdfg" + prefix + m_tmpNameStub + std::to_string(n);
}
DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name, AstNodeDType* dtypep,
AstScope* scopep) {
UASSERT_OBJ(!!scopep != !!modulep(), flp,
"makeNewVar scopep should only be provided for a scoped DfgGraph");
// Create AstVar
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
if (scopep) {
// Add AstVar to the scope's module
scopep->modp()->addStmtsp(varp);
// Create AstVarScope
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
// Add to scope
scopep->addVarsp(vscp);
// Create and return the corresponding variable vertex
if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) return new DfgVarArray{*this, vscp};
return new DfgVarPacked{*this, vscp};
} else {
// Add AstVar to containing module
modulep()->addStmtsp(varp);
// Create and return the corresponding variable vertex
if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) return new DfgVarArray{*this, varp};
return new DfgVarPacked{*this, varp};
}
}
static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
// Dump one DfgVertex in Graphviz format
static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
if (const DfgVarPacked* const varVtxp = vtx.cast<DfgVarPacked>()) {
AstNode* const nodep = varVtxp->nodep();
AstVar* const varp = varVtxp->varp();
os << toDotId(vtx);
os << " [label=\"" << nodep->name() << '\n';
os << cvtToHex(varVtxp) << '\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 {
os << ", shape=box";
}
os << "]\n";
return;
}
if (const DfgVarArray* const arrVtxp = vtx.cast<DfgVarArray>()) {
AstNode* const nodep = arrVtxp->nodep();
AstVar* const varp = arrVtxp->varp();
os << toDotId(vtx);
os << " [label=\"" << nodep->name() << '\n';
os << cvtToHex(arrVtxp) << '\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 {
os << ", shape=box3d";
}
os << "]\n";
return;
}
if (const DfgConst* const constVtxp = vtx.cast<DfgConst>()) {
const V3Number& num = constVtxp->num();
os << toDotId(vtx);
os << " [label=\"";
if (num.width() <= 32 && !num.isSigned()) {
os << constVtxp->width() << "'d" << num.toUInt() << '\n';
os << constVtxp->width() << "'h" << std::hex << num.toUInt() << std::dec << '\n';
} else {
os << num.ascii() << '\n';
}
os << cvtToHex(constVtxp) << '\n';
os << '"';
os << ", shape=plain";
os << "]\n";
return;
}
if (const DfgSel* const selVtxp = vtx.cast<DfgSel>()) {
const uint32_t lsb = selVtxp->lsb();
const uint32_t msb = lsb + selVtxp->width() - 1;
os << toDotId(vtx);
os << " [label=\"SEL _[" << msb << ":" << lsb << "]\n";
os << cvtToHex(selVtxp) << '\n';
vtx.dtypep()->dumpSmall(os);
os << " / F" << vtx.fanout() << '"';
if (vtx.hasMultipleSinks()) {
os << ", shape=doublecircle";
} else {
os << ", shape=circle";
}
os << "]\n";
return;
}
if (vtx.is<DfgVertexSplice>()) {
os << toDotId(vtx);
os << " [label=\"" << vtx.typeName() << '\n';
os << cvtToHex(&vtx) << '\n';
vtx.dtypep()->dumpSmall(os);
os << " / F" << vtx.fanout() << '"';
if (vtx.hasMultipleSinks()) {
os << ", shape=doubleoctagon";
} else {
os << ", shape=octagon";
}
os << "]\n";
return;
}
os << toDotId(vtx);
os << " [label=\"" << vtx.typeName() << '\n';
os << cvtToHex(&vtx) << '\n';
vtx.dtypep()->dumpSmall(os);
os << " / F" << vtx.fanout() << '"';
if (vtx.hasMultipleSinks()) {
os << ", shape=doublecircle";
} else {
os << ", shape=circle";
}
os << "]\n";
}
// Dump one DfgEdge in Graphviz format
static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, size_t idx) {
if (!edge.sourcep()) return;
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
// Header
os << "digraph dfg {\n";
os << "graph [label=\"" << name();
if (!label.empty()) os << "-" << label;
os << "\", labelloc=t, labeljust=l]\n";
os << "graph [rankdir=LR]\n";
if (!p) {
// Emit all vertices and edges
forEachVertex([&](const DfgVertex& vtx) {
dumpDotVertex(os, vtx);
vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) { dumpDotEdge(os, e, i); });
});
} else {
// Emit vertices that satify the predicate 'p'
forEachVertex([&](const DfgVertex& vtx) {
if (!p(vtx)) return;
dumpDotVertex(os, vtx);
vtx.forEachSourceEdge([&](const DfgEdge& e, size_t i) {
if (!e.sourcep() || !p(*e.sourcep())) return;
dumpDotEdge(os, e, i);
});
});
}
// Footer
os << "}\n";
}
std::string DfgGraph::dumpDotString(const std::string& label,
std::function<bool(const DfgVertex&)> p) const {
std::stringstream ss;
dumpDot(ss, label, p);
return ss.str();
}
void DfgGraph::dumpDotFile(const std::string& filename, const std::string& label,
std::function<bool(const DfgVertex&)> p) const {
const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(filename)};
if (os->fail()) v3fatal("Can't write file: " << filename);
dumpDot(*os.get(), label, p);
os->close();
}
void DfgGraph::dumpDotFilePrefixed(const std::string& label,
std::function<bool(const DfgVertex&)> p) const {
std::string filename = name();
if (!label.empty()) filename += "-" + label;
dumpDotFile(v3Global.debugFilename(filename) + ".dot", label, p);
}
// LCOV_EXCL_START // Debug functions for developer use only
void DfgGraph::dumpDotUpstreamCone(const std::string& fileName, const DfgVertex& vtx,
const std::string& name) const {
// Open output file
const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(fileName)};
if (os->fail()) v3fatal("Can't write file: " << fileName);
// Header
*os << "digraph dfg {\n";
if (!name.empty()) *os << "graph [label=\"" << name << "\", labelloc=t, labeljust=l]\n";
*os << "graph [rankdir=LR]\n";
// Work queue for depth first traversal starting from this vertex
std::vector<const DfgVertex*> queue{&vtx};
// Set of already visited vertices
std::unordered_set<const DfgVertex*> visited;
// Depth first traversal
while (!queue.empty()) {
// Pop next work item
const DfgVertex* const vtxp = queue.back();
queue.pop_back();
// Mark vertex as visited
const bool isFirstEncounter = visited.insert(vtxp).second;
// If we have already visited this vertex during the traversal, then move on.
if (!isFirstEncounter) continue;
// Enqueue all sources of this vertex.
vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); });
// Emit this vertex and all of its source edges
dumpDotVertex(*os, *vtxp);
vtxp->forEachSourceEdge([&](const DfgEdge& e, size_t i) { dumpDotEdge(*os, e, i); });
}
// Footer
*os << "}\n";
// Done
os->close();
}
// LCOV_EXCL_STOP
//------------------------------------------------------------------------------
// DfgEdge
//------------------------------------------------------------------------------
void DfgEdge::unlinkSource() {
if (!m_sourcep) return;
#ifdef VL_DEBUG
{
DfgEdge* sinkp = m_sourcep->m_sinksp;
while (sinkp) {
if (sinkp == this) break;
sinkp = sinkp->m_nextp;
}
UASSERT(sinkp, "'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}
, m_dtypep{dtypep}
, m_type{type} {
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;
// If different type, then not equal
if (this->type() != that.type()) return false;
// If different data type, then not equal
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;
// Check vertex specifics
if (!this->selfEquals(that)) return false;
// Check sources
const auto key = (this < &that) ? EqualsCache::key_type{this, &that} //
: EqualsCache::key_type{&that, this};
// 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;
}
}
}
return result >> 1;
}
V3Hash DfgVertex::hash() {
V3Hash& result = user<V3Hash>();
if (!result.value()) {
V3Hash hash{selfHash()};
// Variables are defined by themselves, so there is no need to hash them further
// (especially the sources). This enables sound hashing of graphs circular only through
// variables, which we rely on.
if (!is<DfgVertexVar>()) {
hash += m_type;
if (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 arity = pair.second;
// Sources must always be connected in well-formed graphs
for (size_t i = 0; i < arity; ++i) hash += edgesp[i].m_sourcep->hash();
}
result = hash;
}
return result;
}
uint32_t DfgVertex::fanout() const {
uint32_t result = 0;
forEachSinkEdge([&](const DfgEdge&) { ++result; });
return result;
}
DfgVertexVar* DfgVertex::getResultVar() {
// It's easy if the vertex is already a variable ...
if (DfgVertexVar* const varp = this->cast<DfgVertexVar>()) return varp;
// Inspect existing variables written by this vertex, and choose one
DfgVertexVar* resp = nullptr;
// cppcheck-has-bug-suppress constParameter
this->forEachSink([&resp](DfgVertex& sink) {
DfgVertexVar* const varp = sink.cast<DfgVertexVar>();
if (!varp) return;
// First variable found
if (!resp) {
resp = varp;
return;
}
// Prefer those variables that must be kept anyway
if (resp->hasExtRefs() != varp->hasExtRefs()) {
if (!resp->hasExtRefs()) resp = varp;
return;
}
if (resp->hasModWrRefs() != varp->hasModWrRefs()) {
if (!resp->hasModWrRefs()) resp = varp;
return;
}
if (resp->hasDfgRefs() != varp->hasDfgRefs()) {
if (!resp->hasDfgRefs()) resp = varp;
return;
}
// Prefer those that already have module references
if (resp->hasModRdRefs() != varp->hasModRdRefs()) {
if (!resp->hasModRdRefs()) resp = varp;
return;
}
// 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;
}
// 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;
}
// 'resp' and 'varp' are all the same, keep using the existing 'resp'
});
return resp;
}
AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED {
// If this is a variable, we are done
if (DfgVertexVar* const varp = this->cast<DfgVertexVar>()) return varp->varScopep()->scopep();
// Try the result var first if instructed (usully only in the recursive case)
if (tryResultVar) {
if (DfgVertexVar* const varp = this->getResultVar()) return varp->varScopep()->scopep();
}
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
AstScope*& resultr = cache[this];
if (!resultr) {
// Mark to prevent infinite recursion on circular graphs - should never be called on such
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) {
DfgEdge& edge = edges.first[i];
foundp = edge.sourcep()->scopep(cache, true);
if (foundp != rootp) break;
}
resultr = foundp;
}
// Die on a graph circular through operation vertices
UASSERT_OBJ(resultr != reinterpret_cast<AstScope*>(1), this,
"DfgVertex::scopep called on graph with circular operations");
// Done
return resultr;
}
void DfgVertex::unlinkDelete(DfgGraph& dfg) {
// Unlink source edges
forEachSourceEdge([](DfgEdge& edge, size_t) { edge.unlinkSource(); });
// Unlink sink edges
forEachSinkEdge([](DfgEdge& edge) { edge.unlinkSource(); });
// Remove from graph
dfg.removeVertex(*this);
// Delete
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()}; }
// DfgSpliceArray ----------
bool DfgSpliceArray::selfEquals(const DfgVertex& that) const {
const DfgSpliceArray* const thatp = that.as<DfgSpliceArray>();
const size_t arity = this->arity();
for (size_t i = 0; i < arity; ++i) {
if (driverIndex(i) != thatp->driverIndex(i)) return false;
}
return true;
}
V3Hash DfgSpliceArray::selfHash() const {
V3Hash hash;
const size_t arity = this->arity();
for (size_t i = 0; i < arity; ++i) hash += driverIndex(i);
return hash;
}
// DfgSplicePacked ----------
bool DfgSplicePacked::selfEquals(const DfgVertex& that) const {
const DfgSplicePacked* const thatp = that.as<DfgSplicePacked>();
const size_t arity = this->arity();
for (size_t i = 0; i < arity; ++i) {
if (driverLsb(i) != thatp->driverLsb(i)) return false;
}
return true;
}
V3Hash DfgSplicePacked::selfHash() const {
V3Hash hash;
const size_t arity = this->arity();
for (size_t i = 0; i < arity; ++i) hash += driverLsb(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