CIRCT 20.0.0git
Loading...
Searching...
No Matches
DCPrintDot.cpp
Go to the documentation of this file.
1//===- DCPrintDot.cpp - Analysis Pass -----------------------------*-C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Contains the definitions of the DCPrintDot pass.
10//
11//===----------------------------------------------------------------------===//
14#include "mlir/Pass/Pass.h"
15
19#include "mlir/Support/IndentedOstream.h"
20#include "llvm/ADT/ScopedHashTable.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/Support/raw_ostream.h"
23
24namespace circt {
25namespace dc {
26#define GEN_PASS_DEF_DCDOTPRINT
27#include "circt/Dialect/DC/DCPasses.h.inc"
28} // namespace dc
29} // namespace circt
30
31using namespace circt;
32using namespace dc;
33using namespace mlir;
34
35using ValueMap = llvm::ScopedHashTable<mlir::Value, std::string>;
36
37namespace {
38
39/// all Comb and DC nodetypes
40enum class NodeType {
41 AddOp,
42 AndOp,
43 XorOp,
44 MulOp,
45 OrOp,
46 MuxOp,
47 EqOp,
48 NeOp,
49 GtOp,
50 GeOp,
51 LtOp,
52 LeOp,
53 BranchOp,
54 BufferOp,
55 ForkOp,
56 FromESIOp,
57 JoinOp,
58 MergeOp,
59 PackOp,
60 SelectOp,
61 SinkOp,
62 SourceOp,
63 ToESIOp,
64 UnpackOp,
65 Null
66};
67
68std::string stringify(NodeType type) {
69 switch (type) {
70 case NodeType::AddOp:
71 return "+";
72 case NodeType::AndOp:
73 return "&&";
74 case NodeType::XorOp:
75 return "^";
76 case NodeType::OrOp:
77 return "||";
78 case NodeType::MulOp:
79 return "x";
80 case NodeType::MuxOp:
81 return "mux";
82 case NodeType::EqOp:
83 return "==";
84 case NodeType::NeOp:
85 return "!=";
86 case NodeType::GtOp:
87 return ">";
88 case NodeType::GeOp:
89 return ">=";
90 case NodeType::LtOp:
91 return "<";
92 case NodeType::LeOp:
93 return "<=";
94 case NodeType::BranchOp:
95 return "branch";
96 case NodeType::BufferOp:
97 return "buffer";
98 case NodeType::ForkOp:
99 return "fork";
100 case NodeType::FromESIOp:
101 return "fromESI";
102 case NodeType::JoinOp:
103 return "join";
104 case NodeType::MergeOp:
105 return "merge";
106 case NodeType::PackOp:
107 return "pack";
108 case NodeType::SelectOp:
109 return "select";
110 case NodeType::SinkOp:
111 return "sink";
112 case NodeType::SourceOp:
113 return "source";
114 case NodeType::ToESIOp:
115 return "toESI";
116 case NodeType::UnpackOp:
117 return "unpack";
118 case NodeType::Null:
119 return "null";
120 }
121}
122
123/// stores operand and results for each node in the dot graph
124struct DotNode {
125 NodeType nodeType;
126 SmallVector<std::pair<mlir::Value, std::string>> incoming;
127 SmallVector<std::pair<mlir::Value, std::string>> outgoing;
128};
129} // namespace
130
131/// gives a unique name to each value in the graph
132static SmallVector<std::pair<mlir::Value, std::string>>
133valueToName(const SmallVector<mlir::Value> &values,
134 SmallVector<std::pair<mlir::Value, std::string>> &currentMap,
135 bool tokenFlag) {
136 SmallVector<std::pair<mlir::Value, std::string>> res;
137 for (auto [i, v] : llvm::enumerate(values)) {
138 auto found = false;
139 for (const auto &[key, value] : currentMap) {
140 if (v == key) {
141 res.push_back({v, value});
142 found = true;
143 }
144 }
145 if (!found) {
146 std::string name = "in_" + std::to_string(currentMap.size());
147 if (tokenFlag && i % 2 == 0)
148 name = "token_" + std::to_string(currentMap.size());
149 res.push_back({v, name});
150 currentMap.push_back({v, name});
151 }
152 }
153 return res;
154}
155
156/// creates node in the dataflow graph for DC operations
157static DotNode
158createDCNode(Operation &op,
159 SmallVector<std::pair<mlir::Value, std::string>> &valuesMap) {
160
161 auto tokenFlag = false;
162 if (isa<dc::UnpackOp>(op))
163 tokenFlag = true;
164
165 DotNode n = {NodeType::Null, valueToName(op.getOperands(), valuesMap, false),
166 valueToName(op.getOperands(), valuesMap, tokenFlag)};
167
168 TypeSwitch<mlir::Operation *>(&op)
169 .Case([&](dc::BranchOp) { n.nodeType = NodeType::BranchOp; })
170 .Case([&](dc::BufferOp) { n.nodeType = NodeType::BufferOp; })
171 .Case([&](dc::ForkOp) { n.nodeType = NodeType::ForkOp; })
172 .Case([&](dc::FromESIOp) { n.nodeType = NodeType::FromESIOp; })
173 .Case([&](dc::JoinOp) { n.nodeType = NodeType::JoinOp; })
174 .Case([&](dc::MergeOp) { n.nodeType = NodeType::MergeOp; })
175 .Case([&](dc::PackOp) { n.nodeType = NodeType::PackOp; })
176 .Case([&](dc::SelectOp) { n.nodeType = NodeType::SelectOp; })
177 .Case([&](dc::SinkOp) { n.nodeType = NodeType::SinkOp; })
178 .Case([&](dc::SourceOp) { n.nodeType = NodeType::SourceOp; })
179 .Case([&](dc::ToESIOp) { n.nodeType = NodeType::ToESIOp; })
180 .Case([&](dc::UnpackOp) { n.nodeType = NodeType::UnpackOp; });
181
182 return n;
183}
184
185/// creates node in the dataflow graph for Comb operations
186static DotNode
187createCombNode(Operation &op,
188 SmallVector<std::pair<mlir::Value, std::string>> &valuesMap) {
189
190 DotNode n = {NodeType::Null, valueToName(op.getOperands(), valuesMap, false),
191 valueToName(op.getOperands(), valuesMap, false)};
192
193 TypeSwitch<mlir::Operation *>(&op)
194 .Case([&](comb::AddOp) { n.nodeType = NodeType::AddOp; })
195 .Case([&](comb::AndOp) { n.nodeType = NodeType::AndOp; })
196 .Case([&](comb::XorOp) { n.nodeType = NodeType::XorOp; })
197 .Case([&](comb::OrOp) { n.nodeType = NodeType::OrOp; })
198 .Case([&](comb::MulOp) { n.nodeType = NodeType::MulOp; })
199 .Case([&](comb::MuxOp) { n.nodeType = NodeType::MuxOp; })
200 .Case<comb::ICmpOp>([&](auto op) {
201 switch (op.getPredicate()) {
202 case circt::comb::ICmpPredicate::eq: {
203 n.nodeType = NodeType::EqOp;
204 break;
205 }
206 case circt::comb::ICmpPredicate::ne: {
207 n.nodeType = NodeType::NeOp;
208 break;
209 }
210 case circt::comb::ICmpPredicate::sgt: {
211 n.nodeType = NodeType::GtOp;
212 break;
213 }
214 case circt::comb::ICmpPredicate::sge: {
215 n.nodeType = NodeType::GeOp;
216 break;
217 }
218 case circt::comb::ICmpPredicate::slt: {
219 n.nodeType = NodeType::LtOp;
220 break;
221 }
222 case circt::comb::ICmpPredicate::sle: {
223 n.nodeType = NodeType::LeOp;
224 break;
225 }
226 case circt::comb::ICmpPredicate::ugt: {
227 n.nodeType = NodeType::GtOp;
228 break;
229 }
230 case circt::comb::ICmpPredicate::uge: {
231 n.nodeType = NodeType::GeOp;
232 break;
233 }
234 case circt::comb::ICmpPredicate::ult: {
235 n.nodeType = NodeType::LeOp;
236 break;
237 }
238 case circt::comb::ICmpPredicate::ule: {
239 n.nodeType = NodeType::LtOp;
240 break;
241 }
242 case circt::comb::ICmpPredicate::ceq: {
243 n.nodeType = NodeType::EqOp;
244 break;
245 }
246 case circt::comb::ICmpPredicate::cne: {
247 n.nodeType = NodeType::NeOp;
248 break;
249 }
250 case circt::comb::ICmpPredicate::weq: {
251 n.nodeType = NodeType::EqOp;
252 break;
253 }
254 case circt::comb::ICmpPredicate::wne: {
255 n.nodeType = NodeType::NeOp;
256 break;
257 }
258 }
259 });
260 return n;
261}
262
263namespace {
264/// Emit the dot nodes
265struct DCDotPrintPass : public circt::dc::impl::DCDotPrintBase<DCDotPrintPass> {
266 explicit DCDotPrintPass(raw_ostream &os) : os(os) {}
267 void runOnOperation() override {
268
269 ModuleOp op = getOperation();
270
271 std::error_code ec;
272
273 SmallVector<std::pair<mlir::Value, std::string>> valuesMap;
274 SmallVector<DotNode> nodes;
275
276 auto &moduleOps = op->getRegion(0).getBlocks();
277 for (auto &moduleOp : moduleOps) {
278 auto hwModuleOp = moduleOp.getOps<hw::HWModuleOp>();
279 for (auto hmo : hwModuleOp) {
280 for (auto &op : hmo->getRegion(0).getOps()) {
281 // either dc or comb operation
282 if (op.getDialect()->getNamespace() == "comb") {
283 // create new node
284 auto node = createCombNode(op, valuesMap);
285 nodes.push_back(node);
286 } else if (op.getDialect()->getNamespace() == "dc") {
287 auto node = createDCNode(op, valuesMap);
288 nodes.push_back(node);
289 }
290 }
291 }
292 }
293 os << "digraph{\n";
294 // print all nodes first
295 for (auto [i, n] : llvm::enumerate(nodes)) {
296 os << i << " [shape = polygon, label = \"" << stringify(n.nodeType)
297 << "\"]\n";
298 }
299 for (auto [id, n] : llvm::enumerate(nodes)) {
300 if (n.nodeType == NodeType::UnpackOp)
301 for (auto ic : n.incoming)
302 os << "token_" << ic.second << " -> " << id << R"( [label = ")"
303 << ic.second << "\"]\n";
304 }
305 for (auto [id1, n1] : llvm::enumerate(nodes)) {
306 for (auto [id2, n2] : llvm::enumerate(nodes)) {
307 if (id1 != id2) {
308 for (const auto &n1Out : n1.outgoing) {
309 for (const auto &[i, n2In] : llvm::enumerate(n2.incoming)) {
310 if (n1Out.first == n2In.first) {
311 os << id1 << " -> " << id2 << " [label = \"" << n1Out.second
312 << "\"]\n";
313 }
314 }
315 }
316 }
317 }
318 }
319 for (auto [id, n] : llvm::enumerate(nodes)) {
320 if (n.nodeType == NodeType::PackOp)
321 for (const auto &ic : n.outgoing)
322 os << id << " -> " << ic.second << " [label = \"" << ic.second
323 << "\"]\n";
324 }
325
326 os << "}\n";
327 }
328 raw_ostream &os;
329};
330} // namespace
331
332std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>>
334 return std::make_unique<DCDotPrintPass>(llvm::errs());
335}
static DotNode createDCNode(Operation &op, SmallVector< std::pair< mlir::Value, std::string > > &valuesMap)
creates node in the dataflow graph for DC operations
llvm::ScopedHashTable< mlir::Value, std::string > ValueMap
static SmallVector< std::pair< mlir::Value, std::string > > valueToName(const SmallVector< mlir::Value > &values, SmallVector< std::pair< mlir::Value, std::string > > &currentMap, bool tokenFlag)
gives a unique name to each value in the graph
static DotNode createCombNode(Operation &op, SmallVector< std::pair< mlir::Value, std::string > > &valuesMap)
creates node in the dataflow graph for Comb operations
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createDCDotPrintPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.