CIRCT  20.0.0git
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 //===----------------------------------------------------------------------===//
12 #include "circt/Dialect/DC/DCOps.h"
14 #include "mlir/Pass/Pass.h"
15 
18 #include "circt/Dialect/DC/DCOps.h"
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 
24 namespace circt {
25 namespace dc {
26 #define GEN_PASS_DEF_DCDOTPRINT
27 #include "circt/Dialect/DC/DCPasses.h.inc"
28 } // namespace dc
29 } // namespace circt
30 
31 using namespace circt;
32 using namespace dc;
33 using namespace mlir;
34 
35 using ValueMap = llvm::ScopedHashTable<mlir::Value, std::string>;
36 
37 namespace {
38 
39 /// all Comb and DC nodetypes
40 enum 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 
68 std::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
124 struct 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
132 static SmallVector<std::pair<mlir::Value, std::string>>
133 valueToName(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
157 static DotNode
158 createDCNode(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
186 static DotNode
187 createCombNode(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 
263 namespace {
264 /// Emit the dot nodes
265 struct 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 
332 std::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
Definition: DCPrintDot.cpp:158
llvm::ScopedHashTable< mlir::Value, std::string > ValueMap
Definition: DCPrintDot.cpp:35
static DotNode createCombNode(Operation &op, SmallVector< std::pair< mlir::Value, std::string >> &valuesMap)
creates node in the dataflow graph for Comb operations
Definition: DCPrintDot.cpp:187
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
Definition: DCPrintDot.cpp:133
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createDCDotPrintPass()
Definition: DCPrintDot.cpp:333
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21