xref: /llvm-project/mlir/lib/Transforms/ViewOpGraph.cpp (revision 095b41c6eedb3acc908dc63ee91ff77944c07d75)
12660623aSJacques Pienaar //===- ViewOpGraph.cpp - View/write op graphviz graphs --------------------===//
22660623aSJacques Pienaar //
330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
456222a06SMehdi Amini // See https://llvm.org/LICENSE.txt for license information.
556222a06SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62660623aSJacques Pienaar //
756222a06SMehdi Amini //===----------------------------------------------------------------------===//
82660623aSJacques Pienaar 
92660623aSJacques Pienaar #include "mlir/Transforms/ViewOpGraph.h"
1067d0d7acSMichele Scuttari 
11b00e0c16SChristian Ulmann #include "mlir/Analysis/TopologicalSortUtils.h"
122660623aSJacques Pienaar #include "mlir/IR/Block.h"
1336d3efeaSRiver Riddle #include "mlir/IR/BuiltinTypes.h"
142660623aSJacques Pienaar #include "mlir/IR/Operation.h"
1567d0d7acSMichele Scuttari #include "mlir/Pass/Pass.h"
168d15b7dcSMatthias Springer #include "mlir/Support/IndentedOstream.h"
178d15b7dcSMatthias Springer #include "llvm/Support/Format.h"
189102a16bSMatthias Springer #include "llvm/Support/GraphWriter.h"
19618caf6fSFangrui Song #include <map>
20a1fe1f5fSKazu Hirata #include <optional>
21618caf6fSFangrui Song #include <utility>
222660623aSJacques Pienaar 
2367d0d7acSMichele Scuttari namespace mlir {
2467d0d7acSMichele Scuttari #define GEN_PASS_DEF_VIEWOPGRAPH
2567d0d7acSMichele Scuttari #include "mlir/Transforms/Passes.h.inc"
2667d0d7acSMichele Scuttari } // namespace mlir
2767d0d7acSMichele Scuttari 
284562e389SRiver Riddle using namespace mlir;
294562e389SRiver Riddle 
309102a16bSMatthias Springer static const StringRef kLineStyleControlFlow = "dashed";
318d15b7dcSMatthias Springer static const StringRef kLineStyleDataFlow = "solid";
328d15b7dcSMatthias Springer static const StringRef kShapeNode = "ellipse";
338d15b7dcSMatthias Springer static const StringRef kShapeNone = "plain";
348d15b7dcSMatthias Springer 
35400ad6f9SRiver Riddle /// Return the size limits for eliding large attributes.
36400ad6f9SRiver Riddle static int64_t getLargeAttributeSizeLimit() {
37400ad6f9SRiver Riddle   // Use the default from the printer flags if possible.
380a81ace0SKazu Hirata   if (std::optional<int64_t> limit =
390a81ace0SKazu Hirata           OpPrintingFlags().getLargeElementsAttrLimit())
40400ad6f9SRiver Riddle     return *limit;
41400ad6f9SRiver Riddle   return 16;
42400ad6f9SRiver Riddle }
43400ad6f9SRiver Riddle 
448d15b7dcSMatthias Springer /// Return all values printed onto a stream as a string.
458d15b7dcSMatthias Springer static std::string strFromOs(function_ref<void(raw_ostream &)> func) {
468d15b7dcSMatthias Springer   std::string buf;
478d15b7dcSMatthias Springer   llvm::raw_string_ostream os(buf);
488d15b7dcSMatthias Springer   func(os);
49*095b41c6SJOE1994   return buf;
502660623aSJacques Pienaar }
518d15b7dcSMatthias Springer 
528d15b7dcSMatthias Springer /// Escape special characters such as '\n' and quotation marks.
538d15b7dcSMatthias Springer static std::string escapeString(std::string str) {
548d15b7dcSMatthias Springer   return strFromOs([&](raw_ostream &os) { os.write_escaped(str); });
552660623aSJacques Pienaar }
568d15b7dcSMatthias Springer 
578d15b7dcSMatthias Springer /// Put quotation marks around a given string.
581fc096afSMehdi Amini static std::string quoteString(const std::string &str) {
591fc096afSMehdi Amini   return "\"" + str + "\"";
601fc096afSMehdi Amini }
618d15b7dcSMatthias Springer 
62618caf6fSFangrui Song using AttributeMap = std::map<std::string, std::string>;
638d15b7dcSMatthias Springer 
648d15b7dcSMatthias Springer namespace {
658d15b7dcSMatthias Springer 
668d15b7dcSMatthias Springer /// This struct represents a node in the DOT language. Each node has an
678d15b7dcSMatthias Springer /// identifier and an optional identifier for the cluster (subgraph) that
688d15b7dcSMatthias Springer /// contains the node.
698d15b7dcSMatthias Springer /// Note: In the DOT language, edges can be drawn only from nodes to nodes, but
708d15b7dcSMatthias Springer /// not between clusters. However, edges can be clipped to the boundary of a
718d15b7dcSMatthias Springer /// cluster with `lhead` and `ltail` attributes. Therefore, when creating a new
728d15b7dcSMatthias Springer /// cluster, an invisible "anchor" node is created.
738d15b7dcSMatthias Springer struct Node {
748d15b7dcSMatthias Springer public:
750a81ace0SKazu Hirata   Node(int id = 0, std::optional<int> clusterId = std::nullopt)
768d15b7dcSMatthias Springer       : id(id), clusterId(clusterId) {}
778d15b7dcSMatthias Springer 
788d15b7dcSMatthias Springer   int id;
790a81ace0SKazu Hirata   std::optional<int> clusterId;
802660623aSJacques Pienaar };
812660623aSJacques Pienaar 
828d15b7dcSMatthias Springer /// This pass generates a Graphviz dataflow visualization of an MLIR operation.
838d15b7dcSMatthias Springer /// Note: See https://www.graphviz.org/doc/info/lang.html for more information
848d15b7dcSMatthias Springer /// about the Graphviz DOT language.
8567d0d7acSMichele Scuttari class PrintOpPass : public impl::ViewOpGraphBase<PrintOpPass> {
868d15b7dcSMatthias Springer public:
87039b969bSMichele Scuttari   PrintOpPass(raw_ostream &os) : os(os) {}
88039b969bSMichele Scuttari   PrintOpPass(const PrintOpPass &o) : PrintOpPass(o.os.getOStream()) {}
892660623aSJacques Pienaar 
908d15b7dcSMatthias Springer   void runOnOperation() override {
916eca120dSMehdi Amini     initColorMapping(*getOperation());
928d15b7dcSMatthias Springer     emitGraph([&]() {
938d15b7dcSMatthias Springer       processOperation(getOperation());
948d15b7dcSMatthias Springer       emitAllEdgeStmts();
958d15b7dcSMatthias Springer     });
9642c413b4SHideto Ueno     markAllAnalysesPreserved();
97736ad206SJing Pu   }
98736ad206SJing Pu 
999102a16bSMatthias Springer   /// Create a CFG graph for a region. Used in `Region::viewGraph`.
1009102a16bSMatthias Springer   void emitRegionCFG(Region &region) {
1019102a16bSMatthias Springer     printControlFlowEdges = true;
1029102a16bSMatthias Springer     printDataFlowEdges = false;
1036eca120dSMehdi Amini     initColorMapping(region);
1049102a16bSMatthias Springer     emitGraph([&]() { processRegion(region); });
1059102a16bSMatthias Springer   }
1069102a16bSMatthias Springer 
1078d15b7dcSMatthias Springer private:
1086eca120dSMehdi Amini   /// Generate a color mapping that will color every operation with the same
1096eca120dSMehdi Amini   /// name the same way. It'll interpolate the hue in the HSV color-space,
1106eca120dSMehdi Amini   /// attempting to keep the contrast suitable for black text.
1116eca120dSMehdi Amini   template <typename T>
1126eca120dSMehdi Amini   void initColorMapping(T &irEntity) {
1136eca120dSMehdi Amini     backgroundColors.clear();
1146eca120dSMehdi Amini     SmallVector<Operation *> ops;
1156eca120dSMehdi Amini     irEntity.walk([&](Operation *op) {
1166eca120dSMehdi Amini       auto &entry = backgroundColors[op->getName()];
1176eca120dSMehdi Amini       if (entry.first == 0)
1186eca120dSMehdi Amini         ops.push_back(op);
1196eca120dSMehdi Amini       ++entry.first;
1206eca120dSMehdi Amini     });
1216eca120dSMehdi Amini     for (auto indexedOps : llvm::enumerate(ops)) {
1226eca120dSMehdi Amini       double hue = ((double)indexedOps.index()) / ops.size();
1236eca120dSMehdi Amini       backgroundColors[indexedOps.value()->getName()].second =
1246eca120dSMehdi Amini           std::to_string(hue) + " 1.0 1.0";
1256eca120dSMehdi Amini     }
1266eca120dSMehdi Amini   }
1276eca120dSMehdi Amini 
1288d15b7dcSMatthias Springer   /// Emit all edges. This function should be called after all nodes have been
1298d15b7dcSMatthias Springer   /// emitted.
1308d15b7dcSMatthias Springer   void emitAllEdgeStmts() {
1313bef17eaSArtem Tyurin     if (printDataFlowEdges) {
1323bef17eaSArtem Tyurin       for (const auto &[value, node, label] : dataFlowEdges) {
1333bef17eaSArtem Tyurin         emitEdgeStmt(valueToNode[value], node, label, kLineStyleDataFlow);
1343bef17eaSArtem Tyurin       }
1353bef17eaSArtem Tyurin     }
1363bef17eaSArtem Tyurin 
1378d15b7dcSMatthias Springer     for (const std::string &edge : edges)
1388d15b7dcSMatthias Springer       os << edge << ";\n";
1398d15b7dcSMatthias Springer     edges.clear();
1408d15b7dcSMatthias Springer   }
14117606a10SJing Pu 
1428d15b7dcSMatthias Springer   /// Emit a cluster (subgraph). The specified builder generates the body of the
1438d15b7dcSMatthias Springer   /// cluster. Return the anchor node of the cluster.
1448d15b7dcSMatthias Springer   Node emitClusterStmt(function_ref<void()> builder, std::string label = "") {
1458d15b7dcSMatthias Springer     int clusterId = ++counter;
1468d15b7dcSMatthias Springer     os << "subgraph cluster_" << clusterId << " {\n";
1478d15b7dcSMatthias Springer     os.indent();
1488d15b7dcSMatthias Springer     // Emit invisible anchor node from/to which arrows can be drawn.
1498d15b7dcSMatthias Springer     Node anchorNode = emitNodeStmt(" ", kShapeNone);
1501fc096afSMehdi Amini     os << attrStmt("label", quoteString(escapeString(std::move(label))))
1511fc096afSMehdi Amini        << ";\n";
1528d15b7dcSMatthias Springer     builder();
1538d15b7dcSMatthias Springer     os.unindent();
1548d15b7dcSMatthias Springer     os << "}\n";
1558d15b7dcSMatthias Springer     return Node(anchorNode.id, clusterId);
1568d15b7dcSMatthias Springer   }
1578d15b7dcSMatthias Springer 
1588d15b7dcSMatthias Springer   /// Generate an attribute statement.
1598d15b7dcSMatthias Springer   std::string attrStmt(const Twine &key, const Twine &value) {
1608d15b7dcSMatthias Springer     return (key + " = " + value).str();
1618d15b7dcSMatthias Springer   }
1628d15b7dcSMatthias Springer 
1638d15b7dcSMatthias Springer   /// Emit an attribute list.
1648d15b7dcSMatthias Springer   void emitAttrList(raw_ostream &os, const AttributeMap &map) {
1658d15b7dcSMatthias Springer     os << "[";
1668d15b7dcSMatthias Springer     interleaveComma(map, os, [&](const auto &it) {
167618caf6fSFangrui Song       os << this->attrStmt(it.first, it.second);
1688d15b7dcSMatthias Springer     });
1698d15b7dcSMatthias Springer     os << "]";
1708d15b7dcSMatthias Springer   }
1718d15b7dcSMatthias Springer 
1728d15b7dcSMatthias Springer   // Print an MLIR attribute to `os`. Large attributes are truncated.
1738d15b7dcSMatthias Springer   void emitMlirAttr(raw_ostream &os, Attribute attr) {
174400ad6f9SRiver Riddle     // A value used to elide large container attribute.
175400ad6f9SRiver Riddle     int64_t largeAttrLimit = getLargeAttributeSizeLimit();
1768d15b7dcSMatthias Springer 
1772660623aSJacques Pienaar     // Always emit splat attributes.
1785550c821STres Popp     if (isa<SplatElementsAttr>(attr)) {
1798d15b7dcSMatthias Springer       attr.print(os);
1808d15b7dcSMatthias Springer       return;
1812660623aSJacques Pienaar     }
1822660623aSJacques Pienaar 
1832660623aSJacques Pienaar     // Elide "big" elements attributes.
1845550c821STres Popp     auto elements = dyn_cast<ElementsAttr>(attr);
185400ad6f9SRiver Riddle     if (elements && elements.getNumElements() > largeAttrLimit) {
1868db947daSRahul Kayaith       os << std::string(elements.getShapedType().getRank(), '[') << "..."
1878db947daSRahul Kayaith          << std::string(elements.getShapedType().getRank(), ']') << " : "
1882b86e27dSJacques Pienaar          << elements.getType();
1898d15b7dcSMatthias Springer       return;
1902660623aSJacques Pienaar     }
1912660623aSJacques Pienaar 
1925550c821STres Popp     auto array = dyn_cast<ArrayAttr>(attr);
193400ad6f9SRiver Riddle     if (array && static_cast<int64_t>(array.size()) > largeAttrLimit) {
194563b5910SJing Pu       os << "[...]";
1958d15b7dcSMatthias Springer       return;
196563b5910SJing Pu     }
197563b5910SJing Pu 
1982660623aSJacques Pienaar     // Print all other attributes.
199a87be1c1SMatthias Springer     std::string buf;
200a87be1c1SMatthias Springer     llvm::raw_string_ostream ss(buf);
201a87be1c1SMatthias Springer     attr.print(ss);
202*095b41c6SJOE1994     os << truncateString(buf);
2032660623aSJacques Pienaar   }
2042660623aSJacques Pienaar 
2058d15b7dcSMatthias Springer   /// Append an edge to the list of edges.
2068d15b7dcSMatthias Springer   /// Note: Edges are written to the output stream via `emitAllEdgeStmts`.
2079102a16bSMatthias Springer   void emitEdgeStmt(Node n1, Node n2, std::string label, StringRef style) {
2088d15b7dcSMatthias Springer     AttributeMap attrs;
2098d15b7dcSMatthias Springer     attrs["style"] = style.str();
2108d15b7dcSMatthias Springer     // Do not label edges that start/end at a cluster boundary. Such edges are
2118d15b7dcSMatthias Springer     // clipped at the boundary, but labels are not. This can lead to labels
2128d15b7dcSMatthias Springer     // floating around without any edge next to them.
2138d15b7dcSMatthias Springer     if (!n1.clusterId && !n2.clusterId)
2141fc096afSMehdi Amini       attrs["label"] = quoteString(escapeString(std::move(label)));
2158d15b7dcSMatthias Springer     // Use `ltail` and `lhead` to draw edges between clusters.
2168d15b7dcSMatthias Springer     if (n1.clusterId)
2178d15b7dcSMatthias Springer       attrs["ltail"] = "cluster_" + std::to_string(*n1.clusterId);
2188d15b7dcSMatthias Springer     if (n2.clusterId)
2198d15b7dcSMatthias Springer       attrs["lhead"] = "cluster_" + std::to_string(*n2.clusterId);
2202660623aSJacques Pienaar 
2218d15b7dcSMatthias Springer     edges.push_back(strFromOs([&](raw_ostream &os) {
2228d15b7dcSMatthias Springer       os << llvm::format("v%i -> v%i ", n1.id, n2.id);
2238d15b7dcSMatthias Springer       emitAttrList(os, attrs);
2248d15b7dcSMatthias Springer     }));
22502d7b260SJacques Pienaar   }
2262660623aSJacques Pienaar 
2278d15b7dcSMatthias Springer   /// Emit a graph. The specified builder generates the body of the graph.
2288d15b7dcSMatthias Springer   void emitGraph(function_ref<void()> builder) {
2298d15b7dcSMatthias Springer     os << "digraph G {\n";
2308d15b7dcSMatthias Springer     os.indent();
2318d15b7dcSMatthias Springer     // Edges between clusters are allowed only in compound mode.
2328d15b7dcSMatthias Springer     os << attrStmt("compound", "true") << ";\n";
2338d15b7dcSMatthias Springer     builder();
2348d15b7dcSMatthias Springer     os.unindent();
2358d15b7dcSMatthias Springer     os << "}\n";
2362660623aSJacques Pienaar   }
2372660623aSJacques Pienaar 
2388d15b7dcSMatthias Springer   /// Emit a node statement.
2396eca120dSMehdi Amini   Node emitNodeStmt(std::string label, StringRef shape = kShapeNode,
2406eca120dSMehdi Amini                     StringRef background = "") {
2418d15b7dcSMatthias Springer     int nodeId = ++counter;
2428d15b7dcSMatthias Springer     AttributeMap attrs;
2431fc096afSMehdi Amini     attrs["label"] = quoteString(escapeString(std::move(label)));
2448d15b7dcSMatthias Springer     attrs["shape"] = shape.str();
2456eca120dSMehdi Amini     if (!background.empty()) {
2466eca120dSMehdi Amini       attrs["style"] = "filled";
2476eca120dSMehdi Amini       attrs["fillcolor"] = ("\"" + background + "\"").str();
2486eca120dSMehdi Amini     }
2498d15b7dcSMatthias Springer     os << llvm::format("v%i ", nodeId);
2508d15b7dcSMatthias Springer     emitAttrList(os, attrs);
2518d15b7dcSMatthias Springer     os << ";\n";
2528d15b7dcSMatthias Springer     return Node(nodeId);
2532660623aSJacques Pienaar   }
2542660623aSJacques Pienaar 
2558d15b7dcSMatthias Springer   /// Generate a label for an operation.
2568d15b7dcSMatthias Springer   std::string getLabel(Operation *op) {
2578d15b7dcSMatthias Springer     return strFromOs([&](raw_ostream &os) {
2588d15b7dcSMatthias Springer       // Print operation name and type.
259a87be1c1SMatthias Springer       os << op->getName();
260a87be1c1SMatthias Springer       if (printResultTypes) {
261a87be1c1SMatthias Springer         os << " : (";
262a87be1c1SMatthias Springer         std::string buf;
263a87be1c1SMatthias Springer         llvm::raw_string_ostream ss(buf);
264a87be1c1SMatthias Springer         interleaveComma(op->getResultTypes(), ss);
265*095b41c6SJOE1994         os << truncateString(buf) << ")";
266a87be1c1SMatthias Springer       }
2672660623aSJacques Pienaar 
2688d15b7dcSMatthias Springer       // Print attributes.
269a87be1c1SMatthias Springer       if (printAttrs) {
270a87be1c1SMatthias Springer         os << "\n";
2718d15b7dcSMatthias Springer         for (const NamedAttribute &attr : op->getAttrs()) {
2720c7890c8SRiver Riddle           os << '\n' << attr.getName().getValue() << ": ";
2730c7890c8SRiver Riddle           emitMlirAttr(os, attr.getValue());
2748d15b7dcSMatthias Springer         }
275a87be1c1SMatthias Springer       }
2768d15b7dcSMatthias Springer     });
2778d15b7dcSMatthias Springer   }
2788d15b7dcSMatthias Springer 
2798d15b7dcSMatthias Springer   /// Generate a label for a block argument.
2808d15b7dcSMatthias Springer   std::string getLabel(BlockArgument arg) {
2818d15b7dcSMatthias Springer     return "arg" + std::to_string(arg.getArgNumber());
2828d15b7dcSMatthias Springer   }
2838d15b7dcSMatthias Springer 
2848d15b7dcSMatthias Springer   /// Process a block. Emit a cluster and one node per block argument and
2858d15b7dcSMatthias Springer   /// operation inside the cluster.
2868d15b7dcSMatthias Springer   void processBlock(Block &block) {
2878d15b7dcSMatthias Springer     emitClusterStmt([&]() {
2888d15b7dcSMatthias Springer       for (BlockArgument &blockArg : block.getArguments())
2898d15b7dcSMatthias Springer         valueToNode[blockArg] = emitNodeStmt(getLabel(blockArg));
2908d15b7dcSMatthias Springer 
2918d15b7dcSMatthias Springer       // Emit a node for each operation.
2920a81ace0SKazu Hirata       std::optional<Node> prevNode;
2939102a16bSMatthias Springer       for (Operation &op : block) {
2949102a16bSMatthias Springer         Node nextNode = processOperation(&op);
2959102a16bSMatthias Springer         if (printControlFlowEdges && prevNode)
2969102a16bSMatthias Springer           emitEdgeStmt(*prevNode, nextNode, /*label=*/"",
2979102a16bSMatthias Springer                        kLineStyleControlFlow);
2989102a16bSMatthias Springer         prevNode = nextNode;
2999102a16bSMatthias Springer       }
3008d15b7dcSMatthias Springer     });
3018d15b7dcSMatthias Springer   }
3028d15b7dcSMatthias Springer 
3038d15b7dcSMatthias Springer   /// Process an operation. If the operation has regions, emit a cluster.
3048d15b7dcSMatthias Springer   /// Otherwise, emit a node.
3059102a16bSMatthias Springer   Node processOperation(Operation *op) {
3068d15b7dcSMatthias Springer     Node node;
3078d15b7dcSMatthias Springer     if (op->getNumRegions() > 0) {
3088d15b7dcSMatthias Springer       // Emit cluster for op with regions.
3098d15b7dcSMatthias Springer       node = emitClusterStmt(
3108d15b7dcSMatthias Springer           [&]() {
3118d15b7dcSMatthias Springer             for (Region &region : op->getRegions())
3128d15b7dcSMatthias Springer               processRegion(region);
3138d15b7dcSMatthias Springer           },
3148d15b7dcSMatthias Springer           getLabel(op));
3158d15b7dcSMatthias Springer     } else {
3166eca120dSMehdi Amini       node = emitNodeStmt(getLabel(op), kShapeNode,
3176eca120dSMehdi Amini                           backgroundColors[op->getName()].second);
3188d15b7dcSMatthias Springer     }
3198d15b7dcSMatthias Springer 
3209102a16bSMatthias Springer     // Insert data flow edges originating from each operand.
3219102a16bSMatthias Springer     if (printDataFlowEdges) {
3228d15b7dcSMatthias Springer       unsigned numOperands = op->getNumOperands();
3238d15b7dcSMatthias Springer       for (unsigned i = 0; i < numOperands; i++)
3243bef17eaSArtem Tyurin         dataFlowEdges.push_back({op->getOperand(i), node,
3253bef17eaSArtem Tyurin                                  numOperands == 1 ? "" : std::to_string(i)});
3269102a16bSMatthias Springer     }
3278d15b7dcSMatthias Springer 
3288d15b7dcSMatthias Springer     for (Value result : op->getResults())
3298d15b7dcSMatthias Springer       valueToNode[result] = node;
3309102a16bSMatthias Springer 
3319102a16bSMatthias Springer     return node;
3328d15b7dcSMatthias Springer   }
3338d15b7dcSMatthias Springer 
3348d15b7dcSMatthias Springer   /// Process a region.
3358d15b7dcSMatthias Springer   void processRegion(Region &region) {
3368d15b7dcSMatthias Springer     for (Block &block : region.getBlocks())
3378d15b7dcSMatthias Springer       processBlock(block);
3388d15b7dcSMatthias Springer   }
3398d15b7dcSMatthias Springer 
340a87be1c1SMatthias Springer   /// Truncate long strings.
341a87be1c1SMatthias Springer   std::string truncateString(std::string str) {
342a87be1c1SMatthias Springer     if (str.length() <= maxLabelLen)
343a87be1c1SMatthias Springer       return str;
344a87be1c1SMatthias Springer     return str.substr(0, maxLabelLen) + "...";
345a87be1c1SMatthias Springer   }
346a87be1c1SMatthias Springer 
3478d15b7dcSMatthias Springer   /// Output stream to write DOT file to.
3488d15b7dcSMatthias Springer   raw_indented_ostream os;
3498d15b7dcSMatthias Springer   /// A list of edges. For simplicity, should be emitted after all nodes were
3508d15b7dcSMatthias Springer   /// emitted.
3518d15b7dcSMatthias Springer   std::vector<std::string> edges;
3528d15b7dcSMatthias Springer   /// Mapping of SSA values to Graphviz nodes/clusters.
3538d15b7dcSMatthias Springer   DenseMap<Value, Node> valueToNode;
3543bef17eaSArtem Tyurin   /// Output for data flow edges is delayed until the end to handle cycles
3553bef17eaSArtem Tyurin   std::vector<std::tuple<Value, Node, std::string>> dataFlowEdges;
3568d15b7dcSMatthias Springer   /// Counter for generating unique node/subgraph identifiers.
3578d15b7dcSMatthias Springer   int counter = 0;
3586eca120dSMehdi Amini 
3596eca120dSMehdi Amini   DenseMap<OperationName, std::pair<int, std::string>> backgroundColors;
3602660623aSJacques Pienaar };
3618d15b7dcSMatthias Springer 
3622660623aSJacques Pienaar } // namespace
3632660623aSJacques Pienaar 
364039b969bSMichele Scuttari std::unique_ptr<Pass> mlir::createPrintOpGraphPass(raw_ostream &os) {
365039b969bSMichele Scuttari   return std::make_unique<PrintOpPass>(os);
3662660623aSJacques Pienaar }
3679102a16bSMatthias Springer 
3689102a16bSMatthias Springer /// Generate a CFG for a region and show it in a window.
3699102a16bSMatthias Springer static void llvmViewGraph(Region &region, const Twine &name) {
3709102a16bSMatthias Springer   int fd;
3719102a16bSMatthias Springer   std::string filename = llvm::createGraphFilename(name.str(), fd);
3729102a16bSMatthias Springer   {
3739102a16bSMatthias Springer     llvm::raw_fd_ostream os(fd, /*shouldClose=*/true);
3749102a16bSMatthias Springer     if (fd == -1) {
3759102a16bSMatthias Springer       llvm::errs() << "error opening file '" << filename << "' for writing\n";
3769102a16bSMatthias Springer       return;
3779102a16bSMatthias Springer     }
378039b969bSMichele Scuttari     PrintOpPass pass(os);
3799102a16bSMatthias Springer     pass.emitRegionCFG(region);
3809102a16bSMatthias Springer   }
3819102a16bSMatthias Springer   llvm::DisplayGraph(filename, /*wait=*/false, llvm::GraphProgram::DOT);
3829102a16bSMatthias Springer }
3839102a16bSMatthias Springer 
3849102a16bSMatthias Springer void mlir::Region::viewGraph(const Twine &regionName) {
3859102a16bSMatthias Springer   llvmViewGraph(*this, regionName);
3869102a16bSMatthias Springer }
3879102a16bSMatthias Springer 
3889102a16bSMatthias Springer void mlir::Region::viewGraph() { viewGraph("region"); }
389