1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
//===- ViewOpGraph.cpp - View/write op graphviz graphs --------------------===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
#include "mlir/Transforms/ViewOpGraph.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/Operation.h"
#include "mlir/Pass/Pass.h"
#include "llvm/Support/CommandLine.h"
// NOLINTNEXTLINE
static llvm::cl::opt<int> elideIfLarger(
"print-op-graph-elide-if-larger",
llvm::cl::desc("Upper limit to emit elements attribute rather than elide"),
llvm::cl::init(16));
namespace llvm {
// Specialize GraphTraits to treat Block as a graph of Operations as nodes and
// uses as edges.
template <> struct GraphTraits<mlir::Block *> {
using GraphType = mlir::Block *;
using NodeRef = mlir::Operation *;
using ChildIteratorType = mlir::UseIterator;
static ChildIteratorType child_begin(NodeRef n) {
return ChildIteratorType(n);
}
static ChildIteratorType child_end(NodeRef n) {
return ChildIteratorType(n, /*end=*/true);
}
// Operation's destructor is private so use Operation* instead and use
// mapped iterator.
static mlir::Operation *AddressOf(mlir::Operation &op) { return &op; }
using nodes_iterator =
mapped_iterator<mlir::Block::iterator, decltype(&AddressOf)>;
static nodes_iterator nodes_begin(mlir::Block *b) {
return nodes_iterator(b->begin(), &AddressOf);
}
static nodes_iterator nodes_end(mlir::Block *b) {
return nodes_iterator(b->end(), &AddressOf);
}
};
// Specialize DOTGraphTraits to produce more readable output.
template <>
struct DOTGraphTraits<mlir::Block *> : public DefaultDOTGraphTraits {
using DefaultDOTGraphTraits::DefaultDOTGraphTraits;
static std::string getNodeLabel(mlir::Operation *op, mlir::Block *);
};
std::string DOTGraphTraits<mlir::Block *>::getNodeLabel(mlir::Operation *op,
mlir::Block *b) {
// Reuse the print output for the node labels.
std::string ostr;
raw_string_ostream os(ostr);
os << op->getName() << "\n";
for (auto attr : op->getAttrs()) {
os << '\n' << attr.first << ": ";
// Always emit splat attributes.
if (attr.second.isa<mlir::SplatElementsAttr>()) {
attr.second.print(os);
continue;
}
// Elide "big" elements attributes.
auto elements = attr.second.dyn_cast<mlir::ElementsAttr>();
if (elements && elements.getNumElements() > elideIfLarger) {
os << "...";
continue;
}
// Print all other attributes.
attr.second.print(os);
}
return os.str();
}
} // end namespace llvm
namespace {
// PrintOpPass is simple pass to write graph per function.
// Note: this is a module pass only to avoid interleaving on the same ostream
// due to multi-threading over functions.
struct PrintOpPass : public mlir::ModulePass<PrintOpPass> {
explicit PrintOpPass(llvm::raw_ostream &os = llvm::errs(),
bool short_names = false, const llvm::Twine &title = "")
: os(os), title(title.str()), short_names(short_names) {}
std::string getOpName(mlir::Operation &op) {
auto symbolAttr = op.getAttrOfType<mlir::StringAttr>(
mlir::SymbolTable::getSymbolAttrName());
if (symbolAttr)
return symbolAttr.getValue();
++unnamedOpCtr;
return (op.getName().getStringRef() + llvm::utostr(unnamedOpCtr)).str();
}
// Print all the ops in a module.
void processModule(mlir::ModuleOp module) {
for (mlir::Operation &op : module) {
// Modules may actually be nested, recurse on nesting.
if (auto nestedModule = llvm::dyn_cast<mlir::ModuleOp>(op)) {
processModule(nestedModule);
continue;
}
auto opName = getOpName(op);
for (mlir::Region ®ion : op.getRegions()) {
for (auto indexed_block : llvm::enumerate(region)) {
// Suffix block number if there are more than 1 block.
auto blockName = region.getBlocks().size() == 1
? ""
: ("__" + llvm::utostr(indexed_block.index()));
llvm::WriteGraph(os, &indexed_block.value(), short_names,
llvm::Twine(title) + opName + blockName);
}
}
}
}
void runOnModule() override { processModule(getModule()); }
private:
llvm::raw_ostream &os;
std::string title;
int unnamedOpCtr = 0;
bool short_names;
};
} // namespace
void mlir::viewGraph(mlir::Block &block, const llvm::Twine &name,
bool shortNames, const llvm::Twine &title,
llvm::GraphProgram::Name program) {
llvm::ViewGraph(&block, name, shortNames, title, program);
}
llvm::raw_ostream &mlir::writeGraph(llvm::raw_ostream &os, mlir::Block &block,
bool shortNames, const llvm::Twine &title) {
return llvm::WriteGraph(os, &block, shortNames, title);
}
std::unique_ptr<mlir::OpPassBase<mlir::ModuleOp>>
mlir::createPrintOpGraphPass(llvm::raw_ostream &os, bool shortNames,
const llvm::Twine &title) {
return std::make_unique<PrintOpPass>(os, shortNames, title);
}
static mlir::PassRegistration<PrintOpPass> pass("print-op-graph",
"Print op graph per region");
|