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 ®ion) { 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 ®ion : 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 ®ion) { 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 ®ion, 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 ®ionName) { 3859102a16bSMatthias Springer llvmViewGraph(*this, regionName); 3869102a16bSMatthias Springer } 3879102a16bSMatthias Springer 3889102a16bSMatthias Springer void mlir::Region::viewGraph() { viewGraph("region"); } 389