From 7696b0651c5209a77134611171218479ae82cf18 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 10 Aug 2025 09:19:16 +0100 Subject: [PATCH] Internals: Improve DFG dumping functions --- src/V3Dfg.cpp | 76 +++++++++++++++++++++++++--------------- src/V3Dfg.h | 20 ++++++++--- src/V3DfgBreakCycles.cpp | 7 ++++ 3 files changed, 69 insertions(+), 34 deletions(-) diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 60167ed2e..ca744c99e 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -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 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 -static void dumpDotEdge(std::ostream& os, const DfgEdge& edge, const string& headlabel) { - os << toDotId(*edge.sourcep()) << " -> " << toDotId(*edge.sinkp()); - if (!headlabel.empty()) os << " [headlabel=\"" << headlabel << "\"]"; +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()) { + os << " [headlabel=\"" << sink.srcName(idx) << "\"]"; + } os << '\n'; } -// Dump one DfgVertex and all of its source DfgEdges in Graphviz format -static void dumpDotVertexAndSourceEdges(std::ostream& os, const DfgVertex& vtx) { - dumpDotVertex(os, vtx); - vtx.forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { // - if (edge.sourcep()) { - string headLabel; - if (vtx.arity() > 1 || vtx.is()) headLabel = vtx.srcName(idx); - dumpDotEdge(os, edge, headLabel); - } - }); -} +void DfgGraph::dumpDot(std::ostream& os, const std::string& label, + std::function p) const { + // This generates a graphviz dump, https://www.graphviz.org -void DfgGraph::dumpDot(std::ostream& os, const string& label) const { // Header os << "digraph dfg {\n"; 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 << "graph [rankdir=LR]\n"; - // Emit all vertices - forEachVertex([&](const DfgVertex& vtx) { dumpDotVertexAndSourceEdges(os, vtx); }); + 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"; } -void DfgGraph::dumpDotFile(const string& filename, const string& label) const { - // This generates a file used by graphviz, https://www.graphviz.org - // "hardcoded" parameters: +std::string DfgGraph::dumpDotString(const std::string& label, + std::function 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 p) const { const std::unique_ptr os{V3File::new_ofstream(filename)}; if (os->fail()) v3fatal("Can't write file: " << filename); - dumpDot(*os.get(), label); + dumpDot(*os.get(), label, p); os->close(); } -void DfgGraph::dumpDotFilePrefixed(const string& label) const { - string filename = name(); +void DfgGraph::dumpDotFilePrefixed(const std::string& label, + std::function p) const { + std::string filename = name(); 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 -void DfgGraph::dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx, - const string& name) const { +// 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 os{V3File::new_ofstream(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); }); // 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 diff --git a/src/V3Dfg.h b/src/V3Dfg.h index d9675b627..2ee391d9f 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -746,17 +746,27 @@ public: // 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. - 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 p = {}) const VL_MT_DISABLED; // 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. - 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 p = {}) const VL_MT_DISABLED; + // Same as dumpDotFile, but returns the contents as a string. + std::string dumpDotString(const std::string& label, + std::function p = {}) const VL_MT_DISABLED; // 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. - 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 p = {}) const VL_MT_DISABLED; + // 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. - void dumpDotUpstreamCone(const string& filename, const DfgVertex& vtx, - const string& name = "") const VL_MT_DISABLED; + void dumpDotUpstreamCone(const std::string& filename, const DfgVertex& vtx, + const std::string& name = "") const VL_MT_DISABLED; }; // Specializations of privateTypeTest diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index 1cdb26033..673e2fc66 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -1060,6 +1060,13 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { } } 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(); }); + } + // If an improvement was made, return the still cyclic improved graph if (nImprovements) { UINFO(7, "Graph was improved " << nImprovements << " times");