Internals: Improve DFG dumping functions

This commit is contained in:
Geza Lore 2025-08-10 09:19:16 +01:00
parent e6e52dd60a
commit 7696b0651c
3 changed files with 69 additions and 34 deletions

View File

@ -259,7 +259,7 @@ DfgVertexVar* DfgGraph::makeNewVar(FileLine* flp, const std::string& name, AstNo
} }
} }
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; } static const std::string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
// 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) {
@ -383,25 +383,20 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
} }
// Dump one DfgEdge in Graphviz format // Dump one DfgEdge in Graphviz format
static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, const string& headlabel) { static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, size_t idx) {
os << toDotId(*edge.sourcep()) << " -> " << toDotId(*edge.sinkp()); if (!edge.sourcep()) return;
if (!headlabel.empty()) os << " [headlabel=\"" << headlabel << "\"]"; 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'; os << '\n';
} }
// Dump one DfgVertex and all of its source DfgEdges in Graphviz format void DfgGraph::dumpDot(std::ostream& os, const std::string& label,
static void dumpDotVertexAndSourceEdges(std::ostream& os, const DfgVertex& vtx) { std::function<bool(const DfgVertex&)> p) const {
dumpDotVertex(os, vtx); // This generates a graphviz dump, https://www.graphviz.org
vtx.forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { //
if (edge.sourcep()) {
string headLabel;
if (vtx.arity() > 1 || vtx.is<DfgVertexSplice>()) headLabel = vtx.srcName(idx);
dumpDotEdge(os, edge, headLabel);
}
});
}
void DfgGraph::dumpDot(std::ostream& os, const string& label) const {
// Header // Header
os << "digraph dfg {\n"; os << "digraph dfg {\n";
os << "graph [label=\"" << name(); os << "graph [label=\"" << name();
@ -409,31 +404,53 @@ void DfgGraph::dumpDot(std::ostream& os, const string& label) const {
os << "\", labelloc=t, labeljust=l]\n"; os << "\", labelloc=t, labeljust=l]\n";
os << "graph [rankdir=LR]\n"; os << "graph [rankdir=LR]\n";
// Emit all vertices if (!p) {
forEachVertex([&](const DfgVertex& vtx) { dumpDotVertexAndSourceEdges(os, vtx); }); // 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 // Footer
os << "}\n"; os << "}\n";
} }
void DfgGraph::dumpDotFile(const string& filename, const string& label) const { std::string DfgGraph::dumpDotString(const std::string& label,
// This generates a file used by graphviz, https://www.graphviz.org std::function<bool(const DfgVertex&)> p) const {
// "hardcoded" parameters: 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)}; const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(filename)};
if (os->fail()) v3fatal("Can't write file: " << filename); if (os->fail()) v3fatal("Can't write file: " << filename);
dumpDot(*os.get(), label); dumpDot(*os.get(), label, p);
os->close(); os->close();
} }
void DfgGraph::dumpDotFilePrefixed(const string& label) const { void DfgGraph::dumpDotFilePrefixed(const std::string& label,
string filename = name(); std::function<bool(const DfgVertex&)> p) const {
std::string filename = name();
if (!label.empty()) filename += "-" + label; if (!label.empty()) filename += "-" + label;
dumpDotFile(v3Global.debugFilename(filename) + ".dot", label); dumpDotFile(v3Global.debugFilename(filename) + ".dot", label, p);
} }
// LCOV_EXCL_START // Debug function for developer use only // LCOV_EXCL_START // Debug functions for developer use only
void DfgGraph::dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx, void DfgGraph::dumpDotUpstreamCone(const std::string& fileName, const DfgVertex& vtx,
const string& name) const { const std::string& name) const {
// Open output file // Open output file
const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(fileName)}; const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(fileName)};
if (os->fail()) v3fatal("Can't write file: " << fileName); if (os->fail()) v3fatal("Can't write file: " << fileName);
@ -465,7 +482,8 @@ void DfgGraph::dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx,
vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); }); vtxp->forEachSource([&](const DfgVertex& src) { queue.push_back(&src); });
// Emit this vertex and all of its source edges // Emit this vertex and all of its source edges
dumpDotVertexAndSourceEdges(*os, *vtxp); dumpDotVertex(*os, *vtxp);
vtxp->forEachSourceEdge([&](const DfgEdge& e, size_t i) { dumpDotEdge(*os, e, i); });
} }
// Footer // Footer

View File

@ -746,17 +746,27 @@ public:
// Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of // Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of
// the graph which is included in the output. // the graph which is included in the output.
void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED; // If the predicate function 'p' is provided, only those vertices are dumped that satifty it.
void dumpDot(std::ostream& os, const std::string& label,
std::function<bool(const DfgVertex&)> p = {}) const VL_MT_DISABLED;
// Dump graph in Graphviz format into a new file with the given 'filename'. 'label' is added to // Dump graph in Graphviz format into a new file with the given 'filename'. 'label' is added to
// the name of the graph which is included in the output. // the name of the graph which is included in the output.
void dumpDotFile(const string& filename, const string& label = "") const VL_MT_DISABLED; // If the predicate function 'p' is provided, only those vertices are dumped that satifty it.
void dumpDotFile(const std::string& filename, const std::string& label,
std::function<bool(const DfgVertex&)> p = {}) const VL_MT_DISABLED;
// Same as dumpDotFile, but returns the contents as a string.
std::string dumpDotString(const std::string& label,
std::function<bool(const DfgVertex&)> p = {}) const VL_MT_DISABLED;
// Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is // Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is
// added to the name of the graph, which is included in the file name and the output. // added to the name of the graph, which is included in the file name and the output.
void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED; // If the predicate function 'p' is provided, only those vertices are dumped that satifty it.
void dumpDotFilePrefixed(const std::string& label,
std::function<bool(const DfgVertex&)> p = {}) const VL_MT_DISABLED;
// Dump upstream (source) logic cone starting from given vertex into a file with the given // Dump upstream (source) logic cone starting from given vertex into a file with the given
// 'filename'. 'name' is the name of the graph, which is included in the output. // 'filename'. 'name' is the name of the graph, which is included in the output.
void dumpDotUpstreamCone(const string& filename, const DfgVertex& vtx, void dumpDotUpstreamCone(const std::string& filename, const DfgVertex& vtx,
const string& name = "") const VL_MT_DISABLED; const std::string& name = "") const VL_MT_DISABLED;
}; };
// Specializations of privateTypeTest // Specializations of privateTypeTest

View File

@ -1060,6 +1060,13 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) {
} }
} while (nImprovements != prevNImprovements); } while (nImprovements != prevNImprovements);
if (dumpDfgLevel() >= 9) {
const auto userDataInUse = res.userDataInUse();
V3DfgPasses::colorStronglyConnectedComponents(res);
res.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-remaining",
[](const DfgVertex& vtx) { return vtx.getUser<uint64_t>(); });
}
// If an improvement was made, return the still cyclic improved graph // If an improvement was made, return the still cyclic improved graph
if (nImprovements) { if (nImprovements) {
UINFO(7, "Graph was improved " << nImprovements << " times"); UINFO(7, "Graph was improved " << nImprovements << " times");