CIRCT  17.0.0git
CalyxOps.cpp
Go to the documentation of this file.
1 //===- CalyxOps.cpp - Calyx op code defs ------------------------*- 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 // This is where op definitions live.
10 //
11 //===----------------------------------------------------------------------===//
12 
16 #include "circt/Dialect/HW/HWOps.h"
18 #include "mlir/IR/AsmState.h"
19 #include "mlir/IR/Builders.h"
20 #include "mlir/IR/BuiltinTypes.h"
21 #include "mlir/IR/Diagnostics.h"
22 #include "mlir/IR/DialectImplementation.h"
23 #include "mlir/IR/FunctionImplementation.h"
24 #include "mlir/IR/PatternMatch.h"
25 #include "mlir/IR/SymbolTable.h"
26 #include "mlir/Support/LLVM.h"
27 #include "llvm/ADT/DenseMap.h"
28 #include "llvm/ADT/STLExtras.h"
29 #include "llvm/ADT/SmallSet.h"
30 #include "llvm/ADT/StringExtras.h"
31 #include "llvm/ADT/TypeSwitch.h"
32 #include "llvm/Support/Casting.h"
33 
34 using namespace circt;
35 using namespace circt::calyx;
36 using namespace mlir;
37 
38 //===----------------------------------------------------------------------===//
39 // Utilities related to Direction
40 //===----------------------------------------------------------------------===//
41 
42 Direction direction::get(bool isOutput) {
43  return static_cast<Direction>(isOutput);
44 }
45 
46 IntegerAttr direction::packAttribute(MLIRContext *ctx, size_t nIns,
47  size_t nOuts) {
48  // Pack the array of directions into an APInt. Input direction is zero,
49  // output direction is one.
50  size_t numDirections = nIns + nOuts;
51  APInt portDirections(/*width=*/numDirections, /*value=*/0);
52  for (size_t i = nIns, e = numDirections; i != e; ++i)
53  portDirections.setBit(i);
54 
55  return IntegerAttr::get(IntegerType::get(ctx, numDirections), portDirections);
56 }
57 
58 //===----------------------------------------------------------------------===//
59 // Utilities
60 //===----------------------------------------------------------------------===//
61 
62 /// This pattern collapses a calyx.seq or calyx.par operation when it
63 /// contains exactly one calyx.enable operation.
64 template <typename CtrlOp>
67  LogicalResult matchAndRewrite(CtrlOp ctrlOp,
68  PatternRewriter &rewriter) const override {
69  auto &ops = ctrlOp.getBodyBlock()->getOperations();
70  bool isUnaryControl = (ops.size() == 1) && isa<EnableOp>(ops.front()) &&
71  isa<SeqOp, ParOp>(ctrlOp->getParentOp());
72  if (!isUnaryControl)
73  return failure();
74 
75  ops.front().moveBefore(ctrlOp);
76  rewriter.eraseOp(ctrlOp);
77  return success();
78  }
79 };
80 
81 /// Verify that the value is not a "complex" value. For example, the source
82 /// of an AssignOp should be a constant or port, e.g.
83 /// %and = comb.and %a, %b : i1
84 /// calyx.assign %port = %c1_i1 ? %and : i1 // Incorrect
85 /// calyx.assign %port = %and ? %c1_i1 : i1 // Correct
86 /// TODO(Calyx): This is useful to verify current MLIR can be lowered to the
87 /// native compiler. Remove this when Calyx supports wire declarations.
88 /// See: https://github.com/llvm/circt/pull/1774 for context.
89 template <typename Op>
90 static LogicalResult verifyNotComplexSource(Op op) {
91  Operation *definingOp = op.getSrc().getDefiningOp();
92  if (definingOp == nullptr)
93  // This is a port of the parent component.
94  return success();
95 
96  // Currently, we use the Combinational dialect to perform logical operations
97  // on wires, i.e. comb::AndOp, comb::OrOp, comb::XorOp.
98  if (auto dialect = definingOp->getDialect(); isa<comb::CombDialect>(dialect))
99  return op->emitOpError("has source that is not a port or constant. "
100  "Complex logic should be conducted in the guard.");
101 
102  return success();
103 }
104 
105 /// Convenience function for getting the SSA name of `v` under the scope of
106 /// operation `scopeOp`.
107 static std::string valueName(Operation *scopeOp, Value v) {
108  std::string s;
109  llvm::raw_string_ostream os(s);
110  // CAVEAT: Since commit 27df7158fe MLIR prefers verifiers to print errors for
111  // operations in generic form, and the printer by default runs a verification.
112  // `valueName` is used in some of these verifiers where preferably the generic
113  // operand form should be used instead.
114  AsmState asmState(scopeOp, OpPrintingFlags().assumeVerified());
115  v.printAsOperand(os, asmState);
116  return s;
117 }
118 
119 /// Returns whether this value is either (1) a port on a ComponentOp or (2) a
120 /// port on a cell interface.
121 static bool isPort(Value value) {
122  Operation *definingOp = value.getDefiningOp();
123  return value.isa<BlockArgument>() ||
124  (definingOp && isa<CellInterface>(definingOp));
125 }
126 
127 /// Gets the port for a given BlockArgument.
128 PortInfo calyx::getPortInfo(BlockArgument arg) {
129  Operation *op = arg.getOwner()->getParentOp();
130  assert(isa<ComponentInterface>(op) &&
131  "Only ComponentInterface should support lookup by BlockArgument.");
132  return cast<ComponentInterface>(op).getPortInfo()[arg.getArgNumber()];
133 }
134 
135 /// Returns whether the given operation has a control region.
136 static bool hasControlRegion(Operation *op) {
137  return isa<ControlOp, SeqOp, IfOp, WhileOp, ParOp>(op);
138 }
139 
140 /// Verifies the body of a ControlLikeOp.
141 static LogicalResult verifyControlBody(Operation *op) {
142  if (isa<SeqOp, ParOp>(op))
143  // This does not apply to sequential and parallel regions.
144  return success();
145 
146  // Some ControlLike operations have (possibly) multiple regions, e.g. IfOp.
147  for (auto &region : op->getRegions()) {
148  auto opsIt = region.getOps();
149  size_t numOperations = std::distance(opsIt.begin(), opsIt.end());
150  // A body of a ControlLike operation may have a single EnableOp within it.
151  // However, that must be the only operation.
152  // E.g. Allowed: calyx.control { calyx.enable @A }
153  // Not Allowed: calyx.control { calyx.enable @A calyx.seq { ... } }
154  bool usesEnableAsCompositionOperator =
155  numOperations > 1 && llvm::any_of(region.front(), [](auto &&bodyOp) {
156  return isa<EnableOp>(bodyOp);
157  });
158  if (usesEnableAsCompositionOperator)
159  return op->emitOpError(
160  "EnableOp is not a composition operator. It should be nested "
161  "in a control flow operation, such as \"calyx.seq\"");
162 
163  // Verify that multiple control flow operations are nested inside a single
164  // control operator. See: https://github.com/llvm/circt/issues/1723
165  size_t numControlFlowRegions =
166  llvm::count_if(opsIt, [](auto &&op) { return hasControlRegion(&op); });
167  if (numControlFlowRegions > 1)
168  return op->emitOpError(
169  "has an invalid control sequence. Multiple control flow operations "
170  "must all be nested in a single calyx.seq or calyx.par");
171  }
172  return success();
173 }
174 
175 LogicalResult calyx::verifyComponent(Operation *op) {
176  auto *opParent = op->getParentOp();
177  if (!isa<ModuleOp>(opParent))
178  return op->emitOpError()
179  << "has parent: " << opParent << ", expected ModuleOp.";
180  return success();
181 }
182 
183 LogicalResult calyx::verifyCell(Operation *op) {
184  auto opParent = op->getParentOp();
185  if (!isa<ComponentInterface>(opParent))
186  return op->emitOpError()
187  << "has parent: " << opParent << ", expected ComponentInterface.";
188  return success();
189 }
190 
191 LogicalResult calyx::verifyControlLikeOp(Operation *op) {
192  auto parent = op->getParentOp();
193 
194  if (isa<calyx::EnableOp>(op) &&
195  !isa<calyx::CalyxDialect>(parent->getDialect())) {
196  // Allow embedding calyx.enable ops within other dialects. This is motivated
197  // by allowing experimentation with new styles of Calyx lowering. For more
198  // info and the historical discussion, see:
199  // https://github.com/llvm/circt/pull/3211
200  return success();
201  }
202 
203  if (!hasControlRegion(parent))
204  return op->emitOpError()
205  << "has parent: " << parent
206  << ", which is not allowed for a control-like operation.";
207 
208  if (op->getNumRegions() == 0)
209  return success();
210 
211  auto &region = op->getRegion(0);
212  // Operations that are allowed in the body of a ControlLike op.
213  auto isValidBodyOp = [](Operation *operation) {
214  return isa<EnableOp, SeqOp, IfOp, WhileOp, ParOp>(operation);
215  };
216  for (auto &&bodyOp : region.front()) {
217  if (isValidBodyOp(&bodyOp))
218  continue;
219 
220  return op->emitOpError()
221  << "has operation: " << bodyOp.getName()
222  << ", which is not allowed in this control-like operation";
223  }
224  return verifyControlBody(op);
225 }
226 
227 // Helper function for parsing a group port operation, i.e. GroupDoneOp and
228 // GroupPortOp. These may take one of two different forms:
229 // (1) %<guard> ? %<src> : i1
230 // (2) %<src> : i1
231 static ParseResult parseGroupPort(OpAsmParser &parser, OperationState &result) {
232  SmallVector<OpAsmParser::UnresolvedOperand, 2> operandInfos;
233  OpAsmParser::UnresolvedOperand guardOrSource;
234  if (parser.parseOperand(guardOrSource))
235  return failure();
236 
237  if (succeeded(parser.parseOptionalQuestion())) {
238  OpAsmParser::UnresolvedOperand source;
239  // The guard exists.
240  if (parser.parseOperand(source))
241  return failure();
242  operandInfos.push_back(source);
243  }
244  // No matter if this is the source or guard, it should be last.
245  operandInfos.push_back(guardOrSource);
246 
247  Type type;
248  // Resolving the operands with the same type works here since the source and
249  // guard of a group port is always i1.
250  if (parser.parseColonType(type) ||
251  parser.resolveOperands(operandInfos, type, result.operands))
252  return failure();
253 
254  return success();
255 }
256 
257 // A helper function for printing group ports, i.e. GroupGoOp and GroupDoneOp.
258 template <typename GroupPortType>
259 static void printGroupPort(OpAsmPrinter &p, GroupPortType op) {
260  static_assert(std::is_same<GroupGoOp, GroupPortType>() ||
261  std::is_same<GroupDoneOp, GroupPortType>(),
262  "Should be a Calyx Group port.");
263 
264  p << " ";
265  // The guard is optional.
266  Value guard = op.getGuard(), source = op.getSrc();
267  if (guard)
268  p << guard << " ? ";
269  p << source << " : " << source.getType();
270 }
271 
272 // Collapse nested control of the same type for SeqOp and ParOp, e.g.
273 // calyx.seq { calyx.seq { ... } } -> calyx.seq { ... }
274 template <typename OpTy>
275 static LogicalResult collapseControl(OpTy controlOp,
276  PatternRewriter &rewriter) {
277  static_assert(std::is_same<SeqOp, OpTy>() || std::is_same<ParOp, OpTy>(),
278  "Should be a SeqOp or ParOp.");
279 
280  if (isa<OpTy>(controlOp->getParentOp())) {
281  Block *controlBody = controlOp.getBodyBlock();
282  for (auto &op : make_early_inc_range(*controlBody))
283  op.moveBefore(controlOp);
284 
285  rewriter.eraseOp(controlOp);
286  return success();
287  }
288 
289  return failure();
290 }
291 
292 template <typename OpTy>
293 static LogicalResult emptyControl(OpTy controlOp, PatternRewriter &rewriter) {
294  if (controlOp.getBodyBlock()->empty()) {
295  rewriter.eraseOp(controlOp);
296  return success();
297  }
298  return failure();
299 }
300 
301 /// A helper function to check whether the conditional and group (if it exists)
302 /// needs to be erased to maintain a valid state of a Calyx program. If these
303 /// have no more uses, they will be erased.
304 template <typename OpTy>
306  PatternRewriter &rewriter) {
307  static_assert(std::is_same<OpTy, IfOp>() || std::is_same<OpTy, WhileOp>(),
308  "This is only applicable to WhileOp and IfOp.");
309 
310  // Save information about the operation, and erase it.
311  Value cond = op.getCond();
312  std::optional<StringRef> groupName = op.getGroupName();
313  auto component = op->template getParentOfType<ComponentOp>();
314  rewriter.eraseOp(op);
315 
316  // Clean up the attached conditional and combinational group (if it exists).
317  if (groupName) {
318  auto group = component.getWiresOp().template lookupSymbol<GroupInterface>(
319  *groupName);
320  if (SymbolTable::symbolKnownUseEmpty(group, component.getRegion()))
321  rewriter.eraseOp(group);
322  }
323  // Check the conditional after the Group, since it will be driven within.
324  if (!cond.isa<BlockArgument>() && cond.getDefiningOp()->use_empty())
325  rewriter.eraseOp(cond.getDefiningOp());
326 }
327 
328 //===----------------------------------------------------------------------===//
329 // ComponentInterface
330 //===----------------------------------------------------------------------===//
331 
332 template <typename ComponentTy>
333 static void printComponentInterface(OpAsmPrinter &p, ComponentInterface comp) {
334  auto componentName = comp->template getAttrOfType<StringAttr>(
335  ::mlir::SymbolTable::getSymbolAttrName())
336  .getValue();
337  p << " ";
338  p.printSymbolName(componentName);
339 
340  // Print the port definition list for input and output ports.
341  auto printPortDefList = [&](auto ports) {
342  p << "(";
343  llvm::interleaveComma(ports, p, [&](const PortInfo &port) {
344  p << "%" << port.name.getValue() << ": " << port.type;
345  if (!port.attributes.empty()) {
346  p << " ";
347  p.printAttributeWithoutType(port.attributes);
348  }
349  });
350  p << ")";
351  };
352  printPortDefList(comp.getInputPortInfo());
353  p << " -> ";
354  printPortDefList(comp.getOutputPortInfo());
355 
356  p << " ";
357  p.printRegion(*comp.getRegion(), /*printEntryBlockArgs=*/false,
358  /*printBlockTerminators=*/false,
359  /*printEmptyBlock=*/false);
360 
361  SmallVector<StringRef> elidedAttrs = {
362  "portAttributes",
363  "portNames",
364  "portDirections",
365  "sym_name",
366  ComponentTy::getFunctionTypeAttrName(comp->getName()),
367  ComponentTy::getArgAttrsAttrName(comp->getName()),
368  ComponentTy::getResAttrsAttrName(comp->getName())};
369  p.printOptionalAttrDict(comp->getAttrs(), elidedAttrs);
370 }
371 
372 /// Parses the ports of a Calyx component signature, and adds the corresponding
373 /// port names to `attrName`.
374 static ParseResult
375 parsePortDefList(OpAsmParser &parser, OperationState &result,
376  SmallVectorImpl<OpAsmParser::Argument> &ports,
377  SmallVectorImpl<Type> &portTypes,
378  SmallVectorImpl<NamedAttrList> &portAttrs) {
379  auto parsePort = [&]() -> ParseResult {
380  OpAsmParser::Argument port;
381  Type portType;
382  // Expect each port to have the form `%<ssa-name> : <type>`.
383  if (parser.parseArgument(port) || parser.parseColon() ||
384  parser.parseType(portType))
385  return failure();
386  port.type = portType;
387  ports.push_back(port);
388  portTypes.push_back(portType);
389 
390  NamedAttrList portAttr;
391  portAttrs.push_back(succeeded(parser.parseOptionalAttrDict(portAttr))
392  ? portAttr
393  : NamedAttrList());
394  return success();
395  };
396 
397  return parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
398  parsePort);
399 }
400 
401 /// Parses the signature of a Calyx component.
402 static ParseResult
403 parseComponentSignature(OpAsmParser &parser, OperationState &result,
404  SmallVectorImpl<OpAsmParser::Argument> &ports,
405  SmallVectorImpl<Type> &portTypes) {
406  SmallVector<OpAsmParser::Argument> inPorts, outPorts;
407  SmallVector<Type> inPortTypes, outPortTypes;
408  SmallVector<NamedAttrList> portAttributes;
409 
410  if (parsePortDefList(parser, result, inPorts, inPortTypes, portAttributes))
411  return failure();
412 
413  if (parser.parseArrow() ||
414  parsePortDefList(parser, result, outPorts, outPortTypes, portAttributes))
415  return failure();
416 
417  auto *context = parser.getBuilder().getContext();
418  // Add attribute for port names; these are currently
419  // just inferred from the SSA names of the component.
420  SmallVector<Attribute> portNames;
421  auto getPortName = [context](const auto &port) -> StringAttr {
422  StringRef name = port.ssaName.name;
423  if (name.startswith("%"))
424  name = name.drop_front();
425  return StringAttr::get(context, name);
426  };
427  llvm::transform(inPorts, std::back_inserter(portNames), getPortName);
428  llvm::transform(outPorts, std::back_inserter(portNames), getPortName);
429 
430  result.addAttribute("portNames", ArrayAttr::get(context, portNames));
431  result.addAttribute(
432  "portDirections",
433  direction::packAttribute(context, inPorts.size(), outPorts.size()));
434 
435  ports.append(inPorts);
436  ports.append(outPorts);
437  portTypes.append(inPortTypes);
438  portTypes.append(outPortTypes);
439 
440  SmallVector<Attribute> portAttrs;
441  llvm::transform(portAttributes, std::back_inserter(portAttrs),
442  [&](auto attr) { return attr.getDictionary(context); });
443  result.addAttribute("portAttributes", ArrayAttr::get(context, portAttrs));
444 
445  return success();
446 }
447 
448 template <typename ComponentTy>
449 static ParseResult parseComponentInterface(OpAsmParser &parser,
450  OperationState &result) {
451  using namespace mlir::function_interface_impl;
452 
453  StringAttr componentName;
454  if (parser.parseSymbolName(componentName,
455  ::mlir::SymbolTable::getSymbolAttrName(),
456  result.attributes))
457  return failure();
458 
459  SmallVector<mlir::OpAsmParser::Argument> ports;
460 
461  SmallVector<Type> portTypes;
462  if (parseComponentSignature(parser, result, ports, portTypes))
463  return failure();
464 
465  // Build the component's type for FunctionLike trait. All ports are listed
466  // as arguments so they may be accessed within the component.
467  auto type = parser.getBuilder().getFunctionType(portTypes, /*results=*/{});
468  result.addAttribute(ComponentTy::getFunctionTypeAttrName(result.name),
469  TypeAttr::get(type));
470 
471  auto *body = result.addRegion();
472  if (parser.parseRegion(*body, ports))
473  return failure();
474 
475  if (body->empty())
476  body->push_back(new Block());
477 
478  if (parser.parseOptionalAttrDict(result.attributes))
479  return failure();
480 
481  return success();
482 }
483 
484 /// Returns a new vector containing the concatenation of vectors `a` and `b`.
485 template <typename T>
486 static SmallVector<T> concat(const SmallVectorImpl<T> &a,
487  const SmallVectorImpl<T> &b) {
488  SmallVector<T> out;
489  out.append(a);
490  out.append(b);
491  return out;
492 }
493 
494 static void buildComponentLike(OpBuilder &builder, OperationState &result,
495  StringAttr name, ArrayRef<PortInfo> ports,
496  bool combinational) {
497  using namespace mlir::function_interface_impl;
498 
499  result.addAttribute(::mlir::SymbolTable::getSymbolAttrName(), name);
500 
501  std::pair<SmallVector<Type, 8>, SmallVector<Type, 8>> portIOTypes;
502  std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>> portIONames;
503  std::pair<SmallVector<Attribute, 8>, SmallVector<Attribute, 8>>
504  portIOAttributes;
505  SmallVector<Direction, 8> portDirections;
506  // Avoid using llvm::partition or llvm::sort to preserve relative ordering
507  // between individual inputs and outputs.
508  for (auto &&port : ports) {
509  bool isInput = port.direction == Direction::Input;
510  (isInput ? portIOTypes.first : portIOTypes.second).push_back(port.type);
511  (isInput ? portIONames.first : portIONames.second).push_back(port.name);
512  (isInput ? portIOAttributes.first : portIOAttributes.second)
513  .push_back(port.attributes);
514  }
515  auto portTypes = concat(portIOTypes.first, portIOTypes.second);
516  auto portNames = concat(portIONames.first, portIONames.second);
517  auto portAttributes = concat(portIOAttributes.first, portIOAttributes.second);
518 
519  // Build the function type of the component.
520  auto functionType = builder.getFunctionType(portTypes, {});
521  if (combinational) {
522  result.addAttribute(CombComponentOp::getFunctionTypeAttrName(result.name),
523  TypeAttr::get(functionType));
524  } else {
525  result.addAttribute(ComponentOp::getFunctionTypeAttrName(result.name),
526  TypeAttr::get(functionType));
527  }
528 
529  // Record the port names and number of input ports of the component.
530  result.addAttribute("portNames", builder.getArrayAttr(portNames));
531  result.addAttribute("portDirections",
532  direction::packAttribute(builder.getContext(),
533  portIOTypes.first.size(),
534  portIOTypes.second.size()));
535  // Record the attributes of the ports.
536  result.addAttribute("portAttributes", builder.getArrayAttr(portAttributes));
537 
538  // Create a single-blocked region.
539  Region *region = result.addRegion();
540  Block *body = new Block();
541  region->push_back(body);
542 
543  // Add all ports to the body.
544  body->addArguments(portTypes, SmallVector<Location, 4>(
545  portTypes.size(), builder.getUnknownLoc()));
546 
547  // Insert the WiresOp and ControlOp.
548  IRRewriter::InsertionGuard guard(builder);
549  builder.setInsertionPointToStart(body);
550  builder.create<WiresOp>(result.location);
551  if (!combinational)
552  builder.create<ControlOp>(result.location);
553 }
554 
555 //===----------------------------------------------------------------------===//
556 // ComponentOp
557 //===----------------------------------------------------------------------===//
558 
559 /// This is a helper function that should only be used to get the WiresOp or
560 /// ControlOp of a ComponentOp, which are guaranteed to exist and generally at
561 /// the end of a component's body. In the worst case, this will run in linear
562 /// time with respect to the number of instances within the component.
563 template <typename Op>
564 static Op getControlOrWiresFrom(ComponentOp op) {
565  auto *body = op.getBodyBlock();
566  // We verify there is a single WiresOp and ControlOp,
567  // so this is safe.
568  auto opIt = body->getOps<Op>().begin();
569  return *opIt;
570 }
571 
572 /// Returns the Block argument with the given name from a ComponentOp.
573 /// If the name doesn't exist, returns an empty Value.
574 static Value getBlockArgumentWithName(StringRef name, ComponentOp op) {
575  ArrayAttr portNames = op.getPortNames();
576 
577  for (size_t i = 0, e = portNames.size(); i != e; ++i) {
578  auto portName = portNames[i].cast<StringAttr>();
579  if (portName.getValue() == name)
580  return op.getBodyBlock()->getArgument(i);
581  }
582  return Value{};
583 }
584 
585 WiresOp calyx::ComponentOp::getWiresOp() {
586  return getControlOrWiresFrom<WiresOp>(*this);
587 }
588 
589 ControlOp calyx::ComponentOp::getControlOp() {
590  return getControlOrWiresFrom<ControlOp>(*this);
591 }
592 
593 Value calyx::ComponentOp::getGoPort() {
594  return getBlockArgumentWithName("go", *this);
595 }
596 
597 Value calyx::ComponentOp::getDonePort() {
598  return getBlockArgumentWithName("done", *this);
599 }
600 
601 Value calyx::ComponentOp::getClkPort() {
602  return getBlockArgumentWithName("clk", *this);
603 }
604 
605 Value calyx::ComponentOp::getResetPort() {
606  return getBlockArgumentWithName("reset", *this);
607 }
608 
609 SmallVector<PortInfo> ComponentOp::getPortInfo() {
610  auto portTypes = getArgumentTypes();
611  ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
612  APInt portDirectionsAttr = getPortDirections();
613 
614  SmallVector<PortInfo> results;
615  for (size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
616  results.push_back(PortInfo{portNamesAttr[i].cast<StringAttr>(),
617  portTypes[i],
618  direction::get(portDirectionsAttr[i]),
619  portAttrs[i].cast<DictionaryAttr>()});
620  }
621  return results;
622 }
623 
624 /// A helper function to return a filtered subset of a component's ports.
625 template <typename Pred>
626 static SmallVector<PortInfo> getFilteredPorts(ComponentOp op, Pred p) {
627  SmallVector<PortInfo> ports = op.getPortInfo();
628  llvm::erase_if(ports, p);
629  return ports;
630 }
631 
632 SmallVector<PortInfo> ComponentOp::getInputPortInfo() {
633  return getFilteredPorts(
634  *this, [](const PortInfo &port) { return port.direction == Output; });
635 }
636 
637 SmallVector<PortInfo> ComponentOp::getOutputPortInfo() {
638  return getFilteredPorts(
639  *this, [](const PortInfo &port) { return port.direction == Input; });
640 }
641 
642 void ComponentOp::print(OpAsmPrinter &p) {
643  printComponentInterface<ComponentOp>(p, *this);
644 }
645 
646 ParseResult ComponentOp::parse(OpAsmParser &parser, OperationState &result) {
647  return parseComponentInterface<ComponentOp>(parser, result);
648 }
649 
650 /// Determines whether the given ComponentOp has all the required ports.
651 static LogicalResult hasRequiredPorts(ComponentOp op) {
652  // Get all identifiers from the component ports.
653  llvm::SmallVector<StringRef, 4> identifiers;
654  for (PortInfo &port : op.getPortInfo()) {
655  auto portIds = port.getAllIdentifiers();
656  identifiers.append(portIds.begin(), portIds.end());
657  }
658  // Sort the identifiers: a pre-condition for std::set_intersection.
659  std::sort(identifiers.begin(), identifiers.end());
660 
661  llvm::SmallVector<StringRef, 4> intersection,
662  interfacePorts{"clk", "done", "go", "reset"};
663  // Find the intersection between all identifiers and required ports.
664  std::set_intersection(interfacePorts.begin(), interfacePorts.end(),
665  identifiers.begin(), identifiers.end(),
666  std::back_inserter(intersection));
667 
668  if (intersection.size() == interfacePorts.size())
669  return success();
670 
671  SmallVector<StringRef, 4> difference;
672  std::set_difference(interfacePorts.begin(), interfacePorts.end(),
673  intersection.begin(), intersection.end(),
674  std::back_inserter(difference));
675  return op->emitOpError()
676  << "is missing the following required port attribute identifiers: "
677  << difference;
678 }
679 
680 LogicalResult ComponentOp::verify() {
681  // Verify there is exactly one of each the wires and control operations.
682  auto wIt = getBodyBlock()->getOps<WiresOp>();
683  auto cIt = getBodyBlock()->getOps<ControlOp>();
684  if (std::distance(wIt.begin(), wIt.end()) +
685  std::distance(cIt.begin(), cIt.end()) !=
686  2)
687  return emitOpError() << "requires exactly one of each: '"
688  << WiresOp::getOperationName() << "', '"
689  << ControlOp::getOperationName() << "'.";
690 
691  if (failed(hasRequiredPorts(*this)))
692  return failure();
693 
694  // Verify the component actually does something: has a non-empty Control
695  // region, or continuous assignments.
696  bool hasNoControlConstructs =
697  getControlOp().getBodyBlock()->getOperations().empty();
698  bool hasNoAssignments =
699  getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
700  if (hasNoControlConstructs && hasNoAssignments)
701  return emitOpError(
702  "The component currently does nothing. It needs to either have "
703  "continuous assignments in the Wires region or control constructs in "
704  "the Control region.");
705 
706  return success();
707 }
708 
709 void ComponentOp::build(OpBuilder &builder, OperationState &result,
710  StringAttr name, ArrayRef<PortInfo> ports) {
711  buildComponentLike(builder, result, name, ports, /*combinational=*/false);
712 }
713 
714 void ComponentOp::getAsmBlockArgumentNames(
715  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
716  if (region.empty())
717  return;
718  auto ports = getPortNames();
719  auto *block = &getRegion()->front();
720  for (size_t i = 0, e = block->getNumArguments(); i != e; ++i)
721  setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
722 }
723 
724 //===----------------------------------------------------------------------===//
725 // CombComponentOp
726 //===----------------------------------------------------------------------===//
727 
728 SmallVector<PortInfo> CombComponentOp::getPortInfo() {
729  auto portTypes = getArgumentTypes();
730  ArrayAttr portNamesAttr = getPortNames(), portAttrs = getPortAttributes();
731  APInt portDirectionsAttr = getPortDirections();
732 
733  SmallVector<PortInfo> results;
734  for (size_t i = 0, e = portNamesAttr.size(); i != e; ++i) {
735  results.push_back(PortInfo{portNamesAttr[i].cast<StringAttr>(),
736  portTypes[i],
737  direction::get(portDirectionsAttr[i]),
738  portAttrs[i].cast<DictionaryAttr>()});
739  }
740  return results;
741 }
742 
743 WiresOp calyx::CombComponentOp::getWiresOp() {
744  auto *body = getBodyBlock();
745  auto opIt = body->getOps<WiresOp>().begin();
746  return *opIt;
747 }
748 
749 /// A helper function to return a filtered subset of a comb component's ports.
750 template <typename Pred>
751 static SmallVector<PortInfo> getFilteredPorts(CombComponentOp op, Pred p) {
752  SmallVector<PortInfo> ports = op.getPortInfo();
753  llvm::erase_if(ports, p);
754  return ports;
755 }
756 
757 SmallVector<PortInfo> CombComponentOp::getInputPortInfo() {
758  return getFilteredPorts(
759  *this, [](const PortInfo &port) { return port.direction == Output; });
760 }
761 
762 SmallVector<PortInfo> CombComponentOp::getOutputPortInfo() {
763  return getFilteredPorts(
764  *this, [](const PortInfo &port) { return port.direction == Input; });
765 }
766 
767 void CombComponentOp::print(OpAsmPrinter &p) {
768  printComponentInterface<CombComponentOp>(p, *this);
769 }
770 
771 ParseResult CombComponentOp::parse(OpAsmParser &parser,
772  OperationState &result) {
773  return parseComponentInterface<CombComponentOp>(parser, result);
774 }
775 
776 LogicalResult CombComponentOp::verify() {
777  // Verify there is exactly one wires operation.
778  auto wIt = getBodyBlock()->getOps<WiresOp>();
779  if (std::distance(wIt.begin(), wIt.end()) != 1)
780  return emitOpError() << "requires exactly one "
781  << WiresOp::getOperationName() << " op.";
782 
783  // Verify there is not a control operation.
784  auto cIt = getBodyBlock()->getOps<ControlOp>();
785  if (std::distance(cIt.begin(), cIt.end()) != 0)
786  return emitOpError() << "must not have a `" << ControlOp::getOperationName()
787  << "` op.";
788 
789  // Verify the component actually does something: has continuous assignments.
790  bool hasNoAssignments =
791  getWiresOp().getBodyBlock()->getOps<AssignOp>().empty();
792  if (hasNoAssignments)
793  return emitOpError(
794  "The component currently does nothing. It needs to either have "
795  "continuous assignments in the Wires region or control constructs in "
796  "the Control region.");
797 
798  // Check that all cells are combinational
799  auto cells = getOps<CellInterface>();
800  for (auto cell : cells) {
801  if (!cell.isCombinational())
802  return emitOpError() << "contains non-combinational cell "
803  << cell.instanceName();
804  }
805 
806  // Check that the component has no groups
807  auto groups = getWiresOp().getOps<GroupOp>();
808  if (!groups.empty())
809  return emitOpError() << "contains group " << (*groups.begin()).getSymName();
810 
811  // Combinational groups aren't allowed in combinational components either.
812  // For more information see here:
813  // https://docs.calyxir.org/lang/ref.html#comb-group-definitions
814  auto combGroups = getWiresOp().getOps<CombGroupOp>();
815  if (!combGroups.empty())
816  return emitOpError() << "contains comb group "
817  << (*combGroups.begin()).getSymName();
818 
819  return success();
820 }
821 
822 void CombComponentOp::build(OpBuilder &builder, OperationState &result,
823  StringAttr name, ArrayRef<PortInfo> ports) {
824  buildComponentLike(builder, result, name, ports, /*combinational=*/true);
825 }
826 
827 void CombComponentOp::getAsmBlockArgumentNames(
828  mlir::Region &region, mlir::OpAsmSetValueNameFn setNameFn) {
829  if (region.empty())
830  return;
831  auto ports = getPortNames();
832  auto *block = &getRegion()->front();
833  for (size_t i = 0, e = block->getNumArguments(); i != e; ++i)
834  setNameFn(block->getArgument(i), ports[i].cast<StringAttr>().getValue());
835 }
836 
837 //===----------------------------------------------------------------------===//
838 // ControlOp
839 //===----------------------------------------------------------------------===//
840 LogicalResult ControlOp::verify() { return verifyControlBody(*this); }
841 
842 //===----------------------------------------------------------------------===//
843 // SeqOp
844 //===----------------------------------------------------------------------===//
845 
846 void SeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
847  MLIRContext *context) {
848  patterns.add(collapseControl<SeqOp>);
849  patterns.add(emptyControl<SeqOp>);
850  patterns.insert<CollapseUnaryControl<SeqOp>>(context);
851 }
852 
853 //===----------------------------------------------------------------------===//
854 // ParOp
855 //===----------------------------------------------------------------------===//
856 
857 LogicalResult ParOp::verify() {
858  llvm::SmallSet<StringRef, 8> groupNames;
859 
860  // Add loose requirement that the body of a ParOp may not enable the same
861  // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G }
862  for (EnableOp op : getBodyBlock()->getOps<EnableOp>()) {
863  StringRef groupName = op.getGroupName();
864  if (groupNames.count(groupName))
865  return emitOpError() << "cannot enable the same group: \"" << groupName
866  << "\" more than once.";
867  groupNames.insert(groupName);
868  }
869 
870  return success();
871 }
872 
873 void ParOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
874  MLIRContext *context) {
875  patterns.add(collapseControl<ParOp>);
876  patterns.add(emptyControl<ParOp>);
877  patterns.insert<CollapseUnaryControl<ParOp>>(context);
878 }
879 
880 //===----------------------------------------------------------------------===//
881 // WiresOp
882 //===----------------------------------------------------------------------===//
883 LogicalResult WiresOp::verify() {
884  auto componentInterface = (*this)->getParentOfType<ComponentInterface>();
885  if (llvm::isa<ComponentOp>(componentInterface)) {
886  auto component = llvm::cast<ComponentOp>(componentInterface);
887  auto control = component.getControlOp();
888 
889  // Verify each group is referenced in the control section.
890  for (auto &&op : *getBodyBlock()) {
891  if (!isa<GroupInterface>(op))
892  continue;
893  auto group = cast<GroupInterface>(op);
894  auto groupName = group.symName();
895  if (mlir::SymbolTable::symbolKnownUseEmpty(groupName, control))
896  return op.emitOpError()
897  << "with name: " << groupName
898  << " is unused in the control execution schedule";
899  }
900  }
901 
902  // Verify that:
903  // - At most one continuous assignment exists for any given value
904  // - A continuously assigned wire has no assignments inside groups.
905  for (auto thisAssignment : getBodyBlock()->getOps<AssignOp>()) {
906  // Always assume guarded assignments will not be driven simultaneously. We
907  // liberally assume that guards are mutually exclusive (more elaborate
908  // static and dynamic checking can be performed to validate such cases).
909  if (thisAssignment.getGuard())
910  continue;
911 
912  Value dest = thisAssignment.getDest();
913  for (Operation *user : dest.getUsers()) {
914  auto assignUser = dyn_cast<AssignOp>(user);
915  if (!assignUser || assignUser.getDest() != dest ||
916  assignUser == thisAssignment)
917  continue;
918 
919  return user->emitOpError() << "destination is already continuously "
920  "driven. Other assignment is "
921  << thisAssignment;
922  }
923  }
924 
925  return success();
926 }
927 
928 //===----------------------------------------------------------------------===//
929 // CombGroupOp
930 //===----------------------------------------------------------------------===//
931 
932 /// Verifies the defining operation of a value is combinational.
933 static LogicalResult isCombinational(Value value, GroupInterface group) {
934  Operation *definingOp = value.getDefiningOp();
935  if (definingOp == nullptr || definingOp->hasTrait<Combinational>())
936  // This is a port of the parent component or combinational.
937  return success();
938 
939  // For now, assumes all component instances are combinational. Once
940  // combinational components are supported, this can be strictly enforced.
941  if (isa<InstanceOp>(definingOp))
942  return success();
943 
944  // Constants and logical operations are OK.
945  if (isa<comb::CombDialect, hw::HWDialect>(definingOp->getDialect()))
946  return success();
947 
948  // Reads to MemoryOp and RegisterOp are combinational. Writes are not.
949  if (auto r = dyn_cast<RegisterOp>(definingOp)) {
950  return value == r.getOut()
951  ? success()
952  : group->emitOpError()
953  << "with register: \"" << r.instanceName()
954  << "\" is conducting a memory store. This is not "
955  "combinational.";
956  } else if (auto m = dyn_cast<MemoryOp>(definingOp)) {
957  auto writePorts = {m.writeData(), m.writeEn()};
958  return (llvm::none_of(writePorts, [&](Value p) { return p == value; }))
959  ? success()
960  : group->emitOpError()
961  << "with memory: \"" << m.instanceName()
962  << "\" is conducting a memory store. This "
963  "is not combinational.";
964  }
965 
966  std::string portName =
967  valueName(group->getParentOfType<ComponentOp>(), value);
968  return group->emitOpError() << "with port: " << portName
969  << ". This operation is not combinational.";
970 }
971 
972 /// Verifies a combinational group may contain only combinational primitives or
973 /// perform combinational logic.
974 LogicalResult CombGroupOp::verify() {
975  for (auto &&op : *getBodyBlock()) {
976  auto assign = dyn_cast<AssignOp>(op);
977  if (assign == nullptr)
978  continue;
979  Value dst = assign.getDest(), src = assign.getSrc();
980  if (failed(isCombinational(dst, *this)) ||
981  failed(isCombinational(src, *this)))
982  return failure();
983  }
984  return success();
985 }
986 
987 //===----------------------------------------------------------------------===//
988 // GroupOp
989 //===----------------------------------------------------------------------===//
990 GroupGoOp GroupOp::getGoOp() {
991  auto goOps = getBodyBlock()->getOps<GroupGoOp>();
992  size_t nOps = std::distance(goOps.begin(), goOps.end());
993  return nOps ? *goOps.begin() : GroupGoOp();
994 }
995 
996 GroupDoneOp GroupOp::getDoneOp() {
997  auto body = this->getBodyBlock();
998  return cast<GroupDoneOp>(body->getTerminator());
999 }
1000 
1001 //===----------------------------------------------------------------------===//
1002 // GroupInterface
1003 //===----------------------------------------------------------------------===//
1004 
1005 /// Determines whether the given port is used in the group. Its use depends on
1006 /// the `isDriven` value; if true, then the port should be a destination in an
1007 /// AssignOp. Otherwise, it should be the source, i.e. a read.
1008 static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven) {
1009  return llvm::any_of(port.getUses(), [&](auto &&use) {
1010  auto assignOp = dyn_cast<AssignOp>(use.getOwner());
1011  if (assignOp == nullptr)
1012  return false;
1013 
1014  Operation *parent = assignOp->getParentOp();
1015  if (isa<WiresOp>(parent))
1016  // This is a continuous assignment.
1017  return false;
1018 
1019  // A port is used if it meet the criteria:
1020  // (1) it is a {source, destination} of an assignment.
1021  // (2) that assignment is found in the provided group.
1022 
1023  // If not driven, then read.
1024  Value expected = isDriven ? assignOp.getDest() : assignOp.getSrc();
1025  return expected == port && group == parent;
1026  });
1027 }
1028 
1029 /// Checks whether `port` is driven from within `groupOp`.
1030 static LogicalResult portDrivenByGroup(GroupInterface groupOp, Value port) {
1031  // Check if the port is driven by an assignOp from within `groupOp`.
1032  if (portIsUsedInGroup(groupOp, port, /*isDriven=*/true))
1033  return success();
1034 
1035  // If `port` is an output of a cell then we conservatively enforce that at
1036  // least one input port of the cell must be driven by the group.
1037  if (auto cell = dyn_cast<CellInterface>(port.getDefiningOp());
1038  cell && cell.direction(port) == calyx::Direction::Output)
1039  return groupOp.drivesAnyPort(cell.getInputPorts());
1040 
1041  return failure();
1042 }
1043 
1044 LogicalResult GroupOp::drivesPort(Value port) {
1045  return portDrivenByGroup(*this, port);
1046 }
1047 
1048 LogicalResult CombGroupOp::drivesPort(Value port) {
1049  return portDrivenByGroup(*this, port);
1050 }
1051 
1052 /// Checks whether all ports are driven within the group.
1053 static LogicalResult allPortsDrivenByGroup(GroupInterface group,
1054  ValueRange ports) {
1055  return success(llvm::all_of(ports, [&](Value port) {
1056  return portIsUsedInGroup(group, port, /*isDriven=*/true);
1057  }));
1058 }
1059 
1060 LogicalResult GroupOp::drivesAllPorts(ValueRange ports) {
1061  return allPortsDrivenByGroup(*this, ports);
1062 }
1063 
1064 LogicalResult CombGroupOp::drivesAllPorts(ValueRange ports) {
1065  return allPortsDrivenByGroup(*this, ports);
1066 }
1067 
1068 /// Checks whether any ports are driven within the group.
1069 static LogicalResult anyPortsDrivenByGroup(GroupInterface group,
1070  ValueRange ports) {
1071  return success(llvm::any_of(ports, [&](Value port) {
1072  return portIsUsedInGroup(group, port, /*isDriven=*/true);
1073  }));
1074 }
1075 
1076 LogicalResult GroupOp::drivesAnyPort(ValueRange ports) {
1077  return anyPortsDrivenByGroup(*this, ports);
1078 }
1079 
1080 LogicalResult CombGroupOp::drivesAnyPort(ValueRange ports) {
1081  return anyPortsDrivenByGroup(*this, ports);
1082 }
1083 
1084 /// Checks whether any ports are read within the group.
1085 static LogicalResult anyPortsReadByGroup(GroupInterface group,
1086  ValueRange ports) {
1087  return success(llvm::any_of(ports, [&](Value port) {
1088  return portIsUsedInGroup(group, port, /*isDriven=*/false);
1089  }));
1090 }
1091 
1092 LogicalResult GroupOp::readsAnyPort(ValueRange ports) {
1093  return anyPortsReadByGroup(*this, ports);
1094 }
1095 
1096 LogicalResult CombGroupOp::readsAnyPort(ValueRange ports) {
1097  return anyPortsReadByGroup(*this, ports);
1098 }
1099 
1100 /// Verifies that certain ports of primitives are either driven or read
1101 /// together.
1102 static LogicalResult verifyPrimitivePortDriving(AssignOp assign,
1103  GroupInterface group) {
1104  Operation *destDefiningOp = assign.getDest().getDefiningOp();
1105  if (destDefiningOp == nullptr)
1106  return success();
1107  auto destCell = dyn_cast<CellInterface>(destDefiningOp);
1108  if (destCell == nullptr)
1109  return success();
1110 
1111  LogicalResult verifyWrites =
1112  TypeSwitch<Operation *, LogicalResult>(destCell)
1113  .Case<RegisterOp>([&](auto op) {
1114  // We only want to verify this is written to if the {write enable,
1115  // in} port is driven.
1116  return succeeded(group.drivesAnyPort({op.getWriteEn(), op.getIn()}))
1117  ? group.drivesAllPorts({op.getWriteEn(), op.getIn()})
1118  : success();
1119  })
1120  .Case<MemoryOp>([&](auto op) {
1121  SmallVector<Value> requiredWritePorts;
1122  // If writing to memory, write_en, write_data, and all address ports
1123  // should be driven.
1124  requiredWritePorts.push_back(op.writeEn());
1125  requiredWritePorts.push_back(op.writeData());
1126  for (Value address : op.addrPorts())
1127  requiredWritePorts.push_back(address);
1128 
1129  // We only want to verify the write ports if either write_data or
1130  // write_en is driven.
1131  return succeeded(
1132  group.drivesAnyPort({op.writeData(), op.writeEn()}))
1133  ? group.drivesAllPorts(requiredWritePorts)
1134  : success();
1135  })
1136  .Case<AndLibOp, OrLibOp, XorLibOp, AddLibOp, SubLibOp, GtLibOp,
1137  LtLibOp, EqLibOp, NeqLibOp, GeLibOp, LeLibOp, LshLibOp,
1138  RshLibOp, SgtLibOp, SltLibOp, SeqLibOp, SneqLibOp, SgeLibOp,
1139  SleLibOp, SrshLibOp>([&](auto op) {
1140  Value lhs = op.getLeft(), rhs = op.getRight();
1141  return succeeded(group.drivesAnyPort({lhs, rhs}))
1142  ? group.drivesAllPorts({lhs, rhs})
1143  : success();
1144  })
1145  .Default([&](auto op) { return success(); });
1146 
1147  if (failed(verifyWrites))
1148  return group->emitOpError()
1149  << "with cell: " << destCell->getName() << " \""
1150  << destCell.instanceName()
1151  << "\" is performing a write and failed to drive all necessary "
1152  "ports.";
1153 
1154  Operation *srcDefiningOp = assign.getSrc().getDefiningOp();
1155  if (srcDefiningOp == nullptr)
1156  return success();
1157  auto srcCell = dyn_cast<CellInterface>(srcDefiningOp);
1158  if (srcCell == nullptr)
1159  return success();
1160 
1161  LogicalResult verifyReads =
1162  TypeSwitch<Operation *, LogicalResult>(srcCell)
1163  .Case<MemoryOp>([&](auto op) {
1164  // If reading memory, all address ports should be driven. Note that
1165  // we only want to verify the read ports if read_data is used in the
1166  // group.
1167  return succeeded(group.readsAnyPort({op.readData()}))
1168  ? group.drivesAllPorts(op.addrPorts())
1169  : success();
1170  })
1171  .Default([&](auto op) { return success(); });
1172 
1173  if (failed(verifyReads))
1174  return group->emitOpError() << "with cell: " << srcCell->getName() << " \""
1175  << srcCell.instanceName()
1176  << "\" is having a read performed upon it, and "
1177  "failed to drive all necessary ports.";
1178 
1179  return success();
1180 }
1181 
1182 LogicalResult calyx::verifyGroupInterface(Operation *op) {
1183  auto group = dyn_cast<GroupInterface>(op);
1184  if (group == nullptr)
1185  return success();
1186 
1187  for (auto &&groupOp : *group.getBody()) {
1188  auto assign = dyn_cast<AssignOp>(groupOp);
1189  if (assign == nullptr)
1190  continue;
1191  if (failed(verifyPrimitivePortDriving(assign, group)))
1192  return failure();
1193  }
1194 
1195  return success();
1196 }
1197 
1198 //===----------------------------------------------------------------------===//
1199 // Utilities for operations with the Cell trait.
1200 //===----------------------------------------------------------------------===//
1201 
1202 /// Gives each result of the cell a meaningful name in the form:
1203 /// <instance-name>.<port-name>
1204 static void getCellAsmResultNames(OpAsmSetValueNameFn setNameFn, Operation *op,
1205  ArrayRef<StringRef> portNames) {
1206  auto cellInterface = dyn_cast<CellInterface>(op);
1207  assert(cellInterface && "must implement the Cell interface");
1208 
1209  std::string prefix = cellInterface.instanceName().str() + ".";
1210  for (size_t i = 0, e = portNames.size(); i != e; ++i)
1211  setNameFn(op->getResult(i), prefix + portNames[i].str());
1212 }
1213 
1214 //===----------------------------------------------------------------------===//
1215 // AssignOp
1216 //===----------------------------------------------------------------------===//
1217 
1218 /// Determines whether the given direction is valid with the given inputs. The
1219 /// `isDestination` boolean is used to distinguish whether the value is a source
1220 /// or a destination.
1221 static LogicalResult verifyPortDirection(AssignOp op, Value value,
1222  bool isDestination) {
1223  Operation *definingOp = value.getDefiningOp();
1224  bool isComponentPort = value.isa<BlockArgument>(),
1225  isCellInterfacePort = definingOp && isa<CellInterface>(definingOp);
1226  assert((isComponentPort || isCellInterfacePort) && "Not a port.");
1227 
1228  PortInfo port = isComponentPort
1229  ? getPortInfo(value.cast<BlockArgument>())
1230  : cast<CellInterface>(definingOp).portInfo(value);
1231 
1232  bool isSource = !isDestination;
1233  // Component output ports and cell interface input ports should be driven.
1234  Direction validDirection =
1235  (isDestination && isComponentPort) || (isSource && isCellInterfacePort)
1237  : Direction::Input;
1238 
1239  return port.direction == validDirection
1240  ? success()
1241  : op.emitOpError()
1242  << "has a " << (isComponentPort ? "component" : "cell")
1243  << " port as the "
1244  << (isDestination ? "destination" : "source")
1245  << " with the incorrect direction.";
1246 }
1247 
1248 /// Verifies the value of a given assignment operation. The boolean
1249 /// `isDestination` is used to distinguish whether the destination
1250 /// or source of the AssignOp is to be verified.
1251 static LogicalResult verifyAssignOpValue(AssignOp op, bool isDestination) {
1252  bool isSource = !isDestination;
1253  Value value = isDestination ? op.getDest() : op.getSrc();
1254  if (isPort(value))
1255  return verifyPortDirection(op, value, isDestination);
1256 
1257  // A destination may also be the Go or Done hole of a GroupOp.
1258  if (isDestination && !isa<GroupGoOp, GroupDoneOp>(value.getDefiningOp()))
1259  return op->emitOpError(
1260  "has an invalid destination port. It must be drive-able.");
1261  else if (isSource)
1262  return verifyNotComplexSource(op);
1263 
1264  return success();
1265 }
1266 
1267 LogicalResult AssignOp::verify() {
1268  bool isDestination = true, isSource = false;
1269  if (failed(verifyAssignOpValue(*this, isDestination)))
1270  return failure();
1271  if (failed(verifyAssignOpValue(*this, isSource)))
1272  return failure();
1273 
1274  return success();
1275 }
1276 
1277 ParseResult AssignOp::parse(OpAsmParser &parser, OperationState &result) {
1278  OpAsmParser::UnresolvedOperand destination;
1279  if (parser.parseOperand(destination) || parser.parseEqual())
1280  return failure();
1281 
1282  // An AssignOp takes one of the two following forms:
1283  // (1) %<dest> = %<src> : <type>
1284  // (2) %<dest> = %<guard> ? %<src> : <type>
1285  OpAsmParser::UnresolvedOperand guardOrSource;
1286  if (parser.parseOperand(guardOrSource))
1287  return failure();
1288 
1289  // Since the guard is optional, we need to check if there is an accompanying
1290  // `?` symbol.
1291  OpAsmParser::UnresolvedOperand source;
1292  bool hasGuard = succeeded(parser.parseOptionalQuestion());
1293  if (hasGuard) {
1294  // The guard exists. Parse the source.
1295  if (parser.parseOperand(source))
1296  return failure();
1297  }
1298 
1299  Type type;
1300  if (parser.parseColonType(type) ||
1301  parser.resolveOperand(destination, type, result.operands))
1302  return failure();
1303 
1304  if (hasGuard) {
1305  Type i1Type = parser.getBuilder().getI1Type();
1306  // Since the guard is optional, it is listed last in the arguments of the
1307  // AssignOp. Therefore, we must parse the source first.
1308  if (parser.resolveOperand(source, type, result.operands) ||
1309  parser.resolveOperand(guardOrSource, i1Type, result.operands))
1310  return failure();
1311  } else {
1312  // This is actually a source.
1313  if (parser.resolveOperand(guardOrSource, type, result.operands))
1314  return failure();
1315  }
1316 
1317  return success();
1318 }
1319 
1320 void AssignOp::print(OpAsmPrinter &p) {
1321  p << " " << getDest() << " = ";
1322 
1323  Value bguard = getGuard(), source = getSrc();
1324  // The guard is optional.
1325  if (bguard)
1326  p << bguard << " ? ";
1327 
1328  // We only need to print a single type; the destination and source are
1329  // guaranteed to be the same type.
1330  p << source << " : " << source.getType();
1331 }
1332 
1333 //===----------------------------------------------------------------------===//
1334 // InstanceOp
1335 //===----------------------------------------------------------------------===//
1336 
1337 /// Lookup the component for the symbol. This returns null on
1338 /// invalid IR.
1339 ComponentInterface InstanceOp::getReferencedComponent() {
1340  auto module = (*this)->getParentOfType<ModuleOp>();
1341  if (!module)
1342  return nullptr;
1343 
1344  return module.lookupSymbol<ComponentInterface>(getComponentName());
1345 }
1346 
1347 /// Verifies the port information in comparison with the referenced component
1348 /// of an instance. This helper function avoids conducting a lookup for the
1349 /// referenced component twice.
1350 static LogicalResult
1351 verifyInstanceOpType(InstanceOp instance,
1352  ComponentInterface referencedComponent) {
1353  auto module = instance->getParentOfType<ModuleOp>();
1354  StringRef entryPointName =
1355  module->getAttrOfType<StringAttr>("calyx.entrypoint");
1356  if (instance.getComponentName() == entryPointName)
1357  return instance.emitOpError()
1358  << "cannot reference the entry-point component: '" << entryPointName
1359  << "'.";
1360 
1361  // Verify the instance result ports with those of its referenced component.
1362  SmallVector<PortInfo> componentPorts = referencedComponent.getPortInfo();
1363  size_t numPorts = componentPorts.size();
1364 
1365  size_t numResults = instance.getNumResults();
1366  if (numResults != numPorts)
1367  return instance.emitOpError()
1368  << "has a wrong number of results; expected: " << numPorts
1369  << " but got " << numResults;
1370 
1371  for (size_t i = 0; i != numResults; ++i) {
1372  auto resultType = instance.getResult(i).getType();
1373  auto expectedType = componentPorts[i].type;
1374  if (resultType == expectedType)
1375  continue;
1376  return instance.emitOpError()
1377  << "result type for " << componentPorts[i].name << " must be "
1378  << expectedType << ", but got " << resultType;
1379  }
1380  return success();
1381 }
1382 
1383 LogicalResult InstanceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1384  Operation *op = *this;
1385  auto module = op->getParentOfType<ModuleOp>();
1386  Operation *referencedComponent =
1387  symbolTable.lookupNearestSymbolFrom(module, getComponentNameAttr());
1388  if (referencedComponent == nullptr)
1389  return emitError() << "referencing component: '" << getComponentName()
1390  << "', which does not exist.";
1391 
1392  Operation *shadowedComponentName =
1393  symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1394  if (shadowedComponentName != nullptr)
1395  return emitError() << "instance symbol: '" << instanceName()
1396  << "' is already a symbol for another component.";
1397 
1398  // Verify the referenced component is not instantiating itself.
1399  auto parentComponent = op->getParentOfType<ComponentOp>();
1400  if (parentComponent == referencedComponent)
1401  return emitError() << "recursive instantiation of its parent component: '"
1402  << getComponentName() << "'";
1403 
1404  assert(isa<ComponentInterface>(referencedComponent) &&
1405  "Should be a ComponentInterface.");
1406  return verifyInstanceOpType(*this,
1407  cast<ComponentInterface>(referencedComponent));
1408 }
1409 
1410 /// Provide meaningful names to the result values of an InstanceOp.
1412  getCellAsmResultNames(setNameFn, *this, this->portNames());
1413 }
1414 
1415 SmallVector<StringRef> InstanceOp::portNames() {
1416  SmallVector<StringRef> portNames;
1417  for (Attribute name : getReferencedComponent().getPortNames())
1418  portNames.push_back(name.cast<StringAttr>().getValue());
1419  return portNames;
1420 }
1421 
1422 SmallVector<Direction> InstanceOp::portDirections() {
1423  SmallVector<Direction> portDirections;
1424  for (const PortInfo &port : getReferencedComponent().getPortInfo())
1425  portDirections.push_back(port.direction);
1426  return portDirections;
1427 }
1428 
1429 SmallVector<DictionaryAttr> InstanceOp::portAttributes() {
1430  SmallVector<DictionaryAttr> portAttributes;
1431  for (const PortInfo &port : getReferencedComponent().getPortInfo())
1432  portAttributes.push_back(port.attributes);
1433  return portAttributes;
1434 }
1435 
1437  return isa<CombComponentOp>(getReferencedComponent());
1438 }
1439 
1440 //===----------------------------------------------------------------------===//
1441 // PrimitiveOp
1442 //===----------------------------------------------------------------------===//
1443 
1444 /// Lookup the component for the symbol. This returns null on
1445 /// invalid IR.
1446 hw::HWModuleExternOp PrimitiveOp::getReferencedPrimitive() {
1447  auto module = (*this)->getParentOfType<ModuleOp>();
1448  if (!module)
1449  return nullptr;
1450 
1451  return module.lookupSymbol<hw::HWModuleExternOp>(getPrimitiveName());
1452 }
1453 
1454 /// Verifies the port information in comparison with the referenced component
1455 /// of an instance. This helper function avoids conducting a lookup for the
1456 /// referenced component twice.
1457 static LogicalResult
1458 verifyPrimitiveOpType(PrimitiveOp instance,
1459  hw::HWModuleExternOp referencedPrimitive) {
1460  auto module = instance->getParentOfType<ModuleOp>();
1461  StringRef entryPointName =
1462  module->getAttrOfType<StringAttr>("calyx.entrypoint");
1463  if (instance.getPrimitiveName() == entryPointName)
1464  return instance.emitOpError()
1465  << "cannot reference the entry-point component: '" << entryPointName
1466  << "'.";
1467 
1468  // Verify the instance result ports with those of its referenced component.
1469  SmallVector<hw::PortInfo> primitivePorts = referencedPrimitive.getAllPorts();
1470  size_t numPorts = primitivePorts.size();
1471 
1472  size_t numResults = instance.getNumResults();
1473  if (numResults != numPorts)
1474  return instance.emitOpError()
1475  << "has a wrong number of results; expected: " << numPorts
1476  << " but got " << numResults;
1477 
1478  // Verify parameters match up
1479  ArrayAttr modParameters = referencedPrimitive.getParameters();
1480  ArrayAttr parameters = instance.getParameters().value_or(ArrayAttr());
1481  size_t numExpected = modParameters.size();
1482  size_t numParams = parameters.size();
1483  if (numParams != numExpected)
1484  return instance.emitOpError()
1485  << "has the wrong number of parameters; expected: " << numExpected
1486  << " but got " << numParams;
1487 
1488  for (size_t i = 0; i != numExpected; ++i) {
1489  auto param = parameters[i].cast<circt::hw::ParamDeclAttr>();
1490  auto modParam = modParameters[i].cast<circt::hw::ParamDeclAttr>();
1491 
1492  auto paramName = param.getName();
1493  if (paramName != modParam.getName())
1494  return instance.emitOpError()
1495  << "parameter #" << i << " should have name " << modParam.getName()
1496  << " but has name " << paramName;
1497 
1498  if (param.getType() != modParam.getType())
1499  return instance.emitOpError()
1500  << "parameter " << paramName << " should have type "
1501  << modParam.getType() << " but has type " << param.getType();
1502 
1503  // All instance parameters must have a value. Specify the same value as
1504  // a module's default value if you want the default.
1505  if (!param.getValue())
1506  return instance.emitOpError("parameter ")
1507  << paramName << " must have a value";
1508  }
1509 
1510  for (size_t i = 0; i != numResults; ++i) {
1511  auto resultType = instance.getResult(i).getType();
1512  auto expectedType = primitivePorts[i].type;
1513  auto replacedType = hw::evaluateParametricType(
1514  instance.getLoc(), instance.getParametersAttr(), expectedType);
1515  if (failed(replacedType))
1516  return failure();
1517  if (resultType == replacedType)
1518  continue;
1519  return instance.emitOpError()
1520  << "result type for " << primitivePorts[i].name << " must be "
1521  << expectedType << ", but got " << resultType;
1522  }
1523  return success();
1524 }
1525 
1526 LogicalResult
1527 PrimitiveOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1528  Operation *op = *this;
1529  auto module = op->getParentOfType<ModuleOp>();
1530  Operation *referencedPrimitive =
1531  symbolTable.lookupNearestSymbolFrom(module, getPrimitiveNameAttr());
1532  if (referencedPrimitive == nullptr)
1533  return emitError() << "referencing primitive: '" << getPrimitiveName()
1534  << "', which does not exist.";
1535 
1536  Operation *shadowedPrimitiveName =
1537  symbolTable.lookupNearestSymbolFrom(module, getSymNameAttr());
1538  if (shadowedPrimitiveName != nullptr)
1539  return emitError() << "instance symbol: '" << instanceName()
1540  << "' is already a symbol for another primitive.";
1541 
1542  // Verify the referenced primitive is not instantiating itself.
1543  auto parentPrimitive = op->getParentOfType<hw::HWModuleExternOp>();
1544  if (parentPrimitive == referencedPrimitive)
1545  return emitError() << "recursive instantiation of its parent primitive: '"
1546  << getPrimitiveName() << "'";
1547 
1548  assert(isa<hw::HWModuleExternOp>(referencedPrimitive) &&
1549  "Should be a HardwareModuleExternOp.");
1550 
1551  return verifyPrimitiveOpType(*this,
1552  cast<hw::HWModuleExternOp>(referencedPrimitive));
1553 }
1554 
1555 /// Provide meaningful names to the result values of an PrimitiveOp.
1557  getCellAsmResultNames(setNameFn, *this, this->portNames());
1558 }
1559 
1560 SmallVector<StringRef> PrimitiveOp::portNames() {
1561  SmallVector<StringRef> portNames;
1562  for (hw::PortInfo port : getReferencedPrimitive().getAllPorts())
1563  portNames.push_back(port.name.getValue());
1564 
1565  return portNames;
1566 }
1567 
1569  switch (direction) {
1571  return Direction::Input;
1573  return Direction::Output;
1575  llvm_unreachable("InOut ports not supported by Calyx");
1576  }
1577  llvm_unreachable("Impossible port type");
1578 }
1579 
1580 SmallVector<Direction> PrimitiveOp::portDirections() {
1581  SmallVector<Direction> portDirections;
1582  for (hw::PortInfo port : getReferencedPrimitive().getAllPorts())
1583  portDirections.push_back(convertHWDirectionToCalyx(port.direction));
1584  return portDirections;
1585 }
1586 
1587 bool PrimitiveOp::isCombinational() { return false; }
1588 
1589 /// Returns a new DictionaryAttr containing only the calyx dialect attrs
1590 /// in the input DictionaryAttr. Also strips the 'calyx.' prefix from these
1591 /// attrs.
1592 static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder,
1593  DictionaryAttr dict) {
1594  if (!dict) {
1595  return dict;
1596  }
1597  llvm::SmallVector<NamedAttribute> attrs;
1598  for (NamedAttribute attr : dict) {
1599  Dialect *dialect = attr.getNameDialect();
1600  if (dialect == nullptr || !isa<CalyxDialect>(*dialect))
1601  continue;
1602  StringRef name = attr.getName().strref();
1603  StringAttr newName = builder.getStringAttr(std::get<1>(name.split(".")));
1604  attr.setName(newName);
1605  attrs.push_back(attr);
1606  }
1607  return builder.getDictionaryAttr(attrs);
1608 }
1609 
1610 // Grabs calyx port attributes from the HWModuleExternOp arg/result attributes.
1611 SmallVector<DictionaryAttr> PrimitiveOp::portAttributes() {
1612  SmallVector<DictionaryAttr> portAttributes;
1613  OpBuilder builder(getContext());
1614  hw::HWModuleExternOp prim = getReferencedPrimitive();
1615  for (size_t i = 0, e = prim.getNumArguments(); i != e; ++i) {
1616  DictionaryAttr dict = cleanCalyxPortAttrs(builder, prim.getArgAttrDict(i));
1617  portAttributes.push_back(dict);
1618  }
1619  for (size_t i = 0, e = prim.getNumResults(); i != e; ++i) {
1620  DictionaryAttr dict =
1621  cleanCalyxPortAttrs(builder, prim.getResultAttrDict(i));
1622  portAttributes.push_back(dict);
1623  }
1624  return portAttributes;
1625 }
1626 
1627 /// Parse an parameter list if present. Same format as HW dialect.
1628 /// module-parameter-list ::= `<` parameter-decl (`,` parameter-decl)* `>`
1629 /// parameter-decl ::= identifier `:` type
1630 /// parameter-decl ::= identifier `:` type `=` attribute
1631 ///
1632 static ParseResult parseParameterList(OpAsmParser &parser,
1633  SmallVector<Attribute> &parameters) {
1634 
1635  return parser.parseCommaSeparatedList(
1636  OpAsmParser::Delimiter::OptionalLessGreater, [&]() {
1637  std::string name;
1638  Type type;
1639  Attribute value;
1640 
1641  if (parser.parseKeywordOrString(&name) || parser.parseColonType(type))
1642  return failure();
1643 
1644  // Parse the default value if present.
1645  if (succeeded(parser.parseOptionalEqual())) {
1646  if (parser.parseAttribute(value, type))
1647  return failure();
1648  }
1649 
1650  auto &builder = parser.getBuilder();
1651  parameters.push_back(hw::ParamDeclAttr::get(
1652  builder.getContext(), builder.getStringAttr(name), type, value));
1653  return success();
1654  });
1655 }
1656 
1657 /// Shim to also use this for the InstanceOp custom parser.
1658 static ParseResult parseParameterList(OpAsmParser &parser,
1659  ArrayAttr &parameters) {
1660  SmallVector<Attribute> parseParameters;
1661  if (failed(parseParameterList(parser, parseParameters)))
1662  return failure();
1663 
1664  parameters = ArrayAttr::get(parser.getContext(), parseParameters);
1665 
1666  return success();
1667 }
1668 
1669 /// Print a parameter list for a module or instance. Same format as HW dialect.
1670 static void printParameterList(OpAsmPrinter &p, Operation *op,
1671  ArrayAttr parameters) {
1672  if (parameters.empty())
1673  return;
1674 
1675  p << '<';
1676  llvm::interleaveComma(parameters, p, [&](Attribute param) {
1677  auto paramAttr = param.cast<hw::ParamDeclAttr>();
1678  p << paramAttr.getName().getValue() << ": " << paramAttr.getType();
1679  if (auto value = paramAttr.getValue()) {
1680  p << " = ";
1681  p.printAttributeWithoutType(value);
1682  }
1683  });
1684  p << '>';
1685 }
1686 
1687 //===----------------------------------------------------------------------===//
1688 // GroupGoOp
1689 //===----------------------------------------------------------------------===//
1690 
1691 LogicalResult GroupGoOp::verify() { return verifyNotComplexSource(*this); }
1692 
1693 /// Provide meaningful names to the result value of a GroupGoOp.
1694 void GroupGoOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1695  auto parent = (*this)->getParentOfType<GroupOp>();
1696  StringRef name = parent.getSymName();
1697  std::string resultName = name.str() + ".go";
1698  setNameFn(getResult(), resultName);
1699 }
1700 
1701 void GroupGoOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1702 
1703 ParseResult GroupGoOp::parse(OpAsmParser &parser, OperationState &result) {
1704  if (parseGroupPort(parser, result))
1705  return failure();
1706 
1707  result.addTypes(parser.getBuilder().getI1Type());
1708  return success();
1709 }
1710 
1711 //===----------------------------------------------------------------------===//
1712 // GroupDoneOp
1713 //===----------------------------------------------------------------------===//
1714 
1715 LogicalResult GroupDoneOp::verify() {
1716  Operation *srcOp = getSrc().getDefiningOp();
1717  Value optionalGuard = getGuard();
1718  Operation *guardOp = optionalGuard ? optionalGuard.getDefiningOp() : nullptr;
1719  bool noGuard = (guardOp == nullptr);
1720 
1721  if (srcOp == nullptr)
1722  // This is a port of the parent component.
1723  return success();
1724 
1725  if (isa<hw::ConstantOp>(srcOp) && (noGuard || isa<hw::ConstantOp>(guardOp)))
1726  return emitOpError() << "with constant source"
1727  << (noGuard ? "" : " and constant guard")
1728  << ". This should be a combinational group.";
1729 
1730  return verifyNotComplexSource(*this);
1731 }
1732 
1733 void GroupDoneOp::print(OpAsmPrinter &p) { printGroupPort(p, *this); }
1734 
1735 ParseResult GroupDoneOp::parse(OpAsmParser &parser, OperationState &result) {
1736  return parseGroupPort(parser, result);
1737 }
1738 
1739 //===----------------------------------------------------------------------===//
1740 // RegisterOp
1741 //===----------------------------------------------------------------------===//
1742 
1743 /// Provide meaningful names to the result values of a RegisterOp.
1744 void RegisterOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1745  getCellAsmResultNames(setNameFn, *this, this->portNames());
1746 }
1747 
1748 SmallVector<StringRef> RegisterOp::portNames() {
1749  return {"in", "write_en", "clk", "reset", "out", "done"};
1750 }
1751 
1752 SmallVector<Direction> RegisterOp::portDirections() {
1753  return {Input, Input, Input, Input, Output, Output};
1754 }
1755 
1756 SmallVector<DictionaryAttr> RegisterOp::portAttributes() {
1757  MLIRContext *context = getContext();
1758  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
1759  NamedAttrList writeEn, clk, reset, done;
1760  writeEn.append("go", isSet);
1761  clk.append("clk", isSet);
1762  reset.append("reset", isSet);
1763  done.append("done", isSet);
1764  return {
1765  DictionaryAttr::get(context), // In
1766  writeEn.getDictionary(context), // Write enable
1767  clk.getDictionary(context), // Clk
1768  reset.getDictionary(context), // Reset
1769  DictionaryAttr::get(context), // Out
1770  done.getDictionary(context) // Done
1771  };
1772 }
1773 
1774 bool RegisterOp::isCombinational() { return false; }
1775 
1776 //===----------------------------------------------------------------------===//
1777 // MemoryOp
1778 //===----------------------------------------------------------------------===//
1779 
1780 /// Provide meaningful names to the result values of a MemoryOp.
1781 void MemoryOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
1782  getCellAsmResultNames(setNameFn, *this, this->portNames());
1783 }
1784 
1785 SmallVector<StringRef> MemoryOp::portNames() {
1786  SmallVector<StringRef> portNames;
1787  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i) {
1788  auto nameAttr =
1789  StringAttr::get(this->getContext(), "addr" + std::to_string(i));
1790  portNames.push_back(nameAttr.getValue());
1791  }
1792  portNames.append({"write_data", "write_en", "clk", "read_data", "done"});
1793  return portNames;
1794 }
1795 
1796 SmallVector<Direction> MemoryOp::portDirections() {
1797  SmallVector<Direction> portDirections;
1798  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
1799  portDirections.push_back(Input);
1800  portDirections.append({Input, Input, Input, Output, Output});
1801  return portDirections;
1802 }
1803 
1804 SmallVector<DictionaryAttr> MemoryOp::portAttributes() {
1805  SmallVector<DictionaryAttr> portAttributes;
1806  MLIRContext *context = getContext();
1807  for (size_t i = 0, e = getAddrSizes().size(); i != e; ++i)
1808  portAttributes.push_back(DictionaryAttr::get(context)); // Addresses
1809 
1810  // Use a boolean to indicate this attribute is used.
1811  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1);
1812  NamedAttrList writeEn, clk, reset, done;
1813  writeEn.append("go", isSet);
1814  clk.append("clk", isSet);
1815  done.append("done", isSet);
1816  portAttributes.append({DictionaryAttr::get(context), // In
1817  writeEn.getDictionary(context), // Write enable
1818  clk.getDictionary(context), // Clk
1819  DictionaryAttr::get(context), // Out
1820  done.getDictionary(context)} // Done
1821  );
1822  return portAttributes;
1823 }
1824 
1825 bool MemoryOp::isCombinational() { return false; }
1826 
1827 void MemoryOp::build(OpBuilder &builder, OperationState &state,
1828  StringRef instanceName, int64_t width,
1829  ArrayRef<int64_t> sizes, ArrayRef<int64_t> addrSizes) {
1830  state.addAttribute(SymbolTable::getSymbolAttrName(),
1831  builder.getStringAttr(instanceName));
1832  state.addAttribute("width", builder.getI64IntegerAttr(width));
1833  state.addAttribute("sizes", builder.getI64ArrayAttr(sizes));
1834  state.addAttribute("addrSizes", builder.getI64ArrayAttr(addrSizes));
1835  SmallVector<Type> types;
1836  for (int64_t size : addrSizes)
1837  types.push_back(builder.getIntegerType(size)); // Addresses
1838  types.push_back(builder.getIntegerType(width)); // Write data
1839  types.push_back(builder.getI1Type()); // Write enable
1840  types.push_back(builder.getI1Type()); // Clk
1841  types.push_back(builder.getIntegerType(width)); // Read data
1842  types.push_back(builder.getI1Type()); // Done
1843  state.addTypes(types);
1844 }
1845 
1846 LogicalResult MemoryOp::verify() {
1847  ArrayRef<Attribute> opSizes = getSizes().getValue();
1848  ArrayRef<Attribute> opAddrSizes = getAddrSizes().getValue();
1849  size_t numDims = getSizes().size();
1850  size_t numAddrs = getAddrSizes().size();
1851  if (numDims != numAddrs)
1852  return emitOpError("mismatched number of dimensions (")
1853  << numDims << ") and address sizes (" << numAddrs << ")";
1854 
1855  size_t numExtraPorts = 5; // write data/enable, clk, and read data/done.
1856  if (getNumResults() != numAddrs + numExtraPorts)
1857  return emitOpError("incorrect number of address ports, expected ")
1858  << numAddrs;
1859 
1860  for (size_t i = 0; i < numDims; ++i) {
1861  int64_t size = opSizes[i].cast<IntegerAttr>().getInt();
1862  int64_t addrSize = opAddrSizes[i].cast<IntegerAttr>().getInt();
1863  if (llvm::Log2_64_Ceil(size) > addrSize)
1864  return emitOpError("address size (")
1865  << addrSize << ") for dimension " << i
1866  << " can't address the entire range (" << size << ")";
1867  }
1868 
1869  return success();
1870 }
1871 
1872 //===----------------------------------------------------------------------===//
1873 // EnableOp
1874 //===----------------------------------------------------------------------===//
1875 LogicalResult EnableOp::verify() {
1876  auto component = (*this)->getParentOfType<ComponentOp>();
1877  auto wiresOp = component.getWiresOp();
1878  StringRef name = getGroupName();
1879 
1880  auto groupOp = wiresOp.lookupSymbol<GroupInterface>(name);
1881  if (!groupOp)
1882  return emitOpError() << "with group '" << name
1883  << "', which does not exist.";
1884 
1885  if (isa<CombGroupOp>(groupOp))
1886  return emitOpError() << "with group '" << name
1887  << "', which is a combinational group.";
1888 
1889  return success();
1890 }
1891 
1892 //===----------------------------------------------------------------------===//
1893 // IfOp
1894 //===----------------------------------------------------------------------===//
1895 
1896 LogicalResult IfOp::verify() {
1897  auto component = (*this)->getParentOfType<ComponentOp>();
1898  WiresOp wiresOp = component.getWiresOp();
1899 
1900  if (elseBodyExists() && getElseBody()->empty())
1901  return emitError() << "empty 'else' region.";
1902 
1903  std::optional<StringRef> optGroupName = getGroupName();
1904  if (!optGroupName) {
1905  // No combinational group was provided.
1906  return success();
1907  }
1908  StringRef groupName = *optGroupName;
1909  auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
1910  if (!groupOp)
1911  return emitOpError() << "with group '" << groupName
1912  << "', which does not exist.";
1913 
1914  if (isa<GroupOp>(groupOp))
1915  return emitOpError() << "with group '" << groupName
1916  << "', which is not a combinational group.";
1917 
1918  if (failed(groupOp.drivesPort(getCond())))
1919  return emitError() << "with conditional op: '"
1920  << valueName(component, getCond())
1921  << "' expected to be driven from group: '" << groupName
1922  << "' but no driver was found.";
1923 
1924  return success();
1925 }
1926 
1927 /// Returns the last EnableOp within the child tree of 'parentSeqOp'. If no
1928 /// EnableOp was found (e.g. a "calyx.par" operation is present), returns
1929 /// None.
1930 static std::optional<EnableOp> getLastEnableOp(SeqOp parent) {
1931  auto &lastOp = parent.getBodyBlock()->back();
1932  if (auto enableOp = dyn_cast<EnableOp>(lastOp))
1933  return enableOp;
1934  else if (auto seqOp = dyn_cast<SeqOp>(lastOp))
1935  return getLastEnableOp(seqOp);
1936 
1937  return std::nullopt;
1938 }
1939 
1940 /// Returns a mapping of {enabled Group name, EnableOp} for all EnableOps within
1941 /// the immediate ParOp's body.
1942 static llvm::StringMap<EnableOp> getAllEnableOpsInImmediateBody(ParOp parent) {
1943  llvm::StringMap<EnableOp> enables;
1944  Block *body = parent.getBodyBlock();
1945  for (EnableOp op : body->getOps<EnableOp>())
1946  enables.insert(std::pair(op.getGroupName(), op));
1947 
1948  return enables;
1949 }
1950 
1951 /// Checks preconditions for the common tail pattern. This canonicalization is
1952 /// stringent about not entering nested control operations, as this may cause
1953 /// unintentional changes in behavior.
1954 /// We only look for two cases: (1) both regions are ParOps, and
1955 /// (2) both regions are SeqOps. The case when these are different, e.g. ParOp
1956 /// and SeqOp, will only produce less optimal code, or even worse, change the
1957 /// behavior.
1958 template <typename OpTy>
1959 static bool hasCommonTailPatternPreConditions(IfOp op) {
1960  static_assert(std::is_same<SeqOp, OpTy>() || std::is_same<ParOp, OpTy>(),
1961  "Should be a SeqOp or ParOp.");
1962 
1963  if (!op.thenBodyExists() || !op.elseBodyExists())
1964  return false;
1965  if (op.getThenBody()->empty() || op.getElseBody()->empty())
1966  return false;
1967 
1968  Block *thenBody = op.getThenBody(), *elseBody = op.getElseBody();
1969  return isa<OpTy>(thenBody->front()) && isa<OpTy>(elseBody->front());
1970 }
1971 
1972 /// seq {
1973 /// if %a with @G { if %a with @G {
1974 /// seq { ... calyx.enable @A } seq { ... }
1975 /// else { -> } else {
1976 /// seq { ... calyx.enable @A } seq { ... }
1977 /// } }
1978 /// calyx.enable @A
1979 /// }
1980 struct CommonTailPatternWithSeq : mlir::OpRewritePattern<IfOp> {
1981  using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
1982 
1983  LogicalResult matchAndRewrite(IfOp ifOp,
1984  PatternRewriter &rewriter) const override {
1985  if (!hasCommonTailPatternPreConditions<SeqOp>(ifOp))
1986  return failure();
1987 
1988  auto thenControl = cast<SeqOp>(ifOp.getThenBody()->front()),
1989  elseControl = cast<SeqOp>(ifOp.getElseBody()->front());
1990  std::optional<EnableOp> lastThenEnableOp = getLastEnableOp(thenControl),
1991  lastElseEnableOp = getLastEnableOp(elseControl);
1992 
1993  if (!lastThenEnableOp || !lastElseEnableOp)
1994  return failure();
1995  if (lastThenEnableOp->getGroupName() != lastElseEnableOp->getGroupName())
1996  return failure();
1997 
1998  // Place the IfOp and pulled EnableOp inside a sequential region, in case
1999  // this IfOp is nested in a ParOp. This avoids unintentionally
2000  // parallelizing the pulled out EnableOps.
2001  rewriter.setInsertionPointAfter(ifOp);
2002  SeqOp seqOp = rewriter.create<SeqOp>(ifOp.getLoc());
2003  Block *body = seqOp.getBodyBlock();
2004  ifOp->remove();
2005  body->push_back(ifOp);
2006  rewriter.setInsertionPointToEnd(body);
2007  rewriter.create<EnableOp>(seqOp.getLoc(), lastThenEnableOp->getGroupName());
2008 
2009  // Erase the common EnableOp from the Then and Else regions.
2010  rewriter.eraseOp(*lastThenEnableOp);
2011  rewriter.eraseOp(*lastElseEnableOp);
2012  return success();
2013  }
2014 };
2015 
2016 /// if %a with @G { par {
2017 /// par { if %a with @G {
2018 /// ... par { ... }
2019 /// calyx.enable @A } else {
2020 /// calyx.enable @B -> par { ... }
2021 /// } }
2022 /// } else { calyx.enable @A
2023 /// par { calyx.enable @B
2024 /// ... }
2025 /// calyx.enable @A
2026 /// calyx.enable @B
2027 /// }
2028 /// }
2029 struct CommonTailPatternWithPar : mlir::OpRewritePattern<IfOp> {
2030  using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
2031 
2032  LogicalResult matchAndRewrite(IfOp ifOp,
2033  PatternRewriter &rewriter) const override {
2034  if (!hasCommonTailPatternPreConditions<ParOp>(ifOp))
2035  return failure();
2036  auto thenControl = cast<ParOp>(ifOp.getThenBody()->front()),
2037  elseControl = cast<ParOp>(ifOp.getElseBody()->front());
2038 
2039  llvm::StringMap<EnableOp> A = getAllEnableOpsInImmediateBody(thenControl),
2040  B = getAllEnableOpsInImmediateBody(elseControl);
2041 
2042  // Compute the intersection between `A` and `B`.
2043  SmallVector<StringRef> groupNames;
2044  for (auto a = A.begin(); a != A.end(); ++a) {
2045  StringRef groupName = a->getKey();
2046  auto b = B.find(groupName);
2047  if (b == B.end())
2048  continue;
2049  // This is also an element in B.
2050  groupNames.push_back(groupName);
2051  // Since these are being pulled out, erase them.
2052  rewriter.eraseOp(a->getValue());
2053  rewriter.eraseOp(b->getValue());
2054  }
2055  // Place the IfOp and EnableOp(s) inside a parallel region, in case this
2056  // IfOp is nested in a SeqOp. This avoids unintentionally sequentializing
2057  // the pulled out EnableOps.
2058  rewriter.setInsertionPointAfter(ifOp);
2059  ParOp parOp = rewriter.create<ParOp>(ifOp.getLoc());
2060  Block *body = parOp.getBodyBlock();
2061  ifOp->remove();
2062  body->push_back(ifOp);
2063 
2064  // Pull out the intersection between these two sets, and erase their
2065  // counterparts in the Then and Else regions.
2066  rewriter.setInsertionPointToEnd(body);
2067  for (StringRef groupName : groupNames)
2068  rewriter.create<EnableOp>(parOp.getLoc(), groupName);
2069 
2070  return success();
2071  }
2072 };
2073 
2074 /// This pattern checks for one of two cases that will lead to IfOp deletion:
2075 /// (1) Then and Else bodies are both empty.
2076 /// (2) Then body is empty and Else body does not exist.
2077 struct EmptyIfBody : mlir::OpRewritePattern<IfOp> {
2078  using mlir::OpRewritePattern<IfOp>::OpRewritePattern;
2079  LogicalResult matchAndRewrite(IfOp ifOp,
2080  PatternRewriter &rewriter) const override {
2081  if (!ifOp.getThenBody()->empty())
2082  return failure();
2083  if (ifOp.elseBodyExists() && !ifOp.getElseBody()->empty())
2084  return failure();
2085 
2086  eraseControlWithGroupAndConditional(ifOp, rewriter);
2087 
2088  return success();
2089  }
2090 };
2091 
2092 void IfOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2093  MLIRContext *context) {
2094  patterns.add<CommonTailPatternWithSeq, CommonTailPatternWithPar, EmptyIfBody>(
2095  context);
2096 }
2097 
2098 //===----------------------------------------------------------------------===//
2099 // WhileOp
2100 //===----------------------------------------------------------------------===//
2101 LogicalResult WhileOp::verify() {
2102  auto component = (*this)->getParentOfType<ComponentOp>();
2103  auto wiresOp = component.getWiresOp();
2104 
2105  std::optional<StringRef> optGroupName = getGroupName();
2106  if (!optGroupName) {
2107  /// No combinational group was provided
2108  return success();
2109  }
2110  StringRef groupName = *optGroupName;
2111  auto groupOp = wiresOp.lookupSymbol<GroupInterface>(groupName);
2112  if (!groupOp)
2113  return emitOpError() << "with group '" << groupName
2114  << "', which does not exist.";
2115 
2116  if (isa<GroupOp>(groupOp))
2117  return emitOpError() << "with group '" << groupName
2118  << "', which is not a combinational group.";
2119 
2120  if (failed(groupOp.drivesPort(getCond())))
2121  return emitError() << "conditional op: '" << valueName(component, getCond())
2122  << "' expected to be driven from group: '" << groupName
2123  << "' but no driver was found.";
2124 
2125  return success();
2126 }
2127 
2128 LogicalResult WhileOp::canonicalize(WhileOp whileOp,
2129  PatternRewriter &rewriter) {
2130  if (whileOp.getBodyBlock()->empty()) {
2131  eraseControlWithGroupAndConditional(whileOp, rewriter);
2132  return success();
2133  }
2134 
2135  return failure();
2136 }
2137 
2138 //===----------------------------------------------------------------------===//
2139 // Calyx library ops
2140 //===----------------------------------------------------------------------===//
2141 
2142 LogicalResult PadLibOp::verify() {
2143  unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2144  unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2145  if (inBits >= outBits)
2146  return emitOpError("expected input bits (")
2147  << inBits << ')' << " to be less than output bits (" << outBits
2148  << ')';
2149  return success();
2150 }
2151 
2152 LogicalResult SliceLibOp::verify() {
2153  unsigned inBits = getResult(0).getType().getIntOrFloatBitWidth();
2154  unsigned outBits = getResult(1).getType().getIntOrFloatBitWidth();
2155  if (inBits <= outBits)
2156  return emitOpError("expected input bits (")
2157  << inBits << ')' << " to be greater than output bits (" << outBits
2158  << ')';
2159  return success();
2160 }
2161 
2162 #define ImplBinPipeOpCellInterface(OpType, outName) \
2163  SmallVector<StringRef> OpType::portNames() { \
2164  return {"clk", "reset", "go", "left", "right", outName, "done"}; \
2165  } \
2166  \
2167  SmallVector<Direction> OpType::portDirections() { \
2168  return {Input, Input, Input, Input, Input, Output, Output}; \
2169  } \
2170  \
2171  void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2172  getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2173  } \
2174  \
2175  SmallVector<DictionaryAttr> OpType::portAttributes() { \
2176  MLIRContext *context = getContext(); \
2177  IntegerAttr isSet = IntegerAttr::get(IntegerType::get(context, 1), 1); \
2178  NamedAttrList go, clk, reset, done; \
2179  go.append("go", isSet); \
2180  clk.append("clk", isSet); \
2181  reset.append("reset", isSet); \
2182  done.append("done", isSet); \
2183  return { \
2184  clk.getDictionary(context), /* Clk */ \
2185  reset.getDictionary(context), /* Reset */ \
2186  go.getDictionary(context), /* Go */ \
2187  DictionaryAttr::get(context), /* Lhs */ \
2188  DictionaryAttr::get(context), /* Rhs */ \
2189  DictionaryAttr::get(context), /* Out */ \
2190  done.getDictionary(context) /* Done */ \
2191  }; \
2192  } \
2193  \
2194  bool OpType::isCombinational() { return false; }
2195 
2196 #define ImplUnaryOpCellInterface(OpType) \
2197  SmallVector<StringRef> OpType::portNames() { return {"in", "out"}; } \
2198  SmallVector<Direction> OpType::portDirections() { return {Input, Output}; } \
2199  SmallVector<DictionaryAttr> OpType::portAttributes() { \
2200  return {DictionaryAttr::get(getContext()), \
2201  DictionaryAttr::get(getContext())}; \
2202  } \
2203  bool OpType::isCombinational() { return true; } \
2204  void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2205  getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2206  }
2207 
2208 #define ImplBinOpCellInterface(OpType) \
2209  SmallVector<StringRef> OpType::portNames() { \
2210  return {"left", "right", "out"}; \
2211  } \
2212  SmallVector<Direction> OpType::portDirections() { \
2213  return {Input, Input, Output}; \
2214  } \
2215  void OpType::getAsmResultNames(OpAsmSetValueNameFn setNameFn) { \
2216  getCellAsmResultNames(setNameFn, *this, this->portNames()); \
2217  } \
2218  bool OpType::isCombinational() { return true; } \
2219  SmallVector<DictionaryAttr> OpType::portAttributes() { \
2220  return {DictionaryAttr::get(getContext()), \
2221  DictionaryAttr::get(getContext()), \
2222  DictionaryAttr::get(getContext())}; \
2223  }
2224 
2225 // clang-format off
2226 ImplBinPipeOpCellInterface(MultPipeLibOp, "out")
2227 ImplBinPipeOpCellInterface(DivUPipeLibOp, "out_quotient")
2228 ImplBinPipeOpCellInterface(DivSPipeLibOp, "out_quotient")
2229 ImplBinPipeOpCellInterface(RemUPipeLibOp, "out_remainder")
2230 ImplBinPipeOpCellInterface(RemSPipeLibOp, "out_remainder")
2231 
2232 ImplUnaryOpCellInterface(PadLibOp)
2233 ImplUnaryOpCellInterface(SliceLibOp)
2234 ImplUnaryOpCellInterface(NotLibOp)
2235 ImplUnaryOpCellInterface(WireLibOp)
2236 ImplUnaryOpCellInterface(ExtSILibOp)
2237 
2238 ImplBinOpCellInterface(LtLibOp)
2239 ImplBinOpCellInterface(GtLibOp)
2240 ImplBinOpCellInterface(EqLibOp)
2241 ImplBinOpCellInterface(NeqLibOp)
2242 ImplBinOpCellInterface(GeLibOp)
2243 ImplBinOpCellInterface(LeLibOp)
2244 ImplBinOpCellInterface(SltLibOp)
2245 ImplBinOpCellInterface(SgtLibOp)
2246 ImplBinOpCellInterface(SeqLibOp)
2247 ImplBinOpCellInterface(SneqLibOp)
2248 ImplBinOpCellInterface(SgeLibOp)
2249 ImplBinOpCellInterface(SleLibOp)
2250 
2251 ImplBinOpCellInterface(AddLibOp)
2252 ImplBinOpCellInterface(SubLibOp)
2253 ImplBinOpCellInterface(ShruLibOp)
2254 ImplBinOpCellInterface(RshLibOp)
2255 ImplBinOpCellInterface(SrshLibOp)
2256 ImplBinOpCellInterface(LshLibOp)
2257 ImplBinOpCellInterface(AndLibOp)
2258 ImplBinOpCellInterface(OrLibOp)
2259 ImplBinOpCellInterface(XorLibOp)
2260 // clang-format on
2261 
2262 //===----------------------------------------------------------------------===//
2263 // TableGen generated logic.
2264 //===----------------------------------------------------------------------===//
2265 
2266 #include "circt/Dialect/Calyx/CalyxInterfaces.cpp.inc"
2267 
2268 // Provide the autogenerated implementation guts for the Op classes.
2269 #define GET_OP_CLASSES
2270 #include "circt/Dialect/Calyx/Calyx.cpp.inc"
static LogicalResult verifyPrimitiveOpType(PrimitiveOp instance, hw::HWModuleExternOp referencedPrimitive)
Verifies the port information in comparison with the referenced component of an instance.
Definition: CalyxOps.cpp:1458
static ParseResult parseComponentSignature(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &ports, SmallVectorImpl< Type > &portTypes)
Parses the signature of a Calyx component.
Definition: CalyxOps.cpp:403
static LogicalResult verifyAssignOpValue(AssignOp op, bool isDestination)
Verifies the value of a given assignment operation.
Definition: CalyxOps.cpp:1251
static ParseResult parseParameterList(OpAsmParser &parser, SmallVector< Attribute > &parameters)
Parse an parameter list if present.
Definition: CalyxOps.cpp:1632
Direction convertHWDirectionToCalyx(hw::PortDirection direction)
Definition: CalyxOps.cpp:1568
static SmallVector< PortInfo > getFilteredPorts(ComponentOp op, Pred p)
A helper function to return a filtered subset of a component's ports.
Definition: CalyxOps.cpp:626
static Op getControlOrWiresFrom(ComponentOp op)
This is a helper function that should only be used to get the WiresOp or ControlOp of a ComponentOp,...
Definition: CalyxOps.cpp:564
static LogicalResult verifyPrimitivePortDriving(AssignOp assign, GroupInterface group)
Verifies that certain ports of primitives are either driven or read together.
Definition: CalyxOps.cpp:1102
static bool portIsUsedInGroup(GroupInterface group, Value port, bool isDriven)
Determines whether the given port is used in the group.
Definition: CalyxOps.cpp:1008
static Value getBlockArgumentWithName(StringRef name, ComponentOp op)
Returns the Block argument with the given name from a ComponentOp.
Definition: CalyxOps.cpp:574
static ParseResult parsePortDefList(OpAsmParser &parser, OperationState &result, SmallVectorImpl< OpAsmParser::Argument > &ports, SmallVectorImpl< Type > &portTypes, SmallVectorImpl< NamedAttrList > &portAttrs)
Parses the ports of a Calyx component signature, and adds the corresponding port names to attrName.
Definition: CalyxOps.cpp:375
static std::string valueName(Operation *scopeOp, Value v)
Convenience function for getting the SSA name of v under the scope of operation scopeOp.
Definition: CalyxOps.cpp:107
static LogicalResult verifyNotComplexSource(Op op)
Verify that the value is not a "complex" value.
Definition: CalyxOps.cpp:90
static LogicalResult verifyInstanceOpType(InstanceOp instance, ComponentInterface referencedComponent)
Verifies the port information in comparison with the referenced component of an instance.
Definition: CalyxOps.cpp:1351
static LogicalResult collapseControl(OpTy controlOp, PatternRewriter &rewriter)
Definition: CalyxOps.cpp:275
static LogicalResult anyPortsReadByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are read within the group.
Definition: CalyxOps.cpp:1085
static bool hasControlRegion(Operation *op)
Returns whether the given operation has a control region.
Definition: CalyxOps.cpp:136
static LogicalResult emptyControl(OpTy controlOp, PatternRewriter &rewriter)
Definition: CalyxOps.cpp:293
static LogicalResult verifyControlBody(Operation *op)
Verifies the body of a ControlLikeOp.
Definition: CalyxOps.cpp:141
static void eraseControlWithGroupAndConditional(OpTy op, PatternRewriter &rewriter)
A helper function to check whether the conditional and group (if it exists) needs to be erased to mai...
Definition: CalyxOps.cpp:305
static ParseResult parseComponentInterface(OpAsmParser &parser, OperationState &result)
Definition: CalyxOps.cpp:449
static void printComponentInterface(OpAsmPrinter &p, ComponentInterface comp)
Definition: CalyxOps.cpp:333
static LogicalResult hasRequiredPorts(ComponentOp op)
Determines whether the given ComponentOp has all the required ports.
Definition: CalyxOps.cpp:651
static LogicalResult anyPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether any ports are driven within the group.
Definition: CalyxOps.cpp:1069
static bool isPort(Value value)
Returns whether this value is either (1) a port on a ComponentOp or (2) a port on a cell interface.
Definition: CalyxOps.cpp:121
static LogicalResult verifyPortDirection(AssignOp op, Value value, bool isDestination)
Determines whether the given direction is valid with the given inputs.
Definition: CalyxOps.cpp:1221
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
Definition: CalyxOps.cpp:486
static LogicalResult portDrivenByGroup(GroupInterface groupOp, Value port)
Checks whether port is driven from within groupOp.
Definition: CalyxOps.cpp:1030
static void buildComponentLike(OpBuilder &builder, OperationState &result, StringAttr name, ArrayRef< PortInfo > ports, bool combinational)
Definition: CalyxOps.cpp:494
static void getCellAsmResultNames(OpAsmSetValueNameFn setNameFn, Operation *op, ArrayRef< StringRef > portNames)
Gives each result of the cell a meaningful name in the form: <instance-name>.
Definition: CalyxOps.cpp:1204
static LogicalResult allPortsDrivenByGroup(GroupInterface group, ValueRange ports)
Checks whether all ports are driven within the group.
Definition: CalyxOps.cpp:1053
static LogicalResult isCombinational(Value value, GroupInterface group)
Verifies the defining operation of a value is combinational.
Definition: CalyxOps.cpp:933
static void printParameterList(OpAsmPrinter &p, Operation *op, ArrayAttr parameters)
Print a parameter list for a module or instance. Same format as HW dialect.
Definition: CalyxOps.cpp:1670
static DictionaryAttr cleanCalyxPortAttrs(OpBuilder builder, DictionaryAttr dict)
Returns a new DictionaryAttr containing only the calyx dialect attrs in the input DictionaryAttr.
Definition: CalyxOps.cpp:1592
static void printGroupPort(OpAsmPrinter &p, GroupPortType op)
Definition: CalyxOps.cpp:259
static ParseResult parseGroupPort(OpAsmParser &parser, OperationState &result)
Definition: CalyxOps.cpp:231
static SmallVector< Block *, 8 > intersection(SmallVectorImpl< Block * > &v1, SmallVectorImpl< Block * > &v2)
Calculate intersection of two vectors, returns a new vector.
Builder builder
static size_t bits(::capnp::schema::Type::Reader type)
Return the number of bits used by a Capnp type.
Definition: Schema.cpp:120
Signals that the following operation is combinational.
Definition: CalyxOps.h:48
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:42
IntegerAttr packAttribute(MLIRContext *context, size_t nIns, size_t nOuts)
Returns an IntegerAttr containing the packed representation of the direction counts.
Definition: CalyxOps.cpp:46
LogicalResult verifyComponent(Operation *op)
A helper function to verify each operation with the Ccomponent trait.
Definition: CalyxOps.cpp:175
LogicalResult verifyControlLikeOp(Operation *op)
A helper function to verify each control-like operation has a valid parent and, if applicable,...
Definition: CalyxOps.cpp:191
LogicalResult verifyGroupInterface(Operation *op)
A helper function to verify each operation with the Group Interface trait.
Definition: CalyxOps.cpp:1182
LogicalResult verifyCell(Operation *op)
A helper function to verify each operation with the Cell trait.
Definition: CalyxOps.cpp:183
Direction
The direction of a Component or Cell port.
Definition: CalyxOps.h:65
PortInfo getPortInfo(BlockArgument arg)
Returns port information for the block argument provided.
Definition: CalyxOps.cpp:128
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
mlir::FailureOr< mlir::Type > evaluateParametricType(mlir::Location loc, mlir::ArrayAttr parameters, mlir::Type type)
Returns a resolved version of 'type' wherein any parameter reference has been evaluated based on the ...
PortDirection
A module port direction.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition: LLVM.h:184
This pattern collapses a calyx.seq or calyx.par operation when it contains exactly one calyx....
Definition: CalyxOps.cpp:65
LogicalResult matchAndRewrite(CtrlOp ctrlOp, PatternRewriter &rewriter) const override
Definition: CalyxOps.cpp:67
This holds information about the port for either a Component or Cell.
Definition: CalyxOps.h:78
Direction direction
Definition: CalyxOps.h:81
DictionaryAttr attributes
Definition: CalyxOps.h:82