From f1db9a02362f1cb2f120a3572871e3431c029f6a Mon Sep 17 00:00:00 2001 From: Naveen Seth Hanig Date: Wed, 6 Aug 2025 16:17:56 +0200 Subject: [PATCH] [Support] Enable CRTP for GraphWriter (NFC) Previously, specializing the `GraphWriter` class required a full class specialization. This change introduces CRTP for `GraphWriter`, allowing for partial specialization. This change is in support of printing the module dependency graph as part of the RFC for driver-managed module builds, for which we want to print the graph nodes in a more human-readable format by: - Printing descriptive IDs instead of pointer addresses as node labels. - Printing the full node labels seperatly from the node relations to avoid clutter. With this approach, only `GraphWriter::writeNodes()` needs to be specialized (, aside from `DOTGraphTraits`). Example output: ```dot digraph "Dependency Graph" { node [shape=Mrecord] edge [dir=back] "Clang Module 'A'" [ label="{ ModuleName | ... }" ] "C++20 Module 'B'" [ label="{ ModuleName | ModuleType | InputFile | ... }" ] ... "C++20 Module 'B'" -> "Clang Module 'A'" ... } ``` RFC for driver-managed module builds: https://discourse.llvm.org/t/rfc-modules-support-simple-c-20-modules-use-from-the-clang-driver-without-a-build-system --- llvm/include/llvm/Support/GraphWriter.h | 49 ++++++++++++++++--------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/llvm/include/llvm/Support/GraphWriter.h b/llvm/include/llvm/Support/GraphWriter.h index 39a4c0befbb89..af2e5016298e6 100644 --- a/llvm/include/llvm/Support/GraphWriter.h +++ b/llvm/include/llvm/Support/GraphWriter.h @@ -61,8 +61,7 @@ enum Name { LLVM_ABI bool DisplayGraph(StringRef Filename, bool wait = true, GraphProgram::Name program = GraphProgram::DOT); -template -class GraphWriter { +template class GraphWriterBase { raw_ostream &O; const GraphType &G; bool RenderUsingHTML = false; @@ -75,9 +74,15 @@ class GraphWriter { DOTTraits DTraits; static_assert(std::is_pointer_v, - "FIXME: Currently GraphWriter requires the NodeRef type to be " - "a pointer.\nThe pointer usage should be moved to " - "DOTGraphTraits, and removed from GraphWriter itself."); + "FIXME: Currently GraphWriterBase requires the NodeRef type to " + "be a pointer.\nThe pointer usage should be moved to " + "DOTGraphTraits, and removed from GraphWriterBase itself."); + + // Cast the 'this' pointer to the derived type and return a reference. + Derived &getDerived() { return *static_cast(this); } + const Derived &getDerived() const { + return *static_cast(this); + } // Writes the edge labels of the node to O and returns true if there are any // edge labels not equal to the empty string "". @@ -118,23 +123,24 @@ class GraphWriter { } public: - GraphWriter(raw_ostream &o, const GraphType &g, bool SN) : O(o), G(g) { + GraphWriterBase(raw_ostream &o, const GraphType &g, bool SN) : O(o), G(g) { DTraits = DOTTraits(SN); RenderUsingHTML = DTraits.renderNodesUsingHTML(); } + virtual ~GraphWriterBase() {} void writeGraph(const std::string &Title = "") { // Output the header for the graph... - writeHeader(Title); + getDerived().writeHeader(Title); // Emit all of the nodes in the graph... - writeNodes(); + getDerived().writeNodes(); // Output any customizations on the graph - DOTGraphTraits::addCustomGraphFeatures(G, *this); + DOTGraphTraits::addCustomGraphFeatures(G, getDerived()); // Output the end of the graph - writeFooter(); + getDerived().writeFooter(); } void writeHeader(const std::string &Title) { @@ -166,8 +172,8 @@ class GraphWriter { void writeNodes() { // Loop over the graph, printing it out... for (const auto Node : nodes(G)) - if (!isNodeHidden(Node)) - writeNode(Node); + if (!getDerived().isNodeHidden(Node)) + getDerived().writeNode(Node); } bool isNodeHidden(NodeRef Node) { return DTraits.isNodeHidden(Node, G); } @@ -302,9 +308,9 @@ class GraphWriter { if (DTraits.getEdgeSourceLabel(Node, EI).empty()) edgeidx = -1; - emitEdge(static_cast(Node), edgeidx, - static_cast(TargetNode), DestPort, - DTraits.getEdgeAttributes(Node, EI, G)); + getDerived().emitEdge(static_cast(Node), edgeidx, + static_cast(TargetNode), DestPort, + DTraits.getEdgeAttributes(Node, EI, G)); } } @@ -357,10 +363,17 @@ class GraphWriter { } }; -template +template +class GraphWriter : public GraphWriterBase> { +public: + GraphWriter(raw_ostream &o, const GraphType &g, bool SN) + : GraphWriterBase>(o, g, SN) {} + ~GraphWriter() override {} +}; + +template raw_ostream &WriteGraph(raw_ostream &O, const GraphType &G, - bool ShortNames = false, - const Twine &Title = "") { + bool ShortNames = false, const Twine &Title = "") { // Start the graph emission process... GraphWriter W(O, G, ShortNames);