18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Support/Format.h"
20#include "llvm/Support/GraphWriter.h"
26#define GEN_PASS_DEF_VIEWOPGRAPHPASS
27#include "mlir/Transforms/Passes.h.inc"
40 if (std::optional<int64_t> limit =
49 llvm::raw_string_ostream os(buf);
56 return "\"" + str +
"\"";
64 llvm::raw_string_ostream os(buf);
66 if (llvm::is_contained({
'{',
'|',
'<',
'}',
'>',
'\n',
'"'}, c))
85 Node(
int id = 0, std::optional<int> clusterId = std::nullopt)
86 : id(
id), clusterId(clusterId) {}
89 std::optional<int> clusterId;
99struct OpGraphPrinterOptions {
101 unsigned maxLabelLen = 20;
103 bool printAttrs =
true;
105 bool printControlFlowEdges =
false;
107 bool printDataFlowEdges =
true;
109 bool printResultTypes =
true;
112 static OpGraphPrinterOptions cfgRegionDefaults() {
114 options.printControlFlowEdges =
true;
115 options.printDataFlowEdges =
false;
123class OpGraphPrinter {
128 : opts(
options), os(os), asmState(asmStateOp, opPrintingFlags) {}
131 initColorMapping(op);
133 processOperation(&op);
138 void emitRegionCFGGraph(
Region ®ion) {
139 initColorMapping(region);
140 emitGraph([&]() { processRegion(region); });
147 template <
typename T>
148 void initColorMapping(T &irEntity) {
149 backgroundColors.clear();
152 auto &entry = backgroundColors[op->
getName()];
153 if (entry.first == 0)
157 for (
auto indexedOps : llvm::enumerate(ops)) {
158 double hue = ((double)indexedOps.index()) / ops.size();
161 backgroundColors[indexedOps.value()->getName()].second =
162 std::to_string(hue) +
" 0.3 0.95";
168 void emitAllEdgeStmts() {
169 if (opts.printDataFlowEdges) {
170 for (
const auto &e : dataFlowEdges) {
175 for (
const std::string &edge : edges)
183 const std::string &label =
"") {
184 int clusterId = ++counter;
185 os <<
"subgraph cluster_" << clusterId <<
" {\n";
188 Node anchorNode = emitNodeStmt(
" ",
kShapeNone);
189 os << attrStmt(
"label",
quoteString(label)) <<
";\n";
193 return Node(anchorNode.id, clusterId);
197 std::string attrStmt(
const Twine &key,
const Twine &value) {
198 return (key +
" = " + value).str();
204 interleaveComma(map, os, [&](
const auto &it) {
205 os << this->attrStmt(it.first, it.second);
216 if (isa<SplatElementsAttr>(attr)) {
223 auto elements = dyn_cast<ElementsAttr>(attr);
224 if (elements && elements.getNumElements() > largeAttrLimit) {
225 os << std::string(elements.getShapedType().getRank(),
'[') <<
"..."
226 << std::string(elements.getShapedType().getRank(),
']') <<
" : ";
227 emitMlirType(os, elements.getType());
231 auto array = dyn_cast<ArrayAttr>(attr);
232 if (array &&
static_cast<int64_t>(array.size()) > largeAttrLimit) {
239 llvm::raw_string_ostream ss(buf);
247 llvm::raw_string_ostream ss(buf);
259 void emitEdgeStmt(Node n1, Node n2, std::string port, StringRef style) {
261 attrs[
"style"] = style.str();
264 attrs[
"ltail"] =
"cluster_" + std::to_string(*n1.clusterId);
266 attrs[
"lhead"] =
"cluster_" + std::to_string(*n2.clusterId);
270 if (!port.empty() && !n1.clusterId)
272 os <<
":res" << port <<
":s";
275 if (!port.empty() && !n2.clusterId)
277 os <<
":arg" << port <<
":n";
278 emitAttrList(os, attrs);
284 os <<
"digraph G {\n";
287 os << attrStmt(
"compound",
"true") <<
";\n";
294 Node emitNodeStmt(
const std::string &label, StringRef
shape =
kShapeNode,
295 StringRef background =
"") {
296 int nodeId = ++counter;
299 attrs[
"shape"] =
shape.str();
300 if (!background.empty()) {
301 attrs[
"style"] =
"filled";
302 attrs[
"fillcolor"] =
quoteString(background.str());
304 os << llvm::format(
"v%i ", nodeId);
305 emitAttrList(os, attrs);
310 std::string getValuePortName(
Value operand) {
315 llvm::replace(str,
'%',
'_');
316 llvm::replace(str,
'#',
'_');
320 std::string getClusterLabel(
Operation *op) {
324 if (opts.printResultTypes) {
327 llvm::raw_string_ostream ss(buf);
329 os << truncateString(buf) <<
")";
333 if (opts.printAttrs) {
341 emitMlirAttr(os, attr.getValue());
349 std::string getRecordLabel(
Operation *op) {
356 auto operandToPort = [&](
Value operand) {
357 os <<
"<arg" << getValuePortName(operand) <<
"> ";
358 emitMlirOperand(os, operand);
360 interleave(op->
getOperands(), os, operandToPort,
"|");
367 if (opts.printAttrs) {
372 if (!attrs.
empty()) {
376 os << attr.getName().getValue() <<
": ";
377 emitMlirAttr(os, attr.getValue());
386 os <<
"<res" << getValuePortName(
result) <<
"> ";
387 emitMlirOperand(os,
result);
388 if (opts.printResultTypes) {
390 emitMlirType(os,
result.getType());
393 interleave(op->
getResults(), os, resultToPort,
"|");
404 os <<
"<res" << getValuePortName(arg) <<
"> ";
406 if (opts.printResultTypes) {
408 emitMlirType(os, arg.
getType());
415 void processBlock(
Block &block) {
416 emitClusterStmt([&]() {
418 valueToNode[blockArg] = emitNodeStmt(getLabel(blockArg));
420 std::optional<Node> prevNode;
422 Node nextNode = processOperation(&op);
423 if (opts.printControlFlowEdges && prevNode)
436 node = emitClusterStmt(
439 processRegion(region);
441 getClusterLabel(op));
443 node = emitNodeStmt(getRecordLabel(op),
kShapeNode,
444 backgroundColors[op->
getName()].second);
448 if (opts.printDataFlowEdges) {
450 for (
unsigned i = 0; i < numOperands; i++) {
452 dataFlowEdges.push_back({operand, node, getValuePortName(operand)});
457 valueToNode[
result] = node;
463 void processRegion(
Region ®ion) {
469 std::string truncateString(std::string str) {
470 if (str.length() <= opts.maxLabelLen)
472 return str.substr(0, opts.maxLabelLen) +
"...";
476 OpGraphPrinterOptions opts;
483 std::vector<std::string> edges;
487 std::vector<DataFlowEdge> dataFlowEdges;
499 PrintOpPass() : os(&llvm::errs()) {}
504 PrintOpPass(
const PrintOpPass &other)
507 void runOnOperation()
override {
508 OpGraphPrinterOptions opts;
509 opts.maxLabelLen = maxLabelLen;
510 opts.printAttrs = printAttrs;
511 opts.printControlFlowEdges = printControlFlowEdges;
512 opts.printDataFlowEdges = printDataFlowEdges;
513 opts.printResultTypes = printResultTypes;
515 printer.emitOperationGraph(*getOperation());
516 markAllAnalysesPreserved();
525 return std::make_unique<PrintOpPass>(os);
531 std::string filename = llvm::createGraphFilename(name.str(), fd);
533 llvm::raw_fd_ostream os(fd,
true);
535 llvm::errs() <<
"error opening file '" << filename <<
"' for writing\n";
538 OpGraphPrinter printer(OpGraphPrinterOptions::cfgRegionDefaults(), os,
540 printer.emitRegionCFGGraph(region);
542 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.
This class provides management for the lifetime of the state used when printing the IR.
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.
Operation * getParentOp()
Return the parent operation this region 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.
Include the generated interface declarations.
std::unique_ptr<::mlir::Pass > createViewOpGraphPass()
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
llvm::function_ref< Fn > function_ref