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