CIRCT  19.0.0git
Analysis.cpp
Go to the documentation of this file.
1 //===- Analysis.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 Analysis pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
15 #include "circt/Support/LLVM.h"
16 #include "mlir/Dialect/Arith/IR/Arith.h"
17 #include "mlir/IR/BuiltinTypes.h"
18 #include "mlir/IR/OperationSupport.h"
19 #include "mlir/IR/PatternMatch.h"
20 #include "mlir/Pass/Pass.h"
21 #include "mlir/Support/IndentedOstream.h"
22 #include "llvm/ADT/TypeSwitch.h"
23 
24 #include <optional>
25 
26 namespace circt {
27 namespace handshake {
28 #define GEN_PASS_DEF_HANDSHAKEDOTPRINT
29 #define GEN_PASS_DEF_HANDSHAKEOPCOUNT
30 #define GEN_PASS_DEF_HANDSHAKEADDIDS
31 #include "circt/Dialect/Handshake/HandshakePasses.h.inc"
32 } // namespace handshake
33 } // namespace circt
34 
35 using namespace circt;
36 using namespace handshake;
37 using namespace mlir;
38 
39 static bool isControlOp(Operation *op) {
40  auto controlInterface = dyn_cast<handshake::ControlInterface>(op);
41  return controlInterface && controlInterface.isControl();
42 }
43 
44 namespace {
45 struct HandshakeDotPrintPass
46  : public circt::handshake::impl::HandshakeDotPrintBase<
47  HandshakeDotPrintPass> {
48  void runOnOperation() override {
49  ModuleOp m = getOperation();
50 
51  // Resolve the instance graph to get a top-level module.
52  std::string topLevel;
54  SmallVector<std::string> sortedFuncs;
55  if (resolveInstanceGraph(m, uses, topLevel, sortedFuncs).failed()) {
56  signalPassFailure();
57  return;
58  }
59 
60  handshake::FuncOp topLevelOp =
61  cast<handshake::FuncOp>(m.lookupSymbol(topLevel));
62 
63  // Create top-level graph.
64  std::error_code ec;
65  llvm::raw_fd_ostream outfile(topLevel + ".dot", ec);
66  mlir::raw_indented_ostream os(outfile);
67 
68  os << "Digraph G {\n";
69  os.indent();
70  os << "splines=spline;\n";
71  os << "compound=true; // Allow edges between clusters\n";
72  dotPrint(os, "TOP", topLevelOp, /*isTop=*/true);
73  os.unindent();
74  os << "}\n";
75  outfile.close();
76  };
77 
78 private:
79  /// Prints an instance of a handshake.func to the graph. Returns the unique
80  /// name that was assigned to the instance.
81  std::string dotPrint(mlir::raw_indented_ostream &os, StringRef parentName,
82  handshake::FuncOp f, bool isTop);
83 
84  /// Maintain a mapping of module names and the number of times one of those
85  /// modules have been instantiated in the design. This is used to generate
86  /// unique names in the output graph.
87  std::map<std::string, unsigned> instanceIdMap;
88 
89  /// A mapping between operations and their unique name in the .dot file.
90  DenseMap<Operation *, std::string> opNameMap;
91 
92  /// A mapping between block arguments and their unique name in the .dot file.
93  DenseMap<Value, std::string> argNameMap;
94 
95  void setUsedByMapping(Value v, Operation *op, StringRef node);
96  void setProducedByMapping(Value v, Operation *op, StringRef node);
97 
98  /// Returns the name of the vertex using 'v' through 'consumer'.
99  std::string getUsedByNode(Value v, Operation *consumer);
100  /// Returns the name of the vertex producing 'v' through 'producer'.
101  std::string getProducedByNode(Value v, Operation *producer);
102 
103  /// Maintain mappings between a value, the operation which (uses/produces) it,
104  /// and the node name which the (tail/head) of an edge should refer to. This
105  /// is used to resolve edges across handshake.instance's.
106  // "'value' used by 'operation*' is used by the 'string' vertex"
107  DenseMap<Value, std::map<Operation *, std::string>> usedByMapping;
108  // "'value' produced by 'operation*' is produced from the 'string' vertex"
109  DenseMap<Value, std::map<Operation *, std::string>> producedByMapping;
110 };
111 
112 struct HandshakeOpCountPass
113  : public circt::handshake::impl::HandshakeOpCountBase<
114  HandshakeOpCountPass> {
115  void runOnOperation() override {
116  ModuleOp m = getOperation();
117 
118  for (auto func : m.getOps<handshake::FuncOp>()) {
119  std::map<std::string, int> cnts;
120  for (Operation &op : func.getOps()) {
121  llvm::TypeSwitch<Operation *, void>(&op)
122  .Case<handshake::ConstantOp>([&](auto) { cnts["Constant"]++; })
123  .Case<handshake::MuxOp>([&](auto) { cnts["Mux"]++; })
124  .Case<handshake::LoadOp>([&](auto) { cnts["Load"]++; })
125  .Case<handshake::StoreOp>([&](auto) { cnts["Store"]++; })
126  .Case<handshake::MergeOp>([&](auto) { cnts["Merge"]++; })
127  .Case<handshake::ForkOp>([&](auto) { cnts["Fork"]++; })
128  .Case<handshake::BranchOp>([&](auto) { cnts["Branch"]++; })
129  .Case<handshake::MemoryOp, handshake::ExternalMemoryOp>(
130  [&](auto) { cnts["Memory"]++; })
131  .Case<handshake::ControlMergeOp>(
132  [&](auto) { cnts["CntrlMerge"]++; })
133  .Case<handshake::SinkOp>([&](auto) { cnts["Sink"]++; })
134  .Case<handshake::SourceOp>([&](auto) { cnts["Source"]++; })
135  .Case<handshake::JoinOp>([&](auto) { cnts["Join"]++; })
136  .Case<handshake::BufferOp>([&](auto) { cnts["Buffer"]++; })
137  .Case<handshake::ConditionalBranchOp>(
138  [&](auto) { cnts["Branch"]++; })
139  .Case<arith::AddIOp>([&](auto) { cnts["Add"]++; })
140  .Case<arith::SubIOp>([&](auto) { cnts["Sub"]++; })
141  .Case<arith::AddIOp>([&](auto) { cnts["Add"]++; })
142  .Case<arith::MulIOp>([&](auto) { cnts["Mul"]++; })
143  .Case<arith::CmpIOp>([&](auto) { cnts["Cmp"]++; })
144  .Case<arith::IndexCastOp, arith::ShLIOp, arith::ShRSIOp,
145  arith::ShRUIOp>([&](auto) { cnts["Ext/Sh"]++; })
146  .Case<handshake::ReturnOp>([&](auto) {})
147  .Default([&](auto op) {
148  llvm::outs() << "Unhandled operation: " << *op << "\n";
149  assert(false);
150  });
151  }
152 
153  llvm::outs() << "// RESOURCES"
154  << "\n";
155  for (auto it : cnts)
156  llvm::outs() << it.first << "\t" << it.second << "\n";
157  llvm::outs() << "// END"
158  << "\n";
159  }
160  }
161 };
162 
163 } // namespace
164 
165 /// Prints an operation to the dot file and returns the unique name for the
166 /// operation within the graph.
167 static std::string dotPrintNode(mlir::raw_indented_ostream &outfile,
168  StringRef instanceName, Operation *op,
169  DenseMap<Operation *, unsigned> &opIDs) {
170 
171  // We use "." to distinguish hierarchy in the dot file, but an op by default
172  // prints using "." between the dialect name and the op name. Replace uses of
173  // "." with "_".
174  std::string opDialectName = op->getName().getStringRef().str();
175  std::replace(opDialectName.begin(), opDialectName.end(), '.', '_');
176  std::string opName = (instanceName + "." + opDialectName).str();
177 
178  // Follow the naming convention used in FIRRTL lowering.
179  auto idAttr = op->getAttrOfType<IntegerAttr>("handshake_id");
180  if (idAttr)
181  opName += "_id" + std::to_string(idAttr.getValue().getZExtValue());
182  else
183  opName += std::to_string(opIDs[op]);
184 
185  outfile << "\"" << opName << "\""
186  << " [";
187 
188  /// Fill color
189  outfile << "fillcolor = ";
190  outfile
191  << llvm::TypeSwitch<Operation *, std::string>(op)
192  .Case<handshake::ForkOp, handshake::LazyForkOp, handshake::MuxOp,
193  handshake::JoinOp>([&](auto) { return "lavender"; })
194  .Case<handshake::BufferOp>([&](auto) { return "lightgreen"; })
195  .Case<handshake::ReturnOp>([&](auto) { return "gold"; })
196  .Case<handshake::SinkOp, handshake::ConstantOp>(
197  [&](auto) { return "gainsboro"; })
198  .Case<handshake::MemoryOp, handshake::LoadOp, handshake::StoreOp>(
199  [&](auto) { return "coral"; })
200  .Case<handshake::MergeOp, handshake::ControlMergeOp,
201  handshake::BranchOp, handshake::ConditionalBranchOp>(
202  [&](auto) { return "lightblue"; })
203  .Default([&](auto) { return "moccasin"; });
204 
205  /// Shape
206  outfile << ", shape=";
207  if (op->getDialect()->getNamespace() == "handshake")
208  outfile << "box";
209  else
210  outfile << "oval";
211 
212  /// Label
213  outfile << ", label=\"";
214  outfile << llvm::TypeSwitch<Operation *, std::string>(op)
215  .Case<handshake::ConstantOp>([&](auto op) {
216  return std::to_string(
217  op->template getAttrOfType<mlir::IntegerAttr>("value")
218  .getValue()
219  .getSExtValue());
220  })
221  .Case<handshake::ControlMergeOp>(
222  [&](auto) { return "cmerge"; })
223  .Case<handshake::ConditionalBranchOp>(
224  [&](auto) { return "cbranch"; })
225  .Case<handshake::BufferOp>([&](auto op) {
226  std::string n = "buffer ";
227  n += stringifyEnum(op.getBufferType());
228  return n;
229  })
230  .Case<arith::AddIOp>([&](auto) { return "+"; })
231  .Case<arith::SubIOp>([&](auto) { return "-"; })
232  .Case<arith::AndIOp>([&](auto) { return "&"; })
233  .Case<arith::OrIOp>([&](auto) { return "|"; })
234  .Case<arith::XOrIOp>([&](auto) { return "^"; })
235  .Case<arith::MulIOp>([&](auto) { return "*"; })
236  .Case<arith::ShRSIOp, arith::ShRUIOp>(
237  [&](auto) { return ">>"; })
238  .Case<arith::ShLIOp>([&](auto) { return "<<"; })
239  .Case<arith::CmpIOp>([&](arith::CmpIOp op) {
240  switch (op.getPredicate()) {
241  case arith::CmpIPredicate::eq:
242  return "==";
243  case arith::CmpIPredicate::ne:
244  return "!=";
245  case arith::CmpIPredicate::uge:
246  case arith::CmpIPredicate::sge:
247  return ">=";
248  case arith::CmpIPredicate::ugt:
249  case arith::CmpIPredicate::sgt:
250  return ">";
251  case arith::CmpIPredicate::ule:
252  case arith::CmpIPredicate::sle:
253  return "<=";
254  case arith::CmpIPredicate::ult:
255  case arith::CmpIPredicate::slt:
256  return "<";
257  }
258  llvm_unreachable("unhandled cmpi predicate");
259  })
260  .Default([&](auto op) {
261  auto opDialect = op->getDialect()->getNamespace();
262  std::string label = op->getName().getStringRef().str();
263  if (opDialect == "handshake")
264  label.erase(0, StringLiteral("handshake.").size());
265 
266  return label;
267  });
268  /// If we have an ID attribute, we'll add the ID of the operation as well.
269  /// This helps crossprobing the diagram with the Handshake IR and waveform
270  /// diagrams.
271  if (idAttr)
272  outfile << " [" << std::to_string(idAttr.getValue().getZExtValue()) << "]";
273 
274  outfile << "\"";
275 
276  /// Style; add dashed border for control nodes
277  outfile << ", style=\"filled";
278  if (isControlOp(op))
279  outfile << ", dashed";
280  outfile << "\"";
281  outfile << "]\n";
282 
283  return opName;
284 }
285 
286 /// Returns true if v is used as a control operand in op
287 static bool isControlOperand(Operation *op, Value v) {
288  if (isControlOp(op))
289  return true;
290 
291  return llvm::TypeSwitch<Operation *, bool>(op)
292  .Case<handshake::MuxOp, handshake::ConditionalBranchOp>(
293  [&](auto op) { return v == op.getOperand(0); })
294  .Case<handshake::ControlMergeOp>([&](auto) { return true; })
295  .Default([](auto) { return false; });
296 }
297 
298 static std::string getLocalName(StringRef instanceName, StringRef suffix) {
299  return (instanceName + "." + suffix).str();
300 }
301 
302 static std::string getArgName(handshake::FuncOp op, unsigned index) {
303  return op.getArgName(index).getValue().str();
304 }
305 
306 static std::string getUniqueArgName(StringRef instanceName,
307  handshake::FuncOp op, unsigned index) {
308  return getLocalName(instanceName, getArgName(op, index));
309 }
310 
311 static std::string getResName(handshake::FuncOp op, unsigned index) {
312  return op.getResName(index).getValue().str();
313 }
314 
315 static std::string getUniqueResName(StringRef instanceName,
316  handshake::FuncOp op, unsigned index) {
317  return getLocalName(instanceName, getResName(op, index));
318 }
319 
320 void HandshakeDotPrintPass::setUsedByMapping(Value v, Operation *op,
321  StringRef node) {
322  usedByMapping[v][op] = node;
323 }
324 void HandshakeDotPrintPass::setProducedByMapping(Value v, Operation *op,
325  StringRef node) {
326  producedByMapping[v][op] = node;
327 }
328 
329 std::string HandshakeDotPrintPass::getUsedByNode(Value v, Operation *consumer) {
330  // Check if there is any mapping registerred for the value-use relation.
331  auto it = usedByMapping.find(v);
332  if (it != usedByMapping.end()) {
333  auto it2 = it->second.find(consumer);
334  if (it2 != it->second.end())
335  return it2->second;
336  }
337 
338  // fallback to the registerred name for the operation
339  auto opNameIt = opNameMap.find(consumer);
340  assert(opNameIt != opNameMap.end() &&
341  "No name registered for the operation!");
342  return opNameIt->second;
343 }
344 
345 std::string HandshakeDotPrintPass::getProducedByNode(Value v,
346  Operation *producer) {
347  // Check if there is any mapping registerred for the value-produce relation.
348  auto it = producedByMapping.find(v);
349  if (it != producedByMapping.end()) {
350  auto it2 = it->second.find(producer);
351  if (it2 != it->second.end())
352  return it2->second;
353  }
354 
355  // fallback to the registerred name for the operation
356  auto opNameIt = opNameMap.find(producer);
357  assert(opNameIt != opNameMap.end() &&
358  "No name registered for the operation!");
359  return opNameIt->second;
360 }
361 
362 /// Emits additional, non-graphviz information about the connection between
363 /// from- and to. This does not have any effect on the graph itself, but may be
364 /// used by other tools to reason about the connectivity between nodes.
365 static void tryAddExtraEdgeInfo(mlir::raw_indented_ostream &os, Operation *from,
366  Value result, Operation *to) {
367  os << " // ";
368 
369  if (from) {
370  // Output port information
371  auto results = from->getResults();
372  unsigned resIdx =
373  std::distance(results.begin(), llvm::find(results, result));
374  auto fromNamedOpInterface = dyn_cast<handshake::NamedIOInterface>(from);
375  if (fromNamedOpInterface) {
376  auto resName = fromNamedOpInterface.getResultName(resIdx);
377  os << " output=\"" << resName << "\"";
378  } else
379  os << " output=\"out" << resIdx << "\"";
380  }
381 
382  if (to) {
383  // Input port information
384  auto ops = to->getOperands();
385  unsigned opIdx = std::distance(ops.begin(), llvm::find(ops, result));
386  auto toNamedOpInterface = dyn_cast<handshake::NamedIOInterface>(to);
387  if (toNamedOpInterface) {
388  auto opName = toNamedOpInterface.getOperandName(opIdx);
389  os << " input=\"" << opName << "\"";
390  } else
391  os << " input=\"in" << opIdx << "\"";
392  }
393 }
394 
395 std::string HandshakeDotPrintPass::dotPrint(mlir::raw_indented_ostream &os,
396  StringRef parentName,
397  handshake::FuncOp f, bool isTop) {
398  // Prints DOT representation of the dataflow graph, used for debugging.
399  DenseMap<Block *, unsigned> blockIDs;
400  std::map<std::string, unsigned> opTypeCntrs;
401  DenseMap<Operation *, unsigned> opIDs;
402  auto name = f.getName();
403  unsigned thisId = instanceIdMap[name.str()]++;
404  std::string instanceName = parentName.str() + "." + name.str();
405  // Follow submodule naming convention from FIRRTL lowering:
406  if (!isTop)
407  instanceName += std::to_string(thisId);
408 
409  /// Maintain a reference to any node in the args, body and result. These are
410  /// used to generate cluster edges at the end of this function, to facilitate
411  /// a nice layout.
412  std::optional<std::string> anyArg, anyBody, anyRes;
413 
414  unsigned i = 0;
415 
416  // Sequentially scan across the operations in the function and assign instance
417  // IDs to each operation.
418  for (Block &block : f) {
419  blockIDs[&block] = i++;
420  for (Operation &op : block)
421  opIDs[&op] = opTypeCntrs[op.getName().getStringRef().str()]++;
422  }
423 
424  if (!isTop) {
425  os << "// Subgraph for instance of " << name << "\n";
426  os << "subgraph \"cluster_" << instanceName << "\" {\n";
427  os.indent();
428  os << "label = \"" << name << "\"\n";
429  os << "labeljust=\"l\"\n";
430  os << "color = \"darkgreen\"\n";
431  }
432  os << "node [shape=box style=filled fillcolor=\"white\"]\n";
433 
434  Block *bodyBlock = &f.getBody().front();
435 
436  /// Print function arg and res nodes.
437  os << "// Function argument nodes\n";
438  std::string argsCluster = "cluster_" + instanceName + "_args";
439  os << "subgraph \"" << argsCluster << "\" {\n";
440  os.indent();
441  // No label or border; the subgraph just forces args to stay together in the
442  // diagram.
443  os << "label=\"\"\n";
444  os << "peripheries=0\n";
445  for (const auto &barg : enumerate(bodyBlock->getArguments())) {
446  auto argName = getArgName(f, barg.index());
447  auto localArgName = getLocalName(instanceName, argName);
448  os << "\"" << localArgName << "\" [shape=diamond";
449  if (barg.index() == bodyBlock->getNumArguments() - 1) // ctrl
450  os << ", style=dashed";
451  os << " label=\"" << argName << "\"";
452  os << "]\n";
453  if (!anyArg.has_value())
454  anyArg = localArgName;
455  }
456  os.unindent();
457  os << "}\n";
458 
459  os << "// Function return nodes\n";
460  std::string resCluster = "cluster_" + instanceName + "_res";
461  os << "subgraph \"" << resCluster << "\" {\n";
462  os.indent();
463  // No label or border; the subgraph just forces args to stay together in the
464  // diagram.
465  os << "label=\"\"\n";
466  os << "peripheries=0\n";
467  // Get the return op; a handshake.func always has a terminator, making this
468  // safe.
469  auto returnOp = *f.getBody().getOps<handshake::ReturnOp>().begin();
470  for (const auto &res : llvm::enumerate(returnOp.getOperands())) {
471  auto resName = getResName(f, res.index());
472  auto uniqueResName = getUniqueResName(instanceName, f, res.index());
473  os << "\"" << uniqueResName << "\" [shape=diamond";
474  if (res.index() == bodyBlock->getNumArguments() - 1) // ctrl
475  os << ", style=dashed";
476  os << " label=\"" << resName << "\"";
477  os << "]\n";
478 
479  // Create a mapping between the return op argument uses and the return
480  // nodes.
481  setUsedByMapping(res.value(), returnOp, uniqueResName);
482 
483  if (!anyRes.has_value())
484  anyRes = uniqueResName;
485  }
486  os.unindent();
487  os << "}\n";
488 
489  /// Print operation nodes.
490  std::string opsCluster = "cluster_" + instanceName + "_ops";
491  os << "subgraph \"" << opsCluster << "\" {\n";
492  os.indent();
493  // No label or border; the subgraph just forces args to stay together in the
494  // diagram.
495  os << "label=\"\"\n";
496  os << "peripheries=0\n";
497  for (Operation &op : *bodyBlock) {
498  if (!isa<handshake::InstanceOp, handshake::ReturnOp>(op)) {
499  // Regular node in the diagram.
500  opNameMap[&op] = dotPrintNode(os, instanceName, &op, opIDs);
501  continue;
502  }
503  auto instOp = dyn_cast<handshake::InstanceOp>(op);
504  if (instOp) {
505  // Recurse into instantiated submodule.
506  auto calledFuncOp =
507  instOp->getParentOfType<ModuleOp>().lookupSymbol<handshake::FuncOp>(
508  instOp.getModule());
509  assert(calledFuncOp);
510  auto subInstanceName = dotPrint(os, instanceName, calledFuncOp, false);
511 
512  // Create a mapping between the instance arguments and the arguments to
513  // the module which it instantiated.
514  for (const auto &arg : llvm::enumerate(instOp.getOperands())) {
515  setUsedByMapping(
516  arg.value(), instOp,
517  getUniqueArgName(subInstanceName, calledFuncOp, arg.index()));
518  }
519  // Create a mapping between the instance results and the results from the
520  // module which it instantiated.
521  for (const auto &res : llvm::enumerate(instOp.getResults())) {
522  setProducedByMapping(
523  res.value(), instOp,
524  getUniqueResName(subInstanceName, calledFuncOp, res.index()));
525  }
526  }
527  }
528  if (!opNameMap.empty())
529  anyBody = opNameMap.begin()->second;
530 
531  os.unindent();
532  os << "}\n";
533 
534  /// Print operation result edges.
535  os << "// Operation result edges\n";
536  for (Operation &op : *bodyBlock) {
537  for (auto result : op.getResults()) {
538  for (auto &u : result.getUses()) {
539  Operation *useOp = u.getOwner();
540  if (useOp->getBlock() == bodyBlock) {
541  os << "\"" << getProducedByNode(result, &op);
542  os << "\" -> \"";
543  os << getUsedByNode(result, useOp) << "\"";
544  if (isControlOp(&op) || isControlOperand(useOp, result))
545  os << " [style=\"dashed\"]";
546 
547  // Add extra, non-graphviz info to the edge.
548  tryAddExtraEdgeInfo(os, &op, result, useOp);
549 
550  os << "\n";
551  }
552  }
553  }
554  }
555 
556  if (!isTop)
557  os << "}\n";
558 
559  /// Print edges for function argument uses.
560  os << "// Function argument edges\n";
561  for (const auto &barg : enumerate(bodyBlock->getArguments())) {
562  auto argName = getArgName(f, barg.index());
563  os << "\"" << getLocalName(instanceName, argName) << "\" [shape=diamond";
564  if (barg.index() == bodyBlock->getNumArguments() - 1)
565  os << ", style=dashed";
566  os << "]\n";
567  for (auto *useOp : barg.value().getUsers()) {
568  os << "\"" << getLocalName(instanceName, argName) << "\" -> \""
569  << getUsedByNode(barg.value(), useOp) << "\"";
570  if (isControlOperand(useOp, barg.value()))
571  os << " [style=\"dashed\"]";
572 
573  tryAddExtraEdgeInfo(os, nullptr, barg.value(), useOp);
574  os << "\n";
575  }
576  }
577 
578  /// Print edges from arguments cluster to ops cluster and ops cluster to
579  /// results cluser, to coerce a nice layout.
580  if (anyArg.has_value() && anyBody.has_value())
581  os << "\"" << anyArg.value() << "\" -> \"" << anyBody.value()
582  << "\" [lhead=\"" << opsCluster << "\" ltail=\"" << argsCluster
583  << "\" style=invis]\n";
584  if (anyBody.has_value() && anyRes.has_value())
585  os << "\"" << anyBody.value() << "\" -> \"" << anyRes.value()
586  << "\" [lhead=\"" << resCluster << "\" ltail=\"" << opsCluster
587  << "\" style=invis]\n";
588 
589  os.unindent();
590  return instanceName;
591 }
592 
593 namespace {
594 struct HandshakeAddIDsPass
595  : public circt::handshake::impl::HandshakeAddIDsBase<HandshakeAddIDsPass> {
596  void runOnOperation() override {
597  handshake::FuncOp funcOp = getOperation();
598  auto *ctx = &getContext();
599  OpBuilder builder(funcOp);
600  funcOp.walk([&](Operation *op) {
601  if (op->hasAttr("handshake_id"))
602  return;
603  llvm::SmallVector<NamedAttribute> attrs;
604  llvm::copy(op->getAttrs(), std::back_inserter(attrs));
605  attrs.push_back(builder.getNamedAttr(
606  "handshake_id",
608  opCounters[op->getName().getStringRef().str()]++)));
609 
610  op->setAttrs(DictionaryAttr::get(ctx, attrs));
611  });
612  };
613 
614 private:
615  /// Maintain a counter for each operation type in the function.
616  std::map<std::string, unsigned> opCounters;
617 };
618 } // namespace
619 
620 std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>>
622  return std::make_unique<HandshakeDotPrintPass>();
623 }
624 
625 std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>>
627  return std::make_unique<HandshakeOpCountPass>();
628 }
629 
630 std::unique_ptr<mlir::Pass> circt::handshake::createHandshakeAddIDsPass() {
631  return std::make_unique<HandshakeAddIDsPass>();
632 }
static bool isControlOp(Operation *op)
Definition: Analysis.cpp:39
static std::string getUniqueResName(StringRef instanceName, handshake::FuncOp op, unsigned index)
Definition: Analysis.cpp:315
static std::string getLocalName(StringRef instanceName, StringRef suffix)
Definition: Analysis.cpp:298
static std::string dotPrintNode(mlir::raw_indented_ostream &outfile, StringRef instanceName, Operation *op, DenseMap< Operation *, unsigned > &opIDs)
Prints an operation to the dot file and returns the unique name for the operation within the graph.
Definition: Analysis.cpp:167
static std::string getArgName(handshake::FuncOp op, unsigned index)
Definition: Analysis.cpp:302
static bool isControlOperand(Operation *op, Value v)
Returns true if v is used as a control operand in op.
Definition: Analysis.cpp:287
static std::string getUniqueArgName(StringRef instanceName, handshake::FuncOp op, unsigned index)
Definition: Analysis.cpp:306
static std::string getResName(handshake::FuncOp op, unsigned index)
Definition: Analysis.cpp:311
static void tryAddExtraEdgeInfo(mlir::raw_indented_ostream &os, Operation *from, Value result, Operation *to)
Emits additional, non-graphviz information about the connection between from- and to.
Definition: Analysis.cpp:365
assert(baseType &&"element must be base type")
Builder builder
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createHandshakeOpCountPass()
Definition: Analysis.cpp:626
std::map< std::string, std::set< std::string > > InstanceGraph
Iterates over the handshake::FuncOp's in the program to build an instance graph.
LogicalResult resolveInstanceGraph(ModuleOp moduleOp, InstanceGraph &instanceGraph, std::string &topLevel, SmallVectorImpl< std::string > &sortedFuncs)
Iterates over the handshake::FuncOp's in the program to build an instance graph.
Definition: PassHelpers.cpp:37
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createHandshakeDotPrintPass()
Definition: Analysis.cpp:621
std::unique_ptr< mlir::Pass > createHandshakeAddIDsPass()
Definition: Analysis.cpp:630
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21