16#include "llvm/ADT/STLExtras.h"
17#include "llvm/Support/Format.h"
18#include "llvm/Support/GraphWriter.h"
24#define GEN_PASS_DEF_VIEWOPGRAPHPASS
25#include "mlir/Transforms/Passes.h.inc"
38 if (std::optional<int64_t> limit =
47 llvm::raw_string_ostream os(buf);
54 return "\"" + str +
"\"";
62 llvm::raw_string_ostream os(buf);
64 if (llvm::is_contained({
'{',
'|',
'<',
'}',
'>',
'\n',
'"'}, c))
84 Node(
int id = 0, std::optional<int> clusterId = std::nullopt)
85 : id(
id), clusterId(clusterId) {}
88 std::optional<int> clusterId;
100class PrintOpPass :
public impl::ViewOpGraphPassBase<PrintOpPass> {
102 PrintOpPass() : os(llvm::errs()) {}
103 explicit PrintOpPass(ViewOpGraphPassOptions
options)
104 : impl::ViewOpGraphPassBase<PrintOpPass>(std::move(
options)),
107 PrintOpPass(
const PrintOpPass &o) : PrintOpPass(o.os.
getOStream()) {}
109 void runOnOperation()
override {
110 initColorMapping(*getOperation());
112 processOperation(getOperation());
115 markAllAnalysesPreserved();
119 void emitRegionCFG(
Region ®ion) {
120 printControlFlowEdges =
true;
121 printDataFlowEdges =
false;
122 initColorMapping(region);
123 emitGraph([&]() { processRegion(region); });
130 template <
typename T>
131 void initColorMapping(T &irEntity) {
132 backgroundColors.clear();
135 auto &entry = backgroundColors[op->
getName()];
136 if (entry.first == 0)
140 for (
auto indexedOps : llvm::enumerate(ops)) {
141 double hue = ((double)indexedOps.index()) / ops.size();
144 backgroundColors[indexedOps.value()->getName()].second =
145 std::to_string(hue) +
" 0.3 0.95";
151 void emitAllEdgeStmts() {
152 if (printDataFlowEdges) {
153 for (
const auto &e : dataFlowEdges) {
158 for (
const std::string &edge : edges)
166 const std::string &label =
"") {
167 int clusterId = ++counter;
168 os <<
"subgraph cluster_" << clusterId <<
" {\n";
171 Node anchorNode = emitNodeStmt(
" ",
kShapeNone);
172 os << attrStmt(
"label",
quoteString(label)) <<
";\n";
176 return Node(anchorNode.id, clusterId);
180 std::string attrStmt(
const Twine &key,
const Twine &value) {
181 return (key +
" = " + value).str();
187 interleaveComma(map, os, [&](
const auto &it) {
188 os << this->attrStmt(it.first, it.second);
199 if (isa<SplatElementsAttr>(attr)) {
206 auto elements = dyn_cast<ElementsAttr>(attr);
207 if (elements && elements.getNumElements() > largeAttrLimit) {
208 os << std::string(elements.getShapedType().getRank(),
'[') <<
"..."
209 << std::string(elements.getShapedType().getRank(),
']') <<
" : ";
210 emitMlirType(os, elements.getType());
214 auto array = dyn_cast<ArrayAttr>(attr);
215 if (array &&
static_cast<int64_t>(array.size()) > largeAttrLimit) {
222 llvm::raw_string_ostream ss(buf);
230 llvm::raw_string_ostream ss(buf);
242 void emitEdgeStmt(Node n1, Node n2, std::string port, StringRef style) {
244 attrs[
"style"] = style.str();
247 attrs[
"ltail"] =
"cluster_" + std::to_string(*n1.clusterId);
249 attrs[
"lhead"] =
"cluster_" + std::to_string(*n2.clusterId);
253 if (!port.empty() && !n1.clusterId)
255 os <<
":res" << port <<
":s";
258 if (!port.empty() && !n2.clusterId)
260 os <<
":arg" << port <<
":n";
261 emitAttrList(os, attrs);
267 os <<
"digraph G {\n";
270 os << attrStmt(
"compound",
"true") <<
";\n";
277 Node emitNodeStmt(
const std::string &label, StringRef
shape =
kShapeNode,
278 StringRef background =
"") {
279 int nodeId = ++counter;
282 attrs[
"shape"] =
shape.str();
283 if (!background.empty()) {
284 attrs[
"style"] =
"filled";
285 attrs[
"fillcolor"] =
quoteString(background.str());
287 os << llvm::format(
"v%i ", nodeId);
288 emitAttrList(os, attrs);
293 std::string getValuePortName(
Value operand) {
299 llvm::replace(str,
'%',
'_');
300 llvm::replace(str,
'#',
'_');
304 std::string getClusterLabel(
Operation *op) {
308 if (printResultTypes) {
311 llvm::raw_string_ostream ss(buf);
313 os << truncateString(buf) <<
")";
325 emitMlirAttr(os, attr.getValue());
333 std::string getRecordLabel(
Operation *op) {
340 auto operandToPort = [&](
Value operand) {
341 os <<
"<arg" << getValuePortName(operand) <<
"> ";
342 emitMlirOperand(os, operand);
344 interleave(op->
getOperands(), os, operandToPort,
"|");
355 if (printAttrs && !attrs.
empty()) {
359 os << attr.getName().getValue() <<
": ";
360 emitMlirAttr(os, attr.getValue());
368 os <<
"<res" << getValuePortName(
result) <<
"> ";
369 emitMlirOperand(os,
result);
370 if (printResultTypes) {
372 emitMlirType(os,
result.getType());
375 interleave(op->
getResults(), os, resultToPort,
"|");
386 os <<
"<res" << getValuePortName(arg) <<
"> ";
388 if (printResultTypes) {
390 emitMlirType(os, arg.
getType());
397 void processBlock(
Block &block) {
398 emitClusterStmt([&]() {
400 valueToNode[blockArg] = emitNodeStmt(getLabel(blockArg));
402 std::optional<Node> prevNode;
404 Node nextNode = processOperation(&op);
405 if (printControlFlowEdges && prevNode)
418 node = emitClusterStmt(
421 processRegion(region);
423 getClusterLabel(op));
425 node = emitNodeStmt(getRecordLabel(op),
kShapeNode,
426 backgroundColors[op->
getName()].second);
430 if (printDataFlowEdges) {
432 for (
unsigned i = 0; i < numOperands; i++) {
434 dataFlowEdges.push_back({operand, node, getValuePortName(operand)});
439 valueToNode[
result] = node;
445 void processRegion(
Region ®ion) {
451 std::string truncateString(std::string str) {
452 if (str.length() <= maxLabelLen)
454 return str.substr(0, maxLabelLen) +
"...";
461 std::vector<std::string> edges;
465 std::vector<DataFlowEdge> dataFlowEdges;
475 return std::make_unique<PrintOpPass>(os);
481 std::string filename = llvm::createGraphFilename(name.str(), fd);
483 llvm::raw_fd_ostream os(fd,
true);
485 llvm::errs() <<
"error opening file '" << filename <<
"' for writing\n";
488 PrintOpPass pass(os);
489 pass.emitRegionCFG(region);
491 llvm::DisplayGraph(filename,
false, llvm::GraphProgram::DOT);
MemRefDependenceGraph::Node Node
static llvm::ManagedStatic< PassManagerOptions > options
static std::string quoteString(const std::string &str)
Put quotation marks around a given string.
static std::string escapeLabelString(const std::string &str)
For Graphviz record nodes: " Braces, vertical bars and angle brackets must be escaped with a backslas...
static const StringRef kLineStyleDataFlow
static const StringRef kLineStyleControlFlow
static int64_t getLargeAttributeSizeLimit()
Return the size limits for eliding large attributes.
std::map< std::string, std::string > AttributeMap
static const StringRef kShapeNone
static const StringRef kShapeNode
static void llvmViewGraph(Region ®ion, const Twine &name)
Generate a CFG for a region and show it in a window.
static std::string strFromOs(function_ref< void(raw_ostream &)> func)
Return all values printed onto a stream as a string.
Attributes are known-constant values of operations.
void print(raw_ostream &os, bool elideType=false) const
Print the attribute.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
BlockArgListType getArguments()
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
void append(StringRef name, Attribute attr)
Add an attribute with the specified name.
NamedAttribute represents a combination of a name and an Attribute value.
Set of flags used to control the behavior of the various IR print methods (e.g.
void walkInherentAttrs(Operation *op, InherentAttrVisitor visitor) const
Visit the inherent attributes stored in the properties of op.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
unsigned getNumRegions()
Returns the number of regions held by this operation.
unsigned getNumOperands()
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
void viewGraph(const Twine ®ionName)
Displays the CFG in a window.
BlockListType & getBlocks()
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
void print(raw_ostream &os) const
Print the current type.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
void printAsOperand(raw_ostream &os, AsmState &state) const
Print this value as if it were an operand.
raw_ostream subclass that simplifies indention a sequence of code.
raw_ostream & getOStream() const
Returns the underlying (unindented) raw_ostream.
Include the generated interface declarations.
std::unique_ptr< Pass > createViewOpGraphPass(raw_ostream &os)
Creates a pass to print op graphs with the specified output stream.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
llvm::function_ref< Fn > function_ref